diff --git a/NEWS b/NEWS index 52b999a3a..6d7d13de1 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ ver 0.18.9 (not yet released) * protocol - "findadd" requires the "add" permission +* output + - alsa: improved workaround for noise after manual song change * decoder - vorbis: fix linker failure when libvorbis/libogg are static * encoder diff --git a/src/output/AlsaOutputPlugin.cxx b/src/output/AlsaOutputPlugin.cxx index 4877d3a46..c5db2320d 100644 --- a/src/output/AlsaOutputPlugin.cxx +++ b/src/output/AlsaOutputPlugin.cxx @@ -113,6 +113,18 @@ struct AlsaOutput { */ 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. * 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->out_frame_size = ad->pcm_export->GetFrameSize(audio_format); + ad->must_prepare = false; + return true; } @@ -801,6 +815,7 @@ alsa_cancel(struct audio_output *ao) AlsaOutput *ad = (AlsaOutput *)ao; ad->period_position = 0; + ad->must_prepare = true; 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); + 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); assert(size % ad->out_frame_size == 0);