Annotation of src/usr.bin/aucat/dev.c, Revision 1.26
1.26 ! ratchov 1: /* $OpenBSD: dev.c,v 1.25 2009/01/23 17:38:15 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
4: *
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: #include <stdio.h>
18: #include <stdlib.h>
19: #include <unistd.h>
20:
21: #include "dev.h"
22: #include "abuf.h"
23: #include "aproc.h"
1.3 ratchov 24: #include "pipe.h"
1.1 ratchov 25: #include "conf.h"
1.3 ratchov 26: #include "safile.h"
1.1 ratchov 27:
1.3 ratchov 28: unsigned dev_bufsz, dev_round, dev_rate;
1.1 ratchov 29: struct aparams dev_ipar, dev_opar;
30: struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play;
1.24 ratchov 31:
32: /*
33: * same as dev_init(), but create a fake device that records what is
34: * played
35: */
36: void
37: dev_loopinit(struct aparams *dipar, struct aparams *dopar, unsigned bufsz)
38: {
39: struct abuf *buf;
40: struct aparams par;
41: unsigned cmin, cmax, rate;
42:
43: cmin = (dipar->cmin < dopar->cmin) ? dipar->cmin : dopar->cmin;
44: cmax = (dipar->cmax > dopar->cmax) ? dipar->cmax : dopar->cmax;
45: rate = (dipar->rate > dopar->rate) ? dipar->rate : dopar->rate;
46: aparams_init(&par, cmin, cmax, rate);
47: dev_ipar = par;
48: dev_opar = par;
49: dev_round = (bufsz + 1) / 2;
50: dev_bufsz = dev_round * 2;
1.25 ratchov 51: dev_rate = rate;
1.24 ratchov 52: dev_rec = NULL;
53: dev_play = NULL;
1.25 ratchov 54:
1.24 ratchov 55: buf = abuf_new(dev_bufsz, &par);
56: dev_mix = mix_new("mix", dev_bufsz);
57: dev_mix->refs++;
58: dev_sub = sub_new("sub", dev_bufsz);
59: dev_sub->refs++;
60: aproc_setout(dev_mix, buf);
61: aproc_setin(dev_sub, buf);
62:
63: dev_mix->u.mix.flags |= MIX_AUTOQUIT;
64: dev_sub->u.sub.flags |= SUB_AUTOQUIT;
65:
66: *dipar = dev_ipar;
67: *dopar = dev_opar;
68: }
69:
70: /*
71: * same as dev_done(), but destroy a loopback device
72: */
73: void
74: dev_loopdone(void)
75: {
76: struct file *f;
77:
78: DPRINTF("dev_loopdone:\n");
79:
80: dev_sub->refs--;
81: dev_sub = NULL;
82: dev_mix->refs--;
83: dev_mix = NULL;
84: /*
85: * generate EOF on all inputs
86: */
87: restart:
88: LIST_FOREACH(f, &file_list, entry) {
89: if (f->rproc != NULL) {
90: file_eof(f);
91: goto restart;
92: }
93: }
94: while (file_poll())
95: ; /* nothing */
96: }
1.1 ratchov 97:
1.20 ratchov 98: unsigned
99: dev_roundof(unsigned newrate)
1.1 ratchov 100: {
1.20 ratchov 101: return (dev_round * newrate + dev_rate / 2) / dev_rate;
1.1 ratchov 102: }
103:
104: /*
105: * open the device with the given hardware parameters and create a mixer
106: * and a multiplexer connected to it with all necessary conversions
107: * setup
108: */
1.26 ! ratchov 109: int
1.3 ratchov 110: dev_init(char *devpath,
1.20 ratchov 111: struct aparams *dipar, struct aparams *dopar, unsigned bufsz)
1.1 ratchov 112: {
1.23 ratchov 113: struct file *f;
1.1 ratchov 114: struct aparams ipar, opar;
115: struct aproc *conv;
116: struct abuf *buf;
1.3 ratchov 117: unsigned nfr, ibufsz, obufsz;
1.25 ratchov 118:
1.3 ratchov 119: /*
1.11 ratchov 120: * ask for 1/4 of the buffer for the kernel ring and
121: * limit the block size to 1/4 of the requested buffer
1.3 ratchov 122: */
123: dev_bufsz = (bufsz + 3) / 4;
1.11 ratchov 124: dev_round = (bufsz + 3) / 4;
1.23 ratchov 125: f = (struct file *)safile_new(&safile_ops, devpath,
1.20 ratchov 126: dipar, dopar, &dev_bufsz, &dev_round);
1.23 ratchov 127: if (f == NULL)
1.26 ! ratchov 128: return 0;
1.3 ratchov 129: if (dipar) {
1.19 ratchov 130: #ifdef DEBUG
1.3 ratchov 131: if (debug_level > 0) {
1.9 ratchov 132: fprintf(stderr, "dev_init: hw recording ");
1.3 ratchov 133: aparams_print(dipar);
1.9 ratchov 134: fprintf(stderr, "\n");
1.3 ratchov 135: }
1.19 ratchov 136: #endif
1.20 ratchov 137: dev_rate = dipar->rate;
1.3 ratchov 138: }
139: if (dopar) {
1.19 ratchov 140: #ifdef DEBUG
1.3 ratchov 141: if (debug_level > 0) {
1.9 ratchov 142: fprintf(stderr, "dev_init: hw playing ");
1.3 ratchov 143: aparams_print(dopar);
1.9 ratchov 144: fprintf(stderr, "\n");
1.3 ratchov 145: }
1.19 ratchov 146: #endif
1.20 ratchov 147: dev_rate = dopar->rate;
1.3 ratchov 148: }
1.11 ratchov 149: ibufsz = obufsz = dev_bufsz;
150: bufsz = (bufsz > dev_bufsz) ? bufsz - dev_bufsz : 0;
1.1 ratchov 151:
152: /*
1.11 ratchov 153: * use 1/8 of the buffer for the mixer/converters. Since we
154: * already consumed 1/4 for the device, bufsz represents the
155: * remaining 3/4. So 1/8 is 1/6 of 3/4
156: */
157: nfr = (bufsz + 5) / 6;
158: nfr += dev_round - 1;
159: nfr -= nfr % dev_round;
160: if (nfr == 0)
161: nfr = dev_round;
162:
163: /*
164: * create record chain
1.1 ratchov 165: */
166: if (dipar) {
167: aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate);
168: /*
169: * create the read end
170: */
1.23 ratchov 171: dev_rec = rpipe_new(f);
1.22 ratchov 172: dev_rec->refs++;
1.4 ratchov 173: buf = abuf_new(nfr, dipar);
1.1 ratchov 174: aproc_setout(dev_rec, buf);
1.3 ratchov 175: ibufsz += nfr;
1.1 ratchov 176:
177: /*
178: * append a converter, if needed
179: */
1.4 ratchov 180: if (!aparams_eqenc(dipar, &ipar)) {
1.9 ratchov 181: conv = dec_new("subin", dipar);
1.1 ratchov 182: aproc_setin(conv, buf);
1.4 ratchov 183: buf = abuf_new(nfr, &ipar);
1.1 ratchov 184: aproc_setout(conv, buf);
1.3 ratchov 185: ibufsz += nfr;
1.1 ratchov 186: }
187: dev_ipar = ipar;
188:
189: /*
190: * append a "sub" to which clients will connect
191: */
1.3 ratchov 192: dev_sub = sub_new("sub", nfr);
1.22 ratchov 193: dev_sub->refs++;
1.1 ratchov 194: aproc_setin(dev_sub, buf);
195: } else {
196: dev_rec = NULL;
197: dev_sub = NULL;
198: }
199:
200: /*
201: * create play chain
202: */
203: if (dopar) {
204: aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate);
205: /*
206: * create the write end
207: */
1.23 ratchov 208: dev_play = wpipe_new(f);
1.22 ratchov 209: dev_play->refs++;
1.4 ratchov 210: buf = abuf_new(nfr, dopar);
1.1 ratchov 211: aproc_setin(dev_play, buf);
1.3 ratchov 212: obufsz += nfr;
1.25 ratchov 213:
1.1 ratchov 214: /*
215: * append a converter, if needed
216: */
1.4 ratchov 217: if (!aparams_eqenc(&opar, dopar)) {
1.9 ratchov 218: conv = enc_new("mixout", dopar);
1.1 ratchov 219: aproc_setout(conv, buf);
1.4 ratchov 220: buf = abuf_new(nfr, &opar);
1.1 ratchov 221: aproc_setin(conv, buf);
1.3 ratchov 222: obufsz += nfr;
1.1 ratchov 223: }
224: dev_opar = opar;
225:
226: /*
227: * append a "mix" to which clients will connect
228: */
1.3 ratchov 229: dev_mix = mix_new("mix", nfr);
1.22 ratchov 230: dev_mix->refs++;
1.1 ratchov 231: aproc_setout(dev_mix, buf);
232: } else {
233: dev_play = NULL;
234: dev_mix = NULL;
235: }
1.3 ratchov 236: dev_bufsz = (dopar) ? obufsz : ibufsz;
237: DPRINTF("dev_init: using %u fpb\n", dev_bufsz);
238: dev_start();
1.26 ! ratchov 239: return 1;
1.1 ratchov 240: }
241:
242: /*
243: * cleanly stop and drain everything and close the device
244: * once both play chain and record chain are gone
245: */
246: void
247: dev_done(void)
248: {
249: struct file *f;
250:
1.15 ratchov 251: DPRINTF("dev_done: dev_mix = %p, dev_sub = %p\n", dev_mix, dev_sub);
1.3 ratchov 252: if (dev_mix) {
1.22 ratchov 253: dev_mix->refs--;
1.17 ratchov 254: dev_mix->u.mix.flags |= MIX_AUTOQUIT;
1.22 ratchov 255: dev_mix = NULL;
1.3 ratchov 256: /*
257: * generate EOF on all inputs (but not the device), and
258: * put the mixer in ``autoquit'' state, so once buffers
259: * are drained the mixer will terminate and shutdown the
260: * write-end of the device
261: *
262: * NOTE: since file_eof() can destroy the file and
263: * reorder the file_list, we have to restart the loop
264: * after each call to file_eof()
265: */
266: restart:
267: LIST_FOREACH(f, &file_list, entry) {
1.23 ratchov 268: if (f->rproc != NULL && f->rproc != dev_rec) {
1.3 ratchov 269: file_eof(f);
270: goto restart;
271: }
272: }
273:
274: /*
275: * wait play chain to terminate
276: */
1.23 ratchov 277: if (dev_play) {
278: while (!LIST_EMPTY(&dev_play->ibuflist)) {
279: if (!file_poll())
280: break;
281: }
282: dev_play->refs--;
283: aproc_del(dev_play);
284: dev_play = NULL;
1.3 ratchov 285: }
1.1 ratchov 286: }
1.3 ratchov 287: if (dev_sub) {
1.22 ratchov 288: dev_sub->refs--;
1.17 ratchov 289: dev_sub->u.sub.flags |= SUB_AUTOQUIT;
1.22 ratchov 290: dev_sub = NULL;
1.3 ratchov 291: /*
292: * same as above, but for the record chain: generate eof
293: * on the read-end of the device and wait record buffers
294: * to desappear. We must stop the device first, because
295: * play-end will underrun (and xrun correction code will
296: * insert silence on the record-end of the device)
297: */
1.23 ratchov 298: if (dev_rec) {
299: dev_stop();
300: if (dev_rec->u.io.file)
301: file_eof(dev_rec->u.io.file);
302: while (!LIST_EMPTY(&dev_rec->obuflist)) {
303: if (!file_poll())
304: break;
305: }
306: dev_rec->refs--;
307: aproc_del(dev_rec);
308: dev_rec = NULL;
1.3 ratchov 309: }
1.1 ratchov 310: }
311: }
312:
313: /*
314: * start the (paused) device. By default it's paused
315: */
316: void
317: dev_start(void)
318: {
1.22 ratchov 319: struct file *f;
320:
1.1 ratchov 321: if (dev_mix)
322: dev_mix->u.mix.flags |= MIX_DROP;
323: if (dev_sub)
324: dev_sub->u.sub.flags |= SUB_DROP;
1.22 ratchov 325: if (dev_play && dev_play->u.io.file) {
326: f = dev_play->u.io.file;
327: f->ops->start(f);
328: } else if (dev_rec && dev_rec->u.io.file) {
329: f = dev_rec->u.io.file;
330: f->ops->start(f);
331: }
1.1 ratchov 332: }
333:
334: /*
335: * pause the device
336: */
337: void
338: dev_stop(void)
339: {
1.22 ratchov 340: struct file *f;
341:
342: if (dev_play && dev_play->u.io.file) {
343: f = dev_play->u.io.file;
344: f->ops->stop(f);
345: } else if (dev_rec && dev_rec->u.io.file) {
346: f = dev_rec->u.io.file;
347: f->ops->stop(f);
348: }
1.1 ratchov 349: if (dev_mix)
350: dev_mix->u.mix.flags &= ~MIX_DROP;
351: if (dev_sub)
352: dev_sub->u.sub.flags &= ~SUB_DROP;
353: }
354:
355: /*
1.14 ratchov 356: * find the end points connected to the mix/sub
357: */
358: int
359: dev_getep(struct abuf **sibuf, struct abuf **sobuf)
360: {
361: struct abuf *ibuf, *obuf;
362:
363: if (sibuf) {
364: ibuf = *sibuf;
365: for (;;) {
366: if (!ibuf || !ibuf->rproc) {
367: DPRINTF("dev_getep: reader desappeared\n");
368: return 0;
369: }
370: if (ibuf->rproc == dev_mix)
371: break;
372: ibuf = LIST_FIRST(&ibuf->rproc->obuflist);
373: }
1.21 ratchov 374: *sibuf = ibuf;
1.14 ratchov 375: }
376: if (sobuf) {
377: obuf = *sobuf;
378: for (;;) {
379: if (!obuf || !obuf->wproc) {
380: DPRINTF("dev_getep: writer desappeared\n");
381: return 0;
382: }
383: if (obuf->wproc == dev_sub)
384: break;
385: obuf = LIST_FIRST(&obuf->wproc->ibuflist);
386: }
1.21 ratchov 387: *sobuf = obuf;
1.14 ratchov 388: }
389: return 1;
390: }
391:
392: /*
1.3 ratchov 393: * sync play buffer to rec buffer (for instance when one of
394: * them underruns/overruns)
1.1 ratchov 395: */
396: void
1.3 ratchov 397: dev_sync(struct abuf *ibuf, struct abuf *obuf)
1.1 ratchov 398: {
1.3 ratchov 399: struct abuf *pbuf, *rbuf;
400: int delta;
401:
402: if (!dev_mix || !dev_sub)
403: return;
404: pbuf = LIST_FIRST(&dev_mix->obuflist);
405: if (!pbuf)
406: return;
407: rbuf = LIST_FIRST(&dev_sub->ibuflist);
408: if (!rbuf)
409: return;
1.14 ratchov 410: if (!dev_getep(&ibuf, &obuf))
411: return;
1.3 ratchov 412:
413: /*
414: * calculate delta, the number of frames the play chain is ahead
415: * of the record chain. It's necessary to schedule silences (or
416: * drops) in order to start playback and record in sync.
417: */
1.25 ratchov 418: delta =
419: rbuf->bpf * (pbuf->abspos + pbuf->used) -
1.3 ratchov 420: pbuf->bpf * rbuf->abspos;
421: delta /= pbuf->bpf * rbuf->bpf;
422: DPRINTF("dev_sync: delta = %d, ppos = %u, pused = %u, rpos = %u\n",
423: delta, pbuf->abspos, pbuf->used, rbuf->abspos);
424:
425: if (delta > 0) {
426: /*
427: * if the play chain is ahead (most cases) drop some of
428: * the recorded input, to get both in sync
429: */
430: obuf->drop += delta * obuf->bpf;
431: abuf_ipos(obuf, -delta);
432: } else if (delta < 0) {
433: /*
434: * if record chain is ahead (should never happen,
435: * right?) then insert silence to play
436: */
437: ibuf->silence += -delta * ibuf->bpf;
438: abuf_opos(ibuf, delta);
439: } else
440: DPRINTF("dev_sync: nothing to do\n");
1.1 ratchov 441: }
442:
443: /*
444: * attach the given input and output buffers to the mixer and the
445: * multiplexer respectively. The operation is done synchronously, so
446: * both buffers enter in sync. If buffers do not match play
447: * and rec
448: */
449: void
1.25 ratchov 450: dev_attach(char *name,
451: struct abuf *ibuf, struct aparams *sipar, unsigned underrun,
1.18 ratchov 452: struct abuf *obuf, struct aparams *sopar, unsigned overrun, int vol)
1.1 ratchov 453: {
454: struct abuf *pbuf = NULL, *rbuf = NULL;
1.12 ratchov 455: struct aparams ipar, opar;
1.1 ratchov 456: struct aproc *conv;
1.20 ratchov 457: unsigned round, nblk;
458:
1.1 ratchov 459: if (ibuf) {
1.12 ratchov 460: ipar = *sipar;
1.20 ratchov 461: pbuf = LIST_FIRST(&dev_mix->obuflist);
462: nblk = (dev_bufsz / dev_round + 3) / 4;
463: round = dev_roundof(ipar.rate);
1.10 ratchov 464: if (!aparams_eqenc(&ipar, &dev_opar)) {
465: conv = dec_new(name, &ipar);
466: ipar.bps = dev_opar.bps;
467: ipar.bits = dev_opar.bits;
468: ipar.sig = dev_opar.sig;
469: ipar.le = dev_opar.le;
470: ipar.msb = dev_opar.msb;
1.25 ratchov 471: aproc_setin(conv, ibuf);
1.20 ratchov 472: ibuf = abuf_new(nblk * round, &ipar);
1.9 ratchov 473: aproc_setout(conv, ibuf);
1.8 ratchov 474: }
1.10 ratchov 475: if (!aparams_subset(&ipar, &dev_opar)) {
476: conv = cmap_new(name, &ipar, &dev_opar);
477: ipar.cmin = dev_opar.cmin;
478: ipar.cmax = dev_opar.cmax;
1.7 ratchov 479: aproc_setin(conv, ibuf);
1.20 ratchov 480: ibuf = abuf_new(nblk * round, &ipar);
1.7 ratchov 481: aproc_setout(conv, ibuf);
1.6 ratchov 482: }
1.10 ratchov 483: if (!aparams_eqrate(&ipar, &dev_opar)) {
1.20 ratchov 484: conv = resamp_new(name, round, dev_round);
1.10 ratchov 485: ipar.rate = dev_opar.rate;
1.20 ratchov 486: round = dev_round;
1.5 ratchov 487: aproc_setin(conv, ibuf);
1.20 ratchov 488: ibuf = abuf_new(nblk * round, &ipar);
1.5 ratchov 489: aproc_setout(conv, ibuf);
1.1 ratchov 490: }
491: aproc_setin(dev_mix, ibuf);
1.3 ratchov 492: abuf_opos(ibuf, -dev_mix->u.mix.lat);
1.1 ratchov 493: ibuf->xrun = underrun;
1.18 ratchov 494: ibuf->mixmaxweight = vol;
495: mix_setmaster(dev_mix);
1.1 ratchov 496: }
497: if (obuf) {
1.12 ratchov 498: opar = *sopar;
1.1 ratchov 499: rbuf = LIST_FIRST(&dev_sub->ibuflist);
1.20 ratchov 500: round = dev_roundof(opar.rate);
501: nblk = (dev_bufsz / dev_round + 3) / 4;
1.10 ratchov 502: if (!aparams_eqenc(&opar, &dev_ipar)) {
503: conv = enc_new(name, &opar);
504: opar.bps = dev_ipar.bps;
505: opar.bits = dev_ipar.bits;
506: opar.sig = dev_ipar.sig;
507: opar.le = dev_ipar.le;
508: opar.msb = dev_ipar.msb;
1.9 ratchov 509: aproc_setout(conv, obuf);
1.20 ratchov 510: obuf = abuf_new(nblk * round, &opar);
1.9 ratchov 511: aproc_setin(conv, obuf);
1.8 ratchov 512: }
1.10 ratchov 513: if (!aparams_subset(&opar, &dev_ipar)) {
514: conv = cmap_new(name, &dev_ipar, &opar);
515: opar.cmin = dev_ipar.cmin;
516: opar.cmax = dev_ipar.cmax;
1.7 ratchov 517: aproc_setout(conv, obuf);
1.20 ratchov 518: obuf = abuf_new(nblk * round, &opar);
1.7 ratchov 519: aproc_setin(conv, obuf);
1.6 ratchov 520: }
1.10 ratchov 521: if (!aparams_eqrate(&opar, &dev_ipar)) {
1.20 ratchov 522: conv = resamp_new(name, dev_round, round);
1.10 ratchov 523: opar.rate = dev_ipar.rate;
1.20 ratchov 524: round = dev_round;
1.1 ratchov 525: aproc_setout(conv, obuf);
1.20 ratchov 526: obuf = abuf_new(nblk * round, &opar);
1.1 ratchov 527: aproc_setin(conv, obuf);
528: }
529: aproc_setout(dev_sub, obuf);
1.3 ratchov 530: abuf_ipos(obuf, -dev_sub->u.sub.lat);
1.1 ratchov 531: obuf->xrun = overrun;
532: }
533:
534: /*
1.3 ratchov 535: * sync play to record
1.1 ratchov 536: */
537: if (ibuf && obuf) {
1.3 ratchov 538: ibuf->duplex = obuf;
539: obuf->duplex = ibuf;
540: dev_sync(ibuf, obuf);
1.13 ratchov 541: }
1.14 ratchov 542: }
543:
544: /*
545: * change the playback volume of the fiven stream
546: */
547: void
548: dev_setvol(struct abuf *ibuf, int vol)
549: {
1.21 ratchov 550: DPRINTF("dev_setvol: %p\n", ibuf);
1.16 ratchov 551: if (!dev_getep(&ibuf, NULL)) {
552: DPRINTF("dev_setvol: not connected yet\n");
1.14 ratchov 553: return;
1.16 ratchov 554: }
1.14 ratchov 555: ibuf->mixvol = vol;
1.21 ratchov 556: DPRINTF("dev_setvol: %p -> %d\n", ibuf, vol);
1.13 ratchov 557: }
558:
559: /*
560: * clear buffers of the play and record chains so that when the device
561: * is started, playback and record start in sync
562: */
563: void
564: dev_clear(void)
565: {
566: struct abuf *buf;
567:
568: if (dev_mix) {
1.25 ratchov 569: if (!LIST_EMPTY(&dev_mix->ibuflist)) {
1.13 ratchov 570: fprintf(stderr, "dev_clear: mixer not idle\n");
571: abort();
572: }
573: buf = LIST_FIRST(&dev_mix->obuflist);
574: while (buf) {
575: abuf_clear(buf);
576: buf = LIST_FIRST(&buf->rproc->obuflist);
577: }
578: mix_clear(dev_mix);
579: }
580: if (dev_sub) {
1.25 ratchov 581: if (!LIST_EMPTY(&dev_sub->obuflist)) {
1.13 ratchov 582: fprintf(stderr, "dev_suspend: demux not idle\n");
583: abort();
584: }
585: buf = LIST_FIRST(&dev_sub->ibuflist);
586: while (buf) {
587: abuf_clear(buf);
588: buf = LIST_FIRST(&buf->wproc->ibuflist);
589: }
590: sub_clear(dev_sub);
1.1 ratchov 591: }
592: }