Annotation of src/usr.bin/aucat/aucat.c, Revision 1.49
1.49 ! ratchov 1: /* $OpenBSD: aucat.c,v 1.48 2008/12/07 17:10:41 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.43 ratchov 69: #define MODE_PLAY 1
70: #define MODE_REC 2
71:
1.28 ratchov 72: int debug_level = 0;
73: volatile int quit_flag = 0;
1.7 deraadt 74:
1.28 ratchov 75: /*
76: * SIGINT handler, it raises the quit flag. If the flag is already set,
77: * that means that the last SIGINT was not handled, because the process
78: * is blocked somewhere, so exit
79: */
80: void
81: sigint(int s)
82: {
83: if (quit_flag)
84: _exit(1);
85: quit_flag = 1;
86: }
1.22 ratchov 87:
1.15 ratchov 88: /*
1.28 ratchov 89: * increase debug level on SIGUSR1
1.15 ratchov 90: */
1.28 ratchov 91: void
92: sigusr1(int s)
93: {
94: if (debug_level < 4)
95: debug_level++;
96: }
1.13 uwe 97:
1.11 jaredy 98: /*
1.28 ratchov 99: * decrease debug level on SIGUSR2
1.1 kstailey 100: */
1.28 ratchov 101: void
102: sigusr2(int s)
1.15 ratchov 103: {
1.28 ratchov 104: if (debug_level > 0)
105: debug_level--;
1.15 ratchov 106: }
107:
108: void
109: usage(void)
1.1 kstailey 110: {
1.15 ratchov 111: extern char *__progname;
1.4 millert 112:
1.15 ratchov 113: fprintf(stderr,
1.29 jmc 114: "usage: %s [-lu] [-b nsamples] [-C min:max] [-c min:max] [-e enc] "
115: "[-f device]\n"
1.45 jmc 116: "\t[-h fmt] [-i file] [-m mode] [-o file] [-r rate] [-s socket]\n"
1.44 jmc 117: "\t[-v volume] [-x policy]\n",
1.15 ratchov 118: __progname);
119: }
120:
121: void
122: opt_ch(struct aparams *par)
123: {
124: if (sscanf(optarg, "%u:%u", &par->cmin, &par->cmax) != 2 ||
1.28 ratchov 125: par->cmax < par->cmin || par->cmax > NCHAN_MAX - 1)
1.35 ratchov 126: errx(1, "%s: bad channel range", optarg);
1.15 ratchov 127: }
1.13 uwe 128:
1.15 ratchov 129: void
130: opt_rate(struct aparams *par)
131: {
132: if (sscanf(optarg, "%u", &par->rate) != 1 ||
133: par->rate < RATE_MIN || par->rate > RATE_MAX)
1.35 ratchov 134: errx(1, "%s: bad sample rate", optarg);
135: }
136:
137: void
138: opt_vol(unsigned *vol)
139: {
140: if (sscanf(optarg, "%u", vol) != 1 ||
141: *vol > MIDI_MAXCTL)
142: errx(1, "%s: bad volume", optarg);
1.15 ratchov 143: }
1.13 uwe 144:
1.15 ratchov 145: void
146: opt_enc(struct aparams *par)
147: {
1.28 ratchov 148: int len;
149:
150: len = aparams_strtoenc(par, optarg);
151: if (len == 0 || optarg[len] != '\0')
152: errx(1, "%s: bad encoding", optarg);
1.15 ratchov 153: }
1.4 millert 154:
1.15 ratchov 155: int
156: opt_hdr(void)
157: {
158: if (strcmp("auto", optarg) == 0)
159: return HDR_AUTO;
160: if (strcmp("raw", optarg) == 0)
161: return HDR_RAW;
162: if (strcmp("wav", optarg) == 0)
163: return HDR_WAV;
1.35 ratchov 164: errx(1, "%s: bad header specification", optarg);
1.1 kstailey 165: }
166:
1.22 ratchov 167: int
168: opt_xrun(void)
169: {
170: if (strcmp("ignore", optarg) == 0)
171: return XRUN_IGNORE;
172: if (strcmp("sync", optarg) == 0)
173: return XRUN_SYNC;
174: if (strcmp("error", optarg) == 0)
175: return XRUN_ERROR;
176: errx(1, "%s: onderrun/overrun policy", optarg);
177: }
178:
1.43 ratchov 179: int
180: opt_mode(void)
181: {
182: if (strcmp("play", optarg) == 0)
183: return MODE_PLAY;
184: if (strcmp("rec", optarg) == 0)
185: return MODE_REC;
186: if (strcmp("duplex", optarg) == 0)
187: return MODE_PLAY | MODE_REC;
188: errx(1, "%s: bad mode", optarg);
189: }
190:
1.13 uwe 191: /*
1.42 ratchov 192: * Arguments of -i, -o and -s options are stored in a list.
1.13 uwe 193: */
1.15 ratchov 194: struct farg {
195: SLIST_ENTRY(farg) entry;
1.42 ratchov 196: struct aparams ipar; /* input (read) parameters */
197: struct aparams opar; /* output (write) parameters */
1.15 ratchov 198: unsigned vol; /* last requested volume */
199: char *name; /* optarg pointer (no need to copy it */
200: int hdr; /* header format */
1.22 ratchov 201: int xrun; /* overrun/underrun policy */
1.15 ratchov 202: };
1.13 uwe 203:
1.15 ratchov 204: SLIST_HEAD(farglist, farg);
1.13 uwe 205:
1.15 ratchov 206: /*
207: * Add a farg entry to the given list, corresponding
208: * to the given file name.
209: */
210: void
1.42 ratchov 211: farg_add(struct farglist *list,
212: struct aparams *ipar, struct aparams *opar, unsigned vol,
213: int hdr, int xrun, char *optarg)
1.15 ratchov 214: {
215: struct farg *fa;
216: size_t namelen;
217:
218: fa = malloc(sizeof(struct farg));
219: if (fa == NULL)
220: err(1, "%s", optarg);
221:
222: if (hdr == HDR_AUTO) {
223: namelen = strlen(optarg);
224: if (namelen >= 4 &&
225: strcasecmp(optarg + namelen - 4, ".wav") == 0) {
226: fa->hdr = HDR_WAV;
227: DPRINTF("%s: assuming wav file format\n", optarg);
228: } else {
229: fa->hdr = HDR_RAW;
230: DPRINTF("%s: assuming headerless file\n", optarg);
1.13 uwe 231: }
1.15 ratchov 232: } else
233: fa->hdr = hdr;
1.22 ratchov 234: fa->xrun = xrun;
1.42 ratchov 235: fa->ipar = *ipar;
236: fa->opar = *opar;
1.15 ratchov 237: fa->vol = vol;
238: fa->name = optarg;
239: SLIST_INSERT_HEAD(list, fa, entry);
240: }
1.13 uwe 241:
1.15 ratchov 242: /*
243: * Open an input file and setup converter if necessary.
244: */
245: void
1.26 ratchov 246: newinput(struct farg *fa)
1.15 ratchov 247: {
248: int fd;
1.28 ratchov 249: struct wav *f;
1.26 ratchov 250: struct aproc *proc;
251: struct abuf *buf;
252: unsigned nfr;
1.15 ratchov 253:
254: if (strcmp(fa->name, "-") == 0) {
255: fd = STDIN_FILENO;
256: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
257: warn("stdin");
258: fa->name = "stdin";
259: } else {
260: fd = open(fa->name, O_RDONLY | O_NONBLOCK, 0666);
261: if (fd < 0)
262: err(1, "%s", fa->name);
1.13 uwe 263: }
1.28 ratchov 264: /*
265: * XXX : we should round rate, right ?
266: */
1.42 ratchov 267: f = wav_new_in(&wav_ops, fd, fa->name, &fa->ipar, fa->hdr);
268: nfr = dev_bufsz * fa->ipar.rate / dev_rate;
269: buf = abuf_new(nfr, &fa->ipar);
1.28 ratchov 270: proc = rpipe_new((struct file *)f);
1.26 ratchov 271: aproc_setout(proc, buf);
1.28 ratchov 272: abuf_fill(buf); /* XXX: move this in dev_attach() ? */
1.42 ratchov 273: dev_attach(fa->name, buf, &fa->ipar, fa->xrun,
1.40 ratchov 274: NULL, NULL, 0, ADATA_UNIT);
1.35 ratchov 275: dev_setvol(buf, MIDI_TO_ADATA(fa->vol));
1.15 ratchov 276: }
1.13 uwe 277:
1.15 ratchov 278: /*
279: * Open an output file and setup converter if necessary.
280: */
281: void
1.26 ratchov 282: newoutput(struct farg *fa)
1.15 ratchov 283: {
284: int fd;
1.28 ratchov 285: struct wav *f;
1.26 ratchov 286: struct aproc *proc;
287: struct abuf *buf;
288: unsigned nfr;
1.15 ratchov 289:
290: if (strcmp(fa->name, "-") == 0) {
291: fd = STDOUT_FILENO;
292: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
293: warn("stdout");
294: fa->name = "stdout";
295: } else {
296: fd = open(fa->name,
297: O_WRONLY | O_TRUNC | O_CREAT | O_NONBLOCK, 0666);
298: if (fd < 0)
299: err(1, "%s", fa->name);
300: }
1.28 ratchov 301: /*
302: * XXX : we should round rate, right ?
303: */
1.42 ratchov 304: f = wav_new_out(&wav_ops, fd, fa->name, &fa->opar, fa->hdr);
305: nfr = dev_bufsz * fa->opar.rate / dev_rate;
1.28 ratchov 306: proc = wpipe_new((struct file *)f);
1.42 ratchov 307: buf = abuf_new(nfr, &fa->opar);
1.26 ratchov 308: aproc_setin(proc, buf);
1.42 ratchov 309: dev_attach(fa->name, NULL, NULL, 0, buf, &fa->opar, fa->xrun, 0);
1.13 uwe 310: }
311:
1.1 kstailey 312: int
1.15 ratchov 313: main(int argc, char **argv)
1.1 kstailey 314: {
1.34 ratchov 315: int c, u_flag, l_flag, hdr, xrun, suspend = 0;
1.15 ratchov 316: struct farg *fa;
1.42 ratchov 317: struct farglist ifiles, ofiles, sfiles;
1.26 ratchov 318: struct aparams ipar, opar, dipar, dopar;
1.28 ratchov 319: struct sigaction sa;
1.43 ratchov 320: unsigned bufsz, mode;
1.42 ratchov 321: char *devpath, *dbgenv;
1.21 ratchov 322: const char *errstr;
1.28 ratchov 323: extern char *malloc_options;
1.40 ratchov 324: unsigned volctl;
1.28 ratchov 325:
326: malloc_options = "FGJ";
1.15 ratchov 327:
1.19 ratchov 328: dbgenv = getenv("AUCAT_DEBUG");
329: if (dbgenv) {
1.21 ratchov 330: debug_level = strtonum(dbgenv, 0, 4, &errstr);
331: if (errstr)
332: errx(1, "AUCAT_DEBUG is %s: %s", errstr, dbgenv);
1.19 ratchov 333: }
334:
1.15 ratchov 335: aparams_init(&ipar, 0, 1, 44100);
336: aparams_init(&opar, 0, 1, 44100);
1.26 ratchov 337: u_flag = 0;
1.28 ratchov 338: l_flag = 0;
1.15 ratchov 339: devpath = NULL;
340: SLIST_INIT(&ifiles);
341: SLIST_INIT(&ofiles);
1.42 ratchov 342: SLIST_INIT(&sfiles);
1.28 ratchov 343: hdr = HDR_AUTO;
344: xrun = XRUN_IGNORE;
1.40 ratchov 345: volctl = MIDI_MAXCTL;
1.32 ratchov 346: bufsz = 44100 * 4 / 15; /* XXX: use milliseconds, not frames */
1.43 ratchov 347: mode = 0;
1.15 ratchov 348:
1.43 ratchov 349: while ((c = getopt(argc, argv, "b:c:C:e:r:h:x:v:i:o:f:m:lus:")) != -1) {
1.15 ratchov 350: switch (c) {
1.43 ratchov 351: case 'm':
352: mode = opt_mode();
353: break;
1.15 ratchov 354: case 'h':
1.28 ratchov 355: hdr = opt_hdr();
1.15 ratchov 356: break;
1.22 ratchov 357: case 'x':
1.28 ratchov 358: xrun = opt_xrun();
1.22 ratchov 359: break;
1.15 ratchov 360: case 'c':
361: opt_ch(&ipar);
362: break;
363: case 'C':
364: opt_ch(&opar);
365: break;
366: case 'e':
367: opt_enc(&ipar);
1.28 ratchov 368: aparams_copyenc(&opar, &ipar);
1.15 ratchov 369: break;
370: case 'r':
371: opt_rate(&ipar);
1.28 ratchov 372: opar.rate = ipar.rate;
1.15 ratchov 373: break;
1.35 ratchov 374: case 'v':
1.40 ratchov 375: opt_vol(&volctl);
1.35 ratchov 376: break;
1.15 ratchov 377: case 'i':
1.42 ratchov 378: farg_add(&ifiles, &ipar, &opar, volctl,
379: hdr, xrun, optarg);
1.15 ratchov 380: break;
381: case 'o':
1.42 ratchov 382: farg_add(&ofiles, &ipar, &opar, volctl,
383: hdr, xrun, optarg);
384: break;
385: case 's':
386: farg_add(&sfiles, &ipar, &opar, volctl,
387: hdr, xrun, optarg);
1.15 ratchov 388: break;
1.4 millert 389: case 'f':
1.15 ratchov 390: if (devpath)
391: err(1, "only one -f allowed");
392: devpath = optarg;
1.28 ratchov 393: dipar = opar;
394: dopar = ipar;
1.15 ratchov 395: break;
1.28 ratchov 396: case 'l':
397: l_flag = 1;
1.17 jakemsr 398: break;
1.15 ratchov 399: case 'u':
400: u_flag = 1;
1.4 millert 401: break;
1.28 ratchov 402: case 'b':
1.32 ratchov 403: if (sscanf(optarg, "%u", &bufsz) != 1 || bufsz == 0) {
1.28 ratchov 404: fprintf(stderr, "%s: bad buf size\n", optarg);
405: exit(1);
406: }
407: break;
1.11 jaredy 408: default:
409: usage();
1.15 ratchov 410: exit(1);
1.4 millert 411: }
412: }
413: argc -= optind;
414: argv += optind;
415:
1.15 ratchov 416: if (!devpath) {
1.47 ratchov 417: dopar = ipar;
418: dipar = opar;
1.15 ratchov 419: }
420:
1.28 ratchov 421: if (!l_flag && SLIST_EMPTY(&ifiles) &&
422: SLIST_EMPTY(&ofiles) && argc > 0) {
1.15 ratchov 423: /*
424: * Legacy mode: if no -i or -o options are provided, and
425: * there are arguments then assume the arguments are files
426: * to play.
427: */
428: for (c = 0; c < argc; c++)
429: if (legacy_play(devpath, argv[c]) != 0) {
1.17 jakemsr 430: errx(1, "%s: could not play\n", argv[c]);
1.15 ratchov 431: }
432: exit(0);
433: } else if (argc > 0) {
1.11 jaredy 434: usage();
1.15 ratchov 435: exit(1);
436: }
437:
1.42 ratchov 438: if (!l_flag && !SLIST_EMPTY(&sfiles))
439: errx(1, "can't use -s without -l");
1.43 ratchov 440: if ((l_flag || mode != 0) &&
441: (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles)))
442: errx(1, "can't use -l, -m and -s with -o or -i");
443: if (!mode) {
444: if (l_flag || !SLIST_EMPTY(&ifiles))
445: mode |= MODE_PLAY;
446: if (l_flag || !SLIST_EMPTY(&ofiles))
447: mode |= MODE_REC;
448: }
1.15 ratchov 449:
1.46 ratchov 450: /*
451: * if there are no sockets paths provided use the default
452: */
453: if (l_flag && SLIST_EMPTY(&sfiles)) {
454: farg_add(&sfiles, &dopar, &dipar,
455: MIDI_MAXCTL, HDR_RAW, XRUN_IGNORE, DEFAULT_SOCKET);
456: }
457:
458: if (!u_flag) {
1.26 ratchov 459: /*
460: * Calculate "best" device parameters. Iterate over all
461: * inputs and outputs and find the maximum sample rate
462: * and channel number.
463: */
1.49 ! ratchov 464: aparams_init(&dipar, dipar.cmin, dipar.cmax, dipar.rate);
! 465: aparams_init(&dopar, dopar.cmin, dopar.cmax, dopar.rate);
1.26 ratchov 466: SLIST_FOREACH(fa, &ifiles, entry) {
1.46 ratchov 467: aparams_grow(&dopar, &fa->ipar);
1.26 ratchov 468: }
469: SLIST_FOREACH(fa, &ofiles, entry) {
1.46 ratchov 470: aparams_grow(&dipar, &fa->opar);
471: }
472: SLIST_FOREACH(fa, &sfiles, entry) {
473: aparams_grow(&dopar, &fa->ipar);
474: aparams_grow(&dipar, &fa->opar);
1.17 jakemsr 475: }
1.42 ratchov 476: }
477:
1.28 ratchov 478: quit_flag = 0;
479: sigfillset(&sa.sa_mask);
480: sa.sa_flags = SA_RESTART;
481: sa.sa_handler = sigint;
482: if (sigaction(SIGINT, &sa, NULL) < 0)
483: DPRINTF("sigaction(int) failed\n");
484: #ifdef DEBUG
485: sa.sa_handler = sigusr1;
486: if (sigaction(SIGUSR1, &sa, NULL) < 0)
487: DPRINTF("sigaction(usr1) failed\n");
488: sa.sa_handler = sigusr2;
489: if (sigaction(SIGUSR2, &sa, NULL) < 0)
490: DPRINTF("sigaction(usr2) failed1n");
491: #endif
492: filelist_init();
1.15 ratchov 493: /*
1.32 ratchov 494: * Open the device. Give half of the buffer to the device,
495: * the other half is for the socket/files
1.15 ratchov 496: */
1.26 ratchov 497: dev_init(devpath,
1.43 ratchov 498: (mode & MODE_REC) ? &dipar : NULL,
499: (mode & MODE_PLAY) ? &dopar : NULL,
1.48 ratchov 500: bufsz);
1.28 ratchov 501:
1.15 ratchov 502: /*
503: * Create buffers for all input and output pipes.
504: */
1.26 ratchov 505: while (!SLIST_EMPTY(&ifiles)) {
506: fa = SLIST_FIRST(&ifiles);
507: SLIST_REMOVE_HEAD(&ifiles, entry);
508: newinput(fa);
509: free(fa);
510: }
511: while (!SLIST_EMPTY(&ofiles)) {
512: fa = SLIST_FIRST(&ofiles);
513: SLIST_REMOVE_HEAD(&ofiles, entry);
514: newoutput(fa);
1.42 ratchov 515: free(fa);
516: }
517: while (!SLIST_EMPTY(&sfiles)) {
518: fa = SLIST_FIRST(&sfiles);
519: SLIST_REMOVE_HEAD(&sfiles, entry);
520: (void)listen_new(&listen_ops, fa->name,
521: &fa->opar, &fa->ipar, MIDI_TO_ADATA(fa->vol));
1.26 ratchov 522: free(fa);
1.15 ratchov 523: }
1.13 uwe 524:
1.15 ratchov 525: /*
1.28 ratchov 526: * loop, start audio
1.15 ratchov 527: */
1.28 ratchov 528: for (;;) {
529: if (quit_flag) {
530: if (l_flag)
531: filelist_unlisten();
532: break;
533: }
1.38 ratchov 534: if (!file_poll()) {
1.39 ratchov 535: fprintf(stderr, "Terminated, device disappeared?\n");
1.38 ratchov 536: exit(1);
537: }
1.34 ratchov 538: if ((!dev_mix || dev_mix->u.mix.idle > 2 * dev_bufsz) &&
539: (!dev_sub || dev_sub->u.sub.idle > 2 * dev_bufsz)) {
1.37 ratchov 540: if (!l_flag)
541: break;
1.34 ratchov 542: if (!suspend) {
543: DPRINTF("suspending\n");
544: suspend = 1;
545: dev_stop();
546: dev_clear();
547: }
548: }
549: if ((dev_mix && dev_mix->u.mix.idle == 0) ||
550: (dev_sub && dev_sub->u.sub.idle == 0)) {
551: if (suspend) {
552: DPRINTF("resuming\n");
553: suspend = 0;
554: dev_start();
555: }
556: }
557: }
558: if (suspend) {
559: DPRINTF("resuming to drain\n");
560: suspend = 0;
561: dev_start();
1.28 ratchov 562: }
1.37 ratchov 563: dev_done();
1.28 ratchov 564: filelist_done();
1.11 jaredy 565:
1.28 ratchov 566: sigfillset(&sa.sa_mask);
567: sa.sa_flags = SA_RESTART;
568: sa.sa_handler = SIG_DFL;
569: if (sigaction(SIGINT, &sa, NULL) < 0)
570: DPRINTF("dev_done: sigaction failed\n");
1.15 ratchov 571: return 0;
1.1 kstailey 572: }