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