From f6e1da0dc00dc3437dfc8dd4a963e648fd6584a1 Mon Sep 17 00:00:00 2001 From: Michael Maltese Date: Wed, 22 Mar 2017 16:09:59 -0700 Subject: [PATCH] AudioCommon: add Cubeb backend --- Source/Core/AudioCommon/AudioCommon.cpp | 11 +- Source/Core/AudioCommon/AudioCommon.vcxproj | 2 + .../AudioCommon/AudioCommon.vcxproj.filters | 6 + Source/Core/AudioCommon/CMakeLists.txt | 3 +- Source/Core/AudioCommon/CubebStream.cpp | 115 ++++++++++++++++++ Source/Core/AudioCommon/CubebStream.h | 32 +++++ Source/Core/Core/ConfigManager.h | 1 + Source/UnitTests/UnitTests.vcxproj | 2 +- 8 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 Source/Core/AudioCommon/CubebStream.cpp create mode 100644 Source/Core/AudioCommon/CubebStream.h diff --git a/Source/Core/AudioCommon/AudioCommon.cpp b/Source/Core/AudioCommon/AudioCommon.cpp index fea933e582..6fc5a70c84 100644 --- a/Source/Core/AudioCommon/AudioCommon.cpp +++ b/Source/Core/AudioCommon/AudioCommon.cpp @@ -5,6 +5,7 @@ #include "AudioCommon/AudioCommon.h" #include "AudioCommon/AlsaSoundStream.h" #include "AudioCommon/CoreAudioSoundStream.h" +#include "AudioCommon/CubebStream.h" #include "AudioCommon/Mixer.h" #include "AudioCommon/NullSoundStream.h" #include "AudioCommon/OpenALStream.h" @@ -30,7 +31,9 @@ static const int AUDIO_VOLUME_MAX = 100; void InitSoundStream() { std::string backend = SConfig::GetInstance().sBackend; - if (backend == BACKEND_OPENAL && OpenALStream::isValid()) + if (backend == BACKEND_CUBEB) + g_sound_stream = std::make_unique(); + else if (backend == BACKEND_OPENAL && OpenALStream::isValid()) g_sound_stream = std::make_unique(); else if (backend == BACKEND_NULLSOUND) g_sound_stream = std::make_unique(); @@ -110,6 +113,7 @@ std::vector GetSoundBackends() std::vector backends; backends.push_back(BACKEND_NULLSOUND); + backends.push_back(BACKEND_CUBEB); if (XAudio2_7::isValid() || XAudio2::isValid()) backends.push_back(BACKEND_XAUDIO2); if (AlsaSound::isValid()) @@ -131,6 +135,8 @@ bool SupportsDPL2Decoder(const std::string& backend) if (backend == BACKEND_OPENAL) return true; #endif + if (backend == BACKEND_CUBEB) + return true; if (backend == BACKEND_PULSEAUDIO) return true; return false; @@ -146,7 +152,8 @@ bool SupportsVolumeChanges(const std::string& backend) // FIXME: this one should ask the backend whether it supports it. // but getting the backend from string etc. is probably // too much just to enable/disable a stupid slider... - return backend == BACKEND_COREAUDIO || backend == BACKEND_OPENAL || backend == BACKEND_XAUDIO2; + return backend == BACKEND_COREAUDIO || backend == BACKEND_CUBEB || backend == BACKEND_OPENAL || + backend == BACKEND_XAUDIO2; } void UpdateSoundStream() diff --git a/Source/Core/AudioCommon/AudioCommon.vcxproj b/Source/Core/AudioCommon/AudioCommon.vcxproj index afc86a7222..a112b0454d 100644 --- a/Source/Core/AudioCommon/AudioCommon.vcxproj +++ b/Source/Core/AudioCommon/AudioCommon.vcxproj @@ -37,6 +37,7 @@ + @@ -52,6 +53,7 @@ + diff --git a/Source/Core/AudioCommon/AudioCommon.vcxproj.filters b/Source/Core/AudioCommon/AudioCommon.vcxproj.filters index 3103ad558e..dfd38a0336 100644 --- a/Source/Core/AudioCommon/AudioCommon.vcxproj.filters +++ b/Source/Core/AudioCommon/AudioCommon.vcxproj.filters @@ -23,6 +23,9 @@ SoundStreams + + SoundStreams + @@ -57,6 +60,9 @@ SoundStreams + + SoundStreams + diff --git a/Source/Core/AudioCommon/CMakeLists.txt b/Source/Core/AudioCommon/CMakeLists.txt index 8f0a283db8..5f0c7c143b 100644 --- a/Source/Core/AudioCommon/CMakeLists.txt +++ b/Source/Core/AudioCommon/CMakeLists.txt @@ -1,5 +1,6 @@ set(SRCS AudioCommon.cpp + CubebStream.cpp DPL2Decoder.cpp Mixer.cpp WaveFile.cpp @@ -76,4 +77,4 @@ elseif(APPLE) target_sources(audiocommon PRIVATE CoreAudioSoundStream.cpp) endif() -target_link_libraries(audiocommon PRIVATE SoundTouch) +target_link_libraries(audiocommon PRIVATE cubeb SoundTouch) diff --git a/Source/Core/AudioCommon/CubebStream.cpp b/Source/Core/AudioCommon/CubebStream.cpp new file mode 100644 index 0000000000..21bf6d812c --- /dev/null +++ b/Source/Core/AudioCommon/CubebStream.cpp @@ -0,0 +1,115 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "AudioCommon/CubebStream.h" +#include "AudioCommon/DPL2Decoder.h" +#include "Common/CommonTypes.h" +#include "Common/Logging/Log.h" +#include "Common/Thread.h" +#include "Core/ConfigManager.h" + +// ~10 ms - needs to be at least 240 for surround +constexpr u32 BUFFER_SAMPLES = 512; + +long CubebStream::DataCallback(cubeb_stream* stream, void* user_data, const void* /*input_buffer*/, + void* output_buffer, long num_frames) +{ + auto* self = static_cast(user_data); + + if (self->m_stereo) + { + self->m_mixer->Mix(static_cast(output_buffer), num_frames); + } + else + { + size_t required_capacity = num_frames * 2; + if (required_capacity > self->m_short_buffer.capacity() || + required_capacity > self->m_floatstereo_buffer.capacity()) + { + INFO_LOG(AUDIO, "Expanding conversion buffers size: %li frames", num_frames); + self->m_short_buffer.reserve(required_capacity); + self->m_floatstereo_buffer.reserve(required_capacity); + } + + self->m_mixer->Mix(self->m_short_buffer.data(), num_frames); + + // s16 to float + for (size_t i = 0; i < static_cast(num_frames) * 2; ++i) + self->m_floatstereo_buffer[i] = self->m_short_buffer[i] / static_cast(1 << 15); + + // DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR + DPL2Decode(self->m_floatstereo_buffer.data(), num_frames, static_cast(output_buffer)); + } + + return num_frames; +} + +void CubebStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) +{ +} + +bool CubebStream::Start() +{ + if (cubeb_init(&m_ctx, "Dolphin", nullptr) != CUBEB_OK) + { + ERROR_LOG(AUDIO, "Error initializing cubeb library"); + return false; + } + + INFO_LOG(AUDIO, "Cubeb initialized using %s backend", cubeb_get_backend_id(m_ctx)); + + m_stereo = !SConfig::GetInstance().bDPL2Decoder; + + cubeb_stream_params params; + params.rate = m_mixer->GetSampleRate(); + if (m_stereo) + { + params.channels = 2; + params.format = CUBEB_SAMPLE_S16NE; + params.layout = CUBEB_LAYOUT_STEREO; + } + else + { + params.channels = 6; + params.format = CUBEB_SAMPLE_FLOAT32NE; + params.layout = CUBEB_LAYOUT_3F2_LFE; + } + + u32 minimum_latency = 0; + if (cubeb_get_min_latency(m_ctx, params, &minimum_latency) != CUBEB_OK) + ERROR_LOG(AUDIO, "Error getting minimum latency"); + INFO_LOG(AUDIO, "Minimum latency: %i frames", minimum_latency); + + if (cubeb_stream_init(m_ctx, &m_stream, "Dolphin Audio Output", nullptr, nullptr, nullptr, + ¶ms, std::max(BUFFER_SAMPLES, minimum_latency), DataCallback, + StateCallback, this) != CUBEB_OK) + { + ERROR_LOG(AUDIO, "Error initializing cubeb stream"); + return false; + } + + if (cubeb_stream_start(m_stream) != CUBEB_OK) + { + ERROR_LOG(AUDIO, "Error starting cubeb stream"); + return false; + } + return true; +} + +void CubebStream::Stop() +{ + if (cubeb_stream_stop(m_stream) != CUBEB_OK) + { + ERROR_LOG(AUDIO, "Error stopping cubeb stream"); + } + cubeb_stream_destroy(m_stream); + cubeb_destroy(m_ctx); +} + +void CubebStream::SetVolume(int volume) +{ + cubeb_stream_set_volume(m_stream, volume / 100.0f); +} diff --git a/Source/Core/AudioCommon/CubebStream.h b/Source/Core/AudioCommon/CubebStream.h new file mode 100644 index 0000000000..32434affd1 --- /dev/null +++ b/Source/Core/AudioCommon/CubebStream.h @@ -0,0 +1,32 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "AudioCommon/SoundStream.h" + +#include + +class CubebStream final : public SoundStream +{ +public: + bool Start() override; + void Stop() override; + void SetVolume(int) override; + +private: + bool m_stereo = false; + cubeb* m_ctx = nullptr; + cubeb_stream* m_stream = nullptr; + + std::vector m_short_buffer; + std::vector m_floatstereo_buffer; + + static long DataCallback(cubeb_stream* stream, void* user_data, const void* /*input_buffer*/, + void* output_buffer, long num_frames); + static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state); +}; diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index be35ea8e7e..75b8526b75 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -35,6 +35,7 @@ class TMDReader; #define BACKEND_NULLSOUND _trans("No audio output") #define BACKEND_ALSA "ALSA" #define BACKEND_COREAUDIO "CoreAudio" +#define BACKEND_CUBEB "Cubeb" #define BACKEND_OPENAL "OpenAL" #define BACKEND_PULSEAUDIO "Pulse" #define BACKEND_XAUDIO2 "XAudio2" diff --git a/Source/UnitTests/UnitTests.vcxproj b/Source/UnitTests/UnitTests.vcxproj index 1db4b2f5b2..e80264a60a 100644 --- a/Source/UnitTests/UnitTests.vcxproj +++ b/Source/UnitTests/UnitTests.vcxproj @@ -49,7 +49,7 @@ dolphin codebase. --> $(ExternalsDir)OpenAL\lib;$(ExternalsDir)ffmpeg\lib;%(AdditionalLibraryDirectories) - iphlpapi.lib;winmm.lib;setupapi.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies) + avrt.lib;iphlpapi.lib;winmm.lib;setupapi.lib;opengl32.lib;glu32.lib;rpcrt4.lib;comctl32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies) Console /NODEFAULTLIB:libcmt %(AdditionalOptions) /NODEFAULTLIB:libcmt %(AdditionalOptions)