mirror of https://git.suyu.dev/suyu/suyu
Preliminary effects
This commit is contained in:
parent
1f1c3bddc0
commit
80ac1331b5
|
@ -27,12 +27,13 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
|
||||||
voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
|
voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
|
||||||
sink_context(params.sink_count), splitter_context(),
|
sink_context(params.sink_count), splitter_context(),
|
||||||
voices(params.voice_count), memory{memory_},
|
voices(params.voice_count), memory{memory_},
|
||||||
command_generator(worker_params, voice_context, mix_context, splitter_context, memory),
|
command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
|
||||||
|
memory),
|
||||||
temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) {
|
temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) {
|
||||||
behavior_info.SetUserRevision(params.revision);
|
behavior_info.SetUserRevision(params.revision);
|
||||||
splitter_context.Initialize(behavior_info, params.splitter_count,
|
splitter_context.Initialize(behavior_info, params.splitter_count,
|
||||||
params.num_splitter_send_channels);
|
params.num_splitter_send_channels);
|
||||||
mix_context.Initialize(behavior_info, params.submix_count + 1);
|
mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
|
||||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||||
stream =
|
stream =
|
||||||
audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
||||||
|
@ -106,8 +107,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mix_result =
|
auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
|
||||||
info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, splitter_context);
|
splitter_context, effect_context);
|
||||||
|
|
||||||
if (mix_result.IsError()) {
|
if (mix_result.IsError()) {
|
||||||
LOG_ERROR(Audio, "Failed to update mix parameters");
|
LOG_ERROR(Audio, "Failed to update mix parameters");
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "audio_core/algorithm/interpolate.h"
|
#include "audio_core/algorithm/interpolate.h"
|
||||||
#include "audio_core/command_generator.h"
|
#include "audio_core/command_generator.h"
|
||||||
|
#include "audio_core/effect_context.h"
|
||||||
#include "audio_core/mix_context.h"
|
#include "audio_core/mix_context.h"
|
||||||
#include "audio_core/voice_context.h"
|
#include "audio_core/voice_context.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
@ -68,9 +69,10 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
|
||||||
|
|
||||||
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
|
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
|
||||||
VoiceContext& voice_context, MixContext& mix_context,
|
VoiceContext& voice_context, MixContext& mix_context,
|
||||||
SplitterContext& splitter_context, Core::Memory::Memory& memory)
|
SplitterContext& splitter_context, EffectContext& effect_context,
|
||||||
|
Core::Memory::Memory& memory)
|
||||||
: worker_params(worker_params), voice_context(voice_context), mix_context(mix_context),
|
: worker_params(worker_params), voice_context(voice_context), mix_context(mix_context),
|
||||||
splitter_context(splitter_context), memory(memory),
|
splitter_context(splitter_context), effect_context(effect_context), memory(memory),
|
||||||
mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
|
mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
|
||||||
worker_params.sample_count),
|
worker_params.sample_count),
|
||||||
sample_buffer(MIX_BUFFER_SIZE),
|
sample_buffer(MIX_BUFFER_SIZE),
|
||||||
|
@ -338,6 +340,120 @@ void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
|
||||||
|
const std::size_t effect_count = effect_context.GetCount();
|
||||||
|
const auto buffer_offset = mix_info.GetInParams().buffer_offset;
|
||||||
|
for (std::size_t i = 0; i < effect_count; i++) {
|
||||||
|
const auto index = mix_info.GetEffectOrder(i);
|
||||||
|
if (index == AudioCommon::NO_EFFECT_ORDER) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto* info = effect_context.GetInfo(index);
|
||||||
|
const auto type = info->GetType();
|
||||||
|
|
||||||
|
// TODO(ogniK): Finish remaining effects
|
||||||
|
switch (type) {
|
||||||
|
case EffectType::Aux:
|
||||||
|
GenerateAuxCommand(buffer_offset, info, info->IsEnabled());
|
||||||
|
break;
|
||||||
|
case EffectType::I3dl2Reverb:
|
||||||
|
GenerateI3dl2ReverbEffectCommand(buffer_offset, info, info->IsEnabled());
|
||||||
|
break;
|
||||||
|
case EffectType::BiquadFilter:
|
||||||
|
GenerateBiquadFilterEffectCommand(buffer_offset, info, info->IsEnabled());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->UpdateForCommandGeneration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
|
||||||
|
bool enabled) {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
|
||||||
|
const auto channel_count = params.channel_count;
|
||||||
|
for (s32 i = 0; i < channel_count; i++) {
|
||||||
|
// TODO(ogniK): Actually implement reverb
|
||||||
|
if (params.input[i] != params.output[i]) {
|
||||||
|
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||||
|
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||||
|
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info,
|
||||||
|
bool enabled) {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& params = dynamic_cast<EffectBiquadFilter*>(info)->GetParams();
|
||||||
|
const auto channel_count = params.channel_count;
|
||||||
|
for (s32 i = 0; i < channel_count; i++) {
|
||||||
|
// TODO(ogniK): Actually implement biquad filter
|
||||||
|
if (params.input[i] != params.output[i]) {
|
||||||
|
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||||
|
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||||
|
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) {
|
||||||
|
auto aux = dynamic_cast<EffectAuxInfo*>(info);
|
||||||
|
const auto& params = aux->GetParams();
|
||||||
|
if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) {
|
||||||
|
const auto max_channels = params.count;
|
||||||
|
u32 offset{};
|
||||||
|
for (u32 channel = 0; channel < max_channels; channel++) {
|
||||||
|
u32 write_count = 0;
|
||||||
|
if (channel == (max_channels - 1)) {
|
||||||
|
write_count = offset + worker_params.sample_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto input_index = params.input_mix_buffers[channel] + mix_buffer_offset;
|
||||||
|
const auto output_index = params.output_mix_buffers[channel] + mix_buffer_offset;
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
AuxInfoDSP send_info{};
|
||||||
|
AuxInfoDSP recv_info{};
|
||||||
|
memory.ReadBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
|
||||||
|
memory.ReadBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
|
||||||
|
|
||||||
|
WriteAuxBuffer(send_info, aux->GetSendBuffer(), params.sample_count,
|
||||||
|
GetMixBuffer(input_index), worker_params.sample_count, offset,
|
||||||
|
write_count);
|
||||||
|
memory.WriteBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
|
||||||
|
|
||||||
|
const auto samples_read = ReadAuxBuffer(
|
||||||
|
recv_info, aux->GetRecvBuffer(), params.sample_count,
|
||||||
|
GetMixBuffer(output_index), worker_params.sample_count, offset, write_count);
|
||||||
|
memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
|
||||||
|
|
||||||
|
if (samples_read != worker_params.sample_count &&
|
||||||
|
samples_read <= params.sample_count) {
|
||||||
|
std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
AuxInfoDSP empty{};
|
||||||
|
memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
|
||||||
|
memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
|
||||||
|
if (output_index != input_index) {
|
||||||
|
std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index),
|
||||||
|
worker_params.sample_count * sizeof(s32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += worker_params.sample_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) {
|
ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) {
|
||||||
if (splitter_id == AudioCommon::NO_SPLITTER) {
|
if (splitter_id == AudioCommon::NO_SPLITTER) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -345,6 +461,66 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter
|
||||||
return splitter_context.GetDestinationData(splitter_id, index);
|
return splitter_context.GetDestinationData(splitter_id, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
|
||||||
|
const s32* data, u32 sample_count, u32 write_offset,
|
||||||
|
u32 write_count) {
|
||||||
|
if (max_samples == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u32 offset = dsp_info.write_offset + write_offset;
|
||||||
|
if (send_buffer == 0 || offset > max_samples) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t data_offset{};
|
||||||
|
u32 remaining = sample_count;
|
||||||
|
while (remaining > 0) {
|
||||||
|
// Get position in buffer
|
||||||
|
const auto base = send_buffer + (offset * sizeof(u32));
|
||||||
|
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
||||||
|
// Write to output
|
||||||
|
memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32));
|
||||||
|
offset = (offset + samples_to_grab) % max_samples;
|
||||||
|
remaining -= samples_to_grab;
|
||||||
|
data_offset += samples_to_grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_count != 0) {
|
||||||
|
dsp_info.write_offset = (dsp_info.write_offset + write_count) % max_samples;
|
||||||
|
}
|
||||||
|
return sample_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples,
|
||||||
|
s32* out_data, u32 sample_count, u32 read_offset,
|
||||||
|
u32 read_count) {
|
||||||
|
if (max_samples == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 offset = recv_info.read_offset + read_offset;
|
||||||
|
if (recv_buffer == 0 || offset > max_samples) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 remaining = sample_count;
|
||||||
|
while (remaining > 0) {
|
||||||
|
const auto base = recv_buffer + (offset * sizeof(u32));
|
||||||
|
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
||||||
|
std::vector<s32> buffer(samples_to_grab);
|
||||||
|
memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
|
||||||
|
std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32));
|
||||||
|
out_data += samples_to_grab;
|
||||||
|
offset = (offset + samples_to_grab) % max_samples;
|
||||||
|
remaining -= samples_to_grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_count != 0) {
|
||||||
|
recv_info.read_offset = (recv_info.read_offset + read_count) % max_samples;
|
||||||
|
}
|
||||||
|
return sample_count;
|
||||||
|
}
|
||||||
|
|
||||||
void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
|
void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
|
||||||
s32 channel, s32 node_id) {
|
s32 channel, s32 node_id) {
|
||||||
const auto last = static_cast<s32>(last_volume * 32768.0f);
|
const auto last = static_cast<s32>(last_volume * 32768.0f);
|
||||||
|
@ -398,7 +574,9 @@ void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) {
|
||||||
auto& in_params = mix_info.GetInParams();
|
auto& in_params = mix_info.GetInParams();
|
||||||
GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
|
GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
|
||||||
in_params.sample_rate);
|
in_params.sample_rate);
|
||||||
// TODO(ogniK): Effects
|
|
||||||
|
GenerateEffectCommand(mix_info);
|
||||||
|
|
||||||
GenerateMixCommands(mix_info);
|
GenerateMixCommands(mix_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -476,7 +654,8 @@ void CommandGenerator::GenerateFinalMixCommand() {
|
||||||
|
|
||||||
GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
|
GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
|
||||||
in_params.sample_rate);
|
in_params.sample_rate);
|
||||||
// TODO(ogniK): Effects
|
|
||||||
|
GenerateEffectCommand(mix_info);
|
||||||
|
|
||||||
for (s32 i = 0; i < in_params.buffer_count; i++) {
|
for (s32 i = 0; i < in_params.buffer_count; i++) {
|
||||||
const s32 gain = static_cast<s32>(in_params.volume * 32768.0f);
|
const s32 gain = static_cast<s32>(in_params.volume * 32768.0f);
|
||||||
|
|
|
@ -19,14 +19,17 @@ class MixContext;
|
||||||
class SplitterContext;
|
class SplitterContext;
|
||||||
class ServerSplitterDestinationData;
|
class ServerSplitterDestinationData;
|
||||||
class ServerMixInfo;
|
class ServerMixInfo;
|
||||||
|
class EffectContext;
|
||||||
|
class EffectBase;
|
||||||
|
struct AuxInfoDSP;
|
||||||
using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
|
using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
|
||||||
|
|
||||||
class CommandGenerator {
|
class CommandGenerator {
|
||||||
public:
|
public:
|
||||||
explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
|
explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
|
||||||
VoiceContext& voice_context, MixContext& mix_context,
|
VoiceContext& voice_context, MixContext& mix_context,
|
||||||
SplitterContext& splitter_context, Core::Memory::Memory& memory);
|
SplitterContext& splitter_context, EffectContext& effect_context,
|
||||||
|
Core::Memory::Memory& memory);
|
||||||
~CommandGenerator();
|
~CommandGenerator();
|
||||||
|
|
||||||
void ClearMixBuffers();
|
void ClearMixBuffers();
|
||||||
|
@ -67,8 +70,17 @@ private:
|
||||||
std::size_t mix_buffer_offset);
|
std::size_t mix_buffer_offset);
|
||||||
void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
|
void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
|
||||||
std::size_t mix_buffer_offset, s32 sample_rate);
|
std::size_t mix_buffer_offset, s32 sample_rate);
|
||||||
|
void GenerateEffectCommand(ServerMixInfo& mix_info);
|
||||||
|
void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
|
void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
|
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
||||||
|
|
||||||
|
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
|
||||||
|
u32 sample_count, u32 write_offset, u32 write_count);
|
||||||
|
s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
|
||||||
|
u32 sample_count, u32 read_offset, u32 read_count);
|
||||||
|
|
||||||
// DSP Code
|
// DSP Code
|
||||||
s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
|
s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
|
||||||
s32 channel, std::size_t mix_offset);
|
s32 channel, std::size_t mix_offset);
|
||||||
|
@ -81,6 +93,7 @@ private:
|
||||||
VoiceContext& voice_context;
|
VoiceContext& voice_context;
|
||||||
MixContext& mix_context;
|
MixContext& mix_context;
|
||||||
SplitterContext& splitter_context;
|
SplitterContext& splitter_context;
|
||||||
|
EffectContext& effect_context;
|
||||||
Core::Memory::Memory& memory;
|
Core::Memory::Memory& memory;
|
||||||
std::vector<s32> mix_buffer{};
|
std::vector<s32> mix_buffer{};
|
||||||
std::vector<s32> sample_buffer{};
|
std::vector<s32> sample_buffer{};
|
||||||
|
|
|
@ -26,6 +26,7 @@ constexpr s32 NO_SPLITTER = -1;
|
||||||
constexpr s32 NO_MIX = 0x7fffffff;
|
constexpr s32 NO_MIX = 0x7fffffff;
|
||||||
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
|
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
|
||||||
constexpr s32 FINAL_MIX = 0;
|
constexpr s32 FINAL_MIX = 0;
|
||||||
|
constexpr s32 NO_EFFECT_ORDER = -1;
|
||||||
constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant
|
constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant
|
||||||
// Any size checks seem to take the sample history into account
|
// Any size checks seem to take the sample history into account
|
||||||
// and our const ends up being 0x3f04, the 4 bytes are most
|
// and our const ends up being 0x3f04, the 4 bytes are most
|
||||||
|
|
|
@ -6,6 +6,12 @@
|
||||||
#include "audio_core/effect_context.h"
|
#include "audio_core/effect_context.h"
|
||||||
|
|
||||||
namespace AudioCore {
|
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) {
|
EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) {
|
||||||
effects.reserve(effect_count);
|
effects.reserve(effect_count);
|
||||||
std::generate_n(std::back_inserter(effects), effect_count,
|
std::generate_n(std::back_inserter(effects), effect_count,
|
||||||
|
@ -21,24 +27,273 @@ EffectBase* EffectContext::GetInfo(std::size_t i) {
|
||||||
return effects.at(i).get();
|
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 {
|
const EffectBase* EffectContext::GetInfo(std::size_t i) const {
|
||||||
return effects.at(i).get();
|
return effects.at(i).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectStubbed::EffectStubbed() : EffectBase::EffectBase() {}
|
EffectStubbed::EffectStubbed() : EffectBase::EffectBase(EffectType::Invalid) {}
|
||||||
EffectStubbed::~EffectStubbed() = default;
|
EffectStubbed::~EffectStubbed() = default;
|
||||||
|
|
||||||
void EffectStubbed::Update(EffectInfo::InParams& in_params) {
|
void EffectStubbed::Update(EffectInfo::InParams& in_params) {}
|
||||||
if (in_params.is_new) {
|
void EffectStubbed::UpdateForCommandGeneration() {}
|
||||||
usage = UsageStatus::New;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EffectBase::EffectBase() = default;
|
EffectBase::EffectBase(EffectType effect_type) : effect_type(effect_type) {}
|
||||||
EffectBase::~EffectBase() = default;
|
EffectBase::~EffectBase() = default;
|
||||||
|
|
||||||
UsageStatus EffectBase::GetUsage() const {
|
UsageState EffectBase::GetUsage() const {
|
||||||
return usage;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric::EffectGeneric(EffectType::I3dl2Reverb) {}
|
||||||
|
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
|
||||||
|
|
||||||
|
void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
||||||
|
auto& internal_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 = internal_params.status;
|
||||||
|
mix_id = in_params.mix_id;
|
||||||
|
processing_order = in_params.processing_order;
|
||||||
|
internal_params = *reverb_params;
|
||||||
|
if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
|
||||||
|
internal_params.channel_count = internal_params.max_channels;
|
||||||
|
}
|
||||||
|
enabled = in_params.is_enabled;
|
||||||
|
if (last_status != ParameterStatus::Updated) {
|
||||||
|
internal_params.status = last_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_params.is_new || skipped) {
|
||||||
|
usage = UsageState::Initialized;
|
||||||
|
internal_params.status = ParameterStatus::Initialized;
|
||||||
|
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectI3dl2Reverb::UpdateForCommandGeneration() {
|
||||||
|
if (enabled) {
|
||||||
|
usage = UsageState::Running;
|
||||||
|
} else {
|
||||||
|
usage = UsageState::Stopped;
|
||||||
|
}
|
||||||
|
GetParams().status = ParameterStatus::Updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric::EffectGeneric(EffectType::BiquadFilter) {}
|
||||||
|
EffectBiquadFilter::~EffectBiquadFilter() = default;
|
||||||
|
|
||||||
|
void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
|
||||||
|
auto& internal_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;
|
||||||
|
internal_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::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VAddr EffectAuxInfo::GetSendInfo() const {
|
||||||
|
return send_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VAddr EffectAuxInfo::GetSendBuffer() const {
|
||||||
|
return send_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VAddr EffectAuxInfo::GetRecvInfo() const {
|
||||||
|
return recv_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VAddr EffectAuxInfo::GetRecvBuffer() const {
|
||||||
|
return recv_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectDelay::EffectDelay() : EffectGeneric::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& internal_params = GetParams();
|
||||||
|
if (!ValidChannelCountForEffect(delay_params->max_channels)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto last_status = internal_params.status;
|
||||||
|
mix_id = in_params.mix_id;
|
||||||
|
processing_order = in_params.processing_order;
|
||||||
|
internal_params = *delay_params;
|
||||||
|
if (!ValidChannelCountForEffect(delay_params->channels)) {
|
||||||
|
internal_params.channels = internal_params.max_channels;
|
||||||
|
}
|
||||||
|
enabled = in_params.is_enabled;
|
||||||
|
|
||||||
|
if (last_status != ParameterStatus::Updated) {
|
||||||
|
internal_params.status = last_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_params.is_new || skipped) {
|
||||||
|
usage = UsageState::Initialized;
|
||||||
|
internal_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::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::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& internal_params = GetParams();
|
||||||
|
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto last_status = internal_params.status;
|
||||||
|
mix_id = in_params.mix_id;
|
||||||
|
processing_order = in_params.processing_order;
|
||||||
|
internal_params = *reverb_params;
|
||||||
|
if (!ValidChannelCountForEffect(reverb_params->channels)) {
|
||||||
|
internal_params.channels = internal_params.max_channels;
|
||||||
|
}
|
||||||
|
enabled = in_params.is_enabled;
|
||||||
|
|
||||||
|
if (last_status != ParameterStatus::Updated) {
|
||||||
|
internal_params.status = last_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_params.is_new || skipped) {
|
||||||
|
usage = UsageState::Initialized;
|
||||||
|
internal_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
|
} // namespace AudioCore
|
||||||
|
|
|
@ -31,6 +31,19 @@ enum class UsageStatus : u8 {
|
||||||
Removed = 4,
|
Removed = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class UsageState {
|
||||||
|
Invalid = 0,
|
||||||
|
Initialized = 1,
|
||||||
|
Running = 2,
|
||||||
|
Stopped = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ParameterStatus : u8 {
|
||||||
|
Initialized = 0,
|
||||||
|
Updating = 1,
|
||||||
|
Updated = 2,
|
||||||
|
};
|
||||||
|
|
||||||
struct BufferMixerParams {
|
struct BufferMixerParams {
|
||||||
std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input{};
|
std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input{};
|
||||||
std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output{};
|
std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output{};
|
||||||
|
@ -39,6 +52,14 @@ struct BufferMixerParams {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(BufferMixerParams) == 0x94, "BufferMixerParams is an invalid size");
|
static_assert(sizeof(BufferMixerParams) == 0x94, "BufferMixerParams is an invalid size");
|
||||||
|
|
||||||
|
struct AuxInfoDSP {
|
||||||
|
u32_le read_offset{};
|
||||||
|
u32_le write_offset{};
|
||||||
|
u32_le remaining{};
|
||||||
|
INSERT_PADDING_WORDS(13);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AuxInfoDSP) == 0x40, "AuxInfoDSP is an invalid size");
|
||||||
|
|
||||||
struct AuxInfo {
|
struct AuxInfo {
|
||||||
std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input_mix_buffers{};
|
std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input_mix_buffers{};
|
||||||
std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output_mix_buffers{};
|
std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output_mix_buffers{};
|
||||||
|
@ -54,6 +75,81 @@ struct AuxInfo {
|
||||||
};
|
};
|
||||||
static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
|
static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
|
||||||
|
|
||||||
|
struct I3dl2ReverbParams {
|
||||||
|
std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||||
|
std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||||
|
u16_le max_channels{};
|
||||||
|
u16_le channel_count{};
|
||||||
|
INSERT_PADDING_BYTES(1);
|
||||||
|
u32_le sample_rate{};
|
||||||
|
f32 room_hf{};
|
||||||
|
f32 hf_reference{};
|
||||||
|
f32 decay_time{};
|
||||||
|
f32 hf_decay_ratio{};
|
||||||
|
f32 room{};
|
||||||
|
f32 reflection{};
|
||||||
|
f32 reverb{};
|
||||||
|
f32 diffusion{};
|
||||||
|
f32 reflection_delay{};
|
||||||
|
f32 reverb_delay{};
|
||||||
|
f32 density{};
|
||||||
|
f32 dry_gain{};
|
||||||
|
ParameterStatus status{};
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(I3dl2ReverbParams) == 0x4c, "I3dl2ReverbParams is an invalid size");
|
||||||
|
|
||||||
|
struct BiquadFilterParams {
|
||||||
|
std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||||
|
std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||||
|
std::array<s16_le, 3> numerator;
|
||||||
|
std::array<s16_le, 2> denominator;
|
||||||
|
s8 channel_count{};
|
||||||
|
ParameterStatus status{};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(BiquadFilterParams) == 0x18, "BiquadFilterParams is an invalid size");
|
||||||
|
|
||||||
|
struct DelayParams {
|
||||||
|
std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||||
|
std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||||
|
u16_le max_channels{};
|
||||||
|
u16_le channels{};
|
||||||
|
s32_le max_delay{};
|
||||||
|
s32_le delay{};
|
||||||
|
s32_le sample_rate{};
|
||||||
|
s32_le gain{};
|
||||||
|
s32_le feedback_gain{};
|
||||||
|
s32_le out_gain{};
|
||||||
|
s32_le dry_gain{};
|
||||||
|
s32_le channel_spread{};
|
||||||
|
s32_le low_pass{};
|
||||||
|
ParameterStatus status{};
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(DelayParams) == 0x38, "DelayParams is an invalid size");
|
||||||
|
|
||||||
|
struct ReverbParams {
|
||||||
|
std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||||
|
std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||||
|
u16_le max_channels{};
|
||||||
|
u16_le channels{};
|
||||||
|
s32_le sample_rate{};
|
||||||
|
s32_le mode0{};
|
||||||
|
s32_le mode0_gain{};
|
||||||
|
s32_le pre_delay{};
|
||||||
|
s32_le mode1{};
|
||||||
|
s32_le mode1_gain{};
|
||||||
|
s32_le decay{};
|
||||||
|
s32_le hf_decay_ratio{};
|
||||||
|
s32_le coloration{};
|
||||||
|
s32_le reverb_gain{};
|
||||||
|
s32_le out_gain{};
|
||||||
|
s32_le dry_gain{};
|
||||||
|
ParameterStatus status{};
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ReverbParams) == 0x44, "ReverbParams is an invalid size");
|
||||||
|
|
||||||
class EffectInfo {
|
class EffectInfo {
|
||||||
public:
|
public:
|
||||||
struct InParams {
|
struct InParams {
|
||||||
|
@ -64,7 +160,7 @@ public:
|
||||||
s32_le mix_id{};
|
s32_le mix_id{};
|
||||||
u64_le buffer_address{};
|
u64_le buffer_address{};
|
||||||
u64_le buffer_size{};
|
u64_le buffer_size{};
|
||||||
s32_le priority{};
|
s32_le processing_order{};
|
||||||
INSERT_PADDING_BYTES(4);
|
INSERT_PADDING_BYTES(4);
|
||||||
union {
|
union {
|
||||||
std::array<u8, 0xa0> raw;
|
std::array<u8, 0xa0> raw;
|
||||||
|
@ -79,16 +175,50 @@ public:
|
||||||
static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size");
|
static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AuxAddress {
|
||||||
|
VAddr send_dsp_info{};
|
||||||
|
VAddr send_buffer_base{};
|
||||||
|
VAddr return_dsp_info{};
|
||||||
|
VAddr return_buffer_base{};
|
||||||
|
};
|
||||||
|
|
||||||
class EffectBase {
|
class EffectBase {
|
||||||
public:
|
public:
|
||||||
EffectBase();
|
EffectBase(EffectType effect_type);
|
||||||
~EffectBase();
|
~EffectBase();
|
||||||
|
|
||||||
virtual void Update(EffectInfo::InParams& in_params) = 0;
|
virtual void Update(EffectInfo::InParams& in_params) = 0;
|
||||||
UsageStatus GetUsage() const;
|
virtual void UpdateForCommandGeneration() = 0;
|
||||||
|
UsageState GetUsage() const;
|
||||||
|
EffectType GetType() const;
|
||||||
|
bool IsEnabled() const;
|
||||||
|
s32 GetMixID() const;
|
||||||
|
s32 GetProcessingOrder() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UsageStatus usage{UsageStatus::Invalid};
|
UsageState usage{UsageState::Invalid};
|
||||||
|
EffectType effect_type{};
|
||||||
|
s32 mix_id{};
|
||||||
|
s32 processing_order{};
|
||||||
|
bool enabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class EffectGeneric : public EffectBase {
|
||||||
|
public:
|
||||||
|
EffectGeneric(EffectType effect_type) : EffectBase::EffectBase(effect_type) {}
|
||||||
|
~EffectGeneric() = default;
|
||||||
|
|
||||||
|
T& GetParams() {
|
||||||
|
return internal_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
const I3dl2ReverbParams& GetParams() const {
|
||||||
|
return internal_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T internal_params{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class EffectStubbed : public EffectBase {
|
class EffectStubbed : public EffectBase {
|
||||||
|
@ -97,6 +227,82 @@ public:
|
||||||
~EffectStubbed();
|
~EffectStubbed();
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
|
void UpdateForCommandGeneration() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
|
||||||
|
public:
|
||||||
|
explicit EffectI3dl2Reverb();
|
||||||
|
~EffectI3dl2Reverb();
|
||||||
|
|
||||||
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
|
void UpdateForCommandGeneration() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool skipped = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
|
||||||
|
public:
|
||||||
|
explicit EffectBiquadFilter();
|
||||||
|
~EffectBiquadFilter();
|
||||||
|
|
||||||
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
|
void UpdateForCommandGeneration() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EffectAuxInfo : public EffectGeneric<AuxInfo> {
|
||||||
|
public:
|
||||||
|
explicit EffectAuxInfo();
|
||||||
|
~EffectAuxInfo();
|
||||||
|
|
||||||
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
|
void UpdateForCommandGeneration() override;
|
||||||
|
const VAddr GetSendInfo() const;
|
||||||
|
const VAddr GetSendBuffer() const;
|
||||||
|
const VAddr GetRecvInfo() const;
|
||||||
|
const VAddr GetRecvBuffer() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VAddr send_info{};
|
||||||
|
VAddr send_buffer{};
|
||||||
|
VAddr recv_info{};
|
||||||
|
VAddr recv_buffer{};
|
||||||
|
bool skipped = false;
|
||||||
|
AuxAddress addresses{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class EffectDelay : public EffectGeneric<DelayParams> {
|
||||||
|
public:
|
||||||
|
explicit EffectDelay();
|
||||||
|
~EffectDelay();
|
||||||
|
|
||||||
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
|
void UpdateForCommandGeneration() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool skipped = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EffectBufferMixer : public EffectGeneric<BufferMixerParams> {
|
||||||
|
public:
|
||||||
|
explicit EffectBufferMixer();
|
||||||
|
~EffectBufferMixer();
|
||||||
|
|
||||||
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
|
void UpdateForCommandGeneration() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EffectReverb : public EffectGeneric<ReverbParams> {
|
||||||
|
public:
|
||||||
|
explicit EffectReverb();
|
||||||
|
~EffectReverb();
|
||||||
|
|
||||||
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
|
void UpdateForCommandGeneration() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool skipped = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EffectContext {
|
class EffectContext {
|
||||||
|
@ -106,6 +312,7 @@ public:
|
||||||
|
|
||||||
std::size_t GetCount() const;
|
std::size_t GetCount() const;
|
||||||
EffectBase* GetInfo(std::size_t i);
|
EffectBase* GetInfo(std::size_t i);
|
||||||
|
EffectBase* RetargetEffect(std::size_t i, EffectType effect);
|
||||||
const EffectBase* GetInfo(std::size_t i) const;
|
const EffectBase* GetInfo(std::size_t i) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -244,14 +244,15 @@ bool InfoUpdater::UpdateEffects(EffectContext& effect_context, bool is_active) {
|
||||||
// Update effects
|
// Update effects
|
||||||
for (std::size_t i = 0; i < effect_count; i++) {
|
for (std::size_t i = 0; i < effect_count; i++) {
|
||||||
auto* info = effect_context.GetInfo(i);
|
auto* info = effect_context.GetInfo(i);
|
||||||
|
if (effect_in[i].type != info->GetType()) {
|
||||||
|
info = effect_context.RetargetEffect(i, effect_in[i].type);
|
||||||
|
}
|
||||||
|
|
||||||
info->Update(effect_in[i]);
|
info->Update(effect_in[i]);
|
||||||
|
|
||||||
// TODO(ogniK): Update individual effects
|
if ((!is_active && info->GetUsage() != UsageState::Initialized) ||
|
||||||
if ((!is_active && info->GetUsage() != UsageStatus::New) ||
|
info->GetUsage() == UsageState::Stopped) {
|
||||||
info->GetUsage() == UsageStatus::Removed) {
|
|
||||||
effect_out[i].status = UsageStatus::Removed;
|
effect_out[i].status = UsageStatus::Removed;
|
||||||
} else if (info->GetUsage() == UsageStatus::New) {
|
|
||||||
effect_out[i].status = UsageStatus::New;
|
|
||||||
} else {
|
} else {
|
||||||
effect_out[i].status = UsageStatus::Used;
|
effect_out[i].status = UsageStatus::Used;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +291,8 @@ bool InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||||
SplitterContext& splitter_context) {
|
SplitterContext& splitter_context,
|
||||||
|
EffectContext& effect_context) {
|
||||||
std::vector<MixInfo::InParams> mix_in_params;
|
std::vector<MixInfo::InParams> mix_in_params;
|
||||||
|
|
||||||
if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
|
if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
|
||||||
|
@ -387,13 +389,13 @@ ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buf
|
||||||
auto& mix_info_params = mix_info.GetInParams();
|
auto& mix_info_params = mix_info.GetInParams();
|
||||||
if (mix_info_params.in_use != mix_in.in_use) {
|
if (mix_info_params.in_use != mix_in.in_use) {
|
||||||
mix_info_params.in_use = mix_in.in_use;
|
mix_info_params.in_use = mix_in.in_use;
|
||||||
// TODO(ogniK): Update effect processing order
|
mix_info.ResetEffectProcessingOrder();
|
||||||
should_sort = true;
|
should_sort = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mix_in.in_use) {
|
if (mix_in.in_use) {
|
||||||
should_sort |= mix_info.Update(mix_context.GetEdgeMatrix(), mix_in, behavior_info,
|
should_sort |= mix_info.Update(mix_context.GetEdgeMatrix(), mix_in, behavior_info,
|
||||||
splitter_context);
|
splitter_context, effect_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ public:
|
||||||
bool UpdateEffects(EffectContext& effect_context, bool is_active);
|
bool UpdateEffects(EffectContext& effect_context, bool is_active);
|
||||||
bool UpdateSplitterInfo(SplitterContext& splitter_context);
|
bool UpdateSplitterInfo(SplitterContext& splitter_context);
|
||||||
ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
|
||||||
SplitterContext& splitter_context);
|
SplitterContext& splitter_context, EffectContext& effect_context);
|
||||||
bool UpdateSinks(SinkContext& sink_context);
|
bool UpdateSinks(SinkContext& sink_context);
|
||||||
bool UpdatePerformanceBuffer();
|
bool UpdatePerformanceBuffer();
|
||||||
bool UpdateErrorInfo(BehaviorInfo& in_behavior_info);
|
bool UpdateErrorInfo(BehaviorInfo& in_behavior_info);
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "audio_core/behavior_info.h"
|
#include "audio_core/behavior_info.h"
|
||||||
#include "audio_core/common.h"
|
#include "audio_core/common.h"
|
||||||
|
#include "audio_core/effect_context.h"
|
||||||
#include "audio_core/mix_context.h"
|
#include "audio_core/mix_context.h"
|
||||||
#include "audio_core/splitter_context.h"
|
#include "audio_core/splitter_context.h"
|
||||||
|
|
||||||
|
@ -11,7 +12,8 @@ namespace AudioCore {
|
||||||
MixContext::MixContext() = default;
|
MixContext::MixContext() = default;
|
||||||
MixContext::~MixContext() = default;
|
MixContext::~MixContext() = default;
|
||||||
|
|
||||||
void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count) {
|
void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
|
||||||
|
std::size_t effect_count) {
|
||||||
info_count = mix_count;
|
info_count = mix_count;
|
||||||
infos.resize(info_count);
|
infos.resize(info_count);
|
||||||
auto& final_mix = GetInfo(AudioCommon::FINAL_MIX);
|
auto& final_mix = GetInfo(AudioCommon::FINAL_MIX);
|
||||||
|
@ -21,6 +23,10 @@ void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_c
|
||||||
sorted_info.push_back(&info);
|
sorted_info.push_back(&info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto& info : infos) {
|
||||||
|
info.SetEffectCount(effect_count);
|
||||||
|
}
|
||||||
|
|
||||||
// Only initialize our edge matrix and node states if splitters are supported
|
// Only initialize our edge matrix and node states if splitters are supported
|
||||||
if (behavior_info.IsSplitterSupported()) {
|
if (behavior_info.IsSplitterSupported()) {
|
||||||
node_states.Initialize(mix_count);
|
node_states.Initialize(mix_count);
|
||||||
|
@ -185,7 +191,8 @@ ServerMixInfo::InParams& ServerMixInfo::GetInParams() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
||||||
BehaviorInfo& behavior_info, SplitterContext& splitter_context) {
|
BehaviorInfo& behavior_info, SplitterContext& splitter_context,
|
||||||
|
EffectContext& effect_context) {
|
||||||
in_params.volume = mix_in.volume;
|
in_params.volume = mix_in.volume;
|
||||||
in_params.sample_rate = mix_in.sample_rate;
|
in_params.sample_rate = mix_in.sample_rate;
|
||||||
in_params.buffer_count = mix_in.buffer_count;
|
in_params.buffer_count = mix_in.buffer_count;
|
||||||
|
@ -206,6 +213,15 @@ bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix
|
||||||
in_params.splitter_id = AudioCommon::NO_SPLITTER;
|
in_params.splitter_id = AudioCommon::NO_SPLITTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResetEffectProcessingOrder();
|
||||||
|
const auto effect_count = effect_context.GetCount();
|
||||||
|
for (std::size_t i = 0; i < effect_count; i++) {
|
||||||
|
auto* effect_info = effect_context.GetInfo(i);
|
||||||
|
if (effect_info->GetMixID() == in_params.mix_id) {
|
||||||
|
effect_processing_order[effect_info->GetProcessingOrder()] = static_cast<s32>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(ogniK): Update effect processing order
|
// TODO(ogniK): Update effect processing order
|
||||||
return require_sort;
|
return require_sort;
|
||||||
}
|
}
|
||||||
|
@ -228,6 +244,21 @@ void ServerMixInfo::Cleanup() {
|
||||||
std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size());
|
std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerMixInfo::SetEffectCount(std::size_t count) {
|
||||||
|
effect_processing_order.resize(count);
|
||||||
|
ResetEffectProcessingOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerMixInfo::ResetEffectProcessingOrder() {
|
||||||
|
for (auto& order : effect_processing_order) {
|
||||||
|
order = AudioCommon::NO_EFFECT_ORDER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 ServerMixInfo::GetEffectOrder(std::size_t i) const {
|
||||||
|
return effect_processing_order.at(i);
|
||||||
|
}
|
||||||
|
|
||||||
bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
||||||
SplitterContext& splitter_context) {
|
SplitterContext& splitter_context) {
|
||||||
// Mixes are identical
|
// Mixes are identical
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
class BehaviorInfo;
|
class BehaviorInfo;
|
||||||
|
class EffectContext;
|
||||||
|
|
||||||
class MixInfo {
|
class MixInfo {
|
||||||
public:
|
public:
|
||||||
|
@ -65,11 +66,16 @@ public:
|
||||||
ServerMixInfo::InParams& GetInParams();
|
ServerMixInfo::InParams& GetInParams();
|
||||||
|
|
||||||
bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
||||||
BehaviorInfo& behavior_info, SplitterContext& splitter_context);
|
BehaviorInfo& behavior_info, SplitterContext& splitter_context,
|
||||||
|
EffectContext& effect_context);
|
||||||
bool HasAnyConnection() const;
|
bool HasAnyConnection() const;
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
|
void SetEffectCount(std::size_t count);
|
||||||
|
void ResetEffectProcessingOrder();
|
||||||
|
s32 GetEffectOrder(std::size_t i) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::vector<s32> effect_processing_order;
|
||||||
InParams in_params{};
|
InParams in_params{};
|
||||||
bool UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
bool UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
||||||
SplitterContext& splitter_context);
|
SplitterContext& splitter_context);
|
||||||
|
@ -80,7 +86,8 @@ public:
|
||||||
MixContext();
|
MixContext();
|
||||||
~MixContext();
|
~MixContext();
|
||||||
|
|
||||||
void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count);
|
void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
|
||||||
|
std::size_t effect_count);
|
||||||
void SortInfo();
|
void SortInfo();
|
||||||
bool TsortInfo(SplitterContext& splitter_context);
|
bool TsortInfo(SplitterContext& splitter_context);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue