output/alsa: call snd_pcm_prepare() after snd_pcm_drop()

Don't wait for an optimistic write to fail.  This is an improved
workaround for the infamous Raspberry Pi bug (see commit af991765).
It works much better and comes without the negative side effects.  The
old workaround is now obsolete.
This commit is contained in:
Max Kellermann 2014-03-02 11:12:09 +01:00
parent 0102a8665a
commit a884e37de1
2 changed files with 27 additions and 0 deletions

2
NEWS
View File

@ -1,6 +1,8 @@
ver 0.18.9 (not yet released) ver 0.18.9 (not yet released)
* protocol * protocol
- "findadd" requires the "add" permission - "findadd" requires the "add" permission
* output
- alsa: improved workaround for noise after manual song change
* decoder * decoder
- vorbis: fix linker failure when libvorbis/libogg are static - vorbis: fix linker failure when libvorbis/libogg are static
* encoder * encoder

View File

@ -113,6 +113,18 @@ struct AlsaOutput {
*/ */
unsigned pi_workaround; unsigned pi_workaround;
/**
* Do we need to call snd_pcm_prepare() before the next write?
* It means that we put the device to SND_PCM_STATE_SETUP by
* calling snd_pcm_drop().
*
* Without this flag, we could easily recover after a failed
* optimistic write (returning -EBADFD), but the Raspberry Pi
* audio driver is infamous for generating ugly artefacts from
* this.
*/
bool must_prepare;
/** /**
* This buffer gets allocated after opening the ALSA device. * This buffer gets allocated after opening the ALSA device.
* It contains silence samples, enough to fill one period (see * It contains silence samples, enough to fill one period (see
@ -699,6 +711,8 @@ alsa_open(struct audio_output *ao, AudioFormat &audio_format, Error &error)
ad->in_frame_size = audio_format.GetFrameSize(); ad->in_frame_size = audio_format.GetFrameSize();
ad->out_frame_size = ad->pcm_export->GetFrameSize(audio_format); ad->out_frame_size = ad->pcm_export->GetFrameSize(audio_format);
ad->must_prepare = false;
return true; return true;
} }
@ -801,6 +815,7 @@ alsa_cancel(struct audio_output *ao)
AlsaOutput *ad = (AlsaOutput *)ao; AlsaOutput *ad = (AlsaOutput *)ao;
ad->period_position = 0; ad->period_position = 0;
ad->must_prepare = true;
snd_pcm_drop(ad->pcm); snd_pcm_drop(ad->pcm);
} }
@ -822,6 +837,16 @@ alsa_play(struct audio_output *ao, const void *chunk, size_t size,
assert(size % ad->in_frame_size == 0); assert(size % ad->in_frame_size == 0);
if (ad->must_prepare) {
ad->must_prepare = false;
int err = snd_pcm_prepare(ad->pcm);
if (err < 0) {
error.Set(alsa_output_domain, err, snd_strerror(-err));
return 0;
}
}
chunk = ad->pcm_export->Export(chunk, size, size); chunk = ad->pcm_export->Export(chunk, size, size);
assert(size % ad->out_frame_size == 0); assert(size % ad->out_frame_size == 0);