[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.21

1.21    ! ratchov     1: /*     $OpenBSD: dev.c,v 1.20 2008/12/07 17:10:41 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;
1.1       ratchov    29: struct aparams dev_ipar, dev_opar;
                     30: struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play;
1.3       ratchov    31: struct file *dev_file;
1.1       ratchov    32:
1.20      ratchov    33: unsigned
                     34: dev_roundof(unsigned newrate)
1.1       ratchov    35: {
1.20      ratchov    36:        return (dev_round * newrate + dev_rate / 2) / dev_rate;
1.1       ratchov    37: }
                     38:
                     39: /*
                     40:  * open the device with the given hardware parameters and create a mixer
                     41:  * and a multiplexer connected to it with all necessary conversions
                     42:  * setup
                     43:  */
                     44: void
1.3       ratchov    45: dev_init(char *devpath,
1.20      ratchov    46:     struct aparams *dipar, struct aparams *dopar, unsigned bufsz)
1.1       ratchov    47: {
                     48:        struct aparams ipar, opar;
                     49:        struct aproc *conv;
                     50:        struct abuf *buf;
1.3       ratchov    51:        unsigned nfr, ibufsz, obufsz;
1.20      ratchov    52:
1.3       ratchov    53:        /*
1.11      ratchov    54:         * ask for 1/4 of the buffer for the kernel ring and
                     55:         * limit the block size to 1/4 of the requested buffer
1.3       ratchov    56:         */
                     57:        dev_bufsz = (bufsz + 3) / 4;
1.11      ratchov    58:        dev_round = (bufsz + 3) / 4;
1.3       ratchov    59:        dev_file = (struct file *)safile_new(&safile_ops, devpath,
1.20      ratchov    60:            dipar, dopar, &dev_bufsz, &dev_round);
1.3       ratchov    61:        if (!dev_file)
                     62:                exit(1);
                     63:        if (dipar) {
1.19      ratchov    64: #ifdef DEBUG
1.3       ratchov    65:                if (debug_level > 0) {
1.9       ratchov    66:                        fprintf(stderr, "dev_init: hw recording ");
1.3       ratchov    67:                        aparams_print(dipar);
1.9       ratchov    68:                        fprintf(stderr, "\n");
1.3       ratchov    69:                }
1.19      ratchov    70: #endif
1.20      ratchov    71:                dev_rate = dipar->rate;
1.3       ratchov    72:        }
                     73:        if (dopar) {
1.19      ratchov    74: #ifdef DEBUG
1.3       ratchov    75:                if (debug_level > 0) {
1.9       ratchov    76:                        fprintf(stderr, "dev_init: hw playing ");
1.3       ratchov    77:                        aparams_print(dopar);
1.9       ratchov    78:                        fprintf(stderr, "\n");
1.3       ratchov    79:                }
1.19      ratchov    80: #endif
1.20      ratchov    81:                dev_rate = dopar->rate;
1.3       ratchov    82:        }
1.11      ratchov    83:        ibufsz = obufsz = dev_bufsz;
                     84:        bufsz = (bufsz > dev_bufsz) ? bufsz - dev_bufsz : 0;
1.1       ratchov    85:
                     86:        /*
1.11      ratchov    87:         * use 1/8 of the buffer for the mixer/converters.  Since we
                     88:         * already consumed 1/4 for the device, bufsz represents the
                     89:         * remaining 3/4. So 1/8 is 1/6 of 3/4
                     90:         */
                     91:        nfr = (bufsz + 5) / 6;
                     92:        nfr += dev_round - 1;
                     93:        nfr -= nfr % dev_round;
                     94:        if (nfr == 0)
                     95:                nfr = dev_round;
                     96:
                     97:        /*
                     98:         * create record chain
1.1       ratchov    99:         */
                    100:        if (dipar) {
                    101:                aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate);
                    102:                /*
                    103:                 * create the read end
                    104:                 */
                    105:                dev_rec = rpipe_new(dev_file);
1.4       ratchov   106:                buf = abuf_new(nfr, dipar);
1.1       ratchov   107:                aproc_setout(dev_rec, buf);
1.3       ratchov   108:                ibufsz += nfr;
1.1       ratchov   109:
                    110:                /*
                    111:                 * append a converter, if needed
                    112:                 */
1.4       ratchov   113:                if (!aparams_eqenc(dipar, &ipar)) {
1.9       ratchov   114:                        conv = dec_new("subin", dipar);
1.1       ratchov   115:                        aproc_setin(conv, buf);
1.4       ratchov   116:                        buf = abuf_new(nfr, &ipar);
1.1       ratchov   117:                        aproc_setout(conv, buf);
1.3       ratchov   118:                        ibufsz += nfr;
1.1       ratchov   119:                }
                    120:                dev_ipar = ipar;
                    121:
                    122:                /*
                    123:                 * append a "sub" to which clients will connect
                    124:                 */
1.3       ratchov   125:                dev_sub = sub_new("sub", nfr);
1.1       ratchov   126:                aproc_setin(dev_sub, buf);
                    127:        } else {
                    128:                dev_rec = NULL;
                    129:                dev_sub = NULL;
                    130:        }
                    131:
                    132:        /*
                    133:         * create play chain
                    134:         */
                    135:        if (dopar) {
                    136:                aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate);
                    137:                /*
                    138:                 * create the write end
                    139:                 */
                    140:                dev_play = wpipe_new(dev_file);
1.4       ratchov   141:                buf = abuf_new(nfr, dopar);
1.1       ratchov   142:                aproc_setin(dev_play, buf);
1.3       ratchov   143:                obufsz += nfr;
                    144:
1.1       ratchov   145:                /*
                    146:                 * append a converter, if needed
                    147:                 */
1.4       ratchov   148:                if (!aparams_eqenc(&opar, dopar)) {
1.9       ratchov   149:                        conv = enc_new("mixout", dopar);
1.1       ratchov   150:                        aproc_setout(conv, buf);
1.4       ratchov   151:                        buf = abuf_new(nfr, &opar);
1.1       ratchov   152:                        aproc_setin(conv, buf);
1.3       ratchov   153:                        obufsz += nfr;
1.1       ratchov   154:                }
                    155:                dev_opar = opar;
                    156:
                    157:                /*
                    158:                 * append a "mix" to which clients will connect
                    159:                 */
1.3       ratchov   160:                dev_mix = mix_new("mix", nfr);
1.1       ratchov   161:                aproc_setout(dev_mix, buf);
                    162:        } else {
                    163:                dev_play = NULL;
                    164:                dev_mix = NULL;
                    165:        }
