4a7fd91857
Most notable fix is the voices in Fire Emblem Three Houses
322 lines
9.6 KiB
C++
322 lines
9.6 KiB
C++
// Copyright 2020 yuzu Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <algorithm>
|
|
#include "audio_core/effect_context.h"
|
|
|
|
namespace AudioCore {
|
|
namespace {
|
|
bool ValidChannelCountForEffect(s32 channel_count) {
|
|
return channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6;
|
|
}
|
|
} // namespace
|
|
|
|
EffectContext::EffectContext(std::size_t effect_count_) : effect_count(effect_count_) {
|
|
effects.reserve(effect_count);
|
|
std::generate_n(std::back_inserter(effects), effect_count,
|
|
[] { return std::make_unique<EffectStubbed>(); });
|
|
}
|
|
EffectContext::~EffectContext() = default;
|
|
|
|
std::size_t EffectContext::GetCount() const {
|
|
return effect_count;
|
|
}
|
|
|
|
EffectBase* EffectContext::GetInfo(std::size_t i) {
|
|
return effects.at(i).get();
|
|
}
|
|
|
|
EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
|
|
switch (effect) {
|
|
case EffectType::Invalid:
|
|
effects[i] = std::make_unique<EffectStubbed>();
|
|
break;
|
|
case EffectType::BufferMixer:
|
|
effects[i] = std::make_unique<EffectBufferMixer>();
|
|
break;
|
|
case EffectType::Aux:
|
|
effects[i] = std::make_unique<EffectAuxInfo>();
|
|
break;
|
|
case EffectType::Delay:
|
|
effects[i] = std::make_unique<EffectDelay>();
|
|
break;
|
|
case EffectType::Reverb:
|
|
effects[i] = std::make_unique<EffectReverb>();
|
|
break;
|
|
case EffectType::I3dl2Reverb:
|
|
effects[i] = std::make_unique<EffectI3dl2Reverb>();
|
|
break;
|
|
case EffectType::BiquadFilter:
|
|
effects[i] = std::make_unique<EffectBiquadFilter>();
|
|
break;
|
|
default:
|
|
UNREACHABLE_MSG("Unimplemented effect {}", effect);
|
|
effects[i] = std::make_unique<EffectStubbed>();
|
|
}
|
|
return GetInfo(i);
|
|
}
|
|
|
|
const EffectBase* EffectContext::GetInfo(std::size_t i) const {
|
|
return effects.at(i).get();
|
|
}
|
|
|
|
EffectStubbed::EffectStubbed() : EffectBase(EffectType::Invalid) {}
|
|
EffectStubbed::~EffectStubbed() = default;
|
|
|
|
void EffectStubbed::Update([[maybe_unused]] EffectInfo::InParams& in_params) {}
|
|
void EffectStubbed::UpdateForCommandGeneration() {}
|
|
|
|
EffectBase::EffectBase(EffectType effect_type_) : effect_type(effect_type_) {}
|
|
EffectBase::~EffectBase() = default;
|
|
|
|
UsageState EffectBase::GetUsage() const {
|
|
return usage;
|
|
}
|
|
|
|
EffectType EffectBase::GetType() const {
|
|
return effect_type;
|
|
}
|
|
|
|
bool EffectBase::IsEnabled() const {
|
|
return enabled;
|
|
}
|
|
|
|
s32 EffectBase::GetMixID() const {
|
|
return mix_id;
|
|
}
|
|
|
|
s32 EffectBase::GetProcessingOrder() const {
|
|
return processing_order;
|
|
}
|
|
|
|
std::vector<u8>& EffectBase::GetWorkBuffer() {
|
|
return work_buffer;
|
|
}
|
|
|
|
const std::vector<u8>& EffectBase::GetWorkBuffer() const {
|
|
return work_buffer;
|
|
}
|
|
|
|
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
|
|
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
|
|
|
|
void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
|
auto& params = GetParams();
|
|
const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
|
|
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
|
UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
|
|
return;
|
|
}
|
|
|
|
const auto last_status = params.status;
|
|
mix_id = in_params.mix_id;
|
|
processing_order = in_params.processing_order;
|
|
params = *reverb_params;
|
|
if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
|
|
params.channel_count = params.max_channels;
|
|
}
|
|
enabled = in_params.is_enabled;
|
|
if (last_status != ParameterStatus::Updated) {
|
|
params.status = last_status;
|
|
}
|
|
|
|
if (in_params.is_new || skipped) {
|
|
usage = UsageState::Initialized;
|
|
params.status = ParameterStatus::Initialized;
|
|
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
|
if (!skipped) {
|
|
auto& work_buffer = GetWorkBuffer();
|
|
// Has two buffers internally
|
|
work_buffer.resize(in_params.buffer_size * 2);
|
|
std::fill(work_buffer.begin(), work_buffer.end(), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EffectI3dl2Reverb::UpdateForCommandGeneration() {
|
|
if (enabled) {
|
|
usage = UsageState::Running;
|
|
} else {
|
|
usage = UsageState::Stopped;
|
|
}
|
|
GetParams().status = ParameterStatus::Updated;
|
|
}
|
|
|
|
I3dl2ReverbState& EffectI3dl2Reverb::GetState() {
|
|
return state;
|
|
}
|
|
|
|
const I3dl2ReverbState& EffectI3dl2Reverb::GetState() const {
|
|
return state;
|
|
}
|
|
|
|
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
|
|
EffectBiquadFilter::~EffectBiquadFilter() = default;
|
|
|
|
void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
|
|
auto& params = GetParams();
|
|
const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data());
|
|
mix_id = in_params.mix_id;
|
|
processing_order = in_params.processing_order;
|
|
params = *biquad_params;
|
|
enabled = in_params.is_enabled;
|
|
}
|
|
|
|
void EffectBiquadFilter::UpdateForCommandGeneration() {
|
|
if (enabled) {
|
|
usage = UsageState::Running;
|
|
} else {
|
|
usage = UsageState::Stopped;
|
|
}
|
|
GetParams().status = ParameterStatus::Updated;
|
|
}
|
|
|
|
EffectAuxInfo::EffectAuxInfo() : EffectGeneric(EffectType::Aux) {}
|
|
EffectAuxInfo::~EffectAuxInfo() = default;
|
|
|
|
void EffectAuxInfo::Update(EffectInfo::InParams& in_params) {
|
|
const auto* aux_params = reinterpret_cast<AuxInfo*>(in_params.raw.data());
|
|
mix_id = in_params.mix_id;
|
|
processing_order = in_params.processing_order;
|
|
GetParams() = *aux_params;
|
|
enabled = in_params.is_enabled;
|
|
|
|
if (in_params.is_new || skipped) {
|
|
skipped = aux_params->send_buffer_info == 0 || aux_params->return_buffer_info == 0;
|
|
if (skipped) {
|
|
return;
|
|
}
|
|
|
|
// There's two AuxInfos which are an identical size, the first one is managed by the cpu,
|
|
// the second is managed by the dsp. All we care about is managing the DSP one
|
|
send_info = aux_params->send_buffer_info + sizeof(AuxInfoDSP);
|
|
send_buffer = aux_params->send_buffer_info + (sizeof(AuxInfoDSP) * 2);
|
|
|
|
recv_info = aux_params->return_buffer_info + sizeof(AuxInfoDSP);
|
|
recv_buffer = aux_params->return_buffer_info + (sizeof(AuxInfoDSP) * 2);
|
|
}
|
|
}
|
|
|
|
void EffectAuxInfo::UpdateForCommandGeneration() {
|
|
if (enabled) {
|
|
usage = UsageState::Running;
|
|
} else {
|
|
usage = UsageState::Stopped;
|
|
}
|
|
}
|
|
|
|
VAddr EffectAuxInfo::GetSendInfo() const {
|
|
return send_info;
|
|
}
|
|
|
|
VAddr EffectAuxInfo::GetSendBuffer() const {
|
|
return send_buffer;
|
|
}
|
|
|
|
VAddr EffectAuxInfo::GetRecvInfo() const {
|
|
return recv_info;
|
|
}
|
|
|
|
VAddr EffectAuxInfo::GetRecvBuffer() const {
|
|
return recv_buffer;
|
|
}
|
|
|
|
EffectDelay::EffectDelay() : EffectGeneric(EffectType::Delay) {}
|
|
EffectDelay::~EffectDelay() = default;
|
|
|
|
void EffectDelay::Update(EffectInfo::InParams& in_params) {
|
|
const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data());
|
|
auto& params = GetParams();
|
|
if (!ValidChannelCountForEffect(delay_params->max_channels)) {
|
|
return;
|
|
}
|
|
|
|
const auto last_status = params.status;
|
|
mix_id = in_params.mix_id;
|
|
processing_order = in_params.processing_order;
|
|
params = *delay_params;
|
|
if (!ValidChannelCountForEffect(delay_params->channels)) {
|
|
params.channels = params.max_channels;
|
|
}
|
|
enabled = in_params.is_enabled;
|
|
|
|
if (last_status != ParameterStatus::Updated) {
|
|
params.status = last_status;
|
|
}
|
|
|
|
if (in_params.is_new || skipped) {
|
|
usage = UsageState::Initialized;
|
|
params.status = ParameterStatus::Initialized;
|
|
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
|
}
|
|
}
|
|
|
|
void EffectDelay::UpdateForCommandGeneration() {
|
|
if (enabled) {
|
|
usage = UsageState::Running;
|
|
} else {
|
|
usage = UsageState::Stopped;
|
|
}
|
|
GetParams().status = ParameterStatus::Updated;
|
|
}
|
|
|
|
EffectBufferMixer::EffectBufferMixer() : EffectGeneric(EffectType::BufferMixer) {}
|
|
EffectBufferMixer::~EffectBufferMixer() = default;
|
|
|
|
void EffectBufferMixer::Update(EffectInfo::InParams& in_params) {
|
|
mix_id = in_params.mix_id;
|
|
processing_order = in_params.processing_order;
|
|
GetParams() = *reinterpret_cast<BufferMixerParams*>(in_params.raw.data());
|
|
enabled = in_params.is_enabled;
|
|
}
|
|
|
|
void EffectBufferMixer::UpdateForCommandGeneration() {
|
|
if (enabled) {
|
|
usage = UsageState::Running;
|
|
} else {
|
|
usage = UsageState::Stopped;
|
|
}
|
|
}
|
|
|
|
EffectReverb::EffectReverb() : EffectGeneric(EffectType::Reverb) {}
|
|
EffectReverb::~EffectReverb() = default;
|
|
|
|
void EffectReverb::Update(EffectInfo::InParams& in_params) {
|
|
const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data());
|
|
auto& params = GetParams();
|
|
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
|
return;
|
|
}
|
|
|
|
const auto last_status = params.status;
|
|
mix_id = in_params.mix_id;
|
|
processing_order = in_params.processing_order;
|
|
params = *reverb_params;
|
|
if (!ValidChannelCountForEffect(reverb_params->channels)) {
|
|
params.channels = params.max_channels;
|
|
}
|
|
enabled = in_params.is_enabled;
|
|
|
|
if (last_status != ParameterStatus::Updated) {
|
|
params.status = last_status;
|
|
}
|
|
|
|
if (in_params.is_new || skipped) {
|
|
usage = UsageState::Initialized;
|
|
params.status = ParameterStatus::Initialized;
|
|
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
|
}
|
|
}
|
|
|
|
void EffectReverb::UpdateForCommandGeneration() {
|
|
if (enabled) {
|
|
usage = UsageState::Running;
|
|
} else {
|
|
usage = UsageState::Stopped;
|
|
}
|
|
GetParams().status = ParameterStatus::Updated;
|
|
}
|
|
|
|
} // namespace AudioCore
|