From 0acb3ded9fcbc5739b16194c0c2c2ae53278af5b Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 11 Oct 2025 22:19:03 +0200 Subject: [PATCH] add rev12 support --- src/audio_core/common/feature_support.h | 2 + .../renderer/behavior/behavior_info.cpp | 4 ++ .../renderer/behavior/behavior_info.h | 7 +++ .../renderer/splitter/splitter_context.cpp | 52 +++++++++++++++++-- .../renderer/splitter/splitter_context.h | 2 + .../splitter/splitter_destinations_data.h | 34 ++++++++++-- 6 files changed, 94 insertions(+), 7 deletions(-) diff --git a/src/audio_core/common/feature_support.h b/src/audio_core/common/feature_support.h index 59c1ff00ef..4585be45ef 100644 --- a/src/audio_core/common/feature_support.h +++ b/src/audio_core/common/feature_support.h @@ -48,6 +48,7 @@ enum class SupportTags { ReverbChannelMappingChange, I3dl2ReverbChannelMappingChange, SplitterPrevVolumeReset, + SplitterBiquadFilterParameter, SplitterDestinationV2b, VoiceInParameterV2, @@ -94,6 +95,7 @@ constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) { {SupportTags::DelayChannelMappingChange, 11}, {SupportTags::ReverbChannelMappingChange, 11}, {SupportTags::I3dl2ReverbChannelMappingChange, 11}, + {SupportTags::SplitterBiquadFilterParameter, 12}, {SupportTags::SplitterPrevVolumeReset, 13}, {SupportTags::SplitterDestinationV2b, 15}, {SupportTags::VoiceInParameterV2, 15}, diff --git a/src/audio_core/renderer/behavior/behavior_info.cpp b/src/audio_core/renderer/behavior/behavior_info.cpp index 4bd3912e0f..63b80c503a 100644 --- a/src/audio_core/renderer/behavior/behavior_info.cpp +++ b/src/audio_core/renderer/behavior/behavior_info.cpp @@ -205,4 +205,8 @@ bool BehaviorInfo::IsVoiceInParameterV2Supported() const { return CheckFeatureSupported(SupportTags::VoiceInParameterV2, user_revision); } +bool BehaviorInfo::IsBiquadFilterParameterForSplitterEnabled() const { + return CheckFeatureSupported(SupportTags::SplitterBiquadFilterParameter, user_revision); +} + } // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/behavior/behavior_info.h b/src/audio_core/renderer/behavior/behavior_info.h index 8e9333579e..757f163c5a 100644 --- a/src/audio_core/renderer/behavior/behavior_info.h +++ b/src/audio_core/renderer/behavior/behavior_info.h @@ -389,6 +389,13 @@ public: */ bool IsVoiceInParameterV2Supported() const; + /** + * Check if splitter destinations can carry biquad filter parameters (revision 12+). + * + * @return True if supported, otherwise false. + */ + bool IsBiquadFilterParameterForSplitterEnabled() const; + /// Host version u32 process_revision; /// User version diff --git a/src/audio_core/renderer/splitter/splitter_context.cpp b/src/audio_core/renderer/splitter/splitter_context.cpp index 9d6ac646e9..1ba5daebe9 100644 --- a/src/audio_core/renderer/splitter/splitter_context.cpp +++ b/src/audio_core/renderer/splitter/splitter_context.cpp @@ -43,6 +43,7 @@ void SplitterContext::Setup(std::span splitter_infos_, const u32 s 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(); } @@ -140,8 +141,11 @@ 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++) { - // Version selection based on float coeff/biquad v2b support. - if (!splitter_float_coeff_supported) { + // 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(input + offset); @@ -158,8 +162,50 @@ u32 SplitterContext::UpdateData(const u8* input, u32 offset, const u32 count) { } 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(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(1 << 14); + out.numerator[0] = static_cast(legacy.b[0]) * scale; + out.numerator[1] = static_cast(legacy.b[1]) * scale; + out.numerator[2] = static_cast(legacy.b[2]) * scale; + out.denominator[0] = static_cast(legacy.a[0]) * scale; + out.denominator[1] = static_cast(legacy.a[1]) * scale; + } + + offset += static_cast(sizeof(SplitterDestinationData::InParameterVersion2a)); } else { - // Version 2b: struct contains extra biquad filter fields + // Version 2b: struct contains extra biquad filter fields with float coeffs const auto* data_header_v2b = reinterpret_cast(input + offset); diff --git a/src/audio_core/renderer/splitter/splitter_context.h b/src/audio_core/renderer/splitter/splitter_context.h index 7b1fc7a3b9..c71ce3d7c4 100644 --- a/src/audio_core/renderer/splitter/splitter_context.h +++ b/src/audio_core/renderer/splitter/splitter_context.h @@ -189,6 +189,8 @@ private: bool splitter_bug_fixed{}; /// Is explicit previous mix volume reset supported? bool splitter_prev_volume_reset_supported{}; + /// Is biquad filter parameter for splitter (REV12) supported? + bool splitter_biquad_param_supported{}; /// Is float coefficient/biquad filter v2b parameter supported? bool splitter_float_coeff_supported{}; }; diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.h b/src/audio_core/renderer/splitter/splitter_destinations_data.h index 70ad4becc9..6bfe5e0e34 100644 --- a/src/audio_core/renderer/splitter/splitter_destinations_data.h +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.h @@ -34,6 +34,18 @@ public: static_assert(sizeof(BiquadFilterParameter2) == 0x18, "BiquadFilterParameter2 has the wrong size!"); + /** + * Legacy biquad filter parameter with fixed-point coefficients (SDK REV12+ for splitters). + * Matches the old voice biquad format. + */ + struct BiquadFilterParameterLegacy { + /* 0x00 */ bool enabled; + /* 0x02 */ std::array b; // numerator + /* 0x08 */ std::array a; // denominator (a0 = 1) + }; + static_assert(sizeof(BiquadFilterParameterLegacy) == 0xC, + "BiquadFilterParameterLegacy has the wrong size!"); + struct InParameter { /* 0x00 */ u32 magic; // 'SNDD' /* 0x04 */ s32 id; @@ -45,6 +57,20 @@ public: static_assert(sizeof(InParameter) == 0x70, "SplitterDestinationData::InParameter has the wrong size!"); + struct InParameterVersion2a { + /* 0x00 */ u32 magic; // 'SNDD' + /* 0x04 */ s32 id; + /* 0x08 */ std::array mix_volumes; + /* 0x68 */ u32 mix_id; + /* 0x6C */ std::array + biquad_filters; + /* 0x84 */ bool in_use; + /* 0x85 */ bool reset_prev_volume; // only effective if supported + /* 0x86 */ u8 reserved[10]; + }; + static_assert(sizeof(InParameterVersion2a) == 0x90, + "SplitterDestinationData::InParameterVersion2a has the wrong size!"); + struct InParameterVersion2b { /* 0x00 */ u32 magic; // 'SNDD' /* 0x04 */ s32 id; @@ -111,7 +137,7 @@ public: f32 GetMixVolumePrev(u32 index) const; /** - * Get the previous mix volumes for all mix buffers in this destination. + * Get the previous mix volumes for all mix buffers. * * @return Span of previous mix buffer volumes. */ @@ -149,14 +175,14 @@ public: void SetNext(SplitterDestinationData* next); /** - * Get biquad filter parameters for this destination (REV15+). + * Get biquad filter parameters for this destination (REV15+ or mapped from REV12). * * @return Span of biquad filter parameters. */ std::span GetBiquadFilters(); /** - * Get const biquad filter parameters for this destination (REV15+). + * Get const biquad filter parameters for this destination (REV15+ or mapped from REV12). * * @return Const span of biquad filter parameters. */ @@ -171,7 +197,7 @@ private: std::array mix_volumes{0.0f}; /// Previous mix volumes std::array prev_mix_volumes{0.0f}; - /// Biquad filter parameters (REV15+) + /// Biquad filter parameters (REV15+ or mapped from REV12) std::array biquad_filters{}; /// Next destination in the mix chain SplitterDestinationData* next{};