Annotation of src/usr.bin/aucat/dev.c, Revision 1.9
1.9 ! ratchov 1: /* $OpenBSD: dev.c,v 1.8 2008/11/04 18:24:06 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) {
1.9 ! ratchov 117: fprintf(stderr, "dev_init: hw recording ");
1.3 ratchov 118: aparams_print(dipar);
1.9 ! ratchov 119: fprintf(stderr, "\n");
1.3 ratchov 120: }
121: }
122: if (dopar) {
123: dopar->rate = dev_rate;
124: if (debug_level > 0) {
1.9 ! ratchov 125: fprintf(stderr, "dev_init: hw playing ");
1.3 ratchov 126: aparams_print(dopar);
1.9 ! ratchov 127: fprintf(stderr, "\n");
1.3 ratchov 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.9 ! ratchov 149: conv = dec_new("subin", dipar);
1.1 ratchov 150: aproc_setin(conv, buf);
1.4 ratchov 151: buf = abuf_new(nfr, &ipar);
1.1 ratchov 152: aproc_setout(conv, buf);
1.3 ratchov 153: ibufsz += nfr;
1.1 ratchov 154: }
155: dev_ipar = ipar;
156:
157: /*
158: * append a "sub" to which clients will connect
159: */
1.3 ratchov 160: dev_sub = sub_new("sub", nfr);
1.1 ratchov 161: aproc_setin(dev_sub, buf);
162: } else {
163: dev_rec = NULL;
164: dev_sub = NULL;
165: }
166:
167: /*
168: * create play chain
169: */
170: if (dopar) {
171: aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate);
172: /*
173: * create the write end
174: */
175: dev_play = wpipe_new(dev_file);
1.4 ratchov 176: buf = abuf_new(nfr, dopar);
1.1 ratchov 177: aproc_setin(dev_play, buf);
1.3 ratchov 178: obufsz += nfr;
179:
1.1 ratchov 180: /*
181: * append a converter, if needed
182: */
1.4 ratchov 183: if (!aparams_eqenc(&opar, dopar)) {
1.9 ! ratchov 184: conv = enc_new("mixout", dopar);
1.1 ratchov 185: aproc_setout(conv, buf);
1.4 ratchov 186: buf = abuf_new(nfr, &opar);
1.1 ratchov 187: aproc_setin(conv, buf);
1.3 ratchov 188: obufsz += nfr;
1.1 ratchov 189: }
190: dev_opar = opar;
191:
192: /*
193: * append a "mix" to which clients will connect
194: */
1.3 ratchov 195: dev_mix = mix_new("mix", nfr);
1.1 ratchov 196: aproc_setout(dev_mix, buf);
197: } else {
198: dev_play = NULL;
199: dev_mix = NULL;
200: }
1.3 ratchov 201: dev_bufsz = (dopar) ? obufsz : ibufsz;
202: DPRINTF("dev_init: using %u fpb\n", dev_bufsz);
203: dev_start();
1.1 ratchov 204: }
205:
206: /*
207: * cleanly stop and drain everything and close the device
208: * once both play chain and record chain are gone
209: */
210: void
211: dev_done(void)
212: {
213: struct file *f;
214:
1.3 ratchov 215: if (dev_mix) {
216: /*
217: * generate EOF on all inputs (but not the device), and
218: * put the mixer in ``autoquit'' state, so once buffers
219: * are drained the mixer will terminate and shutdown the
220: * write-end of the device
221: *
222: * NOTE: since file_eof() can destroy the file and
223: * reorder the file_list, we have to restart the loop
224: * after each call to file_eof()
225: */
226: restart:
227: LIST_FOREACH(f, &file_list, entry) {
228: if (f != dev_file && f->rproc) {
229: file_eof(f);
230: goto restart;
231: }
232: }
233: if (dev_mix)
234: dev_mix->u.mix.flags |= MIX_AUTOQUIT;
235:
236: /*
237: * wait play chain to terminate
238: */
239: while (dev_file->wproc != NULL) {
240: if (!file_poll())
241: break;
242: }
243: dev_mix = 0;
1.1 ratchov 244: }
1.3 ratchov 245: if (dev_sub) {
246: /*
247: * same as above, but for the record chain: generate eof
248: * on the read-end of the device and wait record buffers
249: * to desappear. We must stop the device first, because
250: * play-end will underrun (and xrun correction code will
251: * insert silence on the record-end of the device)
252: */
253: dev_stop();
254: file_eof(dev_file);
255: if (dev_sub)
256: dev_sub->u.sub.flags |= SUB_AUTOQUIT;
257: for (;;) {
258: if (!file_poll())
259: break;
260: }
261: dev_sub = NULL;
1.1 ratchov 262: }
263: }
264:
265: /*
266: * start the (paused) device. By default it's paused
267: */
268: void
269: dev_start(void)
270: {
271: if (dev_mix)
272: dev_mix->u.mix.flags |= MIX_DROP;
273: if (dev_sub)
274: dev_sub->u.sub.flags |= SUB_DROP;
1.3 ratchov 275: dev_file->ops->start(dev_file);
1.1 ratchov 276: }
277:
278: /*
279: * pause the device
280: */
281: void
282: dev_stop(void)
283: {
1.3 ratchov 284: dev_file->ops->stop(dev_file);
1.1 ratchov 285: if (dev_mix)
286: dev_mix->u.mix.flags &= ~MIX_DROP;
287: if (dev_sub)
288: dev_sub->u.sub.flags &= ~SUB_DROP;
289: }
290:
291: /*
1.3 ratchov 292: * sync play buffer to rec buffer (for instance when one of
293: * them underruns/overruns)
1.1 ratchov 294: */
295: void
1.3 ratchov 296: dev_sync(struct abuf *ibuf, struct abuf *obuf)
1.1 ratchov 297: {
1.3 ratchov 298: struct abuf *pbuf, *rbuf;
299: int delta;
300:
301: if (!dev_mix || !dev_sub)
302: return;
303: pbuf = LIST_FIRST(&dev_mix->obuflist);
304: if (!pbuf)
305: return;
306: rbuf = LIST_FIRST(&dev_sub->ibuflist);
307: if (!rbuf)
308: return;
309: for (;;) {
310: if (!ibuf || !ibuf->rproc) {
311: DPRINTF("dev_sync: reader desappeared\n");
312: return;
313: }
314: if (ibuf->rproc == dev_mix)
1.1 ratchov 315: break;
1.3 ratchov 316: ibuf = LIST_FIRST(&ibuf->rproc->obuflist);
317: }
318: for (;;) {
319: if (!obuf || !obuf->wproc) {
320: DPRINTF("dev_sync: writer desappeared\n");
321: return;
322: }
323: if (obuf->wproc == dev_sub)
1.1 ratchov 324: break;
1.3 ratchov 325: obuf = LIST_FIRST(&obuf->wproc->ibuflist);
1.1 ratchov 326: }
1.3 ratchov 327:
328: /*
329: * calculate delta, the number of frames the play chain is ahead
330: * of the record chain. It's necessary to schedule silences (or
331: * drops) in order to start playback and record in sync.
332: */
333: delta =
334: rbuf->bpf * (pbuf->abspos + pbuf->used) -
335: pbuf->bpf * rbuf->abspos;
336: delta /= pbuf->bpf * rbuf->bpf;
337: DPRINTF("dev_sync: delta = %d, ppos = %u, pused = %u, rpos = %u\n",
338: delta, pbuf->abspos, pbuf->used, rbuf->abspos);
339:
340: if (delta > 0) {
341: /*
342: * if the play chain is ahead (most cases) drop some of
343: * the recorded input, to get both in sync
344: */
345: obuf->drop += delta * obuf->bpf;
346: abuf_ipos(obuf, -delta);
347: } else if (delta < 0) {
348: /*
349: * if record chain is ahead (should never happen,
350: * right?) then insert silence to play
351: */
352: ibuf->silence += -delta * ibuf->bpf;
353: abuf_opos(ibuf, delta);
354: } else
355: DPRINTF("dev_sync: nothing to do\n");
1.1 ratchov 356: }
357:
358: /*
359: * attach the given input and output buffers to the mixer and the
360: * multiplexer respectively. The operation is done synchronously, so
361: * both buffers enter in sync. If buffers do not match play
362: * and rec
363: */
364: void
365: dev_attach(char *name,
366: struct abuf *ibuf, struct aparams *ipar, unsigned underrun,
367: struct abuf *obuf, struct aparams *opar, unsigned overrun)
368: {
369: struct abuf *pbuf = NULL, *rbuf = NULL;
370: struct aproc *conv;
1.3 ratchov 371: unsigned nfr;
1.1 ratchov 372:
373: if (ibuf) {
374: pbuf = LIST_FIRST(&dev_mix->obuflist);
1.7 ratchov 375: if (!aparams_eqenc(ipar, &dev_opar)) {
1.3 ratchov 376: nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
377: nfr -= nfr % dev_round;
1.9 ! ratchov 378: conv = dec_new(name, ipar);
1.7 ratchov 379: ipar->bps = dev_opar.bps;
380: ipar->bits = dev_opar.bits;
381: ipar->sig = dev_opar.sig;
382: ipar->le = dev_opar.le;
383: ipar->msb = dev_opar.msb;
1.9 ! ratchov 384: aproc_setin(conv, ibuf);
! 385: ibuf = abuf_new(nfr, ipar);
! 386: aproc_setout(conv, ibuf);
1.8 ratchov 387: }
388: if (!aparams_subset(ipar, &dev_opar)) {
1.7 ratchov 389: nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
390: nfr -= nfr % dev_round;
391: conv = cmap_new(name, ipar, &dev_opar);
1.9 ! ratchov 392: ipar->cmin = dev_opar.cmin;
! 393: ipar->cmax = dev_opar.cmax;
1.7 ratchov 394: aproc_setin(conv, ibuf);
1.9 ! ratchov 395: ibuf = abuf_new(nfr, ipar);
1.7 ratchov 396: aproc_setout(conv, ibuf);
1.6 ratchov 397: }
398: if (!aparams_eqrate(ipar, &dev_opar)) {
1.5 ratchov 399: nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
400: nfr -= nfr % dev_round;
401: conv = resamp_new(name, ipar, &dev_opar);
1.9 ! ratchov 402: ipar->rate = dev_opar.rate;
1.5 ratchov 403: aproc_setin(conv, ibuf);
1.9 ! ratchov 404: ibuf = abuf_new(nfr, ipar);
1.5 ratchov 405: aproc_setout(conv, ibuf);
1.1 ratchov 406: }
407: aproc_setin(dev_mix, ibuf);
1.3 ratchov 408: abuf_opos(ibuf, -dev_mix->u.mix.lat);
1.1 ratchov 409: ibuf->xrun = underrun;
410: }
411: if (obuf) {
412: rbuf = LIST_FIRST(&dev_sub->ibuflist);
1.7 ratchov 413: if (!aparams_eqenc(opar, &dev_ipar)) {
1.3 ratchov 414: nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
415: nfr -= nfr % dev_round;
1.9 ! ratchov 416: conv = enc_new(name, opar);
1.7 ratchov 417: opar->bps = dev_ipar.bps;
418: opar->bits = dev_ipar.bits;
419: opar->sig = dev_ipar.sig;
420: opar->le = dev_ipar.le;
421: opar->msb = dev_ipar.msb;
1.9 ! ratchov 422: aproc_setout(conv, obuf);
! 423: obuf = abuf_new(nfr, opar);
! 424: aproc_setin(conv, obuf);
1.8 ratchov 425: }
426: if (!aparams_subset(opar, &dev_ipar)) {
1.7 ratchov 427: nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
428: nfr -= nfr % dev_round;
429: conv = cmap_new(name, &dev_ipar, opar);
1.9 ! ratchov 430: opar->cmin = dev_ipar.cmin;
! 431: opar->cmax = dev_ipar.cmax;
1.7 ratchov 432: aproc_setout(conv, obuf);
1.9 ! ratchov 433: obuf = abuf_new(nfr, opar);
1.7 ratchov 434: aproc_setin(conv, obuf);
1.6 ratchov 435: }
436: if (!aparams_eqrate(opar, &dev_ipar)) {
1.5 ratchov 437: nfr = (dev_bufsz + 3) / 4 + dev_round - 1;
438: nfr -= nfr % dev_round;
439: conv = resamp_new(name, &dev_ipar, opar);
1.9 ! ratchov 440: opar->rate = dev_ipar.rate;
1.1 ratchov 441: aproc_setout(conv, obuf);
1.9 ! ratchov 442: obuf = abuf_new(nfr, opar);
1.1 ratchov 443: aproc_setin(conv, obuf);
444: }
445: aproc_setout(dev_sub, obuf);
1.3 ratchov 446: abuf_ipos(obuf, -dev_sub->u.sub.lat);
1.1 ratchov 447: obuf->xrun = overrun;
448: }
449:
450: /*
1.3 ratchov 451: * sync play to record
1.1 ratchov 452: */
453: if (ibuf && obuf) {
1.3 ratchov 454: ibuf->duplex = obuf;
455: obuf->duplex = ibuf;
456: dev_sync(ibuf, obuf);
1.1 ratchov 457: }
458: }