// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #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 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 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 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 parameter, must be cast to the correct type. */ u8* GetParameter() { return parameter.data(); } /** * Get this effect's parameter data. * * @return Pointer to the parameter, 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 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 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 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 parameter{}; /// State of this effect used by the AudioRenderer across calls std::array state{}; }; } // namespace AudioCore::AudioRenderer