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