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