Annotation of src/usr.bin/aucat/dev.c, Revision 1.2
1.2 ! ratchov 1: /* $OpenBSD: dev.c,v 1.1 2008/08/14 09:58:55 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: #include <signal.h>
21: #include <err.h>
22:
23: #include "dev.h"
24: #include "abuf.h"
25: #include "aproc.h"
26: #include "file.h"
27: #include "conf.h"
28:
29: int quit_flag, pause_flag;
30: unsigned dev_infr, dev_onfr;
31: struct aparams dev_ipar, dev_opar;
32: struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play;
33: struct file *dev_file;
34: struct devops *devops = &devops_sun;
35:
36: /*
37: * SIGINT handler, it raises the quit flag. If the flag is already set,
38: * that means that the last SIGINT was not handled, because the process
39: * is blocked somewhere, so exit
40: */
41: void
42: sigint(int s)
43: {
44: if (quit_flag)
45: _exit(1);
46: quit_flag = 1;
47: }
48:
49: /*
50: * called when the user hits ctrl-z
51: */
52: void
53: sigtstp(int s)
54: {
55: pause_flag = 1;
56: }
57:
58: /*
59: * SIGCONT is send when resumed after SIGTSTP or SIGSTOP. If the pause
60: * flag is not set, that means that the process was not suspended by
61: * dev_suspend(), which means that we lost the sync; since we cannot
62: * resync, just exit
63: */
64: void
65: sigcont(int s)
66: {
67: static char msg[] = "can't resume afer SIGSTOP, terminating...\n";
68:
69: if (!pause_flag) {
70: write(STDERR_FILENO, msg, sizeof(msg) - 1);
71: _exit(1);
72: }
73: }
74:
75: /*
76: * suicide with SIGTSTP (tty stop) as if the user had hit ctrl-z
77: */
78: void
79: dev_suspend(void)
80: {
81: struct sigaction sa;
82:
83: sigfillset(&sa.sa_mask);
84: sa.sa_flags = SA_RESTART;
85: sa.sa_handler = SIG_DFL;
86: if (sigaction(SIGTSTP, &sa, NULL) < 0)
87: err(1, "sigaction");
88: DPRINTF("suspended by tty\n");
89: kill(getpid(), SIGTSTP);
90: pause_flag = 0;
91: sa.sa_handler = sigtstp;
92: if (sigaction(SIGTSTP, &sa, NULL) < 0)
93: err(1, "sigaction");
94: DPRINTF("resumed after suspend\n");
95: }
96:
97: /*
98: * fill playback buffer, so when device is started there
99: * are samples to play
100: */
101: void
102: dev_fill(void)
103: {
104: struct abuf *buf;
105:
106:
107: /*
108: * if there are no inputs, zero fill the mixer
109: */
110: if (dev_mix && LIST_EMPTY(&dev_mix->ibuflist))
111: mix_pushzero(dev_mix);
112: DPRINTF("filling play buffers...\n");
113: for (;;) {
114: if (!dev_file->wproc) {
115: DPRINTF("fill: no writer\n");
116: break;
117: }
118: if (dev_file->events & POLLOUT) {
119: /*
120: * kernel buffers are full, but continue
121: * until the play buffer is full too.
122: */
123: buf = LIST_FIRST(&dev_file->wproc->ibuflist);
124: if (!ABUF_WOK(buf))
125: break; /* buffer full */
126: if (!buf->wproc)
127: break; /* will never be filled */
128: }
129: if (!file_poll())
130: break;
131: if (pause_flag)
132: dev_suspend();
133: }
134: }
135:
136: /*
137: * flush recorded samples once the device is stopped so
138: * they aren't lost
139: */
140: void
141: dev_flush(void)
142: {
143: struct abuf *buf;
144:
145: DPRINTF("flushing record buffers...\n");
146: for (;;) {
147: if (!dev_file->rproc) {
148: DPRINTF("flush: no more reader\n");
149: break;
150: }
151: if (dev_file->events & POLLIN) {
152: /*
153: * we drained kernel buffers, but continue
154: * until the record buffer is empty.
155: */
156: buf = LIST_FIRST(&dev_file->rproc->obuflist);
157: if (!ABUF_ROK(buf))
158: break; /* buffer empty */
159: if (!buf->rproc)
160: break; /* will never be drained */
161: }
162: if (!file_poll())
163: break;
164: if (pause_flag)
165: dev_suspend();
166: }
167: }
168:
169:
170: /*
171: * open the device with the given hardware parameters and create a mixer
172: * and a multiplexer connected to it with all necessary conversions
173: * setup
174: */
175: void
176: dev_init(char *devpath, struct aparams *dipar, struct aparams *dopar)
177: {
178: int fd;
179: struct sigaction sa;
180: unsigned infr, onfr;
181: struct aparams ipar, opar;
182: struct aproc *conv;
183: struct abuf *buf;
184:
185: quit_flag = 0;
186: pause_flag = 0;
187:
188: sigfillset(&sa.sa_mask);
189: sa.sa_flags = SA_RESTART;
190: sa.sa_handler = sigint;
191: if (sigaction(SIGINT, &sa, NULL) < 0)
192: err(1, "sigaction");
193: sa.sa_handler = sigtstp;
194: if (sigaction(SIGTSTP, &sa, NULL) < 0)
195: err(1, "sigaction");
196: sa.sa_handler = sigcont;
197: if (sigaction(SIGCONT, &sa, NULL) < 0)
198: err(1, "sigaction");
199:
200: fd = devops->open(devpath, dipar, dopar, &infr, &onfr);
201: if (fd < 0)
202: exit(1);
203: dev_file = file_new(fd, devpath);
204:
205: /*
206: * create record chain
207: */
208: if (dipar) {
209: aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate);
210: infr *= DEFAULT_NBLK;
211:
212: /*
213: * create the read end
214: */
215: dev_rec = rpipe_new(dev_file);
216: buf = abuf_new(infr, aparams_bpf(dipar));
217: aproc_setout(dev_rec, buf);
218:
219: /*
220: * append a converter, if needed
221: */
222: if (!aparams_eq(dipar, &ipar)) {
223: if (debug_level > 0) {
224: fprintf(stderr, "%s: ", devpath);
225: aparams_print2(dipar, &ipar);
226: fprintf(stderr, "\n");
227: }
228: conv = conv_new("subconv", dipar, &ipar);
229: aproc_setin(conv, buf);
230: buf = abuf_new(infr, aparams_bpf(&ipar));
231: aproc_setout(conv, buf);
232: }
233: dev_ipar = ipar;
234: dev_infr = infr;
235:
236: /*
237: * append a "sub" to which clients will connect
238: */
239: dev_sub = sub_new();
240: aproc_setin(dev_sub, buf);
241: } else {
242: dev_rec = NULL;
243: dev_sub = NULL;
244: }
245:
246: /*
247: * create play chain
248: */
249: if (dopar) {
250: aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate);
251: onfr *= DEFAULT_NBLK;
252:
253: /*
254: * create the write end
255: */
256: dev_play = wpipe_new(dev_file);
257: buf = abuf_new(onfr, aparams_bpf(dopar));
258: aproc_setin(dev_play, buf);
259:
260: /*
261: * append a converter, if needed
262: */
263: if (!aparams_eq(&opar, dopar)) {
264: if (debug_level > 0) {
265: fprintf(stderr, "%s: ", devpath);
266: aparams_print2(&opar, dopar);
267: fprintf(stderr, "\n");
268: }
269: conv = conv_new("mixconv", &opar, dopar);
270: aproc_setout(conv, buf);
271: buf = abuf_new(onfr, aparams_bpf(&opar));
272: aproc_setin(conv, buf);
273: *dopar = opar;
274: }
275: dev_opar = opar;
276: dev_onfr = onfr;
277:
278: /*
279: * append a "mix" to which clients will connect
280: */
281: dev_mix = mix_new();
282: aproc_setout(dev_mix, buf);
283: } else {
284: dev_play = NULL;
285: dev_mix = NULL;
286: }
287: }
288:
289: /*
290: * cleanly stop and drain everything and close the device
291: * once both play chain and record chain are gone
292: */
293: void
294: dev_done(void)
295: {
296: struct sigaction sa;
297: struct file *f;
298:
299: /*
300: * generate EOF on all inputs (including device), so once
301: * buffers are drained, everything will be cleaned
302: */
303: LIST_FOREACH(f, &file_list, entry) {
304: if (f->rproc)
305: file_eof(f);
306: }
307: /*
308: * destroy automatically mixe instead
309: * of generating silence
310: */
311: if (dev_mix)
312: dev_mix->u.mix.flags |= MIX_AUTOQUIT;
313: if (dev_sub)
314: dev_sub->u.sub.flags |= SUB_AUTOQUIT;
315: /*
316: * drain buffers of terminated inputs.
317: */
318: for (;;) {
319: if (!file_poll())
320: break;
321: }
322: devops->close(dev_file->fd);
323:
324: sigfillset(&sa.sa_mask);
325: sa.sa_flags = SA_RESTART;
326: sa.sa_handler = SIG_DFL;
327: if (sigaction(SIGINT, &sa, NULL) < 0)
328: err(1, "sigaction");
329: if (sigaction(SIGTSTP, &sa, NULL) < 0)
330: err(1, "sigaction");
331: if (sigaction(SIGCONT, &sa, NULL) < 0)
332: err(1, "sigaction");
333: }
334:
335: /*
336: * start the (paused) device. By default it's paused
337: */
338: void
339: dev_start(void)
340: {
341: dev_fill();
342: if (dev_mix)
343: dev_mix->u.mix.flags |= MIX_DROP;
344: if (dev_sub)
345: dev_sub->u.sub.flags |= SUB_DROP;
346: devops->start(dev_file->fd);
347: }
348:
349: /*
350: * pause the device
351: */
352: void
353: dev_stop(void)
354: {
355: devops->stop(dev_file->fd);
356: if (dev_mix)
357: dev_mix->u.mix.flags &= ~MIX_DROP;
358: if (dev_sub)
359: dev_sub->u.sub.flags &= ~SUB_DROP;
360: dev_flush();
361: }
362:
363: /*
364: * loop until there's either input or output to process
365: */
366: void
367: dev_run(int autoquit)
368: {
369: while (!quit_flag) {
370: if ((!dev_mix || LIST_EMPTY(&dev_mix->ibuflist)) &&
371: (!dev_sub || LIST_EMPTY(&dev_sub->obuflist)) && autoquit)
372: break;
373: if (!file_poll())
374: break;
375: if (pause_flag) {
376: devops->stop(dev_file->fd);
377: dev_flush();
378: dev_suspend();
379: dev_fill();
380: devops->start(dev_file->fd);
381: }
382: }
383: }
384:
385: /*
386: * attach the given input and output buffers to the mixer and the
387: * multiplexer respectively. The operation is done synchronously, so
388: * both buffers enter in sync. If buffers do not match play
389: * and rec
390: */
391: void
392: dev_attach(char *name,
393: struct abuf *ibuf, struct aparams *ipar, unsigned underrun,
394: struct abuf *obuf, struct aparams *opar, unsigned overrun)
395: {
396: int delta;
397: struct abuf *pbuf = NULL, *rbuf = NULL;
398: struct aproc *conv;
399:
400: if (ibuf) {
401: pbuf = LIST_FIRST(&dev_mix->obuflist);
402: if (!aparams_eq(ipar, &dev_opar)) {
403: if (debug_level > 1) {
404: fprintf(stderr, "dev_attach: %s: ", name);
405: aparams_print2(ipar, &dev_opar);
406: fprintf(stderr, "\n");
407: }
408: conv = conv_new(name, ipar, &dev_opar);
409: aproc_setin(conv, ibuf);
410: ibuf = abuf_new(dev_onfr, aparams_bpf(&dev_opar));
411: aproc_setout(conv, ibuf);
412: }
413: aproc_setin(dev_mix, ibuf);
414: ibuf->xrun = underrun;
415: mix_setmaster(dev_mix);
416: }
417: if (obuf) {
418: rbuf = LIST_FIRST(&dev_sub->ibuflist);
419: if (!aparams_eq(opar, &dev_ipar)) {
420: if (debug_level > 1) {
421: fprintf(stderr, "dev_attach: %s: ", name);
422: aparams_print2(&dev_ipar, opar);
423: fprintf(stderr, "\n");
424: }
425: conv = conv_new(name, &dev_ipar, opar);
426: aproc_setout(conv, obuf);
427: obuf = abuf_new(dev_infr, aparams_bpf(&dev_ipar));
428: aproc_setin(conv, obuf);
429: }
430: aproc_setout(dev_sub, obuf);
431: obuf->xrun = overrun;
432: }
433:
434: /*
435: * calculate delta, the number of frames the play chain is ahead
436: * of the record chain. It's necessary to schedule silences (or
437: * drops) in order to start playback and record in sync.
438: */
439: if (ibuf && obuf) {
440: delta =
441: rbuf->bpf * (pbuf->abspos + pbuf->used) -
442: pbuf->bpf * rbuf->abspos;
443: delta /= pbuf->bpf * rbuf->bpf;
444: DPRINTF("dev_attach: ppos = %u, pused = %u, rpos = %u\n",
445: pbuf->abspos, pbuf->used, rbuf->abspos);
446: } else
447: delta = 0;
448: DPRINTF("dev_attach: delta = %u\n", delta);
449:
450: if (delta > 0) {
451: /*
452: * if the play chain is ahead (most cases) drop some of
453: * the recorded input, to get both in sync
454: */
455: obuf->drop += delta * obuf->bpf;
456: } else if (delta < 0) {
457: /*
458: * if record chain is ahead (should never happen,
459: * right?) then insert silence to play
460: */
461: ibuf->silence += -delta * ibuf->bpf;
462: }
463: if (ibuf && (dev_mix->u.mix.flags & MIX_DROP)) {
464: /*
465: * fill the play buffer with silence to avoid underruns,
466: * drop samples on the input to keep play/record in sync
467: * after the silence insertion
468: */
469: ibuf->silence += dev_onfr * ibuf->bpf;
470: if (obuf)
471: obuf->drop += dev_onfr * obuf->bpf;
472: /*
473: * force data to propagate
474: */
475: abuf_run(ibuf);
476: DPRINTF("dev_attach: ibuf: used = %u, silence = %u\n",
477: ibuf->used, ibuf->silence);
478: }
479: if (obuf && (dev_sub->u.mix.flags & SUB_DROP)) {
480: abuf_run(obuf);
481: DPRINTF("dev_attach: ibuf: used = %u, drop = %u\n",
482: obuf->used, obuf->drop);
483: }
484: }