diff options
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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/aux_.h" + +namespace AudioCore::AudioRenderer { + +void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + if (buffer_unmapped || in_params.is_new) { + const bool send_unmapped{!pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_specific->send_buffer_info_address, + sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))}; + const bool return_unmapped{!pool_mapper.TryAttachBuffer( + error_info, workbuffers[1], in_specific->return_buffer_info_address, + sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))}; + + buffer_unmapped = send_unmapped || return_unmapped; + + if (!buffer_unmapped) { + auto send{workbuffers[0].GetReference(false)}; + send_buffer_info = send + sizeof(AuxInfoDsp); + send_buffer = send + sizeof(AuxBufferInfo); + + auto ret{workbuffers[1].GetReference(false)}; + return_buffer_info = ret + sizeof(AuxInfoDsp); + return_buffer = ret + sizeof(AuxBufferInfo); + } + } else { + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); + } +} + +void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion2)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (buffer_unmapped || in_params.is_new) { + const bool send_unmapped{!pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], params->send_buffer_info_address, + sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))}; + const bool return_unmapped{!pool_mapper.TryAttachBuffer( + error_info, workbuffers[1], params->return_buffer_info_address, + sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))}; + + buffer_unmapped = send_unmapped || return_unmapped; + + if (!buffer_unmapped) { + auto send{workbuffers[0].GetReference(false)}; + send_buffer_info = send + sizeof(AuxInfoDsp); + send_buffer = send + sizeof(AuxBufferInfo); + + auto ret{workbuffers[1].GetReference(false)}; + return_buffer_info = ret + sizeof(AuxInfoDsp); + return_buffer = ret + sizeof(AuxBufferInfo); + } + } else { + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); + } +} + +void AuxInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } +} + +void AuxInfo::InitializeResultState(EffectResultState& result_state) {} + +void AuxInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} + +CpuAddr AuxInfo::GetWorkbuffer(s32 index) { + return workbuffers[index].GetReference(true); +} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" + +namespace AudioCore::AudioRenderer { +/** + * Auxiliary Buffer used for Aux commands. + * Send and return buffers are available (names from the game's perspective). + * Send is read by the host, containing a buffer of samples to be used for whatever purpose. + * Return is written by the host, writing a mix buffer back to the game. + * This allows the game to use pre-processed samples skipping the other render processing, + * and to examine or modify what the audio renderer has generated. + */ +class AuxInfo : public EffectInfoBase { +public: + struct ParameterVersion1 { + /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; + /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; + /* 0x30 */ u32 mix_buffer_count; + /* 0x34 */ u32 sample_rate; + /* 0x38 */ u32 count_max; + /* 0x3C */ u32 mix_buffer_count_max; + /* 0x40 */ CpuAddr send_buffer_info_address; + /* 0x48 */ CpuAddr send_buffer_address; + /* 0x50 */ CpuAddr return_buffer_info_address; + /* 0x58 */ CpuAddr return_buffer_address; + /* 0x60 */ u32 mix_buffer_sample_size; + /* 0x64 */ u32 sample_count; + /* 0x68 */ u32 mix_buffer_sample_count; + }; + static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), + "AuxInfo::ParameterVersion1 has the wrong size!"); + + struct ParameterVersion2 { + /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; + /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; + /* 0x30 */ u32 mix_buffer_count; + /* 0x34 */ u32 sample_rate; + /* 0x38 */ u32 count_max; + /* 0x3C */ u32 mix_buffer_count_max; + /* 0x40 */ CpuAddr send_buffer_info_address; + /* 0x48 */ CpuAddr send_buffer_address; + /* 0x50 */ CpuAddr return_buffer_info_address; + /* 0x58 */ CpuAddr return_buffer_address; + /* 0x60 */ u32 mix_buffer_sample_size; + /* 0x64 */ u32 sample_count; + /* 0x68 */ u32 mix_buffer_sample_count; + }; + static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), + "AuxInfo::ParameterVersion2 has the wrong size!"); + + struct AuxInfoDsp { + /* 0x00 */ u32 read_offset; + /* 0x04 */ u32 write_offset; + /* 0x08 */ u32 lost_sample_count; + /* 0x0C */ u32 total_sample_count; + /* 0x10 */ char unk10[0x30]; + }; + static_assert(sizeof(AuxInfoDsp) == 0x40, "AuxInfo::AuxInfoDsp has the wrong size!"); + + struct AuxBufferInfo { + /* 0x00 */ AuxInfoDsp cpu_info; + /* 0x40 */ AuxInfoDsp dsp_info; + }; + static_assert(sizeof(AuxBufferInfo) == 0x80, "AuxInfo::AuxBufferInfo has the wrong size!"); + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Initialize a new result state. Version 2 only, unused. + * + * @param result_state - Result state to initialize. + */ + void InitializeResultState(EffectResultState& result_state) override; + + /** + * Update the host-side state with the ADSP-side state. Version 2 only, unused. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; + + /** + * Get a workbuffer assigned to this effect with the given index. + * + * @param index - Workbuffer index. + * @return Address of the buffer. + */ + CpuAddr GetWorkbuffer(s32 index) override; +}; + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/biquad_filter.h" + +namespace AudioCore::AudioRenderer { + +void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion2)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void BiquadFilterInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } + + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + params->state = ParameterState::Updated; +} + +void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {} + +void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state, + EffectResultState& dsp_state) {} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" + +namespace AudioCore::AudioRenderer { + +class BiquadFilterInfo : public EffectInfoBase { +public: + struct ParameterVersion1 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ std::array<s16, 3> b; + /* 0x12 */ std::array<s16, 2> a; + /* 0x16 */ s8 channel_count; + /* 0x17 */ ParameterState state; + }; + static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), + "BiquadFilterInfo::ParameterVersion1 has the wrong size!"); + + struct ParameterVersion2 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ std::array<s16, 3> b; + /* 0x12 */ std::array<s16, 2> a; + /* 0x16 */ s8 channel_count; + /* 0x17 */ ParameterState state; + }; + static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), + "BiquadFilterInfo::ParameterVersion2 has the wrong size!"); + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Initialize a new result state. Version 2 only, unused. + * + * @param result_state - Result state to initialize. + */ + void InitializeResultState(EffectResultState& result_state) override; + + /** + * Update the host-side state with the ADSP-side state. Version 2 only, unused. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; +}; + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/buffer_mixer.h" + +namespace AudioCore::AudioRenderer { + +void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion2)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void BufferMixerInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } +} + +void BufferMixerInfo::InitializeResultState(EffectResultState& result_state) {} + +void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state, + EffectResultState& dsp_state) {} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" + +namespace AudioCore::AudioRenderer { + +class BufferMixerInfo : public EffectInfoBase { +public: + struct ParameterVersion1 { + /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; + /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; + /* 0x30 */ std::array<f32, MaxMixBuffers> volumes; + /* 0x90 */ u32 mix_count; + }; + static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), + "BufferMixerInfo::ParameterVersion1 has the wrong size!"); + + struct ParameterVersion2 { + /* 0x00 */ std::array<s8, MaxMixBuffers> inputs; + /* 0x18 */ std::array<s8, MaxMixBuffers> outputs; + /* 0x30 */ std::array<f32, MaxMixBuffers> volumes; + /* 0x90 */ u32 mix_count; + }; + static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), + "BufferMixerInfo::ParameterVersion2 has the wrong size!"); + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Initialize a new result state. Version 2 only, unused. + * + * @param result_state - Result state to initialize. + */ + void InitializeResultState(EffectResultState& result_state) override; + + /** + * Update the host-side state with the ADSP-side state. Version 2 only, unused. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; +}; + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/aux_.h" +#include "audio_core/renderer/effect/capture.h" + +namespace AudioCore::AudioRenderer { + +void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) { + auto in_specific{ + reinterpret_cast<const AuxInfo::ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<AuxInfo::ParameterVersion1*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + if (buffer_unmapped || in_params.is_new) { + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_specific->send_buffer_info_address, + in_specific->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo)); + + if (!buffer_unmapped) { + const auto send_address{workbuffers[0].GetReference(false)}; + send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp); + send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo); + return_buffer_info = 0; + return_buffer = 0; + } + } else { + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); + } +} + +void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) { + auto in_specific{ + reinterpret_cast<const AuxInfo::ParameterVersion2*>(in_params.specific.data())}; + auto params{reinterpret_cast<AuxInfo::ParameterVersion2*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion2)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (buffer_unmapped || in_params.is_new) { + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], params->send_buffer_info_address, + params->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo)); + + if (!buffer_unmapped) { + const auto send_address{workbuffers[0].GetReference(false)}; + send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp); + send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo); + return_buffer_info = 0; + return_buffer = 0; + } + } else { + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); + } +} + +void CaptureInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } +} + +void CaptureInfo::InitializeResultState(EffectResultState& result_state) {} + +void CaptureInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} + +CpuAddr CaptureInfo::GetWorkbuffer(s32 index) { + return workbuffers[index].GetReference(true); +} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" + +namespace AudioCore::AudioRenderer { + +class CaptureInfo : public EffectInfoBase { +public: + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Initialize a new result state. Version 2 only, unused. + * + * @param result_state - Result state to initialize. + */ + void InitializeResultState(EffectResultState& result_state) override; + + /** + * Update the host-side state with the ADSP-side state. Version 2 only, unused. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; + + /** + * Get a workbuffer assigned to this effect with the given index. + * + * @param index - Workbuffer index. + * @return Address of the buffer. + */ + CpuAddr GetWorkbuffer(s32 index) override; +}; + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/compressor.h" + +namespace AudioCore::AudioRenderer { + +void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {} + +void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void CompressorInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } + + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + params->state = ParameterState::Updated; +} + +CpuAddr CompressorInfo::GetWorkbuffer(s32 index) { + return GetSingleBuffer(index); +} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" +#include "common/fixed_point.h" + +namespace AudioCore::AudioRenderer { + +class CompressorInfo : public EffectInfoBase { +public: + struct ParameterVersion1 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ s16 channel_count_max; + /* 0x0E */ s16 channel_count; + /* 0x10 */ s32 sample_rate; + /* 0x14 */ f32 threshold; + /* 0x18 */ f32 compressor_ratio; + /* 0x1C */ s32 attack_time; + /* 0x20 */ s32 release_time; + /* 0x24 */ f32 unk_24; + /* 0x28 */ f32 unk_28; + /* 0x2C */ f32 unk_2C; + /* 0x30 */ f32 out_gain; + /* 0x34 */ ParameterState state; + /* 0x35 */ bool makeup_gain_enabled; + }; + static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), + "CompressorInfo::ParameterVersion1 has the wrong size!"); + + struct ParameterVersion2 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ s16 channel_count_max; + /* 0x0E */ s16 channel_count; + /* 0x10 */ s32 sample_rate; + /* 0x14 */ f32 threshold; + /* 0x18 */ f32 compressor_ratio; + /* 0x1C */ s32 attack_time; + /* 0x20 */ s32 release_time; + /* 0x24 */ f32 unk_24; + /* 0x28 */ f32 unk_28; + /* 0x2C */ f32 unk_2C; + /* 0x30 */ f32 out_gain; + /* 0x34 */ ParameterState state; + /* 0x35 */ bool makeup_gain_enabled; + }; + static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), + "CompressorInfo::ParameterVersion2 has the wrong size!"); + + struct State { + f32 unk_00; + f32 unk_04; + f32 unk_08; + f32 unk_0C; + f32 unk_10; + f32 unk_14; + f32 unk_18; + f32 makeup_gain; + f32 unk_20; + char unk_24[0x1C]; + }; + static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), + "CompressorInfo::State has the wrong size!"); + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Get a workbuffer assigned to this effect with the given index. + * + * @param index - Workbuffer index. + * @return Address of the buffer. + */ + CpuAddr GetWorkbuffer(s32 index) override; +}; + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/delay.h" + +namespace AudioCore::AudioRenderer { + +void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + if (IsChannelCountValid(in_specific->channel_count_max)) { + const auto old_state{params->state}; + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (!IsChannelCountValid(in_specific->channel_count)) { + params->channel_count = params->channel_count_max; + } + + if (!IsChannelCountValid(in_specific->channel_count) || + old_state != ParameterState::Updated) { + params->state = old_state; + } + + if (buffer_unmapped || in_params.is_new) { + usage_state = UsageState::New; + params->state = ParameterState::Initialized; + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); + return; + } + } + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + if (IsChannelCountValid(in_specific->channel_count_max)) { + const auto old_state{params->state}; + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (!IsChannelCountValid(in_specific->channel_count)) { + params->channel_count = params->channel_count_max; + } + + if (!IsChannelCountValid(in_specific->channel_count) || + old_state != ParameterState::Updated) { + params->state = old_state; + } + + if (buffer_unmapped || in_params.is_new) { + usage_state = UsageState::New; + params->state = ParameterState::Initialized; + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); + return; + } + } + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void DelayInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } + + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + params->state = ParameterState::Updated; +} + +void DelayInfo::InitializeResultState(EffectResultState& result_state) {} + +void DelayInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} + +CpuAddr DelayInfo::GetWorkbuffer(s32 index) { + return GetSingleBuffer(index); +} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <vector> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" +#include "common/fixed_point.h" + +namespace AudioCore::AudioRenderer { + +class DelayInfo : public EffectInfoBase { +public: + struct ParameterVersion1 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ u16 channel_count_max; + /* 0x0E */ u16 channel_count; + /* 0x10 */ u32 delay_time_max; + /* 0x14 */ u32 delay_time; + /* 0x18 */ Common::FixedPoint<18, 14> sample_rate; + /* 0x1C */ Common::FixedPoint<18, 14> in_gain; + /* 0x20 */ Common::FixedPoint<18, 14> feedback_gain; + /* 0x24 */ Common::FixedPoint<18, 14> wet_gain; + /* 0x28 */ Common::FixedPoint<18, 14> dry_gain; + /* 0x2C */ Common::FixedPoint<18, 14> channel_spread; + /* 0x30 */ Common::FixedPoint<18, 14> lowpass_amount; + /* 0x34 */ ParameterState state; + }; + static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), + "DelayInfo::ParameterVersion1 has the wrong size!"); + + struct ParameterVersion2 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ s16 channel_count_max; + /* 0x0E */ s16 channel_count; + /* 0x10 */ s32 delay_time_max; + /* 0x14 */ s32 delay_time; + /* 0x18 */ s32 sample_rate; + /* 0x1C */ s32 in_gain; + /* 0x20 */ s32 feedback_gain; + /* 0x24 */ s32 wet_gain; + /* 0x28 */ s32 dry_gain; + /* 0x2C */ s32 channel_spread; + /* 0x30 */ s32 lowpass_amount; + /* 0x34 */ ParameterState state; + }; + static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), + "DelayInfo::ParameterVersion2 has the wrong size!"); + + struct DelayLine { + Common::FixedPoint<50, 14> Read() const { + return buffer[buffer_pos]; + } + + void Write(const Common::FixedPoint<50, 14> value) { + buffer[buffer_pos] = value; + buffer_pos = static_cast<u32>((buffer_pos + 1) % buffer.size()); + } + + s32 sample_count_max{}; + s32 sample_count{}; + std::vector<Common::FixedPoint<50, 14>> buffer{}; + u32 buffer_pos{}; + Common::FixedPoint<18, 14> decay_rate{}; + }; + + struct State { + /* 0x000 */ std::array<s32, 8> unk_000; + /* 0x020 */ std::array<DelayLine, MaxChannels> delay_lines; + /* 0x0B0 */ Common::FixedPoint<18, 14> feedback_gain; + /* 0x0B4 */ Common::FixedPoint<18, 14> delay_feedback_gain; + /* 0x0B8 */ Common::FixedPoint<18, 14> delay_feedback_cross_gain; + /* 0x0BC */ Common::FixedPoint<18, 14> lowpass_gain; + /* 0x0C0 */ Common::FixedPoint<18, 14> lowpass_feedback_gain; + /* 0x0C4 */ std::array<Common::FixedPoint<50, 14>, MaxChannels> lowpass_z; + }; + static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), + "DelayInfo::State has the wrong size!"); + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Initialize a new result state. Version 2 only, unused. + * + * @param result_state - Result state to initialize. + */ + void InitializeResultState(EffectResultState& result_state) override; + + /** + * Update the host-side state with the ADSP-side state. Version 2 only, unused. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; + + /** + * Get a workbuffer assigned to this effect with the given index. + * + * @param index - Workbuffer index. + * @return Address of the buffer. + */ + CpuAddr GetWorkbuffer(s32 index) override; +}; + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/effect_context.h" + +namespace AudioCore::AudioRenderer { + +void EffectContext::Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, + std::span<EffectResultState> result_states_cpu_, + std::span<EffectResultState> result_states_dsp_, + const size_t dsp_state_count_) { + effect_infos = effect_infos_; + effect_count = effect_count_; + result_states_cpu = result_states_cpu_; + result_states_dsp = result_states_dsp_; + dsp_state_count = dsp_state_count_; +} + +EffectInfoBase& EffectContext::GetInfo(const u32 index) { + return effect_infos[index]; +} + +EffectResultState& EffectContext::GetResultState(const u32 index) { + return result_states_cpu[index]; +} + +EffectResultState& EffectContext::GetDspSharedResultState(const u32 index) { + return result_states_dsp[index]; +} + +u32 EffectContext::GetCount() const { + return effect_count; +} + +void EffectContext::UpdateStateByDspShared() { + for (size_t i = 0; i < dsp_state_count; i++) { + effect_infos[i].UpdateResultState(result_states_cpu[i], result_states_dsp[i]); + } +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <span> + +#include "audio_core/renderer/effect/effect_info_base.h" +#include "audio_core/renderer/effect/effect_result_state.h" +#include "common/common_types.h" + +namespace AudioCore::AudioRenderer { + +class EffectContext { +public: + /** + * Initialize the effect context + * @param effect_infos List of effect infos for this context + * @param effect_count The number of effects in the list + * @param result_states_cpu The workbuffer of result states for the CPU for this context + * @param result_states_dsp The workbuffer of result states for the DSP for this context + * @param state_count The number of result states + */ + void Initialize(std::span<EffectInfoBase> effect_infos_, const u32 effect_count_, + std::span<EffectResultState> result_states_cpu_, + std::span<EffectResultState> result_states_dsp_, const size_t dsp_state_count); + + /** + * Get the EffectInfo for a given index + * @param index Which effect to return + * @return Pointer to the effect + */ + EffectInfoBase& GetInfo(const u32 index); + + /** + * Get the CPU result state for a given index + * @param index Which result to return + * @return Pointer to the effect result state + */ + EffectResultState& GetResultState(const u32 index); + + /** + * Get the DSP result state for a given index + * @param index Which result to return + * @return Pointer to the effect result state + */ + EffectResultState& GetDspSharedResultState(const u32 index); + + /** + * Get the number of effects in this context + * @return The number of effects + */ + u32 GetCount() const; + + /** + * Update the CPU and DSP result states for all effects + */ + void UpdateStateByDspShared(); + +private: + /// Workbuffer for all of the effects + std::span<EffectInfoBase> effect_infos{}; + /// Number of effects in the workbuffer + u32 effect_count{}; + /// Workbuffer of states for all effects, kept host-side and not directly modified, dsp states + /// are copied here on the next render frame + std::span<EffectResultState> result_states_cpu{}; + /// Workbuffer of states for all effects, used by the AudioRenderer to track effect state + /// between calls + std::span<EffectResultState> result_states_dsp{}; + /// Number of result states in the workbuffers + size_t dsp_state_count{}; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/behavior/behavior_info.h" +#include "audio_core/renderer/effect/effect_result_state.h" +#include "audio_core/renderer/memory/address_info.h" +#include "audio_core/renderer/memory/pool_mapper.h" +#include "common/common_types.h" + +namespace AudioCore::AudioRenderer { +/** + * Base of all effects. Holds various data and functions used for all derived effects. + * Should not be used directly. + */ +class EffectInfoBase { +public: + enum class Type : u8 { + Invalid, + Mix, + Aux, + Delay, + Reverb, + I3dl2Reverb, + BiquadFilter, + LightLimiter, + Capture, + Compressor, + }; + + enum class UsageState { + Invalid, + New, + Enabled, + Disabled, + }; + + enum class OutStatus : u8 { + Invalid, + New, + Initialized, + Used, + Removed, + }; + + enum class ParameterState : u8 { + Initialized, + Updating, + Updated, + }; + + struct InParameterVersion1 { + /* 0x00 */ Type type; + /* 0x01 */ bool is_new; + /* 0x02 */ bool enabled; + /* 0x04 */ u32 mix_id; + /* 0x08 */ CpuAddr workbuffer; + /* 0x10 */ CpuAddr workbuffer_size; + /* 0x18 */ u32 process_order; + /* 0x1C */ char unk1C[0x4]; + /* 0x20 */ std::array<u8, 0xA0> specific; + }; + static_assert(sizeof(InParameterVersion1) == 0xC0, + "EffectInfoBase::InParameterVersion1 has the wrong size!"); + + struct InParameterVersion2 { + /* 0x00 */ Type type; + /* 0x01 */ bool is_new; + /* 0x02 */ bool enabled; + /* 0x04 */ u32 mix_id; + /* 0x08 */ CpuAddr workbuffer; + /* 0x10 */ CpuAddr workbuffer_size; + /* 0x18 */ u32 process_order; + /* 0x1C */ char unk1C[0x4]; + /* 0x20 */ std::array<u8, 0xA0> specific; + }; + static_assert(sizeof(InParameterVersion2) == 0xC0, + "EffectInfoBase::InParameterVersion2 has the wrong size!"); + + struct OutStatusVersion1 { + /* 0x00 */ OutStatus state; + /* 0x01 */ char unk01[0xF]; + }; + static_assert(sizeof(OutStatusVersion1) == 0x10, + "EffectInfoBase::OutStatusVersion1 has the wrong size!"); + + struct OutStatusVersion2 { + /* 0x00 */ OutStatus state; + /* 0x01 */ char unk01[0xF]; + /* 0x10 */ EffectResultState result_state; + }; + static_assert(sizeof(OutStatusVersion2) == 0x90, + "EffectInfoBase::OutStatusVersion2 has the wrong size!"); + + struct State { + std::array<u8, 0x500> buffer; + }; + static_assert(sizeof(State) == 0x500, "EffectInfoBase::State has the wrong size!"); + + EffectInfoBase() { + Cleanup(); + } + + virtual ~EffectInfoBase() = default; + + /** + * Cleanup this effect, resetting it to a starting state. + */ + void Cleanup() { + type = Type::Invalid; + enabled = false; + mix_id = UnusedMixId; + process_order = InvalidProcessOrder; + buffer_unmapped = false; + parameter = {}; + for (auto& workbuffer : workbuffers) { + workbuffer.Setup(CpuAddr(0), 0); + } + } + + /** + * Forcibly unmap all assigned workbuffers from the AudioRenderer. + * + * @param pool_mapper - Mapper to unmap the buffers. + */ + void ForceUnmapBuffers(const PoolMapper& pool_mapper) { + for (auto& workbuffer : workbuffers) { + if (workbuffer.GetReference(false) != 0) { + pool_mapper.ForceUnmapPointer(workbuffer); + } + } + } + + /** + * Check if this effect is enabled. + * + * @return True if effect is enabled, otherwise false. + */ + bool IsEnabled() const { + return enabled; + } + + /** + * Check if this effect should not be generated. + * + * @return True if effect should be skipped, otherwise false. + */ + bool ShouldSkip() const { + return buffer_unmapped; + } + + /** + * Get the type of this effect. + * + * @return The type of this effect. See EffectInfoBase::Type + */ + Type GetType() const { + return type; + } + + /** + * Set the type of this effect. + * + * @param type_ - The new type of this effect. + */ + void SetType(const Type type_) { + type = type_; + } + + /** + * Get the mix id of this effect. + * + * @return Mix id of this effect. + */ + s32 GetMixId() const { + return mix_id; + } + + /** + * Get the processing order of this effect. + * + * @return Process order of this effect. + */ + s32 GetProcessingOrder() const { + return process_order; + } + + /** + * Get this effect's parameter data. + * + * @return Pointer to the parametter, must be cast to the correct type. + */ + u8* GetParameter() { + return parameter.data(); + } + + /** + * Get this effect's parameter data. + * + * @return Pointer to the parametter, must be cast to the correct type. + */ + u8* GetStateBuffer() { + return state.data(); + } + + /** + * Set this effect's usage state. + * + * @param usage - new usage state of this effect. + */ + void SetUsage(const UsageState usage) { + usage_state = usage; + } + + /** + * Check if this effects need to have its workbuffer information updated. + * Version 1. + * + * @param params - Input parameters. + * @return True if workbuffers need updating, otherwise false. + */ + bool ShouldUpdateWorkBufferInfo(const InParameterVersion1& params) const { + return buffer_unmapped || params.is_new; + } + + /** + * Check if this effects need to have its workbuffer information updated. + * Version 2. + * + * @param params - Input parameters. + * @return True if workbuffers need updating, otherwise false. + */ + bool ShouldUpdateWorkBufferInfo(const InParameterVersion2& params) const { + return buffer_unmapped || params.is_new; + } + + /** + * Get the current usage state of this effect. + * + * @return The current usage state. + */ + UsageState GetUsage() const { + return usage_state; + } + + /** + * Write the current state. Version 1. + * + * @param out_status - Status to write. + * @param renderer_active - Is the AudioRenderer active? + */ + void StoreStatus(OutStatusVersion1& out_status, const bool renderer_active) const { + if (renderer_active) { + if (usage_state != UsageState::Disabled) { + out_status.state = OutStatus::Used; + } else { + out_status.state = OutStatus::Removed; + } + } else if (usage_state == UsageState::New) { + out_status.state = OutStatus::Used; + } else { + out_status.state = OutStatus::Removed; + } + } + + /** + * Write the current state. Version 2. + * + * @param out_status - Status to write. + * @param renderer_active - Is the AudioRenderer active? + */ + void StoreStatus(OutStatusVersion2& out_status, const bool renderer_active) const { + if (renderer_active) { + if (usage_state != UsageState::Disabled) { + out_status.state = OutStatus::Used; + } else { + out_status.state = OutStatus::Removed; + } + } else if (usage_state == UsageState::New) { + out_status.state = OutStatus::Used; + } else { + out_status.state = OutStatus::Removed; + } + } + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + virtual void Update(BehaviorInfo::ErrorInfo& error_info, + [[maybe_unused]] const InParameterVersion1& params, + [[maybe_unused]] const PoolMapper& pool_mapper) { + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); + } + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + virtual void Update(BehaviorInfo::ErrorInfo& error_info, + [[maybe_unused]] const InParameterVersion2& params, + [[maybe_unused]] const PoolMapper& pool_mapper) { + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); + } + + /** + * Update the info after command generation. Usually only changes its state. + */ + virtual void UpdateForCommandGeneration() {} + + /** + * Initialize a new result state. Version 2 only, unused. + * + * @param result_state - Result state to initialize. + */ + virtual void InitializeResultState([[maybe_unused]] EffectResultState& result_state) {} + + /** + * Update the host-side state with the ADSP-side state. Version 2 only, unused. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + virtual void UpdateResultState([[maybe_unused]] EffectResultState& cpu_state, + [[maybe_unused]] EffectResultState& dsp_state) {} + + /** + * Get a workbuffer assigned to this effect with the given index. + * + * @param index - Workbuffer index. + * @return Address of the buffer. + */ + virtual CpuAddr GetWorkbuffer([[maybe_unused]] s32 index) { + return 0; + } + + /** + * Get the first workbuffer assigned to this effect. + * + * @param index - Workbuffer index. Unused. + * @return Address of the buffer. + */ + CpuAddr GetSingleBuffer([[maybe_unused]] const s32 index) { + if (enabled) { + return workbuffers[0].GetReference(true); + } + + if (usage_state != UsageState::Disabled) { + const auto ref{workbuffers[0].GetReference(false)}; + const auto size{workbuffers[0].GetSize()}; + if (ref != 0 && size > 0) { + // Invalidate DSP cache + } + } + return 0; + } + + /** + * Get the send buffer info, used by Aux and Capture. + * + * @return Address of the buffer info. + */ + CpuAddr GetSendBufferInfo() const { + return send_buffer_info; + } + + /** + * Get the send buffer, used by Aux and Capture. + * + * @return Address of the buffer. + */ + CpuAddr GetSendBuffer() const { + return send_buffer; + } + + /** + * Get the return buffer info, used by Aux and Capture. + * + * @return Address of the buffer info. + */ + CpuAddr GetReturnBufferInfo() const { + return return_buffer_info; + } + + /** + * Get the return buffer, used by Aux and Capture. + * + * @return Address of the buffer. + */ + CpuAddr GetReturnBuffer() const { + return return_buffer; + } + +protected: + /// Type of this effect. May be changed + Type type{Type::Invalid}; + /// Is this effect enabled? + bool enabled{}; + /// Are this effect's buffers unmapped? + bool buffer_unmapped{}; + /// Current usage state + UsageState usage_state{UsageState::Invalid}; + /// Mix id of this effect + s32 mix_id{UnusedMixId}; + /// Process order of this effect + s32 process_order{InvalidProcessOrder}; + /// Workbuffers assigned to this effect + std::array<AddressInfo, 2> workbuffers{AddressInfo(CpuAddr(0), 0), AddressInfo(CpuAddr(0), 0)}; + /// Aux/Capture buffer info for reading + CpuAddr send_buffer_info; + /// Aux/Capture buffer for reading + CpuAddr send_buffer; + /// Aux/Capture buffer info for writing + CpuAddr return_buffer_info; + /// Aux/Capture buffer for writing + CpuAddr return_buffer; + /// Parameters of this effect + std::array<u8, sizeof(InParameterVersion2)> parameter{}; + /// State of this effect used by the AudioRenderer across calls + std::array<u8, sizeof(State)> state{}; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "audio_core/renderer/effect/aux_.h" +#include "audio_core/renderer/effect/biquad_filter.h" +#include "audio_core/renderer/effect/buffer_mixer.h" +#include "audio_core/renderer/effect/capture.h" +#include "audio_core/renderer/effect/compressor.h" +#include "audio_core/renderer/effect/delay.h" +#include "audio_core/renderer/effect/i3dl2.h" +#include "audio_core/renderer/effect/light_limiter.h" +#include "audio_core/renderer/effect/reverb.h" +#include "common/common_types.h" + +namespace AudioCore::AudioRenderer { +/** + * Reset an effect, and create a new one of the given type. + * + * @param effect - Effect to reset and re-construct. + * @param type - Type of the new effect to create. + */ +static void ResetEffect(EffectInfoBase* effect, const EffectInfoBase::Type type) { + *effect = {}; + + switch (type) { + case EffectInfoBase::Type::Invalid: + std::construct_at<EffectInfoBase>(effect); + effect->SetType(EffectInfoBase::Type::Invalid); + break; + case EffectInfoBase::Type::Mix: + std::construct_at<BufferMixerInfo>(reinterpret_cast<BufferMixerInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::Mix); + break; + case EffectInfoBase::Type::Aux: + std::construct_at<AuxInfo>(reinterpret_cast<AuxInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::Aux); + break; + case EffectInfoBase::Type::Delay: + std::construct_at<DelayInfo>(reinterpret_cast<DelayInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::Delay); + break; + case EffectInfoBase::Type::Reverb: + std::construct_at<ReverbInfo>(reinterpret_cast<ReverbInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::Reverb); + break; + case EffectInfoBase::Type::I3dl2Reverb: + std::construct_at<I3dl2ReverbInfo>(reinterpret_cast<I3dl2ReverbInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::I3dl2Reverb); + break; + case EffectInfoBase::Type::BiquadFilter: + std::construct_at<BiquadFilterInfo>(reinterpret_cast<BiquadFilterInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::BiquadFilter); + break; + case EffectInfoBase::Type::LightLimiter: + std::construct_at<LightLimiterInfo>(reinterpret_cast<LightLimiterInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::LightLimiter); + break; + case EffectInfoBase::Type::Capture: + std::construct_at<CaptureInfo>(reinterpret_cast<CaptureInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::Capture); + break; + case EffectInfoBase::Type::Compressor: + std::construct_at<CompressorInfo>(reinterpret_cast<CompressorInfo*>(effect)); + effect->SetType(EffectInfoBase::Type::Compressor); + break; + } +} + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> + +#include "common/common_types.h" + +namespace AudioCore::AudioRenderer { + +struct EffectResultState { + std::array<u8, 0x80> state; +}; + +} // 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/i3dl2.h" + +namespace AudioCore::AudioRenderer { + +void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + if (IsChannelCountValid(in_specific->channel_count_max)) { + const auto old_state{params->state}; + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (!IsChannelCountValid(in_specific->channel_count)) { + params->channel_count = params->channel_count_max; + } + + if (!IsChannelCountValid(in_specific->channel_count) || + old_state != ParameterState::Updated) { + params->state = old_state; + } + + if (buffer_unmapped || in_params.is_new) { + usage_state = UsageState::New; + params->state = ParameterState::Initialized; + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); + return; + } + } + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + if (IsChannelCountValid(in_specific->channel_count_max)) { + const auto old_state{params->state}; + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (!IsChannelCountValid(in_specific->channel_count)) { + params->channel_count = params->channel_count_max; + } + + if (!IsChannelCountValid(in_specific->channel_count) || + old_state != ParameterState::Updated) { + params->state = old_state; + } + + if (buffer_unmapped || in_params.is_new) { + usage_state = UsageState::New; + params->state = ParameterState::Initialized; + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); + return; + } + } + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void I3dl2ReverbInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } + + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + params->state = ParameterState::Updated; +} + +void I3dl2ReverbInfo::InitializeResultState(EffectResultState& result_state) {} + +void I3dl2ReverbInfo::UpdateResultState(EffectResultState& cpu_state, + EffectResultState& dsp_state) {} + +CpuAddr I3dl2ReverbInfo::GetWorkbuffer(s32 index) { + return GetSingleBuffer(index); +} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <vector> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" +#include "common/fixed_point.h" + +namespace AudioCore::AudioRenderer { + +class I3dl2ReverbInfo : public EffectInfoBase { +public: + struct ParameterVersion1 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ u16 channel_count_max; + /* 0x0E */ u16 channel_count; + /* 0x10 */ char unk10[0x4]; + /* 0x14 */ u32 sample_rate; + /* 0x18 */ f32 room_HF_gain; + /* 0x1C */ f32 reference_HF; + /* 0x20 */ f32 late_reverb_decay_time; + /* 0x24 */ f32 late_reverb_HF_decay_ratio; + /* 0x28 */ f32 room_gain; + /* 0x2C */ f32 reflection_gain; + /* 0x30 */ f32 reverb_gain; + /* 0x34 */ f32 late_reverb_diffusion; + /* 0x38 */ f32 reflection_delay; + /* 0x3C */ f32 late_reverb_delay_time; + /* 0x40 */ f32 late_reverb_density; + /* 0x44 */ f32 dry_gain; + /* 0x48 */ ParameterState state; + /* 0x49 */ char unk49[0x3]; + }; + static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), + "I3dl2ReverbInfo::ParameterVersion1 has the wrong size!"); + + struct ParameterVersion2 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ u16 channel_count_max; + /* 0x0E */ u16 channel_count; + /* 0x10 */ char unk10[0x4]; + /* 0x14 */ u32 sample_rate; + /* 0x18 */ f32 room_HF_gain; + /* 0x1C */ f32 reference_HF; + /* 0x20 */ f32 late_reverb_decay_time; + /* 0x24 */ f32 late_reverb_HF_decay_ratio; + /* 0x28 */ f32 room_gain; + /* 0x2C */ f32 reflection_gain; + /* 0x30 */ f32 reverb_gain; + /* 0x34 */ f32 late_reverb_diffusion; + /* 0x38 */ f32 reflection_delay; + /* 0x3C */ f32 late_reverb_delay_time; + /* 0x40 */ f32 late_reverb_density; + /* 0x44 */ f32 dry_gain; + /* 0x48 */ ParameterState state; + /* 0x49 */ char unk49[0x3]; + }; + static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), + "I3dl2ReverbInfo::ParameterVersion2 has the wrong size!"); + + static constexpr u32 MaxDelayLines = 4; + static constexpr u32 MaxDelayTaps = 20; + + struct I3dl2DelayLine { + void Initialize(const s32 delay_time) { + max_delay = delay_time; + buffer.resize(delay_time + 1, 0); + buffer_end = &buffer[delay_time]; + output = &buffer[0]; + SetDelay(delay_time); + wet_gain = 0.0f; + } + + void SetDelay(const s32 delay_time) { + if (max_delay < delay_time) { + return; + } + delay = delay_time; + input = &buffer[(output - buffer.data() + delay) % (max_delay + 1)]; + } + + Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) { + Write(sample); + + auto out_sample{Read()}; + + output++; + if (output >= buffer_end) { + output = buffer.data(); + } + + return out_sample; + } + + Common::FixedPoint<50, 14> Read() { + return *output; + } + + void Write(const Common::FixedPoint<50, 14> sample) { + *(input++) = sample; + if (input >= buffer_end) { + input = buffer.data(); + } + } + + Common::FixedPoint<50, 14> TapOut(const s32 index) { + auto out{input - (index + 1)}; + if (out < buffer.data()) { + out += max_delay + 1; + } + return *out; + } + + std::vector<Common::FixedPoint<50, 14>> buffer{}; + Common::FixedPoint<50, 14>* buffer_end{}; + s32 max_delay{}; + Common::FixedPoint<50, 14>* input{}; + Common::FixedPoint<50, 14>* output{}; + s32 delay{}; + f32 wet_gain{}; + }; + + struct State { + f32 lowpass_0; + f32 lowpass_1; + f32 lowpass_2; + I3dl2DelayLine early_delay_line; + std::array<s32, MaxDelayTaps> early_tap_steps; + f32 early_gain; + f32 late_gain; + s32 early_to_late_taps; + std::array<I3dl2DelayLine, MaxDelayLines> fdn_delay_lines; + std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines0; + std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines1; + f32 last_reverb_echo; + I3dl2DelayLine center_delay_line; + std::array<std::array<f32, 3>, MaxDelayLines> lowpass_coeff; + std::array<f32, MaxDelayLines> shelf_filter; + f32 dry_gain; + }; + static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), + "I3dl2ReverbInfo::State is too large!"); + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Initialize a new result state. Version 2 only, unused. + * + * @param result_state - Result state to initialize. + */ + void InitializeResultState(EffectResultState& result_state) override; + + /** + * Update the host-side state with the ADSP-side state. Version 2 only, unused. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; + + /** + * Get a workbuffer assigned to this effect with the given index. + * + * @param index - Workbuffer index. + * @return Address of the buffer. + */ + CpuAddr GetWorkbuffer(s32 index) override; +}; + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/light_limiter.h" + +namespace AudioCore::AudioRenderer { + +void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion1& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (buffer_unmapped || in_params.is_new) { + usage_state = UsageState::New; + params->state = ParameterState::Initialized; + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); + } else { + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); + } +} + +void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info, + const InParameterVersion2& in_params, const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (buffer_unmapped || in_params.is_new) { + usage_state = UsageState::New; + params->state = ParameterState::Initialized; + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); + } else { + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); + } +} + +void LightLimiterInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } + + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + params->state = ParameterState::Updated; + params->statistics_reset_required = false; +} + +void LightLimiterInfo::InitializeResultState(EffectResultState& result_state) { + auto result_state_{reinterpret_cast<StatisticsInternal*>(result_state.state.data())}; + + result_state_->channel_max_sample.fill(0); + result_state_->channel_compression_gain_min.fill(1.0f); +} + +void LightLimiterInfo::UpdateResultState(EffectResultState& cpu_state, + EffectResultState& dsp_state) { + auto cpu_statistics{reinterpret_cast<StatisticsInternal*>(cpu_state.state.data())}; + auto dsp_statistics{reinterpret_cast<StatisticsInternal*>(dsp_state.state.data())}; + + *cpu_statistics = *dsp_statistics; +} + +CpuAddr LightLimiterInfo::GetWorkbuffer(s32 index) { + return GetSingleBuffer(index); +} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <vector> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" +#include "common/fixed_point.h" + +namespace AudioCore::AudioRenderer { + +class LightLimiterInfo : public EffectInfoBase { +public: + enum class ProcessingMode { + Mode0, + Mode1, + }; + + struct ParameterVersion1 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ u16 channel_count_max; + /* 0x0E */ u16 channel_count; + /* 0x0C */ u32 sample_rate; + /* 0x14 */ s32 look_ahead_time_max; + /* 0x18 */ s32 attack_time; + /* 0x1C */ s32 release_time; + /* 0x20 */ s32 look_ahead_time; + /* 0x24 */ f32 attack_coeff; + /* 0x28 */ f32 release_coeff; + /* 0x2C */ f32 threshold; + /* 0x30 */ f32 input_gain; + /* 0x34 */ f32 output_gain; + /* 0x38 */ s32 look_ahead_samples_min; + /* 0x3C */ s32 look_ahead_samples_max; + /* 0x40 */ ParameterState state; + /* 0x41 */ bool statistics_enabled; + /* 0x42 */ bool statistics_reset_required; + /* 0x43 */ ProcessingMode processing_mode; + }; + static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), + "LightLimiterInfo::ParameterVersion1 has the wrong size!"); + + struct ParameterVersion2 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ u16 channel_count_max; + /* 0x0E */ u16 channel_count; + /* 0x0C */ u32 sample_rate; + /* 0x14 */ s32 look_ahead_time_max; + /* 0x18 */ s32 attack_time; + /* 0x1C */ s32 release_time; + /* 0x20 */ s32 look_ahead_time; + /* 0x24 */ f32 attack_coeff; + /* 0x28 */ f32 release_coeff; + /* 0x2C */ f32 threshold; + /* 0x30 */ f32 input_gain; + /* 0x34 */ f32 output_gain; + /* 0x38 */ s32 look_ahead_samples_min; + /* 0x3C */ s32 look_ahead_samples_max; + /* 0x40 */ ParameterState state; + /* 0x41 */ bool statistics_enabled; + /* 0x42 */ bool statistics_reset_required; + /* 0x43 */ ProcessingMode processing_mode; + }; + static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), + "LightLimiterInfo::ParameterVersion2 has the wrong size!"); + + struct State { + std::array<Common::FixedPoint<49, 15>, MaxChannels> samples_average; + std::array<Common::FixedPoint<49, 15>, MaxChannels> compression_gain; + std::array<s32, MaxChannels> look_ahead_sample_offsets; + std::array<std::vector<Common::FixedPoint<49, 15>>, MaxChannels> look_ahead_sample_buffers; + }; + static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), + "LightLimiterInfo::State has the wrong size!"); + + struct StatisticsInternal { + /* 0x00 */ std::array<f32, MaxChannels> channel_max_sample; + /* 0x18 */ std::array<f32, MaxChannels> channel_compression_gain_min; + }; + static_assert(sizeof(StatisticsInternal) == 0x30, + "LightLimiterInfo::StatisticsInternal has the wrong size!"); + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Initialize a new limiter statistics result state. Version 2 only. + * + * @param result_state - Result state to initialize. + */ + void InitializeResultState(EffectResultState& result_state) override; + + /** + * Update the host-side limiter statistics with the ADSP-side one. Version 2 only. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; + + /** + * Get a workbuffer assigned to this effect with the given index. + * + * @param index - Workbuffer index. + * @return Address of the buffer. + */ + CpuAddr GetWorkbuffer(s32 index) override; +}; + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/renderer/effect/reverb.h" + +namespace AudioCore::AudioRenderer { + +void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + + if (IsChannelCountValid(in_specific->channel_count_max)) { + const auto old_state{params->state}; + std::memcpy(params, in_specific, sizeof(ParameterVersion1)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (!IsChannelCountValid(in_specific->channel_count)) { + params->channel_count = params->channel_count_max; + } + + if (!IsChannelCountValid(in_specific->channel_count) || + old_state != ParameterState::Updated) { + params->state = old_state; + } + + if (buffer_unmapped || in_params.is_new) { + usage_state = UsageState::New; + params->state = ParameterState::Initialized; + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); + return; + } + } + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) { + auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())}; + auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())}; + + if (IsChannelCountValid(in_specific->channel_count_max)) { + const auto old_state{params->state}; + std::memcpy(params, in_specific, sizeof(ParameterVersion2)); + mix_id = in_params.mix_id; + process_order = in_params.process_order; + enabled = in_params.enabled; + + if (!IsChannelCountValid(in_specific->channel_count)) { + params->channel_count = params->channel_count_max; + } + + if (!IsChannelCountValid(in_specific->channel_count) || + old_state != ParameterState::Updated) { + params->state = old_state; + } + + if (buffer_unmapped || in_params.is_new) { + usage_state = UsageState::New; + params->state = ParameterState::Initialized; + buffer_unmapped = !pool_mapper.TryAttachBuffer( + error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size); + return; + } + } + error_info.error_code = ResultSuccess; + error_info.address = CpuAddr(0); +} + +void ReverbInfo::UpdateForCommandGeneration() { + if (enabled) { + usage_state = UsageState::Enabled; + } else { + usage_state = UsageState::Disabled; + } + + auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())}; + params->state = ParameterState::Updated; +} + +void ReverbInfo::InitializeResultState(EffectResultState& result_state) {} + +void ReverbInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {} + +CpuAddr ReverbInfo::GetWorkbuffer(s32 index) { + return GetSingleBuffer(index); +} + +} // namespace AudioCore::AudioRenderer 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <vector> + +#include "audio_core/common/common.h" +#include "audio_core/renderer/effect/effect_info_base.h" +#include "common/common_types.h" +#include "common/fixed_point.h" + +namespace AudioCore::AudioRenderer { + +class ReverbInfo : public EffectInfoBase { +public: + struct ParameterVersion1 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ u16 channel_count_max; + /* 0x0E */ u16 channel_count; + /* 0x10 */ u32 sample_rate; + /* 0x14 */ u32 early_mode; + /* 0x18 */ s32 early_gain; + /* 0x1C */ s32 pre_delay; + /* 0x20 */ s32 late_mode; + /* 0x24 */ s32 late_gain; + /* 0x28 */ s32 decay_time; + /* 0x2C */ s32 high_freq_Decay_ratio; + /* 0x30 */ s32 colouration; + /* 0x34 */ s32 base_gain; + /* 0x38 */ s32 wet_gain; + /* 0x3C */ s32 dry_gain; + /* 0x40 */ ParameterState state; + }; + static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1), + "ReverbInfo::ParameterVersion1 has the wrong size!"); + + struct ParameterVersion2 { + /* 0x00 */ std::array<s8, MaxChannels> inputs; + /* 0x06 */ std::array<s8, MaxChannels> outputs; + /* 0x0C */ u16 channel_count_max; + /* 0x0E */ u16 channel_count; + /* 0x10 */ u32 sample_rate; + /* 0x14 */ u32 early_mode; + /* 0x18 */ s32 early_gain; + /* 0x1C */ s32 pre_delay; + /* 0x20 */ s32 late_mode; + /* 0x24 */ s32 late_gain; + /* 0x28 */ s32 decay_time; + /* 0x2C */ s32 high_freq_decay_ratio; + /* 0x30 */ s32 colouration; + /* 0x34 */ s32 base_gain; + /* 0x38 */ s32 wet_gain; + /* 0x3C */ s32 dry_gain; + /* 0x40 */ ParameterState state; + }; + static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2), + "ReverbInfo::ParameterVersion2 has the wrong size!"); + + static constexpr u32 MaxDelayLines = 4; + static constexpr u32 MaxDelayTaps = 10; + static constexpr u32 NumEarlyModes = 5; + static constexpr u32 NumLateModes = 5; + + struct ReverbDelayLine { + void Initialize(const s32 delay_time, const f32 decay_rate) { + buffer.resize(delay_time + 1, 0); + buffer_end = &buffer[delay_time]; + output = &buffer[0]; + decay = decay_rate; + sample_count_max = delay_time; + SetDelay(delay_time); + } + + void SetDelay(const s32 delay_time) { + if (sample_count_max < delay_time) { + return; + } + sample_count = delay_time; + input = &buffer[(output - buffer.data() + sample_count) % (sample_count_max + 1)]; + } + + Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) { + Write(sample); + + auto out_sample{Read()}; + + output++; + if (output >= buffer_end) { + output = buffer.data(); + } + + return out_sample; + } + + Common::FixedPoint<50, 14> Read() { + return *output; + } + + void Write(const Common::FixedPoint<50, 14> sample) { + *(input++) = sample; + if (input >= buffer_end) { + input = buffer.data(); + } + } + + Common::FixedPoint<50, 14> TapOut(const s32 index) { + auto out{input - (index + 1)}; + if (out < buffer.data()) { + out += sample_count; + } + return *out; + } + + s32 sample_count{}; + s32 sample_count_max{}; + std::vector<Common::FixedPoint<50, 14>> buffer{}; + Common::FixedPoint<50, 14>* buffer_end; + Common::FixedPoint<50, 14>* input{}; + Common::FixedPoint<50, 14>* output{}; + Common::FixedPoint<50, 14> decay{}; + }; + + struct State { + ReverbDelayLine pre_delay_line; + ReverbDelayLine center_delay_line; + std::array<s32, MaxDelayTaps> early_delay_times; + std::array<Common::FixedPoint<50, 14>, MaxDelayTaps> early_gains; + s32 pre_delay_time; + std::array<ReverbDelayLine, MaxDelayLines> decay_delay_lines; + std::array<ReverbDelayLine, MaxDelayLines> fdn_delay_lines; + std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_gain; + std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_prev_gain; + std::array<Common::FixedPoint<50, 14>, MaxDelayLines> prev_feedback_output; + }; + static_assert(sizeof(State) <= sizeof(EffectInfoBase::State), + "ReverbInfo::State is too large!"); + + /** + * Update the info with new parameters, version 1. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info with new parameters, version 2. + * + * @param error_info - Used to write call result code. + * @param in_params - New parameters to update the info with. + * @param pool_mapper - Pool for mapping buffers. + */ + void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params, + const PoolMapper& pool_mapper) override; + + /** + * Update the info after command generation. Usually only changes its state. + */ + void UpdateForCommandGeneration() override; + + /** + * Initialize a new result state. Version 2 only, unused. + * + * @param result_state - Result state to initialize. + */ + void InitializeResultState(EffectResultState& result_state) override; + + /** + * Update the host-side state with the ADSP-side state. Version 2 only, unused. + * + * @param cpu_state - Host-side result state to update. + * @param dsp_state - AudioRenderer-side result state to update from. + */ + void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override; + + /** + * Get a workbuffer assigned to this effect with the given index. + * + * @param index - Workbuffer index. + * @return Address of the buffer. + */ + CpuAddr GetWorkbuffer(s32 index) override; +}; + +} // namespace AudioCore::AudioRenderer |