[BACK]Return to dev.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / aucat

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: }