output/alsa: don't generate silence if ALSA-PCM buffer has enough data
If our `ring_buffer` is smaller than the ALSA-PCM buffer (if the
latter has more than the 4 periods we allocate), it can happen that
the start threshold is crossed and ALSA switches to
`SND_PCM_STATE_RUNNING`, but the `ring_buffer` is empty. In this
case, MPDD will generate silence, even though the ALSA-PCM buffer has
enough data. This causes stuttering (#420).
This commit amends an older workaround for a similar problem (commit
e08598e7e2
) by adding a snd_pcm_avail()
check, and only generate silence if there is less than one period of
data in the ALSA-PCM buffer.
Fixes #420
This commit is contained in:
parent
3830748de5
commit
ccafe3f3cf
1
NEWS
1
NEWS
|
@ -1,6 +1,7 @@
|
||||||
ver 0.21.3 (not yet released)
|
ver 0.21.3 (not yet released)
|
||||||
* output
|
* output
|
||||||
- alsa: fix crash bug
|
- alsa: fix crash bug
|
||||||
|
- alsa: fix stuttering at start of playback
|
||||||
|
|
||||||
ver 0.21.2 (2018/11/12)
|
ver 0.21.2 (2018/11/12)
|
||||||
* protocol
|
* protocol
|
||||||
|
|
|
@ -109,6 +109,13 @@ class AlsaOutput final
|
||||||
*/
|
*/
|
||||||
snd_pcm_uframes_t period_frames;
|
snd_pcm_uframes_t period_frames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If snd_pcm_avail() goes above this value and no more data
|
||||||
|
* is available in the #ring_buffer, we need to play some
|
||||||
|
* silence.
|
||||||
|
*/
|
||||||
|
snd_pcm_sframes_t max_avail_frames;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this a buggy alsa-lib version, which needs a workaround
|
* Is this a buggy alsa-lib version, which needs a workaround
|
||||||
* for the snd_pcm_drain() bug always returning -EAGAIN? See
|
* for the snd_pcm_drain() bug always returning -EAGAIN? See
|
||||||
|
@ -482,6 +489,10 @@ AlsaOutput::Setup(AudioFormat &audio_format,
|
||||||
|
|
||||||
period_frames = alsa_period_size;
|
period_frames = alsa_period_size;
|
||||||
|
|
||||||
|
/* generate silence if there's less than once period of data
|
||||||
|
in the ALSA-PCM buffer */
|
||||||
|
max_avail_frames = hw_result.buffer_size - hw_result.period_size;
|
||||||
|
|
||||||
silence = new uint8_t[snd_pcm_frames_to_bytes(pcm, alsa_period_size)];
|
silence = new uint8_t[snd_pcm_frames_to_bytes(pcm, alsa_period_size)];
|
||||||
snd_pcm_format_set_silence(hw_result.format, silence,
|
snd_pcm_format_set_silence(hw_result.format, silence,
|
||||||
alsa_period_size * audio_format.channels);
|
alsa_period_size * audio_format.channels);
|
||||||
|
@ -933,7 +944,8 @@ try {
|
||||||
CopyRingToPeriodBuffer();
|
CopyRingToPeriodBuffer();
|
||||||
|
|
||||||
if (period_buffer.IsEmpty()) {
|
if (period_buffer.IsEmpty()) {
|
||||||
if (snd_pcm_state(pcm) == SND_PCM_STATE_PREPARED) {
|
if (snd_pcm_state(pcm) == SND_PCM_STATE_PREPARED ||
|
||||||
|
snd_pcm_avail(pcm) <= max_avail_frames) {
|
||||||
/* at SND_PCM_STATE_PREPARED (not yet switched
|
/* at SND_PCM_STATE_PREPARED (not yet switched
|
||||||
to SND_PCM_STATE_RUNNING), we have no
|
to SND_PCM_STATE_RUNNING), we have no
|
||||||
pressure to fill the ALSA buffer, because
|
pressure to fill the ALSA buffer, because
|
||||||
|
@ -943,6 +955,11 @@ try {
|
||||||
monitoring the ALSA file descriptor, and
|
monitoring the ALSA file descriptor, and
|
||||||
let it be reactivated by Play()/Activate()
|
let it be reactivated by Play()/Activate()
|
||||||
whenever more data arrives */
|
whenever more data arrives */
|
||||||
|
/* the same applies when there is still enough
|
||||||
|
data in the ALSA-PCM buffer (determined by
|
||||||
|
snd_pcm_avail()); this can happend at the
|
||||||
|
start of playback, when our ring_buffer is
|
||||||
|
smaller than the ALSA-PCM buffer */
|
||||||
|
|
||||||
{
|
{
|
||||||
const std::lock_guard<Mutex> lock(mutex);
|
const std::lock_guard<Mutex> lock(mutex);
|
||||||
|
|
Loading…
Reference in New Issue