diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2013-08-24 12:32:36 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2013-08-24 12:32:36 +0000 |
commit | 1cfd14998d914c90a36c5e3456a9bd754bf60a2e (patch) | |
tree | d944b397e8b14651ee13f3071d80dc54c582b41b /lib/libsndio | |
parent | 8cacaa9a558698840b06432b2090a55a1cad33e2 (diff) |
Move underrun/overrun recovery code from the sun-api back-end to
the the generic code, so it can be used by other back-ends as well.
No behavior change (hopefully).
Diffstat (limited to 'lib/libsndio')
-rw-r--r-- | lib/libsndio/sio.c | 160 | ||||
-rw-r--r-- | lib/libsndio/sio_priv.h | 15 | ||||
-rw-r--r-- | lib/libsndio/sio_sun.c | 107 |
3 files changed, 138 insertions, 144 deletions
diff --git a/lib/libsndio/sio.c b/lib/libsndio/sio.c index a3ebdc102e2..d8ccf0c666c 100644 --- a/lib/libsndio/sio.c +++ b/lib/libsndio/sio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sio.c,v 1.13 2013/04/03 03:13:32 guenther Exp $ */ +/* $OpenBSD: sio.c,v 1.14 2013/08/24 12:32:34 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -98,6 +98,10 @@ sio_close(struct sio_hdl *hdl) int sio_start(struct sio_hdl *hdl) { +#ifdef DEBUG + struct timespec ts; +#endif + if (hdl->eof) { DPRINTF("sio_start: eof\n"); return 0; @@ -110,9 +114,11 @@ sio_start(struct sio_hdl *hdl) #ifdef DEBUG if (!sio_getpar(hdl, &hdl->par)) return 0; - hdl->pollcnt = hdl->wcnt = hdl->rcnt = hdl->realpos = 0; - clock_gettime(CLOCK_MONOTONIC, &hdl->ts); + hdl->pollcnt = hdl->wcnt = hdl->rcnt = hdl->cpos = 0; + clock_gettime(CLOCK_MONOTONIC, &ts); + hdl->start_nsec = 1000000000LL * ts.tv_sec + ts.tv_nsec; #endif + hdl->rdrop = hdl->wsil = 0; if (!hdl->ops->start(hdl)) return 0; hdl->started = 1; @@ -236,6 +242,46 @@ sio_psleep(struct sio_hdl *hdl, int event) return 1; } +static int +sio_rdrop(struct sio_hdl *hdl) +{ +#define DROP_NMAX 0x1000 + static char dummy[DROP_NMAX]; + ssize_t n, todo; + + while (hdl->rdrop > 0) { + todo = hdl->rdrop; + if (todo > DROP_NMAX) + todo = DROP_NMAX; + n = hdl->ops->read(hdl, dummy, todo); + if (n == 0) + return 0; + hdl->rdrop -= n; + DPRINTF("sio_rdrop: dropped %zu bytes\n", n); + } + return 1; +} + +static int +sio_wsil(struct sio_hdl *hdl) +{ +#define ZERO_NMAX 0x1000 + static char zero[ZERO_NMAX]; + ssize_t n, todo; + + while (hdl->wsil > 0) { + todo = hdl->wsil; + if (todo > ZERO_NMAX) + todo = ZERO_NMAX; + n = hdl->ops->write(hdl, zero, todo); + if (n == 0) + return 0; + hdl->wsil -= n; + DPRINTF("sio_wsil: inserted %zu bytes\n", n); + } + return 1; +} + size_t sio_read(struct sio_hdl *hdl, void *buf, size_t len) { @@ -256,6 +302,8 @@ sio_read(struct sio_hdl *hdl, void *buf, size_t len) DPRINTF("sio_read: zero length read ignored\n"); return 0; } + if (!sio_rdrop(hdl)) + return 0; while (todo > 0) { n = hdl->ops->read(hdl, data, todo); if (n == 0) { @@ -280,13 +328,6 @@ sio_write(struct sio_hdl *hdl, const void *buf, size_t len) unsigned int n; const unsigned char *data = buf; size_t todo = len; -#ifdef DEBUG - struct timespec ts0, ts1, dts; - unsigned int us; - - if (sndio_debug >= 2) - clock_gettime(CLOCK_MONOTONIC, &ts0); -#endif if (hdl->eof) { DPRINTF("sio_write: eof\n"); @@ -301,6 +342,8 @@ sio_write(struct sio_hdl *hdl, const void *buf, size_t len) DPRINTF("sio_write: zero length write ignored\n"); return 0; } + if (!sio_wsil(hdl)) + return 0; while (todo > 0) { n = hdl->ops->write(hdl, data, todo); if (n == 0) { @@ -316,19 +359,6 @@ sio_write(struct sio_hdl *hdl, const void *buf, size_t len) hdl->wcnt += n; #endif } -#ifdef DEBUG - if (sndio_debug >= 2) { - clock_gettime(CLOCK_MONOTONIC, &ts1); - timespecsub(&ts0, &hdl->ts, &dts); - DPRINTF("%lld.%09ld: ", (long long)dts.tv_sec, dts.tv_nsec); - - timespecsub(&ts1, &ts0, &dts); - us = dts.tv_sec * 1000000 + dts.tv_nsec/1000; - DPRINTF( - "sio_write: wrote %d bytes of %d in %uus\n", - (int)(len - todo), (int)len, us); - } -#endif return len - todo; } @@ -353,8 +383,7 @@ sio_revents(struct sio_hdl *hdl, struct pollfd *pfd) { int revents; #ifdef DEBUG - struct timespec ts0, ts1, dts; - unsigned int us; + struct timespec ts0, ts1; if (sndio_debug >= 2) clock_gettime(CLOCK_MONOTONIC, &ts0); @@ -368,17 +397,20 @@ sio_revents(struct sio_hdl *hdl, struct pollfd *pfd) if (!hdl->started) return revents & POLLHUP; #ifdef DEBUG - if (sndio_debug >= 2) { + if (sndio_debug >= 3) { clock_gettime(CLOCK_MONOTONIC, &ts1); - timespecsub(&ts0, &hdl->ts, &dts); - DPRINTF("%lld.%09ld: ", (long long)dts.tv_sec, dts.tv_nsec); - - timespecsub(&ts1, &ts0, &dts); - us = dts.tv_sec * 1000000 + dts.tv_nsec/1000; - DPRINTF("sio_revents: revents = 0x%x, complete in %uus\n", - revents, us); + DPRINTF("%09lld: sio_revents: revents = 0x%x, took %lldns\n", + 1000000000LL * ts0.tv_sec + + ts0.tv_nsec - hdl->start_nsec, + revents, + 1000000000LL * (ts1.tv_sec - ts0.tv_sec) + + ts1.tv_nsec - ts0.tv_nsec); } #endif + if ((hdl->mode & SIO_PLAY) && !sio_wsil(hdl)) + revents &= ~POLLOUT; + if ((hdl->mode & SIO_REC) && !sio_rdrop(hdl)) + revents &= ~POLLIN; return revents; } @@ -400,28 +432,54 @@ sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr) hdl->move_addr = addr; } +#ifdef DEBUG +void +sio_printpos(struct sio_hdl *hdl) +{ + struct timespec ts; + long long rpos, rdiff; + long long cpos, cdiff; + long long wpos, wdiff; + + clock_gettime(CLOCK_MONOTONIC, &ts); + + rpos = (hdl->mode & SIO_REC) ? + hdl->rcnt / (hdl->par.bps * hdl->par.rchan) : 0; + wpos = (hdl->mode & SIO_PLAY) ? + hdl->wcnt / (hdl->par.bps * hdl->par.pchan) : 0; + + cdiff = hdl->cpos % hdl->par.round; + cpos = hdl->cpos / hdl->par.round; + if (cdiff > hdl->par.round / 2) { + cpos++; + cdiff = cdiff - hdl->par.round; + } + rdiff = rpos % hdl->par.round; + rpos = rpos / hdl->par.round; + if (rdiff > hdl->par.round / 2) { + rpos++; + rdiff = rdiff - hdl->par.round; + } + wdiff = wpos % hdl->par.round; + wpos = wpos / hdl->par.round; + if (wdiff > hdl->par.round / 2) { + wpos++; + wdiff = wdiff - hdl->par.round; + } + DPRINTF("%011lld: " + "clk %+5lld%+5lld, wr %+5lld%+5lld rd: %+5lld%+5lld\n", + 1000000000LL * ts.tv_sec + ts.tv_nsec - hdl->start_nsec, + cpos, cdiff, wpos, wdiff, rpos, rdiff); +} +#endif + void sio_onmove_cb(struct sio_hdl *hdl, int delta) { #ifdef DEBUG - struct timespec ts0, dts; - long long playpos; - - if (sndio_debug >= 3 && (hdl->mode & SIO_PLAY)) { - clock_gettime(CLOCK_MONOTONIC, &ts0); - timespecsub(&ts0, &hdl->ts, &dts); - DPRINTF("%lld.%09ld: ", (long long)dts.tv_sec, dts.tv_nsec); - hdl->realpos += delta; - playpos = hdl->wcnt / (hdl->par.bps * hdl->par.pchan); - DPRINTF("sio_onmove_cb: delta = %+7d, " - "plat = %+7lld, " - "realpos = %+7lld, " - "bufused = %+7lld\n", - delta, - playpos - hdl->realpos, - hdl->realpos, - hdl->realpos < 0 ? playpos : playpos - hdl->realpos); - } + hdl->cpos += delta; + if (sndio_debug >= 2) + sio_printpos(hdl); #endif if (hdl->move_cb) hdl->move_cb(hdl->move_addr, delta); diff --git a/lib/libsndio/sio_priv.h b/lib/libsndio/sio_priv.h index f95885d2aa9..b8792ffcc46 100644 --- a/lib/libsndio/sio_priv.h +++ b/lib/libsndio/sio_priv.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sio_priv.h,v 1.5 2013/04/03 03:13:32 guenther Exp $ */ +/* $OpenBSD: sio_priv.h,v 1.6 2013/08/24 12:32:35 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -35,12 +35,14 @@ struct sio_hdl { int started; /* true if started */ int nbio; /* true if non-blocking io */ int eof; /* true if error occured */ + int rdrop; /* recorded bytes to drop */ + int wsil; /* silence to play */ #ifdef DEBUG unsigned long long pollcnt; /* times sio_revents was called */ - unsigned long long wcnt; /* bytes written with sio_write() */ - unsigned long long rcnt; /* bytes read with sio_read() */ - long long realpos; - struct timespec ts; + long long wcnt; /* bytes written with sio_write() */ + long long rcnt; /* bytes read with sio_read() */ + long long cpos; + long long start_nsec; struct sio_par par; #endif }; @@ -70,5 +72,8 @@ void sio_create(struct sio_hdl *, struct sio_ops *, unsigned, int); void sio_destroy(struct sio_hdl *); void sio_onmove_cb(struct sio_hdl *, int); void sio_onvol_cb(struct sio_hdl *, unsigned); +#ifdef DEBUG +void sio_printpos(struct sio_hdl *); +#endif #endif /* !defined(SNDIO_PRIV_H) */ diff --git a/lib/libsndio/sio_sun.c b/lib/libsndio/sio_sun.c index 880017566be..779e864f9ce 100644 --- a/lib/libsndio/sio_sun.c +++ b/lib/libsndio/sio_sun.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sio_sun.c,v 1.7 2012/09/14 22:50:26 ratchov Exp $ */ +/* $OpenBSD: sio_sun.c,v 1.8 2013/08/24 12:32:35 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -50,9 +50,7 @@ struct sio_sun_hdl { unsigned int ibpf, obpf; /* bytes per frame */ unsigned int ibytes, obytes; /* bytes the hw transferred */ unsigned int ierr, oerr; /* frames the hw dropped */ - int offset; /* frames play is ahead of record */ int idelta, odelta; /* position reported to client */ - int mix_fd, mix_index; /* /dev/mixerN stuff */ }; static void sio_sun_close(struct sio_hdl *); @@ -450,7 +448,6 @@ sio_sun_start(struct sio_hdl *sh) hdl->obytes = 0; hdl->ierr = 0; hdl->oerr = 0; - hdl->offset = 0; hdl->idelta = 0; hdl->odelta = 0; @@ -726,48 +723,12 @@ sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par) return 1; } -/* - * drop recorded samples to compensate xruns - */ -static int -sio_sun_rdrop(struct sio_sun_hdl *hdl) -{ -#define DROP_NMAX 0x1000 - static char dropbuf[DROP_NMAX]; - ssize_t n, todo; - - while (hdl->offset > 0) { - todo = hdl->offset * hdl->ibpf; - if (todo > DROP_NMAX) - todo = DROP_NMAX - DROP_NMAX % hdl->ibpf; - while ((n = read(hdl->fd, dropbuf, todo)) < 0) { - if (errno == EINTR) - continue; - if (errno != EAGAIN) { - DPERROR("sio_sun_rdrop: read"); - hdl->sio.eof = 1; - } - return 0; - } - if (n == 0) { - DPRINTF("sio_sun_rdrop: eof\n"); - hdl->sio.eof = 1; - return 0; - } - hdl->offset -= (int)n / (int)hdl->ibpf; - DPRINTF("sio_sun_rdrop: dropped %ld/%ld bytes\n", n, todo); - } - return 1; -} - static size_t sio_sun_read(struct sio_hdl *sh, void *buf, size_t len) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; ssize_t n; - if (!sio_sun_rdrop(hdl)) - return 0; while ((n = read(hdl->fd, buf, len)) < 0) { if (errno == EINTR) continue; @@ -817,37 +778,6 @@ sio_sun_autostart(struct sio_sun_hdl *hdl) return 1; } -/* - * insert silence to play to compensate xruns - */ -static int -sio_sun_wsil(struct sio_sun_hdl *hdl) -{ -#define ZERO_NMAX 0x1000 - static char zero[ZERO_NMAX]; - ssize_t n, todo; - - while (hdl->offset < 0) { - todo = (int)-hdl->offset * (int)hdl->obpf; - if (todo > ZERO_NMAX) - todo = ZERO_NMAX - ZERO_NMAX % hdl->obpf; - while ((n = write(hdl->fd, zero, todo)) < 0) { - if (errno == EINTR) - continue; - if (errno != EAGAIN) { - DPERROR("sio_sun_wsil: write"); - hdl->sio.eof = 1; - return 0; - } - return 0; - } - hdl->offset += (int)n / (int)hdl->obpf; - DPRINTF("sio_sun_wsil: inserted %ld/%ld bytes\n", n, todo); - } - return 1; -} - - static size_t sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len) { @@ -855,8 +785,6 @@ sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len) const unsigned char *data = buf; ssize_t n, todo; - if (!sio_sun_wsil(hdl)) - return 0; todo = len; while ((n = write(hdl->fd, data, todo)) < 0) { if (errno == EINTR) @@ -895,7 +823,7 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd) { struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh; struct audio_offset ao; - int xrun, dmove, dierr = 0, doerr = 0, delta; + int xrun, dierr = 0, doerr = 0, offset, delta; int revents = pfd->revents; if (!hdl->sio.started) @@ -934,6 +862,8 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd) hdl->oerr = xrun; if (!(hdl->sio.mode & SIO_REC)) dierr = doerr; + if (doerr > 0) + DPRINTF("play xrun %d\n", doerr); } if (hdl->sio.mode & SIO_REC) { if (ioctl(hdl->fd, AUDIO_RERROR, &xrun) < 0) { @@ -945,11 +875,21 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd) hdl->ierr = xrun; if (!(hdl->sio.mode & SIO_PLAY)) doerr = dierr; + if (dierr > 0) + DPRINTF("rec xrun %d\n", dierr); + } + offset = doerr - dierr; + if (offset > 0) { + hdl->sio.rdrop += offset * hdl->ibpf; + hdl->idelta -= doerr; + hdl->odelta -= doerr; + DPRINTF("will drop %d and pause %d\n", offset, doerr); + } else if (offset < 0) { + hdl->sio.wsil += -offset * hdl->obpf; + hdl->idelta -= dierr; + hdl->odelta -= dierr; + DPRINTF("will insert %d and pause %d\n", -offset, dierr); } - hdl->offset += doerr - dierr; - dmove = dierr > doerr ? dierr : doerr; - hdl->idelta -= dmove; - hdl->odelta -= dmove; delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta; if (delta > 0) { @@ -958,16 +898,7 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd) hdl->odelta -= delta; } - /* - * drop recorded samples or insert silence to play - * right now to adjust revents, and avoid busy loops - * programs - */ if (hdl->filling) - revents |= POLLOUT; - if ((hdl->sio.mode & SIO_PLAY) && !sio_sun_wsil(hdl)) - revents &= ~POLLOUT; - if ((hdl->sio.mode & SIO_REC) && !sio_sun_rdrop(hdl)) - revents &= ~POLLIN; + revents |= POLLOUT; /* XXX: is this necessary ? */ return revents; } |