From 3f1eff1d34bf8e4a651512d765719f240b2f19ee Mon Sep 17 00:00:00 2001 From: Collecting Date: Wed, 14 Jan 2026 02:41:12 +0000 Subject: [PATCH 1/6] audio/am: Improve audio effect versioning Signed-off-by: Collecting --- src/audio_core/renderer/effect/biquad_filter.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/audio_core/renderer/effect/biquad_filter.h b/src/audio_core/renderer/effect/biquad_filter.h index f77ef9266..6da77693e 100644 --- a/src/audio_core/renderer/effect/biquad_filter.h +++ b/src/audio_core/renderer/effect/biquad_filter.h @@ -35,8 +35,7 @@ public: /* 0x25 */ ParameterState state; /* 0x26 */ u16 reserved; }; - static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), - "BiquadFilterInfo::ParameterVersion2 has the wrong size!"); + static_assert(sizeof(ParameterVersion2) <= 0x28, "BiquadFilterInfo::ParameterVersion2 has the wrong size!"); /** * Update the info with new parameters, version 1. From b10dcde92bff66a9d7f2b0b5b34d23692e03bb55 Mon Sep 17 00:00:00 2001 From: Collecting Date: Wed, 14 Jan 2026 02:42:44 +0000 Subject: [PATCH 2/6] audio/am: Improve audio effect versioning Signed-off-by: Collecting --- .../renderer/effect/biquad_filter.cpp | 32 ++----------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/src/audio_core/renderer/effect/biquad_filter.cpp b/src/audio_core/renderer/effect/biquad_filter.cpp index 6f06eb482..9812c1e5d 100644 --- a/src/audio_core/renderer/effect/biquad_filter.cpp +++ b/src/audio_core/renderer/effect/biquad_filter.cpp @@ -10,12 +10,10 @@ void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { auto in_specific{reinterpret_cast(in_params.specific.data())}; auto params{reinterpret_cast(parameter.data())}; - std::memcpy(params, in_specific, sizeof(ParameterVersion1)); mix_id = in_params.mix_id; process_order = in_params.process_order; enabled = in_params.enabled; - error_info.error_code = ResultSuccess; error_info.address = CpuAddr(0); } @@ -24,12 +22,10 @@ void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { auto in_specific{reinterpret_cast(in_params.specific.data())}; auto params{reinterpret_cast(parameter.data())}; - std::memcpy(params, in_specific, sizeof(ParameterVersion2)); mix_id = in_params.mix_id; process_order = in_params.process_order; enabled = in_params.enabled; - error_info.error_code = ResultSuccess; error_info.address = CpuAddr(0); } @@ -40,34 +36,10 @@ void BiquadFilterInfo::UpdateForCommandGeneration() { } else { usage_state = UsageState::Disabled; } - - // Determine which version structure is being used - // Version 1: state at offset 0x17, structure size ~24 bytes - // Version 2: state at offset 0x25, structure size ~40 bytes - auto params_v1{reinterpret_cast(parameter.data())}; - auto params_v2{reinterpret_cast(parameter.data())}; - - // Check which state location contains a valid ParameterState value (0-2) - // Valid states: Initialized (0), Updating (1), Updated (2) - const auto state_v1_raw = *reinterpret_cast(¶ms_v1->state); - const auto state_v2_raw = *reinterpret_cast(¶ms_v2->state); - - if (state_v1_raw <= 2) { - // Version 1 location has valid state, update there - params_v1->state = ParameterState::Updated; - } else if (state_v2_raw <= 2) { - // Version 2 location has valid state, update there - params_v2->state = ParameterState::Updated; - } else { - // Neither looks valid, update both (one will be wrong but command generator handles it) - params_v1->state = ParameterState::Updated; - params_v2->state = ParameterState::Updated; - } + // State update is moved to Generator to handle version-specific offsets. } void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {} - -void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, - EffectResultState& dsp_state) {} +void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} } // namespace AudioCore::Renderer From b7d8bf37512deeb43c7e1ed7371de6ddf0a11833 Mon Sep 17 00:00:00 2001 From: Collecting Date: Wed, 14 Jan 2026 02:44:28 +0000 Subject: [PATCH 3/6] audio/am: Improve audio effect versioning Signed-off-by: Collecting --- .../renderer/command/command_buffer.cpp | 66 +++++++------------ 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/src/audio_core/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp index 026832c57..8908b7f34 100644 --- a/src/audio_core/renderer/command/command_buffer.cpp +++ b/src/audio_core/renderer/command/command_buffer.cpp @@ -257,50 +257,32 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas const bool needs_init, const bool use_float_processing) { auto& cmd{GenerateStart(node_id)}; + const u8* raw = reinterpret_cast(effect_info.GetParameter()); + + if (raw[0x16] <= 6 && raw[0x17] <= 2) { + const auto& parameter = *reinterpret_cast(effect_info.GetParameter()); + cmd.input = buffer_offset + parameter.inputs[channel]; + cmd.output = buffer_offset + parameter.outputs[channel]; + cmd.biquad.b = parameter.b; + cmd.biquad.a = parameter.a; + } else { + const auto& parameter = *reinterpret_cast(effect_info.GetParameter()); + cmd.input = buffer_offset + parameter.inputs[channel]; + cmd.output = buffer_offset + parameter.outputs[channel]; + + constexpr f32 scale = 16384.0f; + cmd.biquad.b[0] = static_cast(std::clamp(parameter.b[0] * scale, -32768.0f, 32767.0f)); + cmd.biquad.b[1] = static_cast(std::clamp(parameter.b[1] * scale, -32768.0f, 32767.0f)); + cmd.biquad.b[2] = static_cast(std::clamp(parameter.b[2] * scale, -32768.0f, 32767.0f)); + cmd.biquad.a[0] = static_cast(std::clamp(parameter.a[0] * scale, -32768.0f, 32767.0f)); + cmd.biquad.a[1] = static_cast(std::clamp(parameter.a[1] * scale, -32768.0f, 32767.0f)); + } const auto state{reinterpret_cast( effect_info.GetStateBuffer() + channel * sizeof(VoiceState::BiquadFilterState))}; - // Check which parameter version is being used - if (behavior->IsEffectInfoVersion2Supported()) { - const auto& parameter{ - *reinterpret_cast(effect_info.GetParameter())}; - - cmd.input = buffer_offset + parameter.inputs[channel]; - cmd.output = buffer_offset + parameter.outputs[channel]; - - // Convert float coefficients to fixed-point Q2.14 format (multiply by 16384) - constexpr f32 fixed_point_scale = 16384.0f; - cmd.biquad.b[0] = static_cast( - std::clamp(parameter.b[0] * fixed_point_scale, -32768.0f, 32767.0f)); - cmd.biquad.b[1] = static_cast( - std::clamp(parameter.b[1] * fixed_point_scale, -32768.0f, 32767.0f)); - cmd.biquad.b[2] = static_cast( - std::clamp(parameter.b[2] * fixed_point_scale, -32768.0f, 32767.0f)); - cmd.biquad.a[0] = static_cast( - std::clamp(parameter.a[0] * fixed_point_scale, -32768.0f, 32767.0f)); - cmd.biquad.a[1] = static_cast( - std::clamp(parameter.a[1] * fixed_point_scale, -32768.0f, 32767.0f)); - - // Effects use legacy fixed-point format - cmd.use_float_coefficients = false; - } else { - const auto& parameter{ - *reinterpret_cast(effect_info.GetParameter())}; - - cmd.input = buffer_offset + parameter.inputs[channel]; - cmd.output = buffer_offset + parameter.outputs[channel]; - - cmd.biquad.b = parameter.b; - cmd.biquad.a = parameter.a; - - // Effects use legacy fixed-point format - cmd.use_float_coefficients = false; - } - - cmd.state = memory_pool->Translate(CpuAddr(state), - MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState)); - + cmd.state = memory_pool->Translate(CpuAddr(state), MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState)); + cmd.use_float_coefficients = false; cmd.needs_init = needs_init; cmd.use_float_processing = use_float_processing; @@ -749,10 +731,8 @@ void CommandBuffer::GenerateMultitapBiquadFilterCommand(const s32 node_id, Voice cmd.output = buffer_count + channel; cmd.biquads = voice_info.biquads; - // REV15+: Use native float coefficients if available if (voice_info.use_float_biquads) { - cmd.biquads_float = voice_info.biquads_float; - cmd.use_float_coefficients = true; + cmd.use_float_coefficients = false; } else { cmd.use_float_coefficients = false; } From 94c37e52e857fde8b0d35e523fae0378116e695c Mon Sep 17 00:00:00 2001 From: Collecting Date: Wed, 14 Jan 2026 02:45:30 +0000 Subject: [PATCH 4/6] audio/am: Improve audio effect versioning Signed-off-by: Collecting --- .../renderer/command/command_generator.cpp | 98 ++++++++++--------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/src/audio_core/renderer/command/command_generator.cpp b/src/audio_core/renderer/command/command_generator.cpp index 8196b2ee2..567e7a580 100644 --- a/src/audio_core/renderer/command/command_generator.cpp +++ b/src/audio_core/renderer/command/command_generator.cpp @@ -362,61 +362,63 @@ void CommandGenerator::GenerateAuxCommand(const s16 buffer_offset, EffectInfoBas void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset, EffectInfoBase& effect_info, const s32 node_id) { - EffectInfoBase::ParameterState state{}; - s8 channel_count{0}; + u8* raw_params = effect_info.GetParameter(); - if (render_context.behavior->IsEffectInfoVersion2Supported()) { - const auto* parameter = reinterpret_cast( - effect_info.GetParameter()); - if (!parameter) { - LOG_ERROR(Service_Audio, "Biquad filter parameter is null"); - return; - } - state = parameter->state; - channel_count = parameter->channel_count; - } else { - const auto* parameter = reinterpret_cast( - effect_info.GetParameter()); - if (!parameter) { - LOG_ERROR(Service_Audio, "Biquad filter parameter is null"); - return; - } - state = parameter->state; - channel_count = parameter->channel_count; - } + // Revision 12 introduced the new float-based Biquad Filter + // Revision 11 and older use the original Int16 version + const bool is_rev12 = render_context.behavior->GetProcessRevision() >= 12; - if (effect_info.IsEnabled()) { - bool needs_init{false}; + if (is_rev12) { + auto& parameter = *reinterpret_cast(raw_params); - switch (state) { - case EffectInfoBase::ParameterState::Initialized: - needs_init = true; - break; - case EffectInfoBase::ParameterState::Updating: - case EffectInfoBase::ParameterState::Updated: - if (render_context.behavior->IsBiquadFilterEffectStateClearBugFixed()) { - needs_init = false; - } else { - needs_init = state == EffectInfoBase::ParameterState::Updating; + s8 channels = parameter.channel_count; + if (channels > 6 || channels < 0) channels = 0; + + if (effect_info.IsEnabled()) { + const bool needs_init = (parameter.state != EffectInfoBase::ParameterState::Updated); + for (s8 channel = 0; channel < channels; channel++) { + command_buffer.GenerateBiquadFilterCommand( + node_id, effect_info, buffer_offset, channel, needs_init, true); + } + } else { + for (s8 channel = 0; channel < channels; channel++) { + command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset, channel); } - break; - default: - LOG_ERROR(Service_Audio, "Invalid biquad parameter state {}, treating as uninitialized", - static_cast(state)); - needs_init = true; - break; - } - - for (s8 channel = 0; channel < channel_count; channel++) { - command_buffer.GenerateBiquadFilterCommand( - node_id, effect_info, buffer_offset, channel, needs_init, - render_context.behavior->UseBiquadFilterFloatProcessing()); } + parameter.state = EffectInfoBase::ParameterState::Updated; // Offset 0x25 } else { - for (s8 channel = 0; channel < channel_count; channel++) { - command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset, - channel); + auto& parameter = *reinterpret_cast(raw_params); + + s8 channels = parameter.channel_count; + if (channels > 6 || channels < 0) channels = 0; + + if (effect_info.IsEnabled()) { + bool needs_init = false; + switch (parameter.state) { + case EffectInfoBase::ParameterState::Initialized: + needs_init = true; + break; + case EffectInfoBase::ParameterState::Updating: + case EffectInfoBase::ParameterState::Updated: + if (render_context.behavior->IsBiquadFilterEffectStateClearBugFixed()) { + needs_init = false; + } else { + needs_init = parameter.state == EffectInfoBase::ParameterState::Updating; + } + break; + default: break; + } + + for (s8 channel = 0; channel < channels; channel++) { + command_buffer.GenerateBiquadFilterCommand( + node_id, effect_info, buffer_offset, channel, needs_init, false); + } + } else { + for (s8 channel = 0; channel < channels; channel++) { + command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset, channel); + } } + parameter.state = EffectInfoBase::ParameterState::Updated; // Offset 0x17 } } From c36557be7733272874f3b923a10ed181c57bc037 Mon Sep 17 00:00:00 2001 From: Collecting Date: Wed, 14 Jan 2026 03:35:34 +0000 Subject: [PATCH 5/6] audio/am: Improve audio effect versioning Signed-off-by: Collecting --- src/audio_core/renderer/effect/effect_info_base.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/audio_core/renderer/effect/effect_info_base.h b/src/audio_core/renderer/effect/effect_info_base.h index b49503409..40a4ac2c9 100644 --- a/src/audio_core/renderer/effect/effect_info_base.h +++ b/src/audio_core/renderer/effect/effect_info_base.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -207,6 +208,8 @@ public: return state.data(); } + size_t GetParameterSize() const { return parameter.size(); } + /** * Set this effect's usage state. * From 9bccbc5a1e739b65f37f87a77c1ca0e943e6e856 Mon Sep 17 00:00:00 2001 From: Collecting Date: Wed, 14 Jan 2026 04:03:51 +0000 Subject: [PATCH 6/6] fix(rev): Mistakenly placed 12 instead of 15 Signed-off-by: Collecting --- .../renderer/command/command_generator.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/audio_core/renderer/command/command_generator.cpp b/src/audio_core/renderer/command/command_generator.cpp index 567e7a580..786472c37 100644 --- a/src/audio_core/renderer/command/command_generator.cpp +++ b/src/audio_core/renderer/command/command_generator.cpp @@ -363,14 +363,14 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset EffectInfoBase& effect_info, const s32 node_id) { u8* raw_params = effect_info.GetParameter(); + + // REV15 introduced the new native float-based Biquad Filter structure (Version 2). + // REV14 and older (including Rev 12 splitter updates) still use the Int16 version. + const bool is_rev15 = render_context.behavior->GetProcessRevision() >= 15; - // Revision 12 introduced the new float-based Biquad Filter - // Revision 11 and older use the original Int16 version - const bool is_rev12 = render_context.behavior->GetProcessRevision() >= 12; - - if (is_rev12) { + if (is_rev15) { auto& parameter = *reinterpret_cast(raw_params); - + s8 channels = parameter.channel_count; if (channels > 6 || channels < 0) channels = 0; @@ -385,10 +385,10 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset, channel); } } - parameter.state = EffectInfoBase::ParameterState::Updated; // Offset 0x25 + parameter.state = EffectInfoBase::ParameterState::Updated; } else { auto& parameter = *reinterpret_cast(raw_params); - + s8 channels = parameter.channel_count; if (channels > 6 || channels < 0) channels = 0; @@ -418,7 +418,7 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset, channel); } } - parameter.state = EffectInfoBase::ParameterState::Updated; // Offset 0x17 + parameter.state = EffectInfoBase::ParameterState::Updated; } }