Project Andio

This commit is contained in:
Kelebek1
2022-07-16 23:48:45 +01:00
parent 6e36f4d230
commit 458da8a948
270 changed files with 33712 additions and 8445 deletions

View File

@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/aux_.h"
namespace AudioCore::AudioRenderer {
void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
const bool send_unmapped{!pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_specific->send_buffer_info_address,
sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))};
const bool return_unmapped{!pool_mapper.TryAttachBuffer(
error_info, workbuffers[1], in_specific->return_buffer_info_address,
sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))};
buffer_unmapped = send_unmapped || return_unmapped;
if (!buffer_unmapped) {
auto send{workbuffers[0].GetReference(false)};
send_buffer_info = send + sizeof(AuxInfoDsp);
send_buffer = send + sizeof(AuxBufferInfo);
auto ret{workbuffers[1].GetReference(false)};
return_buffer_info = ret + sizeof(AuxInfoDsp);
return_buffer = ret + sizeof(AuxBufferInfo);
}
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
const bool send_unmapped{!pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], params->send_buffer_info_address,
sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))};
const bool return_unmapped{!pool_mapper.TryAttachBuffer(
error_info, workbuffers[1], params->return_buffer_info_address,
sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))};
buffer_unmapped = send_unmapped || return_unmapped;
if (!buffer_unmapped) {
auto send{workbuffers[0].GetReference(false)};
send_buffer_info = send + sizeof(AuxInfoDsp);
send_buffer = send + sizeof(AuxBufferInfo);
auto ret{workbuffers[1].GetReference(false)};
return_buffer_info = ret + sizeof(AuxInfoDsp);
return_buffer = ret + sizeof(AuxBufferInfo);
}
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void AuxInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
}
void AuxInfo::InitializeResultState(EffectResultState& result_state) {}
void AuxInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {}
CpuAddr AuxInfo::GetWorkbuffer(s32 index) {
return workbuffers[index].GetReference(true);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
/**
* Auxiliary Buffer used for Aux commands.
* Send and return buffers are available (names from the game's perspective).
* Send is read by the host, containing a buffer of samples to be used for whatever purpose.
* Return is written by the host, writing a mix buffer back to the game.
* This allows the game to use pre-processed samples skipping the other render processing,
* and to examine or modify what the audio renderer has generated.
*/
class AuxInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxMixBuffers> inputs;
/* 0x18 */ std::array<s8, MaxMixBuffers> outputs;
/* 0x30 */ u32 mix_buffer_count;
/* 0x34 */ u32 sample_rate;
/* 0x38 */ u32 count_max;
/* 0x3C */ u32 mix_buffer_count_max;
/* 0x40 */ CpuAddr send_buffer_info_address;
/* 0x48 */ CpuAddr send_buffer_address;
/* 0x50 */ CpuAddr return_buffer_info_address;
/* 0x58 */ CpuAddr return_buffer_address;
/* 0x60 */ u32 mix_buffer_sample_size;
/* 0x64 */ u32 sample_count;
/* 0x68 */ u32 mix_buffer_sample_count;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"AuxInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxMixBuffers> inputs;
/* 0x18 */ std::array<s8, MaxMixBuffers> outputs;
/* 0x30 */ u32 mix_buffer_count;
/* 0x34 */ u32 sample_rate;
/* 0x38 */ u32 count_max;
/* 0x3C */ u32 mix_buffer_count_max;
/* 0x40 */ CpuAddr send_buffer_info_address;
/* 0x48 */ CpuAddr send_buffer_address;
/* 0x50 */ CpuAddr return_buffer_info_address;
/* 0x58 */ CpuAddr return_buffer_address;
/* 0x60 */ u32 mix_buffer_sample_size;
/* 0x64 */ u32 sample_count;
/* 0x68 */ u32 mix_buffer_sample_count;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"AuxInfo::ParameterVersion2 has the wrong size!");
struct AuxInfoDsp {
/* 0x00 */ u32 read_offset;
/* 0x04 */ u32 write_offset;
/* 0x08 */ u32 lost_sample_count;
/* 0x0C */ u32 total_sample_count;
/* 0x10 */ char unk10[0x30];
};
static_assert(sizeof(AuxInfoDsp) == 0x40, "AuxInfo::AuxInfoDsp has the wrong size!");
struct AuxBufferInfo {
/* 0x00 */ AuxInfoDsp cpu_info;
/* 0x40 */ AuxInfoDsp dsp_info;
};
static_assert(sizeof(AuxBufferInfo) == 0x80, "AuxInfo::AuxBufferInfo has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/biquad_filter.h"
namespace AudioCore::AudioRenderer {
void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void BiquadFilterInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {}
void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state,
EffectResultState& dsp_state) {}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
class BiquadFilterInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ std::array<s16, 3> b;
/* 0x12 */ std::array<s16, 2> a;
/* 0x16 */ s8 channel_count;
/* 0x17 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"BiquadFilterInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ std::array<s16, 3> b;
/* 0x12 */ std::array<s16, 2> a;
/* 0x16 */ s8 channel_count;
/* 0x17 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"BiquadFilterInfo::ParameterVersion2 has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/buffer_mixer.h"
namespace AudioCore::AudioRenderer {
void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void BufferMixerInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
}
void BufferMixerInfo::InitializeResultState(EffectResultState& result_state) {}
void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state,
EffectResultState& dsp_state) {}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
class BufferMixerInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxMixBuffers> inputs;
/* 0x18 */ std::array<s8, MaxMixBuffers> outputs;
/* 0x30 */ std::array<f32, MaxMixBuffers> volumes;
/* 0x90 */ u32 mix_count;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"BufferMixerInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxMixBuffers> inputs;
/* 0x18 */ std::array<s8, MaxMixBuffers> outputs;
/* 0x30 */ std::array<f32, MaxMixBuffers> volumes;
/* 0x90 */ u32 mix_count;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"BufferMixerInfo::ParameterVersion2 has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/aux_.h"
#include "audio_core/renderer/effect/capture.h"
namespace AudioCore::AudioRenderer {
void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{
reinterpret_cast<const AuxInfo::ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<AuxInfo::ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_specific->send_buffer_info_address,
in_specific->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo));
if (!buffer_unmapped) {
const auto send_address{workbuffers[0].GetReference(false)};
send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp);
send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo);
return_buffer_info = 0;
return_buffer = 0;
}
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{
reinterpret_cast<const AuxInfo::ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<AuxInfo::ParameterVersion2*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], params->send_buffer_info_address,
params->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo));
if (!buffer_unmapped) {
const auto send_address{workbuffers[0].GetReference(false)};
send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp);
send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo);
return_buffer_info = 0;
return_buffer = 0;
}
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void CaptureInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
}
void CaptureInfo::InitializeResultState(EffectResultState& result_state) {}
void CaptureInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {}
CpuAddr CaptureInfo::GetWorkbuffer(s32 index) {
return workbuffers[index].GetReference(true);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
class CaptureInfo : public EffectInfoBase {
public:
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/compressor.h"
namespace AudioCore::AudioRenderer {
void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {}
void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void CompressorInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
CpuAddr CompressorInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,106 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class CompressorInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ s16 channel_count_max;
/* 0x0E */ s16 channel_count;
/* 0x10 */ s32 sample_rate;
/* 0x14 */ f32 threshold;
/* 0x18 */ f32 compressor_ratio;
/* 0x1C */ s32 attack_time;
/* 0x20 */ s32 release_time;
/* 0x24 */ f32 unk_24;
/* 0x28 */ f32 unk_28;
/* 0x2C */ f32 unk_2C;
/* 0x30 */ f32 out_gain;
/* 0x34 */ ParameterState state;
/* 0x35 */ bool makeup_gain_enabled;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"CompressorInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ s16 channel_count_max;
/* 0x0E */ s16 channel_count;
/* 0x10 */ s32 sample_rate;
/* 0x14 */ f32 threshold;
/* 0x18 */ f32 compressor_ratio;
/* 0x1C */ s32 attack_time;
/* 0x20 */ s32 release_time;
/* 0x24 */ f32 unk_24;
/* 0x28 */ f32 unk_28;
/* 0x2C */ f32 unk_2C;
/* 0x30 */ f32 out_gain;
/* 0x34 */ ParameterState state;
/* 0x35 */ bool makeup_gain_enabled;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"CompressorInfo::ParameterVersion2 has the wrong size!");
struct State {
f32 unk_00;
f32 unk_04;
f32 unk_08;
f32 unk_0C;
f32 unk_10;
f32 unk_14;
f32 unk_18;
f32 makeup_gain;
f32 unk_20;
char unk_24[0x1C];
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"CompressorInfo::State has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/delay.h"
namespace AudioCore::AudioRenderer {
void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void DelayInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void DelayInfo::InitializeResultState(EffectResultState& result_state) {}
void DelayInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {}
CpuAddr DelayInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class DelayInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ u32 delay_time_max;
/* 0x14 */ u32 delay_time;
/* 0x18 */ Common::FixedPoint<18, 14> sample_rate;
/* 0x1C */ Common::FixedPoint<18, 14> in_gain;
/* 0x20 */ Common::FixedPoint<18, 14> feedback_gain;
/* 0x24 */ Common::FixedPoint<18, 14> wet_gain;
/* 0x28 */ Common::FixedPoint<18, 14> dry_gain;
/* 0x2C */ Common::FixedPoint<18, 14> channel_spread;
/* 0x30 */ Common::FixedPoint<18, 14> lowpass_amount;
/* 0x34 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"DelayInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ s16 channel_count_max;
/* 0x0E */ s16 channel_count;
/* 0x10 */ s32 delay_time_max;
/* 0x14 */ s32 delay_time;
/* 0x18 */ s32 sample_rate;
/* 0x1C */ s32 in_gain;
/* 0x20 */ s32 feedback_gain;
/* 0x24 */ s32 wet_gain;
/* 0x28 */ s32 dry_gain;
/* 0x2C */ s32 channel_spread;
/* 0x30 */ s32 lowpass_amount;
/* 0x34 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"DelayInfo::ParameterVersion2 has the wrong size!");
struct DelayLine {
Common::FixedPoint<50, 14> Read() const {
return buffer[buffer_pos];
}
void Write(const Common::FixedPoint<50, 14> value) {
buffer[buffer_pos] = value;
buffer_pos = static_cast<u32>((buffer_pos + 1) % buffer.size());
}
s32 sample_count_max{};
s32 sample_count{};
std::vector<Common::FixedPoint<50, 14>> buffer{};
u32 buffer_pos{};
Common::FixedPoint<18, 14> decay_rate{};
};
struct State {
/* 0x000 */ std::array<s32, 8> unk_000;
/* 0x020 */ std::array<DelayLine, MaxChannels> delay_lines;
/* 0x0B0 */ Common::FixedPoint<18, 14> feedback_gain;
/* 0x0B4 */ Common::FixedPoint<18, 14> delay_feedback_gain;
/* 0x0B8 */ Common::FixedPoint<18, 14> delay_feedback_cross_gain;
/* 0x0BC */ Common::FixedPoint<18, 14> lowpass_gain;
/* 0x0C0 */ Common::FixedPoint<18, 14> lowpass_feedback_gain;
/* 0x0C4 */ std::array<Common::FixedPoint<50, 14>, MaxChannels> lowpass_z;
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"DelayInfo::State has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/i3dl2.h"
namespace AudioCore::AudioRenderer {
void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void I3dl2ReverbInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void I3dl2ReverbInfo::InitializeResultState(EffectResultState& result_state) {}
void I3dl2ReverbInfo::UpdateResultState(EffectResultState& cpu_state,
EffectResultState& dsp_state) {}
CpuAddr I3dl2ReverbInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,200 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class I3dl2ReverbInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ char unk10[0x4];
/* 0x14 */ u32 sample_rate;
/* 0x18 */ f32 room_HF_gain;
/* 0x1C */ f32 reference_HF;
/* 0x20 */ f32 late_reverb_decay_time;
/* 0x24 */ f32 late_reverb_HF_decay_ratio;
/* 0x28 */ f32 room_gain;
/* 0x2C */ f32 reflection_gain;
/* 0x30 */ f32 reverb_gain;
/* 0x34 */ f32 late_reverb_diffusion;
/* 0x38 */ f32 reflection_delay;
/* 0x3C */ f32 late_reverb_delay_time;
/* 0x40 */ f32 late_reverb_density;
/* 0x44 */ f32 dry_gain;
/* 0x48 */ ParameterState state;
/* 0x49 */ char unk49[0x3];
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"I3dl2ReverbInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ char unk10[0x4];
/* 0x14 */ u32 sample_rate;
/* 0x18 */ f32 room_HF_gain;
/* 0x1C */ f32 reference_HF;
/* 0x20 */ f32 late_reverb_decay_time;
/* 0x24 */ f32 late_reverb_HF_decay_ratio;
/* 0x28 */ f32 room_gain;
/* 0x2C */ f32 reflection_gain;
/* 0x30 */ f32 reverb_gain;
/* 0x34 */ f32 late_reverb_diffusion;
/* 0x38 */ f32 reflection_delay;
/* 0x3C */ f32 late_reverb_delay_time;
/* 0x40 */ f32 late_reverb_density;
/* 0x44 */ f32 dry_gain;
/* 0x48 */ ParameterState state;
/* 0x49 */ char unk49[0x3];
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"I3dl2ReverbInfo::ParameterVersion2 has the wrong size!");
static constexpr u32 MaxDelayLines = 4;
static constexpr u32 MaxDelayTaps = 20;
struct I3dl2DelayLine {
void Initialize(const s32 delay_time) {
max_delay = delay_time;
buffer.resize(delay_time + 1, 0);
buffer_end = &buffer[delay_time];
output = &buffer[0];
SetDelay(delay_time);
wet_gain = 0.0f;
}
void SetDelay(const s32 delay_time) {
if (max_delay < delay_time) {
return;
}
delay = delay_time;
input = &buffer[(output - buffer.data() + delay) % (max_delay + 1)];
}
Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) {
Write(sample);
auto out_sample{Read()};
output++;
if (output >= buffer_end) {
output = buffer.data();
}
return out_sample;
}
Common::FixedPoint<50, 14> Read() {
return *output;
}
void Write(const Common::FixedPoint<50, 14> sample) {
*(input++) = sample;
if (input >= buffer_end) {
input = buffer.data();
}
}
Common::FixedPoint<50, 14> TapOut(const s32 index) {
auto out{input - (index + 1)};
if (out < buffer.data()) {
out += max_delay + 1;
}
return *out;
}
std::vector<Common::FixedPoint<50, 14>> buffer{};
Common::FixedPoint<50, 14>* buffer_end{};
s32 max_delay{};
Common::FixedPoint<50, 14>* input{};
Common::FixedPoint<50, 14>* output{};
s32 delay{};
f32 wet_gain{};
};
struct State {
f32 lowpass_0;
f32 lowpass_1;
f32 lowpass_2;
I3dl2DelayLine early_delay_line;
std::array<s32, MaxDelayTaps> early_tap_steps;
f32 early_gain;
f32 late_gain;
s32 early_to_late_taps;
std::array<I3dl2DelayLine, MaxDelayLines> fdn_delay_lines;
std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines0;
std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines1;
f32 last_reverb_echo;
I3dl2DelayLine center_delay_line;
std::array<std::array<f32, 3>, MaxDelayLines> lowpass_coeff;
std::array<f32, MaxDelayLines> shelf_filter;
f32 dry_gain;
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"I3dl2ReverbInfo::State is too large!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/light_limiter.h"
namespace AudioCore::AudioRenderer {
void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void LightLimiterInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
params->statistics_reset_required = false;
}
void LightLimiterInfo::InitializeResultState(EffectResultState& result_state) {
auto result_state_{reinterpret_cast<StatisticsInternal*>(result_state.state.data())};
result_state_->channel_max_sample.fill(0);
result_state_->channel_compression_gain_min.fill(1.0f);
}
void LightLimiterInfo::UpdateResultState(EffectResultState& cpu_state,
EffectResultState& dsp_state) {
auto cpu_statistics{reinterpret_cast<StatisticsInternal*>(cpu_state.state.data())};
auto dsp_statistics{reinterpret_cast<StatisticsInternal*>(dsp_state.state.data())};
*cpu_statistics = *dsp_statistics;
}
CpuAddr LightLimiterInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,138 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class LightLimiterInfo : public EffectInfoBase {
public:
enum class ProcessingMode {
Mode0,
Mode1,
};
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x0C */ u32 sample_rate;
/* 0x14 */ s32 look_ahead_time_max;
/* 0x18 */ s32 attack_time;
/* 0x1C */ s32 release_time;
/* 0x20 */ s32 look_ahead_time;
/* 0x24 */ f32 attack_coeff;
/* 0x28 */ f32 release_coeff;
/* 0x2C */ f32 threshold;
/* 0x30 */ f32 input_gain;
/* 0x34 */ f32 output_gain;
/* 0x38 */ s32 look_ahead_samples_min;
/* 0x3C */ s32 look_ahead_samples_max;
/* 0x40 */ ParameterState state;
/* 0x41 */ bool statistics_enabled;
/* 0x42 */ bool statistics_reset_required;
/* 0x43 */ ProcessingMode processing_mode;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"LightLimiterInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x0C */ u32 sample_rate;
/* 0x14 */ s32 look_ahead_time_max;
/* 0x18 */ s32 attack_time;
/* 0x1C */ s32 release_time;
/* 0x20 */ s32 look_ahead_time;
/* 0x24 */ f32 attack_coeff;
/* 0x28 */ f32 release_coeff;
/* 0x2C */ f32 threshold;
/* 0x30 */ f32 input_gain;
/* 0x34 */ f32 output_gain;
/* 0x38 */ s32 look_ahead_samples_min;
/* 0x3C */ s32 look_ahead_samples_max;
/* 0x40 */ ParameterState state;
/* 0x41 */ bool statistics_enabled;
/* 0x42 */ bool statistics_reset_required;
/* 0x43 */ ProcessingMode processing_mode;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"LightLimiterInfo::ParameterVersion2 has the wrong size!");
struct State {
std::array<Common::FixedPoint<49, 15>, MaxChannels> samples_average;
std::array<Common::FixedPoint<49, 15>, MaxChannels> compression_gain;
std::array<s32, MaxChannels> look_ahead_sample_offsets;
std::array<std::vector<Common::FixedPoint<49, 15>>, MaxChannels> look_ahead_sample_buffers;
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"LightLimiterInfo::State has the wrong size!");
struct StatisticsInternal {
/* 0x00 */ std::array<f32, MaxChannels> channel_max_sample;
/* 0x18 */ std::array<f32, MaxChannels> channel_compression_gain_min;
};
static_assert(sizeof(StatisticsInternal) == 0x30,
"LightLimiterInfo::StatisticsInternal has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new limiter statistics result state. Version 2 only.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side limiter statistics with the ADSP-side one. Version 2 only.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/reverb.h"
namespace AudioCore::AudioRenderer {
void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void ReverbInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void ReverbInfo::InitializeResultState(EffectResultState& result_state) {}
void ReverbInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {}
CpuAddr ReverbInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,190 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class ReverbInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ u32 sample_rate;
/* 0x14 */ u32 early_mode;
/* 0x18 */ s32 early_gain;
/* 0x1C */ s32 pre_delay;
/* 0x20 */ s32 late_mode;
/* 0x24 */ s32 late_gain;
/* 0x28 */ s32 decay_time;
/* 0x2C */ s32 high_freq_Decay_ratio;
/* 0x30 */ s32 colouration;
/* 0x34 */ s32 base_gain;
/* 0x38 */ s32 wet_gain;
/* 0x3C */ s32 dry_gain;
/* 0x40 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"ReverbInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ u32 sample_rate;
/* 0x14 */ u32 early_mode;
/* 0x18 */ s32 early_gain;
/* 0x1C */ s32 pre_delay;
/* 0x20 */ s32 late_mode;
/* 0x24 */ s32 late_gain;
/* 0x28 */ s32 decay_time;
/* 0x2C */ s32 high_freq_decay_ratio;
/* 0x30 */ s32 colouration;
/* 0x34 */ s32 base_gain;
/* 0x38 */ s32 wet_gain;
/* 0x3C */ s32 dry_gain;
/* 0x40 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"ReverbInfo::ParameterVersion2 has the wrong size!");
static constexpr u32 MaxDelayLines = 4;
static constexpr u32 MaxDelayTaps = 10;
static constexpr u32 NumEarlyModes = 5;
static constexpr u32 NumLateModes = 5;
struct ReverbDelayLine {
void Initialize(const s32 delay_time, const f32 decay_rate) {
buffer.resize(delay_time + 1, 0);
buffer_end = &buffer[delay_time];
output = &buffer[0];
decay = decay_rate;
sample_count_max = delay_time;
SetDelay(delay_time);
}
void SetDelay(const s32 delay_time) {
if (sample_count_max < delay_time) {
return;
}
sample_count = delay_time;
input = &buffer[(output - buffer.data() + sample_count) % (sample_count_max + 1)];
}
Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) {
Write(sample);
auto out_sample{Read()};
output++;
if (output >= buffer_end) {
output = buffer.data();
}
return out_sample;
}
Common::FixedPoint<50, 14> Read() {
return *output;
}
void Write(const Common::FixedPoint<50, 14> sample) {
*(input++) = sample;
if (input >= buffer_end) {
input = buffer.data();
}
}
Common::FixedPoint<50, 14> TapOut(const s32 index) {
auto out{input - (index + 1)};
if (out < buffer.data()) {
out += sample_count;
}
return *out;
}
s32 sample_count{};
s32 sample_count_max{};
std::vector<Common::FixedPoint<50, 14>> buffer{};
Common::FixedPoint<50, 14>* buffer_end;
Common::FixedPoint<50, 14>* input{};
Common::FixedPoint<50, 14>* output{};
Common::FixedPoint<50, 14> decay{};
};
struct State {
ReverbDelayLine pre_delay_line;
ReverbDelayLine center_delay_line;
std::array<s32, MaxDelayTaps> early_delay_times;
std::array<Common::FixedPoint<50, 14>, MaxDelayTaps> early_gains;
s32 pre_delay_time;
std::array<ReverbDelayLine, MaxDelayLines> decay_delay_lines;
std::array<ReverbDelayLine, MaxDelayLines> fdn_delay_lines;
std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_gain;
std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_prev_gain;
std::array<Common::FixedPoint<50, 14>, MaxDelayLines> prev_feedback_output;
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"ReverbInfo::State is too large!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer