|
|
|
@ -4,6 +4,7 @@ |
|
|
|
|
|
|
|
#include "audio_core/algorithm/interpolate.h"
|
|
|
|
#include "audio_core/command_generator.h"
|
|
|
|
#include "audio_core/effect_context.h"
|
|
|
|
#include "audio_core/mix_context.h"
|
|
|
|
#include "audio_core/voice_context.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, |
|
|
|
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), |
|
|
|
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) * |
|
|
|
worker_params.sample_count), |
|
|
|
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) { |
|
|
|
if (splitter_id == AudioCommon::NO_SPLITTER) { |
|
|
|
return nullptr; |
|
|
|
@ -345,6 +461,66 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter |
|
|
|
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, |
|
|
|
s32 channel, s32 node_id) { |
|
|
|
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(); |
|
|
|
GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, |
|
|
|
in_params.sample_rate); |
|
|
|
// TODO(ogniK): Effects
|
|
|
|
|
|
|
|
GenerateEffectCommand(mix_info); |
|
|
|
|
|
|
|
GenerateMixCommands(mix_info); |
|
|
|
} |
|
|
|
|
|
|
|
@ -476,7 +654,8 @@ void CommandGenerator::GenerateFinalMixCommand() { |
|
|
|
|
|
|
|
GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, |
|
|
|
in_params.sample_rate); |
|
|
|
// TODO(ogniK): Effects
|
|
|
|
|
|
|
|
GenerateEffectCommand(mix_info); |
|
|
|
|
|
|
|
for (s32 i = 0; i < in_params.buffer_count; i++) { |
|
|
|
const s32 gain = static_cast<s32>(in_params.volume * 32768.0f); |
|
|
|
|