Annotation of src/usr.bin/aucat/aucat.c, Revision 1.27
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.15 ratchov 51: #include <err.h>
1.1 kstailey 52: #include <fcntl.h>
1.15 ratchov 53: #include <signal.h>
1.1 kstailey 54: #include <stdio.h>
1.4 millert 55: #include <stdlib.h>
1.8 david 56: #include <string.h>
1.1 kstailey 57: #include <unistd.h>
1.15 ratchov 58: #include <varargs.h>
1.1 kstailey 59:
1.15 ratchov 60: #include "conf.h"
61: #include "aparams.h"
62: #include "aproc.h"
63: #include "abuf.h"
64: #include "file.h"
65: #include "dev.h"
1.11 jaredy 66:
1.26 ratchov 67: int debug_level = 0, quiet_flag = 0;
68: volatile int quit_flag = 0, pause_flag = 0;
1.7 deraadt 69:
1.26 ratchov 70: void suspend(struct file *);
71: void fill(struct file *);
72: void flush(struct file *);
1.22 ratchov 73:
1.15 ratchov 74: /*
75: * List of allowed encodings and their names.
76: */
77: struct enc {
78: char *name;
79: struct aparams par;
80: } enc_list[] = {
81: /* name bps, bits, le, sign, msb, unused */
82: { "s8", { 1, 8, 1, 1, 1, 0, 0, 0 } },
83: { "u8", { 1, 8, 1, 0, 1, 0, 0, 0 } },
84: { "s16le", { 2, 16, 1, 1, 1, 0, 0, 0 } },
85: { "u16le", { 2, 16, 1, 0, 1, 0, 0, 0 } },
86: { "s16be", { 2, 16, 0, 1, 1, 0, 0, 0 } },
87: { "u16be", { 2, 16, 0, 0, 1, 0, 0, 0 } },
88: { "s24le", { 4, 24, 1, 1, 1, 0, 0, 0 } },
89: { "u24le", { 4, 24, 1, 0, 1, 0, 0, 0 } },
90: { "s24be", { 4, 24, 0, 1, 1, 0, 0, 0 } },
91: { "u24be", { 4, 24, 0, 0, 1, 0, 0, 0 } },
92: { "s32le", { 4, 32, 1, 1, 1, 0, 0, 0 } },
93: { "u32le", { 4, 32, 1, 0, 1, 0, 0, 0 } },
94: { "s32be", { 4, 32, 0, 1, 1, 0, 0, 0 } },
95: { "u32be", { 4, 32, 0, 0, 1, 0, 0, 0 } },
96: { "s24le3", { 3, 24, 1, 1, 1, 0, 0, 0 } },
97: { "u24le3", { 3, 24, 1, 0, 1, 0, 0, 0 } },
98: { "s24be3", { 3, 24, 0, 1, 1, 0, 0, 0 } },
99: { "u24be3", { 3, 24, 0, 0, 1, 0, 0, 0 } },
100: { "s20le3", { 3, 20, 1, 1, 1, 0, 0, 0 } },
101: { "u20le3", { 3, 20, 1, 0, 1, 0, 0, 0 } },
102: { "s20be3", { 3, 20, 0, 1, 1, 0, 0, 0 } },
103: { "u20be3", { 3, 20, 0, 0, 1, 0, 0, 0 } },
104: { "s18le3", { 3, 18, 1, 1, 1, 0, 0, 0 } },
105: { "u18le3", { 3, 18, 1, 0, 1, 0, 0, 0 } },
106: { "s18be3", { 3, 18, 0, 1, 1, 0, 0, 0 } },
107: { "u18be3", { 3, 18, 0, 0, 1, 0, 0, 0 } },
108: { NULL, { 0, 0, 0, 0, 0, 0, 0, 0 } }
109: };
1.13 uwe 110:
1.11 jaredy 111: /*
1.15 ratchov 112: * Search an encoding in the above table. On success fill encoding
113: * part of "par" and return 1, otherwise return 0.
1.1 kstailey 114: */
1.15 ratchov 115: unsigned
116: enc_lookup(char *name, struct aparams *par)
117: {
118: struct enc *e;
119:
120: for (e = enc_list; e->name != NULL; e++) {
121: if (strcmp(e->name, name) == 0) {
122: par->bps = e->par.bps;
123: par->bits = e->par.bits;
124: par->sig = e->par.sig;
125: par->le = e->par.le;
126: par->msb = e->par.msb;
127: return 1;
128: }
129: }
130: return 0;
131: }
132:
133: void
134: usage(void)
1.1 kstailey 135: {
1.15 ratchov 136: extern char *__progname;
1.4 millert 137:
1.15 ratchov 138: fprintf(stderr,
1.27 ! sobrado 139: "usage: %s [-qu] [-C min:max] [-c min:max] [-E enc] [-e enc] "
! 140: "[-f device]\n"
! 141: "\t[-H fmt] [-h fmt] [-i file] [-o file] [-R rate] [-r rate]\n"
! 142: "\t[-X policy] [-x policy]\n",
1.15 ratchov 143: __progname);
144: }
145:
146: void
147: opt_ch(struct aparams *par)
148: {
149: if (sscanf(optarg, "%u:%u", &par->cmin, &par->cmax) != 2 ||
150: par->cmin > CHAN_MAX || par->cmax > CHAN_MAX ||
151: par->cmin > par->cmax)
152: err(1, "%s: bad channel range", optarg);
153: }
1.13 uwe 154:
1.15 ratchov 155: void
156: opt_rate(struct aparams *par)
157: {
158: if (sscanf(optarg, "%u", &par->rate) != 1 ||
159: par->rate < RATE_MIN || par->rate > RATE_MAX)
160: err(1, "%s: bad sample rate", optarg);
161: }
1.13 uwe 162:
1.15 ratchov 163: void
164: opt_enc(struct aparams *par)
165: {
166: if (!enc_lookup(optarg, par))
167: err(1, "%s: bad encoding", optarg);
168: }
1.4 millert 169:
1.15 ratchov 170: int
171: opt_hdr(void)
172: {
173: if (strcmp("auto", optarg) == 0)
174: return HDR_AUTO;
175: if (strcmp("raw", optarg) == 0)
176: return HDR_RAW;
177: if (strcmp("wav", optarg) == 0)
178: return HDR_WAV;
179: err(1, "%s: bad header specification", optarg);
1.1 kstailey 180: }
181:
1.22 ratchov 182: int
183: opt_xrun(void)
184: {
185: if (strcmp("ignore", optarg) == 0)
186: return XRUN_IGNORE;
187: if (strcmp("sync", optarg) == 0)
188: return XRUN_SYNC;
189: if (strcmp("error", optarg) == 0)
190: return XRUN_ERROR;
191: errx(1, "%s: onderrun/overrun policy", optarg);
192: }
193:
1.13 uwe 194: /*
1.15 ratchov 195: * Arguments of -i and -o opations are stored in a list.
1.13 uwe 196: */
1.15 ratchov 197: struct farg {
198: SLIST_ENTRY(farg) entry;
199: struct aparams par; /* last requested format */
200: unsigned vol; /* last requested volume */
201: char *name; /* optarg pointer (no need to copy it */
202: int hdr; /* header format */
1.22 ratchov 203: int xrun; /* overrun/underrun policy */
1.15 ratchov 204: };
1.13 uwe 205:
1.15 ratchov 206: SLIST_HEAD(farglist, farg);
1.13 uwe 207:
1.15 ratchov 208: /*
209: * Add a farg entry to the given list, corresponding
210: * to the given file name.
211: */
212: void
213: opt_file(struct farglist *list,
1.22 ratchov 214: struct aparams *par, unsigned vol, int hdr, int xrun, char *optarg)
1.15 ratchov 215: {
216: struct farg *fa;
217: size_t namelen;
218:
219: fa = malloc(sizeof(struct farg));
220: if (fa == NULL)
221: err(1, "%s", optarg);
222:
223: if (hdr == HDR_AUTO) {
224: namelen = strlen(optarg);
225: if (namelen >= 4 &&
226: strcasecmp(optarg + namelen - 4, ".wav") == 0) {
227: fa->hdr = HDR_WAV;
228: DPRINTF("%s: assuming wav file format\n", optarg);
229: } else {
230: fa->hdr = HDR_RAW;
231: DPRINTF("%s: assuming headerless file\n", optarg);
1.13 uwe 232: }
1.15 ratchov 233: } else
234: fa->hdr = hdr;
1.22 ratchov 235: fa->xrun = xrun;
1.15 ratchov 236: fa->par = *par;
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;
249: struct file *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.15 ratchov 264: f = file_new(fd, fa->name);
1.26 ratchov 265: f->hdr = 0;
266: f->hpar = fa->par;
1.15 ratchov 267: if (fa->hdr == HDR_WAV) {
1.26 ratchov 268: if (!wav_readhdr(fd, &f->hpar, &f->rbytes))
1.15 ratchov 269: exit(1);
1.13 uwe 270: }
1.26 ratchov 271: nfr = dev_onfr * f->hpar.rate / dev_opar.rate;
272: buf = abuf_new(nfr, aparams_bpf(&f->hpar));
273: proc = rpipe_new(f);
274: aproc_setout(proc, buf);
275: dev_attach(fa->name, buf, &f->hpar, fa->xrun, NULL, NULL, 0);
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;
285: struct file *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: }
301: f = file_new(fd, fa->name);
1.26 ratchov 302: f->hdr = fa->hdr;
303: f->hpar = fa->par;
304: if (f->hdr == HDR_WAV) {
1.15 ratchov 305: f->wbytes = WAV_DATAMAX;
1.26 ratchov 306: if (!wav_writehdr(fd, &f->hpar))
1.15 ratchov 307: exit(1);
1.13 uwe 308: }
1.26 ratchov 309: nfr = dev_infr * f->hpar.rate / dev_ipar.rate;
310: proc = wpipe_new(f);
311: buf = abuf_new(nfr, aparams_bpf(&f->hpar));
312: aproc_setin(proc, buf);
313: dev_attach(fa->name, NULL, NULL, 0, buf, &f->hpar, fa->xrun);
1.13 uwe 314: }
315:
1.1 kstailey 316: int
1.15 ratchov 317: main(int argc, char **argv)
1.1 kstailey 318: {
1.26 ratchov 319: int c, u_flag, ohdr, ihdr, ixrun, oxrun;
1.15 ratchov 320: struct farg *fa;
321: struct farglist ifiles, ofiles;
1.26 ratchov 322: struct aparams ipar, opar, dipar, dopar;
1.15 ratchov 323: unsigned ivol, ovol;
1.19 ratchov 324: char *devpath, *dbgenv;
1.21 ratchov 325: const char *errstr;
1.15 ratchov 326:
1.19 ratchov 327: dbgenv = getenv("AUCAT_DEBUG");
328: if (dbgenv) {
1.21 ratchov 329: debug_level = strtonum(dbgenv, 0, 4, &errstr);
330: if (errstr)
331: errx(1, "AUCAT_DEBUG is %s: %s", errstr, dbgenv);
1.19 ratchov 332: }
333:
1.15 ratchov 334: aparams_init(&ipar, 0, 1, 44100);
335: aparams_init(&opar, 0, 1, 44100);
336:
1.26 ratchov 337: u_flag = 0;
1.15 ratchov 338: devpath = NULL;
339: SLIST_INIT(&ifiles);
340: SLIST_INIT(&ofiles);
341: ihdr = ohdr = HDR_AUTO;
1.22 ratchov 342: ixrun = oxrun = XRUN_IGNORE;
1.15 ratchov 343: ivol = ovol = MIDI_TO_ADATA(127);
344:
1.22 ratchov 345: while ((c = getopt(argc, argv, "c:C:e:E:r:R:h:H:x:X:i:o:f:qu"))
346: != -1) {
1.15 ratchov 347: switch (c) {
348: case 'h':
349: ihdr = opt_hdr();
350: break;
351: case 'H':
352: ohdr = opt_hdr();
353: break;
1.22 ratchov 354: case 'x':
355: ixrun = opt_xrun();
356: break;
357: case 'X':
358: oxrun = opt_xrun();
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);
368: break;
369: case 'E':
370: opt_enc(&opar);
371: break;
372: case 'r':
373: opt_rate(&ipar);
374: break;
375: case 'R':
376: opt_rate(&opar);
377: break;
378: case 'i':
1.22 ratchov 379: opt_file(&ifiles, &ipar, 127, ihdr, ixrun, optarg);
1.15 ratchov 380: break;
381: case 'o':
1.22 ratchov 382: opt_file(&ofiles, &opar, 127, ohdr, oxrun, optarg);
1.15 ratchov 383: break;
1.4 millert 384: case 'f':
1.15 ratchov 385: if (devpath)
386: err(1, "only one -f allowed");
387: devpath = optarg;
388: dipar = ipar;
389: dopar = opar;
390: break;
1.17 jakemsr 391: case 'q':
392: quiet_flag = 1;
393: break;
1.15 ratchov 394: case 'u':
395: u_flag = 1;
1.4 millert 396: break;
1.11 jaredy 397: default:
398: usage();
1.15 ratchov 399: exit(1);
1.4 millert 400: }
401: }
402: argc -= optind;
403: argv += optind;
404:
1.15 ratchov 405: if (!devpath) {
406: devpath = getenv("AUDIODEVICE");
407: if (devpath == NULL)
408: devpath = DEFAULT_DEVICE;
409: dipar = ipar;
410: dopar = opar;
411: }
412:
413: if (SLIST_EMPTY(&ifiles) && SLIST_EMPTY(&ofiles) && argc > 0) {
414: /*
415: * Legacy mode: if no -i or -o options are provided, and
416: * there are arguments then assume the arguments are files
417: * to play.
418: */
419: for (c = 0; c < argc; c++)
420: if (legacy_play(devpath, argv[c]) != 0) {
1.17 jakemsr 421: errx(1, "%s: could not play\n", argv[c]);
1.15 ratchov 422: }
423: exit(0);
424: } else if (argc > 0) {
1.11 jaredy 425: usage();
1.15 ratchov 426: exit(1);
427: }
428:
429:
430: if (!u_flag) {
1.26 ratchov 431: /*
432: * Calculate "best" device parameters. Iterate over all
433: * inputs and outputs and find the maximum sample rate
434: * and channel number.
435: */
436: aparams_init(&dipar, CHAN_MAX, 0, RATE_MAX);
437: aparams_init(&dopar, CHAN_MAX, 0, RATE_MIN);
438: SLIST_FOREACH(fa, &ifiles, entry) {
439: if (dopar.cmin > fa->par.cmin)
440: dopar.cmin = fa->par.cmin;
441: if (dopar.cmax < fa->par.cmax)
442: dopar.cmax = fa->par.cmax;
443: if (dopar.rate < fa->par.rate)
444: dopar.rate = fa->par.rate;
445: }
446: SLIST_FOREACH(fa, &ofiles, entry) {
447: if (dipar.cmin > fa->par.cmin)
448: dipar.cmin = fa->par.cmin;
449: if (dipar.cmax < fa->par.cmax)
450: dipar.cmax = fa->par.cmax;
451: if (dipar.rate > fa->par.rate)
452: dipar.rate = fa->par.rate;
1.17 jakemsr 453: }
1.15 ratchov 454: }
1.26 ratchov 455: file_start();
1.15 ratchov 456:
457: /*
1.26 ratchov 458: * Open the device, dev_init() will return new parameters
459: * that must be used by all inputs and outputs.
1.15 ratchov 460: */
1.26 ratchov 461: dev_init(devpath,
462: (!SLIST_EMPTY(&ofiles)) ? &dipar : NULL,
463: (!SLIST_EMPTY(&ifiles)) ? &dopar : NULL);
1.15 ratchov 464:
465: /*
466: * Create buffers for all input and output pipes.
467: */
1.26 ratchov 468: while (!SLIST_EMPTY(&ifiles)) {
469: fa = SLIST_FIRST(&ifiles);
470: SLIST_REMOVE_HEAD(&ifiles, entry);
471: newinput(fa);
472: free(fa);
473: }
474: while (!SLIST_EMPTY(&ofiles)) {
475: fa = SLIST_FIRST(&ofiles);
476: SLIST_REMOVE_HEAD(&ofiles, entry);
477: newoutput(fa);
478: free(fa);
1.15 ratchov 479: }
1.13 uwe 480:
1.15 ratchov 481: /*
1.26 ratchov 482: * Normalize input levels
1.15 ratchov 483: */
1.26 ratchov 484: if (dev_mix)
485: mix_setmaster(dev_mix);
1.13 uwe 486:
1.15 ratchov 487: /*
488: * start audio
489: */
1.17 jakemsr 490: if (!quiet_flag)
491: fprintf(stderr, "starting device...\n");
1.26 ratchov 492: dev_start();
493: if (!quiet_flag)
494: fprintf(stderr, "process started...\n");
495: dev_run(1);
1.17 jakemsr 496: if (!quiet_flag)
1.26 ratchov 497: fprintf(stderr, "stopping device...\n");
498: dev_done();
1.11 jaredy 499:
1.15 ratchov 500: file_stop();
501: return 0;
1.1 kstailey 502: }