early-access version 2830
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| yuzu emulator early access | ||||
| ============= | ||||
|  | ||||
| This is the source code for early-access 2829. | ||||
| This is the source code for early-access 2830. | ||||
|  | ||||
| ## Legal Notice | ||||
|  | ||||
|   | ||||
| @@ -57,6 +57,8 @@ add_library(audio_core STATIC | ||||
|     renderer/command/effect/biquad_filter.h | ||||
|     renderer/command/effect/capture.cpp | ||||
|     renderer/command/effect/capture.h | ||||
|     renderer/command/effect/compressor.cpp | ||||
|     renderer/command/effect/compressor.h | ||||
|     renderer/command/effect/delay.cpp | ||||
|     renderer/command/effect/delay.h | ||||
|     renderer/command/effect/i3dl2_reverb.cpp | ||||
| @@ -106,27 +108,29 @@ add_library(audio_core STATIC | ||||
|     renderer/command/command_processing_time_estimator.h | ||||
|     renderer/command/commands.h | ||||
|     renderer/command/icommand.h | ||||
|     renderer/effect/effect_aux_info.cpp | ||||
|     renderer/effect/effect_aux_info.h | ||||
|     renderer/effect/effect_biquad_filter_info.cpp | ||||
|     renderer/effect/effect_biquad_filter_info.h | ||||
|     renderer/effect/effect_buffer_mixer_info.cpp | ||||
|     renderer/effect/effect_buffer_mixer_info.h | ||||
|     renderer/effect/effect_capture_info.cpp | ||||
|     renderer/effect/effect_capture_info.h | ||||
|     renderer/effect/aux_.cpp | ||||
|     renderer/effect/aux_.h | ||||
|     renderer/effect/biquad_filter.cpp | ||||
|     renderer/effect/biquad_filter.h | ||||
|     renderer/effect/buffer_mixer.cpp | ||||
|     renderer/effect/buffer_mixer.h | ||||
|     renderer/effect/capture.cpp | ||||
|     renderer/effect/capture.h | ||||
|     renderer/effect/compressor.cpp | ||||
|     renderer/effect/compressor.h | ||||
|     renderer/effect/delay.cpp | ||||
|     renderer/effect/delay.h | ||||
|     renderer/effect/effect_context.cpp | ||||
|     renderer/effect/effect_context.h | ||||
|     renderer/effect/effect_delay_info.cpp | ||||
|     renderer/effect/effect_delay_info.h | ||||
|     renderer/effect/effect_i3dl2_info.cpp | ||||
|     renderer/effect/effect_i3dl2_info.h | ||||
|     renderer/effect/effect_reset.h | ||||
|     renderer/effect/effect_info_base.h | ||||
|     renderer/effect/effect_light_limiter_info.cpp | ||||
|     renderer/effect/effect_light_limiter_info.h | ||||
|     renderer/effect/effect_reset.h | ||||
|     renderer/effect/effect_result_state.h | ||||
|     renderer/effect/effect_reverb_info.h | ||||
|     renderer/effect/effect_reverb_info.cpp | ||||
|     renderer/effect/i3dl2.cpp | ||||
|     renderer/effect/i3dl2.h | ||||
|     renderer/effect/light_limiter.cpp | ||||
|     renderer/effect/light_limiter.h | ||||
|     renderer/effect/reverb.h | ||||
|     renderer/effect/reverb.cpp | ||||
|     renderer/mix/mix_context.cpp | ||||
|     renderer/mix/mix_context.h | ||||
|     renderer/mix/mix_info.cpp | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <numeric> | ||||
| #include <span> | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_funcs.h" | ||||
| @@ -40,6 +41,25 @@ enum class SessionTypes { | ||||
|     FinalOutputRecorder, | ||||
| }; | ||||
|  | ||||
| enum class Channels : u32 { | ||||
|     FrontLeft, | ||||
|     FrontRight, | ||||
|     Center, | ||||
|     LFE, | ||||
|     BackLeft, | ||||
|     BackRight, | ||||
| }; | ||||
|  | ||||
| // These are used by Delay, Reverb and I3dl2Reverb prior to Revision 11. | ||||
| enum class OldChannels : u32 { | ||||
|     FrontLeft, | ||||
|     FrontRight, | ||||
|     BackLeft, | ||||
|     BackRight, | ||||
|     Center, | ||||
|     LFE, | ||||
| }; | ||||
|  | ||||
| constexpr u32 BufferCount = 32; | ||||
|  | ||||
| constexpr u32 MaxRendererSessions = 2; | ||||
| @@ -66,6 +86,27 @@ constexpr bool IsChannelCountValid(u16 channel_count) { | ||||
|            (channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6); | ||||
| } | ||||
|  | ||||
| constexpr void UseOldChannelMapping(std::span<s16> inputs, std::span<s16> outputs) { | ||||
|     constexpr auto old_center{static_cast<u32>(OldChannels::Center)}; | ||||
|     constexpr auto new_center{static_cast<u32>(Channels::Center)}; | ||||
|     constexpr auto old_lfe{static_cast<u32>(OldChannels::LFE)}; | ||||
|     constexpr auto new_lfe{static_cast<u32>(Channels::LFE)}; | ||||
|  | ||||
|     auto center{inputs[old_center]}; | ||||
|     auto lfe{inputs[old_lfe]}; | ||||
|     inputs[old_center] = inputs[new_center]; | ||||
|     inputs[old_lfe] = inputs[new_lfe]; | ||||
|     inputs[new_center] = center; | ||||
|     inputs[new_lfe] = lfe; | ||||
|  | ||||
|     center = outputs[old_center]; | ||||
|     lfe = outputs[old_lfe]; | ||||
|     outputs[old_center] = outputs[new_center]; | ||||
|     outputs[old_lfe] = outputs[new_lfe]; | ||||
|     outputs[new_center] = center; | ||||
|     outputs[new_lfe] = lfe; | ||||
| } | ||||
|  | ||||
| constexpr u32 GetSplitterInParamHeaderMagic() { | ||||
|     return Common::MakeMagic('S', 'N', 'D', 'H'); | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore { | ||||
| constexpr u32 CurrentRevision = 10; | ||||
| constexpr u32 CurrentRevision = 11; | ||||
|  | ||||
| enum class SupportTags { | ||||
|     CommandProcessingTimeEstimatorVersion4, | ||||
| @@ -40,6 +40,9 @@ enum class SupportTags { | ||||
|     LongSizePreDelay, | ||||
|     AudioUsbDeviceOutput, | ||||
|     DeviceApiVersion2, | ||||
|     DelayChannelMappingChange, | ||||
|     ReverbChannelMappingChange, | ||||
|     I3dl2ReverbChannelMappingChange, | ||||
|  | ||||
|     // Not a real tag, just here to get the count. | ||||
|     Size | ||||
| @@ -80,6 +83,9 @@ constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) { | ||||
|             {SupportTags::EffectInfoVer2, 9}, | ||||
|             {SupportTags::CommandProcessingTimeEstimatorVersion4, 10}, | ||||
|             {SupportTags::MultiTapBiquadFilterProcessing, 10}, | ||||
|             {SupportTags::DelayChannelMappingChange, 11}, | ||||
|             {SupportTags::ReverbChannelMappingChange, 11}, | ||||
|             {SupportTags::I3dl2ReverbChannelMappingChange, 11}, | ||||
|         }}; | ||||
|  | ||||
|     const auto& feature = | ||||
|   | ||||
| @@ -104,17 +104,22 @@ bool BehaviorInfo::IsCommandProcessingTimeEstimatorVersion4Supported() const { | ||||
|                                  user_revision); | ||||
| } | ||||
|  | ||||
| bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const { | ||||
| bool BehaviorInfo::IsCommandProcessingTimeEstimatorVersion5Supported() const { | ||||
|     return CheckFeatureSupported(SupportTags::CommandProcessingTimeEstimatorVersion4, | ||||
|                                  user_revision); | ||||
| } | ||||
|  | ||||
| bool BehaviorInfo::IsAudioRendererProcessingTimeLimit70PercentSupported() const { | ||||
|     return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit70Percent, | ||||
|                                  user_revision); | ||||
| } | ||||
|  | ||||
| bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const { | ||||
| bool BehaviorInfo::IsAudioRendererProcessingTimeLimit75PercentSupported() const { | ||||
|     return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit75Percent, | ||||
|                                  user_revision); | ||||
| } | ||||
|  | ||||
| bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const { | ||||
| bool BehaviorInfo::IsAudioRendererProcessingTimeLimit80PercentSupported() const { | ||||
|     return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit80Percent, | ||||
|                                  user_revision); | ||||
| } | ||||
| @@ -147,7 +152,7 @@ bool BehaviorInfo::IsVoicePlayedSampleCountResetAtLoopPointSupported() const { | ||||
|                                  user_revision); | ||||
| } | ||||
|  | ||||
| bool BehaviorInfo::IsBiquadFilterEffectStateClaerBugFixed() const { | ||||
| bool BehaviorInfo::IsBiquadFilterEffectStateClearBugFixed() const { | ||||
|     return CheckFeatureSupported(SupportTags::BiquadFilterEffectStateClearBugFix, user_revision); | ||||
| } | ||||
|  | ||||
| @@ -171,4 +176,16 @@ bool BehaviorInfo::IsDeviceApiVersion2Supported() const { | ||||
|     return CheckFeatureSupported(SupportTags::DeviceApiVersion2, user_revision); | ||||
| } | ||||
|  | ||||
| bool BehaviorInfo::IsDelayChannelMappingChanged() const { | ||||
|     return CheckFeatureSupported(SupportTags::DelayChannelMappingChange, user_revision); | ||||
| } | ||||
|  | ||||
| bool BehaviorInfo::IsReverbChannelMappingChanged() const { | ||||
|     return CheckFeatureSupported(SupportTags::ReverbChannelMappingChange, user_revision); | ||||
| } | ||||
|  | ||||
| bool BehaviorInfo::IsI3dl2ReverbChannelMappingChanged() const { | ||||
|     return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
|   | ||||
| @@ -199,28 +199,32 @@ public: | ||||
|     bool IsCommandProcessingTimeEstimatorVersion4Supported() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if the AudioRenderer can use up to 70% of the allocated processing timeslice. | ||||
|      * Note: Name is correct, Nintendo have the typo here | ||||
|      * Check if the command time estimator version 5 is supported. | ||||
|      * | ||||
|      * @return True if supported, otherwise false. | ||||
|      */ | ||||
|     bool IsAudioRenererProcessingTimeLimit70PercentSupported() const; | ||||
|     bool IsCommandProcessingTimeEstimatorVersion5Supported() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if the AudioRenderer can use up to 70% of the allocated processing timeslice. | ||||
|      * | ||||
|      * @return True if supported, otherwise false. | ||||
|      */ | ||||
|     bool IsAudioRendererProcessingTimeLimit70PercentSupported() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if the AudioRenderer can use up to 75% of the allocated processing timeslice. | ||||
|      * Note: Name is correct, Nintendo have the typo here | ||||
|      * | ||||
|      * @return True if supported, otherwise false. | ||||
|      */ | ||||
|     bool IsAudioRenererProcessingTimeLimit75PercentSupported() const; | ||||
|     bool IsAudioRendererProcessingTimeLimit75PercentSupported() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if the AudioRenderer can use up to 80% of the allocated processing timeslice. | ||||
|      * Note: Name is correct, Nintendo have the typo here | ||||
|      * | ||||
|      * @return True if supported, otherwise false. | ||||
|      */ | ||||
|     bool IsAudioRenererProcessingTimeLimit80PercentSupported() const; | ||||
|     bool IsAudioRendererProcessingTimeLimit80PercentSupported() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if voice flushing is supported | ||||
| @@ -279,11 +283,10 @@ public: | ||||
|      * Check if the clear state bug for biquad filters is fixed. | ||||
|      * The biquad state was not marked as needing re-initialisation when the effect was updated, it | ||||
|      * was only initialized once with a new effect. | ||||
|      * Note: Name is correct, Nintendo have the typo here | ||||
|      * | ||||
|      * @return True if fixed, otherwise false. | ||||
|      */ | ||||
|     bool IsBiquadFilterEffectStateClaerBugFixed() const; | ||||
|     bool IsBiquadFilterEffectStateClearBugFixed() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if Q23 precision is supported for fixed point. | ||||
| @@ -322,6 +325,42 @@ public: | ||||
|      */ | ||||
|     bool IsDeviceApiVersion2Supported() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if new channel mappings are used for Delay commands. | ||||
|      * Older commands used: | ||||
|      *   front left/front right/back left/back right/center/lfe | ||||
|      * Whereas everywhere else in the code uses: | ||||
|      *   front left/front right/center/lfe/back left/back right | ||||
|      * This corrects that and makes everything standardised. | ||||
|      * | ||||
|      * @return True if supported, otherwise false. | ||||
|      */ | ||||
|     bool IsDelayChannelMappingChanged() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if new channel mappings are used for Reverb commands. | ||||
|      * Older commands used: | ||||
|      *   front left/front right/back left/back right/center/lfe | ||||
|      * Whereas everywhere else in the code uses: | ||||
|      *   front left/front right/center/lfe/back left/back right | ||||
|      * This corrects that and makes everything standardised. | ||||
|      * | ||||
|      * @return True if supported, otherwise false. | ||||
|      */ | ||||
|     bool IsReverbChannelMappingChanged() const; | ||||
|  | ||||
|     /** | ||||
|      * Check if new channel mappings are used for I3dl2Reverb commands. | ||||
|      * Older commands used: | ||||
|      *   front left/front right/back left/back right/center/lfe | ||||
|      * Whereas everywhere else in the code uses: | ||||
|      *   front left/front right/center/lfe/back left/back right | ||||
|      * This corrects that and makes everything standardised. | ||||
|      * | ||||
|      * @return True if supported, otherwise false. | ||||
|      */ | ||||
|     bool IsI3dl2ReverbChannelMappingChanged() const; | ||||
|  | ||||
|     /// Host version | ||||
|     u32 process_revision; | ||||
|     /// User version | ||||
|   | ||||
| @@ -5,9 +5,9 @@ | ||||
| #include "audio_core/renderer/command/command_buffer.h" | ||||
| #include "audio_core/renderer/command/command_list_header.h" | ||||
| #include "audio_core/renderer/command/command_processing_time_estimator.h" | ||||
| #include "audio_core/renderer/effect/effect_biquad_filter_info.h" | ||||
| #include "audio_core/renderer/effect/effect_delay_info.h" | ||||
| #include "audio_core/renderer/effect/effect_reverb_info.h" | ||||
| #include "audio_core/renderer/effect/biquad_filter.h" | ||||
| #include "audio_core/renderer/effect/delay.h" | ||||
| #include "audio_core/renderer/effect/reverb.h" | ||||
| #include "audio_core/renderer/memory/memory_pool_info.h" | ||||
| #include "audio_core/renderer/mix/mix_info.h" | ||||
| #include "audio_core/renderer/sink/circular_buffer_sink_info.h" | ||||
| @@ -368,9 +368,13 @@ void CommandBuffer::GenerateDelayCommand(const s32 node_id, EffectInfoBase& effe | ||||
|     if (IsChannelCountValid(parameter.channel_count)) { | ||||
|         const auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(DelayInfo::State))}; | ||||
|         if (state_buffer) { | ||||
|             for (s8 channel = 0; channel < parameter.channel_count; channel++) { | ||||
|                 cmd.inputs[channel] = static_cast<s8>(buffer_offset + parameter.inputs[channel]); | ||||
|                 cmd.outputs[channel] = static_cast<s8>(buffer_offset + parameter.outputs[channel]); | ||||
|             for (s16 channel = 0; channel < parameter.channel_count; channel++) { | ||||
|                 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel]; | ||||
|                 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel]; | ||||
|             } | ||||
|  | ||||
|             if (!behavior->IsDelayChannelMappingChanged() && parameter.channel_count == 6) { | ||||
|                 UseOldChannelMapping(cmd.inputs, cmd.outputs); | ||||
|             } | ||||
|  | ||||
|             cmd.parameter = parameter; | ||||
| @@ -506,11 +510,15 @@ void CommandBuffer::GenerateReverbCommand(const s32 node_id, EffectInfoBase& eff | ||||
|     if (IsChannelCountValid(parameter.channel_count)) { | ||||
|         const auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(ReverbInfo::State))}; | ||||
|         if (state_buffer) { | ||||
|             for (s8 channel = 0; channel < parameter.channel_count; channel++) { | ||||
|             for (s16 channel = 0; channel < parameter.channel_count; channel++) { | ||||
|                 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel]; | ||||
|                 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel]; | ||||
|             } | ||||
|  | ||||
|             if (!behavior->IsReverbChannelMappingChanged() && parameter.channel_count == 6) { | ||||
|                 UseOldChannelMapping(cmd.inputs, cmd.outputs); | ||||
|             } | ||||
|  | ||||
|             cmd.parameter = parameter; | ||||
|             cmd.effect_enabled = effect_info.IsEnabled(); | ||||
|             cmd.state = state_buffer; | ||||
| @@ -534,11 +542,15 @@ void CommandBuffer::GenerateI3dl2ReverbCommand(const s32 node_id, EffectInfoBase | ||||
|         const auto state_buffer{ | ||||
|             memory_pool->Translate(CpuAddr(state), sizeof(I3dl2ReverbInfo::State))}; | ||||
|         if (state_buffer) { | ||||
|             for (s8 channel = 0; channel < parameter.channel_count; channel++) { | ||||
|             for (s16 channel = 0; channel < parameter.channel_count; channel++) { | ||||
|                 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel]; | ||||
|                 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel]; | ||||
|             } | ||||
|  | ||||
|             if (!behavior->IsI3dl2ReverbChannelMappingChanged() && parameter.channel_count == 6) { | ||||
|                 UseOldChannelMapping(cmd.inputs, cmd.outputs); | ||||
|             } | ||||
|  | ||||
|             cmd.parameter = parameter; | ||||
|             cmd.effect_enabled = effect_info.IsEnabled(); | ||||
|             cmd.state = state_buffer; | ||||
| @@ -675,4 +687,28 @@ void CommandBuffer::GenerateCaptureCommand(const s32 node_id, EffectInfoBase& ef | ||||
|     GenerateEnd<CaptureCommand>(cmd); | ||||
| } | ||||
|  | ||||
| void CommandBuffer::GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info, | ||||
|                                               s32 node_id) { | ||||
|     auto& cmd{GenerateStart<CompressorCommand, CommandId::Compressor>(node_id)}; | ||||
|  | ||||
|     auto& parameter{ | ||||
|         *reinterpret_cast<CompressorInfo::ParameterVersion2*>(effect_info.GetParameter())}; | ||||
|     auto state{reinterpret_cast<CompressorInfo::State*>(effect_info.GetStateBuffer())}; | ||||
|  | ||||
|     if (IsChannelCountValid(parameter.channel_count)) { | ||||
|         auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(CompressorInfo::State))}; | ||||
|         if (state_buffer) { | ||||
|             for (u16 channel = 0; channel < parameter.channel_count; channel++) { | ||||
|                 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel]; | ||||
|                 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel]; | ||||
|             } | ||||
|             cmd.parameter = parameter; | ||||
|             cmd.workbuffer = state_buffer; | ||||
|             cmd.enabled = effect_info.IsEnabled(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     GenerateEnd<CompressorCommand>(cmd); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| #include <span> | ||||
|  | ||||
| #include "audio_core/renderer/command/commands.h" | ||||
| #include "audio_core/renderer/effect/effect_light_limiter_info.h" | ||||
| #include "audio_core/renderer/effect/light_limiter.h" | ||||
| #include "audio_core/renderer/performance/performance_manager.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| @@ -428,6 +428,15 @@ public: | ||||
|                                 s16 output_index, s16 buffer_offset, u32 update_count, | ||||
|                                 u32 count_max, u32 write_offset); | ||||
|  | ||||
|     /** | ||||
|      * Generate a compressor command, adding it to the command list. | ||||
|      * | ||||
|      * @param buffer_offset - Base mix buffer offset to use. | ||||
|      * @param effect_info   - Capture effect info to generate this command from. | ||||
|      * @param node_id       - Node id of the voice this command is generated for. | ||||
|      */ | ||||
|     void GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id); | ||||
|  | ||||
|     /// Command list buffer generated commands will be added to | ||||
|     std::span<u8> command_list{}; | ||||
|     /// Input sample count, unused | ||||
|   | ||||
| @@ -6,11 +6,12 @@ | ||||
| #include "audio_core/renderer/command/command_buffer.h" | ||||
| #include "audio_core/renderer/command/command_generator.h" | ||||
| #include "audio_core/renderer/command/command_list_header.h" | ||||
| #include "audio_core/renderer/effect/effect_aux_info.h" | ||||
| #include "audio_core/renderer/effect/effect_biquad_filter_info.h" | ||||
| #include "audio_core/renderer/effect/effect_buffer_mixer_info.h" | ||||
| #include "audio_core/renderer/effect/effect_capture_info.h" | ||||
| #include "audio_core/renderer/effect/aux_.h" | ||||
| #include "audio_core/renderer/effect/biquad_filter.h" | ||||
| #include "audio_core/renderer/effect/buffer_mixer.h" | ||||
| #include "audio_core/renderer/effect/capture.h" | ||||
| #include "audio_core/renderer/effect/effect_context.h" | ||||
| #include "audio_core/renderer/effect/light_limiter.h" | ||||
| #include "audio_core/renderer/mix/mix_context.h" | ||||
| #include "audio_core/renderer/performance/detail_aspect.h" | ||||
| #include "audio_core/renderer/performance/entry_aspect.h" | ||||
| @@ -371,7 +372,7 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset | ||||
|             break; | ||||
|         case EffectInfoBase::ParameterState::Updating: | ||||
|         case EffectInfoBase::ParameterState::Updated: | ||||
|             if (render_context.behavior->IsBiquadFilterEffectStateClaerBugFixed()) { | ||||
|             if (render_context.behavior->IsBiquadFilterEffectStateClearBugFixed()) { | ||||
|                 needs_init = false; | ||||
|             } else { | ||||
|                 needs_init = parameter.state == EffectInfoBase::ParameterState::Updating; | ||||
| @@ -442,6 +443,11 @@ void CommandGenerator::GenerateCaptureCommand(const s16 buffer_offset, EffectInf | ||||
|     } | ||||
| } | ||||
|  | ||||
| void CommandGenerator::GenerateCompressorCommand(const s16 buffer_offset, | ||||
|                                                  EffectInfoBase& effect_info, const s32 node_id) { | ||||
|     command_buffer.GenerateCompressorCommand(buffer_offset, effect_info, node_id); | ||||
| } | ||||
|  | ||||
| void CommandGenerator::GenerateEffectCommand(MixInfo& mix_info) { | ||||
|     const auto effect_count{effect_context.GetCount()}; | ||||
|     for (u32 i = 0; i < effect_count; i++) { | ||||
| @@ -550,6 +556,17 @@ void CommandGenerator::GenerateEffectCommand(MixInfo& mix_info) { | ||||
|             } | ||||
|         } break; | ||||
|  | ||||
|         case EffectInfoBase::Type::Compressor: { | ||||
|             DetailAspect capture_detail_aspect(*this, entry_type, mix_info.node_id, | ||||
|                                                PerformanceDetailType::Unk13); | ||||
|             GenerateCompressorCommand(mix_info.buffer_offset, effect_info, mix_info.node_id); | ||||
|             if (capture_detail_aspect.initialized) { | ||||
|                 command_buffer.GeneratePerformanceCommand( | ||||
|                     capture_detail_aspect.node_id, PerformanceState::Stop, | ||||
|                     capture_detail_aspect.performance_entry_address); | ||||
|             } | ||||
|         } break; | ||||
|  | ||||
|         default: | ||||
|             LOG_ERROR(Service_Audio, "Invalid effect type {}", | ||||
|                       static_cast<u32>(effect_info.GetType())); | ||||
|   | ||||
| @@ -244,6 +244,16 @@ public: | ||||
|      */ | ||||
|     void GenerateCaptureCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id); | ||||
|  | ||||
|     /** | ||||
|      * Generate a compressor effect command. | ||||
|      * | ||||
|      * @param buffer_offset    - Base mix buffer offset to use. | ||||
|      * @param effect_info_base - Compressor effect info. | ||||
|      * @param node_id          - Node id of the mix this command is generated for. | ||||
|      */ | ||||
|     void GenerateCompressorCommand(const s16 buffer_offset, EffectInfoBase& effect_info, | ||||
|                                    const s32 node_id); | ||||
|  | ||||
|     /** | ||||
|      * Generate all effect commands for a mix. | ||||
|      * | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -43,6 +43,7 @@ public: | ||||
|     virtual u32 Estimate(const LightLimiterVersion2Command& command) const = 0; | ||||
|     virtual u32 Estimate(const MultiTapBiquadFilterCommand& command) const = 0; | ||||
|     virtual u32 Estimate(const CaptureCommand& command) const = 0; | ||||
|     virtual u32 Estimate(const CompressorCommand& command) const = 0; | ||||
| }; | ||||
|  | ||||
| class CommandProcessingTimeEstimatorVersion1 final : public ICommandProcessingTimeEstimator { | ||||
| @@ -79,6 +80,7 @@ public: | ||||
|     u32 Estimate(const LightLimiterVersion2Command& command) const override; | ||||
|     u32 Estimate(const MultiTapBiquadFilterCommand& command) const override; | ||||
|     u32 Estimate(const CaptureCommand& command) const override; | ||||
|     u32 Estimate(const CompressorCommand& command) const override; | ||||
|  | ||||
| private: | ||||
|     u32 sample_count{}; | ||||
| @@ -119,6 +121,7 @@ public: | ||||
|     u32 Estimate(const LightLimiterVersion2Command& command) const override; | ||||
|     u32 Estimate(const MultiTapBiquadFilterCommand& command) const override; | ||||
|     u32 Estimate(const CaptureCommand& command) const override; | ||||
|     u32 Estimate(const CompressorCommand& command) const override; | ||||
|  | ||||
| private: | ||||
|     u32 sample_count{}; | ||||
| @@ -159,6 +162,7 @@ public: | ||||
|     u32 Estimate(const LightLimiterVersion2Command& command) const override; | ||||
|     u32 Estimate(const MultiTapBiquadFilterCommand& command) const override; | ||||
|     u32 Estimate(const CaptureCommand& command) const override; | ||||
|     u32 Estimate(const CompressorCommand& command) const override; | ||||
|  | ||||
| private: | ||||
|     u32 sample_count{}; | ||||
| @@ -199,6 +203,48 @@ public: | ||||
|     u32 Estimate(const LightLimiterVersion2Command& command) const override; | ||||
|     u32 Estimate(const MultiTapBiquadFilterCommand& command) const override; | ||||
|     u32 Estimate(const CaptureCommand& command) const override; | ||||
|     u32 Estimate(const CompressorCommand& command) const override; | ||||
|  | ||||
| private: | ||||
|     u32 sample_count{}; | ||||
|     u32 buffer_count{}; | ||||
| }; | ||||
|  | ||||
| class CommandProcessingTimeEstimatorVersion5 final : public ICommandProcessingTimeEstimator { | ||||
| public: | ||||
|     CommandProcessingTimeEstimatorVersion5(u32 sample_count_, u32 buffer_count_) | ||||
|         : sample_count{sample_count_}, buffer_count{buffer_count_} {} | ||||
|  | ||||
|     u32 Estimate(const PcmInt16DataSourceVersion1Command& command) const override; | ||||
|     u32 Estimate(const PcmInt16DataSourceVersion2Command& command) const override; | ||||
|     u32 Estimate(const PcmFloatDataSourceVersion1Command& command) const override; | ||||
|     u32 Estimate(const PcmFloatDataSourceVersion2Command& command) const override; | ||||
|     u32 Estimate(const AdpcmDataSourceVersion1Command& command) const override; | ||||
|     u32 Estimate(const AdpcmDataSourceVersion2Command& command) const override; | ||||
|     u32 Estimate(const VolumeCommand& command) const override; | ||||
|     u32 Estimate(const VolumeRampCommand& command) const override; | ||||
|     u32 Estimate(const BiquadFilterCommand& command) const override; | ||||
|     u32 Estimate(const MixCommand& command) const override; | ||||
|     u32 Estimate(const MixRampCommand& command) const override; | ||||
|     u32 Estimate(const MixRampGroupedCommand& command) const override; | ||||
|     u32 Estimate(const DepopPrepareCommand& command) const override; | ||||
|     u32 Estimate(const DepopForMixBuffersCommand& command) const override; | ||||
|     u32 Estimate(const DelayCommand& command) const override; | ||||
|     u32 Estimate(const UpsampleCommand& command) const override; | ||||
|     u32 Estimate(const DownMix6chTo2chCommand& command) const override; | ||||
|     u32 Estimate(const AuxCommand& command) const override; | ||||
|     u32 Estimate(const DeviceSinkCommand& command) const override; | ||||
|     u32 Estimate(const CircularBufferSinkCommand& command) const override; | ||||
|     u32 Estimate(const ReverbCommand& command) const override; | ||||
|     u32 Estimate(const I3dl2ReverbCommand& command) const override; | ||||
|     u32 Estimate(const PerformanceCommand& command) const override; | ||||
|     u32 Estimate(const ClearMixBufferCommand& command) const override; | ||||
|     u32 Estimate(const CopyMixBufferCommand& command) const override; | ||||
|     u32 Estimate(const LightLimiterVersion1Command& command) const override; | ||||
|     u32 Estimate(const LightLimiterVersion2Command& command) const override; | ||||
|     u32 Estimate(const MultiTapBiquadFilterCommand& command) const override; | ||||
|     u32 Estimate(const CaptureCommand& command) const override; | ||||
|     u32 Estimate(const CompressorCommand& command) const override; | ||||
|  | ||||
| private: | ||||
|     u32 sample_count{}; | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| #include "audio_core/renderer/command/effect/aux_.h" | ||||
| #include "audio_core/renderer/command/effect/biquad_filter.h" | ||||
| #include "audio_core/renderer/command/effect/capture.h" | ||||
| #include "audio_core/renderer/command/effect/compressor.h" | ||||
| #include "audio_core/renderer/command/effect/delay.h" | ||||
| #include "audio_core/renderer/command/effect/i3dl2_reverb.h" | ||||
| #include "audio_core/renderer/command/effect/light_limiter.h" | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|  | ||||
| #include "audio_core/renderer/adsp/command_list_processor.h" | ||||
| #include "audio_core/renderer/command/effect/aux_.h" | ||||
| #include "audio_core/renderer/effect/effect_aux_info.h" | ||||
| #include "audio_core/renderer/effect/aux_.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
|  | ||||
| #include "audio_core/renderer/adsp/command_list_processor.h" | ||||
| #include "audio_core/renderer/command/effect/capture.h" | ||||
| #include "audio_core/renderer/effect/effect_aux_info.h" | ||||
| #include "audio_core/renderer/effect/aux_.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|   | ||||
							
								
								
									
										156
									
								
								src/audio_core/renderer/command/effect/compressor.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										156
									
								
								src/audio_core/renderer/command/effect/compressor.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include <cmath> | ||||
| #include <span> | ||||
| #include <vector> | ||||
|  | ||||
| #include "audio_core/renderer/adsp/command_list_processor.h" | ||||
| #include "audio_core/renderer/command/effect/compressor.h" | ||||
| #include "audio_core/renderer/effect/compressor.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| static void SetCompressorEffectParameter(CompressorInfo::ParameterVersion2& params, | ||||
|                                          CompressorInfo::State& state) { | ||||
|     const auto ratio{1.0f / params.compressor_ratio}; | ||||
|     auto makeup_gain{0.0f}; | ||||
|     if (params.makeup_gain_enabled) { | ||||
|         makeup_gain = (params.threshold * 0.5f) * (ratio - 1.0f) - 3.0f; | ||||
|     } | ||||
|     state.makeup_gain = makeup_gain; | ||||
|     state.unk_18 = params.unk_28; | ||||
|  | ||||
|     const auto a{(params.out_gain + makeup_gain) / 20.0f * 3.3219f}; | ||||
|     const auto b{(a - std::trunc(a)) * 0.69315f}; | ||||
|     const auto c{std::pow(2.0f, b)}; | ||||
|  | ||||
|     state.unk_0C = (1.0f - ratio) / 6.0f; | ||||
|     state.unk_14 = params.threshold + 1.5f; | ||||
|     state.unk_10 = params.threshold - 1.5f; | ||||
|     state.unk_20 = c; | ||||
| } | ||||
|  | ||||
| static void InitializeCompressorEffect(CompressorInfo::ParameterVersion2& params, | ||||
|                                        CompressorInfo::State& state) { | ||||
|     std::memset(&state, 0, sizeof(CompressorInfo::State)); | ||||
|  | ||||
|     state.unk_00 = 0; | ||||
|     state.unk_04 = 1.0f; | ||||
|     state.unk_08 = 1.0f; | ||||
|  | ||||
|     SetCompressorEffectParameter(params, state); | ||||
| } | ||||
|  | ||||
| static void ApplyCompressorEffect(CompressorInfo::ParameterVersion2& params, | ||||
|                                   CompressorInfo::State& state, bool enabled, | ||||
|                                   std::vector<std::span<const s32>> input_buffers, | ||||
|                                   std::vector<std::span<s32>> output_buffers, u32 sample_count) { | ||||
|     if (enabled) { | ||||
|         auto state_00{state.unk_00}; | ||||
|         auto state_04{state.unk_04}; | ||||
|         auto state_08{state.unk_08}; | ||||
|         auto state_18{state.unk_18}; | ||||
|  | ||||
|         for (u32 i = 0; i < sample_count; i++) { | ||||
|             auto a{0.0f}; | ||||
|             for (s16 channel = 0; channel < params.channel_count; channel++) { | ||||
|                 const auto input_sample{Common::FixedPoint<49, 15>(input_buffers[channel][i])}; | ||||
|                 a += (input_sample * input_sample).to_float(); | ||||
|             } | ||||
|  | ||||
|             state_00 += params.unk_24 * ((a / params.channel_count) - state.unk_00); | ||||
|  | ||||
|             auto b{-100.0f}; | ||||
|             auto c{0.0f}; | ||||
|             if (state_00 >= 1.0e-10) { | ||||
|                 b = std::log10(state_00) * 10.0f; | ||||
|                 c = 1.0f; | ||||
|             } | ||||
|  | ||||
|             if (b >= state.unk_10) { | ||||
|                 const auto d{b >= state.unk_14 | ||||
|                                  ? ((1.0f / params.compressor_ratio) - 1.0f) * | ||||
|                                        (b - params.threshold) | ||||
|                                  : (b - state.unk_10) * (b - state.unk_10) * -state.unk_0C}; | ||||
|                 const auto e{d / 20.0f * 3.3219f}; | ||||
|                 const auto f{(e - std::trunc(e)) * 0.69315f}; | ||||
|                 c = std::pow(2.0f, f); | ||||
|             } | ||||
|  | ||||
|             state_18 = params.unk_28; | ||||
|             auto tmp{c}; | ||||
|             if ((state_04 - c) <= 0.08f) { | ||||
|                 state_18 = params.unk_2C; | ||||
|                 if (((state_04 - c) >= -0.08f) && (std::abs(state_08 - c) >= 0.001f)) { | ||||
|                     tmp = state_04; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             state_04 = tmp; | ||||
|             state_08 += (c - state_08) * state_18; | ||||
|  | ||||
|             for (s16 channel = 0; channel < params.channel_count; channel++) { | ||||
|                 output_buffers[channel][i] = static_cast<s32>( | ||||
|                     static_cast<f32>(input_buffers[channel][i]) * state_08 * state.unk_20); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         state.unk_00 = state_00; | ||||
|         state.unk_04 = state_04; | ||||
|         state.unk_08 = state_08; | ||||
|         state.unk_18 = state_18; | ||||
|     } else { | ||||
|         for (s16 channel = 0; channel < params.channel_count; channel++) { | ||||
|             if (params.inputs[channel] != params.outputs[channel]) { | ||||
|                 std::memcpy((char*)output_buffers[channel].data(), | ||||
|                             (char*)input_buffers[channel].data(), | ||||
|                             output_buffers[channel].size_bytes()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor, | ||||
|                              std::string& string) { | ||||
|     string += fmt::format("CompressorCommand\n\tenabled {} \n\tinputs: ", effect_enabled); | ||||
|     for (s16 i = 0; i < parameter.channel_count; i++) { | ||||
|         string += fmt::format("{:02X}, ", inputs[i]); | ||||
|     } | ||||
|     string += "\n\toutputs: "; | ||||
|     for (s16 i = 0; i < parameter.channel_count; i++) { | ||||
|         string += fmt::format("{:02X}, ", outputs[i]); | ||||
|     } | ||||
|     string += "\n"; | ||||
| } | ||||
|  | ||||
| void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) { | ||||
|     std::vector<std::span<const s32>> input_buffers(parameter.channel_count); | ||||
|     std::vector<std::span<s32>> output_buffers(parameter.channel_count); | ||||
|  | ||||
|     for (s16 i = 0; i < parameter.channel_count; i++) { | ||||
|         input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count, | ||||
|                                                          processor.sample_count); | ||||
|         output_buffers[i] = processor.mix_buffers.subspan(outputs[i] * processor.sample_count, | ||||
|                                                           processor.sample_count); | ||||
|     } | ||||
|  | ||||
|     auto state_{reinterpret_cast<CompressorInfo::State*>(state)}; | ||||
|  | ||||
|     if (effect_enabled) { | ||||
|         if (parameter.state == CompressorInfo::ParameterState::Updating) { | ||||
|             SetCompressorEffectParameter(parameter, *state_); | ||||
|         } else if (parameter.state == CompressorInfo::ParameterState::Initialized) { | ||||
|             InitializeCompressorEffect(parameter, *state_); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ApplyCompressorEffect(parameter, *state_, effect_enabled, input_buffers, output_buffers, | ||||
|                           processor.sample_count); | ||||
| } | ||||
|  | ||||
| bool CompressorCommand::Verify(const ADSP::CommandListProcessor& processor) { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										60
									
								
								src/audio_core/renderer/command/effect/compressor.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										60
									
								
								src/audio_core/renderer/command/effect/compressor.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <string> | ||||
|  | ||||
| #include "audio_core/renderer/command/icommand.h" | ||||
| #include "audio_core/renderer/effect/compressor.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
| namespace ADSP { | ||||
| class CommandListProcessor; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * AudioRenderer command for limiting volume between a high and low threshold. | ||||
|  * Version 1. | ||||
|  */ | ||||
| struct CompressorCommand : ICommand { | ||||
|     /** | ||||
|      * Print this command's information to a string. | ||||
|      * | ||||
|      * @param processor - The CommandListProcessor processing this command. | ||||
|      * @param string    - The string to print into. | ||||
|      */ | ||||
|     void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override; | ||||
|  | ||||
|     /** | ||||
|      * Process this command. | ||||
|      * | ||||
|      * @param processor - The CommandListProcessor processing this command. | ||||
|      */ | ||||
|     void Process(const ADSP::CommandListProcessor& processor) override; | ||||
|  | ||||
|     /** | ||||
|      * Verify this command's data is valid. | ||||
|      * | ||||
|      * @param processor - The CommandListProcessor processing this command. | ||||
|      * @return True if the command is valid, otherwise false. | ||||
|      */ | ||||
|     bool Verify(const ADSP::CommandListProcessor& processor) override; | ||||
|  | ||||
|     /// Input mix buffer offsets for each channel | ||||
|     std::array<s16, MaxChannels> inputs; | ||||
|     /// Output mix buffer offsets for each channel | ||||
|     std::array<s16, MaxChannels> outputs; | ||||
|     /// Input parameters | ||||
|     CompressorInfo::ParameterVersion2 parameter; | ||||
|     /// State, updated each call | ||||
|     CpuAddr state; | ||||
|     /// Game-supplied workbuffer (Unused) | ||||
|     CpuAddr workbuffer; | ||||
|     /// Is this effect enabled? | ||||
|     bool effect_enabled; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
| @@ -65,84 +65,78 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params, | ||||
|  * Delay effect impl, according to the parameters and current state, on the input mix buffers, | ||||
|  * saving the results to the output mix buffers. | ||||
|  * | ||||
|  * @tparam NumChannels - Number of channels to process. 1-6. | ||||
|  * @param params       - Input parameters to use. | ||||
|  * @param state        - State to use, must be initialized (see InitializeDelayEffect). | ||||
|  * @param inputs       - Input mix buffers to performan the delay on. | ||||
|  * @param outputs      - Output mix buffers to receive the delayed samples. | ||||
|  * @param sample_count - Number of samples to process. | ||||
|  */ | ||||
| template <size_t Channels> | ||||
| template <size_t NumChannels> | ||||
| static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state, | ||||
|                        std::vector<std::span<const s32>>& inputs, | ||||
|                        std::vector<std::span<s32>>& outputs, const u32 sample_count) { | ||||
|     for (u32 i = 0; i < sample_count; i++) { | ||||
|         std::array<Common::FixedPoint<50, 14>, Channels> input_samples{}; | ||||
|         for (u32 channel = 0; channel < Channels; channel++) { | ||||
|             input_samples[channel] = inputs[channel][i] * 64; | ||||
|     for (u32 sample_index = 0; sample_index < sample_count; sample_index++) { | ||||
|         std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{}; | ||||
|         for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|             input_samples[channel] = inputs[channel][sample_index] * 64; | ||||
|         } | ||||
|  | ||||
|         std::array<Common::FixedPoint<50, 14>, Channels> delay_samples{}; | ||||
|         for (u32 channel = 0; channel < Channels; channel++) { | ||||
|         std::array<Common::FixedPoint<50, 14>, NumChannels> delay_samples{}; | ||||
|         for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|             delay_samples[channel] = state.delay_lines[channel].Read(); | ||||
|         } | ||||
|  | ||||
|         std::array<std::array<Common::FixedPoint<18, 14>, Channels>, Channels> matrix{}; | ||||
|         if constexpr (Channels == 1) { | ||||
|         // clang-format off | ||||
|         std::array<std::array<Common::FixedPoint<18, 14>, NumChannels>, NumChannels> matrix{}; | ||||
|         if constexpr (NumChannels == 1) { | ||||
|             matrix = {{ | ||||
|                 {state.feedback_gain}, | ||||
|             }}; | ||||
|         } else if constexpr (Channels == 2) { | ||||
|         } else if constexpr (NumChannels == 2) { | ||||
|             matrix = {{ | ||||
|                 {state.delay_feedback_gain, state.delay_feedback_cross_gain}, | ||||
|                 {state.delay_feedback_cross_gain, state.delay_feedback_gain}, | ||||
|             }}; | ||||
|         } else if constexpr (Channels == 4) { | ||||
|         } else if constexpr (NumChannels == 4) { | ||||
|             matrix = {{ | ||||
|                 {state.delay_feedback_gain, state.delay_feedback_cross_gain, | ||||
|                  state.delay_feedback_cross_gain, 0.0f}, | ||||
|                 {state.delay_feedback_cross_gain, state.delay_feedback_gain, 0.0f, | ||||
|                  state.delay_feedback_cross_gain}, | ||||
|                 {state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_gain, | ||||
|                  state.delay_feedback_cross_gain}, | ||||
|                 {0.0f, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, | ||||
|                  state.delay_feedback_gain}, | ||||
|                 {state.delay_feedback_gain, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, 0.0f}, | ||||
|                 {state.delay_feedback_cross_gain, state.delay_feedback_gain, 0.0f, state.delay_feedback_cross_gain}, | ||||
|                 {state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain}, | ||||
|                 {0.0f, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, state.delay_feedback_gain}, | ||||
|             }}; | ||||
|         } else if constexpr (Channels == 6) { | ||||
|         } else if constexpr (NumChannels == 6) { | ||||
|             matrix = {{ | ||||
|                 {state.delay_feedback_gain, 0.0f, 0.0f, 0.0f, state.delay_feedback_cross_gain, | ||||
|                  state.delay_feedback_cross_gain}, | ||||
|                 {0.0f, state.delay_feedback_gain, 0.0f, state.delay_feedback_cross_gain, | ||||
|                  state.delay_feedback_cross_gain, 0.0f}, | ||||
|                 {state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_gain, | ||||
|                  state.delay_feedback_cross_gain, 0.0f, 0.0f}, | ||||
|                 {0.0f, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, | ||||
|                  state.delay_feedback_gain, 0.0f, 0.0f}, | ||||
|                 {state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, 0.0f, 0.0f, | ||||
|                  state.delay_feedback_gain, 0.0f}, | ||||
|                 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, params.feedback_gain}, | ||||
|                 {state.delay_feedback_gain, 0.0f, state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_cross_gain, 0.0f}, | ||||
|                 {0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain, 0.0f, 0.0f, state.delay_feedback_cross_gain}, | ||||
|                 {state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, state.delay_feedback_gain, 0.0f, 0.0f, 0.0f}, | ||||
|                 {0.0f, 0.0f, 0.0f, params.feedback_gain, 0.0f, 0.0f}, | ||||
|                 {state.delay_feedback_cross_gain, 0.0f, 0.0f, 0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain}, | ||||
|                 {0.0f, state.delay_feedback_cross_gain, 0.0f, 0.0f, state.delay_feedback_cross_gain, state.delay_feedback_gain}, | ||||
|             }}; | ||||
|         } | ||||
|         // clang-format on | ||||
|  | ||||
|         std::array<Common::FixedPoint<50, 14>, Channels> gained_samples{}; | ||||
|         for (u32 channel = 0; channel < Channels; channel++) { | ||||
|         std::array<Common::FixedPoint<50, 14>, NumChannels> gained_samples{}; | ||||
|         for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|             Common::FixedPoint<50, 14> delay{}; | ||||
|             for (u32 j = 0; j < Channels; j++) { | ||||
|             for (u32 j = 0; j < NumChannels; j++) { | ||||
|                 delay += delay_samples[j] * matrix[j][channel]; | ||||
|             } | ||||
|             gained_samples[channel] = input_samples[channel] * params.in_gain + delay; | ||||
|         } | ||||
|  | ||||
|         for (u32 channel = 0; channel < Channels; channel++) { | ||||
|         for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|             state.lowpass_z[channel] = gained_samples[channel] * state.lowpass_gain + | ||||
|                                        state.lowpass_z[channel] * state.lowpass_feedback_gain; | ||||
|             state.delay_lines[channel].Write(state.lowpass_z[channel]); | ||||
|         } | ||||
|  | ||||
|         for (u32 channel = 0; channel < Channels; channel++) { | ||||
|             outputs[channel][i] = (input_samples[channel] * params.dry_gain + | ||||
|                                    delay_samples[channel] * params.wet_gain) | ||||
|                                       .to_int_floor() / | ||||
|                                   64; | ||||
|         for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|             outputs[channel][sample_index] = (input_samples[channel] * params.dry_gain + | ||||
|                                               delay_samples[channel] * params.wet_gain) | ||||
|                                                  .to_int_floor() / | ||||
|                                              64; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| #include <string> | ||||
|  | ||||
| #include "audio_core/renderer/command/icommand.h" | ||||
| #include "audio_core/renderer/effect/effect_delay_info.h" | ||||
| #include "audio_core/renderer/effect/delay.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
| @@ -44,9 +44,9 @@ struct DelayCommand : ICommand { | ||||
|     bool Verify(const ADSP::CommandListProcessor& processor) override; | ||||
|  | ||||
|     /// Input mix buffer offsets for each channel | ||||
|     std::array<s8, MaxChannels> inputs; | ||||
|     std::array<s16, MaxChannels> inputs; | ||||
|     /// Output mix buffer offsets for each channel | ||||
|     std::array<s8, MaxChannels> outputs; | ||||
|     std::array<s16, MaxChannels> outputs; | ||||
|     /// Input parameters | ||||
|     DelayInfo::ParameterVersion1 parameter; | ||||
|     /// State, updated each call | ||||
|   | ||||
| @@ -232,14 +232,14 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(I3dl2ReverbInfo::I3dl2DelayLi | ||||
|  * Impl. Apply a I3DL2 reverb according to the current state, on the input mix buffers, | ||||
|  * saving the results to the output mix buffers. | ||||
|  * | ||||
|  * @tparam Channels    - Number of channels to process. 1-6. | ||||
|  * @tparam NumChannels - Number of channels to process. 1-6. | ||||
|                          Inputs/outputs should have this many buffers. | ||||
|  * @param state        - State to use, must be initialized (see InitializeI3dl2ReverbEffect). | ||||
|  * @param inputs       - Input mix buffers to perform the reverb on. | ||||
|  * @param outputs      - Output mix buffers to receive the reverbed samples. | ||||
|  * @param sample_count - Number of samples to process. | ||||
|  */ | ||||
| template <size_t Channels> | ||||
| template <size_t NumChannels> | ||||
| static void ApplyI3dl2ReverbEffect(I3dl2ReverbInfo::State& state, | ||||
|                                    std::span<std::span<const s32>> inputs, | ||||
|                                    std::span<std::span<s32>> outputs, const u32 sample_count) { | ||||
| @@ -253,46 +253,46 @@ static void ApplyI3dl2ReverbEffect(I3dl2ReverbInfo::State& state, | ||||
|         0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3, | ||||
|     }; | ||||
|     constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{ | ||||
|         4, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, | ||||
|         2, 0, 0, 1, 1, 1, 1, 4, 4, 4, 1, 1, 1, 0, 0, 0, 0, 5, 5, 5, | ||||
|     }; | ||||
|  | ||||
|     std::span<const u8> tap_indexes{}; | ||||
|     if constexpr (Channels == 1) { | ||||
|     if constexpr (NumChannels == 1) { | ||||
|         tap_indexes = OutTapIndexes1Ch; | ||||
|     } else if constexpr (Channels == 2) { | ||||
|     } else if constexpr (NumChannels == 2) { | ||||
|         tap_indexes = OutTapIndexes2Ch; | ||||
|     } else if constexpr (Channels == 4) { | ||||
|     } else if constexpr (NumChannels == 4) { | ||||
|         tap_indexes = OutTapIndexes4Ch; | ||||
|     } else if constexpr (Channels == 6) { | ||||
|     } else if constexpr (NumChannels == 6) { | ||||
|         tap_indexes = OutTapIndexes6Ch; | ||||
|     } | ||||
|  | ||||
|     for (u32 i = 0; i < sample_count; i++) { | ||||
|     for (u32 sample_index = 0; sample_index < sample_count; sample_index++) { | ||||
|         Common::FixedPoint<50, 14> early_to_late_tap{ | ||||
|             state.early_delay_line.TapOut(state.early_to_late_taps)}; | ||||
|         std::array<Common::FixedPoint<50, 14>, Channels> output_samples{}; | ||||
|         std::array<Common::FixedPoint<50, 14>, NumChannels> output_samples{}; | ||||
|  | ||||
|         for (u32 early_tap = 0; early_tap < I3dl2ReverbInfo::MaxDelayTaps; early_tap++) { | ||||
|             output_samples[tap_indexes[early_tap]] += | ||||
|                 state.early_delay_line.TapOut(state.early_tap_steps[early_tap]) * | ||||
|                 EarlyGains[early_tap]; | ||||
|             if constexpr (Channels == 6) { | ||||
|                 output_samples[5] += | ||||
|             if constexpr (NumChannels == 6) { | ||||
|                 output_samples[static_cast<u32>(Channels::LFE)] += | ||||
|                     state.early_delay_line.TapOut(state.early_tap_steps[early_tap]) * | ||||
|                     EarlyGains[early_tap]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Common::FixedPoint<50, 14> current_sample{}; | ||||
|         for (u32 channel = 0; channel < Channels; channel++) { | ||||
|             current_sample += inputs[channel][i]; | ||||
|         for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|             current_sample += inputs[channel][sample_index]; | ||||
|         } | ||||
|  | ||||
|         state.lowpass_0 = | ||||
|             (current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1).to_float(); | ||||
|         state.early_delay_line.Tick(state.lowpass_0); | ||||
|  | ||||
|         for (u32 channel = 0; channel < Channels; channel++) { | ||||
|         for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|             output_samples[channel] *= state.early_gain; | ||||
|         } | ||||
|  | ||||
| @@ -321,26 +321,34 @@ static void ApplyI3dl2ReverbEffect(I3dl2ReverbInfo::State& state, | ||||
|                 state.fdn_delay_lines[delay_line], mix_matrix[delay_line]); | ||||
|         } | ||||
|  | ||||
|         const auto out_channels{std::min(Channels, size_t(4))}; | ||||
|         for (u32 channel = 0; channel < out_channels; channel++) { | ||||
|             auto out_sample{output_samples[channel] + allpass_samples[channel] + | ||||
|                             state.dry_gain * static_cast<f32>(inputs[channel][i])}; | ||||
|             outputs[channel][i] = | ||||
|                 static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f)); | ||||
|         } | ||||
|         if constexpr (NumChannels == 6) { | ||||
|             const std::array<Common::FixedPoint<50, 14>, MaxChannels> allpass_outputs{ | ||||
|                 allpass_samples[0], allpass_samples[1], allpass_samples[2] - allpass_samples[3], | ||||
|                 allpass_samples[3], allpass_samples[2], allpass_samples[3], | ||||
|             }; | ||||
|  | ||||
|         if constexpr (Channels == 6) { | ||||
|             auto center{ | ||||
|                 state.center_delay_line.Tick((allpass_samples[2] - allpass_samples[3]) * 0.5f)}; | ||||
|             auto out_sample{static_cast<f32>(inputs[4][i]) * state.dry_gain + | ||||
|                             output_samples[4] * state.early_gain + center}; | ||||
|             outputs[4][i] = | ||||
|                 static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f)); | ||||
|             for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|                 Common::FixedPoint<50, 14> allpass{}; | ||||
|  | ||||
|             out_sample = static_cast<f32>(inputs[5][i]) * state.dry_gain + | ||||
|                          output_samples[5] * state.early_gain + allpass_samples[3]; | ||||
|             outputs[5][i] = | ||||
|                 static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f)); | ||||
|                 if (channel == static_cast<u32>(Channels::Center)) { | ||||
|                     allpass = state.center_delay_line.Tick(allpass_outputs[channel] * 0.5f); | ||||
|                 } else { | ||||
|                     allpass = allpass_outputs[channel]; | ||||
|                 } | ||||
|  | ||||
|                 auto out_sample{output_samples[channel] + allpass + | ||||
|                                 state.dry_gain * static_cast<f32>(inputs[channel][sample_index])}; | ||||
|  | ||||
|                 outputs[channel][sample_index] = | ||||
|                     static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f)); | ||||
|             } | ||||
|         } else { | ||||
|             for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|                 auto out_sample{output_samples[channel] + allpass_samples[channel] + | ||||
|                                 state.dry_gain * static_cast<f32>(inputs[channel][sample_index])}; | ||||
|                 outputs[channel][sample_index] = | ||||
|                     static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| #include <string> | ||||
|  | ||||
| #include "audio_core/renderer/command/icommand.h" | ||||
| #include "audio_core/renderer/effect/effect_i3dl2_info.h" | ||||
| #include "audio_core/renderer/effect/i3dl2.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|   | ||||
| @@ -53,6 +53,15 @@ static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& p | ||||
|     constexpr s64 min{std::numeric_limits<s32>::min()}; | ||||
|     constexpr s64 max{std::numeric_limits<s32>::max()}; | ||||
|  | ||||
|     const auto recip_estimate = [](f64 a) -> f64 { | ||||
|         s32 q, s; | ||||
|         f64 r; | ||||
|         q = (s32)(a * 512.0);               /* a in units of 1/512 rounded down */ | ||||
|         r = 1.0 / (((f64)q + 0.5) / 512.0); /* reciprocal r */ | ||||
|         s = (s32)(256.0 * r + 0.5);         /* r in units of 1/256 rounded to nearest */ | ||||
|         return ((f64)s / 256.0); | ||||
|     }; | ||||
|  | ||||
|     if (enabled) { | ||||
|         if (statistics && params.statistics_reset_required) { | ||||
|             for (u32 i = 0; i < params.channel_count; i++) { | ||||
| @@ -75,9 +84,17 @@ static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& p | ||||
|                 state.samples_average[channel] += | ||||
|                     ((abs_sample - state.samples_average[channel]) * coeff).to_float(); | ||||
|  | ||||
|                 // Reciprocal estimate | ||||
|                 auto new_average_sample{Common::FixedPoint<49, 15>( | ||||
|                     recip_estimate(state.samples_average[channel].to_double()))}; | ||||
|                 if (params.processing_mode != LightLimiterInfo::ProcessingMode::Mode1) { | ||||
|                     // Two Newton-Raphson steps | ||||
|                     auto temp{2.0 - (state.samples_average[channel] * new_average_sample)}; | ||||
|                     new_average_sample = 2.0 - (state.samples_average[channel] * temp); | ||||
|                 } | ||||
|  | ||||
|                 auto above_threshold{state.samples_average[channel] > params.threshold}; | ||||
|                 auto attenuation{above_threshold ? params.threshold / state.samples_average[channel] | ||||
|                                                  : 1.0f}; | ||||
|                 auto attenuation{above_threshold ? params.threshold * new_average_sample : 1.0f}; | ||||
|                 coeff = attenuation < state.compression_gain[channel] ? params.attack_coeff | ||||
|                                                                       : params.release_coeff; | ||||
|                 state.compression_gain[channel] += | ||||
| @@ -173,8 +190,6 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP | ||||
| } | ||||
|  | ||||
| void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) { | ||||
|     auto state_{reinterpret_cast<LightLimiterInfo::State*>(state)}; | ||||
|  | ||||
|     std::vector<std::span<const s32>> input_buffers(parameter.channel_count); | ||||
|     std::vector<std::span<s32>> output_buffers(parameter.channel_count); | ||||
|  | ||||
| @@ -185,6 +200,8 @@ void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& proc | ||||
|                                                           processor.sample_count); | ||||
|     } | ||||
|  | ||||
|     auto state_{reinterpret_cast<LightLimiterInfo::State*>(state)}; | ||||
|  | ||||
|     if (effect_enabled) { | ||||
|         if (parameter.state == LightLimiterInfo::ParameterState::Updating) { | ||||
|             UpdateLightLimiterEffectParameter(parameter, *state_); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| #include <string> | ||||
|  | ||||
| #include "audio_core/renderer/command/icommand.h" | ||||
| #include "audio_core/renderer/effect/effect_light_limiter_info.h" | ||||
| #include "audio_core/renderer/effect/light_limiter.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|   | ||||
| @@ -239,7 +239,7 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine& | ||||
|  * Impl. Apply a Reverb according to the current state, on the input mix buffers, | ||||
|  * saving the results to the output mix buffers. | ||||
|  * | ||||
|  * @tparam Channels    - Number of channels to process. 1-6. | ||||
|  * @tparam NumChannels - Number of channels to process. 1-6. | ||||
|                          Inputs/outputs should have this many buffers. | ||||
|  * @param params       - Input parameters to update the state. | ||||
|  * @param state        - State to use, must be initialized (see InitializeReverbEffect). | ||||
| @@ -247,7 +247,7 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine& | ||||
|  * @param outputs      - Output mix buffers to receive the reverbed samples. | ||||
|  * @param sample_count - Number of samples to process. | ||||
|  */ | ||||
| template <size_t Channels> | ||||
| template <size_t NumChannels> | ||||
| static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state, | ||||
|                               std::vector<std::span<const s32>>& inputs, | ||||
|                               std::vector<std::span<s32>>& outputs, const u32 sample_count) { | ||||
| @@ -261,38 +261,38 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever | ||||
|         0, 0, 1, 1, 0, 1, 2, 2, 3, 3, | ||||
|     }; | ||||
|     constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{ | ||||
|         0, 0, 1, 1, 4, 4, 2, 2, 3, 3, | ||||
|         0, 0, 1, 1, 2, 2, 4, 4, 5, 5, | ||||
|     }; | ||||
|  | ||||
|     std::span<const u8> tap_indexes{}; | ||||
|     if constexpr (Channels == 1) { | ||||
|     if constexpr (NumChannels == 1) { | ||||
|         tap_indexes = OutTapIndexes1Ch; | ||||
|     } else if constexpr (Channels == 2) { | ||||
|     } else if constexpr (NumChannels == 2) { | ||||
|         tap_indexes = OutTapIndexes2Ch; | ||||
|     } else if constexpr (Channels == 4) { | ||||
|     } else if constexpr (NumChannels == 4) { | ||||
|         tap_indexes = OutTapIndexes4Ch; | ||||
|     } else if constexpr (Channels == 6) { | ||||
|     } else if constexpr (NumChannels == 6) { | ||||
|         tap_indexes = OutTapIndexes6Ch; | ||||
|     } | ||||
|  | ||||
|     for (u32 sample_index = 0; sample_index < sample_count; sample_index++) { | ||||
|         std::array<Common::FixedPoint<50, 14>, Channels> output_samples{}; | ||||
|         std::array<Common::FixedPoint<50, 14>, NumChannels> output_samples{}; | ||||
|  | ||||
|         for (u32 early_tap = 0; early_tap < ReverbInfo::MaxDelayTaps; early_tap++) { | ||||
|             const auto sample{state.pre_delay_line.TapOut(state.early_delay_times[early_tap]) * | ||||
|                               state.early_gains[early_tap]}; | ||||
|             output_samples[tap_indexes[early_tap]] += sample; | ||||
|             if constexpr (Channels == 6) { | ||||
|                 output_samples[5] += sample; | ||||
|             if constexpr (NumChannels == 6) { | ||||
|                 output_samples[static_cast<u32>(Channels::LFE)] += sample; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if constexpr (Channels == 6) { | ||||
|             output_samples[5] *= 0.2f; | ||||
|         if constexpr (NumChannels == 6) { | ||||
|             output_samples[static_cast<u32>(Channels::LFE)] *= 0.2f; | ||||
|         } | ||||
|  | ||||
|         Common::FixedPoint<50, 14> input_sample{}; | ||||
|         for (u32 channel = 0; channel < Channels; channel++) { | ||||
|         for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|             input_sample += inputs[channel][sample_index]; | ||||
|         } | ||||
|  | ||||
| @@ -316,34 +316,41 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever | ||||
|             state.prev_feedback_output[1] - state.prev_feedback_output[2] + pre_delay_sample, | ||||
|         }; | ||||
|  | ||||
|         std::array<Common::FixedPoint<50, 14>, ReverbInfo::MaxDelayLines> out_line_samples{}; | ||||
|         std::array<Common::FixedPoint<50, 14>, ReverbInfo::MaxDelayLines> allpass_samples{}; | ||||
|         for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) { | ||||
|             out_line_samples[i] = Axfx2AllPassTick(state.decay_delay_lines[i], | ||||
|                                                    state.fdn_delay_lines[i], mix_matrix[i]); | ||||
|             allpass_samples[i] = Axfx2AllPassTick(state.decay_delay_lines[i], | ||||
|                                                   state.fdn_delay_lines[i], mix_matrix[i]); | ||||
|         } | ||||
|  | ||||
|         const auto dry_gain{Common::FixedPoint<50, 14>::from_base(params.dry_gain)}; | ||||
|         const auto wet_gain{Common::FixedPoint<50, 14>::from_base(params.wet_gain)}; | ||||
|  | ||||
|         const auto out_channels{std::min(Channels, size_t(4))}; | ||||
|         for (u32 channel = 0; channel < out_channels; channel++) { | ||||
|             auto in_sample{inputs[channel][channel] * dry_gain}; | ||||
|             auto out_sample{((output_samples[channel] + out_line_samples[channel]) * wet_gain) / | ||||
|                             64}; | ||||
|             outputs[channel][sample_index] = (in_sample + out_sample).to_int(); | ||||
|         } | ||||
|         if constexpr (NumChannels == 6) { | ||||
|             const std::array<Common::FixedPoint<50, 14>, MaxChannels> allpass_outputs{ | ||||
|                 allpass_samples[0], allpass_samples[1], allpass_samples[2] - allpass_samples[3], | ||||
|                 allpass_samples[3], allpass_samples[2], allpass_samples[3], | ||||
|             }; | ||||
|  | ||||
|         if constexpr (Channels == 6) { | ||||
|             auto center{ | ||||
|                 state.center_delay_line.Tick((out_line_samples[2] - out_line_samples[3]) * 0.5f)}; | ||||
|             auto in_sample{inputs[4][sample_index] * dry_gain}; | ||||
|             auto out_sample{((output_samples[4] + center) * wet_gain) / 64}; | ||||
|             for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|                 auto in_sample{inputs[channel][sample_index] * dry_gain}; | ||||
|  | ||||
|             outputs[4][sample_index] = (in_sample + out_sample).to_int(); | ||||
|                 Common::FixedPoint<50, 14> allpass{}; | ||||
|                 if (channel == static_cast<u32>(Channels::Center)) { | ||||
|                     allpass = state.center_delay_line.Tick(allpass_outputs[channel] * 0.5f); | ||||
|                 } else { | ||||
|                     allpass = allpass_outputs[channel]; | ||||
|                 } | ||||
|  | ||||
|             in_sample = inputs[5][sample_index] * dry_gain; | ||||
|             out_sample = ((output_samples[5] + out_line_samples[3]) * wet_gain) / 64; | ||||
|  | ||||
|             outputs[5][sample_index] = (in_sample + out_sample).to_int(); | ||||
|                 auto out_sample{((output_samples[channel] + allpass) * wet_gain) / 64}; | ||||
|                 outputs[channel][sample_index] = (in_sample + out_sample).to_int(); | ||||
|             } | ||||
|         } else { | ||||
|             for (u32 channel = 0; channel < NumChannels; channel++) { | ||||
|                 auto in_sample{inputs[channel][sample_index] * dry_gain}; | ||||
|                 auto out_sample{((output_samples[channel] + allpass_samples[channel]) * wet_gain) / | ||||
|                                 64}; | ||||
|                 outputs[channel][sample_index] = (in_sample + out_sample).to_int(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
| #include <string> | ||||
|  | ||||
| #include "audio_core/renderer/command/icommand.h" | ||||
| #include "audio_core/renderer/effect/effect_reverb_info.h" | ||||
| #include "audio_core/renderer/effect/reverb.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|   | ||||
| @@ -42,6 +42,7 @@ enum class CommandId : u8 { | ||||
|     /* 0x1B */ LightLimiterVersion2, | ||||
|     /* 0x1C */ MultiTapBiquadFilter, | ||||
|     /* 0x1D */ Capture, | ||||
|     /* 0x1E */ Compressor, | ||||
| }; | ||||
|  | ||||
| constexpr u32 CommandMagic{0xCAFEBABE}; | ||||
|   | ||||
| @@ -43,8 +43,9 @@ void DepopForMixBuffersCommand::Dump([[maybe_unused]] const ADSP::CommandListPro | ||||
| } | ||||
|  | ||||
| void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& processor) { | ||||
|     std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), MaxMixBuffers}; | ||||
|     auto end_index{std::min(processor.buffer_count, input + count)}; | ||||
|     std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index}; | ||||
|  | ||||
|     for (u32 index = input; index < end_index; index++) { | ||||
|         const auto depop_sample{depop_buff[index]}; | ||||
|         if (depop_sample != 0) { | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class CommandListProcessor; | ||||
|  | ||||
| /** | ||||
|  * AudioRenderer command for downmixing 6 channels to 2. | ||||
|  * Channels: | ||||
|  * Channel layout (SMPTE): | ||||
|  *     0 - front left | ||||
|  *     1 - front right | ||||
|  *     2 - center | ||||
|   | ||||
							
								
								
									
										93
									
								
								src/audio_core/renderer/effect/aux_.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										93
									
								
								src/audio_core/renderer/effect/aux_.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/aux_.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                      const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(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; | ||||
|     if (buffer_unmapped || in_params.is_new) { | ||||
|         const bool send_unmapped{!pool_mapper.TryAttachBuffer( | ||||
|             error_info, workbuffers[0], in_specific->send_buffer_info_address, | ||||
|             sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))}; | ||||
|         const bool return_unmapped{!pool_mapper.TryAttachBuffer( | ||||
|             error_info, workbuffers[1], in_specific->return_buffer_info_address, | ||||
|             sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))}; | ||||
|  | ||||
|         buffer_unmapped = send_unmapped || return_unmapped; | ||||
|  | ||||
|         if (!buffer_unmapped) { | ||||
|             auto send{workbuffers[0].GetReference(false)}; | ||||
|             send_buffer_info = send + sizeof(AuxInfoDsp); | ||||
|             send_buffer = send + sizeof(AuxBufferInfo); | ||||
|  | ||||
|             auto ret{workbuffers[1].GetReference(false)}; | ||||
|             return_buffer_info = ret + sizeof(AuxInfoDsp); | ||||
|             return_buffer = ret + sizeof(AuxBufferInfo); | ||||
|         } | ||||
|     } else { | ||||
|         error_info.error_code = ResultSuccess; | ||||
|         error_info.address = CpuAddr(0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                      const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion2*>(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; | ||||
|  | ||||
|     if (buffer_unmapped || in_params.is_new) { | ||||
|         const bool send_unmapped{!pool_mapper.TryAttachBuffer( | ||||
|             error_info, workbuffers[0], params->send_buffer_info_address, | ||||
|             sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))}; | ||||
|         const bool return_unmapped{!pool_mapper.TryAttachBuffer( | ||||
|             error_info, workbuffers[1], params->return_buffer_info_address, | ||||
|             sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))}; | ||||
|  | ||||
|         buffer_unmapped = send_unmapped || return_unmapped; | ||||
|  | ||||
|         if (!buffer_unmapped) { | ||||
|             auto send{workbuffers[0].GetReference(false)}; | ||||
|             send_buffer_info = send + sizeof(AuxInfoDsp); | ||||
|             send_buffer = send + sizeof(AuxBufferInfo); | ||||
|  | ||||
|             auto ret{workbuffers[1].GetReference(false)}; | ||||
|             return_buffer_info = ret + sizeof(AuxInfoDsp); | ||||
|             return_buffer = ret + sizeof(AuxBufferInfo); | ||||
|         } | ||||
|     } else { | ||||
|         error_info.error_code = ResultSuccess; | ||||
|         error_info.address = CpuAddr(0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AuxInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void AuxInfo::InitializeResultState(EffectResultState& result_state) {} | ||||
|  | ||||
| void AuxInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} | ||||
|  | ||||
| CpuAddr AuxInfo::GetWorkbuffer(s32 index) { | ||||
|     return workbuffers[index].GetReference(true); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										123
									
								
								src/audio_core/renderer/effect/aux_.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										123
									
								
								src/audio_core/renderer/effect/aux_.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
| /** | ||||
|  * Auxiliary Buffer used for Aux commands. | ||||
|  * Send and return buffers are available (names from the game's perspective). | ||||
|  * Send is read by the host, containing a buffer of samples to be used for whatever purpose. | ||||
|  * Return is written by the host, writing a mix buffer back to the game. | ||||
|  * This allows the game to use pre-processed samples skipping the other render processing, | ||||
|  * and to examine or modify what the audio renderer has generated. | ||||
|  */ | ||||
| class AuxInfo : public EffectInfoBase { | ||||
| public: | ||||
|     struct ParameterVersion1 { | ||||
|         /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; | ||||
|         /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; | ||||
|         /* 0x30 */ u32 mix_buffer_count; | ||||
|         /* 0x34 */ u32 sample_rate; | ||||
|         /* 0x38 */ u32 count_max; | ||||
|         /* 0x3C */ u32 mix_buffer_count_max; | ||||
|         /* 0x40 */ CpuAddr send_buffer_info_address; | ||||
|         /* 0x48 */ CpuAddr send_buffer_address; | ||||
|         /* 0x50 */ CpuAddr return_buffer_info_address; | ||||
|         /* 0x58 */ CpuAddr return_buffer_address; | ||||
|         /* 0x60 */ u32 mix_buffer_sample_size; | ||||
|         /* 0x64 */ u32 sample_count; | ||||
|         /* 0x68 */ u32 mix_buffer_sample_count; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||||
|                   "AuxInfo::ParameterVersion1 has the wrong size!"); | ||||
|  | ||||
|     struct ParameterVersion2 { | ||||
|         /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; | ||||
|         /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; | ||||
|         /* 0x30 */ u32 mix_buffer_count; | ||||
|         /* 0x34 */ u32 sample_rate; | ||||
|         /* 0x38 */ u32 count_max; | ||||
|         /* 0x3C */ u32 mix_buffer_count_max; | ||||
|         /* 0x40 */ CpuAddr send_buffer_info_address; | ||||
|         /* 0x48 */ CpuAddr send_buffer_address; | ||||
|         /* 0x50 */ CpuAddr return_buffer_info_address; | ||||
|         /* 0x58 */ CpuAddr return_buffer_address; | ||||
|         /* 0x60 */ u32 mix_buffer_sample_size; | ||||
|         /* 0x64 */ u32 sample_count; | ||||
|         /* 0x68 */ u32 mix_buffer_sample_count; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||||
|                   "AuxInfo::ParameterVersion2 has the wrong size!"); | ||||
|  | ||||
|     struct AuxInfoDsp { | ||||
|         /* 0x00 */ u32 read_offset; | ||||
|         /* 0x04 */ u32 write_offset; | ||||
|         /* 0x08 */ u32 lost_sample_count; | ||||
|         /* 0x0C */ u32 total_sample_count; | ||||
|         /* 0x10 */ char unk10[0x30]; | ||||
|     }; | ||||
|     static_assert(sizeof(AuxInfoDsp) == 0x40, "AuxInfo::AuxInfoDsp has the wrong size!"); | ||||
|  | ||||
|     struct AuxBufferInfo { | ||||
|         /* 0x00 */ AuxInfoDsp cpu_info; | ||||
|         /* 0x40 */ AuxInfoDsp dsp_info; | ||||
|     }; | ||||
|     static_assert(sizeof(AuxBufferInfo) == 0x80, "AuxInfo::AuxBufferInfo has the wrong size!"); | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Initialize a new result state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param result_state - Result state to initialize. | ||||
|      */ | ||||
|     void InitializeResultState(EffectResultState& result_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param cpu_state - Host-side result state to update. | ||||
|      * @param dsp_state - AudioRenderer-side result state to update from. | ||||
|      */ | ||||
|     void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Get a workbuffer assigned to this effect with the given index. | ||||
|      * | ||||
|      * @param index - Workbuffer index. | ||||
|      * @return Address of the buffer. | ||||
|      */ | ||||
|     CpuAddr GetWorkbuffer(s32 index) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										52
									
								
								src/audio_core/renderer/effect/biquad_filter.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										52
									
								
								src/audio_core/renderer/effect/biquad_filter.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/biquad_filter.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                               const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(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); | ||||
| } | ||||
|  | ||||
| void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                               const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion2*>(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); | ||||
| } | ||||
|  | ||||
| void BiquadFilterInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
|  | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|     params->state = ParameterState::Updated; | ||||
| } | ||||
|  | ||||
| void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {} | ||||
|  | ||||
| void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, | ||||
|                                          EffectResultState& dsp_state) {} | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										79
									
								
								src/audio_core/renderer/effect/biquad_filter.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										79
									
								
								src/audio_core/renderer/effect/biquad_filter.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| class BiquadFilterInfo : public EffectInfoBase { | ||||
| public: | ||||
|     struct ParameterVersion1 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ std::array<s16, 3> b; | ||||
|         /* 0x12 */ std::array<s16, 2> a; | ||||
|         /* 0x16 */ s8 channel_count; | ||||
|         /* 0x17 */ ParameterState state; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||||
|                   "BiquadFilterInfo::ParameterVersion1 has the wrong size!"); | ||||
|  | ||||
|     struct ParameterVersion2 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ std::array<s16, 3> b; | ||||
|         /* 0x12 */ std::array<s16, 2> a; | ||||
|         /* 0x16 */ s8 channel_count; | ||||
|         /* 0x17 */ ParameterState state; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||||
|                   "BiquadFilterInfo::ParameterVersion2 has the wrong size!"); | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Initialize a new result state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param result_state - Result state to initialize. | ||||
|      */ | ||||
|     void InitializeResultState(EffectResultState& result_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param cpu_state - Host-side result state to update. | ||||
|      * @param dsp_state - AudioRenderer-side result state to update from. | ||||
|      */ | ||||
|     void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										49
									
								
								src/audio_core/renderer/effect/buffer_mixer.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										49
									
								
								src/audio_core/renderer/effect/buffer_mixer.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/buffer_mixer.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                              const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(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); | ||||
| } | ||||
|  | ||||
| void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                              const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion2*>(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); | ||||
| } | ||||
|  | ||||
| void BufferMixerInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void BufferMixerInfo::InitializeResultState(EffectResultState& result_state) {} | ||||
|  | ||||
| void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state, | ||||
|                                         EffectResultState& dsp_state) {} | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										75
									
								
								src/audio_core/renderer/effect/buffer_mixer.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										75
									
								
								src/audio_core/renderer/effect/buffer_mixer.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| class BufferMixerInfo : public EffectInfoBase { | ||||
| public: | ||||
|     struct ParameterVersion1 { | ||||
|         /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; | ||||
|         /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; | ||||
|         /* 0x30 */ std::array<f32, MaxMixBuffers> volumes; | ||||
|         /* 0x90 */ u32 mix_count; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||||
|                   "BufferMixerInfo::ParameterVersion1 has the wrong size!"); | ||||
|  | ||||
|     struct ParameterVersion2 { | ||||
|         /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; | ||||
|         /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; | ||||
|         /* 0x30 */ std::array<f32, MaxMixBuffers> volumes; | ||||
|         /* 0x90 */ u32 mix_count; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||||
|                   "BufferMixerInfo::ParameterVersion2 has the wrong size!"); | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Initialize a new result state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param result_state - Result state to initialize. | ||||
|      */ | ||||
|     void InitializeResultState(EffectResultState& result_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param cpu_state - Host-side result state to update. | ||||
|      * @param dsp_state - AudioRenderer-side result state to update from. | ||||
|      */ | ||||
|     void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										82
									
								
								src/audio_core/renderer/effect/capture.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										82
									
								
								src/audio_core/renderer/effect/capture.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/aux_.h" | ||||
| #include "audio_core/renderer/effect/capture.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                          const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{ | ||||
|         reinterpret_cast<const AuxInfo::ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<AuxInfo::ParameterVersion1*>(parameter.data())}; | ||||
|  | ||||
|     std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion1)); | ||||
|     mix_id = in_params.mix_id; | ||||
|     process_order = in_params.process_order; | ||||
|     enabled = in_params.enabled; | ||||
|     if (buffer_unmapped || in_params.is_new) { | ||||
|         buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|             error_info, workbuffers[0], in_specific->send_buffer_info_address, | ||||
|             in_specific->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo)); | ||||
|  | ||||
|         if (!buffer_unmapped) { | ||||
|             const auto send_address{workbuffers[0].GetReference(false)}; | ||||
|             send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp); | ||||
|             send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo); | ||||
|             return_buffer_info = 0; | ||||
|             return_buffer = 0; | ||||
|         } | ||||
|     } else { | ||||
|         error_info.error_code = ResultSuccess; | ||||
|         error_info.address = CpuAddr(0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                          const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{ | ||||
|         reinterpret_cast<const AuxInfo::ParameterVersion2*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<AuxInfo::ParameterVersion2*>(parameter.data())}; | ||||
|  | ||||
|     std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion2)); | ||||
|     mix_id = in_params.mix_id; | ||||
|     process_order = in_params.process_order; | ||||
|     enabled = in_params.enabled; | ||||
|  | ||||
|     if (buffer_unmapped || in_params.is_new) { | ||||
|         buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|             error_info, workbuffers[0], params->send_buffer_info_address, | ||||
|             params->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo)); | ||||
|  | ||||
|         if (!buffer_unmapped) { | ||||
|             const auto send_address{workbuffers[0].GetReference(false)}; | ||||
|             send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp); | ||||
|             send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo); | ||||
|             return_buffer_info = 0; | ||||
|             return_buffer = 0; | ||||
|         } | ||||
|     } else { | ||||
|         error_info.error_code = ResultSuccess; | ||||
|         error_info.address = CpuAddr(0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void CaptureInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
| } | ||||
|  | ||||
| void CaptureInfo::InitializeResultState(EffectResultState& result_state) {} | ||||
|  | ||||
| void CaptureInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} | ||||
|  | ||||
| CpuAddr CaptureInfo::GetWorkbuffer(s32 index) { | ||||
|     return workbuffers[index].GetReference(true); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										65
									
								
								src/audio_core/renderer/effect/capture.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										65
									
								
								src/audio_core/renderer/effect/capture.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| class CaptureInfo : public EffectInfoBase { | ||||
| public: | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Initialize a new result state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param result_state - Result state to initialize. | ||||
|      */ | ||||
|     void InitializeResultState(EffectResultState& result_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param cpu_state - Host-side result state to update. | ||||
|      * @param dsp_state - AudioRenderer-side result state to update from. | ||||
|      */ | ||||
|     void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Get a workbuffer assigned to this effect with the given index. | ||||
|      * | ||||
|      * @param index - Workbuffer index. | ||||
|      * @return Address of the buffer. | ||||
|      */ | ||||
|     CpuAddr GetWorkbuffer(s32 index) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										40
									
								
								src/audio_core/renderer/effect/compressor.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										40
									
								
								src/audio_core/renderer/effect/compressor.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/compressor.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                             const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {} | ||||
|  | ||||
| void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                             const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(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); | ||||
| } | ||||
|  | ||||
| void CompressorInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
|  | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|     params->state = ParameterState::Updated; | ||||
| } | ||||
|  | ||||
| CpuAddr CompressorInfo::GetWorkbuffer(s32 index) { | ||||
|     return GetSingleBuffer(index); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										106
									
								
								src/audio_core/renderer/effect/compressor.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										106
									
								
								src/audio_core/renderer/effect/compressor.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/fixed_point.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| class CompressorInfo : public EffectInfoBase { | ||||
| public: | ||||
|     struct ParameterVersion1 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ s16 channel_count_max; | ||||
|         /* 0x0E */ s16 channel_count; | ||||
|         /* 0x10 */ s32 sample_rate; | ||||
|         /* 0x14 */ f32 threshold; | ||||
|         /* 0x18 */ f32 compressor_ratio; | ||||
|         /* 0x1C */ s32 attack_time; | ||||
|         /* 0x20 */ s32 release_time; | ||||
|         /* 0x24 */ f32 unk_24; | ||||
|         /* 0x28 */ f32 unk_28; | ||||
|         /* 0x2C */ f32 unk_2C; | ||||
|         /* 0x30 */ f32 out_gain; | ||||
|         /* 0x34 */ ParameterState state; | ||||
|         /* 0x35 */ bool makeup_gain_enabled; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||||
|                   "CompressorInfo::ParameterVersion1 has the wrong size!"); | ||||
|  | ||||
|     struct ParameterVersion2 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ s16 channel_count_max; | ||||
|         /* 0x0E */ s16 channel_count; | ||||
|         /* 0x10 */ s32 sample_rate; | ||||
|         /* 0x14 */ f32 threshold; | ||||
|         /* 0x18 */ f32 compressor_ratio; | ||||
|         /* 0x1C */ s32 attack_time; | ||||
|         /* 0x20 */ s32 release_time; | ||||
|         /* 0x24 */ f32 unk_24; | ||||
|         /* 0x28 */ f32 unk_28; | ||||
|         /* 0x2C */ f32 unk_2C; | ||||
|         /* 0x30 */ f32 out_gain; | ||||
|         /* 0x34 */ ParameterState state; | ||||
|         /* 0x35 */ bool makeup_gain_enabled; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||||
|                   "CompressorInfo::ParameterVersion2 has the wrong size!"); | ||||
|  | ||||
|     struct State { | ||||
|         f32 unk_00; | ||||
|         f32 unk_04; | ||||
|         f32 unk_08; | ||||
|         f32 unk_0C; | ||||
|         f32 unk_10; | ||||
|         f32 unk_14; | ||||
|         f32 unk_18; | ||||
|         f32 makeup_gain; | ||||
|         f32 unk_20; | ||||
|         char unk_24[0x1C]; | ||||
|     }; | ||||
|     static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||||
|                   "CompressorInfo::State has the wrong size!"); | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Get a workbuffer assigned to this effect with the given index. | ||||
|      * | ||||
|      * @param index - Workbuffer index. | ||||
|      * @return Address of the buffer. | ||||
|      */ | ||||
|     CpuAddr GetWorkbuffer(s32 index) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										93
									
								
								src/audio_core/renderer/effect/delay.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										93
									
								
								src/audio_core/renderer/effect/delay.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/delay.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                        const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|  | ||||
|     if (IsChannelCountValid(in_specific->channel_count_max)) { | ||||
|         const auto old_state{params->state}; | ||||
|         std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||||
|         mix_id = in_params.mix_id; | ||||
|         process_order = in_params.process_order; | ||||
|         enabled = in_params.enabled; | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count)) { | ||||
|             params->channel_count = params->channel_count_max; | ||||
|         } | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count) || | ||||
|             old_state != ParameterState::Updated) { | ||||
|             params->state = old_state; | ||||
|         } | ||||
|  | ||||
|         if (buffer_unmapped || in_params.is_new) { | ||||
|             usage_state = UsageState::New; | ||||
|             params->state = ParameterState::Initialized; | ||||
|             buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|                 error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     error_info.error_code = ResultSuccess; | ||||
|     error_info.address = CpuAddr(0); | ||||
| } | ||||
|  | ||||
| void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                        const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|  | ||||
|     if (IsChannelCountValid(in_specific->channel_count_max)) { | ||||
|         const auto old_state{params->state}; | ||||
|         std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||||
|         mix_id = in_params.mix_id; | ||||
|         process_order = in_params.process_order; | ||||
|         enabled = in_params.enabled; | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count)) { | ||||
|             params->channel_count = params->channel_count_max; | ||||
|         } | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count) || | ||||
|             old_state != ParameterState::Updated) { | ||||
|             params->state = old_state; | ||||
|         } | ||||
|  | ||||
|         if (buffer_unmapped || in_params.is_new) { | ||||
|             usage_state = UsageState::New; | ||||
|             params->state = ParameterState::Initialized; | ||||
|             buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|                 error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     error_info.error_code = ResultSuccess; | ||||
|     error_info.address = CpuAddr(0); | ||||
| } | ||||
|  | ||||
| void DelayInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
|  | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|     params->state = ParameterState::Updated; | ||||
| } | ||||
|  | ||||
| void DelayInfo::InitializeResultState(EffectResultState& result_state) {} | ||||
|  | ||||
| void DelayInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} | ||||
|  | ||||
| CpuAddr DelayInfo::GetWorkbuffer(s32 index) { | ||||
|     return GetSingleBuffer(index); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										135
									
								
								src/audio_core/renderer/effect/delay.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										135
									
								
								src/audio_core/renderer/effect/delay.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <vector> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/fixed_point.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| class DelayInfo : public EffectInfoBase { | ||||
| public: | ||||
|     struct ParameterVersion1 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ u16 channel_count_max; | ||||
|         /* 0x0E */ u16 channel_count; | ||||
|         /* 0x10 */ u32 delay_time_max; | ||||
|         /* 0x14 */ u32 delay_time; | ||||
|         /* 0x18 */ Common::FixedPoint<18, 14> sample_rate; | ||||
|         /* 0x1C */ Common::FixedPoint<18, 14> in_gain; | ||||
|         /* 0x20 */ Common::FixedPoint<18, 14> feedback_gain; | ||||
|         /* 0x24 */ Common::FixedPoint<18, 14> wet_gain; | ||||
|         /* 0x28 */ Common::FixedPoint<18, 14> dry_gain; | ||||
|         /* 0x2C */ Common::FixedPoint<18, 14> channel_spread; | ||||
|         /* 0x30 */ Common::FixedPoint<18, 14> lowpass_amount; | ||||
|         /* 0x34 */ ParameterState state; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||||
|                   "DelayInfo::ParameterVersion1 has the wrong size!"); | ||||
|  | ||||
|     struct ParameterVersion2 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ s16 channel_count_max; | ||||
|         /* 0x0E */ s16 channel_count; | ||||
|         /* 0x10 */ s32 delay_time_max; | ||||
|         /* 0x14 */ s32 delay_time; | ||||
|         /* 0x18 */ s32 sample_rate; | ||||
|         /* 0x1C */ s32 in_gain; | ||||
|         /* 0x20 */ s32 feedback_gain; | ||||
|         /* 0x24 */ s32 wet_gain; | ||||
|         /* 0x28 */ s32 dry_gain; | ||||
|         /* 0x2C */ s32 channel_spread; | ||||
|         /* 0x30 */ s32 lowpass_amount; | ||||
|         /* 0x34 */ ParameterState state; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||||
|                   "DelayInfo::ParameterVersion2 has the wrong size!"); | ||||
|  | ||||
|     struct DelayLine { | ||||
|         Common::FixedPoint<50, 14> Read() const { | ||||
|             return buffer[buffer_pos]; | ||||
|         } | ||||
|  | ||||
|         void Write(const Common::FixedPoint<50, 14> value) { | ||||
|             buffer[buffer_pos] = value; | ||||
|             buffer_pos = static_cast<u32>((buffer_pos + 1) % buffer.size()); | ||||
|         } | ||||
|  | ||||
|         s32 sample_count_max{}; | ||||
|         s32 sample_count{}; | ||||
|         std::vector<Common::FixedPoint<50, 14>> buffer{}; | ||||
|         u32 buffer_pos{}; | ||||
|         Common::FixedPoint<18, 14> decay_rate{}; | ||||
|     }; | ||||
|  | ||||
|     struct State { | ||||
|         /* 0x000 */ std::array<s32, 8> unk_000; | ||||
|         /* 0x020 */ std::array<DelayLine, MaxChannels> delay_lines; | ||||
|         /* 0x0B0 */ Common::FixedPoint<18, 14> feedback_gain; | ||||
|         /* 0x0B4 */ Common::FixedPoint<18, 14> delay_feedback_gain; | ||||
|         /* 0x0B8 */ Common::FixedPoint<18, 14> delay_feedback_cross_gain; | ||||
|         /* 0x0BC */ Common::FixedPoint<18, 14> lowpass_gain; | ||||
|         /* 0x0C0 */ Common::FixedPoint<18, 14> lowpass_feedback_gain; | ||||
|         /* 0x0C4 */ std::array<Common::FixedPoint<50, 14>, MaxChannels> lowpass_z; | ||||
|     }; | ||||
|     static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||||
|                   "DelayInfo::State has the wrong size!"); | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Initialize a new result state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param result_state - Result state to initialize. | ||||
|      */ | ||||
|     void InitializeResultState(EffectResultState& result_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param cpu_state - Host-side result state to update. | ||||
|      * @param dsp_state - AudioRenderer-side result state to update from. | ||||
|      */ | ||||
|     void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Get a workbuffer assigned to this effect with the given index. | ||||
|      * | ||||
|      * @param index - Workbuffer index. | ||||
|      * @return Address of the buffer. | ||||
|      */ | ||||
|     CpuAddr GetWorkbuffer(s32 index) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
| @@ -29,6 +29,7 @@ public: | ||||
|         BiquadFilter, | ||||
|         LightLimiter, | ||||
|         Capture, | ||||
|         Compressor, | ||||
|     }; | ||||
|  | ||||
|     enum class UsageState { | ||||
|   | ||||
| @@ -3,14 +3,15 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "audio_core/renderer/effect/effect_aux_info.h" | ||||
| #include "audio_core/renderer/effect/effect_biquad_filter_info.h" | ||||
| #include "audio_core/renderer/effect/effect_buffer_mixer_info.h" | ||||
| #include "audio_core/renderer/effect/effect_capture_info.h" | ||||
| #include "audio_core/renderer/effect/effect_delay_info.h" | ||||
| #include "audio_core/renderer/effect/effect_i3dl2_info.h" | ||||
| #include "audio_core/renderer/effect/effect_light_limiter_info.h" | ||||
| #include "audio_core/renderer/effect/effect_reverb_info.h" | ||||
| #include "audio_core/renderer/effect/aux_.h" | ||||
| #include "audio_core/renderer/effect/biquad_filter.h" | ||||
| #include "audio_core/renderer/effect/buffer_mixer.h" | ||||
| #include "audio_core/renderer/effect/capture.h" | ||||
| #include "audio_core/renderer/effect/compressor.h" | ||||
| #include "audio_core/renderer/effect/delay.h" | ||||
| #include "audio_core/renderer/effect/i3dl2.h" | ||||
| #include "audio_core/renderer/effect/light_limiter.h" | ||||
| #include "audio_core/renderer/effect/reverb.h" | ||||
| #include "common/common_types.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
| @@ -60,6 +61,10 @@ static void ResetEffect(EffectInfoBase* effect, const EffectInfoBase::Type type) | ||||
|         std::construct_at<CaptureInfo>(reinterpret_cast<CaptureInfo*>(effect)); | ||||
|         effect->SetType(EffectInfoBase::Type::Capture); | ||||
|         break; | ||||
|     case EffectInfoBase::Type::Compressor: | ||||
|         std::construct_at<CompressorInfo>(reinterpret_cast<CompressorInfo*>(effect)); | ||||
|         effect->SetType(EffectInfoBase::Type::Compressor); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										94
									
								
								src/audio_core/renderer/effect/i3dl2.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										94
									
								
								src/audio_core/renderer/effect/i3dl2.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/i3dl2.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                              const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|  | ||||
|     if (IsChannelCountValid(in_specific->channel_count_max)) { | ||||
|         const auto old_state{params->state}; | ||||
|         std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||||
|         mix_id = in_params.mix_id; | ||||
|         process_order = in_params.process_order; | ||||
|         enabled = in_params.enabled; | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count)) { | ||||
|             params->channel_count = params->channel_count_max; | ||||
|         } | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count) || | ||||
|             old_state != ParameterState::Updated) { | ||||
|             params->state = old_state; | ||||
|         } | ||||
|  | ||||
|         if (buffer_unmapped || in_params.is_new) { | ||||
|             usage_state = UsageState::New; | ||||
|             params->state = ParameterState::Initialized; | ||||
|             buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|                 error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     error_info.error_code = ResultSuccess; | ||||
|     error_info.address = CpuAddr(0); | ||||
| } | ||||
|  | ||||
| void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                              const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|  | ||||
|     if (IsChannelCountValid(in_specific->channel_count_max)) { | ||||
|         const auto old_state{params->state}; | ||||
|         std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||||
|         mix_id = in_params.mix_id; | ||||
|         process_order = in_params.process_order; | ||||
|         enabled = in_params.enabled; | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count)) { | ||||
|             params->channel_count = params->channel_count_max; | ||||
|         } | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count) || | ||||
|             old_state != ParameterState::Updated) { | ||||
|             params->state = old_state; | ||||
|         } | ||||
|  | ||||
|         if (buffer_unmapped || in_params.is_new) { | ||||
|             usage_state = UsageState::New; | ||||
|             params->state = ParameterState::Initialized; | ||||
|             buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|                 error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     error_info.error_code = ResultSuccess; | ||||
|     error_info.address = CpuAddr(0); | ||||
| } | ||||
|  | ||||
| void I3dl2ReverbInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
|  | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|     params->state = ParameterState::Updated; | ||||
| } | ||||
|  | ||||
| void I3dl2ReverbInfo::InitializeResultState(EffectResultState& result_state) {} | ||||
|  | ||||
| void I3dl2ReverbInfo::UpdateResultState(EffectResultState& cpu_state, | ||||
|                                         EffectResultState& dsp_state) {} | ||||
|  | ||||
| CpuAddr I3dl2ReverbInfo::GetWorkbuffer(s32 index) { | ||||
|     return GetSingleBuffer(index); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										200
									
								
								src/audio_core/renderer/effect/i3dl2.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										200
									
								
								src/audio_core/renderer/effect/i3dl2.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,200 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <vector> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/fixed_point.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| class I3dl2ReverbInfo : public EffectInfoBase { | ||||
| public: | ||||
|     struct ParameterVersion1 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ u16 channel_count_max; | ||||
|         /* 0x0E */ u16 channel_count; | ||||
|         /* 0x10 */ char unk10[0x4]; | ||||
|         /* 0x14 */ u32 sample_rate; | ||||
|         /* 0x18 */ f32 room_HF_gain; | ||||
|         /* 0x1C */ f32 reference_HF; | ||||
|         /* 0x20 */ f32 late_reverb_decay_time; | ||||
|         /* 0x24 */ f32 late_reverb_HF_decay_ratio; | ||||
|         /* 0x28 */ f32 room_gain; | ||||
|         /* 0x2C */ f32 reflection_gain; | ||||
|         /* 0x30 */ f32 reverb_gain; | ||||
|         /* 0x34 */ f32 late_reverb_diffusion; | ||||
|         /* 0x38 */ f32 reflection_delay; | ||||
|         /* 0x3C */ f32 late_reverb_delay_time; | ||||
|         /* 0x40 */ f32 late_reverb_density; | ||||
|         /* 0x44 */ f32 dry_gain; | ||||
|         /* 0x48 */ ParameterState state; | ||||
|         /* 0x49 */ char unk49[0x3]; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||||
|                   "I3dl2ReverbInfo::ParameterVersion1 has the wrong size!"); | ||||
|  | ||||
|     struct ParameterVersion2 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ u16 channel_count_max; | ||||
|         /* 0x0E */ u16 channel_count; | ||||
|         /* 0x10 */ char unk10[0x4]; | ||||
|         /* 0x14 */ u32 sample_rate; | ||||
|         /* 0x18 */ f32 room_HF_gain; | ||||
|         /* 0x1C */ f32 reference_HF; | ||||
|         /* 0x20 */ f32 late_reverb_decay_time; | ||||
|         /* 0x24 */ f32 late_reverb_HF_decay_ratio; | ||||
|         /* 0x28 */ f32 room_gain; | ||||
|         /* 0x2C */ f32 reflection_gain; | ||||
|         /* 0x30 */ f32 reverb_gain; | ||||
|         /* 0x34 */ f32 late_reverb_diffusion; | ||||
|         /* 0x38 */ f32 reflection_delay; | ||||
|         /* 0x3C */ f32 late_reverb_delay_time; | ||||
|         /* 0x40 */ f32 late_reverb_density; | ||||
|         /* 0x44 */ f32 dry_gain; | ||||
|         /* 0x48 */ ParameterState state; | ||||
|         /* 0x49 */ char unk49[0x3]; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||||
|                   "I3dl2ReverbInfo::ParameterVersion2 has the wrong size!"); | ||||
|  | ||||
|     static constexpr u32 MaxDelayLines = 4; | ||||
|     static constexpr u32 MaxDelayTaps = 20; | ||||
|  | ||||
|     struct I3dl2DelayLine { | ||||
|         void Initialize(const s32 delay_time) { | ||||
|             max_delay = delay_time; | ||||
|             buffer.resize(delay_time + 1, 0); | ||||
|             buffer_end = &buffer[delay_time]; | ||||
|             output = &buffer[0]; | ||||
|             SetDelay(delay_time); | ||||
|             wet_gain = 0.0f; | ||||
|         } | ||||
|  | ||||
|         void SetDelay(const s32 delay_time) { | ||||
|             if (max_delay < delay_time) { | ||||
|                 return; | ||||
|             } | ||||
|             delay = delay_time; | ||||
|             input = &buffer[(output - buffer.data() + delay) % (max_delay + 1)]; | ||||
|         } | ||||
|  | ||||
|         Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) { | ||||
|             Write(sample); | ||||
|  | ||||
|             auto out_sample{Read()}; | ||||
|  | ||||
|             output++; | ||||
|             if (output >= buffer_end) { | ||||
|                 output = buffer.data(); | ||||
|             } | ||||
|  | ||||
|             return out_sample; | ||||
|         } | ||||
|  | ||||
|         Common::FixedPoint<50, 14> Read() { | ||||
|             return *output; | ||||
|         } | ||||
|  | ||||
|         void Write(const Common::FixedPoint<50, 14> sample) { | ||||
|             *(input++) = sample; | ||||
|             if (input >= buffer_end) { | ||||
|                 input = buffer.data(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Common::FixedPoint<50, 14> TapOut(const s32 index) { | ||||
|             auto out{input - (index + 1)}; | ||||
|             if (out < buffer.data()) { | ||||
|                 out += max_delay + 1; | ||||
|             } | ||||
|             return *out; | ||||
|         } | ||||
|  | ||||
|         std::vector<Common::FixedPoint<50, 14>> buffer{}; | ||||
|         Common::FixedPoint<50, 14>* buffer_end{}; | ||||
|         s32 max_delay{}; | ||||
|         Common::FixedPoint<50, 14>* input{}; | ||||
|         Common::FixedPoint<50, 14>* output{}; | ||||
|         s32 delay{}; | ||||
|         f32 wet_gain{}; | ||||
|     }; | ||||
|  | ||||
|     struct State { | ||||
|         f32 lowpass_0; | ||||
|         f32 lowpass_1; | ||||
|         f32 lowpass_2; | ||||
|         I3dl2DelayLine early_delay_line; | ||||
|         std::array<s32, MaxDelayTaps> early_tap_steps; | ||||
|         f32 early_gain; | ||||
|         f32 late_gain; | ||||
|         s32 early_to_late_taps; | ||||
|         std::array<I3dl2DelayLine, MaxDelayLines> fdn_delay_lines; | ||||
|         std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines0; | ||||
|         std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines1; | ||||
|         f32 last_reverb_echo; | ||||
|         I3dl2DelayLine center_delay_line; | ||||
|         std::array<std::array<f32, 3>, MaxDelayLines> lowpass_coeff; | ||||
|         std::array<f32, MaxDelayLines> shelf_filter; | ||||
|         f32 dry_gain; | ||||
|     }; | ||||
|     static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||||
|                   "I3dl2ReverbInfo::State is too large!"); | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Initialize a new result state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param result_state - Result state to initialize. | ||||
|      */ | ||||
|     void InitializeResultState(EffectResultState& result_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param cpu_state - Host-side result state to update. | ||||
|      * @param dsp_state - AudioRenderer-side result state to update from. | ||||
|      */ | ||||
|     void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Get a workbuffer assigned to this effect with the given index. | ||||
|      * | ||||
|      * @param index - Workbuffer index. | ||||
|      * @return Address of the buffer. | ||||
|      */ | ||||
|     CpuAddr GetWorkbuffer(s32 index) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										81
									
								
								src/audio_core/renderer/effect/light_limiter.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										81
									
								
								src/audio_core/renderer/effect/light_limiter.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/light_limiter.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                               const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(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; | ||||
|  | ||||
|     if (buffer_unmapped || in_params.is_new) { | ||||
|         usage_state = UsageState::New; | ||||
|         params->state = ParameterState::Initialized; | ||||
|         buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|             error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||||
|     } else { | ||||
|         error_info.error_code = ResultSuccess; | ||||
|         error_info.address = CpuAddr(0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||||
|                               const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(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; | ||||
|  | ||||
|     if (buffer_unmapped || in_params.is_new) { | ||||
|         usage_state = UsageState::New; | ||||
|         params->state = ParameterState::Initialized; | ||||
|         buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|             error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||||
|     } else { | ||||
|         error_info.error_code = ResultSuccess; | ||||
|         error_info.address = CpuAddr(0); | ||||
|     } | ||||
| } | ||||
|  | ||||
| void LightLimiterInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
|  | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|     params->state = ParameterState::Updated; | ||||
|     params->statistics_reset_required = false; | ||||
| } | ||||
|  | ||||
| void LightLimiterInfo::InitializeResultState(EffectResultState& result_state) { | ||||
|     auto result_state_{reinterpret_cast<StatisticsInternal*>(result_state.state.data())}; | ||||
|  | ||||
|     result_state_->channel_max_sample.fill(0); | ||||
|     result_state_->channel_compression_gain_min.fill(1.0f); | ||||
| } | ||||
|  | ||||
| void LightLimiterInfo::UpdateResultState(EffectResultState& cpu_state, | ||||
|                                          EffectResultState& dsp_state) { | ||||
|     auto cpu_statistics{reinterpret_cast<StatisticsInternal*>(cpu_state.state.data())}; | ||||
|     auto dsp_statistics{reinterpret_cast<StatisticsInternal*>(dsp_state.state.data())}; | ||||
|  | ||||
|     *cpu_statistics = *dsp_statistics; | ||||
| } | ||||
|  | ||||
| CpuAddr LightLimiterInfo::GetWorkbuffer(s32 index) { | ||||
|     return GetSingleBuffer(index); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										138
									
								
								src/audio_core/renderer/effect/light_limiter.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										138
									
								
								src/audio_core/renderer/effect/light_limiter.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,138 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <vector> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/fixed_point.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| class LightLimiterInfo : public EffectInfoBase { | ||||
| public: | ||||
|     enum class ProcessingMode { | ||||
|         Mode0, | ||||
|         Mode1, | ||||
|     }; | ||||
|  | ||||
|     struct ParameterVersion1 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ u16 channel_count_max; | ||||
|         /* 0x0E */ u16 channel_count; | ||||
|         /* 0x0C */ u32 sample_rate; | ||||
|         /* 0x14 */ s32 look_ahead_time_max; | ||||
|         /* 0x18 */ s32 attack_time; | ||||
|         /* 0x1C */ s32 release_time; | ||||
|         /* 0x20 */ s32 look_ahead_time; | ||||
|         /* 0x24 */ f32 attack_coeff; | ||||
|         /* 0x28 */ f32 release_coeff; | ||||
|         /* 0x2C */ f32 threshold; | ||||
|         /* 0x30 */ f32 input_gain; | ||||
|         /* 0x34 */ f32 output_gain; | ||||
|         /* 0x38 */ s32 look_ahead_samples_min; | ||||
|         /* 0x3C */ s32 look_ahead_samples_max; | ||||
|         /* 0x40 */ ParameterState state; | ||||
|         /* 0x41 */ bool statistics_enabled; | ||||
|         /* 0x42 */ bool statistics_reset_required; | ||||
|         /* 0x43 */ ProcessingMode processing_mode; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||||
|                   "LightLimiterInfo::ParameterVersion1 has the wrong size!"); | ||||
|  | ||||
|     struct ParameterVersion2 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ u16 channel_count_max; | ||||
|         /* 0x0E */ u16 channel_count; | ||||
|         /* 0x0C */ u32 sample_rate; | ||||
|         /* 0x14 */ s32 look_ahead_time_max; | ||||
|         /* 0x18 */ s32 attack_time; | ||||
|         /* 0x1C */ s32 release_time; | ||||
|         /* 0x20 */ s32 look_ahead_time; | ||||
|         /* 0x24 */ f32 attack_coeff; | ||||
|         /* 0x28 */ f32 release_coeff; | ||||
|         /* 0x2C */ f32 threshold; | ||||
|         /* 0x30 */ f32 input_gain; | ||||
|         /* 0x34 */ f32 output_gain; | ||||
|         /* 0x38 */ s32 look_ahead_samples_min; | ||||
|         /* 0x3C */ s32 look_ahead_samples_max; | ||||
|         /* 0x40 */ ParameterState state; | ||||
|         /* 0x41 */ bool statistics_enabled; | ||||
|         /* 0x42 */ bool statistics_reset_required; | ||||
|         /* 0x43 */ ProcessingMode processing_mode; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||||
|                   "LightLimiterInfo::ParameterVersion2 has the wrong size!"); | ||||
|  | ||||
|     struct State { | ||||
|         std::array<Common::FixedPoint<49, 15>, MaxChannels> samples_average; | ||||
|         std::array<Common::FixedPoint<49, 15>, MaxChannels> compression_gain; | ||||
|         std::array<s32, MaxChannels> look_ahead_sample_offsets; | ||||
|         std::array<std::vector<Common::FixedPoint<49, 15>>, MaxChannels> look_ahead_sample_buffers; | ||||
|     }; | ||||
|     static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||||
|                   "LightLimiterInfo::State has the wrong size!"); | ||||
|  | ||||
|     struct StatisticsInternal { | ||||
|         /* 0x00 */ std::array<f32, MaxChannels> channel_max_sample; | ||||
|         /* 0x18 */ std::array<f32, MaxChannels> channel_compression_gain_min; | ||||
|     }; | ||||
|     static_assert(sizeof(StatisticsInternal) == 0x30, | ||||
|                   "LightLimiterInfo::StatisticsInternal has the wrong size!"); | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Initialize a new limiter statistics result state. Version 2 only. | ||||
|      * | ||||
|      * @param result_state - Result state to initialize. | ||||
|      */ | ||||
|     void InitializeResultState(EffectResultState& result_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the host-side limiter statistics with the ADSP-side one. Version 2 only. | ||||
|      * | ||||
|      * @param cpu_state - Host-side result state to update. | ||||
|      * @param dsp_state - AudioRenderer-side result state to update from. | ||||
|      */ | ||||
|     void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Get a workbuffer assigned to this effect with the given index. | ||||
|      * | ||||
|      * @param index - Workbuffer index. | ||||
|      * @return Address of the buffer. | ||||
|      */ | ||||
|     CpuAddr GetWorkbuffer(s32 index) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										93
									
								
								src/audio_core/renderer/effect/reverb.cpp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										93
									
								
								src/audio_core/renderer/effect/reverb.cpp
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #include "audio_core/renderer/effect/reverb.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                         const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|  | ||||
|     if (IsChannelCountValid(in_specific->channel_count_max)) { | ||||
|         const auto old_state{params->state}; | ||||
|         std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||||
|         mix_id = in_params.mix_id; | ||||
|         process_order = in_params.process_order; | ||||
|         enabled = in_params.enabled; | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count)) { | ||||
|             params->channel_count = params->channel_count_max; | ||||
|         } | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count) || | ||||
|             old_state != ParameterState::Updated) { | ||||
|             params->state = old_state; | ||||
|         } | ||||
|  | ||||
|         if (buffer_unmapped || in_params.is_new) { | ||||
|             usage_state = UsageState::New; | ||||
|             params->state = ParameterState::Initialized; | ||||
|             buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|                 error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     error_info.error_code = ResultSuccess; | ||||
|     error_info.address = CpuAddr(0); | ||||
| } | ||||
|  | ||||
| void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                         const PoolMapper& pool_mapper) { | ||||
|     auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; | ||||
|     auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; | ||||
|  | ||||
|     if (IsChannelCountValid(in_specific->channel_count_max)) { | ||||
|         const auto old_state{params->state}; | ||||
|         std::memcpy(params, in_specific, sizeof(ParameterVersion2)); | ||||
|         mix_id = in_params.mix_id; | ||||
|         process_order = in_params.process_order; | ||||
|         enabled = in_params.enabled; | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count)) { | ||||
|             params->channel_count = params->channel_count_max; | ||||
|         } | ||||
|  | ||||
|         if (!IsChannelCountValid(in_specific->channel_count) || | ||||
|             old_state != ParameterState::Updated) { | ||||
|             params->state = old_state; | ||||
|         } | ||||
|  | ||||
|         if (buffer_unmapped || in_params.is_new) { | ||||
|             usage_state = UsageState::New; | ||||
|             params->state = ParameterState::Initialized; | ||||
|             buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||||
|                 error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     error_info.error_code = ResultSuccess; | ||||
|     error_info.address = CpuAddr(0); | ||||
| } | ||||
|  | ||||
| void ReverbInfo::UpdateForCommandGeneration() { | ||||
|     if (enabled) { | ||||
|         usage_state = UsageState::Enabled; | ||||
|     } else { | ||||
|         usage_state = UsageState::Disabled; | ||||
|     } | ||||
|  | ||||
|     auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||||
|     params->state = ParameterState::Updated; | ||||
| } | ||||
|  | ||||
| void ReverbInfo::InitializeResultState(EffectResultState& result_state) {} | ||||
|  | ||||
| void ReverbInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} | ||||
|  | ||||
| CpuAddr ReverbInfo::GetWorkbuffer(s32 index) { | ||||
|     return GetSingleBuffer(index); | ||||
| } | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
							
								
								
									
										190
									
								
								src/audio_core/renderer/effect/reverb.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										190
									
								
								src/audio_core/renderer/effect/reverb.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,190 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <vector> | ||||