1.3       ratchov   166:        dev_bufsz = (dopar) ? obufsz : ibufsz;
                    167:        DPRINTF("dev_init: using %u fpb\n", dev_bufsz);
                    168:        dev_start();
1.1       ratchov   169: }
                    170:
                    171: /*
                    172:  * cleanly stop and drain everything and close the device
                    173:  * once both play chain and record chain are gone
                    174:  */
                    175: void
                    176: dev_done(void)
                    177: {
                    178:        struct file *f;
                    179:
1.15      ratchov   180:        DPRINTF("dev_done: dev_mix = %p, dev_sub = %p\n", dev_mix, dev_sub);
1.3       ratchov   181:        if (dev_mix) {
1.17      ratchov   182:                dev_mix->u.mix.flags |= MIX_AUTOQUIT;
1.3       ratchov   183:                /*
                    184:                 * generate EOF on all inputs (but not the device), and
                    185:                 * put the mixer in ``autoquit'' state, so once buffers
                    186:                 * are drained the mixer will terminate and shutdown the
                    187:                 * write-end of the device
                    188:                 *
                    189:                 * NOTE: since file_eof() can destroy the file and
                    190:                 * reorder the file_list, we have to restart the loop
                    191:                 * after each call to file_eof()
                    192:                 */
                    193:        restart:
                    194:                LIST_FOREACH(f, &file_list, entry) {
                    195:                        if (f != dev_file && f->rproc) {
                    196:                                file_eof(f);
                    197:                                goto restart;
                    198:                        }
                    199:                }
                    200:
                    201:                /*
                    202:                 * wait play chain to terminate
                    203:                 */
                    204:                while (dev_file->wproc != NULL) {
                    205:                        if (!file_poll())
                    206:                                break;
                    207:                }
                    208:                dev_mix = 0;
1.1       ratchov   209:        }
1.3       ratchov   210:        if (dev_sub) {
1.17      ratchov   211:                dev_sub->u.sub.flags |= SUB_AUTOQUIT;
1.3       ratchov   212:                /*
                    213:                 * same as above, but for the record chain: generate eof
                    214:                 * on the read-end of the device and wait record buffers
                    215:                 * to desappear.  We must stop the device first, because
                    216:                 * play-end will underrun (and xrun correction code will
                    217:                 * insert silence on the record-end of the device)
                    218:                 */
                    219:                dev_stop();
                    220:                file_eof(dev_file);
                    221:                for (;;) {
                    222:                        if (!file_poll())
                    223:                                break;
                    224:                }
                    225:                dev_sub = NULL;
1.1       ratchov   226:        }
                    227: }
                    228:
                    229: /*
                    230:  * start the (paused) device. By default it's paused
                    231:  */
                    232: void
                    233: dev_start(void)
                    234: {
                    235:        if (dev_mix)
                    236:                dev_mix->u.mix.flags |= MIX_DROP;
                    237:        if (dev_sub)
                    238:                dev_sub->u.sub.flags |= SUB_DROP;
1.3       ratchov   239:        dev_file->ops->start(dev_file);
1.1       ratchov   240: }
                    241:
                    242: /*
                    243:  * pause the device
                    244:  */
                    245: void
                    246: dev_stop(void)
                    247: {
1.3       ratchov   248:        dev_file->ops->stop(dev_file);
1.1       ratchov   249:        if (dev_mix)
                    250:                dev_mix->u.mix.flags &= ~MIX_DROP;
                    251:        if (dev_sub)
                    252:                dev_sub->u.sub.flags &= ~SUB_DROP;
                    253: }
                    254:
                    255: /*
1.14      ratchov   256:  * find the end points connected to the mix/sub
                    257:  */
                    258: int
                    259: dev_getep(struct abuf **sibuf, struct abuf **sobuf)
                    260: {
                    261:        struct abuf *ibuf, *obuf;
                    262:
                    263:        if (sibuf) {
                    264:                ibuf = *sibuf;
                    265:                for (;;) {
                    266:                        if (!ibuf || !ibuf->rproc) {
                    267:                                DPRINTF("dev_getep: reader desappeared\n");
                    268:                                return 0;
                    269:                        }
                    270:                        if (ibuf->rproc == dev_mix)
                    271:                                break;
                    272:                        ibuf = LIST_FIRST(&ibuf->rproc->obuflist);
                    273:                }
1.21    ! ratchov   274:                *sibuf = ibuf;
1.14      ratchov   275:        }
                    276:        if (sobuf) {
                    277:                obuf = *sobuf;
                    278:                for (;;) {
                    279:                        if (!obuf || !obuf->wproc) {
                    280:                                DPRINTF("dev_getep: writer desappeared\n");
                    281:                                return 0;
                    282:                        }
                    283:                        if (obuf->wproc == dev_sub)
                    284:                                break;
                    285:                        obuf = LIST_FIRST(&obuf->wproc->ibuflist);
                    286:                }
1.21    ! ratchov   287:                *sobuf = obuf;
1.14      ratchov   288:        }
                    289:        return 1;
                    290: }
                    291:
                    292: /*
1.3       ratchov   293:  * sync play buffer to rec buffer (for instance when one of
                    294:  * them underruns/overruns)
1.1       ratchov   295:  */
                    296: void
