diff options
| author | 2022-07-16 23:48:45 +0100 | |
|---|---|---|
| committer | 2022-07-22 01:11:32 +0100 | |
| commit | 458da8a94877677f086f06cdeecf959ec4283a33 (patch) | |
| tree | 583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/renderer/effect | |
| parent | Merge pull request #8598 from Link4565/recv-dontwait (diff) | |
| download | yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip | |
Project Andio
Diffstat (limited to 'src/audio_core/renderer/effect')
23 files changed, 2426 insertions, 0 deletions
diff --git a/src/audio_core/renderer/effect/aux_.cpp b/src/audio_core/renderer/effect/aux_.cpp new file mode 100644 index 000000000..51e780ef1 --- /dev/null +++ b/src/audio_core/renderer/effect/aux_.cpp | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/aux_.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 9 | const PoolMapper& pool_mapper) { | ||
| 10 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 11 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 12 | |||
| 13 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 14 | mix_id = in_params.mix_id; | ||
| 15 | process_order = in_params.process_order; | ||
| 16 | enabled = in_params.enabled; | ||
| 17 | if (buffer_unmapped || in_params.is_new) { | ||
| 18 | const bool send_unmapped{!pool_mapper.TryAttachBuffer( | ||
| 19 | error_info, workbuffers[0], in_specific->send_buffer_info_address, | ||
| 20 | sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))}; | ||
| 21 | const bool return_unmapped{!pool_mapper.TryAttachBuffer( | ||
| 22 | error_info, workbuffers[1], in_specific->return_buffer_info_address, | ||
| 23 | sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))}; | ||
| 24 | |||
| 25 | buffer_unmapped = send_unmapped || return_unmapped; | ||
| 26 | |||
| 27 | if (!buffer_unmapped) { | ||
| 28 | auto send{workbuffers[0].GetReference(false)}; | ||
| 29 | send_buffer_info = send + sizeof(AuxInfoDsp); | ||
| 30 | send_buffer = send + sizeof(AuxBufferInfo); | ||
| 31 | |||
| 32 | auto ret{workbuffers[1].GetReference(false)}; | ||
| 33 | return_buffer_info = ret + sizeof(AuxInfoDsp); | ||
| 34 | return_buffer = ret + sizeof(AuxBufferInfo); | ||
| 35 | } | ||
| 36 | } else { | ||
| 37 | error_info.error_code = ResultSuccess; | ||
| 38 | error_info.address = CpuAddr(0); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 43 | const PoolMapper& pool_mapper) { | ||
| 44 | auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; | ||
| 45 | auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; | ||
| 46 | |||
| 47 | std::memcpy(params, in_specific, sizeof(ParameterVersion2)); | ||
| 48 | mix_id = in_params.mix_id; | ||
| 49 | process_order = in_params.process_order; | ||
| 50 | enabled = in_params.enabled; | ||
| 51 | |||
| 52 | if (buffer_unmapped || in_params.is_new) { | ||
| 53 | const bool send_unmapped{!pool_mapper.TryAttachBuffer( | ||
| 54 | error_info, workbuffers[0], params->send_buffer_info_address, | ||
| 55 | sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))}; | ||
| 56 | const bool return_unmapped{!pool_mapper.TryAttachBuffer( | ||
| 57 | error_info, workbuffers[1], params->return_buffer_info_address, | ||
| 58 | sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))}; | ||
| 59 | |||
| 60 | buffer_unmapped = send_unmapped || return_unmapped; | ||
| 61 | |||
| 62 | if (!buffer_unmapped) { | ||
| 63 | auto send{workbuffers[0].GetReference(false)}; | ||
| 64 | send_buffer_info = send + sizeof(AuxInfoDsp); | ||
| 65 | send_buffer = send + sizeof(AuxBufferInfo); | ||
| 66 | |||
| 67 | auto ret{workbuffers[1].GetReference(false)}; | ||
| 68 | return_buffer_info = ret + sizeof(AuxInfoDsp); | ||
| 69 | return_buffer = ret + sizeof(AuxBufferInfo); | ||
| 70 | } | ||
| 71 | } else { | ||
| 72 | error_info.error_code = ResultSuccess; | ||
| 73 | error_info.address = CpuAddr(0); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | void AuxInfo::UpdateForCommandGeneration() { | ||
| 78 | if (enabled) { | ||
| 79 | usage_state = UsageState::Enabled; | ||
| 80 | } else { | ||
| 81 | usage_state = UsageState::Disabled; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | void AuxInfo::InitializeResultState(EffectResultState& result_state) {} | ||
| 86 | |||
| 87 | void AuxInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} | ||
| 88 | |||
| 89 | CpuAddr AuxInfo::GetWorkbuffer(s32 index) { | ||
| 90 | return workbuffers[index].GetReference(true); | ||
| 91 | } | ||
| 92 | |||
| 93 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/aux_.h b/src/audio_core/renderer/effect/aux_.h new file mode 100644 index 000000000..4d3d9e3d9 --- /dev/null +++ b/src/audio_core/renderer/effect/aux_.h | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore::AudioRenderer { | ||
| 13 | /** | ||
| 14 | * Auxiliary Buffer used for Aux commands. | ||
| 15 | * Send and return buffers are available (names from the game's perspective). | ||
| 16 | * Send is read by the host, containing a buffer of samples to be used for whatever purpose. | ||
| 17 | * Return is written by the host, writing a mix buffer back to the game. | ||
| 18 | * This allows the game to use pre-processed samples skipping the other render processing, | ||
| 19 | * and to examine or modify what the audio renderer has generated. | ||
| 20 | */ | ||
| 21 | class AuxInfo : public EffectInfoBase { | ||
| 22 | public: | ||
| 23 | struct ParameterVersion1 { | ||
| 24 | /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; | ||
| 25 | /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; | ||
| 26 | /* 0x30 */ u32 mix_buffer_count; | ||
| 27 | /* 0x34 */ u32 sample_rate; | ||
| 28 | /* 0x38 */ u32 count_max; | ||
| 29 | /* 0x3C */ u32 mix_buffer_count_max; | ||
| 30 | /* 0x40 */ CpuAddr send_buffer_info_address; | ||
| 31 | /* 0x48 */ CpuAddr send_buffer_address; | ||
| 32 | /* 0x50 */ CpuAddr return_buffer_info_address; | ||
| 33 | /* 0x58 */ CpuAddr return_buffer_address; | ||
| 34 | /* 0x60 */ u32 mix_buffer_sample_size; | ||
| 35 | /* 0x64 */ u32 sample_count; | ||
| 36 | /* 0x68 */ u32 mix_buffer_sample_count; | ||
| 37 | }; | ||
| 38 | static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||
| 39 | "AuxInfo::ParameterVersion1 has the wrong size!"); | ||
| 40 | |||
| 41 | struct ParameterVersion2 { | ||
| 42 | /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; | ||
| 43 | /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; | ||
| 44 | /* 0x30 */ u32 mix_buffer_count; | ||
| 45 | /* 0x34 */ u32 sample_rate; | ||
| 46 | /* 0x38 */ u32 count_max; | ||
| 47 | /* 0x3C */ u32 mix_buffer_count_max; | ||
| 48 | /* 0x40 */ CpuAddr send_buffer_info_address; | ||
| 49 | /* 0x48 */ CpuAddr send_buffer_address; | ||
| 50 | /* 0x50 */ CpuAddr return_buffer_info_address; | ||
| 51 | /* 0x58 */ CpuAddr return_buffer_address; | ||
| 52 | /* 0x60 */ u32 mix_buffer_sample_size; | ||
| 53 | /* 0x64 */ u32 sample_count; | ||
| 54 | /* 0x68 */ u32 mix_buffer_sample_count; | ||
| 55 | }; | ||
| 56 | static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||
| 57 | "AuxInfo::ParameterVersion2 has the wrong size!"); | ||
| 58 | |||
| 59 | struct AuxInfoDsp { | ||
| 60 | /* 0x00 */ u32 read_offset; | ||
| 61 | /* 0x04 */ u32 write_offset; | ||
| 62 | /* 0x08 */ u32 lost_sample_count; | ||
| 63 | /* 0x0C */ u32 total_sample_count; | ||
| 64 | /* 0x10 */ char unk10[0x30]; | ||
| 65 | }; | ||
| 66 | static_assert(sizeof(AuxInfoDsp) == 0x40, "AuxInfo::AuxInfoDsp has the wrong size!"); | ||
| 67 | |||
| 68 | struct AuxBufferInfo { | ||
| 69 | /* 0x00 */ AuxInfoDsp cpu_info; | ||
| 70 | /* 0x40 */ AuxInfoDsp dsp_info; | ||
| 71 | }; | ||
| 72 | static_assert(sizeof(AuxBufferInfo) == 0x80, "AuxInfo::AuxBufferInfo has the wrong size!"); | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Update the info with new parameters, version 1. | ||
| 76 | * | ||
| 77 | * @param error_info - Used to write call result code. | ||
| 78 | * @param in_params - New parameters to update the info with. | ||
| 79 | * @param pool_mapper - Pool for mapping buffers. | ||
| 80 | */ | ||
| 81 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 82 | const PoolMapper& pool_mapper) override; | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Update the info with new parameters, version 2. | ||
| 86 | * | ||
| 87 | * @param error_info - Used to write call result code. | ||
| 88 | * @param in_params - New parameters to update the info with. | ||
| 89 | * @param pool_mapper - Pool for mapping buffers. | ||
| 90 | */ | ||
| 91 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 92 | const PoolMapper& pool_mapper) override; | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Update the info after command generation. Usually only changes its state. | ||
| 96 | */ | ||
| 97 | void UpdateForCommandGeneration() override; | ||
| 98 | |||
| 99 | /** | ||
| 100 | * Initialize a new result state. Version 2 only, unused. | ||
| 101 | * | ||
| 102 | * @param result_state - Result state to initialize. | ||
| 103 | */ | ||
| 104 | void InitializeResultState(EffectResultState& result_state) override; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||
| 108 | * | ||
| 109 | * @param cpu_state - Host-side result state to update. | ||
| 110 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 111 | */ | ||
| 112 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Get a workbuffer assigned to this effect with the given index. | ||
| 116 | * | ||
| 117 | * @param index - Workbuffer index. | ||
| 118 | * @return Address of the buffer. | ||
| 119 | */ | ||
| 120 | CpuAddr GetWorkbuffer(s32 index) override; | ||
| 121 | }; | ||
| 122 | |||
| 123 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/biquad_filter.cpp b/src/audio_core/renderer/effect/biquad_filter.cpp new file mode 100644 index 000000000..a1efb3231 --- /dev/null +++ b/src/audio_core/renderer/effect/biquad_filter.cpp | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/biquad_filter.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | ||
| 10 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 11 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 12 | |||
| 13 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 14 | mix_id = in_params.mix_id; | ||
| 15 | process_order = in_params.process_order; | ||
| 16 | enabled = in_params.enabled; | ||
| 17 | |||
| 18 | error_info.error_code = ResultSuccess; | ||
| 19 | error_info.address = CpuAddr(0); | ||
| 20 | } | ||
| 21 | |||
| 22 | void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 23 | const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||
| 24 | auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; | ||
| 25 | auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; | ||
| 26 | |||
| 27 | std::memcpy(params, in_specific, sizeof(ParameterVersion2)); | ||
| 28 | mix_id = in_params.mix_id; | ||
| 29 | process_order = in_params.process_order; | ||
| 30 | enabled = in_params.enabled; | ||
| 31 | |||
| 32 | error_info.error_code = ResultSuccess; | ||
| 33 | error_info.address = CpuAddr(0); | ||
| 34 | } | ||
| 35 | |||
| 36 | void BiquadFilterInfo::UpdateForCommandGeneration() { | ||
| 37 | if (enabled) { | ||
| 38 | usage_state = UsageState::Enabled; | ||
| 39 | } else { | ||
| 40 | usage_state = UsageState::Disabled; | ||
| 41 | } | ||
| 42 | |||
| 43 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 44 | params->state = ParameterState::Updated; | ||
| 45 | } | ||
| 46 | |||
| 47 | void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {} | ||
| 48 | |||
| 49 | void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, | ||
| 50 | EffectResultState& dsp_state) {} | ||
| 51 | |||
| 52 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/biquad_filter.h b/src/audio_core/renderer/effect/biquad_filter.h new file mode 100644 index 000000000..f53fd5bab --- /dev/null +++ b/src/audio_core/renderer/effect/biquad_filter.h | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore::AudioRenderer { | ||
| 13 | |||
| 14 | class BiquadFilterInfo : public EffectInfoBase { | ||
| 15 | public: | ||
| 16 | struct ParameterVersion1 { | ||
| 17 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 18 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 19 | /* 0x0C */ std::array<s16, 3> b; | ||
| 20 | /* 0x12 */ std::array<s16, 2> a; | ||
| 21 | /* 0x16 */ s8 channel_count; | ||
| 22 | /* 0x17 */ ParameterState state; | ||
| 23 | }; | ||
| 24 | static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||
| 25 | "BiquadFilterInfo::ParameterVersion1 has the wrong size!"); | ||
| 26 | |||
| 27 | struct ParameterVersion2 { | ||
| 28 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 29 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 30 | /* 0x0C */ std::array<s16, 3> b; | ||
| 31 | /* 0x12 */ std::array<s16, 2> a; | ||
| 32 | /* 0x16 */ s8 channel_count; | ||
| 33 | /* 0x17 */ ParameterState state; | ||
| 34 | }; | ||
| 35 | static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||
| 36 | "BiquadFilterInfo::ParameterVersion2 has the wrong size!"); | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Update the info with new parameters, version 1. | ||
| 40 | * | ||
| 41 | * @param error_info - Used to write call result code. | ||
| 42 | * @param in_params - New parameters to update the info with. | ||
| 43 | * @param pool_mapper - Pool for mapping buffers. | ||
| 44 | */ | ||
| 45 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 46 | const PoolMapper& pool_mapper) override; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Update the info with new parameters, version 2. | ||
| 50 | * | ||
| 51 | * @param error_info - Used to write call result code. | ||
| 52 | * @param in_params - New parameters to update the info with. | ||
| 53 | * @param pool_mapper - Pool for mapping buffers. | ||
| 54 | */ | ||
| 55 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 56 | const PoolMapper& pool_mapper) override; | ||
| 57 | |||
| 58 | /** | ||
| 59 | * Update the info after command generation. Usually only changes its state. | ||
| 60 | */ | ||
| 61 | void UpdateForCommandGeneration() override; | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Initialize a new result state. Version 2 only, unused. | ||
| 65 | * | ||
| 66 | * @param result_state - Result state to initialize. | ||
| 67 | */ | ||
| 68 | void InitializeResultState(EffectResultState& result_state) override; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||
| 72 | * | ||
| 73 | * @param cpu_state - Host-side result state to update. | ||
| 74 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 75 | */ | ||
| 76 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||
| 77 | }; | ||
| 78 | |||
| 79 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/buffer_mixer.cpp b/src/audio_core/renderer/effect/buffer_mixer.cpp new file mode 100644 index 000000000..9c8877f01 --- /dev/null +++ b/src/audio_core/renderer/effect/buffer_mixer.cpp | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/buffer_mixer.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | ||
| 10 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 11 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 12 | |||
| 13 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 14 | mix_id = in_params.mix_id; | ||
| 15 | process_order = in_params.process_order; | ||
| 16 | enabled = in_params.enabled; | ||
| 17 | |||
| 18 | error_info.error_code = ResultSuccess; | ||
| 19 | error_info.address = CpuAddr(0); | ||
| 20 | } | ||
| 21 | |||
| 22 | void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 23 | const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||
| 24 | auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; | ||
| 25 | auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; | ||
| 26 | |||
| 27 | std::memcpy(params, in_specific, sizeof(ParameterVersion2)); | ||
| 28 | mix_id = in_params.mix_id; | ||
| 29 | process_order = in_params.process_order; | ||
| 30 | enabled = in_params.enabled; | ||
| 31 | |||
| 32 | error_info.error_code = ResultSuccess; | ||
| 33 | error_info.address = CpuAddr(0); | ||
| 34 | } | ||
| 35 | |||
| 36 | void BufferMixerInfo::UpdateForCommandGeneration() { | ||
| 37 | if (enabled) { | ||
| 38 | usage_state = UsageState::Enabled; | ||
| 39 | } else { | ||
| 40 | usage_state = UsageState::Disabled; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | void BufferMixerInfo::InitializeResultState(EffectResultState& result_state) {} | ||
| 45 | |||
| 46 | void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state, | ||
| 47 | EffectResultState& dsp_state) {} | ||
| 48 | |||
| 49 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/buffer_mixer.h b/src/audio_core/renderer/effect/buffer_mixer.h new file mode 100644 index 000000000..23eed4a8b --- /dev/null +++ b/src/audio_core/renderer/effect/buffer_mixer.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore::AudioRenderer { | ||
| 13 | |||
| 14 | class BufferMixerInfo : public EffectInfoBase { | ||
| 15 | public: | ||
| 16 | struct ParameterVersion1 { | ||
| 17 | /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; | ||
| 18 | /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; | ||
| 19 | /* 0x30 */ std::array<f32, MaxMixBuffers> volumes; | ||
| 20 | /* 0x90 */ u32 mix_count; | ||
| 21 | }; | ||
| 22 | static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||
| 23 | "BufferMixerInfo::ParameterVersion1 has the wrong size!"); | ||
| 24 | |||
| 25 | struct ParameterVersion2 { | ||
| 26 | /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; | ||
| 27 | /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; | ||
| 28 | /* 0x30 */ std::array<f32, MaxMixBuffers> volumes; | ||
| 29 | /* 0x90 */ u32 mix_count; | ||
| 30 | }; | ||
| 31 | static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||
| 32 | "BufferMixerInfo::ParameterVersion2 has the wrong size!"); | ||
| 33 | |||
| 34 | /** | ||
| 35 | * Update the info with new parameters, version 1. | ||
| 36 | * | ||
| 37 | * @param error_info - Used to write call result code. | ||
| 38 | * @param in_params - New parameters to update the info with. | ||
| 39 | * @param pool_mapper - Pool for mapping buffers. | ||
| 40 | */ | ||
| 41 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 42 | const PoolMapper& pool_mapper) override; | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Update the info with new parameters, version 2. | ||
| 46 | * | ||
| 47 | * @param error_info - Used to write call result code. | ||
| 48 | * @param in_params - New parameters to update the info with. | ||
| 49 | * @param pool_mapper - Pool for mapping buffers. | ||
| 50 | */ | ||
| 51 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 52 | const PoolMapper& pool_mapper) override; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Update the info after command generation. Usually only changes its state. | ||
| 56 | */ | ||
| 57 | void UpdateForCommandGeneration() override; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * Initialize a new result state. Version 2 only, unused. | ||
| 61 | * | ||
| 62 | * @param result_state - Result state to initialize. | ||
| 63 | */ | ||
| 64 | void InitializeResultState(EffectResultState& result_state) override; | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||
| 68 | * | ||
| 69 | * @param cpu_state - Host-side result state to update. | ||
| 70 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 71 | */ | ||
| 72 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||
| 73 | }; | ||
| 74 | |||
| 75 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/capture.cpp b/src/audio_core/renderer/effect/capture.cpp new file mode 100644 index 000000000..3f038efdb --- /dev/null +++ b/src/audio_core/renderer/effect/capture.cpp | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/aux_.h" | ||
| 5 | #include "audio_core/renderer/effect/capture.h" | ||
| 6 | |||
| 7 | namespace AudioCore::AudioRenderer { | ||
| 8 | |||
| 9 | void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 10 | const PoolMapper& pool_mapper) { | ||
| 11 | auto in_specific{ | ||
| 12 | reinterpret_cast<const AuxInfo::ParameterVersion1*>(in_params.specific.data())}; | ||
| 13 | auto params{reinterpret_cast<AuxInfo::ParameterVersion1*>(parameter.data())}; | ||
| 14 | |||
| 15 | std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion1)); | ||
| 16 | mix_id = in_params.mix_id; | ||
| 17 | process_order = in_params.process_order; | ||
| 18 | enabled = in_params.enabled; | ||
| 19 | if (buffer_unmapped || in_params.is_new) { | ||
| 20 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 21 | error_info, workbuffers[0], in_specific->send_buffer_info_address, | ||
| 22 | in_specific->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo)); | ||
| 23 | |||
| 24 | if (!buffer_unmapped) { | ||
| 25 | const auto send_address{workbuffers[0].GetReference(false)}; | ||
| 26 | send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp); | ||
| 27 | send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo); | ||
| 28 | return_buffer_info = 0; | ||
| 29 | return_buffer = 0; | ||
| 30 | } | ||
| 31 | } else { | ||
| 32 | error_info.error_code = ResultSuccess; | ||
| 33 | error_info.address = CpuAddr(0); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 38 | const PoolMapper& pool_mapper) { | ||
| 39 | auto in_specific{ | ||
| 40 | reinterpret_cast<const AuxInfo::ParameterVersion2*>(in_params.specific.data())}; | ||
| 41 | auto params{reinterpret_cast<AuxInfo::ParameterVersion2*>(parameter.data())}; | ||
| 42 | |||
| 43 | std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion2)); | ||
| 44 | mix_id = in_params.mix_id; | ||
| 45 | process_order = in_params.process_order; | ||
| 46 | enabled = in_params.enabled; | ||
| 47 | |||
| 48 | if (buffer_unmapped || in_params.is_new) { | ||
| 49 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 50 | error_info, workbuffers[0], params->send_buffer_info_address, | ||
| 51 | params->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo)); | ||
| 52 | |||
| 53 | if (!buffer_unmapped) { | ||
| 54 | const auto send_address{workbuffers[0].GetReference(false)}; | ||
| 55 | send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp); | ||
| 56 | send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo); | ||
| 57 | return_buffer_info = 0; | ||
| 58 | return_buffer = 0; | ||
| 59 | } | ||
| 60 | } else { | ||
| 61 | error_info.error_code = ResultSuccess; | ||
| 62 | error_info.address = CpuAddr(0); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | void CaptureInfo::UpdateForCommandGeneration() { | ||
| 67 | if (enabled) { | ||
| 68 | usage_state = UsageState::Enabled; | ||
| 69 | } else { | ||
| 70 | usage_state = UsageState::Disabled; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | void CaptureInfo::InitializeResultState(EffectResultState& result_state) {} | ||
| 75 | |||
| 76 | void CaptureInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} | ||
| 77 | |||
| 78 | CpuAddr CaptureInfo::GetWorkbuffer(s32 index) { | ||
| 79 | return workbuffers[index].GetReference(true); | ||
| 80 | } | ||
| 81 | |||
| 82 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/capture.h b/src/audio_core/renderer/effect/capture.h new file mode 100644 index 000000000..6fbed8e6b --- /dev/null +++ b/src/audio_core/renderer/effect/capture.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore::AudioRenderer { | ||
| 13 | |||
| 14 | class CaptureInfo : public EffectInfoBase { | ||
| 15 | public: | ||
| 16 | /** | ||
| 17 | * Update the info with new parameters, version 1. | ||
| 18 | * | ||
| 19 | * @param error_info - Used to write call result code. | ||
| 20 | * @param in_params - New parameters to update the info with. | ||
| 21 | * @param pool_mapper - Pool for mapping buffers. | ||
| 22 | */ | ||
| 23 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 24 | const PoolMapper& pool_mapper) override; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Update the info with new parameters, version 2. | ||
| 28 | * | ||
| 29 | * @param error_info - Used to write call result code. | ||
| 30 | * @param in_params - New parameters to update the info with. | ||
| 31 | * @param pool_mapper - Pool for mapping buffers. | ||
| 32 | */ | ||
| 33 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 34 | const PoolMapper& pool_mapper) override; | ||
| 35 | |||
| 36 | /** | ||
| 37 | * Update the info after command generation. Usually only changes its state. | ||
| 38 | */ | ||
| 39 | void UpdateForCommandGeneration() override; | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Initialize a new result state. Version 2 only, unused. | ||
| 43 | * | ||
| 44 | * @param result_state - Result state to initialize. | ||
| 45 | */ | ||
| 46 | void InitializeResultState(EffectResultState& result_state) override; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||
| 50 | * | ||
| 51 | * @param cpu_state - Host-side result state to update. | ||
| 52 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 53 | */ | ||
| 54 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Get a workbuffer assigned to this effect with the given index. | ||
| 58 | * | ||
| 59 | * @param index - Workbuffer index. | ||
| 60 | * @return Address of the buffer. | ||
| 61 | */ | ||
| 62 | CpuAddr GetWorkbuffer(s32 index) override; | ||
| 63 | }; | ||
| 64 | |||
| 65 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/compressor.cpp b/src/audio_core/renderer/effect/compressor.cpp new file mode 100644 index 000000000..220ae02f9 --- /dev/null +++ b/src/audio_core/renderer/effect/compressor.cpp | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/compressor.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {} | ||
| 10 | |||
| 11 | void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 12 | const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||
| 13 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 14 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 15 | |||
| 16 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 17 | mix_id = in_params.mix_id; | ||
| 18 | process_order = in_params.process_order; | ||
| 19 | enabled = in_params.enabled; | ||
| 20 | |||
| 21 | error_info.error_code = ResultSuccess; | ||
| 22 | error_info.address = CpuAddr(0); | ||
| 23 | } | ||
| 24 | |||
| 25 | void CompressorInfo::UpdateForCommandGeneration() { | ||
| 26 | if (enabled) { | ||
| 27 | usage_state = UsageState::Enabled; | ||
| 28 | } else { | ||
| 29 | usage_state = UsageState::Disabled; | ||
| 30 | } | ||
| 31 | |||
| 32 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 33 | params->state = ParameterState::Updated; | ||
| 34 | } | ||
| 35 | |||
| 36 | CpuAddr CompressorInfo::GetWorkbuffer(s32 index) { | ||
| 37 | return GetSingleBuffer(index); | ||
| 38 | } | ||
| 39 | |||
| 40 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/compressor.h b/src/audio_core/renderer/effect/compressor.h new file mode 100644 index 000000000..019a5ae58 --- /dev/null +++ b/src/audio_core/renderer/effect/compressor.h | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/fixed_point.h" | ||
| 12 | |||
| 13 | namespace AudioCore::AudioRenderer { | ||
| 14 | |||
| 15 | class CompressorInfo : public EffectInfoBase { | ||
| 16 | public: | ||
| 17 | struct ParameterVersion1 { | ||
| 18 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 19 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 20 | /* 0x0C */ s16 channel_count_max; | ||
| 21 | /* 0x0E */ s16 channel_count; | ||
| 22 | /* 0x10 */ s32 sample_rate; | ||
| 23 | /* 0x14 */ f32 threshold; | ||
| 24 | /* 0x18 */ f32 compressor_ratio; | ||
| 25 | /* 0x1C */ s32 attack_time; | ||
| 26 | /* 0x20 */ s32 release_time; | ||
| 27 | /* 0x24 */ f32 unk_24; | ||
| 28 | /* 0x28 */ f32 unk_28; | ||
| 29 | /* 0x2C */ f32 unk_2C; | ||
| 30 | /* 0x30 */ f32 out_gain; | ||
| 31 | /* 0x34 */ ParameterState state; | ||
| 32 | /* 0x35 */ bool makeup_gain_enabled; | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||
| 35 | "CompressorInfo::ParameterVersion1 has the wrong size!"); | ||
| 36 | |||
| 37 | struct ParameterVersion2 { | ||
| 38 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 39 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 40 | /* 0x0C */ s16 channel_count_max; | ||
| 41 | /* 0x0E */ s16 channel_count; | ||
| 42 | /* 0x10 */ s32 sample_rate; | ||
| 43 | /* 0x14 */ f32 threshold; | ||
| 44 | /* 0x18 */ f32 compressor_ratio; | ||
| 45 | /* 0x1C */ s32 attack_time; | ||
| 46 | /* 0x20 */ s32 release_time; | ||
| 47 | /* 0x24 */ f32 unk_24; | ||
| 48 | /* 0x28 */ f32 unk_28; | ||
| 49 | /* 0x2C */ f32 unk_2C; | ||
| 50 | /* 0x30 */ f32 out_gain; | ||
| 51 | /* 0x34 */ ParameterState state; | ||
| 52 | /* 0x35 */ bool makeup_gain_enabled; | ||
| 53 | }; | ||
| 54 | static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||
| 55 | "CompressorInfo::ParameterVersion2 has the wrong size!"); | ||
| 56 | |||
| 57 | struct State { | ||
| 58 | f32 unk_00; | ||
| 59 | f32 unk_04; | ||
| 60 | f32 unk_08; | ||
| 61 | f32 unk_0C; | ||
| 62 | f32 unk_10; | ||
| 63 | f32 unk_14; | ||
| 64 | f32 unk_18; | ||
| 65 | f32 makeup_gain; | ||
| 66 | f32 unk_20; | ||
| 67 | char unk_24[0x1C]; | ||
| 68 | }; | ||
| 69 | static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||
| 70 | "CompressorInfo::State has the wrong size!"); | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Update the info with new parameters, version 1. | ||
| 74 | * | ||
| 75 | * @param error_info - Used to write call result code. | ||
| 76 | * @param in_params - New parameters to update the info with. | ||
| 77 | * @param pool_mapper - Pool for mapping buffers. | ||
| 78 | */ | ||
| 79 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 80 | const PoolMapper& pool_mapper) override; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Update the info with new parameters, version 2. | ||
| 84 | * | ||
| 85 | * @param error_info - Used to write call result code. | ||
| 86 | * @param in_params - New parameters to update the info with. | ||
| 87 | * @param pool_mapper - Pool for mapping buffers. | ||
| 88 | */ | ||
| 89 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 90 | const PoolMapper& pool_mapper) override; | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Update the info after command generation. Usually only changes its state. | ||
| 94 | */ | ||
| 95 | void UpdateForCommandGeneration() override; | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Get a workbuffer assigned to this effect with the given index. | ||
| 99 | * | ||
| 100 | * @param index - Workbuffer index. | ||
| 101 | * @return Address of the buffer. | ||
| 102 | */ | ||
| 103 | CpuAddr GetWorkbuffer(s32 index) override; | ||
| 104 | }; | ||
| 105 | |||
| 106 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/delay.cpp b/src/audio_core/renderer/effect/delay.cpp new file mode 100644 index 000000000..d9853efd9 --- /dev/null +++ b/src/audio_core/renderer/effect/delay.cpp | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/delay.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 9 | const PoolMapper& pool_mapper) { | ||
| 10 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 11 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 12 | |||
| 13 | if (IsChannelCountValid(in_specific->channel_count_max)) { | ||
| 14 | const auto old_state{params->state}; | ||
| 15 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 16 | mix_id = in_params.mix_id; | ||
| 17 | process_order = in_params.process_order; | ||
| 18 | enabled = in_params.enabled; | ||
| 19 | |||
| 20 | if (!IsChannelCountValid(in_specific->channel_count)) { | ||
| 21 | params->channel_count = params->channel_count_max; | ||
| 22 | } | ||
| 23 | |||
| 24 | if (!IsChannelCountValid(in_specific->channel_count) || | ||
| 25 | old_state != ParameterState::Updated) { | ||
| 26 | params->state = old_state; | ||
| 27 | } | ||
| 28 | |||
| 29 | if (buffer_unmapped || in_params.is_new) { | ||
| 30 | usage_state = UsageState::New; | ||
| 31 | params->state = ParameterState::Initialized; | ||
| 32 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 33 | error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | error_info.error_code = ResultSuccess; | ||
| 38 | error_info.address = CpuAddr(0); | ||
| 39 | } | ||
| 40 | |||
| 41 | void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 42 | const PoolMapper& pool_mapper) { | ||
| 43 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 44 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 45 | |||
| 46 | if (IsChannelCountValid(in_specific->channel_count_max)) { | ||
| 47 | const auto old_state{params->state}; | ||
| 48 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 49 | mix_id = in_params.mix_id; | ||
| 50 | process_order = in_params.process_order; | ||
| 51 | enabled = in_params.enabled; | ||
| 52 | |||
| 53 | if (!IsChannelCountValid(in_specific->channel_count)) { | ||
| 54 | params->channel_count = params->channel_count_max; | ||
| 55 | } | ||
| 56 | |||
| 57 | if (!IsChannelCountValid(in_specific->channel_count) || | ||
| 58 | old_state != ParameterState::Updated) { | ||
| 59 | params->state = old_state; | ||
| 60 | } | ||
| 61 | |||
| 62 | if (buffer_unmapped || in_params.is_new) { | ||
| 63 | usage_state = UsageState::New; | ||
| 64 | params->state = ParameterState::Initialized; | ||
| 65 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 66 | error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||
| 67 | return; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | error_info.error_code = ResultSuccess; | ||
| 71 | error_info.address = CpuAddr(0); | ||
| 72 | } | ||
| 73 | |||
| 74 | void DelayInfo::UpdateForCommandGeneration() { | ||
| 75 | if (enabled) { | ||
| 76 | usage_state = UsageState::Enabled; | ||
| 77 | } else { | ||
| 78 | usage_state = UsageState::Disabled; | ||
| 79 | } | ||
| 80 | |||
| 81 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 82 | params->state = ParameterState::Updated; | ||
| 83 | } | ||
| 84 | |||
| 85 | void DelayInfo::InitializeResultState(EffectResultState& result_state) {} | ||
| 86 | |||
| 87 | void DelayInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} | ||
| 88 | |||
| 89 | CpuAddr DelayInfo::GetWorkbuffer(s32 index) { | ||
| 90 | return GetSingleBuffer(index); | ||
| 91 | } | ||
| 92 | |||
| 93 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/delay.h b/src/audio_core/renderer/effect/delay.h new file mode 100644 index 000000000..accc42a06 --- /dev/null +++ b/src/audio_core/renderer/effect/delay.h | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "audio_core/common/common.h" | ||
| 10 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/fixed_point.h" | ||
| 13 | |||
| 14 | namespace AudioCore::AudioRenderer { | ||
| 15 | |||
| 16 | class DelayInfo : public EffectInfoBase { | ||
| 17 | public: | ||
| 18 | struct ParameterVersion1 { | ||
| 19 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 20 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 21 | /* 0x0C */ u16 channel_count_max; | ||
| 22 | /* 0x0E */ u16 channel_count; | ||
| 23 | /* 0x10 */ u32 delay_time_max; | ||
| 24 | /* 0x14 */ u32 delay_time; | ||
| 25 | /* 0x18 */ Common::FixedPoint<18, 14> sample_rate; | ||
| 26 | /* 0x1C */ Common::FixedPoint<18, 14> in_gain; | ||
| 27 | /* 0x20 */ Common::FixedPoint<18, 14> feedback_gain; | ||
| 28 | /* 0x24 */ Common::FixedPoint<18, 14> wet_gain; | ||
| 29 | /* 0x28 */ Common::FixedPoint<18, 14> dry_gain; | ||
| 30 | /* 0x2C */ Common::FixedPoint<18, 14> channel_spread; | ||
| 31 | /* 0x30 */ Common::FixedPoint<18, 14> lowpass_amount; | ||
| 32 | /* 0x34 */ ParameterState state; | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||
| 35 | "DelayInfo::ParameterVersion1 has the wrong size!"); | ||
| 36 | |||
| 37 | struct ParameterVersion2 { | ||
| 38 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 39 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 40 | /* 0x0C */ s16 channel_count_max; | ||
| 41 | /* 0x0E */ s16 channel_count; | ||
| 42 | /* 0x10 */ s32 delay_time_max; | ||
| 43 | /* 0x14 */ s32 delay_time; | ||
| 44 | /* 0x18 */ s32 sample_rate; | ||
| 45 | /* 0x1C */ s32 in_gain; | ||
| 46 | /* 0x20 */ s32 feedback_gain; | ||
| 47 | /* 0x24 */ s32 wet_gain; | ||
| 48 | /* 0x28 */ s32 dry_gain; | ||
| 49 | /* 0x2C */ s32 channel_spread; | ||
| 50 | /* 0x30 */ s32 lowpass_amount; | ||
| 51 | /* 0x34 */ ParameterState state; | ||
| 52 | }; | ||
| 53 | static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||
| 54 | "DelayInfo::ParameterVersion2 has the wrong size!"); | ||
| 55 | |||
| 56 | struct DelayLine { | ||
| 57 | Common::FixedPoint<50, 14> Read() const { | ||
| 58 | return buffer[buffer_pos]; | ||
| 59 | } | ||
| 60 | |||
| 61 | void Write(const Common::FixedPoint<50, 14> value) { | ||
| 62 | buffer[buffer_pos] = value; | ||
| 63 | buffer_pos = static_cast<u32>((buffer_pos + 1) % buffer.size()); | ||
| 64 | } | ||
| 65 | |||
| 66 | s32 sample_count_max{}; | ||
| 67 | s32 sample_count{}; | ||
| 68 | std::vector<Common::FixedPoint<50, 14>> buffer{}; | ||
| 69 | u32 buffer_pos{}; | ||
| 70 | Common::FixedPoint<18, 14> decay_rate{}; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct State { | ||
| 74 | /* 0x000 */ std::array<s32, 8> unk_000; | ||
| 75 | /* 0x020 */ std::array<DelayLine, MaxChannels> delay_lines; | ||
| 76 | /* 0x0B0 */ Common::FixedPoint<18, 14> feedback_gain; | ||
| 77 | /* 0x0B4 */ Common::FixedPoint<18, 14> delay_feedback_gain; | ||
| 78 | /* 0x0B8 */ Common::FixedPoint<18, 14> delay_feedback_cross_gain; | ||
| 79 | /* 0x0BC */ Common::FixedPoint<18, 14> lowpass_gain; | ||
| 80 | /* 0x0C0 */ Common::FixedPoint<18, 14> lowpass_feedback_gain; | ||
| 81 | /* 0x0C4 */ std::array<Common::FixedPoint<50, 14>, MaxChannels> lowpass_z; | ||
| 82 | }; | ||
| 83 | static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||
| 84 | "DelayInfo::State has the wrong size!"); | ||
| 85 | |||
| 86 | /** | ||
| 87 | * Update the info with new parameters, version 1. | ||
| 88 | * | ||
| 89 | * @param error_info - Used to write call result code. | ||
| 90 | * @param in_params - New parameters to update the info with. | ||
| 91 | * @param pool_mapper - Pool for mapping buffers. | ||
| 92 | */ | ||
| 93 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 94 | const PoolMapper& pool_mapper) override; | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Update the info with new parameters, version 2. | ||
| 98 | * | ||
| 99 | * @param error_info - Used to write call result code. | ||
| 100 | * @param in_params - New parameters to update the info with. | ||
| 101 | * @param pool_mapper - Pool for mapping buffers. | ||
| 102 | */ | ||
| 103 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 104 | const PoolMapper& pool_mapper) override; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Update the info after command generation. Usually only changes its state. | ||
| 108 | */ | ||
| 109 | void UpdateForCommandGeneration() override; | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Initialize a new result state. Version 2 only, unused. | ||
| 113 | * | ||
| 114 | * @param result_state - Result state to initialize. | ||
| 115 | */ | ||
| 116 | void InitializeResultState(EffectResultState& result_state) override; | ||
| 117 | |||
| 118 | /** | ||
| 119 | * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||
| 120 | * | ||
| 121 | * @param cpu_state - Host-side result state to update. | ||
| 122 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 123 | */ | ||
| 124 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||
| 125 | |||
| 126 | /** | ||
| 127 | * Get a workbuffer assigned to this effect with the given index. | ||
| 128 | * | ||
| 129 | * @param index - Workbuffer index. | ||
| 130 | * @return Address of the buffer. | ||
| 131 | */ | ||
| 132 | CpuAddr GetWorkbuffer(s32 index) override; | ||
| 133 | }; | ||
| 134 | |||
| 135 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/effect_context.cpp b/src/audio_core/renderer/effect/effect_context.cpp new file mode 100644 index 000000000..74c7801c9 --- /dev/null +++ b/src/audio_core/renderer/effect/effect_context.cpp | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/effect_context.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void EffectContext::Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, | ||
| 9 | std::span<EffectResultState> result_states_cpu_, | ||
| 10 | std::span<EffectResultState> result_states_dsp_, | ||
| 11 | const size_t dsp_state_count_) { | ||
| 12 | effect_infos = effect_infos_; | ||
| 13 | effect_count = effect_count_; | ||
| 14 | result_states_cpu = result_states_cpu_; | ||
| 15 | result_states_dsp = result_states_dsp_; | ||
| 16 | dsp_state_count = dsp_state_count_; | ||
| 17 | } | ||
| 18 | |||
| 19 | EffectInfoBase& EffectContext::GetInfo(const u32 index) { | ||
| 20 | return effect_infos[index]; | ||
| 21 | } | ||
| 22 | |||
| 23 | EffectResultState& EffectContext::GetResultState(const u32 index) { | ||
| 24 | return result_states_cpu[index]; | ||
| 25 | } | ||
| 26 | |||
| 27 | EffectResultState& EffectContext::GetDspSharedResultState(const u32 index) { | ||
| 28 | return result_states_dsp[index]; | ||
| 29 | } | ||
| 30 | |||
| 31 | u32 EffectContext::GetCount() const { | ||
| 32 | return effect_count; | ||
| 33 | } | ||
| 34 | |||
| 35 | void EffectContext::UpdateStateByDspShared() { | ||
| 36 | for (size_t i = 0; i < dsp_state_count; i++) { | ||
| 37 | effect_infos[i].UpdateResultState(result_states_cpu[i], result_states_dsp[i]); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/effect_context.h b/src/audio_core/renderer/effect/effect_context.h new file mode 100644 index 000000000..85955bd9c --- /dev/null +++ b/src/audio_core/renderer/effect/effect_context.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 9 | #include "audio_core/renderer/effect/effect_result_state.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore::AudioRenderer { | ||
| 13 | |||
| 14 | class EffectContext { | ||
| 15 | public: | ||
| 16 | /** | ||
| 17 | * Initialize the effect context | ||
| 18 | * @param effect_infos List of effect infos for this context | ||
| 19 | * @param effect_count The number of effects in the list | ||
| 20 | * @param result_states_cpu The workbuffer of result states for the CPU for this context | ||
| 21 | * @param result_states_dsp The workbuffer of result states for the DSP for this context | ||
| 22 | * @param state_count The number of result states | ||
| 23 | */ | ||
| 24 | void Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, | ||
| 25 | std::span<EffectResultState> result_states_cpu_, | ||
| 26 | std::span<EffectResultState> result_states_dsp_, const size_t dsp_state_count); | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Get the EffectInfo for a given index | ||
| 30 | * @param index Which effect to return | ||
| 31 | * @return Pointer to the effect | ||
| 32 | */ | ||
| 33 | EffectInfoBase& GetInfo(const u32 index); | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Get the CPU result state for a given index | ||
| 37 | * @param index Which result to return | ||
| 38 | * @return Pointer to the effect result state | ||
| 39 | */ | ||
| 40 | EffectResultState& GetResultState(const u32 index); | ||
| 41 | |||
| 42 | /** | ||
| 43 | * Get the DSP result state for a given index | ||
| 44 | * @param index Which result to return | ||
| 45 | * @return Pointer to the effect result state | ||
| 46 | */ | ||
| 47 | EffectResultState& GetDspSharedResultState(const u32 index); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Get the number of effects in this context | ||
| 51 | * @return The number of effects | ||
| 52 | */ | ||
| 53 | u32 GetCount() const; | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Update the CPU and DSP result states for all effects | ||
| 57 | */ | ||
| 58 | void UpdateStateByDspShared(); | ||
| 59 | |||
| 60 | private: | ||
| 61 | /// Workbuffer for all of the effects | ||
| 62 | std::span<EffectInfoBase> effect_infos{}; | ||
| 63 | /// Number of effects in the workbuffer | ||
| 64 | u32 effect_count{}; | ||
| 65 | /// Workbuffer of states for all effects, kept host-side and not directly modified, dsp states | ||
| 66 | /// are copied here on the next render frame | ||
| 67 | std::span<EffectResultState> result_states_cpu{}; | ||
| 68 | /// Workbuffer of states for all effects, used by the AudioRenderer to track effect state | ||
| 69 | /// between calls | ||
| 70 | std::span<EffectResultState> result_states_dsp{}; | ||
| 71 | /// Number of result states in the workbuffers | ||
| 72 | size_t dsp_state_count{}; | ||
| 73 | }; | ||
| 74 | |||
| 75 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/effect_info_base.h b/src/audio_core/renderer/effect/effect_info_base.h new file mode 100644 index 000000000..43d0589cc --- /dev/null +++ b/src/audio_core/renderer/effect/effect_info_base.h | |||
| @@ -0,0 +1,435 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "audio_core/common/common.h" | ||
| 9 | #include "audio_core/renderer/behavior/behavior_info.h" | ||
| 10 | #include "audio_core/renderer/effect/effect_result_state.h" | ||
| 11 | #include "audio_core/renderer/memory/address_info.h" | ||
| 12 | #include "audio_core/renderer/memory/pool_mapper.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | |||
| 15 | namespace AudioCore::AudioRenderer { | ||
| 16 | /** | ||
| 17 | * Base of all effects. Holds various data and functions used for all derived effects. | ||
| 18 | * Should not be used directly. | ||
| 19 | */ | ||
| 20 | class EffectInfoBase { | ||
| 21 | public: | ||
| 22 | enum class Type : u8 { | ||
| 23 | Invalid, | ||
| 24 | Mix, | ||
| 25 | Aux, | ||
| 26 | Delay, | ||
| 27 | Reverb, | ||
| 28 | I3dl2Reverb, | ||
| 29 | BiquadFilter, | ||
| 30 | LightLimiter, | ||
| 31 | Capture, | ||
| 32 | Compressor, | ||
| 33 | }; | ||
| 34 | |||
| 35 | enum class UsageState { | ||
| 36 | Invalid, | ||
| 37 | New, | ||
| 38 | Enabled, | ||
| 39 | Disabled, | ||
| 40 | }; | ||
| 41 | |||
| 42 | enum class OutStatus : u8 { | ||
| 43 | Invalid, | ||
| 44 | New, | ||
| 45 | Initialized, | ||
| 46 | Used, | ||
| 47 | Removed, | ||
| 48 | }; | ||
| 49 | |||
| 50 | enum class ParameterState : u8 { | ||
| 51 | Initialized, | ||
| 52 | Updating, | ||
| 53 | Updated, | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct InParameterVersion1 { | ||
| 57 | /* 0x00 */ Type type; | ||
| 58 | /* 0x01 */ bool is_new; | ||
| 59 | /* 0x02 */ bool enabled; | ||
| 60 | /* 0x04 */ u32 mix_id; | ||
| 61 | /* 0x08 */ CpuAddr workbuffer; | ||
| 62 | /* 0x10 */ CpuAddr workbuffer_size; | ||
| 63 | /* 0x18 */ u32 process_order; | ||
| 64 | /* 0x1C */ char unk1C[0x4]; | ||
| 65 | /* 0x20 */ std::array<u8, 0xA0> specific; | ||
| 66 | }; | ||
| 67 | static_assert(sizeof(InParameterVersion1) == 0xC0, | ||
| 68 | "EffectInfoBase::InParameterVersion1 has the wrong size!"); | ||
| 69 | |||
| 70 | struct InParameterVersion2 { | ||
| 71 | /* 0x00 */ Type type; | ||
| 72 | /* 0x01 */ bool is_new; | ||
| 73 | /* 0x02 */ bool enabled; | ||
| 74 | /* 0x04 */ u32 mix_id; | ||
| 75 | /* 0x08 */ CpuAddr workbuffer; | ||
| 76 | /* 0x10 */ CpuAddr workbuffer_size; | ||
| 77 | /* 0x18 */ u32 process_order; | ||
| 78 | /* 0x1C */ char unk1C[0x4]; | ||
| 79 | /* 0x20 */ std::array<u8, 0xA0> specific; | ||
| 80 | }; | ||
| 81 | static_assert(sizeof(InParameterVersion2) == 0xC0, | ||
| 82 | "EffectInfoBase::InParameterVersion2 has the wrong size!"); | ||
| 83 | |||
| 84 | struct OutStatusVersion1 { | ||
| 85 | /* 0x00 */ OutStatus state; | ||
| 86 | /* 0x01 */ char unk01[0xF]; | ||
| 87 | }; | ||
| 88 | static_assert(sizeof(OutStatusVersion1) == 0x10, | ||
| 89 | "EffectInfoBase::OutStatusVersion1 has the wrong size!"); | ||
| 90 | |||
| 91 | struct OutStatusVersion2 { | ||
| 92 | /* 0x00 */ OutStatus state; | ||
| 93 | /* 0x01 */ char unk01[0xF]; | ||
| 94 | /* 0x10 */ EffectResultState result_state; | ||
| 95 | }; | ||
| 96 | static_assert(sizeof(OutStatusVersion2) == 0x90, | ||
| 97 | "EffectInfoBase::OutStatusVersion2 has the wrong size!"); | ||
| 98 | |||
| 99 | struct State { | ||
| 100 | std::array<u8, 0x500> buffer; | ||
| 101 | }; | ||
| 102 | static_assert(sizeof(State) == 0x500, "EffectInfoBase::State has the wrong size!"); | ||
| 103 | |||
| 104 | EffectInfoBase() { | ||
| 105 | Cleanup(); | ||
| 106 | } | ||
| 107 | |||
| 108 | virtual ~EffectInfoBase() = default; | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Cleanup this effect, resetting it to a starting state. | ||
| 112 | */ | ||
| 113 | void Cleanup() { | ||
| 114 | type = Type::Invalid; | ||
| 115 | enabled = false; | ||
| 116 | mix_id = UnusedMixId; | ||
| 117 | process_order = InvalidProcessOrder; | ||
| 118 | buffer_unmapped = false; | ||
| 119 | parameter = {}; | ||
| 120 | for (auto& workbuffer : workbuffers) { | ||
| 121 | workbuffer.Setup(CpuAddr(0), 0); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | /** | ||
| 126 | * Forcibly unmap all assigned workbuffers from the AudioRenderer. | ||
| 127 | * | ||
| 128 | * @param pool_mapper - Mapper to unmap the buffers. | ||
| 129 | */ | ||
| 130 | void ForceUnmapBuffers(const PoolMapper& pool_mapper) { | ||
| 131 | for (auto& workbuffer : workbuffers) { | ||
| 132 | if (workbuffer.GetReference(false) != 0) { | ||
| 133 | pool_mapper.ForceUnmapPointer(workbuffer); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | /** | ||
| 139 | * Check if this effect is enabled. | ||
| 140 | * | ||
| 141 | * @return True if effect is enabled, otherwise false. | ||
| 142 | */ | ||
| 143 | bool IsEnabled() const { | ||
| 144 | return enabled; | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Check if this effect should not be generated. | ||
| 149 | * | ||
| 150 | * @return True if effect should be skipped, otherwise false. | ||
| 151 | */ | ||
| 152 | bool ShouldSkip() const { | ||
| 153 | return buffer_unmapped; | ||
| 154 | } | ||
| 155 | |||
| 156 | /** | ||
| 157 | * Get the type of this effect. | ||
| 158 | * | ||
| 159 | * @return The type of this effect. See EffectInfoBase::Type | ||
| 160 | */ | ||
| 161 | Type GetType() const { | ||
| 162 | return type; | ||
| 163 | } | ||
| 164 | |||
| 165 | /** | ||
| 166 | * Set the type of this effect. | ||
| 167 | * | ||
| 168 | * @param type_ - The new type of this effect. | ||
| 169 | */ | ||
| 170 | void SetType(const Type type_) { | ||
| 171 | type = type_; | ||
| 172 | } | ||
| 173 | |||
| 174 | /** | ||
| 175 | * Get the mix id of this effect. | ||
| 176 | * | ||
| 177 | * @return Mix id of this effect. | ||
| 178 | */ | ||
| 179 | s32 GetMixId() const { | ||
| 180 | return mix_id; | ||
| 181 | } | ||
| 182 | |||
| 183 | /** | ||
| 184 | * Get the processing order of this effect. | ||
| 185 | * | ||
| 186 | * @return Process order of this effect. | ||
| 187 | */ | ||
| 188 | s32 GetProcessingOrder() const { | ||
| 189 | return process_order; | ||
| 190 | } | ||
| 191 | |||
| 192 | /** | ||
| 193 | * Get this effect's parameter data. | ||
| 194 | * | ||
| 195 | * @return Pointer to the parametter, must be cast to the correct type. | ||
| 196 | */ | ||
| 197 | u8* GetParameter() { | ||
| 198 | return parameter.data(); | ||
| 199 | } | ||
| 200 | |||
| 201 | /** | ||
| 202 | * Get this effect's parameter data. | ||
| 203 | * | ||
| 204 | * @return Pointer to the parametter, must be cast to the correct type. | ||
| 205 | */ | ||
| 206 | u8* GetStateBuffer() { | ||
| 207 | return state.data(); | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Set this effect's usage state. | ||
| 212 | * | ||
| 213 | * @param usage - new usage state of this effect. | ||
| 214 | */ | ||
| 215 | void SetUsage(const UsageState usage) { | ||
| 216 | usage_state = usage; | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 220 | * Check if this effects need to have its workbuffer information updated. | ||
| 221 | * Version 1. | ||
| 222 | * | ||
| 223 | * @param params - Input parameters. | ||
| 224 | * @return True if workbuffers need updating, otherwise false. | ||
| 225 | */ | ||
| 226 | bool ShouldUpdateWorkBufferInfo(const InParameterVersion1& params) const { | ||
| 227 | return buffer_unmapped || params.is_new; | ||
| 228 | } | ||
| 229 | |||
| 230 | /** | ||
| 231 | * Check if this effects need to have its workbuffer information updated. | ||
| 232 | * Version 2. | ||
| 233 | * | ||
| 234 | * @param params - Input parameters. | ||
| 235 | * @return True if workbuffers need updating, otherwise false. | ||
| 236 | */ | ||
| 237 | bool ShouldUpdateWorkBufferInfo(const InParameterVersion2& params) const { | ||
| 238 | return buffer_unmapped || params.is_new; | ||
| 239 | } | ||
| 240 | |||
| 241 | /** | ||
| 242 | * Get the current usage state of this effect. | ||
| 243 | * | ||
| 244 | * @return The current usage state. | ||
| 245 | */ | ||
| 246 | UsageState GetUsage() const { | ||
| 247 | return usage_state; | ||
| 248 | } | ||
| 249 | |||
| 250 | /** | ||
| 251 | * Write the current state. Version 1. | ||
| 252 | * | ||
| 253 | * @param out_status - Status to write. | ||
| 254 | * @param renderer_active - Is the AudioRenderer active? | ||
| 255 | */ | ||
| 256 | void StoreStatus(OutStatusVersion1& out_status, const bool renderer_active) const { | ||
| 257 | if (renderer_active) { | ||
| 258 | if (usage_state != UsageState::Disabled) { | ||
| 259 | out_status.state = OutStatus::Used; | ||
| 260 | } else { | ||
| 261 | out_status.state = OutStatus::Removed; | ||
| 262 | } | ||
| 263 | } else if (usage_state == UsageState::New) { | ||
| 264 | out_status.state = OutStatus::Used; | ||
| 265 | } else { | ||
| 266 | out_status.state = OutStatus::Removed; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | /** | ||
| 271 | * Write the current state. Version 2. | ||
| 272 | * | ||
| 273 | * @param out_status - Status to write. | ||
| 274 | * @param renderer_active - Is the AudioRenderer active? | ||
| 275 | */ | ||
| 276 | void StoreStatus(OutStatusVersion2& out_status, const bool renderer_active) const { | ||
| 277 | if (renderer_active) { | ||
| 278 | if (usage_state != UsageState::Disabled) { | ||
| 279 | out_status.state = OutStatus::Used; | ||
| 280 | } else { | ||
| 281 | out_status.state = OutStatus::Removed; | ||
| 282 | } | ||
| 283 | } else if (usage_state == UsageState::New) { | ||
| 284 | out_status.state = OutStatus::Used; | ||
| 285 | } else { | ||
| 286 | out_status.state = OutStatus::Removed; | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | /** | ||
| 291 | * Update the info with new parameters, version 1. | ||
| 292 | * | ||
| 293 | * @param error_info - Used to write call result code. | ||
| 294 | * @param in_params - New parameters to update the info with. | ||
| 295 | * @param pool_mapper - Pool for mapping buffers. | ||
| 296 | */ | ||
| 297 | virtual void Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 298 | [[maybe_unused]] const InParameterVersion1& params, | ||
| 299 | [[maybe_unused]] const PoolMapper& pool_mapper) { | ||
| 300 | error_info.error_code = ResultSuccess; | ||
| 301 | error_info.address = CpuAddr(0); | ||
| 302 | } | ||
| 303 | |||
| 304 | /** | ||
| 305 | * Update the info with new parameters, version 2. | ||
| 306 | * | ||
| 307 | * @param error_info - Used to write call result code. | ||
| 308 | * @param in_params - New parameters to update the info with. | ||
| 309 | * @param pool_mapper - Pool for mapping buffers. | ||
| 310 | */ | ||
| 311 | virtual void Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 312 | [[maybe_unused]] const InParameterVersion2& params, | ||
| 313 | [[maybe_unused]] const PoolMapper& pool_mapper) { | ||
| 314 | error_info.error_code = ResultSuccess; | ||
| 315 | error_info.address = CpuAddr(0); | ||
| 316 | } | ||
| 317 | |||
| 318 | /** | ||
| 319 | * Update the info after command generation. Usually only changes its state. | ||
| 320 | */ | ||
| 321 | virtual void UpdateForCommandGeneration() {} | ||
| 322 | |||
| 323 | /** | ||
| 324 | * Initialize a new result state. Version 2 only, unused. | ||
| 325 | * | ||
| 326 | * @param result_state - Result state to initialize. | ||
| 327 | */ | ||
| 328 | virtual void InitializeResultState([[maybe_unused]] EffectResultState& result_state) {} | ||
| 329 | |||
| 330 | /** | ||
| 331 | * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||
| 332 | * | ||
| 333 | * @param cpu_state - Host-side result state to update. | ||
| 334 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 335 | */ | ||
| 336 | virtual void UpdateResultState([[maybe_unused]] EffectResultState& cpu_state, | ||
| 337 | [[maybe_unused]] EffectResultState& dsp_state) {} | ||
| 338 | |||
| 339 | /** | ||
| 340 | * Get a workbuffer assigned to this effect with the given index. | ||
| 341 | * | ||
| 342 | * @param index - Workbuffer index. | ||
| 343 | * @return Address of the buffer. | ||
| 344 | */ | ||
| 345 | virtual CpuAddr GetWorkbuffer([[maybe_unused]] s32 index) { | ||
| 346 | return 0; | ||
| 347 | } | ||
| 348 | |||
| 349 | /** | ||
| 350 | * Get the first workbuffer assigned to this effect. | ||
| 351 | * | ||
| 352 | * @param index - Workbuffer index. Unused. | ||
| 353 | * @return Address of the buffer. | ||
| 354 | */ | ||
| 355 | CpuAddr GetSingleBuffer([[maybe_unused]] const s32 index) { | ||
| 356 | if (enabled) { | ||
| 357 | return workbuffers[0].GetReference(true); | ||
| 358 | } | ||
| 359 | |||
| 360 | if (usage_state != UsageState::Disabled) { | ||
| 361 | const auto ref{workbuffers[0].GetReference(false)}; | ||
| 362 | const auto size{workbuffers[0].GetSize()}; | ||
| 363 | if (ref != 0 && size > 0) { | ||
| 364 | // Invalidate DSP cache | ||
| 365 | } | ||
| 366 | } | ||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | |||
| 370 | /** | ||
| 371 | * Get the send buffer info, used by Aux and Capture. | ||
| 372 | * | ||
| 373 | * @return Address of the buffer info. | ||
| 374 | */ | ||
| 375 | CpuAddr GetSendBufferInfo() const { | ||
| 376 | return send_buffer_info; | ||
| 377 | } | ||
| 378 | |||
| 379 | /** | ||
| 380 | * Get the send buffer, used by Aux and Capture. | ||
| 381 | * | ||
| 382 | * @return Address of the buffer. | ||
| 383 | */ | ||
| 384 | CpuAddr GetSendBuffer() const { | ||
| 385 | return send_buffer; | ||
| 386 | } | ||
| 387 | |||
| 388 | /** | ||
| 389 | * Get the return buffer info, used by Aux and Capture. | ||
| 390 | * | ||
| 391 | * @return Address of the buffer info. | ||
| 392 | */ | ||
| 393 | CpuAddr GetReturnBufferInfo() const { | ||
| 394 | return return_buffer_info; | ||
| 395 | } | ||
| 396 | |||
| 397 | /** | ||
| 398 | * Get the return buffer, used by Aux and Capture. | ||
| 399 | * | ||
| 400 | * @return Address of the buffer. | ||
| 401 | */ | ||
| 402 | CpuAddr GetReturnBuffer() const { | ||
| 403 | return return_buffer; | ||
| 404 | } | ||
| 405 | |||
| 406 | protected: | ||
| 407 | /// Type of this effect. May be changed | ||
| 408 | Type type{Type::Invalid}; | ||
| 409 | /// Is this effect enabled? | ||
| 410 | bool enabled{}; | ||
| 411 | /// Are this effect's buffers unmapped? | ||
| 412 | bool buffer_unmapped{}; | ||
| 413 | /// Current usage state | ||
| 414 | UsageState usage_state{UsageState::Invalid}; | ||
| 415 | /// Mix id of this effect | ||
| 416 | s32 mix_id{UnusedMixId}; | ||
| 417 | /// Process order of this effect | ||
| 418 | s32 process_order{InvalidProcessOrder}; | ||
| 419 | /// Workbuffers assigned to this effect | ||
| 420 | std::array<AddressInfo, 2> workbuffers{AddressInfo(CpuAddr(0), 0), AddressInfo(CpuAddr(0), 0)}; | ||
| 421 | /// Aux/Capture buffer info for reading | ||
| 422 | CpuAddr send_buffer_info; | ||
| 423 | /// Aux/Capture buffer for reading | ||
| 424 | CpuAddr send_buffer; | ||
| 425 | /// Aux/Capture buffer info for writing | ||
| 426 | CpuAddr return_buffer_info; | ||
| 427 | /// Aux/Capture buffer for writing | ||
| 428 | CpuAddr return_buffer; | ||
| 429 | /// Parameters of this effect | ||
| 430 | std::array<u8, sizeof(InParameterVersion2)> parameter{}; | ||
| 431 | /// State of this effect used by the AudioRenderer across calls | ||
| 432 | std::array<u8, sizeof(State)> state{}; | ||
| 433 | }; | ||
| 434 | |||
| 435 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/effect_reset.h b/src/audio_core/renderer/effect/effect_reset.h new file mode 100644 index 000000000..1ea67e334 --- /dev/null +++ b/src/audio_core/renderer/effect/effect_reset.h | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/renderer/effect/aux_.h" | ||
| 7 | #include "audio_core/renderer/effect/biquad_filter.h" | ||
| 8 | #include "audio_core/renderer/effect/buffer_mixer.h" | ||
| 9 | #include "audio_core/renderer/effect/capture.h" | ||
| 10 | #include "audio_core/renderer/effect/compressor.h" | ||
| 11 | #include "audio_core/renderer/effect/delay.h" | ||
| 12 | #include "audio_core/renderer/effect/i3dl2.h" | ||
| 13 | #include "audio_core/renderer/effect/light_limiter.h" | ||
| 14 | #include "audio_core/renderer/effect/reverb.h" | ||
| 15 | #include "common/common_types.h" | ||
| 16 | |||
| 17 | namespace AudioCore::AudioRenderer { | ||
| 18 | /** | ||
| 19 | * Reset an effect, and create a new one of the given type. | ||
| 20 | * | ||
| 21 | * @param effect - Effect to reset and re-construct. | ||
| 22 | * @param type - Type of the new effect to create. | ||
| 23 | */ | ||
| 24 | static void ResetEffect(EffectInfoBase* effect, const EffectInfoBase::Type type) { | ||
| 25 | *effect = {}; | ||
| 26 | |||
| 27 | switch (type) { | ||
| 28 | case EffectInfoBase::Type::Invalid: | ||
| 29 | std::construct_at<EffectInfoBase>(effect); | ||
| 30 | effect->SetType(EffectInfoBase::Type::Invalid); | ||
| 31 | break; | ||
| 32 | case EffectInfoBase::Type::Mix: | ||
| 33 | std::construct_at<BufferMixerInfo>(reinterpret_cast<BufferMixerInfo*>(effect)); | ||
| 34 | effect->SetType(EffectInfoBase::Type::Mix); | ||
| 35 | break; | ||
| 36 | case EffectInfoBase::Type::Aux: | ||
| 37 | std::construct_at<AuxInfo>(reinterpret_cast<AuxInfo*>(effect)); | ||
| 38 | effect->SetType(EffectInfoBase::Type::Aux); | ||
| 39 | break; | ||
| 40 | case EffectInfoBase::Type::Delay: | ||
| 41 | std::construct_at<DelayInfo>(reinterpret_cast<DelayInfo*>(effect)); | ||
| 42 | effect->SetType(EffectInfoBase::Type::Delay); | ||
| 43 | break; | ||
| 44 | case EffectInfoBase::Type::Reverb: | ||
| 45 | std::construct_at<ReverbInfo>(reinterpret_cast<ReverbInfo*>(effect)); | ||
| 46 | effect->SetType(EffectInfoBase::Type::Reverb); | ||
| 47 | break; | ||
| 48 | case EffectInfoBase::Type::I3dl2Reverb: | ||
| 49 | std::construct_at<I3dl2ReverbInfo>(reinterpret_cast<I3dl2ReverbInfo*>(effect)); | ||
| 50 | effect->SetType(EffectInfoBase::Type::I3dl2Reverb); | ||
| 51 | break; | ||
| 52 | case EffectInfoBase::Type::BiquadFilter: | ||
| 53 | std::construct_at<BiquadFilterInfo>(reinterpret_cast<BiquadFilterInfo*>(effect)); | ||
| 54 | effect->SetType(EffectInfoBase::Type::BiquadFilter); | ||
| 55 | break; | ||
| 56 | case EffectInfoBase::Type::LightLimiter: | ||
| 57 | std::construct_at<LightLimiterInfo>(reinterpret_cast<LightLimiterInfo*>(effect)); | ||
| 58 | effect->SetType(EffectInfoBase::Type::LightLimiter); | ||
| 59 | break; | ||
| 60 | case EffectInfoBase::Type::Capture: | ||
| 61 | std::construct_at<CaptureInfo>(reinterpret_cast<CaptureInfo*>(effect)); | ||
| 62 | effect->SetType(EffectInfoBase::Type::Capture); | ||
| 63 | break; | ||
| 64 | case EffectInfoBase::Type::Compressor: | ||
| 65 | std::construct_at<CompressorInfo>(reinterpret_cast<CompressorInfo*>(effect)); | ||
| 66 | effect->SetType(EffectInfoBase::Type::Compressor); | ||
| 67 | break; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/effect_result_state.h b/src/audio_core/renderer/effect/effect_result_state.h new file mode 100644 index 000000000..ae096ad69 --- /dev/null +++ b/src/audio_core/renderer/effect/effect_result_state.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace AudioCore::AudioRenderer { | ||
| 11 | |||
| 12 | struct EffectResultState { | ||
| 13 | std::array<u8, 0x80> state; | ||
| 14 | }; | ||
| 15 | |||
| 16 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/i3dl2.cpp b/src/audio_core/renderer/effect/i3dl2.cpp new file mode 100644 index 000000000..960b29cfc --- /dev/null +++ b/src/audio_core/renderer/effect/i3dl2.cpp | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/i3dl2.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | ||
| 10 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 11 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 12 | |||
| 13 | if (IsChannelCountValid(in_specific->channel_count_max)) { | ||
| 14 | const auto old_state{params->state}; | ||
| 15 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 16 | mix_id = in_params.mix_id; | ||
| 17 | process_order = in_params.process_order; | ||
| 18 | enabled = in_params.enabled; | ||
| 19 | |||
| 20 | if (!IsChannelCountValid(in_specific->channel_count)) { | ||
| 21 | params->channel_count = params->channel_count_max; | ||
| 22 | } | ||
| 23 | |||
| 24 | if (!IsChannelCountValid(in_specific->channel_count) || | ||
| 25 | old_state != ParameterState::Updated) { | ||
| 26 | params->state = old_state; | ||
| 27 | } | ||
| 28 | |||
| 29 | if (buffer_unmapped || in_params.is_new) { | ||
| 30 | usage_state = UsageState::New; | ||
| 31 | params->state = ParameterState::Initialized; | ||
| 32 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 33 | error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | error_info.error_code = ResultSuccess; | ||
| 38 | error_info.address = CpuAddr(0); | ||
| 39 | } | ||
| 40 | |||
| 41 | void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 42 | const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||
| 43 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 44 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 45 | |||
| 46 | if (IsChannelCountValid(in_specific->channel_count_max)) { | ||
| 47 | const auto old_state{params->state}; | ||
| 48 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 49 | mix_id = in_params.mix_id; | ||
| 50 | process_order = in_params.process_order; | ||
| 51 | enabled = in_params.enabled; | ||
| 52 | |||
| 53 | if (!IsChannelCountValid(in_specific->channel_count)) { | ||
| 54 | params->channel_count = params->channel_count_max; | ||
| 55 | } | ||
| 56 | |||
| 57 | if (!IsChannelCountValid(in_specific->channel_count) || | ||
| 58 | old_state != ParameterState::Updated) { | ||
| 59 | params->state = old_state; | ||
| 60 | } | ||
| 61 | |||
| 62 | if (buffer_unmapped || in_params.is_new) { | ||
| 63 | usage_state = UsageState::New; | ||
| 64 | params->state = ParameterState::Initialized; | ||
| 65 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 66 | error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||
| 67 | return; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | error_info.error_code = ResultSuccess; | ||
| 71 | error_info.address = CpuAddr(0); | ||
| 72 | } | ||
| 73 | |||
| 74 | void I3dl2ReverbInfo::UpdateForCommandGeneration() { | ||
| 75 | if (enabled) { | ||
| 76 | usage_state = UsageState::Enabled; | ||
| 77 | } else { | ||
| 78 | usage_state = UsageState::Disabled; | ||
| 79 | } | ||
| 80 | |||
| 81 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 82 | params->state = ParameterState::Updated; | ||
| 83 | } | ||
| 84 | |||
| 85 | void I3dl2ReverbInfo::InitializeResultState(EffectResultState& result_state) {} | ||
| 86 | |||
| 87 | void I3dl2ReverbInfo::UpdateResultState(EffectResultState& cpu_state, | ||
| 88 | EffectResultState& dsp_state) {} | ||
| 89 | |||
| 90 | CpuAddr I3dl2ReverbInfo::GetWorkbuffer(s32 index) { | ||
| 91 | return GetSingleBuffer(index); | ||
| 92 | } | ||
| 93 | |||
| 94 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/i3dl2.h b/src/audio_core/renderer/effect/i3dl2.h new file mode 100644 index 000000000..7a088a627 --- /dev/null +++ b/src/audio_core/renderer/effect/i3dl2.h | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "audio_core/common/common.h" | ||
| 10 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/fixed_point.h" | ||
| 13 | |||
| 14 | namespace AudioCore::AudioRenderer { | ||
| 15 | |||
| 16 | class I3dl2ReverbInfo : public EffectInfoBase { | ||
| 17 | public: | ||
| 18 | struct ParameterVersion1 { | ||
| 19 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 20 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 21 | /* 0x0C */ u16 channel_count_max; | ||
| 22 | /* 0x0E */ u16 channel_count; | ||
| 23 | /* 0x10 */ char unk10[0x4]; | ||
| 24 | /* 0x14 */ u32 sample_rate; | ||
| 25 | /* 0x18 */ f32 room_HF_gain; | ||
| 26 | /* 0x1C */ f32 reference_HF; | ||
| 27 | /* 0x20 */ f32 late_reverb_decay_time; | ||
| 28 | /* 0x24 */ f32 late_reverb_HF_decay_ratio; | ||
| 29 | /* 0x28 */ f32 room_gain; | ||
| 30 | /* 0x2C */ f32 reflection_gain; | ||
| 31 | /* 0x30 */ f32 reverb_gain; | ||
| 32 | /* 0x34 */ f32 late_reverb_diffusion; | ||
| 33 | /* 0x38 */ f32 reflection_delay; | ||
| 34 | /* 0x3C */ f32 late_reverb_delay_time; | ||
| 35 | /* 0x40 */ f32 late_reverb_density; | ||
| 36 | /* 0x44 */ f32 dry_gain; | ||
| 37 | /* 0x48 */ ParameterState state; | ||
| 38 | /* 0x49 */ char unk49[0x3]; | ||
| 39 | }; | ||
| 40 | static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||
| 41 | "I3dl2ReverbInfo::ParameterVersion1 has the wrong size!"); | ||
| 42 | |||
| 43 | struct ParameterVersion2 { | ||
| 44 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 45 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 46 | /* 0x0C */ u16 channel_count_max; | ||
| 47 | /* 0x0E */ u16 channel_count; | ||
| 48 | /* 0x10 */ char unk10[0x4]; | ||
| 49 | /* 0x14 */ u32 sample_rate; | ||
| 50 | /* 0x18 */ f32 room_HF_gain; | ||
| 51 | /* 0x1C */ f32 reference_HF; | ||
| 52 | /* 0x20 */ f32 late_reverb_decay_time; | ||
| 53 | /* 0x24 */ f32 late_reverb_HF_decay_ratio; | ||
| 54 | /* 0x28 */ f32 room_gain; | ||
| 55 | /* 0x2C */ f32 reflection_gain; | ||
| 56 | /* 0x30 */ f32 reverb_gain; | ||
| 57 | /* 0x34 */ f32 late_reverb_diffusion; | ||
| 58 | /* 0x38 */ f32 reflection_delay; | ||
| 59 | /* 0x3C */ f32 late_reverb_delay_time; | ||
| 60 | /* 0x40 */ f32 late_reverb_density; | ||
| 61 | /* 0x44 */ f32 dry_gain; | ||
| 62 | /* 0x48 */ ParameterState state; | ||
| 63 | /* 0x49 */ char unk49[0x3]; | ||
| 64 | }; | ||
| 65 | static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||
| 66 | "I3dl2ReverbInfo::ParameterVersion2 has the wrong size!"); | ||
| 67 | |||
| 68 | static constexpr u32 MaxDelayLines = 4; | ||
| 69 | static constexpr u32 MaxDelayTaps = 20; | ||
| 70 | |||
| 71 | struct I3dl2DelayLine { | ||
| 72 | void Initialize(const s32 delay_time) { | ||
| 73 | max_delay = delay_time; | ||
| 74 | buffer.resize(delay_time + 1, 0); | ||
| 75 | buffer_end = &buffer[delay_time]; | ||
| 76 | output = &buffer[0]; | ||
| 77 | SetDelay(delay_time); | ||
| 78 | wet_gain = 0.0f; | ||
| 79 | } | ||
| 80 | |||
| 81 | void SetDelay(const s32 delay_time) { | ||
| 82 | if (max_delay < delay_time) { | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | delay = delay_time; | ||
| 86 | input = &buffer[(output - buffer.data() + delay) % (max_delay + 1)]; | ||
| 87 | } | ||
| 88 | |||
| 89 | Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) { | ||
| 90 | Write(sample); | ||
| 91 | |||
| 92 | auto out_sample{Read()}; | ||
| 93 | |||
| 94 | output++; | ||
| 95 | if (output >= buffer_end) { | ||
| 96 | output = buffer.data(); | ||
| 97 | } | ||
| 98 | |||
| 99 | return out_sample; | ||
| 100 | } | ||
| 101 | |||
| 102 | Common::FixedPoint<50, 14> Read() { | ||
| 103 | return *output; | ||
| 104 | } | ||
| 105 | |||
| 106 | void Write(const Common::FixedPoint<50, 14> sample) { | ||
| 107 | *(input++) = sample; | ||
| 108 | if (input >= buffer_end) { | ||
| 109 | input = buffer.data(); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | Common::FixedPoint<50, 14> TapOut(const s32 index) { | ||
| 114 | auto out{input - (index + 1)}; | ||
| 115 | if (out < buffer.data()) { | ||
| 116 | out += max_delay + 1; | ||
| 117 | } | ||
| 118 | return *out; | ||
| 119 | } | ||
| 120 | |||
| 121 | std::vector<Common::FixedPoint<50, 14>> buffer{}; | ||
| 122 | Common::FixedPoint<50, 14>* buffer_end{}; | ||
| 123 | s32 max_delay{}; | ||
| 124 | Common::FixedPoint<50, 14>* input{}; | ||
| 125 | Common::FixedPoint<50, 14>* output{}; | ||
| 126 | s32 delay{}; | ||
| 127 | f32 wet_gain{}; | ||
| 128 | }; | ||
| 129 | |||
| 130 | struct State { | ||
| 131 | f32 lowpass_0; | ||
| 132 | f32 lowpass_1; | ||
| 133 | f32 lowpass_2; | ||
| 134 | I3dl2DelayLine early_delay_line; | ||
| 135 | std::array<s32, MaxDelayTaps> early_tap_steps; | ||
| 136 | f32 early_gain; | ||
| 137 | f32 late_gain; | ||
| 138 | s32 early_to_late_taps; | ||
| 139 | std::array<I3dl2DelayLine, MaxDelayLines> fdn_delay_lines; | ||
| 140 | std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines0; | ||
| 141 | std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines1; | ||
| 142 | f32 last_reverb_echo; | ||
| 143 | I3dl2DelayLine center_delay_line; | ||
| 144 | std::array<std::array<f32, 3>, MaxDelayLines> lowpass_coeff; | ||
| 145 | std::array<f32, MaxDelayLines> shelf_filter; | ||
| 146 | f32 dry_gain; | ||
| 147 | }; | ||
| 148 | static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||
| 149 | "I3dl2ReverbInfo::State is too large!"); | ||
| 150 | |||
| 151 | /** | ||
| 152 | * Update the info with new parameters, version 1. | ||
| 153 | * | ||
| 154 | * @param error_info - Used to write call result code. | ||
| 155 | * @param in_params - New parameters to update the info with. | ||
| 156 | * @param pool_mapper - Pool for mapping buffers. | ||
| 157 | */ | ||
| 158 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 159 | const PoolMapper& pool_mapper) override; | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Update the info with new parameters, version 2. | ||
| 163 | * | ||
| 164 | * @param error_info - Used to write call result code. | ||
| 165 | * @param in_params - New parameters to update the info with. | ||
| 166 | * @param pool_mapper - Pool for mapping buffers. | ||
| 167 | */ | ||
| 168 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 169 | const PoolMapper& pool_mapper) override; | ||
| 170 | |||
| 171 | /** | ||
| 172 | * Update the info after command generation. Usually only changes its state. | ||
| 173 | */ | ||
| 174 | void UpdateForCommandGeneration() override; | ||
| 175 | |||
| 176 | /** | ||
| 177 | * Initialize a new result state. Version 2 only, unused. | ||
| 178 | * | ||
| 179 | * @param result_state - Result state to initialize. | ||
| 180 | */ | ||
| 181 | void InitializeResultState(EffectResultState& result_state) override; | ||
| 182 | |||
| 183 | /** | ||
| 184 | * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||
| 185 | * | ||
| 186 | * @param cpu_state - Host-side result state to update. | ||
| 187 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 188 | */ | ||
| 189 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||
| 190 | |||
| 191 | /** | ||
| 192 | * Get a workbuffer assigned to this effect with the given index. | ||
| 193 | * | ||
| 194 | * @param index - Workbuffer index. | ||
| 195 | * @return Address of the buffer. | ||
| 196 | */ | ||
| 197 | CpuAddr GetWorkbuffer(s32 index) override; | ||
| 198 | }; | ||
| 199 | |||
| 200 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/light_limiter.cpp b/src/audio_core/renderer/effect/light_limiter.cpp new file mode 100644 index 000000000..1635a952d --- /dev/null +++ b/src/audio_core/renderer/effect/light_limiter.cpp | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/light_limiter.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 9 | const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { | ||
| 10 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 11 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 12 | |||
| 13 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 14 | mix_id = in_params.mix_id; | ||
| 15 | process_order = in_params.process_order; | ||
| 16 | enabled = in_params.enabled; | ||
| 17 | |||
| 18 | if (buffer_unmapped || in_params.is_new) { | ||
| 19 | usage_state = UsageState::New; | ||
| 20 | params->state = ParameterState::Initialized; | ||
| 21 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 22 | error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||
| 23 | } else { | ||
| 24 | error_info.error_code = ResultSuccess; | ||
| 25 | error_info.address = CpuAddr(0); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, | ||
| 30 | const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { | ||
| 31 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 32 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 33 | |||
| 34 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 35 | mix_id = in_params.mix_id; | ||
| 36 | process_order = in_params.process_order; | ||
| 37 | enabled = in_params.enabled; | ||
| 38 | |||
| 39 | if (buffer_unmapped || in_params.is_new) { | ||
| 40 | usage_state = UsageState::New; | ||
| 41 | params->state = ParameterState::Initialized; | ||
| 42 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 43 | error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||
| 44 | } else { | ||
| 45 | error_info.error_code = ResultSuccess; | ||
| 46 | error_info.address = CpuAddr(0); | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | void LightLimiterInfo::UpdateForCommandGeneration() { | ||
| 51 | if (enabled) { | ||
| 52 | usage_state = UsageState::Enabled; | ||
| 53 | } else { | ||
| 54 | usage_state = UsageState::Disabled; | ||
| 55 | } | ||
| 56 | |||
| 57 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 58 | params->state = ParameterState::Updated; | ||
| 59 | params->statistics_reset_required = false; | ||
| 60 | } | ||
| 61 | |||
| 62 | void LightLimiterInfo::InitializeResultState(EffectResultState& result_state) { | ||
| 63 | auto result_state_{reinterpret_cast<StatisticsInternal*>(result_state.state.data())}; | ||
| 64 | |||
| 65 | result_state_->channel_max_sample.fill(0); | ||
| 66 | result_state_->channel_compression_gain_min.fill(1.0f); | ||
| 67 | } | ||
| 68 | |||
| 69 | void LightLimiterInfo::UpdateResultState(EffectResultState& cpu_state, | ||
| 70 | EffectResultState& dsp_state) { | ||
| 71 | auto cpu_statistics{reinterpret_cast<StatisticsInternal*>(cpu_state.state.data())}; | ||
| 72 | auto dsp_statistics{reinterpret_cast<StatisticsInternal*>(dsp_state.state.data())}; | ||
| 73 | |||
| 74 | *cpu_statistics = *dsp_statistics; | ||
| 75 | } | ||
| 76 | |||
| 77 | CpuAddr LightLimiterInfo::GetWorkbuffer(s32 index) { | ||
| 78 | return GetSingleBuffer(index); | ||
| 79 | } | ||
| 80 | |||
| 81 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/light_limiter.h b/src/audio_core/renderer/effect/light_limiter.h new file mode 100644 index 000000000..338d67bbc --- /dev/null +++ b/src/audio_core/renderer/effect/light_limiter.h | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "audio_core/common/common.h" | ||
| 10 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/fixed_point.h" | ||
| 13 | |||
| 14 | namespace AudioCore::AudioRenderer { | ||
| 15 | |||
| 16 | class LightLimiterInfo : public EffectInfoBase { | ||
| 17 | public: | ||
| 18 | enum class ProcessingMode { | ||
| 19 | Mode0, | ||
| 20 | Mode1, | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct ParameterVersion1 { | ||
| 24 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 25 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 26 | /* 0x0C */ u16 channel_count_max; | ||
| 27 | /* 0x0E */ u16 channel_count; | ||
| 28 | /* 0x0C */ u32 sample_rate; | ||
| 29 | /* 0x14 */ s32 look_ahead_time_max; | ||
| 30 | /* 0x18 */ s32 attack_time; | ||
| 31 | /* 0x1C */ s32 release_time; | ||
| 32 | /* 0x20 */ s32 look_ahead_time; | ||
| 33 | /* 0x24 */ f32 attack_coeff; | ||
| 34 | /* 0x28 */ f32 release_coeff; | ||
| 35 | /* 0x2C */ f32 threshold; | ||
| 36 | /* 0x30 */ f32 input_gain; | ||
| 37 | /* 0x34 */ f32 output_gain; | ||
| 38 | /* 0x38 */ s32 look_ahead_samples_min; | ||
| 39 | /* 0x3C */ s32 look_ahead_samples_max; | ||
| 40 | /* 0x40 */ ParameterState state; | ||
| 41 | /* 0x41 */ bool statistics_enabled; | ||
| 42 | /* 0x42 */ bool statistics_reset_required; | ||
| 43 | /* 0x43 */ ProcessingMode processing_mode; | ||
| 44 | }; | ||
| 45 | static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||
| 46 | "LightLimiterInfo::ParameterVersion1 has the wrong size!"); | ||
| 47 | |||
| 48 | struct ParameterVersion2 { | ||
| 49 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 50 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 51 | /* 0x0C */ u16 channel_count_max; | ||
| 52 | /* 0x0E */ u16 channel_count; | ||
| 53 | /* 0x0C */ u32 sample_rate; | ||
| 54 | /* 0x14 */ s32 look_ahead_time_max; | ||
| 55 | /* 0x18 */ s32 attack_time; | ||
| 56 | /* 0x1C */ s32 release_time; | ||
| 57 | /* 0x20 */ s32 look_ahead_time; | ||
| 58 | /* 0x24 */ f32 attack_coeff; | ||
| 59 | /* 0x28 */ f32 release_coeff; | ||
| 60 | /* 0x2C */ f32 threshold; | ||
| 61 | /* 0x30 */ f32 input_gain; | ||
| 62 | /* 0x34 */ f32 output_gain; | ||
| 63 | /* 0x38 */ s32 look_ahead_samples_min; | ||
| 64 | /* 0x3C */ s32 look_ahead_samples_max; | ||
| 65 | /* 0x40 */ ParameterState state; | ||
| 66 | /* 0x41 */ bool statistics_enabled; | ||
| 67 | /* 0x42 */ bool statistics_reset_required; | ||
| 68 | /* 0x43 */ ProcessingMode processing_mode; | ||
| 69 | }; | ||
| 70 | static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||
| 71 | "LightLimiterInfo::ParameterVersion2 has the wrong size!"); | ||
| 72 | |||
| 73 | struct State { | ||
| 74 | std::array<Common::FixedPoint<49, 15>, MaxChannels> samples_average; | ||
| 75 | std::array<Common::FixedPoint<49, 15>, MaxChannels> compression_gain; | ||
| 76 | std::array<s32, MaxChannels> look_ahead_sample_offsets; | ||
| 77 | std::array<std::vector<Common::FixedPoint<49, 15>>, MaxChannels> look_ahead_sample_buffers; | ||
| 78 | }; | ||
| 79 | static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||
| 80 | "LightLimiterInfo::State has the wrong size!"); | ||
| 81 | |||
| 82 | struct StatisticsInternal { | ||
| 83 | /* 0x00 */ std::array<f32, MaxChannels> channel_max_sample; | ||
| 84 | /* 0x18 */ std::array<f32, MaxChannels> channel_compression_gain_min; | ||
| 85 | }; | ||
| 86 | static_assert(sizeof(StatisticsInternal) == 0x30, | ||
| 87 | "LightLimiterInfo::StatisticsInternal has the wrong size!"); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Update the info with new parameters, version 1. | ||
| 91 | * | ||
| 92 | * @param error_info - Used to write call result code. | ||
| 93 | * @param in_params - New parameters to update the info with. | ||
| 94 | * @param pool_mapper - Pool for mapping buffers. | ||
| 95 | */ | ||
| 96 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 97 | const PoolMapper& pool_mapper) override; | ||
| 98 | |||
| 99 | /** | ||
| 100 | * Update the info with new parameters, version 2. | ||
| 101 | * | ||
| 102 | * @param error_info - Used to write call result code. | ||
| 103 | * @param in_params - New parameters to update the info with. | ||
| 104 | * @param pool_mapper - Pool for mapping buffers. | ||
| 105 | */ | ||
| 106 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 107 | const PoolMapper& pool_mapper) override; | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Update the info after command generation. Usually only changes its state. | ||
| 111 | */ | ||
| 112 | void UpdateForCommandGeneration() override; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Initialize a new limiter statistics result state. Version 2 only. | ||
| 116 | * | ||
| 117 | * @param result_state - Result state to initialize. | ||
| 118 | */ | ||
| 119 | void InitializeResultState(EffectResultState& result_state) override; | ||
| 120 | |||
| 121 | /** | ||
| 122 | * Update the host-side limiter statistics with the ADSP-side one. Version 2 only. | ||
| 123 | * | ||
| 124 | * @param cpu_state - Host-side result state to update. | ||
| 125 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 126 | */ | ||
| 127 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||
| 128 | |||
| 129 | /** | ||
| 130 | * Get a workbuffer assigned to this effect with the given index. | ||
| 131 | * | ||
| 132 | * @param index - Workbuffer index. | ||
| 133 | * @return Address of the buffer. | ||
| 134 | */ | ||
| 135 | CpuAddr GetWorkbuffer(s32 index) override; | ||
| 136 | }; | ||
| 137 | |||
| 138 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/reverb.cpp b/src/audio_core/renderer/effect/reverb.cpp new file mode 100644 index 000000000..2d32383d0 --- /dev/null +++ b/src/audio_core/renderer/effect/reverb.cpp | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/effect/reverb.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 9 | const PoolMapper& pool_mapper) { | ||
| 10 | auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; | ||
| 11 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 12 | |||
| 13 | if (IsChannelCountValid(in_specific->channel_count_max)) { | ||
| 14 | const auto old_state{params->state}; | ||
| 15 | std::memcpy(params, in_specific, sizeof(ParameterVersion1)); | ||
| 16 | mix_id = in_params.mix_id; | ||
| 17 | process_order = in_params.process_order; | ||
| 18 | enabled = in_params.enabled; | ||
| 19 | |||
| 20 | if (!IsChannelCountValid(in_specific->channel_count)) { | ||
| 21 | params->channel_count = params->channel_count_max; | ||
| 22 | } | ||
| 23 | |||
| 24 | if (!IsChannelCountValid(in_specific->channel_count) || | ||
| 25 | old_state != ParameterState::Updated) { | ||
| 26 | params->state = old_state; | ||
| 27 | } | ||
| 28 | |||
| 29 | if (buffer_unmapped || in_params.is_new) { | ||
| 30 | usage_state = UsageState::New; | ||
| 31 | params->state = ParameterState::Initialized; | ||
| 32 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 33 | error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | error_info.error_code = ResultSuccess; | ||
| 38 | error_info.address = CpuAddr(0); | ||
| 39 | } | ||
| 40 | |||
| 41 | void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 42 | const PoolMapper& pool_mapper) { | ||
| 43 | auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; | ||
| 44 | auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; | ||
| 45 | |||
| 46 | if (IsChannelCountValid(in_specific->channel_count_max)) { | ||
| 47 | const auto old_state{params->state}; | ||
| 48 | std::memcpy(params, in_specific, sizeof(ParameterVersion2)); | ||
| 49 | mix_id = in_params.mix_id; | ||
| 50 | process_order = in_params.process_order; | ||
| 51 | enabled = in_params.enabled; | ||
| 52 | |||
| 53 | if (!IsChannelCountValid(in_specific->channel_count)) { | ||
| 54 | params->channel_count = params->channel_count_max; | ||
| 55 | } | ||
| 56 | |||
| 57 | if (!IsChannelCountValid(in_specific->channel_count) || | ||
| 58 | old_state != ParameterState::Updated) { | ||
| 59 | params->state = old_state; | ||
| 60 | } | ||
| 61 | |||
| 62 | if (buffer_unmapped || in_params.is_new) { | ||
| 63 | usage_state = UsageState::New; | ||
| 64 | params->state = ParameterState::Initialized; | ||
| 65 | buffer_unmapped = !pool_mapper.TryAttachBuffer( | ||
| 66 | error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); | ||
| 67 | return; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | error_info.error_code = ResultSuccess; | ||
| 71 | error_info.address = CpuAddr(0); | ||
| 72 | } | ||
| 73 | |||
| 74 | void ReverbInfo::UpdateForCommandGeneration() { | ||
| 75 | if (enabled) { | ||
| 76 | usage_state = UsageState::Enabled; | ||
| 77 | } else { | ||
| 78 | usage_state = UsageState::Disabled; | ||
| 79 | } | ||
| 80 | |||
| 81 | auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; | ||
| 82 | params->state = ParameterState::Updated; | ||
| 83 | } | ||
| 84 | |||
| 85 | void ReverbInfo::InitializeResultState(EffectResultState& result_state) {} | ||
| 86 | |||
| 87 | void ReverbInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} | ||
| 88 | |||
| 89 | CpuAddr ReverbInfo::GetWorkbuffer(s32 index) { | ||
| 90 | return GetSingleBuffer(index); | ||
| 91 | } | ||
| 92 | |||
| 93 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/effect/reverb.h b/src/audio_core/renderer/effect/reverb.h new file mode 100644 index 000000000..b4df9f6ef --- /dev/null +++ b/src/audio_core/renderer/effect/reverb.h | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "audio_core/common/common.h" | ||
| 10 | #include "audio_core/renderer/effect/effect_info_base.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/fixed_point.h" | ||
| 13 | |||
| 14 | namespace AudioCore::AudioRenderer { | ||
| 15 | |||
| 16 | class ReverbInfo : public EffectInfoBase { | ||
| 17 | public: | ||
| 18 | struct ParameterVersion1 { | ||
| 19 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 20 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 21 | /* 0x0C */ u16 channel_count_max; | ||
| 22 | /* 0x0E */ u16 channel_count; | ||
| 23 | /* 0x10 */ u32 sample_rate; | ||
| 24 | /* 0x14 */ u32 early_mode; | ||
| 25 | /* 0x18 */ s32 early_gain; | ||
| 26 | /* 0x1C */ s32 pre_delay; | ||
| 27 | /* 0x20 */ s32 late_mode; | ||
| 28 | /* 0x24 */ s32 late_gain; | ||
| 29 | /* 0x28 */ s32 decay_time; | ||
| 30 | /* 0x2C */ s32 high_freq_Decay_ratio; | ||
| 31 | /* 0x30 */ s32 colouration; | ||
| 32 | /* 0x34 */ s32 base_gain; | ||
| 33 | /* 0x38 */ s32 wet_gain; | ||
| 34 | /* 0x3C */ s32 dry_gain; | ||
| 35 | /* 0x40 */ ParameterState state; | ||
| 36 | }; | ||
| 37 | static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), | ||
| 38 | "ReverbInfo::ParameterVersion1 has the wrong size!"); | ||
| 39 | |||
| 40 | struct ParameterVersion2 { | ||
| 41 | /* 0x00 */ std::array<s8, MaxChannels> inputs; | ||
| 42 | /* 0x06 */ std::array<s8, MaxChannels> outputs; | ||
| 43 | /* 0x0C */ u16 channel_count_max; | ||
| 44 | /* 0x0E */ u16 channel_count; | ||
| 45 | /* 0x10 */ u32 sample_rate; | ||
| 46 | /* 0x14 */ u32 early_mode; | ||
| 47 | /* 0x18 */ s32 early_gain; | ||
| 48 | /* 0x1C */ s32 pre_delay; | ||
| 49 | /* 0x20 */ s32 late_mode; | ||
| 50 | /* 0x24 */ s32 late_gain; | ||
| 51 | /* 0x28 */ s32 decay_time; | ||
| 52 | /* 0x2C */ s32 high_freq_decay_ratio; | ||
| 53 | /* 0x30 */ s32 colouration; | ||
| 54 | /* 0x34 */ s32 base_gain; | ||
| 55 | /* 0x38 */ s32 wet_gain; | ||
| 56 | /* 0x3C */ s32 dry_gain; | ||
| 57 | /* 0x40 */ ParameterState state; | ||
| 58 | }; | ||
| 59 | static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), | ||
| 60 | "ReverbInfo::ParameterVersion2 has the wrong size!"); | ||
| 61 | |||
| 62 | static constexpr u32 MaxDelayLines = 4; | ||
| 63 | static constexpr u32 MaxDelayTaps = 10; | ||
| 64 | static constexpr u32 NumEarlyModes = 5; | ||
| 65 | static constexpr u32 NumLateModes = 5; | ||
| 66 | |||
| 67 | struct ReverbDelayLine { | ||
| 68 | void Initialize(const s32 delay_time, const f32 decay_rate) { | ||
| 69 | buffer.resize(delay_time + 1, 0); | ||
| 70 | buffer_end = &buffer[delay_time]; | ||
| 71 | output = &buffer[0]; | ||
| 72 | decay = decay_rate; | ||
| 73 | sample_count_max = delay_time; | ||
| 74 | SetDelay(delay_time); | ||
| 75 | } | ||
| 76 | |||
| 77 | void SetDelay(const s32 delay_time) { | ||
| 78 | if (sample_count_max < delay_time) { | ||
| 79 | return; | ||
| 80 | } | ||
| 81 | sample_count = delay_time; | ||
| 82 | input = &buffer[(output - buffer.data() + sample_count) % (sample_count_max + 1)]; | ||
| 83 | } | ||
| 84 | |||
| 85 | Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) { | ||
| 86 | Write(sample); | ||
| 87 | |||
| 88 | auto out_sample{Read()}; | ||
| 89 | |||
| 90 | output++; | ||
| 91 | if (output >= buffer_end) { | ||
| 92 | output = buffer.data(); | ||
| 93 | } | ||
| 94 | |||
| 95 | return out_sample; | ||
| 96 | } | ||
| 97 | |||
| 98 | Common::FixedPoint<50, 14> Read() { | ||
| 99 | return *output; | ||
| 100 | } | ||
| 101 | |||
| 102 | void Write(const Common::FixedPoint<50, 14> sample) { | ||
| 103 | *(input++) = sample; | ||
| 104 | if (input >= buffer_end) { | ||
| 105 | input = buffer.data(); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | Common::FixedPoint<50, 14> TapOut(const s32 index) { | ||
| 110 | auto out{input - (index + 1)}; | ||
| 111 | if (out < buffer.data()) { | ||
| 112 | out += sample_count; | ||
| 113 | } | ||
| 114 | return *out; | ||
| 115 | } | ||
| 116 | |||
| 117 | s32 sample_count{}; | ||
| 118 | s32 sample_count_max{}; | ||
| 119 | std::vector<Common::FixedPoint<50, 14>> buffer{}; | ||
| 120 | Common::FixedPoint<50, 14>* buffer_end; | ||
| 121 | Common::FixedPoint<50, 14>* input{}; | ||
| 122 | Common::FixedPoint<50, 14>* output{}; | ||
| 123 | Common::FixedPoint<50, 14> decay{}; | ||
| 124 | }; | ||
| 125 | |||
| 126 | struct State { | ||
| 127 | ReverbDelayLine pre_delay_line; | ||
| 128 | ReverbDelayLine center_delay_line; | ||
| 129 | std::array<s32, MaxDelayTaps> early_delay_times; | ||
| 130 | std::array<Common::FixedPoint<50, 14>, MaxDelayTaps> early_gains; | ||
| 131 | s32 pre_delay_time; | ||
| 132 | std::array<ReverbDelayLine, MaxDelayLines> decay_delay_lines; | ||
| 133 | std::array<ReverbDelayLine, MaxDelayLines> fdn_delay_lines; | ||
| 134 | std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_gain; | ||
| 135 | std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_prev_gain; | ||
| 136 | std::array<Common::FixedPoint<50, 14>, MaxDelayLines> prev_feedback_output; | ||
| 137 | }; | ||
| 138 | static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), | ||
| 139 | "ReverbInfo::State is too large!"); | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Update the info with new parameters, version 1. | ||
| 143 | * | ||
| 144 | * @param error_info - Used to write call result code. | ||
| 145 | * @param in_params - New parameters to update the info with. | ||
| 146 | * @param pool_mapper - Pool for mapping buffers. | ||
| 147 | */ | ||
| 148 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, | ||
| 149 | const PoolMapper& pool_mapper) override; | ||
| 150 | |||
| 151 | /** | ||
| 152 | * Update the info with new parameters, version 2. | ||
| 153 | * | ||
| 154 | * @param error_info - Used to write call result code. | ||
| 155 | * @param in_params - New parameters to update the info with. | ||
| 156 | * @param pool_mapper - Pool for mapping buffers. | ||
| 157 | */ | ||
| 158 | void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, | ||
| 159 | const PoolMapper& pool_mapper) override; | ||
| 160 | |||
| 161 | /** | ||
| 162 | * Update the info after command generation. Usually only changes its state. | ||
| 163 | */ | ||
| 164 | void UpdateForCommandGeneration() override; | ||
| 165 | |||
| 166 | /** | ||
| 167 | * Initialize a new result state. Version 2 only, unused. | ||
| 168 | * | ||
| 169 | * @param result_state - Result state to initialize. | ||
| 170 | */ | ||
| 171 | void InitializeResultState(EffectResultState& result_state) override; | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Update the host-side state with the ADSP-side state. Version 2 only, unused. | ||
| 175 | * | ||
| 176 | * @param cpu_state - Host-side result state to update. | ||
| 177 | * @param dsp_state - AudioRenderer-side result state to update from. | ||
| 178 | */ | ||
| 179 | void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; | ||
| 180 | |||
| 181 | /** | ||
| 182 | * Get a workbuffer assigned to this effect with the given index. | ||
| 183 | * | ||
| 184 | * @param index - Workbuffer index. | ||
| 185 | * @return Address of the buffer. | ||
| 186 | */ | ||
| 187 | CpuAddr GetWorkbuffer(s32 index) override; | ||
| 188 | }; | ||
| 189 | |||
| 190 | } // namespace AudioCore::AudioRenderer | ||