From 49743b0987906f50bc3fa56ec3bf56aaf95cde22 Mon Sep 17 00:00:00 2001 From: JPikachu Date: Tue, 22 Jul 2025 15:01:02 +0100 Subject: [PATCH 1/2] [svc, audio] Implement REV13 audio renderer support and stub IAudioDevice commands up to 18.0.0+ Fully implements support for the AudioRenderer REV13 interface. - Adds SplitterDestinationInParameter with `reset_prev_volume` support. - Updates SplitterContext to parse REV13 destination parameters. - Handles per-destination volume ramp resets. - Verified against Ryujinx commit: https://git.bixed.xyz/Bix/Ryujinx/commit/a2c003501371463fd1f98d2e5a7602ae19c21d7c Stubs new commands in IAudioDevice introduced in 17.0.0+ and 18.0.0+: - Command 15: AcquireAudioInputDeviceNotification() - Command 16: ReleaseAudioInputDeviceNotification() - Command 17: AcquireAudioOutputDeviceNotification() - Command 18: ReleaseAudioOutputDeviceNotification() - Command 19: SetAudioDeviceOutputVolumeAutoTuneEnabled() - Command 20: IsAudioDeviceOutputVolumeAutoTuneEnabled() These were referenced directly from Ryujinx too. Should fix audio reset issues in The Legend of Zelda: Echoes of Wisdom. Note: due to minimal documentation on the audio services some guess work has been made and it may not be 100% accuracte to the switch's services. --- src/audio_core/common/feature_support.h | 7 +++ src/audio_core/renderer/audio_renderer.h | 4 ++ src/audio_core/renderer/splitter/parameters.h | 22 +++++++++ .../renderer/splitter/splitter_context.cpp | 30 ++++++++++-- .../renderer/splitter/splitter_context.h | 7 +++ .../splitter/splitter_destinations_data.cpp | 15 ++++++ .../splitter/splitter_destinations_data.h | 20 ++++++++ src/audio_core/renderer/system.cpp | 15 ++++++ src/core/hle/service/audio/audio_device.cpp | 48 ++++++++++++++++--- src/core/hle/service/audio/audio_device.h | 10 ++++ 10 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 src/audio_core/renderer/splitter/parameters.h diff --git a/src/audio_core/common/feature_support.h b/src/audio_core/common/feature_support.h index eef2a844ba..cfb48b43d8 100644 --- a/src/audio_core/common/feature_support.h +++ b/src/audio_core/common/feature_support.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -44,6 +47,8 @@ enum class SupportTags { DelayChannelMappingChange, ReverbChannelMappingChange, I3dl2ReverbChannelMappingChange, + DecodingBehaviourFlag, + BiquadFilterParameterForSplitter, // Not a real tag, just here to get the count. Size @@ -87,6 +92,8 @@ constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) { {SupportTags::DelayChannelMappingChange, 11}, {SupportTags::ReverbChannelMappingChange, 11}, {SupportTags::I3dl2ReverbChannelMappingChange, 11}, + {SupportTags::DecodingBehaviourFlag, 12}, + {SupportTags::BiquadFilterParameterForSplitter, 13}, }}; const auto& feature = diff --git a/src/audio_core/renderer/audio_renderer.h b/src/audio_core/renderer/audio_renderer.h index f16adeda78..82c5c14cdd 100644 --- a/src/audio_core/renderer/audio_renderer.h +++ b/src/audio_core/renderer/audio_renderer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -93,6 +96,7 @@ private: bool system_registered{}; /// Audio render system, main driver of audio rendering System system; + bool m_volume_ramp_reset = false; }; } // namespace Renderer diff --git a/src/audio_core/renderer/splitter/parameters.h b/src/audio_core/renderer/splitter/parameters.h new file mode 100644 index 0000000000..658d1667c5 --- /dev/null +++ b/src/audio_core/renderer/splitter/parameters.h @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace AudioCore::Renderer { + +/** + * Input struct passed to a splitter destination in REV13. + */ +struct SplitterDestinationInParameter { + /* 0x00 */ s32 magic; + /* 0x04 */ s32 id; + /* 0x08 */ bool reset_prev_volume; + /* 0x09 */ u8 reserved[11]; // Padding to align to 0x14 +}; +static_assert(sizeof(SplitterDestinationInParameter) == 0x14, + "SplitterDestinationInParameter must be 0x14 bytes."); + +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/splitter/splitter_context.cpp b/src/audio_core/renderer/splitter/splitter_context.cpp index d0f3b60c29..b5a396b52a 100644 --- a/src/audio_core/renderer/splitter/splitter_context.cpp +++ b/src/audio_core/renderer/splitter/splitter_context.cpp @@ -1,9 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "audio_core/common/audio_renderer_parameter.h" #include "audio_core/common/workbuffer_allocator.h" #include "audio_core/renderer/behavior/behavior_info.h" +#include "audio_core/renderer/splitter/parameters.h" #include "audio_core/renderer/splitter/splitter_context.h" #include "common/alignment.h" @@ -134,19 +138,30 @@ u32 SplitterContext::UpdateInfo(const u8* input, u32 offset, const u32 splitter_ u32 SplitterContext::UpdateData(const u8* input, u32 offset, const u32 count) { for (u32 i = 0; i < count; i++) { - auto data_header{ - reinterpret_cast(input + offset)}; + const auto* data_header = + reinterpret_cast(input + offset); if (data_header->magic != GetSplitterSendDataMagic()) { continue; } - if (data_header->id < 0 || data_header->id > destinations_count) { + if (data_header->id < 0 || data_header->id >= destinations_count) { continue; } - splitter_destinations[data_header->id].Update(*data_header); - offset += sizeof(SplitterDestinationData::InParameter); + auto& destination = splitter_destinations[data_header->id]; + destination.Update(*data_header); + + // REV13: Set reset flag if present in extended struct + const auto* reset_data = + reinterpret_cast( + input + offset + sizeof(SplitterDestinationData::InParameter)); + if (reset_data->reset_prev_volume) { + destination.SetResetPrevVolume(); + } + + offset += sizeof(SplitterDestinationData::InParameter) + + sizeof(SplitterDestinationInParameter); } return offset; @@ -214,4 +229,9 @@ u64 SplitterContext::CalcWorkBufferSize(const BehaviorInfo& behavior, return size; } +std::span SplitterContext::GetDestinations() { + return std::span{splitter_destinations, + static_cast(destinations_count)}; +} + } // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/splitter/splitter_context.h b/src/audio_core/renderer/splitter/splitter_context.h index 1c0b846719..91360472fd 100644 --- a/src/audio_core/renderer/splitter/splitter_context.h +++ b/src/audio_core/renderer/splitter/splitter_context.h @@ -159,6 +159,13 @@ public: static u64 CalcWorkBufferSize(const BehaviorInfo& behavior, const AudioRendererParameterInternal& params); + /** + * Get all active splitter destinations. + * + * @return Span of splitter destinations. + */ + std::span GetDestinations(); + private: /** * Setup the context. diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp index 5ec37e48e1..9869d31476 100644 --- a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -84,4 +87,16 @@ void SplitterDestinationData::SetNext(SplitterDestinationData* next_) { next = next_; } +void SplitterDestinationData::SetResetPrevVolume() { + m_reset_prev_volume = true; +} + +bool SplitterDestinationData::ShouldResetPrevVolume() const { + return m_reset_prev_volume; +} + +void SplitterDestinationData::ClearResetPrevVolume() { + m_reset_prev_volume = false; +} + } // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.h b/src/audio_core/renderer/splitter/splitter_destinations_data.h index 90edfc667c..797c22f4ce 100644 --- a/src/audio_core/renderer/splitter/splitter_destinations_data.h +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -115,6 +118,21 @@ public: */ void SetNext(SplitterDestinationData* next); + /** + * REV13: Called during UpdateData() if the struct's flag is set + */ + void SetResetPrevVolume(); + + /** + * REV13: Called during GenerateCommand() to check for a reset + */ + bool ShouldResetPrevVolume() const; + + /** + * REV13: Called after reset is applied to clear the flag + */ + void ClearResetPrevVolume(); + private: /// Id of this destination const s32 id; @@ -130,6 +148,8 @@ private: bool in_use{}; /// Does this destination need its volumes updated? bool need_update{}; + /// REV13: Flag to reset previous volume ramps + bool m_reset_prev_volume{false}; }; } // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/system.cpp b/src/audio_core/renderer/system.cpp index c30d68426c..59af6732cb 100644 --- a/src/audio_core/renderer/system.cpp +++ b/src/audio_core/renderer/system.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -688,6 +691,7 @@ u64 System::GenerateCommand(std::span in_command_buffer, sink_context, splitter_context, perf_manager}; voice_context.SortInfo(); + command_generator.GenerateVoiceCommands(); const auto start_estimated_time{drop_voice_param * @@ -697,6 +701,17 @@ u64 System::GenerateCommand(std::span in_command_buffer, command_generator.GenerateFinalMixCommands(); command_generator.GenerateSinkCommands(); + // REV13: Reset previous volumes for any splitter destinations requesting it + for (auto& destination : splitter_context.GetDestinations()) { + if (destination.ShouldResetPrevVolume()) { + for (auto& volume : destination.GetMixVolumePrev()) { + volume = 0.0f; + } + destination.ClearResetPrevVolume(); + } + } + + if (drop_voice) { f32 time_limit_percent{70.0f}; if (render_context.behavior->IsAudioRendererProcessingTimeLimit80PercentSupported()) { diff --git a/src/core/hle/service/audio/audio_device.cpp b/src/core/hle/service/audio/audio_device.cpp index 782dddc804..80b3204a2d 100644 --- a/src/core/hle/service/audio/audio_device.cpp +++ b/src/core/hle/service/audio/audio_device.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -29,12 +32,12 @@ IAudioDevice::IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u {12, D<&IAudioDevice::QueryAudioDeviceOutputEvent>, "QueryAudioDeviceOutputEvent"}, {13, D<&IAudioDevice::GetActiveAudioDeviceName>, "GetActiveAudioOutputDeviceName"}, {14, D<&IAudioDevice::ListAudioOutputDeviceName>, "ListAudioOutputDeviceName"}, - {15, nullptr, "AcquireAudioInputDeviceNotification"}, // 17.0.0+ - {16, nullptr, "ReleaseAudioInputDeviceNotification"}, // 17.0.0+ - {17, nullptr, "AcquireAudioOutputDeviceNotification"}, // 17.0.0+ - {18, nullptr, "ReleaseAudioOutputDeviceNotification"}, // 17.0.0+ - {19, nullptr, "SetAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+ - {20, nullptr, "IsAudioDeviceOutputVolumeAutoTuneEnabled"} // 18.0.0+ + {15, D<&IAudioDevice::AcquireAudioInputDeviceNotification>, "AcquireAudioInputDeviceNotification"}, // 17.0.0+ + {16, D<&IAudioDevice::ReleaseAudioInputDeviceNotification>, "ReleaseAudioInputDeviceNotification"}, // 17.0.0+ + {17, D<&IAudioDevice::AcquireAudioOutputDeviceNotification>, "AcquireAudioOutputDeviceNotification"}, // 17.0.0+ + {18, D<&IAudioDevice::ReleaseAudioOutputDeviceNotification>, "ReleaseAudioOutputDeviceNotification"}, // 17.0.0+ + {19, D<&IAudioDevice::SetAudioDeviceOutputVolumeAutoTuneEnabled>, "SetAudioDeviceOutputVolumeAutoTuneEnabled"}, // 18.0.0+ + {20, D<&IAudioDevice::IsAudioDeviceOutputVolumeAutoTuneEnabled>, "IsAudioDeviceOutputVolumeAutoTuneEnabled"} // 18.0.0+ }; RegisterHandlers(functions); @@ -166,4 +169,37 @@ Result IAudioDevice::ListAudioOutputDeviceName( R_SUCCEED(); } +Result IAudioDevice::AcquireAudioInputDeviceNotification(OutCopyHandle out_event_handle, u64 device_id) { + LOG_DEBUG(Service_Audio, "(STUBBED) AcquireAudioInputDeviceNotification called. device_id={:016X}", device_id); + *out_event_handle = event; // Reuse existing stub logic as placeholder + R_SUCCEED(); +} + +Result IAudioDevice::ReleaseAudioInputDeviceNotification(u64 device_id) { + LOG_DEBUG(Service_Audio, "(STUBBED) ReleaseAudioInputDeviceNotification called. device_id={:016X}", device_id); + R_SUCCEED(); +} + +Result IAudioDevice::AcquireAudioOutputDeviceNotification(OutCopyHandle out_event_handle, u64 device_id) { + LOG_DEBUG(Service_Audio, "(STUBBED) AcquireAudioOutputDeviceNotification called. device_id={:016X}", device_id); + *out_event_handle = event; // Reuse existing stub logic as placeholder + R_SUCCEED(); +} + +Result IAudioDevice::ReleaseAudioOutputDeviceNotification(u64 device_id) { + LOG_DEBUG(Service_Audio, "(STUBBED) ReleaseAudioOutputDeviceNotification called. device_id={:016X}", device_id); + R_SUCCEED(); +} + +Result IAudioDevice::SetAudioDeviceOutputVolumeAutoTuneEnabled(bool enabled) { + LOG_DEBUG(Service_Audio, "(STUBBED) SetAudioDeviceOutputVolumeAutoTuneEnabled called. enabled={}", enabled); + R_SUCCEED(); +} + +Result IAudioDevice::IsAudioDeviceOutputVolumeAutoTuneEnabled(Out out_enabled) { + *out_enabled = false; + LOG_DEBUG(Service_Audio, "(STUBBED) IsAudioDeviceOutputVolumeAutoTuneEnabled called. returning false"); + R_SUCCEED(); +} + } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio_device.h b/src/core/hle/service/audio/audio_device.h index 752157272c..ce74f5bfe0 100644 --- a/src/core/hle/service/audio/audio_device.h +++ b/src/core/hle/service/audio/audio_device.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -49,6 +52,13 @@ private: Result ListAudioOutputDeviceName( OutArray out_names, Out out_count); + Result AcquireAudioInputDeviceNotification(OutCopyHandle out_event_handle, u64 device_id); + Result ReleaseAudioInputDeviceNotification(u64 device_id); + Result AcquireAudioOutputDeviceNotification(OutCopyHandle out_event_handle, u64 device_id); + Result ReleaseAudioOutputDeviceNotification(u64 device_id); + Result SetAudioDeviceOutputVolumeAutoTuneEnabled(bool enabled); + Result IsAudioDeviceOutputVolumeAutoTuneEnabled(Out out_enabled); + KernelHelpers::ServiceContext service_context; std::unique_ptr impl; From a536072cad3fa69f3b74e34164cbeab011cf6e10 Mon Sep 17 00:00:00 2001 From: JPikachu Date: Tue, 22 Jul 2025 15:12:00 +0100 Subject: [PATCH 2/2] [fix] dumb headers --- src/audio_core/renderer/splitter/splitter_context.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/audio_core/renderer/splitter/splitter_context.h b/src/audio_core/renderer/splitter/splitter_context.h index 91360472fd..df116aff49 100644 --- a/src/audio_core/renderer/splitter/splitter_context.h +++ b/src/audio_core/renderer/splitter/splitter_context.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later