Annotation of src/usr.bin/aucat/aucat.c, Revision 1.28
1.27 sobrado 1: /* $OpenBSD: aucat.c,v 1.26 2008/08/14 09:58:55 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.28 ! ratchov 111: "usage: %s [-lu] [-C min:max] [-c min:max] [-d level] "
! 112: "[-e enc]\n"
! 113: "\t[-f device] [-h fmt] [-i file] [-o file] "
! 114: "[-r rate] [-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.15 ratchov 123: err(1, "%s: bad channel range", optarg);
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)
131: err(1, "%s: bad sample rate", optarg);
132: }
1.13 uwe 133:
1.15 ratchov 134: void
135: opt_enc(struct aparams *par)
136: {
1.28 ! ratchov 137: int len;
! 138:
! 139: len = aparams_strtoenc(par, optarg);
! 140: if (len == 0 || optarg[len] != '\0')
! 141: errx(1, "%s: bad encoding", optarg);
1.15 ratchov 142: }
1.4 millert 143:
1.15 ratchov 144: int
145: opt_hdr(void)
146: {
147: if (strcmp("auto", optarg) == 0)
148: return HDR_AUTO;
149: if (strcmp("raw", optarg) == 0)
150: return HDR_RAW;
151: if (strcmp("wav", optarg) == 0)
152: return HDR_WAV;
153: err(1, "%s: bad header specification", optarg);
1.1 kstailey 154: }
155:
1.22 ratchov 156: int
157: opt_xrun(void)
158: {
159: if (strcmp("ignore", optarg) == 0)
160: return XRUN_IGNORE;
161: if (strcmp("sync", optarg) == 0)
162: return XRUN_SYNC;
163: if (strcmp("error", optarg) == 0)
164: return XRUN_ERROR;
165: errx(1, "%s: onderrun/overrun policy", optarg);
166: }
167:
1.13 uwe 168: /*
1.15 ratchov 169: * Arguments of -i and -o opations are stored in a list.
1.13 uwe 170: */
1.15 ratchov 171: struct farg {
172: SLIST_ENTRY(farg) entry;
173: struct aparams par; /* last requested format */
174: unsigned vol; /* last requested volume */
175: char *name; /* optarg pointer (no need to copy it */
176: int hdr; /* header format */
1.22 ratchov 177: int xrun; /* overrun/underrun policy */
1.15 ratchov 178: };
1.13 uwe 179:
1.15 ratchov 180: SLIST_HEAD(farglist, farg);
1.13 uwe 181:
1.15 ratchov 182: /*
183: * Add a farg entry to the given list, corresponding
184: * to the given file name.
185: */
186: void
187: opt_file(struct farglist *list,
1.22 ratchov 188: struct aparams *par, unsigned vol, int hdr, int xrun, char *optarg)
1.15 ratchov 189: {
190: struct farg *fa;
191: size_t namelen;
192:
193: fa = malloc(sizeof(struct farg));
194: if (fa == NULL)
195: err(1, "%s", optarg);
196:
197: if (hdr == HDR_AUTO) {
198: namelen = strlen(optarg);
199: if (namelen >= 4 &&
200: strcasecmp(optarg + namelen - 4, ".wav") == 0) {
201: fa->hdr = HDR_WAV;
202: DPRINTF("%s: assuming wav file format\n", optarg);
203: } else {
204: fa->hdr = HDR_RAW;
205: DPRINTF("%s: assuming headerless file\n", optarg);
1.13 uwe 206: }
1.15 ratchov 207: } else
208: fa->hdr = hdr;
1.22 ratchov 209: fa->xrun = xrun;
1.15 ratchov 210: fa->par = *par;
211: fa->vol = vol;
212: fa->name = optarg;
213: SLIST_INSERT_HEAD(list, fa, entry);
214: }
1.13 uwe 215:
1.15 ratchov 216: /*
217: * Open an input file and setup converter if necessary.
218: */
219: void
1.26 ratchov 220: newinput(struct farg *fa)
1.15 ratchov 221: {
222: int fd;
1.28 ! ratchov 223: struct wav *f;
1.26 ratchov 224: struct aproc *proc;
225: struct abuf *buf;
226: unsigned nfr;
1.15 ratchov 227:
228: if (strcmp(fa->name, "-") == 0) {
229: fd = STDIN_FILENO;
230: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
231: warn("stdin");
232: fa->name = "stdin";
233: } else {
234: fd = open(fa->name, O_RDONLY | O_NONBLOCK, 0666);
235: if (fd < 0)
236: err(1, "%s", fa->name);
1.13 uwe 237: }
1.28 ! ratchov 238: /*
! 239: * XXX : we should round rate, right ?
! 240: */
! 241: f = wav_new_in(&wav_ops, fd, fa->name, &fa->par, fa->hdr);
! 242: nfr = dev_bufsz * fa->par.rate / dev_rate;
! 243: buf = abuf_new(nfr, aparams_bpf(&fa->par));
! 244: proc = rpipe_new((struct file *)f);
1.26 ratchov 245: aproc_setout(proc, buf);
1.28 ! ratchov 246: abuf_fill(buf); /* XXX: move this in dev_attach() ? */
! 247: dev_attach(fa->name, buf, &fa->par, fa->xrun, NULL, NULL, 0);
1.15 ratchov 248: }
1.13 uwe 249:
1.15 ratchov 250: /*
251: * Open an output file and setup converter if necessary.
252: */
253: void
1.26 ratchov 254: newoutput(struct farg *fa)
1.15 ratchov 255: {
256: int fd;
1.28 ! ratchov 257: struct wav *f;
1.26 ratchov 258: struct aproc *proc;
259: struct abuf *buf;
260: unsigned nfr;
1.15 ratchov 261:
262: if (strcmp(fa->name, "-") == 0) {
263: fd = STDOUT_FILENO;
264: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
265: warn("stdout");
266: fa->name = "stdout";
267: } else {
268: fd = open(fa->name,
269: O_WRONLY | O_TRUNC | O_CREAT | O_NONBLOCK, 0666);
270: if (fd < 0)
271: err(1, "%s", fa->name);
272: }
1.28 ! ratchov 273: /*
! 274: * XXX : we should round rate, right ?
! 275: */
! 276: f = wav_new_out(&wav_ops, fd, fa->name, &fa->par, fa->hdr);
! 277: nfr = dev_bufsz * fa->par.rate / dev_rate;
! 278: proc = wpipe_new((struct file *)f);
! 279: buf = abuf_new(nfr, aparams_bpf(&fa->par));
1.26 ratchov 280: aproc_setin(proc, buf);
1.28 ! ratchov 281: dev_attach(fa->name, NULL, NULL, 0, buf, &fa->par, fa->xrun);
1.13 uwe 282: }
283:
1.1 kstailey 284: int
1.15 ratchov 285: main(int argc, char **argv)
1.1 kstailey 286: {
1.28 ! ratchov 287: int c, u_flag, l_flag, hdr, xrun;
1.15 ratchov 288: struct farg *fa;
289: struct farglist ifiles, ofiles;
1.26 ratchov 290: struct aparams ipar, opar, dipar, dopar;
1.28 ! ratchov 291: struct sigaction sa;
! 292: unsigned ivol, ovol, bufsz = 0;
! 293: char *devpath, *dbgenv, *listenpath;
1.21 ratchov 294: const char *errstr;
1.28 ! ratchov 295: extern char *malloc_options;
! 296:
! 297: malloc_options = "FGJ";
1.15 ratchov 298:
1.19 ratchov 299: dbgenv = getenv("AUCAT_DEBUG");
300: if (dbgenv) {
1.21 ratchov 301: debug_level = strtonum(dbgenv, 0, 4, &errstr);
302: if (errstr)
303: errx(1, "AUCAT_DEBUG is %s: %s", errstr, dbgenv);
1.19 ratchov 304: }
305:
1.15 ratchov 306: aparams_init(&ipar, 0, 1, 44100);
307: aparams_init(&opar, 0, 1, 44100);
308:
1.26 ratchov 309: u_flag = 0;
1.28 ! ratchov 310: l_flag = 0;
1.15 ratchov 311: devpath = NULL;
312: SLIST_INIT(&ifiles);
313: SLIST_INIT(&ofiles);
1.28 ! ratchov 314: hdr = HDR_AUTO;
! 315: xrun = XRUN_IGNORE;
1.15 ratchov 316: ivol = ovol = MIDI_TO_ADATA(127);
317:
1.28 ! ratchov 318: while ((c = getopt(argc, argv, "b:c:C:e:r:h:x:i:o:f:lqu"))
1.22 ratchov 319: != -1) {
1.15 ratchov 320: switch (c) {
321: case 'h':
1.28 ! ratchov 322: hdr = opt_hdr();
1.15 ratchov 323: break;
1.22 ratchov 324: case 'x':
1.28 ! ratchov 325: xrun = opt_xrun();
1.22 ratchov 326: break;
1.15 ratchov 327: case 'c':
328: opt_ch(&ipar);
329: break;
330: case 'C':
331: opt_ch(&opar);
332: break;
333: case 'e':
334: opt_enc(&ipar);
1.28 ! ratchov 335: aparams_copyenc(&opar, &ipar);
1.15 ratchov 336: break;
337: case 'r':
338: opt_rate(&ipar);
1.28 ! ratchov 339: opar.rate = ipar.rate;
1.15 ratchov 340: break;
341: case 'i':
1.28 ! ratchov 342: opt_file(&ifiles, &ipar, 127, hdr, xrun, optarg);
1.15 ratchov 343: break;
344: case 'o':
1.28 ! ratchov 345: opt_file(&ofiles, &opar, 127, hdr, xrun, optarg);
1.15 ratchov 346: break;
1.4 millert 347: case 'f':
1.15 ratchov 348: if (devpath)
349: err(1, "only one -f allowed");
350: devpath = optarg;
1.28 ! ratchov 351: dipar = opar;
! 352: dopar = ipar;
1.15 ratchov 353: break;
1.28 ! ratchov 354: case 'l':
! 355: l_flag = 1;
1.17 jakemsr 356: break;
1.15 ratchov 357: case 'u':
358: u_flag = 1;
1.4 millert 359: break;
1.28 ! ratchov 360: case 'b':
! 361: if (sscanf(optarg, "%u", &bufsz) != 1) {
! 362: fprintf(stderr, "%s: bad buf size\n", optarg);
! 363: exit(1);
! 364: }
! 365: break;
1.11 jaredy 366: default:
367: usage();
1.15 ratchov 368: exit(1);
1.4 millert 369: }
370: }
371: argc -= optind;
372: argv += optind;
373:
1.15 ratchov 374: if (!devpath) {
375: dipar = ipar;
376: dopar = opar;
377: }
378:
1.28 ! ratchov 379: if (!l_flag && SLIST_EMPTY(&ifiles) &&
! 380: SLIST_EMPTY(&ofiles) && argc > 0) {
1.15 ratchov 381: /*
382: * Legacy mode: if no -i or -o options are provided, and
383: * there are arguments then assume the arguments are files
384: * to play.
385: */
386: for (c = 0; c < argc; c++)
387: if (legacy_play(devpath, argv[c]) != 0) {
1.17 jakemsr 388: errx(1, "%s: could not play\n", argv[c]);
1.15 ratchov 389: }
390: exit(0);
391: } else if (argc > 0) {
1.11 jaredy 392: usage();
1.15 ratchov 393: exit(1);
394: }
395:
1.28 ! ratchov 396: if (l_flag && (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles)))
! 397: errx(1, "can't use -l with -o or -i");
1.15 ratchov 398:
1.28 ! ratchov 399: if (!u_flag && !l_flag) {
1.26 ratchov 400: /*
401: * Calculate "best" device parameters. Iterate over all
402: * inputs and outputs and find the maximum sample rate
403: * and channel number.
404: */
1.28 ! ratchov 405: aparams_init(&dipar, NCHAN_MAX - 1, 0, RATE_MAX);
! 406: aparams_init(&dopar, NCHAN_MAX - 1, 0, RATE_MIN);
1.26 ratchov 407: SLIST_FOREACH(fa, &ifiles, entry) {
408: if (dopar.cmin > fa->par.cmin)
409: dopar.cmin = fa->par.cmin;
410: if (dopar.cmax < fa->par.cmax)
411: dopar.cmax = fa->par.cmax;
412: if (dopar.rate < fa->par.rate)
413: dopar.rate = fa->par.rate;
414: }
415: SLIST_FOREACH(fa, &ofiles, entry) {
416: if (dipar.cmin > fa->par.cmin)
417: dipar.cmin = fa->par.cmin;
418: if (dipar.cmax < fa->par.cmax)
419: dipar.cmax = fa->par.cmax;
420: if (dipar.rate > fa->par.rate)
421: dipar.rate = fa->par.rate;
1.17 jakemsr 422: }
1.15 ratchov 423: }
1.28 ! ratchov 424:
! 425: quit_flag = 0;
! 426: sigfillset(&sa.sa_mask);
! 427: sa.sa_flags = SA_RESTART;
! 428: sa.sa_handler = sigint;
! 429: if (sigaction(SIGINT, &sa, NULL) < 0)
! 430: DPRINTF("sigaction(int) failed\n");
! 431: #ifdef DEBUG
! 432: sa.sa_handler = sigusr1;
! 433: if (sigaction(SIGUSR1, &sa, NULL) < 0)
! 434: DPRINTF("sigaction(usr1) failed\n");
! 435: sa.sa_handler = sigusr2;
! 436: if (sigaction(SIGUSR2, &sa, NULL) < 0)
! 437: DPRINTF("sigaction(usr2) failed1n");
! 438: #endif
! 439: filelist_init();
1.15 ratchov 440:
441: /*
1.28 ! ratchov 442: * Open the device.
1.15 ratchov 443: */
1.26 ratchov 444: dev_init(devpath,
1.28 ! ratchov 445: (l_flag || !SLIST_EMPTY(&ofiles)) ? &dipar : NULL,
! 446: (l_flag || !SLIST_EMPTY(&ifiles)) ? &dopar : NULL,
! 447: bufsz);
! 448:
! 449: if (l_flag) {
! 450: listenpath = getenv("AUCAT_SOCKET");
! 451: if (!listenpath)
! 452: listenpath = DEFAULT_SOCKET;
! 453: (void)listen_new(&listen_ops, listenpath);
! 454: }
1.15 ratchov 455:
456: /*
457: * Create buffers for all input and output pipes.
458: */
1.26 ratchov 459: while (!SLIST_EMPTY(&ifiles)) {
460: fa = SLIST_FIRST(&ifiles);
461: SLIST_REMOVE_HEAD(&ifiles, entry);
462: newinput(fa);
463: free(fa);
464: }
465: while (!SLIST_EMPTY(&ofiles)) {
466: fa = SLIST_FIRST(&ofiles);
467: SLIST_REMOVE_HEAD(&ofiles, entry);
468: newoutput(fa);
469: free(fa);
1.15 ratchov 470: }
1.13 uwe 471:
1.15 ratchov 472: /*
1.28 ! ratchov 473: * automatically terminate when there no are streams
1.15 ratchov 474: */
1.28 ! ratchov 475: if (!l_flag) {
! 476: if (dev_mix)
! 477: dev_mix->u.mix.flags |= MIX_AUTOQUIT;
! 478: if (dev_sub)
! 479: dev_sub->u.sub.flags |= SUB_AUTOQUIT;
! 480: }
1.13 uwe 481:
1.15 ratchov 482: /*
1.28 ! ratchov 483: * loop, start audio
1.15 ratchov 484: */
1.28 ! ratchov 485: for (;;) {
! 486: if (quit_flag) {
! 487: if (l_flag)
! 488: filelist_unlisten();
! 489: break;
! 490: }
! 491: if (!file_poll())
! 492: break;
! 493: }
! 494:
1.26 ratchov 495: dev_done();
1.28 ! ratchov 496: filelist_done();
1.11 jaredy 497:
1.28 ! ratchov 498: sigfillset(&sa.sa_mask);
! 499: sa.sa_flags = SA_RESTART;
! 500: sa.sa_handler = SIG_DFL;
! 501: if (sigaction(SIGINT, &sa, NULL) < 0)
! 502: DPRINTF("dev_done: sigaction failed\n");
1.15 ratchov 503: return 0;
1.1 kstailey 504: }