JACK tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
9.1KB

  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.
  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. #include <iostream>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <ctype.h>
  23. #include <stdio.h>
  24. #include <signal.h>
  25. #include "alsathread.h"
  26. #include "jackclient.h"
  27. #include "lfqueue.h"
  28. static const char *clopt = "hvLj:d:r:p:n:c:Q:I:";
  29. static void help (void)
  30. {
  31. fprintf (stderr, "\n%s-%s\n", APPNAME, VERSION);
  32. fprintf (stderr, "(C) 2012-2013 Fons Adriaensen <fons@linuxaudio.org>\n");
  33. fprintf (stderr, "Use ALSA playback device as a Jack client, with resampling.\n\n");
  34. fprintf (stderr, "Usage: %s <options>\n", APPNAME);
  35. fprintf (stderr, "Options:\n");
  36. fprintf (stderr, " -h Display this text\n");
  37. fprintf (stderr, " -j <jackname> Name as Jack client [%s]\n", APPNAME);
  38. fprintf (stderr, " -d <device> ALSA playback device [none]\n");
  39. fprintf (stderr, " -r <rate> Sample rate [48000]\n");
  40. fprintf (stderr, " -p <period> Period size [256]\n");
  41. fprintf (stderr, " -n <nfrags> Number of fragments [2]\n");
  42. fprintf (stderr, " -c <nchannels> Number of channels [2]\n");
  43. fprintf (stderr, " -Q <quality> Resampling quality [48]\n");
  44. fprintf (stderr, " -O <latency> Latency adjustment[0]\n");
  45. fprintf (stderr, " -L Force 16-bit and 2 channels [off]\n");
  46. fprintf (stderr, " -v Print tracing information [off]\n");
  47. exit (1);
  48. }
  49. class zita_j2a
  50. {
  51. Lfq_int32 *commq;
  52. Lfq_adata *alsaq;
  53. Lfq_jdata *infoq;
  54. Lfq_audio *audioq;
  55. bool stop;
  56. bool v_opt;
  57. bool L_opt;
  58. char *jname;
  59. char *device;
  60. int fsamp;
  61. int bsize;
  62. int nfrag;
  63. int nchan;
  64. int rqual;
  65. int ltcor;
  66. public:
  67. zita_j2a()
  68. {
  69. commq = new Lfq_int32(16);
  70. alsaq = new Lfq_adata(256);
  71. infoq = new Lfq_jdata(256);
  72. audioq = 0;
  73. stop = false;
  74. v_opt = false;
  75. L_opt = false;
  76. jname = strdup(APPNAME);
  77. device = 0;
  78. fsamp = 0;
  79. bsize = 0;
  80. nfrag = 2;
  81. nchan = 2;
  82. rqual = 48;
  83. ltcor = 0;
  84. A = 0;
  85. P = 0;
  86. J = 0;
  87. }
  88. private:
  89. int procoptions (int ac, const char *av [])
  90. {
  91. int k;
  92. optind = 1;
  93. opterr = 0;
  94. while ((k = getopt (ac, (char **) av, (char *) clopt)) != -1)
  95. {
  96. if (optarg && (*optarg == '-'))
  97. {
  98. fprintf (stderr, " Missing argument for '-%c' option.\n", k);
  99. fprintf (stderr, " Use '-h' to see all options.\n");
  100. exit (1);
  101. }
  102. switch (k)
  103. {
  104. case 'h' : help (); exit (0);
  105. case 'v' : v_opt = true; break;
  106. case 'L' : L_opt = true; break;
  107. case 'j' : jname = optarg; break;
  108. case 'd' : device = optarg; break;
  109. case 'r' : fsamp = atoi (optarg); break;
  110. case 'p' : bsize = atoi (optarg); break;
  111. case 'n' : nfrag = atoi (optarg); break;
  112. case 'c' : nchan = atoi (optarg); break;
  113. case 'Q' : rqual = atoi (optarg); break;
  114. case 'O' : ltcor = atoi (optarg); break;
  115. case '?':
  116. if (optopt != ':' && strchr (clopt, optopt))
  117. {
  118. fprintf (stderr, " Missing argument for '-%c' option.\n", optopt);
  119. }
  120. else if (isprint (optopt))
  121. {
  122. fprintf (stderr, " Unknown option '-%c'.\n", optopt);
  123. }
  124. else
  125. {
  126. fprintf (stderr, " Unknown option character '0x%02x'.\n", optopt & 255);
  127. }
  128. fprintf (stderr, " Use '-h' to see all options.\n");
  129. return 1;
  130. default:
  131. return 1;
  132. }
  133. }
  134. return 0;
  135. }
  136. int parse_options (const char* load_init)
  137. {
  138. int argsz;
  139. int argc = 0;
  140. const char** argv;
  141. char* args = strdup (load_init);
  142. char* token;
  143. char* ptr = args;
  144. char* savep;
  145. if (!load_init) {
  146. return 0;
  147. }
  148. argsz = 8; /* random guess at "maxargs" */
  149. argv = (const char **) malloc (sizeof (char *) * argsz);
  150. argv[argc++] = APPNAME;
  151. while (1) {
  152. if ((token = strtok_r (ptr, " ", &savep)) == NULL) {
  153. break;
  154. }
  155. if (argc == argsz) {
  156. argsz *= 2;
  157. argv = (const char **) realloc (argv, sizeof (char *) * argsz);
  158. }
  159. argv[argc++] = token;
  160. ptr = NULL;
  161. }
  162. return procoptions (argc, argv);
  163. }
  164. void printinfo (void)
  165. {
  166. int n;
  167. double e, r;
  168. Jdata *J;
  169. n = 0;
  170. e = r = 0;
  171. while (infoq->rd_avail ())
  172. {
  173. J = infoq->rd_datap ();
  174. if (J->_state == Jackclient::TERM)
  175. {
  176. printf ("Fatal error condition, terminating.\n");
  177. stop = true;
  178. return;
  179. }
  180. else if (J->_state == Jackclient::WAIT)
  181. {
  182. printf ("Detected excessive timing errors, waiting 15 seconds.\n");
  183. printf ("This may happen with current Jack1 after freewheeling.\n");
  184. n = 0;
  185. }
  186. else if (J->_state == Jackclient::SYNC0)
  187. {
  188. printf ("Starting synchronisation.\n");
  189. }
  190. else if (v_opt)
  191. {
  192. n++;
  193. e += J->_error;
  194. r += J->_ratio;
  195. }
  196. infoq->rd_commit ();
  197. }
  198. if (n) printf ("%8.3lf %10.6lf\n", e / n, r / n);
  199. }
  200. Alsa_pcmi *A;
  201. Alsathread *P;
  202. Jackclient *J;
  203. public:
  204. int jack_initialize (jack_client_t* client, const char* load_init)
  205. {
  206. int k, k_del, opts;
  207. double t_jack;
  208. double t_alsa;
  209. double t_del;
  210. if (parse_options (load_init)) {
  211. return 1;
  212. }
  213. if (device == 0) help ();
  214. if (rqual < 16) rqual = 16;
  215. if (rqual > 96) rqual = 96;
  216. if ((fsamp && fsamp < 8000) || (bsize && bsize < 16) || (nfrag < 2) || (nchan < 1))
  217. {
  218. fprintf (stderr, "Illegal parameter value(s).\n");
  219. return 1;
  220. }
  221. J = new Jackclient (client, 0, Jackclient::PLAY, 0, this);
  222. usleep (100000);
  223. /* if SR and/or bufsize are unspecified, use the same values
  224. as the JACK server.
  225. */
  226. if (fsamp == 0)
  227. {
  228. fsamp = J->fsamp();
  229. }
  230. if (bsize == 0)
  231. {
  232. bsize = J->bsize();
  233. }
  234. opts = 0;
  235. if (v_opt) opts |= Alsa_pcmi::DEBUG_ALL;
  236. if (L_opt) opts |= Alsa_pcmi::FORCE_16B | Alsa_pcmi::FORCE_2CH;
  237. A = new Alsa_pcmi (device, 0, 0, fsamp, bsize, nfrag, opts);
  238. if (A->state ())
  239. {
  240. fprintf (stderr, "Can't open ALSA playback device '%s'.\n", device);
  241. return 1;
  242. }
  243. if (v_opt) A->printinfo ();
  244. if (nchan > A->nplay ())
  245. {
  246. nchan = A->nplay ();
  247. fprintf (stderr, "Warning: only %d channels are available.\n", nchan);
  248. }
  249. P = new Alsathread (A, Alsathread::PLAY);
  250. J->register_ports (nchan);
  251. t_alsa = (double) bsize / fsamp;
  252. if (t_alsa < 1e-3) t_alsa = 1e-3;
  253. t_jack = (double) J->bsize () / J->fsamp ();
  254. t_del = 1.5 * t_alsa + t_jack;
  255. k_del = (int)(t_del * fsamp);
  256. for (k = 256; k < k_del + J->bsize (); k *= 2);
  257. audioq = new Lfq_audio (k, nchan);
  258. P->start (audioq, commq, alsaq, J->rprio () + 10);
  259. J->start (audioq, commq, alsaq, infoq, (double) fsamp / J->fsamp (), k_del, ltcor, rqual);
  260. return 0;
  261. }
  262. void jack_finish (void* arg)
  263. {
  264. commq->wr_int32 (Alsathread::TERM);
  265. usleep (100000);
  266. delete P;
  267. delete A;
  268. delete J;
  269. delete audioq;
  270. }
  271. };
  272. extern "C" {
  273. int
  274. jack_initialize (jack_client_t* client, const char* load_init)
  275. {
  276. zita_j2a *c = new zita_j2a();
  277. c->jack_initialize(client, load_init);
  278. }
  279. void jack_finish (void* arg)
  280. {
  281. Jackclient *J = (Jackclient *)arg;
  282. zita_j2a *c = (zita_j2a *)J->getarg();
  283. c->jack_finish(arg);
  284. delete c;
  285. }
  286. } /* extern "C" */