1 // ----------------------------------------------------------------------------
3 // Copyright (C) 2012 Fons Adriaensen <fons@linuxaudio.org>
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.
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 // ----------------------------------------------------------------------------
26 #include "alsathread.h"
27 #include "jackclient.h"
29 #include "jack/control.h"
31 static const char *clopt
= "hvLSwj:d:r:p:n:c:Q:I:";
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 capture 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 capture 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 (" -I <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]");
78 commq
= new Lfq_int32(16);
79 alsaq
= new Lfq_adata(256);
80 infoq
= new Lfq_jdata(256);
87 jname
= strdup(APPNAME
);
103 int procoptions (int ac
, const char *av
[])
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.");
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 'I' : ltcor
= atoi (optarg
); break;
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
);
143 jack_error (APPNAME
": Unknown option character '0x%02x'.", optopt
& 255);
145 jack_error (APPNAME
": Use '-h' to see all options.");
154 int parse_options (const char* load_init
)
159 char* args
= strdup (load_init
);
168 argsz
= 8; /* random guess at "maxargs" */
169 argv
= (const char **) malloc (sizeof (char *) * argsz
);
171 argv
[argc
++] = APPNAME
;
175 if ((token
= strtok_r (ptr
, " ", &savep
)) == NULL
) {
181 argv
= (const char **) realloc (argv
, sizeof (char *) * argsz
);
184 argv
[argc
++] = token
;
188 return procoptions (argc
, argv
);
191 void printinfo (void)
200 while (infoq
->rd_avail ())
202 J
= infoq
->rd_datap ();
203 if (J
->_state
== Jackclient::TERM
)
205 jack_error (APPNAME
": Fatal error condition, terminating.");
209 else if (J
->_state
== Jackclient::WAIT
)
211 jack_info (APPNAME
": Detected excessive timing errors, waiting 10 seconds.");
214 else if (J
->_state
== Jackclient::SYNC0
)
216 jack_info (APPNAME
": Starting synchronisation.");
223 if (J
->_bstat
< k
) k
= J
->_bstat
;
227 if (n
) jack_info (APPNAME
": %8.3lf %10.6lf %5d", e
/ n
, r
/ n
, k
);
238 static void* _retry_alsa_pcmi (void *arg
)
240 ((zita_a2j
*)arg
)->retry_alsa_pcmi ();
244 void retry_alsa_pcmi ()
252 a
= new Alsa_pcmi (0, device
, 0, fsamp
, bsize
, nfrag
, topts
);
260 if (v_opt
) A
->printinfo ();
261 C
= new Alsathread (A
, Alsathread::CAPT
);
263 jack_initialize_part2 ();
264 jack_info (APPNAME
": Device is now available and has been activated");
274 jack_initialize (jack_client_t
* client
, const char* load_init
)
278 if (parse_options (load_init
)) {
279 jack_error (APPNAME
": parse options failed");
290 if (rqual
< 16) rqual
= 16;
291 if (rqual
> 96) rqual
= 96;
292 if ((fsamp
< 8000) || (bsize
< 16) || (nfrag
< 2) || (nchan
< 1))
294 jack_error (APPNAME
": Illegal parameter value(s).");
300 if (v_opt
) opts
|= Alsa_pcmi::DEBUG_ALL
;
301 if (L_opt
) opts
|= Alsa_pcmi::FORCE_16B
| Alsa_pcmi::FORCE_2CH
;
304 J
= new Jackclient (client
, 0, Jackclient::CAPT
, nchan
, S_opt
, this);
305 A
= new Alsa_pcmi (0, device
, 0, fsamp
, bsize
, nfrag
, opts
);
307 // if device is not available, spawn thread to keep trying
313 pthread_create (&t
, NULL
, _retry_alsa_pcmi
, this);
314 jack_info (APPNAME
": Could not open device, will keep trying in new thread...");
318 // otherwise continue as normal
319 if (v_opt
) A
->printinfo ();
320 C
= new Alsathread (A
, Alsathread::CAPT
);
324 A
= new Alsa_pcmi (0, device
, 0, fsamp
, bsize
, nfrag
, opts
);
327 jack_error (APPNAME
": Can't open ALSA capture device '%s'.", device
);
331 if (v_opt
) A
->printinfo ();
332 if (nchan
> A
->ncapt ())
335 jack_error (APPNAME
": Warning: only %d channels are available.", nchan
);
337 C
= new Alsathread (A
, Alsathread::CAPT
);
338 J
= new Jackclient (client
, 0, Jackclient::CAPT
, nchan
, S_opt
, this);
342 jack_initialize_part2 ();
346 void jack_initialize_part2 ()
353 t_alsa
= (double) bsize
/ fsamp
;
354 if (t_alsa
< 1e-3) t_alsa
= 1e-3;
355 t_jack
= (double) J
->bsize () / J
->fsamp ();
356 t_del
= t_alsa
+ t_jack
;
357 k_del
= (int)(t_del
* fsamp
);
358 for (k
= 256; k
< 2 * k_del
; k
*= 2);
359 audioq
= new Lfq_audio (k
, nchan
);
363 k
= (fsamp
< J
->fsamp ()) ? fsamp
: J
->fsamp ();
364 if (k
< 44100) k
= 44100;
365 rqual
= (int)((6.7 * k
) / (k
- 38000));
367 if (rqual
< 16) rqual
= 16;
368 if (rqual
> 96) rqual
= 96;
370 C
->start (audioq
, commq
, alsaq
, J
->rprio () + 10);
371 J
->start (audioq
, commq
, alsaq
, infoq
, J
->fsamp () / (double) fsamp
, k_del
, ltcor
, rqual
);
374 void jack_finish (void* arg
)
379 pthread_join(t
, NULL
);
382 commq
->wr_int32 (Alsathread::TERM
);
394 jack_initialize (jack_client_t
* client
, const char* load_init
)
396 zita_a2j
*c
= new zita_a2j();
397 return c
->jack_initialize(client
, load_init
);
400 void jack_finish (void* arg
)
403 Jackclient
*J
= (Jackclient
*)arg
;
404 zita_a2j
*c
= (zita_a2j
*)J
->getarg();