[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:
    a2c0035013

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.
This commit is contained in:
JPikachu 2025-07-22 15:01:02 +01:00 committed by crueter
parent 1f34d836b4
commit 09deed5048
10 changed files with 167 additions and 11 deletions

View file

@ -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 =

View file

@ -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

View file

@ -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

View file

@ -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<const SplitterDestinationData::InParameter*>(input + offset)};
const auto* data_header =
reinterpret_cast<const SplitterDestinationData::InParameter*>(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<const SplitterDestinationInParameter*>(
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<SplitterDestinationData> SplitterContext::GetDestinations() {
return std::span<SplitterDestinationData>{splitter_destinations,
static_cast<size_t>(destinations_count)};
}
} // namespace AudioCore::Renderer

View file

@ -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<SplitterDestinationData> GetDestinations();
private:
/**
* Setup the context.

View file

@ -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

View file

@ -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

View file

@ -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<u8> 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<u8> 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()) {

View file

@ -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<Kernel::KEvent> 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<Kernel::KEvent> 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<bool> out_enabled) {
*out_enabled = false;
LOG_DEBUG(Service_Audio, "(STUBBED) IsAudioDeviceOutputVolumeAutoTuneEnabled called. returning false");
R_SUCCEED();
}
} // namespace Service::Audio

View file

@ -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<AudioDevice::AudioDeviceName, BufferAttr_HipcMapAlias> out_names,
Out<s32> out_count);
Result AcquireAudioInputDeviceNotification(OutCopyHandle<Kernel::KEvent> out_event_handle, u64 device_id);
Result ReleaseAudioInputDeviceNotification(u64 device_id);
Result AcquireAudioOutputDeviceNotification(OutCopyHandle<Kernel::KEvent> out_event_handle, u64 device_id);
Result ReleaseAudioOutputDeviceNotification(u64 device_id);
Result SetAudioDeviceOutputVolumeAutoTuneEnabled(bool enabled);
Result IsAudioDeviceOutputVolumeAutoTuneEnabled(Out<bool> out_enabled);
KernelHelpers::ServiceContext service_context;
std::unique_ptr<AudioCore::Renderer::AudioDevice> impl;