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.

270 lines
7.7KB

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