From 7d106eff1054204587d8c4f5537102640d1c7806 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Wed, 27 Apr 2016 12:04:15 +0100 Subject: [PATCH 1/3] AudioCore: Implement NullSink --- src/audio_core/CMakeLists.txt | 1 + src/audio_core/null_sink.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 src/audio_core/null_sink.h diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index a965af2915..46d3de578f 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -15,6 +15,7 @@ set(HEADERS hle/filter.h hle/pipe.h interpolate.h + null_sink.h sink.h ) diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h new file mode 100644 index 0000000000..faf0ee4e1e --- /dev/null +++ b/src/audio_core/null_sink.h @@ -0,0 +1,29 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "audio_core/audio_core.h" +#include "audio_core/sink.h" + +namespace AudioCore { + +class NullSink final : public Sink { +public: + ~NullSink() override = default; + + unsigned int GetNativeSampleRate() const override { + return native_sample_rate; + } + + void EnqueueSamples(const std::vector&) override {} + + size_t SamplesInQueue() const override { + return 0; + } +}; + +} // namespace AudioCore From 8b94422e3e51d29171c7e8cc02bec1fbec9b29ea Mon Sep 17 00:00:00 2001 From: MerryMage Date: Thu, 28 Apr 2016 14:28:59 +0100 Subject: [PATCH 2/3] AudioCore: List of sink types --- src/audio_core/CMakeLists.txt | 2 ++ src/audio_core/sink_details.cpp | 17 +++++++++++++++++ src/audio_core/sink_details.h | 27 +++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/audio_core/sink_details.cpp create mode 100644 src/audio_core/sink_details.h diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index 46d3de578f..5a2747e78d 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -5,6 +5,7 @@ set(SRCS hle/filter.cpp hle/pipe.cpp interpolate.cpp + sink_details.cpp ) set(HEADERS @@ -17,6 +18,7 @@ set(HEADERS interpolate.h null_sink.h sink.h + sink_details.h ) include_directories(../../externals/soundtouch/include) diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp new file mode 100644 index 0000000000..20412daaf2 --- /dev/null +++ b/src/audio_core/sink_details.cpp @@ -0,0 +1,17 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#include "audio_core/null_sink.h" +#include "audio_core/sink_details.h" + +namespace AudioCore { + +const std::vector g_sink_details = { + { "null", []() { return std::make_unique(); } }, +}; + +} // namespace AudioCore diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h new file mode 100644 index 0000000000..4b30cf835f --- /dev/null +++ b/src/audio_core/sink_details.h @@ -0,0 +1,27 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +namespace AudioCore { + +class Sink; + +struct SinkDetails { + SinkDetails(const char* id_, std::function()> factory_) + : id(id_), factory(factory_) {} + + /// Name for this sink. + const char* id; + /// A method to call to construct an instance of this type of sink. + std::function()> factory; +}; + +extern const std::vector g_sink_details; + +} // namespace AudioCore From 4e971f44a27c2e4abc25ddf0720d287a688e0a4d Mon Sep 17 00:00:00 2001 From: MerryMage Date: Wed, 27 Apr 2016 13:53:23 +0100 Subject: [PATCH 3/3] Audio: Add sink selection to configuration files --- src/audio_core/audio_core.cpp | 33 +++++++++++++++++++++++++++++---- src/audio_core/audio_core.h | 5 +++++ src/audio_core/hle/dsp.cpp | 9 +++++++++ src/audio_core/hle/dsp.h | 11 +++++++++++ src/audio_core/sink_details.cpp | 1 + src/citra/config.cpp | 3 +++ src/citra/default_ini.h | 5 +++++ src/citra_qt/config.cpp | 8 ++++++++ src/core/settings.cpp | 5 +++++ src/core/settings.h | 3 +++ 10 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index cbe869a04c..d42249ebd9 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -2,9 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include + #include "audio_core/audio_core.h" #include "audio_core/hle/dsp.h" #include "audio_core/hle/pipe.h" +#include "audio_core/null_sink.h" +#include "audio_core/sink.h" +#include "audio_core/sink_details.h" #include "core/core_timing.h" #include "core/hle/kernel/vm_manager.h" @@ -28,7 +34,6 @@ static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event); } -/// Initialise Audio void Init() { DSP::HLE::Init(); @@ -36,7 +41,6 @@ void Init() { CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event); } -/// Add DSP address spaces to Process's address space. void AddAddressSpace(Kernel::VMManager& address_space) { auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite); @@ -45,10 +49,31 @@ void AddAddressSpace(Kernel::VMManager& address_space) { address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite); } -/// Shutdown Audio +void SelectSink(std::string sink_id) { + if (sink_id == "auto") { + // Auto-select. + // g_sink_details is ordered in terms of desirability, with the best choice at the front. + const auto& sink_detail = g_sink_details.front(); + DSP::HLE::SetSink(sink_detail.factory()); + return; + } + + auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) { + return sink_detail.id == sink_id; + }); + + if (iter == g_sink_details.end()) { + LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); + DSP::HLE::SetSink(std::make_unique()); + return; + } + + DSP::HLE::SetSink(iter->factory()); +} + void Shutdown() { CoreTiming::UnscheduleEvent(tick_event, 0); DSP::HLE::Shutdown(); } -} //namespace +} // namespace AudioCore diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index b349895ea7..f618361f30 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -4,6 +4,8 @@ #pragma once +#include + namespace Kernel { class VMManager; } @@ -18,6 +20,9 @@ void Init(); /// Add DSP address spaces to a Process. void AddAddressSpace(Kernel::VMManager& vm_manager); +/// Select the sink to use based on sink id. +void SelectSink(std::string sink_id); + /// Shutdown Audio Core void Shutdown(); diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp index 5759a5b9e4..4d44bd2d93 100644 --- a/src/audio_core/hle/dsp.cpp +++ b/src/audio_core/hle/dsp.cpp @@ -2,8 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + #include "audio_core/hle/dsp.h" #include "audio_core/hle/pipe.h" +#include "audio_core/sink.h" namespace DSP { namespace HLE { @@ -35,6 +38,8 @@ static SharedMemory& WriteRegion() { return g_regions[1 - CurrentRegionIndex()]; } +static std::unique_ptr sink; + void Init() { DSP::HLE::ResetPipes(); } @@ -46,5 +51,9 @@ bool Tick() { return true; } +void SetSink(std::unique_ptr sink_) { + sink = std::move(sink_); +} + } // namespace HLE } // namespace DSP diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index f0f1252844..4f2410c27a 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "audio_core/hle/common.h" @@ -15,6 +16,10 @@ #include "common/common_types.h" #include "common/swap.h" +namespace AudioCore { +class Sink; +} + namespace DSP { namespace HLE { @@ -535,5 +540,11 @@ void Shutdown(); */ bool Tick(); +/** + * Set the output sink. This must be called before calling Tick(). + * @param sink The sink to which audio will be output to. + */ +void SetSink(std::unique_ptr sink); + } // namespace HLE } // namespace DSP diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp index 20412daaf2..d2cc741032 100644 --- a/src/audio_core/sink_details.cpp +++ b/src/audio_core/sink_details.cpp @@ -10,6 +10,7 @@ namespace AudioCore { +// g_sink_details is ordered in terms of desirability, with the best choice at the top. const std::vector g_sink_details = { { "null", []() { return std::make_unique(); } }, }; diff --git a/src/citra/config.cpp b/src/citra/config.cpp index 9e2ecd307f..0d17c80bf2 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -71,6 +71,9 @@ void Config::ReadValues() { Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0); Settings::values.bg_blue = (float)sdl2_config->GetReal("Renderer", "bg_blue", 1.0); + // Audio + Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); + // Data Storage Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h index 1f1aa716b8..0e6171736e 100644 --- a/src/citra/default_ini.h +++ b/src/citra/default_ini.h @@ -56,6 +56,11 @@ bg_red = bg_blue = bg_green = +[Audio] +# Which audio output engine to use. +# auto (default): Auto-select, null: No audio output +output_engine = + [Data Storage] # Whether to create a virtual SD card. # 1 (default): Yes, 0: No diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp index 7dc61fe408..b5bb75537d 100644 --- a/src/citra_qt/config.cpp +++ b/src/citra_qt/config.cpp @@ -52,6 +52,10 @@ void Config::ReadValues() { Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat(); qt_config->endGroup(); + qt_config->beginGroup("Audio"); + Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); + qt_config->endGroup(); + qt_config->beginGroup("Data Storage"); Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); qt_config->endGroup(); @@ -138,6 +142,10 @@ void Config::SaveValues() { qt_config->setValue("bg_blue", (double)Settings::values.bg_blue); qt_config->endGroup(); + qt_config->beginGroup("Audio"); + qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); + qt_config->endGroup(); + qt_config->beginGroup("Data Storage"); qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); qt_config->endGroup(); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index eaf5c8461e..77261eafe2 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -4,6 +4,8 @@ #include "settings.h" +#include "audio_core/audio_core.h" + #include "core/gdbstub/gdbstub.h" #include "video_core/video_core.h" @@ -20,6 +22,9 @@ void Apply() { VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; VideoCore::g_shader_jit_enabled = values.use_shader_jit; VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; + + AudioCore::SelectSink(values.sink_id); + } } // namespace diff --git a/src/core/settings.h b/src/core/settings.h index d620d84615..04c0a47f91 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -63,6 +63,9 @@ struct Values { std::string log_filter; + // Audio + std::string sink_id; + // Debugging bool use_gdbstub; u16 gdbstub_port;