|  | ||||
| #include "audio_core/common/common.h" | ||||
| #include "audio_core/renderer/effect/effect_info_base.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/fixed_point.h" | ||||
|  | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| class ReverbInfo : public EffectInfoBase { | ||||
| public: | ||||
|     struct ParameterVersion1 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ u16 channel_count_max; | ||||
|         /* 0x0E */ u16 channel_count; | ||||
|         /* 0x10 */ u32 sample_rate; | ||||
|         /* 0x14 */ u32 early_mode; | ||||
|         /* 0x18 */ s32 early_gain; | ||||
|         /* 0x1C */ s32 pre_delay; | ||||
|         /* 0x20 */ s32 late_mode; | ||||
|         /* 0x24 */ s32 late_gain; | ||||
|         /* 0x28 */ s32 decay_time; | ||||
|         /* 0x2C */ s32 high_freq_Decay_ratio; | ||||
|         /* 0x30 */ s32 colouration; | ||||
|         /* 0x34 */ s32 base_gain; | ||||
|         /* 0x38 */ s32 wet_gain; | ||||
|         /* 0x3C */ s32 dry_gain; | ||||
|         /* 0x40 */ ParameterState state; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||||
|                   "ReverbInfo::ParameterVersion1 has the wrong size!"); | ||||
|  | ||||
|     struct ParameterVersion2 { | ||||
|         /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||||
|         /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||||
|         /* 0x0C */ u16 channel_count_max; | ||||
|         /* 0x0E */ u16 channel_count; | ||||
|         /* 0x10 */ u32 sample_rate; | ||||
|         /* 0x14 */ u32 early_mode; | ||||
|         /* 0x18 */ s32 early_gain; | ||||
|         /* 0x1C */ s32 pre_delay; | ||||
|         /* 0x20 */ s32 late_mode; | ||||
|         /* 0x24 */ s32 late_gain; | ||||
|         /* 0x28 */ s32 decay_time; | ||||
|         /* 0x2C */ s32 high_freq_decay_ratio; | ||||
|         /* 0x30 */ s32 colouration; | ||||
|         /* 0x34 */ s32 base_gain; | ||||
|         /* 0x38 */ s32 wet_gain; | ||||
|         /* 0x3C */ s32 dry_gain; | ||||
|         /* 0x40 */ ParameterState state; | ||||
|     }; | ||||
|     static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||||
|                   "ReverbInfo::ParameterVersion2 has the wrong size!"); | ||||
|  | ||||
|     static constexpr u32 MaxDelayLines = 4; | ||||
|     static constexpr u32 MaxDelayTaps = 10; | ||||
|     static constexpr u32 NumEarlyModes = 5; | ||||
|     static constexpr u32 NumLateModes = 5; | ||||
|  | ||||
|     struct ReverbDelayLine { | ||||
|         void Initialize(const s32 delay_time, const f32 decay_rate) { | ||||
|             buffer.resize(delay_time + 1, 0); | ||||
|             buffer_end = &buffer[delay_time]; | ||||
|             output = &buffer[0]; | ||||
|             decay = decay_rate; | ||||
|             sample_count_max = delay_time; | ||||
|             SetDelay(delay_time); | ||||
|         } | ||||
|  | ||||
|         void SetDelay(const s32 delay_time) { | ||||
|             if (sample_count_max < delay_time) { | ||||
|                 return; | ||||
|             } | ||||
|             sample_count = delay_time; | ||||
|             input = &buffer[(output - buffer.data() + sample_count) % (sample_count_max + 1)]; | ||||
|         } | ||||
|  | ||||
|         Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) { | ||||
|             Write(sample); | ||||
|  | ||||
|             auto out_sample{Read()}; | ||||
|  | ||||
|             output++; | ||||
|             if (output >= buffer_end) { | ||||
|                 output = buffer.data(); | ||||
|             } | ||||
|  | ||||
|             return out_sample; | ||||
|         } | ||||
|  | ||||
|         Common::FixedPoint<50, 14> Read() { | ||||
|             return *output; | ||||
|         } | ||||
|  | ||||
|         void Write(const Common::FixedPoint<50, 14> sample) { | ||||
|             *(input++) = sample; | ||||
|             if (input >= buffer_end) { | ||||
|                 input = buffer.data(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Common::FixedPoint<50, 14> TapOut(const s32 index) { | ||||
|             auto out{input - (index + 1)}; | ||||
|             if (out < buffer.data()) { | ||||
|                 out += sample_count; | ||||
|             } | ||||
|             return *out; | ||||
|         } | ||||
|  | ||||
|         s32 sample_count{}; | ||||
|         s32 sample_count_max{}; | ||||
|         std::vector<Common::FixedPoint<50, 14>> buffer{}; | ||||
|         Common::FixedPoint<50, 14>* buffer_end; | ||||
|         Common::FixedPoint<50, 14>* input{}; | ||||
|         Common::FixedPoint<50, 14>* output{}; | ||||
|         Common::FixedPoint<50, 14> decay{}; | ||||
|     }; | ||||
|  | ||||
|     struct State { | ||||
|         ReverbDelayLine pre_delay_line; | ||||
|         ReverbDelayLine center_delay_line; | ||||
|         std::array<s32, MaxDelayTaps> early_delay_times; | ||||
|         std::array<Common::FixedPoint<50, 14>, MaxDelayTaps> early_gains; | ||||
|         s32 pre_delay_time; | ||||
|         std::array<ReverbDelayLine, MaxDelayLines> decay_delay_lines; | ||||
|         std::array<ReverbDelayLine, MaxDelayLines> fdn_delay_lines; | ||||
|         std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_gain; | ||||
|         std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_prev_gain; | ||||
|         std::array<Common::FixedPoint<50, 14>, MaxDelayLines> prev_feedback_output; | ||||
|     }; | ||||
|     static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||||
|                   "ReverbInfo::State is too large!"); | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 1. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info with new parameters, version 2. | ||||
|      * | ||||
|      * @param error_info  - Used to write call result code. | ||||
|      * @param in_params   - New parameters to update the info with. | ||||
|      * @param pool_mapper - Pool for mapping buffers. | ||||
|      */ | ||||
|     void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||||
|                 const PoolMapper& pool_mapper) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the info after command generation. Usually only changes its state. | ||||
|      */ | ||||
|     void UpdateForCommandGeneration() override; | ||||
|  | ||||
|     /** | ||||
|      * Initialize a new result state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param result_state - Result state to initialize. | ||||
|      */ | ||||
|     void InitializeResultState(EffectResultState& result_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||||
|      * | ||||
|      * @param cpu_state - Host-side result state to update. | ||||
|      * @param dsp_state - AudioRenderer-side result state to update from. | ||||
|      */ | ||||
|     void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||||
|  | ||||
|     /** | ||||
|      * Get a workbuffer assigned to this effect with the given index. | ||||
|      * | ||||
|      * @param index - Workbuffer index. | ||||
|      * @return Address of the buffer. | ||||
|      */ | ||||
|     CpuAddr GetWorkbuffer(s32 index) override; | ||||
| }; | ||||
|  | ||||
| } // namespace AudioCore::AudioRenderer | ||||
| @@ -9,19 +9,20 @@ | ||||
| namespace AudioCore::AudioRenderer { | ||||
|  | ||||
| enum class PerformanceDetailType : u8 { | ||||
|     /*  0 */ Invalid, | ||||
|     /*  1 */ Unk1, | ||||
|     /*  2 */ Unk2, | ||||
|     /*  3 */ Unk3, | ||||
|     /*  4 */ Unk4, | ||||
|     /*  5 */ Unk5, | ||||
|     /*  6 */ Unk6, | ||||
|     /*  7 */ Unk7, | ||||
|     /*  8 */ Unk8, | ||||
|     /*  9 */ Unk9, | ||||
|     /* 10 */ Unk10, | ||||
|     /* 11 */ Unk11, | ||||
|     /* 12 */ Unk12, | ||||
|     Invalid, | ||||
|     Unk1, | ||||
|     Unk2, | ||||
|     Unk3, | ||||
|     Unk4, | ||||
|     Unk5, | ||||
|     Unk6, | ||||
|     Unk7, | ||||
|     Unk8, | ||||
|     Unk9, | ||||
|     Unk10, | ||||
|     Unk11, | ||||
|     Unk12, | ||||
|     Unk13, | ||||
| }; | ||||
|  | ||||
| struct PerformanceDetailVersion1 { | ||||
|   | ||||
| @@ -367,7 +367,11 @@ Result System::Initialize(const AudioRendererParameterInternal& params, | ||||
|  | ||||
|     // nn::audio::dsp::FlushDataCache(transferMemory, transferMemorySize); | ||||
|  | ||||
|     if (behavior.IsCommandProcessingTimeEstimatorVersion4Supported()) { | ||||
|     if (behavior.IsCommandProcessingTimeEstimatorVersion5Supported()) { | ||||
|         command_processing_time_estimator = | ||||
|             std::make_unique<CommandProcessingTimeEstimatorVersion5>(sample_count, | ||||
|                                                                      mix_buffer_count); | ||||
|     } else if (behavior.IsCommandProcessingTimeEstimatorVersion4Supported()) { | ||||
|         command_processing_time_estimator = | ||||
|             std::make_unique<CommandProcessingTimeEstimatorVersion4>(sample_count, | ||||
|                                                                      mix_buffer_count); | ||||
| @@ -595,13 +599,13 @@ void System::SendCommandToDsp() { | ||||
|                 memory_pool_info.Translate(CpuAddr(command_workbuffer.data()), command_size)}; | ||||
|  | ||||
|             auto time_limit_percent{70.0f}; | ||||
|             if (behavior.IsAudioRenererProcessingTimeLimit80PercentSupported()) { | ||||
|             if (behavior.IsAudioRendererProcessingTimeLimit80PercentSupported()) { | ||||
|                 time_limit_percent = 80.0f; | ||||
|             } else if (behavior.IsAudioRenererProcessingTimeLimit75PercentSupported()) { | ||||
|             } else if (behavior.IsAudioRendererProcessingTimeLimit75PercentSupported()) { | ||||
|                 time_limit_percent = 75.0f; | ||||
|             } else { | ||||
|                 // result ignored and 70 is used anyway | ||||
|                 behavior.IsAudioRenererProcessingTimeLimit70PercentSupported(); | ||||
|                 behavior.IsAudioRendererProcessingTimeLimit70PercentSupported(); | ||||
|                 time_limit_percent = 70.0f; | ||||
|             } | ||||
|  | ||||
| @@ -698,13 +702,14 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer, | ||||
|  | ||||
|     if (drop_voice) { | ||||
|         f32 time_limit_percent{70.0f}; | ||||
|         if (render_context.behavior->IsAudioRenererProcessingTimeLimit80PercentSupported()) { | ||||
|         if (render_context.behavior->IsAudioRendererProcessingTimeLimit80PercentSupported()) { | ||||
|             time_limit_percent = 80.0f; | ||||
|         } else if (render_context.behavior->IsAudioRenererProcessingTimeLimit75PercentSupported()) { | ||||
|         } else if (render_context.behavior | ||||
|                        ->IsAudioRendererProcessingTimeLimit75PercentSupported()) { | ||||
|             time_limit_percent = 75.0f; | ||||
|         } else { | ||||
|             // result is ignored | ||||
|             render_context.behavior->IsAudioRenererProcessingTimeLimit70PercentSupported(); | ||||
|             render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported(); | ||||
|             time_limit_percent = 70.0f; | ||||
|         } | ||||
|         const auto time_limit{static_cast<u32>( | ||||
|   | ||||
| @@ -107,7 +107,7 @@ private: | ||||
|     } | ||||
|  | ||||
|     void RequestUpdate(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_DEBUG(Service_Audio, "called"); | ||||
|         LOG_TRACE(Service_Audio, "called"); | ||||
|  | ||||
|         std::vector<u8> input{ctx.ReadBuffer(0)}; | ||||
|  | ||||
|   | ||||
| @@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) { | ||||
|     const auto sample_rate = rp.Pop<u32>(); | ||||
|     const auto channel_count = rp.Pop<u32>(); | ||||
|  | ||||
|     LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); | ||||
|     LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); | ||||
|  | ||||
|     ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | ||||
|                    sample_rate == 12000 || sample_rate == 8000, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user