[audio_core/hid] Audio REV12+15 support + HID fixes (#2719)

This fixes newer updates / games.

Implements partial audio rev15, rev13, rev12 and HID issues on SDK20+ games.

Credits to LotP (Ryubing) and Zephyron (Citron) for their research and implementation.

Co-authored-by: Zephyron <zephyron@citron-emu.org>
Co-authored-by: Shinmegumi <shinmegumi@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2719
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: unknown <sahyno1996@gmail.com>
Co-committed-by: unknown <sahyno1996@gmail.com>
This commit is contained in:
unknown 2025-10-12 17:03:14 +02:00 • committed by crueter
parent 1e1b8ad33f
commit a3ef2cc183
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
27 changed files with 727 additions and 36 deletions

View file

@ -237,6 +237,13 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, VoiceInfo& vo
cmd.biquad = voice_info.biquads[biquad_index];
if (voice_info.use_float_biquads) {
cmd.biquad_float = voice_info.biquads_float[biquad_index];
cmd.use_float_coefficients = true;
} else {
cmd.use_float_coefficients = false;
}
cmd.state = memory_pool->Translate(CpuAddr(voice_state.biquad_states[biquad_index].data()),
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
@ -263,6 +270,9 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas
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));
@ -658,6 +668,13 @@ void CommandBuffer::GenerateMultitapBiquadFilterCommand(const s32 node_id, Voice
cmd.output = buffer_count + channel;
cmd.biquads = voice_info.biquads;
if (voice_info.use_float_biquads) {
cmd.biquads_float = voice_info.biquads_float;
cmd.use_float_coefficients = true;
} else {
cmd.use_float_coefficients = false;
}
cmd.states[0] =
memory_pool->Translate(CpuAddr(voice_state.biquad_states[0].data()),
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));

View file

@ -51,6 +51,40 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
state.s3 = Common::BitCast<s64>(s[3]);
}
/**
* Biquad filter float implementation with native float coefficients.
*/
void ApplyBiquadFilterFloat2(std::span<s32> output, std::span<const s32> input,
std::array<f32, 3>& b, std::array<f32, 2>& a,
VoiceState::BiquadFilterState& state, const u32 sample_count) {
constexpr f64 min{std::numeric_limits<s32>::min()};
constexpr f64 max{std::numeric_limits<s32>::max()};
std::array<f64, 3> b_double{static_cast<f64>(b[0]), static_cast<f64>(b[1]),
static_cast<f64>(b[2])};
std::array<f64, 2> a_double{static_cast<f64>(a[0]), static_cast<f64>(a[1])};
std::array<f64, 4> s{Common::BitCast<f64>(state.s0), Common::BitCast<f64>(state.s1),
Common::BitCast<f64>(state.s2), Common::BitCast<f64>(state.s3)};
for (u32 i = 0; i < sample_count; i++) {
f64 in_sample{static_cast<f64>(input[i])};
auto sample{in_sample * b_double[0] + s[0] * b_double[1] + s[1] * b_double[2] +
s[2] * a_double[0] + s[3] * a_double[1]};
output[i] = static_cast<s32>(std::clamp(sample, min, max));
s[1] = s[0];
s[0] = in_sample;
s[3] = s[2];
s[2] = sample;
}
state.s0 = Common::BitCast<s64>(s[0]);
state.s1 = Common::BitCast<s64>(s[1]);
state.s2 = Common::BitCast<s64>(s[2]);
state.s3 = Common::BitCast<s64>(s[3]);
}
/**
* Biquad filter s32 implementation.
*
@ -98,8 +132,14 @@ void BiquadFilterCommand::Process(const AudioRenderer::CommandListProcessor& pro
processor.mix_buffers.subspan(output * processor.sample_count, processor.sample_count)};
if (use_float_processing) {
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
processor.sample_count);
// REV15+: Use native float coefficients if available
if (use_float_coefficients) {
ApplyBiquadFilterFloat2(output_buffer, input_buffer, biquad_float.numerator,
biquad_float.denominator, *state_, processor.sample_count);
} else {
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
processor.sample_count);
}
} else {
ApplyBiquadFilterInt(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
processor.sample_count);

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
@ -50,12 +53,16 @@ struct BiquadFilterCommand : ICommand {
s16 output;
/// Input parameters for biquad
VoiceInfo::BiquadFilterParameter biquad;
/// Input parameters for biquad (REV15+ native float)
VoiceInfo::BiquadFilterParameter2 biquad_float;
/// Biquad state, updated each call
CpuAddr state;
/// If true, reset the state
bool needs_init;
/// If true, use float processing rather than int
bool use_float_processing;
/// If true, use native float coefficients (REV15+)
bool use_float_coefficients;
};
/**
@ -72,4 +79,18 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
std::array<s16, 3>& b, std::array<s16, 2>& a,
VoiceState::BiquadFilterState& state, const u32 sample_count);
/**
* Biquad filter float implementation with native float coefficients (SDK REV15+).
*
* @param output - Output container for filtered samples.
* @param input - Input container for samples to be filtered.
* @param b - Feedforward coefficients (float).
* @param a - Feedback coefficients (float).
* @param state - State to track previous samples.
* @param sample_count - Number of samples to process.
*/
void ApplyBiquadFilterFloat2(std::span<s32> output, std::span<const s32> input,
std::array<f32, 3>& b, std::array<f32, 2>& a,
VoiceState::BiquadFilterState& state, const u32 sample_count);
} // 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
@ -33,8 +36,14 @@ void MultiTapBiquadFilterCommand::Process(const AudioRenderer::CommandListProces
*state = {};
}
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
processor.sample_count);
// REV15+: Use native float coefficients if available
if (use_float_coefficients) {
ApplyBiquadFilterFloat2(output_buffer, input_buffer, biquads_float[i].numerator,
biquads_float[i].denominator, *state, processor.sample_count);
} else {
ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
processor.sample_count);
}
}
}

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
@ -49,12 +52,16 @@ struct MultiTapBiquadFilterCommand : ICommand {
s16 output;
/// Biquad parameters
std::array<VoiceInfo::BiquadFilterParameter, MaxBiquadFilters> biquads;
/// Biquad parameters (REV15+ native float)
std::array<VoiceInfo::BiquadFilterParameter2, MaxBiquadFilters> biquads_float;
/// Biquad states, updated each call
std::array<CpuAddr, MaxBiquadFilters> states;
/// If each biquad needs initialisation
std::array<bool, MaxBiquadFilters> needs_init;
/// Number of active biquads
u8 filter_tap_count;
/// If true, use native float coefficients (REV15+)
bool use_float_coefficients;
};
} // namespace AudioCore::Renderer