1.3       ratchov   297: dev_sync(struct abuf *ibuf, struct abuf *obuf)
1.1       ratchov   298: {
1.3       ratchov   299:        struct abuf *pbuf, *rbuf;
                    300:        int delta;
                    301:
                    302:        if (!dev_mix || !dev_sub)
                    303:                return;
                    304:        pbuf = LIST_FIRST(&dev_mix->obuflist);
                    305:        if (!pbuf)
                    306:                return;
                    307:        rbuf = LIST_FIRST(&dev_sub->ibuflist);
                    308:        if (!rbuf)
                    309:                return;
1.14      ratchov   310:        if (!dev_getep(&ibuf, &obuf))
                    311:                return;
1.3       ratchov   312:
                    313:        /*
                    314:         * calculate delta, the number of frames the play chain is ahead
                    315:         * of the record chain. It's necessary to schedule silences (or
                    316:         * drops) in order to start playback and record in sync.
                    317:         */
                    318:        delta =
                    319:            rbuf->bpf * (pbuf->abspos + pbuf->used) -
                    320:            pbuf->bpf *  rbuf->abspos;
                    321:        delta /= pbuf->bpf * rbuf->bpf;
                    322:        DPRINTF("dev_sync: delta = %d, ppos = %u, pused = %u, rpos = %u\n",
                    323:            delta, pbuf->abspos, pbuf->used, rbuf->abspos);
                    324:
                    325:        if (delta > 0) {
                    326:                /*
                    327:                 * if the play chain is ahead (most cases) drop some of
                    328:                 * the recorded input, to get both in sync
                    329:                 */
                    330:                obuf->drop += delta * obuf->bpf;
                    331:                abuf_ipos(obuf, -delta);
                    332:        } else if (delta < 0) {
                    333:                /*
                    334:                 * if record chain is ahead (should never happen,
                    335:                 * right?) then insert silence to play
                    336:                 */
                    337:                ibuf->silence += -delta * ibuf->bpf;
                    338:                abuf_opos(ibuf, delta);
                    339:        } else
                    340:                DPRINTF("dev_sync: nothing to do\n");
1.1       ratchov   341: }
                    342:
                    343: /*
                    344:  * attach the given input and output buffers to the mixer and the
                    345:  * multiplexer respectively. The operation is done synchronously, so
                    346:  * both buffers enter in sync. If buffers do not match play
                    347:  * and rec
                    348:  */
                    349: void
                    350: dev_attach(char *name,
1.10      ratchov   351:     struct abuf *ibuf, struct aparams *sipar, unsigned underrun,
1.18      ratchov   352:     struct abuf *obuf, struct aparams *sopar, unsigned overrun, int vol)
1.1       ratchov   353: {
                    354:        struct abuf *pbuf = NULL, *rbuf = NULL;
1.12      ratchov   355:        struct aparams ipar, opar;
1.1       ratchov   356:        struct aproc *conv;
1.20      ratchov   357:        unsigned round, nblk;
                    358:
1.1       ratchov   359:        if (ibuf) {
1.12      ratchov   360:                ipar = *sipar;
1.20      ratchov   361:                pbuf = LIST_FIRST(&dev_mix->obuflist);
                    362:                nblk = (dev_bufsz / dev_round + 3) / 4;
                    363:                round = dev_roundof(ipar.rate);
1.10      ratchov   364:                if (!aparams_eqenc(&ipar, &dev_opar)) {
                    365:                        conv = dec_new(name, &ipar);
                    366:                        ipar.bps = dev_opar.bps;
                    367:                        ipar.bits = dev_opar.bits;
                    368:                        ipar.sig = dev_opar.sig;
                    369:                        ipar.le = dev_opar.le;
                    370:                        ipar.msb = dev_opar.msb;
1.9       ratchov   371:                        aproc_setin(conv, ibuf);
1.20      ratchov   372:                        ibuf = abuf_new(nblk * round, &ipar);
1.9       ratchov   373:                        aproc_setout(conv, ibuf);
1.8       ratchov   374:                }
1.10      ratchov   375:                if (!aparams_subset(&ipar, &dev_opar)) {
                    376:                        conv = cmap_new(name, &ipar, &dev_opar);
                    377:                        ipar.cmin = dev_opar.cmin;
                    378:                        ipar.cmax = dev_opar.cmax;
1.7       ratchov   379:                        aproc_setin(conv, ibuf);
1.20      ratchov   380:                        ibuf = abuf_new(nblk * round, &ipar);
1.7       ratchov   381:                        aproc_setout(conv, ibuf);
1.6       ratchov   382:                }
1.10      ratchov   383:                if (!aparams_eqrate(&ipar, &dev_opar)) {
1.20      ratchov   384:                        conv = resamp_new(name, round, dev_round);
1.10      ratchov   385:                        ipar.rate = dev_opar.rate;
1.20      ratchov   386:                        round = dev_round;
1.5       ratchov   387:                        aproc_setin(conv, ibuf);
1.20      ratchov   388:                        ibuf = abuf_new(nblk * round, &ipar);
1.5       ratchov   389:                        aproc_setout(conv, ibuf);
1.1       ratchov   390:                }
                    391:                aproc_setin(dev_mix, ibuf);
1.3       ratchov   392:                abuf_opos(ibuf, -dev_mix->u.mix.lat);
1.1       ratchov   393:                ibuf->xrun = underrun;
1.18      ratchov   394:                ibuf->mixmaxweight = vol;
                    395:                mix_setmaster(dev_mix);
1.1       ratchov   396:        }
                    397:        if (obuf) {
1.12      ratchov   398:                opar = *sopar;
1.1       ratchov   399:                rbuf = LIST_FIRST(&dev_sub->ibuflist);
1.20      ratchov   400:                round = dev_roundof(opar.rate);
                    401:                nblk = (dev_bufsz / dev_round + 3) / 4;
1.10      ratchov   402:                if (!aparams_eqenc(&opar, &dev_ipar)) {
                    403:                        conv = enc_new(name, &opar);
                    404:                        opar.bps = dev_ipar.bps;
                    405:                        opar.bits = dev_ipar.bits;
                    406:                        opar.sig = dev_ipar.sig;
                    407:                        opar.le = dev_ipar.le;
                    408:                        opar.msb = dev_ipar.msb;
1.9       ratchov   409:                        aproc_setout(conv, obuf);
1.20      ratchov   410:                        obuf = abuf_new(nblk * round, &opar);
1.9       ratchov   411:                        aproc_setin(conv, obuf);
1.8       ratchov   412:                }
1.10      ratchov   413:                if (!aparams_subset(&opar, &dev_ipar)) {
                    414:                        conv = cmap_new(name, &dev_ipar, &opar);
                    415:                        opar.cmin = dev_ipar.cmin;
                    416:                        opar.cmax = dev_ipar.cmax;
1.7       ratchov   417:                        aproc_setout(conv, obuf);
1.20      ratchov   418:                        obuf = abuf_new(nblk * round, &opar);
1.7       ratchov   419:                        aproc_setin(conv, obuf);
1.6       ratchov   420:                }
1.10      ratchov   421:                if (!aparams_eqrate(&opar, &dev_ipar)) {
1.20      ratchov   422:                        conv = resamp_new(name, dev_round, round);
1.10      ratchov   423:                        opar.rate = dev_ipar.rate;
1.20      ratchov   424:                        round = dev_round;
1.1       ratchov   425:                        aproc_setout(conv, obuf);
1.20      ratchov   426:                        obuf = abuf_new(nblk * round, &opar);
1.1       ratchov   427:                        aproc_setin(conv, obuf);
                    428:                }
                    429:                aproc_setout(dev_sub, obuf);
1.3       ratchov   430:                abuf_ipos(obuf, -dev_sub->u.sub.lat);
1.1       ratchov   431:                obuf->xrun = overrun;
                    432:        }
                    433:
                    434:        /*
1.3       ratchov   435:         * sync play to record
1.1       ratchov   436:         */
                    437:        if (ibuf && obuf) {
1.3       ratchov   438:                ibuf->duplex = obuf;
                    439:                obuf->duplex = ibuf;
                    440:                dev_sync(ibuf, obuf);
1.13      ratchov   441:        }
1.14      ratchov   442: }
                    443:
                    444: /*
                    445:  * change the playback volume of the fiven stream
                    446:  */
                    447: void
                    448: dev_setvol(struct abuf *ibuf, int vol)
                    449: {
1.21    ! ratchov   450:        DPRINTF("dev_setvol: %p\n", ibuf);
1.16      ratchov   451:        if (!dev_getep(&ibuf, NULL)) {
                    452:                DPRINTF("dev_setvol: not connected yet\n");
1.14      ratchov   453:                return;
1.16      ratchov   454:        }
1.14      ratchov   455:        ibuf->mixvol = vol;
1.21    ! ratchov   456:        DPRINTF("dev_setvol: %p -> %d\n", ibuf, vol);
1.13      ratchov   457: }
                    458:
                    459: /*
                    460:  * clear buffers of the play and record chains so that when the device
                    461:  * is started, playback and record start in sync
                    462:  */
                    463: void
                    464: dev_clear(void)
                    465: {
                    466:        struct abuf *buf;
                    467:
                    468:        if (dev_mix) {
                    469:                if (!LIST_EMPTY(&dev_mix->ibuflist)) {
                    470:                        fprintf(stderr, "dev_clear: mixer not idle\n");
                    471:                        abort();
                    472:                }
                    473:                buf = LIST_FIRST(&dev_mix->obuflist);
                    474:                while (buf) {
                    475:                        abuf_clear(buf);
                    476:                        buf = LIST_FIRST(&buf->rproc->obuflist);
                    477:                }
                    478:                mix_clear(dev_mix);
                    479:        }
                    480:        if (dev_sub) {
                    481:                if (!LIST_EMPTY(&dev_sub->obuflist)) {
                    482:                        fprintf(stderr, "dev_suspend: demux not idle\n");
                    483:                        abort();
                    484:                }
                    485:                buf = LIST_FIRST(&dev_sub->ibuflist);
                    486:                while (buf) {
                    487:                        abuf_clear(buf);
                    488:                        buf = LIST_FIRST(&buf->wproc->ibuflist);
                    489:                }
                    490:                sub_clear(dev_sub);
1.1       ratchov   491:        }
                    492: }