From ab89decac5c08c4eb4114535391332e0a0312d5f Mon Sep 17 00:00:00 2001 From: pierre Date: Fri, 21 May 2010 22:48:57 +0000 Subject: [PATCH] Linux: Better ALSA support This removes some bad behaviour, like the audio thread busy waiting inside ALSA code, missing begins of voice samples(due to DSP_HLE code not getting called often enough). Turns out my understanding of ALSA API was lacking. Tested with pulse plugin and directly on my intel-hda/realtek hardware. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5461 8ced0084-cf51-0410-be5f-012b33b47a6e --- .../Core/AudioCommon/Src/AlsaSoundStream.cpp | 58 ++++++++++++------- Source/Core/AudioCommon/Src/AlsaSoundStream.h | 1 + 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/Source/Core/AudioCommon/Src/AlsaSoundStream.cpp b/Source/Core/AudioCommon/Src/AlsaSoundStream.cpp index 760082e1aa..b72a10b83c 100644 --- a/Source/Core/AudioCommon/Src/AlsaSoundStream.cpp +++ b/Source/Core/AudioCommon/Src/AlsaSoundStream.cpp @@ -19,10 +19,11 @@ #include "Thread.h" #include "AlsaSoundStream.h" -#define BUFFER_SIZE 4096 -#define BUFFER_SIZE_BYTES (BUFFER_SIZE*2*2) +#define FRAME_COUNT_MIN 256 +#define BUFFER_SIZE_MAX 8192 +#define BUFFER_SIZE_BYTES (BUFFER_SIZE_MAX*2*2) -AlsaSound::AlsaSound(CMixer *mixer) : SoundStream(mixer), thread_data(0), handle(NULL) +AlsaSound::AlsaSound(CMixer *mixer) : SoundStream(mixer), thread_data(0), handle(NULL), frames_to_deliver(FRAME_COUNT_MIN) { mix_buffer = new u8[BUFFER_SIZE_BYTES]; } @@ -60,9 +61,10 @@ void AlsaSound::Update() // Called on audio thread. void AlsaSound::SoundLoop() { - AlsaInit(); - // nakee: What is the optimal value? - int frames_to_deliver = BUFFER_SIZE; + if (!AlsaInit()) { + thread_data = 2; + return; + } while (!thread_data) { m_mixer->Mix(reinterpret_cast(mix_buffer), frames_to_deliver); @@ -88,7 +90,7 @@ bool AlsaSound::AlsaInit() int dir; snd_pcm_sw_params_t *swparams; snd_pcm_hw_params_t *hwparams; - snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t buffer_size,buffer_size_max; unsigned int periods; err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); @@ -135,31 +137,22 @@ bool AlsaSound::AlsaInit() return false; } - periods = 2; - err = snd_pcm_hw_params_set_periods_min(handle, hwparams, &periods, &dir); + periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN; + err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &periods, &dir); if (err < 0) { ERROR_LOG(AUDIO, "Cannot set Minimum periods: %s\n", snd_strerror(err)); return false; } - buffer_size = BUFFER_SIZE; - err = snd_pcm_hw_params_set_buffer_size_min(handle, hwparams, &buffer_size); + buffer_size_max = BUFFER_SIZE_MAX; + err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams, &buffer_size_max); if (err < 0) { ERROR_LOG(AUDIO, "Cannot set minimum buffer size: %s\n", snd_strerror(err)); return false; } - buffer_size = BUFFER_SIZE*2; - err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size); - if (err < 0) - { - ERROR_LOG(AUDIO, "Cannot set buffer size: %s\n", snd_strerror(err)); - return false; - } - NOTICE_LOG(AUDIO, "ALSA gave us %d \"hardware\" buffers of %d samples.\n", periods, buffer_size); - err = snd_pcm_hw_params(handle, hwparams); if (err < 0) { @@ -167,6 +160,31 @@ bool AlsaSound::AlsaInit() return false; } + err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size); + if (err < 0) + { + ERROR_LOG(AUDIO, "Cannot get buffer size: %s\n", snd_strerror(err)); + return false; + } + + err = snd_pcm_hw_params_get_periods_max(hwparams, &periods, &dir); + if (err < 0) + { + ERROR_LOG(AUDIO, "Cannot get periods: %s\n", snd_strerror(err)); + return false; + } + + //periods is the number of fragments alsa can wait for during one + //buffer_size + frames_to_deliver = buffer_size / periods; + //limit the minimum size. pulseaudio advertises a minimum of 32 samples. + if (frames_to_deliver < FRAME_COUNT_MIN) + frames_to_deliver = FRAME_COUNT_MIN; + //it is probably a bad idea to try to send more than one buffer of data + if ((unsigned int)frames_to_deliver > buffer_size) + frames_to_deliver = buffer_size; + NOTICE_LOG(AUDIO, "ALSA gave us a %d sample \"hardware\" buffer with %d periods. Will send %d samples per fragments.\n", buffer_size, periods, frames_to_deliver); + snd_pcm_sw_params_alloca(&swparams); err = snd_pcm_sw_params_current(handle, swparams); diff --git a/Source/Core/AudioCommon/Src/AlsaSoundStream.h b/Source/Core/AudioCommon/Src/AlsaSoundStream.h index cbb8d6f6f3..c763ca1ce0 100644 --- a/Source/Core/AudioCommon/Src/AlsaSoundStream.h +++ b/Source/Core/AudioCommon/Src/AlsaSoundStream.h @@ -59,6 +59,7 @@ private: volatile int thread_data; snd_pcm_t *handle; + int frames_to_deliver; #else public: AlsaSound(CMixer *mixer) : SoundStream(mixer) {}