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