summaryrefslogtreecommitdiff
path: root/lib/libsndio
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2014-03-05 20:40:50 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2014-03-05 20:40:50 +0000
commitb44d0b8f3ec7db2b4ec2a73b639b3b990bdd7c94 (patch)
tree8755deebb0979f62f2f4bffa835577a478cd0413 /lib/libsndio
parenta18588b445edf7039f38a835c60b47c33d0d623c (diff)
- use relative read/write positions to keep track of buffer
usage as seen by the caller - refresh current position before reading/writing data, to keep the current position always consistent with read & write pointers - fix confusion between processed samples by the hardware (as reported by GET{I,O}OFFS) and samples played as reported by GETINFO(). Fixes incorrect delta propagated after xruns in play-only and rec-only modes - don't wait for POLLIN or POLLOUT to be set to refresh ring pointers, as they may not be set in certain cases. Fixes sndiod crash after few days of continuous playback. - save current parameters instead of calling getpar() too often
Diffstat (limited to 'lib/libsndio')
-rw-r--r--lib/libsndio/sio.c81
-rw-r--r--lib/libsndio/sio_aucat.c14
-rw-r--r--lib/libsndio/sio_priv.h10
-rw-r--r--lib/libsndio/sio_sun.c27
4 files changed, 74 insertions, 58 deletions
diff --git a/lib/libsndio/sio.c b/lib/libsndio/sio.c
index 184731e6ee1..74e858755b1 100644
--- a/lib/libsndio/sio.c
+++ b/lib/libsndio/sio.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sio.c,v 1.16 2013/12/20 08:51:28 ratchov Exp $ */
+/* $OpenBSD: sio.c,v 1.17 2014/03/05 20:40:49 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -111,10 +111,12 @@ sio_start(struct sio_hdl *hdl)
hdl->eof = 1;
return 0;
}
-#ifdef DEBUG
+ hdl->cpos = 0;
+ hdl->rused = hdl->wused = 0;
if (!sio_getpar(hdl, &hdl->par))
return 0;
- hdl->pollcnt = hdl->wcnt = hdl->rcnt = hdl->cpos = 0;
+#ifdef DEBUG
+ hdl->pollcnt = 0;
clock_gettime(CLOCK_MONOTONIC, &ts);
hdl->start_nsec = 1000000000LL * ts.tv_sec + ts.tv_nsec;
#endif
@@ -140,8 +142,8 @@ sio_stop(struct sio_hdl *hdl)
if (!hdl->ops->stop(hdl))
return 0;
#ifdef DEBUG
- DPRINTFN(2, "libsndio: polls: %llu, written = %llu, read: %llu\n",
- hdl->pollcnt, hdl->wcnt, hdl->rcnt);
+ DPRINTFN(2, "libsndio: polls: %llu, samples = %llu\n",
+ hdl->pollcnt, hdl->cpos);
#endif
hdl->started = 0;
return 1;
@@ -287,7 +289,7 @@ sio_read(struct sio_hdl *hdl, void *buf, size_t len)
{
unsigned int n;
char *data = buf;
- size_t todo = len;
+ size_t todo = len, maxread;
if (hdl->eof) {
DPRINTF("sio_read: eof\n");
@@ -302,10 +304,13 @@ 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 (!sio_rdrop(hdl))
+ return 0;
+ maxread = hdl->rused;
+ if (maxread > todo)
+ maxread = todo;
+ n = maxread > 0 ? hdl->ops->read(hdl, data, maxread) : 0;
if (n == 0) {
if (hdl->nbio || hdl->eof || todo < len)
break;
@@ -315,9 +320,7 @@ sio_read(struct sio_hdl *hdl, void *buf, size_t len)
}
data += n;
todo -= n;
-#ifdef DEBUG
- hdl->rcnt += n;
-#endif
+ hdl->rused -= n;
}
return len - todo;
}
@@ -327,7 +330,7 @@ sio_write(struct sio_hdl *hdl, const void *buf, size_t len)
{
unsigned int n;
const unsigned char *data = buf;
- size_t todo = len;
+ size_t todo = len, maxwrite;
if (hdl->eof) {
DPRINTF("sio_write: eof\n");
@@ -342,10 +345,14 @@ 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 (!sio_wsil(hdl))
+ return 0;
+ maxwrite = hdl->par.bufsz * hdl->par.pchan * hdl->par.bps -
+ hdl->wused;
+ if (maxwrite > todo)
+ maxwrite = todo;
+ n = maxwrite > 0 ? hdl->ops->write(hdl, data, maxwrite) : 0;
if (n == 0) {
if (hdl->nbio || hdl->eof)
break;
@@ -355,9 +362,7 @@ sio_write(struct sio_hdl *hdl, const void *buf, size_t len)
}
data += n;
todo -= n;
-#ifdef DEBUG
- hdl->wcnt += n;
-#endif
+ hdl->wused += n;
}
return len - todo;
}
@@ -397,7 +402,7 @@ sio_revents(struct sio_hdl *hdl, struct pollfd *pfd)
if (!hdl->started)
return revents & POLLHUP;
#ifdef DEBUG
- if (_sndio_debug >= 3) {
+ if (_sndio_debug >= 4) {
clock_gettime(CLOCK_MONOTONIC, &ts1);
DPRINTF("%09lld: sio_revents: revents = 0x%x, took %lldns\n",
1000000000LL * ts0.tv_sec +
@@ -440,13 +445,18 @@ _sio_printpos(struct sio_hdl *hdl)
long long rpos, rdiff;
long long cpos, cdiff;
long long wpos, wdiff;
+ unsigned rbpf, wbpf, rround, wround;
clock_gettime(CLOCK_MONOTONIC, &ts);
+ rbpf = (hdl->mode & SIO_REC) ? hdl->par.bps * hdl->par.rchan : 1;
+ wbpf = (hdl->mode & SIO_PLAY) ? hdl->par.bps * hdl->par.pchan : 1;
+ rround = hdl->par.round * rbpf;
+ wround = hdl->par.round * wbpf;
rpos = (hdl->mode & SIO_REC) ?
- hdl->rcnt / (hdl->par.bps * hdl->par.rchan) : 0;
+ hdl->cpos * rbpf - hdl->rused : 0;
wpos = (hdl->mode & SIO_PLAY) ?
- hdl->wcnt / (hdl->par.bps * hdl->par.pchan) : 0;
+ hdl->cpos * wbpf + hdl->wused : 0;
cdiff = hdl->cpos % hdl->par.round;
cpos = hdl->cpos / hdl->par.round;
@@ -454,17 +464,17 @@ _sio_printpos(struct sio_hdl *hdl)
cpos++;
cdiff = cdiff - hdl->par.round;
}
- rdiff = rpos % hdl->par.round;
- rpos = rpos / hdl->par.round;
- if (rdiff > hdl->par.round / 2) {
+ rdiff = rpos % rround;
+ rpos = rpos / rround;
+ if (rdiff > rround / 2) {
rpos++;
- rdiff = rdiff - hdl->par.round;
+ rdiff = rdiff - rround;
}
- wdiff = wpos % hdl->par.round;
- wpos = wpos / hdl->par.round;
- if (wdiff > hdl->par.round / 2) {
+ wdiff = wpos % wround;
+ wpos = wpos / wround;
+ if (wdiff > wround / 2) {
wpos++;
- wdiff = wdiff - hdl->par.round;
+ wdiff = wdiff - wround;
}
DPRINTF("%011lld: "
"clk %+5lld%+5lld, wr %+5lld%+5lld rd: %+5lld%+5lld\n",
@@ -476,10 +486,19 @@ _sio_printpos(struct sio_hdl *hdl)
void
_sio_onmove_cb(struct sio_hdl *hdl, int delta)
{
-#ifdef DEBUG
hdl->cpos += delta;
+ if (hdl->mode & SIO_REC)
+ hdl->rused += delta * (hdl->par.bps * hdl->par.rchan);
+ if (hdl->mode & SIO_PLAY)
+ hdl->wused -= delta * (hdl->par.bps * hdl->par.pchan);
+#ifdef DEBUG
if (_sndio_debug >= 3)
_sio_printpos(hdl);
+ if ((hdl->mode & SIO_PLAY) && hdl->wused < 0) {
+ DPRINTFN(1, "sndio: h/w failure: negative buffer usage\n");
+ hdl->eof = 1;
+ return;
+ }
#endif
if (hdl->move_cb)
hdl->move_cb(hdl->move_addr, delta);
diff --git a/lib/libsndio/sio_aucat.c b/lib/libsndio/sio_aucat.c
index e1cdab28f2c..3e0e23d3f69 100644
--- a/lib/libsndio/sio_aucat.c
+++ b/lib/libsndio/sio_aucat.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sio_aucat.c,v 1.16 2013/12/20 08:51:28 ratchov Exp $ */
+/* $OpenBSD: sio_aucat.c,v 1.17 2014/03/05 20:40:49 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -180,17 +180,11 @@ static int
sio_aucat_start(struct sio_hdl *sh)
{
struct sio_aucat_hdl *hdl = (struct sio_aucat_hdl *)sh;
- struct sio_par par;
- /*
- * save bpf
- */
- if (!sio_getpar(&hdl->sio, &par))
- return 0;
- hdl->wbpf = par.bps * par.pchan;
- hdl->rbpf = par.bps * par.rchan;
+ hdl->wbpf = hdl->sio.par.bps * hdl->sio.par.pchan;
+ hdl->rbpf = hdl->sio.par.bps * hdl->sio.par.rchan;
hdl->aucat.maxwrite = 0;
- hdl->round = par.round;
+ hdl->round = hdl->sio.par.round;
hdl->delta = 0;
DPRINTFN(2, "aucat: start, maxwrite = %d\n", hdl->aucat.maxwrite);
diff --git a/lib/libsndio/sio_priv.h b/lib/libsndio/sio_priv.h
index 40ff7089780..b74647fa683 100644
--- a/lib/libsndio/sio_priv.h
+++ b/lib/libsndio/sio_priv.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sio_priv.h,v 1.7 2013/11/13 22:38:22 ratchov Exp $ */
+/* $OpenBSD: sio_priv.h,v 1.8 2014/03/05 20:40:49 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -37,13 +37,13 @@ struct sio_hdl {
int eof; /* true if error occured */
int rdrop; /* recorded bytes to drop */
int wsil; /* silence to play */
+ int rused; /* bytes used in read buffer */
+ int wused; /* bytes used in write buffer */
+ long long cpos; /* clock since start */
+ struct sio_par par;
#ifdef DEBUG
unsigned long long pollcnt; /* times sio_revents was called */
- 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
};
diff --git a/lib/libsndio/sio_sun.c b/lib/libsndio/sio_sun.c
index 023ff4b1b5a..948302eaa96 100644
--- a/lib/libsndio/sio_sun.c
+++ b/lib/libsndio/sio_sun.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sio_sun.c,v 1.10 2013/12/20 08:51:28 ratchov Exp $ */
+/* $OpenBSD: sio_sun.c,v 1.11 2014/03/05 20:40:49 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -436,14 +436,11 @@ sio_sun_close(struct sio_hdl *sh)
static int
sio_sun_start(struct sio_hdl *sh)
{
- struct sio_par par;
struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
struct audio_info aui;
- if (!sio_getpar(&hdl->sio, &par))
- return 0;
- hdl->obpf = par.pchan * par.bps;
- hdl->ibpf = par.rchan * par.bps;
+ hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps;
+ hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps;
hdl->ibytes = 0;
hdl->obytes = 0;
hdl->ierr = 0;
@@ -828,7 +825,7 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
if (!hdl->sio.started)
return pfd->revents;
- if ((revents & POLLOUT) && (hdl->sio.mode & SIO_PLAY)) {
+ if (hdl->sio.mode & SIO_PLAY) {
if (ioctl(hdl->fd, AUDIO_GETOOFFS, &ao) < 0) {
DPERROR("sio_sun_revents: GETOOFFS");
hdl->sio.eof = 1;
@@ -840,7 +837,7 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
if (!(hdl->sio.mode & SIO_REC))
hdl->idelta += delta;
}
- if ((revents & POLLIN) && (hdl->sio.mode & SIO_REC)) {
+ if (hdl->sio.mode & SIO_REC) {
if (ioctl(hdl->fd, AUDIO_GETIOFFS, &ao) < 0) {
DPERROR("sio_sun_revents: GETIOFFS");
hdl->sio.eof = 1;
@@ -878,16 +875,22 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
if (dierr > 0)
DPRINTFN(2, "rec xrun %d\n", dierr);
}
+
+ /*
+ * GET{I,O}OFFS report positions including xruns,
+ * so we have to substract to get the real position
+ */
+ hdl->idelta -= dierr;
+ hdl->odelta -= doerr;
+
offset = doerr - dierr;
if (offset > 0) {
hdl->sio.rdrop += offset * hdl->ibpf;
- hdl->idelta -= doerr;
- hdl->odelta -= doerr;
+ hdl->idelta -= offset;
DPRINTFN(2, "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;
+ hdl->odelta -= offset;
DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr);
}