aboutsummaryrefslogtreecommitdiff
path: root/src/audio_core/renderer/splitter/splitter_context.cpp
diff options
context:
space:
mode:
authorunknown <sahyno1996@gmail.com>2025-10-12 17:03:14 +0200
committercrueter <crueter@eden-emu.dev>2025-10-12 17:03:14 +0200
commita3ef2cc1838a007a2f424e88212aa6f1eb93eab3 (patch)
tree6ab37fe23b6d30a33a352deb631866720d012ecb /src/audio_core/renderer/splitter/splitter_context.cpp
parent1e1b8ad33face3f3409aec50818d2b4fda8e6a0d (diff)
[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>
Diffstat (limited to 'src/audio_core/renderer/splitter/splitter_context.cpp')
-rw-r--r--src/audio_core/renderer/splitter/splitter_context.cpp117
1 files changed, 103 insertions, 14 deletions
diff --git a/src/audio_core/renderer/splitter/splitter_context.cpp b/src/audio_core/renderer/splitter/splitter_context.cpp
index fb118e981e..1ba5daebe9 100644
--- a/src/audio_core/renderer/splitter/splitter_context.cpp
+++ b/src/audio_core/renderer/splitter/splitter_context.cpp
@@ -35,12 +35,16 @@ SplitterDestinationData& SplitterContext::GetData(const u32 index) {
void SplitterContext::Setup(std::span<SplitterInfo> splitter_infos_, const u32 splitter_info_count_,
SplitterDestinationData* splitter_destinations_,
- const u32 destination_count_, const bool splitter_bug_fixed_) {
+ const u32 destination_count_, const bool splitter_bug_fixed_,
+ const BehaviorInfo& behavior) {
splitter_infos = splitter_infos_;
info_count = splitter_info_count_;
splitter_destinations = splitter_destinations_;
destinations_count = destination_count_;
splitter_bug_fixed = splitter_bug_fixed_;
+ splitter_prev_volume_reset_supported = behavior.IsSplitterPrevVolumeResetSupported();
+ splitter_biquad_param_supported = behavior.IsBiquadFilterParameterForSplitterEnabled();
+ splitter_float_coeff_supported = behavior.IsSplitterDestinationV2bSupported();
}
bool SplitterContext::UsingSplitter() const {
@@ -84,7 +88,7 @@ bool SplitterContext::Initialize(const BehaviorInfo& behavior,
}
Setup(splitter_infos, params.splitter_infos, splitter_destinations,
- params.splitter_destinations, behavior.IsSplitterBugFixed());
+ params.splitter_destinations, behavior.IsSplitterBugFixed(), behavior);
}
return true;
}
@@ -137,19 +141,104 @@ 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)};
-
- if (data_header->magic != GetSplitterSendDataMagic()) {
- continue;
- }
-
- if (data_header->id < 0 || data_header->id > destinations_count) {
- continue;
+ // Version selection based on feature flags:
+ // - REV12: integer biquad params (Version2a)
+ // - REV15: float coeff/biquad v2b
+ // - older: no biquad fields
+ if (!splitter_biquad_param_supported) {
+ 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) {
+ continue;
+ }
+
+ auto modified_params = *data_header;
+ if (!splitter_prev_volume_reset_supported) {
+ modified_params.reset_prev_volume = false;
+ }
+ splitter_destinations[data_header->id].Update(modified_params);
+ offset += sizeof(SplitterDestinationData::InParameter);
+ } else if (!splitter_float_coeff_supported) {
+ // Version 2a: struct contains legacy fixed-point biquad filter fields (REV12+)
+ const auto* data_header_v2a =
+ reinterpret_cast<const SplitterDestinationData::InParameterVersion2a*>(input +
+ offset);
+
+ if (data_header_v2a->magic != GetSplitterSendDataMagic()) {
+ continue;
+ }
+ if (data_header_v2a->id < 0 || data_header_v2a->id > destinations_count) {
+ continue;
+ }
+
+ // Map common fields to the base format
+ SplitterDestinationData::InParameter mapped{};
+ mapped.magic = data_header_v2a->magic;
+ mapped.id = data_header_v2a->id;
+ mapped.mix_volumes = data_header_v2a->mix_volumes;
+ mapped.mix_id = data_header_v2a->mix_id;
+ mapped.in_use = data_header_v2a->in_use;
+ mapped.reset_prev_volume =
+ splitter_prev_volume_reset_supported ? data_header_v2a->reset_prev_volume : false;
+
+ auto& destination = splitter_destinations[data_header_v2a->id];
+ destination.Update(mapped);
+
+ // Convert legacy fixed-point biquad params into float representation
+ auto biquad_filters = destination.GetBiquadFilters();
+ for (size_t filter_idx = 0; filter_idx < MaxBiquadFilters; filter_idx++) {
+ const auto& legacy = data_header_v2a->biquad_filters[filter_idx];
+ auto& out = biquad_filters[filter_idx];
+ out.enabled = legacy.enabled;
+ // s16 fixed-point scale: use Q14 like voices (b and a are s16, 1.0 ~= 1<<14)
+ constexpr float scale = 1.0f / static_cast<float>(1 << 14);
+ out.numerator[0] = static_cast<float>(legacy.b[0]) * scale;
+ out.numerator[1] = static_cast<float>(legacy.b[1]) * scale;
+ out.numerator[2] = static_cast<float>(legacy.b[2]) * scale;
+ out.denominator[0] = static_cast<float>(legacy.a[0]) * scale;
+ out.denominator[1] = static_cast<float>(legacy.a[1]) * scale;
+ }
+
+ offset += static_cast<u32>(sizeof(SplitterDestinationData::InParameterVersion2a));
+ } else {
+ // Version 2b: struct contains extra biquad filter fields with float coeffs
+ const auto* data_header_v2b =
+ reinterpret_cast<const SplitterDestinationData::InParameterVersion2b*>(input +
+ offset);
+
+ if (data_header_v2b->magic != GetSplitterSendDataMagic()) {
+ continue;
+ }
+ if (data_header_v2b->id < 0 || data_header_v2b->id > destinations_count) {
+ continue;
+ }
+
+ // Map common fields to the old format
+ SplitterDestinationData::InParameter mapped{};
+ mapped.magic = data_header_v2b->magic;
+ mapped.id = data_header_v2b->id;
+ mapped.mix_volumes = data_header_v2b->mix_volumes;
+ mapped.mix_id = data_header_v2b->mix_id;
+ mapped.in_use = data_header_v2b->in_use;
+ mapped.reset_prev_volume =
+ splitter_prev_volume_reset_supported ? data_header_v2b->reset_prev_volume : false;
+
+ // Store biquad filters from V2b (REV15+)
+ auto& destination = splitter_destinations[data_header_v2b->id];
+ destination.Update(mapped);
+
+ // Copy biquad filter parameters
+ auto biquad_filters = destination.GetBiquadFilters();
+ for (size_t filter_idx = 0; filter_idx < MaxBiquadFilters; filter_idx++) {
+ biquad_filters[filter_idx] = data_header_v2b->biquad_filters[filter_idx];
+ }
+
+ offset += static_cast<u32>(sizeof(SplitterDestinationData::InParameterVersion2b));
}
-
- splitter_destinations[data_header->id].Update(*data_header);
- offset += sizeof(SplitterDestinationData::InParameter);
}
return offset;