diff --git a/sndiod/dev.c b/sndiod/dev.c index 61dd802..5026827 100644 --- a/sndiod/dev.c +++ b/sndiod/dev.c @@ -58,10 +58,10 @@ int dev_getpos(struct dev *); struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); void dev_adjpar(struct dev *, int, int, int); -int dev_open_do(struct dev *); +int dev_allocbufs(struct dev *); int dev_open(struct dev *); void dev_exitall(struct dev *); -void dev_close_do(struct dev *); +void dev_freebufs(struct dev *); void dev_close(struct dev *); int dev_ref(struct dev *); void dev_unref(struct dev *); @@ -83,6 +83,7 @@ void slot_attach(struct slot *); void slot_ready(struct slot *); void slot_allocbufs(struct slot *); void slot_freebufs(struct slot *); +void slot_initconv(struct slot *); void slot_start(struct slot *); void slot_detach(struct slot *); void slot_stop(struct slot *); @@ -1047,15 +1048,8 @@ dev_adjpar(struct dev *d, int mode, * monitor, midi control, and any necessary conversions. */ int -dev_open_do(struct dev *d) +dev_allocbufs(struct dev *d) { - if (!dev_sio_open(d)) { - if (log_level >= 1) { - dev_log(d); - log_puts(": failed to open audio device\n"); - } - return 0; - } if (d->mode & MODE_REC) { /* * Create device <-> demuxer buffer @@ -1089,7 +1083,6 @@ dev_open_do(struct dev *d) } else d->encbuf = NULL; } - d->pstate = DEV_INIT; if (log_level >= 2) { dev_log(d); log_puts(": "); @@ -1133,9 +1126,15 @@ dev_open(struct dev *d) d->pchan = 2; if (d->rchan == 0) d->rchan = 2; - if (!dev_open_do(d)) + if (!dev_sio_open(d)) { + if (log_level >= 1) { + dev_log(d); + log_puts(": failed to open audio device\n"); + } + return 0; + } + if (!dev_allocbufs(d)) return 0; - /* * we use the "sndiod" group name. find a unused @@ -1163,6 +1162,8 @@ dev_open(struct dev *d) } dev_addctl(d, "sndiod", gunit, CTL_NUM, CTLADDR_MASTER, "master", -1, "level", NULL, -1, d->master); + + d->pstate = DEV_INIT; return 1; } @@ -1195,31 +1196,14 @@ dev_exitall(struct dev *d) * ensure buffers are drained */ void -dev_close_do(struct dev *d) +dev_freebufs(struct dev *d) { - struct ctl *c, **pc; - #ifdef DEBUG if (log_level >= 3) { dev_log(d); log_puts(": closing\n"); } #endif - pc = &d->ctl_list; - while ((c = *pc) != NULL) { - if (c->addr >= CTLADDR_END) { - if (c->refs_mask == 0) { - *pc = c->next; - xfree(c); - continue; - } - c->type = CTL_NONE; - c->desc_mask = ~0; - } - pc = &c->next; - } - d->pstate = DEV_CFG; - dev_sio_close(d); if (d->mode & MODE_PLAY) { if (d->encbuf != NULL) xfree(d->encbuf); @@ -1241,7 +1225,9 @@ dev_close(struct dev *d) struct ctl *c; dev_exitall(d); - dev_close_do(d); + d->pstate = DEV_CFG; + dev_sio_close(d); + dev_freebufs(d); /* there are no clients, just free remaining local controls */ while ((c = d->ctl_list) != NULL) { @@ -1254,67 +1240,43 @@ dev_close(struct dev *d) * Close the device, but attempt to migrate everything to a new sndio * device. */ -void +int dev_reopen(struct dev *d) { struct slot *s; + struct ctl *c, **pc; long long pos; - unsigned int mode, round, bufsz, rate, pstate; + unsigned int pstate; int delta; /* not opened */ if (d->pstate == DEV_CFG) - return; - - if (log_level >= 1) { - dev_log(d); - log_puts(": reopening device\n"); - } + return 1; /* save state */ - mode = d->mode; - round = d->round; - bufsz = d->bufsz; - rate = d->rate; delta = d->delta; pstate = d->pstate; - /* close device */ - dev_close_do(d); + if (!dev_sio_reopen(d)) + return 0; - /* open device */ - if (!dev_open_do(d)) { - if (log_level >= 1) { - dev_log(d); - log_puts(": found no working alternate device\n"); - } - dev_exitall(d); - return; - } + /* reopen returns a stopped device */ + d->pstate = DEV_INIT; - /* check if new parameters are compatible with old ones */ - if (d->mode != mode || - d->round != round || - d->bufsz != bufsz || - d->rate != rate) { - if (log_level >= 1) { - dev_log(d); - log_puts(": alternate device not compatible\n"); - } - dev_close(d); - return; - } + /* reallocate new buffers, with new parameters */ + dev_freebufs(d); + dev_allocbufs(d); /* * adjust time positions, make anything go back delta ticks, so * that the new device can start at zero */ for (s = d->slot_list; s != NULL; s = s->next) { - pos = (long long)(d->round - delta) * s->round + s->delta_rem; - s->delta_rem = pos % d->round; - s->delta += pos / (int)d->round; - s->delta -= s->round; - if (log_level >= 2) { + pos = (long long)s->delta * d->round + s->delta_rem; + pos -= (long long)delta * s->round; + s->delta_rem = pos % (int)d->round; + s->delta = pos / (int)d->round; + if (log_level >= 3) { slot_log(s); log_puts(": adjusted: delta -> "); log_puti(s->delta); @@ -1322,6 +1284,9 @@ dev_reopen(struct dev *d) log_puti(s->delta_rem); log_puts("\n"); } + + /* reinitilize the format conversion chain */ + slot_initconv(s); } if (d->tstate == MMC_RUN) { d->mtc.delta -= delta * MTC_SEC; @@ -1333,9 +1298,29 @@ dev_reopen(struct dev *d) } } + /* remove controls of old device */ + pc = &d->ctl_list; + while ((c = *pc) != NULL) { + if (c->addr >= CTLADDR_END) { + if (c->refs_mask == 0) { + *pc = c->next; + xfree(c); + continue; + } + c->type = CTL_NONE; + c->desc_mask = ~0; + } + pc = &c->next; + } + + /* add new device controls */ + dev_siomix_open(d); + /* start the device if needed */ if (pstate == DEV_RUN) dev_wakeup(d); + + return 1; } int @@ -1591,31 +1576,15 @@ dev_mmcloc(struct dev *d, unsigned int origin) dev_mmcstart(d); } - /* * allocate buffers & conversion chain */ void -slot_allocbufs(struct slot *s) +slot_initconv(struct slot *s) { - unsigned int dev_nch; struct dev *d = s->dev; if (s->mode & MODE_PLAY) { - s->mix.bpf = s->par.bps * s->mix.nch; - abuf_init(&s->mix.buf, s->appbufsz * s->mix.bpf); - - dev_nch = s->opt->pmax - s->opt->pmin + 1; - s->mix.decbuf = NULL; - s->mix.resampbuf = NULL; - s->mix.join = 1; - s->mix.expand = 1; - if (s->opt->dup) { - if (dev_nch > s->mix.nch) - s->mix.expand = dev_nch / s->mix.nch; - else if (dev_nch < s->mix.nch) - s->mix.join = s->mix.nch / dev_nch; - } cmap_init(&s->mix.cmap, s->opt->pmin, s->opt->pmin + s->mix.nch - 1, s->opt->pmin, s->opt->pmin + s->mix.nch - 1, @@ -1623,32 +1592,22 @@ slot_allocbufs(struct slot *s) s->opt->pmin, s->opt->pmax); if (!aparams_native(&s->par)) { dec_init(&s->mix.dec, &s->par, s->mix.nch); - s->mix.decbuf = - xmalloc(s->round * s->mix.nch * sizeof(adata_t)); } if (s->rate != d->rate) { resamp_init(&s->mix.resamp, s->round, d->round, s->mix.nch); - s->mix.resampbuf = - xmalloc(d->round * s->mix.nch * sizeof(adata_t)); + } + s->mix.join = 1; + s->mix.expand = 1; + if (s->opt->dup) { + if (s->mix.cmap.nch > s->mix.nch) + s->mix.expand = s->mix.cmap.nch / s->mix.nch; + else if (s->mix.cmap.nch > 0) + s->mix.join = s->mix.nch / s->mix.cmap.nch; } } if (s->mode & MODE_RECMASK) { - s->sub.bpf = s->par.bps * s->sub.nch; - abuf_init(&s->sub.buf, s->appbufsz * s->sub.bpf); - - dev_nch = s->opt->rmax - s->opt->rmin + 1; - s->sub.encbuf = NULL; - s->sub.resampbuf = NULL; - s->sub.join = 1; - s->sub.expand = 1; - if (s->opt->dup) { - if (dev_nch > s->sub.nch) - s->sub.join = dev_nch / s->sub.nch; - else if (dev_nch < s->sub.nch) - s->sub.expand = s->sub.nch / dev_nch; - } cmap_init(&s->sub.cmap, 0, ((s->mode & MODE_MON) ? d->pchan : d->rchan) - 1, s->opt->rmin, s->opt->rmax, @@ -1657,13 +1616,17 @@ slot_allocbufs(struct slot *s) if (s->rate != d->rate) { resamp_init(&s->sub.resamp, d->round, s->round, s->sub.nch); - s->sub.resampbuf = - xmalloc(d->round * s->sub.nch * sizeof(adata_t)); } if (!aparams_native(&s->par)) { enc_init(&s->sub.enc, &s->par, s->sub.nch); - s->sub.encbuf = - xmalloc(s->round * s->sub.nch * sizeof(adata_t)); + } + s->sub.join = 1; + s->sub.expand = 1; + if (s->opt->dup) { + if (s->sub.cmap.nch > s->sub.nch) + s->sub.join = s->sub.cmap.nch / s->sub.nch; + else if (s->sub.cmap.nch > 0) + s->sub.expand = s->sub.nch / s->sub.cmap.nch; } /* @@ -1683,6 +1646,49 @@ slot_allocbufs(struct slot *s) s->appbufsz * s->sub.nch * sizeof(adata_t)); } } +} + +/* + * allocate buffers & conversion chain + */ +void +slot_allocbufs(struct slot *s) +{ + struct dev *d = s->dev; + + if (s->mode & MODE_PLAY) { + s->mix.bpf = s->par.bps * s->mix.nch; + abuf_init(&s->mix.buf, s->appbufsz * s->mix.bpf); + + s->mix.decbuf = NULL; + s->mix.resampbuf = NULL; + if (!aparams_native(&s->par)) { + s->mix.decbuf = + xmalloc(s->round * s->mix.nch * sizeof(adata_t)); + } + if (s->rate != d->rate) { + s->mix.resampbuf = + xmalloc(d->round * s->mix.nch * sizeof(adata_t)); + } + } + + if (s->mode & MODE_RECMASK) { + s->sub.bpf = s->par.bps * s->sub.nch; + abuf_init(&s->sub.buf, s->appbufsz * s->sub.bpf); + + s->sub.encbuf = NULL; + s->sub.resampbuf = NULL; + if (s->rate != d->rate) { + s->sub.resampbuf = + xmalloc(d->round * s->sub.nch * sizeof(adata_t)); + } + if (!aparams_native(&s->par)) { + s->sub.encbuf = + xmalloc(s->round * s->sub.nch * sizeof(adata_t)); + } + } + + slot_initconv(s); #ifdef DEBUG if (log_level >= 3) { diff --git a/sndiod/dev.h b/sndiod/dev.h index 1d3e93d..7130793 100644 --- a/sndiod/dev.h +++ b/sndiod/dev.h @@ -254,7 +254,7 @@ extern struct dev *dev_list; void dev_log(struct dev *); void dev_close(struct dev *); -void dev_reopen(struct dev *); +int dev_reopen(struct dev *); struct dev *dev_new(char *, struct aparams *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int); struct dev *dev_bynum(int); diff --git a/sndiod/dev_siomix.c b/sndiod/dev_siomix.c index 8c39c92..f816499 100644 --- a/sndiod/dev_siomix.c +++ b/sndiod/dev_siomix.c @@ -95,14 +95,13 @@ dev_siomix_onctl(void *arg, unsigned int addr, unsigned int val) * open the mixer device. */ void -dev_siomix_open(struct dev *d, char *path) +dev_siomix_open(struct dev *d) { - d->siomix.hdl = siomix_open(path, SIOMIX_READ | SIOMIX_WRITE, 0); if (d->siomix.hdl == NULL) return; siomix_ondesc(d->siomix.hdl, dev_siomix_ondesc, d); siomix_onctl(d->siomix.hdl, dev_siomix_onctl, d); - d->siomix.file = file_new(&dev_siomix_ops, d, path, + d->siomix.file = file_new(&dev_siomix_ops, d, "mix", siomix_nfds(d->siomix.hdl)); } @@ -115,8 +114,6 @@ dev_siomix_close(struct dev *d) if (d->siomix.hdl == NULL) return; file_del(d->siomix.file); - siomix_close(d->siomix.hdl); - d->siomix.hdl = NULL; } int diff --git a/sndiod/dev_siomix.h b/sndiod/dev_siomix.h index 3ad9cdf..2c74387 100644 --- a/sndiod/dev_siomix.h +++ b/sndiod/dev_siomix.h @@ -26,7 +26,7 @@ struct dev_siomix { struct file *file; }; -void dev_siomix_open(struct dev *, char *); +void dev_siomix_open(struct dev *); void dev_siomix_close(struct dev *); #endif /* !defined(DEV_SIOMIX_H) */ diff --git a/sndiod/listen.c b/sndiod/listen.c index 938a94f..54c9684 100644 --- a/sndiod/listen.c +++ b/sndiod/listen.c @@ -16,7 +16,6 @@ */ #include #include -#include #include #include diff --git a/sndiod/midi.c b/sndiod/midi.c index 00f941c..cf7aa74 100644 --- a/sndiod/midi.c +++ b/sndiod/midi.c @@ -596,25 +596,14 @@ port_done(struct port *c) port_drain(c); } -void +int port_reopen(struct port *p) { if (p->state == PORT_CFG) - return; + return 1; - if (log_level >= 1) { - port_log(p); - log_puts(": reopening port\n"); - } + if (!port_mio_reopen(p)) + return 0; - port_mio_close(p); - - if (!port_mio_open(p)) { - if (log_level >= 1) { - port_log(p); - log_puts(": found no working alternate port\n"); - } - p->state = PORT_CFG; - port_exitall(p); - } + return 1; } diff --git a/sndiod/midi.h b/sndiod/midi.h index 26f6e86..9d7c9f5 100644 --- a/sndiod/midi.h +++ b/sndiod/midi.h @@ -121,6 +121,6 @@ int port_init(struct port *); void port_done(struct port *); void port_drain(struct port *); int port_close(struct port *); -void port_reopen(struct port *); +int port_reopen(struct port *); #endif /* !defined(MIDI_H) */ diff --git a/sndiod/miofile.c b/sndiod/miofile.c index 7669468..1bf5e5c 100644 --- a/sndiod/miofile.c +++ b/sndiod/miofile.c @@ -47,18 +47,26 @@ struct fileops port_mio_ops = { /* * open the port using one of the provided paths */ -static char * +static struct mio_hdl * port_mio_openlist(struct port *c, unsigned int mode) { + struct mio_hdl *hdl; struct name *n; n = c->path_list; while (1) { if (n == NULL) break; - c->mio.hdl = mio_open(n->str, mode, 1); - if (c->mio.hdl != NULL) - return n->str; + hdl = mio_open(n->str, mode, 1); + if (hdl != NULL) { + if (log_level >= 2) { + port_log(c); + log_puts(": using "); + log_puts(n->str); + log_puts("\n"); + } + return hdl; + } n = n->next; } return NULL; @@ -67,12 +75,37 @@ port_mio_openlist(struct port *c, unsigned int mode) int port_mio_open(struct port *p) { - char *path; - - path = port_mio_openlist(p, p->midi->mode); + p->mio.hdl = port_mio_openlist(p, p->midi->mode); if (p->mio.hdl == NULL) return 0; - p->mio.file = file_new(&port_mio_ops, p, path, mio_nfds(p->mio.hdl)); + p->mio.file = file_new(&port_mio_ops, p, "port", mio_nfds(p->mio.hdl)); + return 1; +} + +/* + * Open an alternate port. Upon success, close the old port + * and continue using the new one. + */ +int +port_mio_reopen(struct port *p) +{ + struct mio_hdl *hdl; + + hdl = port_mio_openlist(p, p->midi->mode); + if (hdl == NULL) { + if (log_level >= 1) { + port_log(p); + log_puts(": couldn't open an alternate port\n"); + } + return 0; + } + + /* close unused device */ + file_del(p->mio.file); + mio_close(p->mio.hdl); + + p->mio.hdl = hdl; + p->mio.file = file_new(&port_mio_ops, p, "port", mio_nfds(hdl)); return 1; } @@ -150,5 +183,6 @@ port_mio_hup(void *arg) { struct port *p = arg; - port_reopen(p); + if (!port_reopen(p)) + port_close(p); } diff --git a/sndiod/miofile.h b/sndiod/miofile.h index 78ef047..3d971ca 100644 --- a/sndiod/miofile.h +++ b/sndiod/miofile.h @@ -25,6 +25,7 @@ struct port_mio { }; int port_mio_open(struct port *); +int port_mio_reopen(struct port *); void port_mio_close(struct port *); #endif /* !defined(MIOFILE_H) */ diff --git a/sndiod/siofile.c b/sndiod/siofile.c index 376e647..73c74b1 100644 --- a/sndiod/siofile.c +++ b/sndiod/siofile.c @@ -87,18 +87,36 @@ dev_sio_timeout(void *arg) /* * open the device using one of the provided paths */ -static char * -dev_sio_openlist(struct dev *d, unsigned int mode) +static struct sio_hdl * +dev_sio_openlist(struct dev *d, unsigned int mode, struct siomix_hdl **rmixhdl) { struct name *n; + struct sio_hdl *hdl; + struct siomix_hdl *mixhdl; n = d->path_list; while (1) { if (n == NULL) break; - d->sio.hdl = sio_open(n->str, mode, 1); - if (d->sio.hdl != NULL) - return n->str; + hdl = sio_open(n->str, mode, 1); + if (hdl != NULL) { + if (log_level >= 2) { + dev_log(d); + log_puts(": using "); + log_puts(n->str); + log_puts("\n"); + } + mixhdl = siomix_open(n->str, + SIOMIX_READ | SIOMIX_WRITE, 0); + if (mixhdl == NULL) { + if (log_level >= 1) { + dev_log(d); + log_puts(": no mixer\n"); + } + } + *rmixhdl = mixhdl; + return hdl; + } n = n->next; } return NULL; @@ -112,18 +130,18 @@ dev_sio_open(struct dev *d) { struct sio_par par; unsigned int mode = d->mode & (MODE_PLAY | MODE_REC); - char *path; - path = dev_sio_openlist(d, mode); - if (path == NULL) { + d->sio.hdl = dev_sio_openlist(d, mode, &d->siomix.hdl); + if (d->sio.hdl == NULL) { if (mode != (SIO_PLAY | SIO_REC)) return 0; - path = dev_sio_openlist(d, SIO_PLAY); - if (path != NULL) + d->sio.hdl = dev_sio_openlist(d, SIO_PLAY, &d->siomix.hdl); + if (d->sio.hdl != NULL) mode = SIO_PLAY; else { - path = dev_sio_openlist(d, SIO_REC); - if (path != NULL) + d->sio.hdl = dev_sio_openlist(d, + SIO_REC, &d->siomix.hdl); + if (d->sio.hdl != NULL) mode = SIO_REC; else return 0; @@ -233,18 +251,98 @@ dev_sio_open(struct dev *d) if (!(mode & MODE_REC)) d->mode &= ~MODE_REC; sio_onmove(d->sio.hdl, dev_sio_onmove, d); - d->sio.file = file_new(&dev_sio_ops, d, path, sio_nfds(d->sio.hdl)); + d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(d->sio.hdl)); timo_set(&d->sio.watchdog, dev_sio_timeout, d); - if (log_level >= 1) { - dev_log(d); - log_puts(": using "); - log_puts(path); - log_puts("\n"); - } - dev_siomix_open(d, path); + dev_siomix_open(d); return 1; bad_close: sio_close(d->sio.hdl); + if (d->siomix.hdl) { + siomix_close(d->siomix.hdl); + d->siomix.hdl = NULL; + } + return 0; +} + +/* + * Open an alternate device. Upon success and if the new device is + * compatible with the old one, close the old device and continue + * using the new one. The new device is not started. + */ +int +dev_sio_reopen(struct dev *d) +{ + struct siomix_hdl *mixhdl; + struct sio_par par; + struct sio_hdl *hdl; + + hdl = dev_sio_openlist(d, d->mode & (MODE_PLAY | MODE_REC), &mixhdl); + if (hdl == NULL) { + if (log_level >= 1) { + dev_log(d); + log_puts(": couldn't open an alternate device\n"); + } + return 0; + } + + sio_initpar(&par); + par.bits = d->par.bits; + par.bps = d->par.bps; + par.sig = d->par.sig; + par.le = d->par.le; + par.msb = d->par.msb; + if (d->mode & SIO_PLAY) + par.pchan = d->pchan; + if (d->mode & SIO_REC) + par.rchan = d->rchan; + par.appbufsz = d->bufsz; + par.round = d->round; + par.rate = d->rate; + if (!sio_setpar(hdl, &par)) + goto bad_close; + if (!sio_getpar(hdl, &par)) + goto bad_close; + + /* check if new parameters are compatible with old ones */ + if (par.round != d->round || par.bufsz != d->bufsz || + par.rate != d->rate) { + if (log_level >= 1) { + dev_log(d); + log_puts(": alternate device not compatible\n"); + } + goto bad_close; + } + + /* close unused device */ + timo_del(&d->sio.watchdog); + file_del(d->sio.file); + sio_close(d->sio.hdl); + dev_siomix_close(d); + if (d->siomix.hdl) { + siomix_close(d->siomix.hdl); + d->siomix.hdl = NULL; + } + + /* update parameters */ + d->par.bits = par.bits; + d->par.bps = par.bps; + d->par.sig = par.sig; + d->par.le = par.le; + d->par.msb = par.msb; + if (d->mode & SIO_PLAY) + d->pchan = par.pchan; + if (d->mode & SIO_REC) + d->rchan = par.rchan; + + d->sio.hdl = hdl; + d->siomix.hdl = mixhdl; + d->sio.file = file_new(&dev_sio_ops, d, "dev", sio_nfds(hdl)); + sio_onmove(hdl, dev_sio_onmove, d); + return 1; +bad_close: + sio_close(hdl); + if (mixhdl) + siomix_close(mixhdl); return 0; } @@ -261,6 +359,10 @@ dev_sio_close(struct dev *d) timo_del(&d->sio.watchdog); file_del(d->sio.file); sio_close(d->sio.hdl); + if (d->siomix.hdl) { + siomix_close(d->siomix.hdl); + d->siomix.hdl = NULL; + } } void @@ -523,5 +625,6 @@ dev_sio_hup(void *arg) log_puts(": disconnected\n"); } #endif - dev_reopen(d); + if (!dev_reopen(d)) + dev_close(d); } diff --git a/sndiod/siofile.h b/sndiod/siofile.h index d20066f..69c2bb4 100644 --- a/sndiod/siofile.h +++ b/sndiod/siofile.h @@ -38,6 +38,7 @@ struct dev_sio { }; int dev_sio_open(struct dev *); +int dev_sio_reopen(struct dev *); void dev_sio_close(struct dev *); void dev_sio_log(struct dev *); void dev_sio_start(struct dev *); diff --git a/sndiod/sndiod.8 b/sndiod/sndiod.8 index 58b29bf..a52980b 100644 --- a/sndiod/sndiod.8 +++ b/sndiod/sndiod.8 @@ -186,7 +186,7 @@ Examples: .Va u8 , s16le , s24le3 , s24le4lsb . .It Fl F Ar device Specify an alternate device to use. -If doesn't work, the one given with the last +If it doesn't work, the one given with the last .Fl f or .Fl F @@ -261,7 +261,7 @@ The default is (i.e. full-duplex). .It Fl Q Ar port Specify an alternate MIDI port to use. -If doesn't work, the one given with the last +If it doesn't work, the one given with the last .Fl Q or .Fl q