ref: 69aa432c2d828aaedfa1b3d62a118e29ec469943
parent: 4976f1410cc69ae475fdaedc574a404ed606dafd
author: cbagwell <cbagwell>
date: Sat Sep 17 16:52:34 EDT 2005
Add underrun recovery to alsa driver.
--- a/src/alsa.c
+++ b/src/alsa.c
@@ -64,7 +64,7 @@
if ((err = snd_pcm_open(&(alsa->pcm_handle), ft->filename,
mode, 0)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot open audio device\n");
+ st_fail_errno(ft, ST_EPERM, "cannot open audio device");
return ST_EOF;
}
@@ -71,7 +71,7 @@
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
st_fail_errno(ft, ST_ENOMEM,
- "cannot allocate hardware parameter structure\n");
+ "cannot allocate hardware parameter structure");
return ST_EOF;
}
@@ -78,7 +78,7 @@
if ((err = snd_pcm_hw_params_any(alsa->pcm_handle, hw_params)) < 0)
{
st_fail_errno(ft, ST_EPERM,
- "cannot initialize hardware parameter structure\n");
+ "cannot initialize hardware parameter structure");
return ST_EOF;
}
@@ -86,7 +86,7 @@
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
st_fail_errno(ft, ST_EPERM,
- "cannot set access type\n");
+ "cannot set access type");
return ST_EOF;
}
@@ -111,7 +111,7 @@
if ((err = snd_pcm_hw_params_set_format(alsa->pcm_handle,
hw_params, fmt)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot set sample format\n");
+ st_fail_errno(ft, ST_EPERM, "cannot set sample format");
return ST_EOF;
}
@@ -126,7 +126,7 @@
rate = max_rate;
if (rate != ft->info.rate)
{
- st_warn("alsa: Hardware does not support %d. Forcing sample rate to %d.\n", ft->info.rate, rate);
+ st_warn("alsa: Hardware does not support %d. Forcing sample rate to %d.", ft->info.rate, rate);
}
dir = 0;
@@ -135,12 +135,12 @@
&rate,
&dir)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot set sample rate\n");
+ st_fail_errno(ft, ST_EPERM, "cannot set sample rate");
return ST_EOF;
}
if (rate != ft->info.rate)
{
- st_warn("Could not set exact rate of %d. Approximating with %d\n",
+ st_warn("Could not set exact rate of %d. Approximating with %d",
ft->info.rate, rate);
}
@@ -150,15 +150,14 @@
hw_params,
ft->info.channels)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot set channel count\n");
+ st_fail_errno(ft, ST_EPERM, "cannot set channel count");
return ST_EOF;
}
-#if 0
/* Set number of periods. Periods used to be called fragments. */
if (snd_pcm_hw_params_set_periods(alsa->pcm_handle, hw_params, 2, 0) < 0)
{
- st_fail_errno(ft, ST_EPERM, "Error setting periods.\n");
+ st_fail_errno(ft, ST_EPERM, "Error setting periods.");
return ST_EOF;
}
@@ -165,14 +164,13 @@
/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
if (snd_pcm_hw_params_set_buffer_size(alsa->pcm_handle, hw_params, (ST_BUFSIZ * 8)>>2) < 0) {
- st_fail_errno(ft, ST_EPERM, "Error setting buffersize.\n");
+ st_fail_errno(ft, ST_EPERM, "Error setting buffersize.");
return ST_EOF;
}
-#endif
if ((err = snd_pcm_hw_params(alsa->pcm_handle, hw_params)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot set parameters\n");
+ st_fail_errno(ft, ST_EPERM, "cannot set parameters");
return ST_EOF;
}
@@ -180,7 +178,7 @@
if ((err = snd_pcm_prepare(alsa->pcm_handle)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot prepare audio interface for use\n");
+ st_fail_errno(ft, ST_EPERM, "cannot prepare audio interface for use");
return ST_EOF;
}
@@ -190,6 +188,37 @@
}
/*
+ * Underrun and suspend recovery
+ */
+static int xrun_recovery(snd_pcm_t *handle, int err)
+{
+ if (err == -EPIPE)
+ { /* under-run */
+ err = snd_pcm_prepare(handle);
+ if (err < 0)
+ st_warn("Can't recovery from overrun, prepare failed: %s", snd_strerror(err));
+ return 0;
+ }
+ else
+ {
+ if (err == -ESTRPIPE)
+ {
+ /* wait until the suspend flag is released */
+ while ((err = snd_pcm_resume(handle)) == -EAGAIN)
+ sleep(1);
+ if (err < 0)
+ {
+ err = snd_pcm_prepare(handle);
+ if (err < 0)
+ st_warn("Can't recovery from suspend, prepare failed: %s", snd_strerror(err));
+ }
+ }
+ return 0;
+ }
+ return err;
+}
+
+/*
* Do anything required before you start reading samples.
* Read file header.
* Find out sampling rate,
@@ -204,6 +233,7 @@
st_ssize_t st_alsaread(ft_t ft, st_sample_t *buf, st_ssize_t nsamp)
{
st_ssize_t len;
+ int err;
alsa_priv_t alsa = (alsa_priv_t)ft->priv;
void (*read_buf)(st_sample_t *, char *, st_ssize_t, char) = 0;
@@ -251,11 +281,30 @@
/* Prevent overflow */
if (nsamp > alsa->buf_size/ft->info.size)
nsamp = (alsa->buf_size/ft->info.size);
+ len = 0;
- len = snd_pcm_readi(alsa->pcm_handle, alsa->buf, nsamp);
+ while (len < nsamp)
+ {
+ /* ALSA library takes "frame" counts. */
+ err = snd_pcm_readi(alsa->pcm_handle, alsa->buf,
+ (nsamp-len)/ft->info.channels);
+ if (err == -EAGAIN)
+ continue;
+ if (err < 0)
+ {
+ if (xrun_recovery(alsa->pcm_handle, err) < 0)
+ {
+ st_fail_errno(ft, ST_EPERM, "ALSA write error");
+ return ST_EOF;
+ }
+ }
+ else
+ {
+ read_buf(buf+len, alsa->buf, err, ft->swap);
+ len += err * ft->info.channels;
+ }
+ }
- read_buf(buf, alsa->buf, len, ft->swap);
-
return len;
}
@@ -299,7 +348,7 @@
if ((err = snd_pcm_open(&(alsa->pcm_handle), ft->filename,
SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot open audio device\n");
+ st_fail_errno(ft, ST_EPERM, "cannot open audio device");
return ST_EOF;
}
@@ -306,7 +355,7 @@
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
{
st_fail_errno(ft, ST_ENOMEM,
- "cannot allocate hardware parameter structure\n");
+ "cannot allocate hardware parameter structure");
return ST_EOF;
}
@@ -313,7 +362,7 @@
if ((err = snd_pcm_hw_params_any(alsa->pcm_handle, hw_params)) < 0)
{
st_fail_errno(ft, ST_EPERM,
- "cannot initialize hardware parameter structure\n");
+ "cannot initialize hardware parameter structure");
return ST_EOF;
}
@@ -321,7 +370,7 @@
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
st_fail_errno(ft, ST_EPERM,
- "cannot set access type\n");
+ "cannot set access type");
return ST_EOF;
}
@@ -346,7 +395,7 @@
if ((err = snd_pcm_hw_params_set_format(alsa->pcm_handle,
hw_params, fmt)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot set sample format\n");
+ st_fail_errno(ft, ST_EPERM, "cannot set sample format");
return ST_EOF;
}
@@ -361,7 +410,7 @@
rate = max_rate;
if (rate != ft->info.rate)
{
- st_warn("alsa: Hardware does not support %d. Forcing sample rate to %d.\n", ft->info.rate, rate);
+ st_warn("alsa: Hardware does not support %d. Forcing sample rate to %d.", ft->info.rate, rate);
}
dir = 0;
@@ -370,12 +419,12 @@
&rate,
&dir)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot set sample rate\n");
+ st_fail_errno(ft, ST_EPERM, "cannot set sample rate");
return ST_EOF;
}
if (rate != ft->info.rate)
{
- st_warn("Could not set exact rate of %d. Approximating with %d\n",
+ st_warn("Could not set exact rate of %d. Approximating with %d",
ft->info.rate, rate);
}
@@ -385,7 +434,7 @@
hw_params,
ft->info.channels)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot set channel count\n");
+ st_fail_errno(ft, ST_EPERM, "cannot set channel count");
return ST_EOF;
}
@@ -393,7 +442,7 @@
/* Set number of periods. Periods used to be called fragments. */
if (snd_pcm_hw_params_set_periods(alsa->pcm_handle, hw_params, 2, 0) < 0)
{
- st_fail_errno(ft, ST_EPERM, "Error setting periods.\n");
+ st_fail_errno(ft, ST_EPERM, "Error setting periods.");
return ST_EOF;
}
@@ -400,7 +449,7 @@
/* Set buffer size (in frames). The resulting latency is given by */
/* latency = periodsize * periods / (rate * bytes_per_frame) */
if (snd_pcm_hw_params_set_buffer_size(alsa->pcm_handle, hw_params, (ST_BUFSIZ * 2)>>2) < 0) {
- st_fail_errno(ft, ST_EPERM, "Error setting buffersize.\n");
+ st_fail_errno(ft, ST_EPERM, "Error setting buffersize.");
return ST_EOF;
}
#endif
@@ -407,7 +456,7 @@
if ((err = snd_pcm_hw_params(alsa->pcm_handle, hw_params)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot set parameters\n");
+ st_fail_errno(ft, ST_EPERM, "cannot set parameters");
return ST_EOF;
}
@@ -415,7 +464,7 @@
if ((err = snd_pcm_prepare(alsa->pcm_handle)) < 0)
{
- st_fail_errno(ft, ST_EPERM, "cannot prepare audio interface for use\n");
+ st_fail_errno(ft, ST_EPERM, "cannot prepare audio interface for use");
return ST_EOF;
}
@@ -427,6 +476,7 @@
st_ssize_t st_alsawrite(ft_t ft, st_sample_t *buf, st_ssize_t nsamp)
{
st_ssize_t len;
+ int err;
alsa_priv_t alsa = (alsa_priv_t)ft->priv;
void (*write_buf)(char *, st_sample_t *, st_ssize_t, char) = 0;
@@ -473,12 +523,26 @@
/* Prevent overflow */
if (nsamp > alsa->buf_size/ft->info.size)
nsamp = (alsa->buf_size/ft->info.size);
+ len = 0;
write_buf(alsa->buf, buf, nsamp, ft->swap);
- if ((len = snd_pcm_writei(alsa->pcm_handle, alsa->buf, nsamp)) != nsamp) {
- snd_pcm_prepare(alsa->pcm_handle);
- fprintf(stderr, "Report me! Buffer underrun thats not dealt with\n");
+ while (len < nsamp)
+ {
+ err = snd_pcm_writei(alsa->pcm_handle, alsa->buf,
+ (nsamp-len)/ft->info.channels);
+ if (err == -EAGAIN)
+ continue;
+ if (err < 0)
+ {
+ if (xrun_recovery(alsa->pcm_handle, err) < 0)
+ {
+ st_fail_errno(ft, ST_EPERM, "ALSA write error\n");
+ return ST_EOF;
+ }
+ }
+ else
+ len += err * ft->info.channels;
}
return len;
@@ -516,7 +580,7 @@
if (ft->info.size != ST_SIZE_WORD &&
ft->info.size != ST_SIZE_BYTE)
{
- st_warn("ALSA driver only supports byte and word samples. Changing to word.\n");
+ st_warn("ALSA driver only supports byte and word samples. Changing to word.");
ft->info.size = ST_SIZE_WORD;
}
@@ -525,12 +589,12 @@
{
if (ft->info.size == ST_SIZE_WORD)
{
- st_warn("ALSA driver only supports signed and unsigned samples. Changing to signed.\n");
+ st_warn("ALSA driver only supports signed and unsigned samples. Changing to signed.");
ft->info.encoding = ST_ENCODING_SIGN2;
}
else
{
- st_warn("ALSA driver only supports signed and unsigned samples. Changing to unsigned.\n");
+ st_warn("ALSA driver only supports signed and unsigned samples. Changing to unsigned.");
ft->info.encoding = ST_ENCODING_UNSIGNED;
}
}
--- a/src/sox.c
+++ b/src/sox.c
@@ -553,6 +553,11 @@
ilen = (*file_desc[current_input]->h->read)(file_desc[current_input],
efftab[0].obuf,
(st_ssize_t)ST_BUFSIZ);
+ if (ilen > ST_BUFSIZ)
+ {
+ st_warn("WARNING: Corrupt value of %d! Assuming 0 bytes read.\n", ilen);
+ ilen = 0;
+ }
/* FIXME: libst needs the feof() and ferror() concepts
* to see if ST_EOF means a real failure. Until then we
@@ -562,6 +567,7 @@
efftab[0].olen = 0;
else
efftab[0].olen = ilen;
+ efftab[0].odone = 0;
read_samples += (efftab[0].olen / file_desc[0]->info.channels);
@@ -993,7 +999,7 @@
do {
/* run entire chain BACKWARDS: pull, don't push.*/
/* this is because buffering system isn't a nice queueing system */
- for(e = neffects - 1; e >= input_eff; e--)
+ for(e = neffects - 1; e > input_eff; e--)
{
/* flow_effect returns ST_EOF when it will not process
* any more samples. This is used to bail out early.
@@ -1019,7 +1025,9 @@
*/
if (efftab[e].odone < efftab[e].olen)
{
- /* fprintf(stderr, "Breaking out of loop to flush buffer\n"); */
+#if 0
+ fprintf(stderr, "Breaking out of loop to flush buffer\n");
+#endif
break;
}
}
@@ -1067,7 +1075,7 @@
* show no more data.
*/
havedata = 0;
- for(e = neffects - 1; e >= input_eff; e--)
+ for(e = neffects - 1; e > input_eff; e--)
{
/* If odone and olen are the same then this buffer
* can be reused.
@@ -1113,7 +1121,12 @@
* fact to caller.
*/
if (input_eff > 0)
+ {
+#if 0
+ fprintf(stderr, "Effect return ST_EOF\n");
+#endif
return ST_EOF;
+ }
return ST_SUCCESS;
}
@@ -1127,7 +1140,9 @@
/* I have no input data ? */
if (efftab[e-1].odone == efftab[e-1].olen)
{
- /* fprintf(stderr, "%s no data to pull to me!\n", efftab[e].name); */
+#if 0
+ fprintf(stderr, "%s no data to pull to me!\n", efftab[e].name);
+#endif
return 0;
}
@@ -1137,8 +1152,10 @@
*/
idone = efftab[e-1].olen - efftab[e-1].odone;
odone = ST_BUFSIZ - efftab[e].olen;
- /* fprintf(stderr, "pre %s idone=%d, odone=%d\n", efftab[e].name, idone, odone); */
- /* fprintf(stderr, "pre %s odone1=%d, olen1=%d odone=%d olen=%d\n", efftab[e].name, efftab[e-1].odone, efftab[e-1].olen, efftab[e].odone, efftab[e].olen); */
+#if 0
+ fprintf(stderr, "pre %s idone=%d, odone=%d\n", efftab[e].name, idone, odone);
+ fprintf(stderr, "pre %s odone1=%d, olen1=%d odone=%d olen=%d\n", efftab[e].name, efftab[e-1].odone, efftab[e-1].olen, efftab[e].odone, efftab[e].olen);
+#endif
effstatus = (* efftab[e].h->flow)(&efftab[e],
&efftab[e-1].obuf[efftab[e-1].odone],
@@ -1150,9 +1167,10 @@
/* Leave efftab[e].odone were it was since we didn't consume data */
/*efftab[e].odone = 0;*/
efftab[e].olen += odone;
-
- /* fprintf(stderr, "post %s idone=%d, odone=%d\n", efftab[e].name, idone, odone); */
- /* fprintf(stderr, "post %s odone1=%d, olen1=%d odone=%d olen=%d\n", efftab[e].name, efftab[e-1].odone, efftab[e-1].olen, efftab[e].odone, efftab[e].olen); */
+#if 0
+ fprintf(stderr, "post %s idone=%d, odone=%d\n", efftab[e].name, idone, odone);
+ fprintf(stderr, "post %s odone1=%d, olen1=%d odone=%d olen=%d\n", efftab[e].name, efftab[e-1].odone, efftab[e-1].olen, efftab[e].odone, efftab[e].olen);
+#endif
done = idone + odone;
} else {