From 06b83a58a6d60063cd5fad934aa7f3a75f0ce040 Mon Sep 17 00:00:00 2001 From: Maufeat Date: Sat, 13 Dec 2025 19:44:41 +0100 Subject: [audio] correct biquad filter v2 parameters (#3142) We had the same struct for v1 and v2 - this was tested only with MP4, should output correct sounds now and boot it. Co-authored-by: MaranBr Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3142 Reviewed-by: MaranBr Reviewed-by: crueter Co-authored-by: Maufeat Co-committed-by: Maufeat --- src/audio_core/renderer/command/command_buffer.cpp | 56 +++++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) (limited to 'src/audio_core/renderer/command/command_buffer.cpp') diff --git a/src/audio_core/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp index a0a574fc64..804aba438d 100644 --- a/src/audio_core/renderer/command/command_buffer.cpp +++ b/src/audio_core/renderer/command/command_buffer.cpp @@ -21,6 +21,15 @@ namespace AudioCore::Renderer { +namespace { +constexpr f32 BiquadParameterFixedScaleQ14 = 16384.0f; // 1 << 14 + +[[nodiscard]] inline s16 ToQ14Clamped(f32 v) { + const f32 scaled = std::clamp(v * BiquadParameterFixedScaleQ14, -32768.0f, 32767.0f); + return static_cast(scaled); +} +} // namespace + template T& CommandBuffer::GenerateStart(const s32 node_id) { if (size + sizeof(T) >= command_list.size_bytes()) { @@ -259,18 +268,42 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas const bool use_float_processing) { auto& cmd{GenerateStart(node_id)}; - const auto& parameter{ - *reinterpret_cast(effect_info.GetParameter())}; const auto state{reinterpret_cast( effect_info.GetStateBuffer() + channel * sizeof(VoiceState::BiquadFilterState))}; - cmd.input = buffer_offset + parameter.inputs[channel]; - cmd.output = buffer_offset + parameter.outputs[channel]; + if (behavior->IsEffectInfoVersion2Supported()) { + const auto& p{ + *reinterpret_cast(effect_info.GetParameter())}; - cmd.biquad.b = parameter.b; - cmd.biquad.a = parameter.a; + if (!IsChannelCountValid(p.channel_count) || channel < 0 || channel >= p.channel_count) { + return; + } + + cmd.input = buffer_offset + p.inputs[channel]; + cmd.output = buffer_offset + p.outputs[channel]; + + // Convert float coefficients to Q2.14 fixed-point as expected by the legacy DSP path. + cmd.biquad.b[0] = ToQ14Clamped(p.b[0]); + cmd.biquad.b[1] = ToQ14Clamped(p.b[1]); + cmd.biquad.b[2] = ToQ14Clamped(p.b[2]); + cmd.biquad.a[0] = ToQ14Clamped(p.a[0]); + cmd.biquad.a[1] = ToQ14Clamped(p.a[1]); + } else { + const auto& p{ + *reinterpret_cast(effect_info.GetParameter())}; - // Effects use legacy fixed-point format + if (!IsChannelCountValid(p.channel_count) || channel < 0 || channel >= p.channel_count) { + return; + } + + cmd.input = buffer_offset + p.inputs[channel]; + cmd.output = buffer_offset + p.outputs[channel]; + + cmd.biquad.b = p.b; + cmd.biquad.a = p.a; + } + + // Effects always use the fixed-point coefficient path on the DSP. cmd.use_float_coefficients = false; cmd.state = memory_pool->Translate(CpuAddr(state), @@ -595,6 +628,15 @@ void CommandBuffer::GenerateCopyMixBufferCommand(const s32 node_id, EffectInfoBa const s16 buffer_offset, const s8 channel) { auto& cmd{GenerateStart(node_id)}; + if (behavior->IsEffectInfoVersion2Supported()) { + const auto& parameter_v2{ + *reinterpret_cast(effect_info.GetParameter())}; + cmd.input_index = buffer_offset + parameter_v2.inputs[channel]; + cmd.output_index = buffer_offset + parameter_v2.outputs[channel]; + GenerateEnd(cmd); + return; + } + const auto& parameter{ *reinterpret_cast(effect_info.GetParameter())}; cmd.input_index = buffer_offset + parameter.inputs[channel]; -- cgit v1.2.3