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