summaryrefslogtreecommitdiffstats
path: root/src/audio_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core')
-rw-r--r--src/audio_core/CMakeLists.txt11
-rw-r--r--src/audio_core/audio_core.cpp2
-rw-r--r--src/audio_core/audio_manager.cpp17
-rw-r--r--src/audio_core/audio_manager.h19
-rw-r--r--src/audio_core/in/audio_in_system.cpp8
-rw-r--r--src/audio_core/in/audio_in_system.h2
-rw-r--r--src/audio_core/out/audio_out_system.cpp10
-rw-r--r--src/audio_core/out/audio_out_system.h4
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.cpp4
-rw-r--r--src/audio_core/renderer/adsp/audio_renderer.h2
-rw-r--r--src/audio_core/renderer/behavior/info_updater.cpp2
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.cpp2
-rw-r--r--src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp2
-rw-r--r--src/audio_core/renderer/system.cpp89
-rw-r--r--src/audio_core/renderer/system.h16
-rw-r--r--src/audio_core/renderer/system_manager.cpp2
-rw-r--r--src/audio_core/renderer/voice/voice_context.cpp4
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp31
-rw-r--r--src/audio_core/sink/cubeb_sink.h7
-rw-r--r--src/audio_core/sink/sdl2_sink.cpp14
-rw-r--r--src/audio_core/sink/sdl2_sink.h7
-rw-r--r--src/audio_core/sink/sink_details.cpp66
-rw-r--r--src/audio_core/sink/sink_details.h2
-rw-r--r--src/audio_core/sink/sink_stream.cpp9
24 files changed, 209 insertions, 123 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 144f1bab2..0a1f3bf18 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -206,20 +206,11 @@ if (MSVC)
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
- /we4456 # Declaration of 'identifier' hides previous local declaration
- /we4457 # Declaration of 'identifier' hides function parameter
- /we4458 # Declaration of 'identifier' hides class member
- /we4459 # Declaration of 'identifier' hides global declaration
+ /we4800 # Implicit conversion from 'type' to bool. Possible information loss
)
else()
target_compile_options(audio_core PRIVATE
-Werror=conversion
- -Werror=ignored-qualifiers
- -Werror=shadow
- -Werror=unused-variable
-
- $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
- $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
-Wno-sign-conversion
)
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp
index c845330cd..07a679c32 100644
--- a/src/audio_core/audio_core.cpp
+++ b/src/audio_core/audio_core.cpp
@@ -8,7 +8,7 @@
namespace AudioCore {
-AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>(system)} {
+AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>()} {
CreateSinks();
// Must be created after the sinks
adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink);
diff --git a/src/audio_core/audio_manager.cpp b/src/audio_core/audio_manager.cpp
index 2f1bba9c3..2acde668e 100644
--- a/src/audio_core/audio_manager.cpp
+++ b/src/audio_core/audio_manager.cpp
@@ -1,14 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "audio_core/audio_in_manager.h"
#include "audio_core/audio_manager.h"
-#include "audio_core/audio_out_manager.h"
#include "core/core.h"
+#include "core/hle/service/audio/errors.h"
namespace AudioCore {
-AudioManager::AudioManager(Core::System& system_) : system{system_} {
+AudioManager::AudioManager() {
thread = std::jthread([this]() { ThreadFunc(); });
}
@@ -27,7 +26,7 @@ Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
if (buffer_events[index] == nullptr) {
- buffer_events[index] = buffer_func;
+ buffer_events[index] = std::move(buffer_func);
needs_update = true;
events.SetAudioEvent(Event::Type::AudioOutManager, true);
}
@@ -43,7 +42,7 @@ Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
if (buffer_events[index] == nullptr) {
- buffer_events[index] = buffer_func;
+ buffer_events[index] = std::move(buffer_func);
needs_update = true;
events.SetAudioEvent(Event::Type::AudioInManager, true);
}
@@ -60,19 +59,21 @@ void AudioManager::ThreadFunc() {
running = true;
while (running) {
- auto timed_out{events.Wait(l, std::chrono::seconds(2))};
+ const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
if (events.CheckAudioEventSet(Event::Type::Max)) {
break;
}
for (size_t i = 0; i < buffer_events.size(); i++) {
- if (events.CheckAudioEventSet(Event::Type(i)) || timed_out) {
+ const auto event_type = static_cast<Event::Type>(i);
+
+ if (events.CheckAudioEventSet(event_type) || timed_out) {
if (buffer_events[i]) {
buffer_events[i]();
}
}
- events.SetAudioEvent(Event::Type(i), false);
+ events.SetAudioEvent(event_type, false);
}
}
}
diff --git a/src/audio_core/audio_manager.h b/src/audio_core/audio_manager.h
index 8cbd95e22..abf077de4 100644
--- a/src/audio_core/audio_manager.h
+++ b/src/audio_core/audio_manager.h
@@ -10,22 +10,11 @@
#include <thread>
#include "audio_core/audio_event.h"
-#include "core/hle/service/audio/errors.h"
-namespace Core {
-class System;
-}
+union Result;
namespace AudioCore {
-namespace AudioOut {
-class Manager;
-}
-
-namespace AudioIn {
-class Manager;
-}
-
/**
* The AudioManager's main purpose is to wait for buffer events for the audio in and out managers,
* and call an associated callback to release buffers.
@@ -43,7 +32,7 @@ class AudioManager {
using BufferEventFunc = std::function<void()>;
public:
- explicit AudioManager(Core::System& system);
+ explicit AudioManager();
/**
* Shutdown the audio manager.
@@ -80,10 +69,6 @@ private:
*/
void ThreadFunc();
- /// Core system
- Core::System& system;
- /// Have sessions started palying?
- bool sessions_started{};
/// Is the main thread running?
std::atomic<bool> running{};
/// Unused
diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp
index e7f918a47..4324cafd8 100644
--- a/src/audio_core/in/audio_in_system.cpp
+++ b/src/audio_core/in/audio_in_system.cpp
@@ -23,7 +23,7 @@ System::~System() {
void System::Finalize() {
Stop();
session->Finalize();
- buffer_event->GetWritableEvent().Signal();
+ buffer_event->Signal();
}
void System::StartSession() {
@@ -56,7 +56,7 @@ Result System::IsConfigValid(const std::string_view device_name,
return ResultSuccess;
}
-Result System::Initialize(std::string& device_name, const AudioInParameter& in_params,
+Result System::Initialize(std::string device_name, const AudioInParameter& in_params,
const u32 handle_, const u64 applet_resource_user_id_) {
auto result{IsConfigValid(device_name, in_params)};
if (result.IsError()) {
@@ -142,7 +142,7 @@ void System::ReleaseBuffers() {
if (signal) {
// Signal if any buffer was released, or if none are registered, we need more.
- buffer_event->GetWritableEvent().Signal();
+ buffer_event->Signal();
}
}
@@ -159,7 +159,7 @@ bool System::FlushAudioInBuffers() {
buffers.FlushBuffers(buffers_released);
if (buffers_released > 0) {
- buffer_event->GetWritableEvent().Signal();
+ buffer_event->Signal();
}
return true;
}
diff --git a/src/audio_core/in/audio_in_system.h b/src/audio_core/in/audio_in_system.h
index b9dc0e60f..1c5154638 100644
--- a/src/audio_core/in/audio_in_system.h
+++ b/src/audio_core/in/audio_in_system.h
@@ -97,7 +97,7 @@ public:
* @param applet_resource_user_id - Unused.
* @return Result code.
*/
- Result Initialize(std::string& device_name, const AudioInParameter& in_params, u32 handle,
+ Result Initialize(std::string device_name, const AudioInParameter& in_params, u32 handle,
u64 applet_resource_user_id);
/**
diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp
index 8b907590a..a66208ed9 100644
--- a/src/audio_core/out/audio_out_system.cpp
+++ b/src/audio_core/out/audio_out_system.cpp
@@ -24,7 +24,7 @@ System::~System() {
void System::Finalize() {
Stop();
session->Finalize();
- buffer_event->GetWritableEvent().Signal();
+ buffer_event->Signal();
}
std::string_view System::GetDefaultOutputDeviceName() const {
@@ -49,8 +49,8 @@ Result System::IsConfigValid(std::string_view device_name,
return Service::Audio::ERR_INVALID_CHANNEL_COUNT;
}
-Result System::Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle_,
- u64& applet_resource_user_id_) {
+Result System::Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle_,
+ u64 applet_resource_user_id_) {
auto result = IsConfigValid(device_name, in_params);
if (result.IsError()) {
return result;
@@ -141,7 +141,7 @@ void System::ReleaseBuffers() {
bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)};
if (signal) {
// Signal if any buffer was released, or if none are registered, we need more.
- buffer_event->GetWritableEvent().Signal();
+ buffer_event->Signal();
}
}
@@ -158,7 +158,7 @@ bool System::FlushAudioOutBuffers() {
buffers.FlushBuffers(buffers_released);
if (buffers_released > 0) {
- buffer_event->GetWritableEvent().Signal();
+ buffer_event->Signal();
}
return true;
}
diff --git a/src/audio_core/out/audio_out_system.h b/src/audio_core/out/audio_out_system.h
index 0817b2f37..b95cb91be 100644
--- a/src/audio_core/out/audio_out_system.h
+++ b/src/audio_core/out/audio_out_system.h
@@ -88,8 +88,8 @@ public:
* @param applet_resource_user_id - Unused.
* @return Result code.
*/
- Result Initialize(std::string& device_name, const AudioOutParameter& in_params, u32 handle,
- u64& applet_resource_user_id);
+ Result Initialize(std::string device_name, const AudioOutParameter& in_params, u32 handle,
+ u64 applet_resource_user_id);
/**
* Start this system.
diff --git a/src/audio_core/renderer/adsp/audio_renderer.cpp b/src/audio_core/renderer/adsp/audio_renderer.cpp
index bafe4822a..d982ef630 100644
--- a/src/audio_core/renderer/adsp/audio_renderer.cpp
+++ b/src/audio_core/renderer/adsp/audio_renderer.cpp
@@ -47,7 +47,7 @@ RenderMessage AudioRenderer_Mailbox::ADSPWaitMessage() {
return msg;
}
-CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const s32 session_id) {
+CommandBuffer& AudioRenderer_Mailbox::GetCommandBuffer(const u32 session_id) {
return command_buffers[session_id];
}
@@ -132,7 +132,7 @@ void AudioRenderer::CreateSinkStreams() {
}
void AudioRenderer::ThreadFunc() {
- constexpr char name[]{"yuzu:AudioRenderer"};
+ constexpr char name[]{"AudioRenderer"};
MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name);
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
diff --git a/src/audio_core/renderer/adsp/audio_renderer.h b/src/audio_core/renderer/adsp/audio_renderer.h
index 02e923c84..151f38c1b 100644
--- a/src/audio_core/renderer/adsp/audio_renderer.h
+++ b/src/audio_core/renderer/adsp/audio_renderer.h
@@ -83,7 +83,7 @@ public:
* @param session_id - The session id to get (0 or 1).
* @return The command buffer.
*/
- CommandBuffer& GetCommandBuffer(s32 session_id);
+ CommandBuffer& GetCommandBuffer(u32 session_id);
/**
* Set the command buffer with the given session id (0 or 1).
diff --git a/src/audio_core/renderer/behavior/info_updater.cpp b/src/audio_core/renderer/behavior/info_updater.cpp
index c0a307b89..574cf0982 100644
--- a/src/audio_core/renderer/behavior/info_updater.cpp
+++ b/src/audio_core/renderer/behavior/info_updater.cpp
@@ -91,7 +91,7 @@ Result InfoUpdater::UpdateVoices(VoiceContext& voice_context,
voice_info.Initialize();
for (u32 channel = 0; channel < in_param.channel_count; channel++) {
- std::memset(voice_states[channel], 0, sizeof(VoiceState));
+ *voice_states[channel] = {};
}
}
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp
index 1baae74fd..edb30ce72 100644
--- a/src/audio_core/renderer/command/effect/biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp
@@ -94,7 +94,7 @@ void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor
void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) {
auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)};
if (needs_init) {
- std::memset(state_, 0, sizeof(VoiceState::BiquadFilterState));
+ *state_ = {};
}
auto input_buffer{
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
index b3c3ba4ba..48a7cba8a 100644
--- a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
@@ -30,7 +30,7 @@ void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& proc
for (u32 i = 0; i < filter_tap_count; i++) {
auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])};
if (needs_init[i]) {
- std::memset(state, 0, sizeof(VoiceState::BiquadFilterState));
+ *state = {};
}
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp
index 7a217969e..4fac30c7c 100644
--- a/src/audio_core/renderer/system.cpp
+++ b/src/audio_core/renderer/system.cpp
@@ -98,9 +98,8 @@ System::System(Core::System& core_, Kernel::KEvent* adsp_rendered_event_)
: core{core_}, adsp{core.AudioCore().GetADSP()}, adsp_rendered_event{adsp_rendered_event_} {}
Result System::Initialize(const AudioRendererParameterInternal& params,
- Kernel::KTransferMemory* transfer_memory, const u64 transfer_memory_size,
- const u32 process_handle_, const u64 applet_resource_user_id_,
- const s32 session_id_) {
+ Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
+ u32 process_handle_, u64 applet_resource_user_id_, s32 session_id_) {
if (!CheckValidRevision(params.revision)) {
return Service::Audio::ERR_INVALID_REVISION;
}
@@ -354,6 +353,8 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
render_time_limit_percent = 100;
drop_voice = params.voice_drop_enabled && params.execution_mode == ExecutionMode::Auto;
+ drop_voice_param = 1.0f;
+ num_voices_dropped = 0;
allocator.Align(0x40);
command_workbuffer_size = allocator.GetRemainingSize();
@@ -534,7 +535,7 @@ Result System::Update(std::span<const u8> input, std::span<u8> performance, std:
return result;
}
- adsp_rendered_event->GetWritableEvent().Clear();
+ adsp_rendered_event->Clear();
num_times_updated++;
const auto end_time{core.CoreTiming().GetClockTicks()};
@@ -547,7 +548,7 @@ u32 System::GetRenderingTimeLimit() const {
return render_time_limit_percent;
}
-void System::SetRenderingTimeLimit(const u32 limit) {
+void System::SetRenderingTimeLimit(u32 limit) {
render_time_limit_percent = limit;
}
@@ -625,7 +626,7 @@ void System::SendCommandToDsp() {
reset_command_buffers = false;
command_buffer_size = command_size;
if (remaining_command_count == 0) {
- adsp_rendered_event->GetWritableEvent().Signal();
+ adsp_rendered_event->Signal();
}
} else {
adsp.ClearRemainCount(session_id);
@@ -635,7 +636,7 @@ void System::SendCommandToDsp() {
}
u64 System::GenerateCommand(std::span<u8> in_command_buffer,
- [[maybe_unused]] const u64 command_buffer_size_) {
+ [[maybe_unused]] u64 command_buffer_size_) {
PoolMapper::ClearUseState(memory_pool_workbuffer, memory_pool_count);
const auto start_time{core.CoreTiming().GetClockTicks()};
@@ -693,7 +694,8 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
voice_context.SortInfo();
- const auto start_estimated_time{command_buffer.estimated_process_time};
+ const auto start_estimated_time{drop_voice_param *
+ static_cast<f32>(command_buffer.estimated_process_time)};
command_generator.GenerateVoiceCommands();
command_generator.GenerateSubMixCommands();
@@ -712,11 +714,16 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported();
time_limit_percent = 70.0f;
}
+
+ const auto end_estimated_time{drop_voice_param *
+ static_cast<f32>(command_buffer.estimated_process_time)};
+ const auto estimated_time{start_estimated_time - end_estimated_time};
+
const auto time_limit{static_cast<u32>(
- static_cast<f32>(start_estimated_time - command_buffer.estimated_process_time) +
- (((time_limit_percent / 100.0f) * 2'880'000.0) *
- (static_cast<f32>(render_time_limit_percent) / 100.0f)))};
- num_voices_dropped = DropVoices(command_buffer, start_estimated_time, time_limit);
+ estimated_time + (((time_limit_percent / 100.0f) * 2'880'000.0) *
+ (static_cast<f32>(render_time_limit_percent) / 100.0f)))};
+ num_voices_dropped =
+ DropVoices(command_buffer, static_cast<u32>(start_estimated_time), time_limit);
}
command_list_header->buffer_size = command_buffer.size;
@@ -737,24 +744,33 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
return command_buffer.size;
}
-u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_process_time,
- const u32 time_limit) {
+f32 System::GetVoiceDropParameter() const {
+ return drop_voice_param;
+}
+
+void System::SetVoiceDropParameter(f32 voice_drop_) {
+ drop_voice_param = voice_drop_;
+}
+
+u32 System::DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit) {
u32 i{0};
auto command_list{command_buffer.command_list.data() + sizeof(CommandListHeader)};
- ICommand* cmd{};
+ ICommand* cmd{nullptr};
- for (; i < command_buffer.count; i++) {
+ // Find a first valid voice to drop
+ while (i < command_buffer.count) {
cmd = reinterpret_cast<ICommand*>(command_list);
- if (cmd->type != CommandId::Performance &&
- cmd->type != CommandId::DataSourcePcmInt16Version1 &&
- cmd->type != CommandId::DataSourcePcmInt16Version2 &&
- cmd->type != CommandId::DataSourcePcmFloatVersion1 &&
- cmd->type != CommandId::DataSourcePcmFloatVersion2 &&
- cmd->type != CommandId::DataSourceAdpcmVersion1 &&
- cmd->type != CommandId::DataSourceAdpcmVersion2) {
+ if (cmd->type == CommandId::Performance ||
+ cmd->type == CommandId::DataSourcePcmInt16Version1 ||
+ cmd->type == CommandId::DataSourcePcmInt16Version2 ||
+ cmd->type == CommandId::DataSourcePcmFloatVersion1 ||
+ cmd->type == CommandId::DataSourcePcmFloatVersion2 ||
+ cmd->type == CommandId::DataSourceAdpcmVersion1 ||
+ cmd->type == CommandId::DataSourceAdpcmVersion2) {
break;
}
command_list += cmd->size;
+ i++;
}
if (cmd == nullptr || command_buffer.count == 0 || i >= command_buffer.count) {
@@ -767,6 +783,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
const auto node_id_type{cmd->node_id >> 28};
const auto node_id_base{cmd->node_id & 0xFFF};
+ // If the new estimated process time falls below the limit, we're done dropping.
if (estimated_process_time <= time_limit) {
break;
}
@@ -775,6 +792,7 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
break;
}
+ // Don't drop voices marked with the highest priority.
auto& voice_info{voice_context.GetInfo(node_id_base)};
if (voice_info.priority == HighestVoicePriority) {
break;
@@ -783,18 +801,23 @@ u32 System::DropVoices(CommandBuffer& command_buffer, const u32 estimated_proces
voices_dropped++;
voice_info.voice_dropped = true;
- if (i < command_buffer.count) {
- while (cmd->node_id == node_id) {
- if (cmd->type == CommandId::DepopPrepare) {
- cmd->enabled = true;
- } else if (cmd->type == CommandId::Performance || !cmd->enabled) {
- cmd->enabled = false;
- }
- i++;
- command_list += cmd->size;
- cmd = reinterpret_cast<ICommand*>(command_list);
+ // First iteration should drop the voice, and then iterate through all of the commands tied
+ // to the voice. We don't need reverb on a voice which we've just removed, for example.
+ // Depops can't be removed otherwise we'll introduce audio popping, and we don't
+ // remove perf commands. Lower the estimated time for each command dropped.
+ while (i < command_buffer.count && cmd->node_id == node_id) {
+ if (cmd->type == CommandId::DepopPrepare) {
+ cmd->enabled = true;
+ } else if (cmd->enabled && cmd->type != CommandId::Performance) {
+ cmd->enabled = false;
+ estimated_process_time -= static_cast<u32>(
+ drop_voice_param * static_cast<f32>(cmd->estimated_process_time));
}
+ command_list += cmd->size;
+ cmd = reinterpret_cast<ICommand*>(command_list);
+ i++;
}
+ i++;
}
return voices_dropped;
}
diff --git a/src/audio_core/renderer/system.h b/src/audio_core/renderer/system.h
index bcbe65b07..429196e41 100644
--- a/src/audio_core/renderer/system.h
+++ b/src/audio_core/renderer/system.h
@@ -196,6 +196,20 @@ public:
*/
u32 DropVoices(CommandBuffer& command_buffer, u32 estimated_process_time, u32 time_limit);
+ /**
+ * Get the current voice drop parameter.
+ *
+ * @return The current voice drop.
+ */
+ f32 GetVoiceDropParameter() const;
+
+ /**
+ * Set the voice drop parameter.
+ *
+ * @param The new voice drop.
+ */
+ void SetVoiceDropParameter(f32 voice_drop);
+
private:
/// Core system
Core::System& core;
@@ -301,6 +315,8 @@ private:
u32 num_voices_dropped{};
/// Tick that rendering started
u64 render_start_tick{};
+ /// Parameter to control the threshold for dropping voices if the audio graph gets too large
+ f32 drop_voice_param{1.0f};
};
} // namespace AudioRenderer
diff --git a/src/audio_core/renderer/system_manager.cpp b/src/audio_core/renderer/system_manager.cpp
index 9c1331e19..f66b2b890 100644
--- a/src/audio_core/renderer/system_manager.cpp
+++ b/src/audio_core/renderer/system_manager.cpp
@@ -94,7 +94,7 @@ bool SystemManager::Remove(System& system_) {
}
void SystemManager::ThreadFunc() {
- constexpr char name[]{"yuzu:AudioRenderSystemManager"};
+ constexpr char name[]{"AudioRenderSystemManager"};
MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name);
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp
index eafb51b01..a501a677d 100644
--- a/src/audio_core/renderer/voice/voice_context.cpp
+++ b/src/audio_core/renderer/voice/voice_context.cpp
@@ -74,8 +74,8 @@ void VoiceContext::SortInfo() {
}
std::ranges::sort(sorted_voice_info, [](const VoiceInfo* a, const VoiceInfo* b) {
- return a->priority != b->priority ? a->priority < b->priority
- : a->sort_order < b->sort_order;
+ return a->priority != b->priority ? a->priority > b->priority
+ : a->sort_order > b->sort_order;
});
}
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index 36b115ad6..32c1b1cb3 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -66,10 +66,10 @@ public:
const auto latency_error = cubeb_get_min_latency(ctx, &params, &minimum_latency);
if (latency_error != CUBEB_OK) {
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
- minimum_latency = 256U;
+ minimum_latency = TargetSampleCount * 2;
}
- minimum_latency = std::max(minimum_latency, 256u);
+ minimum_latency = std::max(minimum_latency, TargetSampleCount * 2);
LOG_INFO(Service_Audio,
"Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) "
@@ -326,4 +326,31 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
return device_list;
}
+u32 GetCubebLatency() {
+ cubeb* ctx;
+
+ if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
+ LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
+ // Return a large latency so we choose SDL instead.
+ return 10000u;
+ }
+
+ cubeb_stream_params params{};
+ params.rate = TargetSampleRate;
+ params.channels = 2;
+ params.format = CUBEB_SAMPLE_S16LE;
+ params.prefs = CUBEB_STREAM_PREF_NONE;
+ params.layout = CUBEB_LAYOUT_STEREO;
+
+ u32 latency{0};
+ const auto latency_error = cubeb_get_min_latency(ctx, &params, &latency);
+ if (latency_error != CUBEB_OK) {
+ LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error);
+ latency = TargetSampleCount * 2;
+ }
+ latency = std::max(latency, TargetSampleCount * 2);
+ cubeb_destroy(ctx);
+ return latency;
+}
+
} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h
index 4b0cb160d..3302cb98d 100644
--- a/src/audio_core/sink/cubeb_sink.h
+++ b/src/audio_core/sink/cubeb_sink.h
@@ -96,4 +96,11 @@ private:
*/
std::vector<std::string> ListCubebSinkDevices(bool capture);
+/**
+ * Get the reported latency for this sink.
+ *
+ * @return Minimum latency for this sink.
+ */
+u32 GetCubebLatency();
+
} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp
index 1bd001b94..c138dc628 100644
--- a/src/audio_core/sink/sdl2_sink.cpp
+++ b/src/audio_core/sink/sdl2_sink.cpp
@@ -47,11 +47,7 @@ public:
spec.freq = TargetSampleRate;
spec.channels = static_cast<u8>(device_channels);
spec.format = AUDIO_S16SYS;
- if (type == StreamType::Render) {
- spec.samples = TargetSampleCount;
- } else {
- spec.samples = 1024;
- }
+ spec.samples = TargetSampleCount * 2;
spec.callback = &SDLSinkStream::DataCallback;
spec.userdata = this;
@@ -234,10 +230,16 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) {
const int device_count = SDL_GetNumAudioDevices(capture);
for (int i = 0; i < device_count; ++i) {
- device_list.emplace_back(SDL_GetAudioDeviceName(i, 0));
+ if (const char* name = SDL_GetAudioDeviceName(i, capture)) {
+ device_list.emplace_back(name);
+ }
}
return device_list;
}
+u32 GetSDLLatency() {
+ return TargetSampleCount * 2;
+}
+
} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h
index f01eddc1b..27ed1ab94 100644
--- a/src/audio_core/sink/sdl2_sink.h
+++ b/src/audio_core/sink/sdl2_sink.h
@@ -87,4 +87,11 @@ private:
*/
std::vector<std::string> ListSDLSinkDevices(bool capture);
+/**
+ * Get the reported latency for this sink.
+ *
+ * @return Minimum latency for this sink.
+ */
+u32 GetSDLLatency();
+
} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp
index 67bdab779..39ea6d91b 100644
--- a/src/audio_core/sink/sink_details.cpp
+++ b/src/audio_core/sink/sink_details.cpp
@@ -21,58 +21,80 @@ namespace {
struct SinkDetails {
using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
using ListDevicesFn = std::vector<std::string> (*)(bool);
+ using LatencyFn = u32 (*)();
/// Name for this sink.
- const char* id;
+ std::string_view id;
/// A method to call to construct an instance of this type of sink.
FactoryFn factory;
/// A method to call to list available devices.
ListDevicesFn list_devices;
+ /// Method to get the latency of this backend.
+ LatencyFn latency;
};
// sink_details is ordered in terms of desirability, with the best choice at the top.
constexpr SinkDetails sink_details[] = {
#ifdef HAVE_CUBEB
- SinkDetails{"cubeb",
- [](std::string_view device_id) -> std::unique_ptr<Sink> {
- return std::make_unique<CubebSink>(device_id);
- },
- &ListCubebSinkDevices},
+ SinkDetails{
+ "cubeb",
+ [](std::string_view device_id) -> std::unique_ptr<Sink> {
+ return std::make_unique<CubebSink>(device_id);
+ },
+ &ListCubebSinkDevices,
+ &GetCubebLatency,
+ },
#endif
#ifdef HAVE_SDL2
- SinkDetails{"sdl2",
- [](std::string_view device_id) -> std::unique_ptr<Sink> {
- return std::make_unique<SDLSink>(device_id);
- },
- &ListSDLSinkDevices},
+ SinkDetails{
+ "sdl2",
+ [](std::string_view device_id) -> std::unique_ptr<Sink> {
+ return std::make_unique<SDLSink>(device_id);
+ },
+ &ListSDLSinkDevices,
+ &GetSDLLatency,
+ },
#endif
SinkDetails{"null",
[](std::string_view device_id) -> std::unique_ptr<Sink> {
return std::make_unique<NullSink>(device_id);
},
- [](bool capture) { return std::vector<std::string>{"null"}; }},
+ [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }},
};
const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) {
- auto iter =
- std::find_if(std::begin(sink_details), std::end(sink_details),
- [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
+ const auto find_backend{[](std::string_view id) {
+ return std::find_if(std::begin(sink_details), std::end(sink_details),
+ [&id](const auto& sink_detail) { return sink_detail.id == id; });
+ }};
- if (sink_id == "auto" || iter == std::end(sink_details)) {
- if (sink_id != "auto") {
- LOG_ERROR(Audio, "Invalid sink_id {}", sink_id);
+ auto iter = find_backend(sink_id);
+
+ if (sink_id == "auto") {
+ // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which
+ // causes audio issues, in that case go with SDL.
+#if defined(HAVE_CUBEB) && defined(HAVE_SDL2)
+ iter = find_backend("cubeb");
+ if (iter->latency() > TargetSampleCount * 3) {
+ iter = find_backend("sdl2");
}
- // Auto-select.
- // sink_details is ordered in terms of desirability, with the best choice at the front.
+#else
iter = std::begin(sink_details);
+#endif
+ LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id);
+ }
+
+ if (iter == std::end(sink_details)) {
+ LOG_ERROR(Audio, "Invalid sink_id {}", sink_id);
+ iter = find_backend("null");
}
return *iter;
}
} // Anonymous namespace
-std::vector<const char*> GetSinkIDs() {
- std::vector<const char*> sink_ids(std::size(sink_details));
+std::vector<std::string_view> GetSinkIDs() {
+ std::vector<std::string_view> sink_ids(std::size(sink_details));
std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
[](const auto& sink) { return sink.id; });
diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h
index 3ebdb1e30..e75932898 100644
--- a/src/audio_core/sink/sink_details.h
+++ b/src/audio_core/sink/sink_details.h
@@ -19,7 +19,7 @@ class Sink;
*
* @return Vector of available sink names.
*/
-std::vector<const char*> GetSinkIDs();
+std::vector<std::string_view> GetSinkIDs();
/**
* Gets the list of devices for a particular sink identified by the given ID.
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 37fe725e4..849f862b0 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -214,8 +214,13 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
// video play out without attempting to stall.
// Can hopefully remove this later with a more complete NVDEC implementation.
const auto nvdec_active{system.AudioCore().IsNVDECActive()};
- if (!nvdec_active && queued_buffers > max_queue_size) {
+
+ // Core timing cannot be paused in single-core mode, so Stall ends up being called over and over
+ // and never recovers to a normal state, so just skip attempting to sync things on single-core.
+ if (system.IsMulticore() && !nvdec_active && queued_buffers > max_queue_size) {
Stall();
+ } else if (system.IsMulticore() && queued_buffers <= max_queue_size) {
+ Unstall();
}
while (frames_written < num_frames) {
@@ -255,7 +260,7 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
std::memcpy(&last_frame[0], &output_buffer[(frames_written - 1) * frame_size],
frame_size_bytes);
- if (stalled && queued_buffers <= max_queue_size) {
+ if (system.IsMulticore() && queued_buffers <= max_queue_size) {
Unstall();
}
}