zalsa: add "-w" argument to wait for soundcard to be available
[jack2.git] / tools / zalsa / zita-j2a.cc
bloba341ab68b7214560b5bb183ec8f4bba2d804b154
1 // ----------------------------------------------------------------------------
2 //
3 // Copyright (C) 2012 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.
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/>.
18 // ----------------------------------------------------------------------------
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <stdio.h>
25 #include <signal.h>
26 #include "alsathread.h"
27 #include "jackclient.h"
28 #include "lfqueue.h"
29 #include "jack/control.h"
31 static const char *clopt = "hvLSwj:d:r:p:n:c:Q:O:";
33 static void help (void)
35 jack_info ("%s-%s", APPNAME, VERSION);
36 jack_info ("(C) 2012-2018 Fons Adriaensen <fons@linuxaudio.org>");
37 jack_info ("Use ALSA playback device as a Jack client.");
38 jack_info ("Options:");
39 jack_info (" -h Display this text");
40 jack_info (" -j <jackname> Name as Jack client [%s]", APPNAME);
41 jack_info (" -d <device> ALSA playback device [none]");
42 jack_info (" -r <rate> Sample rate [48000]");
43 jack_info (" -p <period> Period size [256]");
44 jack_info (" -n <nfrags> Number of fragments [2]");
45 jack_info (" -c <nchannels> Number of channels [2]");
46 jack_info (" -S Word clock sync, no resampling");
47 jack_info (" -Q <quality> Resampling quality, 16..96 [auto]");
48 jack_info (" -O <samples> Latency adjustment [0]");
49 jack_info (" -L Force 16-bit and 2 channels [off]");
50 jack_info (" -w Wait until soundcard is available [off]");
51 jack_info (" -v Print tracing information [off]");
54 class zita_j2a
56 Lfq_int32 *commq;
57 Lfq_adata *alsaq;
58 Lfq_jdata *infoq;
59 Lfq_audio *audioq;
60 bool stop;
61 bool v_opt;
62 bool L_opt;
63 bool S_opt;
64 bool w_opt;
65 char *jname;
66 char *device;
67 int fsamp;
68 int bsize;
69 int nfrag;
70 int nchan;
71 int rqual;
72 int ltcor;
74 public:
76 zita_j2a()
78 commq = new Lfq_int32(16);
79 alsaq = new Lfq_adata(256);
80 infoq = new Lfq_jdata(256);
81 audioq = 0;
82 stop = false;
83 v_opt = false;
84 L_opt = false;
85 S_opt = false;
86 w_opt = false;
87 jname = strdup(APPNAME);
88 device = 0;
89 fsamp = 48000;
90 bsize = 128;
91 nfrag = 2;
92 nchan = 2;
93 rqual = 0;
94 ltcor = 0;
95 A = 0;
96 P = 0;
97 J = 0;
98 t = 0;
101 private:
103 int procoptions (int ac, const char *av [])
105 int k;
107 optind = 1;
108 opterr = 0;
109 while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1)
111 if (optarg && (*optarg == '-'))
113 jack_error (APPNAME ": Missing argument for '-%c' option.", k);
114 jack_error (APPNAME ": Use '-h' to see all options.");
115 return 1;
117 switch (k)
119 case 'h' : help (); return 1;
120 case 'v' : v_opt = true; break;
121 case 'L' : L_opt = true; break;
122 case 'S' : S_opt = true; break;
123 case 'w' : w_opt = true; break;
124 case 'j' : jname = optarg; break;
125 case 'd' : device = optarg; break;
126 case 'r' : fsamp = atoi (optarg); break;
127 case 'p' : bsize = atoi (optarg); break;
128 case 'n' : nfrag = atoi (optarg); break;
129 case 'c' : nchan = atoi (optarg); break;
130 case 'Q' : rqual = atoi (optarg); break;
131 case 'O' : ltcor = atoi (optarg); break;
132 case '?':
133 if (optopt != ':' && strchr (clopt, optopt))
135 jack_error (APPNAME ": Missing argument for '-%c' option.", optopt);
137 else if (isprint (optopt))
139 jack_error (APPNAME ": Unknown option '-%c'.", optopt);
141 else
143 jack_error (APPNAME ": Unknown option character '0x%02x'.", optopt & 255);
145 jack_error (APPNAME ": Use '-h' to see all options.");
146 return 1;
147 default:
148 return 1;
152 return 0;
155 int parse_options (const char* load_init)
157 int argsz;
158 int argc = 0;
159 const char** argv;
160 char* args = strdup (load_init);
161 char* token;
162 char* ptr = args;
163 char* savep;
165 if (!load_init) {
166 return 0;
169 argsz = 8; /* random guess at "maxargs" */
170 argv = (const char **) malloc (sizeof (char *) * argsz);
172 argv[argc++] = APPNAME;
174 while (1) {
176 if ((token = strtok_r (ptr, " ", &savep)) == NULL) {
177 break;
180 if (argc == argsz) {
181 argsz *= 2;
182 argv = (const char **) realloc (argv, sizeof (char *) * argsz);
185 argv[argc++] = token;
186 ptr = NULL;
189 return procoptions (argc, argv);
192 void printinfo (void)
194 int n, k;
195 double e, r;
196 Jdata *J;
198 n = 0;
199 k = 99999;
200 e = r = 0;
201 while (infoq->rd_avail ())
203 J = infoq->rd_datap ();
204 if (J->_state == Jackclient::TERM)
206 jack_info (APPNAME ": Fatal error condition, terminating.");
207 stop = true;
208 return;
210 else if (J->_state == Jackclient::WAIT)
212 jack_info (APPNAME ": Detected excessive timing errors, waiting 10 seconds.");
213 n = 0;
215 else if (J->_state == Jackclient::SYNC0)
217 jack_info (APPNAME ": Starting synchronisation.");
219 else if (v_opt)
221 n++;
222 e += J->_error;
223 r += J->_ratio;
224 if (J->_bstat < k) k = J->_bstat;
226 infoq->rd_commit ();
228 if (n) jack_info ("%8.3lf %10.6lf %5d", e / n, r / n, k);
231 Alsa_pcmi *A;
232 Alsathread *P;
233 Jackclient *J;
235 pthread_t t;
236 int topts;
238 static void* _retry_alsa_pcmi (void *arg)
240 ((zita_j2a*)arg)->retry_alsa_pcmi ();
241 return NULL;
244 void retry_alsa_pcmi ()
246 Alsa_pcmi *a;
248 while (! stop)
250 sleep(1);
252 a = new Alsa_pcmi (device, 0, 0, fsamp, bsize, nfrag, topts);
253 if (a->state ())
255 delete a;
256 continue;
259 A = a;
260 if (v_opt) A->printinfo ();
261 P = new Alsathread (A, Alsathread::PLAY);
262 usleep (100*1000);
263 jack_initialize_part2 ();
264 jack_info (APPNAME ": Device is now available and has been activated");
265 break;
268 t = 0;
271 public:
273 int jack_initialize (jack_client_t* client, const char* load_init)
275 int opts;
277 if (parse_options (load_init)) {
278 delete this;
279 return 1;
282 if (device == 0)
284 help ();
285 delete this;
286 return 1;
288 if (rqual < 16) rqual = 16;
289 if (rqual > 96) rqual = 96;
290 if ((fsamp < 8000) || (bsize < 16) || (nfrag < 2) || (nchan < 1))
292 jack_error (APPNAME ": Illegal parameter value(s).");
293 delete this;
294 return 1;
297 opts = 0;
298 if (v_opt) opts |= Alsa_pcmi::DEBUG_ALL;
299 if (L_opt) opts |= Alsa_pcmi::FORCE_16B | Alsa_pcmi::FORCE_2CH;
300 if (w_opt)
302 J = new Jackclient (client, 0, Jackclient::PLAY, nchan, S_opt, this);
304 // if device is not available, spawn thread to keep trying
305 A = new Alsa_pcmi (device, 0, 0, fsamp, bsize, nfrag, opts);
306 if (A->state ())
308 delete A;
309 A = NULL;
310 topts = opts;
311 pthread_create (&t, NULL, _retry_alsa_pcmi, this);
312 jack_info (APPNAME ": Could not open device, will keep trying in new thread...");
313 return 0;
316 // otherwise continue as normal
317 if (v_opt) A->printinfo ();
318 P = new Alsathread (A, Alsathread::PLAY);
320 else
322 A = new Alsa_pcmi (device, 0, 0, fsamp, bsize, nfrag, opts);
323 if (A->state ())
325 jack_error (APPNAME ": Can't open ALSA playback device '%s'.", device);
326 delete this;
327 return 1;
329 if (v_opt) A->printinfo ();
330 if (nchan > A->nplay ())
332 nchan = A->nplay ();
333 jack_error (APPNAME ": Warning: only %d channels are available.", nchan);
335 P = new Alsathread (A, Alsathread::PLAY);
336 J = new Jackclient (client, 0, Jackclient::PLAY, nchan, S_opt, this);
339 usleep (100*1000);
340 jack_initialize_part2 ();
341 return 0;
344 void jack_initialize_part2 ()
346 int k, k_del;
347 double t_jack;
348 double t_alsa;
349 double t_del;
351 t_alsa = (double) bsize / fsamp;
352 if (t_alsa < 1e-3) t_alsa = 1e-3;
353 t_jack = (double) J->bsize () / J->fsamp ();
354 t_del = t_alsa + t_jack;
355 k_del = (int)(t_del * fsamp);
356 for (k = 256; k < 2 * k_del; k *= 2);
357 audioq = new Lfq_audio (k, nchan);
359 if (rqual == 0)
361 k = (fsamp < J->fsamp ()) ? fsamp : J->fsamp ();
362 if (k < 44100) k = 44100;
363 rqual = (int)((6.7 * k) / (k - 38000));
365 if (rqual < 16) rqual = 16;
366 if (rqual > 96) rqual = 96;
368 P->start (audioq, commq, alsaq, J->rprio () + 10);
369 J->start (audioq, commq, alsaq, infoq, (double) fsamp / J->fsamp (), k_del, ltcor, rqual);
372 void jack_finish (void* arg)
374 if (t != 0)
376 stop = true;
377 pthread_join(t, NULL);
378 t = 0;
381 commq->wr_int32 (Alsathread::TERM);
382 usleep (100*1000);
383 delete P;
384 delete A;
385 delete J;
386 delete audioq;
390 extern "C" {
393 jack_initialize (jack_client_t* client, const char* load_init)
395 zita_j2a *c = new zita_j2a();
396 return c->jack_initialize(client, load_init);
399 void jack_finish (void* arg)
401 if (!arg) return;
402 Jackclient *J = (Jackclient *)arg;
403 zita_j2a *c = (zita_j2a *)J->getarg();
404 c->jack_finish(arg);
405 delete c;
408 } /* extern "C" */