From 488a679ca71c63645598743c08c318164a6c38ae Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Wed, 9 Jan 2013 17:39:19 -0600 Subject: [PATCH 1/3] use pulseaudio "simple" api --- Source/Core/AudioCommon/CMakeLists.txt | 3 +- .../Core/AudioCommon/Src/PulseAudioStream.cpp | 294 +++--------------- .../Core/AudioCommon/Src/PulseAudioStream.h | 23 +- Source/Core/DolphinWX/Src/ConfigMain.cpp | 3 +- 4 files changed, 56 insertions(+), 267 deletions(-) diff --git a/Source/Core/AudioCommon/CMakeLists.txt b/Source/Core/AudioCommon/CMakeLists.txt index be8c58d14a..b2005515cf 100644 --- a/Source/Core/AudioCommon/CMakeLists.txt +++ b/Source/Core/AudioCommon/CMakeLists.txt @@ -23,7 +23,8 @@ endif(OPENAL_FOUND) if(PULSEAUDIO_FOUND) set(SRCS ${SRCS} Src/PulseAudioStream.cpp) - set(LIBS ${LIBS} ${PULSEAUDIO_LIBRARIES}) +# TODO: remove hacks + set(LIBS ${LIBS} ${PULSEAUDIO_LIBRARIES} pulse-simple) endif(PULSEAUDIO_FOUND) if(WIN32) diff --git a/Source/Core/AudioCommon/Src/PulseAudioStream.cpp b/Source/Core/AudioCommon/Src/PulseAudioStream.cpp index 4851b4bd12..82b7ee5fa9 100644 --- a/Source/Core/AudioCommon/Src/PulseAudioStream.cpp +++ b/Source/Core/AudioCommon/Src/PulseAudioStream.cpp @@ -22,31 +22,31 @@ #include "PulseAudioStream.h" -#define BUFFER_SIZE 4096 -#define BUFFER_SIZE_BYTES (BUFFER_SIZE * 4) +namespace +{ +const size_t BUFFER_SAMPLES = 512; +const size_t CHANNEL_COUNT = 2; +const size_t BUFFER_SIZE = BUFFER_SAMPLES * CHANNEL_COUNT; +} PulseAudio::PulseAudio(CMixer *mixer) - : SoundStream(mixer), thread_running(false), mainloop(NULL) - , context(NULL), stream(NULL), iVolume(100) -{ - mix_buffer = new u8[BUFFER_SIZE_BYTES]; -} - -PulseAudio::~PulseAudio() -{ - delete [] mix_buffer; -} + : SoundStream(mixer) + , mix_buffer(BUFFER_SIZE) + , thread() + , run_thread() + , pa() +{} bool PulseAudio::Start() { - thread_running = true; + run_thread = true; thread = std::thread(std::mem_fun(&PulseAudio::SoundLoop), this); return true; } void PulseAudio::Stop() { - thread_running = false; + run_thread = false; thread.join(); } @@ -60,260 +60,54 @@ void PulseAudio::SoundLoop() { Common::SetCurrentThreadName("Audio thread - pulse"); - thread_running = PulseInit(); - - while (thread_running) + if (PulseInit()) { - int frames_to_deliver = 512; - m_mixer->Mix((short *)mix_buffer, frames_to_deliver); - if (!Write(mix_buffer, frames_to_deliver * 4)) - ERROR_LOG(AUDIO, "PulseAudio failure writing data"); + while (run_thread) + { + m_mixer->Mix(&mix_buffer[0], mix_buffer.size() / CHANNEL_COUNT); + Write(&mix_buffer[0], mix_buffer.size() * sizeof(s16)); + } + + PulseShutdown(); } - PulseShutdown(); } bool PulseAudio::PulseInit() { - // The Sample format to use - const pa_sample_spec ss = + pa_sample_spec ss = {}; + ss.format = PA_SAMPLE_S16LE; + ss.channels = 2; + ss.rate = m_mixer->GetSampleRate(); + + int error; + pa = pa_simple_new(nullptr, "dolphin-emu", PA_STREAM_PLAYBACK, + nullptr, "audio", &ss, nullptr, nullptr, &error); + + if (!pa) { - PA_SAMPLE_S16LE, - m_mixer->GetSampleRate(), - 2 - }; - - mainloop = pa_threaded_mainloop_new(); - - context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "dolphin-emu"); - pa_context_set_state_callback(context, ContextStateCB, this); - - if (pa_context_connect(context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) - { - ERROR_LOG(AUDIO, "PulseAudio failed to connect context: %s", - pa_strerror(pa_context_errno(context))); + ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", + pa_strerror(error)); return false; } - - pa_threaded_mainloop_lock(mainloop); - pa_threaded_mainloop_start(mainloop); - - for (;;) + else { - pa_context_state_t state; - - state = pa_context_get_state(context); - - if (state == PA_CONTEXT_READY) - break; - - if (!PA_CONTEXT_IS_GOOD(state)) - { - ERROR_LOG(AUDIO, "PulseAudio context state failure: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - // Wait until the context is ready - pa_threaded_mainloop_wait(mainloop); + NOTICE_LOG(AUDIO, "Pulse successfully initialized."); + //SetVolume(iVolume); + return true; } - - if (!(stream = pa_stream_new(context, "emulator", &ss, NULL))) - { - ERROR_LOG(AUDIO, "PulseAudio failed to create playback stream: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - // Set callbacks for the playback stream - pa_stream_set_state_callback(stream, StreamStateCB, this); - pa_stream_set_write_callback(stream, StreamWriteCB, this); - - if (pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_NOFLAGS, NULL, NULL) < 0) - { - ERROR_LOG(AUDIO, "PulseAudio failed to connect playback stream: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - for (;;) - { - pa_stream_state_t state; - - state = pa_stream_get_state(stream); - - if (state == PA_STREAM_READY) - break; - - if (!PA_STREAM_IS_GOOD(state)) - { - ERROR_LOG(AUDIO, "PulseAudio stream state failure: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - // Wait until the stream is ready - pa_threaded_mainloop_wait(mainloop); - } - - pa_threaded_mainloop_unlock(mainloop); - - SetVolume(iVolume); - - NOTICE_LOG(AUDIO, "Pulse successfully initialized."); - return true; } void PulseAudio::PulseShutdown() { - if (mainloop) - pa_threaded_mainloop_stop(mainloop); - - if (stream) - pa_stream_unref(stream); - - if (context) - { - pa_context_disconnect(context); - pa_context_unref(context); - } - - if (mainloop) - pa_threaded_mainloop_free(mainloop); + pa_simple_free(pa); } -void PulseAudio::SignalMainLoop() +void PulseAudio::Write(const void *data, size_t length) { - pa_threaded_mainloop_signal(mainloop, 0); -} - -void PulseAudio::ContextStateCB(pa_context *c, void *userdata) -{ - switch (pa_context_get_state(c)) + int error; + if (pa_simple_write(pa, data, length, &error) < 0) { - case PA_CONTEXT_READY: - case PA_CONTEXT_TERMINATED: - case PA_CONTEXT_FAILED: - ((PulseAudio *)userdata)->SignalMainLoop(); - break; - - default: - break; + ERROR_LOG(AUDIO, "PulseAudio failed to write data: %s", + pa_strerror(error)); } } - -void PulseAudio::StreamStateCB(pa_stream *s, void * userdata) -{ - switch (pa_stream_get_state(s)) - { - case PA_STREAM_READY: - case PA_STREAM_TERMINATED: - case PA_STREAM_FAILED: - ((PulseAudio *)userdata)->SignalMainLoop(); - break; - - default: - break; - } -} - -void PulseAudio::StreamWriteCB(pa_stream *s, size_t length, void *userdata) -{ - ((PulseAudio *)userdata)->SignalMainLoop(); -} - -static bool StateIsGood(pa_context *context, pa_stream *stream) -{ - if (!context || !PA_CONTEXT_IS_GOOD(pa_context_get_state(context)) || - !stream || !PA_STREAM_IS_GOOD(pa_stream_get_state(stream))) - { - if ((context && pa_context_get_state(context) == PA_CONTEXT_FAILED) || - (stream && pa_stream_get_state(stream) == PA_STREAM_FAILED)) - { - ERROR_LOG(AUDIO, "PulseAudio state failure: %s", - pa_strerror(pa_context_errno(context))); - } - else - { - ERROR_LOG(AUDIO, "PulseAudio state failure: %s", - pa_strerror(PA_ERR_BADSTATE)); - } - return false; - } - return true; -} - -bool PulseAudio::Write(const void *data, size_t length) -{ - if (!data || length == 0 || !stream) - return false; - - pa_threaded_mainloop_lock(mainloop); - - if (!StateIsGood(context, stream)) - { - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - while (length > 0) - { - size_t l; - int r; - - while (!(l = pa_stream_writable_size(stream))) - { - pa_threaded_mainloop_wait(mainloop); - if (!StateIsGood(context, stream)) - { - pa_threaded_mainloop_unlock(mainloop); - return false; - } - } - - if (l == (size_t)-1) - { - ERROR_LOG(AUDIO, "PulseAudio invalid stream: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - if (l > length) - l = length; - - r = pa_stream_write(stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); - if (r < 0) - { - ERROR_LOG(AUDIO, "PulseAudio error writing to stream: %s", - pa_strerror(pa_context_errno(context))); - pa_threaded_mainloop_unlock(mainloop); - return false; - } - - data = (const uint8_t*) data + l; - length -= l; - } - - pa_threaded_mainloop_unlock(mainloop); - return true; -} - -void PulseAudio::SetVolume(int volume) -{ - iVolume = volume; - - if (!stream) - return; - - pa_cvolume cvolume; - const pa_channel_map *channels = pa_stream_get_channel_map(stream); - pa_cvolume_set(&cvolume, channels->channels, - iVolume * (PA_VOLUME_NORM - PA_VOLUME_MUTED) / 100); - - pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), - &cvolume, NULL, this); -} diff --git a/Source/Core/AudioCommon/Src/PulseAudioStream.h b/Source/Core/AudioCommon/Src/PulseAudioStream.h index f3a5a43e79..38399e2506 100644 --- a/Source/Core/AudioCommon/Src/PulseAudioStream.h +++ b/Source/Core/AudioCommon/Src/PulseAudioStream.h @@ -19,7 +19,8 @@ #define _PULSE_AUDIO_STREAM_H #if defined(HAVE_PULSEAUDIO) && HAVE_PULSEAUDIO -#include +#include +#include #endif #include "Common.h" @@ -27,16 +28,16 @@ #include "Thread.h" +#include + class PulseAudio : public SoundStream { #if defined(HAVE_PULSEAUDIO) && HAVE_PULSEAUDIO public: PulseAudio(CMixer *mixer); - virtual ~PulseAudio(); virtual bool Start(); virtual void Stop(); - virtual void SetVolume(int volume); static bool isValid() {return true;} @@ -46,22 +47,16 @@ public: private: virtual void SoundLoop(); + bool PulseInit(); void PulseShutdown(); - bool Write(const void *data, size_t bytes); - void SignalMainLoop(); - static void ContextStateCB(pa_context *c, void *userdata); - static void StreamStateCB(pa_stream *s, void * userdata); - static void StreamWriteCB(pa_stream *s, size_t length, void *userdata); + void Write(const void *data, size_t bytes); - u8 *mix_buffer; + std::vector mix_buffer; std::thread thread; - volatile bool thread_running; + volatile bool run_thread; - pa_threaded_mainloop *mainloop; - pa_context *context; - pa_stream *stream; - int iVolume; + pa_simple* pa; #else public: PulseAudio(CMixer *mixer) : SoundStream(mixer) {} diff --git a/Source/Core/DolphinWX/Src/ConfigMain.cpp b/Source/Core/DolphinWX/Src/ConfigMain.cpp index ce4cd9cb22..20b0a0e31e 100644 --- a/Source/Core/DolphinWX/Src/ConfigMain.cpp +++ b/Source/Core/DolphinWX/Src/ConfigMain.cpp @@ -966,8 +966,7 @@ bool CConfigMain::SupportsVolumeChanges(std::string backend) return (backend == BACKEND_DIRECTSOUND || backend == BACKEND_COREAUDIO || backend == BACKEND_OPENAL || - backend == BACKEND_XAUDIO2 || - backend == BACKEND_PULSEAUDIO); + backend == BACKEND_XAUDIO2); } From 5c371549d319ae125fcfa6278e2ac569b21c0f78 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Wed, 9 Jan 2013 18:39:28 -0600 Subject: [PATCH 2/3] fix cmake hacks --- CMakeLists.txt | 2 +- Source/Core/AudioCommon/CMakeLists.txt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f36e1f8a2..4b0d5c42dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,7 +289,7 @@ else() message("bluez NOT found, disabling bluetooth support") endif(BLUEZ_FOUND) -check_lib(PULSEAUDIO libpulse QUIET) +check_lib(PULSEAUDIO libpulse-simple QUIET) if(PULSEAUDIO_FOUND) add_definitions(-DHAVE_PULSEAUDIO=1) message("PulseAudio found, enabling PulseAudio sound backend") diff --git a/Source/Core/AudioCommon/CMakeLists.txt b/Source/Core/AudioCommon/CMakeLists.txt index b2005515cf..be8c58d14a 100644 --- a/Source/Core/AudioCommon/CMakeLists.txt +++ b/Source/Core/AudioCommon/CMakeLists.txt @@ -23,8 +23,7 @@ endif(OPENAL_FOUND) if(PULSEAUDIO_FOUND) set(SRCS ${SRCS} Src/PulseAudioStream.cpp) -# TODO: remove hacks - set(LIBS ${LIBS} ${PULSEAUDIO_LIBRARIES} pulse-simple) + set(LIBS ${LIBS} ${PULSEAUDIO_LIBRARIES}) endif(PULSEAUDIO_FOUND) if(WIN32) From 202c005e61c12e546a69fb290ace57e170196c6a Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Thu, 10 Jan 2013 13:00:50 -0600 Subject: [PATCH 3/3] Remove commented code. No longer supporting setting volume with PulseAudio. --- Source/Core/AudioCommon/Src/PulseAudioStream.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Core/AudioCommon/Src/PulseAudioStream.cpp b/Source/Core/AudioCommon/Src/PulseAudioStream.cpp index 82b7ee5fa9..923a338190 100644 --- a/Source/Core/AudioCommon/Src/PulseAudioStream.cpp +++ b/Source/Core/AudioCommon/Src/PulseAudioStream.cpp @@ -92,7 +92,6 @@ bool PulseAudio::PulseInit() else { NOTICE_LOG(AUDIO, "Pulse successfully initialized."); - //SetVolume(iVolume); return true; } }