diff options
30 files changed, 2505 insertions, 1083 deletions
diff --git a/lib/libsndio/aucat.c b/lib/libsndio/aucat.c index 89988433f68..65cef3bb5d7 100644 --- a/lib/libsndio/aucat.c +++ b/lib/libsndio/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.34 2010/01/20 13:17:22 jakemsr Exp $ */ +/* $OpenBSD: aucat.c,v 1.35 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -43,8 +43,6 @@ struct aucat_hdl { int maxwrite; /* latency constraint */ int events; /* events the user requested */ unsigned curvol, reqvol; /* current and requested volume */ - unsigned devbufsz; /* server side buffer size (in frames) */ - unsigned attached; /* stream attached to device */ int delta; /* some of received deltas */ }; @@ -153,14 +151,12 @@ aucat_runmsg(struct aucat_hdl *hdl) hdl->rtodo = hdl->rmsg.u.data.size; break; case AMSG_MOVE: - if (!hdl->attached) { - DPRINTF("aucat_runmsg: attached\n"); - hdl->maxwrite += hdl->devbufsz * hdl->wbpf; - hdl->attached = 1; - } + DPRINTF("aucat: tick, delta = %d\n", hdl->rmsg.u.ts.delta); + if (hdl->rmsg.u.ts.delta > 0) + hdl->maxwrite += hdl->rmsg.u.ts.delta * hdl->wbpf; hdl->delta += hdl->rmsg.u.ts.delta; if (hdl->delta >= 0) { - hdl->maxwrite += hdl->delta * hdl->wbpf; + DPRINTF("aucat: move: maxwrite = %d\n", hdl->maxwrite); sio_onmove_cb(&hdl->sio, hdl->delta); hdl->delta = 0; } @@ -316,8 +312,6 @@ aucat_start(struct sio_hdl *sh) hdl->wbpf = par.bps * par.pchan; hdl->rbpf = par.bps * par.rchan; hdl->maxwrite = hdl->wbpf * par.appbufsz; - hdl->devbufsz = par.bufsz - par.appbufsz; - hdl->attached = 0; hdl->delta = 0; AMSG_INIT(&hdl->wmsg); @@ -463,6 +457,8 @@ static int aucat_getcap(struct sio_hdl *sh, struct sio_cap *cap) { struct aucat_hdl *hdl = (struct aucat_hdl *)sh; + unsigned i, bps, le, sig, chan, rindex, rmult; + static unsigned rates[] = { 8000, 11025, 12000 }; AMSG_INIT(&hdl->wmsg); hdl->wmsg.cmd = AMSG_GETCAP; @@ -477,18 +473,70 @@ aucat_getcap(struct sio_hdl *sh, struct sio_cap *cap) hdl->sio.eof = 1; return 0; } - cap->enc[0].bits = hdl->rmsg.u.cap.bits; - cap->enc[0].bps = SIO_BPS(hdl->rmsg.u.cap.bits); - cap->enc[0].sig = 1; - cap->enc[0].le = SIO_LE_NATIVE; - cap->enc[0].msb = 1; - cap->rchan[0] = hdl->rmsg.u.cap.rchan; - cap->pchan[0] = hdl->rmsg.u.cap.pchan; - cap->rate[0] = hdl->rmsg.u.cap.rate; - cap->confs[0].enc = 1; - cap->confs[0].rchan = (hdl->rmsg.u.cap.rchan > 0) ? 1 : 0; - cap->confs[0].pchan = (hdl->rmsg.u.cap.pchan > 0) ? 1 : 0; - cap->confs[0].rate = 1; + bps = 1; + sig = le = 0; + cap->confs[0].enc = 0; + for (i = 0; i < SIO_NENC; i++) { + if (bps > 4) + break; + cap->confs[0].enc |= 1 << i; + cap->enc[i].bits = bps == 4 ? 24 : bps * 8; + cap->enc[i].bps = bps; + cap->enc[i].sig = sig ^ 1; + cap->enc[i].le = bps > 1 ? le : SIO_LE_NATIVE; + cap->enc[i].msb = 1; + le++; + if (le > 1 || bps == 1) { + le = 0; + sig++; + } + if (sig > 1 || (le == 0 && bps > 1)) { + sig = 0; + bps++; + } + } + chan = 1; + cap->confs[0].rchan = 0; + for (i = 0; i < SIO_NCHAN; i++) { + if (chan > 16) + break; + cap->confs[0].rchan |= 1 << i; + cap->rchan[i] = chan; + if (chan >= 12) { + chan += 4; + } else if (chan >= 2) { + chan += 2; + } else + chan++; + } + chan = 1; + cap->confs[0].pchan = 0; + for (i = 0; i < SIO_NCHAN; i++) { + if (chan > 16) + break; + cap->confs[0].pchan |= 1 << i; + cap->pchan[i] = chan; + if (chan >= 12) { + chan += 4; + } else if (chan >= 2) { + chan += 2; + } else + chan++; + } + rindex = 0; + rmult = 1; + cap->confs[0].rate = 0; + for (i = 0; i < SIO_NRATE; i++) { + if (rmult >= 32) + break; + cap->rate[i] = rates[rindex] * rmult; + cap->confs[0].rate |= 1 << i; + rindex++; + if (rindex == sizeof(rates) / sizeof(unsigned)) { + rindex = 0; + rmult *= 2; + } + } cap->nconf = 1; return 1; } @@ -532,6 +580,7 @@ aucat_read(struct sio_hdl *sh, void *buf, size_t len) hdl->rstate = STATE_MSG; hdl->rtodo = sizeof(struct amsg); } + DPRINTF("aucat: read: n = %zd\n", n); return n; } @@ -611,6 +660,7 @@ aucat_write(struct sio_hdl *sh, const void *buf, size_t len) return 0; } hdl->maxwrite -= n; + DPRINTF("aucat: write: n = %zd, maxwrite = %d\n", n, hdl->maxwrite); hdl->wtodo -= n; if (hdl->wtodo == 0) { hdl->wstate = STATE_IDLE; @@ -631,6 +681,7 @@ aucat_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events) events |= POLLIN; pfd->fd = hdl->fd; pfd->events = events; + DPRINTF("aucat: pollfd: %x -> %x\n", hdl->events, pfd->events); return 1; } @@ -654,6 +705,7 @@ aucat_revents(struct sio_hdl *sh, struct pollfd *pfd) } if (hdl->sio.eof) return POLLHUP; + DPRINTF("aucat: revents: %x\n", revents & hdl->events); return revents & (hdl->events | POLLHUP); } diff --git a/lib/libsndio/sio_open.3 b/lib/libsndio/sio_open.3 index 15c3a689262..689affe68a8 100644 --- a/lib/libsndio/sio_open.3 +++ b/lib/libsndio/sio_open.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: sio_open.3,v 1.22 2009/12/30 08:27:12 ratchov Exp $ +.\" $OpenBSD: sio_open.3,v 1.23 2010/04/06 20:07:01 ratchov Exp $ .\" .\" Copyright (c) 2007 Alexandre Ratchov <alex@caoua.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: December 30 2009 $ +.Dd $Mdocdate: April 6 2010 $ .Dt SIO_OPEN 3 .Os .Sh NAME @@ -239,36 +239,26 @@ or constants. .El .Pp -There are two approaches to negotiate parameters of the stream: +The following approach is recommended to negotiate parameters of the stream: .Bl -bullet .It -Advanced applications may use native parameters of -the audio subsystem. -This is the best approach from a performance point of view -since it involves no extra format conversions. -The -.Fn sio_getcap , -described below, -can be used to get the list of native parameter sets and then -.Fn sio_initpar -and -.Fn sio_setpar -can be used to select a working set. -.It -Simpler applications that do not have performance constraints may set up -the audio subsystem to use their own parameters. -The +Initialize a .Va sio_par -structure must be initialized using the +structure using .Fn sio_initpar -function, filled with the desired parameters and -the +and fill it with +the desired parameters. +If the application supports any value for a given parameter, +then the corresponding parameter should be left unset. +Then call .Fn sio_setpar -function must be called. -Finally, the +to request the stream to use them. +.It +Call .Fn sio_getpar -function should be used to ensure that parameters were actually -accepted. +to retrieve the actual parameters of the stream +and check that they are usable. +If they are not, then fail or set up a conversion layer. Sometimes the rate set can be slightly different to what was requested. A difference of about 0.5% is not audible and should be ignored. .El @@ -297,28 +287,17 @@ Can be used to set the .Va le parameter when native byte order is required. .El -.Pp -Note that (once initialized with the -.Fn sio_initpar -function), not all fields of the -.Va sio_par -structure must be filled; it is recommended to fill only -the required parameters, as other ones will default to -reasonable values. -This approach also ensures that if, in the future, newer parameters -are added, then older unaware applications will continue to -behave correctly. .Ss Getting stream capabilities -Advanced applications can fetch the native -parameters of the audio subsystem and then choose parameters -optimal for both the application and the audio subsystem. -In this case applications must be able to do -the necessary format conversions. +There's no way to get an exhaustive list of all parameter +combinations the stream supports. +Applications that need to have a set of working +parameter combinations in advance can use the +.Fn sio_getcap +function. +.Pp The .Va sio_cap -structure, filled by the -.Fn sio_getcap -function, contains the list of parameter configurations. +structure contains the list of parameter configurations. Each configuration contains multiple parameter sets. The application must examine all configurations, and choose its parameter set from diff --git a/lib/libsndio/sndio.c b/lib/libsndio/sndio.c index a4da9322070..57cf91e2976 100644 --- a/lib/libsndio/sndio.c +++ b/lib/libsndio/sndio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sndio.c,v 1.23 2009/08/28 10:52:14 ratchov Exp $ */ +/* $OpenBSD: sndio.c,v 1.24 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -547,7 +547,7 @@ sio_onmove_cb(struct sio_hdl *hdl, int delta) struct timeval tv0, dtv; long long playpos; - if (sio_debug >= 2 && (hdl->mode & SIO_PLAY)) { + if (sio_debug >= 3 && (hdl->mode & SIO_PLAY)) { gettimeofday(&tv0, NULL); timersub(&tv0, &hdl->tv, &dtv); DPRINTF("%ld.%06ld: ", dtv.tv_sec, dtv.tv_usec); diff --git a/lib/libsndio/sun.c b/lib/libsndio/sun.c index 2e8502cd203..11a61053add 100644 --- a/lib/libsndio/sun.c +++ b/lib/libsndio/sun.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sun.c,v 1.27 2010/02/10 23:03:53 ratchov Exp $ */ +/* $OpenBSD: sun.c,v 1.28 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -877,7 +877,7 @@ sun_revents(struct sio_hdl *sh, struct pollfd *pfd) hdl->idelta -= dmove; hdl->odelta -= dmove; - if ((revents & POLLOUT) && !(hdl->sio.mode & SIO_REC)) { + if ((revents & POLLOUT) && (hdl->sio.mode & SIO_PLAY)) { if (ioctl(hdl->fd, AUDIO_GETOOFFS, &ao) < 0) { DPERROR("sun_revents: GETOOFFS"); hdl->sio.eof = 1; @@ -890,7 +890,7 @@ sun_revents(struct sio_hdl *sh, struct pollfd *pfd) hdl->odelta = 0; } } - if ((revents & POLLIN) && (hdl->sio.mode & SIO_REC)) { + if ((revents & POLLIN) && !(hdl->sio.mode & SIO_PLAY)) { if (ioctl(hdl->fd, AUDIO_GETIOFFS, &ao) < 0) { DPERROR("sun_revents: GETIOFFS"); hdl->sio.eof = 1; diff --git a/usr.bin/aucat/abuf.c b/usr.bin/aucat/abuf.c index fcffe898b70..b1efba206fe 100644 --- a/usr.bin/aucat/abuf.c +++ b/usr.bin/aucat/abuf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: abuf.c,v 1.20 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: abuf.c,v 1.21 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -105,12 +105,9 @@ abuf_new(unsigned nfr, struct aparams *par) /* * fill fifo pointers */ - buf->len = len; + buf->len = nfr; buf->used = 0; buf->start = 0; - buf->abspos = 0; - buf->silence = 0; - buf->drop = 0; buf->rproc = NULL; buf->wproc = NULL; buf->duplex = NULL; @@ -161,9 +158,6 @@ abuf_clear(struct abuf *buf) #endif buf->used = 0; buf->start = 0; - buf->abspos = 0; - buf->silence = 0; - buf->drop = 0; } /* @@ -191,7 +185,7 @@ abuf_rgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs) if (count > used) count = used; *rsize = count; - return (unsigned char *)buf + sizeof(struct abuf) + start; + return (unsigned char *)buf + sizeof(struct abuf) + start * buf->bpf; } /* @@ -213,7 +207,6 @@ abuf_rdiscard(struct abuf *buf, unsigned count) buf->start += count; if (buf->start >= buf->len) buf->start -= buf->len; - buf->abspos += count; } /* @@ -260,7 +253,7 @@ abuf_wgetblk(struct abuf *buf, unsigned *rsize, unsigned ofs) if (count > avail) count = avail; *rsize = count; - return (unsigned char *)buf + sizeof(struct abuf) + end; + return (unsigned char *)buf + sizeof(struct abuf) + end * buf->bpf; } /* @@ -271,47 +264,17 @@ int abuf_flush_do(struct abuf *buf) { struct aproc *p; - unsigned count; - if (buf->drop > 0) { - count = buf->drop; - if (count > buf->used) - count = buf->used; - if (count == 0) { -#ifdef DEBUG - if (debug_level >= 4) { - abuf_dbg(buf); - dbg_puts(": flush: no data to drop\n"); - } -#endif - return 0; - } - abuf_rdiscard(buf, count); - buf->drop -= count; -#ifdef DEBUG - if (debug_level >= 4) { - abuf_dbg(buf); - dbg_puts(": flush: dropped "); - dbg_putu(count); - dbg_puts(", to drop = "); - dbg_putu(buf->drop); - dbg_puts("\n"); - } -#endif - } else { - p = buf->rproc; - if (!p) - return 0; + p = buf->rproc; + if (!p) + return 0; #ifdef DEBUG - if (debug_level >= 4) { - aproc_dbg(p); - dbg_puts(": in\n"); - } -#endif - if (!p->ops->in(p, buf)) - return 0; + if (debug_level >= 4) { + aproc_dbg(p); + dbg_puts(": in\n"); } - return 1; +#endif + return p->ops->in(p, buf); } /* @@ -322,51 +285,17 @@ int abuf_fill_do(struct abuf *buf) { struct aproc *p; - unsigned char *data; - unsigned count; - - if (buf->silence > 0) { - data = abuf_wgetblk(buf, &count, 0); - if (count >= buf->silence) - count = buf->silence; - if (count == 0) { -#ifdef DEBUG - if (debug_level >= 4) { - abuf_dbg(buf); - dbg_puts(": fill: no space for silence\n"); - } -#endif - return 0; - } - memset(data, 0, count); - abuf_wcommit(buf, count); - buf->silence -= count; -#ifdef DEBUG - if (debug_level >= 4) { - abuf_dbg(buf); - dbg_puts(": fill: inerted "); - dbg_putu(count); - dbg_puts(", remaining silence = "); - dbg_putu(buf->silence); - dbg_puts("\n"); - } -#endif - p = buf->wproc; - } else { - p = buf->wproc; - if (!p) - return 0; + + p = buf->wproc; + if (!p) + return 0; #ifdef DEBUG - if (debug_level >= 4) { - aproc_dbg(p); - dbg_puts(": out\n"); - } -#endif - if (!p->ops->out(p, buf)) { - return 0; - } + if (debug_level >= 4) { + aproc_dbg(p); + dbg_puts(": out\n"); } - return 1; +#endif + return p->ops->out(p, buf); } /* diff --git a/usr.bin/aucat/abuf.h b/usr.bin/aucat/abuf.h index 79c5e9a773d..6ce365dc085 100644 --- a/usr.bin/aucat/abuf.h +++ b/usr.bin/aucat/abuf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: abuf.h,v 1.21 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: abuf.h,v 1.22 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -39,9 +39,6 @@ struct abuf { unsigned start; /* offset where data starts */ unsigned used; /* valid data */ unsigned len; /* size of the ring */ - unsigned abspos; /* frame number of the start position */ - unsigned silence; /* silence to insert on next write */ - unsigned drop; /* bytes to drop on next read */ struct aproc *rproc; /* reader */ struct aproc *wproc; /* writer */ struct abuf *duplex; /* link to buffer of the other direction */ @@ -56,8 +53,9 @@ struct abuf { int weight; /* dynamic range */ int maxweight; /* max dynamic range allowed */ unsigned vol; /* volume within the dynamic range */ - unsigned done; /* bytes ready */ + unsigned done; /* frames ready */ unsigned xrun; /* underrun policy */ + int drop; /* frames to drop on next read */ } mix; struct { unsigned st; /* MIDI running status */ @@ -73,11 +71,12 @@ struct abuf { */ union { struct { - unsigned todo; /* bytes to process */ + unsigned todo; /* frames to process */ } mix; struct { - unsigned done; /* bytes copied */ + unsigned done; /* frames copied */ unsigned xrun; /* overrun policy */ + int silence; /* silence to add on next write */ } sub; } w; }; @@ -86,12 +85,12 @@ struct abuf { * the buffer contains at least one frame. This macro should * be used to check if the buffer can be flushed */ -#define ABUF_ROK(b) ((b)->used >= (b)->bpf) +#define ABUF_ROK(b) ((b)->used > 0) /* * there's room for at least one frame */ -#define ABUF_WOK(b) ((b)->len - (b)->used >= (b)->bpf) +#define ABUF_WOK(b) ((b)->len - (b)->used > 0) /* * the buffer is empty and has no writer anymore @@ -105,18 +104,6 @@ struct abuf { */ #define ABUF_HUP(b) (!ABUF_WOK(b) && (b)->rproc == NULL) -/* - * similar to !ABUF_WOK, but is used for file i/o, where - * operation may not involve an integer number of frames - */ -#define ABUF_FULL(b) ((b)->used == (b)->len) - -/* - * same as !ABUF_ROK, but used for files, where - * operations are byte orientated, not frame-oriented - */ -#define ABUF_EMPTY(b) ((b)->used == 0) - struct abuf *abuf_new(unsigned, struct aparams *); void abuf_del(struct abuf *); void abuf_dbg(struct abuf *); diff --git a/usr.bin/aucat/amsg.h b/usr.bin/aucat/amsg.h index c712c998ae3..911cf572f16 100644 --- a/usr.bin/aucat/amsg.h +++ b/usr.bin/aucat/amsg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: amsg.h,v 1.14 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: amsg.h,v 1.15 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -87,7 +87,8 @@ struct amsg { #define AMSG_REC 0x2 /* audio recording */ #define AMSG_MIDIIN 0x4 /* MIDI thru input */ #define AMSG_MIDIOUT 0x8 /* MIDI thru output */ -#define AMSG_MIXER 0x10 /* MIDI mixer */ +#define AMSG_MON 0x10 /* audio monitoring */ +#define AMSG_RECMASK (AMSG_REC | AMSG_MON) /* can record ? */ uint16_t proto; /* protocol type */ #define AMSG_VERSION 1 uint8_t version; /* protocol version */ diff --git a/usr.bin/aucat/aproc.c b/usr.bin/aucat/aproc.c index 318326bbb42..b1001f2ab65 100644 --- a/usr.bin/aucat/aproc.c +++ b/usr.bin/aucat/aproc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aproc.c,v 1.50 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: aproc.c,v 1.51 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -48,6 +48,18 @@ #include "dbg.h" #endif +/* + * Same as ABUF_ROK(), but consider that a buffer is + * readable if there's silence pending to be inserted + */ +#define MIX_ROK(buf) (ABUF_ROK(buf) || (buf)->r.mix.drop < 0) + +/* + * Same as ABUF_WOK(), but consider that a buffer is + * writeable if there are samples to drop + */ +#define SUB_WOK(buf) (ABUF_WOK(buf) || (buf)->w.sub.silence < 0) + #ifdef DEBUG void aproc_dbg(struct aproc *p) @@ -275,20 +287,40 @@ aproc_depend(struct aproc *p, struct aproc *dep) } int -rfile_in(struct aproc *p, struct abuf *ibuf_dummy) +rfile_do(struct aproc *p, unsigned todo, unsigned *done) { struct abuf *obuf = LIST_FIRST(&p->obuflist); struct file *f = p->u.io.file; unsigned char *data; - unsigned count; + unsigned n, count, off; - if (ABUF_FULL(obuf) || !(f->state & FILE_ROK)) - return 0; + off = p->u.io.partial; data = abuf_wgetblk(obuf, &count, 0); - count = file_read(f, data, count); - if (count == 0) + if (count > todo) + count = todo; + n = file_read(f, data + off, count * obuf->bpf - off); + if (n == 0) + return 0; + n += off; + p->u.io.partial = n % obuf->bpf; + count = n / obuf->bpf; + if (count > 0) + abuf_wcommit(obuf, count); + if (done) + *done = count; + return 1; +} + +int +rfile_in(struct aproc *p, struct abuf *ibuf_dummy) +{ + struct abuf *obuf = LIST_FIRST(&p->obuflist); + struct file *f = p->u.io.file; + + if (!ABUF_WOK(obuf) || !(f->state & FILE_ROK)) + return 0; + if (!rfile_do(p, obuf->len, NULL)) return 0; - abuf_wcommit(obuf, count); if (!abuf_flush(obuf)) return 0; return 1; @@ -298,18 +330,13 @@ int rfile_out(struct aproc *p, struct abuf *obuf) { struct file *f = p->u.io.file; - unsigned char *data; - unsigned count; if (f->state & FILE_RINUSE) return 0; - if (ABUF_FULL(obuf) || !(f->state & FILE_ROK)) + if (!ABUF_WOK(obuf) || !(f->state & FILE_ROK)) return 0; - data = abuf_wgetblk(obuf, &count, 0); - count = file_read(f, data, count); - if (count == 0) + if (!rfile_do(p, obuf->len, NULL)) return 0; - abuf_wcommit(obuf, count); return 1; } @@ -322,6 +349,12 @@ rfile_done(struct aproc *p) if (f == NULL) return; /* + * disconnect from file structure + */ + f->rproc = NULL; + p->u.io.file = NULL; + + /* * all buffers must be detached before deleting f->wproc, * because otherwise it could trigger this code again */ @@ -329,11 +362,18 @@ rfile_done(struct aproc *p) if (obuf) abuf_eof(obuf); if (f->wproc) { - f->rproc = NULL; aproc_del(f->wproc); } else file_del(f); - p->u.io.file = NULL; + +#ifdef DEBUG + if (debug_level >= 2 && p->u.io.partial > 0) { + aproc_dbg(p); + dbg_puts(": "); + dbg_putu(p->u.io.partial); + dbg_puts(" bytes lost in partial read\n"); + } +#endif } void @@ -368,6 +408,7 @@ rfile_new(struct file *f) p = aproc_new(&rfile_ops, f->name); p->u.io.file = f; + p->u.io.partial = 0; f->rproc = p; return p; } @@ -381,6 +422,12 @@ wfile_done(struct aproc *p) if (f == NULL) return; /* + * disconnect from file structure + */ + f->wproc = NULL; + p->u.io.file = NULL; + + /* * all buffers must be detached before deleting f->rproc, * because otherwise it could trigger this code again */ @@ -388,29 +435,54 @@ wfile_done(struct aproc *p) if (ibuf) abuf_hup(ibuf); if (f->rproc) { - f->wproc = NULL; aproc_del(f->rproc); } else file_del(f); - p->u.io.file = NULL; +#ifdef DEBUG + if (debug_level >= 2 && p->u.io.partial > 0) { + aproc_dbg(p); + dbg_puts(": "); + dbg_putu(p->u.io.partial); + dbg_puts(" bytes lost in partial write\n"); + } +#endif } int -wfile_in(struct aproc *p, struct abuf *ibuf) +wfile_do(struct aproc *p, unsigned todo, unsigned *done) { + struct abuf *ibuf = LIST_FIRST(&p->ibuflist); struct file *f = p->u.io.file; unsigned char *data; - unsigned count; + unsigned n, count, off; + + off = p->u.io.partial; + data = abuf_rgetblk(ibuf, &count, 0); + if (count > todo) + count = todo; + n = file_write(f, data + off, count * ibuf->bpf - off); + if (n == 0) + return 0; + n += off; + p->u.io.partial = n % ibuf->bpf; + count = n / ibuf->bpf; + if (count > 0) + abuf_rdiscard(ibuf, count); + if (done) + *done = count; + return 1; +} +int +wfile_in(struct aproc *p, struct abuf *ibuf) +{ + struct file *f = p->u.io.file; if (f->state & FILE_WINUSE) return 0; - if (ABUF_EMPTY(ibuf) || !(f->state & FILE_WOK)) + if (!ABUF_ROK(ibuf) || !(f->state & FILE_WOK)) return 0; - data = abuf_rgetblk(ibuf, &count, 0); - count = file_write(f, data, count); - if (count == 0) + if (!wfile_do(p, ibuf->len, NULL)) return 0; - abuf_rdiscard(ibuf, count); return 1; } @@ -419,22 +491,13 @@ wfile_out(struct aproc *p, struct abuf *obuf_dummy) { struct abuf *ibuf = LIST_FIRST(&p->ibuflist); struct file *f = p->u.io.file; - unsigned char *data; - unsigned count; if (!abuf_fill(ibuf)) return 0; - if (ABUF_EMPTY(ibuf) || !(f->state & FILE_WOK)) - return 0; - data = abuf_rgetblk(ibuf, &count, 0); - if (count == 0) { - /* XXX: this can't happen, right ? */ + if (!ABUF_ROK(ibuf) || !(f->state & FILE_WOK)) return 0; - } - count = file_write(f, data, count); - if (count == 0) + if (!wfile_do(p, ibuf->len, NULL)) return 0; - abuf_rdiscard(ibuf, count); return 1; } @@ -470,45 +533,83 @@ wfile_new(struct file *f) p = aproc_new(&wfile_ops, f->name); p->u.io.file = f; + p->u.io.partial = 0; f->wproc = p; return p; } /* + * Drop as much as possible samples from the reader end, + * negative values mean ``insert silence''. + */ +void +mix_drop(struct abuf *buf, int extra) +{ + unsigned count; + + buf->r.mix.drop += extra; + while (buf->r.mix.drop > 0) { + count = buf->r.mix.drop; + if (count > buf->used) + count = buf->used; + if (count == 0) { +#ifdef DEBUG + if (debug_level >= 4) { + abuf_dbg(buf); + dbg_puts(": drop: no data\n"); + } +#endif + return; + } + abuf_rdiscard(buf, count); + buf->r.mix.drop -= count; +#ifdef DEBUG + if (debug_level >= 4) { + abuf_dbg(buf); + dbg_puts(": dropped "); + dbg_putu(count); + dbg_puts(", to drop = "); + dbg_putu(buf->r.mix.drop); + dbg_puts("\n"); + } +#endif + } +} + +/* * Append the given amount of silence (or less if there's not enough - * space), and crank mixitodo accordingly. + * space), and crank w.mix.todo accordingly. */ void -mix_bzero(struct abuf *obuf, unsigned zcount) +mix_bzero(struct abuf *obuf) { short *odata; unsigned ocount; + odata = (short *)abuf_wgetblk(obuf, &ocount, obuf->w.mix.todo); + if (ocount == 0) + return; + memset(odata, 0, ocount * obuf->bpf); + obuf->w.mix.todo += ocount; #ifdef DEBUG if (debug_level >= 4) { abuf_dbg(obuf); dbg_puts(": bzero("); - dbg_putu(zcount); + dbg_putu(obuf->w.mix.todo); dbg_puts(")\n"); } #endif - odata = (short *)abuf_wgetblk(obuf, &ocount, obuf->w.mix.todo); - ocount -= ocount % obuf->bpf; - if (ocount > zcount) - ocount = zcount; - memset(odata, 0, ocount); - obuf->w.mix.todo += ocount; } /* * Mix an input block over an output block. */ -void +unsigned mix_badd(struct abuf *ibuf, struct abuf *obuf) { short *idata, *odata; unsigned i, j, icnt, onext, ostart; - unsigned scount, icount, ocount, zcount; + unsigned scount, icount, ocount; int vol; #ifdef DEBUG @@ -518,31 +619,37 @@ mix_badd(struct abuf *ibuf, struct abuf *obuf) dbg_putu(ibuf->r.mix.done); dbg_puts("/"); dbg_putu(obuf->w.mix.todo); + dbg_puts(", drop = "); + dbg_puti(ibuf->r.mix.drop); dbg_puts("\n"); } #endif /* - * Calculate the maximum we can read. + * Insert silence for xrun correction */ - idata = (short *)abuf_rgetblk(ibuf, &icount, 0); - icount /= ibuf->bpf; - if (icount == 0) - return; + if (ibuf->r.mix.drop < 0) { + icount = -ibuf->r.mix.drop; + ocount = obuf->len - obuf->used; + if (ocount > obuf->w.mix.todo) + ocount = obuf->w.mix.todo; + scount = (icount < ocount) ? icount : ocount; + ibuf->r.mix.done += scount; + ibuf->r.mix.drop += scount; + } /* - * Zero-fill if necessary. + * Calculate the maximum we can read. */ - zcount = ibuf->r.mix.done + icount * obuf->bpf; - if (zcount > obuf->w.mix.todo) - mix_bzero(obuf, zcount - obuf->w.mix.todo); + idata = (short *)abuf_rgetblk(ibuf, &icount, 0); + if (icount == 0) + return 0; /* * Calculate the maximum we can write. */ odata = (short *)abuf_wgetblk(obuf, &ocount, ibuf->r.mix.done); - ocount /= obuf->bpf; if (ocount == 0) - return; + return 0; vol = (ibuf->r.mix.weight * ibuf->r.mix.vol) >> ADATA_SHIFT; ostart = ibuf->cmin - obuf->cmin; @@ -558,30 +665,30 @@ mix_badd(struct abuf *ibuf, struct abuf *obuf) } odata += onext; } - abuf_rdiscard(ibuf, scount * ibuf->bpf); - ibuf->r.mix.done += scount * obuf->bpf; + abuf_rdiscard(ibuf, scount); + ibuf->r.mix.done += scount; #ifdef DEBUG if (debug_level >= 4) { abuf_dbg(ibuf); dbg_puts(": badd: done = "); - dbg_putu(scount); - dbg_puts(", todo = "); dbg_putu(ibuf->r.mix.done); dbg_puts("/"); dbg_putu(obuf->w.mix.todo); dbg_puts("\n"); } #endif + return scount; } /* * Handle buffer underrun, return 0 if stream died. */ int -mix_xrun(struct abuf *i, struct abuf *obuf) +mix_xrun(struct aproc *p, struct abuf *i) { - unsigned fdrop; + struct abuf *obuf = LIST_FIRST(&p->obuflist); + unsigned fdrop, remain; if (i->r.mix.done > 0) return 1; @@ -589,22 +696,34 @@ mix_xrun(struct abuf *i, struct abuf *obuf) abuf_hup(i); return 0; } - mix_bzero(obuf, obuf->len); - fdrop = obuf->w.mix.todo / obuf->bpf; + fdrop = obuf->w.mix.todo; #ifdef DEBUG if (debug_level >= 3) { abuf_dbg(i); dbg_puts(": underrun, dropping "); dbg_putu(fdrop); dbg_puts(" + "); - dbg_putu(i->drop / i->bpf); + dbg_putu(i->r.mix.drop); dbg_puts("\n"); } #endif - i->r.mix.done += fdrop * obuf->bpf; + i->r.mix.done += fdrop; if (i->r.mix.xrun == XRUN_SYNC) - i->drop += fdrop * i->bpf; + mix_drop(i, fdrop); else { + remain = fdrop % p->u.mix.round; + if (remain) + remain = p->u.mix.round - remain; + mix_drop(i, -(int)remain); + fdrop += remain; +#ifdef DEBUG + if (debug_level >= 3) { + abuf_dbg(i); + dbg_puts(": underrun, adding "); + dbg_putu(remain); + dbg_puts("\n"); + } +#endif abuf_opos(i, -(int)fdrop); if (i->duplex) { #ifdef DEBUG @@ -613,7 +732,7 @@ mix_xrun(struct abuf *i, struct abuf *obuf) dbg_puts(": full-duplex resync\n"); } #endif - i->duplex->drop += fdrop * i->duplex->bpf; + sub_silence(i->duplex, -(int)fdrop); abuf_ipos(i->duplex, -(int)fdrop); } } @@ -625,6 +744,8 @@ mix_in(struct aproc *p, struct abuf *ibuf) { struct abuf *i, *inext, *obuf = LIST_FIRST(&p->obuflist); unsigned odone; + unsigned maxwrite; + unsigned scount; #ifdef DEBUG if (debug_level >= 4) { @@ -640,27 +761,50 @@ mix_in(struct aproc *p, struct abuf *ibuf) dbg_puts("\n"); } #endif - if (!ABUF_ROK(ibuf)) + if (!MIX_ROK(ibuf)) return 0; - odone = obuf->len; + mix_bzero(obuf); + scount = 0; + odone = obuf->w.mix.todo; for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, ient); - if (!abuf_fill(i)) + if (i->r.mix.drop >= 0 && !abuf_fill(i)) continue; /* eof */ - mix_badd(i, obuf); + mix_drop(i, 0); + scount += mix_badd(i, obuf); if (odone > i->r.mix.done) odone = i->r.mix.done; } - if (LIST_EMPTY(&p->ibuflist) || odone == 0) + if (LIST_EMPTY(&p->ibuflist) || scount == 0) return 0; - p->u.mix.lat += odone / obuf->bpf; - LIST_FOREACH(i, &p->ibuflist, ient) { - i->r.mix.done -= odone; +#ifdef DEBUG + if (debug_level >= 4) { + aproc_dbg(p); + dbg_puts(": maxwrite = "); + dbg_putu(p->u.mix.maxlat); + dbg_puts(" - "); + dbg_putu(p->u.mix.lat); + dbg_puts(" = "); + dbg_putu(p->u.mix.maxlat - p->u.mix.lat); + dbg_puts("\n"); + } +#endif + maxwrite = p->u.mix.maxlat - p->u.mix.lat; + if (maxwrite > 0) { + if (odone > maxwrite) + odone = maxwrite; + p->u.mix.lat += odone; + p->u.mix.abspos += odone; + LIST_FOREACH(i, &p->ibuflist, ient) { + i->r.mix.done -= odone; + } + abuf_wcommit(obuf, odone); + obuf->w.mix.todo -= odone; + if (APROC_OK(p->u.mix.mon)) + mon_snoop(p->u.mix.mon, obuf, obuf->used - odone, odone); + if (!abuf_flush(obuf)) + return 0; /* hup */ } - abuf_wcommit(obuf, odone); - obuf->w.mix.todo -= odone; - if (!abuf_flush(obuf)) - return 0; /* hup */ return 1; } @@ -669,6 +813,8 @@ mix_out(struct aproc *p, struct abuf *obuf) { struct abuf *i, *inext; unsigned odone; + unsigned maxwrite; + unsigned scount; #ifdef DEBUG if (debug_level >= 4) { @@ -686,18 +832,34 @@ mix_out(struct aproc *p, struct abuf *obuf) #endif if (!ABUF_WOK(obuf)) return 0; +#ifdef DEBUG + if (debug_level >= 4) { + aproc_dbg(p); + dbg_puts(": maxwrite = "); + dbg_putu(p->u.mix.maxlat); + dbg_puts(" - "); + dbg_putu(p->u.mix.lat); + dbg_puts(" = "); + dbg_putu(p->u.mix.maxlat - p->u.mix.lat); + dbg_puts("\n"); + } +#endif + maxwrite = p->u.mix.maxlat - p->u.mix.lat; + mix_bzero(obuf); + scount = 0; odone = obuf->len; for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, ient); - if (!abuf_fill(i)) + if (i->r.mix.drop >= 0 && !abuf_fill(i)) continue; /* eof */ - if (!ABUF_ROK(i)) { + mix_drop(i, 0); + if (maxwrite > 0 && !MIX_ROK(i)) { if (p->flags & APROC_DROP) { - if (!mix_xrun(i, obuf)) + if (!mix_xrun(p, i)) continue; } } else - mix_badd(i, obuf); + scount += mix_badd(i, obuf); if (odone > i->r.mix.done) odone = i->r.mix.done; } @@ -708,25 +870,31 @@ mix_out(struct aproc *p, struct abuf *obuf) } if (!(p->flags & APROC_DROP)) return 0; - mix_bzero(obuf, obuf->len); odone = obuf->w.mix.todo; - p->u.mix.idle += odone / obuf->bpf; + p->u.mix.idle += odone; } - if (odone == 0) - return 0; - p->u.mix.lat += odone / obuf->bpf; - LIST_FOREACH(i, &p->ibuflist, ient) { - i->r.mix.done -= odone; + if (maxwrite > 0) { + if (odone > maxwrite) + odone = maxwrite; + p->u.mix.lat += odone; + p->u.mix.abspos += odone; + LIST_FOREACH(i, &p->ibuflist, ient) { + i->r.mix.done -= odone; + } + abuf_wcommit(obuf, odone); + obuf->w.mix.todo -= odone; + if (APROC_OK(p->u.mix.mon)) + mon_snoop(p->u.mix.mon, obuf, obuf->used - odone, odone); } - abuf_wcommit(obuf, odone); - obuf->w.mix.todo -= odone; + if (scount == 0) + return 0; return 1; } void mix_eof(struct aproc *p, struct abuf *ibuf) { - struct abuf *i, *obuf = LIST_FIRST(&p->obuflist); + struct abuf *i, *inext, *obuf = LIST_FIRST(&p->obuflist); unsigned odone; mix_setmaster(p); @@ -742,8 +910,11 @@ mix_eof(struct aproc *p, struct abuf *ibuf) * Find a blocked input. */ odone = obuf->len; - LIST_FOREACH(i, &p->ibuflist, ient) { - if (ABUF_ROK(i) && i->r.mix.done < obuf->w.mix.todo) { + for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) { + inext = LIST_NEXT(i, ient); + if (!abuf_fill(i)) + continue; + if (MIX_ROK(i) && i->r.mix.done < obuf->w.mix.todo) { abuf_run(i); return; } @@ -781,6 +952,7 @@ mix_newin(struct aproc *p, struct abuf *ibuf) ibuf->r.mix.weight = ADATA_UNIT; ibuf->r.mix.maxweight = ADATA_UNIT; ibuf->r.mix.xrun = XRUN_IGNORE; + ibuf->r.mix.drop = 0; } void @@ -790,8 +962,8 @@ mix_newout(struct aproc *p, struct abuf *obuf) if (debug_level >= 3) { aproc_dbg(p); dbg_puts(": newin, will use "); - dbg_putu(obuf->len / obuf->bpf); - dbg_puts(" fr\n"); + dbg_putu(obuf->len); + dbg_puts("\n"); } #endif obuf->w.mix.todo = 0; @@ -800,6 +972,7 @@ mix_newout(struct aproc *p, struct abuf *obuf) void mix_opos(struct aproc *p, struct abuf *obuf, int delta) { + p->u.mix.lat -= delta; #ifdef DEBUG if (debug_level >= 4) { aproc_dbg(p); @@ -807,13 +980,14 @@ mix_opos(struct aproc *p, struct abuf *obuf, int delta) dbg_puti(p->u.mix.lat); dbg_puts("/"); dbg_puti(p->u.mix.maxlat); - dbg_puts(" fr\n"); + dbg_puts("\n"); } #endif - p->u.mix.lat -= delta; - if (p->u.mix.ctl) + if (APROC_OK(p->u.mix.ctl)) ctl_ontick(p->u.mix.ctl, delta); aproc_opos(p, obuf, delta); + if (APROC_OK(p->u.mix.mon)) + p->u.mix.mon->ops->ipos(p->u.mix.mon, NULL, delta); } struct aproc_ops mix_ops = { @@ -830,14 +1004,16 @@ struct aproc_ops mix_ops = { }; struct aproc * -mix_new(char *name, int maxlat, struct aproc *ctl) +mix_new(char *name, int maxlat, unsigned round, struct aproc *ctl) { struct aproc *p; p = aproc_new(&mix_ops, name); p->u.mix.idle = 0; p->u.mix.lat = 0; + p->u.mix.round = round; p->u.mix.maxlat = maxlat; + p->u.mix.abspos = 0; p->u.mix.ctl = ctl; return p; } @@ -895,6 +1071,7 @@ mix_clear(struct aproc *p) struct abuf *obuf = LIST_FIRST(&p->obuflist); p->u.mix.lat = 0; + p->u.mix.abspos = 0; obuf->w.mix.todo = 0; } @@ -907,16 +1084,19 @@ mix_prime(struct aproc *p) for (;;) { if (!ABUF_WOK(obuf)) break; - todo = (p->u.mix.maxlat - p->u.mix.lat) * obuf->bpf; + todo = p->u.mix.maxlat - p->u.mix.lat; if (todo == 0) break; - mix_bzero(obuf, obuf->len); + mix_bzero(obuf); count = obuf->w.mix.todo; if (count > todo) count = todo; obuf->w.mix.todo -= count; - p->u.mix.lat += count / obuf->bpf; + p->u.mix.lat += count; + p->u.mix.abspos += count; abuf_wcommit(obuf, count); + if (APROC_OK(p->u.mix.mon)) + mon_snoop(p->u.mix.mon, obuf, 0, count); abuf_flush(obuf); } #ifdef DEBUG @@ -932,6 +1112,45 @@ mix_prime(struct aproc *p) } /* + * Append as much as possible silence on the writer end + */ +void +sub_silence(struct abuf *buf, int extra) +{ + unsigned char *data; + unsigned count; + + buf->w.sub.silence += extra; + if (buf->w.sub.silence > 0) { + data = abuf_wgetblk(buf, &count, 0); + if (count >= buf->w.sub.silence) + count = buf->w.sub.silence; + if (count == 0) { +#ifdef DEBUG + if (debug_level >= 4) { + abuf_dbg(buf); + dbg_puts(": no space for silence\n"); + } +#endif + return; + } + memset(data, 0, count * buf->bpf); + abuf_wcommit(buf, count); + buf->w.sub.silence -= count; +#ifdef DEBUG + if (debug_level >= 4) { + abuf_dbg(buf); + dbg_puts(": appended "); + dbg_putu(count); + dbg_puts(", remaining silence = "); + dbg_putu(buf->w.sub.silence); + dbg_puts("\n"); + } +#endif + } +} + +/* * Copy data from ibuf to obuf. */ void @@ -941,12 +1160,21 @@ sub_bcopy(struct abuf *ibuf, struct abuf *obuf) unsigned i, j, ocnt, inext, istart; unsigned icount, ocount, scount; + /* + * Drop samples for xrun correction + */ + if (obuf->w.sub.silence < 0) { + scount = -obuf->w.sub.silence; + if (scount > ibuf->used) + scount = ibuf->used; + obuf->w.sub.done += scount; + obuf->w.sub.silence += scount; + } + idata = (short *)abuf_rgetblk(ibuf, &icount, obuf->w.sub.done); - icount /= ibuf->bpf; if (icount == 0) return; odata = (short *)abuf_wgetblk(obuf, &ocount, 0); - ocount /= obuf->bpf; if (ocount == 0) return; istart = obuf->cmin - ibuf->cmin; @@ -962,14 +1190,14 @@ sub_bcopy(struct abuf *ibuf, struct abuf *obuf) } idata += inext; } - abuf_wcommit(obuf, scount * obuf->bpf); - obuf->w.sub.done += scount * ibuf->bpf; + abuf_wcommit(obuf, scount); + obuf->w.sub.done += scount; #ifdef DEBUG if (debug_level >= 4) { abuf_dbg(obuf); dbg_puts(": bcopy "); dbg_putu(scount); - dbg_puts(" fr\n"); + dbg_puts("\n"); } #endif } @@ -978,9 +1206,10 @@ sub_bcopy(struct abuf *ibuf, struct abuf *obuf) * Handle buffer overruns. Return 0 if the stream died. */ int -sub_xrun(struct abuf *ibuf, struct abuf *i) +sub_xrun(struct aproc *p, struct abuf *i) { - unsigned fdrop; + struct abuf *ibuf = LIST_FIRST(&p->ibuflist); + unsigned fdrop, remain; if (i->w.sub.done > 0) return 1; @@ -988,20 +1217,35 @@ sub_xrun(struct abuf *ibuf, struct abuf *i) abuf_eof(i); return 0; } - fdrop = ibuf->used / ibuf->bpf; + fdrop = ibuf->used; #ifdef DEBUG if (debug_level >= 3) { abuf_dbg(i); dbg_puts(": overrun, silence "); dbg_putu(fdrop); dbg_puts(" + "); - dbg_putu(i->silence / i->bpf); + dbg_putu(i->w.sub.silence); dbg_puts("\n"); } #endif + i->w.sub.done += fdrop; if (i->w.sub.xrun == XRUN_SYNC) - i->silence += fdrop * i->bpf; + sub_silence(i, fdrop); else { + remain = fdrop % p->u.sub.round; + if (remain) + remain = p->u.sub.round - remain; + sub_silence(i, -(int)remain); + fdrop += remain; +#ifdef DEBUG + if (debug_level >= 3) { + abuf_dbg(i); + dbg_puts(": overrun, adding "); + dbg_putu(remain); + dbg_puts("\n"); + } +#endif + abuf_ipos(i, -(int)fdrop); if (i->duplex) { #ifdef DEBUG @@ -1010,11 +1254,10 @@ sub_xrun(struct abuf *ibuf, struct abuf *i) dbg_puts(": full-duplex resync\n"); } #endif - i->duplex->silence += fdrop * i->duplex->bpf; + mix_drop(i->duplex, -(int)fdrop); abuf_opos(i->duplex, -(int)fdrop); } } - i->w.sub.done += fdrop * ibuf->bpf; return 1; } @@ -1029,9 +1272,10 @@ sub_in(struct aproc *p, struct abuf *ibuf) idone = ibuf->len; for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, oent); - if (!ABUF_WOK(i)) { + sub_silence(i, 0); + if (!SUB_WOK(i)) { if (p->flags & APROC_DROP) { - if (!sub_xrun(ibuf, i)) + if (!sub_xrun(p, i)) continue; } } else @@ -1049,7 +1293,7 @@ sub_in(struct aproc *p, struct abuf *ibuf) if (!(p->flags & APROC_DROP)) return 0; idone = ibuf->used; - p->u.sub.idle += idone / ibuf->bpf; + p->u.sub.idle += idone; } if (idone == 0) return 0; @@ -1057,7 +1301,9 @@ sub_in(struct aproc *p, struct abuf *ibuf) i->w.sub.done -= idone; } abuf_rdiscard(ibuf, idone); - p->u.sub.lat -= idone / ibuf->bpf; + abuf_opos(ibuf, idone); + p->u.sub.lat -= idone; + p->u.sub.abspos += idone; return 1; } @@ -1068,13 +1314,14 @@ sub_out(struct aproc *p, struct abuf *obuf) struct abuf *i, *inext; unsigned idone; - if (!ABUF_WOK(obuf)) + if (!SUB_WOK(obuf)) return 0; if (!abuf_fill(ibuf)) return 0; /* eof */ idone = ibuf->len; for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, oent); + sub_silence(i, 0); sub_bcopy(ibuf, i); if (idone > i->w.sub.done) idone = i->w.sub.done; @@ -1087,7 +1334,9 @@ sub_out(struct aproc *p, struct abuf *obuf) i->w.sub.done -= idone; } abuf_rdiscard(ibuf, idone); - p->u.sub.lat -= idone / ibuf->bpf; + abuf_opos(ibuf, idone); + p->u.sub.lat -= idone; + p->u.sub.abspos += idone; return 1; } @@ -1100,7 +1349,7 @@ sub_eof(struct aproc *p, struct abuf *ibuf) void sub_hup(struct aproc *p, struct abuf *obuf) { - struct abuf *i, *ibuf = LIST_FIRST(&p->ibuflist); + struct abuf *i, *inext, *ibuf = LIST_FIRST(&p->ibuflist); unsigned idone; if (!aproc_inuse(p)) { @@ -1114,8 +1363,11 @@ sub_hup(struct aproc *p, struct abuf *obuf) * Find a blocked output. */ idone = ibuf->len; - LIST_FOREACH(i, &p->obuflist, oent) { - if (ABUF_WOK(i) && i->w.sub.done < ibuf->used) { + for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) { + inext = LIST_NEXT(i, oent); + if (!abuf_flush(i)) + continue; + if (SUB_WOK(i) && i->w.sub.done < ibuf->used) { abuf_run(i); return; } @@ -1144,6 +1396,7 @@ sub_newout(struct aproc *p, struct abuf *obuf) p->u.sub.idle = 0; obuf->w.sub.done = 0; obuf->w.sub.xrun = XRUN_IGNORE; + obuf->w.sub.silence = 0; } void @@ -1157,10 +1410,10 @@ sub_ipos(struct aproc *p, struct abuf *ibuf, int delta) dbg_puti(p->u.sub.lat); dbg_puts("/"); dbg_puti(p->u.sub.maxlat); - dbg_puts(" fr\n"); + dbg_puts("\n"); } #endif - if (p->u.sub.ctl) + if (APROC_OK(p->u.sub.ctl)) ctl_ontick(p->u.sub.ctl, delta); aproc_ipos(p, ibuf, delta); } @@ -1179,14 +1432,16 @@ struct aproc_ops sub_ops = { }; struct aproc * -sub_new(char *name, int maxlat, struct aproc *ctl) +sub_new(char *name, int maxlat, unsigned round, struct aproc *ctl) { struct aproc *p; p = aproc_new(&sub_ops, name); p->u.sub.idle = 0; p->u.sub.lat = 0; + p->u.sub.round = round; p->u.sub.maxlat = maxlat; + p->u.sub.abspos = 0; p->u.sub.ctl = ctl; return p; } @@ -1194,7 +1449,8 @@ sub_new(char *name, int maxlat, struct aproc *ctl) void sub_clear(struct aproc *p) { - p->u.mix.lat = 0; + p->u.sub.lat = 0; + p->u.sub.abspos = 0; } /* @@ -1221,12 +1477,10 @@ resamp_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) * Calculate max frames readable at once from the input buffer. */ idata = (short *)abuf_rgetblk(ibuf, &icount, 0); - ifr = icount / ibuf->bpf; - icount = ifr * ibuf->bpf; + ifr = icount; odata = (short *)abuf_wgetblk(obuf, &ocount, 0); - ofr = ocount / obuf->bpf; - ocount = ofr * obuf->bpf; + ofr = ocount; /* * Partially copy structures into local variables, to avoid @@ -1258,6 +1512,8 @@ resamp_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) #endif for (;;) { if (diff < 0) { + if (ifr == 0) + break; ctx_start ^= 1; ctx = ctxbuf + ctx_start; for (c = inch; c > 0; c--) { @@ -1265,9 +1521,10 @@ resamp_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) ctx += RESAMP_NCTX; } diff += oblksz; - if (--ifr == 0) - break; + ifr--; } else { + if (ofr == 0) + break; ctx = ctxbuf; for (c = onch; c > 0; c--) { s1 = ctx[ctx_start]; @@ -1276,8 +1533,7 @@ resamp_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) *odata++ = s1 + (s2 - s1) * diff / (int)oblksz; } diff -= iblksz; - if (--ofr == 0) - break; + ofr--; } } p->u.resamp.diff = diff; @@ -1297,8 +1553,8 @@ resamp_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) /* * Update FIFO pointers. */ - icount -= ifr * ibuf->bpf; - ocount -= ofr * obuf->bpf; + icount -= ifr; + ocount -= ofr; abuf_rdiscard(ibuf, icount); abuf_wcommit(obuf, ocount); } @@ -1421,11 +1677,9 @@ cmap_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) * Calculate max frames readable at once from the input buffer. */ idata = (short *)abuf_rgetblk(ibuf, &icount, 0); - icount /= ibuf->bpf; if (icount == 0) return; odata = (short *)abuf_wgetblk(obuf, &ocount, 0); - ocount /= obuf->bpf; if (ocount == 0) return; scount = icount < ocount ? icount : ocount; @@ -1456,8 +1710,8 @@ cmap_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) dbg_puts(" fr\n"); } #endif - abuf_rdiscard(ibuf, scount * ibuf->bpf); - abuf_wcommit(obuf, scount * obuf->bpf); + abuf_rdiscard(ibuf, scount); + abuf_wcommit(obuf, scount); } int @@ -1555,11 +1809,9 @@ enc_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) * Calculate max frames readable at once from the input buffer. */ idata = (short *)abuf_rgetblk(ibuf, &icount, 0); - icount /= ibuf->bpf; if (icount == 0) return; odata = abuf_wgetblk(obuf, &ocount, 0); - ocount /= obuf->bpf; if (ocount == 0) return; scount = (icount < ocount) ? icount : ocount; @@ -1569,7 +1821,7 @@ enc_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) aproc_dbg(p); dbg_puts(": bcopy "); dbg_putu(scount); - dbg_puts(" fr * "); + dbg_puts(" fr / "); dbg_putu(nch); dbg_puts(" ch\n"); } @@ -1605,8 +1857,8 @@ enc_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) /* * Update FIFO pointers. */ - abuf_rdiscard(ibuf, scount * ibuf->bpf); - abuf_wcommit(obuf, scount * obuf->bpf); + abuf_rdiscard(ibuf, scount); + abuf_wcommit(obuf, scount); } int @@ -1715,11 +1967,9 @@ dec_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) * Calculate max frames readable at once from the input buffer. */ idata = abuf_rgetblk(ibuf, &icount, 0); - icount /= ibuf->bpf; if (icount == 0) return; odata = (short *)abuf_wgetblk(obuf, &ocount, 0); - ocount /= obuf->bpf; if (ocount == 0) return; scount = (icount < ocount) ? icount : ocount; @@ -1729,7 +1979,7 @@ dec_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) aproc_dbg(p); dbg_puts(": bcopy "); dbg_putu(scount); - dbg_puts(" fr * "); + dbg_puts(" fr / "); dbg_putu(nch); dbg_puts(" ch\n"); } @@ -1765,8 +2015,8 @@ dec_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf) /* * Update FIFO pointers. */ - abuf_rdiscard(ibuf, scount * ibuf->bpf); - abuf_wcommit(obuf, scount * obuf->bpf); + abuf_rdiscard(ibuf, scount); + abuf_wcommit(obuf, scount); } int @@ -1852,3 +2102,168 @@ dec_new(char *name, struct aparams *par) #endif return p; } + +/* + * Commit and flush part of the output buffer + */ +void +mon_flush(struct aproc *p) +{ + struct abuf *obuf = LIST_FIRST(&p->obuflist); + unsigned count; + +#ifdef DEBUG + if (debug_level >= 4) { + aproc_dbg(p); + dbg_puts(": delta = "); + dbg_puti(p->u.mon.delta); + dbg_puts("/"); + dbg_putu(p->u.mon.bufsz); + dbg_puts(" pending = "); + dbg_puti(p->u.mon.pending); + dbg_puts("\n"); + } +#endif + if (p->u.mon.delta <= 0 || p->u.mon.pending == 0) + return; + count = p->u.mon.delta; + if (count > p->u.mon.pending) + count = p->u.mon.pending; + abuf_wcommit(obuf, count); + p->u.mon.pending -= count; + p->u.mon.delta -= count; + abuf_flush(obuf); +} + +/* + * Copy one block. + */ +void +mon_snoop(struct aproc *p, struct abuf *ibuf, unsigned pos, unsigned todo) +{ + struct abuf *obuf = LIST_FIRST(&p->obuflist); + unsigned scount, icount, ocount; + short *idata, *odata; + +#ifdef DEBUG + if (debug_level >= 4) { + aproc_dbg(p); + dbg_puts(": snoop "); + dbg_putu(pos); + dbg_puts(".."); + dbg_putu(todo); + dbg_puts("\n"); + } +#endif + if (!abuf_flush(obuf)) + return; + + while (todo > 0) { + /* + * Calculate max frames readable at once from the input buffer. + */ + idata = (short *)abuf_rgetblk(ibuf, &icount, pos); + odata = (short *)abuf_wgetblk(obuf, &ocount, p->u.mon.pending); + scount = (icount < ocount) ? icount : ocount; +#ifdef DEBUG + if (debug_level >= 4) { + aproc_dbg(p); + dbg_puts(": snooping "); + dbg_putu(scount); + dbg_puts(" fr\n"); + } + if (scount == 0) { + dbg_puts("monitor xrun, not allowed\n"); + dbg_panic(); + } +#endif + memcpy(odata, idata, scount * obuf->bpf); + p->u.mon.pending += scount; + todo -= scount; + pos += scount; + } + mon_flush(p); +} + +int +mon_in(struct aproc *p, struct abuf *ibuf) +{ +#ifdef DEBUG + dbg_puts("monitor can't have inputs to read\n"); + dbg_panic(); +#endif + return 0; +} + +/* + * put the monitor into ``empty'' state + */ +void +mon_clear(struct aproc *p) +{ + p->u.mon.pending = 0; + p->u.mon.delta = 0; +} + +int +mon_out(struct aproc *p, struct abuf *obuf) +{ + /* + * can't trigger monitored stream to produce data + */ + return 0; +} + +void +mon_eof(struct aproc *p, struct abuf *ibuf) +{ +#ifdef DEBUG + dbg_puts("monitor can't have inputs to eof\n"); + dbg_panic(); +#endif +} + +void +mon_hup(struct aproc *p, struct abuf *obuf) +{ + aproc_del(p); +} + +void +mon_ipos(struct aproc *p, struct abuf *ibuf, int delta) +{ + aproc_ipos(p, ibuf, delta); + p->u.mon.delta += delta; + mon_flush(p); +} + +struct aproc_ops mon_ops = { + "mon", + mon_in, + mon_out, + mon_eof, + mon_hup, + NULL, + NULL, + mon_ipos, + aproc_opos, + NULL +}; + +struct aproc * +mon_new(char *name, unsigned bufsz) +{ + struct aproc *p; + + p = aproc_new(&mon_ops, name); + p->u.mon.pending = 0; + p->u.mon.delta = 0; + p->u.mon.bufsz = bufsz; +#ifdef DEBUG + if (debug_level >= 3) { + aproc_dbg(p); + dbg_puts(": new\n"); + } +#endif + return p; +} diff --git a/usr.bin/aucat/aproc.h b/usr.bin/aucat/aproc.h index 7b910155d3f..40eb26f7433 100644 --- a/usr.bin/aucat/aproc.h +++ b/usr.bin/aucat/aproc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: aproc.h,v 1.31 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: aproc.h,v 1.32 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -130,26 +130,37 @@ struct aproc { union { /* follow type-specific data */ struct { /* file/device io */ struct file *file; /* file to read/write */ + unsigned partial; /* bytes of partial frame */ } io; struct { unsigned idle; /* frames since idleing */ + unsigned round; /* block size, for xruns */ int lat; /* current latency */ int maxlat; /* max latency allowed */ - struct aproc *ctl; + unsigned abspos; /* frames produced */ + struct aproc *ctl; /* MIDI control/sync */ + struct aproc *mon; /* snoop output */ } mix; struct { unsigned idle; /* frames since idleing */ + unsigned round; /* block size, for xruns */ int lat; /* current latency */ int maxlat; /* max latency allowed */ + unsigned abspos; /* frames consumed */ struct aproc *ctl; } sub; struct { + int delta; /* time position */ + unsigned bufsz; /* buffer size (latency) */ + unsigned pending; /* uncommited samples */ + } mon; + struct { #define RESAMP_NCTX 2 unsigned ctx_start; short ctx[NCHAN_MAX * RESAMP_NCTX]; unsigned iblksz, oblksz; int diff; - int idelta, odelta; /* remainder of resamp_[io]pos */ + int idelta, odelta; /* remainder of resamp_xpos */ } resamp; struct { short ctx[NCHAN_MAX]; @@ -191,6 +202,8 @@ struct aproc { struct ctl_ops { void (*vol)(void *, unsigned); void (*start)(void *); + void (*stop)(void *); + void (*loc)(void *, unsigned); } *ops; void *arg; unsigned unit; @@ -203,6 +216,18 @@ struct aproc { } u; }; +/* + * Check if the given pointer is a valid aproc structure. + * + * aproc structures are not free()'d immediately, because + * there may be pointers to them, instead the APROC_ZOMB flag + * is set which means that they should not be used. When + * aprocs reference counter reaches zero, they are actually + * freed + */ +#define APROC_OK(p) ((p) && !((p)->flags & APROC_ZOMB)) + + struct aproc *aproc_new(struct aproc_ops *, char *); void aproc_del(struct aproc *); void aproc_dbg(struct aproc *); @@ -210,18 +235,40 @@ void aproc_setin(struct aproc *, struct abuf *); void aproc_setout(struct aproc *, struct abuf *); int aproc_depend(struct aproc *, struct aproc *); +void aproc_ipos(struct aproc *, struct abuf *, int); +void aproc_opos(struct aproc *, struct abuf *, int); + struct aproc *rfile_new(struct file *); struct aproc *wfile_new(struct file *); -struct aproc *mix_new(char *, int, struct aproc *); -struct aproc *sub_new(char *, int, struct aproc *); +struct aproc *mix_new(char *, int, unsigned, struct aproc *); +struct aproc *sub_new(char *, int, unsigned, struct aproc *); struct aproc *resamp_new(char *, unsigned, unsigned); struct aproc *cmap_new(char *, struct aparams *, struct aparams *); struct aproc *enc_new(char *, struct aparams *); struct aproc *dec_new(char *, struct aparams *); +struct aproc *mon_new(char *, unsigned); + +int rfile_in(struct aproc *, struct abuf *); +int rfile_out(struct aproc *, struct abuf *); +void rfile_eof(struct aproc *, struct abuf *); +void rfile_hup(struct aproc *, struct abuf *); +void rfile_done(struct aproc *); +int rfile_do(struct aproc *, unsigned, unsigned *); + +int wfile_in(struct aproc *, struct abuf *); +int wfile_out(struct aproc *, struct abuf *); +void wfile_eof(struct aproc *, struct abuf *); +void wfile_hup(struct aproc *, struct abuf *); +void wfile_done(struct aproc *); +int wfile_do(struct aproc *, unsigned, unsigned *); void mix_setmaster(struct aproc *); void mix_clear(struct aproc *); void mix_prime(struct aproc *); +void mix_drop(struct abuf *, int); +void sub_silence(struct abuf *, int); void sub_clear(struct aproc *); +void mon_snoop(struct aproc *, struct abuf *, unsigned, unsigned); +void mon_clear(struct aproc *); #endif /* !defined(APROC_H) */ diff --git a/usr.bin/aucat/aucat.1 b/usr.bin/aucat/aucat.1 index d29af368242..d0a75d212f8 100644 --- a/usr.bin/aucat/aucat.1 +++ b/usr.bin/aucat/aucat.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: aucat.1,v 1.65 2010/04/03 17:59:17 ratchov Exp $ +.\" $OpenBSD: aucat.1,v 1.66 2010/04/06 20:07:01 ratchov Exp $ .\" .\" Copyright (c) 2006 Alexandre Ratchov <alex@caoua.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 3 2010 $ +.Dd $Mdocdate: April 6 2010 $ .Dt AUCAT 1 .Os .Sh NAME @@ -33,6 +33,7 @@ .Op Fl i Ar file .Op Fl m Ar mode .Op Fl o Ar file +.Op Fl q Ar device .Op Fl r Ar rate .Op Fl s Ar name .Op Fl t Ar mode @@ -57,7 +58,7 @@ which does not convert on the fly and supports playback of .au files. The options are as follows: .Bl -tag -width Ds .It Fl b Ar nframes -The buffer size in frames. +The buffer size of the audio device in frames. A frame consists of one sample for each channel in the stream. This is the number of frames that will be buffered before being played and thus controls the playback latency. @@ -105,15 +106,20 @@ but other names can be used with the .Fl s option. .It Fl m Ar mode -Set the server mode. +Set the stream mode. Valid modes are .Ar play , .Ar rec , and -.Ar duplex , -for play-only, record-only, and full-duplex, respectively. +.Ar mon , +corresponding to playback, recording and monitoring. +A monitoring stream is a fake recording stream corresponding to +the mix of all playback streams. +Multiple modes can be specified, separated by commas, +but the same stream cannot be used for both recording and monitoring. The default is -.Ar duplex . +.Ar play , rec +(i.e. full-duplex). .It Fl n Loopback mode. Instead of using an audio device, send input streams @@ -125,6 +131,11 @@ Add this file to the list of files in which to store recorded samples. If the option argument is .Sq - then standard output will be used. +.It Fl q Ar device +The +.Xr sndio 7 +MIDI device to use for controlling stream volumes or +to start multiple streams synchronously. .It Fl r Ar rate Sample rate in Hertz of the playback or record stream. The default is 44100Hz. @@ -275,7 +286,7 @@ File formats are specified using the .Fl h option. The following file formats are supported: -.Bl -tag -width s32lexxx -offset -indent +.Bl -tag -width s32lexxx -offset indent .It raw Headerless file. This format is recommended since it has no limitations. @@ -296,7 +307,7 @@ Encodings are specified using the option. The following encodings are supported: .Pp -.Bl -tag -width s32lexxx -offset -indent -compact +.Bl -tag -width s32lexxx -offset indent -compact .It s8 signed 8-bit .It u8 @@ -411,7 +422,7 @@ faders. Clients connected to sub-devices created with .Fl t are controlled by the following MMC (MIDI Machine Control) messages: -.Bl -tag -width relocateXXX -offset -indent +.Bl -tag -width relocateXXX -offset indent .It stop Put the sub-device in stopped mode (the default). In this mode, any stream attempting to start playback or recording @@ -448,7 +459,7 @@ and block sizes .Pq Fl z are recommended for maximum accuracy: .Pp -.Bl -bullet -offset -indent -compact +.Bl -bullet -offset indent -compact .It 44100Hz, 441 frames .It @@ -475,6 +486,54 @@ behave normally, while streams connected to wait for the MMC start signal and start synchronously. Regardless of which device a stream is connected to, its playback volume knob is exposed. +.Pp +If +.Nm +is used to play and record audio files, it offers +similar MIDI control. +.Nm +can open a +.Xr sndio 7 +MIDI device allowing MIDI hardware or software +to control playback and recording in real time. +.Pp +A MIDI channel is assigned to each stream, and the volume +is changed using the standard volume controller (number 7). +Streams created with +.Fl t +option are controlled by the following MIDI Machine Control (MMC) messages: +.Bl -tag -width relocateXXX -offset indent +.It start +Start all streams synchronously. +By default, streams are created in a stopped state. +.It stop +Playback or recording is stopped, and +the stream is rewound back to the starting position. +.It relocate +Streams are relocated to the requested time postion +relative to the beginning of the stream, at which playback +and recording must start. +If the requested position is beyond the end of file, +the stream is temporarly disabled until a valid postion is requested. +.El +.Pp +For instance, the following command will play a file on the +.Va aucat:0.mmc +audio device, and give full control to MIDI software or hardware +connected to the +.Va midithru:0 +MIDI device: +.Bd -literal -offset indent +$ aucat -f aucat:0.mmc -t slave -q midithru:0 -i file.wav +.Ed +.Pp +At this stage, +.Nm +will start, stop and relocate automatically following all user +actions in the MIDI sequencer. +Note that the sequencer must use +.Va aucat:0 +as the MTC source, i.e. the audio server, not the audio player. .Sh LEGACY MODE If neither .Fl i diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c index 81093f42a4c..bb70afd7703 100644 --- a/usr.bin/aucat/aucat.c +++ b/usr.bin/aucat/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.82 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: aucat.c,v 1.83 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -43,9 +43,6 @@ #include "dbg.h" #endif -#define MODE_PLAY 1 -#define MODE_REC 2 - #define PROG_AUCAT "aucat" #define PROG_MIDICAT "midicat" @@ -159,16 +156,33 @@ opt_xrun(void) errx(1, "%s: bad underrun/overrun policy", optarg); } -int +unsigned opt_mode(void) { - if (strcmp("play", optarg) == 0) - return MODE_PLAY; - if (strcmp("rec", optarg) == 0) - return MODE_REC; - if (strcmp("duplex", optarg) == 0) - return MODE_PLAY | MODE_REC; - errx(1, "%s: bad mode", optarg); + unsigned mode = 0; + char *p = optarg; + size_t len; + + for (p = optarg; *p != NULL; p++) { + len = strcspn(p, ","); + if (strncmp("play", p, len) == 0) { + mode |= MODE_PLAY; + } else if (strncmp("rec", p, len) == 0) { + mode |= MODE_REC; + } else if (strncmp("mon", p, len) == 0) { + mode |= MODE_MON; + } else if (strncmp("duplex", p, len) == 0) { + /* XXX: backward compat, remove this */ + mode |= MODE_REC | MODE_PLAY; + } else + errx(1, "%s: bad mode", optarg); + p += len; + if (*p == '\0') + break; + } + if (mode == 0) + errx(1, "empty mode"); + return mode; } /* @@ -183,6 +197,7 @@ struct farg { int hdr; /* header format */ int xrun; /* overrun/underrun policy */ int mmc; /* MMC mode */ + unsigned mode; }; SLIST_HEAD(farglist, farg); @@ -194,7 +209,7 @@ SLIST_HEAD(farglist, farg); void farg_add(struct farglist *list, struct aparams *ipar, struct aparams *opar, unsigned vol, - int hdr, int xrun, int mmc, char *name) + int hdr, int xrun, int mmc, unsigned mode, char *name) { struct farg *fa; size_t namelen; @@ -212,12 +227,15 @@ farg_add(struct farglist *list, } } else fa->hdr = hdr; + if (mmc && xrun == XRUN_IGNORE) + xrun = XRUN_SYNC; fa->xrun = xrun; fa->ipar = *ipar; fa->opar = *opar; fa->vol = vol; fa->name = name; fa->mmc = mmc; + fa->mode = mode; SLIST_INSERT_HEAD(list, fa, entry); } @@ -287,28 +305,61 @@ getbasepath(char *base, size_t size) } void +stopall(char *base) +{ + struct file *f; + + restart: + LIST_FOREACH(f, &file_list, entry) { + /* + * skip connected streams (handled by dev_done()) + */ + if (APROC_OK(dev_mix) && f->rproc && + aproc_depend(dev_mix, f->rproc)) + continue; + if (APROC_OK(dev_sub) && f->wproc && + aproc_depend(f->wproc, dev_sub)) + continue; + if (APROC_OK(dev_midi)) { + if (f->rproc && aproc_depend(dev_midi, f->rproc)) + continue; + if (f->wproc && aproc_depend(f->wproc, dev_midi)) + continue; + } + /* + * kill anything else + */ + file_close(f); + goto restart; + } +} + +void aucat_usage(void) { (void)fputs("usage: " PROG_AUCAT " [-dlnu] [-b nframes] " - "[-C min:max] [-c min:max] [-e enc] [-f device]\n" - "\t[-h fmt] [-i file] [-m mode] [-o file] [-r rate] [-s name]\n" - "\t[-t mode] [-U unit] [-v volume] [-x policy] [-z nframes]\n", + "[-C min:max] [-c min:max] [-e enc]\n\t" + "[-f device] [-h fmt] [-i file] [-m mode]" + "[-o file] [-q device]\n\t" + "[-r rate] [-s name] [-t mode] [-U unit] " + "[-v volume] [-x policy]\n\t" + "[-z nframes]\n", stderr); } int aucat_main(int argc, char **argv) { - int c, u_flag, d_flag, l_flag, n_flag, hdr, xrun, suspend = 0, unit; + int c, u_flag, d_flag, l_flag, n_flag, hdr, xrun, unit; struct farg *fa; - struct farglist ifiles, ofiles, sfiles; + struct farglist ifiles, ofiles, sfiles, qfiles; struct aparams ipar, opar, dipar, dopar; char base[PATH_MAX], path[PATH_MAX], *file; unsigned bufsz, round, mode; char *devpath; const char *str; unsigned volctl; - int mmc; + int mmc, autostart; aparams_init(&ipar, 0, 1, 44100); aparams_init(&opar, 0, 1, 44100); @@ -322,14 +373,16 @@ aucat_main(int argc, char **argv) SLIST_INIT(&ifiles); SLIST_INIT(&ofiles); SLIST_INIT(&sfiles); + SLIST_INIT(&qfiles); hdr = HDR_AUTO; xrun = XRUN_IGNORE; volctl = MIDI_MAXCTL; - mode = 0; + mode = MODE_PLAY | MODE_REC; bufsz = 0; round = 0; + autostart = 1; - while ((c = getopt(argc, argv, "dnb:c:C:e:r:h:x:v:i:o:f:m:lus:U:t:z:")) != -1) { + while ((c = getopt(argc, argv, "dnb:c:C:e:r:h:x:v:i:o:f:m:luq:s:U:t:z:")) != -1) { switch (c) { case 'd': #ifdef DEBUG @@ -352,6 +405,8 @@ aucat_main(int argc, char **argv) break; case 't': mmc = opt_mmc(); + if (mmc) + autostart = 0; break; case 'c': opt_ch(&ipar); @@ -379,18 +434,22 @@ aucat_main(int argc, char **argv) if (strcmp(file, "-") == 0) file = NULL; farg_add(&ifiles, &ipar, &opar, volctl, - hdr, xrun, 0, file); + hdr, xrun, mmc, mode & MODE_PLAY, file); break; case 'o': file = optarg; if (strcmp(file, "-") == 0) file = NULL; farg_add(&ofiles, &ipar, &opar, volctl, - hdr, xrun, 0, file); + hdr, xrun, mmc, mode & MODE_RECMASK, file); break; case 's': farg_add(&sfiles, &ipar, &opar, volctl, - hdr, xrun, mmc, optarg); + hdr, xrun, mmc, mode, optarg); + break; + case 'q': + farg_add(&qfiles, &aparams_none, &aparams_none, + 0, HDR_RAW, 0, 0, 0, optarg); break; case 'f': if (devpath) @@ -401,6 +460,7 @@ aucat_main(int argc, char **argv) break; case 'l': l_flag = 1; + autostart = 0; break; case 'u': u_flag = 1; @@ -451,22 +511,12 @@ aucat_main(int argc, char **argv) if (!l_flag && (!SLIST_EMPTY(&sfiles) || unit >= 0)) errx(1, "can't use -s or -U without -l"); - if ((l_flag || mode != 0) && - (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles))) - errx(1, "can't use -l, -m and -s with -o or -i"); - if (!mode) { - if (l_flag || !SLIST_EMPTY(&ifiles)) - mode |= MODE_PLAY; - if (l_flag || !SLIST_EMPTY(&ofiles)) - mode |= MODE_REC; - if (!mode) { - aucat_usage(); - exit(1); - } - } + if (l_flag && (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles))) + errx(1, "can't use -l, and -s with -o or -i"); if (n_flag) { - if (devpath != NULL || l_flag) - errx(1, "can't use -n with -f or -l"); + if (devpath != NULL || !SLIST_EMPTY(&qfiles) || + l_flag || !autostart) + errx(1, "can't use -n with -f, -q, -t or -l"); if (SLIST_EMPTY(&ifiles) || SLIST_EMPTY(&ofiles)) errx(1, "both -i and -o are required with -n"); } @@ -476,28 +526,43 @@ aucat_main(int argc, char **argv) */ if (l_flag && SLIST_EMPTY(&sfiles)) { farg_add(&sfiles, &dopar, &dipar, - volctl, HDR_RAW, XRUN_IGNORE, mmc, DEFAULT_OPT); + volctl, HDR_RAW, XRUN_IGNORE, mmc, mode, DEFAULT_OPT); } - if (!u_flag) { - /* - * Calculate "best" device parameters. Iterate over all - * inputs and outputs and find the maximum sample rate - * and channel number. - */ - aparams_init(&dipar, dipar.cmin, dipar.cmax, dipar.rate); - aparams_init(&dopar, dopar.cmin, dopar.cmax, dopar.rate); - SLIST_FOREACH(fa, &ifiles, entry) { + /* + * Check modes and calculate "best" device parameters. Iterate over all + * inputs and outputs and find the maximum sample rate and channel + * number. + */ + mode = 0; + aparams_init(&dipar, dipar.cmin, dipar.cmax, dipar.rate); + aparams_init(&dopar, dopar.cmin, dopar.cmax, dopar.rate); + SLIST_FOREACH(fa, &ifiles, entry) { + if (fa->mode == 0) + errx(1, "%s: not in play mode", fa->name); + mode |= fa->mode; + if (!u_flag) aparams_grow(&dopar, &fa->ipar); - } - SLIST_FOREACH(fa, &ofiles, entry) { + } + SLIST_FOREACH(fa, &ofiles, entry) { + if (fa->mode == 0) + errx(1, "%s: not in rec/mon mode", fa->name); + if ((fa->mode & MODE_REC) && (fa->mode & MODE_MON)) + errx(1, "%s: can't record and monitor", fa->name); + mode |= fa->mode; + if (!u_flag) aparams_grow(&dipar, &fa->opar); - } - SLIST_FOREACH(fa, &sfiles, entry) { + } + SLIST_FOREACH(fa, &sfiles, entry) { + if ((fa->mode & MODE_REC) && (fa->mode & MODE_MON)) + errx(1, "%s: can't record and monitor", fa->name); + mode |= fa->mode; + if (!u_flag) { aparams_grow(&dopar, &fa->ipar); aparams_grow(&dipar, &fa->opar); } } + if (!round) round = ((mode & MODE_REC) ? dipar.rate : dopar.rate) / 15; if (!bufsz) @@ -516,12 +581,13 @@ aucat_main(int argc, char **argv) * the other half is for the socket/files. */ if (n_flag) { + if (mode & MODE_MON) + errx(1, "monitoring not allowed in loopback mode"); dev_loopinit(&dipar, &dopar, bufsz); } else { - if (!dev_init(devpath, - (mode & MODE_REC) ? &dipar : NULL, - (mode & MODE_PLAY) ? &dopar : NULL, - bufsz, round)) { + if ((mode & MODE_MON) && !(mode & MODE_PLAY)) + errx(1, "no playback stream to monitor"); + if (!dev_init(devpath, mode, &dipar, &dopar, bufsz, round)) { errx(1, "%s: can't open device", devpath ? devpath : "<default>"); } @@ -530,26 +596,33 @@ aucat_main(int argc, char **argv) /* * Create buffers for all input and output pipes. */ + while (!SLIST_EMPTY(&qfiles)) { + fa = SLIST_FIRST(&qfiles); + SLIST_REMOVE_HEAD(&qfiles, entry); + if (!dev_thruadd(fa->name, 1, 1)) + errx(1, "%s: can't open device", fa->name); + free(fa); + } while (!SLIST_EMPTY(&ifiles)) { fa = SLIST_FIRST(&ifiles); SLIST_REMOVE_HEAD(&ifiles, entry); - if (!wav_new_in(&wav_ops, fa->name, - fa->hdr, &fa->ipar, fa->xrun, fa->vol)) + if (!wav_new_in(&wav_ops, fa->mode, fa->name, + fa->hdr, &fa->ipar, fa->xrun, fa->vol, fa->mmc)) exit(1); free(fa); } while (!SLIST_EMPTY(&ofiles)) { fa = SLIST_FIRST(&ofiles); SLIST_REMOVE_HEAD(&ofiles, entry); - if (!wav_new_out(&wav_ops, fa->name, - fa->hdr, &fa->opar, fa->xrun)) + if (!wav_new_out(&wav_ops, fa->mode, fa->name, + fa->hdr, &fa->opar, fa->xrun, fa->mmc)) free(fa); } while (!SLIST_EMPTY(&sfiles)) { fa = SLIST_FIRST(&sfiles); SLIST_REMOVE_HEAD(&sfiles, entry); opt_new(fa->name, &fa->opar, &fa->ipar, - MIDI_TO_ADATA(fa->vol), fa->mmc); + MIDI_TO_ADATA(fa->vol), fa->mmc, fa->mode); free(fa); } if (l_flag) { @@ -559,6 +632,14 @@ aucat_main(int argc, char **argv) if (!d_flag && daemon(0, 0) < 0) err(1, "daemon"); } + if (autostart) { + /* + * inject artificial mmc start + */ + ctl_start(dev_midi); + } + if (l_flag) + dev_prime(); /* * Loop, start audio. @@ -567,57 +648,48 @@ aucat_main(int argc, char **argv) if (quit_flag) { break; } - if ((dev_mix && LIST_EMPTY(&dev_mix->obuflist)) || - (dev_sub && LIST_EMPTY(&dev_sub->ibuflist))) { + if ((APROC_OK(dev_mix) && LIST_EMPTY(&dev_mix->obuflist)) || + (APROC_OK(dev_sub) && LIST_EMPTY(&dev_sub->ibuflist))) { fprintf(stderr, "device disappeared, terminating\n"); break; } + if (!l_flag && ctl_idle(dev_midi)) + break; if (!file_poll()) break; - if ((!dev_mix || dev_mix->u.mix.idle > 2 * dev_bufsz) && - (!dev_sub || dev_sub->u.sub.idle > 2 * dev_bufsz) && - ((dev_mix || dev_sub) && dev_midi->u.ctl.tstate != CTL_RUN)) { - if (!l_flag) - break; - if (!suspend) { -#ifdef DEBUG - if (debug_level >= 2) - dbg_puts("suspending\n"); -#endif - suspend = 1; + if ((!APROC_OK(dev_mix) || dev_mix->u.mix.idle > 2 * dev_bufsz) && + (!APROC_OK(dev_sub) || dev_sub->u.sub.idle > 2 * dev_bufsz) && + (!APROC_OK(dev_submon) || dev_submon->u.sub.idle > 2 * dev_bufsz) && + (!APROC_OK(dev_midi) || dev_midi->u.ctl.tstate != CTL_RUN)) { + if (dev_pstate == DEV_RUN) { + dev_pstate = DEV_INIT; dev_stop(); dev_clear(); - dev_prime(); + /* + * priming buffer in non-server mode is not + * ok, because it will insert silence and + * break synchronization + */ + if (l_flag) + dev_prime(); } } - if ((dev_mix && dev_mix->u.mix.idle == 0) || - (dev_sub && dev_sub->u.sub.idle == 0) || - ((dev_mix || dev_sub) && dev_midi->u.ctl.tstate == CTL_RUN)) { - if (suspend) { -#ifdef DEBUG - if (debug_level >= 2) - dbg_puts("resuming\n"); -#endif - suspend = 0; - dev_start(); - } + /* + * move device state machine + * XXX: move this to dev.c + */ + if (dev_pstate == DEV_START) { + dev_pstate = DEV_RUN; + dev_start(); } } + stopall(base); + dev_done(); + filelist_done(); if (l_flag) { - filelist_unlisten(); - if (rmdir(base) < 0) + if (rmdir(base) < 0 && errno != ENOTEMPTY) warn("rmdir(\"%s\")", base); } - if (suspend) { -#ifdef DEBUG - if (debug_level >= 2) - dbg_puts("resuming to drain\n"); -#endif - suspend = 0; - dev_start(); - } - dev_done(); - filelist_done(); unsetsig(); return 0; } @@ -625,8 +697,8 @@ aucat_main(int argc, char **argv) void midicat_usage(void) { - (void)fputs("usage: " PROG_MIDICAT " [-dl] [-f device] " - "[-i file] [-o file] [-U unit]\n", + (void)fputs("usage: " PROG_MIDICAT " [-dl] " + "[-i file] [-o file] [-q device] [-U unit]\n", stderr); } int @@ -648,7 +720,7 @@ midicat_main(int argc, char **argv) SLIST_INIT(&ifiles); SLIST_INIT(&ofiles); - while ((c = getopt(argc, argv, "di:o:lf:U:")) != -1) { + while ((c = getopt(argc, argv, "di:o:lf:q:U:")) != -1) { switch (c) { case 'd': #ifdef DEBUG @@ -659,15 +731,17 @@ midicat_main(int argc, char **argv) break; case 'i': farg_add(&ifiles, &aparams_none, &aparams_none, - 0, HDR_RAW, 0, 0, optarg); + 0, HDR_RAW, 0, 0, 0, optarg); break; case 'o': farg_add(&ofiles, &aparams_none, &aparams_none, - 0, HDR_RAW, 0, 0, optarg); + 0, HDR_RAW, 0, 0, 0, optarg); break; - case 'f': + /* XXX: backward compat, remove this */ + case 'f': + case 'q': farg_add(&dfiles, &aparams_none, &aparams_none, - 0, HDR_RAW, 0, 0, optarg); + 0, HDR_RAW, 0, 0, 0, optarg); break; case 'l': l_flag = 1; @@ -703,12 +777,12 @@ midicat_main(int argc, char **argv) filelist_init(); dev_thruinit(); - if (!l_flag) + if (!l_flag && APROC_OK(dev_midi)) dev_midi->flags |= APROC_QUIT; if ((!SLIST_EMPTY(&ifiles) || !SLIST_EMPTY(&ofiles)) && SLIST_EMPTY(&dfiles)) { farg_add(&dfiles, &aparams_none, &aparams_none, - 0, HDR_RAW, 0, 0, NULL); + 0, HDR_RAW, 0, 0, 0, NULL); } while (!SLIST_EMPTY(&dfiles)) { fa = SLIST_FIRST(&dfiles); @@ -767,7 +841,6 @@ midicat_main(int argc, char **argv) dev_midiattach(NULL, buf); free(fa); } - /* * loop, start processing */ @@ -778,13 +851,13 @@ midicat_main(int argc, char **argv) if (!file_poll()) break; } + stopall(base); + dev_done(); + filelist_done(); if (l_flag) { - filelist_unlisten(); - if (rmdir(base) < 0) + if (rmdir(base) < 0 && errno != ENOTEMPTY) warn("rmdir(\"%s\")", base); } - dev_done(); - filelist_done(); unsetsig(); return 0; } diff --git a/usr.bin/aucat/conf.h b/usr.bin/aucat/conf.h index 47bc9ae7e11..1923cd0279e 100644 --- a/usr.bin/aucat/conf.h +++ b/usr.bin/aucat/conf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.h,v 1.14 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: conf.h,v 1.15 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -31,19 +31,6 @@ extern int debug_level; #endif /* - * Number of blocks in the device play/record buffers. Because Sun API - * cannot notify apps of the current positions, we have to use all N - * buffers devices blocks plus one extra block, to make write() block, - * so that poll() can return the exact postition. - */ -#define DEV_NBLK 2 - -/* - * Number of blocks in the wav-file i/o buffers. - */ -#define WAV_NBLK 6 - -/* * socket and option names */ #define DEFAULT_MIDITHRU "midithru" diff --git a/usr.bin/aucat/dev.c b/usr.bin/aucat/dev.c index 09e97da8ea0..1813b75bede 100644 --- a/usr.bin/aucat/dev.c +++ b/usr.bin/aucat/dev.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dev.c,v 1.45 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: dev.c,v 1.46 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -27,13 +27,15 @@ #include "miofile.h" #include "siofile.h" #include "midi.h" +#include "opt.h" #ifdef DEBUG #include "dbg.h" #endif +unsigned dev_pstate; unsigned dev_bufsz, dev_round, dev_rate; struct aparams dev_ipar, dev_opar; -struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play; +struct aproc *dev_mix, *dev_sub, *dev_rec, *dev_play, *dev_submon, *dev_mon; struct aproc *dev_midi; /* @@ -101,6 +103,14 @@ dev_loopinit(struct aparams *dipar, struct aparams *dopar, unsigned bufsz) struct aparams par; unsigned cmin, cmax, rate; + /* + * in principle we don't need control, but the start-stop mechanism + * depend on it and it's simpler to reuse this mechanism rather than + * dealing with lots of special cases + */ + dev_midi = ctl_new("ctl"); + dev_midi->refs++; + cmin = (dipar->cmin < dopar->cmin) ? dipar->cmin : dopar->cmin; cmax = (dipar->cmax > dopar->cmax) ? dipar->cmax : dopar->cmax; rate = (dipar->rate > dopar->rate) ? dipar->rate : dopar->rate; @@ -112,11 +122,14 @@ dev_loopinit(struct aparams *dipar, struct aparams *dopar, unsigned bufsz) dev_rate = rate; dev_rec = NULL; dev_play = NULL; + dev_mon = NULL; + dev_submon = NULL; + dev_pstate = DEV_INIT; buf = abuf_new(dev_bufsz, &par); - dev_mix = mix_new("mix", dev_bufsz, NULL); + dev_mix = mix_new("mix", dev_bufsz, 1, NULL); dev_mix->refs++; - dev_sub = sub_new("sub", dev_bufsz, NULL); + dev_sub = sub_new("sub", dev_bufsz, 1, NULL); dev_sub->refs++; aproc_setout(dev_mix, buf); aproc_setin(dev_sub, buf); @@ -140,14 +153,13 @@ dev_roundof(unsigned newrate) * setup. */ int -dev_init(char *devpath, +dev_init(char *devpath, unsigned mode, struct aparams *dipar, struct aparams *dopar, unsigned bufsz, unsigned round) { struct file *f; struct aparams ipar, opar; struct aproc *conv; struct abuf *buf; - unsigned nfr, ibufsz, obufsz; dev_midi = ctl_new("ctl"); dev_midi->refs++; @@ -157,13 +169,13 @@ dev_init(char *devpath, * limit the block size to 1/4 of the requested buffer. */ dev_round = round; - dev_bufsz = (bufsz + 3) / 4 + (dev_round - 1); - dev_bufsz -= dev_bufsz % dev_round; + dev_bufsz = bufsz; f = (struct file *)siofile_new(&siofile_ops, devpath, - dipar, dopar, &dev_bufsz, &dev_round); + mode & (MODE_PLAY | MODE_REC), dipar, dopar, + &dev_bufsz, &dev_round); if (f == NULL) return 0; - if (dipar) { + if (mode & MODE_REC) { #ifdef DEBUG if (debug_level >= 2) { dbg_puts("hw recording "); @@ -173,7 +185,7 @@ dev_init(char *devpath, #endif dev_rate = dipar->rate; } - if (dopar) { + if (mode & MODE_PLAY) { #ifdef DEBUG if (debug_level >= 2) { dbg_puts("hw playing "); @@ -183,33 +195,19 @@ dev_init(char *devpath, #endif dev_rate = dopar->rate; } - ibufsz = obufsz = dev_bufsz; - bufsz = (bufsz > dev_bufsz) ? bufsz - dev_bufsz : 0; - - /* - * Use 1/8 of the buffer for the mixer/converters. Since we - * already consumed 1/4 for the device, bufsz represents the - * remaining 3/4. So 1/8 is 1/6 of 3/4. - */ - nfr = (bufsz + 5) / 6; - nfr += dev_round - 1; - nfr -= nfr % dev_round; - if (nfr == 0) - nfr = dev_round; /* * Create record chain. */ - if (dipar) { + if (mode & MODE_REC) { aparams_init(&ipar, dipar->cmin, dipar->cmax, dipar->rate); /* * Create the read end. */ - dev_rec = rfile_new(f); + dev_rec = rsio_new(f); dev_rec->refs++; - buf = abuf_new(nfr, dipar); + buf = abuf_new(dev_bufsz, dipar); aproc_setout(dev_rec, buf); - ibufsz += nfr; /* * Append a converter, if needed. @@ -217,9 +215,8 @@ dev_init(char *devpath, if (!aparams_eqenc(dipar, &ipar)) { conv = dec_new("rec", dipar); aproc_setin(conv, buf); - buf = abuf_new(nfr, &ipar); + buf = abuf_new(dev_round, &ipar); aproc_setout(conv, buf); - ibufsz += nfr; } dev_ipar = ipar; @@ -227,7 +224,8 @@ dev_init(char *devpath, * Append a "sub" to which clients will connect. * Link it to the controller only in record-only mode */ - dev_sub = sub_new("rec", ibufsz, dopar ? NULL : dev_midi); + dev_sub = sub_new("rec", dev_bufsz, dev_round, + dopar ? NULL : dev_midi); dev_sub->refs++; aproc_setin(dev_sub, buf); } else { @@ -238,16 +236,15 @@ dev_init(char *devpath, /* * Create play chain. */ - if (dopar) { + if (mode & MODE_PLAY) { aparams_init(&opar, dopar->cmin, dopar->cmax, dopar->rate); /* * Create the write end. */ - dev_play = wfile_new(f); + dev_play = wsio_new(f); dev_play->refs++; - buf = abuf_new(nfr, dopar); + buf = abuf_new(dev_bufsz, dopar); aproc_setin(dev_play, buf); - obufsz += nfr; /* * Append a converter, if needed. @@ -255,23 +252,50 @@ dev_init(char *devpath, if (!aparams_eqenc(&opar, dopar)) { conv = enc_new("play", dopar); aproc_setout(conv, buf); - buf = abuf_new(nfr, &opar); + buf = abuf_new(dev_round, &opar); aproc_setin(conv, buf); - obufsz += nfr; } dev_opar = opar; /* * Append a "mix" to which clients will connect. */ - dev_mix = mix_new("play", obufsz, dev_midi); + dev_mix = mix_new("play", dev_bufsz, dev_round, dev_midi); dev_mix->refs++; aproc_setout(dev_mix, buf); } else { dev_play = NULL; dev_mix = NULL; } - dev_bufsz = (dopar) ? obufsz : ibufsz; + + /* + * Create monitoring chain + */ + if (mode & MODE_MON) { + dev_mon = mon_new("mon", dev_bufsz); + dev_mon->refs++; + buf = abuf_new(dev_bufsz, &dev_opar); + aproc_setout(dev_mon, buf); + + /* + * Append a "sub" to which clients will connect. + * Link it to the controller only in record-only mode + */ + dev_submon = sub_new("mon", dev_bufsz, dev_round, NULL); + dev_submon->refs++; + aproc_setin(dev_submon, buf); + + /* + * Attack to the mixer + */ + dev_mix->u.mix.mon = dev_mon; + dev_mon->refs++; + } else { + dev_submon = NULL; + if (APROC_OK(dev_mix)) + dev_mix->u.mix.mon = NULL; + } + #ifdef DEBUG if (debug_level >= 2) { dbg_puts("device block size is "); @@ -281,7 +305,7 @@ dev_init(char *devpath, dbg_puts(" blocks\n"); } #endif - dev_start(); + dev_pstate = DEV_INIT; return 1; } @@ -310,6 +334,11 @@ dev_done(void) * after each call to file_eof(). */ dev_mix->flags |= APROC_QUIT; + if (APROC_OK(dev_mix->u.mix.mon)) { + dev_mix->u.mix.mon->refs--; + aproc_del(dev_mix->u.mix.mon); + dev_mix->u.mix.mon = NULL; + } restart_mix: LIST_FOREACH(f, &file_list, entry) { if (f->rproc != NULL && @@ -318,7 +347,7 @@ dev_done(void) goto restart_mix; } } - } else if (dev_sub) { + } else if (dev_sub || dev_submon) { /* * Same as above, but since there's no mixer, * we generate EOF on the record-end of the @@ -327,7 +356,8 @@ dev_done(void) restart_sub: LIST_FOREACH(f, &file_list, entry) { if (f->rproc != NULL && - aproc_depend(dev_sub, f->rproc)) { + (aproc_depend(dev_sub, f->rproc) || + aproc_depend(dev_submon, f->rproc))) { file_eof(f); goto restart_sub; } @@ -347,32 +377,37 @@ dev_done(void) } } if (dev_mix) { - dev_mix->refs--; - if (dev_mix->flags & APROC_ZOMB) + if (--dev_mix->refs == 0 && (dev_mix->flags & APROC_ZOMB)) aproc_del(dev_mix); dev_mix = NULL; } if (dev_play) { - dev_play->refs--; - if (dev_play->flags & APROC_ZOMB) + if (--dev_play->refs == 0 && (dev_play->flags & APROC_ZOMB)) aproc_del(dev_play); dev_play = NULL; } if (dev_sub) { - dev_sub->refs--; - if (dev_sub->flags & APROC_ZOMB) + if (--dev_sub->refs == 0 && (dev_sub->flags & APROC_ZOMB)) aproc_del(dev_sub); dev_sub = NULL; } if (dev_rec) { - dev_rec->refs--; - if (dev_rec->flags & APROC_ZOMB) + if (--dev_rec->refs == 0 && (dev_rec->flags & APROC_ZOMB)) aproc_del(dev_rec); dev_rec = NULL; } + if (dev_submon) { + if (--dev_submon->refs == 0 && (dev_submon->flags & APROC_ZOMB)) + aproc_del(dev_submon); + dev_submon = NULL; + } + if (dev_mon) { + if (--dev_mon->refs == 0 && (dev_mon->flags & APROC_ZOMB)) + aproc_del(dev_mon); + dev_mon = NULL; + } if (dev_midi) { - dev_midi->refs--; - if (dev_midi->flags & APROC_ZOMB) + if (--dev_midi->refs == 0 && (dev_midi->flags & APROC_ZOMB)) aproc_del(dev_midi); dev_midi = NULL; } @@ -390,49 +425,64 @@ dev_start(void) { struct file *f; - if (dev_mix) +#ifdef DEBUG + if (debug_level >= 2) + dbg_puts("starting audio device\n"); +#endif + if (APROC_OK(dev_mix)) dev_mix->flags |= APROC_DROP; - if (dev_sub) + if (APROC_OK(dev_sub)) dev_sub->flags |= APROC_DROP; - if (dev_play && dev_play->u.io.file) { + if (APROC_OK(dev_submon)) + dev_submon->flags |= APROC_DROP; + if (APROC_OK(dev_play) && dev_play->u.io.file) { f = dev_play->u.io.file; f->ops->start(f); - } else if (dev_rec && dev_rec->u.io.file) { + } else if (APROC_OK(dev_rec) && dev_rec->u.io.file) { f = dev_rec->u.io.file; f->ops->start(f); } } /* - * Pause the device. + * Pause the device. This may trigger context switches, + * so it shouldn't be called from aproc methods */ void dev_stop(void) { struct file *f; - if (dev_play && dev_play->u.io.file) { + if (APROC_OK(dev_play) && dev_play->u.io.file) { f = dev_play->u.io.file; f->ops->stop(f); - } else if (dev_rec && dev_rec->u.io.file) { + } else if (APROC_OK(dev_rec) && dev_rec->u.io.file) { f = dev_rec->u.io.file; f->ops->stop(f); } - if (dev_mix) + if (APROC_OK(dev_mix)) dev_mix->flags &= ~APROC_DROP; - if (dev_sub) + if (APROC_OK(dev_sub)) dev_sub->flags &= ~APROC_DROP; + if (APROC_OK(dev_submon)) + dev_submon->flags &= ~APROC_DROP; +#ifdef DEBUG + if (debug_level >= 2) + dbg_puts("audio device stopped\n"); +#endif } /* * Find the end points connected to the mix/sub. */ int -dev_getep(struct abuf **sibuf, struct abuf **sobuf) +dev_getep(unsigned mode, struct abuf **sibuf, struct abuf **sobuf) { struct abuf *ibuf, *obuf; - if (sibuf && *sibuf) { + if (mode & MODE_PLAY) { + if (!APROC_OK(dev_mix)) + return 0; ibuf = *sibuf; for (;;) { if (!ibuf || !ibuf->rproc) { @@ -450,7 +500,9 @@ dev_getep(struct abuf **sibuf, struct abuf **sobuf) } *sibuf = ibuf; } - if (sobuf && *sobuf) { + if (mode & MODE_REC) { + if (!APROC_OK(dev_sub)) + return 0; obuf = *sobuf; for (;;) { if (!obuf || !obuf->wproc) { @@ -468,6 +520,26 @@ dev_getep(struct abuf **sibuf, struct abuf **sobuf) } *sobuf = obuf; } + if (mode & MODE_MON) { + if (!APROC_OK(dev_submon)) + return 0; + obuf = *sobuf; + for (;;) { + if (!obuf || !obuf->wproc) { +#ifdef DEBUG + if (debug_level >= 3) { + abuf_dbg(*sobuf); + dbg_puts(": not connected to device\n"); + } +#endif + return 0; + } + if (obuf->wproc == dev_submon) + break; + obuf = LIST_FIRST(&obuf->wproc->ibuflist); + } + *sobuf = obuf; + } return 1; } @@ -476,45 +548,38 @@ dev_getep(struct abuf **sibuf, struct abuf **sobuf) * them underruns/overruns). */ void -dev_sync(struct abuf *ibuf, struct abuf *obuf) +dev_sync(unsigned mode, struct abuf *ibuf, struct abuf *obuf) { - struct abuf *pbuf, *rbuf; int delta; - if (!dev_mix || !dev_sub) + if (!dev_getep(mode, &ibuf, &obuf)) return; - pbuf = LIST_FIRST(&dev_mix->obuflist); - if (!pbuf) - return; - rbuf = LIST_FIRST(&dev_sub->ibuflist); - if (!rbuf) - return; - if (!dev_getep(&ibuf, &obuf)) - return; - /* * Calculate delta, the number of frames the play chain is ahead * of the record chain. It's necessary to schedule silences (or * drops) in order to start playback and record in sync. */ - delta = - rbuf->bpf * (pbuf->abspos + pbuf->used) - - pbuf->bpf * rbuf->abspos; - delta /= pbuf->bpf * rbuf->bpf; + if (APROC_OK(dev_mix) && APROC_OK(dev_sub)) { + delta = dev_mix->u.mix.abspos - dev_sub->u.sub.abspos; + } else if (APROC_OK(dev_mix)) { + delta = dev_mix->u.mix.lat; + } else + delta = 0; #ifdef DEBUG if (debug_level >= 3) { dbg_puts("syncing device, delta = "); dbg_putu(delta); - dbg_puts(": "); - abuf_dbg(pbuf); - dbg_puts(" abspos = "); - dbg_putu(pbuf->abspos); - dbg_puts(" used = "); - dbg_putu(pbuf->used); - dbg_puts(" <---> "); - abuf_dbg(rbuf); - dbg_puts(" abspos = "); - dbg_putu(rbuf->abspos); + dbg_puts(" "); + if (APROC_OK(dev_mix)) { + aproc_dbg(dev_mix); + dbg_puts(": abspos = "); + dbg_putu(dev_mix->u.mix.abspos); + } + if (APROC_OK(dev_sub)) { + aproc_dbg(dev_sub); + dbg_puts(": abspos = "); + dbg_putu(dev_sub->u.sub.abspos); + } dbg_puts("\n"); } #endif @@ -523,19 +588,15 @@ dev_sync(struct abuf *ibuf, struct abuf *obuf) * The play chain is ahead (most cases) drop some of * the recorded input, to get both in sync. */ - if (obuf) { - obuf->drop += delta * obuf->bpf; - abuf_ipos(obuf, -delta); - } + if (mode & MODE_RECMASK) + sub_silence(obuf, -delta); } else if (delta < 0) { /* * The record chain is ahead (should never happen, * right?) then insert silence to play. */ - if (ibuf) { - ibuf->silence += -delta * ibuf->bpf; - abuf_opos(ibuf, delta); - } + if (mode & MODE_PLAY) + mix_drop(ibuf, delta); } } @@ -550,23 +611,20 @@ dev_getpos(void) int plat = 0, rlat = 0; int delta; - if (dev_mix) { + if (APROC_OK(dev_mix)) { pbuf = LIST_FIRST(&dev_mix->obuflist); if (!pbuf) return 0; plat = -dev_mix->u.mix.lat; } - if (dev_sub) { + if (APROC_OK(dev_sub)) { rbuf = LIST_FIRST(&dev_sub->ibuflist); if (!rbuf) return 0; rlat = -dev_sub->u.sub.lat; } - if (dev_mix && dev_sub) { - delta = - rbuf->bpf * (pbuf->abspos + pbuf->used) - - pbuf->bpf * rbuf->abspos; - delta /= pbuf->bpf * rbuf->bpf; + if (APROC_OK(dev_mix) && APROC_OK(dev_sub)) { + delta = dev_mix->u.mix.abspos - dev_sub->u.sub.abspos; if (delta > 0) rlat -= delta; else if (delta < 0) @@ -581,7 +639,7 @@ dev_getpos(void) } #endif } - return dev_mix ? plat : rlat; + return APROC_OK(dev_mix) ? plat : rlat; } /* @@ -591,16 +649,25 @@ dev_getpos(void) * and rec. */ void -dev_attach(char *name, - struct abuf *ibuf, struct aparams *sipar, unsigned underrun, - struct abuf *obuf, struct aparams *sopar, unsigned overrun, int vol) +dev_attach(char *name, unsigned mode, + struct abuf *ibuf, struct aparams *sipar, + struct abuf *obuf, struct aparams *sopar, + unsigned xrun, int vol) { struct abuf *pbuf = NULL, *rbuf = NULL; struct aparams ipar, opar; struct aproc *conv; unsigned round, nblk; - if (ibuf) { +#ifdef DEBUG + if ((!APROC_OK(dev_mix) && (mode & MODE_PLAY)) || + (!APROC_OK(dev_sub) && (mode & MODE_REC)) || + (!APROC_OK(dev_submon) && (mode & MODE_MON))) { + dbg_puts("mode beyond device mode, not attaching\n"); + return; + } +#endif + if (mode & MODE_PLAY) { ipar = *sipar; pbuf = LIST_FIRST(&dev_mix->obuflist); nblk = (dev_bufsz / dev_round + 3) / 4; @@ -633,13 +700,11 @@ dev_attach(char *name, aproc_setout(conv, ibuf); } aproc_setin(dev_mix, ibuf); - if (dev_mix->u.mix.lat > 0) - abuf_opos(ibuf, -dev_mix->u.mix.lat); - ibuf->r.mix.xrun = underrun; + ibuf->r.mix.xrun = xrun; ibuf->r.mix.maxweight = vol; mix_setmaster(dev_mix); } - if (obuf) { + if (mode & MODE_REC) { opar = *sopar; rbuf = LIST_FIRST(&dev_sub->ibuflist); round = dev_roundof(opar.rate); @@ -672,19 +737,58 @@ dev_attach(char *name, aproc_setin(conv, obuf); } aproc_setout(dev_sub, obuf); - if (dev_sub->u.sub.lat > 0) - abuf_ipos(obuf, -dev_sub->u.sub.lat); - obuf->w.sub.xrun = overrun; + obuf->w.sub.xrun = xrun; + } + if (mode & MODE_MON) { + opar = *sopar; + rbuf = LIST_FIRST(&dev_submon->ibuflist); + round = dev_roundof(opar.rate); + nblk = (dev_bufsz / dev_round + 3) / 4; + if (!aparams_eqenc(&opar, &dev_opar)) { + conv = enc_new(name, &opar); + opar.bps = dev_opar.bps; + opar.bits = dev_opar.bits; + opar.sig = dev_opar.sig; + opar.le = dev_opar.le; + opar.msb = dev_opar.msb; + aproc_setout(conv, obuf); + obuf = abuf_new(nblk * round, &opar); + aproc_setin(conv, obuf); + } + if (!aparams_subset(&opar, &dev_opar)) { + conv = cmap_new(name, &dev_opar, &opar); + opar.cmin = dev_opar.cmin; + opar.cmax = dev_opar.cmax; + aproc_setout(conv, obuf); + obuf = abuf_new(nblk * round, &opar); + aproc_setin(conv, obuf); + } + if (!aparams_eqrate(&opar, &dev_opar)) { + conv = resamp_new(name, dev_round, round); + opar.rate = dev_opar.rate; + round = dev_round; + aproc_setout(conv, obuf); + obuf = abuf_new(nblk * round, &opar); + aproc_setin(conv, obuf); + } + aproc_setout(dev_submon, obuf); + obuf->w.sub.xrun = xrun; } /* * Sync play to record. */ - if (ibuf && obuf) { + if ((mode & MODE_PLAY) && (mode & MODE_RECMASK)) { ibuf->duplex = obuf; obuf->duplex = ibuf; } - dev_sync(ibuf, obuf); + dev_sync(mode, ibuf, obuf); + + /* + * Start device if not already started + */ + if (dev_pstate == DEV_INIT) + dev_pstate = DEV_START; } /* @@ -701,7 +805,7 @@ dev_setvol(struct abuf *ibuf, int vol) dbg_puts("\n"); } #endif - if (!dev_getep(&ibuf, NULL)) { + if (!dev_getep(MODE_PLAY, &ibuf, NULL)) { return; } ibuf->r.mix.vol = vol; @@ -716,7 +820,7 @@ dev_clear(void) { struct abuf *buf; - if (dev_mix) { + if (APROC_OK(dev_mix)) { #ifdef DEBUG if (!LIST_EMPTY(&dev_mix->ibuflist)) { dbg_puts("play end not idle, can't clear device\n"); @@ -730,7 +834,7 @@ dev_clear(void) } mix_clear(dev_mix); } - if (dev_sub) { + if (APROC_OK(dev_sub)) { #ifdef DEBUG if (!LIST_EMPTY(&dev_sub->obuflist)) { dbg_puts("record end not idle, can't clear device\n"); @@ -744,6 +848,22 @@ dev_clear(void) } sub_clear(dev_sub); } + if (APROC_OK(dev_submon)) { +#ifdef DEBUG + dbg_puts("clearing monitor\n"); + if (!LIST_EMPTY(&dev_submon->obuflist)) { + dbg_puts("monitoring end not idle, can't clear device\n"); + dbg_panic(); + } +#endif + buf = LIST_FIRST(&dev_submon->ibuflist); + while (buf) { + abuf_clear(buf); + buf = LIST_FIRST(&buf->wproc->ibuflist); + } + sub_clear(dev_submon); + mon_clear(dev_mon); + } } /* @@ -753,7 +873,7 @@ dev_clear(void) void dev_prime(void) { - if (dev_mix) { + if (APROC_OK(dev_mix)) { #ifdef DEBUG if (!LIST_EMPTY(&dev_mix->ibuflist)) { dbg_puts("play end not idle, can't prime device\n"); diff --git a/usr.bin/aucat/dev.h b/usr.bin/aucat/dev.h index 2f3195d701c..777894ac1d3 100644 --- a/usr.bin/aucat/dev.h +++ b/usr.bin/aucat/dev.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dev.h,v 1.19 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: dev.h,v 1.20 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -21,25 +21,33 @@ struct aproc; struct aparams; struct abuf; +#define DEV_INIT 0 +#define DEV_START 1 +#define DEV_RUN 2 +#define DEV_STOP 3 + +extern unsigned dev_pstate; extern unsigned dev_bufsz, dev_round, dev_rate; extern struct aparams dev_ipar, dev_opar; -extern struct aproc *dev_mix, *dev_sub, *dev_midi; +extern struct aproc *dev_mix, *dev_sub, *dev_midi, *dev_submon, *dev_mon; void dev_thruinit(void); int dev_thruadd(char *, int, int); void dev_midiattach(struct abuf *, struct abuf *); unsigned dev_roundof(unsigned); void dev_loopinit(struct aparams *, struct aparams *, unsigned); -int dev_init(char *, struct aparams *, struct aparams *, unsigned, unsigned); +int dev_init(char *, unsigned, + struct aparams *, struct aparams *, unsigned, unsigned); void dev_start(void); void dev_stop(void); void dev_run(int); void dev_done(void); -int dev_getep(struct abuf **, struct abuf **); -void dev_sync(struct abuf *, struct abuf *); +int dev_getep(unsigned, struct abuf **, struct abuf **); +void dev_sync(unsigned, struct abuf *, struct abuf *); +unsigned dev_getmode(void); int dev_getpos(void); -void dev_attach(char *, - struct abuf *, struct aparams *, unsigned, +void dev_attach(char *, unsigned, + struct abuf *, struct aparams *, struct abuf *, struct aparams *, unsigned, int); void dev_setvol(struct abuf *, int); void dev_clear(void); diff --git a/usr.bin/aucat/file.c b/usr.bin/aucat/file.c index 4172325ec10..497a456fa25 100644 --- a/usr.bin/aucat/file.c +++ b/usr.bin/aucat/file.c @@ -1,4 +1,4 @@ -/* $OpenBSD: file.c,v 1.17 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: file.c,v 1.18 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -63,8 +63,6 @@ #define MAXFDS 100 -extern struct fileops listen_ops, pipe_ops; - struct timeval file_tv; struct filelist file_list; struct timo *timo_queue; @@ -520,23 +518,6 @@ filelist_done(void) timo_done(); } -/* - * Close all listening sockets. - * - * XXX: remove this - */ -void -filelist_unlisten(void) -{ - struct file *f, *fnext; - - for (f = LIST_FIRST(&file_list); f != NULL; f = fnext) { - fnext = LIST_NEXT(f, entry); - if (f->ops == &listen_ops) - file_del(f); - } -} - unsigned file_read(struct file *f, unsigned char *data, unsigned count) { @@ -676,6 +657,8 @@ file_close(struct file *f) dbg_puts(": closing\n"); } #endif + if (f->wproc == NULL && f->rproc == NULL) + f->state |= FILE_ZOMB; if (!(f->state & (FILE_RINUSE | FILE_WINUSE))) { p = f->rproc; if (p) { diff --git a/usr.bin/aucat/headers.c b/usr.bin/aucat/headers.c index 33978d50b90..80efddbcfbc 100644 --- a/usr.bin/aucat/headers.c +++ b/usr.bin/aucat/headers.c @@ -1,4 +1,4 @@ -/* $OpenBSD: headers.c,v 1.12 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: headers.c,v 1.13 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -129,7 +129,7 @@ wav_readfmt(int fd, unsigned csize, struct aparams *par, short **map) } int -wav_readhdr(int fd, struct aparams *par, off_t *datasz, short **map) +wav_readhdr(int fd, struct aparams *par, off_t *startpos, off_t *datasz, short **map) { struct wavriff riff; struct wavchunk chunk; @@ -161,6 +161,7 @@ wav_readhdr(int fd, struct aparams *par, off_t *datasz, short **map) return 0; fmt_done = 1; } else if (memcmp(chunk.id, wav_id_data, 4) == 0) { + *startpos = pos; *datasz = csize; break; } else { @@ -186,10 +187,12 @@ wav_readhdr(int fd, struct aparams *par, off_t *datasz, short **map) return 1; } +/* + * Write header and seek to start position + */ int -wav_writehdr(int fd, struct aparams *par) +wav_writehdr(int fd, struct aparams *par, off_t *startpos, off_t datasz) { - off_t datasz; unsigned nch = par->cmax - par->cmin + 1; struct { struct wavriff riff; @@ -198,16 +201,6 @@ wav_writehdr(int fd, struct aparams *par) struct wavchunk data_hdr; } hdr; - datasz = lseek(fd, 0, SEEK_CUR); - if (datasz < 0) { - warn("wav_writehdr: lseek(end)"); - return 0; - } - if (datasz >= sizeof(hdr)) - datasz -= sizeof(hdr); - else - datasz = 0; - /* * Check that encoding is supported by .wav file format. */ @@ -253,5 +246,6 @@ wav_writehdr(int fd, struct aparams *par) warn("wav_writehdr: write"); return 0; } + *startpos = sizeof(hdr); return 1; } diff --git a/usr.bin/aucat/legacy.c b/usr.bin/aucat/legacy.c index a5eb1d5a710..370a571c597 100644 --- a/usr.bin/aucat/legacy.c +++ b/usr.bin/aucat/legacy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: legacy.c,v 1.11 2010/04/05 19:52:42 jakemsr Exp $ */ +/* $OpenBSD: legacy.c,v 1.12 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 1997 Kenneth Stailey. All rights reserved. * @@ -64,7 +64,7 @@ legacy_play(char *dev, char *aufile) struct sio_par spar, par; struct aparams apar; ssize_t rd; - off_t datasz; + off_t datasz, dummy; char buf[5120]; size_t readsz; int fd, fmt = FMT_RAW; @@ -96,7 +96,7 @@ legacy_play(char *dev, char *aufile) if (read(fd, &chan, sizeof(chan)) == sizeof(chan)) chan = ntohl(chan); } else if (!strncmp(magic, "RIFF", 4) && - wav_readhdr(fd, &apar, &datasz, &map)) { + wav_readhdr(fd, &apar, &dummy, &datasz, &map)) { fmt = FMT_WAV; } diff --git a/usr.bin/aucat/midi.c b/usr.bin/aucat/midi.c index 70d9aec2f3a..191be1b17be 100644 --- a/usr.bin/aucat/midi.c +++ b/usr.bin/aucat/midi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: midi.c,v 1.18 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: midi.c,v 1.19 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -132,7 +132,7 @@ thru_rt(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned c) if (debug_level >= 4) { aproc_dbg(p); dbg_puts(": "); - dbg_putu(c); + dbg_putx(c); dbg_puts(": flushing realtime message\n"); } #endif @@ -350,16 +350,15 @@ ctl_slotdbg(struct aproc *p, int slot) { struct ctl_slot *s; - aproc_dbg(p); if (slot < 0) { - dbg_puts("/none"); + dbg_puts("none"); } else { s = p->u.ctl.slot + slot; dbg_puts(s->name); dbg_putu(s->unit); - dbg_puts("="); + dbg_puts("("); dbg_putu(s->vol); - dbg_puts("/"); + dbg_puts(")/"); switch (s->tstate) { case CTL_OFF: dbg_puts("off"); @@ -394,7 +393,7 @@ ctl_sendmsg(struct aproc *p, struct abuf *ibuf, unsigned char *msg, unsigned len for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) { inext = LIST_NEXT(i, oent); - if (i->duplex == ibuf) + if (i->duplex && i->duplex == ibuf) continue; itodo = len; idata = msg; @@ -639,8 +638,10 @@ ctl_trystart(struct aproc *p, int caller) if (p->u.ctl.tstate != CTL_START) { #ifdef DEBUG - aproc_dbg(p); - dbg_puts(": not in starting state\n"); + if (debug_level >= 3) { + ctl_slotdbg(p, caller); + dbg_puts(": server not started, delayd\n"); + } #endif return 0; } @@ -649,8 +650,10 @@ ctl_trystart(struct aproc *p, int caller) continue; if (s->tstate != CTL_OFF && s->tstate != CTL_START) { #ifdef DEBUG - ctl_slotdbg(p, i); - dbg_puts(": not ready to start, start delayed\n"); + if (debug_level >= 3) { + ctl_slotdbg(p, i); + dbg_puts(": not ready, server delayed\n"); + } #endif return 0; } @@ -660,8 +663,10 @@ ctl_trystart(struct aproc *p, int caller) continue; if (s->tstate == CTL_START) { #ifdef DEBUG - ctl_slotdbg(p, i); - dbg_puts(": started\n"); + if (debug_level >= 3) { + ctl_slotdbg(p, i); + dbg_puts(": started\n"); + } #endif s->tstate = CTL_RUN; s->ops->start(s->arg); @@ -671,10 +676,10 @@ ctl_trystart(struct aproc *p, int caller) p->u.ctl.slot[caller].tstate = CTL_RUN; p->u.ctl.tstate = CTL_RUN; p->u.ctl.delta = MTC_SEC * dev_getpos(); - if (dev_rate % (30 * 4 * dev_round)) { + if (dev_rate % (30 * 4 * dev_round) == 0) { p->u.ctl.fps_id = MTC_FPS_30; p->u.ctl.fps = 30; - } else if (dev_rate % (25 * 4 * dev_round)) { + } else if (dev_rate % (25 * 4 * dev_round) == 0) { p->u.ctl.fps_id = MTC_FPS_25; p->u.ctl.fps = 25; } else { @@ -682,13 +687,17 @@ ctl_trystart(struct aproc *p, int caller) p->u.ctl.fps = 24; } #ifdef DEBUG - ctl_slotdbg(p, caller); - dbg_puts(": started server at "); - dbg_puti(p->u.ctl.delta); - dbg_puts(", "); - dbg_puti(p->u.ctl.fps); - dbg_puts(" mtc fps\n"); + if (debug_level >= 3) { + ctl_slotdbg(p, caller); + dbg_puts(": started server at "); + dbg_puti(p->u.ctl.delta); + dbg_puts(", "); + dbg_puti(p->u.ctl.fps); + dbg_puts(" mtc fps\n"); + } #endif + if (dev_pstate == DEV_INIT) + dev_pstate = DEV_START; ctl_full(p); return 1; } @@ -702,8 +711,15 @@ ctl_slotnew(struct aproc *p, char *who, struct ctl_ops *ops, void *arg, int tr) int idx; struct ctl_slot *s; - if (p == NULL) + if (!APROC_OK(p)) { +#ifdef DEBUG + if (debug_level >= 1) { + dbg_puts(who); + dbg_puts(": MIDI control not available\n"); + } +#endif return -1; + } idx = ctl_getidx(p, who); if (idx < 0) return -1; @@ -726,7 +742,7 @@ ctl_slotdel(struct aproc *p, int index) unsigned i; struct ctl_slot *s; - if (p == NULL) + if (!APROC_OK(p)) return; p->u.ctl.slot[index].ops = NULL; if (!(p->flags & APROC_QUIT)) @@ -779,7 +795,7 @@ ctl_slotvol(struct aproc *p, int slot, unsigned vol) { unsigned char msg[3]; - if (p == NULL) + if (!APROC_OK(p)) return; #ifdef DEBUG if (debug_level >= 3) { @@ -807,7 +823,7 @@ ctl_slotstart(struct aproc *p, int slot) { struct ctl_slot *s = p->u.ctl.slot + slot; - if (p == NULL) + if (!APROC_OK(p)) return 1; if (s->tstate == CTL_OFF || p->u.ctl.tstate == CTL_OFF) return 1; @@ -831,7 +847,7 @@ ctl_slotstop(struct aproc *p, int slot) { struct ctl_slot *s = p->u.ctl.slot + slot; - if (p == NULL) + if (!APROC_OK(p)) return; /* * tag the stream as not trying to start, @@ -842,6 +858,118 @@ ctl_slotstop(struct aproc *p, int slot) } /* + * start all slots simultaneously + */ +void +ctl_start(struct aproc *p) +{ + if (!APROC_OK(p)) + return; + if (p->u.ctl.tstate == CTL_STOP) { + p->u.ctl.tstate = CTL_START; + (void)ctl_trystart(p, -1); +#ifdef DEBUG + } else { + if (debug_level >= 3) { + aproc_dbg(p); + dbg_puts(": ignoring mmc start\n"); + } +#endif + } +} + +/* + * stop all slots simultaneously + */ +void +ctl_stop(struct aproc *p) +{ + unsigned i; + struct ctl_slot *s; + + if (!APROC_OK(p)) + return; + switch (p->u.ctl.tstate) { + case CTL_START: + p->u.ctl.tstate = CTL_STOP; + return; + case CTL_RUN: + p->u.ctl.tstate = CTL_STOP; + break; + default: +#ifdef DEBUG + if (debug_level >= 3) { + aproc_dbg(p); + dbg_puts(": ignored mmc stop\n"); + } +#endif + return; + } + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + if (!s->ops) + continue; + if (s->tstate == CTL_RUN) { +#ifdef DEBUG + if (debug_level >= 3) { + ctl_slotdbg(p, i); + dbg_puts(": requested to stop\n"); + } +#endif + s->ops->stop(s->arg); + } + } +} + +/* + * relocate all slots simultaneously + */ +void +ctl_loc(struct aproc *p, unsigned origin) +{ + unsigned i, tstate; + struct ctl_slot *s; + + if (!APROC_OK(p)) + return; +#ifdef DEBUG + if (debug_level >= 2) { + dbg_puts("server relocated to "); + dbg_putu(origin); + dbg_puts("\n"); + } +#endif + tstate = p->u.ctl.tstate; + if (tstate == CTL_RUN) + ctl_stop(p); + p->u.ctl.origin = origin; + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + if (!s->ops) + continue; + s->ops->loc(s->arg, p->u.ctl.origin); + } + if (tstate == CTL_RUN) + ctl_start(p); +} + +/* + * check if there are controlled streams + */ +int +ctl_idle(struct aproc *p) +{ + unsigned i; + struct ctl_slot *s; + + if (!APROC_OK(p)) + return 1; + for (i = 0, s = p->u.ctl.slot; i < CTL_NSLOT; i++, s++) { + if (s->ops) + return 0; + } + return 1; +} + +/* * handle a MIDI event received from ibuf */ void @@ -883,41 +1011,21 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) switch (ibuf->r.midi.msg[4]) { case 0x01: /* mmc stop */ #ifdef DEBUG - if (debug_level >= 1) { + if (debug_level >= 3) { abuf_dbg(ibuf); dbg_puts(": mmc stop\n"); } #endif - if (p->u.ctl.tstate == CTL_RUN || - p->u.ctl.tstate == CTL_START) - p->u.ctl.tstate = CTL_STOP; -#ifdef DEBUG - else { - if (debug_level >= 1) { - aproc_dbg(p); - dbg_puts(": ignored mmc stop\n"); - } - } -#endif + ctl_stop(p); break; case 0x02: /* mmc start */ #ifdef DEBUG - if (debug_level >= 1) { + if (debug_level >= 3) { abuf_dbg(ibuf); dbg_puts(": mmc start\n"); } #endif - if (p->u.ctl.tstate == CTL_STOP) { - p->u.ctl.tstate = CTL_START; - (void)ctl_trystart(p, -1); -#ifdef DEBUG - } else { - if (debug_level >= 1) { - abuf_dbg(ibuf); - dbg_puts(": ignoring mmc start\n"); - } -#endif - } + ctl_start(p); break; } } @@ -943,20 +1051,12 @@ ctl_ev(struct aproc *p, struct abuf *ibuf) p->u.ctl.origin = 0; return; } - p->u.ctl.origin = + ctl_loc(p, (ibuf->r.midi.msg[7] & 0x1f) * 3600 * MTC_SEC + ibuf->r.midi.msg[8] * 60 * MTC_SEC + ibuf->r.midi.msg[9] * MTC_SEC + ibuf->r.midi.msg[10] * (MTC_SEC / fps) + - ibuf->r.midi.msg[11] * (MTC_SEC / 100 / fps); -#ifdef DEBUG - if (debug_level >= 1) { - aproc_dbg(p); - dbg_puts(": relocated to "); - dbg_putu(p->u.ctl.origin); - dbg_puts("\n"); - } -#endif + ibuf->r.midi.msg[11] * (MTC_SEC / 100 / fps)); } } diff --git a/usr.bin/aucat/midi.h b/usr.bin/aucat/midi.h index 809dc2ae90f..89f7703c8a5 100644 --- a/usr.bin/aucat/midi.h +++ b/usr.bin/aucat/midi.h @@ -1,4 +1,4 @@ -/* $OpenBSD: midi.h,v 1.7 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: midi.h,v 1.8 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -27,4 +27,8 @@ int ctl_slotstart(struct aproc *, int); void ctl_slotstop(struct aproc *, int); void ctl_ontick(struct aproc *, int); +void ctl_stop(struct aproc *); +void ctl_start(struct aproc *); +int ctl_idle(struct aproc *); + #endif /* !defined(MIDI_H) */ diff --git a/usr.bin/aucat/midicat.1 b/usr.bin/aucat/midicat.1 index 11c828798e1..6baec0a512c 100644 --- a/usr.bin/aucat/midicat.1 +++ b/usr.bin/aucat/midicat.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: midicat.1,v 1.8 2010/04/03 17:59:17 ratchov Exp $ +.\" $OpenBSD: midicat.1,v 1.9 2010/04/06 20:07:01 ratchov Exp $ .\" .\" Copyright (c) 2006 Alexandre Ratchov <alex@caoua.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 3 2010 $ +.Dd $Mdocdate: April 6 2010 $ .Dt MIDICAT 1 .Os .Sh NAME @@ -23,9 +23,9 @@ .Sh SYNOPSIS .Nm midicat .Op Fl dl -.Op Fl f Ar device .Op Fl i Ar file .Op Fl o Ar file +.Op Fl q Ar device .Op Fl U Ar unit .Sh DESCRIPTION The @@ -45,13 +45,6 @@ If this option is specified, .Nm will run in the foreground and log to .Em stderr . -.It Fl f Ar device -The -.Xr midi 4 -device or -.Nm -socket to use for MIDI input/output. -In server mode, devices are subscribed to the MIDI thru box. .It Fl i Ar file Send contents of this file to the device. If the option argument is @@ -71,6 +64,11 @@ Store received data from the device into this file. If the option argument is .Sq - then standard output will be used. +.It Fl q Ar device +The +.Xr sndio 7 +MIDI device to use for MIDI input/output. +In server mode, devices are subscribed to the MIDI thru box. .It Fl U Ar unit Use the given unit number when creating a software MIDI thru box. Only one diff --git a/usr.bin/aucat/opt.c b/usr.bin/aucat/opt.c index 2bcaee5a3fc..a8975338ee8 100644 --- a/usr.bin/aucat/opt.c +++ b/usr.bin/aucat/opt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: opt.c,v 1.6 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: opt.c,v 1.7 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -27,8 +27,8 @@ struct optlist opt_list = SLIST_HEAD_INITIALIZER(&opt_list); void -opt_new(char *name, - struct aparams *wpar, struct aparams *rpar, int maxweight, int mmc) +opt_new(char *name, struct aparams *wpar, struct aparams *rpar, + int maxweight, int mmc, unsigned mode) { struct opt *o; unsigned len; @@ -54,21 +54,39 @@ opt_new(char *name, exit(1); } memcpy(o->name, name, len + 1); - o->wpar = *wpar; - o->rpar = *rpar; + if (mode & MODE_RECMASK) + o->wpar = (mode & MODE_MON) ? *rpar : *wpar; + if (mode & MODE_PLAY) + o->rpar = *rpar; o->maxweight = maxweight; o->mmc = mmc; + o->mode = mode; #ifdef DEBUG if (debug_level >= 2) { dbg_puts(o->name); - dbg_puts(": rec "); - aparams_dbg(&o->wpar); - dbg_puts(", play "); - aparams_dbg(&o->rpar); - dbg_puts(", vol "); - dbg_putu(o->maxweight); + dbg_puts(":"); + if (mode & MODE_REC) { + dbg_puts(" rec="); + dbg_putu(o->wpar.cmin); + dbg_puts(":"); + dbg_putu(o->wpar.cmax); + } + if (mode & MODE_PLAY) { + dbg_puts(" play="); + dbg_putu(o->rpar.cmin); + dbg_puts(":"); + dbg_putu(o->rpar.cmax); + dbg_puts(" vol="); + dbg_putu(o->maxweight); + } + if (mode & MODE_MON) { + dbg_puts(" mon="); + dbg_putu(o->wpar.cmin); + dbg_puts(":"); + dbg_putu(o->wpar.cmax); + } if (o->mmc) - dbg_puts(", mmc"); + dbg_puts(" mmc"); dbg_puts("\n"); } #endif diff --git a/usr.bin/aucat/opt.h b/usr.bin/aucat/opt.h index 875f0537601..e920a2f2f30 100644 --- a/usr.bin/aucat/opt.h +++ b/usr.bin/aucat/opt.h @@ -1,4 +1,4 @@ -/* $OpenBSD: opt.h,v 1.4 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: opt.h,v 1.5 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -28,11 +28,18 @@ struct opt { struct aparams wpar; /* template for clients write params */ struct aparams rpar; /* template for clients read params */ int mmc; /* true if MMC control enabled */ +#define MODE_PLAY 0x1 /* allowed to play */ +#define MODE_REC 0x2 /* allowed to rec */ +#define MODE_MIDIIN 0x4 /* allowed to read midi */ +#define MODE_MIDIOUT 0x8 /* allowed to write midi */ +#define MODE_MON 0x10 /* allowed to monitor */ +#define MODE_RECMASK (MODE_REC | MODE_MON) + unsigned mode; /* bitmap of above */ }; SLIST_HEAD(optlist,opt); -void opt_new(char *, struct aparams *, struct aparams *, int, int); +void opt_new(char *, struct aparams *, struct aparams *, int, int, unsigned); struct opt *opt_byname(char *); #endif /* !defined(OPT_H) */ diff --git a/usr.bin/aucat/pipe.c b/usr.bin/aucat/pipe.c index 76f98baa832..065f6f0ad7a 100644 --- a/usr.bin/aucat/pipe.c +++ b/usr.bin/aucat/pipe.c @@ -139,3 +139,54 @@ pipe_close(struct file *file) close(f->fd); } + +off_t +pipe_endpos(struct file *file) +{ + struct pipe *f = (struct pipe *)file; + off_t pos; + + pos = lseek(f->fd, 0, SEEK_END); + if (pos < 0) { +#ifdef DEBUG + file_dbg(&f->file); + dbg_puts(": couldn't get file size\n"); +#endif + return 0; + } + return pos; +} + +int +pipe_seek(struct file *file, off_t pos) +{ + struct pipe *f = (struct pipe *)file; + off_t newpos; + + newpos = lseek(f->fd, pos, SEEK_SET); + if (newpos < 0) { +#ifdef DEBUG + file_dbg(&f->file); + dbg_puts(": couldn't seek\n"); +#endif + /* XXX: call eof() */ + return 0; + } + return 1; +} + +int +pipe_trunc(struct file *file, off_t pos) +{ + struct pipe *f = (struct pipe *)file; + + if (ftruncate(f->fd, pos) < 0) { +#ifdef DEBUG + file_dbg(&f->file); + dbg_puts(": couldn't truncate file\n"); +#endif + /* XXX: call hup() */ + return 0; + } + return 1; +} diff --git a/usr.bin/aucat/pipe.h b/usr.bin/aucat/pipe.h index adcc0e89f5f..63ecb85b7ad 100644 --- a/usr.bin/aucat/pipe.h +++ b/usr.bin/aucat/pipe.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pipe.h,v 1.4 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: pipe.h,v 1.5 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -33,5 +33,8 @@ unsigned pipe_write(struct file *, unsigned char *, unsigned); int pipe_nfds(struct file *); int pipe_pollfd(struct file *, struct pollfd *, int); int pipe_revents(struct file *, struct pollfd *); +int pipe_seek(struct file *, off_t); +int pipe_trunc(struct file *, off_t); +off_t pipe_endpos(struct file *); #endif /* !defined(PIPE_H) */ diff --git a/usr.bin/aucat/siofile.c b/usr.bin/aucat/siofile.c index f52a0c2d647..72577eff1b2 100644 --- a/usr.bin/aucat/siofile.c +++ b/usr.bin/aucat/siofile.c @@ -1,4 +1,4 @@ -/* $OpenBSD: siofile.c,v 1.3 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: siofile.c,v 1.4 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -26,6 +26,7 @@ #include "aparams.h" #include "aproc.h" +#include "abuf.h" #include "conf.h" #include "dev.h" #include "file.h" @@ -37,6 +38,9 @@ struct siofile { struct file file; struct sio_hdl *hdl; + unsigned wtickets, wbpf; + unsigned rtickets, rbpf; + unsigned bufsz; int started; }; @@ -62,6 +66,95 @@ struct fileops siofile_ops = { siofile_revents }; +int wsio_out(struct aproc *, struct abuf *); +int rsio_in(struct aproc *, struct abuf *); + +struct aproc_ops rsio_ops = { + "rsio", + rsio_in, + rfile_out, + rfile_eof, + rfile_hup, + NULL, /* newin */ + NULL, /* newout */ + aproc_ipos, + aproc_opos, + rfile_done +}; + +struct aproc_ops wsio_ops = { + "wsio", + wfile_in, + wsio_out, + wfile_eof, + wfile_hup, + NULL, /* newin */ + NULL, /* newout */ + aproc_ipos, + aproc_opos, + wfile_done +}; + +struct aproc * +rsio_new(struct file *f) +{ + struct aproc *p; + + p = aproc_new(&rsio_ops, f->name); + p->u.io.file = f; + p->u.io.partial = 0; + f->rproc = p; + return p; +} + +struct aproc * +wsio_new(struct file *f) +{ + struct aproc *p; + + p = aproc_new(&wsio_ops, f->name); + p->u.io.file = f; + p->u.io.partial = 0; + f->wproc = p; + return p; +} + +int +wsio_out(struct aproc *p, struct abuf *obuf) +{ + struct siofile *f = (struct siofile *)p->u.io.file; + + if (f->wtickets == 0) { +#ifdef DEBUG + if (debug_level >= 4) { + file_dbg(&f->file); + dbg_puts(": no more write tickets\n"); + } +#endif + f->file.state &= ~FILE_WOK; + return 0; + } + return wfile_out(p, obuf); +} + +int +rsio_in(struct aproc *p, struct abuf *ibuf) +{ + struct siofile *f = (struct siofile *)p->u.io.file; + + if (f->rtickets == 0) { +#ifdef DEBUG + if (debug_level >= 4) { + file_dbg(&f->file); + dbg_puts(": no more read tickets\n"); + } +#endif + f->file.state &= ~FILE_ROK; + return 0; + } + return rfile_in(p, ibuf); +} + void siofile_cb(void *addr, int delta) { @@ -70,12 +163,18 @@ siofile_cb(void *addr, int delta) #ifdef DEBUG if (delta < 0 || delta > (60 * RATE_MAX)) { - dbg_puts(f->file.name); + file_dbg(&f->file); dbg_puts(": "); dbg_puti(delta); dbg_puts(": bogus sndio delta"); dbg_panic(); } + if (debug_level >= 4) { + file_dbg(&f->file); + dbg_puts(": tick, delta = "); + dbg_puti(delta); + dbg_puts("\n"); + } #endif if (delta != 0) { p = f->file.wproc; @@ -87,31 +186,27 @@ siofile_cb(void *addr, int delta) if (p && p->ops->ipos) p->ops->ipos(p, NULL, delta); } + f->wtickets += delta * f->wbpf; + f->rtickets += delta * f->rbpf; } /* * Open the device. */ struct siofile * -siofile_new(struct fileops *ops, char *path, +siofile_new(struct fileops *ops, char *path, unsigned mode, struct aparams *ipar, struct aparams *opar, unsigned *bufsz, unsigned *round) { struct sio_par par; struct sio_hdl *hdl; struct siofile *f; - int mode; - mode = 0; - if (ipar) - mode |= SIO_REC; - if (opar) - mode |= SIO_PLAY; hdl = sio_open(path, mode, 1); if (hdl == NULL) return NULL; sio_initpar(&par); - if (ipar) { + if (mode & SIO_REC) { par.bits = ipar->bits; par.bps = ipar->bps; par.sig = ipar->sig; @@ -127,7 +222,7 @@ siofile_new(struct fileops *ops, char *path, par.msb = opar->msb; par.rate = opar->rate; } - if (opar) + if (mode & SIO_PLAY) par.pchan = opar->cmax - opar->cmin + 1; par.appbufsz = *bufsz; par.round = *round; @@ -135,7 +230,7 @@ siofile_new(struct fileops *ops, char *path, goto bad_close; if (!sio_getpar(hdl, &par)) goto bad_close; - if (ipar) { + if (mode & SIO_REC) { ipar->bits = par.bits; ipar->bps = par.bps; ipar->sig = par.sig; @@ -144,7 +239,7 @@ siofile_new(struct fileops *ops, char *path, ipar->rate = par.rate; ipar->cmax = ipar->cmin + par.rchan - 1; } - if (opar) { + if (mode & SIO_PLAY) { opar->bits = par.bits; opar->bps = par.bps; opar->sig = par.sig; @@ -162,6 +257,11 @@ siofile_new(struct fileops *ops, char *path, goto bad_close; f->hdl = hdl; f->started = 0; + f->wtickets = 0; + f->rtickets = 0; + f->wbpf = par.pchan * par.bps; + f->rbpf = par.rchan * par.bps; + f->bufsz = par.bufsz; sio_onmove(f->hdl, siofile_cb, f); return f; bad_close: @@ -183,6 +283,8 @@ siofile_start(struct file *file) return; } f->started = 1; + f->wtickets = f->bufsz * f->wbpf; + f->rtickets = 0; #ifdef DEBUG if (debug_level >= 3) { file_dbg(&f->file); @@ -219,6 +321,14 @@ siofile_read(struct file *file, unsigned char *data, unsigned count) struct siofile *f = (struct siofile *)file; unsigned n; +#ifdef DEBUG + if (f->rtickets == 0) { + file_dbg(&f->file); + dbg_puts(": called with no read tickets\n"); + } +#endif + if (count > f->rtickets) + count = f->rtickets; n = f->started ? sio_read(f->hdl, data, count) : 0; if (n == 0) { f->file.state &= ~FILE_ROK; @@ -237,6 +347,17 @@ siofile_read(struct file *file, unsigned char *data, unsigned count) #endif } return 0; + } else { + f->rtickets -= n; + if (f->rtickets == 0) { + f->file.state &= ~FILE_ROK; +#ifdef DEBUG + if (debug_level >= 4) { + file_dbg(&f->file); + dbg_puts(": read tickets exhausted\n"); + } +#endif + } } return n; @@ -248,6 +369,14 @@ siofile_write(struct file *file, unsigned char *data, unsigned count) struct siofile *f = (struct siofile *)file; unsigned n; +#ifdef DEBUG + if (f->wtickets == 0) { + file_dbg(&f->file); + dbg_puts(": called with no write tickets\n"); + } +#endif + if (count > f->wtickets) + count = f->wtickets; n = f->started ? sio_write(f->hdl, data, count) : 0; if (n == 0) { f->file.state &= ~FILE_WOK; @@ -266,6 +395,17 @@ siofile_write(struct file *file, unsigned char *data, unsigned count) #endif } return 0; + } else { + f->wtickets -= n; + if (f->wtickets == 0) { + f->file.state &= ~FILE_WOK; +#ifdef DEBUG + if (debug_level >= 4) { + file_dbg(&f->file); + dbg_puts(": write tickets exhausted\n"); + } +#endif + } } return n; } diff --git a/usr.bin/aucat/siofile.h b/usr.bin/aucat/siofile.h index b218bf1080d..7f72255a347 100644 --- a/usr.bin/aucat/siofile.h +++ b/usr.bin/aucat/siofile.h @@ -1,4 +1,4 @@ -/* $OpenBSD: siofile.h,v 1.3 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: siofile.h,v 1.4 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -20,9 +20,12 @@ struct fileops; struct siofile; struct aparams; +struct aproc; -struct siofile *siofile_new(struct fileops *, char *, +struct siofile *siofile_new(struct fileops *, char *, unsigned, struct aparams *, struct aparams *, unsigned *, unsigned *); +struct aproc *rsio_new(struct file *f); +struct aproc *wsio_new(struct file *f); extern struct fileops siofile_ops; diff --git a/usr.bin/aucat/sock.c b/usr.bin/aucat/sock.c index bb12d836d70..eb7f6a68bd3 100644 --- a/usr.bin/aucat/sock.c +++ b/usr.bin/aucat/sock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sock.c,v 1.41 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: sock.c,v 1.42 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -14,12 +14,6 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* - * TODO: - * - * change f->bufsz to contain only socket-side buffer, - * because it's less error prone - */ #include <stdio.h> #include <stdlib.h> @@ -36,7 +30,7 @@ #include "dbg.h" #endif -int sock_attach(struct sock *, int); +void sock_attach(struct sock *, int); int sock_read(struct sock *); int sock_write(struct sock *); int sock_execmsg(struct sock *); @@ -59,11 +53,11 @@ struct fileops sock_ops = { void sock_dbg(struct sock *f) { - static char *pstates[] = { "hel", "ini", "sta", "run", "mid" }; + static char *pstates[] = { "hel", "ini", "sta", "rdy", "run", "mid" }; static char *rstates[] = { "rdat", "rmsg", "rret" }; static char *wstates[] = { "widl", "wmsg", "wdat" }; - if (f->slot >= 0 && dev_midi) { + if (f->slot >= 0 && APROC_OK(dev_midi)) { dbg_puts(dev_midi->u.ctl.slot[f->slot].name); dbg_putu(dev_midi->u.ctl.slot[f->slot].unit); } else @@ -79,10 +73,14 @@ sock_dbg(struct sock *f) void sock_setvol(void *, unsigned); void sock_startreq(void *); +void sock_stopreq(void *); +void sock_locreq(void *, unsigned); struct ctl_ops ctl_sockops = { sock_setvol, - sock_startreq + sock_startreq, + sock_stopreq, + sock_locreq }; void @@ -158,7 +156,7 @@ rsock_opos(struct aproc *p, struct abuf *obuf, int delta) { struct sock *f = (struct sock *)p->u.io.file; - if (f->mode & AMSG_REC) + if (f->mode & AMSG_RECMASK) return; f->delta += delta; @@ -257,7 +255,7 @@ wsock_ipos(struct aproc *p, struct abuf *obuf, int delta) { struct sock *f = (struct sock *)p->u.io.file; - if (!(f->mode & AMSG_REC)) + if (!(f->mode & AMSG_RECMASK)) return; f->delta += delta; @@ -306,9 +304,9 @@ sock_new(struct fileops *ops, int fd) f->mode = 0; f->opt = opt_byname("default"); if (f->opt) { - if (dev_sub) + if (f->opt->mode & MODE_RECMASK) f->wpar = f->opt->wpar; - if (dev_mix) + if (f->opt->mode & MODE_PLAY) f->rpar = f->opt->rpar; } f->xrun = AMSG_IGNORE; @@ -316,17 +314,20 @@ sock_new(struct fileops *ops, int fd) f->round = dev_round; f->delta = 0; f->tickpending = 0; + f->startpending = 0; f->vol = f->lastvol = MIDI_MAXCTL; f->slot = -1; wproc = aproc_new(&wsock_ops, f->pipe.file.name); wproc->u.io.file = &f->pipe.file; + wproc->u.io.partial = 0; f->pipe.file.wproc = wproc; f->wstate = SOCK_WIDLE; f->wtodo = 0xdeadbeef; rproc = aproc_new(&rsock_ops, f->pipe.file.name); rproc->u.io.file = &f->pipe.file; + rproc->u.io.partial = 0; f->pipe.file.rproc = rproc; f->rstate = SOCK_RMSG; f->rtodo = sizeof(struct amsg); @@ -357,6 +358,7 @@ sock_freebuf(struct sock *f) if (wbuf) abuf_hup(wbuf); f->tickpending = 0; + f->startpending = 0; } /* @@ -367,28 +369,40 @@ sock_allocbuf(struct sock *f) { struct abuf *rbuf = NULL, *wbuf = NULL; + f->pstate = SOCK_START; if (f->mode & AMSG_PLAY) { rbuf = abuf_new(f->bufsz, &f->rpar); aproc_setout(f->pipe.file.rproc, rbuf); + if (!ABUF_WOK(rbuf) || (f->pipe.file.state & FILE_EOF)) + f->pstate = SOCK_READY; } - if (f->mode & AMSG_REC) { + if (f->mode & AMSG_RECMASK) { wbuf = abuf_new(f->bufsz, &f->wpar); aproc_setin(f->pipe.file.wproc, wbuf); - f->walign = dev_round * wbuf->bpf; + f->walign = f->round; } f->delta = 0; + f->wmax = 0; + f->rmax = f->bufsz; f->tickpending = 0; + f->startpending = 0; #ifdef DEBUG if (debug_level >= 3) { sock_dbg(f); dbg_puts(": allocating "); dbg_putu(f->bufsz); - dbg_puts(" fr buffers\n"); + dbg_puts(" fr buffers, rmax = "); + dbg_putu(f->rmax); + dbg_puts("\n"); } #endif - f->pstate = SOCK_START; - if (!(f->mode & AMSG_PLAY) && ctl_slotstart(dev_midi, f->slot)) - (void)sock_attach(f, 0); + if (f->mode & AMSG_PLAY) { + f->pstate = SOCK_START; + } else { + f->pstate = SOCK_READY; + if (ctl_slotstart(dev_midi, f->slot)) + (void)sock_attach(f, 0); + } } /* @@ -423,9 +437,9 @@ sock_startreq(void *arg) struct sock *f = (struct sock *)arg; #ifdef DEBUG - if (f->pstate != SOCK_START) { + if (f->pstate != SOCK_READY) { sock_dbg(f); - dbg_puts(": not in START state\n"); + dbg_puts(": not in READY state\n"); dbg_panic(); } #endif @@ -433,9 +447,41 @@ sock_startreq(void *arg) } /* + * Callback invoked by MMC stop + */ +void +sock_stopreq(void *arg) +{ +#ifdef DEBUG + struct sock *f = (struct sock *)arg; + + if (debug_level >= 3) { + sock_dbg(f); + dbg_puts(": ignored STOP signal\n"); + } +#endif +} + +/* + * Callback invoked by MMC relocate, ignored + */ +void +sock_locreq(void *arg, unsigned mmcpos) +{ +#ifdef DEBUG + struct sock *f = (struct sock *)arg; + + if (debug_level >= 3) { + sock_dbg(f); + dbg_puts(": ignored RELOCATE signal\n"); + } +#endif +} + +/* * Attach play and/or record buffers to dev_mix and/or dev_sub. */ -int +void sock_attach(struct sock *f, int force) { struct abuf *rbuf, *wbuf; @@ -448,23 +494,30 @@ sock_attach(struct sock *f, int force) * the buffer isn't completely filled. */ if (!force && rbuf && ABUF_WOK(rbuf)) - return 0; + return; + /* + * get the current position, the origin is when + * the first sample is played/recorded + */ + f->delta = dev_getpos() * (int)f->round / (int)dev_round; + f->startpending = 1; + f->pstate = SOCK_RUN; #ifdef DEBUG if (debug_level >= 3) { sock_dbg(f); - dbg_puts(": attaching to device\n"); + dbg_puts(": attaching at "); + dbg_puti(f->delta); + dbg_puts("\n"); } #endif - f->pstate = SOCK_RUN; - /* - * Attach them to the device. + * We dont check whether the device is dying, + * because dev_xxx() functions are supposed to + * work (i.e., not to crash) */ - dev_attach(f->pipe.file.name, - (f->mode & AMSG_PLAY) ? rbuf : NULL, &f->rpar, f->xrun, - (f->mode & AMSG_REC) ? wbuf : NULL, &f->wpar, f->xrun, - f->opt->maxweight); + dev_attach(f->pipe.file.name, f->mode, + rbuf, &f->rpar, wbuf, &f->wpar, f->xrun, f->opt->maxweight); if (f->mode & AMSG_PLAY) dev_setvol(rbuf, MIDI_TO_ADATA(f->vol)); @@ -475,7 +528,6 @@ sock_attach(struct sock *f, int force) if (!sock_write(f)) break; } - return 1; } void @@ -483,6 +535,7 @@ sock_reset(struct sock *f) { switch (f->pstate) { case SOCK_START: + case SOCK_READY: if (ctl_slotstart(dev_midi, f->slot)) { (void)sock_attach(f, 1); f->pstate = SOCK_RUN; @@ -584,8 +637,7 @@ sock_rdata(struct sock *f) { struct aproc *p; struct abuf *obuf; - unsigned char *data; - unsigned count, n; + unsigned n; #ifdef DEBUG if (f->pstate != SOCK_MIDI && f->rtodo == 0) { @@ -598,17 +650,20 @@ sock_rdata(struct sock *f) obuf = LIST_FIRST(&p->obuflist); if (obuf == NULL) return 0; - if (ABUF_FULL(obuf) || !(f->pipe.file.state & FILE_ROK)) - return 0; - data = abuf_wgetblk(obuf, &count, 0); - if (f->pstate != SOCK_MIDI && count > f->rtodo) - count = f->rtodo; - n = file_read(&f->pipe.file, data, count); - if (n == 0) + if (!ABUF_WOK(obuf) || !(f->pipe.file.state & FILE_ROK)) return 0; - abuf_wcommit(obuf, n); - if (f->pstate != SOCK_MIDI) + if (f->pstate == SOCK_MIDI) { + if (!rfile_do(p, obuf->len, NULL)) + return 0; + } else { + if (!rfile_do(p, f->rtodo, &n)) + return 0; f->rtodo -= n; + if (f->pstate == SOCK_START) { + if (!ABUF_WOK(obuf) || (f->pipe.file.state & FILE_EOF)) + f->pstate = SOCK_READY; + } + } return 1; } @@ -621,10 +676,7 @@ sock_wdata(struct sock *f) { struct aproc *p; struct abuf *ibuf; - unsigned char *data; - unsigned count, n; -#define ZERO_MAX 0x1000 - static unsigned char zero[ZERO_MAX]; + unsigned n; #ifdef DEBUG if (f->pstate != SOCK_MIDI && f->wtodo == 0) { @@ -637,32 +689,22 @@ sock_wdata(struct sock *f) return 0; p = f->pipe.file.wproc; ibuf = LIST_FIRST(&p->ibuflist); - if (ibuf) { - if (ABUF_EMPTY(ibuf)) - return 0; - data = abuf_rgetblk(ibuf, &count, 0); - if (f->pstate != SOCK_MIDI && count > f->wtodo) - count = f->wtodo; - n = file_write(&f->pipe.file, data, count); - if (n == 0) +#ifdef DEBUG + if (f->pstate != SOCK_MIDI && ibuf == NULL) { + sock_dbg(f); + dbg_puts(": attempted to write on detached buffer\n"); + dbg_panic(); + } +#endif + if (ibuf == NULL) + return 0; + if (!ABUF_ROK(ibuf)) + return 0; + if (f->pstate == SOCK_MIDI) { + if (!wfile_do(p, ibuf->len, NULL)) return 0; - abuf_rdiscard(ibuf, n); - if (f->pstate != SOCK_MIDI) - f->wtodo -= n; } else { - if (f->pstate == SOCK_MIDI) - return 0; - /* - * There's no dev_detach() routine yet, - * so now we abruptly destroy the buffer. - * Until we implement dev_detach, complete - * the packet with zeros... - */ - count = ZERO_MAX; - if (count > f->wtodo) - count = f->wtodo; - n = file_write(&f->pipe.file, zero, count); - if (n == 0) + if (!wfile_do(p, f->wtodo, &n)) return 0; f->wtodo -= n; } @@ -720,7 +762,7 @@ sock_setpar(struct sock *f) f->rpar.le = f->wpar.le = p->le ? 1 : 0; if (AMSG_ISSET(p->msb)) f->rpar.msb = f->wpar.msb = p->msb ? 1 : 0; - if (AMSG_ISSET(p->rchan) && (f->mode & AMSG_REC)) { + if (AMSG_ISSET(p->rchan) && (f->mode & AMSG_RECMASK)) { if (p->rchan < 1) p->rchan = 1; if (p->rchan > NCHAN_MAX) @@ -835,8 +877,8 @@ sock_setpar(struct sock *f) } if (AMSG_ISSET(p->appbufsz)) { rate = (f->mode & AMSG_PLAY) ? f->rpar.rate : f->wpar.rate; - min = 2; - max = 2 + rate / dev_round; + min = 1; + max = 1 + rate / dev_round; min *= f->round; max *= f->round; p->appbufsz += f->round - 1; @@ -857,7 +899,7 @@ sock_setpar(struct sock *f) } #ifdef DEBUG if (debug_level >= 2) { - if (f->slot >= -1 && dev_midi) { + if (f->slot >= 0 && dev_midi) { dbg_puts(dev_midi->u.ctl.slot[f->slot].name); dbg_putu(dev_midi->u.ctl.slot[f->slot].unit); } else @@ -868,7 +910,7 @@ sock_setpar(struct sock *f) dbg_puts(", play = "); aparams_dbg(&f->rpar); } - if (f->mode & AMSG_REC) { + if (f->mode & AMSG_RECMASK) { dbg_puts(", rec:"); aparams_dbg(&f->wpar); } @@ -928,7 +970,7 @@ sock_hello(struct sock *f) /* * XXX : dev_midi can no longer be NULL, right ? */ - if (dev_midi && (p->proto & (AMSG_MIDIIN | AMSG_MIDIOUT))) { + if (APROC_OK(dev_midi) && (p->proto & (AMSG_MIDIIN | AMSG_MIDIOUT))) { if (p->proto & ~(AMSG_MIDIIN | AMSG_MIDIOUT)) { #ifdef DEBUG if (debug_level >= 1) { @@ -948,9 +990,9 @@ sock_hello(struct sock *f) f->opt = opt_byname(p->opt); if (f->opt == NULL) return 0; - if (dev_sub) + if (f->opt->mode & MODE_RECMASK) f->wpar = f->opt->wpar; - if (dev_mix) + if (f->opt->mode & MODE_PLAY) f->rpar = f->opt->rpar; if (f->opt->mmc) f->xrun = AMSG_SYNC; @@ -968,7 +1010,7 @@ sock_hello(struct sock *f) } f->mode = 0; if (p->proto & AMSG_PLAY) { - if (!dev_mix) { + if (!APROC_OK(dev_mix) || !(f->opt->mode & MODE_PLAY)) { #ifdef DEBUG if (debug_level >= 1) { sock_dbg(f); @@ -980,7 +1022,8 @@ sock_hello(struct sock *f) f->mode |= AMSG_PLAY; } if (p->proto & AMSG_REC) { - if (!dev_sub) { + if (!(APROC_OK(dev_sub) && (f->opt->mode & MODE_REC)) && + !(APROC_OK(dev_submon) && (f->opt->mode & MODE_MON))) { #ifdef DEBUG if (debug_level >= 1) { sock_dbg(f); @@ -989,9 +1032,9 @@ sock_hello(struct sock *f) #endif return 0; } - f->mode |= AMSG_REC; + f->mode |= (f->opt->mode & MODE_MON) ? AMSG_MON : AMSG_REC; } - if (dev_midi) { + if (APROC_OK(dev_midi)) { f->slot = ctl_slotnew(dev_midi, p->who, &ctl_sockops, f, f->opt->mmc); @@ -1017,6 +1060,7 @@ int sock_execmsg(struct sock *f) { struct amsg *m = &f->rmsg; + struct abuf *obuf; switch (m->cmd) { case AMSG_DATA: @@ -1026,7 +1070,8 @@ sock_execmsg(struct sock *f) dbg_puts(": DATA message\n"); } #endif - if (f->pstate != SOCK_RUN && f->pstate != SOCK_START) { + if (f->pstate != SOCK_RUN && f->pstate != SOCK_START && + f->pstate != SOCK_READY) { #ifdef DEBUG if (debug_level >= 1) { sock_dbg(f); @@ -1046,8 +1091,8 @@ sock_execmsg(struct sock *f) aproc_del(f->pipe.file.rproc); return 0; } - if (f->pstate == SOCK_START && - ABUF_FULL(LIST_FIRST(&f->pipe.file.rproc->obuflist))) { + obuf = LIST_FIRST(&f->pipe.file.rproc->obuflist); + if (f->pstate == SOCK_START && !ABUF_WOK(obuf)) { #ifdef DEBUG if (debug_level >= 1) { sock_dbg(f); @@ -1057,8 +1102,31 @@ sock_execmsg(struct sock *f) aproc_del(f->pipe.file.rproc); return 0; } + if (m->u.data.size % obuf->bpf != 0) { +#ifdef DEBUG + if (debug_level >= 1) { + sock_dbg(f); + dbg_puts(": unaligned data chunk\n"); + } +#endif + aproc_del(f->pipe.file.rproc); + return 0; + } f->rstate = SOCK_RDATA; - f->rtodo = m->u.data.size; + f->rtodo = m->u.data.size / obuf->bpf; +#ifdef DEBUG + if (f->rtodo > f->rmax && debug_level >= 2) { + sock_dbg(f); + dbg_puts(": received past current position, rtodo = "); + dbg_putu(f->rtodo); + dbg_puts(", rmax = "); + dbg_putu(f->rmax); + dbg_puts("\n"); + aproc_del(f->pipe.file.rproc); + return 0; + } +#endif + f->rmax -= f->rtodo; if (f->rtodo == 0) { #ifdef DEBUG if (debug_level >= 1) { @@ -1098,7 +1166,8 @@ sock_execmsg(struct sock *f) dbg_puts(": STOP message\n"); } #endif - if (f->pstate != SOCK_RUN && f->pstate != SOCK_START) { + if (f->pstate != SOCK_RUN && + f->pstate != SOCK_START && f->pstate != SOCK_READY) { #ifdef DEBUG if (debug_level >= 1) { sock_dbg(f); @@ -1107,11 +1176,18 @@ sock_execmsg(struct sock *f) #endif aproc_del(f->pipe.file.rproc); return 0; + /* + * XXX: device could have desappeared at this point, + * see how this is fixed in wav.c + */ } - if (f->pstate == SOCK_START && + if ((f->pstate == SOCK_START || f->pstate == SOCK_READY) && ctl_slotstart(dev_midi, f->slot)) (void)sock_attach(f, 1); - sock_freebuf(f); + if (f->wstate != SOCK_WDATA || f->wtodo == 0) + sock_freebuf(f); + else + f->pstate = SOCK_STOP; AMSG_INIT(m); m->cmd = AMSG_ACK; f->rstate = SOCK_RRET; @@ -1161,14 +1237,24 @@ sock_execmsg(struct sock *f) AMSG_INIT(m); m->cmd = AMSG_GETPAR; m->u.par.legacy_mode = f->mode; - m->u.par.bits = f->rpar.bits; - m->u.par.bps = f->rpar.bps; - m->u.par.sig = f->rpar.sig; - m->u.par.le = f->rpar.le; - m->u.par.msb = f->rpar.msb; - m->u.par.rate = f->rpar.rate; - m->u.par.rchan = f->wpar.cmax - f->wpar.cmin + 1; - m->u.par.pchan = f->rpar.cmax - f->rpar.cmin + 1; + if (f->mode & AMSG_PLAY) { + m->u.par.bits = f->rpar.bits; + m->u.par.bps = f->rpar.bps; + m->u.par.sig = f->rpar.sig; + m->u.par.le = f->rpar.le; + m->u.par.msb = f->rpar.msb; + m->u.par.rate = f->rpar.rate; + m->u.par.pchan = f->rpar.cmax - f->rpar.cmin + 1; + } + if (f->mode & AMSG_RECMASK) { + m->u.par.bits = f->wpar.bits; + m->u.par.bps = f->wpar.bps; + m->u.par.sig = f->wpar.sig; + m->u.par.le = f->wpar.le; + m->u.par.msb = f->wpar.msb; + m->u.par.rate = f->wpar.rate; + m->u.par.rchan = f->wpar.cmax - f->wpar.cmin + 1; + } m->u.par.appbufsz = f->bufsz; m->u.par.bufsz = f->bufsz + (dev_bufsz / dev_round) * f->round; @@ -1196,9 +1282,9 @@ sock_execmsg(struct sock *f) AMSG_INIT(m); m->cmd = AMSG_GETCAP; m->u.cap.rate = dev_rate; - m->u.cap.pchan = dev_mix ? + m->u.cap.pchan = (f->opt->mode & MODE_PLAY) ? (f->opt->rpar.cmax - f->opt->rpar.cmin + 1) : 0; - m->u.cap.rchan = dev_sub ? + m->u.cap.rchan = (f->opt->mode & (MODE_PLAY | MODE_REC)) ? (f->opt->wpar.cmax - f->opt->wpar.cmin + 1) : 0; m->u.cap.bits = sizeof(short) * 8; m->u.cap.bps = sizeof(short); @@ -1212,8 +1298,8 @@ sock_execmsg(struct sock *f) dbg_puts(": SETVOL message\n"); } #endif - if (f->pstate != SOCK_RUN && - f->pstate != SOCK_START && f->pstate != SOCK_INIT) { + if (f->pstate != SOCK_RUN && f->pstate != SOCK_START && + f->pstate != SOCK_INIT && f->pstate != SOCK_READY) { #ifdef DEBUG if (debug_level >= 1) { sock_dbg(f); @@ -1321,7 +1407,7 @@ sock_buildmsg(struct sock *f) { struct aproc *p; struct abuf *ibuf; - unsigned size; + unsigned size, max; if (f->pstate == SOCK_MIDI) { #ifdef DEBUG @@ -1338,7 +1424,7 @@ sock_buildmsg(struct sock *f) /* * If pos changed, build a MOVE message. */ - if (f->tickpending) { + if ((f->tickpending && f->delta > 0) || f->startpending) { #ifdef DEBUG if (debug_level >= 4) { sock_dbg(f); @@ -1347,6 +1433,9 @@ sock_buildmsg(struct sock *f) dbg_puts("\n"); } #endif + f->wmax += f->delta; + if (f->delta > 0) + f->rmax += f->delta; AMSG_INIT(&f->wmsg); f->wmsg.cmd = AMSG_MOVE; f->wmsg.u.ts.delta = f->delta; @@ -1354,6 +1443,7 @@ sock_buildmsg(struct sock *f) f->wstate = SOCK_WMSG; f->delta = 0; f->tickpending = 0; + f->startpending = 0; return 1; } @@ -1384,17 +1474,29 @@ sock_buildmsg(struct sock *f) p = f->pipe.file.wproc; ibuf = LIST_FIRST(&p->ibuflist); if (ibuf && ABUF_ROK(ibuf)) { - size = ibuf->used - (ibuf->used % ibuf->bpf); - if (size > AMSG_DATAMAX) - size = AMSG_DATAMAX - (AMSG_DATAMAX % ibuf->bpf); +#ifdef DEBUG + if (ibuf->used > f->wmax && debug_level >= 3) { + sock_dbg(f); + dbg_puts(": attempt to send past current position\n"); + } +#endif + max = AMSG_DATAMAX / ibuf->bpf; + size = ibuf->used; if (size > f->walign) size = f->walign; + if (size > f->wmax) + size = f->wmax; + if (size > max) + size = max; + if (size == 0) + return 0; f->walign -= size; + f->wmax -= size; if (f->walign == 0) - f->walign = dev_round * ibuf->bpf; + f->walign = f->round; AMSG_INIT(&f->wmsg); f->wmsg.cmd = AMSG_DATA; - f->wmsg.u.data.size = size; + f->wmsg.u.data.size = size * ibuf->bpf; f->wtodo = sizeof(struct amsg); f->wstate = SOCK_WMSG; return 1; @@ -1440,9 +1542,11 @@ sock_read(struct sock *f) f->rtodo = sizeof(struct amsg); } /* - * XXX: have to way that the buffer is full before starting + * XXX: sock_attach() may not start if there's not enough + * samples queues, if so ctl_slotstart() will trigger + * other streams, but this one won't start. */ - if (f->pstate == SOCK_START && ctl_slotstart(dev_midi, f->slot)) + if (f->pstate == SOCK_READY && ctl_slotstart(dev_midi, f->slot)) (void)sock_attach(f, 0); break; case SOCK_RRET: @@ -1525,8 +1629,12 @@ sock_write(struct sock *f) f->wtodo = 0xdeadbeef; break; } + /* + * XXX: why not set f->wtodo in sock_wmsg() ? + */ f->wstate = SOCK_WDATA; - f->wtodo = f->wmsg.u.data.size; + f->wtodo = f->wmsg.u.data.size / + LIST_FIRST(&f->pipe.file.wproc->ibuflist)->bpf; /* PASSTHROUGH */ case SOCK_WDATA: if (!sock_wdata(f)) @@ -1535,6 +1643,8 @@ sock_write(struct sock *f) break; f->wstate = SOCK_WIDLE; f->wtodo = 0xdeadbeef; + if (f->pstate == SOCK_STOP) + sock_freebuf(f); /* PASSTHROUGH */ case SOCK_WIDLE: if (!sock_return(f)) diff --git a/usr.bin/aucat/sock.h b/usr.bin/aucat/sock.h index bc6e62a1da4..abeb61f1c4e 100644 --- a/usr.bin/aucat/sock.h +++ b/usr.bin/aucat/sock.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sock.h,v 1.14 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: sock.h,v 1.15 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -30,6 +30,8 @@ struct sock { * to decode/encode messages in the stream. */ struct amsg rmsg, wmsg; /* messages being sent/received */ + unsigned wmax; /* max frames we're allowed to write */ + unsigned rmax; /* max frames we're allowed to read */ unsigned rtodo; /* input bytes not read yet */ unsigned wtodo; /* output bytes not written yet */ #define SOCK_RDATA 0 /* data chunk being read */ @@ -43,14 +45,17 @@ struct sock { #define SOCK_HELLO 0 /* waiting for HELLO message */ #define SOCK_INIT 1 /* parameter negotiation */ #define SOCK_START 2 /* filling play buffers */ -#define SOCK_RUN 3 /* attached to the mix / sub */ -#define SOCK_MIDI 4 /* raw byte stream (midi) */ +#define SOCK_READY 3 /* play buffers full */ +#define SOCK_RUN 4 /* attached to the mix / sub */ +#define SOCK_STOP 5 /* draining rec buffers */ +#define SOCK_MIDI 6 /* raw byte stream (midi) */ unsigned pstate; /* one of the above */ unsigned mode; /* a set of AMSG_PLAY, AMSG_REC */ struct aparams rpar; /* read (ie play) parameters */ struct aparams wpar; /* write (ie rec) parameters */ int delta; /* pos. change to send */ int tickpending; /* delta waiting to be transmitted */ + int startpending; /* initial delta waiting to be transmitted */ unsigned walign; /* align data packets to this */ unsigned bufsz; /* total buffer size */ unsigned round; /* block size */ diff --git a/usr.bin/aucat/wav.c b/usr.bin/aucat/wav.c index e811c61aa6b..af7a3de9982 100644 --- a/usr.bin/aucat/wav.c +++ b/usr.bin/aucat/wav.c @@ -23,7 +23,9 @@ #include "aproc.h" #include "conf.h" #include "dev.h" +#include "midi.h" #include "wav.h" +#include "opt.h" #ifdef DEBUG #include "dbg.h" #endif @@ -123,19 +125,33 @@ int rwav_out(struct aproc *, struct abuf *); void rwav_eof(struct aproc *, struct abuf *); void rwav_hup(struct aproc *, struct abuf *); void rwav_done(struct aproc *); +struct aproc *rwav_new(struct file *); int wwav_in(struct aproc *, struct abuf *); int wwav_out(struct aproc *, struct abuf *); void wwav_eof(struct aproc *, struct abuf *); void wwav_hup(struct aproc *, struct abuf *); void wwav_done(struct aproc *); +struct aproc *wwav_new(struct file *); + +void wav_setvol(void *, unsigned); +void wav_startreq(void *); +void wav_stopreq(void *); +void wav_locreq(void *, unsigned); + +struct ctl_ops ctl_wavops = { + wav_setvol, + wav_startreq, + wav_stopreq, + wav_locreq +}; struct aproc_ops rwav_ops = { "rwav", rwav_in, rwav_out, - rwav_eof, - rwav_hup, + rfile_eof, + rfile_hup, NULL, /* newin */ NULL, /* newout */ NULL, /* ipos */ @@ -147,8 +163,8 @@ struct aproc_ops wwav_ops = { "wwav", wwav_in, wwav_out, - wwav_eof, - wwav_hup, + wfile_eof, + wfile_hup, NULL, /* newin */ NULL, /* newout */ NULL, /* ipos */ @@ -156,189 +172,466 @@ struct aproc_ops wwav_ops = { wwav_done }; -struct aproc * -rwav_new(struct file *f) +#ifdef DEBUG +/* + * print the given wav structure + */ +void +wav_dbg(struct wav *f) { - struct aproc *p; + static char *pstates[] = { "ini", "sta", "rdy", "run", "fai" }; - p = aproc_new(&rwav_ops, f->name); - p->u.io.file = f; - f->rproc = p; - return p; + dbg_puts("wav("); + if (f->slot >= 0 && APROC_OK(dev_midi)) { + dbg_puts(dev_midi->u.ctl.slot[f->slot].name); + dbg_putu(dev_midi->u.ctl.slot[f->slot].unit); + } else + dbg_puts(f->pipe.file.name); + dbg_puts(")/"); + dbg_puts(pstates[f->pstate]); } +#endif -int -rwav_in(struct aproc *p, struct abuf *ibuf_dummy) +/* + * convert ``count'' samples using the given char->short map + */ +void +wav_conv(unsigned char *data, unsigned count, short *map) { - struct abuf *obuf = LIST_FIRST(&p->obuflist); - struct file *f = p->u.io.file; - unsigned char *data; - unsigned count; + unsigned i; + unsigned char *iptr; + short *optr; - if (ABUF_FULL(obuf) || !(f->state & FILE_ROK)) - return 0; - data = abuf_wgetblk(obuf, &count, 0); - count = file_read(f, data, count); - if (count == 0) - return 0; - abuf_wcommit(obuf, count); - if (!abuf_flush(obuf)) - return 0; - return 1; + iptr = data + count; + optr = (short *)data + count; + for (i = count; i > 0; i--) { + --optr; + --iptr; + *optr = map[*iptr]; + } } -int -rwav_out(struct aproc *p, struct abuf *obuf) +/* + * read method of the file structure + */ +unsigned +wav_read(struct file *file, unsigned char *data, unsigned count) { - struct file *f = p->u.io.file; - unsigned char *data; - unsigned count; + struct wav *f = (struct wav *)file; + unsigned n; - if (f->state & FILE_RINUSE) - return 0; - if (ABUF_FULL(obuf) || !(f->state & FILE_ROK)) - return 0; - data = abuf_wgetblk(obuf, &count, 0); - count = file_read(f, data, count); - if (count == 0) + if (f->map) + count /= sizeof(short); + if (f->rbytes >= 0 && count > f->rbytes) { + count = f->rbytes; /* file->rbytes fits in count */ + if (count == 0) { +#ifdef DEBUG + if (debug_level >= 3) { + wav_dbg(f); + dbg_puts(": read complete\n"); + } +#endif + if (!f->tr) + file_eof(&f->pipe.file); + return 0; + } + } + n = pipe_read(file, data, count); + if (n == 0) return 0; - abuf_wcommit(obuf, count); - return 1; + if (f->rbytes >= 0) + f->rbytes -= n; + if (f->map) { + wav_conv(data, n, f->map); + n *= sizeof(short); + } + return n; } -void -rwav_done(struct aproc *p) +/* + * write method of the file structure + */ +unsigned +wav_write(struct file *file, unsigned char *data, unsigned count) { - struct file *f = p->u.io.file; - struct abuf *obuf; + struct wav *f = (struct wav *)file; + unsigned n; - if (f == NULL) - return; - /* - * all buffers must be detached before deleting f->wproc, - * because otherwise it could trigger this code again - */ - obuf = LIST_FIRST(&p->obuflist); - if (obuf) - abuf_eof(obuf); - if (f->wproc) { - f->rproc = NULL; - aproc_del(f->wproc); - } else - file_del(f); - p->u.io.file = NULL; + if (f->wbytes >= 0 && count > f->wbytes) { + count = f->wbytes; /* wbytes fits in count */ + if (count == 0) { +#ifdef DEBUG + if (debug_level >= 3) { + wav_dbg(f); + dbg_puts(": write complete\n"); + } +#endif + file_hup(&f->pipe.file); + return 0; + } + } + n = pipe_write(file, data, count); + if (f->wbytes >= 0) + f->wbytes -= n; + f->endpos += n; + return n; } +/* + * close method of the file structure + */ void -rwav_eof(struct aproc *p, struct abuf *ibuf_dummy) +wav_close(struct file *file) +{ + struct wav *f = (struct wav *)file; + + if (f->mode & MODE_RECMASK) { + pipe_trunc(&f->pipe.file, f->endpos); + if (f->hdr == HDR_WAV) { + wav_writehdr(f->pipe.fd, + &f->hpar, + &f->startpos, + f->endpos - f->startpos); + } + } + pipe_close(file); +} + +/* + * attach play (rec) abuf structure to the device and + * switch to the ``RUN'' state; the play abug must not be empty + */ +int +wav_attach(struct wav *f, int force) { - aproc_del(p); + struct abuf *rbuf = NULL, *wbuf = NULL; + + if (f->mode & MODE_PLAY) + rbuf = LIST_FIRST(&f->pipe.file.rproc->obuflist); + if (f->mode & MODE_RECMASK) + wbuf = LIST_FIRST(&f->pipe.file.wproc->ibuflist); + f->pstate = WAV_RUN; +#ifdef DEBUG + if (debug_level >= 3) { + wav_dbg(f); + dbg_puts(": attaching\n"); + } +#endif + dev_attach(f->pipe.file.name, f->mode, + rbuf, &f->hpar, wbuf, &f->hpar, f->xrun, f->maxweight); + if (f->mode & MODE_PLAY) + dev_setvol(rbuf, MIDI_TO_ADATA(f->vol)); + return 1; } +/* + * allocate the play (rec) abuf structure; if this is a + * file to record, then attach it to the device + * + * XXX: buffer size should be larger than dev_bufsz, because + * in non-server mode we don't prime play buffers with + * silence + */ void -rwav_hup(struct aproc *p, struct abuf *obuf) +wav_allocbuf(struct wav *f) { - aproc_del(p); + struct abuf *buf; + unsigned nfr; + + f->pstate = WAV_START; + if (f->mode & MODE_PLAY) { + nfr = 2 * dev_bufsz * f->hpar.rate / dev_rate; + buf = abuf_new(nfr, &f->hpar); + aproc_setout(f->pipe.file.rproc, buf); + abuf_fill(buf); + if (!ABUF_WOK(buf) || (f->pipe.file.state & FILE_EOF)) + f->pstate = WAV_READY; + } + if (f->mode & MODE_RECMASK) { + nfr = 2 * dev_bufsz * f->hpar.rate / dev_rate; + buf = abuf_new(nfr, &f->hpar); + aproc_setin(f->pipe.file.wproc, buf); + f->pstate = WAV_READY; + } +#ifdef DEBUG + if (debug_level >= 3) { + wav_dbg(f); + dbg_puts(": allocating buffers\n"); + } +#endif + if (f->pstate == WAV_READY && ctl_slotstart(dev_midi, f->slot)) + (void)wav_attach(f, 0); } -struct aproc * -wwav_new(struct file *f) +/* + * free abuf structure and switch to the ``INIT'' state + */ +void +wav_freebuf(struct wav *f) { - struct aproc *p; + struct abuf *rbuf = NULL, *wbuf = NULL; - p = aproc_new(&wwav_ops, f->name); - p->u.io.file = f; - f->wproc = p; - return p; + if (f->mode & MODE_PLAY) + rbuf = LIST_FIRST(&f->pipe.file.rproc->obuflist); + if (f->mode & MODE_RECMASK) + wbuf = LIST_FIRST(&f->pipe.file.wproc->ibuflist); + f->pstate = WAV_INIT; +#ifdef DEBUG + if (debug_level >= 3) { + wav_dbg(f); + dbg_puts(": freeing buffers\n"); + } +#endif + if (rbuf || wbuf) + ctl_slotstop(dev_midi, f->slot); + if (rbuf) + abuf_eof(rbuf); + if (wbuf) + abuf_hup(wbuf); } +/* + * switch to the ``INIT'' state performing + * necessary actions to reach it + */ void -wwav_done(struct aproc *p) +wav_reset(struct wav *f) { - struct file *f = p->u.io.file; - struct abuf *ibuf; + switch (f->pstate) { + case WAV_START: + case WAV_READY: + if (ctl_slotstart(dev_midi, f->slot)) + (void)wav_attach(f, 1); + /* PASSTHROUGH */ + case WAV_RUN: + wav_freebuf(f); + f->pstate = WAV_INIT; + /* PASSTHROUGH */ + case WAV_INIT: + case WAV_FAILED: + /* nothing yet */ + break; + } +} - if (f == NULL) - return; +/* + * terminate the wav reader/writer + */ +void +wav_exit(struct wav *f) +{ + if (f->mode & MODE_PLAY) { + aproc_del(f->pipe.file.rproc); + } else if (f->mode & MODE_RECMASK) { + aproc_del(f->pipe.file.wproc); + } +} + +/* + * seek to f->mmcpos and prepare to start, close + * the file on error. + */ +int +wav_seekmmc(struct wav *f) +{ /* - * all buffers must be detached before deleting f->rproc, - * because otherwise it could trigger this code again + * don't go beyond the end-of-file, if so + * put it in INIT state so it dosn't start */ - ibuf = LIST_FIRST(&p->ibuflist); - if (ibuf) - abuf_hup(ibuf); - if (f->rproc) { - f->wproc = NULL; - aproc_del(f->rproc); - } else - file_del(f); - p->u.io.file = NULL; + if (f->mmcpos > f->endpos) { + wav_reset(f); + f->pstate = WAV_FAILED; + /* + * don't make other stream wait for us + */ + if (f->slot >= 0) + ctl_slotstart(dev_midi, f->slot); + return 0; + } + if (!pipe_seek(&f->pipe.file, f->mmcpos)) { + wav_exit(f); + return 0; + } + if (f->hdr == HDR_WAV) + f->wbytes = WAV_DATAMAX - f->mmcpos; + f->rbytes = f->endpos - f->mmcpos; + wav_reset(f); + wav_allocbuf(f); + return 1; } +/* + * read samples from the file and possibly start it + */ int -wwav_in(struct aproc *p, struct abuf *ibuf) +wav_rdata(struct wav *f) { - struct file *f = p->u.io.file; - unsigned char *data; - unsigned count; + struct aproc *p; + struct abuf *obuf; - if (f->state & FILE_WINUSE) + p = f->pipe.file.rproc; + obuf = LIST_FIRST(&p->obuflist); + if (obuf == NULL) return 0; - if (ABUF_EMPTY(ibuf) || !(f->state & FILE_WOK)) + if (!ABUF_WOK(obuf) || !(f->pipe.file.state & FILE_ROK)) return 0; - data = abuf_rgetblk(ibuf, &count, 0); - count = file_write(f, data, count); - if (count == 0) + if (!rfile_do(p, obuf->len, NULL)) return 0; - abuf_rdiscard(ibuf, count); + switch (f->pstate) { + case WAV_START: + if (!ABUF_WOK(obuf) || (f->pipe.file.state & FILE_EOF)) + f->pstate = WAV_READY; + /* PASSTHROUGH */ + case WAV_READY: + if (ctl_slotstart(dev_midi, f->slot)) + (void)wav_attach(f, 0); + break; +#ifdef DEBUG + case WAV_RUN: + break; + default: + wav_dbg(f); + dbg_puts(": bad state\n"); + dbg_panic(); +#endif + } + if (f->rbytes == 0 && f->tr) { +#ifdef DEBUG + if (debug_level >= 3) { + wav_dbg(f); + dbg_puts(": trying to restart\n"); + } +#endif + if (!wav_seekmmc(f)) + return 0; + } return 1; } int -wwav_out(struct aproc *p, struct abuf *obuf_dummy) +wav_wdata(struct wav *f) { - struct abuf *ibuf = LIST_FIRST(&p->ibuflist); - struct file *f = p->u.io.file; - unsigned char *data; - unsigned count; + struct aproc *p; + struct abuf *ibuf; - if (!abuf_fill(ibuf)) + if (!(f->pipe.file.state & FILE_WOK)) return 0; - if (ABUF_EMPTY(ibuf) || !(f->state & FILE_WOK)) + p = f->pipe.file.wproc; + ibuf = LIST_FIRST(&p->ibuflist); + if (ibuf == NULL) return 0; - data = abuf_rgetblk(ibuf, &count, 0); - if (count == 0) { - /* XXX: this can't happen, right ? */ + if (!ABUF_ROK(ibuf)) return 0; - } - count = file_write(f, data, count); - if (count == 0) + if (!wfile_do(p, ibuf->len, NULL)) return 0; - abuf_rdiscard(ibuf, count); return 1; } +/* + * callback to set the volume, invoked by the MIDI control code + */ +void +wav_setvol(void *arg, unsigned vol) +{ + struct wav *f = (struct wav *)arg; + struct abuf *rbuf; + + f->vol = vol; + if ((f->mode & MODE_PLAY) && f->pstate == WAV_RUN) { + rbuf = LIST_FIRST(&f->pipe.file.rproc->obuflist); + dev_setvol(rbuf, MIDI_TO_ADATA(vol)); + } +} + +/* + * callback to start the stream, invoked by the MIDI control code + */ +void +wav_startreq(void *arg) +{ + struct wav *f = (struct wav *)arg; + + switch (f->pstate) { + case WAV_FAILED: +#ifdef DEBUG + if (debug_level >= 2) { + wav_dbg(f); + dbg_puts(": skipped (failed to seek)\n"); + } +#endif + return; + case WAV_READY: + if (f->mode & MODE_RECMASK) + f->endpos = f->startpos; + (void)wav_attach(f, 0); + break; +#ifdef DEBUG + default: + wav_dbg(f); + dbg_puts(": not in READY state\n"); + dbg_panic(); + break; +#endif + } +} + +/* + * callback to stop the stream, invoked by the MIDI control code + */ void -wwav_eof(struct aproc *p, struct abuf *ibuf) +wav_stopreq(void *arg) { - aproc_del(p); + struct wav *f = (struct wav *)arg; + +#ifdef DEBUG + if (debug_level >= 2) { + wav_dbg(f); + dbg_puts(": stopping"); + if (f->pstate != WAV_FAILED && (f->mode & MODE_RECMASK)) { + dbg_puts(", "); + dbg_putu(f->endpos); + dbg_puts(" bytes recorded"); + } + dbg_puts("\n"); + } +#endif + if (!f->tr) { + wav_exit(f); + return; + } + (void)wav_seekmmc(f); } +/* + * callback to relocate the stream, invoked by the MIDI control code + * on a stopped stream + */ void -wwav_hup(struct aproc *p, struct abuf *obuf_dummy) +wav_locreq(void *arg, unsigned mmc) { - aproc_del(p); + struct wav *f = (struct wav *)arg; + +#ifdef DEBUG + if (f->pstate == WAV_RUN) { + wav_dbg(f); + dbg_puts(": in RUN state\n"); + dbg_panic(); + } +#endif + f->mmcpos = f->startpos + + ((off_t)mmc * f->hpar.rate / MTC_SEC) * aparams_bpf(&f->hpar); + (void)wav_seekmmc(f); } +/* + * create a file reader in the ``INIT'' state + */ struct wav * -wav_new_in(struct fileops *ops, char *name, unsigned hdr, - struct aparams *par, unsigned xrun, unsigned volctl) +wav_new_in(struct fileops *ops, unsigned mode, char *name, unsigned hdr, + struct aparams *par, unsigned xrun, unsigned volctl, int tr) { int fd; struct wav *f; - struct aproc *p; - struct abuf *buf; - unsigned nfr; if (name != NULL) { fd = open(name, O_RDONLY | O_NONBLOCK, 0666); @@ -356,43 +649,59 @@ wav_new_in(struct fileops *ops, char *name, unsigned hdr, if (f == NULL) return NULL; if (hdr == HDR_WAV) { - if (!wav_readhdr(f->pipe.fd, par, &f->rbytes, &f->map)) { + if (!wav_readhdr(f->pipe.fd, par, &f->startpos, &f->rbytes, &f->map)) { file_del((struct file *)f); return NULL; } - f->hpar = *par; + f->endpos = f->startpos + f->rbytes; } else { - f->rbytes = -1; + f->startpos = 0; + f->endpos = pipe_endpos(&f->pipe.file); + if (f->endpos > 0) { + if (!pipe_seek(&f->pipe.file, 0)) { + file_del((struct file *)f); + return NULL; + } + f->rbytes = f->endpos; + } else + f->rbytes = -1; f->map = NULL; } + f->tr = tr; + f->mode = mode; + f->hpar = *par; f->hdr = 0; - nfr = dev_bufsz * par->rate / dev_rate; - buf = abuf_new(nfr, par); - p = rwav_new((struct file *)f); - aproc_setout(p, buf); - abuf_fill(buf); /* XXX: move this in dev_attach() ? */ - dev_attach(name, buf, par, xrun, NULL, NULL, 0, ADATA_UNIT); - dev_setvol(buf, MIDI_TO_ADATA(volctl)); + f->xrun = xrun; + f->maxweight = MIDI_TO_ADATA(volctl); + f->slot = ctl_slotnew(dev_midi, "play", &ctl_wavops, f, 1); + rwav_new((struct file *)f); + wav_allocbuf(f); #ifdef DEBUG if (debug_level >= 2) { dbg_puts(name); dbg_puts(": playing "); + dbg_putu(f->startpos); + dbg_puts(".."); + dbg_putu(f->endpos); + dbg_puts(": playing "); aparams_dbg(par); + if (f->tr) + dbg_puts(", mmc"); dbg_puts("\n"); } #endif return f; } +/* + * create a file writer in the ``INIT'' state + */ struct wav * -wav_new_out(struct fileops *ops, char *name, unsigned hdr, - struct aparams *par, unsigned xrun) +wav_new_out(struct fileops *ops, unsigned mode, char *name, unsigned hdr, + struct aparams *par, unsigned xrun, int tr) { int fd; struct wav *f; - struct aproc *p; - struct abuf *buf; - unsigned nfr; if (name == NULL) { name = "stdout"; @@ -414,20 +723,24 @@ wav_new_out(struct fileops *ops, char *name, unsigned hdr, par->le = 1; par->sig = (par->bits <= 8) ? 0 : 1; par->bps = (par->bits + 7) / 8; - if (!wav_writehdr(f->pipe.fd, par)) { + if (!wav_writehdr(f->pipe.fd, par, &f->startpos, 0)) { file_del((struct file *)f); return NULL; } - f->hpar = *par; f->wbytes = WAV_DATAMAX; - } else + f->endpos = f->startpos; + } else { f->wbytes = -1; + f->startpos = f->endpos = 0; + } + f->tr = tr; + f->mode = mode; + f->hpar = *par; f->hdr = hdr; - nfr = dev_bufsz * par->rate / dev_rate; - p = wwav_new((struct file *)f); - buf = abuf_new(nfr, par); - aproc_setin(p, buf); - dev_attach(name, NULL, NULL, 0, buf, par, xrun, 0); + f->xrun = xrun; + f->slot = ctl_slotnew(dev_midi, "rec", &ctl_wavops, f, 1); + wwav_new((struct file *)f); + wav_allocbuf(f); #ifdef DEBUG if (debug_level >= 2) { dbg_puts(name); @@ -440,86 +753,107 @@ wav_new_out(struct fileops *ops, char *name, unsigned hdr, } void -wav_conv(unsigned char *data, unsigned count, short *map) +rwav_done(struct aproc *p) { - unsigned i; - unsigned char *iptr; - short *optr; + struct wav *f = (struct wav *)p->u.io.file; - iptr = data + count; - optr = (short *)data + count; - for (i = count; i > 0; i--) { - --optr; - --iptr; - *optr = map[*iptr]; + if (f->slot >= 0) + ctl_slotdel(dev_midi, f->slot); + f->slot = -1; + rfile_done(p); +} + +int +rwav_in(struct aproc *p, struct abuf *ibuf_dummy) +{ + struct wav *f = (struct wav *)p->u.io.file; + struct abuf *obuf; + + if (!wav_rdata(f)) + return 0; + obuf = LIST_FIRST(&p->obuflist); + if (obuf && f->pstate >= WAV_RUN) { + if (!abuf_flush(obuf)) + return 0; } + return 1; } -unsigned -wav_read(struct file *file, unsigned char *data, unsigned count) +int +rwav_out(struct aproc *p, struct abuf *obuf) { - struct wav *f = (struct wav *)file; - unsigned n; + struct wav *f = (struct wav *)p->u.io.file; - if (f->map) - count /= sizeof(short); - if (f->rbytes >= 0 && count > f->rbytes) { - count = f->rbytes; /* file->rbytes fits in count */ - if (count == 0) { -#ifdef DEBUG - if (debug_level >= 3) { - file_dbg(&f->pipe.file); - dbg_puts(": read complete\n"); - } -#endif - file_eof(&f->pipe.file); + if (f->pipe.file.state & FILE_RINUSE) + return 0; + for (;;) { + if (!wav_rdata(f)) return 0; - } } - n = pipe_read(file, data, count); - if (n == 0) + return 1; +} + +struct aproc * +rwav_new(struct file *f) +{ + struct aproc *p; + + p = aproc_new(&rwav_ops, f->name); + p->u.io.file = f; + p->u.io.partial = 0;; + f->rproc = p; + return p; +} + +void +wwav_done(struct aproc *p) +{ + struct wav *f = (struct wav *)p->u.io.file; + + if (f->slot >= 0) + ctl_slotdel(dev_midi, f->slot); + f->slot = -1; + wfile_done(p); +} + +int +wwav_in(struct aproc *p, struct abuf *ibuf) +{ + struct wav *f = (struct wav *)p->u.io.file; + + if (f->pipe.file.state & FILE_WINUSE) return 0; - if (f->rbytes >= 0) - f->rbytes -= n; - if (f->map) { - wav_conv(data, n, f->map); - n *= sizeof(short); + for (;;) { + if (!wav_wdata(f)) + return 0; } - return n; + return 1; } -unsigned -wav_write(struct file *file, unsigned char *data, unsigned count) +int +wwav_out(struct aproc *p, struct abuf *obuf_dummy) { - struct wav *f = (struct wav *)file; - unsigned n; + struct abuf *ibuf = LIST_FIRST(&p->ibuflist); + struct wav *f = (struct wav *)p->u.io.file; - if (f->wbytes >= 0 && count > f->wbytes) { - count = f->wbytes; /* wbytes fits in count */ - if (count == 0) { -#ifdef DEBUG - if (debug_level >= 3) { - file_dbg(&f->pipe.file); - dbg_puts(": write complete\n"); - } -#endif - file_hup(&f->pipe.file); + if (ibuf && f->pstate == WAV_RUN) { + if (!abuf_fill(ibuf)) return 0; - } } - n = pipe_write(file, data, count); - if (f->wbytes >= 0) - f->wbytes -= n; - return n; + if (!wav_wdata(f)) + return 0; + return 1; } -void -wav_close(struct file *file) +struct aproc * +wwav_new(struct file *f) { - struct wav *f = (struct wav *)file; + struct aproc *p; - if (f->hdr == HDR_WAV) - wav_writehdr(f->pipe.fd, &f->hpar); - pipe_close(file); + p = aproc_new(&wwav_ops, f->name); + p->u.io.file = f; + p->u.io.partial = 0;; + f->wproc = p; + return p; } diff --git a/usr.bin/aucat/wav.h b/usr.bin/aucat/wav.h index 8e8288cdecd..0c7b8cf8424 100644 --- a/usr.bin/aucat/wav.h +++ b/usr.bin/aucat/wav.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wav.h,v 1.7 2010/04/03 17:59:17 ratchov Exp $ */ +/* $OpenBSD: wav.h,v 1.8 2010/04/06 20:07:01 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -28,23 +28,38 @@ struct wav { #define HDR_RAW 1 /* no headers, ie openbsd native ;-) */ #define HDR_WAV 2 /* microsoft riff wave */ unsigned hdr; /* HDR_RAW or HDR_WAV */ + unsigned xrun; /* xrun policy */ struct aparams hpar; /* parameters to write on the header */ off_t rbytes; /* bytes to read, -1 if no limit */ off_t wbytes; /* bytes to write, -1 if no limit */ + off_t startpos; /* beginning of the data chunk */ + off_t endpos; /* end of the data chunk */ + off_t mmcpos; /* play/rec start point set by MMC */ short *map; /* mulaw/alaw -> s16 conversion table */ + int slot; /* mixer ctl slot number */ + int tr; /* use MMC control */ + unsigned vol; /* current volume */ + unsigned maxweight; /* dynamic range when vol == 127 */ +#define WAV_INIT 0 /* not trying to do anything */ +#define WAV_START 1 /* buffer allocated */ +#define WAV_READY 2 /* buffer filled enough */ +#define WAV_RUN 3 /* buffer attached to device */ +#define WAV_FAILED 4 /* failed to seek */ + unsigned pstate; /* one of above */ + unsigned mode; /* bitmap of MODE_* */ }; extern struct fileops wav_ops; -struct wav *wav_new_in(struct fileops *, char *, unsigned, - struct aparams *, unsigned, unsigned); -struct wav *wav_new_out(struct fileops *, char *, unsigned, - struct aparams *, unsigned); +struct wav *wav_new_in(struct fileops *, unsigned, char *, unsigned, + struct aparams *, unsigned, unsigned, int); +struct wav *wav_new_out(struct fileops *, unsigned, char *, unsigned, + struct aparams *, unsigned, int); unsigned wav_read(struct file *, unsigned char *, unsigned); unsigned wav_write(struct file *, unsigned char *, unsigned); void wav_close(struct file *); -int wav_readhdr(int, struct aparams *, off_t *, short **); -int wav_writehdr(int, struct aparams *); +int wav_readhdr(int, struct aparams *, off_t *, off_t *, short **); +int wav_writehdr(int, struct aparams *, off_t *, off_t); void wav_conv(unsigned char *, unsigned, short *); /* legacy */ |