Annotation of src/usr.bin/aucat/aucat.c, Revision 1.38
1.38 ! ratchov 1: /* $OpenBSD: aucat.c,v 1.37 2008/11/11 13:12:03 ratchov Exp $ */
1.1 kstailey 2: /*
1.15 ratchov 3: * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
1.1 kstailey 4: *
1.15 ratchov 5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17: /*
18: * TODO:
19: *
20: * (hard) use parsable encoding names instead of the lookup
21: * table. For instance, [s|u]bits[le|be][/bytes{msb|lsb}], example
22: * s8, s16le, s24le/3msb. This would give names that correspond to
23: * what use most linux-centric apps, but for which we have an
24: * algorithm to convert the name to a aparams structure.
25: *
26: * (easy) uses {chmin-chmax} instead of chmin:chmax notation for
27: * channels specification to match the notation used in rmix.
28: *
29: * (easy) use comma-separated parameters syntax, example:
30: * s24le/3msb,{3-6},48000 so we don't have to use three -e, -r, -c
31: * flags, but only one -p flag that specify one or more parameters.
32: *
33: * (hard) if all inputs are over, the mixer terminates and closes
34: * the write end of the device. It should continue writing zeros
35: * until the recording is over (or be able to stop write end of
36: * the device)
37: *
38: * (hard) implement -n flag (no device) to connect all inputs to
39: * the outputs.
40: *
41: * (hard) ignore input files that are not audible (because channels
42: * they provide are not used on the output). Similarly ignore
43: * outputs that are zero filled (because channels they consume are
44: * not provided).
1.1 kstailey 45: */
46:
1.15 ratchov 47: #include <sys/param.h>
1.1 kstailey 48: #include <sys/types.h>
1.15 ratchov 49: #include <sys/queue.h>
1.13 uwe 50:
1.28 ratchov 51: #include <signal.h>
1.15 ratchov 52: #include <err.h>
1.1 kstailey 53: #include <fcntl.h>
1.15 ratchov 54: #include <signal.h>
1.1 kstailey 55: #include <stdio.h>
1.4 millert 56: #include <stdlib.h>
1.8 david 57: #include <string.h>
1.1 kstailey 58: #include <unistd.h>
1.15 ratchov 59: #include <varargs.h>
1.1 kstailey 60:
1.15 ratchov 61: #include "conf.h"
62: #include "aparams.h"
63: #include "aproc.h"
64: #include "abuf.h"
1.28 ratchov 65: #include "wav.h"
66: #include "listen.h"
1.15 ratchov 67: #include "dev.h"
1.11 jaredy 68:
1.28 ratchov 69: int debug_level = 0;
70: volatile int quit_flag = 0;
1.7 deraadt 71:
1.28 ratchov 72: /*
73: * SIGINT handler, it raises the quit flag. If the flag is already set,
74: * that means that the last SIGINT was not handled, because the process
75: * is blocked somewhere, so exit
76: */
77: void
78: sigint(int s)
79: {
80: if (quit_flag)
81: _exit(1);
82: quit_flag = 1;
83: }
1.22 ratchov 84:
1.15 ratchov 85: /*
1.28 ratchov 86: * increase debug level on SIGUSR1
1.15 ratchov 87: */
1.28 ratchov 88: void
89: sigusr1(int s)
90: {
91: if (debug_level < 4)
92: debug_level++;
93: }
1.13 uwe 94:
1.11 jaredy 95: /*
1.28 ratchov 96: * decrease debug level on SIGUSR2
1.1 kstailey 97: */
1.28 ratchov 98: void
99: sigusr2(int s)
1.15 ratchov 100: {
1.28 ratchov 101: if (debug_level > 0)
102: debug_level--;
1.15 ratchov 103: }
104:
105: void
106: usage(void)
1.1 kstailey 107: {
1.15 ratchov 108: extern char *__progname;
1.4 millert 109:
1.15 ratchov 110: fprintf(stderr,
1.29 jmc 111: "usage: %s [-lu] [-b nsamples] [-C min:max] [-c min:max] [-e enc] "
112: "[-f device]\n"
1.35 ratchov 113: "\t[-h fmt] [-i file] [-o file] [-r rate] [-v volume] "
114: "[-x policy]\n",
1.15 ratchov 115: __progname);
116: }
117:
118: void
119: opt_ch(struct aparams *par)
120: {
121: if (sscanf(optarg, "%u:%u", &par->cmin, &par->cmax) != 2 ||
1.28 ratchov 122: par->cmax < par->cmin || par->cmax > NCHAN_MAX - 1)
1.35 ratchov 123: errx(1, "%s: bad channel range", optarg);
1.15 ratchov 124: }
1.13 uwe 125:
1.15 ratchov 126: void
127: opt_rate(struct aparams *par)
128: {
129: if (sscanf(optarg, "%u", &par->rate) != 1 ||
130: par->rate < RATE_MIN || par->rate > RATE_MAX)
1.35 ratchov 131: errx(1, "%s: bad sample rate", optarg);
132: }
133:
134: void
135: opt_vol(unsigned *vol)
136: {
137: if (sscanf(optarg, "%u", vol) != 1 ||
138: *vol > MIDI_MAXCTL)
139: errx(1, "%s: bad volume", optarg);
1.15 ratchov 140: }
1.13 uwe 141:
1.15 ratchov 142: void
143: opt_enc(struct aparams *par)
144: {
1.28 ratchov 145: int len;
146:
147: len = aparams_strtoenc(par, optarg);
148: if (len == 0 || optarg[len] != '\0')
149: errx(1, "%s: bad encoding", optarg);
1.15 ratchov 150: }
1.4 millert 151:
1.15 ratchov 152: int
153: opt_hdr(void)
154: {
155: if (strcmp("auto", optarg) == 0)
156: return HDR_AUTO;
157: if (strcmp("raw", optarg) == 0)
158: return HDR_RAW;
159: if (strcmp("wav", optarg) == 0)
160: return HDR_WAV;
1.35 ratchov 161: errx(1, "%s: bad header specification", optarg);
1.1 kstailey 162: }
163:
1.22 ratchov 164: int
165: opt_xrun(void)
166: {
167: if (strcmp("ignore", optarg) == 0)
168: return XRUN_IGNORE;
169: if (strcmp("sync", optarg) == 0)
170: return XRUN_SYNC;
171: if (strcmp("error", optarg) == 0)
172: return XRUN_ERROR;
173: errx(1, "%s: onderrun/overrun policy", optarg);
174: }
175:
1.13 uwe 176: /*
1.15 ratchov 177: * Arguments of -i and -o opations are stored in a list.
1.13 uwe 178: */
1.15 ratchov 179: struct farg {
180: SLIST_ENTRY(farg) entry;
181: struct aparams par; /* last requested format */
182: unsigned vol; /* last requested volume */
183: char *name; /* optarg pointer (no need to copy it */
184: int hdr; /* header format */
1.22 ratchov 185: int xrun; /* overrun/underrun policy */
1.15 ratchov 186: };
1.13 uwe 187:
1.15 ratchov 188: SLIST_HEAD(farglist, farg);
1.13 uwe 189:
1.15 ratchov 190: /*
191: * Add a farg entry to the given list, corresponding
192: * to the given file name.
193: */
194: void
195: opt_file(struct farglist *list,
1.22 ratchov 196: struct aparams *par, unsigned vol, int hdr, int xrun, char *optarg)
1.15 ratchov 197: {
198: struct farg *fa;
199: size_t namelen;
200:
201: fa = malloc(sizeof(struct farg));
202: if (fa == NULL)
203: err(1, "%s", optarg);
204:
205: if (hdr == HDR_AUTO) {
206: namelen = strlen(optarg);
207: if (namelen >= 4 &&
208: strcasecmp(optarg + namelen - 4, ".wav") == 0) {
209: fa->hdr = HDR_WAV;
210: DPRINTF("%s: assuming wav file format\n", optarg);
211: } else {
212: fa->hdr = HDR_RAW;
213: DPRINTF("%s: assuming headerless file\n", optarg);
1.13 uwe 214: }
1.15 ratchov 215: } else
216: fa->hdr = hdr;
1.22 ratchov 217: fa->xrun = xrun;
1.15 ratchov 218: fa->par = *par;
219: fa->vol = vol;
220: fa->name = optarg;
221: SLIST_INSERT_HEAD(list, fa, entry);
222: }
1.13 uwe 223:
1.15 ratchov 224: /*
225: * Open an input file and setup converter if necessary.
226: */
227: void
1.26 ratchov 228: newinput(struct farg *fa)
1.15 ratchov 229: {
230: int fd;
1.28 ratchov 231: struct wav *f;
1.26 ratchov 232: struct aproc *proc;
233: struct abuf *buf;
234: unsigned nfr;
1.15 ratchov 235:
236: if (strcmp(fa->name, "-") == 0) {
237: fd = STDIN_FILENO;
238: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
239: warn("stdin");
240: fa->name = "stdin";
241: } else {
242: fd = open(fa->name, O_RDONLY | O_NONBLOCK, 0666);
243: if (fd < 0)
244: err(1, "%s", fa->name);
1.13 uwe 245: }
1.28 ratchov 246: /*
247: * XXX : we should round rate, right ?
248: */
249: f = wav_new_in(&wav_ops, fd, fa->name, &fa->par, fa->hdr);
250: nfr = dev_bufsz * fa->par.rate / dev_rate;
1.31 ratchov 251: buf = abuf_new(nfr, &fa->par);
1.28 ratchov 252: proc = rpipe_new((struct file *)f);
1.26 ratchov 253: aproc_setout(proc, buf);
1.28 ratchov 254: abuf_fill(buf); /* XXX: move this in dev_attach() ? */
255: dev_attach(fa->name, buf, &fa->par, fa->xrun, NULL, NULL, 0);
1.35 ratchov 256: dev_setvol(buf, MIDI_TO_ADATA(fa->vol));
1.15 ratchov 257: }
1.13 uwe 258:
1.15 ratchov 259: /*
260: * Open an output file and setup converter if necessary.
261: */
262: void
1.26 ratchov 263: newoutput(struct farg *fa)
1.15 ratchov 264: {
265: int fd;
1.28 ratchov 266: struct wav *f;
1.26 ratchov 267: struct aproc *proc;
268: struct abuf *buf;
269: unsigned nfr;
1.15 ratchov 270:
271: if (strcmp(fa->name, "-") == 0) {
272: fd = STDOUT_FILENO;
273: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
274: warn("stdout");
275: fa->name = "stdout";
276: } else {
277: fd = open(fa->name,
278: O_WRONLY | O_TRUNC | O_CREAT | O_NONBLOCK, 0666);
279: if (fd < 0)
280: err(1, "%s", fa->name);
281: }
1.28 ratchov 282: /*
283: * XXX : we should round rate, right ?
284: */
285: f = wav_new_out(&wav_ops, fd, fa->name, &fa->par, fa->hdr);
286: nfr = dev_bufsz * fa->par.rate / dev_rate;
287: proc = wpipe_new((struct file *)f);
1.31 ratchov 288: buf = abuf_new(nfr, &fa->par);
1.26 ratchov 289: aproc_setin(proc, buf);
1.28 ratchov 290: dev_attach(fa->name, NULL, NULL, 0, buf, &fa->par, fa->xrun);
1.13 uwe 291: }
292:
1.1 kstailey 293: int
1.15 ratchov 294: main(int argc, char **argv)
1.1 kstailey 295: {
1.34 ratchov 296: int c, u_flag, l_flag, hdr, xrun, suspend = 0;
1.15 ratchov 297: struct farg *fa;
298: struct farglist ifiles, ofiles;
1.26 ratchov 299: struct aparams ipar, opar, dipar, dopar;
1.28 ratchov 300: struct sigaction sa;
1.35 ratchov 301: unsigned ivol, bufsz;
1.28 ratchov 302: char *devpath, *dbgenv, *listenpath;
1.21 ratchov 303: const char *errstr;
1.28 ratchov 304: extern char *malloc_options;
305:
306: malloc_options = "FGJ";
1.15 ratchov 307:
1.19 ratchov 308: dbgenv = getenv("AUCAT_DEBUG");
309: if (dbgenv) {
1.21 ratchov 310: debug_level = strtonum(dbgenv, 0, 4, &errstr);
311: if (errstr)
312: errx(1, "AUCAT_DEBUG is %s: %s", errstr, dbgenv);
1.19 ratchov 313: }
314:
1.15 ratchov 315: aparams_init(&ipar, 0, 1, 44100);
316: aparams_init(&opar, 0, 1, 44100);
1.26 ratchov 317: u_flag = 0;
1.28 ratchov 318: l_flag = 0;
1.15 ratchov 319: devpath = NULL;
320: SLIST_INIT(&ifiles);
321: SLIST_INIT(&ofiles);
1.28 ratchov 322: hdr = HDR_AUTO;
323: xrun = XRUN_IGNORE;
1.35 ratchov 324: ivol = MIDI_MAXCTL;
1.32 ratchov 325: bufsz = 44100 * 4 / 15; /* XXX: use milliseconds, not frames */
1.15 ratchov 326:
1.35 ratchov 327: while ((c = getopt(argc, argv, "b:c:C:e:r:h:x:v:i:o:f:lu")) != -1) {
1.15 ratchov 328: switch (c) {
329: case 'h':
1.28 ratchov 330: hdr = opt_hdr();
1.15 ratchov 331: break;
1.22 ratchov 332: case 'x':
1.28 ratchov 333: xrun = opt_xrun();
1.22 ratchov 334: break;
1.15 ratchov 335: case 'c':
336: opt_ch(&ipar);
337: break;
338: case 'C':
339: opt_ch(&opar);
340: break;
341: case 'e':
342: opt_enc(&ipar);
1.28 ratchov 343: aparams_copyenc(&opar, &ipar);
1.15 ratchov 344: break;
345: case 'r':
346: opt_rate(&ipar);
1.28 ratchov 347: opar.rate = ipar.rate;
1.15 ratchov 348: break;
1.35 ratchov 349: case 'v':
350: opt_vol(&ivol);
351: break;
1.15 ratchov 352: case 'i':
1.35 ratchov 353: opt_file(&ifiles, &ipar, ivol, hdr, xrun, optarg);
1.15 ratchov 354: break;
355: case 'o':
1.28 ratchov 356: opt_file(&ofiles, &opar, 127, hdr, xrun, optarg);
1.15 ratchov 357: break;
1.4 millert 358: case 'f':
1.15 ratchov 359: if (devpath)
360: err(1, "only one -f allowed");
361: devpath = optarg;
1.28 ratchov 362: dipar = opar;
363: dopar = ipar;
1.15 ratchov 364: break;
1.28 ratchov 365: case 'l':
366: l_flag = 1;
1.17 jakemsr 367: break;
1.15 ratchov 368: case 'u':
369: u_flag = 1;
1.4 millert 370: break;
1.28 ratchov 371: case 'b':
1.32 ratchov 372: if (sscanf(optarg, "%u", &bufsz) != 1 || bufsz == 0) {
1.28 ratchov 373: fprintf(stderr, "%s: bad buf size\n", optarg);
374: exit(1);
375: }
376: break;
1.11 jaredy 377: default:
378: usage();
1.15 ratchov 379: exit(1);
1.4 millert 380: }
381: }
382: argc -= optind;
383: argv += optind;
384:
1.15 ratchov 385: if (!devpath) {
386: dipar = ipar;
387: dopar = opar;
388: }
389:
1.28 ratchov 390: if (!l_flag && SLIST_EMPTY(&ifiles) &&
391: SLIST_EMPTY(&ofiles) && argc > 0) {
1.15 ratchov 392: /*
393: * Legacy mode: if no -i or -o options are provided, and
394: * there are arguments then assume the arguments are files
395: * to play.
396: */
397: for (c = 0; c < argc; c++)
398: if (legacy_play(devpath, argv[c]) != 0) {
1.17 jakemsr 399: errx(1, "%s: could not play\n", argv[c]);
1.15 ratchov 400: }
401: exit(0);
402: } else if (argc > 0) {
1.11 jaredy 403: usage();
1.15 ratchov 404: exit(1);
405: }
406:
1.28 ratchov 407: if (l_flag && (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles)))
408: errx(1, "can't use -l with -o or -i");
1.15 ratchov 409:
1.28 ratchov 410: if (!u_flag && !l_flag) {
1.26 ratchov 411: /*
412: * Calculate "best" device parameters. Iterate over all
413: * inputs and outputs and find the maximum sample rate
414: * and channel number.
415: */
1.28 ratchov 416: aparams_init(&dipar, NCHAN_MAX - 1, 0, RATE_MAX);
417: aparams_init(&dopar, NCHAN_MAX - 1, 0, RATE_MIN);
1.26 ratchov 418: SLIST_FOREACH(fa, &ifiles, entry) {
419: if (dopar.cmin > fa->par.cmin)
420: dopar.cmin = fa->par.cmin;
421: if (dopar.cmax < fa->par.cmax)
422: dopar.cmax = fa->par.cmax;
423: if (dopar.rate < fa->par.rate)
424: dopar.rate = fa->par.rate;
425: }
426: SLIST_FOREACH(fa, &ofiles, entry) {
427: if (dipar.cmin > fa->par.cmin)
428: dipar.cmin = fa->par.cmin;
429: if (dipar.cmax < fa->par.cmax)
430: dipar.cmax = fa->par.cmax;
431: if (dipar.rate > fa->par.rate)
432: dipar.rate = fa->par.rate;
1.17 jakemsr 433: }
1.15 ratchov 434: }
1.28 ratchov 435:
436: quit_flag = 0;
437: sigfillset(&sa.sa_mask);
438: sa.sa_flags = SA_RESTART;
439: sa.sa_handler = sigint;
440: if (sigaction(SIGINT, &sa, NULL) < 0)
441: DPRINTF("sigaction(int) failed\n");
442: #ifdef DEBUG
443: sa.sa_handler = sigusr1;
444: if (sigaction(SIGUSR1, &sa, NULL) < 0)
445: DPRINTF("sigaction(usr1) failed\n");
446: sa.sa_handler = sigusr2;
447: if (sigaction(SIGUSR2, &sa, NULL) < 0)
448: DPRINTF("sigaction(usr2) failed1n");
449: #endif
450: filelist_init();
1.15 ratchov 451:
452: /*
1.32 ratchov 453: * Open the device. Give half of the buffer to the device,
454: * the other half is for the socket/files
1.15 ratchov 455: */
1.26 ratchov 456: dev_init(devpath,
1.28 ratchov 457: (l_flag || !SLIST_EMPTY(&ofiles)) ? &dipar : NULL,
458: (l_flag || !SLIST_EMPTY(&ifiles)) ? &dopar : NULL,
1.32 ratchov 459: bufsz, l_flag);
1.28 ratchov 460:
461: if (l_flag) {
462: listenpath = getenv("AUCAT_SOCKET");
463: if (!listenpath)
464: listenpath = DEFAULT_SOCKET;
465: (void)listen_new(&listen_ops, listenpath);
466: }
1.15 ratchov 467:
468: /*
469: * Create buffers for all input and output pipes.
470: */
1.26 ratchov 471: while (!SLIST_EMPTY(&ifiles)) {
472: fa = SLIST_FIRST(&ifiles);
473: SLIST_REMOVE_HEAD(&ifiles, entry);
474: newinput(fa);
475: free(fa);
476: }
477: while (!SLIST_EMPTY(&ofiles)) {
478: fa = SLIST_FIRST(&ofiles);
479: SLIST_REMOVE_HEAD(&ofiles, entry);
480: newoutput(fa);
481: free(fa);
1.15 ratchov 482: }
1.13 uwe 483:
1.15 ratchov 484: /*
1.28 ratchov 485: * loop, start audio
1.15 ratchov 486: */
1.28 ratchov 487: for (;;) {
488: if (quit_flag) {
489: if (l_flag)
490: filelist_unlisten();
491: break;
492: }
1.38 ! ratchov 493: if (!file_poll()) {
! 494: fprintf(stderr, "Terminated, device desappeared?\n");
! 495: exit(1);
! 496: }
1.34 ratchov 497: if ((!dev_mix || dev_mix->u.mix.idle > 2 * dev_bufsz) &&
498: (!dev_sub || dev_sub->u.sub.idle > 2 * dev_bufsz)) {
1.37 ratchov 499: if (!l_flag)
500: break;
1.34 ratchov 501: if (!suspend) {
502: DPRINTF("suspending\n");
503: suspend = 1;
504: dev_stop();
505: dev_clear();
506: }
507: }
508: if ((dev_mix && dev_mix->u.mix.idle == 0) ||
509: (dev_sub && dev_sub->u.sub.idle == 0)) {
510: if (suspend) {
511: DPRINTF("resuming\n");
512: suspend = 0;
513: dev_start();
514: }
515: }
516: }
517: if (suspend) {
518: DPRINTF("resuming to drain\n");
519: suspend = 0;
520: dev_start();
1.28 ratchov 521: }
1.37 ratchov 522: dev_done();
1.28 ratchov 523: filelist_done();
1.11 jaredy 524:
1.28 ratchov 525: sigfillset(&sa.sa_mask);
526: sa.sa_flags = SA_RESTART;
527: sa.sa_handler = SIG_DFL;
528: if (sigaction(SIGINT, &sa, NULL) < 0)
529: DPRINTF("dev_done: sigaction failed\n");
1.15 ratchov 530: return 0;
1.1 kstailey 531: }