[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:
parent
1f34d836b4
commit
09deed5048
10 changed files with 167 additions and 11 deletions
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
22
src/audio_core/renderer/splitter/parameters.h
Normal file
22
src/audio_core/renderer/splitter/parameters.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue