1 // ----------------------------------------------------------------------------
2 //
3 // Copyright (C) 2013-2020 Fons Adriaensen <fons@linuxaudio.org>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // ----------------------------------------------------------------------------
19
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <signal.h>
25 #include <getopt.h>
26 #include <math.h>
27 #include <sys/mman.h>
28 #include <unistd.h> // for usleep
29 #include "jacktx.h"
30 #include "nettx.h"
31 #include "lfqueue.h"
32 #include "netdata.h"
33 #include "zsockets.h"
34
35
36 #define APPNAME "zita-j2n"
37
38
39 static Lfq_packdata *packq = 0;
40 static Lfq_timedata *timeq = 0;
41 static Lfq_int32 *infoq = 0;
42 static Netdata descpack (40);
43 static volatile bool stop = false;
44
45
46 static const char *name_arg = APPNAME;
47 static const char *serv_arg = 0;
48 static int chan_arg = 2;
49 static const char *addr_arg = 0;
50 static int port_arg = 0;
51 static const char *dev_arg = 0;
52 static int mtu_arg = 1500;
53 static int hops_arg = 1;
54 static int form_arg = Netdata::FM_24BIT;
55 static bool ipv4_opt = false;
56 static bool ipv6_opt = false;
57
58
help(void)59 static void help (void)
60 {
61 fprintf (stderr, "\n%s-%s\n", APPNAME, VERSION);
62 fprintf (stderr, "(C) 2013-2020 Fons Adriaensen <fons@linuxaudio.org>\n");
63 fprintf (stderr, "Send audio to zita-n2j.\n\n");
64 fprintf (stderr, "Usage: %s <options> ip-address ip-port \n", APPNAME);
65 fprintf (stderr, " %s <options> ip-address ip-port interface\n", APPNAME);
66 fprintf (stderr, "Options:\n");
67 fprintf (stderr, " --help Display this text\n");
68 fprintf (stderr, " --jname <name> Jack client name [%s]\n", APPNAME);
69 fprintf (stderr, " --jserv <name> Jack server name\n");
70 fprintf (stderr, " --chan <nchan> Number of channels [%d]\n", chan_arg);
71 fprintf (stderr, " --16bit Send 16-bit samples\n");
72 fprintf (stderr, " --24bit Send 24-bit samples (default)\n");
73 fprintf (stderr, " --float Send floating point samples\n");
74 fprintf (stderr, " --mtu <size> Maximum packet size [%d]\n", mtu_arg);
75 fprintf (stderr, " --hops <hops> Number of hops for multicast [%d]\n", hops_arg);
76 fprintf (stderr, " --ipv4 Use IPV4 only\n");
77 fprintf (stderr, " --ipv6 Use IPV6 only\n");
78
79 exit (1);
80 }
81
82
83 enum { HELP, NAME, SERV, CHAN, BIT16, BIT24, FLT32, MTU, HOPS, IPV4, IPV6 };
84
85
86 static struct option options [] =
87 {
88 { "help", 0, 0, HELP },
89 { "jname", 1, 0, NAME },
90 { "jserv", 1, 0, SERV },
91 { "chan", 1, 0, CHAN },
92 { "mtu", 1, 0, MTU },
93 { "hops", 1, 0, HOPS },
94 { "16bit", 0, 0, BIT16 },
95 { "24bit", 0, 0, BIT24 },
96 { "float", 0, 0, FLT32 },
97 { "ipv4", 0, 0, IPV4 },
98 { "ipv6", 0, 0, IPV6 },
99 { 0, 0, 0, 0 }
100 };
101
102
getint(const char * optname)103 static int getint (const char *optname)
104 {
105 int v;
106
107 if (sscanf (optarg, "%d", &v) != 1)
108 {
109 fprintf (stderr, "Bad option argument: --%s %s\n", optname, optarg);
110 exit (1);
111 }
112 return v;
113 }
114
115
procoptions(int ac,char * av[])116 static void procoptions (int ac, char *av [])
117 {
118 int k;
119
120 while ((k = getopt_long (ac, av, "", options, 0)) != -1)
121 {
122 switch (k)
123 {
124 case '?':
125 case HELP:
126 help ();
127 break;
128 case NAME:
129 name_arg = optarg;
130 break;
131 case SERV:
132 serv_arg = optarg;
133 break;
134 case CHAN:
135 chan_arg = getint ("chan");
136 break;
137 case MTU:
138 mtu_arg = getint ("mtu");
139 break;
140 case HOPS:
141 hops_arg = getint ("hops");
142 break;
143 case BIT16:
144 form_arg = Netdata::FM_16BIT;
145 break;
146 case BIT24:
147 form_arg = Netdata::FM_24BIT;
148 break;
149 case FLT32:
150 form_arg = Netdata::FM_FLOAT;
151 break;
152 case IPV4:
153 ipv4_opt = true;
154 break;
155 case IPV6:
156 ipv6_opt = true;
157 break;
158 }
159 }
160 if (ac < optind + 2) help ();
161 if (ac > optind + 3) help ();
162 addr_arg = av [optind++];
163 port_arg = atoi (av [optind++]);
164 if (ac == optind + 1) dev_arg = av [optind];
165 }
166
167
siginthandler(int)168 static void siginthandler (int)
169 {
170 signal (SIGINT, SIG_IGN);
171 stop = true;
172 }
173
174
checkstatus(void)175 static void checkstatus (void)
176 {
177 int state;
178
179 while (infoq->rd_avail ())
180 {
181 state = infoq->rd_int32 ();
182 switch (state)
183 {
184 case Jacktx::TERM:
185 printf ("Fatal error condition, terminating.\n");
186 stop = true;
187 return;
188 }
189 }
190 }
191
192
opensocket(Sockaddr * A)193 static int opensocket (Sockaddr *A)
194 {
195 int fd = -1;
196
197 if (A->is_multicast ())
198 {
199 if (dev_arg) fd = sock_open_mcsend (A, dev_arg, 1, hops_arg);
200 else
201 {
202 fprintf (stderr, "Multicast requires a network device.\n");
203 exit (1);
204 }
205 }
206 else
207 {
208 if (dev_arg) fprintf (stderr, "Ignored extra argument '%s'.\n", dev_arg);
209 fd = sock_open_dgram (A, 0);
210 }
211 if (fd < 0)
212 {
213 fprintf (stderr, "Failed to open socket.\n");
214 exit (1);
215 }
216 return fd;
217 }
218
219
220
main(int ac,char * av[])221 int main (int ac, char *av [])
222 {
223 Sockaddr A;
224 int sockfd, psize, ppper, npack, ipfam;
225 Jacktx *jacktx = 0;
226 Nettx *nettx = 0;
227
228 procoptions (ac, av);
229
230 if ((chan_arg < 1) || (chan_arg > Netdata::MAXCHAN))
231 {
232 fprintf (stderr, "Number of channels is out of range.\n");
233 exit (1);
234 }
235
236 ipfam = AF_UNSPEC;
237 if (ipv4_opt) ipfam = AF_INET;
238 if (ipv6_opt) ipfam = AF_INET6;
239 if (A.set_addr (ipfam, SOCK_DGRAM, 0, addr_arg))
240 {
241 fprintf (stderr, "Address resolution failed.\n");
242 exit (1);
243 }
244 if ((port_arg < 1) || (port_arg > 65535))
245 {
246 fprintf (stderr, "Port number is out of range.\n");
247 exit (1);
248 }
249 A.set_port (port_arg);
250
251 if (mlockall (MCL_CURRENT | MCL_FUTURE))
252 {
253 fprintf (stderr, "Warning: memory lock failed.\n");
254 }
255
256 jacktx = new Jacktx (name_arg, serv_arg, chan_arg);
257 nettx = new Nettx;
258 usleep (100000);
259
260 sockfd = opensocket (&A);
261 psize = mtu_arg - ((A.family () == AF_INET6) ? 48 : 28);
262 ppper = Netdata::packetsperperiod (psize, jacktx->bsize (), form_arg, chan_arg);
263 npack = ppper * (int)(ceil (0.25 * jacktx->fsamp () / jacktx->bsize ()));
264 packq = new Lfq_packdata (npack, psize);
265 timeq = new Lfq_timedata (4);
266 infoq = new Lfq_int32 (16);
267
268 descpack.init_audio_desc (0, form_arg, chan_arg, psize, jacktx->fsamp (), jacktx->bsize ());
269 nettx->start (packq, timeq, &descpack, sockfd, jacktx->rprio () + 5);
270 jacktx->start (packq, timeq, infoq, nettx, form_arg, ppper);
271
272 signal (SIGINT, siginthandler);
273 while (! stop)
274 {
275 usleep (500000);
276 nettx->trigger ();
277 checkstatus ();
278 }
279
280 nettx->stop ();
281 usleep (100000);
282 delete jacktx;
283 delete nettx;
284 delete packq;
285 delete timeq;
286 delete infoq;
287
288 return 0;
289 }
290