mirror of https://github.com/ericonr/sndio.git
handle xruns
This commit is contained in:
parent
5136ef4aad
commit
a3a786b16e
|
@ -56,7 +56,7 @@ struct sio_alsa_hdl {
|
||||||
int nfds, infds, onfds;
|
int nfds, infds, onfds;
|
||||||
int running;
|
int running;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
long long writepos, readpos, realpos;
|
long long wpos, rpos, cpos;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -318,6 +318,41 @@ sio_alsa_close(struct sio_hdl *sh)
|
||||||
free(hdl);
|
free(hdl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
void
|
||||||
|
sio_alsa_printpos(struct sio_alsa_hdl *hdl, int delta)
|
||||||
|
{
|
||||||
|
long long rpos, rdiff;
|
||||||
|
long long cpos, cdiff;
|
||||||
|
long long wpos, wdiff;
|
||||||
|
|
||||||
|
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 = hdl->rpos % hdl->par.round;
|
||||||
|
rpos = hdl->rpos / hdl->par.round;
|
||||||
|
if (rdiff > hdl->par.round / 2) {
|
||||||
|
rpos++;
|
||||||
|
rdiff = rdiff - hdl->par.round;
|
||||||
|
}
|
||||||
|
|
||||||
|
wdiff = hdl->wpos % hdl->par.round;
|
||||||
|
wpos = hdl->wpos / hdl->par.round;
|
||||||
|
if (wdiff > hdl->par.round / 2) {
|
||||||
|
wpos++;
|
||||||
|
wdiff = wdiff - hdl->par.round;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr,
|
||||||
|
"clk: %+4lld %+4lld, wr %+4lld %+4lld rd: %+4lld %+4lld\n",
|
||||||
|
cpos, cdiff, wpos, wdiff, rpos, rdiff);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sio_alsa_start(struct sio_hdl *sh)
|
sio_alsa_start(struct sio_hdl *sh)
|
||||||
{
|
{
|
||||||
|
@ -338,7 +373,7 @@ sio_alsa_start(struct sio_hdl *sh)
|
||||||
hdl->idrop = 0;
|
hdl->idrop = 0;
|
||||||
hdl->running = 0;
|
hdl->running = 0;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
hdl->realpos = hdl->readpos = hdl->writepos = 0;
|
hdl->cpos = hdl->rpos = hdl->wpos = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (hdl->sio.mode & SIO_PLAY) {
|
if (hdl->sio.mode & SIO_PLAY) {
|
||||||
|
@ -374,7 +409,7 @@ sio_alsa_start(struct sio_hdl *sh)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (sndio_debug > 1) {
|
if (sndio_debug) {
|
||||||
if (hdl->sio.mode & SIO_REC)
|
if (hdl->sio.mode & SIO_REC)
|
||||||
snd_pcm_dump(hdl->ipcm, output);
|
snd_pcm_dump(hdl->ipcm, output);
|
||||||
if (hdl->sio.mode & SIO_PLAY)
|
if (hdl->sio.mode & SIO_PLAY)
|
||||||
|
@ -418,6 +453,73 @@ sio_alsa_stop(struct sio_hdl *sh)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sio_alsa_xrun(struct sio_alsa_hdl *hdl)
|
||||||
|
{
|
||||||
|
long long _wpos, _rpos, _cpos;
|
||||||
|
int wdiff, rdiff, cdiff;
|
||||||
|
|
||||||
|
fprintf(stderr, "xrun: idelta = %d, odelta = %d\n", hdl->idelta, hdl->odelta);
|
||||||
|
fprintf(stderr, "xrun: stop ");
|
||||||
|
sio_alsa_printpos(hdl, 0);
|
||||||
|
|
||||||
|
cdiff = hdl->par.round - (hdl->cpos % hdl->par.round);
|
||||||
|
if (cdiff == hdl->par.round)
|
||||||
|
cdiff = 0;
|
||||||
|
if (hdl->sio.mode & SIO_PLAY) {
|
||||||
|
hdl->odelta += cdiff;
|
||||||
|
if (hdl->odelta) {
|
||||||
|
fprintf(stderr, "xrun: advancing by %d\n", hdl->odelta);
|
||||||
|
hdl->cpos += hdl->odelta;
|
||||||
|
sio_onmove_cb(&hdl->sio, hdl->odelta);
|
||||||
|
hdl->odelta = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hdl->idelta += cdiff;
|
||||||
|
if (hdl->idelta) {
|
||||||
|
fprintf(stderr, "xrun: advancing by %d\n", hdl->idelta);
|
||||||
|
hdl->cpos += hdl->idelta;
|
||||||
|
sio_onmove_cb(&hdl->sio, hdl->idelta);
|
||||||
|
hdl->idelta = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "xrun: tick ");
|
||||||
|
sio_alsa_printpos(hdl, 0);
|
||||||
|
|
||||||
|
if (hdl->sio.mode & SIO_PLAY) {
|
||||||
|
wdiff = hdl->wpos - hdl->cpos;
|
||||||
|
} else {
|
||||||
|
/* XXX */
|
||||||
|
}
|
||||||
|
|
||||||
|
_wpos = hdl->wpos;
|
||||||
|
_rpos = hdl->rpos;
|
||||||
|
_cpos = hdl->cpos;
|
||||||
|
if (!sio_alsa_stop(&hdl->sio))
|
||||||
|
return 0;
|
||||||
|
if (!sio_alsa_start(&hdl->sio))
|
||||||
|
return 0;
|
||||||
|
hdl->running = 1;
|
||||||
|
|
||||||
|
if (hdl->sio.mode & SIO_PLAY) {
|
||||||
|
fprintf(stderr, "xrun: inserting silence: %d\n", wdiff);
|
||||||
|
hdl->osil = wdiff;
|
||||||
|
if (hdl->sio.mode & SIO_REC) {
|
||||||
|
hdl->idrop = wdiff;
|
||||||
|
hdl->osil += _wpos - _rpos;
|
||||||
|
/* XXX */
|
||||||
|
}
|
||||||
|
hdl->cpos -= hdl->odelta;
|
||||||
|
} else {
|
||||||
|
/* XXX */
|
||||||
|
}
|
||||||
|
fprintf(stderr, "xrun: osil = %d, idrop = %d\n", hdl->osil, hdl->idrop);
|
||||||
|
fprintf(stderr, "xrun: corr ");
|
||||||
|
sio_alsa_printpos(hdl, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sio_alsa_setpar(struct sio_hdl *sh, struct sio_par *par)
|
sio_alsa_setpar(struct sio_hdl *sh, struct sio_par *par)
|
||||||
{
|
{
|
||||||
|
@ -841,6 +943,8 @@ sio_alsa_rdrop(struct sio_alsa_hdl *hdl)
|
||||||
while ((n = snd_pcm_readi(hdl->ipcm, buf, todo)) < 0) {
|
while ((n = snd_pcm_readi(hdl->ipcm, buf, todo)) < 0) {
|
||||||
if (n == -EINTR)
|
if (n == -EINTR)
|
||||||
continue;
|
continue;
|
||||||
|
if (n == -EPIPE && sio_alsa_xrun(hdl))
|
||||||
|
continue;
|
||||||
if (n != -EAGAIN) {
|
if (n != -EAGAIN) {
|
||||||
DALSA("couldn't read data to drop", n);
|
DALSA("couldn't read data to drop", n);
|
||||||
hdl->sio.eof = 1;
|
hdl->sio.eof = 1;
|
||||||
|
@ -853,6 +957,9 @@ sio_alsa_rdrop(struct sio_alsa_hdl *hdl)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
hdl->idrop -= n;
|
hdl->idrop -= n;
|
||||||
|
#ifdef DEBUG
|
||||||
|
hdl->rpos += n;
|
||||||
|
#endif
|
||||||
DPRINTF("sio_alsa_rdrop: dropped %ld/%ld frames\n", n, todo);
|
DPRINTF("sio_alsa_rdrop: dropped %ld/%ld frames\n", n, todo);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -869,8 +976,8 @@ sio_alsa_read(struct sio_hdl *sh, void *buf, size_t len)
|
||||||
while ((n = snd_pcm_readi(hdl->ipcm, buf, todo)) < 0) {
|
while ((n = snd_pcm_readi(hdl->ipcm, buf, todo)) < 0) {
|
||||||
if (n == -EINTR)
|
if (n == -EINTR)
|
||||||
continue;
|
continue;
|
||||||
//if (n == -ESTRPIPE)
|
if (n == -EPIPE && sio_alsa_xrun(hdl))
|
||||||
// continue;
|
continue;
|
||||||
if (n != -EAGAIN) {
|
if (n != -EAGAIN) {
|
||||||
DALSA("couldn't read data", n);
|
DALSA("couldn't read data", n);
|
||||||
hdl->sio.eof = 1;
|
hdl->sio.eof = 1;
|
||||||
|
@ -883,7 +990,7 @@ sio_alsa_read(struct sio_hdl *sh, void *buf, size_t len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
hdl->readpos += n;
|
hdl->rpos += n;
|
||||||
#endif
|
#endif
|
||||||
hdl->idelta += n;
|
hdl->idelta += n;
|
||||||
n *= hdl->ibpf;
|
n *= hdl->ibpf;
|
||||||
|
@ -908,12 +1015,17 @@ sio_alsa_wsil(struct sio_alsa_hdl *hdl)
|
||||||
while ((n = snd_pcm_writei(hdl->opcm, zero, todo)) < 0) {
|
while ((n = snd_pcm_writei(hdl->opcm, zero, todo)) < 0) {
|
||||||
if (n == -EINTR)
|
if (n == -EINTR)
|
||||||
continue;
|
continue;
|
||||||
|
if (n == -ESTRPIPE && sio_alsa_xrun(hdl))
|
||||||
|
continue;
|
||||||
if (n != -EAGAIN) {
|
if (n != -EAGAIN) {
|
||||||
DALSA("couldn't write silence", n);
|
DALSA("couldn't write silence", n);
|
||||||
hdl->sio.eof = 1;
|
hdl->sio.eof = 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
hdl->wpos += n;
|
||||||
|
#endif
|
||||||
hdl->osil -= n;
|
hdl->osil -= n;
|
||||||
DPRINTF("sio_alsa_wsil: inserted %ld/%ld frames\n", n, todo);
|
DPRINTF("sio_alsa_wsil: inserted %ld/%ld frames\n", n, todo);
|
||||||
}
|
}
|
||||||
|
@ -943,6 +1055,8 @@ sio_alsa_write(struct sio_hdl *sh, const void *buf, size_t len)
|
||||||
while ((n = snd_pcm_writei(hdl->opcm, buf, todo)) < 0) {
|
while ((n = snd_pcm_writei(hdl->opcm, buf, todo)) < 0) {
|
||||||
if (n == -EINTR)
|
if (n == -EINTR)
|
||||||
continue;
|
continue;
|
||||||
|
if ((n == -ESTRPIPE || n == -EPIPE) && sio_alsa_xrun(hdl))
|
||||||
|
continue;
|
||||||
if (n != -EAGAIN) {
|
if (n != -EAGAIN) {
|
||||||
DALSA("couldn't write data", n);
|
DALSA("couldn't write data", n);
|
||||||
hdl->sio.eof = 1;
|
hdl->sio.eof = 1;
|
||||||
|
@ -951,7 +1065,7 @@ sio_alsa_write(struct sio_hdl *sh, const void *buf, size_t len)
|
||||||
}
|
}
|
||||||
DPRINTF("wrote %zd\n", n);
|
DPRINTF("wrote %zd\n", n);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
hdl->writepos += n;
|
hdl->wpos += n;
|
||||||
#endif
|
#endif
|
||||||
hdl->odelta += n;
|
hdl->odelta += n;
|
||||||
n *= hdl->obpf;
|
n *= hdl->obpf;
|
||||||
|
@ -974,10 +1088,6 @@ sio_alsa_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
|
||||||
if (hdl->sio.eof)
|
if (hdl->sio.eof)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
DPRINTF("sio_alsa_pollfd: count = %d, nfds = %d\n",
|
|
||||||
snd_pcm_poll_descriptors_count(hdl->opcm),
|
|
||||||
hdl->nfds);
|
|
||||||
|
|
||||||
memset(pfd, 0, sizeof(struct pollfd) * hdl->nfds);
|
memset(pfd, 0, sizeof(struct pollfd) * hdl->nfds);
|
||||||
if ((hdl->sio.mode & SIO_PLAY) && hdl->sio.started) {
|
if ((hdl->sio.mode & SIO_PLAY) && hdl->sio.started) {
|
||||||
if (!hdl->running &&
|
if (!hdl->running &&
|
||||||
|
@ -1020,41 +1130,6 @@ sio_alsa_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
|
||||||
return hdl->onfds + hdl->infds;
|
return hdl->onfds + hdl->infds;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
void
|
|
||||||
sio_alsa_printpos(struct sio_alsa_hdl *hdl, int delta)
|
|
||||||
{
|
|
||||||
long long rpos, rdiff;
|
|
||||||
long long cpos, cdiff;
|
|
||||||
long long wpos, wdiff;
|
|
||||||
|
|
||||||
cdiff = hdl->realpos % hdl->par.round;
|
|
||||||
cpos = hdl->realpos / hdl->par.round;
|
|
||||||
if (cdiff > hdl->par.round / 2) {
|
|
||||||
cpos++;
|
|
||||||
cdiff = cdiff - hdl->par.round;
|
|
||||||
}
|
|
||||||
|
|
||||||
rdiff = hdl->readpos % hdl->par.round;
|
|
||||||
rpos = hdl->readpos / hdl->par.round;
|
|
||||||
if (rdiff > hdl->par.round / 2) {
|
|
||||||
rpos++;
|
|
||||||
rdiff = rdiff - hdl->par.round;
|
|
||||||
}
|
|
||||||
|
|
||||||
wdiff = hdl->writepos % hdl->par.round;
|
|
||||||
wpos = hdl->writepos / hdl->par.round;
|
|
||||||
if (wdiff > hdl->par.round / 2) {
|
|
||||||
wpos++;
|
|
||||||
wdiff = wdiff - hdl->par.round;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr,
|
|
||||||
"clk: %+4lld %+4lld, wr %+4lld %+4lld rd: %+4lld %+4lld\n",
|
|
||||||
cpos, cdiff, wpos, wdiff, rpos, rdiff);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int
|
int
|
||||||
sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
|
sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
|
||||||
{
|
{
|
||||||
|
@ -1080,9 +1155,9 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
|
||||||
if (hdl->sio.mode & SIO_PLAY) {
|
if (hdl->sio.mode & SIO_PLAY) {
|
||||||
ostate = snd_pcm_state(hdl->opcm);
|
ostate = snd_pcm_state(hdl->opcm);
|
||||||
if (ostate == SND_PCM_STATE_XRUN) {
|
if (ostate == SND_PCM_STATE_XRUN) {
|
||||||
fprintf(stderr, "sio_alsa_revents: play xrun\n");
|
if (!sio_alsa_xrun(hdl))
|
||||||
hdl->sio.eof = 1;
|
return POLLHUP;
|
||||||
return POLLHUP;
|
return 0;
|
||||||
}
|
}
|
||||||
err = snd_pcm_poll_descriptors_revents(hdl->opcm, pfd, hdl->onfds, &r);
|
err = snd_pcm_poll_descriptors_revents(hdl->opcm, pfd, hdl->onfds, &r);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
@ -1115,6 +1190,11 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
|
||||||
ostate == SND_PCM_STATE_PREPARED)) {
|
ostate == SND_PCM_STATE_PREPARED)) {
|
||||||
oavail = snd_pcm_avail_update(hdl->opcm);
|
oavail = snd_pcm_avail_update(hdl->opcm);
|
||||||
if (oavail < 0) {
|
if (oavail < 0) {
|
||||||
|
if (oavail == -EPIPE) {
|
||||||
|
if (!sio_alsa_xrun(hdl))
|
||||||
|
return POLLHUP;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
DALSA("couldn't get play buffer pointer", oavail);
|
DALSA("couldn't get play buffer pointer", oavail);
|
||||||
hdl->sio.eof = 1;
|
hdl->sio.eof = 1;
|
||||||
return POLLHUP;
|
return POLLHUP;
|
||||||
|
@ -1124,7 +1204,7 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
|
||||||
hdl->oused = oused;
|
hdl->oused = oused;
|
||||||
while (hdl->odelta > 0) {
|
while (hdl->odelta > 0) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
hdl->realpos += hdl->odelta;
|
hdl->cpos += hdl->odelta;
|
||||||
if (sndio_debug)
|
if (sndio_debug)
|
||||||
sio_alsa_printpos(hdl, hdl->odelta);
|
sio_alsa_printpos(hdl, hdl->odelta);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1137,6 +1217,11 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
|
||||||
istate == SND_PCM_STATE_PREPARED)) {
|
istate == SND_PCM_STATE_PREPARED)) {
|
||||||
iused = snd_pcm_avail_update(hdl->ipcm);
|
iused = snd_pcm_avail_update(hdl->ipcm);
|
||||||
if (iused < 0) {
|
if (iused < 0) {
|
||||||
|
if (iused == -EPIPE) {
|
||||||
|
if (!sio_alsa_xrun(hdl))
|
||||||
|
return POLLHUP;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
DALSA("couldn't get rec buffer pointer", iused);
|
DALSA("couldn't get rec buffer pointer", iused);
|
||||||
hdl->sio.eof = 1;
|
hdl->sio.eof = 1;
|
||||||
return POLLHUP;
|
return POLLHUP;
|
||||||
|
@ -1145,7 +1230,7 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
|
||||||
hdl->iused = iused;
|
hdl->iused = iused;
|
||||||
if (hdl->idelta > 0) {
|
if (hdl->idelta > 0) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
hdl->realpos += hdl->idelta;
|
hdl->cpos += hdl->idelta;
|
||||||
if (sndio_debug)
|
if (sndio_debug)
|
||||||
sio_onmove_cb(&hdl->sio, hdl->idelta);
|
sio_onmove_cb(&hdl->sio, hdl->idelta);
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue