Merge pull request #1729 from MerryMage/null-sink
Audio Config: Implement null sink and implement sink configuration
This commit is contained in:
commit
c1f0044a4b
|
@ -5,6 +5,7 @@ set(SRCS
|
||||||
hle/filter.cpp
|
hle/filter.cpp
|
||||||
hle/pipe.cpp
|
hle/pipe.cpp
|
||||||
interpolate.cpp
|
interpolate.cpp
|
||||||
|
sink_details.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
|
@ -15,7 +16,9 @@ set(HEADERS
|
||||||
hle/filter.h
|
hle/filter.h
|
||||||
hle/pipe.h
|
hle/pipe.h
|
||||||
interpolate.h
|
interpolate.h
|
||||||
|
null_sink.h
|
||||||
sink.h
|
sink.h
|
||||||
|
sink_details.h
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(../../externals/soundtouch/include)
|
include_directories(../../externals/soundtouch/include)
|
||||||
|
|
|
@ -2,9 +2,15 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "audio_core/audio_core.h"
|
#include "audio_core/audio_core.h"
|
||||||
#include "audio_core/hle/dsp.h"
|
#include "audio_core/hle/dsp.h"
|
||||||
#include "audio_core/hle/pipe.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/core_timing.h"
|
||||||
#include "core/hle/kernel/vm_manager.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);
|
CoreTiming::ScheduleEvent(audio_frame_ticks - cycles_late, tick_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialise Audio
|
|
||||||
void Init() {
|
void Init() {
|
||||||
DSP::HLE::Init();
|
DSP::HLE::Init();
|
||||||
|
|
||||||
|
@ -36,7 +41,6 @@ void Init() {
|
||||||
CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event);
|
CoreTiming::ScheduleEvent(audio_frame_ticks, tick_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add DSP address spaces to Process's address space.
|
|
||||||
void AddAddressSpace(Kernel::VMManager& address_space) {
|
void AddAddressSpace(Kernel::VMManager& address_space) {
|
||||||
auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom();
|
auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom();
|
||||||
address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite);
|
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);
|
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<NullSink>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DSP::HLE::SetSink(iter->factory());
|
||||||
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
CoreTiming::UnscheduleEvent(tick_event, 0);
|
CoreTiming::UnscheduleEvent(tick_event, 0);
|
||||||
DSP::HLE::Shutdown();
|
DSP::HLE::Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
} //namespace
|
} // namespace AudioCore
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class VMManager;
|
class VMManager;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +20,9 @@ void Init();
|
||||||
/// Add DSP address spaces to a Process.
|
/// Add DSP address spaces to a Process.
|
||||||
void AddAddressSpace(Kernel::VMManager& vm_manager);
|
void AddAddressSpace(Kernel::VMManager& vm_manager);
|
||||||
|
|
||||||
|
/// Select the sink to use based on sink id.
|
||||||
|
void SelectSink(std::string sink_id);
|
||||||
|
|
||||||
/// Shutdown Audio Core
|
/// Shutdown Audio Core
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,11 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "audio_core/hle/dsp.h"
|
#include "audio_core/hle/dsp.h"
|
||||||
#include "audio_core/hle/pipe.h"
|
#include "audio_core/hle/pipe.h"
|
||||||
|
#include "audio_core/sink.h"
|
||||||
|
|
||||||
namespace DSP {
|
namespace DSP {
|
||||||
namespace HLE {
|
namespace HLE {
|
||||||
|
@ -35,6 +38,8 @@ static SharedMemory& WriteRegion() {
|
||||||
return g_regions[1 - CurrentRegionIndex()];
|
return g_regions[1 - CurrentRegionIndex()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<AudioCore::Sink> sink;
|
||||||
|
|
||||||
void Init() {
|
void Init() {
|
||||||
DSP::HLE::ResetPipes();
|
DSP::HLE::ResetPipes();
|
||||||
}
|
}
|
||||||
|
@ -46,5 +51,9 @@ bool Tick() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetSink(std::unique_ptr<AudioCore::Sink> sink_) {
|
||||||
|
sink = std::move(sink_);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
} // namespace DSP
|
} // namespace DSP
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "audio_core/hle/common.h"
|
#include "audio_core/hle/common.h"
|
||||||
|
@ -15,6 +16,10 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
|
||||||
|
namespace AudioCore {
|
||||||
|
class Sink;
|
||||||
|
}
|
||||||
|
|
||||||
namespace DSP {
|
namespace DSP {
|
||||||
namespace HLE {
|
namespace HLE {
|
||||||
|
|
||||||
|
@ -535,5 +540,11 @@ void Shutdown();
|
||||||
*/
|
*/
|
||||||
bool Tick();
|
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<AudioCore::Sink> sink);
|
||||||
|
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
} // namespace DSP
|
} // namespace DSP
|
||||||
|
|
|
@ -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 <cstddef>
|
||||||
|
|
||||||
|
#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<s16>&) override {}
|
||||||
|
|
||||||
|
size_t SamplesInQueue() const override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace AudioCore
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "audio_core/null_sink.h"
|
||||||
|
#include "audio_core/sink_details.h"
|
||||||
|
|
||||||
|
namespace AudioCore {
|
||||||
|
|
||||||
|
// g_sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||||
|
const std::vector<SinkDetails> g_sink_details = {
|
||||||
|
{ "null", []() { return std::make_unique<NullSink>(); } },
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace AudioCore
|
|
@ -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 <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace AudioCore {
|
||||||
|
|
||||||
|
class Sink;
|
||||||
|
|
||||||
|
struct SinkDetails {
|
||||||
|
SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>()> 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<std::unique_ptr<Sink>()> factory;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const std::vector<SinkDetails> g_sink_details;
|
||||||
|
|
||||||
|
} // namespace AudioCore
|
|
@ -71,6 +71,9 @@ void Config::ReadValues() {
|
||||||
Settings::values.bg_green = (float)sdl2_config->GetReal("Renderer", "bg_green", 1.0);
|
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);
|
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
|
// Data Storage
|
||||||
Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
|
Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,11 @@ bg_red =
|
||||||
bg_blue =
|
bg_blue =
|
||||||
bg_green =
|
bg_green =
|
||||||
|
|
||||||
|
[Audio]
|
||||||
|
# Which audio output engine to use.
|
||||||
|
# auto (default): Auto-select, null: No audio output
|
||||||
|
output_engine =
|
||||||
|
|
||||||
[Data Storage]
|
[Data Storage]
|
||||||
# Whether to create a virtual SD card.
|
# Whether to create a virtual SD card.
|
||||||
# 1 (default): Yes, 0: No
|
# 1 (default): Yes, 0: No
|
||||||
|
|
|
@ -52,6 +52,10 @@ void Config::ReadValues() {
|
||||||
Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat();
|
Settings::values.bg_blue = qt_config->value("bg_blue", 1.0).toFloat();
|
||||||
qt_config->endGroup();
|
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");
|
qt_config->beginGroup("Data Storage");
|
||||||
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
|
Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
@ -138,6 +142,10 @@ void Config::SaveValues() {
|
||||||
qt_config->setValue("bg_blue", (double)Settings::values.bg_blue);
|
qt_config->setValue("bg_blue", (double)Settings::values.bg_blue);
|
||||||
qt_config->endGroup();
|
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->beginGroup("Data Storage");
|
||||||
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
|
qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
|
||||||
qt_config->endGroup();
|
qt_config->endGroup();
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include "audio_core/audio_core.h"
|
||||||
|
|
||||||
#include "core/gdbstub/gdbstub.h"
|
#include "core/gdbstub/gdbstub.h"
|
||||||
|
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
@ -20,6 +22,9 @@ void Apply() {
|
||||||
VideoCore::g_hw_renderer_enabled = values.use_hw_renderer;
|
VideoCore::g_hw_renderer_enabled = values.use_hw_renderer;
|
||||||
VideoCore::g_shader_jit_enabled = values.use_shader_jit;
|
VideoCore::g_shader_jit_enabled = values.use_shader_jit;
|
||||||
VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution;
|
VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution;
|
||||||
|
|
||||||
|
AudioCore::SelectSink(values.sink_id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -63,6 +63,9 @@ struct Values {
|
||||||
|
|
||||||
std::string log_filter;
|
std::string log_filter;
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
std::string sink_id;
|
||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
bool use_gdbstub;
|
bool use_gdbstub;
|
||||||
u16 gdbstub_port;
|
u16 gdbstub_port;
|
||||||
|
|
Reference in New Issue