| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977 |
- // Copyright 2020 yuzu Emulator Project
- // Licensed under GPLv2 or any later version
- // Refer to the license.txt file included.
- #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"
- namespace AudioCore {
- namespace {
- constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
- constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
- template <std::size_t N>
- void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
- for (std::size_t i = 0; i < static_cast<std::size_t>(sample_count); i += N) {
- for (std::size_t j = 0; j < N; j++) {
- output[i + j] +=
- static_cast<s32>((static_cast<s64>(input[i + j]) * gain + 0x4000) >> 15);
- }
- }
- }
- s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sample_count) {
- s32 x = 0;
- for (s32 i = 0; i < sample_count; i++) {
- x = static_cast<s32>(static_cast<float>(input[i]) * gain);
- output[i] += x;
- gain += delta;
- }
- return x;
- }
- void ApplyGain(s32* output, const s32* input, s32 gain, s32 delta, s32 sample_count) {
- for (s32 i = 0; i < sample_count; i++) {
- output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
- gain += delta;
- }
- }
- void ApplyGainWithoutDelta(s32* output, const s32* input, s32 gain, s32 sample_count) {
- for (s32 i = 0; i < sample_count; i++) {
- output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
- }
- }
- s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
- const bool positive = first_sample > 0;
- auto final_sample = std::abs(first_sample);
- for (s32 i = 0; i < sample_count; i++) {
- final_sample = static_cast<s32>((static_cast<s64>(final_sample) * delta) >> 15);
- if (positive) {
- output[i] += final_sample;
- } else {
- output[i] -= final_sample;
- }
- }
- if (positive) {
- return final_sample;
- } else {
- return -final_sample;
- }
- }
- } // namespace
- CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
- VoiceContext& voice_context, MixContext& mix_context,
- 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), 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),
- depop_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
- worker_params.sample_count) {}
- CommandGenerator::~CommandGenerator() = default;
- void CommandGenerator::ClearMixBuffers() {
- std::fill(mix_buffer.begin(), mix_buffer.end(), 0);
- std::fill(sample_buffer.begin(), sample_buffer.end(), 0);
- // std::fill(depop_buffer.begin(), depop_buffer.end(), 0);
- }
- void CommandGenerator::GenerateVoiceCommands() {
- if (dumping_frame) {
- LOG_DEBUG(Audio, "(DSP_TRACE) GenerateVoiceCommands");
- }
- // Grab all our voices
- const auto voice_count = voice_context.GetVoiceCount();
- for (std::size_t i = 0; i < voice_count; i++) {
- auto& voice_info = voice_context.GetSortedInfo(i);
- // Update voices and check if we should queue them
- if (voice_info.ShouldSkip() || !voice_info.UpdateForCommandGeneration(voice_context)) {
- continue;
- }
- // Queue our voice
- GenerateVoiceCommand(voice_info);
- }
- // Update our splitters
- splitter_context.UpdateInternalState();
- }
- void CommandGenerator::GenerateVoiceCommand(ServerVoiceInfo& voice_info) {
- auto& in_params = voice_info.GetInParams();
- const auto channel_count = in_params.channel_count;
- for (s32 channel = 0; channel < channel_count; channel++) {
- const auto resource_id = in_params.voice_channel_resource_id[channel];
- auto& dsp_state = voice_context.GetDspSharedState(resource_id);
- auto& channel_resource = voice_context.GetChannelResource(resource_id);
- // Decode our samples for our channel
- GenerateDataSourceCommand(voice_info, dsp_state, channel);
- if (in_params.should_depop) {
- in_params.last_volume = 0.0f;
- } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER ||
- in_params.mix_id != AudioCommon::NO_MIX) {
- // Apply a biquad filter if needed
- GenerateBiquadFilterCommandForVoice(voice_info, dsp_state,
- worker_params.mix_buffer_count, channel);
- // Base voice volume ramping
- GenerateVolumeRampCommand(in_params.last_volume, in_params.volume, channel,
- in_params.node_id);
- in_params.last_volume = in_params.volume;
- if (in_params.mix_id != AudioCommon::NO_MIX) {
- // If we're using a mix id
- auto& mix_info = mix_context.GetInfo(in_params.mix_id);
- const auto& dest_mix_params = mix_info.GetInParams();
- // Voice Mixing
- GenerateVoiceMixCommand(
- channel_resource.GetCurrentMixVolume(), channel_resource.GetLastMixVolume(),
- dsp_state, dest_mix_params.buffer_offset, dest_mix_params.buffer_count,
- worker_params.mix_buffer_count + channel, in_params.node_id);
- // Update last mix volumes
- channel_resource.UpdateLastMixVolumes();
- } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) {
- s32 base = channel;
- while (auto* destination_data =
- GetDestinationData(in_params.splitter_info_id, base)) {
- base += channel_count;
- if (!destination_data->IsConfigured()) {
- continue;
- }
- if (destination_data->GetMixId() >= static_cast<int>(mix_context.GetCount())) {
- continue;
- }
- const auto& mix_info = mix_context.GetInfo(destination_data->GetMixId());
- const auto& dest_mix_params = mix_info.GetInParams();
- GenerateVoiceMixCommand(
- destination_data->CurrentMixVolumes(), destination_data->LastMixVolumes(),
- dsp_state, dest_mix_params.buffer_offset, dest_mix_params.buffer_count,
- worker_params.mix_buffer_count + channel, in_params.node_id);
- destination_data->MarkDirty();
- }
- }
- // Update biquad filter enabled states
- for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
- in_params.was_biquad_filter_enabled[i] = in_params.biquad_filter[i].enabled;
- }
- }
- }
- }
- void CommandGenerator::GenerateSubMixCommands() {
- const auto mix_count = mix_context.GetCount();
- for (std::size_t i = 0; i < mix_count; i++) {
- auto& mix_info = mix_context.GetSortedInfo(i);
- const auto& in_params = mix_info.GetInParams();
- if (!in_params.in_use || in_params.mix_id == AudioCommon::FINAL_MIX) {
- continue;
- }
- GenerateSubMixCommand(mix_info);
- }
- }
- void CommandGenerator::GenerateFinalMixCommands() {
- GenerateFinalMixCommand();
- }
- void CommandGenerator::PreCommand() {
- if (!dumping_frame) {
- return;
- }
- for (std::size_t i = 0; i < splitter_context.GetInfoCount(); i++) {
- const auto& base = splitter_context.GetInfo(i);
- std::string graph = fmt::format("b[{}]", i);
- const auto* head = base.GetHead();
- while (head != nullptr) {
- graph += fmt::format("->{}", head->GetMixId());
- head = head->GetNextDestination();
- }
- LOG_DEBUG(Audio, "(DSP_TRACE) SplitterGraph splitter_info={}, {}", i, graph);
- }
- }
- void CommandGenerator::PostCommand() {
- if (!dumping_frame) {
- return;
- }
- dumping_frame = false;
- }
- void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
- s32 channel) {
- const auto& in_params = voice_info.GetInParams();
- const auto depop = in_params.should_depop;
- if (depop) {
- if (in_params.mix_id != AudioCommon::NO_MIX) {
- auto& mix_info = mix_context.GetInfo(in_params.mix_id);
- const auto& mix_in = mix_info.GetInParams();
- GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset);
- } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) {
- s32 index{};
- while (const auto* destination =
- GetDestinationData(in_params.splitter_info_id, index++)) {
- if (!destination->IsConfigured()) {
- continue;
- }
- auto& mix_info = mix_context.GetInfo(destination->GetMixId());
- const auto& mix_in = mix_info.GetInParams();
- GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset);
- }
- }
- } else {
- switch (in_params.sample_format) {
- case SampleFormat::Pcm16:
- DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(channel), dsp_state, channel,
- worker_params.sample_rate, worker_params.sample_count,
- in_params.node_id);
- break;
- case SampleFormat::Adpcm:
- ASSERT(channel == 0 && in_params.channel_count == 1);
- DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(0), dsp_state, 0,
- worker_params.sample_rate, worker_params.sample_count,
- in_params.node_id);
- break;
- default:
- UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
- }
- }
- }
- void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info,
- VoiceState& dsp_state,
- s32 mix_buffer_count, s32 channel) {
- for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
- const auto& in_params = voice_info.GetInParams();
- auto& biquad_filter = in_params.biquad_filter[i];
- // Check if biquad filter is actually used
- if (!biquad_filter.enabled) {
- continue;
- }
- // Reinitialize our biquad filter state if it was enabled previously
- if (!in_params.was_biquad_filter_enabled[i]) {
- dsp_state.biquad_filter_state.fill(0);
- }
- // Generate biquad filter
- // GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
- // dsp_state.biquad_filter_state,
- // mix_buffer_count + channel, mix_buffer_count +
- // channel, worker_params.sample_count,
- // voice_info.GetInParams().node_id);
- }
- }
- void AudioCore::CommandGenerator::GenerateBiquadFilterCommand(
- s32 mix_buffer, const BiquadFilterParameter& params, std::array<s64, 2>& state,
- std::size_t input_offset, std::size_t output_offset, s32 sample_count, s32 node_id) {
- if (dumping_frame) {
- LOG_DEBUG(Audio,
- "(DSP_TRACE) GenerateBiquadFilterCommand node_id={}, "
- "input_mix_buffer={}, output_mix_buffer={}",
- node_id, input_offset, output_offset);
- }
- const auto* input = GetMixBuffer(input_offset);
- auto* output = GetMixBuffer(output_offset);
- // Biquad filter parameters
- const auto [n0, n1, n2] = params.numerator;
- const auto [d0, d1] = params.denominator;
- // Biquad filter states
- auto [s0, s1] = state;
- constexpr s64 int32_min = std::numeric_limits<s32>::min();
- constexpr s64 int32_max = std::numeric_limits<s32>::max();
- for (int i = 0; i < sample_count; ++i) {
- const auto sample = static_cast<s64>(input[i]);
- const auto f = (sample * n0 + s0 + 0x4000) >> 15;
- const auto y = std::clamp(f, int32_min, int32_max);
- s0 = sample * n1 + y * d0 + s1;
- s1 = sample * n2 + y * d1;
- output[i] = static_cast<s32>(y);
- }
- state = {s0, s1};
- }
- void CommandGenerator::GenerateDepopPrepareCommand(VoiceState& dsp_state,
- std::size_t mix_buffer_count,
- std::size_t mix_buffer_offset) {
- for (std::size_t i = 0; i < mix_buffer_count; i++) {
- auto& sample = dsp_state.previous_samples[i];
- if (sample != 0) {
- depop_buffer[mix_buffer_offset + i] += sample;
- sample = 0;
- }
- }
- }
- void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
- std::size_t mix_buffer_offset,
- s32 sample_rate) {
- const std::size_t end_offset =
- std::min(mix_buffer_offset + mix_buffer_count, GetTotalMixBufferCount());
- const s32 delta = sample_rate == 48000 ? 0x7B29 : 0x78CB;
- for (std::size_t i = mix_buffer_offset; i < end_offset; i++) {
- if (depop_buffer[i] == 0) {
- continue;
- }
- depop_buffer[i] =
- ApplyMixDepop(GetMixBuffer(i), depop_buffer[i], delta, worker_params.sample_count);
- }
- }
- 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 != static_cast<int>(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;
- }
- 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);
- const auto current = static_cast<s32>(current_volume * 32768.0f);
- const auto delta = static_cast<s32>((static_cast<float>(current) - static_cast<float>(last)) /
- static_cast<float>(worker_params.sample_count));
- if (dumping_frame) {
- LOG_DEBUG(Audio,
- "(DSP_TRACE) GenerateVolumeRampCommand node_id={}, input={}, output={}, "
- "last_volume={}, current_volume={}",
- node_id, GetMixChannelBufferOffset(channel), GetMixChannelBufferOffset(channel),
- last_volume, current_volume);
- }
- // Apply generic gain on samples
- ApplyGain(GetChannelMixBuffer(channel), GetChannelMixBuffer(channel), last, delta,
- worker_params.sample_count);
- }
- void CommandGenerator::GenerateVoiceMixCommand(const MixVolumeBuffer& mix_volumes,
- const MixVolumeBuffer& last_mix_volumes,
- VoiceState& dsp_state, s32 mix_buffer_offset,
- s32 mix_buffer_count, s32 voice_index, s32 node_id) {
- // Loop all our mix buffers
- for (s32 i = 0; i < mix_buffer_count; i++) {
- if (last_mix_volumes[i] != 0.0f || mix_volumes[i] != 0.0f) {
- const auto delta = static_cast<float>((mix_volumes[i] - last_mix_volumes[i])) /
- static_cast<float>(worker_params.sample_count);
- if (dumping_frame) {
- LOG_DEBUG(Audio,
- "(DSP_TRACE) GenerateVoiceMixCommand node_id={}, input={}, "
- "output={}, last_volume={}, current_volume={}",
- node_id, voice_index, mix_buffer_offset + i, last_mix_volumes[i],
- mix_volumes[i]);
- }
- dsp_state.previous_samples[i] =
- ApplyMixRamp(GetMixBuffer(mix_buffer_offset + i), GetMixBuffer(voice_index),
- last_mix_volumes[i], delta, worker_params.sample_count);
- } else {
- dsp_state.previous_samples[i] = 0;
- }
- }
- }
- void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) {
- if (dumping_frame) {
- LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand");
- }
- const auto& in_params = mix_info.GetInParams();
- GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
- in_params.sample_rate);
- GenerateEffectCommand(mix_info);
- GenerateMixCommands(mix_info);
- }
- void CommandGenerator::GenerateMixCommands(ServerMixInfo& mix_info) {
- if (!mix_info.HasAnyConnection()) {
- return;
- }
- const auto& in_params = mix_info.GetInParams();
- if (in_params.dest_mix_id != AudioCommon::NO_MIX) {
- const auto& dest_mix = mix_context.GetInfo(in_params.dest_mix_id);
- const auto& dest_in_params = dest_mix.GetInParams();
- const auto buffer_count = in_params.buffer_count;
- for (s32 i = 0; i < buffer_count; i++) {
- for (s32 j = 0; j < dest_in_params.buffer_count; j++) {
- const auto mixed_volume = in_params.volume * in_params.mix_volume[i][j];
- if (mixed_volume != 0.0f) {
- GenerateMixCommand(dest_in_params.buffer_offset + j,
- in_params.buffer_offset + i, mixed_volume,
- in_params.node_id);
- }
- }
- }
- } else if (in_params.splitter_id != AudioCommon::NO_SPLITTER) {
- s32 base{};
- while (const auto* destination_data = GetDestinationData(in_params.splitter_id, base++)) {
- if (!destination_data->IsConfigured()) {
- continue;
- }
- const auto& dest_mix = mix_context.GetInfo(destination_data->GetMixId());
- const auto& dest_in_params = dest_mix.GetInParams();
- const auto mix_index = (base - 1) % in_params.buffer_count + in_params.buffer_offset;
- for (std::size_t i = 0; i < static_cast<std::size_t>(dest_in_params.buffer_count);
- i++) {
- const auto mixed_volume = in_params.volume * destination_data->GetMixVolume(i);
- if (mixed_volume != 0.0f) {
- GenerateMixCommand(dest_in_params.buffer_offset + i, mix_index, mixed_volume,
- in_params.node_id);
- }
- }
- }
- }
- }
- void CommandGenerator::GenerateMixCommand(std::size_t output_offset, std::size_t input_offset,
- float volume, s32 node_id) {
- if (dumping_frame) {
- LOG_DEBUG(Audio,
- "(DSP_TRACE) GenerateMixCommand node_id={}, input={}, output={}, volume={}",
- node_id, input_offset, output_offset, volume);
- }
- auto* output = GetMixBuffer(output_offset);
- const auto* input = GetMixBuffer(input_offset);
- const s32 gain = static_cast<s32>(volume * 32768.0f);
- // Mix with loop unrolling
- if (worker_params.sample_count % 4 == 0) {
- ApplyMix<4>(output, input, gain, worker_params.sample_count);
- } else if (worker_params.sample_count % 2 == 0) {
- ApplyMix<2>(output, input, gain, worker_params.sample_count);
- } else {
- ApplyMix<1>(output, input, gain, worker_params.sample_count);
- }
- }
- void CommandGenerator::GenerateFinalMixCommand() {
- if (dumping_frame) {
- LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand");
- }
- auto& mix_info = mix_context.GetFinalMixInfo();
- const auto& in_params = mix_info.GetInParams();
- GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
- in_params.sample_rate);
- GenerateEffectCommand(mix_info);
- for (s32 i = 0; i < in_params.buffer_count; i++) {
- const s32 gain = static_cast<s32>(in_params.volume * 32768.0f);
- if (dumping_frame) {
- LOG_DEBUG(
- Audio,
- "(DSP_TRACE) ApplyGainWithoutDelta node_id={}, input={}, output={}, volume={}",
- in_params.node_id, in_params.buffer_offset + i, in_params.buffer_offset + i,
- in_params.volume);
- }
- ApplyGainWithoutDelta(GetMixBuffer(in_params.buffer_offset + i),
- GetMixBuffer(in_params.buffer_offset + i), gain,
- worker_params.sample_count);
- }
- }
- s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
- s32 sample_count, s32 channel, std::size_t mix_offset) {
- const auto& in_params = voice_info.GetInParams();
- const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
- if (wave_buffer.buffer_address == 0) {
- return 0;
- }
- if (wave_buffer.buffer_size == 0) {
- return 0;
- }
- if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
- return 0;
- }
- const auto samples_remaining =
- (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
- const auto start_offset =
- ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) *
- sizeof(s16);
- const auto buffer_pos = wave_buffer.buffer_address + start_offset;
- const auto samples_processed = std::min(sample_count, samples_remaining);
- if (in_params.channel_count == 1) {
- std::vector<s16> buffer(samples_processed);
- memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
- for (std::size_t i = 0; i < buffer.size(); i++) {
- sample_buffer[mix_offset + i] = buffer[i];
- }
- } else {
- const auto channel_count = in_params.channel_count;
- std::vector<s16> buffer(samples_processed * channel_count);
- memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
- for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) {
- sample_buffer[mix_offset + i] = buffer[i * channel_count + channel];
- }
- }
- return samples_processed;
- }
- s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
- s32 sample_count, s32 channel, std::size_t mix_offset) {
- const auto& in_params = voice_info.GetInParams();
- const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
- if (wave_buffer.buffer_address == 0) {
- return 0;
- }
- if (wave_buffer.buffer_size == 0) {
- return 0;
- }
- if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
- return 0;
- }
- constexpr std::array<int, 16> SIGNED_NIBBLES = {
- {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
- constexpr std::size_t FRAME_LEN = 8;
- constexpr std::size_t NIBBLES_PER_SAMPLE = 16;
- constexpr std::size_t SAMPLES_PER_FRAME = 14;
- auto frame_header = dsp_state.context.header;
- s32 idx = (frame_header >> 4) & 0xf;
- s32 scale = frame_header & 0xf;
- s16 yn1 = dsp_state.context.yn1;
- s16 yn2 = dsp_state.context.yn2;
- Codec::ADPCM_Coeff coeffs;
- memory.ReadBlock(in_params.additional_params_address, coeffs.data(),
- sizeof(Codec::ADPCM_Coeff));
- s32 coef1 = coeffs[idx * 2];
- s32 coef2 = coeffs[idx * 2 + 1];
- const auto samples_remaining =
- (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
- const auto samples_processed = std::min(sample_count, samples_remaining);
- const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset;
- const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME;
- auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) +
- samples_remaining_in_frame + (samples_remaining_in_frame != 0 ? 2 : 0);
- const auto decode_sample = [&](const int nibble) -> s16 {
- const int xn = nibble * (1 << scale);
- // We first transform everything into 11 bit fixed point, perform the second order
- // digital filter, then transform back.
- // 0x400 == 0.5 in 11 bit fixed point.
- // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
- int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
- // Clamp to output range.
- val = std::clamp<s32>(val, -32768, 32767);
- // Advance output feedback.
- yn2 = yn1;
- yn1 = static_cast<s16>(val);
- return yn1;
- };
- std::size_t buffer_offset{};
- std::vector<u8> buffer(
- std::max((samples_processed / FRAME_LEN) * SAMPLES_PER_FRAME, FRAME_LEN));
- memory.ReadBlock(wave_buffer.buffer_address + (position_in_frame / 2), buffer.data(),
- buffer.size());
- std::size_t cur_mix_offset = mix_offset;
- auto remaining_samples = samples_processed;
- while (remaining_samples > 0) {
- if (position_in_frame % NIBBLES_PER_SAMPLE == 0) {
- // Read header
- frame_header = buffer[buffer_offset++];
- idx = (frame_header >> 4) & 0xf;
- scale = frame_header & 0xf;
- coef1 = coeffs[idx * 2];
- coef2 = coeffs[idx * 2 + 1];
- position_in_frame += 2;
- // Decode entire frame
- if (remaining_samples >= static_cast<int>(SAMPLES_PER_FRAME)) {
- for (std::size_t i = 0; i < SAMPLES_PER_FRAME / 2; i++) {
- // Sample 1
- const s32 s0 = SIGNED_NIBBLES[buffer[buffer_offset] >> 4];
- const s32 s1 = SIGNED_NIBBLES[buffer[buffer_offset++] & 0xf];
- const s16 sample_1 = decode_sample(s0);
- const s16 sample_2 = decode_sample(s1);
- sample_buffer[cur_mix_offset++] = sample_1;
- sample_buffer[cur_mix_offset++] = sample_2;
- }
- remaining_samples -= SAMPLES_PER_FRAME;
- position_in_frame += SAMPLES_PER_FRAME;
- continue;
- }
- }
- // Decode mid frame
- s32 current_nibble = buffer[buffer_offset];
- if (position_in_frame++ & 0x1) {
- current_nibble &= 0xf;
- buffer_offset++;
- } else {
- current_nibble >>= 4;
- }
- const s16 sample = decode_sample(SIGNED_NIBBLES[current_nibble]);
- sample_buffer[cur_mix_offset++] = sample;
- remaining_samples--;
- }
- dsp_state.context.header = frame_header;
- dsp_state.context.yn1 = yn1;
- dsp_state.context.yn2 = yn2;
- return samples_processed;
- }
- s32* CommandGenerator::GetMixBuffer(std::size_t index) {
- return mix_buffer.data() + (index * worker_params.sample_count);
- }
- const s32* CommandGenerator::GetMixBuffer(std::size_t index) const {
- return mix_buffer.data() + (index * worker_params.sample_count);
- }
- std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const {
- return worker_params.mix_buffer_count + channel;
- }
- std::size_t CommandGenerator::GetTotalMixBufferCount() const {
- return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT;
- }
- s32* CommandGenerator::GetChannelMixBuffer(s32 channel) {
- return GetMixBuffer(worker_params.mix_buffer_count + channel);
- }
- const s32* CommandGenerator::GetChannelMixBuffer(s32 channel) const {
- return GetMixBuffer(worker_params.mix_buffer_count + channel);
- }
- void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output,
- VoiceState& dsp_state, s32 channel,
- s32 target_sample_rate, s32 sample_count,
- s32 node_id) {
- const auto& in_params = voice_info.GetInParams();
- if (dumping_frame) {
- LOG_DEBUG(Audio,
- "(DSP_TRACE) DecodeFromWaveBuffers, node_id={}, channel={}, "
- "format={}, sample_count={}, sample_rate={}, mix_id={}, splitter_id={}",
- node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate,
- in_params.mix_id, in_params.splitter_info_id);
- }
- ASSERT_OR_EXECUTE(output != nullptr, { return; });
- const auto resample_rate = static_cast<s32>(
- static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
- static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
- if (dsp_state.fraction + sample_count * resample_rate >
- static_cast<s32>(SCALED_MIX_BUFFER_SIZE - 4ULL)) {
- return;
- }
- auto min_required_samples =
- std::min(static_cast<s32>(SCALED_MIX_BUFFER_SIZE) - dsp_state.fraction, resample_rate);
- if (min_required_samples >= sample_count) {
- min_required_samples = sample_count;
- }
- std::size_t temp_mix_offset{};
- bool is_buffer_completed{false};
- auto samples_remaining = sample_count;
- while (samples_remaining > 0 && !is_buffer_completed) {
- const auto samples_to_output = std::min(samples_remaining, min_required_samples);
- const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15;
- if (!in_params.behavior_flags.is_pitch_and_src_skipped) {
- // Append sample histtory for resampler
- for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
- sample_buffer[temp_mix_offset + i] = dsp_state.sample_history[i];
- }
- temp_mix_offset += 4;
- }
- s32 samples_read{};
- while (samples_read < samples_to_read) {
- const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
- // No more data can be read
- if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) {
- is_buffer_completed = true;
- break;
- }
- if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 &&
- wave_buffer.context_address != 0 && wave_buffer.context_size != 0) {
- // TODO(ogniK): ADPCM loop context
- }
- s32 samples_decoded{0};
- switch (in_params.sample_format) {
- case SampleFormat::Pcm16:
- samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read,
- channel, temp_mix_offset);
- break;
- case SampleFormat::Adpcm:
- samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read,
- channel, temp_mix_offset);
- break;
- default:
- UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
- }
- temp_mix_offset += samples_decoded;
- samples_read += samples_decoded;
- dsp_state.offset += samples_decoded;
- dsp_state.played_sample_count += samples_decoded;
- if (dsp_state.offset >=
- (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) ||
- samples_decoded == 0) {
- // Reset our sample offset
- dsp_state.offset = 0;
- if (wave_buffer.is_looping) {
- if (samples_decoded == 0) {
- // End of our buffer
- is_buffer_completed = true;
- break;
- }
- if (in_params.behavior_flags.is_played_samples_reset_at_loop_point.Value()) {
- dsp_state.played_sample_count = 0;
- }
- } else {
- // Update our wave buffer states
- dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false;
- dsp_state.wave_buffer_consumed++;
- dsp_state.wave_buffer_index =
- (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
- if (wave_buffer.end_of_stream) {
- dsp_state.played_sample_count = 0;
- }
- }
- }
- }
- if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) {
- // No need to resample
- std::memcpy(output, sample_buffer.data(), samples_read * sizeof(s32));
- } else {
- std::fill(sample_buffer.begin() + temp_mix_offset,
- sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read),
- 0);
- AudioCore::Resample(output, sample_buffer.data(), resample_rate, dsp_state.fraction,
- samples_to_output);
- // Resample
- for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
- dsp_state.sample_history[i] = sample_buffer[samples_to_read + i];
- }
- }
- output += samples_to_output;
- samples_remaining -= samples_to_output;
- }
- }
- } // namespace AudioCore
|