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