From 8d5825ee007a7889992d5a01e482d1589dd6a7ee Mon Sep 17 00:00:00 2001 From: ivan-83 Date: Mon, 9 Mar 2015 09:11:46 +0300 Subject: [PATCH] Fix poor sound quality with ALSA and decrease "ALSA lib pcm.c:7339:(snd_pcm_recover) underrun occurred" + little code cleanup --- channels/rdpsnd/client/alsa/rdpsnd_alsa.c | 79 ++++++++++------------- include/freerdp/client/rdpsnd.h | 2 - 2 files changed, 35 insertions(+), 46 deletions(-) diff --git a/channels/rdpsnd/client/alsa/rdpsnd_alsa.c b/channels/rdpsnd/client/alsa/rdpsnd_alsa.c index 569822349..6c87ac359 100644 --- a/channels/rdpsnd/client/alsa/rdpsnd_alsa.c +++ b/channels/rdpsnd/client/alsa/rdpsnd_alsa.c @@ -60,8 +60,6 @@ struct rdpsnd_alsa_plugin int bytes_per_channel; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; - snd_pcm_uframes_t start_threshold; - snd_async_handler_t* pcm_callback; FREERDP_DSP_CONTEXT* dsp_context; }; @@ -104,19 +102,42 @@ static int rdpsnd_alsa_set_hw_params(rdpsndAlsaPlugin* alsa) status = snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size_max); SND_PCM_CHECK("snd_pcm_hw_params_get_buffer_size_max", status); - if (alsa->buffer_size > buffer_size_max) + /** + * ALSA Parameters + * + * http://www.alsa-project.org/main/index.php/FramesPeriods + * + * buffer_size = period_size * periods + * period_bytes = period_size * bytes_per_frame + * bytes_per_frame = channels * bytes_per_sample + * + * A frame is equivalent of one sample being played, + * irrespective of the number of channels or the number of bits + * + * A period is the number of frames in between each hardware interrupt. + * + * The buffer size always has to be greater than one period size. + * Commonly this is (2 * period_size), but some hardware can do 8 periods per buffer. + * It is also possible for the buffer size to not be an integer multiple of the period size. + */ + + int interrupts_per_sec_near = 50; + int bytes_per_sec = (alsa->actual_rate * alsa->bytes_per_channel * alsa->actual_channels); + + alsa->buffer_size = buffer_size_max; + alsa->period_size = (bytes_per_sec / interrupts_per_sec_near); + + if (alsa->period_size > buffer_size_max) { - WLog_ERR(TAG, "Warning: requested sound buffer size %d, got %d instead\n", + WLog_ERR(TAG, "Warning: requested sound buffer size %d, got %d instead\n", (int) alsa->buffer_size, (int) buffer_size_max); - alsa->buffer_size = buffer_size_max; + alsa->period_size = (buffer_size_max / 8); } /* Set buffer size */ status = snd_pcm_hw_params_set_buffer_size_near(alsa->pcm_handle, hw_params, &alsa->buffer_size); SND_PCM_CHECK("snd_pcm_hw_params_set_buffer_size_near", status); - alsa->period_size = alsa->buffer_size / 2; - /* Set period size */ status = snd_pcm_hw_params_set_period_size_near(alsa->pcm_handle, hw_params, &alsa->period_size, NULL); SND_PCM_CHECK("snd_pcm_hw_params_set_period_size_near", status); @@ -134,15 +155,16 @@ static int rdpsnd_alsa_set_sw_params(rdpsndAlsaPlugin* alsa) int status; snd_pcm_sw_params_t* sw_params; - alsa->start_threshold = alsa->buffer_size; - status = snd_pcm_sw_params_malloc(&sw_params); SND_PCM_CHECK("snd_pcm_sw_params_malloc", status); status = snd_pcm_sw_params_current(alsa->pcm_handle, sw_params); SND_PCM_CHECK("snd_pcm_sw_params_current", status); - status = snd_pcm_sw_params_set_start_threshold(alsa->pcm_handle, sw_params, alsa->start_threshold); + status = snd_pcm_sw_params_set_avail_min(alsa->pcm_handle, sw_params, (alsa->bytes_per_channel * alsa->actual_channels)); + SND_PCM_CHECK("snd_pcm_sw_params_set_avail_min", status); + + status = snd_pcm_sw_params_set_start_threshold(alsa->pcm_handle, sw_params, alsa->block_size); SND_PCM_CHECK("snd_pcm_sw_params_set_start_threshold", status); status = snd_pcm_sw_params(alsa->pcm_handle, sw_params); @@ -170,32 +192,8 @@ static int rdpsnd_alsa_validate_params(rdpsndAlsaPlugin* alsa) static int rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa) { - /** - * ALSA Parameters - * - * http://www.alsa-project.org/main/index.php/FramesPeriods - * - * buffer_size = period_size * periods - * period_bytes = period_size * bytes_per_frame - * bytes_per_frame = channels * bytes_per_sample - * - * A frame is equivalent of one sample being played, - * irrespective of the number of channels or the number of bits - * - * A period is the number of frames in between each hardware interrupt. - * - * The buffer size always has to be greater than one period size. - * Commonly this is (2 * period_size), but some hardware can do 8 periods per buffer. - * It is also possible for the buffer size to not be an integer multiple of the period size. - */ - - int interrupts_per_sec_near = 20; - int bytes_per_sec = alsa->actual_rate * alsa->bytes_per_channel * alsa->actual_channels; - snd_pcm_drop(alsa->pcm_handle); - alsa->buffer_size = bytes_per_sec / (interrupts_per_sec_near / 2); - if (rdpsnd_alsa_set_hw_params(alsa) < 0) return -1; @@ -543,7 +541,6 @@ static void rdpsnd_alsa_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave) UINT32 wCurrentTime; snd_htimestamp_t tstamp; snd_pcm_uframes_t frames; - rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device; offset = 0; @@ -591,15 +588,9 @@ static void rdpsnd_alsa_wave_play(rdpsndDevicePlugin* device, RDPSND_WAVE* wave) free(data); - snd_pcm_htimestamp(alsa->pcm_handle, &frames, &tstamp); - - wave->wPlaybackDelay = ((frames * 1000) / alsa->actual_rate); - - wave->wLocalTimeB = GetTickCount(); - wave->wLocalTimeB += wave->wPlaybackDelay; - wave->wLatency = (UINT16) (wave->wLocalTimeB - wave->wLocalTimeA); - wave->wTimeStampB = wave->wTimeStampA + wave->wLatency; - //WLog_ERR(TAG, "wTimeStampA: %d wTimeStampB: %d wLatency: %d\n", wave->wTimeStampA, wave->wTimeStampB, wave->wLatency); + /* From rdpsnd_main.c */ + wave->wTimeStampB = wave->wTimeStampA + wave->wAudioLength + 65; + wave->wLocalTimeB = wave->wLocalTimeA + wave->wAudioLength + 65; } static COMMAND_LINE_ARGUMENT_A rdpsnd_alsa_args[] = diff --git a/include/freerdp/client/rdpsnd.h b/include/freerdp/client/rdpsnd.h index 21debb1bb..d52950c3d 100644 --- a/include/freerdp/client/rdpsnd.h +++ b/include/freerdp/client/rdpsnd.h @@ -37,9 +37,7 @@ struct _RDPSND_WAVE UINT16 wTimeStampA; UINT16 wTimeStampB; - UINT16 wLatency; UINT16 wAudioLength; - UINT16 wPlaybackDelay; UINT32 wLocalTimeA; UINT32 wLocalTimeB;