summaryrefslogtreecommitdiffstats
path: root/src/audio_core/renderer/command/command_buffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core/renderer/command/command_buffer.cpp')
-rw-r--r--src/audio_core/renderer/command/command_buffer.cpp714
1 files changed, 714 insertions, 0 deletions
diff --git a/src/audio_core/renderer/command/command_buffer.cpp b/src/audio_core/renderer/command/command_buffer.cpp
new file mode 100644
index 000000000..40074cf14
--- /dev/null
+++ b/src/audio_core/renderer/command/command_buffer.cpp
@@ -0,0 +1,714 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "audio_core/renderer/behavior/behavior_info.h"
+#include "audio_core/renderer/command/command_buffer.h"
+#include "audio_core/renderer/command/command_list_header.h"
+#include "audio_core/renderer/command/command_processing_time_estimator.h"
+#include "audio_core/renderer/effect/biquad_filter.h"
+#include "audio_core/renderer/effect/delay.h"
+#include "audio_core/renderer/effect/reverb.h"
+#include "audio_core/renderer/memory/memory_pool_info.h"
+#include "audio_core/renderer/mix/mix_info.h"
+#include "audio_core/renderer/sink/circular_buffer_sink_info.h"
+#include "audio_core/renderer/sink/device_sink_info.h"
+#include "audio_core/renderer/sink/sink_info_base.h"
+#include "audio_core/renderer/voice/voice_info.h"
+#include "audio_core/renderer/voice/voice_state.h"
+
+namespace AudioCore::AudioRenderer {
+
+template <typename T, CommandId Id>
+T& CommandBuffer::GenerateStart(const s32 node_id) {
+ if (size + sizeof(T) >= command_list.size_bytes()) {
+ LOG_ERROR(
+ Service_Audio,
+ "Attempting to write commands beyond the end of allocated command buffer memory!");
+ UNREACHABLE();
+ }
+
+ auto& cmd{*std::construct_at<T>(reinterpret_cast<T*>(&command_list[size]))};
+
+ cmd.magic = CommandMagic;
+ cmd.enabled = true;
+ cmd.type = Id;
+ cmd.size = sizeof(T);
+ cmd.node_id = node_id;
+
+ return cmd;
+}
+
+template <typename T>
+void CommandBuffer::GenerateEnd(T& cmd) {
+ cmd.estimated_process_time = time_estimator->Estimate(cmd);
+ estimated_process_time += cmd.estimated_process_time;
+ size += sizeof(T);
+ count++;
+}
+
+void CommandBuffer::GeneratePcmInt16Version1Command(const s32 node_id,
+ const MemoryPoolInfo& memory_pool_,
+ VoiceInfo& voice_info,
+ const VoiceState& voice_state,
+ const s16 buffer_count, const s8 channel) {
+ auto& cmd{
+ GenerateStart<PcmInt16DataSourceVersion1Command, CommandId::DataSourcePcmInt16Version1>(
+ node_id)};
+
+ cmd.src_quality = voice_info.src_quality;
+ cmd.output_index = buffer_count + channel;
+ cmd.flags = voice_info.flags & 3;
+ cmd.sample_rate = voice_info.sample_rate;
+ cmd.pitch = voice_info.pitch;
+ cmd.channel_index = channel;
+ cmd.channel_count = voice_info.channel_count;
+
+ for (u32 i = 0; i < MaxWaveBuffers; i++) {
+ voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
+ }
+
+ cmd.voice_state = memory_pool_.Translate(CpuAddr(&voice_state), sizeof(VoiceState));
+
+ GenerateEnd<PcmInt16DataSourceVersion1Command>(cmd);
+}
+
+void CommandBuffer::GeneratePcmInt16Version2Command(const s32 node_id, VoiceInfo& voice_info,
+ const VoiceState& voice_state,
+ const s16 buffer_count, const s8 channel) {
+ auto& cmd{
+ GenerateStart<PcmInt16DataSourceVersion2Command, CommandId::DataSourcePcmInt16Version2>(
+ node_id)};
+
+ cmd.src_quality = voice_info.src_quality;
+ cmd.output_index = buffer_count + channel;
+ cmd.flags = voice_info.flags & 3;
+ cmd.sample_rate = voice_info.sample_rate;
+ cmd.pitch = voice_info.pitch;
+ cmd.channel_index = channel;
+ cmd.channel_count = voice_info.channel_count;
+
+ for (u32 i = 0; i < MaxWaveBuffers; i++) {
+ voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
+ }
+
+ cmd.voice_state = memory_pool->Translate(CpuAddr(&voice_state), sizeof(VoiceState));
+
+ GenerateEnd<PcmInt16DataSourceVersion2Command>(cmd);
+}
+
+void CommandBuffer::GeneratePcmFloatVersion1Command(const s32 node_id,
+ const MemoryPoolInfo& memory_pool_,
+ VoiceInfo& voice_info,
+ const VoiceState& voice_state,
+ const s16 buffer_count, const s8 channel) {
+ auto& cmd{
+ GenerateStart<PcmFloatDataSourceVersion1Command, CommandId::DataSourcePcmFloatVersion1>(
+ node_id)};
+
+ cmd.src_quality = voice_info.src_quality;
+ cmd.output_index = buffer_count + channel;
+ cmd.flags = voice_info.flags & 3;
+ cmd.sample_rate = voice_info.sample_rate;
+ cmd.pitch = voice_info.pitch;
+ cmd.channel_index = channel;
+ cmd.channel_count = voice_info.channel_count;
+
+ for (u32 i = 0; i < MaxWaveBuffers; i++) {
+ voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
+ }
+
+ cmd.voice_state = memory_pool_.Translate(CpuAddr(&voice_state), sizeof(VoiceState));
+
+ GenerateEnd<PcmFloatDataSourceVersion1Command>(cmd);
+}
+
+void CommandBuffer::GeneratePcmFloatVersion2Command(const s32 node_id, VoiceInfo& voice_info,
+ const VoiceState& voice_state,
+ const s16 buffer_count, const s8 channel) {
+ auto& cmd{
+ GenerateStart<PcmFloatDataSourceVersion2Command, CommandId::DataSourcePcmFloatVersion2>(
+ node_id)};
+
+ cmd.src_quality = voice_info.src_quality;
+ cmd.output_index = buffer_count + channel;
+ cmd.flags = voice_info.flags & 3;
+ cmd.sample_rate = voice_info.sample_rate;
+ cmd.pitch = voice_info.pitch;
+ cmd.channel_index = channel;
+ cmd.channel_count = voice_info.channel_count;
+
+ for (u32 i = 0; i < MaxWaveBuffers; i++) {
+ voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
+ }
+
+ cmd.voice_state = memory_pool->Translate(CpuAddr(&voice_state), sizeof(VoiceState));
+
+ GenerateEnd<PcmFloatDataSourceVersion2Command>(cmd);
+}
+
+void CommandBuffer::GenerateAdpcmVersion1Command(const s32 node_id,
+ const MemoryPoolInfo& memory_pool_,
+ VoiceInfo& voice_info,
+ const VoiceState& voice_state,
+ const s16 buffer_count, const s8 channel) {
+ auto& cmd{
+ GenerateStart<AdpcmDataSourceVersion1Command, CommandId::DataSourceAdpcmVersion1>(node_id)};
+
+ cmd.src_quality = voice_info.src_quality;
+ cmd.output_index = buffer_count + channel;
+ cmd.flags = voice_info.flags & 3;
+ cmd.sample_rate = voice_info.sample_rate;
+ cmd.pitch = voice_info.pitch;
+
+ for (u32 i = 0; i < MaxWaveBuffers; i++) {
+ voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
+ }
+
+ cmd.voice_state = memory_pool_.Translate(CpuAddr(&voice_state), sizeof(VoiceState));
+ cmd.data_address = voice_info.data_address.GetReference(true);
+ cmd.data_size = voice_info.data_address.GetSize();
+
+ GenerateEnd<AdpcmDataSourceVersion1Command>(cmd);
+}
+
+void CommandBuffer::GenerateAdpcmVersion2Command(const s32 node_id, VoiceInfo& voice_info,
+ const VoiceState& voice_state,
+ const s16 buffer_count, const s8 channel) {
+ auto& cmd{
+ GenerateStart<AdpcmDataSourceVersion2Command, CommandId::DataSourceAdpcmVersion2>(node_id)};
+
+ cmd.src_quality = voice_info.src_quality;
+ cmd.output_index = buffer_count + channel;
+ cmd.flags = voice_info.flags & 3;
+ cmd.sample_rate = voice_info.sample_rate;
+ cmd.pitch = voice_info.pitch;
+ cmd.channel_index = channel;
+ cmd.channel_count = voice_info.channel_count;
+
+ for (u32 i = 0; i < MaxWaveBuffers; i++) {
+ voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
+ }
+
+ cmd.voice_state = memory_pool->Translate(CpuAddr(&voice_state), sizeof(VoiceState));
+ cmd.data_address = voice_info.data_address.GetReference(true);
+ cmd.data_size = voice_info.data_address.GetSize();
+
+ GenerateEnd<AdpcmDataSourceVersion2Command>(cmd);
+}
+
+void CommandBuffer::GenerateVolumeCommand(const s32 node_id, const s16 buffer_offset,
+ const s16 input_index, const f32 volume,
+ const u8 precision) {
+ auto& cmd{GenerateStart<VolumeCommand, CommandId::Volume>(node_id)};
+
+ cmd.precision = precision;
+ cmd.input_index = buffer_offset + input_index;
+ cmd.output_index = buffer_offset + input_index;
+ cmd.volume = volume;
+
+ GenerateEnd<VolumeCommand>(cmd);
+}
+
+void CommandBuffer::GenerateVolumeRampCommand(const s32 node_id, VoiceInfo& voice_info,
+ const s16 buffer_count, const u8 precision) {
+ auto& cmd{GenerateStart<VolumeRampCommand, CommandId::VolumeRamp>(node_id)};
+
+ cmd.input_index = buffer_count;
+ cmd.output_index = buffer_count;
+ cmd.prev_volume = voice_info.prev_volume;
+ cmd.volume = voice_info.volume;
+ cmd.precision = precision;
+
+ GenerateEnd<VolumeRampCommand>(cmd);
+}
+
+void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, VoiceInfo& voice_info,
+ const VoiceState& voice_state,
+ const s16 buffer_count, const s8 channel,
+ const u32 biquad_index,
+ const bool use_float_processing) {
+ auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
+
+ cmd.input = buffer_count + channel;
+ cmd.output = buffer_count + channel;
+
+ cmd.biquad = voice_info.biquads[biquad_index];
+
+ cmd.state = memory_pool->Translate(CpuAddr(voice_state.biquad_states[biquad_index].data()),
+ MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
+
+ cmd.needs_init = !voice_info.biquad_initialized[biquad_index];
+ cmd.use_float_processing = use_float_processing;
+
+ GenerateEnd<BiquadFilterCommand>(cmd);
+}
+
+void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBase& effect_info,
+ const s16 buffer_offset, const s8 channel,
+ const bool needs_init,
+ const bool use_float_processing) {
+ auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
+
+ const auto& parameter{
+ *reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
+ const auto state{
+ reinterpret_cast<VoiceState::BiquadFilterState*>(effect_info.GetStateBuffer())};
+
+ cmd.input = buffer_offset + parameter.inputs[channel];
+ cmd.output = buffer_offset + parameter.outputs[channel];
+
+ cmd.biquad.b = parameter.b;
+ cmd.biquad.a = parameter.a;
+
+ cmd.state = memory_pool->Translate(CpuAddr(state),
+ MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
+
+ cmd.needs_init = needs_init;
+ cmd.use_float_processing = use_float_processing;
+
+ GenerateEnd<BiquadFilterCommand>(cmd);
+}
+
+void CommandBuffer::GenerateMixCommand(const s32 node_id, const s16 input_index,
+ const s16 output_index, const s16 buffer_offset,
+ const f32 volume, const u8 precision) {
+ auto& cmd{GenerateStart<MixCommand, CommandId::Mix>(node_id)};
+
+ cmd.input_index = input_index;
+ cmd.output_index = output_index;
+ cmd.volume = volume;
+ cmd.precision = precision;
+
+ GenerateEnd<MixCommand>(cmd);
+}
+
+void CommandBuffer::GenerateMixRampCommand(const s32 node_id,
+ [[maybe_unused]] const s16 buffer_count,
+ const s16 input_index, const s16 output_index,
+ const f32 volume, const f32 prev_volume,
+ const CpuAddr prev_samples, const u8 precision) {
+ if (volume == 0.0f && prev_volume == 0.0f) {
+ return;
+ }
+
+ auto& cmd{GenerateStart<MixRampCommand, CommandId::MixRamp>(node_id)};
+
+ cmd.input_index = input_index;
+ cmd.output_index = output_index;
+ cmd.prev_volume = prev_volume;
+ cmd.volume = volume;
+ cmd.previous_sample = prev_samples;
+ cmd.precision = precision;
+
+ GenerateEnd<MixRampCommand>(cmd);
+}
+
+void CommandBuffer::GenerateMixRampGroupedCommand(const s32 node_id, const s16 buffer_count,
+ const s16 input_index, s16 output_index,
+ std::span<const f32> volumes,
+ std::span<const f32> prev_volumes,
+ const CpuAddr prev_samples, const u8 precision) {
+ auto& cmd{GenerateStart<MixRampGroupedCommand, CommandId::MixRampGrouped>(node_id)};
+
+ cmd.buffer_count = buffer_count;
+
+ for (s32 i = 0; i < buffer_count; i++) {
+ cmd.inputs[i] = input_index;
+ cmd.outputs[i] = output_index++;
+ cmd.prev_volumes[i] = prev_volumes[i];
+ cmd.volumes[i] = volumes[i];
+ }
+
+ cmd.previous_samples = prev_samples;
+ cmd.precision = precision;
+
+ GenerateEnd<MixRampGroupedCommand>(cmd);
+}
+
+void CommandBuffer::GenerateDepopPrepareCommand(const s32 node_id, const VoiceState& voice_state,
+ std::span<const s32> buffer, const s16 buffer_count,
+ s16 buffer_offset, const bool was_playing) {
+ auto& cmd{GenerateStart<DepopPrepareCommand, CommandId::DepopPrepare>(node_id)};
+
+ cmd.enabled = was_playing;
+
+ for (u32 i = 0; i < MaxMixBuffers; i++) {
+ cmd.inputs[i] = buffer_offset++;
+ }
+
+ cmd.previous_samples = memory_pool->Translate(CpuAddr(voice_state.previous_samples.data()),
+ MaxMixBuffers * sizeof(s32));
+ cmd.buffer_count = buffer_count;
+ cmd.depop_buffer = memory_pool->Translate(CpuAddr(buffer.data()), buffer_count * sizeof(s32));
+
+ GenerateEnd<DepopPrepareCommand>(cmd);
+}
+
+void CommandBuffer::GenerateDepopForMixBuffersCommand(const s32 node_id, const MixInfo& mix_info,
+ std::span<const s32> depop_buffer) {
+ auto& cmd{GenerateStart<DepopForMixBuffersCommand, CommandId::DepopForMixBuffers>(node_id)};
+
+ cmd.input = mix_info.buffer_offset;
+ cmd.count = mix_info.buffer_count;
+ cmd.decay = mix_info.sample_rate == TargetSampleRate ? 0.96218872f : 0.94369507f;
+ cmd.depop_buffer =
+ memory_pool->Translate(CpuAddr(depop_buffer.data()), mix_info.buffer_count * sizeof(s32));
+
+ GenerateEnd<DepopForMixBuffersCommand>(cmd);
+}
+
+void CommandBuffer::GenerateDelayCommand(const s32 node_id, EffectInfoBase& effect_info,
+ const s16 buffer_offset) {
+ auto& cmd{GenerateStart<DelayCommand, CommandId::Delay>(node_id)};
+
+ const auto& parameter{
+ *reinterpret_cast<DelayInfo::ParameterVersion1*>(effect_info.GetParameter())};
+ const auto state{effect_info.GetStateBuffer()};
+
+ if (IsChannelCountValid(parameter.channel_count)) {
+ const auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(DelayInfo::State))};
+ if (state_buffer) {
+ for (s16 channel = 0; channel < parameter.channel_count; channel++) {
+ cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
+ cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
+ }
+
+ if (!behavior->IsDelayChannelMappingChanged() && parameter.channel_count == 6) {
+ UseOldChannelMapping(cmd.inputs, cmd.outputs);
+ }
+
+ cmd.parameter = parameter;
+ cmd.effect_enabled = effect_info.IsEnabled();
+ cmd.state = state_buffer;
+ cmd.workbuffer = effect_info.GetWorkbuffer(-1);
+ }
+ }
+
+ GenerateEnd<DelayCommand>(cmd);
+}
+
+void CommandBuffer::GenerateUpsampleCommand(const s32 node_id, const s16 buffer_offset,
+ UpsamplerInfo& upsampler_info, const u32 input_count,
+ std::span<const s8> inputs, const s16 buffer_count,
+ const u32 sample_count_, const u32 sample_rate_) {
+ auto& cmd{GenerateStart<UpsampleCommand, CommandId::Upsample>(node_id)};
+
+ cmd.samples_buffer = memory_pool->Translate(upsampler_info.samples_pos,
+ upsampler_info.sample_count * sizeof(s32));
+ cmd.inputs = memory_pool->Translate(CpuAddr(upsampler_info.inputs.data()), MaxChannels);
+ cmd.buffer_count = buffer_count;
+ cmd.unk_20 = 0;
+ cmd.source_sample_count = sample_count_;
+ cmd.source_sample_rate = sample_rate_;
+
+ upsampler_info.input_count = input_count;
+ for (u32 i = 0; i < input_count; i++) {
+ upsampler_info.inputs[i] = buffer_offset + inputs[i];
+ }
+
+ cmd.upsampler_info = memory_pool->Translate(CpuAddr(&upsampler_info), sizeof(UpsamplerInfo));
+
+ GenerateEnd<UpsampleCommand>(cmd);
+}
+
+void CommandBuffer::GenerateDownMix6chTo2chCommand(const s32 node_id, std::span<const s8> inputs,
+ const s16 buffer_offset,
+ std::span<const f32> downmix_coeff) {
+ auto& cmd{GenerateStart<DownMix6chTo2chCommand, CommandId::DownMix6chTo2ch>(node_id)};
+
+ for (u32 i = 0; i < MaxChannels; i++) {
+ cmd.inputs[i] = buffer_offset + inputs[i];
+ cmd.outputs[i] = buffer_offset + inputs[i];
+ }
+
+ for (u32 i = 0; i < 4; i++) {
+ cmd.down_mix_coeff[i] = downmix_coeff[i];
+ }
+
+ GenerateEnd<DownMix6chTo2chCommand>(cmd);
+}
+
+void CommandBuffer::GenerateAuxCommand(const s32 node_id, EffectInfoBase& effect_info,
+ const s16 input_index, const s16 output_index,
+ const s16 buffer_offset, const u32 update_count,
+ const u32 count_max, const u32 write_offset) {
+ auto& cmd{GenerateStart<AuxCommand, CommandId::Aux>(node_id)};
+
+ if (effect_info.GetSendBuffer() != 0 && effect_info.GetReturnBuffer() != 0) {
+ cmd.input = buffer_offset + input_index;
+ cmd.output = buffer_offset + output_index;
+ cmd.send_buffer_info = effect_info.GetSendBufferInfo();
+ cmd.send_buffer = effect_info.GetSendBuffer();
+ cmd.return_buffer_info = effect_info.GetReturnBufferInfo();
+ cmd.return_buffer = effect_info.GetReturnBuffer();
+ cmd.count_max = count_max;
+ cmd.write_offset = write_offset;
+ cmd.update_count = update_count;
+ cmd.effect_enabled = effect_info.IsEnabled();
+ }
+
+ GenerateEnd<AuxCommand>(cmd);
+}
+
+void CommandBuffer::GenerateDeviceSinkCommand(const s32 node_id, const s16 buffer_offset,
+ SinkInfoBase& sink_info, const u32 session_id,
+ std::span<s32> samples_buffer) {
+ auto& cmd{GenerateStart<DeviceSinkCommand, CommandId::DeviceSink>(node_id)};
+ const auto& parameter{
+ *reinterpret_cast<DeviceSinkInfo::DeviceInParameter*>(sink_info.GetParameter())};
+ auto state{*reinterpret_cast<DeviceSinkInfo::DeviceState*>(sink_info.GetState())};
+
+ cmd.session_id = session_id;
+
+ if (state.upsampler_info != nullptr) {
+ const auto size_{state.upsampler_info->sample_count * parameter.input_count};
+ const auto size_bytes{size_ * sizeof(s32)};
+ const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)};
+ cmd.sample_buffer = {reinterpret_cast<s32*>(addr),
+ parameter.input_count * state.upsampler_info->sample_count};
+ } else {
+ cmd.sample_buffer = samples_buffer;
+ }
+
+ cmd.input_count = parameter.input_count;
+ for (u32 i = 0; i < parameter.input_count; i++) {
+ cmd.inputs[i] = buffer_offset + parameter.inputs[i];
+ }
+
+ GenerateEnd<DeviceSinkCommand>(cmd);
+}
+
+void CommandBuffer::GenerateCircularBufferSinkCommand(const s32 node_id, SinkInfoBase& sink_info,
+ const s16 buffer_offset) {
+ auto& cmd{GenerateStart<CircularBufferSinkCommand, CommandId::CircularBufferSink>(node_id)};
+ const auto& parameter{*reinterpret_cast<CircularBufferSinkInfo::CircularBufferInParameter*>(
+ sink_info.GetParameter())};
+ auto state{
+ *reinterpret_cast<CircularBufferSinkInfo::CircularBufferState*>(sink_info.GetState())};
+
+ cmd.input_count = parameter.input_count;
+ for (u32 i = 0; i < parameter.input_count; i++) {
+ cmd.inputs[i] = buffer_offset + parameter.inputs[i];
+ }
+
+ cmd.address = state.address_info.GetReference(true);
+ cmd.size = parameter.size;
+ cmd.pos = state.current_pos;
+
+ GenerateEnd<CircularBufferSinkCommand>(cmd);
+}
+
+void CommandBuffer::GenerateReverbCommand(const s32 node_id, EffectInfoBase& effect_info,
+ const s16 buffer_offset,
+ const bool long_size_pre_delay_supported) {
+ auto& cmd{GenerateStart<ReverbCommand, CommandId::Reverb>(node_id)};
+
+ const auto& parameter{
+ *reinterpret_cast<ReverbInfo::ParameterVersion2*>(effect_info.GetParameter())};
+ const auto state{effect_info.GetStateBuffer()};
+
+ if (IsChannelCountValid(parameter.channel_count)) {
+ const auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(ReverbInfo::State))};
+ if (state_buffer) {
+ for (s16 channel = 0; channel < parameter.channel_count; channel++) {
+ cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
+ cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
+ }
+
+ if (!behavior->IsReverbChannelMappingChanged() && parameter.channel_count == 6) {
+ UseOldChannelMapping(cmd.inputs, cmd.outputs);
+ }
+
+ cmd.parameter = parameter;
+ cmd.effect_enabled = effect_info.IsEnabled();
+ cmd.state = state_buffer;
+ cmd.workbuffer = effect_info.GetWorkbuffer(-1);
+ cmd.long_size_pre_delay_supported = long_size_pre_delay_supported;
+ }
+ }
+
+ GenerateEnd<ReverbCommand>(cmd);
+}
+
+void CommandBuffer::GenerateI3dl2ReverbCommand(const s32 node_id, EffectInfoBase& effect_info,
+ const s16 buffer_offset) {
+ auto& cmd{GenerateStart<I3dl2ReverbCommand, CommandId::I3dl2Reverb>(node_id)};
+
+ const auto& parameter{
+ *reinterpret_cast<I3dl2ReverbInfo::ParameterVersion1*>(effect_info.GetParameter())};
+ const auto state{effect_info.GetStateBuffer()};
+
+ if (IsChannelCountValid(parameter.channel_count)) {
+ const auto state_buffer{
+ memory_pool->Translate(CpuAddr(state), sizeof(I3dl2ReverbInfo::State))};
+ if (state_buffer) {
+ for (s16 channel = 0; channel < parameter.channel_count; channel++) {
+ cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
+ cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
+ }
+
+ if (!behavior->IsI3dl2ReverbChannelMappingChanged() && parameter.channel_count == 6) {
+ UseOldChannelMapping(cmd.inputs, cmd.outputs);
+ }
+
+ cmd.parameter = parameter;
+ cmd.effect_enabled = effect_info.IsEnabled();
+ cmd.state = state_buffer;
+ cmd.workbuffer = effect_info.GetWorkbuffer(-1);
+ }
+ }
+
+ GenerateEnd<I3dl2ReverbCommand>(cmd);
+}
+
+void CommandBuffer::GeneratePerformanceCommand(const s32 node_id, const PerformanceState state,
+ const PerformanceEntryAddresses& entry_addresses) {
+ auto& cmd{GenerateStart<PerformanceCommand, CommandId::Performance>(node_id)};
+
+ cmd.state = state;
+ cmd.entry_address = entry_addresses;
+
+ GenerateEnd<PerformanceCommand>(cmd);
+}
+
+void CommandBuffer::GenerateClearMixCommand(const s32 node_id) {
+ auto& cmd{GenerateStart<ClearMixBufferCommand, CommandId::ClearMixBuffer>(node_id)};
+ GenerateEnd<ClearMixBufferCommand>(cmd);
+}
+
+void CommandBuffer::GenerateCopyMixBufferCommand(const s32 node_id, EffectInfoBase& effect_info,
+ const s16 buffer_offset, const s8 channel) {
+ auto& cmd{GenerateStart<CopyMixBufferCommand, CommandId::CopyMixBuffer>(node_id)};
+
+ const auto& parameter{
+ *reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
+ cmd.input_index = buffer_offset + parameter.inputs[channel];
+ cmd.output_index = buffer_offset + parameter.outputs[channel];
+
+ GenerateEnd<CopyMixBufferCommand>(cmd);
+}
+
+void CommandBuffer::GenerateLightLimiterCommand(
+ const s32 node_id, const s16 buffer_offset,
+ const LightLimiterInfo::ParameterVersion1& parameter, const LightLimiterInfo::State& state,
+ const bool enabled, const CpuAddr workbuffer) {
+ auto& cmd{GenerateStart<LightLimiterVersion1Command, CommandId::LightLimiterVersion1>(node_id)};
+
+ if (IsChannelCountValid(parameter.channel_count)) {
+ const auto state_buffer{
+ memory_pool->Translate(CpuAddr(&state), sizeof(LightLimiterInfo::State))};
+ if (state_buffer) {
+ for (s8 channel = 0; channel < parameter.channel_count; channel++) {
+ cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
+ cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
+ }
+
+ std::memcpy(&cmd.parameter, &parameter, sizeof(LightLimiterInfo::ParameterVersion1));
+ cmd.effect_enabled = enabled;
+ cmd.state = state_buffer;
+ cmd.workbuffer = workbuffer;
+ }
+ }
+
+ GenerateEnd<LightLimiterVersion1Command>(cmd);
+}
+
+void CommandBuffer::GenerateLightLimiterCommand(
+ const s32 node_id, const s16 buffer_offset,
+ const LightLimiterInfo::ParameterVersion2& parameter,
+ const LightLimiterInfo::StatisticsInternal& statistics, const LightLimiterInfo::State& state,
+ const bool enabled, const CpuAddr workbuffer) {
+ auto& cmd{GenerateStart<LightLimiterVersion2Command, CommandId::LightLimiterVersion2>(node_id)};
+ if (IsChannelCountValid(parameter.channel_count)) {
+ const auto state_buffer{
+ memory_pool->Translate(CpuAddr(&state), sizeof(LightLimiterInfo::State))};
+ if (state_buffer) {
+ for (s8 channel = 0; channel < parameter.channel_count; channel++) {
+ cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
+ cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
+ }
+
+ cmd.parameter = parameter;
+ cmd.effect_enabled = enabled;
+ cmd.state = state_buffer;
+ if (cmd.parameter.statistics_enabled) {
+ cmd.result_state = memory_pool->Translate(
+ CpuAddr(&statistics), sizeof(LightLimiterInfo::StatisticsInternal));
+ } else {
+ cmd.result_state = 0;
+ }
+ cmd.workbuffer = workbuffer;
+ }
+ }
+
+ GenerateEnd<LightLimiterVersion2Command>(cmd);
+}
+
+void CommandBuffer::GenerateMultitapBiquadFilterCommand(const s32 node_id, VoiceInfo& voice_info,
+ const VoiceState& voice_state,
+ const s16 buffer_count, const s8 channel) {
+ auto& cmd{GenerateStart<MultiTapBiquadFilterCommand, CommandId::MultiTapBiquadFilter>(node_id)};
+
+ cmd.input = buffer_count + channel;
+ cmd.output = buffer_count + channel;
+ cmd.biquads = voice_info.biquads;
+
+ cmd.states[0] =
+ memory_pool->Translate(CpuAddr(voice_state.biquad_states[0].data()),
+ MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
+ cmd.states[1] =
+ memory_pool->Translate(CpuAddr(voice_state.biquad_states[1].data()),
+ MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
+
+ cmd.needs_init[0] = !voice_info.biquad_initialized[0];
+ cmd.needs_init[1] = !voice_info.biquad_initialized[1];
+ cmd.filter_tap_count = MaxBiquadFilters;
+
+ GenerateEnd<MultiTapBiquadFilterCommand>(cmd);
+}
+
+void CommandBuffer::GenerateCaptureCommand(const s32 node_id, EffectInfoBase& effect_info,
+ const s16 input_index, const s16 output_index,
+ const s16 buffer_offset, const u32 update_count,
+ const u32 count_max, const u32 write_offset) {
+ auto& cmd{GenerateStart<CaptureCommand, CommandId::Capture>(node_id)};
+
+ if (effect_info.GetSendBuffer()) {
+ cmd.input = buffer_offset + input_index;
+ cmd.output = buffer_offset + output_index;
+ cmd.send_buffer_info = effect_info.GetSendBufferInfo();
+ cmd.send_buffer = effect_info.GetSendBuffer();
+ cmd.count_max = count_max;
+ cmd.write_offset = write_offset;
+ cmd.update_count = update_count;
+ cmd.effect_enabled = effect_info.IsEnabled();
+ }
+
+ GenerateEnd<CaptureCommand>(cmd);
+}
+
+void CommandBuffer::GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info,
+ s32 node_id) {
+ auto& cmd{GenerateStart<CompressorCommand, CommandId::Compressor>(node_id)};
+
+ auto& parameter{
+ *reinterpret_cast<CompressorInfo::ParameterVersion2*>(effect_info.GetParameter())};
+ auto state{reinterpret_cast<CompressorInfo::State*>(effect_info.GetStateBuffer())};
+
+ if (IsChannelCountValid(parameter.channel_count)) {
+ auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(CompressorInfo::State))};
+ if (state_buffer) {
+ for (u16 channel = 0; channel < parameter.channel_count; channel++) {
+ cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
+ cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
+ }
+ cmd.parameter = parameter;
+ cmd.workbuffer = state_buffer;
+ cmd.enabled = effect_info.IsEnabled();
+ }
+ }
+
+ GenerateEnd<CompressorCommand>(cmd);
+}
+
+} // namespace AudioCore::AudioRenderer