summaryrefslogtreecommitdiff
path: root/src/audio_core/renderer/command
diff options
context:
space:
mode:
authorGravatar Kelebek12022-07-16 23:48:45 +0100
committerGravatar Kelebek12022-07-22 01:11:32 +0100
commit458da8a94877677f086f06cdeecf959ec4283a33 (patch)
tree583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/renderer/command
parentMerge pull request #8598 from Link4565/recv-dontwait (diff)
downloadyuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz
yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz
yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip
Project Andio
Diffstat (limited to 'src/audio_core/renderer/command')
-rw-r--r--src/audio_core/renderer/command/command_buffer.cpp714
-rw-r--r--src/audio_core/renderer/command/command_buffer.h466
-rw-r--r--src/audio_core/renderer/command/command_generator.cpp796
-rw-r--r--src/audio_core/renderer/command/command_generator.h349
-rw-r--r--src/audio_core/renderer/command/command_list_header.h22
-rw-r--r--src/audio_core/renderer/command/command_processing_time_estimator.cpp3620
-rw-r--r--src/audio_core/renderer/command/command_processing_time_estimator.h254
-rw-r--r--src/audio_core/renderer/command/commands.h32
-rw-r--r--src/audio_core/renderer/command/data_source/adpcm.cpp84
-rw-r--r--src/audio_core/renderer/command/data_source/adpcm.h119
-rw-r--r--src/audio_core/renderer/command/data_source/decode.cpp428
-rw-r--r--src/audio_core/renderer/command/data_source/decode.h59
-rw-r--r--src/audio_core/renderer/command/data_source/pcm_float.cpp86
-rw-r--r--src/audio_core/renderer/command/data_source/pcm_float.h113
-rw-r--r--src/audio_core/renderer/command/data_source/pcm_int16.cpp87
-rw-r--r--src/audio_core/renderer/command/data_source/pcm_int16.h110
-rw-r--r--src/audio_core/renderer/command/effect/aux_.cpp207
-rw-r--r--src/audio_core/renderer/command/effect/aux_.h66
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.cpp118
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.h74
-rw-r--r--src/audio_core/renderer/command/effect/capture.cpp142
-rw-r--r--src/audio_core/renderer/command/effect/capture.h62
-rw-r--r--src/audio_core/renderer/command/effect/compressor.cpp156
-rw-r--r--src/audio_core/renderer/command/effect/compressor.h60
-rw-r--r--src/audio_core/renderer/command/effect/delay.cpp238
-rw-r--r--src/audio_core/renderer/command/effect/delay.h60
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.cpp437
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.h60
-rw-r--r--src/audio_core/renderer/command/effect/light_limiter.cpp222
-rw-r--r--src/audio_core/renderer/command/effect/light_limiter.h103
-rw-r--r--src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp45
-rw-r--r--src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h59
-rw-r--r--src/audio_core/renderer/command/effect/reverb.cpp440
-rw-r--r--src/audio_core/renderer/command/effect/reverb.h62
-rw-r--r--src/audio_core/renderer/command/icommand.h93
-rw-r--r--src/audio_core/renderer/command/mix/clear_mix.cpp24
-rw-r--r--src/audio_core/renderer/command/mix/clear_mix.h45
-rw-r--r--src/audio_core/renderer/command/mix/copy_mix.cpp27
-rw-r--r--src/audio_core/renderer/command/mix/copy_mix.h49
-rw-r--r--src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp64
-rw-r--r--src/audio_core/renderer/command/mix/depop_for_mix_buffers.h55
-rw-r--r--src/audio_core/renderer/command/mix/depop_prepare.cpp36
-rw-r--r--src/audio_core/renderer/command/mix/depop_prepare.h54
-rw-r--r--src/audio_core/renderer/command/mix/mix.cpp70
-rw-r--r--src/audio_core/renderer/command/mix/mix.h54
-rw-r--r--src/audio_core/renderer/command/mix/mix_ramp.cpp94
-rw-r--r--src/audio_core/renderer/command/mix/mix_ramp.h73
-rw-r--r--src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp65
-rw-r--r--src/audio_core/renderer/command/mix/mix_ramp_grouped.h61
-rw-r--r--src/audio_core/renderer/command/mix/volume.cpp72
-rw-r--r--src/audio_core/renderer/command/mix/volume.h53
-rw-r--r--src/audio_core/renderer/command/mix/volume_ramp.cpp84
-rw-r--r--src/audio_core/renderer/command/mix/volume_ramp.h56
-rw-r--r--src/audio_core/renderer/command/performance/performance.cpp43
-rw-r--r--src/audio_core/renderer/command/performance/performance.h51
-rw-r--r--src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp74
-rw-r--r--src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h59
-rw-r--r--src/audio_core/renderer/command/resample/resample.cpp883
-rw-r--r--src/audio_core/renderer/command/resample/resample.h29
-rw-r--r--src/audio_core/renderer/command/resample/upsample.cpp262
-rw-r--r--src/audio_core/renderer/command/resample/upsample.h60
-rw-r--r--src/audio_core/renderer/command/sink/circular_buffer.cpp48
-rw-r--r--src/audio_core/renderer/command/sink/circular_buffer.h55
-rw-r--r--src/audio_core/renderer/command/sink/device.cpp55
-rw-r--r--src/audio_core/renderer/command/sink/device.h57
65 files changed, 12755 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 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/behavior/behavior_info.h"
5#include "audio_core/renderer/command/command_buffer.h"
6#include "audio_core/renderer/command/command_list_header.h"
7#include "audio_core/renderer/command/command_processing_time_estimator.h"
8#include "audio_core/renderer/effect/biquad_filter.h"
9#include "audio_core/renderer/effect/delay.h"
10#include "audio_core/renderer/effect/reverb.h"
11#include "audio_core/renderer/memory/memory_pool_info.h"
12#include "audio_core/renderer/mix/mix_info.h"
13#include "audio_core/renderer/sink/circular_buffer_sink_info.h"
14#include "audio_core/renderer/sink/device_sink_info.h"
15#include "audio_core/renderer/sink/sink_info_base.h"
16#include "audio_core/renderer/voice/voice_info.h"
17#include "audio_core/renderer/voice/voice_state.h"
18
19namespace AudioCore::AudioRenderer {
20
21template <typename T, CommandId Id>
22T& CommandBuffer::GenerateStart(const s32 node_id) {
23 if (size + sizeof(T) >= command_list.size_bytes()) {
24 LOG_ERROR(
25 Service_Audio,
26 "Attempting to write commands beyond the end of allocated command buffer memory!");
27 UNREACHABLE();
28 }
29
30 auto& cmd{*std::construct_at<T>(reinterpret_cast<T*>(&command_list[size]))};
31
32 cmd.magic = CommandMagic;
33 cmd.enabled = true;
34 cmd.type = Id;
35 cmd.size = sizeof(T);
36 cmd.node_id = node_id;
37
38 return cmd;
39}
40
41template <typename T>
42void CommandBuffer::GenerateEnd(T& cmd) {
43 cmd.estimated_process_time = time_estimator->Estimate(cmd);
44 estimated_process_time += cmd.estimated_process_time;
45 size += sizeof(T);
46 count++;
47}
48
49void CommandBuffer::GeneratePcmInt16Version1Command(const s32 node_id,
50 const MemoryPoolInfo& memory_pool_,
51 VoiceInfo& voice_info,
52 const VoiceState& voice_state,
53 const s16 buffer_count, const s8 channel) {
54 auto& cmd{
55 GenerateStart<PcmInt16DataSourceVersion1Command, CommandId::DataSourcePcmInt16Version1>(
56 node_id)};
57
58 cmd.src_quality = voice_info.src_quality;
59 cmd.output_index = buffer_count + channel;
60 cmd.flags = voice_info.flags & 3;
61 cmd.sample_rate = voice_info.sample_rate;
62 cmd.pitch = voice_info.pitch;
63 cmd.channel_index = channel;
64 cmd.channel_count = voice_info.channel_count;
65
66 for (u32 i = 0; i < MaxWaveBuffers; i++) {
67 voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
68 }
69
70 cmd.voice_state = memory_pool_.Translate(CpuAddr(&voice_state), sizeof(VoiceState));
71
72 GenerateEnd<PcmInt16DataSourceVersion1Command>(cmd);
73}
74
75void CommandBuffer::GeneratePcmInt16Version2Command(const s32 node_id, VoiceInfo& voice_info,
76 const VoiceState& voice_state,
77 const s16 buffer_count, const s8 channel) {
78 auto& cmd{
79 GenerateStart<PcmInt16DataSourceVersion2Command, CommandId::DataSourcePcmInt16Version2>(
80 node_id)};
81
82 cmd.src_quality = voice_info.src_quality;
83 cmd.output_index = buffer_count + channel;
84 cmd.flags = voice_info.flags & 3;
85 cmd.sample_rate = voice_info.sample_rate;
86 cmd.pitch = voice_info.pitch;
87 cmd.channel_index = channel;
88 cmd.channel_count = voice_info.channel_count;
89
90 for (u32 i = 0; i < MaxWaveBuffers; i++) {
91 voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
92 }
93
94 cmd.voice_state = memory_pool->Translate(CpuAddr(&voice_state), sizeof(VoiceState));
95
96 GenerateEnd<PcmInt16DataSourceVersion2Command>(cmd);
97}
98
99void CommandBuffer::GeneratePcmFloatVersion1Command(const s32 node_id,
100 const MemoryPoolInfo& memory_pool_,
101 VoiceInfo& voice_info,
102 const VoiceState& voice_state,
103 const s16 buffer_count, const s8 channel) {
104 auto& cmd{
105 GenerateStart<PcmFloatDataSourceVersion1Command, CommandId::DataSourcePcmFloatVersion1>(
106 node_id)};
107
108 cmd.src_quality = voice_info.src_quality;
109 cmd.output_index = buffer_count + channel;
110 cmd.flags = voice_info.flags & 3;
111 cmd.sample_rate = voice_info.sample_rate;
112 cmd.pitch = voice_info.pitch;
113 cmd.channel_index = channel;
114 cmd.channel_count = voice_info.channel_count;
115
116 for (u32 i = 0; i < MaxWaveBuffers; i++) {
117 voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
118 }
119
120 cmd.voice_state = memory_pool_.Translate(CpuAddr(&voice_state), sizeof(VoiceState));
121
122 GenerateEnd<PcmFloatDataSourceVersion1Command>(cmd);
123}
124
125void CommandBuffer::GeneratePcmFloatVersion2Command(const s32 node_id, VoiceInfo& voice_info,
126 const VoiceState& voice_state,
127 const s16 buffer_count, const s8 channel) {
128 auto& cmd{
129 GenerateStart<PcmFloatDataSourceVersion2Command, CommandId::DataSourcePcmFloatVersion2>(
130 node_id)};
131
132 cmd.src_quality = voice_info.src_quality;
133 cmd.output_index = buffer_count + channel;
134 cmd.flags = voice_info.flags & 3;
135 cmd.sample_rate = voice_info.sample_rate;
136 cmd.pitch = voice_info.pitch;
137 cmd.channel_index = channel;
138 cmd.channel_count = voice_info.channel_count;
139
140 for (u32 i = 0; i < MaxWaveBuffers; i++) {
141 voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
142 }
143
144 cmd.voice_state = memory_pool->Translate(CpuAddr(&voice_state), sizeof(VoiceState));
145
146 GenerateEnd<PcmFloatDataSourceVersion2Command>(cmd);
147}
148
149void CommandBuffer::GenerateAdpcmVersion1Command(const s32 node_id,
150 const MemoryPoolInfo& memory_pool_,
151 VoiceInfo& voice_info,
152 const VoiceState& voice_state,
153 const s16 buffer_count, const s8 channel) {
154 auto& cmd{
155 GenerateStart<AdpcmDataSourceVersion1Command, CommandId::DataSourceAdpcmVersion1>(node_id)};
156
157 cmd.src_quality = voice_info.src_quality;
158 cmd.output_index = buffer_count + channel;
159 cmd.flags = voice_info.flags & 3;
160 cmd.sample_rate = voice_info.sample_rate;
161 cmd.pitch = voice_info.pitch;
162
163 for (u32 i = 0; i < MaxWaveBuffers; i++) {
164 voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
165 }
166
167 cmd.voice_state = memory_pool_.Translate(CpuAddr(&voice_state), sizeof(VoiceState));
168 cmd.data_address = voice_info.data_address.GetReference(true);
169 cmd.data_size = voice_info.data_address.GetSize();
170
171 GenerateEnd<AdpcmDataSourceVersion1Command>(cmd);
172}
173
174void CommandBuffer::GenerateAdpcmVersion2Command(const s32 node_id, VoiceInfo& voice_info,
175 const VoiceState& voice_state,
176 const s16 buffer_count, const s8 channel) {
177 auto& cmd{
178 GenerateStart<AdpcmDataSourceVersion2Command, CommandId::DataSourceAdpcmVersion2>(node_id)};
179
180 cmd.src_quality = voice_info.src_quality;
181 cmd.output_index = buffer_count + channel;
182 cmd.flags = voice_info.flags & 3;
183 cmd.sample_rate = voice_info.sample_rate;
184 cmd.pitch = voice_info.pitch;
185 cmd.channel_index = channel;
186 cmd.channel_count = voice_info.channel_count;
187
188 for (u32 i = 0; i < MaxWaveBuffers; i++) {
189 voice_info.wavebuffers[i].Copy(cmd.wave_buffers[i]);
190 }
191
192 cmd.voice_state = memory_pool->Translate(CpuAddr(&voice_state), sizeof(VoiceState));
193 cmd.data_address = voice_info.data_address.GetReference(true);
194 cmd.data_size = voice_info.data_address.GetSize();
195
196 GenerateEnd<AdpcmDataSourceVersion2Command>(cmd);
197}
198
199void CommandBuffer::GenerateVolumeCommand(const s32 node_id, const s16 buffer_offset,
200 const s16 input_index, const f32 volume,
201 const u8 precision) {
202 auto& cmd{GenerateStart<VolumeCommand, CommandId::Volume>(node_id)};
203
204 cmd.precision = precision;
205 cmd.input_index = buffer_offset + input_index;
206 cmd.output_index = buffer_offset + input_index;
207 cmd.volume = volume;
208
209 GenerateEnd<VolumeCommand>(cmd);
210}
211
212void CommandBuffer::GenerateVolumeRampCommand(const s32 node_id, VoiceInfo& voice_info,
213 const s16 buffer_count, const u8 precision) {
214 auto& cmd{GenerateStart<VolumeRampCommand, CommandId::VolumeRamp>(node_id)};
215
216 cmd.input_index = buffer_count;
217 cmd.output_index = buffer_count;
218 cmd.prev_volume = voice_info.prev_volume;
219 cmd.volume = voice_info.volume;
220 cmd.precision = precision;
221
222 GenerateEnd<VolumeRampCommand>(cmd);
223}
224
225void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, VoiceInfo& voice_info,
226 const VoiceState& voice_state,
227 const s16 buffer_count, const s8 channel,
228 const u32 biquad_index,
229 const bool use_float_processing) {
230 auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
231
232 cmd.input = buffer_count + channel;
233 cmd.output = buffer_count + channel;
234
235 cmd.biquad = voice_info.biquads[biquad_index];
236
237 cmd.state = memory_pool->Translate(CpuAddr(voice_state.biquad_states[biquad_index].data()),
238 MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
239
240 cmd.needs_init = !voice_info.biquad_initialized[biquad_index];
241 cmd.use_float_processing = use_float_processing;
242
243 GenerateEnd<BiquadFilterCommand>(cmd);
244}
245
246void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBase& effect_info,
247 const s16 buffer_offset, const s8 channel,
248 const bool needs_init,
249 const bool use_float_processing) {
250 auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
251
252 const auto& parameter{
253 *reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
254 const auto state{
255 reinterpret_cast<VoiceState::BiquadFilterState*>(effect_info.GetStateBuffer())};
256
257 cmd.input = buffer_offset + parameter.inputs[channel];
258 cmd.output = buffer_offset + parameter.outputs[channel];
259
260 cmd.biquad.b = parameter.b;
261 cmd.biquad.a = parameter.a;
262
263 cmd.state = memory_pool->Translate(CpuAddr(state),
264 MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
265
266 cmd.needs_init = needs_init;
267 cmd.use_float_processing = use_float_processing;
268
269 GenerateEnd<BiquadFilterCommand>(cmd);
270}
271
272void CommandBuffer::GenerateMixCommand(const s32 node_id, const s16 input_index,
273 const s16 output_index, const s16 buffer_offset,
274 const f32 volume, const u8 precision) {
275 auto& cmd{GenerateStart<MixCommand, CommandId::Mix>(node_id)};
276
277 cmd.input_index = input_index;
278 cmd.output_index = output_index;
279 cmd.volume = volume;
280 cmd.precision = precision;
281
282 GenerateEnd<MixCommand>(cmd);
283}
284
285void CommandBuffer::GenerateMixRampCommand(const s32 node_id,
286 [[maybe_unused]] const s16 buffer_count,
287 const s16 input_index, const s16 output_index,
288 const f32 volume, const f32 prev_volume,
289 const CpuAddr prev_samples, const u8 precision) {
290 if (volume == 0.0f && prev_volume == 0.0f) {
291 return;
292 }
293
294 auto& cmd{GenerateStart<MixRampCommand, CommandId::MixRamp>(node_id)};
295
296 cmd.input_index = input_index;
297 cmd.output_index = output_index;
298 cmd.prev_volume = prev_volume;
299 cmd.volume = volume;
300 cmd.previous_sample = prev_samples;
301 cmd.precision = precision;
302
303 GenerateEnd<MixRampCommand>(cmd);
304}
305
306void CommandBuffer::GenerateMixRampGroupedCommand(const s32 node_id, const s16 buffer_count,
307 const s16 input_index, s16 output_index,
308 std::span<const f32> volumes,
309 std::span<const f32> prev_volumes,
310 const CpuAddr prev_samples, const u8 precision) {
311 auto& cmd{GenerateStart<MixRampGroupedCommand, CommandId::MixRampGrouped>(node_id)};
312
313 cmd.buffer_count = buffer_count;
314
315 for (s32 i = 0; i < buffer_count; i++) {
316 cmd.inputs[i] = input_index;
317 cmd.outputs[i] = output_index++;
318 cmd.prev_volumes[i] = prev_volumes[i];
319 cmd.volumes[i] = volumes[i];
320 }
321
322 cmd.previous_samples = prev_samples;
323 cmd.precision = precision;
324
325 GenerateEnd<MixRampGroupedCommand>(cmd);
326}
327
328void CommandBuffer::GenerateDepopPrepareCommand(const s32 node_id, const VoiceState& voice_state,
329 std::span<const s32> buffer, const s16 buffer_count,
330 s16 buffer_offset, const bool was_playing) {
331 auto& cmd{GenerateStart<DepopPrepareCommand, CommandId::DepopPrepare>(node_id)};
332
333 cmd.enabled = was_playing;
334
335 for (u32 i = 0; i < MaxMixBuffers; i++) {
336 cmd.inputs[i] = buffer_offset++;
337 }
338
339 cmd.previous_samples = memory_pool->Translate(CpuAddr(voice_state.previous_samples.data()),
340 MaxMixBuffers * sizeof(s32));
341 cmd.buffer_count = buffer_count;
342 cmd.depop_buffer = memory_pool->Translate(CpuAddr(buffer.data()), buffer_count * sizeof(s32));
343
344 GenerateEnd<DepopPrepareCommand>(cmd);
345}
346
347void CommandBuffer::GenerateDepopForMixBuffersCommand(const s32 node_id, const MixInfo& mix_info,
348 std::span<const s32> depop_buffer) {
349 auto& cmd{GenerateStart<DepopForMixBuffersCommand, CommandId::DepopForMixBuffers>(node_id)};
350
351 cmd.input = mix_info.buffer_offset;
352 cmd.count = mix_info.buffer_count;
353 cmd.decay = mix_info.sample_rate == TargetSampleRate ? 0.96218872f : 0.94369507f;
354 cmd.depop_buffer =
355 memory_pool->Translate(CpuAddr(depop_buffer.data()), mix_info.buffer_count * sizeof(s32));
356
357 GenerateEnd<DepopForMixBuffersCommand>(cmd);
358}
359
360void CommandBuffer::GenerateDelayCommand(const s32 node_id, EffectInfoBase& effect_info,
361 const s16 buffer_offset) {
362 auto& cmd{GenerateStart<DelayCommand, CommandId::Delay>(node_id)};
363
364 const auto& parameter{
365 *reinterpret_cast<DelayInfo::ParameterVersion1*>(effect_info.GetParameter())};
366 const auto state{effect_info.GetStateBuffer()};
367
368 if (IsChannelCountValid(parameter.channel_count)) {
369 const auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(DelayInfo::State))};
370 if (state_buffer) {
371 for (s16 channel = 0; channel < parameter.channel_count; channel++) {
372 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
373 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
374 }
375
376 if (!behavior->IsDelayChannelMappingChanged() && parameter.channel_count == 6) {
377 UseOldChannelMapping(cmd.inputs, cmd.outputs);
378 }
379
380 cmd.parameter = parameter;
381 cmd.effect_enabled = effect_info.IsEnabled();
382 cmd.state = state_buffer;
383 cmd.workbuffer = effect_info.GetWorkbuffer(-1);
384 }
385 }
386
387 GenerateEnd<DelayCommand>(cmd);
388}
389
390void CommandBuffer::GenerateUpsampleCommand(const s32 node_id, const s16 buffer_offset,
391 UpsamplerInfo& upsampler_info, const u32 input_count,
392 std::span<const s8> inputs, const s16 buffer_count,
393 const u32 sample_count_, const u32 sample_rate_) {
394 auto& cmd{GenerateStart<UpsampleCommand, CommandId::Upsample>(node_id)};
395
396 cmd.samples_buffer = memory_pool->Translate(upsampler_info.samples_pos,
397 upsampler_info.sample_count * sizeof(s32));
398 cmd.inputs = memory_pool->Translate(CpuAddr(upsampler_info.inputs.data()), MaxChannels);
399 cmd.buffer_count = buffer_count;
400 cmd.unk_20 = 0;
401 cmd.source_sample_count = sample_count_;
402 cmd.source_sample_rate = sample_rate_;
403
404 upsampler_info.input_count = input_count;
405 for (u32 i = 0; i < input_count; i++) {
406 upsampler_info.inputs[i] = buffer_offset + inputs[i];
407 }
408
409 cmd.upsampler_info = memory_pool->Translate(CpuAddr(&upsampler_info), sizeof(UpsamplerInfo));
410
411 GenerateEnd<UpsampleCommand>(cmd);
412}
413
414void CommandBuffer::GenerateDownMix6chTo2chCommand(const s32 node_id, std::span<const s8> inputs,
415 const s16 buffer_offset,
416 std::span<const f32> downmix_coeff) {
417 auto& cmd{GenerateStart<DownMix6chTo2chCommand, CommandId::DownMix6chTo2ch>(node_id)};
418
419 for (u32 i = 0; i < MaxChannels; i++) {
420 cmd.inputs[i] = buffer_offset + inputs[i];
421 cmd.outputs[i] = buffer_offset + inputs[i];
422 }
423
424 for (u32 i = 0; i < 4; i++) {
425 cmd.down_mix_coeff[i] = downmix_coeff[i];
426 }
427
428 GenerateEnd<DownMix6chTo2chCommand>(cmd);
429}
430
431void CommandBuffer::GenerateAuxCommand(const s32 node_id, EffectInfoBase& effect_info,
432 const s16 input_index, const s16 output_index,
433 const s16 buffer_offset, const u32 update_count,
434 const u32 count_max, const u32 write_offset) {
435 auto& cmd{GenerateStart<AuxCommand, CommandId::Aux>(node_id)};
436
437 if (effect_info.GetSendBuffer() != 0 && effect_info.GetReturnBuffer() != 0) {
438 cmd.input = buffer_offset + input_index;
439 cmd.output = buffer_offset + output_index;
440 cmd.send_buffer_info = effect_info.GetSendBufferInfo();
441 cmd.send_buffer = effect_info.GetSendBuffer();
442 cmd.return_buffer_info = effect_info.GetReturnBufferInfo();
443 cmd.return_buffer = effect_info.GetReturnBuffer();
444 cmd.count_max = count_max;
445 cmd.write_offset = write_offset;
446 cmd.update_count = update_count;
447 cmd.effect_enabled = effect_info.IsEnabled();
448 }
449
450 GenerateEnd<AuxCommand>(cmd);
451}
452
453void CommandBuffer::GenerateDeviceSinkCommand(const s32 node_id, const s16 buffer_offset,
454 SinkInfoBase& sink_info, const u32 session_id,
455 std::span<s32> samples_buffer) {
456 auto& cmd{GenerateStart<DeviceSinkCommand, CommandId::DeviceSink>(node_id)};
457 const auto& parameter{
458 *reinterpret_cast<DeviceSinkInfo::DeviceInParameter*>(sink_info.GetParameter())};
459 auto state{*reinterpret_cast<DeviceSinkInfo::DeviceState*>(sink_info.GetState())};
460
461 cmd.session_id = session_id;
462
463 if (state.upsampler_info != nullptr) {
464 const auto size_{state.upsampler_info->sample_count * parameter.input_count};
465 const auto size_bytes{size_ * sizeof(s32)};
466 const auto addr{memory_pool->Translate(state.upsampler_info->samples_pos, size_bytes)};
467 cmd.sample_buffer = {reinterpret_cast<s32*>(addr),
468 parameter.input_count * state.upsampler_info->sample_count};
469 } else {
470 cmd.sample_buffer = samples_buffer;
471 }
472
473 cmd.input_count = parameter.input_count;
474 for (u32 i = 0; i < parameter.input_count; i++) {
475 cmd.inputs[i] = buffer_offset + parameter.inputs[i];
476 }
477
478 GenerateEnd<DeviceSinkCommand>(cmd);
479}
480
481void CommandBuffer::GenerateCircularBufferSinkCommand(const s32 node_id, SinkInfoBase& sink_info,
482 const s16 buffer_offset) {
483 auto& cmd{GenerateStart<CircularBufferSinkCommand, CommandId::CircularBufferSink>(node_id)};
484 const auto& parameter{*reinterpret_cast<CircularBufferSinkInfo::CircularBufferInParameter*>(
485 sink_info.GetParameter())};
486 auto state{
487 *reinterpret_cast<CircularBufferSinkInfo::CircularBufferState*>(sink_info.GetState())};
488
489 cmd.input_count = parameter.input_count;
490 for (u32 i = 0; i < parameter.input_count; i++) {
491 cmd.inputs[i] = buffer_offset + parameter.inputs[i];
492 }
493
494 cmd.address = state.address_info.GetReference(true);
495 cmd.size = parameter.size;
496 cmd.pos = state.current_pos;
497
498 GenerateEnd<CircularBufferSinkCommand>(cmd);
499}
500
501void CommandBuffer::GenerateReverbCommand(const s32 node_id, EffectInfoBase& effect_info,
502 const s16 buffer_offset,
503 const bool long_size_pre_delay_supported) {
504 auto& cmd{GenerateStart<ReverbCommand, CommandId::Reverb>(node_id)};
505
506 const auto& parameter{
507 *reinterpret_cast<ReverbInfo::ParameterVersion2*>(effect_info.GetParameter())};
508 const auto state{effect_info.GetStateBuffer()};
509
510 if (IsChannelCountValid(parameter.channel_count)) {
511 const auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(ReverbInfo::State))};
512 if (state_buffer) {
513 for (s16 channel = 0; channel < parameter.channel_count; channel++) {
514 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
515 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
516 }
517
518 if (!behavior->IsReverbChannelMappingChanged() && parameter.channel_count == 6) {
519 UseOldChannelMapping(cmd.inputs, cmd.outputs);
520 }
521
522 cmd.parameter = parameter;
523 cmd.effect_enabled = effect_info.IsEnabled();
524 cmd.state = state_buffer;
525 cmd.workbuffer = effect_info.GetWorkbuffer(-1);
526 cmd.long_size_pre_delay_supported = long_size_pre_delay_supported;
527 }
528 }
529
530 GenerateEnd<ReverbCommand>(cmd);
531}
532
533void CommandBuffer::GenerateI3dl2ReverbCommand(const s32 node_id, EffectInfoBase& effect_info,
534 const s16 buffer_offset) {
535 auto& cmd{GenerateStart<I3dl2ReverbCommand, CommandId::I3dl2Reverb>(node_id)};
536
537 const auto& parameter{
538 *reinterpret_cast<I3dl2ReverbInfo::ParameterVersion1*>(effect_info.GetParameter())};
539 const auto state{effect_info.GetStateBuffer()};
540
541 if (IsChannelCountValid(parameter.channel_count)) {
542 const auto state_buffer{
543 memory_pool->Translate(CpuAddr(state), sizeof(I3dl2ReverbInfo::State))};
544 if (state_buffer) {
545 for (s16 channel = 0; channel < parameter.channel_count; channel++) {
546 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
547 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
548 }
549
550 if (!behavior->IsI3dl2ReverbChannelMappingChanged() && parameter.channel_count == 6) {
551 UseOldChannelMapping(cmd.inputs, cmd.outputs);
552 }
553
554 cmd.parameter = parameter;
555 cmd.effect_enabled = effect_info.IsEnabled();
556 cmd.state = state_buffer;
557 cmd.workbuffer = effect_info.GetWorkbuffer(-1);
558 }
559 }
560
561 GenerateEnd<I3dl2ReverbCommand>(cmd);
562}
563
564void CommandBuffer::GeneratePerformanceCommand(const s32 node_id, const PerformanceState state,
565 const PerformanceEntryAddresses& entry_addresses) {
566 auto& cmd{GenerateStart<PerformanceCommand, CommandId::Performance>(node_id)};
567
568 cmd.state = state;
569 cmd.entry_address = entry_addresses;
570
571 GenerateEnd<PerformanceCommand>(cmd);
572}
573
574void CommandBuffer::GenerateClearMixCommand(const s32 node_id) {
575 auto& cmd{GenerateStart<ClearMixBufferCommand, CommandId::ClearMixBuffer>(node_id)};
576 GenerateEnd<ClearMixBufferCommand>(cmd);
577}
578
579void CommandBuffer::GenerateCopyMixBufferCommand(const s32 node_id, EffectInfoBase& effect_info,
580 const s16 buffer_offset, const s8 channel) {
581 auto& cmd{GenerateStart<CopyMixBufferCommand, CommandId::CopyMixBuffer>(node_id)};
582
583 const auto& parameter{
584 *reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
585 cmd.input_index = buffer_offset + parameter.inputs[channel];
586 cmd.output_index = buffer_offset + parameter.outputs[channel];
587
588 GenerateEnd<CopyMixBufferCommand>(cmd);
589}
590
591void CommandBuffer::GenerateLightLimiterCommand(
592 const s32 node_id, const s16 buffer_offset,
593 const LightLimiterInfo::ParameterVersion1& parameter, const LightLimiterInfo::State& state,
594 const bool enabled, const CpuAddr workbuffer) {
595 auto& cmd{GenerateStart<LightLimiterVersion1Command, CommandId::LightLimiterVersion1>(node_id)};
596
597 if (IsChannelCountValid(parameter.channel_count)) {
598 const auto state_buffer{
599 memory_pool->Translate(CpuAddr(&state), sizeof(LightLimiterInfo::State))};
600 if (state_buffer) {
601 for (s8 channel = 0; channel < parameter.channel_count; channel++) {
602 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
603 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
604 }
605
606 std::memcpy(&cmd.parameter, &parameter, sizeof(LightLimiterInfo::ParameterVersion1));
607 cmd.effect_enabled = enabled;
608 cmd.state = state_buffer;
609 cmd.workbuffer = workbuffer;
610 }
611 }
612
613 GenerateEnd<LightLimiterVersion1Command>(cmd);
614}
615
616void CommandBuffer::GenerateLightLimiterCommand(
617 const s32 node_id, const s16 buffer_offset,
618 const LightLimiterInfo::ParameterVersion2& parameter,
619 const LightLimiterInfo::StatisticsInternal& statistics, const LightLimiterInfo::State& state,
620 const bool enabled, const CpuAddr workbuffer) {
621 auto& cmd{GenerateStart<LightLimiterVersion2Command, CommandId::LightLimiterVersion2>(node_id)};
622 if (IsChannelCountValid(parameter.channel_count)) {
623 const auto state_buffer{
624 memory_pool->Translate(CpuAddr(&state), sizeof(LightLimiterInfo::State))};
625 if (state_buffer) {
626 for (s8 channel = 0; channel < parameter.channel_count; channel++) {
627 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
628 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
629 }
630
631 cmd.parameter = parameter;
632 cmd.effect_enabled = enabled;
633 cmd.state = state_buffer;
634 if (cmd.parameter.statistics_enabled) {
635 cmd.result_state = memory_pool->Translate(
636 CpuAddr(&statistics), sizeof(LightLimiterInfo::StatisticsInternal));
637 } else {
638 cmd.result_state = 0;
639 }
640 cmd.workbuffer = workbuffer;
641 }
642 }
643
644 GenerateEnd<LightLimiterVersion2Command>(cmd);
645}
646
647void CommandBuffer::GenerateMultitapBiquadFilterCommand(const s32 node_id, VoiceInfo& voice_info,
648 const VoiceState& voice_state,
649 const s16 buffer_count, const s8 channel) {
650 auto& cmd{GenerateStart<MultiTapBiquadFilterCommand, CommandId::MultiTapBiquadFilter>(node_id)};
651
652 cmd.input = buffer_count + channel;
653 cmd.output = buffer_count + channel;
654 cmd.biquads = voice_info.biquads;
655
656 cmd.states[0] =
657 memory_pool->Translate(CpuAddr(voice_state.biquad_states[0].data()),
658 MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
659 cmd.states[1] =
660 memory_pool->Translate(CpuAddr(voice_state.biquad_states[1].data()),
661 MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
662
663 cmd.needs_init[0] = !voice_info.biquad_initialized[0];
664 cmd.needs_init[1] = !voice_info.biquad_initialized[1];
665 cmd.filter_tap_count = MaxBiquadFilters;
666
667 GenerateEnd<MultiTapBiquadFilterCommand>(cmd);
668}
669
670void CommandBuffer::GenerateCaptureCommand(const s32 node_id, EffectInfoBase& effect_info,
671 const s16 input_index, const s16 output_index,
672 const s16 buffer_offset, const u32 update_count,
673 const u32 count_max, const u32 write_offset) {
674 auto& cmd{GenerateStart<CaptureCommand, CommandId::Capture>(node_id)};
675
676 if (effect_info.GetSendBuffer()) {
677 cmd.input = buffer_offset + input_index;
678 cmd.output = buffer_offset + output_index;
679 cmd.send_buffer_info = effect_info.GetSendBufferInfo();
680 cmd.send_buffer = effect_info.GetSendBuffer();
681 cmd.count_max = count_max;
682 cmd.write_offset = write_offset;
683 cmd.update_count = update_count;
684 cmd.effect_enabled = effect_info.IsEnabled();
685 }
686
687 GenerateEnd<CaptureCommand>(cmd);
688}
689
690void CommandBuffer::GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info,
691 s32 node_id) {
692 auto& cmd{GenerateStart<CompressorCommand, CommandId::Compressor>(node_id)};
693
694 auto& parameter{
695 *reinterpret_cast<CompressorInfo::ParameterVersion2*>(effect_info.GetParameter())};
696 auto state{reinterpret_cast<CompressorInfo::State*>(effect_info.GetStateBuffer())};
697
698 if (IsChannelCountValid(parameter.channel_count)) {
699 auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(CompressorInfo::State))};
700 if (state_buffer) {
701 for (u16 channel = 0; channel < parameter.channel_count; channel++) {
702 cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
703 cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
704 }
705 cmd.parameter = parameter;
706 cmd.workbuffer = state_buffer;
707 cmd.enabled = effect_info.IsEnabled();
708 }
709 }
710
711 GenerateEnd<CompressorCommand>(cmd);
712}
713
714} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/command_buffer.h b/src/audio_core/renderer/command/command_buffer.h
new file mode 100644
index 000000000..496b0e50a
--- /dev/null
+++ b/src/audio_core/renderer/command/command_buffer.h
@@ -0,0 +1,466 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "audio_core/renderer/command/commands.h"
9#include "audio_core/renderer/effect/light_limiter.h"
10#include "audio_core/renderer/performance/performance_manager.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14struct UpsamplerInfo;
15struct VoiceState;
16class EffectInfoBase;
17class ICommandProcessingTimeEstimator;
18class MixInfo;
19class MemoryPoolInfo;
20class SinkInfoBase;
21class VoiceInfo;
22
23/**
24 * Utility functions to generate and add commands into the current command list.
25 */
26class CommandBuffer {
27public:
28 /**
29 * Generate a PCM s16 version 1 command, adding it to the command list.
30 *
31 * @param node_id - Node id of the voice this command is generated for.
32 * @param memory_pool - Memory pool for translating buffer addresses to the DSP.
33 * @param voice_info - The voice info this command is generated from.
34 * @param voice_state - The voice state the DSP will use for this command.
35 * @param buffer_count - Number of mix buffers in use,
36 * data will be read into this index + channel.
37 * @param channel - Channel index for this command.
38 */
39 void GeneratePcmInt16Version1Command(s32 node_id, const MemoryPoolInfo& memory_pool,
40 VoiceInfo& voice_info, const VoiceState& voice_state,
41 s16 buffer_count, s8 channel);
42
43 /**
44 * Generate a PCM s16 version 2 command, adding it to the command list.
45 *
46 * @param node_id - Node id of the voice this command is generated for.
47 * @param voice_info - The voice info this command is generated from.
48 * @param voice_state - The voice state the DSP will use for this command.
49 * @param buffer_count - Number of mix buffers in use,
50 * data will be read into this index + channel.
51 * @param channel - Channel index for this command.
52 */
53 void GeneratePcmInt16Version2Command(s32 node_id, VoiceInfo& voice_info,
54 const VoiceState& voice_state, s16 buffer_count,
55 s8 channel);
56
57 /**
58 * Generate a PCM f32 version 1 command, adding it to the command list.
59 *
60 * @param node_id - Node id of the voice this command is generated for.
61 * @param memory_pool - Memory pool for translating buffer addresses to the DSP.
62 * @param voice_info - The voice info this command is generated from.
63 * @param voice_state - The voice state the DSP will use for this command.
64 * @param buffer_count - Number of mix buffers in use,
65 * data will be read into this index + channel.
66 * @param channel - Channel index for this command.
67 */
68 void GeneratePcmFloatVersion1Command(s32 node_id, const MemoryPoolInfo& memory_pool,
69 VoiceInfo& voice_info, const VoiceState& voice_state,
70 s16 buffer_count, s8 channel);
71
72 /**
73 * Generate a PCM f32 version 2 command, adding it to the command list.
74 *
75 * @param node_id - Node id of the voice this command is generated for.
76 * @param voice_info - The voice info this command is generated from.
77 * @param voice_state - The voice state the DSP will use for this command.
78 * @param buffer_count - Number of mix buffers in use,
79 * data will be read into this index + channel.
80 * @param channel - Channel index for this command.
81 */
82 void GeneratePcmFloatVersion2Command(s32 node_id, VoiceInfo& voice_info,
83 const VoiceState& voice_state, s16 buffer_count,
84 s8 channel);
85
86 /**
87 * Generate an ADPCM version 1 command, adding it to the command list.
88 *
89 * @param node_id - Node id of the voice this command is generated for.
90 * @param memory_pool - Memory pool for translating buffer addresses to the DSP.
91 * @param voice_info - The voice info this command is generated from.
92 * @param voice_state - The voice state the DSP will use for this command.
93 * @param buffer_count - Number of mix buffers in use,
94 * data will be read into this index + channel.
95 * @param channel - Channel index for this command.
96 */
97 void GenerateAdpcmVersion1Command(s32 node_id, const MemoryPoolInfo& memory_pool,
98 VoiceInfo& voice_info, const VoiceState& voice_state,
99 s16 buffer_count, s8 channel);
100
101 /**
102 * Generate an ADPCM version 2 command, adding it to the command list.
103 *
104 * @param node_id - Node id of the voice this command is generated for.
105 * @param voice_info - The voice info this command is generated from.
106 * @param voice_state - The voice state the DSP will use for this command.
107 * @param buffer_count - Number of mix buffers in use,
108 * data will be read into this index + channel.
109 * @param channel - Channel index for this command.
110 */
111 void GenerateAdpcmVersion2Command(s32 node_id, VoiceInfo& voice_info,
112 const VoiceState& voice_state, s16 buffer_count, s8 channel);
113
114 /**
115 * Generate a volume command, adding it to the command list.
116 *
117 * @param node_id - Node id of the voice this command is generated for.
118 * @param buffer_offset - Base mix buffer index to generate this command at.
119 * @param input_index - Channel index and mix buffer offset for this command.
120 * @param volume - Mix volume added to the input samples.
121 * @param precision - Number of decimal bits for fixed point operations.
122 */
123 void GenerateVolumeCommand(s32 node_id, s16 buffer_offset, s16 input_index, f32 volume,
124 u8 precision);
125
126 /**
127 * Generate a volume ramp command, adding it to the command list.
128 *
129 * @param node_id - Node id of the voice this command is generated for.
130 * @param voice_info - The voice info this command takes its volumes from.
131 * @param buffer_count - Number of active mix buffers, command will generate at this index.
132 * @param precision - Number of decimal bits for fixed point operations.
133 */
134 void GenerateVolumeRampCommand(s32 node_id, VoiceInfo& voice_info, s16 buffer_count,
135 u8 precision);
136
137 /**
138 * Generate a biquad filter command from a voice, adding it to the command list.
139 *
140 * @param node_id - Node id of the voice this command is generated for.
141 * @param voice_info - The voice info this command takes biquad parameters from.
142 * @param voice_state - Used by the AudioRenderer to track previous samples.
143 * @param buffer_count - Number of active mix buffers,
144 * command will generate at this index + channel.
145 * @param channel - Channel index for this filter to work on.
146 * @param biquad_index - Which biquad filter to use for this command (0-1).
147 * @param use_float_processing - Should int or float processing be used?
148 */
149 void GenerateBiquadFilterCommand(s32 node_id, VoiceInfo& voice_info,
150 const VoiceState& voice_state, s16 buffer_count, s8 channel,
151 u32 biquad_index, bool use_float_processing);
152
153 /**
154 * Generate a biquad filter effect command, adding it to the command list.
155 *
156 * @param node_id - Node id of the voice this command is generated for.
157 * @param effect_info - The effect info this command takes biquad parameters from.
158 * @param buffer_offset - Mix buffer offset this command will use,
159 * command will generate at this index + channel.
160 * @param channel - Channel index for this filter to work on.
161 * @param needs_init - True if the biquad state needs initialisation.
162 * @param use_float_processing - Should int or float processing be used?
163 */
164 void GenerateBiquadFilterCommand(s32 node_id, EffectInfoBase& effect_info, s16 buffer_offset,
165 s8 channel, bool needs_init, bool use_float_processing);
166
167 /**
168 * Generate a mix command, adding it to the command list.
169 *
170 * @param node_id - Node id of the voice this command is generated for.
171 * @param input_index - Input mix buffer index for this command.
172 * Added to the buffer offset.
173 * @param output_index - Output mix buffer index for this command.
174 * Added to the buffer offset.
175 * @param buffer_offset - Mix buffer offset this command will use.
176 * @param volume - Volume to be applied to the input.
177 * @param precision - Number of decimal bits for fixed point operations.
178 */
179 void GenerateMixCommand(s32 node_id, s16 input_index, s16 output_index, s16 buffer_offset,
180 f32 volume, u8 precision);
181
182 /**
183 * Generate a mix ramp command, adding it to the command list.
184 *
185 * @param node_id - Node id of the voice this command is generated for.
186 * @param buffer_count - Number of active mix buffers.
187 * @param input_index - Input mix buffer index for this command.
188 * Added to buffer_count.
189 * @param output_index - Output mix buffer index for this command.
190 * Added to buffer_count.
191 * @param volume - Current mix volume used for calculating the ramp.
192 * @param prev_volume - Previous mix volume, used for calculating the ramp,
193 * also applied to the input.
194 * @param precision - Number of decimal bits for fixed point operations.
195 */
196 void GenerateMixRampCommand(s32 node_id, s16 buffer_count, s16 input_index, s16 output_index,
197 f32 volume, f32 prev_volume, CpuAddr prev_samples, u8 precision);
198
199 /**
200 * Generate a mix ramp grouped command, adding it to the command list.
201 *
202 * @param node_id - Node id of the voice this command is generated for.
203 * @param buffer_count - Number of active mix buffers.
204 * @param input_index - Input mix buffer index for this command.
205 * Added to buffer_count.
206 * @param output_index - Output mix buffer index for this command.
207 * Added to buffer_count.
208 * @param volumes - Current mix volumes used for calculating the ramp.
209 * @param prev_volumes - Previous mix volumes, used for calculating the ramp,
210 * also applied to the input.
211 * @param precision - Number of decimal bits for fixed point operations.
212 */
213 void GenerateMixRampGroupedCommand(s32 node_id, s16 buffer_count, s16 input_index,
214 s16 output_index, std::span<const f32> volumes,
215 std::span<const f32> prev_volumes, CpuAddr prev_samples,
216 u8 precision);
217
218 /**
219 * Generate a depop prepare command, adding it to the command list.
220 *
221 * @param node_id - Node id of the voice this command is generated for.
222 * @param voice_state - State to track the previous depop samples for each mix buffer.
223 * @param buffer - State to track the current depop samples for each mix buffer.
224 * @param buffer_count - Number of active mix buffers.
225 * @param buffer_offset - Base mix buffer index to generate the channel depops at.
226 * @param was_playing - Command only needs to work if the voice was previously playing.
227 */
228 void GenerateDepopPrepareCommand(s32 node_id, const VoiceState& voice_state,
229 std::span<const s32> buffer, s16 buffer_count,
230 s16 buffer_offset, bool was_playing);
231
232 /**
233 * Generate a depop command, adding it to the command list.
234 *
235 * @param node_id - Node id of the voice this command is generated for.
236 * @param mix_info - Mix info to get the buffer count and base offsets from.
237 * @param depop_buffer - Buffer of current depop sample values to be added to the input
238 * channels.
239 */
240 void GenerateDepopForMixBuffersCommand(s32 node_id, const MixInfo& mix_info,
241 std::span<const s32> depop_buffer);
242
243 /**
244 * Generate a delay command, adding it to the command list.
245 *
246 * @param node_id - Node id of the voice this command is generated for.
247 * @param effect_info - Delay effect info to generate this command from.
248 * @param buffer_offset - Base mix buffer offset to apply the apply the delay.
249 */
250 void GenerateDelayCommand(s32 node_id, EffectInfoBase& effect_info, s16 buffer_offset);
251
252 /**
253 * Generate an upsample command, adding it to the command list.
254 *
255 * @param node_id - Node id of the voice this command is generated for.
256 * @param buffer_offset - Base mix buffer offset to upsample.
257 * @param upsampler_info - Upsampler info to control the upsampling.
258 * @param input_count - Number of input channels to upsample.
259 * @param inputs - Input mix buffer indexes.
260 * @param buffer_count - Number of active mix buffers.
261 * @param sample_count - Source sample count of the input.
262 * @param sample_rate - Source sample rate of the input.
263 */
264 void GenerateUpsampleCommand(s32 node_id, s16 buffer_offset, UpsamplerInfo& upsampler_info,
265 u32 input_count, std::span<const s8> inputs, s16 buffer_count,
266 u32 sample_count, u32 sample_rate);
267
268 /**
269 * Generate a downmix 6 -> 2 command, adding it to the command list.
270 *
271 * @param node_id - Node id of the voice this command is generated for.
272 * @param inputs - Input mix buffer indexes.
273 * @param buffer_offset - Base mix buffer offset of the channels to downmix.
274 * @param downmix_coeff - Downmixing coefficients.
275 */
276 void GenerateDownMix6chTo2chCommand(s32 node_id, std::span<const s8> inputs, s16 buffer_offset,
277 std::span<const f32> downmix_coeff);
278
279 /**
280 * Generate an aux buffer command, adding it to the command list.
281 *
282 * @param node_id - Node id of the voice this command is generated for.
283 * @param effect_info - Aux effect info to generate this command from.
284 * @param input_index - Input mix buffer index for this command.
285 * Added to buffer_offset.
286 * @param output_index - Output mix buffer index for this command.
287 * Added to buffer_offset.
288 * @param buffer_offset - Base mix buffer offset to use.
289 * @param update_count - Number of samples to write back to the game as updated, can be 0.
290 * @param count_max - Maximum number of samples to read or write.
291 * @param write_offset - Current read or write offset within the buffer.
292 */
293 void GenerateAuxCommand(s32 node_id, EffectInfoBase& effect_info, s16 input_index,
294 s16 output_index, s16 buffer_offset, u32 update_count, u32 count_max,
295 u32 write_offset);
296
297 /**
298 * Generate a device sink command, adding it to the command list.
299 *
300 * @param node_id - Node id of the voice this command is generated for.
301 * @param buffer_offset - Base mix buffer offset to use.
302 * @param sink_info - The sink_info to generate this command from.
303 * @session_id - System session id this command is generated from.
304 * @samples_buffer - The buffer to be sent to the sink if upsampling is not used.
305 */
306 void GenerateDeviceSinkCommand(s32 node_id, s16 buffer_offset, SinkInfoBase& sink_info,
307 u32 session_id, std::span<s32> samples_buffer);
308
309 /**
310 * Generate a circular buffer sink command, adding it to the command list.
311 *
312 * @param node_id - Node id of the voice this command is generated for.
313 * @param sink_info - The sink_info to generate this command from.
314 * @param buffer_offset - Base mix buffer offset to use.
315 */
316 void GenerateCircularBufferSinkCommand(s32 node_id, SinkInfoBase& sink_info, s16 buffer_offset);
317
318 /**
319 * Generate a reverb command, adding it to the command list.
320 *
321 * @param node_id - Node id of the voice this command is generated for.
322 * @param effect_info - Reverb effect info to generate this command from.
323 * @param buffer_offset - Base mix buffer offset to use.
324 * @param long_size_pre_delay_supported - Should a longer pre-delay time be used before reverb
325 * begins?
326 */
327 void GenerateReverbCommand(s32 node_id, EffectInfoBase& effect_info, s16 buffer_offset,
328 bool long_size_pre_delay_supported);
329
330 /**
331 * Generate an I3DL2 reverb command, adding it to the command list.
332 *
333 * @param node_id - Node id of the voice this command is generated for.
334 * @param effect_info - I3DL2Reverb effect info to generate this command from.
335 * @param buffer_offset - Base mix buffer offset to use.
336 */
337 void GenerateI3dl2ReverbCommand(s32 node_id, EffectInfoBase& effect_info, s16 buffer_offset);
338
339 /**
340 * Generate a performance command, adding it to the command list.
341 *
342 * @param node_id - Node id of the voice this command is generated for.
343 * @param state - State of the performance.
344 * @param entry_addresses - The addresses to be filled in by the AudioRenderer.
345 */
346 void GeneratePerformanceCommand(s32 node_id, PerformanceState state,
347 const PerformanceEntryAddresses& entry_addresses);
348
349 /**
350 * Generate a clear mix command, adding it to the command list.
351 *
352 * @param node_id - Node id of the voice this command is generated for.
353 */
354 void GenerateClearMixCommand(s32 node_id);
355
356 /**
357 * Generate a copy mix command, adding it to the command list.
358 *
359 * @param node_id - Node id of the voice this command is generated for.
360 * @param effect_info - BiquadFilter effect info to generate this command from.
361 * @param buffer_offset - Base mix buffer offset to use.
362 * @param channel - Index to the effect's parameters input indexes for this command.
363 */
364 void GenerateCopyMixBufferCommand(s32 node_id, EffectInfoBase& effect_info, s16 buffer_offset,
365 s8 channel);
366
367 /**
368 * Generate a light limiter version 1 command, adding it to the command list.
369 *
370 * @param node_id - Node id of the voice this command is generated for.
371 * @param buffer_offset - Base mix buffer offset to use.
372 * @param parameter - Effect parameter to generate from.
373 * @param state - State used by the AudioRenderer between commands.
374 * @param enabled - Is this command enabled?
375 * @param workbuffer - Game-supplied memory for the state.
376 */
377 void GenerateLightLimiterCommand(s32 node_id, s16 buffer_offset,
378 const LightLimiterInfo::ParameterVersion1& parameter,
379 const LightLimiterInfo::State& state, bool enabled,
380 CpuAddr workbuffer);
381
382 /**
383 * Generate a light limiter version 2 command, adding it to the command list.
384 *
385 * @param node_id - Node id of the voice this command is generated for.
386 * @param buffer_offset - Base mix buffer offset to use.
387 * @param parameter - Effect parameter to generate from.
388 * @param statistics - Statistics reported by the AudioRenderer on the limiter's state.
389 * @param state - State used by the AudioRenderer between commands.
390 * @param enabled - Is this command enabled?
391 * @param workbuffer - Game-supplied memory for the state.
392 */
393 void GenerateLightLimiterCommand(s32 node_id, s16 buffer_offset,
394 const LightLimiterInfo::ParameterVersion2& parameter,
395 const LightLimiterInfo::StatisticsInternal& statistics,
396 const LightLimiterInfo::State& state, bool enabled,
397 CpuAddr workbuffer);
398
399 /**
400 * Generate a multitap biquad filter command, adding it to the command list.
401 *
402 * @param node_id - Node id of the voice this command is generated for.
403 * @param voice_info - The voice info this command takes biquad parameters from.
404 * @param voice_state - Used by the AudioRenderer to track previous samples.
405 * @param buffer_count - Number of active mix buffers,
406 * command will generate at this index + channel.
407 * @param channel - Channel index for this filter to work on.
408 */
409 void GenerateMultitapBiquadFilterCommand(s32 node_id, VoiceInfo& voice_info,
410 const VoiceState& voice_state, s16 buffer_count,
411 s8 channel);
412
413 /**
414 * Generate a capture command, adding it to the command list.
415 *
416 * @param node_id - Node id of the voice this command is generated for.
417 * @param effect_info - Capture effect info to generate this command from.
418 * @param input_index - Input mix buffer index for this command.
419 * Added to buffer_offset.
420 * @param output_index - Output mix buffer index for this command (unused).
421 * Added to buffer_offset.
422 * @param buffer_offset - Base mix buffer offset to use.
423 * @param update_count - Number of samples to write back to the game as updated, can be 0.
424 * @param count_max - Maximum number of samples to read or write.
425 * @param write_offset - Current read or write offset within the buffer.
426 */
427 void GenerateCaptureCommand(s32 node_id, EffectInfoBase& effect_info, s16 input_index,
428 s16 output_index, s16 buffer_offset, u32 update_count,
429 u32 count_max, u32 write_offset);
430
431 /**
432 * Generate a compressor command, adding it to the command list.
433 *
434 * @param buffer_offset - Base mix buffer offset to use.
435 * @param effect_info - Capture effect info to generate this command from.
436 * @param node_id - Node id of the voice this command is generated for.
437 */
438 void GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id);
439
440 /// Command list buffer generated commands will be added to
441 std::span<u8> command_list{};
442 /// Input sample count, unused
443 u32 sample_count{};
444 /// Input sample rate, unused
445 u32 sample_rate{};
446 /// Current size of the command buffer
447 u64 size{};
448 /// Current number of commands added
449 u32 count{};
450 /// Current estimated processing time for all commands
451 u32 estimated_process_time{};
452 /// Used for mapping buffers for the AudioRenderer
453 MemoryPoolInfo* memory_pool{};
454 /// Used for estimating command process times
455 ICommandProcessingTimeEstimator* time_estimator{};
456 /// Used to check which rendering features are currently enabled
457 BehaviorInfo* behavior{};
458
459private:
460 template <typename T, CommandId Id>
461 T& GenerateStart(const s32 node_id);
462 template <typename T>
463 void GenerateEnd(T& cmd);
464};
465
466} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/command_generator.cpp b/src/audio_core/renderer/command/command_generator.cpp
new file mode 100644
index 000000000..2ea50d128
--- /dev/null
+++ b/src/audio_core/renderer/command/command_generator.cpp
@@ -0,0 +1,796 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/common/audio_renderer_parameter.h"
5#include "audio_core/renderer/behavior/behavior_info.h"
6#include "audio_core/renderer/command/command_buffer.h"
7#include "audio_core/renderer/command/command_generator.h"
8#include "audio_core/renderer/command/command_list_header.h"
9#include "audio_core/renderer/effect/aux_.h"
10#include "audio_core/renderer/effect/biquad_filter.h"
11#include "audio_core/renderer/effect/buffer_mixer.h"
12#include "audio_core/renderer/effect/capture.h"
13#include "audio_core/renderer/effect/effect_context.h"
14#include "audio_core/renderer/effect/light_limiter.h"
15#include "audio_core/renderer/mix/mix_context.h"
16#include "audio_core/renderer/performance/detail_aspect.h"
17#include "audio_core/renderer/performance/entry_aspect.h"
18#include "audio_core/renderer/sink/device_sink_info.h"
19#include "audio_core/renderer/sink/sink_context.h"
20#include "audio_core/renderer/splitter/splitter_context.h"
21#include "audio_core/renderer/voice/voice_context.h"
22#include "common/alignment.h"
23
24namespace AudioCore::AudioRenderer {
25
26CommandGenerator::CommandGenerator(CommandBuffer& command_buffer_,
27 const CommandListHeader& command_list_header_,
28 const AudioRendererSystemContext& render_context_,
29 VoiceContext& voice_context_, MixContext& mix_context_,
30 EffectContext& effect_context_, SinkContext& sink_context_,
31 SplitterContext& splitter_context_,
32 PerformanceManager* performance_manager_)
33 : command_buffer{command_buffer_}, command_header{command_list_header_},
34 render_context{render_context_}, voice_context{voice_context_}, mix_context{mix_context_},
35 effect_context{effect_context_}, sink_context{sink_context_},
36 splitter_context{splitter_context_}, performance_manager{performance_manager_} {
37 command_buffer.GenerateClearMixCommand(InvalidNodeId);
38}
39
40void CommandGenerator::GenerateDataSourceCommand(VoiceInfo& voice_info,
41 const VoiceState& voice_state, const s8 channel) {
42 if (voice_info.mix_id == UnusedMixId) {
43 if (voice_info.splitter_id != UnusedSplitterId) {
44 auto destination{splitter_context.GetDesintationData(voice_info.splitter_id, 0)};
45 u32 dest_id{0};
46 while (destination != nullptr) {
47 if (destination->IsConfigured()) {
48 auto mix_id{destination->GetMixId()};
49 if (mix_id < mix_context.GetCount()) {
50 auto mix_info{mix_context.GetInfo(mix_id)};
51 command_buffer.GenerateDepopPrepareCommand(
52 voice_info.node_id, voice_state, render_context.depop_buffer,
53 mix_info->buffer_count, mix_info->buffer_offset,
54 voice_info.was_playing);
55 }
56 }
57 dest_id++;
58 destination = splitter_context.GetDesintationData(voice_info.splitter_id, dest_id);
59 }
60 }
61 } else {
62 auto mix_info{mix_context.GetInfo(voice_info.mix_id)};
63 command_buffer.GenerateDepopPrepareCommand(
64 voice_info.node_id, voice_state, render_context.depop_buffer, mix_info->buffer_count,
65 mix_info->buffer_offset, voice_info.was_playing);
66 }
67
68 if (voice_info.was_playing) {
69 return;
70 }
71
72 if (render_context.behavior->IsWaveBufferVer2Supported()) {
73 switch (voice_info.sample_format) {
74 case SampleFormat::PcmInt16:
75 command_buffer.GeneratePcmInt16Version2Command(
76 voice_info.node_id, voice_info, voice_state, render_context.mix_buffer_count,
77 channel);
78 break;
79 case SampleFormat::PcmFloat:
80 command_buffer.GeneratePcmFloatVersion2Command(
81 voice_info.node_id, voice_info, voice_state, render_context.mix_buffer_count,
82 channel);
83 break;
84 case SampleFormat::Adpcm:
85 command_buffer.GenerateAdpcmVersion2Command(voice_info.node_id, voice_info, voice_state,
86 render_context.mix_buffer_count, channel);
87 break;
88 default:
89 LOG_ERROR(Service_Audio, "Invalid SampleFormat {}",
90 static_cast<u32>(voice_info.sample_format));
91 break;
92 }
93 } else {
94 switch (voice_info.sample_format) {
95 case SampleFormat::PcmInt16:
96 command_buffer.GeneratePcmInt16Version1Command(
97 voice_info.node_id, *command_buffer.memory_pool, voice_info, voice_state,
98 render_context.mix_buffer_count, channel);
99 break;
100 case SampleFormat::PcmFloat:
101 command_buffer.GeneratePcmFloatVersion1Command(
102 voice_info.node_id, *command_buffer.memory_pool, voice_info, voice_state,
103 render_context.mix_buffer_count, channel);
104 break;
105 case SampleFormat::Adpcm:
106 command_buffer.GenerateAdpcmVersion1Command(
107 voice_info.node_id, *command_buffer.memory_pool, voice_info, voice_state,
108 render_context.mix_buffer_count, channel);
109 break;
110 default:
111 LOG_ERROR(Service_Audio, "Invalid SampleFormat {}",
112 static_cast<u32>(voice_info.sample_format));
113 break;
114 }
115 }
116}
117
118void CommandGenerator::GenerateVoiceMixCommand(std::span<const f32> mix_volumes,
119 std::span<const f32> prev_mix_volumes,
120 const VoiceState& voice_state, s16 output_index,
121 const s16 buffer_count, const s16 input_index,
122 const s32 node_id) {
123 u8 precision{15};
124 if (render_context.behavior->IsVolumeMixParameterPrecisionQ23Supported()) {
125 precision = 23;
126 }
127
128 if (buffer_count > 8) {
129 const auto prev_samples{render_context.memory_pool_info->Translate(
130 CpuAddr(voice_state.previous_samples.data()), buffer_count * sizeof(s32))};
131 command_buffer.GenerateMixRampGroupedCommand(node_id, buffer_count, input_index,
132 output_index, mix_volumes, prev_mix_volumes,
133 prev_samples, precision);
134 } else {
135 for (s16 i = 0; i < buffer_count; i++, output_index++) {
136 const auto prev_samples{render_context.memory_pool_info->Translate(
137 CpuAddr(&voice_state.previous_samples[i]), sizeof(s32))};
138
139 command_buffer.GenerateMixRampCommand(node_id, buffer_count, input_index, output_index,
140 mix_volumes[i], prev_mix_volumes[i], prev_samples,
141 precision);
142 }
143 }
144}
145
146void CommandGenerator::GenerateBiquadFilterCommandForVoice(VoiceInfo& voice_info,
147 const VoiceState& voice_state,
148 const s16 buffer_count, const s8 channel,
149 const s32 node_id) {
150 const bool both_biquads_enabled{voice_info.biquads[0].enabled && voice_info.biquads[1].enabled};
151 const auto use_float_processing{render_context.behavior->UseBiquadFilterFloatProcessing()};
152
153 if (both_biquads_enabled && render_context.behavior->UseMultiTapBiquadFilterProcessing() &&
154 use_float_processing) {
155 command_buffer.GenerateMultitapBiquadFilterCommand(node_id, voice_info, voice_state,
156 buffer_count, channel);
157 } else {
158 for (u32 i = 0; i < MaxBiquadFilters; i++) {
159 if (voice_info.biquads[i].enabled) {
160 command_buffer.GenerateBiquadFilterCommand(node_id, voice_info, voice_state,
161 buffer_count, channel, i,
162 use_float_processing);
163 }
164 }
165 }
166}
167
168void CommandGenerator::GenerateVoiceCommand(VoiceInfo& voice_info) {
169 u8 precision{15};
170 if (render_context.behavior->IsVolumeMixParameterPrecisionQ23Supported()) {
171 precision = 23;
172 }
173
174 for (s8 channel = 0; channel < voice_info.channel_count; channel++) {
175 const auto resource_id{voice_info.channel_resource_ids[channel]};
176 auto& voice_state{voice_context.GetDspSharedState(resource_id)};
177 auto& channel_resource{voice_context.GetChannelResource(resource_id)};
178
179 PerformanceDetailType detail_type{PerformanceDetailType::Invalid};
180 switch (voice_info.sample_format) {
181 case SampleFormat::PcmInt16:
182 detail_type = PerformanceDetailType::Unk1;
183 break;
184 case SampleFormat::PcmFloat:
185 detail_type = PerformanceDetailType::Unk10;
186 break;
187 default:
188 detail_type = PerformanceDetailType::Unk2;
189 break;
190 }
191
192 DetailAspect data_source_detail(*this, PerformanceEntryType::Voice, voice_info.node_id,
193 detail_type);
194 GenerateDataSourceCommand(voice_info, voice_state, channel);
195
196 if (data_source_detail.initialized) {
197 command_buffer.GeneratePerformanceCommand(data_source_detail.node_id,
198 PerformanceState::Stop,
199 data_source_detail.performance_entry_address);
200 }
201
202 if (voice_info.was_playing) {
203 voice_info.prev_volume = 0.0f;
204 continue;
205 }
206
207 if (!voice_info.HasAnyConnection()) {
208 continue;
209 }
210
211 DetailAspect biquad_detail_aspect(*this, PerformanceEntryType::Voice, voice_info.node_id,
212 PerformanceDetailType::Unk4);
213 GenerateBiquadFilterCommandForVoice(
214 voice_info, voice_state, render_context.mix_buffer_count, channel, voice_info.node_id);
215
216 if (biquad_detail_aspect.initialized) {
217 command_buffer.GeneratePerformanceCommand(
218 biquad_detail_aspect.node_id, PerformanceState::Stop,
219 biquad_detail_aspect.performance_entry_address);
220 }
221
222 DetailAspect volume_ramp_detail_aspect(*this, PerformanceEntryType::Voice,
223 voice_info.node_id, PerformanceDetailType::Unk3);
224 command_buffer.GenerateVolumeRampCommand(
225 voice_info.node_id, voice_info, render_context.mix_buffer_count + channel, precision);
226 if (volume_ramp_detail_aspect.initialized) {
227 command_buffer.GeneratePerformanceCommand(
228 volume_ramp_detail_aspect.node_id, PerformanceState::Stop,
229 volume_ramp_detail_aspect.performance_entry_address);
230 }
231
232 voice_info.prev_volume = voice_info.volume;
233
234 if (voice_info.mix_id == UnusedMixId) {
235 if (voice_info.splitter_id != UnusedSplitterId) {
236 auto i{channel};
237 auto destination{splitter_context.GetDesintationData(voice_info.splitter_id, i)};
238 while (destination != nullptr) {
239 if (destination->IsConfigured()) {
240 const auto mix_id{destination->GetMixId()};
241 if (mix_id < mix_context.GetCount() &&
242 static_cast<s32>(mix_id) != UnusedSplitterId) {
243 auto mix_info{mix_context.GetInfo(mix_id)};
244 GenerateVoiceMixCommand(
245 destination->GetMixVolume(), destination->GetMixVolumePrev(),
246 voice_state, mix_info->buffer_offset, mix_info->buffer_count,
247 render_context.mix_buffer_count + channel, voice_info.node_id);
248 destination->MarkAsNeedToUpdateInternalState();
249 }
250 }
251 i += voice_info.channel_count;
252 destination = splitter_context.GetDesintationData(voice_info.splitter_id, i);
253 }
254 }
255 } else {
256 DetailAspect volume_mix_detail_aspect(*this, PerformanceEntryType::Voice,
257 voice_info.node_id, PerformanceDetailType::Unk3);
258 auto mix_info{mix_context.GetInfo(voice_info.mix_id)};
259 GenerateVoiceMixCommand(channel_resource.mix_volumes, channel_resource.prev_mix_volumes,
260 voice_state, mix_info->buffer_offset, mix_info->buffer_count,
261 render_context.mix_buffer_count + channel, voice_info.node_id);
262 if (volume_mix_detail_aspect.initialized) {
263 command_buffer.GeneratePerformanceCommand(
264 volume_mix_detail_aspect.node_id, PerformanceState::Stop,
265 volume_mix_detail_aspect.performance_entry_address);
266 }
267
268 channel_resource.prev_mix_volumes = channel_resource.mix_volumes;
269 }
270 voice_info.biquad_initialized[0] = voice_info.biquads[0].enabled;
271 voice_info.biquad_initialized[1] = voice_info.biquads[1].enabled;
272 }
273}
274
275void CommandGenerator::GenerateVoiceCommands() {
276 const auto voice_count{voice_context.GetCount()};
277
278 for (u32 i = 0; i < voice_count; i++) {
279 auto sorted_info{voice_context.GetSortedInfo(i)};
280
281 if (sorted_info->ShouldSkip() || !sorted_info->UpdateForCommandGeneration(voice_context)) {
282 continue;
283 }
284
285 EntryAspect voice_entry_aspect(*this, PerformanceEntryType::Voice, sorted_info->node_id);
286
287 GenerateVoiceCommand(*sorted_info);
288
289 if (voice_entry_aspect.initialized) {
290 command_buffer.GeneratePerformanceCommand(voice_entry_aspect.node_id,
291 PerformanceState::Stop,
292 voice_entry_aspect.performance_entry_address);
293 }
294 }
295
296 splitter_context.UpdateInternalState();
297}
298
299void CommandGenerator::GenerateBufferMixerCommand(const s16 buffer_offset,
300 EffectInfoBase& effect_info, const s32 node_id) {
301 u8 precision{15};
302 if (render_context.behavior->IsVolumeMixParameterPrecisionQ23Supported()) {
303 precision = 23;
304 }
305
306 if (effect_info.IsEnabled()) {
307 const auto& parameter{
308 *reinterpret_cast<BufferMixerInfo::ParameterVersion1*>(effect_info.GetParameter())};
309 for (u32 i = 0; i < parameter.mix_count; i++) {
310 if (parameter.volumes[i] != 0.0f) {
311 command_buffer.GenerateMixCommand(node_id, buffer_offset + parameter.inputs[i],
312 buffer_offset + parameter.outputs[i],
313 buffer_offset, parameter.volumes[i], precision);
314 }
315 }
316 }
317}
318
319void CommandGenerator::GenerateDelayCommand(const s16 buffer_offset, EffectInfoBase& effect_info,
320 const s32 node_id) {
321 command_buffer.GenerateDelayCommand(node_id, effect_info, buffer_offset);
322}
323
324void CommandGenerator::GenerateReverbCommand(const s16 buffer_offset, EffectInfoBase& effect_info,
325 const s32 node_id,
326 const bool long_size_pre_delay_supported) {
327 command_buffer.GenerateReverbCommand(node_id, effect_info, buffer_offset,
328 long_size_pre_delay_supported);
329}
330
331void CommandGenerator::GenerateI3dl2ReverbEffectCommand(const s16 buffer_offset,
332 EffectInfoBase& effect_info,
333 const s32 node_id) {
334 command_buffer.GenerateI3dl2ReverbCommand(node_id, effect_info, buffer_offset);
335}
336
337void CommandGenerator::GenerateAuxCommand(const s16 buffer_offset, EffectInfoBase& effect_info,
338 const s32 node_id) {
339
340 if (effect_info.IsEnabled()) {
341 effect_info.GetWorkbuffer(0);
342 effect_info.GetWorkbuffer(1);
343 }
344
345 if (effect_info.GetSendBuffer() != 0 && effect_info.GetReturnBuffer() != 0) {
346 const auto& parameter{
347 *reinterpret_cast<AuxInfo::ParameterVersion1*>(effect_info.GetParameter())};
348 auto channel_index{parameter.mix_buffer_count - 1};
349 u32 write_offset{0};
350 for (u32 i = 0; i < parameter.mix_buffer_count; i++, channel_index--) {
351 auto new_update_count{command_header.sample_count + write_offset};
352 const auto update_count{channel_index > 0 ? 0 : new_update_count};
353 command_buffer.GenerateAuxCommand(node_id, effect_info, parameter.inputs[i],
354 parameter.outputs[i], buffer_offset, update_count,
355 parameter.count_max, write_offset);
356 write_offset = new_update_count;
357 }
358 }
359}
360
361void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset,
362 EffectInfoBase& effect_info,
363 const s32 node_id) {
364 const auto& parameter{
365 *reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
366 if (effect_info.IsEnabled()) {
367 bool needs_init{false};
368
369 switch (parameter.state) {
370 case EffectInfoBase::ParameterState::Initialized:
371 needs_init = true;
372 break;
373 case EffectInfoBase::ParameterState::Updating:
374 case EffectInfoBase::ParameterState::Updated:
375 if (render_context.behavior->IsBiquadFilterEffectStateClearBugFixed()) {
376 needs_init = false;
377 } else {
378 needs_init = parameter.state == EffectInfoBase::ParameterState::Updating;
379 }
380 break;
381 default:
382 LOG_ERROR(Service_Audio, "Invalid biquad parameter state {}",
383 static_cast<u32>(parameter.state));
384 break;
385 }
386
387 for (s8 channel = 0; channel < parameter.channel_count; channel++) {
388 command_buffer.GenerateBiquadFilterCommand(
389 node_id, effect_info, buffer_offset, channel, needs_init,
390 render_context.behavior->UseBiquadFilterFloatProcessing());
391 }
392 } else {
393 for (s8 channel = 0; channel < parameter.channel_count; channel++) {
394 command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset,
395 channel);
396 }
397 }
398}
399
400void CommandGenerator::GenerateLightLimiterEffectCommand(const s16 buffer_offset,
401 EffectInfoBase& effect_info,
402 const s32 node_id,
403 const u32 effect_index) {
404
405 const auto& state{*reinterpret_cast<LightLimiterInfo::State*>(effect_info.GetStateBuffer())};
406
407 if (render_context.behavior->IsEffectInfoVersion2Supported()) {
408 const auto& parameter{
409 *reinterpret_cast<LightLimiterInfo::ParameterVersion2*>(effect_info.GetParameter())};
410 const auto& result_state{*reinterpret_cast<LightLimiterInfo::StatisticsInternal*>(
411 &effect_context.GetDspSharedResultState(effect_index))};
412 command_buffer.GenerateLightLimiterCommand(node_id, buffer_offset, parameter, result_state,
413 state, effect_info.IsEnabled(),
414 effect_info.GetWorkbuffer(-1));
415 } else {
416 const auto& parameter{
417 *reinterpret_cast<LightLimiterInfo::ParameterVersion1*>(effect_info.GetParameter())};
418 command_buffer.GenerateLightLimiterCommand(node_id, buffer_offset, parameter, state,
419 effect_info.IsEnabled(),
420 effect_info.GetWorkbuffer(-1));
421 }
422}
423
424void CommandGenerator::GenerateCaptureCommand(const s16 buffer_offset, EffectInfoBase& effect_info,
425 const s32 node_id) {
426 if (effect_info.IsEnabled()) {
427 effect_info.GetWorkbuffer(0);
428 }
429
430 if (effect_info.GetSendBuffer()) {
431 const auto& parameter{
432 *reinterpret_cast<AuxInfo::ParameterVersion1*>(effect_info.GetParameter())};
433 auto channel_index{parameter.mix_buffer_count - 1};
434 u32 write_offset{0};
435 for (u32 i = 0; i < parameter.mix_buffer_count; i++, channel_index--) {
436 auto new_update_count{command_header.sample_count + write_offset};
437 const auto update_count{channel_index > 0 ? 0 : new_update_count};
438 command_buffer.GenerateCaptureCommand(node_id, effect_info, parameter.inputs[i],
439 parameter.outputs[i], buffer_offset, update_count,
440 parameter.count_max, write_offset);
441 write_offset = new_update_count;
442 }
443 }
444}
445
446void CommandGenerator::GenerateCompressorCommand(const s16 buffer_offset,
447 EffectInfoBase& effect_info, const s32 node_id) {
448 command_buffer.GenerateCompressorCommand(buffer_offset, effect_info, node_id);
449}
450
451void CommandGenerator::GenerateEffectCommand(MixInfo& mix_info) {
452 const auto effect_count{effect_context.GetCount()};
453 for (u32 i = 0; i < effect_count; i++) {
454 const auto effect_index{mix_info.effect_order_buffer[i]};
455 if (effect_index == -1) {
456 break;
457 }
458
459 auto& effect_info = effect_context.GetInfo(effect_index);
460 if (effect_info.ShouldSkip()) {
461 continue;
462 }
463
464 const auto entry_type{mix_info.mix_id == FinalMixId ? PerformanceEntryType::FinalMix
465 : PerformanceEntryType::SubMix};
466
467 switch (effect_info.GetType()) {
468 case EffectInfoBase::Type::Mix: {
469 DetailAspect mix_detail_aspect(*this, entry_type, mix_info.node_id,
470 PerformanceDetailType::Unk5);
471 GenerateBufferMixerCommand(mix_info.buffer_offset, effect_info, mix_info.node_id);
472 if (mix_detail_aspect.initialized) {
473 command_buffer.GeneratePerformanceCommand(
474 mix_detail_aspect.node_id, PerformanceState::Stop,
475 mix_detail_aspect.performance_entry_address);
476 }
477 } break;
478
479 case EffectInfoBase::Type::Aux: {
480 DetailAspect aux_detail_aspect(*this, entry_type, mix_info.node_id,
481 PerformanceDetailType::Unk7);
482 GenerateAuxCommand(mix_info.buffer_offset, effect_info, mix_info.node_id);
483 if (aux_detail_aspect.initialized) {
484 command_buffer.GeneratePerformanceCommand(
485 aux_detail_aspect.node_id, PerformanceState::Stop,
486 aux_detail_aspect.performance_entry_address);
487 }
488 } break;
489
490 case EffectInfoBase::Type::Delay: {
491 DetailAspect delay_detail_aspect(*this, entry_type, mix_info.node_id,
492 PerformanceDetailType::Unk6);
493 GenerateDelayCommand(mix_info.buffer_offset, effect_info, mix_info.node_id);
494 if (delay_detail_aspect.initialized) {
495 command_buffer.GeneratePerformanceCommand(
496 delay_detail_aspect.node_id, PerformanceState::Stop,
497 delay_detail_aspect.performance_entry_address);
498 }
499 } break;
500
501 case EffectInfoBase::Type::Reverb: {
502 DetailAspect reverb_detail_aspect(*this, entry_type, mix_info.node_id,
503 PerformanceDetailType::Unk8);
504 GenerateReverbCommand(mix_info.buffer_offset, effect_info, mix_info.node_id,
505 render_context.behavior->IsLongSizePreDelaySupported());
506 if (reverb_detail_aspect.initialized) {
507 command_buffer.GeneratePerformanceCommand(
508 reverb_detail_aspect.node_id, PerformanceState::Stop,
509 reverb_detail_aspect.performance_entry_address);
510 }
511 } break;
512
513 case EffectInfoBase::Type::I3dl2Reverb: {
514 DetailAspect i3dl2_detail_aspect(*this, entry_type, mix_info.node_id,
515 PerformanceDetailType::Unk9);
516 GenerateI3dl2ReverbEffectCommand(mix_info.buffer_offset, effect_info, mix_info.node_id);
517 if (i3dl2_detail_aspect.initialized) {
518 command_buffer.GeneratePerformanceCommand(
519 i3dl2_detail_aspect.node_id, PerformanceState::Stop,
520 i3dl2_detail_aspect.performance_entry_address);
521 }
522 } break;
523
524 case EffectInfoBase::Type::BiquadFilter: {
525 DetailAspect biquad_detail_aspect(*this, entry_type, mix_info.node_id,
526 PerformanceDetailType::Unk4);
527 GenerateBiquadFilterEffectCommand(mix_info.buffer_offset, effect_info,
528 mix_info.node_id);
529 if (biquad_detail_aspect.initialized) {
530 command_buffer.GeneratePerformanceCommand(
531 biquad_detail_aspect.node_id, PerformanceState::Stop,
532 biquad_detail_aspect.performance_entry_address);
533 }
534 } break;
535
536 case EffectInfoBase::Type::LightLimiter: {
537 DetailAspect light_limiter_detail_aspect(*this, entry_type, mix_info.node_id,
538 PerformanceDetailType::Unk11);
539 GenerateLightLimiterEffectCommand(mix_info.buffer_offset, effect_info, mix_info.node_id,
540 effect_index);
541 if (light_limiter_detail_aspect.initialized) {
542 command_buffer.GeneratePerformanceCommand(
543 light_limiter_detail_aspect.node_id, PerformanceState::Stop,
544 light_limiter_detail_aspect.performance_entry_address);
545 }
546 } break;
547
548 case EffectInfoBase::Type::Capture: {
549 DetailAspect capture_detail_aspect(*this, entry_type, mix_info.node_id,
550 PerformanceDetailType::Unk12);
551 GenerateCaptureCommand(mix_info.buffer_offset, effect_info, mix_info.node_id);
552 if (capture_detail_aspect.initialized) {
553 command_buffer.GeneratePerformanceCommand(
554 capture_detail_aspect.node_id, PerformanceState::Stop,
555 capture_detail_aspect.performance_entry_address);
556 }
557 } break;
558
559 case EffectInfoBase::Type::Compressor: {
560 DetailAspect capture_detail_aspect(*this, entry_type, mix_info.node_id,
561 PerformanceDetailType::Unk13);
562 GenerateCompressorCommand(mix_info.buffer_offset, effect_info, mix_info.node_id);
563 if (capture_detail_aspect.initialized) {
564 command_buffer.GeneratePerformanceCommand(
565 capture_detail_aspect.node_id, PerformanceState::Stop,
566 capture_detail_aspect.performance_entry_address);
567 }
568 } break;
569
570 default:
571 LOG_ERROR(Service_Audio, "Invalid effect type {}",
572 static_cast<u32>(effect_info.GetType()));
573 break;
574 }
575
576 effect_info.UpdateForCommandGeneration();
577 }
578}
579
580void CommandGenerator::GenerateMixCommands(MixInfo& mix_info) {
581 u8 precision{15};
582 if (render_context.behavior->IsVolumeMixParameterPrecisionQ23Supported()) {
583 precision = 23;
584 }
585
586 if (!mix_info.HasAnyConnection()) {
587 return;
588 }
589
590 if (mix_info.dst_mix_id == UnusedMixId) {
591 if (mix_info.dst_splitter_id != UnusedSplitterId) {
592 s16 dest_id{0};
593 auto destination{
594 splitter_context.GetDesintationData(mix_info.dst_splitter_id, dest_id)};
595 while (destination != nullptr) {
596 if (destination->IsConfigured()) {
597 auto splitter_mix_id{destination->GetMixId()};
598 if (splitter_mix_id < mix_context.GetCount()) {
599 auto splitter_mix_info{mix_context.GetInfo(splitter_mix_id)};
600 const s16 input_index{static_cast<s16>(mix_info.buffer_offset +
601 (dest_id % mix_info.buffer_count))};
602 for (s16 i = 0; i < splitter_mix_info->buffer_count; i++) {
603 auto volume{mix_info.volume * destination->GetMixVolume(i)};
604 if (volume != 0.0f) {
605 command_buffer.GenerateMixCommand(
606 mix_info.node_id, input_index,
607 splitter_mix_info->buffer_offset + i, mix_info.buffer_offset,
608 volume, precision);
609 }
610 }
611 }
612 }
613 dest_id++;
614 destination =
615 splitter_context.GetDesintationData(mix_info.dst_splitter_id, dest_id);
616 }
617 }
618 } else {
619 auto dest_mix_info{mix_context.GetInfo(mix_info.dst_mix_id)};
620 for (s16 i = 0; i < mix_info.buffer_count; i++) {
621 for (s16 j = 0; j < dest_mix_info->buffer_count; j++) {
622 auto volume{mix_info.volume * mix_info.mix_volumes[i][j]};
623 if (volume != 0.0f) {
624 command_buffer.GenerateMixCommand(mix_info.node_id, mix_info.buffer_offset + i,
625 dest_mix_info->buffer_offset + j,
626 mix_info.buffer_offset, volume, precision);
627 }
628 }
629 }
630 }
631}
632
633void CommandGenerator::GenerateSubMixCommand(MixInfo& mix_info) {
634 command_buffer.GenerateDepopForMixBuffersCommand(mix_info.node_id, mix_info,
635 render_context.depop_buffer);
636 GenerateEffectCommand(mix_info);
637
638 DetailAspect mix_detail_aspect(*this, PerformanceEntryType::SubMix, mix_info.node_id,
639 PerformanceDetailType::Unk5);
640
641 GenerateMixCommands(mix_info);
642
643 if (mix_detail_aspect.initialized) {
644 command_buffer.GeneratePerformanceCommand(mix_detail_aspect.node_id, PerformanceState::Stop,
645 mix_detail_aspect.performance_entry_address);
646 }
647}
648
649void CommandGenerator::GenerateSubMixCommands() {
650 const auto submix_count{mix_context.GetCount()};
651 for (s32 i = 0; i < submix_count; i++) {
652 auto sorted_info{mix_context.GetSortedInfo(i)};
653 if (!sorted_info->in_use || sorted_info->mix_id == FinalMixId) {
654 continue;
655 }
656
657 EntryAspect submix_entry_aspect(*this, PerformanceEntryType::SubMix, sorted_info->node_id);
658
659 GenerateSubMixCommand(*sorted_info);
660
661 if (submix_entry_aspect.initialized) {
662 command_buffer.GeneratePerformanceCommand(
663 submix_entry_aspect.node_id, PerformanceState::Stop,
664 submix_entry_aspect.performance_entry_address);
665 }
666 }
667}
668
669void CommandGenerator::GenerateFinalMixCommand() {
670 auto& final_mix_info{*mix_context.GetFinalMixInfo()};
671
672 command_buffer.GenerateDepopForMixBuffersCommand(final_mix_info.node_id, final_mix_info,
673 render_context.depop_buffer);
674 GenerateEffectCommand(final_mix_info);
675
676 u8 precision{15};
677 if (render_context.behavior->IsVolumeMixParameterPrecisionQ23Supported()) {
678 precision = 23;
679 }
680
681 for (s16 i = 0; i < final_mix_info.buffer_count; i++) {
682 DetailAspect volume_aspect(*this, PerformanceEntryType::FinalMix, final_mix_info.node_id,
683 PerformanceDetailType::Unk3);
684 command_buffer.GenerateVolumeCommand(final_mix_info.node_id, final_mix_info.buffer_offset,
685 i, final_mix_info.volume, precision);
686 if (volume_aspect.initialized) {
687 command_buffer.GeneratePerformanceCommand(volume_aspect.node_id, PerformanceState::Stop,
688 volume_aspect.performance_entry_address);
689 }
690 }
691}
692
693void CommandGenerator::GenerateFinalMixCommands() {
694 auto final_mix_info{mix_context.GetFinalMixInfo()};
695 EntryAspect final_mix_entry(*this, PerformanceEntryType::FinalMix, final_mix_info->node_id);
696 GenerateFinalMixCommand();
697 if (final_mix_entry.initialized) {
698 command_buffer.GeneratePerformanceCommand(final_mix_entry.node_id, PerformanceState::Stop,
699 final_mix_entry.performance_entry_address);
700 }
701}
702
703void CommandGenerator::GenerateSinkCommands() {
704 const auto sink_count{sink_context.GetCount()};
705
706 for (u32 i = 0; i < sink_count; i++) {
707 auto sink_info{sink_context.GetInfo(i)};
708 if (sink_info->IsUsed() && sink_info->GetType() == SinkInfoBase::Type::DeviceSink) {
709 auto state{reinterpret_cast<DeviceSinkInfo::DeviceState*>(sink_info->GetState())};
710 if (command_header.sample_rate != TargetSampleRate &&
711 state->upsampler_info == nullptr) {
712 auto device_state{sink_info->GetDeviceState()};
713 device_state->upsampler_info = render_context.upsampler_manager->Allocate();
714 }
715
716 EntryAspect device_sink_entry(*this, PerformanceEntryType::Sink,
717 sink_info->GetNodeId());
718 auto final_mix{mix_context.GetFinalMixInfo()};
719 GenerateSinkCommand(final_mix->buffer_offset, *sink_info);
720
721 if (device_sink_entry.initialized) {
722 command_buffer.GeneratePerformanceCommand(
723 device_sink_entry.node_id, PerformanceState::Stop,
724 device_sink_entry.performance_entry_address);
725 }
726 }
727 }
728
729 for (u32 i = 0; i < sink_count; i++) {
730 auto sink_info{sink_context.GetInfo(i)};
731 if (sink_info->IsUsed() && sink_info->GetType() == SinkInfoBase::Type::CircularBufferSink) {
732 EntryAspect circular_buffer_entry(*this, PerformanceEntryType::Sink,
733 sink_info->GetNodeId());
734 auto final_mix{mix_context.GetFinalMixInfo()};
735 GenerateSinkCommand(final_mix->buffer_offset, *sink_info);
736
737 if (circular_buffer_entry.initialized) {
738 command_buffer.GeneratePerformanceCommand(
739 circular_buffer_entry.node_id, PerformanceState::Stop,
740 circular_buffer_entry.performance_entry_address);
741 }
742 }
743 }
744}
745
746void CommandGenerator::GenerateSinkCommand(const s16 buffer_offset, SinkInfoBase& sink_info) {
747 if (sink_info.ShouldSkip()) {
748 return;
749 }
750
751 switch (sink_info.GetType()) {
752 case SinkInfoBase::Type::DeviceSink:
753 GenerateDeviceSinkCommand(buffer_offset, sink_info);
754 break;
755
756 case SinkInfoBase::Type::CircularBufferSink:
757 command_buffer.GenerateCircularBufferSinkCommand(sink_info.GetNodeId(), sink_info,
758 buffer_offset);
759 break;
760
761 default:
762 LOG_ERROR(Service_Audio, "Invalid sink type {}", static_cast<u32>(sink_info.GetType()));
763 break;
764 }
765
766 sink_info.UpdateForCommandGeneration();
767}
768
769void CommandGenerator::GenerateDeviceSinkCommand(const s16 buffer_offset, SinkInfoBase& sink_info) {
770 auto& parameter{
771 *reinterpret_cast<DeviceSinkInfo::DeviceInParameter*>(sink_info.GetParameter())};
772 auto state{*reinterpret_cast<DeviceSinkInfo::DeviceState*>(sink_info.GetState())};
773
774 if (render_context.channels == 2 && parameter.downmix_enabled) {
775 command_buffer.GenerateDownMix6chTo2chCommand(InvalidNodeId, parameter.inputs,
776 buffer_offset, parameter.downmix_coeff);
777 }
778
779 if (state.upsampler_info != nullptr) {
780 command_buffer.GenerateUpsampleCommand(
781 InvalidNodeId, buffer_offset, *state.upsampler_info, parameter.input_count,
782 parameter.inputs, command_header.buffer_count, command_header.sample_count,
783 command_header.sample_rate);
784 }
785
786 command_buffer.GenerateDeviceSinkCommand(InvalidNodeId, buffer_offset, sink_info,
787 render_context.session_id,
788 command_header.samples_buffer);
789}
790
791void CommandGenerator::GeneratePerformanceCommand(
792 s32 node_id, PerformanceState state, const PerformanceEntryAddresses& entry_addresses) {
793 command_buffer.GeneratePerformanceCommand(node_id, state, entry_addresses);
794}
795
796} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/command_generator.h b/src/audio_core/renderer/command/command_generator.h
new file mode 100644
index 000000000..d80d9b0d8
--- /dev/null
+++ b/src/audio_core/renderer/command/command_generator.h
@@ -0,0 +1,349 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "audio_core/renderer/command/commands.h"
9#include "audio_core/renderer/performance/performance_manager.h"
10#include "common/common_types.h"
11
12namespace AudioCore {
13struct AudioRendererSystemContext;
14
15namespace AudioRenderer {
16class CommandBuffer;
17struct CommandListHeader;
18class VoiceContext;
19class MixContext;
20class EffectContext;
21class SplitterContext;
22class SinkContext;
23class BehaviorInfo;
24class VoiceInfo;
25struct VoiceState;
26class MixInfo;
27class SinkInfoBase;
28
29/**
30 * Generates all commands to build up a command list, which are sent to the AudioRender for
31 * processing.
32 */
33class CommandGenerator {
34public:
35 explicit CommandGenerator(CommandBuffer& command_buffer,
36 const CommandListHeader& command_list_header,
37 const AudioRendererSystemContext& render_context,
38 VoiceContext& voice_context, MixContext& mix_context,
39 EffectContext& effect_context, SinkContext& sink_context,
40 SplitterContext& splitter_context,
41 PerformanceManager* performance_manager);
42
43 /**
44 * Calculate the buffer size needed for commands.
45 *
46 * @param behavior - Used to check what features are enabled.
47 * @param params - Input rendering parameters for numbers of voices/mixes/sinks etc.
48 */
49 static u64 CalculateCommandBufferSize(const BehaviorInfo& behavior,
50 const AudioRendererParameterInternal& params) {
51 u64 size{0};
52
53 // Effects
54 size += params.effects * sizeof(EffectInfoBase);
55
56 // Voices
57 u64 voice_size{0};
58 if (behavior.IsWaveBufferVer2Supported()) {
59 voice_size = std::max(std::max(sizeof(AdpcmDataSourceVersion2Command),
60 sizeof(PcmInt16DataSourceVersion2Command)),
61 sizeof(PcmFloatDataSourceVersion2Command));
62 } else {
63 voice_size = std::max(std::max(sizeof(AdpcmDataSourceVersion1Command),
64 sizeof(PcmInt16DataSourceVersion1Command)),
65 sizeof(PcmFloatDataSourceVersion1Command));
66 }
67 voice_size += sizeof(BiquadFilterCommand) * MaxBiquadFilters;
68 voice_size += sizeof(VolumeRampCommand);
69 voice_size += sizeof(MixRampGroupedCommand);
70
71 size += params.voices * (params.splitter_infos * sizeof(DepopPrepareCommand) + voice_size);
72
73 // Sub mixes
74 size += sizeof(DepopForMixBuffersCommand) +
75 (sizeof(MixCommand) * MaxMixBuffers) * MaxMixBuffers;
76
77 // Final mix
78 size += sizeof(DepopForMixBuffersCommand) + sizeof(VolumeCommand) * MaxMixBuffers;
79
80 // Splitters
81 size += params.splitter_destinations * sizeof(MixRampCommand) * MaxMixBuffers;
82
83 // Sinks
84 size +=
85 params.sinks * std::max(sizeof(DeviceSinkCommand), sizeof(CircularBufferSinkCommand));
86
87 // Performance
88 size += (params.effects + params.voices + params.sinks + params.sub_mixes + 1 +
89 PerformanceManager::MaxDetailEntries) *
90 sizeof(PerformanceCommand);
91 return size;
92 }
93
94 /**
95 * Get the current command buffer used to generate commands.
96 *
97 * @return The command buffer.
98 */
99 CommandBuffer& GetCommandBuffer() {
100 return command_buffer;
101 }
102
103 /**
104 * Get the current performance manager,
105 *
106 * @return The performance manager. May be nullptr.
107 */
108 PerformanceManager* GetPerformanceManager() {
109 return performance_manager;
110 }
111
112 /**
113 * Generate a data source command.
114 * These are the basis for all audio output.
115 *
116 * @param voice_info - Generate the command from this voice.
117 * @param voice_state - State used by the AudioRenderer across calls.
118 * @param channel - Channel index to generate the command into.
119 */
120 void GenerateDataSourceCommand(VoiceInfo& voice_info, const VoiceState& voice_state,
121 s8 channel);
122
123 /**
124 * Generate voice mixing commands.
125 * These are used to mix buffers together, to mix one input to many outputs,
126 * and also used as copy commands to move data around and prevent it being accidentally
127 * overwritten, e.g by another data source command into the same channel.
128 *
129 * @param mix_volumes - Current volumes of the mix.
130 * @param prev_mix_volumes - Previous volumes of the mix.
131 * @param voice_state - State used by the AudioRenderer across calls.
132 * @param output_index - Output mix buffer index.
133 * @param buffer_count - Number of active mix buffers.
134 * @param input_index - Input mix buffer index.
135 * @param node_id - Node id of the voice this command is generated for.
136 */
137 void GenerateVoiceMixCommand(std::span<const f32> mix_volumes,
138 std::span<const f32> prev_mix_volumes,
139 const VoiceState& voice_state, s16 output_index, s16 buffer_count,
140 s16 input_index, s32 node_id);
141
142 /**
143 * Generate a biquad filter command for a voice.
144 *
145 * @param voice_info - Voice info this command is generated from.
146 * @param voice_state - State used by the AudioRenderer across calls.
147 * @param buffer_count - Number of active mix buffers.
148 * @param channel - Channel index of this command.
149 * @param node_id - Node id of the voice this command is generated for.
150 */
151 void GenerateBiquadFilterCommandForVoice(VoiceInfo& voice_info, const VoiceState& voice_state,
152 s16 buffer_count, s8 channel, s32 node_id);
153
154 /**
155 * Generate commands for a voice.
156 * Includes a data source, biquad filter, volume and mixing.
157 *
158 * @param voice_info - Voice info these commands are generated from.
159 */
160 void GenerateVoiceCommand(VoiceInfo& voice_info);
161
162 /**
163 * Generate commands for all voices.
164 */
165 void GenerateVoiceCommands();
166
167 /**
168 * Generate a mixing command.
169 *
170 * @param buffer_offset - Base mix buffer offset to use.
171 * @param effect_info_base - BufferMixer effect info.
172 * @param node_id - Node id of the mix this command is generated for.
173 */
174 void GenerateBufferMixerCommand(s16 buffer_offset, EffectInfoBase& effect_info_base,
175 s32 node_id);
176
177 /**
178 * Generate a delay effect command.
179 *
180 * @param buffer_offset - Base mix buffer offset to use.
181 * @param effect_info_base - Delay effect info.
182 * @param node_id - Node id of the mix this command is generated for.
183 */
184 void GenerateDelayCommand(s16 buffer_offset, EffectInfoBase& effect_info_base, s32 node_id);
185
186 /**
187 * Generate a reverb effect command.
188 *
189 * @param buffer_offset - Base mix buffer offset to use.
190 * @param effect_info_base - Reverb effect info.
191 * @param node_id - Node id of the mix this command is generated for.
192 * @param long_size_pre_delay_supported - Use a longer pre-delay time before reverb starts.
193 */
194 void GenerateReverbCommand(s16 buffer_offset, EffectInfoBase& effect_info_base, s32 node_id,
195 bool long_size_pre_delay_supported);
196
197 /**
198 * Generate an I3DL2 reverb effect command.
199 *
200 * @param buffer_offset - Base mix buffer offset to use.
201 * @param effect_info_base - I3DL2Reverb effect info.
202 * @param node_id - Node id of the mix this command is generated for.
203 */
204 void GenerateI3dl2ReverbEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info,
205 s32 node_id);
206
207 /**
208 * Generate an aux effect command.
209 *
210 * @param buffer_offset - Base mix buffer offset to use.
211 * @param effect_info_base - Aux effect info.
212 * @param node_id - Node id of the mix this command is generated for.
213 */
214 void GenerateAuxCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id);
215
216 /**
217 * Generate a biquad filter effect command.
218 *
219 * @param buffer_offset - Base mix buffer offset to use.
220 * @param effect_info_base - Aux effect info.
221 * @param node_id - Node id of the mix this command is generated for.
222 */
223 void GenerateBiquadFilterEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info,
224 s32 node_id);
225
226 /**
227 * Generate a light limiter effect command.
228 *
229 * @param buffer_offset - Base mix buffer offset to use.
230 * @param effect_info_base - Limiter effect info.
231 * @param node_id - Node id of the mix this command is generated for.
232 * @param effect_index - Index for the statistics state.
233 */
234 void GenerateLightLimiterEffectCommand(s16 buffer_offset, EffectInfoBase& effect_info,
235 s32 node_id, u32 effect_index);
236
237 /**
238 * Generate a capture effect command.
239 * Writes a mix buffer back to game memory.
240 *
241 * @param buffer_offset - Base mix buffer offset to use.
242 * @param effect_info_base - Capture effect info.
243 * @param node_id - Node id of the mix this command is generated for.
244 */
245 void GenerateCaptureCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id);
246
247 /**
248 * Generate a compressor effect command.
249 *
250 * @param buffer_offset - Base mix buffer offset to use.
251 * @param effect_info_base - Compressor effect info.
252 * @param node_id - Node id of the mix this command is generated for.
253 */
254 void GenerateCompressorCommand(const s16 buffer_offset, EffectInfoBase& effect_info,
255 const s32 node_id);
256
257 /**
258 * Generate all effect commands for a mix.
259 *
260 * @param mix_info - Mix to generate effects from.
261 */
262 void GenerateEffectCommand(MixInfo& mix_info);
263
264 /**
265 * Generate all mix commands.
266 *
267 * @param mix_info - Mix to generate effects from.
268 */
269 void GenerateMixCommands(MixInfo& mix_info);
270
271 /**
272 * Generate a submix command.
273 * Generates all effects and all mixing commands.
274 *
275 * @param mix_info - Mix to generate effects from.
276 */
277 void GenerateSubMixCommand(MixInfo& mix_info);
278
279 /**
280 * Generate all submix command.
281 */
282 void GenerateSubMixCommands();
283
284 /**
285 * Generate the final mix.
286 */
287 void GenerateFinalMixCommand();
288
289 /**
290 * Generate the final mix commands.
291 */
292 void GenerateFinalMixCommands();
293
294 /**
295 * Generate all sink commands.
296 */
297 void GenerateSinkCommands();
298
299 /**
300 * Generate a sink command.
301 * Sends samples out to the backend, or a game-supplied circular buffer.
302 *
303 * @param buffer_offset - Base mix buffer offset to use.
304 * @param sink_info - Sink info to generate the commands from.
305 */
306 void GenerateSinkCommand(s16 buffer_offset, SinkInfoBase& sink_info);
307
308 /**
309 * Generate a device sink command.
310 * Sends samples out to the backend.
311 *
312 * @param buffer_offset - Base mix buffer offset to use.
313 * @param sink_info - Sink info to generate the commands from.
314 */
315 void GenerateDeviceSinkCommand(s16 buffer_offset, SinkInfoBase& sink_info);
316
317 /**
318 * Generate a performance command.
319 * Used to report performance metrics of the AudioRenderer back to the game.
320 *
321 * @param buffer_offset - Base mix buffer offset to use.
322 * @param sink_info - Sink info to generate the commands from.
323 */
324 void GeneratePerformanceCommand(s32 node_id, PerformanceState state,
325 const PerformanceEntryAddresses& entry_addresses);
326
327private:
328 /// Commands will be written by this buffer
329 CommandBuffer& command_buffer;
330 /// Header information for the commands generated
331 const CommandListHeader& command_header;
332 /// Various things to control generation
333 const AudioRendererSystemContext& render_context;
334 /// Used for generating voices
335 VoiceContext& voice_context;
336 /// Used for generating mixes
337 MixContext& mix_context;
338 /// Used for generating effects
339 EffectContext& effect_context;
340 /// Used for generating sinks
341 SinkContext& sink_context;
342 /// Used for generating submixes
343 SplitterContext& splitter_context;
344 /// Used for generating performance
345 PerformanceManager* performance_manager;
346};
347
348} // namespace AudioRenderer
349} // namespace AudioCore
diff --git a/src/audio_core/renderer/command/command_list_header.h b/src/audio_core/renderer/command/command_list_header.h
new file mode 100644
index 000000000..988530b1f
--- /dev/null
+++ b/src/audio_core/renderer/command/command_list_header.h
@@ -0,0 +1,22 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "audio_core/common/common.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12
13struct CommandListHeader {
14 u64 buffer_size;
15 u32 command_count;
16 std::span<s32> samples_buffer;
17 s16 buffer_count;
18 u32 sample_count;
19 u32 sample_rate;
20};
21
22} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/command_processing_time_estimator.cpp b/src/audio_core/renderer/command/command_processing_time_estimator.cpp
new file mode 100644
index 000000000..3091f587a
--- /dev/null
+++ b/src/audio_core/renderer/command/command_processing_time_estimator.cpp
@@ -0,0 +1,3620 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/command/command_processing_time_estimator.h"
5
6namespace AudioCore::AudioRenderer {
7
8u32 CommandProcessingTimeEstimatorVersion1::Estimate(
9 const PcmInt16DataSourceVersion1Command& command) const {
10 return static_cast<u32>(command.pitch * 0.25f * 1.2f);
11}
12
13u32 CommandProcessingTimeEstimatorVersion1::Estimate(
14 const PcmInt16DataSourceVersion2Command& command) const {
15 return static_cast<u32>(command.pitch * 0.25f * 1.2f);
16}
17
18u32 CommandProcessingTimeEstimatorVersion1::Estimate(
19 [[maybe_unused]] const PcmFloatDataSourceVersion1Command& command) const {
20 return 0;
21}
22
23u32 CommandProcessingTimeEstimatorVersion1::Estimate(
24 [[maybe_unused]] const PcmFloatDataSourceVersion2Command& command) const {
25 return 0;
26}
27
28u32 CommandProcessingTimeEstimatorVersion1::Estimate(
29 const AdpcmDataSourceVersion1Command& command) const {
30 return static_cast<u32>(command.pitch * 0.25f * 1.2f);
31}
32
33u32 CommandProcessingTimeEstimatorVersion1::Estimate(
34 const AdpcmDataSourceVersion2Command& command) const {
35 return static_cast<u32>(command.pitch * 0.25f * 1.2f);
36}
37
38u32 CommandProcessingTimeEstimatorVersion1::Estimate(
39 [[maybe_unused]] const VolumeCommand& command) const {
40 return static_cast<u32>((static_cast<f32>(sample_count) * 8.8f) * 1.2f);
41}
42
43u32 CommandProcessingTimeEstimatorVersion1::Estimate(
44 [[maybe_unused]] const VolumeRampCommand& command) const {
45 return static_cast<u32>((static_cast<f32>(sample_count) * 9.8f) * 1.2f);
46}
47
48u32 CommandProcessingTimeEstimatorVersion1::Estimate(
49 [[maybe_unused]] const BiquadFilterCommand& command) const {
50 return static_cast<u32>((static_cast<f32>(sample_count) * 58.0f) * 1.2f);
51}
52
53u32 CommandProcessingTimeEstimatorVersion1::Estimate(
54 [[maybe_unused]] const MixCommand& command) const {
55 return static_cast<u32>((static_cast<f32>(sample_count) * 10.0f) * 1.2f);
56}
57
58u32 CommandProcessingTimeEstimatorVersion1::Estimate(
59 [[maybe_unused]] const MixRampCommand& command) const {
60 return static_cast<u32>((static_cast<f32>(sample_count) * 14.4f) * 1.2f);
61}
62
63u32 CommandProcessingTimeEstimatorVersion1::Estimate(const MixRampGroupedCommand& command) const {
64 u32 count{0};
65 for (u32 i = 0; i < command.buffer_count; i++) {
66 if (command.volumes[i] != 0.0f || command.prev_volumes[i] != 0.0f) {
67 count++;
68 }
69 }
70
71 return static_cast<u32>(((static_cast<f32>(sample_count) * 14.4f) * 1.2f) *
72 static_cast<f32>(count));
73}
74
75u32 CommandProcessingTimeEstimatorVersion1::Estimate(
76 [[maybe_unused]] const DepopPrepareCommand& command) const {
77 return 1080;
78}
79
80u32 CommandProcessingTimeEstimatorVersion1::Estimate(
81 const DepopForMixBuffersCommand& command) const {
82 return static_cast<u32>((static_cast<f32>(sample_count) * 8.9f) *
83 static_cast<f32>(command.count));
84}
85
86u32 CommandProcessingTimeEstimatorVersion1::Estimate(const DelayCommand& command) const {
87 return static_cast<u32>((static_cast<f32>(sample_count) * command.parameter.channel_count) *
88 202.5f);
89}
90
91u32 CommandProcessingTimeEstimatorVersion1::Estimate(
92 [[maybe_unused]] const UpsampleCommand& command) const {
93 return 357915;
94}
95
96u32 CommandProcessingTimeEstimatorVersion1::Estimate(
97 [[maybe_unused]] const DownMix6chTo2chCommand& command) const {
98 return 16108;
99}
100
101u32 CommandProcessingTimeEstimatorVersion1::Estimate(const AuxCommand& command) const {
102 if (command.enabled) {
103 return 15956;
104 }
105 return 3765;
106}
107
108u32 CommandProcessingTimeEstimatorVersion1::Estimate(
109 [[maybe_unused]] const DeviceSinkCommand& command) const {
110 return 10042;
111}
112
113u32 CommandProcessingTimeEstimatorVersion1::Estimate(
114 [[maybe_unused]] const CircularBufferSinkCommand& command) const {
115 return 55;
116}
117
118u32 CommandProcessingTimeEstimatorVersion1::Estimate(const ReverbCommand& command) const {
119 if (command.enabled) {
120 return static_cast<u32>(
121 (command.parameter.channel_count * static_cast<f32>(sample_count) * 750) * 1.2f);
122 }
123 return 0;
124}
125
126u32 CommandProcessingTimeEstimatorVersion1::Estimate(const I3dl2ReverbCommand& command) const {
127 if (command.enabled) {
128 return static_cast<u32>(
129 (command.parameter.channel_count * static_cast<f32>(sample_count) * 530) * 1.2f);
130 }
131 return 0;
132}
133
134u32 CommandProcessingTimeEstimatorVersion1::Estimate(
135 [[maybe_unused]] const PerformanceCommand& command) const {
136 return 1454;
137}
138
139u32 CommandProcessingTimeEstimatorVersion1::Estimate(
140 [[maybe_unused]] const ClearMixBufferCommand& command) const {
141 return static_cast<u32>(
142 ((static_cast<f32>(sample_count) * 0.83f) * static_cast<f32>(buffer_count)) * 1.2f);
143}
144
145u32 CommandProcessingTimeEstimatorVersion1::Estimate(
146 [[maybe_unused]] const CopyMixBufferCommand& command) const {
147 return 0;
148}
149
150u32 CommandProcessingTimeEstimatorVersion1::Estimate(
151 [[maybe_unused]] const LightLimiterVersion1Command& command) const {
152 return 0;
153}
154
155u32 CommandProcessingTimeEstimatorVersion1::Estimate(
156 [[maybe_unused]] const LightLimiterVersion2Command& command) const {
157 return 0;
158}
159
160u32 CommandProcessingTimeEstimatorVersion1::Estimate(
161 [[maybe_unused]] const MultiTapBiquadFilterCommand& command) const {
162 return 0;
163}
164
165u32 CommandProcessingTimeEstimatorVersion1::Estimate(
166 [[maybe_unused]] const CaptureCommand& command) const {
167 return 0;
168}
169
170u32 CommandProcessingTimeEstimatorVersion1::Estimate(
171 [[maybe_unused]] const CompressorCommand& command) const {
172 return 0;
173}
174
175u32 CommandProcessingTimeEstimatorVersion2::Estimate(
176 const PcmInt16DataSourceVersion1Command& command) const {
177 switch (sample_count) {
178 case 160:
179 return static_cast<u32>(
180 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
181 (command.pitch * 2.0f) * 749.269f +
182 6138.94f);
183 case 240:
184 return static_cast<u32>(
185 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
186 (command.pitch * 2.0f) * 1195.456f +
187 7797.047f);
188 default:
189 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
190 return 0;
191 }
192}
193
194u32 CommandProcessingTimeEstimatorVersion2::Estimate(
195 const PcmInt16DataSourceVersion2Command& command) const {
196 switch (sample_count) {
197 case 160:
198 return static_cast<u32>(
199 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
200 (command.pitch * 2.0f) * 749.269f +
201 6138.94f);
202 case 240:
203 return static_cast<u32>(
204 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
205 (command.pitch * 2.0f) * 1195.456f +
206 7797.047f);
207 default:
208 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
209 return 0;
210 }
211}
212
213u32 CommandProcessingTimeEstimatorVersion2::Estimate(
214 const PcmFloatDataSourceVersion1Command& command) const {
215 switch (sample_count) {
216 case 160:
217 return static_cast<u32>(
218 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
219 (command.pitch * 2.0f) * 749.269f +
220 6138.94f);
221 case 240:
222 return static_cast<u32>(
223 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
224 (command.pitch * 2.0f) * 1195.456f +
225 7797.047f);
226 default:
227 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
228 return 0;
229 }
230}
231
232u32 CommandProcessingTimeEstimatorVersion2::Estimate(
233 const PcmFloatDataSourceVersion2Command& command) const {
234 switch (sample_count) {
235 case 160:
236 return static_cast<u32>(
237 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
238 (command.pitch * 2.0f) * 749.269f +
239 6138.94f);
240 case 240:
241 return static_cast<u32>(
242 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
243 (command.pitch * 2.0f) * 1195.456f +
244 7797.047f);
245 default:
246 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
247 return 0;
248 }
249}
250
251u32 CommandProcessingTimeEstimatorVersion2::Estimate(
252 const AdpcmDataSourceVersion1Command& command) const {
253 switch (sample_count) {
254 case 160:
255 return static_cast<u32>(
256 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
257 (command.pitch * 2.0f) * 2125.588f +
258 9039.47f);
259 case 240:
260 return static_cast<u32>(
261 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
262 (command.pitch * 2.0f) * 3564.088 +
263 6225.471);
264 default:
265 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
266 return 0;
267 }
268}
269
270u32 CommandProcessingTimeEstimatorVersion2::Estimate(
271 const AdpcmDataSourceVersion2Command& command) const {
272 switch (sample_count) {
273 case 160:
274 return static_cast<u32>(
275 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
276 (command.pitch * 2.0f) * 2125.588f +
277 9039.47f);
278 case 240:
279 return static_cast<u32>(
280 (static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
281 (command.pitch * 2.0f) * 3564.088 +
282 6225.471);
283 default:
284 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
285 return 0;
286 }
287}
288
289u32 CommandProcessingTimeEstimatorVersion2::Estimate(
290 [[maybe_unused]] const VolumeCommand& command) const {
291 switch (sample_count) {
292 case 160:
293 return static_cast<u32>(1280.3f);
294 case 240:
295 return static_cast<u32>(1737.8f);
296 default:
297 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
298 return 0;
299 }
300}
301
302u32 CommandProcessingTimeEstimatorVersion2::Estimate(
303 [[maybe_unused]] const VolumeRampCommand& command) const {
304 switch (sample_count) {
305 case 160:
306 return static_cast<u32>(1403.9f);
307 case 240:
308 return static_cast<u32>(1884.3f);
309 default:
310 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
311 return 0;
312 }
313}
314
315u32 CommandProcessingTimeEstimatorVersion2::Estimate(
316 [[maybe_unused]] const BiquadFilterCommand& command) const {
317 switch (sample_count) {
318 case 160:
319 return static_cast<u32>(4813.2f);
320 case 240:
321 return static_cast<u32>(6915.4f);
322 default:
323 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
324 return 0;
325 }
326}
327
328u32 CommandProcessingTimeEstimatorVersion2::Estimate(
329 [[maybe_unused]] const MixCommand& command) const {
330 switch (sample_count) {
331 case 160:
332 return static_cast<u32>(1342.2f);
333 case 240:
334 return static_cast<u32>(1833.2f);
335 default:
336 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
337 return 0;
338 }
339}
340
341u32 CommandProcessingTimeEstimatorVersion2::Estimate(
342 [[maybe_unused]] const MixRampCommand& command) const {
343 switch (sample_count) {
344 case 160:
345 return static_cast<u32>(1859.0f);
346 case 240:
347 return static_cast<u32>(2286.1f);
348 default:
349 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
350 return 0;
351 }
352}
353
354u32 CommandProcessingTimeEstimatorVersion2::Estimate(const MixRampGroupedCommand& command) const {
355 u32 count{0};
356 for (u32 i = 0; i < command.buffer_count; i++) {
357 if (command.volumes[i] != 0.0f || command.prev_volumes[i] != 0.0f) {
358 count++;
359 }
360 }
361
362 switch (sample_count) {
363 case 160:
364 return static_cast<u32>((static_cast<f32>(sample_count) * 7.245f) *
365 static_cast<f32>(count));
366 case 240:
367 return static_cast<u32>((static_cast<f32>(sample_count) * 7.245f) *
368 static_cast<f32>(count));
369 default:
370 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
371 return 0;
372 }
373}
374
375u32 CommandProcessingTimeEstimatorVersion2::Estimate(
376 [[maybe_unused]] const DepopPrepareCommand& command) const {
377 switch (sample_count) {
378 case 160:
379 return static_cast<u32>(306.62f);
380 case 240:
381 return static_cast<u32>(293.22f);
382 default:
383 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
384 return 0;
385 }
386}
387
388u32 CommandProcessingTimeEstimatorVersion2::Estimate(
389 [[maybe_unused]] const DepopForMixBuffersCommand& command) const {
390 switch (sample_count) {
391 case 160:
392 return static_cast<u32>(762.96f);
393 case 240:
394 return static_cast<u32>(726.96f);
395 default:
396 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
397 return 0;
398 }
399}
400
401u32 CommandProcessingTimeEstimatorVersion2::Estimate(const DelayCommand& command) const {
402 switch (sample_count) {
403 case 160:
404 if (command.enabled) {
405 switch (command.parameter.channel_count) {
406 case 1:
407 return static_cast<u32>(41635.555f);
408 case 2:
409 return static_cast<u32>(97861.211f);
410 case 4:
411 return static_cast<u32>(192515.516f);
412 case 6:
413 return static_cast<u32>(301755.969f);
414 default:
415 LOG_ERROR(Service_Audio, "Invalid channel count {}",
416 command.parameter.channel_count);
417 return 0;
418 }
419 }
420 switch (command.parameter.channel_count) {
421 case 1:
422 return static_cast<u32>(578.529f);
423 case 2:
424 return static_cast<u32>(663.064f);
425 case 4:
426 return static_cast<u32>(703.983f);
427 case 6:
428 return static_cast<u32>(760.032f);
429 default:
430 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
431 return 0;
432 }
433 case 240:
434 if (command.enabled) {
435 switch (command.parameter.channel_count) {
436 case 1:
437 return static_cast<u32>(8770.345f);
438 case 2:
439 return static_cast<u32>(25741.18f);
440 case 4:
441 return static_cast<u32>(47551.168f);
442 case 6:
443 return static_cast<u32>(81629.219f);
444 default:
445 LOG_ERROR(Service_Audio, "Invalid channel count {}",
446 command.parameter.channel_count);
447 return 0;
448 }
449 }
450 switch (command.parameter.channel_count) {
451 case 1:
452 return static_cast<u32>(521.283f);
453 case 2:
454 return static_cast<u32>(585.396f);
455 case 4:
456 return static_cast<u32>(629.884f);
457 case 6:
458 return static_cast<u32>(713.57f);
459 default:
460 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
461 return 0;
462 }
463 default:
464 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
465 return 0;
466 }
467}
468
469u32 CommandProcessingTimeEstimatorVersion2::Estimate(
470 [[maybe_unused]] const UpsampleCommand& command) const {
471 switch (sample_count) {
472 case 160:
473 return static_cast<u32>(292000.0f);
474 case 240:
475 return static_cast<u32>(0.0f);
476 default:
477 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
478 return 0;
479 }
480}
481
482u32 CommandProcessingTimeEstimatorVersion2::Estimate(
483 [[maybe_unused]] const DownMix6chTo2chCommand& command) const {
484 switch (sample_count) {
485 case 160:
486 return static_cast<u32>(10009.0f);
487 case 240:
488 return static_cast<u32>(14577.0f);
489 default:
490 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
491 return 0;
492 }
493}
494
495u32 CommandProcessingTimeEstimatorVersion2::Estimate(const AuxCommand& command) const {
496 // Is this function bugged, returning the wrong time?
497 // Surely the larger time should be returned when enabled...
498 // CMP W8, #0
499 // MOV W8, #0x60; // 489.163f
500 // MOV W10, #0x64; // 7177.936f
501 // CSEL X8, X10, X8, EQ
502
503 switch (sample_count) {
504 case 160:
505 if (command.enabled) {
506 return static_cast<u32>(489.163f);
507 }
508 return static_cast<u32>(7177.936f);
509 case 240:
510 if (command.enabled) {
511 return static_cast<u32>(485.562f);
512 }
513 return static_cast<u32>(9499.822f);
514 default:
515 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
516 return 0;
517 }
518}
519
520u32 CommandProcessingTimeEstimatorVersion2::Estimate(const DeviceSinkCommand& command) const {
521 switch (command.input_count) {
522 case 2:
523 switch (sample_count) {
524 case 160:
525 return static_cast<u32>(9261.545f);
526 case 240:
527 return static_cast<u32>(9336.054f);
528 default:
529 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
530 return 0;
531 }
532 case 6:
533 switch (sample_count) {
534 case 160:
535 return static_cast<u32>(9336.054f);
536 case 240:
537 return static_cast<u32>(9566.728f);
538 default:
539 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
540 return 0;
541 }
542 default:
543 LOG_ERROR(Service_Audio, "Invalid input count {}", command.input_count);
544 return 0;
545 }
546}
547
548u32 CommandProcessingTimeEstimatorVersion2::Estimate(
549 const CircularBufferSinkCommand& command) const {
550 switch (sample_count) {
551 case 160:
552 return static_cast<u32>(static_cast<f32>(command.input_count) * 853.629f + 1284.517f);
553 case 240:
554 return static_cast<u32>(static_cast<f32>(command.input_count) * 1726.021f + 1369.683f);
555 default:
556 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
557 return 0;
558 }
559}
560
561u32 CommandProcessingTimeEstimatorVersion2::Estimate(const ReverbCommand& command) const {
562 switch (sample_count) {
563 case 160:
564 if (command.enabled) {
565 switch (command.parameter.channel_count) {
566 case 1:
567 return static_cast<u32>(97192.227f);
568 case 2:
569 return static_cast<u32>(103278.555f);
570 case 4:
571 return static_cast<u32>(109579.039f);
572 case 6:
573 return static_cast<u32>(115065.438f);
574 default:
575 LOG_ERROR(Service_Audio, "Invalid channel count {}",
576 command.parameter.channel_count);
577 return 0;
578 }
579 }
580 switch (command.parameter.channel_count) {
581 case 1:
582 return static_cast<u32>(492.009f);
583 case 2:
584 return static_cast<u32>(554.463f);
585 case 4:
586 return static_cast<u32>(595.864f);
587 case 6:
588 return static_cast<u32>(656.617f);
589 default:
590 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
591 return 0;
592 }
593 case 240:
594 if (command.enabled) {
595 switch (command.parameter.channel_count) {
596 case 1:
597 return static_cast<u32>(136463.641f);
598 case 2:
599 return static_cast<u32>(145749.047f);
600 case 4:
601 return static_cast<u32>(154796.938f);
602 case 6:
603 return static_cast<u32>(161968.406f);
604 default:
605 LOG_ERROR(Service_Audio, "Invalid channel count {}",
606 command.parameter.channel_count);
607 return 0;
608 }
609 }
610 switch (command.parameter.channel_count) {
611 case 1:
612 return static_cast<u32>(495.789f);
613 case 2:
614 return static_cast<u32>(527.163f);
615 case 4:
616 return static_cast<u32>(598.752f);
617 case 6:
618 return static_cast<u32>(666.025f);
619 default:
620 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
621 return 0;
622 }
623 default:
624 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
625 return 0;
626 }
627}
628
629u32 CommandProcessingTimeEstimatorVersion2::Estimate(const I3dl2ReverbCommand& command) const {
630 switch (sample_count) {
631 case 160:
632 if (command.enabled) {
633 switch (command.parameter.channel_count) {
634 case 1:
635 return static_cast<u32>(138836.484f);
636 case 2:
637 return static_cast<u32>(135428.172f);
638 case 4:
639 return static_cast<u32>(199181.844f);
640 case 6:
641 return static_cast<u32>(247345.906f);
642 default:
643 LOG_ERROR(Service_Audio, "Invalid channel count {}",
644 command.parameter.channel_count);
645 return 0;
646 }
647 }
648 switch (command.parameter.channel_count) {
649 case 1:
650 return static_cast<u32>(718.704f);
651 case 2:
652 return static_cast<u32>(751.296f);
653 case 4:
654 return static_cast<u32>(797.464f);
655 case 6:
656 return static_cast<u32>(867.426f);
657 default:
658 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
659 return 0;
660 }
661 case 240:
662 if (command.enabled) {
663 switch (command.parameter.channel_count) {
664 case 1:
665 return static_cast<u32>(199952.734f);
666 case 2:
667 return static_cast<u32>(195199.5f);
668 case 4:
669 return static_cast<u32>(290575.875f);
670 case 6:
671 return static_cast<u32>(363494.531f);
672 default:
673 LOG_ERROR(Service_Audio, "Invalid channel count {}",
674 command.parameter.channel_count);
675 return 0;
676 }
677 }
678 switch (command.parameter.channel_count) {
679 case 1:
680 return static_cast<u32>(534.24f);
681 case 2:
682 return static_cast<u32>(570.874f);
683 case 4:
684 return static_cast<u32>(660.933f);
685 case 6:
686 return static_cast<u32>(694.596f);
687 default:
688 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
689 return 0;
690 }
691 default:
692 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
693 return 0;
694 }
695}
696
697u32 CommandProcessingTimeEstimatorVersion2::Estimate(
698 [[maybe_unused]] const PerformanceCommand& command) const {
699 switch (sample_count) {
700 case 160:
701 return static_cast<u32>(489.35f);
702 case 240:
703 return static_cast<u32>(491.18f);
704 default:
705 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
706 return 0;
707 }
708}
709
710u32 CommandProcessingTimeEstimatorVersion2::Estimate(
711 [[maybe_unused]] const ClearMixBufferCommand& command) const {
712 switch (sample_count) {
713 case 160:
714 return static_cast<u32>(static_cast<f32>(buffer_count) * 260.4f + 139.65f);
715 case 240:
716 return static_cast<u32>(static_cast<f32>(buffer_count) * 668.85f + 193.2f);
717 default:
718 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
719 return 0;
720 }
721}
722
723u32 CommandProcessingTimeEstimatorVersion2::Estimate(
724 [[maybe_unused]] const CopyMixBufferCommand& command) const {
725 switch (sample_count) {
726 case 160:
727 return static_cast<u32>(836.32f);
728 case 240:
729 return static_cast<u32>(1000.9f);
730 default:
731 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
732 return 0;
733 }
734}
735
736u32 CommandProcessingTimeEstimatorVersion2::Estimate(
737 [[maybe_unused]] const LightLimiterVersion1Command& command) const {
738 return 0;
739}
740
741u32 CommandProcessingTimeEstimatorVersion2::Estimate(
742 [[maybe_unused]] const LightLimiterVersion2Command& command) const {
743 return 0;
744}
745
746u32 CommandProcessingTimeEstimatorVersion2::Estimate(
747 [[maybe_unused]] const MultiTapBiquadFilterCommand& command) const {
748 return 0;
749}
750
751u32 CommandProcessingTimeEstimatorVersion2::Estimate(
752 [[maybe_unused]] const CaptureCommand& command) const {
753 return 0;
754}
755
756u32 CommandProcessingTimeEstimatorVersion2::Estimate(
757 [[maybe_unused]] const CompressorCommand& command) const {
758 return 0;
759}
760
761u32 CommandProcessingTimeEstimatorVersion3::Estimate(
762 const PcmInt16DataSourceVersion1Command& command) const {
763 switch (sample_count) {
764 case 160:
765 return static_cast<u32>(
766 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
767 (command.pitch * 0.000030518f)) *
768 427.52f +
769 6329.442f);
770 case 240:
771 return static_cast<u32>(
772 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
773 (command.pitch * 0.000030518f)) *
774 710.143f +
775 7853.286f);
776 default:
777 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
778 return 0;
779 }
780}
781
782u32 CommandProcessingTimeEstimatorVersion3::Estimate(
783 const PcmInt16DataSourceVersion2Command& command) const {
784 switch (sample_count) {
785 case 160:
786 switch (command.src_quality) {
787 case SrcQuality::Medium:
788 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
789 static_cast<f32>(sample_count)) *
790 (command.pitch * 0.000030518f)) -
791 1.0f) *
792 427.52f +
793 6329.442f);
794 case SrcQuality::High:
795 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
796 static_cast<f32>(sample_count)) *
797 (command.pitch * 0.000030518f)) -
798 1.0f) *
799 371.876f +
800 8049.415f);
801 case SrcQuality::Low:
802 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
803 static_cast<f32>(sample_count)) *
804 (command.pitch * 0.000030518f)) -
805 1.0f) *
806 423.43f +
807 5062.659f);
808 default:
809 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
810 static_cast<u32>(command.src_quality));
811 return 0;
812 }
813
814 case 240:
815 switch (command.src_quality) {
816 case SrcQuality::Medium:
817 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
818 static_cast<f32>(sample_count)) *
819 (command.pitch * 0.000030518f)) -
820 1.0f) *
821 710.143f +
822 7853.286f);
823 case SrcQuality::High:
824 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
825 static_cast<f32>(sample_count)) *
826 (command.pitch * 0.000030518f)) -
827 1.0f) *
828 610.487f +
829 10138.842f);
830 case SrcQuality::Low:
831 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
832 static_cast<f32>(sample_count)) *
833 (command.pitch * 0.000030518f)) -
834 1.0f) *
835 676.722f +
836 5810.962f);
837 default:
838 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
839 static_cast<u32>(command.src_quality));
840 return 0;
841 }
842
843 default:
844 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
845 return 0;
846 }
847}
848
849u32 CommandProcessingTimeEstimatorVersion3::Estimate(
850 const PcmFloatDataSourceVersion1Command& command) const {
851 switch (sample_count) {
852 case 160:
853 return static_cast<u32>(
854 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
855 (command.pitch * 0.000030518f)) *
856 1672.026f +
857 7681.211f);
858 case 240:
859 return static_cast<u32>(
860 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
861 (command.pitch * 0.000030518f)) *
862 2550.414f +
863 9663.969f);
864 default:
865 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
866 return 0;
867 }
868}
869
870u32 CommandProcessingTimeEstimatorVersion3::Estimate(
871 const PcmFloatDataSourceVersion2Command& command) const {
872 switch (sample_count) {
873 case 160:
874 switch (command.src_quality) {
875 case SrcQuality::Medium:
876 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
877 static_cast<f32>(sample_count)) *
878 (command.pitch * 0.000030518f)) -
879 1.0f) *
880 1672.026f +
881 7681.211f);
882 case SrcQuality::High:
883 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
884 static_cast<f32>(sample_count)) *
885 (command.pitch * 0.000030518f)) -
886 1.0f) *
887 1672.982f +
888 9038.011f);
889 case SrcQuality::Low:
890 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
891 static_cast<f32>(sample_count)) *
892 (command.pitch * 0.000030518f)) -
893 1.0f) *
894 1673.216f +
895 6027.577f);
896 default:
897 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
898 static_cast<u32>(command.src_quality));
899 return 0;
900 }
901
902 case 240:
903 switch (command.src_quality) {
904 case SrcQuality::Medium:
905 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
906 static_cast<f32>(sample_count)) *
907 (command.pitch * 0.000030518f)) -
908 1.0f) *
909 2550.414f +
910 9663.969f);
911 case SrcQuality::High:
912 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
913 static_cast<f32>(sample_count)) *
914 (command.pitch * 0.000030518f)) -
915 1.0f) *
916 2522.303f +
917 11758.571f);
918 case SrcQuality::Low:
919 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
920 static_cast<f32>(sample_count)) *
921 (command.pitch * 0.000030518f)) -
922 1.0f) *
923 2537.061f +
924 7369.309f);
925 default:
926 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
927 static_cast<u32>(command.src_quality));
928 return 0;
929 }
930
931 default:
932 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
933 return 0;
934 }
935}
936
937u32 CommandProcessingTimeEstimatorVersion3::Estimate(
938 const AdpcmDataSourceVersion1Command& command) const {
939 switch (sample_count) {
940 case 160:
941 return static_cast<u32>(
942 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
943 (command.pitch * 0.000030518f)) *
944 1827.665f +
945 7913.808f);
946 case 240:
947 return static_cast<u32>(
948 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
949 (command.pitch * 0.000030518f)) *
950 2756.372f +
951 9736.702f);
952 default:
953 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
954 return 0;
955 }
956}
957
958u32 CommandProcessingTimeEstimatorVersion3::Estimate(
959 const AdpcmDataSourceVersion2Command& command) const {
960 switch (sample_count) {
961 case 160:
962 switch (command.src_quality) {
963 case SrcQuality::Medium:
964 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
965 static_cast<f32>(sample_count)) *
966 (command.pitch * 0.000030518f)) -
967 1.0f) *
968 1827.665f +
969 7913.808f);
970 case SrcQuality::High:
971 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
972 static_cast<f32>(sample_count)) *
973 (command.pitch * 0.000030518f)) -
974 1.0f) *
975 1829.285f +
976 9607.814f);
977 case SrcQuality::Low:
978 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
979 static_cast<f32>(sample_count)) *
980 (command.pitch * 0.000030518f)) -
981 1.0f) *
982 1824.609f +
983 6517.476f);
984 default:
985 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
986 static_cast<u32>(command.src_quality));
987 return 0;
988 }
989
990 case 240:
991 switch (command.src_quality) {
992 case SrcQuality::Medium:
993 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
994 static_cast<f32>(sample_count)) *
995 (command.pitch * 0.000030518f)) -
996 1.0f) *
997 2756.372f +
998 9736.702f);
999 case SrcQuality::High:
1000 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1001 static_cast<f32>(sample_count)) *
1002 (command.pitch * 0.000030518f)) -
1003 1.0f) *
1004 2731.308f +
1005 12154.379f);
1006 case SrcQuality::Low:
1007 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1008 static_cast<f32>(sample_count)) *
1009 (command.pitch * 0.000030518f)) -
1010 1.0f) *
1011 2732.152f +
1012 7929.442f);
1013 default:
1014 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
1015 static_cast<u32>(command.src_quality));
1016 return 0;
1017 }
1018
1019 default:
1020 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1021 return 0;
1022 }
1023}
1024
1025u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1026 [[maybe_unused]] const VolumeCommand& command) const {
1027 switch (sample_count) {
1028 case 160:
1029 return static_cast<u32>(1311.1f);
1030 case 240:
1031 return static_cast<u32>(1713.6f);
1032 default:
1033 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1034 return 0;
1035 }
1036}
1037
1038u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1039 [[maybe_unused]] const VolumeRampCommand& command) const {
1040 switch (sample_count) {
1041 case 160:
1042 return static_cast<u32>(1425.3f);
1043 case 240:
1044 return static_cast<u32>(1700.0f);
1045 default:
1046 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1047 return 0;
1048 }
1049}
1050
1051u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1052 [[maybe_unused]] const BiquadFilterCommand& command) const {
1053 switch (sample_count) {
1054 case 160:
1055 return static_cast<u32>(4173.2f);
1056 case 240:
1057 return static_cast<u32>(5585.1f);
1058 default:
1059 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1060 return 0;
1061 }
1062}
1063
1064u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1065 [[maybe_unused]] const MixCommand& command) const {
1066 switch (sample_count) {
1067 case 160:
1068 return static_cast<u32>(1402.8f);
1069 case 240:
1070 return static_cast<u32>(1853.2f);
1071 default:
1072 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1073 return 0;
1074 }
1075}
1076
1077u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1078 [[maybe_unused]] const MixRampCommand& command) const {
1079 switch (sample_count) {
1080 case 160:
1081 return static_cast<u32>(1968.7f);
1082 case 240:
1083 return static_cast<u32>(2459.4f);
1084 default:
1085 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1086 return 0;
1087 }
1088}
1089
1090u32 CommandProcessingTimeEstimatorVersion3::Estimate(const MixRampGroupedCommand& command) const {
1091 u32 count{0};
1092 for (u32 i = 0; i < command.buffer_count; i++) {
1093 if (command.volumes[i] != 0.0f || command.prev_volumes[i] != 0.0f) {
1094 count++;
1095 }
1096 }
1097
1098 switch (sample_count) {
1099 case 160:
1100 return static_cast<u32>((static_cast<f32>(sample_count) * 6.708f) *
1101 static_cast<f32>(count));
1102 case 240:
1103 return static_cast<u32>((static_cast<f32>(sample_count) * 6.443f) *
1104 static_cast<f32>(count));
1105 default:
1106 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1107 return 0;
1108 }
1109}
1110
1111u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1112 [[maybe_unused]] const DepopPrepareCommand& command) const {
1113 return 0;
1114}
1115
1116u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1117 [[maybe_unused]] const DepopForMixBuffersCommand& command) const {
1118 switch (sample_count) {
1119 case 160:
1120 return static_cast<u32>(739.64f);
1121 case 240:
1122 return static_cast<u32>(910.97f);
1123 default:
1124 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1125 return 0;
1126 }
1127}
1128
1129u32 CommandProcessingTimeEstimatorVersion3::Estimate(const DelayCommand& command) const {
1130 switch (sample_count) {
1131 case 160:
1132 if (command.enabled) {
1133 switch (command.parameter.channel_count) {
1134 case 1:
1135 return static_cast<u32>(8929.042f);
1136 case 2:
1137 return static_cast<u32>(25500.75f);
1138 case 4:
1139 return static_cast<u32>(47759.617f);
1140 case 6:
1141 return static_cast<u32>(82203.07f);
1142 default:
1143 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1144 command.parameter.channel_count);
1145 return 0;
1146 }
1147 }
1148 switch (command.parameter.channel_count) {
1149 case 1:
1150 return static_cast<u32>(1295.206f);
1151 case 2:
1152 return static_cast<u32>(1213.6f);
1153 case 4:
1154 return static_cast<u32>(942.028f);
1155 case 6:
1156 return static_cast<u32>(1001.553f);
1157 default:
1158 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1159 return 0;
1160 }
1161 case 240:
1162 if (command.enabled) {
1163 switch (command.parameter.channel_count) {
1164 case 1:
1165 return static_cast<u32>(11941.051f);
1166 case 2:
1167 return static_cast<u32>(37197.371f);
1168 case 4:
1169 return static_cast<u32>(69749.836f);
1170 case 6:
1171 return static_cast<u32>(120042.398f);
1172 default:
1173 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1174 command.parameter.channel_count);
1175 return 0;
1176 }
1177 }
1178 switch (command.parameter.channel_count) {
1179 case 1:
1180 return static_cast<u32>(997.668f);
1181 case 2:
1182 return static_cast<u32>(977.634f);
1183 case 4:
1184 return static_cast<u32>(792.309f);
1185 case 6:
1186 return static_cast<u32>(875.427f);
1187 default:
1188 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1189 return 0;
1190 }
1191 default:
1192 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1193 return 0;
1194 }
1195}
1196
1197u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1198 [[maybe_unused]] const UpsampleCommand& command) const {
1199 switch (sample_count) {
1200 case 160:
1201 return static_cast<u32>(312990.0f);
1202 case 240:
1203 return static_cast<u32>(0.0f);
1204 default:
1205 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1206 return 0;
1207 }
1208}
1209
1210u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1211 [[maybe_unused]] const DownMix6chTo2chCommand& command) const {
1212 switch (sample_count) {
1213 case 160:
1214 return static_cast<u32>(9949.7f);
1215 case 240:
1216 return static_cast<u32>(14679.0f);
1217 default:
1218 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1219 return 0;
1220 }
1221}
1222
1223u32 CommandProcessingTimeEstimatorVersion3::Estimate(const AuxCommand& command) const {
1224 switch (sample_count) {
1225 case 160:
1226 if (command.enabled) {
1227 return static_cast<u32>(7182.136f);
1228 }
1229 return static_cast<u32>(472.111f);
1230 case 240:
1231 if (command.enabled) {
1232 return static_cast<u32>(9435.961f);
1233 }
1234 return static_cast<u32>(462.619f);
1235 default:
1236 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1237 return 0;
1238 }
1239}
1240
1241u32 CommandProcessingTimeEstimatorVersion3::Estimate(const DeviceSinkCommand& command) const {
1242 switch (command.input_count) {
1243 case 2:
1244 switch (sample_count) {
1245 case 160:
1246 return static_cast<u32>(8979.956f);
1247 case 240:
1248 return static_cast<u32>(9221.907f);
1249 default:
1250 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1251 return 0;
1252 }
1253 case 6:
1254 switch (sample_count) {
1255 case 160:
1256 return static_cast<u32>(9177.903f);
1257 case 240:
1258 return static_cast<u32>(9725.897f);
1259 default:
1260 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1261 return 0;
1262 }
1263 default:
1264 LOG_ERROR(Service_Audio, "Invalid input count {}", command.input_count);
1265 return 0;
1266 }
1267}
1268
1269u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1270 const CircularBufferSinkCommand& command) const {
1271 switch (sample_count) {
1272 case 160:
1273 return static_cast<u32>(static_cast<f32>(command.input_count) * 531.069f + 0.0f);
1274 case 240:
1275 return static_cast<u32>(static_cast<f32>(command.input_count) * 770.257f + 0.0f);
1276 default:
1277 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1278 return 0;
1279 }
1280}
1281
1282u32 CommandProcessingTimeEstimatorVersion3::Estimate(const ReverbCommand& command) const {
1283 switch (sample_count) {
1284 case 160:
1285 if (command.enabled) {
1286 switch (command.parameter.channel_count) {
1287 case 1:
1288 return static_cast<u32>(81475.055f);
1289 case 2:
1290 return static_cast<u32>(84975.0f);
1291 case 4:
1292 return static_cast<u32>(91625.148f);
1293 case 6:
1294 return static_cast<u32>(95332.266f);
1295 default:
1296 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1297 command.parameter.channel_count);
1298 return 0;
1299 }
1300 }
1301 switch (command.parameter.channel_count) {
1302 case 1:
1303 return static_cast<u32>(536.298f);
1304 case 2:
1305 return static_cast<u32>(588.798f);
1306 case 4:
1307 return static_cast<u32>(643.702f);
1308 case 6:
1309 return static_cast<u32>(705.999f);
1310 default:
1311 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1312 return 0;
1313 }
1314 case 240:
1315 if (command.enabled) {
1316 switch (command.parameter.channel_count) {
1317 case 1:
1318 return static_cast<u32>(120174.469f);
1319 case 2:
1320 return static_cast<u32>(125262.219f);
1321 case 4:
1322 return static_cast<u32>(135751.234f);
1323 case 6:
1324 return static_cast<u32>(141129.234f);
1325 default:
1326 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1327 command.parameter.channel_count);
1328 return 0;
1329 }
1330 }
1331 switch (command.parameter.channel_count) {
1332 case 1:
1333 return static_cast<u32>(617.641f);
1334 case 2:
1335 return static_cast<u32>(659.536f);
1336 case 4:
1337 return static_cast<u32>(711.438f);
1338 case 6:
1339 return static_cast<u32>(778.071f);
1340 default:
1341 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1342 return 0;
1343 }
1344 default:
1345 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1346 return 0;
1347 }
1348}
1349
1350u32 CommandProcessingTimeEstimatorVersion3::Estimate(const I3dl2ReverbCommand& command) const {
1351 switch (sample_count) {
1352 case 160:
1353 if (command.enabled) {
1354 switch (command.parameter.channel_count) {
1355 case 1:
1356 return static_cast<u32>(116754.984f);
1357 case 2:
1358 return static_cast<u32>(125912.055f);
1359 case 4:
1360 return static_cast<u32>(146336.031f);
1361 case 6:
1362 return static_cast<u32>(165812.656f);
1363 default:
1364 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1365 command.parameter.channel_count);
1366 return 0;
1367 }
1368 }
1369 switch (command.parameter.channel_count) {
1370 case 1:
1371 return static_cast<u32>(735.0f);
1372 case 2:
1373 return static_cast<u32>(766.615f);
1374 case 4:
1375 return static_cast<u32>(834.067f);
1376 case 6:
1377 return static_cast<u32>(875.437f);
1378 default:
1379 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1380 return 0;
1381 }
1382 case 240:
1383 if (command.enabled) {
1384 switch (command.parameter.channel_count) {
1385 case 1:
1386 return static_cast<u32>(170292.344f);
1387 case 2:
1388 return static_cast<u32>(183875.625f);
1389 case 4:
1390 return static_cast<u32>(214696.188f);
1391 case 6:
1392 return static_cast<u32>(243846.766f);
1393 default:
1394 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1395 command.parameter.channel_count);
1396 return 0;
1397 }
1398 }
1399 switch (command.parameter.channel_count) {
1400 case 1:
1401 return static_cast<u32>(508.473f);
1402 case 2:
1403 return static_cast<u32>(582.445f);
1404 case 4:
1405 return static_cast<u32>(626.419f);
1406 case 6:
1407 return static_cast<u32>(682.468f);
1408 default:
1409 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1410 return 0;
1411 }
1412 default:
1413 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1414 return 0;
1415 }
1416}
1417
1418u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1419 [[maybe_unused]] const PerformanceCommand& command) const {
1420 switch (sample_count) {
1421 case 160:
1422 return static_cast<u32>(498.17f);
1423 case 240:
1424 return static_cast<u32>(489.42f);
1425 default:
1426 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1427 return 0;
1428 }
1429}
1430
1431u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1432 [[maybe_unused]] const ClearMixBufferCommand& command) const {
1433 switch (sample_count) {
1434 case 160:
1435 return static_cast<u32>(static_cast<f32>(buffer_count - 1) * 266.645f + 0.0f);
1436 case 240:
1437 return static_cast<u32>(static_cast<f32>(buffer_count - 1) * 440.681f + 0.0f);
1438 default:
1439 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1440 return 0;
1441 }
1442}
1443
1444u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1445 [[maybe_unused]] const CopyMixBufferCommand& command) const {
1446 switch (sample_count) {
1447 case 160:
1448 return static_cast<u32>(842.59f);
1449 case 240:
1450 return static_cast<u32>(986.72f);
1451 default:
1452 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1453 return 0;
1454 }
1455}
1456
1457u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1458 const LightLimiterVersion1Command& command) const {
1459 switch (sample_count) {
1460 case 160:
1461 if (command.enabled) {
1462 switch (command.parameter.channel_count) {
1463 case 1:
1464 return static_cast<u32>(21392.383f);
1465 case 2:
1466 return static_cast<u32>(26829.389f);
1467 case 4:
1468 return static_cast<u32>(32405.152f);
1469 case 6:
1470 return static_cast<u32>(52218.586f);
1471 default:
1472 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1473 command.parameter.channel_count);
1474 return 0;
1475 }
1476 }
1477 switch (command.parameter.channel_count) {
1478 case 1:
1479 return static_cast<u32>(897.004f);
1480 case 2:
1481 return static_cast<u32>(931.549f);
1482 case 4:
1483 return static_cast<u32>(975.387f);
1484 case 6:
1485 return static_cast<u32>(1016.778f);
1486 default:
1487 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1488 return 0;
1489 }
1490 case 240:
1491 if (command.enabled) {
1492 switch (command.parameter.channel_count) {
1493 case 1:
1494 return static_cast<u32>(30555.504f);
1495 case 2:
1496 return static_cast<u32>(39010.785f);
1497 case 4:
1498 return static_cast<u32>(48270.18f);
1499 case 6:
1500 return static_cast<u32>(76711.875f);
1501 default:
1502 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1503 command.parameter.channel_count);
1504 return 0;
1505 }
1506 }
1507 switch (command.parameter.channel_count) {
1508 case 1:
1509 return static_cast<u32>(874.429f);
1510 case 2:
1511 return static_cast<u32>(921.553f);
1512 case 4:
1513 return static_cast<u32>(945.262f);
1514 case 6:
1515 return static_cast<u32>(992.26f);
1516 default:
1517 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1518 return 0;
1519 }
1520 default:
1521 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1522 return 0;
1523 }
1524}
1525
1526u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1527 const LightLimiterVersion2Command& command) const {
1528 switch (sample_count) {
1529 case 160:
1530 if (command.enabled) {
1531 if (command.parameter.statistics_enabled) {
1532 switch (command.parameter.channel_count) {
1533 case 1:
1534 return static_cast<u32>(23308.928f);
1535 case 2:
1536 return static_cast<u32>(29954.062f);
1537 case 4:
1538 return static_cast<u32>(35807.477f);
1539 case 6:
1540 return static_cast<u32>(58339.773f);
1541 default:
1542 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1543 command.parameter.channel_count);
1544 return 0;
1545 }
1546 }
1547 switch (command.parameter.channel_count) {
1548 case 1:
1549 return static_cast<u32>(21392.383f);
1550 case 2:
1551 return static_cast<u32>(26829.389f);
1552 case 4:
1553 return static_cast<u32>(32405.152f);
1554 case 6:
1555 return static_cast<u32>(52218.586f);
1556 default:
1557 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1558 command.parameter.channel_count);
1559 return 0;
1560 }
1561 }
1562 switch (command.parameter.channel_count) {
1563 case 1:
1564 return static_cast<u32>(897.004f);
1565 case 2:
1566 return static_cast<u32>(931.549f);
1567 case 4:
1568 return static_cast<u32>(975.387f);
1569 case 6:
1570 return static_cast<u32>(1016.778f);
1571 default:
1572 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1573 return 0;
1574 }
1575 case 240:
1576 if (command.enabled) {
1577 if (command.parameter.statistics_enabled) {
1578 switch (command.parameter.channel_count) {
1579 case 1:
1580 return static_cast<u32>(33526.121f);
1581 case 2:
1582 return static_cast<u32>(43549.355f);
1583 case 4:
1584 return static_cast<u32>(52190.281f);
1585 case 6:
1586 return static_cast<u32>(85526.516f);
1587 default:
1588 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1589 command.parameter.channel_count);
1590 return 0;
1591 }
1592 }
1593 switch (command.parameter.channel_count) {
1594 case 1:
1595 return static_cast<u32>(30555.504f);
1596 case 2:
1597 return static_cast<u32>(39010.785f);
1598 case 4:
1599 return static_cast<u32>(48270.18f);
1600 case 6:
1601 return static_cast<u32>(76711.875f);
1602 default:
1603 LOG_ERROR(Service_Audio, "Invalid channel count {}",
1604 command.parameter.channel_count);
1605 return 0;
1606 }
1607 }
1608 switch (command.parameter.channel_count) {
1609 case 1:
1610 return static_cast<u32>(874.429f);
1611 case 2:
1612 return static_cast<u32>(921.553f);
1613 case 4:
1614 return static_cast<u32>(945.262f);
1615 case 6:
1616 return static_cast<u32>(992.26f);
1617 default:
1618 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
1619 return 0;
1620 }
1621 default:
1622 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1623 return 0;
1624 }
1625}
1626
1627u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1628 [[maybe_unused]] const MultiTapBiquadFilterCommand& command) const {
1629 return 0;
1630}
1631
1632u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1633 [[maybe_unused]] const CaptureCommand& command) const {
1634 return 0;
1635}
1636
1637u32 CommandProcessingTimeEstimatorVersion3::Estimate(
1638 [[maybe_unused]] const CompressorCommand& command) const {
1639 return 0;
1640}
1641
1642u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1643 const PcmInt16DataSourceVersion1Command& command) const {
1644 switch (sample_count) {
1645 case 160:
1646 return static_cast<u32>(
1647 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
1648 (command.pitch * 0.000030518f)) *
1649 427.52f +
1650 6329.442f);
1651 case 240:
1652 return static_cast<u32>(
1653 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
1654 (command.pitch * 0.000030518f)) *
1655 710.143f +
1656 7853.286f);
1657 default:
1658 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1659 return 0;
1660 }
1661}
1662
1663u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1664 const PcmInt16DataSourceVersion2Command& command) const {
1665 switch (sample_count) {
1666 case 160:
1667 switch (command.src_quality) {
1668 case SrcQuality::Medium:
1669 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1670 static_cast<f32>(sample_count)) *
1671 (command.pitch * 0.000030518f)) -
1672 1.0f) *
1673 427.52f +
1674 6329.442f);
1675 case SrcQuality::High:
1676 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1677 static_cast<f32>(sample_count)) *
1678 (command.pitch * 0.000030518f)) -
1679 1.0f) *
1680 371.876f +
1681 8049.415f);
1682 case SrcQuality::Low:
1683 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1684 static_cast<f32>(sample_count)) *
1685 (command.pitch * 0.000030518f)) -
1686 1.0f) *
1687 423.43f +
1688 5062.659f);
1689 default:
1690 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
1691 static_cast<u32>(command.src_quality));
1692 return 0;
1693 }
1694
1695 case 240:
1696 switch (command.src_quality) {
1697 case SrcQuality::Medium:
1698 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1699 static_cast<f32>(sample_count)) *
1700 (command.pitch * 0.000030518f)) -
1701 1.0f) *
1702 710.143f +
1703 7853.286f);
1704 case SrcQuality::High:
1705 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1706 static_cast<f32>(sample_count)) *
1707 (command.pitch * 0.000030518f)) -
1708 1.0f) *
1709 610.487f +
1710 10138.842f);
1711 case SrcQuality::Low:
1712 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1713 static_cast<f32>(sample_count)) *
1714 (command.pitch * 0.000030518f)) -
1715 1.0f) *
1716 676.722f +
1717 5810.962f);
1718 default:
1719 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
1720 static_cast<u32>(command.src_quality));
1721 return 0;
1722 }
1723
1724 default:
1725 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1726 return 0;
1727 }
1728}
1729
1730u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1731 const PcmFloatDataSourceVersion1Command& command) const {
1732 switch (sample_count) {
1733 case 160:
1734 return static_cast<u32>(
1735 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
1736 (command.pitch * 0.000030518f)) *
1737 1672.026f +
1738 7681.211f);
1739 case 240:
1740 return static_cast<u32>(
1741 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
1742 (command.pitch * 0.000030518f)) *
1743 2550.414f +
1744 9663.969f);
1745 default:
1746 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1747 return 0;
1748 }
1749}
1750
1751u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1752 const PcmFloatDataSourceVersion2Command& command) const {
1753 switch (sample_count) {
1754 case 160:
1755 switch (command.src_quality) {
1756 case SrcQuality::Medium:
1757 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1758 static_cast<f32>(sample_count)) *
1759 (command.pitch * 0.000030518f)) -
1760 1.0f) *
1761 1672.026f +
1762 7681.211f);
1763 case SrcQuality::High:
1764 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1765 static_cast<f32>(sample_count)) *
1766 (command.pitch * 0.000030518f)) -
1767 1.0f) *
1768 1672.982f +
1769 9038.011f);
1770 case SrcQuality::Low:
1771 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1772 static_cast<f32>(sample_count)) *
1773 (command.pitch * 0.000030518f)) -
1774 1.0f) *
1775 1673.216f +
1776 6027.577f);
1777 default:
1778 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
1779 static_cast<u32>(command.src_quality));
1780 return 0;
1781 }
1782
1783 case 240:
1784 switch (command.src_quality) {
1785 case SrcQuality::Medium:
1786 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1787 static_cast<f32>(sample_count)) *
1788 (command.pitch * 0.000030518f)) -
1789 1.0f) *
1790 2550.414f +
1791 9663.969f);
1792 case SrcQuality::High:
1793 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1794 static_cast<f32>(sample_count)) *
1795 (command.pitch * 0.000030518f)) -
1796 1.0f) *
1797 2522.303f +
1798 11758.571f);
1799 case SrcQuality::Low:
1800 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1801 static_cast<f32>(sample_count)) *
1802 (command.pitch * 0.000030518f)) -
1803 1.0f) *
1804 2537.061f +
1805 7369.309f);
1806 default:
1807 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
1808 static_cast<u32>(command.src_quality));
1809 return 0;
1810 }
1811
1812 default:
1813 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1814 return 0;
1815 }
1816}
1817
1818u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1819 const AdpcmDataSourceVersion1Command& command) const {
1820 switch (sample_count) {
1821 case 160:
1822 return static_cast<u32>(
1823 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
1824 (command.pitch * 0.000030518f)) *
1825 1827.665f +
1826 7913.808f);
1827 case 240:
1828 return static_cast<u32>(
1829 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
1830 (command.pitch * 0.000030518f)) *
1831 2756.372f +
1832 9736.702f);
1833 default:
1834 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1835 return 0;
1836 }
1837}
1838
1839u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1840 const AdpcmDataSourceVersion2Command& command) const {
1841 switch (sample_count) {
1842 case 160:
1843 switch (command.src_quality) {
1844 case SrcQuality::Medium:
1845 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1846 static_cast<f32>(sample_count)) *
1847 (command.pitch * 0.000030518f)) -
1848 1.0f) *
1849 1827.665f +
1850 7913.808f);
1851 case SrcQuality::High:
1852 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1853 static_cast<f32>(sample_count)) *
1854 (command.pitch * 0.000030518f)) -
1855 1.0f) *
1856 1829.285f +
1857 9607.814f);
1858 case SrcQuality::Low:
1859 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1860 static_cast<f32>(sample_count)) *
1861 (command.pitch * 0.000030518f)) -
1862 1.0f) *
1863 1824.609f +
1864 6517.476f);
1865 default:
1866 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
1867 static_cast<u32>(command.src_quality));
1868 return 0;
1869 }
1870
1871 case 240:
1872 switch (command.src_quality) {
1873 case SrcQuality::Medium:
1874 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1875 static_cast<f32>(sample_count)) *
1876 (command.pitch * 0.000030518f)) -
1877 1.0f) *
1878 2756.372f +
1879 9736.702f);
1880 case SrcQuality::High:
1881 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1882 static_cast<f32>(sample_count)) *
1883 (command.pitch * 0.000030518f)) -
1884 1.0f) *
1885 2731.308f +
1886 12154.379f);
1887 case SrcQuality::Low:
1888 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
1889 static_cast<f32>(sample_count)) *
1890 (command.pitch * 0.000030518f)) -
1891 1.0f) *
1892 2732.152f +
1893 7929.442f);
1894 default:
1895 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
1896 static_cast<u32>(command.src_quality));
1897 return 0;
1898 }
1899
1900 default:
1901 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1902 return 0;
1903 }
1904}
1905
1906u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1907 [[maybe_unused]] const VolumeCommand& command) const {
1908 switch (sample_count) {
1909 case 160:
1910 return static_cast<u32>(1311.1f);
1911 case 240:
1912 return static_cast<u32>(1713.6f);
1913 default:
1914 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1915 return 0;
1916 }
1917}
1918
1919u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1920 [[maybe_unused]] const VolumeRampCommand& command) const {
1921 switch (sample_count) {
1922 case 160:
1923 return static_cast<u32>(1425.3f);
1924 case 240:
1925 return static_cast<u32>(1700.0f);
1926 default:
1927 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1928 return 0;
1929 }
1930}
1931
1932u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1933 [[maybe_unused]] const BiquadFilterCommand& command) const {
1934 switch (sample_count) {
1935 case 160:
1936 return static_cast<u32>(4173.2f);
1937 case 240:
1938 return static_cast<u32>(5585.1f);
1939 default:
1940 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1941 return 0;
1942 }
1943}
1944
1945u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1946 [[maybe_unused]] const MixCommand& command) const {
1947 switch (sample_count) {
1948 case 160:
1949 return static_cast<u32>(1402.8f);
1950 case 240:
1951 return static_cast<u32>(1853.2f);
1952 default:
1953 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1954 return 0;
1955 }
1956}
1957
1958u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1959 [[maybe_unused]] const MixRampCommand& command) const {
1960 switch (sample_count) {
1961 case 160:
1962 return static_cast<u32>(1968.7f);
1963 case 240:
1964 return static_cast<u32>(2459.4f);
1965 default:
1966 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1967 return 0;
1968 }
1969}
1970
1971u32 CommandProcessingTimeEstimatorVersion4::Estimate(const MixRampGroupedCommand& command) const {
1972 u32 count{0};
1973 for (u32 i = 0; i < command.buffer_count; i++) {
1974 if (command.volumes[i] != 0.0f || command.prev_volumes[i] != 0.0f) {
1975 count++;
1976 }
1977 }
1978
1979 switch (sample_count) {
1980 case 160:
1981 return static_cast<u32>((static_cast<f32>(sample_count) * 6.708f) *
1982 static_cast<f32>(count));
1983 case 240:
1984 return static_cast<u32>((static_cast<f32>(sample_count) * 6.443f) *
1985 static_cast<f32>(count));
1986 default:
1987 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
1988 return 0;
1989 }
1990}
1991
1992u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1993 [[maybe_unused]] const DepopPrepareCommand& command) const {
1994 return 0;
1995}
1996
1997u32 CommandProcessingTimeEstimatorVersion4::Estimate(
1998 [[maybe_unused]] const DepopForMixBuffersCommand& command) const {
1999 switch (sample_count) {
2000 case 160:
2001 return static_cast<u32>(739.64f);
2002 case 240:
2003 return static_cast<u32>(910.97f);
2004 default:
2005 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2006 return 0;
2007 }
2008}
2009
2010u32 CommandProcessingTimeEstimatorVersion4::Estimate(const DelayCommand& command) const {
2011 switch (sample_count) {
2012 case 160:
2013 if (command.enabled) {
2014 switch (command.parameter.channel_count) {
2015 case 1:
2016 return static_cast<u32>(8929.042f);
2017 case 2:
2018 return static_cast<u32>(25500.75f);
2019 case 4:
2020 return static_cast<u32>(47759.617f);
2021 case 6:
2022 return static_cast<u32>(82203.07f);
2023 default:
2024 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2025 command.parameter.channel_count);
2026 return 0;
2027 }
2028 }
2029 switch (command.parameter.channel_count) {
2030 case 1:
2031 return static_cast<u32>(1295.206f);
2032 case 2:
2033 return static_cast<u32>(1213.6f);
2034 case 4:
2035 return static_cast<u32>(942.028f);
2036 case 6:
2037 return static_cast<u32>(1001.553f);
2038 default:
2039 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2040 return 0;
2041 }
2042 case 240:
2043 if (command.enabled) {
2044 switch (command.parameter.channel_count) {
2045 case 1:
2046 return static_cast<u32>(11941.051f);
2047 case 2:
2048 return static_cast<u32>(37197.371f);
2049 case 4:
2050 return static_cast<u32>(69749.836f);
2051 case 6:
2052 return static_cast<u32>(120042.398f);
2053 default:
2054 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2055 command.parameter.channel_count);
2056 return 0;
2057 }
2058 }
2059 switch (command.parameter.channel_count) {
2060 case 1:
2061 return static_cast<u32>(997.668f);
2062 case 2:
2063 return static_cast<u32>(977.634f);
2064 case 4:
2065 return static_cast<u32>(792.309f);
2066 case 6:
2067 return static_cast<u32>(875.427f);
2068 default:
2069 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2070 return 0;
2071 }
2072 default:
2073 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2074 return 0;
2075 }
2076}
2077
2078u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2079 [[maybe_unused]] const UpsampleCommand& command) const {
2080 switch (sample_count) {
2081 case 160:
2082 return static_cast<u32>(312990.0f);
2083 case 240:
2084 return static_cast<u32>(0.0f);
2085 default:
2086 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2087 return 0;
2088 }
2089}
2090
2091u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2092 [[maybe_unused]] const DownMix6chTo2chCommand& command) const {
2093 switch (sample_count) {
2094 case 160:
2095 return static_cast<u32>(9949.7f);
2096 case 240:
2097 return static_cast<u32>(14679.0f);
2098 default:
2099 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2100 return 0;
2101 }
2102}
2103
2104u32 CommandProcessingTimeEstimatorVersion4::Estimate(const AuxCommand& command) const {
2105 switch (sample_count) {
2106 case 160:
2107 if (command.enabled) {
2108 return static_cast<u32>(7182.136f);
2109 }
2110 return static_cast<u32>(472.111f);
2111 case 240:
2112 if (command.enabled) {
2113 return static_cast<u32>(9435.961f);
2114 }
2115 return static_cast<u32>(462.619f);
2116 default:
2117 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2118 return 0;
2119 }
2120}
2121
2122u32 CommandProcessingTimeEstimatorVersion4::Estimate(const DeviceSinkCommand& command) const {
2123 switch (command.input_count) {
2124 case 2:
2125 switch (sample_count) {
2126 case 160:
2127 return static_cast<u32>(8979.956f);
2128 case 240:
2129 return static_cast<u32>(9221.907f);
2130 default:
2131 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2132 return 0;
2133 }
2134 case 6:
2135 switch (sample_count) {
2136 case 160:
2137 return static_cast<u32>(9177.903f);
2138 case 240:
2139 return static_cast<u32>(9725.897f);
2140 default:
2141 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2142 return 0;
2143 }
2144 default:
2145 LOG_ERROR(Service_Audio, "Invalid input count {}", command.input_count);
2146 return 0;
2147 }
2148}
2149
2150u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2151 const CircularBufferSinkCommand& command) const {
2152 switch (sample_count) {
2153 case 160:
2154 return static_cast<u32>(static_cast<f32>(command.input_count) * 531.069f + 0.0f);
2155 case 240:
2156 return static_cast<u32>(static_cast<f32>(command.input_count) * 770.257f + 0.0f);
2157 default:
2158 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2159 return 0;
2160 }
2161}
2162
2163u32 CommandProcessingTimeEstimatorVersion4::Estimate(const ReverbCommand& command) const {
2164 switch (sample_count) {
2165 case 160:
2166 if (command.enabled) {
2167 switch (command.parameter.channel_count) {
2168 case 1:
2169 return static_cast<u32>(81475.055f);
2170 case 2:
2171 return static_cast<u32>(84975.0f);
2172 case 4:
2173 return static_cast<u32>(91625.148f);
2174 case 6:
2175 return static_cast<u32>(95332.266f);
2176 default:
2177 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2178 command.parameter.channel_count);
2179 return 0;
2180 }
2181 }
2182 switch (command.parameter.channel_count) {
2183 case 1:
2184 return static_cast<u32>(536.298f);
2185 case 2:
2186 return static_cast<u32>(588.798f);
2187 case 4:
2188 return static_cast<u32>(643.702f);
2189 case 6:
2190 return static_cast<u32>(705.999f);
2191 default:
2192 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2193 return 0;
2194 }
2195 case 240:
2196 if (command.enabled) {
2197 switch (command.parameter.channel_count) {
2198 case 1:
2199 return static_cast<u32>(120174.469f);
2200 case 2:
2201 return static_cast<u32>(125262.219f);
2202 case 4:
2203 return static_cast<u32>(135751.234f);
2204 case 6:
2205 return static_cast<u32>(141129.234f);
2206 default:
2207 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2208 command.parameter.channel_count);
2209 return 0;
2210 }
2211 }
2212 switch (command.parameter.channel_count) {
2213 case 1:
2214 return static_cast<u32>(617.641f);
2215 case 2:
2216 return static_cast<u32>(659.536f);
2217 case 4:
2218 return static_cast<u32>(711.438f);
2219 case 6:
2220 return static_cast<u32>(778.071f);
2221 default:
2222 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2223 return 0;
2224 }
2225 default:
2226 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2227 return 0;
2228 }
2229}
2230
2231u32 CommandProcessingTimeEstimatorVersion4::Estimate(const I3dl2ReverbCommand& command) const {
2232 switch (sample_count) {
2233 case 160:
2234 if (command.enabled) {
2235 switch (command.parameter.channel_count) {
2236 case 1:
2237 return static_cast<u32>(116754.984f);
2238 case 2:
2239 return static_cast<u32>(125912.055f);
2240 case 4:
2241 return static_cast<u32>(146336.031f);
2242 case 6:
2243 return static_cast<u32>(165812.656f);
2244 default:
2245 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2246 command.parameter.channel_count);
2247 return 0;
2248 }
2249 }
2250 switch (command.parameter.channel_count) {
2251 case 1:
2252 return static_cast<u32>(735.0f);
2253 case 2:
2254 return static_cast<u32>(766.615f);
2255 case 4:
2256 return static_cast<u32>(834.067f);
2257 case 6:
2258 return static_cast<u32>(875.437f);
2259 default:
2260 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2261 return 0;
2262 }
2263 case 240:
2264 if (command.enabled) {
2265 switch (command.parameter.channel_count) {
2266 case 1:
2267 return static_cast<u32>(170292.344f);
2268 case 2:
2269 return static_cast<u32>(183875.625f);
2270 case 4:
2271 return static_cast<u32>(214696.188f);
2272 case 6:
2273 return static_cast<u32>(243846.766f);
2274 default:
2275 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2276 command.parameter.channel_count);
2277 return 0;
2278 }
2279 }
2280 switch (command.parameter.channel_count) {
2281 case 1:
2282 return static_cast<u32>(508.473f);
2283 case 2:
2284 return static_cast<u32>(582.445f);
2285 case 4:
2286 return static_cast<u32>(626.419f);
2287 case 6:
2288 return static_cast<u32>(682.468f);
2289 default:
2290 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2291 return 0;
2292 }
2293 default:
2294 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2295 return 0;
2296 }
2297}
2298
2299u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2300 [[maybe_unused]] const PerformanceCommand& command) const {
2301 switch (sample_count) {
2302 case 160:
2303 return static_cast<u32>(498.17f);
2304 case 240:
2305 return static_cast<u32>(489.42f);
2306 default:
2307 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2308 return 0;
2309 }
2310}
2311
2312u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2313 [[maybe_unused]] const ClearMixBufferCommand& command) const {
2314 switch (sample_count) {
2315 case 160:
2316 return static_cast<u32>(static_cast<f32>(buffer_count - 1) * 266.645f + 0.0f);
2317 case 240:
2318 return static_cast<u32>(static_cast<f32>(buffer_count - 1) * 440.681f + 0.0f);
2319 default:
2320 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2321 return 0;
2322 }
2323}
2324
2325u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2326 [[maybe_unused]] const CopyMixBufferCommand& command) const {
2327 switch (sample_count) {
2328 case 160:
2329 return static_cast<u32>(842.59f);
2330 case 240:
2331 return static_cast<u32>(986.72f);
2332 default:
2333 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2334 return 0;
2335 }
2336}
2337
2338u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2339 const LightLimiterVersion1Command& command) const {
2340 switch (sample_count) {
2341 case 160:
2342 if (command.enabled) {
2343 switch (command.parameter.channel_count) {
2344 case 1:
2345 return static_cast<u32>(21392.383f);
2346 case 2:
2347 return static_cast<u32>(26829.389f);
2348 case 4:
2349 return static_cast<u32>(32405.152f);
2350 case 6:
2351 return static_cast<u32>(52218.586f);
2352 default:
2353 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2354 command.parameter.channel_count);
2355 return 0;
2356 }
2357 }
2358 switch (command.parameter.channel_count) {
2359 case 1:
2360 return static_cast<u32>(897.004f);
2361 case 2:
2362 return static_cast<u32>(931.549f);
2363 case 4:
2364 return static_cast<u32>(975.387f);
2365 case 6:
2366 return static_cast<u32>(1016.778f);
2367 default:
2368 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2369 return 0;
2370 }
2371 case 240:
2372 if (command.enabled) {
2373 switch (command.parameter.channel_count) {
2374 case 1:
2375 return static_cast<u32>(30555.504f);
2376 case 2:
2377 return static_cast<u32>(39010.785f);
2378 case 4:
2379 return static_cast<u32>(48270.18f);
2380 case 6:
2381 return static_cast<u32>(76711.875f);
2382 default:
2383 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2384 command.parameter.channel_count);
2385 return 0;
2386 }
2387 }
2388 switch (command.parameter.channel_count) {
2389 case 1:
2390 return static_cast<u32>(874.429f);
2391 case 2:
2392 return static_cast<u32>(921.553f);
2393 case 4:
2394 return static_cast<u32>(945.262f);
2395 case 6:
2396 return static_cast<u32>(992.26f);
2397 default:
2398 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2399 return 0;
2400 }
2401 default:
2402 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2403 return 0;
2404 }
2405}
2406
2407u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2408 const LightLimiterVersion2Command& command) const {
2409 switch (sample_count) {
2410 case 160:
2411 if (command.enabled) {
2412 if (command.parameter.statistics_enabled) {
2413 switch (command.parameter.channel_count) {
2414 case 1:
2415 return static_cast<u32>(23308.928f);
2416 case 2:
2417 return static_cast<u32>(29954.062f);
2418 case 4:
2419 return static_cast<u32>(35807.477f);
2420 case 6:
2421 return static_cast<u32>(58339.773f);
2422 default:
2423 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2424 command.parameter.channel_count);
2425 return 0;
2426 }
2427 }
2428 switch (command.parameter.channel_count) {
2429 case 1:
2430 return static_cast<u32>(21392.383f);
2431 case 2:
2432 return static_cast<u32>(26829.389f);
2433 case 4:
2434 return static_cast<u32>(32405.152f);
2435 case 6:
2436 return static_cast<u32>(52218.586f);
2437 default:
2438 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2439 command.parameter.channel_count);
2440 return 0;
2441 }
2442 }
2443 switch (command.parameter.channel_count) {
2444 case 1:
2445 return static_cast<u32>(897.004f);
2446 case 2:
2447 return static_cast<u32>(931.549f);
2448 case 4:
2449 return static_cast<u32>(975.387f);
2450 case 6:
2451 return static_cast<u32>(1016.778f);
2452 default:
2453 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2454 return 0;
2455 }
2456 case 240:
2457 if (command.enabled) {
2458 if (command.parameter.statistics_enabled) {
2459 switch (command.parameter.channel_count) {
2460 case 1:
2461 return static_cast<u32>(33526.121f);
2462 case 2:
2463 return static_cast<u32>(43549.355f);
2464 case 4:
2465 return static_cast<u32>(52190.281f);
2466 case 6:
2467 return static_cast<u32>(85526.516f);
2468 default:
2469 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2470 command.parameter.channel_count);
2471 return 0;
2472 }
2473 }
2474 switch (command.parameter.channel_count) {
2475 case 1:
2476 return static_cast<u32>(30555.504f);
2477 case 2:
2478 return static_cast<u32>(39010.785f);
2479 case 4:
2480 return static_cast<u32>(48270.18f);
2481 case 6:
2482 return static_cast<u32>(76711.875f);
2483 default:
2484 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2485 command.parameter.channel_count);
2486 return 0;
2487 }
2488 }
2489 switch (command.parameter.channel_count) {
2490 case 1:
2491 return static_cast<u32>(874.429f);
2492 case 2:
2493 return static_cast<u32>(921.553f);
2494 case 4:
2495 return static_cast<u32>(945.262f);
2496 case 6:
2497 return static_cast<u32>(992.26f);
2498 default:
2499 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2500 return 0;
2501 }
2502 default:
2503 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2504 return 0;
2505 }
2506}
2507
2508u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2509 [[maybe_unused]] const MultiTapBiquadFilterCommand& command) const {
2510 switch (sample_count) {
2511 case 160:
2512 return static_cast<u32>(7424.5f);
2513 case 240:
2514 return static_cast<u32>(9730.4f);
2515 default:
2516 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2517 return 0;
2518 }
2519}
2520
2521u32 CommandProcessingTimeEstimatorVersion4::Estimate(const CaptureCommand& command) const {
2522 switch (sample_count) {
2523 case 160:
2524 if (command.enabled) {
2525 return static_cast<u32>(426.982f);
2526 }
2527 return static_cast<u32>(4261.005f);
2528 case 240:
2529 if (command.enabled) {
2530 return static_cast<u32>(435.204f);
2531 }
2532 return static_cast<u32>(5858.265f);
2533 default:
2534 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2535 return 0;
2536 }
2537}
2538
2539u32 CommandProcessingTimeEstimatorVersion4::Estimate(
2540 [[maybe_unused]] const CompressorCommand& command) const {
2541 return 0;
2542}
2543
2544u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2545 const PcmInt16DataSourceVersion1Command& command) const {
2546 switch (sample_count) {
2547 case 160:
2548 return static_cast<u32>(
2549 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
2550 (command.pitch * 0.000030518f)) *
2551 427.52f +
2552 6329.442f);
2553 case 240:
2554 return static_cast<u32>(
2555 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
2556 (command.pitch * 0.000030518f)) *
2557 710.143f +
2558 7853.286f);
2559 default:
2560 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2561 return 0;
2562 }
2563}
2564
2565u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2566 const PcmInt16DataSourceVersion2Command& command) const {
2567 switch (sample_count) {
2568 case 160:
2569 switch (command.src_quality) {
2570 case SrcQuality::Medium:
2571 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2572 static_cast<f32>(sample_count)) *
2573 (command.pitch * 0.000030518f)) -
2574 1.0f) *
2575 427.52f +
2576 6329.442f);
2577 case SrcQuality::High:
2578 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2579 static_cast<f32>(sample_count)) *
2580 (command.pitch * 0.000030518f)) -
2581 1.0f) *
2582 371.876f +
2583 8049.415f);
2584 case SrcQuality::Low:
2585 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2586 static_cast<f32>(sample_count)) *
2587 (command.pitch * 0.000030518f)) -
2588 1.0f) *
2589 423.43f +
2590 5062.659f);
2591 default:
2592 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
2593 static_cast<u32>(command.src_quality));
2594 return 0;
2595 }
2596
2597 case 240:
2598 switch (command.src_quality) {
2599 case SrcQuality::Medium:
2600 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2601 static_cast<f32>(sample_count)) *
2602 (command.pitch * 0.000030518f)) -
2603 1.0f) *
2604 710.143f +
2605 7853.286f);
2606 case SrcQuality::High:
2607 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2608 static_cast<f32>(sample_count)) *
2609 (command.pitch * 0.000030518f)) -
2610 1.0f) *
2611 610.487f +
2612 10138.842f);
2613 case SrcQuality::Low:
2614 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2615 static_cast<f32>(sample_count)) *
2616 (command.pitch * 0.000030518f)) -
2617 1.0f) *
2618 676.722f +
2619 5810.962f);
2620 default:
2621 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
2622 static_cast<u32>(command.src_quality));
2623 return 0;
2624 }
2625
2626 default:
2627 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2628 return 0;
2629 }
2630}
2631
2632u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2633 const PcmFloatDataSourceVersion1Command& command) const {
2634 switch (sample_count) {
2635 case 160:
2636 return static_cast<u32>(
2637 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
2638 (command.pitch * 0.000030518f)) *
2639 1672.026f +
2640 7681.211f);
2641 case 240:
2642 return static_cast<u32>(
2643 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
2644 (command.pitch * 0.000030518f)) *
2645 2550.414f +
2646 9663.969f);
2647 default:
2648 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2649 return 0;
2650 }
2651}
2652
2653u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2654 const PcmFloatDataSourceVersion2Command& command) const {
2655 switch (sample_count) {
2656 case 160:
2657 switch (command.src_quality) {
2658 case SrcQuality::Medium:
2659 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2660 static_cast<f32>(sample_count)) *
2661 (command.pitch * 0.000030518f)) -
2662 1.0f) *
2663 1672.026f +
2664 7681.211f);
2665 case SrcQuality::High:
2666 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2667 static_cast<f32>(sample_count)) *
2668 (command.pitch * 0.000030518f)) -
2669 1.0f) *
2670 1672.982f +
2671 9038.011f);
2672 case SrcQuality::Low:
2673 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2674 static_cast<f32>(sample_count)) *
2675 (command.pitch * 0.000030518f)) -
2676 1.0f) *
2677 1673.216f +
2678 6027.577f);
2679 default:
2680 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
2681 static_cast<u32>(command.src_quality));
2682 return 0;
2683 }
2684
2685 case 240:
2686 switch (command.src_quality) {
2687 case SrcQuality::Medium:
2688 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2689 static_cast<f32>(sample_count)) *
2690 (command.pitch * 0.000030518f)) -
2691 1.0f) *
2692 2550.414f +
2693 9663.969f);
2694 case SrcQuality::High:
2695 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2696 static_cast<f32>(sample_count)) *
2697 (command.pitch * 0.000030518f)) -
2698 1.0f) *
2699 2522.303f +
2700 11758.571f);
2701 case SrcQuality::Low:
2702 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2703 static_cast<f32>(sample_count)) *
2704 (command.pitch * 0.000030518f)) -
2705 1.0f) *
2706 2537.061f +
2707 7369.309f);
2708 default:
2709 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
2710 static_cast<u32>(command.src_quality));
2711 return 0;
2712 }
2713
2714 default:
2715 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2716 return 0;
2717 }
2718}
2719
2720u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2721 const AdpcmDataSourceVersion1Command& command) const {
2722 switch (sample_count) {
2723 case 160:
2724 return static_cast<u32>(
2725 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
2726 (command.pitch * 0.000030518f)) *
2727 1827.665f +
2728 7913.808f);
2729 case 240:
2730 return static_cast<u32>(
2731 ((static_cast<f32>(command.sample_rate) / 200.0f / static_cast<f32>(sample_count)) *
2732 (command.pitch * 0.000030518f)) *
2733 2756.372f +
2734 9736.702f);
2735 default:
2736 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2737 return 0;
2738 }
2739}
2740
2741u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2742 const AdpcmDataSourceVersion2Command& command) const {
2743 switch (sample_count) {
2744 case 160:
2745 switch (command.src_quality) {
2746 case SrcQuality::Medium:
2747 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2748 static_cast<f32>(sample_count)) *
2749 (command.pitch * 0.000030518f)) -
2750 1.0f) *
2751 1827.665f +
2752 7913.808f);
2753 case SrcQuality::High:
2754 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2755 static_cast<f32>(sample_count)) *
2756 (command.pitch * 0.000030518f)) -
2757 1.0f) *
2758 1829.285f +
2759 9607.814f);
2760 case SrcQuality::Low:
2761 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2762 static_cast<f32>(sample_count)) *
2763 (command.pitch * 0.000030518f)) -
2764 1.0f) *
2765 1824.609f +
2766 6517.476f);
2767 default:
2768 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
2769 static_cast<u32>(command.src_quality));
2770 return 0;
2771 }
2772
2773 case 240:
2774 switch (command.src_quality) {
2775 case SrcQuality::Medium:
2776 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2777 static_cast<f32>(sample_count)) *
2778 (command.pitch * 0.000030518f)) -
2779 1.0f) *
2780 2756.372f +
2781 9736.702f);
2782 case SrcQuality::High:
2783 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2784 static_cast<f32>(sample_count)) *
2785 (command.pitch * 0.000030518f)) -
2786 1.0f) *
2787 2731.308f +
2788 12154.379f);
2789 case SrcQuality::Low:
2790 return static_cast<u32>((((static_cast<f32>(command.sample_rate) / 200.0f /
2791 static_cast<f32>(sample_count)) *
2792 (command.pitch * 0.000030518f)) -
2793 1.0f) *
2794 2732.152f +
2795 7929.442f);
2796 default:
2797 LOG_ERROR(Service_Audio, "Invalid SRC quality {}",
2798 static_cast<u32>(command.src_quality));
2799 return 0;
2800 }
2801
2802 default:
2803 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2804 return 0;
2805 }
2806}
2807
2808u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2809 [[maybe_unused]] const VolumeCommand& command) const {
2810 switch (sample_count) {
2811 case 160:
2812 return static_cast<u32>(1311.1f);
2813 case 240:
2814 return static_cast<u32>(1713.6f);
2815 default:
2816 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2817 return 0;
2818 }
2819}
2820
2821u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2822 [[maybe_unused]] const VolumeRampCommand& command) const {
2823 switch (sample_count) {
2824 case 160:
2825 return static_cast<u32>(1425.3f);
2826 case 240:
2827 return static_cast<u32>(1700.0f);
2828 default:
2829 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2830 return 0;
2831 }
2832}
2833
2834u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2835 [[maybe_unused]] const BiquadFilterCommand& command) const {
2836 switch (sample_count) {
2837 case 160:
2838 return static_cast<u32>(4173.2f);
2839 case 240:
2840 return static_cast<u32>(5585.1f);
2841 default:
2842 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2843 return 0;
2844 }
2845}
2846
2847u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2848 [[maybe_unused]] const MixCommand& command) const {
2849 switch (sample_count) {
2850 case 160:
2851 return static_cast<u32>(1402.8f);
2852 case 240:
2853 return static_cast<u32>(1853.2f);
2854 default:
2855 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2856 return 0;
2857 }
2858}
2859
2860u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2861 [[maybe_unused]] const MixRampCommand& command) const {
2862 switch (sample_count) {
2863 case 160:
2864 return static_cast<u32>(1968.7f);
2865 case 240:
2866 return static_cast<u32>(2459.4f);
2867 default:
2868 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2869 return 0;
2870 }
2871}
2872
2873u32 CommandProcessingTimeEstimatorVersion5::Estimate(const MixRampGroupedCommand& command) const {
2874 u32 count{0};
2875 for (u32 i = 0; i < command.buffer_count; i++) {
2876 if (command.volumes[i] != 0.0f || command.prev_volumes[i] != 0.0f) {
2877 count++;
2878 }
2879 }
2880
2881 switch (sample_count) {
2882 case 160:
2883 return static_cast<u32>((static_cast<f32>(sample_count) * 6.708f) *
2884 static_cast<f32>(count));
2885 case 240:
2886 return static_cast<u32>((static_cast<f32>(sample_count) * 6.443f) *
2887 static_cast<f32>(count));
2888 default:
2889 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2890 return 0;
2891 }
2892}
2893
2894u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2895 [[maybe_unused]] const DepopPrepareCommand& command) const {
2896 return 0;
2897}
2898
2899u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2900 [[maybe_unused]] const DepopForMixBuffersCommand& command) const {
2901 switch (sample_count) {
2902 case 160:
2903 return static_cast<u32>(739.64f);
2904 case 240:
2905 return static_cast<u32>(910.97f);
2906 default:
2907 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2908 return 0;
2909 }
2910}
2911
2912u32 CommandProcessingTimeEstimatorVersion5::Estimate(const DelayCommand& command) const {
2913 switch (sample_count) {
2914 case 160:
2915 if (command.enabled) {
2916 switch (command.parameter.channel_count) {
2917 case 1:
2918 return static_cast<u32>(8929.042f);
2919 case 2:
2920 return static_cast<u32>(25500.75f);
2921 case 4:
2922 return static_cast<u32>(47759.617f);
2923 case 6:
2924 return static_cast<u32>(82203.07f);
2925 default:
2926 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2927 command.parameter.channel_count);
2928 return 0;
2929 }
2930 }
2931 switch (command.parameter.channel_count) {
2932 case 1:
2933 return static_cast<u32>(1295.206f);
2934 case 2:
2935 return static_cast<u32>(1213.6f);
2936 case 4:
2937 return static_cast<u32>(942.028f);
2938 case 6:
2939 return static_cast<u32>(1001.553f);
2940 default:
2941 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2942 return 0;
2943 }
2944 case 240:
2945 if (command.enabled) {
2946 switch (command.parameter.channel_count) {
2947 case 1:
2948 return static_cast<u32>(11941.051f);
2949 case 2:
2950 return static_cast<u32>(37197.371f);
2951 case 4:
2952 return static_cast<u32>(69749.836f);
2953 case 6:
2954 return static_cast<u32>(120042.398f);
2955 default:
2956 LOG_ERROR(Service_Audio, "Invalid channel count {}",
2957 command.parameter.channel_count);
2958 return 0;
2959 }
2960 }
2961 switch (command.parameter.channel_count) {
2962 case 1:
2963 return static_cast<u32>(997.668f);
2964 case 2:
2965 return static_cast<u32>(977.634f);
2966 case 4:
2967 return static_cast<u32>(792.309f);
2968 case 6:
2969 return static_cast<u32>(875.427f);
2970 default:
2971 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
2972 return 0;
2973 }
2974 default:
2975 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2976 return 0;
2977 }
2978}
2979
2980u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2981 [[maybe_unused]] const UpsampleCommand& command) const {
2982 switch (sample_count) {
2983 case 160:
2984 return static_cast<u32>(312990.0f);
2985 case 240:
2986 return static_cast<u32>(0.0f);
2987 default:
2988 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
2989 return 0;
2990 }
2991}
2992
2993u32 CommandProcessingTimeEstimatorVersion5::Estimate(
2994 [[maybe_unused]] const DownMix6chTo2chCommand& command) const {
2995 switch (sample_count) {
2996 case 160:
2997 return static_cast<u32>(9949.7f);
2998 case 240:
2999 return static_cast<u32>(14679.0f);
3000 default:
3001 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3002 return 0;
3003 }
3004}
3005
3006u32 CommandProcessingTimeEstimatorVersion5::Estimate(const AuxCommand& command) const {
3007 switch (sample_count) {
3008 case 160:
3009 if (command.enabled) {
3010 return static_cast<u32>(7182.136f);
3011 }
3012 return static_cast<u32>(472.111f);
3013 case 240:
3014 if (command.enabled) {
3015 return static_cast<u32>(9435.961f);
3016 }
3017 return static_cast<u32>(462.619f);
3018 default:
3019 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3020 return 0;
3021 }
3022}
3023
3024u32 CommandProcessingTimeEstimatorVersion5::Estimate(const DeviceSinkCommand& command) const {
3025 switch (command.input_count) {
3026 case 2:
3027 switch (sample_count) {
3028 case 160:
3029 return static_cast<u32>(8979.956f);
3030 case 240:
3031 return static_cast<u32>(9221.907f);
3032 default:
3033 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3034 return 0;
3035 }
3036 case 6:
3037 switch (sample_count) {
3038 case 160:
3039 return static_cast<u32>(9177.903f);
3040 case 240:
3041 return static_cast<u32>(9725.897f);
3042 default:
3043 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3044 return 0;
3045 }
3046 default:
3047 LOG_ERROR(Service_Audio, "Invalid input count {}", command.input_count);
3048 return 0;
3049 }
3050}
3051
3052u32 CommandProcessingTimeEstimatorVersion5::Estimate(
3053 const CircularBufferSinkCommand& command) const {
3054 switch (sample_count) {
3055 case 160:
3056 return static_cast<u32>(static_cast<f32>(command.input_count) * 531.069f + 0.0f);
3057 case 240:
3058 return static_cast<u32>(static_cast<f32>(command.input_count) * 770.257f + 0.0f);
3059 default:
3060 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3061 return 0;
3062 }
3063}
3064
3065u32 CommandProcessingTimeEstimatorVersion5::Estimate(const ReverbCommand& command) const {
3066 switch (sample_count) {
3067 case 160:
3068 if (command.enabled) {
3069 switch (command.parameter.channel_count) {
3070 case 1:
3071 return static_cast<u32>(81475.055f);
3072 case 2:
3073 return static_cast<u32>(84975.0f);
3074 case 4:
3075 return static_cast<u32>(91625.148f);
3076 case 6:
3077 return static_cast<u32>(95332.266f);
3078 default:
3079 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3080 command.parameter.channel_count);
3081 return 0;
3082 }
3083 }
3084 switch (command.parameter.channel_count) {
3085 case 1:
3086 return static_cast<u32>(536.298f);
3087 case 2:
3088 return static_cast<u32>(588.798f);
3089 case 4:
3090 return static_cast<u32>(643.702f);
3091 case 6:
3092 return static_cast<u32>(705.999f);
3093 default:
3094 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3095 return 0;
3096 }
3097 case 240:
3098 if (command.enabled) {
3099 switch (command.parameter.channel_count) {
3100 case 1:
3101 return static_cast<u32>(120174.469f);
3102 case 2:
3103 return static_cast<u32>(125262.219f);
3104 case 4:
3105 return static_cast<u32>(135751.234f);
3106 case 6:
3107 return static_cast<u32>(141129.234f);
3108 default:
3109 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3110 command.parameter.channel_count);
3111 return 0;
3112 }
3113 }
3114 switch (command.parameter.channel_count) {
3115 case 1:
3116 return static_cast<u32>(617.641f);
3117 case 2:
3118 return static_cast<u32>(659.536f);
3119 case 4:
3120 return static_cast<u32>(711.438f);
3121 case 6:
3122 return static_cast<u32>(778.071f);
3123 default:
3124 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3125 return 0;
3126 }
3127 default:
3128 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3129 return 0;
3130 }
3131}
3132
3133u32 CommandProcessingTimeEstimatorVersion5::Estimate(const I3dl2ReverbCommand& command) const {
3134 switch (sample_count) {
3135 case 160:
3136 if (command.enabled) {
3137 switch (command.parameter.channel_count) {
3138 case 1:
3139 return static_cast<u32>(116754.984f);
3140 case 2:
3141 return static_cast<u32>(125912.055f);
3142 case 4:
3143 return static_cast<u32>(146336.031f);
3144 case 6:
3145 return static_cast<u32>(165812.656f);
3146 default:
3147 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3148 command.parameter.channel_count);
3149 return 0;
3150 }
3151 }
3152 switch (command.parameter.channel_count) {
3153 case 1:
3154 return static_cast<u32>(735.0f);
3155 case 2:
3156 return static_cast<u32>(766.615f);
3157 case 4:
3158 return static_cast<u32>(834.067f);
3159 case 6:
3160 return static_cast<u32>(875.437f);
3161 default:
3162 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3163 return 0;
3164 }
3165 case 240:
3166 if (command.enabled) {
3167 switch (command.parameter.channel_count) {
3168 case 1:
3169 return static_cast<u32>(170292.344f);
3170 case 2:
3171 return static_cast<u32>(183875.625f);
3172 case 4:
3173 return static_cast<u32>(214696.188f);
3174 case 6:
3175 return static_cast<u32>(243846.766f);
3176 default:
3177 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3178 command.parameter.channel_count);
3179 return 0;
3180 }
3181 }
3182 switch (command.parameter.channel_count) {
3183 case 1:
3184 return static_cast<u32>(508.473f);
3185 case 2:
3186 return static_cast<u32>(582.445f);
3187 case 4:
3188 return static_cast<u32>(626.419f);
3189 case 6:
3190 return static_cast<u32>(682.468f);
3191 default:
3192 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3193 return 0;
3194 }
3195 default:
3196 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3197 return 0;
3198 }
3199}
3200
3201u32 CommandProcessingTimeEstimatorVersion5::Estimate(
3202 [[maybe_unused]] const PerformanceCommand& command) const {
3203 switch (sample_count) {
3204 case 160:
3205 return static_cast<u32>(498.17f);
3206 case 240:
3207 return static_cast<u32>(489.42f);
3208 default:
3209 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3210 return 0;
3211 }
3212}
3213
3214u32 CommandProcessingTimeEstimatorVersion5::Estimate(
3215 [[maybe_unused]] const ClearMixBufferCommand& command) const {
3216 switch (sample_count) {
3217 case 160:
3218 return static_cast<u32>(static_cast<f32>(buffer_count - 1) * 266.645f + 0.0f);
3219 case 240:
3220 return static_cast<u32>(static_cast<f32>(buffer_count - 1) * 440.681f + 0.0f);
3221 default:
3222 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3223 return 0;
3224 }
3225}
3226
3227u32 CommandProcessingTimeEstimatorVersion5::Estimate(
3228 [[maybe_unused]] const CopyMixBufferCommand& command) const {
3229 switch (sample_count) {
3230 case 160:
3231 return static_cast<u32>(842.59f);
3232 case 240:
3233 return static_cast<u32>(986.72f);
3234 default:
3235 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3236 return 0;
3237 }
3238}
3239
3240u32 CommandProcessingTimeEstimatorVersion5::Estimate(
3241 const LightLimiterVersion1Command& command) const {
3242 switch (sample_count) {
3243 case 160:
3244 if (command.enabled) {
3245 switch (command.parameter.channel_count) {
3246 case 1:
3247 return static_cast<u32>(21508.01f);
3248 case 2:
3249 return static_cast<u32>(23120.453f);
3250 case 4:
3251 return static_cast<u32>(26270.053f);
3252 case 6:
3253 return static_cast<u32>(40471.902f);
3254 default:
3255 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3256 command.parameter.channel_count);
3257 return 0;
3258 }
3259 }
3260 switch (command.parameter.channel_count) {
3261 case 1:
3262 return static_cast<u32>(897.004f);
3263 case 2:
3264 return static_cast<u32>(931.549f);
3265 case 4:
3266 return static_cast<u32>(975.387f);
3267 case 6:
3268 return static_cast<u32>(1016.778f);
3269 default:
3270 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3271 return 0;
3272 }
3273 case 240:
3274 if (command.enabled) {
3275 switch (command.parameter.channel_count) {
3276 case 1:
3277 return static_cast<u32>(30565.961f);
3278 case 2:
3279 return static_cast<u32>(32812.91f);
3280 case 4:
3281 return static_cast<u32>(37354.852f);
3282 case 6:
3283 return static_cast<u32>(58486.699f);
3284 default:
3285 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3286 command.parameter.channel_count);
3287 return 0;
3288 }
3289 }
3290 switch (command.parameter.channel_count) {
3291 case 1:
3292 return static_cast<u32>(874.429f);
3293 case 2:
3294 return static_cast<u32>(921.553f);
3295 case 4:
3296 return static_cast<u32>(945.262f);
3297 case 6:
3298 return static_cast<u32>(992.26f);
3299 default:
3300 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3301 return 0;
3302 }
3303 default:
3304 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3305 return 0;
3306 }
3307}
3308
3309u32 CommandProcessingTimeEstimatorVersion5::Estimate(
3310 const LightLimiterVersion2Command& command) const {
3311 switch (sample_count) {
3312 case 160:
3313 if (command.enabled) {
3314 if (command.parameter.processing_mode == LightLimiterInfo::ProcessingMode::Mode0) {
3315 if (command.parameter.statistics_enabled) {
3316 switch (command.parameter.channel_count) {
3317 case 1:
3318 return static_cast<u32>(23639.584f);
3319 case 2:
3320 return static_cast<u32>(24666.725f);
3321 case 4:
3322 return static_cast<u32>(28876.459f);
3323 case 6:
3324 return static_cast<u32>(47096.078f);
3325 default:
3326 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3327 command.parameter.channel_count);
3328 return 0;
3329 }
3330 } else {
3331 if (command.parameter.statistics_enabled) {
3332 switch (command.parameter.channel_count) {
3333 case 1:
3334 return static_cast<u32>(21508.01f);
3335 case 2:
3336 return static_cast<u32>(23120.453f);
3337 case 4:
3338 return static_cast<u32>(26270.053f);
3339 case 6:
3340 return static_cast<u32>(40471.902f);
3341 default:
3342 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3343 command.parameter.channel_count);
3344 return 0;
3345 }
3346 }
3347 }
3348 } else if (command.parameter.processing_mode ==
3349 LightLimiterInfo::ProcessingMode::Mode1) {
3350 if (command.parameter.statistics_enabled) {
3351 switch (command.parameter.channel_count) {
3352 case 1:
3353 return static_cast<u32>(23639.584f);
3354 case 2:
3355 return static_cast<u32>(29954.062f);
3356 case 4:
3357 return static_cast<u32>(35807.477f);
3358 case 6:
3359 return static_cast<u32>(58339.773f);
3360 default:
3361 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3362 command.parameter.channel_count);
3363 return 0;
3364 }
3365 } else {
3366 if (command.parameter.statistics_enabled) {
3367 switch (command.parameter.channel_count) {
3368 case 1:
3369 return static_cast<u32>(23639.584f);
3370 case 2:
3371 return static_cast<u32>(29954.062f);
3372 case 4:
3373 return static_cast<u32>(35807.477f);
3374 case 6:
3375 return static_cast<u32>(58339.773f);
3376 default:
3377 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3378 command.parameter.channel_count);
3379 return 0;
3380 }
3381 }
3382 }
3383 } else {
3384 LOG_ERROR(Service_Audio, "Invalid processing mode {}",
3385 command.parameter.processing_mode);
3386 return 0;
3387 }
3388 }
3389 switch (command.parameter.channel_count) {
3390 case 1:
3391 return static_cast<u32>(897.004f);
3392 case 2:
3393 return static_cast<u32>(931.549f);
3394 case 4:
3395 return static_cast<u32>(975.387f);
3396 case 6:
3397 return static_cast<u32>(1016.778f);
3398 default:
3399 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3400 return 0;
3401 }
3402 case 240:
3403 if (command.enabled) {
3404 if (command.parameter.processing_mode == LightLimiterInfo::ProcessingMode::Mode0) {
3405 if (command.parameter.statistics_enabled) {
3406 switch (command.parameter.channel_count) {
3407 case 1:
3408 return static_cast<u32>(33875.023f);
3409 case 2:
3410 return static_cast<u32>(35199.938f);
3411 case 4:
3412 return static_cast<u32>(41371.230f);
3413 case 6:
3414 return static_cast<u32>(68370.914f);
3415 default:
3416 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3417 command.parameter.channel_count);
3418 return 0;
3419 }
3420 } else {
3421 switch (command.parameter.channel_count) {
3422 case 1:
3423 return static_cast<u32>(30565.961f);
3424 case 2:
3425 return static_cast<u32>(32812.91f);
3426 case 4:
3427 return static_cast<u32>(37354.852f);
3428 case 6:
3429 return static_cast<u32>(58486.699f);
3430 default:
3431 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3432 command.parameter.channel_count);
3433 return 0;
3434 }
3435 }
3436 } else if (command.parameter.processing_mode ==
3437 LightLimiterInfo::ProcessingMode::Mode1) {
3438 if (command.parameter.statistics_enabled) {
3439 switch (command.parameter.channel_count) {
3440 case 1:
3441 return static_cast<u32>(33942.980f);
3442 case 2:
3443 return static_cast<u32>(28698.893f);
3444 case 4:
3445 return static_cast<u32>(34774.277f);
3446 case 6:
3447 return static_cast<u32>(61897.773f);
3448 default:
3449 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3450 command.parameter.channel_count);
3451 return 0;
3452 }
3453 } else {
3454 switch (command.parameter.channel_count) {
3455 case 1:
3456 return static_cast<u32>(30610.248f);
3457 case 2:
3458 return static_cast<u32>(26322.408f);
3459 case 4:
3460 return static_cast<u32>(30369.000f);
3461 case 6:
3462 return static_cast<u32>(51892.090f);
3463 default:
3464 LOG_ERROR(Service_Audio, "Invalid channel count {}",
3465 command.parameter.channel_count);
3466 return 0;
3467 }
3468 }
3469 } else {
3470 LOG_ERROR(Service_Audio, "Invalid processing mode {}",
3471 command.parameter.processing_mode);
3472 return 0;
3473 }
3474 }
3475 switch (command.parameter.channel_count) {
3476 case 1:
3477 return static_cast<u32>(874.429f);
3478 case 2:
3479 return static_cast<u32>(921.553f);
3480 case 4:
3481 return static_cast<u32>(945.262f);
3482 case 6:
3483 return static_cast<u32>(992.26f);
3484 default:
3485 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3486 return 0;
3487 }
3488 default:
3489 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3490 return 0;
3491 }
3492}
3493
3494u32 CommandProcessingTimeEstimatorVersion5::Estimate(
3495 [[maybe_unused]] const MultiTapBiquadFilterCommand& command) const {
3496 switch (sample_count) {
3497 case 160:
3498 return static_cast<u32>(7424.5f);
3499 case 240:
3500 return static_cast<u32>(9730.4f);
3501 default:
3502 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3503 return 0;
3504 }
3505}
3506
3507u32 CommandProcessingTimeEstimatorVersion5::Estimate(const CaptureCommand& command) const {
3508 switch (sample_count) {
3509 case 160:
3510 if (command.enabled) {
3511 return static_cast<u32>(426.982f);
3512 }
3513 return static_cast<u32>(4261.005f);
3514 case 240:
3515 if (command.enabled) {
3516 return static_cast<u32>(435.204f);
3517 }
3518 return static_cast<u32>(5858.265f);
3519 default:
3520 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3521 return 0;
3522 }
3523}
3524
3525u32 CommandProcessingTimeEstimatorVersion5::Estimate(const CompressorCommand& command) const {
3526 if (command.enabled) {
3527 switch (command.parameter.channel_count) {
3528 case 1:
3529 switch (sample_count) {
3530 case 160:
3531 return static_cast<u32>(34430.570f);
3532 case 240:
3533 return static_cast<u32>(51095.348f);
3534 default:
3535 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3536 return 0;
3537 }
3538 case 2:
3539 switch (sample_count) {
3540 case 160:
3541 return static_cast<u32>(44253.320f);
3542 case 240:
3543 return static_cast<u32>(65693.094f);
3544 default:
3545 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3546 return 0;
3547 }
3548 case 4:
3549 switch (sample_count) {
3550 case 160:
3551 return static_cast<u32>(63827.457f);
3552 case 240:
3553 return static_cast<u32>(95382.852f);
3554 default:
3555 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3556 return 0;
3557 }
3558 case 6:
3559 switch (sample_count) {
3560 case 160:
3561 return static_cast<u32>(83361.484f);
3562 case 240:
3563 return static_cast<u32>(124509.906f);
3564 default:
3565 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3566 return 0;
3567 }
3568 default:
3569 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3570 return 0;
3571 }
3572 }
3573 switch (command.parameter.channel_count) {
3574 case 1:
3575 switch (sample_count) {
3576 case 160:
3577 return static_cast<u32>(630.115f);
3578 case 240:
3579 return static_cast<u32>(840.136f);
3580 default:
3581 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3582 return 0;
3583 }
3584 case 2:
3585 switch (sample_count) {
3586 case 160:
3587 return static_cast<u32>(638.274f);
3588 case 240:
3589 return static_cast<u32>(826.098f);
3590 default:
3591 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3592 return 0;
3593 }
3594 case 4:
3595 switch (sample_count) {
3596 case 160:
3597 return static_cast<u32>(705.862f);
3598 case 240:
3599 return static_cast<u32>(901.876f);
3600 default:
3601 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3602 return 0;
3603 }
3604 case 6:
3605 switch (sample_count) {
3606 case 160:
3607 return static_cast<u32>(782.019f);
3608 case 240:
3609 return static_cast<u32>(965.286f);
3610 default:
3611 LOG_ERROR(Service_Audio, "Invalid sample count {}", sample_count);
3612 return 0;
3613 }
3614 default:
3615 LOG_ERROR(Service_Audio, "Invalid channel count {}", command.parameter.channel_count);
3616 return 0;
3617 }
3618}
3619
3620} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/command_processing_time_estimator.h b/src/audio_core/renderer/command/command_processing_time_estimator.h
new file mode 100644
index 000000000..452217196
--- /dev/null
+++ b/src/audio_core/renderer/command/command_processing_time_estimator.h
@@ -0,0 +1,254 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "audio_core/renderer/command/commands.h"
7#include "common/common_types.h"
8
9namespace AudioCore::AudioRenderer {
10/**
11 * Estimate the processing time required for all commands.
12 */
13class ICommandProcessingTimeEstimator {
14public:
15 virtual ~ICommandProcessingTimeEstimator() = default;
16
17 virtual u32 Estimate(const PcmInt16DataSourceVersion1Command& command) const = 0;
18 virtual u32 Estimate(const PcmInt16DataSourceVersion2Command& command) const = 0;
19 virtual u32 Estimate(const PcmFloatDataSourceVersion1Command& command) const = 0;
20 virtual u32 Estimate(const PcmFloatDataSourceVersion2Command& command) const = 0;
21 virtual u32 Estimate(const AdpcmDataSourceVersion1Command& command) const = 0;
22 virtual u32 Estimate(const AdpcmDataSourceVersion2Command& command) const = 0;
23 virtual u32 Estimate(const VolumeCommand& command) const = 0;
24 virtual u32 Estimate(const VolumeRampCommand& command) const = 0;
25 virtual u32 Estimate(const BiquadFilterCommand& command) const = 0;
26 virtual u32 Estimate(const MixCommand& command) const = 0;
27 virtual u32 Estimate(const MixRampCommand& command) const = 0;
28 virtual u32 Estimate(const MixRampGroupedCommand& command) const = 0;
29 virtual u32 Estimate(const DepopPrepareCommand& command) const = 0;
30 virtual u32 Estimate(const DepopForMixBuffersCommand& command) const = 0;
31 virtual u32 Estimate(const DelayCommand& command) const = 0;
32 virtual u32 Estimate(const UpsampleCommand& command) const = 0;
33 virtual u32 Estimate(const DownMix6chTo2chCommand& command) const = 0;
34 virtual u32 Estimate(const AuxCommand& command) const = 0;
35 virtual u32 Estimate(const DeviceSinkCommand& command) const = 0;
36 virtual u32 Estimate(const CircularBufferSinkCommand& command) const = 0;
37 virtual u32 Estimate(const ReverbCommand& command) const = 0;
38 virtual u32 Estimate(const I3dl2ReverbCommand& command) const = 0;
39 virtual u32 Estimate(const PerformanceCommand& command) const = 0;
40 virtual u32 Estimate(const ClearMixBufferCommand& command) const = 0;
41 virtual u32 Estimate(const CopyMixBufferCommand& command) const = 0;
42 virtual u32 Estimate(const LightLimiterVersion1Command& command) const = 0;
43 virtual u32 Estimate(const LightLimiterVersion2Command& command) const = 0;
44 virtual u32 Estimate(const MultiTapBiquadFilterCommand& command) const = 0;
45 virtual u32 Estimate(const CaptureCommand& command) const = 0;
46 virtual u32 Estimate(const CompressorCommand& command) const = 0;
47};
48
49class CommandProcessingTimeEstimatorVersion1 final : public ICommandProcessingTimeEstimator {
50public:
51 CommandProcessingTimeEstimatorVersion1(u32 sample_count_, u32 buffer_count_)
52 : sample_count{sample_count_}, buffer_count{buffer_count_} {}
53
54 u32 Estimate(const PcmInt16DataSourceVersion1Command& command) const override;
55 u32 Estimate(const PcmInt16DataSourceVersion2Command& command) const override;
56 u32 Estimate(const PcmFloatDataSourceVersion1Command& command) const override;
57 u32 Estimate(const PcmFloatDataSourceVersion2Command& command) const override;
58 u32 Estimate(const AdpcmDataSourceVersion1Command& command) const override;
59 u32 Estimate(const AdpcmDataSourceVersion2Command& command) const override;
60 u32 Estimate(const VolumeCommand& command) const override;
61 u32 Estimate(const VolumeRampCommand& command) const override;
62 u32 Estimate(const BiquadFilterCommand& command) const override;
63 u32 Estimate(const MixCommand& command) const override;
64 u32 Estimate(const MixRampCommand& command) const override;
65 u32 Estimate(const MixRampGroupedCommand& command) const override;
66 u32 Estimate(const DepopPrepareCommand& command) const override;
67 u32 Estimate(const DepopForMixBuffersCommand& command) const override;
68 u32 Estimate(const DelayCommand& command) const override;
69 u32 Estimate(const UpsampleCommand& command) const override;
70 u32 Estimate(const DownMix6chTo2chCommand& command) const override;
71 u32 Estimate(const AuxCommand& command) const override;
72 u32 Estimate(const DeviceSinkCommand& command) const override;
73 u32 Estimate(const CircularBufferSinkCommand& command) const override;
74 u32 Estimate(const ReverbCommand& command) const override;
75 u32 Estimate(const I3dl2ReverbCommand& command) const override;
76 u32 Estimate(const PerformanceCommand& command) const override;
77 u32 Estimate(const ClearMixBufferCommand& command) const override;
78 u32 Estimate(const CopyMixBufferCommand& command) const override;
79 u32 Estimate(const LightLimiterVersion1Command& command) const override;
80 u32 Estimate(const LightLimiterVersion2Command& command) const override;
81 u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
82 u32 Estimate(const CaptureCommand& command) const override;
83 u32 Estimate(const CompressorCommand& command) const override;
84
85private:
86 u32 sample_count{};
87 u32 buffer_count{};
88};
89
90class CommandProcessingTimeEstimatorVersion2 final : public ICommandProcessingTimeEstimator {
91public:
92 CommandProcessingTimeEstimatorVersion2(u32 sample_count_, u32 buffer_count_)
93 : sample_count{sample_count_}, buffer_count{buffer_count_} {}
94
95 u32 Estimate(const PcmInt16DataSourceVersion1Command& command) const override;
96 u32 Estimate(const PcmInt16DataSourceVersion2Command& command) const override;
97 u32 Estimate(const PcmFloatDataSourceVersion1Command& command) const override;
98 u32 Estimate(const PcmFloatDataSourceVersion2Command& command) const override;
99 u32 Estimate(const AdpcmDataSourceVersion1Command& command) const override;
100 u32 Estimate(const AdpcmDataSourceVersion2Command& command) const override;
101 u32 Estimate(const VolumeCommand& command) const override;
102 u32 Estimate(const VolumeRampCommand& command) const override;
103 u32 Estimate(const BiquadFilterCommand& command) const override;
104 u32 Estimate(const MixCommand& command) const override;
105 u32 Estimate(const MixRampCommand& command) const override;
106 u32 Estimate(const MixRampGroupedCommand& command) const override;
107 u32 Estimate(const DepopPrepareCommand& command) const override;
108 u32 Estimate(const DepopForMixBuffersCommand& command) const override;
109 u32 Estimate(const DelayCommand& command) const override;
110 u32 Estimate(const UpsampleCommand& command) const override;
111 u32 Estimate(const DownMix6chTo2chCommand& command) const override;
112 u32 Estimate(const AuxCommand& command) const override;
113 u32 Estimate(const DeviceSinkCommand& command) const override;
114 u32 Estimate(const CircularBufferSinkCommand& command) const override;
115 u32 Estimate(const ReverbCommand& command) const override;
116 u32 Estimate(const I3dl2ReverbCommand& command) const override;
117 u32 Estimate(const PerformanceCommand& command) const override;
118 u32 Estimate(const ClearMixBufferCommand& command) const override;
119 u32 Estimate(const CopyMixBufferCommand& command) const override;
120 u32 Estimate(const LightLimiterVersion1Command& command) const override;
121 u32 Estimate(const LightLimiterVersion2Command& command) const override;
122 u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
123 u32 Estimate(const CaptureCommand& command) const override;
124 u32 Estimate(const CompressorCommand& command) const override;
125
126private:
127 u32 sample_count{};
128 u32 buffer_count{};
129};
130
131class CommandProcessingTimeEstimatorVersion3 final : public ICommandProcessingTimeEstimator {
132public:
133 CommandProcessingTimeEstimatorVersion3(u32 sample_count_, u32 buffer_count_)
134 : sample_count{sample_count_}, buffer_count{buffer_count_} {}
135
136 u32 Estimate(const PcmInt16DataSourceVersion1Command& command) const override;
137 u32 Estimate(const PcmInt16DataSourceVersion2Command& command) const override;
138 u32 Estimate(const PcmFloatDataSourceVersion1Command& command) const override;
139 u32 Estimate(const PcmFloatDataSourceVersion2Command& command) const override;
140 u32 Estimate(const AdpcmDataSourceVersion1Command& command) const override;
141 u32 Estimate(const AdpcmDataSourceVersion2Command& command) const override;
142 u32 Estimate(const VolumeCommand& command) const override;
143 u32 Estimate(const VolumeRampCommand& command) const override;
144 u32 Estimate(const BiquadFilterCommand& command) const override;
145 u32 Estimate(const MixCommand& command) const override;
146 u32 Estimate(const MixRampCommand& command) const override;
147 u32 Estimate(const MixRampGroupedCommand& command) const override;
148 u32 Estimate(const DepopPrepareCommand& command) const override;
149 u32 Estimate(const DepopForMixBuffersCommand& command) const override;
150 u32 Estimate(const DelayCommand& command) const override;
151 u32 Estimate(const UpsampleCommand& command) const override;
152 u32 Estimate(const DownMix6chTo2chCommand& command) const override;
153 u32 Estimate(const AuxCommand& command) const override;
154 u32 Estimate(const DeviceSinkCommand& command) const override;
155 u32 Estimate(const CircularBufferSinkCommand& command) const override;
156 u32 Estimate(const ReverbCommand& command) const override;
157 u32 Estimate(const I3dl2ReverbCommand& command) const override;
158 u32 Estimate(const PerformanceCommand& command) const override;
159 u32 Estimate(const ClearMixBufferCommand& command) const override;
160 u32 Estimate(const CopyMixBufferCommand& command) const override;
161 u32 Estimate(const LightLimiterVersion1Command& command) const override;
162 u32 Estimate(const LightLimiterVersion2Command& command) const override;
163 u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
164 u32 Estimate(const CaptureCommand& command) const override;
165 u32 Estimate(const CompressorCommand& command) const override;
166
167private:
168 u32 sample_count{};
169 u32 buffer_count{};
170};
171
172class CommandProcessingTimeEstimatorVersion4 final : public ICommandProcessingTimeEstimator {
173public:
174 CommandProcessingTimeEstimatorVersion4(u32 sample_count_, u32 buffer_count_)
175 : sample_count{sample_count_}, buffer_count{buffer_count_} {}
176
177 u32 Estimate(const PcmInt16DataSourceVersion1Command& command) const override;
178 u32 Estimate(const PcmInt16DataSourceVersion2Command& command) const override;
179 u32 Estimate(const PcmFloatDataSourceVersion1Command& command) const override;
180 u32 Estimate(const PcmFloatDataSourceVersion2Command& command) const override;
181 u32 Estimate(const AdpcmDataSourceVersion1Command& command) const override;
182 u32 Estimate(const AdpcmDataSourceVersion2Command& command) const override;
183 u32 Estimate(const VolumeCommand& command) const override;
184 u32 Estimate(const VolumeRampCommand& command) const override;
185 u32 Estimate(const BiquadFilterCommand& command) const override;
186 u32 Estimate(const MixCommand& command) const override;
187 u32 Estimate(const MixRampCommand& command) const override;
188 u32 Estimate(const MixRampGroupedCommand& command) const override;
189 u32 Estimate(const DepopPrepareCommand& command) const override;
190 u32 Estimate(const DepopForMixBuffersCommand& command) const override;
191 u32 Estimate(const DelayCommand& command) const override;
192 u32 Estimate(const UpsampleCommand& command) const override;
193 u32 Estimate(const DownMix6chTo2chCommand& command) const override;
194 u32 Estimate(const AuxCommand& command) const override;
195 u32 Estimate(const DeviceSinkCommand& command) const override;
196 u32 Estimate(const CircularBufferSinkCommand& command) const override;
197 u32 Estimate(const ReverbCommand& command) const override;
198 u32 Estimate(const I3dl2ReverbCommand& command) const override;
199 u32 Estimate(const PerformanceCommand& command) const override;
200 u32 Estimate(const ClearMixBufferCommand& command) const override;
201 u32 Estimate(const CopyMixBufferCommand& command) const override;
202 u32 Estimate(const LightLimiterVersion1Command& command) const override;
203 u32 Estimate(const LightLimiterVersion2Command& command) const override;
204 u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
205 u32 Estimate(const CaptureCommand& command) const override;
206 u32 Estimate(const CompressorCommand& command) const override;
207
208private:
209 u32 sample_count{};
210 u32 buffer_count{};
211};
212
213class CommandProcessingTimeEstimatorVersion5 final : public ICommandProcessingTimeEstimator {
214public:
215 CommandProcessingTimeEstimatorVersion5(u32 sample_count_, u32 buffer_count_)
216 : sample_count{sample_count_}, buffer_count{buffer_count_} {}
217
218 u32 Estimate(const PcmInt16DataSourceVersion1Command& command) const override;
219 u32 Estimate(const PcmInt16DataSourceVersion2Command& command) const override;
220 u32 Estimate(const PcmFloatDataSourceVersion1Command& command) const override;
221 u32 Estimate(const PcmFloatDataSourceVersion2Command& command) const override;
222 u32 Estimate(const AdpcmDataSourceVersion1Command& command) const override;
223 u32 Estimate(const AdpcmDataSourceVersion2Command& command) const override;
224 u32 Estimate(const VolumeCommand& command) const override;
225 u32 Estimate(const VolumeRampCommand& command) const override;
226 u32 Estimate(const BiquadFilterCommand& command) const override;
227 u32 Estimate(const MixCommand& command) const override;
228 u32 Estimate(const MixRampCommand& command) const override;
229 u32 Estimate(const MixRampGroupedCommand& command) const override;
230 u32 Estimate(const DepopPrepareCommand& command) const override;
231 u32 Estimate(const DepopForMixBuffersCommand& command) const override;
232 u32 Estimate(const DelayCommand& command) const override;
233 u32 Estimate(const UpsampleCommand& command) const override;
234 u32 Estimate(const DownMix6chTo2chCommand& command) const override;
235 u32 Estimate(const AuxCommand& command) const override;
236 u32 Estimate(const DeviceSinkCommand& command) const override;
237 u32 Estimate(const CircularBufferSinkCommand& command) const override;
238 u32 Estimate(const ReverbCommand& command) const override;
239 u32 Estimate(const I3dl2ReverbCommand& command) const override;
240 u32 Estimate(const PerformanceCommand& command) const override;
241 u32 Estimate(const ClearMixBufferCommand& command) const override;
242 u32 Estimate(const CopyMixBufferCommand& command) const override;
243 u32 Estimate(const LightLimiterVersion1Command& command) const override;
244 u32 Estimate(const LightLimiterVersion2Command& command) const override;
245 u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
246 u32 Estimate(const CaptureCommand& command) const override;
247 u32 Estimate(const CompressorCommand& command) const override;
248
249private:
250 u32 sample_count{};
251 u32 buffer_count{};
252};
253
254} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/commands.h b/src/audio_core/renderer/command/commands.h
new file mode 100644
index 000000000..6d8b8546d
--- /dev/null
+++ b/src/audio_core/renderer/command/commands.h
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "audio_core/renderer/command/data_source/adpcm.h"
7#include "audio_core/renderer/command/data_source/pcm_float.h"
8#include "audio_core/renderer/command/data_source/pcm_int16.h"
9#include "audio_core/renderer/command/effect/aux_.h"
10#include "audio_core/renderer/command/effect/biquad_filter.h"
11#include "audio_core/renderer/command/effect/capture.h"
12#include "audio_core/renderer/command/effect/compressor.h"
13#include "audio_core/renderer/command/effect/delay.h"
14#include "audio_core/renderer/command/effect/i3dl2_reverb.h"
15#include "audio_core/renderer/command/effect/light_limiter.h"
16#include "audio_core/renderer/command/effect/multi_tap_biquad_filter.h"
17#include "audio_core/renderer/command/effect/reverb.h"
18#include "audio_core/renderer/command/icommand.h"
19#include "audio_core/renderer/command/mix/clear_mix.h"
20#include "audio_core/renderer/command/mix/copy_mix.h"
21#include "audio_core/renderer/command/mix/depop_for_mix_buffers.h"
22#include "audio_core/renderer/command/mix/depop_prepare.h"
23#include "audio_core/renderer/command/mix/mix.h"
24#include "audio_core/renderer/command/mix/mix_ramp.h"
25#include "audio_core/renderer/command/mix/mix_ramp_grouped.h"
26#include "audio_core/renderer/command/mix/volume.h"
27#include "audio_core/renderer/command/mix/volume_ramp.h"
28#include "audio_core/renderer/command/performance/performance.h"
29#include "audio_core/renderer/command/resample/downmix_6ch_to_2ch.h"
30#include "audio_core/renderer/command/resample/upsample.h"
31#include "audio_core/renderer/command/sink/circular_buffer.h"
32#include "audio_core/renderer/command/sink/device.h"
diff --git a/src/audio_core/renderer/command/data_source/adpcm.cpp b/src/audio_core/renderer/command/data_source/adpcm.cpp
new file mode 100644
index 000000000..e66ed2990
--- /dev/null
+++ b/src/audio_core/renderer/command/data_source/adpcm.cpp
@@ -0,0 +1,84 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <span>
5
6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/data_source/adpcm.h"
8#include "audio_core/renderer/command/data_source/decode.h"
9
10namespace AudioCore::AudioRenderer {
11
12void AdpcmDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor,
13 std::string& string) {
14 string += fmt::format("AdpcmDataSourceVersion1Command\n\toutput_index {:02X} source sample "
15 "rate {} target sample rate {} src quality {}\n",
16 output_index, sample_rate, processor.target_sample_rate, src_quality);
17}
18
19void AdpcmDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
20 auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count,
21 processor.sample_count)};
22
23 DecodeFromWaveBuffersArgs args{
24 .sample_format{SampleFormat::Adpcm},
25 .output{out_buffer},
26 .voice_state{reinterpret_cast<VoiceState*>(voice_state)},
27 .wave_buffers{wave_buffers},
28 .channel{0},
29 .channel_count{1},
30 .src_quality{src_quality},
31 .pitch{pitch},
32 .source_sample_rate{sample_rate},
33 .target_sample_rate{processor.target_sample_rate},
34 .sample_count{processor.sample_count},
35 .data_address{data_address},
36 .data_size{data_size},
37 .IsVoicePlayedSampleCountResetAtLoopPointSupported{(flags & 1) != 0},
38 .IsVoicePitchAndSrcSkippedSupported{(flags & 2) != 0},
39 };
40
41 DecodeFromWaveBuffers(*processor.memory, args);
42}
43
44bool AdpcmDataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) {
45 return true;
46}
47
48void AdpcmDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor,
49 std::string& string) {
50 string += fmt::format("AdpcmDataSourceVersion2Command\n\toutput_index {:02X} source sample "
51 "rate {} target sample rate {} src quality {}\n",
52 output_index, sample_rate, processor.target_sample_rate, src_quality);
53}
54
55void AdpcmDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
56 auto out_buffer{processor.mix_buffers.subspan(output_index * processor.sample_count,
57 processor.sample_count)};
58
59 DecodeFromWaveBuffersArgs args{
60 .sample_format{SampleFormat::Adpcm},
61 .output{out_buffer},
62 .voice_state{reinterpret_cast<VoiceState*>(voice_state)},
63 .wave_buffers{wave_buffers},
64 .channel{0},
65 .channel_count{1},
66 .src_quality{src_quality},
67 .pitch{pitch},
68 .source_sample_rate{sample_rate},
69 .target_sample_rate{processor.target_sample_rate},
70 .sample_count{processor.sample_count},
71 .data_address{data_address},
72 .data_size{data_size},
73 .IsVoicePlayedSampleCountResetAtLoopPointSupported{(flags & 1) != 0},
74 .IsVoicePitchAndSrcSkippedSupported{(flags & 2) != 0},
75 };
76
77 DecodeFromWaveBuffers(*processor.memory, args);
78}
79
80bool AdpcmDataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) {
81 return true;
82}
83
84} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/data_source/adpcm.h b/src/audio_core/renderer/command/data_source/adpcm.h
new file mode 100644
index 000000000..a9cf9cee4
--- /dev/null
+++ b/src/audio_core/renderer/command/data_source/adpcm.h
@@ -0,0 +1,119 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8
9#include "audio_core/common/common.h"
10#include "audio_core/common/wave_buffer.h"
11#include "audio_core/renderer/command/icommand.h"
12#include "common/common_types.h"
13
14namespace AudioCore::AudioRenderer {
15namespace ADSP {
16class CommandListProcessor;
17}
18
19/**
20 * AudioRenderer command to decode ADPCM-encoded version 1 wavebuffers
21 * into the output_index mix buffer.
22 */
23struct AdpcmDataSourceVersion1Command : ICommand {
24 /**
25 * Print this command's information to a string.
26 *
27 * @param processor - The CommandListProcessor processing this command.
28 * @param string - The string to print into.
29 */
30 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
31
32 /**
33 * Process this command.
34 *
35 * @param processor - The CommandListProcessor processing this command.
36 */
37 void Process(const ADSP::CommandListProcessor& processor) override;
38
39 /**
40 * Verify this command's data is valid.
41 *
42 * @param processor - The CommandListProcessor processing this command.
43 * @return True if the command is valid, otherwise false.
44 */
45 bool Verify(const ADSP::CommandListProcessor& processor) override;
46
47 /// Quality used for sample rate conversion
48 SrcQuality src_quality;
49 /// Mix buffer index for decoded samples
50 s16 output_index;
51 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags)
52 u16 flags;
53 /// Wavebuffer sample rate
54 u32 sample_rate;
55 /// Pitch used for sample rate conversion
56 f32 pitch;
57 /// Wavebuffers containing the wavebuffer address, context address, looping information etc
58 std::array<WaveBufferVersion2, MaxWaveBuffers> wave_buffers;
59 /// Voice state, updated each call and written back to game
60 CpuAddr voice_state;
61 /// Coefficients data address
62 CpuAddr data_address;
63 /// Coefficients data size
64 u64 data_size;
65};
66
67/**
68 * AudioRenderer command to decode ADPCM-encoded version 2 wavebuffers
69 * into the output_index mix buffer.
70 */
71struct AdpcmDataSourceVersion2Command : ICommand {
72 /**
73 * Print this command's information to a string.
74 *
75 * @param processor - The CommandListProcessor processing this command.
76 * @param string - The string to print into.
77 */
78 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
79
80 /**
81 * Process this command.
82 *
83 * @param processor - The CommandListProcessor processing this command.
84 */
85 void Process(const ADSP::CommandListProcessor& processor) override;
86
87 /**
88 * Verify this command's data is valid.
89 *
90 * @param processor - The CommandListProcessor processing this command.
91 * @return True if the command is valid, otherwise false.
92 */
93 bool Verify(const ADSP::CommandListProcessor& processor) override;
94
95 /// Quality used for sample rate conversion
96 SrcQuality src_quality;
97 /// Mix buffer index for decoded samples
98 s16 output_index;
99 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags)
100 u16 flags;
101 /// Wavebuffer sample rate
102 u32 sample_rate;
103 /// Pitch used for sample rate conversion
104 f32 pitch;
105 /// Target channel to read within the wavebuffer
106 s8 channel_index;
107 /// Number of channels within the wavebuffer
108 s8 channel_count;
109 /// Wavebuffers containing the wavebuffer address, context address, looping information etc
110 std::array<WaveBufferVersion2, MaxWaveBuffers> wave_buffers;
111 /// Voice state, updated each call and written back to game
112 CpuAddr voice_state;
113 /// Coefficients data address
114 CpuAddr data_address;
115 /// Coefficients data size
116 u64 data_size;
117};
118
119} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp
new file mode 100644
index 000000000..ff5d31bd6
--- /dev/null
+++ b/src/audio_core/renderer/command/data_source/decode.cpp
@@ -0,0 +1,428 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5#include <vector>
6
7#include "audio_core/renderer/command/data_source/decode.h"
8#include "audio_core/renderer/command/resample/resample.h"
9#include "common/fixed_point.h"
10#include "common/logging/log.h"
11#include "core/memory.h"
12
13namespace AudioCore::AudioRenderer {
14
15constexpr u32 TempBufferSize = 0x3F00;
16constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
17
18/**
19 * Decode PCM data. Only s16 or f32 is supported.
20 *
21 * @tparam T - Type to decode. Only s16 and f32 are supported.
22 * @param memory - Core memory for reading samples.
23 * @param out_buffer - Output mix buffer to receive the samples.
24 * @param req - Information for how to decode.
25 * @return Number of samples decoded.
26 */
27template <typename T>
28static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
29 const DecodeArg& req) {
30 constexpr s32 min{std::numeric_limits<s16>::min()};
31 constexpr s32 max{std::numeric_limits<s16>::max()};
32
33 if (req.buffer == 0 || req.buffer_size == 0) {
34 return 0;
35 }
36
37 if (req.start_offset >= req.end_offset) {
38 return 0;
39 }
40
41 auto samples_to_decode{
42 std::min(req.samples_to_read, req.end_offset - req.start_offset - req.offset)};
43 u32 channel_count{static_cast<u32>(req.channel_count)};
44
45 switch (req.channel_count) {
46 default: {
47 const VAddr source{req.buffer +
48 (((req.start_offset + req.offset) * channel_count) * sizeof(T))};
49 const u64 size{channel_count * samples_to_decode};
50 const u64 size_bytes{size * sizeof(T)};
51
52 std::vector<T> samples(size);
53 memory.ReadBlockUnsafe(source, samples.data(), size_bytes);
54
55 if constexpr (std::is_floating_point_v<T>) {
56 for (u32 i = 0; i < samples_to_decode; i++) {
57 auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
58 std::numeric_limits<s16>::max())};
59 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
60 }
61 } else {
62 for (u32 i = 0; i < samples_to_decode; i++) {
63 out_buffer[i] = samples[i * channel_count + req.target_channel];
64 }
65 }
66 } break;
67
68 case 1:
69 if (req.target_channel != 0) {
70 LOG_ERROR(Service_Audio, "Invalid target channel, expected 0, got {}",
71 req.target_channel);
72 return 0;
73 }
74
75 const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))};
76 std::vector<T> samples(samples_to_decode);
77 memory.ReadBlockUnsafe(source, samples.data(), samples_to_decode * sizeof(T));
78
79 if constexpr (std::is_floating_point_v<T>) {
80 for (u32 i = 0; i < samples_to_decode; i++) {
81 auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] *
82 std::numeric_limits<s16>::max())};
83 out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max));
84 }
85 } else {
86 std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16));
87 }
88 break;
89 }
90
91 return samples_to_decode;
92}
93
94/**
95 * Decode ADPCM data.
96 *
97 * @param memory - Core memory for reading samples.
98 * @param out_buffer - Output mix buffer to receive the samples.
99 * @param req - Information for how to decode.
100 * @return Number of samples decoded.
101 */
102static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
103 const DecodeArg& req) {
104 constexpr u32 SamplesPerFrame{14};
105 constexpr u32 NibblesPerFrame{16};
106
107 if (req.buffer == 0 || req.buffer_size == 0) {
108 return 0;
109 }
110
111 if (req.end_offset < req.start_offset) {
112 return 0;
113 }
114
115 auto end{(req.end_offset % SamplesPerFrame) +
116 NibblesPerFrame * (req.end_offset / SamplesPerFrame)};
117 if (req.end_offset % SamplesPerFrame) {
118 end += 3;
119 } else {
120 end += 1;
121 }
122
123 if (req.buffer_size < end / 2) {
124 return 0;
125 }
126
127 auto samples_to_process{
128 std::min(req.end_offset - req.start_offset - req.offset, req.samples_to_read)};
129
130 auto samples_to_read{samples_to_process};
131 auto start_pos{req.start_offset + req.offset};
132 auto samples_remaining_in_frame{start_pos % SamplesPerFrame};
133 auto position_in_frame{(start_pos / SamplesPerFrame) * NibblesPerFrame +
134 samples_remaining_in_frame};
135
136 if (samples_remaining_in_frame) {
137 position_in_frame += 2;
138 }
139
140 const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)};
141 std::vector<u8> wavebuffer(size);
142 memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(),
143 wavebuffer.size());
144
145 auto context{req.adpcm_context};
146 auto header{context->header};
147 u8 coeff_index{static_cast<u8>((header >> 4U) & 0xFU)};
148 u8 scale{static_cast<u8>(header & 0xFU)};
149 s32 coeff0{req.coefficients[coeff_index * 2 + 0]};
150 s32 coeff1{req.coefficients[coeff_index * 2 + 1]};
151
152 auto yn0{context->yn0};
153 auto yn1{context->yn1};
154
155 static constexpr std::array<s32, 16> Steps{
156 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1,
157 };
158
159 const auto decode_sample = [&](const s32 code) -> s16 {
160 const auto xn = code * (1 << scale);
161 const auto prediction = coeff0 * yn0 + coeff1 * yn1;
162 const auto sample = ((xn << 11) + 0x400 + prediction) >> 11;
163 const auto saturated = std::clamp<s32>(sample, -0x8000, 0x7FFF);
164 yn1 = yn0;
165 yn0 = static_cast<s16>(saturated);
166 return yn0;
167 };
168
169 u32 read_index{0};
170 u32 write_index{0};
171
172 while (samples_to_read > 0) {
173 // Are we at a new frame?
174 if ((position_in_frame % NibblesPerFrame) == 0) {
175 header = wavebuffer[read_index++];
176 coeff_index = (header >> 4) & 0xF;
177 scale = header & 0xF;
178 coeff0 = req.coefficients[coeff_index * 2 + 0];
179 coeff1 = req.coefficients[coeff_index * 2 + 1];
180 position_in_frame += 2;
181
182 // Can we consume all of this frame's samples?
183 if (samples_to_read >= SamplesPerFrame) {
184 // Can grab all samples until the next header
185 for (u32 i = 0; i < SamplesPerFrame / 2; i++) {
186 auto code0{Steps[(wavebuffer[read_index] >> 4) & 0xF]};
187 auto code1{Steps[wavebuffer[read_index] & 0xF]};
188 read_index++;
189
190 out_buffer[write_index++] = decode_sample(code0);
191 out_buffer[write_index++] = decode_sample(code1);
192 }
193
194 position_in_frame += SamplesPerFrame;
195 samples_to_read -= SamplesPerFrame;
196 continue;
197 }
198 }
199
200 // Decode a single sample
201 auto code{wavebuffer[read_index]};
202 if (position_in_frame & 1) {
203 code &= 0xF;
204 read_index++;
205 } else {
206 code >>= 4;
207 }
208
209 out_buffer[write_index++] = decode_sample(Steps[code]);
210
211 position_in_frame++;
212 samples_to_read--;
213 }
214
215 context->header = header;
216 context->yn0 = yn0;
217 context->yn1 = yn1;
218
219 return samples_to_process;
220}
221
222/**
223 * Decode implementation.
224 * Decode wavebuffers according to the given args.
225 *
226 * @param memory - Core memory to read data from.
227 * @param args - The wavebuffer data, and information for how to decode it.
228 */
229void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) {
230 auto& voice_state{*args.voice_state};
231 auto remaining_sample_count{args.sample_count};
232 auto fraction{voice_state.fraction};
233
234 const auto sample_rate_ratio{
235 (Common::FixedPoint<49, 15>(args.source_sample_rate) / args.target_sample_rate) *
236 args.pitch};
237 const auto size_required{fraction + remaining_sample_count * sample_rate_ratio};
238
239 if (size_required < 0) {
240 return;
241 }
242
243 auto pitch{PitchBySrcQuality[static_cast<u32>(args.src_quality)]};
244 if (static_cast<u32>(pitch + size_required.to_int_floor()) > TempBufferSize) {
245 return;
246 }
247
248 auto max_remaining_sample_count{
249 ((Common::FixedPoint<17, 15>(TempBufferSize) - fraction) / sample_rate_ratio)
250 .to_uint_floor()};
251 max_remaining_sample_count = std::min(max_remaining_sample_count, remaining_sample_count);
252
253 auto wavebuffers_consumed{voice_state.wave_buffers_consumed};
254 auto wavebuffer_index{voice_state.wave_buffer_index};
255 auto played_sample_count{voice_state.played_sample_count};
256
257 bool is_buffer_starved{false};
258 u32 offset{voice_state.offset};
259
260 auto output_buffer{args.output};
261 std::vector<s16> temp_buffer(TempBufferSize, 0);
262
263 while (remaining_sample_count > 0) {
264 const auto samples_to_write{std::min(remaining_sample_count, max_remaining_sample_count)};
265 const auto samples_to_read{
266 (fraction + samples_to_write * sample_rate_ratio).to_uint_floor()};
267
268 u32 temp_buffer_pos{0};
269
270 if (!args.IsVoicePitchAndSrcSkippedSupported) {
271 for (u32 i = 0; i < pitch; i++) {
272 temp_buffer[i] = voice_state.sample_history[i];
273 }
274 temp_buffer_pos = pitch;
275 }
276
277 u32 samples_read{0};
278 while (samples_read < samples_to_read) {
279 if (wavebuffer_index >= MaxWaveBuffers) {
280 LOG_ERROR(Service_Audio, "Invalid wavebuffer index! {}", wavebuffer_index);
281 wavebuffer_index = 0;
282 voice_state.wave_buffer_valid.fill(false);
283 wavebuffers_consumed = MaxWaveBuffers;
284 }
285
286 if (!voice_state.wave_buffer_valid[wavebuffer_index]) {
287 is_buffer_starved = true;
288 break;
289 }
290
291 auto& wavebuffer{args.wave_buffers[wavebuffer_index]};
292
293 if (offset == 0 && args.sample_format == SampleFormat::Adpcm &&
294 wavebuffer.context != 0) {
295 memory.ReadBlockUnsafe(wavebuffer.context, &voice_state.adpcm_context,
296 wavebuffer.context_size);
297 }
298
299 auto start_offset{wavebuffer.start_offset};
300 auto end_offset{wavebuffer.end_offset};
301
302 if (wavebuffer.loop && voice_state.loop_count > 0 &&
303 wavebuffer.loop_start_offset != 0 && wavebuffer.loop_end_offset != 0 &&
304 wavebuffer.loop_start_offset <= wavebuffer.loop_end_offset) {
305 start_offset = wavebuffer.loop_start_offset;
306 end_offset = wavebuffer.loop_end_offset;
307 }
308
309 DecodeArg decode_arg{.buffer{wavebuffer.buffer},
310 .buffer_size{wavebuffer.buffer_size},
311 .start_offset{start_offset},
312 .end_offset{end_offset},
313 .channel_count{args.channel_count},
314 .coefficients{},
315 .adpcm_context{nullptr},
316 .target_channel{args.channel},
317 .offset{offset},
318 .samples_to_read{samples_to_read - samples_read}};
319
320 s32 samples_decoded{0};
321
322 switch (args.sample_format) {
323 case SampleFormat::PcmInt16:
324 samples_decoded = DecodePcm<s16>(
325 memory, {&temp_buffer[temp_buffer_pos], TempBufferSize - temp_buffer_pos},
326 decode_arg);
327 break;
328
329 case SampleFormat::PcmFloat:
330 samples_decoded = DecodePcm<f32>(
331 memory, {&temp_buffer[temp_buffer_pos], TempBufferSize - temp_buffer_pos},
332 decode_arg);
333 break;
334
335 case SampleFormat::Adpcm: {
336 decode_arg.adpcm_context = &voice_state.adpcm_context;
337 memory.ReadBlockUnsafe(args.data_address, &decode_arg.coefficients, args.data_size);
338 samples_decoded = DecodeAdpcm(
339 memory, {&temp_buffer[temp_buffer_pos], TempBufferSize - temp_buffer_pos},
340 decode_arg);
341 } break;
342
343 default:
344 LOG_ERROR(Service_Audio, "Invalid sample format to decode {}",
345 static_cast<u32>(args.sample_format));
346 samples_decoded = 0;
347 break;
348 }
349
350 played_sample_count += samples_decoded;
351 samples_read += samples_decoded;
352 temp_buffer_pos += samples_decoded;
353 offset += samples_decoded;
354
355 if (samples_decoded == 0 || offset >= end_offset - start_offset) {
356 offset = 0;
357 if (!wavebuffer.loop) {
358 voice_state.wave_buffer_valid[wavebuffer_index] = false;
359 voice_state.loop_count = 0;
360
361 if (wavebuffer.stream_ended) {
362 played_sample_count = 0;
363 }
364
365 wavebuffer_index = (wavebuffer_index + 1) % MaxWaveBuffers;
366 wavebuffers_consumed++;
367 } else {
368 voice_state.loop_count++;
369 if (wavebuffer.loop_count > 0 &&
370 (voice_state.loop_count > wavebuffer.loop_count || samples_decoded == 0)) {
371 voice_state.wave_buffer_valid[wavebuffer_index] = false;
372 voice_state.loop_count = 0;
373
374 if (wavebuffer.stream_ended) {
375 played_sample_count = 0;
376 }
377
378 wavebuffer_index = (wavebuffer_index + 1) % MaxWaveBuffers;
379 wavebuffers_consumed++;
380 }
381
382 if (samples_decoded == 0) {
383 is_buffer_starved = true;
384 break;
385 }
386
387 if (args.IsVoicePlayedSampleCountResetAtLoopPointSupported) {
388 played_sample_count = 0;
389 }
390 }
391 }
392 }
393
394 if (args.IsVoicePitchAndSrcSkippedSupported) {
395 if (samples_read > output_buffer.size()) {
396 LOG_ERROR(Service_Audio, "Attempting to write past the end of output buffer!");
397 }
398 for (u32 i = 0; i < samples_read; i++) {
399 output_buffer[i] = temp_buffer[i];
400 }
401 } else {
402 std::memset(&temp_buffer[temp_buffer_pos], 0,
403 (samples_to_read - samples_read) * sizeof(s16));
404
405 Resample(output_buffer, temp_buffer, sample_rate_ratio, fraction, samples_to_write,
406 args.src_quality);
407
408 std::memcpy(voice_state.sample_history.data(), &temp_buffer[samples_to_read],
409 pitch * sizeof(s16));
410 }
411
412 remaining_sample_count -= samples_to_write;
413 if (remaining_sample_count != 0 && is_buffer_starved) {
414 LOG_ERROR(Service_Audio, "Samples remaining but buffer is starving??");
415 break;
416 }
417
418 output_buffer = output_buffer.subspan(samples_to_write);
419 }
420
421 voice_state.wave_buffers_consumed = wavebuffers_consumed;
422 voice_state.played_sample_count = played_sample_count;
423 voice_state.wave_buffer_index = wavebuffer_index;
424 voice_state.offset = offset;
425 voice_state.fraction = fraction;
426}
427
428} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/data_source/decode.h b/src/audio_core/renderer/command/data_source/decode.h
new file mode 100644
index 000000000..4d63d6fa8
--- /dev/null
+++ b/src/audio_core/renderer/command/data_source/decode.h
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8
9#include "audio_core/common/common.h"
10#include "audio_core/common/wave_buffer.h"
11#include "audio_core/renderer/voice/voice_state.h"
12#include "common/common_types.h"
13
14namespace Core::Memory {
15class Memory;
16}
17
18namespace AudioCore::AudioRenderer {
19
20struct DecodeFromWaveBuffersArgs {
21 SampleFormat sample_format;
22 std::span<s32> output;
23 VoiceState* voice_state;
24 std::span<WaveBufferVersion2> wave_buffers;
25 s8 channel;
26 s8 channel_count;
27 SrcQuality src_quality;
28 f32 pitch;
29 u32 source_sample_rate;
30 u32 target_sample_rate;
31 u32 sample_count;
32 CpuAddr data_address;
33 u64 data_size;
34 bool IsVoicePlayedSampleCountResetAtLoopPointSupported;
35 bool IsVoicePitchAndSrcSkippedSupported;
36};
37
38struct DecodeArg {
39 CpuAddr buffer;
40 u64 buffer_size;
41 u32 start_offset;
42 u32 end_offset;
43 s8 channel_count;
44 std::array<s16, 16> coefficients;
45 VoiceState::AdpcmContext* adpcm_context;
46 s8 target_channel;
47 u32 offset;
48 u32 samples_to_read;
49};
50
51/**
52 * Decode wavebuffers according to the given args.
53 *
54 * @param memory - Core memory to read data from.
55 * @param args - The wavebuffer data, and information for how to decode it.
56 */
57void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args);
58
59} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/data_source/pcm_float.cpp b/src/audio_core/renderer/command/data_source/pcm_float.cpp
new file mode 100644
index 000000000..be77fab69
--- /dev/null
+++ b/src/audio_core/renderer/command/data_source/pcm_float.cpp
@@ -0,0 +1,86 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/data_source/decode.h"
6#include "audio_core/renderer/command/data_source/pcm_float.h"
7
8namespace AudioCore::AudioRenderer {
9
10void PcmFloatDataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor,
11 std::string& string) {
12 string +=
13 fmt::format("PcmFloatDataSourceVersion1Command\n\toutput_index {:02X} channel {} "
14 "channel count {} source sample rate {} target sample rate {} src quality {}\n",
15 output_index, channel_index, channel_count, sample_rate,
16 processor.target_sample_rate, src_quality);
17}
18
19void PcmFloatDataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
20 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count,
21 processor.sample_count);
22
23 DecodeFromWaveBuffersArgs args{
24 .sample_format{SampleFormat::PcmFloat},
25 .output{out_buffer},
26 .voice_state{reinterpret_cast<VoiceState*>(voice_state)},
27 .wave_buffers{wave_buffers},
28 .channel{channel_index},
29 .channel_count{channel_count},
30 .src_quality{src_quality},
31 .pitch{pitch},
32 .source_sample_rate{sample_rate},
33 .target_sample_rate{processor.target_sample_rate},
34 .sample_count{processor.sample_count},
35 .data_address{0},
36 .data_size{0},
37 .IsVoicePlayedSampleCountResetAtLoopPointSupported{(flags & 1) != 0},
38 .IsVoicePitchAndSrcSkippedSupported{(flags & 2) != 0},
39 };
40
41 DecodeFromWaveBuffers(*processor.memory, args);
42}
43
44bool PcmFloatDataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) {
45 return true;
46}
47
48void PcmFloatDataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor,
49 std::string& string) {
50 string +=
51 fmt::format("PcmFloatDataSourceVersion2Command\n\toutput_index {:02X} channel {} "
52 "channel count {} source sample rate {} target sample rate {} src quality {}\n",
53 output_index, channel_index, channel_count, sample_rate,
54 processor.target_sample_rate, src_quality);
55}
56
57void PcmFloatDataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
58 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count,
59 processor.sample_count);
60
61 DecodeFromWaveBuffersArgs args{
62 .sample_format{SampleFormat::PcmFloat},
63 .output{out_buffer},
64 .voice_state{reinterpret_cast<VoiceState*>(voice_state)},
65 .wave_buffers{wave_buffers},
66 .channel{channel_index},
67 .channel_count{channel_count},
68 .src_quality{src_quality},
69 .pitch{pitch},
70 .source_sample_rate{sample_rate},
71 .target_sample_rate{processor.target_sample_rate},
72 .sample_count{processor.sample_count},
73 .data_address{0},
74 .data_size{0},
75 .IsVoicePlayedSampleCountResetAtLoopPointSupported{(flags & 1) != 0},
76 .IsVoicePitchAndSrcSkippedSupported{(flags & 2) != 0},
77 };
78
79 DecodeFromWaveBuffers(*processor.memory, args);
80}
81
82bool PcmFloatDataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) {
83 return true;
84}
85
86} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/data_source/pcm_float.h b/src/audio_core/renderer/command/data_source/pcm_float.h
new file mode 100644
index 000000000..e4af77c20
--- /dev/null
+++ b/src/audio_core/renderer/command/data_source/pcm_float.h
@@ -0,0 +1,113 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/common/wave_buffer.h"
9#include "audio_core/renderer/command/icommand.h"
10#include "common/common_types.h"
11
12namespace AudioCore::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor;
15}
16
17/**
18 * AudioRenderer command to decode PCM float-encoded version 1 wavebuffers
19 * into the output_index mix buffer.
20 */
21struct PcmFloatDataSourceVersion1Command : ICommand {
22 /**
23 * Print this command's information to a string.
24 *
25 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into.
27 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
29
30 /**
31 * Process this command.
32 *
33 * @param processor - The CommandListProcessor processing this command.
34 */
35 void Process(const ADSP::CommandListProcessor& processor) override;
36
37 /**
38 * Verify this command's data is valid.
39 *
40 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false.
42 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override;
44
45 /// Quality used for sample rate conversion
46 SrcQuality src_quality;
47 /// Mix buffer index for decoded samples
48 s16 output_index;
49 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags)
50 u16 flags;
51 /// Wavebuffer sample rate
52 u32 sample_rate;
53 /// Pitch used for sample rate conversion
54 f32 pitch;
55 /// Target channel to read within the wavebuffer
56 s8 channel_index;
57 /// Number of channels within the wavebuffer
58 s8 channel_count;
59 /// Wavebuffers containing the wavebuffer address, context address, looping information etc
60 std::array<WaveBufferVersion2, MaxWaveBuffers> wave_buffers;
61 /// Voice state, updated each call and written back to game
62 CpuAddr voice_state;
63};
64
65/**
66 * AudioRenderer command to decode PCM float-encoded version 2 wavebuffers
67 * into the output_index mix buffer.
68 */
69struct PcmFloatDataSourceVersion2Command : ICommand {
70 /**
71 * Print this command's information to a string.
72 *
73 * @param processor - The CommandListProcessor processing this command.
74 * @param string - The string to print into.
75 */
76 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
77
78 /**
79 * Process this command.
80 *
81 * @param processor - The CommandListProcessor processing this command.
82 */
83 void Process(const ADSP::CommandListProcessor& processor) override;
84
85 /**
86 * Verify this command's data is valid.
87 *
88 * @param processor - The CommandListProcessor processing this command.
89 * @return True if the command is valid, otherwise false.
90 */
91 bool Verify(const ADSP::CommandListProcessor& processor) override;
92
93 /// Quality used for sample rate conversion
94 SrcQuality src_quality;
95 /// Mix buffer index for decoded samples
96 s16 output_index;
97 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags)
98 u16 flags;
99 /// Wavebuffer sample rate
100 u32 sample_rate;
101 /// Pitch used for sample rate conversion
102 f32 pitch;
103 /// Target channel to read within the wavebuffer
104 s8 channel_index;
105 /// Number of channels within the wavebuffer
106 s8 channel_count;
107 /// Wavebuffers containing the wavebuffer address, context address, looping information etc
108 std::array<WaveBufferVersion2, MaxWaveBuffers> wave_buffers;
109 /// Voice state, updated each call and written back to game
110 CpuAddr voice_state;
111};
112
113} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/data_source/pcm_int16.cpp b/src/audio_core/renderer/command/data_source/pcm_int16.cpp
new file mode 100644
index 000000000..7a27463e4
--- /dev/null
+++ b/src/audio_core/renderer/command/data_source/pcm_int16.cpp
@@ -0,0 +1,87 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <span>
5
6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/data_source/decode.h"
8#include "audio_core/renderer/command/data_source/pcm_int16.h"
9
10namespace AudioCore::AudioRenderer {
11
12void PcmInt16DataSourceVersion1Command::Dump(const ADSP::CommandListProcessor& processor,
13 std::string& string) {
14 string +=
15 fmt::format("PcmInt16DataSourceVersion1Command\n\toutput_index {:02X} channel {} "
16 "channel count {} source sample rate {} target sample rate {} src quality {}\n",
17 output_index, channel_index, channel_count, sample_rate,
18 processor.target_sample_rate, src_quality);
19}
20
21void PcmInt16DataSourceVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
22 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count,
23 processor.sample_count);
24
25 DecodeFromWaveBuffersArgs args{
26 .sample_format{SampleFormat::PcmInt16},
27 .output{out_buffer},
28 .voice_state{reinterpret_cast<VoiceState*>(voice_state)},
29 .wave_buffers{wave_buffers},
30 .channel{channel_index},
31 .channel_count{channel_count},
32 .src_quality{src_quality},
33 .pitch{pitch},
34 .source_sample_rate{sample_rate},
35 .target_sample_rate{processor.target_sample_rate},
36 .sample_count{processor.sample_count},
37 .data_address{0},
38 .data_size{0},
39 .IsVoicePlayedSampleCountResetAtLoopPointSupported{(flags & 1) != 0},
40 .IsVoicePitchAndSrcSkippedSupported{(flags & 2) != 0},
41 };
42
43 DecodeFromWaveBuffers(*processor.memory, args);
44}
45
46bool PcmInt16DataSourceVersion1Command::Verify(const ADSP::CommandListProcessor& processor) {
47 return true;
48}
49
50void PcmInt16DataSourceVersion2Command::Dump(const ADSP::CommandListProcessor& processor,
51 std::string& string) {
52 string +=
53 fmt::format("PcmInt16DataSourceVersion2Command\n\toutput_index {:02X} channel {} "
54 "channel count {} source sample rate {} target sample rate {} src quality {}\n",
55 output_index, channel_index, channel_count, sample_rate,
56 processor.target_sample_rate, src_quality);
57}
58
59void PcmInt16DataSourceVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
60 auto out_buffer = processor.mix_buffers.subspan(output_index * processor.sample_count,
61 processor.sample_count);
62 DecodeFromWaveBuffersArgs args{
63 .sample_format{SampleFormat::PcmInt16},
64 .output{out_buffer},
65 .voice_state{reinterpret_cast<VoiceState*>(voice_state)},
66 .wave_buffers{wave_buffers},
67 .channel{channel_index},
68 .channel_count{channel_count},
69 .src_quality{src_quality},
70 .pitch{pitch},
71 .source_sample_rate{sample_rate},
72 .target_sample_rate{processor.target_sample_rate},
73 .sample_count{processor.sample_count},
74 .data_address{0},
75 .data_size{0},
76 .IsVoicePlayedSampleCountResetAtLoopPointSupported{(flags & 1) != 0},
77 .IsVoicePitchAndSrcSkippedSupported{(flags & 2) != 0},
78 };
79
80 DecodeFromWaveBuffers(*processor.memory, args);
81}
82
83bool PcmInt16DataSourceVersion2Command::Verify(const ADSP::CommandListProcessor& processor) {
84 return true;
85}
86
87} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/data_source/pcm_int16.h b/src/audio_core/renderer/command/data_source/pcm_int16.h
new file mode 100644
index 000000000..5de1ad60d
--- /dev/null
+++ b/src/audio_core/renderer/command/data_source/pcm_int16.h
@@ -0,0 +1,110 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/common/wave_buffer.h"
9#include "audio_core/renderer/command/icommand.h"
10#include "common/common_types.h"
11
12namespace AudioCore::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor;
15}
16
17/**
18 * AudioRenderer command to decode PCM s16-encoded version 1 wavebuffers
19 * into the output_index mix buffer.
20 */
21struct PcmInt16DataSourceVersion1Command : ICommand {
22 /**
23 * Print this command's information to a string.
24 *
25 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into.
27 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
29
30 /**
31 * Process this command.
32 *
33 * @param processor - The CommandListProcessor processing this command.
34 */
35 void Process(const ADSP::CommandListProcessor& processor) override;
36
37 /**
38 * Verify this command's data is valid.
39 *
40 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false.
42 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override;
44
45 /// Quality used for sample rate conversion
46 SrcQuality src_quality;
47 /// Mix buffer index for decoded samples
48 s16 output_index;
49 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags)
50 u16 flags;
51 /// Wavebuffer sample rate
52 u32 sample_rate;
53 /// Pitch used for sample rate conversion
54 f32 pitch;
55 /// Target channel to read within the wavebuffer
56 s8 channel_index;
57 /// Number of channels within the wavebuffer
58 s8 channel_count;
59 /// Wavebuffers containing the wavebuffer address, context address, looping information etc
60 std::array<WaveBufferVersion2, MaxWaveBuffers> wave_buffers;
61 /// Voice state, updated each call and written back to game
62 CpuAddr voice_state;
63};
64
65/**
66 * AudioRenderer command to decode PCM s16-encoded version 2 wavebuffers
67 * into the output_index mix buffer.
68 */
69struct PcmInt16DataSourceVersion2Command : ICommand {
70 /**
71 * Print this command's information to a string.
72 * @param processor - The CommandListProcessor processing this command.
73 * @param string - The string to print into.
74 */
75 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
76
77 /**
78 * Process this command.
79 * @param processor - The CommandListProcessor processing this command.
80 */
81 void Process(const ADSP::CommandListProcessor& processor) override;
82
83 /**
84 * Verify this command's data is valid.
85 * @param processor - The CommandListProcessor processing this command.
86 * @return True if the command is valid, otherwise false.
87 */
88 bool Verify(const ADSP::CommandListProcessor& processor) override;
89
90 /// Quality used for sample rate conversion
91 SrcQuality src_quality;
92 /// Mix buffer index for decoded samples
93 s16 output_index;
94 /// Flags to control decoding (see AudioCore::AudioRenderer::VoiceInfo::Flags)
95 u16 flags;
96 /// Wavebuffer sample rate
97 u32 sample_rate;
98 /// Pitch used for sample rate conversion
99 f32 pitch;
100 /// Target channel to read within the wavebuffer
101 s8 channel_index;
102 /// Number of channels within the wavebuffer
103 s8 channel_count;
104 /// Wavebuffers containing the wavebuffer address, context address, looping information etc
105 std::array<WaveBufferVersion2, MaxWaveBuffers> wave_buffers;
106 /// Voice state, updated each call and written back to game
107 CpuAddr voice_state;
108};
109
110} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/aux_.cpp b/src/audio_core/renderer/command/effect/aux_.cpp
new file mode 100644
index 000000000..e76db893f
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/aux_.cpp
@@ -0,0 +1,207 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/effect/aux_.h"
6#include "audio_core/renderer/effect/aux_.h"
7#include "core/memory.h"
8
9namespace AudioCore::AudioRenderer {
10/**
11 * Reset an AuxBuffer.
12 *
13 * @param memory - Core memory for writing.
14 * @param aux_info - Memory address pointing to the AuxInfo to reset.
15 */
16static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) {
17 if (aux_info == 0) {
18 LOG_ERROR(Service_Audio, "Aux info is 0!");
19 return;
20 }
21
22 auto info{reinterpret_cast<AuxInfo::AuxInfoDsp*>(memory.GetPointer(aux_info))};
23 info->read_offset = 0;
24 info->write_offset = 0;
25 info->total_sample_count = 0;
26}
27
28/**
29 * Write the given input mix buffer to the memory at send_buffer, and update send_info_ if
30 * update_count is set, to notify the game that an update happened.
31 *
32 * @param memory - Core memory for writing.
33 * @param send_info_ - Meta information for where to write the mix buffer.
34 * @param sample_count - Unused.
35 * @param send_buffer - Memory address to write the mix buffer to.
36 * @param count_max - Maximum number of samples in the receiving buffer.
37 * @param input - Input mix buffer to write.
38 * @param write_count_ - Number of samples to write.
39 * @param write_offset - Current offset to begin writing the receiving buffer at.
40 * @param update_count - If non-zero, send_info_ will be updated.
41 * @return Number of samples written.
42 */
43static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_info_,
44 [[maybe_unused]] u32 sample_count, const CpuAddr send_buffer,
45 const u32 count_max, std::span<const s32> input,
46 const u32 write_count_, const u32 write_offset,
47 const u32 update_count) {
48 if (write_count_ > count_max) {
49 LOG_ERROR(Service_Audio,
50 "write_count must be smaller than count_max! write_count {}, count_max {}",
51 write_count_, count_max);
52 return 0;
53 }
54
55 if (input.empty()) {
56 LOG_ERROR(Service_Audio, "input buffer is empty!");
57 return 0;
58 }
59
60 if (send_buffer == 0) {
61 LOG_ERROR(Service_Audio, "send_buffer is 0!");
62 return 0;
63 }
64
65 if (count_max == 0) {
66 return 0;
67 }
68
69 AuxInfo::AuxInfoDsp send_info{};
70 memory.ReadBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp));
71
72 u32 target_write_offset{send_info.write_offset + write_offset};
73 if (target_write_offset > count_max || write_count_ == 0) {
74 return 0;
75 }
76
77 u32 write_count{write_count_};
78 u32 write_pos{0};
79 while (write_count > 0) {
80 u32 to_write{std::min(count_max - target_write_offset, write_count)};
81
82 if (to_write > 0) {
83 memory.WriteBlockUnsafe(send_buffer + target_write_offset * sizeof(s32),
84 &input[write_pos], to_write * sizeof(s32));
85 }
86
87 target_write_offset = (target_write_offset + to_write) % count_max;
88 write_count -= to_write;
89 write_pos += to_write;
90 }
91
92 if (update_count) {
93 send_info.write_offset = (send_info.write_offset + update_count) % count_max;
94 }
95
96 memory.WriteBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp));
97
98 return write_count_;
99}
100
101/**
102 * Read the given memory at return_buffer into the output mix buffer, and update return_info_ if
103 * update_count is set, to notify the game that an update happened.
104 *
105 * @param memory - Core memory for writing.
106 * @param return_info_ - Meta information for where to read the mix buffer.
107 * @param return_buffer - Memory address to read the samples from.
108 * @param count_max - Maximum number of samples in the receiving buffer.
109 * @param output - Output mix buffer which will receive the samples.
110 * @param count_ - Number of samples to read.
111 * @param read_offset - Current offset to begin reading the return_buffer at.
112 * @param update_count - If non-zero, send_info_ will be updated.
113 * @return Number of samples read.
114 */
115static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr return_info_,
116 const CpuAddr return_buffer, const u32 count_max, std::span<s32> output,
117 const u32 count_, const u32 read_offset, const u32 update_count) {
118 if (count_max == 0) {
119 return 0;
120 }
121
122 if (count_ > count_max) {
123 LOG_ERROR(Service_Audio, "count must be smaller than count_max! count {}, count_max {}",
124 count_, count_max);
125 return 0;
126 }
127
128 if (output.empty()) {
129 LOG_ERROR(Service_Audio, "output buffer is empty!");
130 return 0;
131 }
132
133 if (return_buffer == 0) {
134 LOG_ERROR(Service_Audio, "return_buffer is 0!");
135 return 0;
136 }
137
138 AuxInfo::AuxInfoDsp return_info{};
139 memory.ReadBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp));
140
141 u32 target_read_offset{return_info.read_offset + read_offset};
142 if (target_read_offset > count_max) {
143 return 0;
144 }
145
146 u32 read_count{count_};
147 u32 read_pos{0};
148 while (read_count > 0) {
149 u32 to_read{std::min(count_max - target_read_offset, read_count)};
150
151 if (to_read > 0) {
152 memory.ReadBlockUnsafe(return_buffer + target_read_offset * sizeof(s32),
153 &output[read_pos], to_read * sizeof(s32));
154 }
155
156 target_read_offset = (target_read_offset + to_read) % count_max;
157 read_count -= to_read;
158 read_pos += to_read;
159 }
160
161 if (update_count) {
162 return_info.read_offset = (return_info.read_offset + update_count) % count_max;
163 }
164
165 memory.WriteBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp));
166
167 return count_;
168}
169
170void AuxCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
171 std::string& string) {
172 string += fmt::format("AuxCommand\n\tenabled {} input {:02X} output {:02X}\n", effect_enabled,
173 input, output);
174}
175
176void AuxCommand::Process(const ADSP::CommandListProcessor& processor) {
177 auto input_buffer{
178 processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)};
179 auto output_buffer{
180 processor.mix_buffers.subspan(output * processor.sample_count, processor.sample_count)};
181
182 if (effect_enabled) {
183 WriteAuxBufferDsp(*processor.memory, send_buffer_info, processor.sample_count, send_buffer,
184 count_max, input_buffer, processor.sample_count, write_offset,
185 update_count);
186
187 auto read{ReadAuxBufferDsp(*processor.memory, return_buffer_info, return_buffer, count_max,
188 output_buffer, processor.sample_count, write_offset,
189 update_count)};
190
191 if (read != processor.sample_count) {
192 std::memset(&output_buffer[read], 0, processor.sample_count - read);
193 }
194 } else {
195 ResetAuxBufferDsp(*processor.memory, send_buffer_info);
196 ResetAuxBufferDsp(*processor.memory, return_buffer_info);
197 if (input != output) {
198 std::memcpy(output_buffer.data(), input_buffer.data(), output_buffer.size_bytes());
199 }
200 }
201}
202
203bool AuxCommand::Verify(const ADSP::CommandListProcessor& processor) {
204 return true;
205}
206
207} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/aux_.h b/src/audio_core/renderer/command/effect/aux_.h
new file mode 100644
index 000000000..825c93732
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/aux_.h
@@ -0,0 +1,66 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command to read and write an auxiliary buffer, writing the input mix buffer to game
18 * memory, and reading into the output buffer from game memory.
19 */
20struct AuxCommand : ICommand {
21 /**
22 * Print this command's information to a string.
23 *
24 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into.
26 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
28
29 /**
30 * Process this command.
31 *
32 * @param processor - The CommandListProcessor processing this command.
33 */
34 void Process(const ADSP::CommandListProcessor& processor) override;
35
36 /**
37 * Verify this command's data is valid.
38 *
39 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false.
41 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override;
43
44 /// Input mix buffer index
45 s16 input;
46 /// Output mix buffer index
47 s16 output;
48 /// Meta info for writing
49 CpuAddr send_buffer_info;
50 /// Meta info for reading
51 CpuAddr return_buffer_info;
52 /// Game memory write buffer
53 CpuAddr send_buffer;
54 /// Game memory read buffer
55 CpuAddr return_buffer;
56 /// Max samples to read/write
57 u32 count_max;
58 /// Current read/write offset
59 u32 write_offset;
60 /// Number of samples to update per call
61 u32 update_count;
62 /// is this effect enabled?
63 bool effect_enabled;
64};
65
66} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp
new file mode 100644
index 000000000..1baae74fd
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp
@@ -0,0 +1,118 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/effect/biquad_filter.h"
6#include "audio_core/renderer/voice/voice_state.h"
7
8namespace AudioCore::AudioRenderer {
9/**
10 * Biquad filter float implementation.
11 *
12 * @param output - Output container for filtered samples.
13 * @param input - Input container for samples to be filtered.
14 * @param b - Feedforward coefficients.
15 * @param a - Feedback coefficients.
16 * @param state - State to track previous samples between calls.
17 * @param sample_count - Number of samples to process.
18 */
19void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
20 std::array<s16, 3>& b_, std::array<s16, 2>& a_,
21 VoiceState::BiquadFilterState& state, const u32 sample_count) {
22 constexpr s64 min{std::numeric_limits<s32>::min()};
23 constexpr s64 max{std::numeric_limits<s32>::max()};
24 std::array<f64, 3> b{Common::FixedPoint<50, 14>::from_base(b_[0]).to_double(),
25 Common::FixedPoint<50, 14>::from_base(b_[1]).to_double(),
26 Common::FixedPoint<50, 14>::from_base(b_[2]).to_double()};
27 std::array<f64, 2> a{Common::FixedPoint<50, 14>::from_base(a_[0]).to_double(),
28 Common::FixedPoint<50, 14>::from_base(a_[1]).to_double()};
29 std::array<f64, 4> s{state.s0.to_double(), state.s1.to_double(), state.s2.to_double(),
30 state.s3.to_double()};
31
32 for (u32 i = 0; i < sample_count; i++) {
33 f64 in_sample{static_cast<f64>(input[i])};
34 auto sample{in_sample * b[0] + s[0] * b[1] + s[1] * b[2] + s[2] * a[0] + s[3] * a[1]};
35
36 output[i] = static_cast<s32>(std::clamp(static_cast<s64>(sample), min, max));
37
38 s[1] = s[0];
39 s[0] = in_sample;
40 s[3] = s[2];
41 s[2] = sample;
42 }
43
44 state.s0 = s[0];
45 state.s1 = s[1];
46 state.s2 = s[2];
47 state.s3 = s[3];
48}
49
50/**
51 * Biquad filter s32 implementation.
52 *
53 * @param output - Output container for filtered samples.
54 * @param input - Input container for samples to be filtered.
55 * @param b - Feedforward coefficients.
56 * @param a - Feedback coefficients.
57 * @param state - State to track previous samples between calls.
58 * @param sample_count - Number of samples to process.
59 */
60static void ApplyBiquadFilterInt(std::span<s32> output, std::span<const s32> input,
61 std::array<s16, 3>& b_, std::array<s16, 2>& a_,
62 VoiceState::BiquadFilterState& state, const u32 sample_count) {
63 constexpr s64 min{std::numeric_limits<s32>::min()};
64 constexpr s64 max{std::numeric_limits<s32>::max()};
65 std::array<Common::FixedPoint<50, 14>, 3> b{
66 Common::FixedPoint<50, 14>::from_base(b_[0]),
67 Common::FixedPoint<50, 14>::from_base(b_[1]),
68 Common::FixedPoint<50, 14>::from_base(b_[2]),
69 };
70 std::array<Common::FixedPoint<50, 14>, 3> a{
71 Common::FixedPoint<50, 14>::from_base(a_[0]),
72 Common::FixedPoint<50, 14>::from_base(a_[1]),
73 };
74
75 for (u32 i = 0; i < sample_count; i++) {
76 s64 in_sample{input[i]};
77 auto sample{in_sample * b[0] + state.s0};
78 const auto out_sample{std::clamp(sample.to_long(), min, max)};
79
80 output[i] = static_cast<s32>(out_sample);
81
82 state.s0 = state.s1 + b[1] * in_sample + a[0] * out_sample;
83 state.s1 = 0 + b[2] * in_sample + a[1] * out_sample;
84 }
85}
86
87void BiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
88 std::string& string) {
89 string += fmt::format(
90 "BiquadFilterCommand\n\tinput {:02X} output {:02X} needs_init {} use_float_processing {}\n",
91 input, output, needs_init, use_float_processing);
92}
93
94void BiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) {
95 auto state_{reinterpret_cast<VoiceState::BiquadFilterState*>(state)};
96 if (needs_init) {
97 std::memset(state_, 0, sizeof(VoiceState::BiquadFilterState));
98 }
99
100 auto input_buffer{
101 processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)};
102 auto output_buffer{
103 processor.mix_buffers.subspan(output * processor.sample_count, processor.sample_count)};
104
105 if (use_float_processing) {
106 ApplyBiquadFilterFloat(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
107 processor.sample_count);
108 } else {
109 ApplyBiquadFilterInt(output_buffer, input_buffer, biquad.b, biquad.a, *state_,
110 processor.sample_count);
111 }
112}
113
114bool BiquadFilterCommand::Verify(const ADSP::CommandListProcessor& processor) {
115 return true;
116}
117
118} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.h b/src/audio_core/renderer/command/effect/biquad_filter.h
new file mode 100644
index 000000000..4c9c42d29
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/biquad_filter.h
@@ -0,0 +1,74 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "audio_core/renderer/voice/voice_info.h"
10#include "audio_core/renderer/voice/voice_state.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for applying a biquad filter to the input mix buffer, saving the results to
20 * the output mix buffer.
21 */
22struct BiquadFilterCommand : ICommand {
23 /**
24 * Print this command's information to a string.
25 *
26 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into.
28 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
30
31 /**
32 * Process this command.
33 *
34 * @param processor - The CommandListProcessor processing this command.
35 */
36 void Process(const ADSP::CommandListProcessor& processor) override;
37
38 /**
39 * Verify this command's data is valid.
40 *
41 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false.
43 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override;
45
46 /// Input mix buffer index
47 s16 input;
48 /// Output mix buffer index
49 s16 output;
50 /// Input parameters for biquad
51 VoiceInfo::BiquadFilterParameter biquad;
52 /// Biquad state, updated each call
53 CpuAddr state;
54 /// If true, reset the state
55 bool needs_init;
56 /// If true, use float processing rather than int
57 bool use_float_processing;
58};
59
60/**
61 * Biquad filter float implementation.
62 *
63 * @param output - Output container for filtered samples.
64 * @param input - Input container for samples to be filtered.
65 * @param b - Feedforward coefficients.
66 * @param a - Feedback coefficients.
67 * @param state - State to track previous samples.
68 * @param sample_count - Number of samples to process.
69 */
70void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
71 std::array<s16, 3>& b, std::array<s16, 2>& a,
72 VoiceState::BiquadFilterState& state, const u32 sample_count);
73
74} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/capture.cpp b/src/audio_core/renderer/command/effect/capture.cpp
new file mode 100644
index 000000000..042fd286e
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/capture.cpp
@@ -0,0 +1,142 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/effect/capture.h"
6#include "audio_core/renderer/effect/aux_.h"
7#include "core/memory.h"
8
9namespace AudioCore::AudioRenderer {
10/**
11 * Reset an AuxBuffer.
12 *
13 * @param memory - Core memory for writing.
14 * @param aux_info - Memory address pointing to the AuxInfo to reset.
15 */
16static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) {
17 if (aux_info == 0) {
18 LOG_ERROR(Service_Audio, "Aux info is 0!");
19 return;
20 }
21
22 memory.Write32(VAddr(aux_info + offsetof(AuxInfo::AuxInfoDsp, read_offset)), 0);
23 memory.Write32(VAddr(aux_info + offsetof(AuxInfo::AuxInfoDsp, write_offset)), 0);
24 memory.Write32(VAddr(aux_info + offsetof(AuxInfo::AuxInfoDsp, total_sample_count)), 0);
25}
26
27/**
28 * Write the given input mix buffer to the memory at send_buffer, and update send_info_ if
29 * update_count is set, to notify the game that an update happened.
30 *
31 * @param memory - Core memory for writing.
32 * @param send_info_ - Header information for where to write the mix buffer.
33 * @param send_buffer - Memory address to write the mix buffer to.
34 * @param count_max - Maximum number of samples in the receiving buffer.
35 * @param input - Input mix buffer to write.
36 * @param write_count_ - Number of samples to write.
37 * @param write_offset - Current offset to begin writing the receiving buffer at.
38 * @param update_count - If non-zero, send_info_ will be updated.
39 * @return Number of samples written.
40 */
41static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr send_info_,
42 const CpuAddr send_buffer, u32 count_max, std::span<const s32> input,
43 const u32 write_count_, const u32 write_offset,
44 const u32 update_count) {
45 if (write_count_ > count_max) {
46 LOG_ERROR(Service_Audio,
47 "write_count must be smaller than count_max! write_count {}, count_max {}",
48 write_count_, count_max);
49 return 0;
50 }
51
52 if (send_info_ == 0) {
53 LOG_ERROR(Service_Audio, "send_info is 0!");
54 return 0;
55 }
56
57 if (input.empty()) {
58 LOG_ERROR(Service_Audio, "input buffer is empty!");
59 return 0;
60 }
61
62 if (send_buffer == 0) {
63 LOG_ERROR(Service_Audio, "send_buffer is 0!");
64 return 0;
65 }
66
67 if (count_max == 0) {
68 return 0;
69 }
70
71 AuxInfo::AuxBufferInfo send_info{};
72 memory.ReadBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxBufferInfo));
73
74 u32 target_write_offset{send_info.dsp_info.write_offset + write_offset};
75 if (target_write_offset > count_max || write_count_ == 0) {
76 return 0;
77 }
78
79 u32 write_count{write_count_};
80 u32 write_pos{0};
81 while (write_count > 0) {
82 u32 to_write{std::min(count_max - target_write_offset, write_count)};
83
84 if (to_write > 0) {
85 memory.WriteBlockUnsafe(send_buffer + target_write_offset * sizeof(s32),
86 &input[write_pos], to_write * sizeof(s32));
87 }
88
89 target_write_offset = (target_write_offset + to_write) % count_max;
90 write_count -= to_write;
91 write_pos += to_write;
92 }
93
94 if (update_count) {
95 const auto count_diff{send_info.dsp_info.total_sample_count -
96 send_info.cpu_info.total_sample_count};
97 if (count_diff >= count_max) {
98 auto dsp_lost_count{send_info.dsp_info.lost_sample_count + update_count};
99 if (dsp_lost_count - send_info.cpu_info.lost_sample_count <
100 send_info.dsp_info.lost_sample_count - send_info.cpu_info.lost_sample_count) {
101 dsp_lost_count = send_info.cpu_info.lost_sample_count - 1;
102 }
103 send_info.dsp_info.lost_sample_count = dsp_lost_count;
104 }
105
106 send_info.dsp_info.write_offset =
107 (send_info.dsp_info.write_offset + update_count + count_max) % count_max;
108
109 auto new_sample_count{send_info.dsp_info.total_sample_count + update_count};
110 if (new_sample_count - send_info.cpu_info.total_sample_count < count_diff) {
111 new_sample_count = send_info.cpu_info.total_sample_count - 1;
112 }
113 send_info.dsp_info.total_sample_count = new_sample_count;
114 }
115
116 memory.WriteBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxBufferInfo));
117
118 return write_count_;
119}
120
121void CaptureCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
122 std::string& string) {
123 string += fmt::format("CaptureCommand\n\tenabled {} input {:02X} output {:02X}", effect_enabled,
124 input, output);
125}
126
127void CaptureCommand::Process(const ADSP::CommandListProcessor& processor) {
128 if (effect_enabled) {
129 auto input_buffer{
130 processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)};
131 WriteAuxBufferDsp(*processor.memory, send_buffer_info, send_buffer, count_max, input_buffer,
132 processor.sample_count, write_offset, update_count);
133 } else {
134 ResetAuxBufferDsp(*processor.memory, send_buffer_info);
135 }
136}
137
138bool CaptureCommand::Verify(const ADSP::CommandListProcessor& processor) {
139 return true;
140}
141
142} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/capture.h b/src/audio_core/renderer/command/effect/capture.h
new file mode 100644
index 000000000..8670acb24
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/capture.h
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for capturing a mix buffer. That is, writing it back to a given game memory
18 * address.
19 */
20struct CaptureCommand : ICommand {
21 /**
22 * Print this command's information to a string.
23 *
24 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into.
26 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
28
29 /**
30 * Process this command.
31 *
32 * @param processor - The CommandListProcessor processing this command.
33 */
34 void Process(const ADSP::CommandListProcessor& processor) override;
35
36 /**
37 * Verify this command's data is valid.
38 *
39 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false.
41 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override;
43
44 /// Input mix buffer index
45 s16 input;
46 /// Output mix buffer index
47 s16 output;
48 /// Meta info for writing
49 CpuAddr send_buffer_info;
50 /// Game memory write buffer
51 CpuAddr send_buffer;
52 /// Max samples to read/write
53 u32 count_max;
54 /// Current read/write offset
55 u32 write_offset;
56 /// Number of samples to update per call
57 u32 update_count;
58 /// is this effect enabled?
59 bool effect_enabled;
60};
61
62} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/compressor.cpp b/src/audio_core/renderer/command/effect/compressor.cpp
new file mode 100644
index 000000000..2ebc140f1
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/compressor.cpp
@@ -0,0 +1,156 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <cmath>
5#include <span>
6#include <vector>
7
8#include "audio_core/renderer/adsp/command_list_processor.h"
9#include "audio_core/renderer/command/effect/compressor.h"
10#include "audio_core/renderer/effect/compressor.h"
11
12namespace AudioCore::AudioRenderer {
13
14static void SetCompressorEffectParameter(CompressorInfo::ParameterVersion2& params,
15 CompressorInfo::State& state) {
16 const auto ratio{1.0f / params.compressor_ratio};
17 auto makeup_gain{0.0f};
18 if (params.makeup_gain_enabled) {
19 makeup_gain = (params.threshold * 0.5f) * (ratio - 1.0f) - 3.0f;
20 }
21 state.makeup_gain = makeup_gain;
22 state.unk_18 = params.unk_28;
23
24 const auto a{(params.out_gain + makeup_gain) / 20.0f * 3.3219f};
25 const auto b{(a - std::trunc(a)) * 0.69315f};
26 const auto c{std::pow(2.0f, b)};
27
28 state.unk_0C = (1.0f - ratio) / 6.0f;
29 state.unk_14 = params.threshold + 1.5f;
30 state.unk_10 = params.threshold - 1.5f;
31 state.unk_20 = c;
32}
33
34static void InitializeCompressorEffect(CompressorInfo::ParameterVersion2& params,
35 CompressorInfo::State& state) {
36 std::memset(&state, 0, sizeof(CompressorInfo::State));
37
38 state.unk_00 = 0;
39 state.unk_04 = 1.0f;
40 state.unk_08 = 1.0f;
41
42 SetCompressorEffectParameter(params, state);
43}
44
45static void ApplyCompressorEffect(CompressorInfo::ParameterVersion2& params,
46 CompressorInfo::State& state, bool enabled,
47 std::vector<std::span<const s32>> input_buffers,
48 std::vector<std::span<s32>> output_buffers, u32 sample_count) {
49 if (enabled) {
50 auto state_00{state.unk_00};
51 auto state_04{state.unk_04};
52 auto state_08{state.unk_08};
53 auto state_18{state.unk_18};
54
55 for (u32 i = 0; i < sample_count; i++) {
56 auto a{0.0f};
57 for (s16 channel = 0; channel < params.channel_count; channel++) {
58 const auto input_sample{Common::FixedPoint<49, 15>(input_buffers[channel][i])};
59 a += (input_sample * input_sample).to_float();
60 }
61
62 state_00 += params.unk_24 * ((a / params.channel_count) - state.unk_00);
63
64 auto b{-100.0f};
65 auto c{0.0f};
66 if (state_00 >= 1.0e-10) {
67 b = std::log10(state_00) * 10.0f;
68 c = 1.0f;
69 }
70
71 if (b >= state.unk_10) {
72 const auto d{b >= state.unk_14
73 ? ((1.0f / params.compressor_ratio) - 1.0f) *
74 (b - params.threshold)
75 : (b - state.unk_10) * (b - state.unk_10) * -state.unk_0C};
76 const auto e{d / 20.0f * 3.3219f};
77 const auto f{(e - std::trunc(e)) * 0.69315f};
78 c = std::pow(2.0f, f);
79 }
80
81 state_18 = params.unk_28;
82 auto tmp{c};
83 if ((state_04 - c) <= 0.08f) {
84 state_18 = params.unk_2C;
85 if (((state_04 - c) >= -0.08f) && (std::abs(state_08 - c) >= 0.001f)) {
86 tmp = state_04;
87 }
88 }
89
90 state_04 = tmp;
91 state_08 += (c - state_08) * state_18;
92
93 for (s16 channel = 0; channel < params.channel_count; channel++) {
94 output_buffers[channel][i] = static_cast<s32>(
95 static_cast<f32>(input_buffers[channel][i]) * state_08 * state.unk_20);
96 }
97 }
98
99 state.unk_00 = state_00;
100 state.unk_04 = state_04;
101 state.unk_08 = state_08;
102 state.unk_18 = state_18;
103 } else {
104 for (s16 channel = 0; channel < params.channel_count; channel++) {
105 if (params.inputs[channel] != params.outputs[channel]) {
106 std::memcpy((char*)output_buffers[channel].data(),
107 (char*)input_buffers[channel].data(),
108 output_buffers[channel].size_bytes());
109 }
110 }
111 }
112}
113
114void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
115 std::string& string) {
116 string += fmt::format("CompressorCommand\n\tenabled {} \n\tinputs: ", effect_enabled);
117 for (s16 i = 0; i < parameter.channel_count; i++) {
118 string += fmt::format("{:02X}, ", inputs[i]);
119 }
120 string += "\n\toutputs: ";
121 for (s16 i = 0; i < parameter.channel_count; i++) {
122 string += fmt::format("{:02X}, ", outputs[i]);
123 }
124 string += "\n";
125}
126
127void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) {
128 std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
129 std::vector<std::span<s32>> output_buffers(parameter.channel_count);
130
131 for (s16 i = 0; i < parameter.channel_count; i++) {
132 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
133 processor.sample_count);
134 output_buffers[i] = processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
135 processor.sample_count);
136 }
137
138 auto state_{reinterpret_cast<CompressorInfo::State*>(state)};
139
140 if (effect_enabled) {
141 if (parameter.state == CompressorInfo::ParameterState::Updating) {
142 SetCompressorEffectParameter(parameter, *state_);
143 } else if (parameter.state == CompressorInfo::ParameterState::Initialized) {
144 InitializeCompressorEffect(parameter, *state_);
145 }
146 }
147
148 ApplyCompressorEffect(parameter, *state_, effect_enabled, input_buffers, output_buffers,
149 processor.sample_count);
150}
151
152bool CompressorCommand::Verify(const ADSP::CommandListProcessor& processor) {
153 return true;
154}
155
156} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/compressor.h b/src/audio_core/renderer/command/effect/compressor.h
new file mode 100644
index 000000000..f8e96cb43
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/compressor.h
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8
9#include "audio_core/renderer/command/icommand.h"
10#include "audio_core/renderer/effect/compressor.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for limiting volume between a high and low threshold.
20 * Version 1.
21 */
22struct CompressorCommand : ICommand {
23 /**
24 * Print this command's information to a string.
25 *
26 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into.
28 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
30
31 /**
32 * Process this command.
33 *
34 * @param processor - The CommandListProcessor processing this command.
35 */
36 void Process(const ADSP::CommandListProcessor& processor) override;
37
38 /**
39 * Verify this command's data is valid.
40 *
41 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false.
43 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override;
45
46 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs;
48 /// Output mix buffer offsets for each channel
49 std::array<s16, MaxChannels> outputs;
50 /// Input parameters
51 CompressorInfo::ParameterVersion2 parameter;
52 /// State, updated each call
53 CpuAddr state;
54 /// Game-supplied workbuffer (Unused)
55 CpuAddr workbuffer;
56 /// Is this effect enabled?
57 bool effect_enabled;
58};
59
60} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/delay.cpp b/src/audio_core/renderer/command/effect/delay.cpp
new file mode 100644
index 000000000..a4e408d40
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/delay.cpp
@@ -0,0 +1,238 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/effect/delay.h"
6
7namespace AudioCore::AudioRenderer {
8/**
9 * Update the DelayInfo state according to the given parameters.
10 *
11 * @param params - Input parameters to update the state.
12 * @param state - State to be updated.
13 */
14static void SetDelayEffectParameter(const DelayInfo::ParameterVersion1& params,
15 DelayInfo::State& state) {
16 auto channel_spread{params.channel_spread};
17 state.feedback_gain = params.feedback_gain * 0.97998046875f;
18 state.delay_feedback_gain = state.feedback_gain * (1.0f - channel_spread);
19 if (params.channel_count == 4 || params.channel_count == 6) {
20 channel_spread >>= 1;
21 }
22 state.delay_feedback_cross_gain = channel_spread * state.feedback_gain;
23 state.lowpass_feedback_gain = params.lowpass_amount * 0.949951171875f;
24 state.lowpass_gain = 1.0f - state.lowpass_feedback_gain;
25}
26
27/**
28 * Initialize a new DelayInfo state according to the given parameters.
29 *
30 * @param params - Input parameters to update the state.
31 * @param state - State to be updated.
32 * @param workbuffer - Game-supplied memory for the state. (Unused)
33 */
34static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
35 DelayInfo::State& state,
36 [[maybe_unused]] const CpuAddr workbuffer) {
37 state = {};
38
39 for (u32 channel = 0; channel < params.channel_count; channel++) {
40 Common::FixedPoint<32, 32> sample_count_max{0.064f};
41 sample_count_max *= params.sample_rate.to_int_floor() * params.delay_time_max;
42
43 Common::FixedPoint<18, 14> delay_time{params.delay_time};
44 delay_time *= params.sample_rate / 1000;
45 Common::FixedPoint<32, 32> sample_count{delay_time};
46
47 if (sample_count > sample_count_max) {
48 sample_count = sample_count_max;
49 }
50
51 state.delay_lines[channel].sample_count_max = sample_count_max.to_int_floor();
52 state.delay_lines[channel].sample_count = sample_count.to_int_floor();
53 state.delay_lines[channel].buffer.resize(state.delay_lines[channel].sample_count, 0);
54 if (state.delay_lines[channel].buffer.size() == 0) {
55 state.delay_lines[channel].buffer.push_back(0);
56 }
57 state.delay_lines[channel].buffer_pos = 0;
58 state.delay_lines[channel].decay_rate = 1.0f;
59 }
60
61 SetDelayEffectParameter(params, state);
62}
63
64/**
65 * Delay effect impl, according to the parameters and current state, on the input mix buffers,
66 * saving the results to the output mix buffers.
67 *
68 * @tparam NumChannels - Number of channels to process. 1-6.
69 * @param params - Input parameters to use.
70 * @param state - State to use, must be initialized (see InitializeDelayEffect).
71 * @param inputs - Input mix buffers to performan the delay on.
72 * @param outputs - Output mix buffers to receive the delayed samples.
73 * @param sample_count - Number of samples to process.
74 */
75template <size_t NumChannels>
76static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
77 std::vector<std::span<const s32>>& inputs,
78 std::vector<std::span<s32>>& outputs, const u32 sample_count) {
79 for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
80 std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{};
81 for (u32 channel = 0; channel < NumChannels; channel++) {
82 input_samples[channel] = inputs[channel][sample_index] * 64;
83 }
84
85 std::array<Common::FixedPoint<50, 14>, NumChannels> delay_samples{};
86 for (u32 channel = 0; channel < NumChannels; channel++) {
87 delay_samples[channel] = state.delay_lines[channel].Read();
88 }
89
90 // clang-format off
91 std::array<std::array<Common::FixedPoint<18, 14>, NumChannels>, NumChannels> matrix{};
92 if constexpr (NumChannels == 1) {
93 matrix = {{
94 {state.feedback_gain},
95 }};
96 } else if constexpr (NumChannels == 2) {
97 matrix = {{
98 {state.delay_feedback_gain, state.delay_feedback_cross_gain},
99 {state.delay_feedback_cross_gain, state.delay_feedback_gain},
100 }};
101 } else if constexpr (NumChannels == 4) {
102 matrix = {{
103 {state.delay_feedback_gain, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, 0.0f},
104 {state.delay_feedback_cross_gain, state.delay_feedback_gain, 0.0f, state.delay_feedback_cross_gain},
105 {state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain},
106 {0.0f, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, state.delay_feedback_gain},
107 }};
108 } else if constexpr (NumChannels == 6) {
109 matrix = {{
110 {state.delay_feedback_gain, 0.0f, state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_cross_gain, 0.0f},
111 {0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain, 0.0f, 0.0f, state.delay_feedback_cross_gain},
112 {state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, state.delay_feedback_gain, 0.0f, 0.0f, 0.0f},
113 {0.0f, 0.0f, 0.0f, params.feedback_gain, 0.0f, 0.0f},
114 {state.delay_feedback_cross_gain, 0.0f, 0.0f, 0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain},
115 {0.0f, state.delay_feedback_cross_gain, 0.0f, 0.0f, state.delay_feedback_cross_gain, state.delay_feedback_gain},
116 }};
117 }
118 // clang-format on
119
120 std::array<Common::FixedPoint<50, 14>, NumChannels> gained_samples{};
121 for (u32 channel = 0; channel < NumChannels; channel++) {
122 Common::FixedPoint<50, 14> delay{};
123 for (u32 j = 0; j < NumChannels; j++) {
124 delay += delay_samples[j] * matrix[j][channel];
125 }
126 gained_samples[channel] = input_samples[channel] * params.in_gain + delay;
127 }
128
129 for (u32 channel = 0; channel < NumChannels; channel++) {
130 state.lowpass_z[channel] = gained_samples[channel] * state.lowpass_gain +
131 state.lowpass_z[channel] * state.lowpass_feedback_gain;
132 state.delay_lines[channel].Write(state.lowpass_z[channel]);
133 }
134
135 for (u32 channel = 0; channel < NumChannels; channel++) {
136 outputs[channel][sample_index] = (input_samples[channel] * params.dry_gain +
137 delay_samples[channel] * params.wet_gain)
138 .to_int_floor() /
139 64;
140 }
141 }
142}
143
144/**
145 * Apply a delay effect if enabled, according to the parameters and current state, on the input mix
146 * buffers, saving the results to the output mix buffers.
147 *
148 * @param params - Input parameters to use.
149 * @param state - State to use, must be initialized (see InitializeDelayEffect).
150 * @param enabled - If enabled, delay will be applied, otherwise input is copied to output.
151 * @param inputs - Input mix buffers to performan the delay on.
152 * @param outputs - Output mix buffers to receive the delayed samples.
153 * @param sample_count - Number of samples to process.
154 */
155static void ApplyDelayEffect(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
156 const bool enabled, std::vector<std::span<const s32>>& inputs,
157 std::vector<std::span<s32>>& outputs, const u32 sample_count) {
158
159 if (!IsChannelCountValid(params.channel_count)) {
160 LOG_ERROR(Service_Audio, "Invalid delay channels {}", params.channel_count);
161 return;
162 }
163
164 if (enabled) {
165 switch (params.channel_count) {
166 case 1:
167 ApplyDelay<1>(params, state, inputs, outputs, sample_count);
168 break;
169 case 2:
170 ApplyDelay<2>(params, state, inputs, outputs, sample_count);
171 break;
172 case 4:
173 ApplyDelay<4>(params, state, inputs, outputs, sample_count);
174 break;
175 case 6:
176 ApplyDelay<6>(params, state, inputs, outputs, sample_count);
177 break;
178 default:
179 for (u32 channel = 0; channel < params.channel_count; channel++) {
180 if (inputs[channel].data() != outputs[channel].data()) {
181 std::memcpy(outputs[channel].data(), inputs[channel].data(),
182 sample_count * sizeof(s32));
183 }
184 }
185 break;
186 }
187 } else {
188 for (u32 channel = 0; channel < params.channel_count; channel++) {
189 if (inputs[channel].data() != outputs[channel].data()) {
190 std::memcpy(outputs[channel].data(), inputs[channel].data(),
191 sample_count * sizeof(s32));
192 }
193 }
194 }
195}
196
197void DelayCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
198 std::string& string) {
199 string += fmt::format("DelayCommand\n\tenabled {} \n\tinputs: ", effect_enabled);
200 for (u32 i = 0; i < MaxChannels; i++) {
201 string += fmt::format("{:02X}, ", inputs[i]);
202 }
203 string += "\n\toutputs: ";
204 for (u32 i = 0; i < MaxChannels; i++) {
205 string += fmt::format("{:02X}, ", outputs[i]);
206 }
207 string += "\n";
208}
209
210void DelayCommand::Process(const ADSP::CommandListProcessor& processor) {
211 std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
212 std::vector<std::span<s32>> output_buffers(parameter.channel_count);
213
214 for (s16 i = 0; i < parameter.channel_count; i++) {
215 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
216 processor.sample_count);
217 output_buffers[i] = processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
218 processor.sample_count);
219 }
220
221 auto state_{reinterpret_cast<DelayInfo::State*>(state)};
222
223 if (effect_enabled) {
224 if (parameter.state == DelayInfo::ParameterState::Updating) {
225 SetDelayEffectParameter(parameter, *state_);
226 } else if (parameter.state == DelayInfo::ParameterState::Initialized) {
227 InitializeDelayEffect(parameter, *state_, workbuffer);
228 }
229 }
230 ApplyDelayEffect(parameter, *state_, effect_enabled, input_buffers, output_buffers,
231 processor.sample_count);
232}
233
234bool DelayCommand::Verify(const ADSP::CommandListProcessor& processor) {
235 return true;
236}
237
238} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/delay.h b/src/audio_core/renderer/command/effect/delay.h
new file mode 100644
index 000000000..b7a15ae6b
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/delay.h
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8
9#include "audio_core/renderer/command/icommand.h"
10#include "audio_core/renderer/effect/delay.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for a delay effect. Delays inputs mix buffers according to the parameters
20 * and state, outputs receives the delayed samples.
21 */
22struct DelayCommand : ICommand {
23 /**
24 * Print this command's information to a string.
25 *
26 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into.
28 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
30
31 /**
32 * Process this command.
33 *
34 * @param processor - The CommandListProcessor processing this command.
35 */
36 void Process(const ADSP::CommandListProcessor& processor) override;
37
38 /**
39 * Verify this command's data is valid.
40 *
41 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false.
43 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override;
45
46 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs;
48 /// Output mix buffer offsets for each channel
49 std::array<s16, MaxChannels> outputs;
50 /// Input parameters
51 DelayInfo::ParameterVersion1 parameter;
52 /// State, updated each call
53 CpuAddr state;
54 /// Game-supplied workbuffer (Unused)
55 CpuAddr workbuffer;
56 /// Is this effect enabled?
57 bool effect_enabled;
58};
59
60} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
new file mode 100644
index 000000000..c4bf3943a
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
@@ -0,0 +1,437 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <numbers>
5
6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/effect/i3dl2_reverb.h"
8
9namespace AudioCore::AudioRenderer {
10
11constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> MinDelayLineTimes{
12 5.0f,
13 6.0f,
14 13.0f,
15 14.0f,
16};
17constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> MaxDelayLineTimes{
18 45.7042007446f,
19 82.7817001343f,
20 149.938293457f,
21 271.575805664f,
22};
23constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> Decay0MaxDelayLineTimes{17.0f, 13.0f,
24 9.0f, 7.0f};
25constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayLines> Decay1MaxDelayLineTimes{19.0f, 11.0f,
26 10.0f, 6.0f};
27constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayTaps> EarlyTapTimes{
28 0.0171360000968f,
29 0.0591540001333f,
30 0.161733001471f,
31 0.390186011791f,
32 0.425262004137f,
33 0.455410987139f,
34 0.689737021923f,
35 0.74590998888f,
36 0.833844006062f,
37 0.859502017498f,
38 0.0f,
39 0.0750240013003f,
40 0.168788000941f,
41 0.299901008606f,
42 0.337442994118f,
43 0.371903002262f,
44 0.599011003971f,
45 0.716741025448f,
46 0.817858994007f,
47 0.85166400671f,
48};
49
50constexpr std::array<f32, I3dl2ReverbInfo::MaxDelayTaps> EarlyGains{
51 0.67096f, 0.61027f, 1.0f, 0.3568f, 0.68361f, 0.65978f, 0.51939f,
52 0.24712f, 0.45945f, 0.45021f, 0.64196f, 0.54879f, 0.92925f, 0.3827f,
53 0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
54
55/**
56 * Update the I3dl2ReverbInfo state according to the given parameters.
57 *
58 * @param params - Input parameters to update the state.
59 * @param state - State to be updated.
60 * @param reset - If enabled, the state buffers will be reset. Only set this on initialize.
61 */
62static void UpdateI3dl2ReverbEffectParameter(const I3dl2ReverbInfo::ParameterVersion1& params,
63 I3dl2ReverbInfo::State& state, const bool reset) {
64 const auto pow_10 = [](f32 val) -> f32 {
65 return (val >= 0.0f) ? 1.0f : (val <= -5.3f) ? 0.0f : std::pow(10.0f, val);
66 };
67 const auto sin = [](f32 degrees) -> f32 {
68 return std::sin(degrees * std::numbers::pi_v<f32> / 180.0f);
69 };
70 const auto cos = [](f32 degrees) -> f32 {
71 return std::cos(degrees * std::numbers::pi_v<f32> / 180.0f);
72 };
73
74 Common::FixedPoint<50, 14> delay{static_cast<f32>(params.sample_rate) / 1000.0f};
75
76 state.dry_gain = params.dry_gain;
77 Common::FixedPoint<50, 14> early_gain{
78 std::min(params.room_gain + params.reflection_gain, 5000.0f) / 2000.0f};
79 state.early_gain = pow_10(early_gain.to_float());
80 Common::FixedPoint<50, 14> late_gain{std::min(params.room_gain + params.reverb_gain, 5000.0f) /
81 2000.0f};
82 state.late_gain = pow_10(late_gain.to_float());
83
84 Common::FixedPoint<50, 14> hf_gain{pow_10(params.room_HF_gain / 2000.0f)};
85 if (hf_gain >= 1.0f) {
86 state.lowpass_1 = 0.0f;
87 state.lowpass_2 = 1.0f;
88 } else {
89 const auto reference_hf{(params.reference_HF * 256.0f) /
90 static_cast<f32>(params.sample_rate)};
91 const Common::FixedPoint<50, 14> a{1.0f - hf_gain.to_float()};
92 const Common::FixedPoint<50, 14> b{2.0f + (-cos(reference_hf) * (hf_gain * 2.0f))};
93 const Common::FixedPoint<50, 14> c{
94 std::sqrt(std::pow(b.to_float(), 2.0f) + (std::pow(a.to_float(), 2.0f) * -4.0f))};
95
96 state.lowpass_1 = std::min(((b - c) / (a * 2.0f)).to_float(), 0.99723f);
97 state.lowpass_2 = 1.0f - state.lowpass_1;
98 }
99
100 state.early_to_late_taps =
101 (((params.reflection_delay + params.late_reverb_delay_time) * 1000.0f) * delay).to_int();
102 state.last_reverb_echo = params.late_reverb_diffusion * 0.6f * 0.01f;
103
104 for (u32 i = 0; i < I3dl2ReverbInfo::MaxDelayLines; i++) {
105 auto curr_delay{
106 ((MinDelayLineTimes[i] + (params.late_reverb_density / 100.0f) *
107 (MaxDelayLineTimes[i] - MinDelayLineTimes[i])) *
108 delay)
109 .to_int()};
110 state.fdn_delay_lines[i].SetDelay(curr_delay);
111
112 const auto a{
113 (static_cast<f32>(state.fdn_delay_lines[i].delay + state.decay_delay_lines0[i].delay +
114 state.decay_delay_lines1[i].delay) *
115 -60.0f) /
116 (params.late_reverb_decay_time * static_cast<f32>(params.sample_rate))};
117 const auto b{a / params.late_reverb_HF_decay_ratio};
118 const auto c{
119 cos(((params.reference_HF * 0.5f) * 128.0f) / static_cast<f32>(params.sample_rate)) /
120 sin(((params.reference_HF * 0.5f) * 128.0f) / static_cast<f32>(params.sample_rate))};
121 const auto d{pow_10((b - a) / 40.0f)};
122 const auto e{pow_10((b + a) / 40.0f) * 0.7071f};
123
124 state.lowpass_coeff[i][0] = ((c * d + 1.0f) * e) / (c + d);
125 state.lowpass_coeff[i][1] = ((1.0f - (c * d)) * e) / (c + d);
126 state.lowpass_coeff[i][2] = (c - d) / (c + d);
127
128 state.decay_delay_lines0[i].wet_gain = state.last_reverb_echo;
129 state.decay_delay_lines1[i].wet_gain = state.last_reverb_echo * -0.9f;
130 }
131
132 if (reset) {
133 state.shelf_filter.fill(0.0f);
134 state.lowpass_0 = 0.0f;
135 for (u32 i = 0; i < I3dl2ReverbInfo::MaxDelayLines; i++) {
136 std::ranges::fill(state.fdn_delay_lines[i].buffer, 0);
137 std::ranges::fill(state.decay_delay_lines0[i].buffer, 0);
138 std::ranges::fill(state.decay_delay_lines1[i].buffer, 0);
139 }
140 std::ranges::fill(state.center_delay_line.buffer, 0);
141 std::ranges::fill(state.early_delay_line.buffer, 0);
142 }
143
144 const auto reflection_time{(params.late_reverb_delay_time * 0.9998f + 0.02f) * 1000.0f};
145 const auto reflection_delay{params.reflection_delay * 1000.0f};
146 for (u32 i = 0; i < I3dl2ReverbInfo::MaxDelayTaps; i++) {
147 auto length{((reflection_delay + reflection_time * EarlyTapTimes[i]) * delay).to_int()};
148 if (length >= state.early_delay_line.max_delay) {
149 length = state.early_delay_line.max_delay;
150 }
151 state.early_tap_steps[i] = length;
152 }
153}
154
155/**
156 * Initialize a new I3dl2ReverbInfo state according to the given parameters.
157 *
158 * @param params - Input parameters to update the state.
159 * @param state - State to be updated.
160 * @param workbuffer - Game-supplied memory for the state. (Unused)
161 */
162static void InitializeI3dl2ReverbEffect(const I3dl2ReverbInfo::ParameterVersion1& params,
163 I3dl2ReverbInfo::State& state, const CpuAddr workbuffer) {
164 state = {};
165 Common::FixedPoint<50, 14> delay{static_cast<f32>(params.sample_rate) / 1000};
166
167 for (u32 i = 0; i < I3dl2ReverbInfo::MaxDelayLines; i++) {
168 auto fdn_delay_time{(MaxDelayLineTimes[i] * delay).to_uint_floor()};
169 state.fdn_delay_lines[i].Initialize(fdn_delay_time);
170
171 auto decay0_delay_time{(Decay0MaxDelayLineTimes[i] * delay).to_uint_floor()};
172 state.decay_delay_lines0[i].Initialize(decay0_delay_time);
173
174 auto decay1_delay_time{(Decay1MaxDelayLineTimes[i] * delay).to_uint_floor()};
175 state.decay_delay_lines1[i].Initialize(decay1_delay_time);
176 }
177
178 const auto center_delay_time{(5 * delay).to_uint_floor()};
179 state.center_delay_line.Initialize(center_delay_time);
180
181 const auto early_delay_time{(400 * delay).to_uint_floor()};
182 state.early_delay_line.Initialize(early_delay_time);
183
184 UpdateI3dl2ReverbEffectParameter(params, state, true);
185}
186
187/**
188 * Pass-through the effect, copying input to output directly, with no reverb applied.
189 *
190 * @param inputs - Array of input mix buffers to copy.
191 * @param outputs - Array of output mix buffers to receive copy.
192 * @param channel_count - Number of channels in inputs and outputs.
193 * @param sample_count - Number of samples within each channel (unused).
194 */
195static void ApplyI3dl2ReverbEffectBypass(std::span<std::span<const s32>> inputs,
196 std::span<std::span<s32>> outputs, const u32 channel_count,
197 [[maybe_unused]] const u32 sample_count) {
198 for (u32 i = 0; i < channel_count; i++) {
199 if (inputs[i].data() != outputs[i].data()) {
200 std::memcpy(outputs[i].data(), inputs[i].data(), outputs[i].size_bytes());
201 }
202 }
203}
204
205/**
206 * Tick the delay lines, reading and returning their current output, and writing a new decaying
207 * sample (mix).
208 *
209 * @param decay0 - The first decay line.
210 * @param decay1 - The second decay line.
211 * @param fdn - Feedback delay network.
212 * @param mix - The new calculated sample to be written and decayed.
213 * @return The next delayed and decayed sample.
214 */
215static Common::FixedPoint<50, 14> Axfx2AllPassTick(I3dl2ReverbInfo::I3dl2DelayLine& decay0,
216 I3dl2ReverbInfo::I3dl2DelayLine& decay1,
217 I3dl2ReverbInfo::I3dl2DelayLine& fdn,
218 const Common::FixedPoint<50, 14> mix) {
219 auto val{decay0.Read()};
220 auto mixed{mix - (val * decay0.wet_gain)};
221 auto out{decay0.Tick(mixed) + (mixed * decay0.wet_gain)};
222
223 val = decay1.Read();
224 mixed = out - (val * decay1.wet_gain);
225 out = decay1.Tick(mixed) + (mixed * decay1.wet_gain);
226
227 fdn.Tick(out);
228 return out;
229}
230
231/**
232 * Impl. Apply a I3DL2 reverb according to the current state, on the input mix buffers,
233 * saving the results to the output mix buffers.
234 *
235 * @tparam NumChannels - Number of channels to process. 1-6.
236 Inputs/outputs should have this many buffers.
237 * @param state - State to use, must be initialized (see InitializeI3dl2ReverbEffect).
238 * @param inputs - Input mix buffers to perform the reverb on.
239 * @param outputs - Output mix buffers to receive the reverbed samples.
240 * @param sample_count - Number of samples to process.
241 */
242template <size_t NumChannels>
243static void ApplyI3dl2ReverbEffect(I3dl2ReverbInfo::State& state,
244 std::span<std::span<const s32>> inputs,
245 std::span<std::span<s32>> outputs, const u32 sample_count) {
246 constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
247 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
248 };
249 constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes2Ch{
250 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1,
251 };
252 constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes4Ch{
253 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3,
254 };
255 constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{
256 2, 0, 0, 1, 1, 1, 1, 4, 4, 4, 1, 1, 1, 0, 0, 0, 0, 5, 5, 5,
257 };
258
259 std::span<const u8> tap_indexes{};
260 if constexpr (NumChannels == 1) {
261 tap_indexes = OutTapIndexes1Ch;
262 } else if constexpr (NumChannels == 2) {
263 tap_indexes = OutTapIndexes2Ch;
264 } else if constexpr (NumChannels == 4) {
265 tap_indexes = OutTapIndexes4Ch;
266 } else if constexpr (NumChannels == 6) {
267 tap_indexes = OutTapIndexes6Ch;
268 }
269
270 for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
271 Common::FixedPoint<50, 14> early_to_late_tap{
272 state.early_delay_line.TapOut(state.early_to_late_taps)};
273 std::array<Common::FixedPoint<50, 14>, NumChannels> output_samples{};
274
275 for (u32 early_tap = 0; early_tap < I3dl2ReverbInfo::MaxDelayTaps; early_tap++) {
276 output_samples[tap_indexes[early_tap]] +=
277 state.early_delay_line.TapOut(state.early_tap_steps[early_tap]) *
278 EarlyGains[early_tap];
279 if constexpr (NumChannels == 6) {
280 output_samples[static_cast<u32>(Channels::LFE)] +=
281 state.early_delay_line.TapOut(state.early_tap_steps[early_tap]) *
282 EarlyGains[early_tap];
283 }
284 }
285
286 Common::FixedPoint<50, 14> current_sample{};
287 for (u32 channel = 0; channel < NumChannels; channel++) {
288 current_sample += inputs[channel][sample_index];
289 }
290
291 state.lowpass_0 =
292 (current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1).to_float();
293 state.early_delay_line.Tick(state.lowpass_0);
294
295 for (u32 channel = 0; channel < NumChannels; channel++) {
296 output_samples[channel] *= state.early_gain;
297 }
298
299 std::array<Common::FixedPoint<50, 14>, I3dl2ReverbInfo::MaxDelayLines> filtered_samples{};
300 for (u32 delay_line = 0; delay_line < I3dl2ReverbInfo::MaxDelayLines; delay_line++) {
301 filtered_samples[delay_line] =
302 state.fdn_delay_lines[delay_line].Read() * state.lowpass_coeff[delay_line][0] +
303 state.shelf_filter[delay_line];
304 state.shelf_filter[delay_line] =
305 (filtered_samples[delay_line] * state.lowpass_coeff[delay_line][2] +
306 state.fdn_delay_lines[delay_line].Read() * state.lowpass_coeff[delay_line][1])
307 .to_float();
308 }
309
310 const std::array<Common::FixedPoint<50, 14>, I3dl2ReverbInfo::MaxDelayLines> mix_matrix{
311 filtered_samples[1] + filtered_samples[2] + early_to_late_tap * state.late_gain,
312 -filtered_samples[0] - filtered_samples[3] + early_to_late_tap * state.late_gain,
313 filtered_samples[0] - filtered_samples[3] + early_to_late_tap * state.late_gain,
314 filtered_samples[1] - filtered_samples[2] + early_to_late_tap * state.late_gain,
315 };
316
317 std::array<Common::FixedPoint<50, 14>, I3dl2ReverbInfo::MaxDelayLines> allpass_samples{};
318 for (u32 delay_line = 0; delay_line < I3dl2ReverbInfo::MaxDelayLines; delay_line++) {
319 allpass_samples[delay_line] = Axfx2AllPassTick(
320 state.decay_delay_lines0[delay_line], state.decay_delay_lines1[delay_line],
321 state.fdn_delay_lines[delay_line], mix_matrix[delay_line]);
322 }
323
324 if constexpr (NumChannels == 6) {
325 const std::array<Common::FixedPoint<50, 14>, MaxChannels> allpass_outputs{
326 allpass_samples[0], allpass_samples[1], allpass_samples[2] - allpass_samples[3],
327 allpass_samples[3], allpass_samples[2], allpass_samples[3],
328 };
329
330 for (u32 channel = 0; channel < NumChannels; channel++) {
331 Common::FixedPoint<50, 14> allpass{};
332
333 if (channel == static_cast<u32>(Channels::Center)) {
334 allpass = state.center_delay_line.Tick(allpass_outputs[channel] * 0.5f);
335 } else {
336 allpass = allpass_outputs[channel];
337 }
338
339 auto out_sample{output_samples[channel] + allpass +
340 state.dry_gain * static_cast<f32>(inputs[channel][sample_index])};
341
342 outputs[channel][sample_index] =
343 static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f));
344 }
345 } else {
346 for (u32 channel = 0; channel < NumChannels; channel++) {
347 auto out_sample{output_samples[channel] + allpass_samples[channel] +
348 state.dry_gain * static_cast<f32>(inputs[channel][sample_index])};
349 outputs[channel][sample_index] =
350 static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f));
351 }
352 }
353 }
354}
355
356/**
357 * Apply a I3DL2 reverb if enabled, according to the current state, on the input mix buffers,
358 * saving the results to the output mix buffers.
359 *
360 * @param params - Input parameters to use.
361 * @param state - State to use, must be initialized (see InitializeI3dl2ReverbEffect).
362 * @param enabled - If enabled, delay will be applied, otherwise input is copied to output.
363 * @param inputs - Input mix buffers to performan the delay on.
364 * @param outputs - Output mix buffers to receive the delayed samples.
365 * @param sample_count - Number of samples to process.
366 */
367static void ApplyI3dl2ReverbEffect(const I3dl2ReverbInfo::ParameterVersion1& params,
368 I3dl2ReverbInfo::State& state, const bool enabled,
369 std::span<std::span<const s32>> inputs,
370 std::span<std::span<s32>> outputs, const u32 sample_count) {
371 if (enabled) {
372 switch (params.channel_count) {
373 case 0:
374 return;
375 case 1:
376 ApplyI3dl2ReverbEffect<1>(state, inputs, outputs, sample_count);
377 break;
378 case 2:
379 ApplyI3dl2ReverbEffect<2>(state, inputs, outputs, sample_count);
380 break;
381 case 4:
382 ApplyI3dl2ReverbEffect<4>(state, inputs, outputs, sample_count);
383 break;
384 case 6:
385 ApplyI3dl2ReverbEffect<6>(state, inputs, outputs, sample_count);
386 break;
387 default:
388 ApplyI3dl2ReverbEffectBypass(inputs, outputs, params.channel_count, sample_count);
389 break;
390 }
391 } else {
392 ApplyI3dl2ReverbEffectBypass(inputs, outputs, params.channel_count, sample_count);
393 }
394}
395
396void I3dl2ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
397 std::string& string) {
398 string += fmt::format("I3dl2ReverbCommand\n\tenabled {} \n\tinputs: ", effect_enabled);
399 for (u32 i = 0; i < parameter.channel_count; i++) {
400 string += fmt::format("{:02X}, ", inputs[i]);
401 }
402 string += "\n\toutputs: ";
403 for (u32 i = 0; i < parameter.channel_count; i++) {
404 string += fmt::format("{:02X}, ", outputs[i]);
405 }
406 string += "\n";
407}
408
409void I3dl2ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
410 std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
411 std::vector<std::span<s32>> output_buffers(parameter.channel_count);
412
413 for (u32 i = 0; i < parameter.channel_count; i++) {
414 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
415 processor.sample_count);
416 output_buffers[i] = processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
417 processor.sample_count);
418 }
419
420 auto state_{reinterpret_cast<I3dl2ReverbInfo::State*>(state)};
421
422 if (effect_enabled) {
423 if (parameter.state == I3dl2ReverbInfo::ParameterState::Updating) {
424 UpdateI3dl2ReverbEffectParameter(parameter, *state_, false);
425 } else if (parameter.state == I3dl2ReverbInfo::ParameterState::Initialized) {
426 InitializeI3dl2ReverbEffect(parameter, *state_, workbuffer);
427 }
428 }
429 ApplyI3dl2ReverbEffect(parameter, *state_, effect_enabled, input_buffers, output_buffers,
430 processor.sample_count);
431}
432
433bool I3dl2ReverbCommand::Verify(const ADSP::CommandListProcessor& processor) {
434 return true;
435}
436
437} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.h b/src/audio_core/renderer/command/effect/i3dl2_reverb.h
new file mode 100644
index 000000000..243877056
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.h
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8
9#include "audio_core/renderer/command/icommand.h"
10#include "audio_core/renderer/effect/i3dl2.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for a I3DL2Reverb effect. Apply a reverb to inputs mix buffer according to
20 * the I3DL2 spec, outputs receives the results.
21 */
22struct I3dl2ReverbCommand : ICommand {
23 /**
24 * Print this command's information to a string.
25 *
26 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into.
28 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
30
31 /**
32 * Process this command.
33 *
34 * @param processor - The CommandListProcessor processing this command.
35 */
36 void Process(const ADSP::CommandListProcessor& processor) override;
37
38 /**
39 * Verify this command's data is valid.
40 *
41 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false.
43 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override;
45
46 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs;
48 /// Output mix buffer offsets for each channel
49 std::array<s16, MaxChannels> outputs;
50 /// Input parameters
51 I3dl2ReverbInfo::ParameterVersion1 parameter;
52 /// State, updated each call
53 CpuAddr state;
54 /// Game-supplied workbuffer (Unused)
55 CpuAddr workbuffer;
56 /// Is this effect enabled?
57 bool effect_enabled;
58};
59
60} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/light_limiter.cpp b/src/audio_core/renderer/command/effect/light_limiter.cpp
new file mode 100644
index 000000000..e8fb0e2fc
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/light_limiter.cpp
@@ -0,0 +1,222 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/effect/light_limiter.h"
6
7namespace AudioCore::AudioRenderer {
8/**
9 * Update the LightLimiterInfo state according to the given parameters.
10 * A no-op.
11 *
12 * @param params - Input parameters to update the state.
13 * @param state - State to be updated.
14 */
15static void UpdateLightLimiterEffectParameter(const LightLimiterInfo::ParameterVersion2& params,
16 LightLimiterInfo::State& state) {}
17
18/**
19 * Initialize a new LightLimiterInfo state according to the given parameters.
20 *
21 * @param params - Input parameters to update the state.
22 * @param state - State to be updated.
23 * @param workbuffer - Game-supplied memory for the state. (Unused)
24 */
25static void InitializeLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params,
26 LightLimiterInfo::State& state, const CpuAddr workbuffer) {
27 state = {};
28 state.samples_average.fill(0.0f);
29 state.compression_gain.fill(1.0f);
30 state.look_ahead_sample_offsets.fill(0);
31 for (u32 i = 0; i < params.channel_count; i++) {
32 state.look_ahead_sample_buffers[i].resize(params.look_ahead_samples_max, 0.0f);
33 }
34}
35
36/**
37 * Apply a light limiter effect if enabled, according to the current state, on the input mix
38 * buffers, saving the results to the output mix buffers.
39 *
40 * @param params - Input parameters to use.
41 * @param state - State to use, must be initialized (see InitializeLightLimiterEffect).
42 * @param enabled - If enabled, limiter will be applied, otherwise input is copied to output.
43 * @param inputs - Input mix buffers to perform the limiter on.
44 * @param outputs - Output mix buffers to receive the limited samples.
45 * @param sample_count - Number of samples to process.
46 * @params statistics - Optional output statistics, only used with version 2.
47 */
48static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& params,
49 LightLimiterInfo::State& state, const bool enabled,
50 std::vector<std::span<const s32>>& inputs,
51 std::vector<std::span<s32>>& outputs, const u32 sample_count,
52 LightLimiterInfo::StatisticsInternal* statistics) {
53 constexpr s64 min{std::numeric_limits<s32>::min()};
54 constexpr s64 max{std::numeric_limits<s32>::max()};
55
56 const auto recip_estimate = [](f64 a) -> f64 {
57 s32 q, s;
58 f64 r;
59 q = (s32)(a * 512.0); /* a in units of 1/512 rounded down */
60 r = 1.0 / (((f64)q + 0.5) / 512.0); /* reciprocal r */
61 s = (s32)(256.0 * r + 0.5); /* r in units of 1/256 rounded to nearest */
62 return ((f64)s / 256.0);
63 };
64
65 if (enabled) {
66 if (statistics && params.statistics_reset_required) {
67 for (u32 i = 0; i < params.channel_count; i++) {
68 statistics->channel_compression_gain_min[i] = 1.0f;
69 statistics->channel_max_sample[i] = 0;
70 }
71 }
72
73 for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
74 for (u32 channel = 0; channel < params.channel_count; channel++) {
75 auto sample{(Common::FixedPoint<49, 15>(inputs[channel][sample_index]) /
76 Common::FixedPoint<49, 15>::one) *
77 params.input_gain};
78 auto abs_sample{sample};
79 if (sample < 0.0f) {
80 abs_sample = -sample;
81 }
82 auto coeff{abs_sample > state.samples_average[channel] ? params.attack_coeff
83 : params.release_coeff};
84 state.samples_average[channel] +=
85 ((abs_sample - state.samples_average[channel]) * coeff).to_float();
86
87 // Reciprocal estimate
88 auto new_average_sample{Common::FixedPoint<49, 15>(
89 recip_estimate(state.samples_average[channel].to_double()))};
90 if (params.processing_mode != LightLimiterInfo::ProcessingMode::Mode1) {
91 // Two Newton-Raphson steps
92 auto temp{2.0 - (state.samples_average[channel] * new_average_sample)};
93 new_average_sample = 2.0 - (state.samples_average[channel] * temp);
94 }
95
96 auto above_threshold{state.samples_average[channel] > params.threshold};
97 auto attenuation{above_threshold ? params.threshold * new_average_sample : 1.0f};
98 coeff = attenuation < state.compression_gain[channel] ? params.attack_coeff
99 : params.release_coeff;
100 state.compression_gain[channel] +=
101 (attenuation - state.compression_gain[channel]) * coeff;
102
103 auto lookahead_sample{
104 state.look_ahead_sample_buffers[channel]
105 [state.look_ahead_sample_offsets[channel]]};
106
107 state.look_ahead_sample_buffers[channel][state.look_ahead_sample_offsets[channel]] =
108 sample;
109 state.look_ahead_sample_offsets[channel] =
110 (state.look_ahead_sample_offsets[channel] + 1) % params.look_ahead_samples_min;
111
112 outputs[channel][sample_index] = static_cast<s32>(
113 std::clamp((lookahead_sample * state.compression_gain[channel] *
114 params.output_gain * Common::FixedPoint<49, 15>::one)
115 .to_long(),
116 min, max));
117
118 if (statistics) {
119 statistics->channel_max_sample[channel] =
120 std::max(statistics->channel_max_sample[channel], abs_sample.to_float());
121 statistics->channel_compression_gain_min[channel] =
122 std::min(statistics->channel_compression_gain_min[channel],
123 state.compression_gain[channel].to_float());
124 }
125 }
126 }
127 } else {
128 for (u32 i = 0; i < params.channel_count; i++) {
129 if (params.inputs[i] != params.outputs[i]) {
130 std::memcpy(outputs[i].data(), inputs[i].data(), outputs[i].size_bytes());
131 }
132 }
133 }
134}
135
136void LightLimiterVersion1Command::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
137 std::string& string) {
138 string += fmt::format("LightLimiterVersion1Command\n\tinputs: ");
139 for (u32 i = 0; i < MaxChannels; i++) {
140 string += fmt::format("{:02X}, ", inputs[i]);
141 }
142 string += "\n\toutputs: ";
143 for (u32 i = 0; i < MaxChannels; i++) {
144 string += fmt::format("{:02X}, ", outputs[i]);
145 }
146 string += "\n";
147}
148
149void LightLimiterVersion1Command::Process(const ADSP::CommandListProcessor& processor) {
150 std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
151 std::vector<std::span<s32>> output_buffers(parameter.channel_count);
152
153 for (u32 i = 0; i < parameter.channel_count; i++) {
154 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
155 processor.sample_count);
156 output_buffers[i] = processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
157 processor.sample_count);
158 }
159
160 auto state_{reinterpret_cast<LightLimiterInfo::State*>(state)};
161
162 if (effect_enabled) {
163 if (parameter.state == LightLimiterInfo::ParameterState::Updating) {
164 UpdateLightLimiterEffectParameter(parameter, *state_);
165 } else if (parameter.state == LightLimiterInfo::ParameterState::Initialized) {
166 InitializeLightLimiterEffect(parameter, *state_, workbuffer);
167 }
168 }
169
170 LightLimiterInfo::StatisticsInternal* statistics{nullptr};
171 ApplyLightLimiterEffect(parameter, *state_, effect_enabled, input_buffers, output_buffers,
172 processor.sample_count, statistics);
173}
174
175bool LightLimiterVersion1Command::Verify(const ADSP::CommandListProcessor& processor) {
176 return true;
177}
178
179void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
180 std::string& string) {
181 string += fmt::format("LightLimiterVersion2Command\n\tinputs: \n");
182 for (u32 i = 0; i < MaxChannels; i++) {
183 string += fmt::format("{:02X}, ", inputs[i]);
184 }
185 string += "\n\toutputs: ";
186 for (u32 i = 0; i < MaxChannels; i++) {
187 string += fmt::format("{:02X}, ", outputs[i]);
188 }
189 string += "\n";
190}
191
192void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
193 std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
194 std::vector<std::span<s32>> output_buffers(parameter.channel_count);
195
196 for (u32 i = 0; i < parameter.channel_count; i++) {
197 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
198 processor.sample_count);
199 output_buffers[i] = processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
200 processor.sample_count);
201 }
202
203 auto state_{reinterpret_cast<LightLimiterInfo::State*>(state)};
204
205 if (effect_enabled) {
206 if (parameter.state == LightLimiterInfo::ParameterState::Updating) {
207 UpdateLightLimiterEffectParameter(parameter, *state_);
208 } else if (parameter.state == LightLimiterInfo::ParameterState::Initialized) {
209 InitializeLightLimiterEffect(parameter, *state_, workbuffer);
210 }
211 }
212
213 auto statistics{reinterpret_cast<LightLimiterInfo::StatisticsInternal*>(result_state)};
214 ApplyLightLimiterEffect(parameter, *state_, effect_enabled, input_buffers, output_buffers,
215 processor.sample_count, statistics);
216}
217
218bool LightLimiterVersion2Command::Verify(const ADSP::CommandListProcessor& processor) {
219 return true;
220}
221
222} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/light_limiter.h b/src/audio_core/renderer/command/effect/light_limiter.h
new file mode 100644
index 000000000..5d98272c7
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/light_limiter.h
@@ -0,0 +1,103 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8
9#include "audio_core/renderer/command/icommand.h"
10#include "audio_core/renderer/effect/light_limiter.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for limiting volume between a high and low threshold.
20 * Version 1.
21 */
22struct LightLimiterVersion1Command : ICommand {
23 /**
24 * Print this command's information to a string.
25 *
26 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into.
28 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
30
31 /**
32 * Process this command.
33 *
34 * @param processor - The CommandListProcessor processing this command.
35 */
36 void Process(const ADSP::CommandListProcessor& processor) override;
37
38 /**
39 * Verify this command's data is valid.
40 *
41 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false.
43 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override;
45
46 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs;
48 /// Output mix buffer offsets for each channel
49 std::array<s16, MaxChannels> outputs;
50 /// Input parameters
51 LightLimiterInfo::ParameterVersion2 parameter;
52 /// State, updated each call
53 CpuAddr state;
54 /// Game-supplied workbuffer (Unused)
55 CpuAddr workbuffer;
56 /// Is this effect enabled?
57 bool effect_enabled;
58};
59
60/**
61 * AudioRenderer command for limiting volume between a high and low threshold.
62 * Version 2 with output statistics.
63 */
64struct LightLimiterVersion2Command : ICommand {
65 /**
66 * Print this command's information to a string.
67 *
68 * @param processor - The CommandListProcessor processing this command.
69 * @param string - The string to print into.
70 */
71 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
72
73 /**
74 * Process this command.
75 *
76 * @param processor - The CommandListProcessor processing this command.
77 */
78 void Process(const ADSP::CommandListProcessor& processor) override;
79
80 /**
81 * Verify this command's data is valid.
82 *
83 * @param processor - The CommandListProcessor processing this command.
84 */
85 bool Verify(const ADSP::CommandListProcessor& processor) override;
86
87 /// Input mix buffer offsets for each channel
88 std::array<s16, MaxChannels> inputs;
89 /// Output mix buffer offsets for each channel
90 std::array<s16, MaxChannels> outputs;
91 /// Input parameters
92 LightLimiterInfo::ParameterVersion2 parameter;
93 /// State, updated each call
94 CpuAddr state;
95 /// Game-supplied workbuffer (Unused)
96 CpuAddr workbuffer;
97 /// Optional statistics, sent back to the sysmodule
98 CpuAddr result_state;
99 /// Is this effect enabled?
100 bool effect_enabled;
101};
102
103} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
new file mode 100644
index 000000000..b3c3ba4ba
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.cpp
@@ -0,0 +1,45 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/effect/biquad_filter.h"
6#include "audio_core/renderer/command/effect/multi_tap_biquad_filter.h"
7
8namespace AudioCore::AudioRenderer {
9
10void MultiTapBiquadFilterCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
11 std::string& string) {
12 string += fmt::format(
13 "MultiTapBiquadFilterCommand\n\tinput {:02X}\n\toutput {:02X}\n\tneeds_init ({}, {})\n",
14 input, output, needs_init[0], needs_init[1]);
15}
16
17void MultiTapBiquadFilterCommand::Process(const ADSP::CommandListProcessor& processor) {
18 if (filter_tap_count > MaxBiquadFilters) {
19 LOG_ERROR(Service_Audio, "Too many filter taps! {}", filter_tap_count);
20 filter_tap_count = MaxBiquadFilters;
21 }
22
23 auto input_buffer{
24 processor.mix_buffers.subspan(input * processor.sample_count, processor.sample_count)};
25 auto output_buffer{
26 processor.mix_buffers.subspan(output * processor.sample_count, processor.sample_count)};
27
28 // TODO: Fix this, currently just applies the filter to the input twice,
29 // and doesn't chain the biquads together at all.
30 for (u32 i = 0; i < filter_tap_count; i++) {
31 auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(states[i])};
32 if (needs_init[i]) {
33 std::memset(state, 0, sizeof(VoiceState::BiquadFilterState));
34 }
35
36 ApplyBiquadFilterFloat(output_buffer, input_buffer, biquads[i].b, biquads[i].a, *state,
37 processor.sample_count);
38 }
39}
40
41bool MultiTapBiquadFilterCommand::Verify(const ADSP::CommandListProcessor& processor) {
42 return true;
43}
44
45} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h
new file mode 100644
index 000000000..99c2c0830
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/multi_tap_biquad_filter.h
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8
9#include "audio_core/renderer/command/icommand.h"
10#include "audio_core/renderer/voice/voice_info.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for applying multiple biquads at once.
20 */
21struct MultiTapBiquadFilterCommand : ICommand {
22 /**
23 * Print this command's information to a string.
24 *
25 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into.
27 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
29
30 /**
31 * Process this command.
32 *
33 * @param processor - The CommandListProcessor processing this command.
34 */
35 void Process(const ADSP::CommandListProcessor& processor) override;
36
37 /**
38 * Verify this command's data is valid.
39 *
40 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false.
42 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override;
44
45 /// Input mix buffer index
46 s16 input;
47 /// Output mix buffer index
48 s16 output;
49 /// Biquad parameters
50 std::array<VoiceInfo::BiquadFilterParameter, MaxBiquadFilters> biquads;
51 /// Biquad states, updated each call
52 std::array<CpuAddr, MaxBiquadFilters> states;
53 /// If each biquad needs initialisation
54 std::array<bool, MaxBiquadFilters> needs_init;
55 /// Number of active biquads
56 u8 filter_tap_count;
57};
58
59} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp
new file mode 100644
index 000000000..fe2b1eb43
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/reverb.cpp
@@ -0,0 +1,440 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <numbers>
5#include <ranges>
6
7#include "audio_core/renderer/adsp/command_list_processor.h"
8#include "audio_core/renderer/command/effect/reverb.h"
9
10namespace AudioCore::AudioRenderer {
11
12constexpr std::array<f32, ReverbInfo::MaxDelayLines> FdnMaxDelayLineTimes = {
13 53.9532470703125f,
14 79.19256591796875f,
15 116.23876953125f,
16 170.61529541015625f,
17};
18
19constexpr std::array<f32, ReverbInfo::MaxDelayLines> DecayMaxDelayLineTimes = {
20 7.0f,
21 9.0f,
22 13.0f,
23 17.0f,
24};
25
26constexpr std::array<std::array<f32, ReverbInfo::MaxDelayTaps + 1>, ReverbInfo::NumEarlyModes>
27 EarlyDelayTimes = {
28 {{0.000000f, 3.500000f, 2.799988f, 3.899963f, 2.699951f, 13.399963f, 7.899963f, 8.399963f,
29 9.899963f, 12.000000f, 12.500000f},
30 {0.000000f, 11.799988f, 5.500000f, 11.199951f, 10.399963f, 38.099976f, 22.199951f,
31 29.599976f, 21.199951f, 24.799988f, 40.000000f},
32 {0.000000f, 41.500000f, 20.500000f, 41.299988f, 0.000000f, 29.500000f, 33.799988f,
33 45.199951f, 46.799988f, 0.000000f, 50.000000f},
34 {33.099976f, 43.299988f, 22.799988f, 37.899963f, 14.899963f, 35.299988f, 17.899963f,
35 34.199951f, 0.000000f, 43.299988f, 50.000000f},
36 {0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f,
37 0.000000f, 0.000000f, 0.000000f}},
38};
39
40constexpr std::array<std::array<f32, ReverbInfo::MaxDelayTaps>, ReverbInfo::NumEarlyModes>
41 EarlyDelayGains = {{
42 {0.699951f, 0.679993f, 0.699951f, 0.679993f, 0.699951f, 0.679993f, 0.699951f, 0.679993f,
43 0.679993f, 0.679993f},
44 {0.699951f, 0.679993f, 0.699951f, 0.679993f, 0.699951f, 0.679993f, 0.679993f, 0.679993f,
45 0.679993f, 0.679993f},
46 {0.500000f, 0.699951f, 0.699951f, 0.679993f, 0.500000f, 0.679993f, 0.679993f, 0.699951f,
47 0.679993f, 0.000000f},
48 {0.929993f, 0.919983f, 0.869995f, 0.859985f, 0.939941f, 0.809998f, 0.799988f, 0.769958f,
49 0.759949f, 0.649963f},
50 {0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f, 0.000000f,
51 0.000000f, 0.000000f},
52 }};
53
54constexpr std::array<std::array<f32, ReverbInfo::MaxDelayLines>, ReverbInfo::NumLateModes>
55 FdnDelayTimes = {{
56 {53.953247f, 79.192566f, 116.238770f, 130.615295f},
57 {53.953247f, 79.192566f, 116.238770f, 170.615295f},
58 {5.000000f, 10.000000f, 5.000000f, 10.000000f},
59 {47.029968f, 71.000000f, 103.000000f, 170.000000f},
60 {53.953247f, 79.192566f, 116.238770f, 170.615295f},
61 }};
62
63constexpr std::array<std::array<f32, ReverbInfo::MaxDelayLines>, ReverbInfo::NumLateModes>
64 DecayDelayTimes = {{
65 {7.000000f, 9.000000f, 13.000000f, 17.000000f},
66 {7.000000f, 9.000000f, 13.000000f, 17.000000f},
67 {1.000000f, 1.000000f, 1.000000f, 1.000000f},
68 {7.000000f, 7.000000f, 13.000000f, 9.000000f},
69 {7.000000f, 9.000000f, 13.000000f, 17.000000f},
70 }};
71
72/**
73 * Update the ReverbInfo state according to the given parameters.
74 *
75 * @param params - Input parameters to update the state.
76 * @param state - State to be updated.
77 */
78static void UpdateReverbEffectParameter(const ReverbInfo::ParameterVersion2& params,
79 ReverbInfo::State& state) {
80 const auto pow_10 = [](f32 val) -> f32 {
81 return (val >= 0.0f) ? 1.0f : (val <= -5.3f) ? 0.0f : std::pow(10.0f, val);
82 };
83 const auto cos = [](f32 degrees) -> f32 {
84 return std::cos(degrees * std::numbers::pi_v<f32> / 180.0f);
85 };
86
87 static bool unk_initialized{false};
88 static Common::FixedPoint<50, 14> unk_value{};
89
90 const auto sample_rate{Common::FixedPoint<50, 14>::from_base(params.sample_rate)};
91 const auto pre_delay_time{Common::FixedPoint<50, 14>::from_base(params.pre_delay)};
92
93 for (u32 i = 0; i < ReverbInfo::MaxDelayTaps; i++) {
94 auto early_delay{
95 ((pre_delay_time + EarlyDelayTimes[params.early_mode][i]) * sample_rate).to_int()};
96 early_delay = std::min(early_delay, state.pre_delay_line.sample_count_max);
97 state.early_delay_times[i] = early_delay + 1;
98 state.early_gains[i] = Common::FixedPoint<50, 14>::from_base(params.early_gain) *
99 EarlyDelayGains[params.early_mode][i];
100 }
101
102 if (params.channel_count == 2) {
103 state.early_gains[4] * 0.5f;
104 state.early_gains[5] * 0.5f;
105 }
106
107 auto pre_time{
108 ((pre_delay_time + EarlyDelayTimes[params.early_mode][10]) * sample_rate).to_int()};
109 state.pre_delay_time = std::min(pre_time, state.pre_delay_line.sample_count_max);
110
111 if (!unk_initialized) {
112 unk_value = cos((1280.0f / sample_rate).to_float());
113 unk_initialized = true;
114 }
115
116 for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) {
117 const auto fdn_delay{(FdnDelayTimes[params.late_mode][i] * sample_rate).to_int()};
118 state.fdn_delay_lines[i].sample_count =
119 std::min(fdn_delay, state.fdn_delay_lines[i].sample_count_max);
120 state.fdn_delay_lines[i].buffer_end =
121 &state.fdn_delay_lines[i].buffer[state.fdn_delay_lines[i].sample_count - 1];
122
123 const auto decay_delay{(DecayDelayTimes[params.late_mode][i] * sample_rate).to_int()};
124 state.decay_delay_lines[i].sample_count =
125 std::min(decay_delay, state.decay_delay_lines[i].sample_count_max);
126 state.decay_delay_lines[i].buffer_end =
127 &state.decay_delay_lines[i].buffer[state.decay_delay_lines[i].sample_count - 1];
128
129 state.decay_delay_lines[i].decay =
130 0.5999755859375f * (1.0f - Common::FixedPoint<50, 14>::from_base(params.colouration));
131
132 auto a{(Common::FixedPoint<50, 14>(state.fdn_delay_lines[i].sample_count_max) +
133 state.decay_delay_lines[i].sample_count_max) *
134 -3};
135 auto b{a / (Common::FixedPoint<50, 14>::from_base(params.decay_time) * sample_rate)};
136 Common::FixedPoint<50, 14> c{0.0f};
137 Common::FixedPoint<50, 14> d{0.0f};
138 auto hf_decay_ratio{Common::FixedPoint<50, 14>::from_base(params.high_freq_decay_ratio)};
139
140 if (hf_decay_ratio > 0.99493408203125f) {
141 c = 0.0f;
142 d = 1.0f;
143 } else {
144 const auto e{
145 pow_10(((((1.0f / hf_decay_ratio) - 1.0f) * 2) / 100 * (b / 10)).to_float())};
146 const auto f{1.0f - e};
147 const auto g{2.0f - (unk_value * e * 2)};
148 const auto h{std::sqrt(std::pow(g.to_float(), 2.0f) - (std::pow(f, 2.0f) * 4))};
149
150 c = (g - h) / (f * 2.0f);
151 d = 1.0f - c;
152 }
153
154 state.hf_decay_prev_gain[i] = c;
155 state.hf_decay_gain[i] = pow_10((b / 1000).to_float()) * d * 0.70709228515625f;
156 state.prev_feedback_output[i] = 0;
157 }
158}
159
160/**
161 * Initialize a new ReverbInfo state according to the given parameters.
162 *
163 * @param params - Input parameters to update the state.
164 * @param state - State to be updated.
165 * @param workbuffer - Game-supplied memory for the state. (Unused)
166 * @param long_size_pre_delay_supported - Use a longer pre-delay time before reverb begins.
167 */
168static void InitializeReverbEffect(const ReverbInfo::ParameterVersion2& params,
169 ReverbInfo::State& state, const CpuAddr workbuffer,
170 const bool long_size_pre_delay_supported) {
171 state = {};
172
173 auto delay{Common::FixedPoint<50, 14>::from_base(params.sample_rate)};
174
175 for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) {
176 auto fdn_delay_time{(FdnMaxDelayLineTimes[i] * delay).to_uint_floor()};
177 state.fdn_delay_lines[i].Initialize(fdn_delay_time, 1.0f);
178
179 auto decay_delay_time{(DecayMaxDelayLineTimes[i] * delay).to_uint_floor()};
180 state.decay_delay_lines[i].Initialize(decay_delay_time, 0.0f);
181 }
182
183 const auto pre_delay{long_size_pre_delay_supported ? 350.0f : 150.0f};
184 const auto pre_delay_line{(pre_delay * delay).to_uint_floor()};
185 state.pre_delay_line.Initialize(pre_delay_line, 1.0f);
186
187 const auto center_delay_time{(5 * delay).to_uint_floor()};
188 state.center_delay_line.Initialize(center_delay_time, 1.0f);
189
190 UpdateReverbEffectParameter(params, state);
191
192 for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) {
193 std::ranges::fill(state.fdn_delay_lines[i].buffer, 0);
194 std::ranges::fill(state.decay_delay_lines[i].buffer, 0);
195 }
196 std::ranges::fill(state.center_delay_line.buffer, 0);
197 std::ranges::fill(state.pre_delay_line.buffer, 0);
198}
199
200/**
201 * Pass-through the effect, copying input to output directly, with no reverb applied.
202 *
203 * @param inputs - Array of input mix buffers to copy.
204 * @param outputs - Array of output mix buffers to receive copy.
205 * @param channel_count - Number of channels in inputs and outputs.
206 * @param sample_count - Number of samples within each channel.
207 */
208static void ApplyReverbEffectBypass(std::span<std::span<const s32>> inputs,
209 std::span<std::span<s32>> outputs, const u32 channel_count,
210 const u32 sample_count) {
211 for (u32 i = 0; i < channel_count; i++) {
212 if (inputs[i].data() != outputs[i].data()) {
213 std::memcpy(outputs[i].data(), inputs[i].data(), outputs[i].size_bytes());
214 }
215 }
216}
217
218/**
219 * Tick the delay lines, reading and returning their current output, and writing a new decaying
220 * sample (mix).
221 *
222 * @param decay - The decay line.
223 * @param fdn - Feedback delay network.
224 * @param mix - The new calculated sample to be written and decayed.
225 * @return The next delayed and decayed sample.
226 */
227static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine& decay,
228 ReverbInfo::ReverbDelayLine& fdn,
229 const Common::FixedPoint<50, 14> mix) {
230 const auto val{decay.Read()};
231 const auto mixed{mix - (val * decay.decay)};
232 const auto out{decay.Tick(mixed) + (mixed * decay.decay)};
233
234 fdn.Tick(out);
235 return out;
236}
237
238/**
239 * Impl. Apply a Reverb according to the current state, on the input mix buffers,
240 * saving the results to the output mix buffers.
241 *
242 * @tparam NumChannels - Number of channels to process. 1-6.
243 Inputs/outputs should have this many buffers.
244 * @param params - Input parameters to update the state.
245 * @param state - State to use, must be initialized (see InitializeReverbEffect).
246 * @param inputs - Input mix buffers to perform the reverb on.
247 * @param outputs - Output mix buffers to receive the reverbed samples.
248 * @param sample_count - Number of samples to process.
249 */
250template <size_t NumChannels>
251static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
252 std::vector<std::span<const s32>>& inputs,
253 std::vector<std::span<s32>>& outputs, const u32 sample_count) {
254 constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes1Ch{
255 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
256 };
257 constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes2Ch{
258 0, 0, 1, 1, 0, 1, 0, 0, 1, 1,
259 };
260 constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes4Ch{
261 0, 0, 1, 1, 0, 1, 2, 2, 3, 3,
262 };
263 constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{
264 0, 0, 1, 1, 2, 2, 4, 4, 5, 5,
265 };
266
267 std::span<const u8> tap_indexes{};
268 if constexpr (NumChannels == 1) {
269 tap_indexes = OutTapIndexes1Ch;
270 } else if constexpr (NumChannels == 2) {
271 tap_indexes = OutTapIndexes2Ch;
272 } else if constexpr (NumChannels == 4) {
273 tap_indexes = OutTapIndexes4Ch;
274 } else if constexpr (NumChannels == 6) {
275 tap_indexes = OutTapIndexes6Ch;
276 }
277
278 for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
279 std::array<Common::FixedPoint<50, 14>, NumChannels> output_samples{};
280
281 for (u32 early_tap = 0; early_tap < ReverbInfo::MaxDelayTaps; early_tap++) {
282 const auto sample{state.pre_delay_line.TapOut(state.early_delay_times[early_tap]) *
283 state.early_gains[early_tap]};
284 output_samples[tap_indexes[early_tap]] += sample;
285 if constexpr (NumChannels == 6) {
286 output_samples[static_cast<u32>(Channels::LFE)] += sample;
287 }
288 }
289
290 if constexpr (NumChannels == 6) {
291 output_samples[static_cast<u32>(Channels::LFE)] *= 0.2f;
292 }
293
294 Common::FixedPoint<50, 14> input_sample{};
295 for (u32 channel = 0; channel < NumChannels; channel++) {
296 input_sample += inputs[channel][sample_index];
297 }
298
299 input_sample *= 64;
300 input_sample *= Common::FixedPoint<50, 14>::from_base(params.base_gain);
301 state.pre_delay_line.Write(input_sample);
302
303 for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) {
304 state.prev_feedback_output[i] =
305 state.prev_feedback_output[i] * state.hf_decay_prev_gain[i] +
306 state.fdn_delay_lines[i].Read() * state.hf_decay_gain[i];
307 }
308
309 Common::FixedPoint<50, 14> pre_delay_sample{
310 state.pre_delay_line.Read() * Common::FixedPoint<50, 14>::from_base(params.late_gain)};
311
312 std::array<Common::FixedPoint<50, 14>, ReverbInfo::MaxDelayLines> mix_matrix{
313 state.prev_feedback_output[2] + state.prev_feedback_output[1] + pre_delay_sample,
314 -state.prev_feedback_output[0] - state.prev_feedback_output[3] + pre_delay_sample,
315 state.prev_feedback_output[0] - state.prev_feedback_output[3] + pre_delay_sample,
316 state.prev_feedback_output[1] - state.prev_feedback_output[2] + pre_delay_sample,
317 };
318
319 std::array<Common::FixedPoint<50, 14>, ReverbInfo::MaxDelayLines> allpass_samples{};
320 for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) {
321 allpass_samples[i] = Axfx2AllPassTick(state.decay_delay_lines[i],
322 state.fdn_delay_lines[i], mix_matrix[i]);
323 }
324
325 const auto dry_gain{Common::FixedPoint<50, 14>::from_base(params.dry_gain)};
326 const auto wet_gain{Common::FixedPoint<50, 14>::from_base(params.wet_gain)};
327
328 if constexpr (NumChannels == 6) {
329 const std::array<Common::FixedPoint<50, 14>, MaxChannels> allpass_outputs{
330 allpass_samples[0], allpass_samples[1], allpass_samples[2] - allpass_samples[3],
331 allpass_samples[3], allpass_samples[2], allpass_samples[3],
332 };
333
334 for (u32 channel = 0; channel < NumChannels; channel++) {
335 auto in_sample{inputs[channel][sample_index] * dry_gain};
336
337 Common::FixedPoint<50, 14> allpass{};
338 if (channel == static_cast<u32>(Channels::Center)) {
339 allpass = state.center_delay_line.Tick(allpass_outputs[channel] * 0.5f);
340 } else {
341 allpass = allpass_outputs[channel];
342 }
343
344 auto out_sample{((output_samples[channel] + allpass) * wet_gain) / 64};
345 outputs[channel][sample_index] = (in_sample + out_sample).to_int();
346 }
347 } else {
348 for (u32 channel = 0; channel < NumChannels; channel++) {
349 auto in_sample{inputs[channel][sample_index] * dry_gain};
350 auto out_sample{((output_samples[channel] + allpass_samples[channel]) * wet_gain) /
351 64};
352 outputs[channel][sample_index] = (in_sample + out_sample).to_int();
353 }
354 }
355 }
356}
357
358/**
359 * Apply a Reverb if enabled, according to the current state, on the input mix buffers,
360 * saving the results to the output mix buffers.
361 *
362 * @param params - Input parameters to use.
363 * @param state - State to use, must be initialized (see InitializeReverbEffect).
364 * @param enabled - If enabled, delay will be applied, otherwise input is copied to output.
365 * @param inputs - Input mix buffers to performan the reverb on.
366 * @param outputs - Output mix buffers to receive the reverbed samples.
367 * @param sample_count - Number of samples to process.
368 */
369static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
370 const bool enabled, std::vector<std::span<const s32>>& inputs,
371 std::vector<std::span<s32>>& outputs, const u32 sample_count) {
372 if (enabled) {
373 switch (params.channel_count) {
374 case 0:
375 return;
376 case 1:
377 ApplyReverbEffect<1>(params, state, inputs, outputs, sample_count);
378 break;
379 case 2:
380 ApplyReverbEffect<2>(params, state, inputs, outputs, sample_count);
381 break;
382 case 4:
383 ApplyReverbEffect<4>(params, state, inputs, outputs, sample_count);
384 break;
385 case 6:
386 ApplyReverbEffect<6>(params, state, inputs, outputs, sample_count);
387 break;
388 default:
389 ApplyReverbEffectBypass(inputs, outputs, params.channel_count, sample_count);
390 break;
391 }
392 } else {
393 ApplyReverbEffectBypass(inputs, outputs, params.channel_count, sample_count);
394 }
395}
396
397void ReverbCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
398 std::string& string) {
399 string += fmt::format(
400 "ReverbCommand\n\tenabled {} long_size_pre_delay_supported {}\n\tinputs: ", effect_enabled,
401 long_size_pre_delay_supported);
402 for (u32 i = 0; i < MaxChannels; i++) {
403 string += fmt::format("{:02X}, ", inputs[i]);
404 }
405 string += "\n\toutputs: ";
406 for (u32 i = 0; i < MaxChannels; i++) {
407 string += fmt::format("{:02X}, ", outputs[i]);
408 }
409 string += "\n";
410}
411
412void ReverbCommand::Process(const ADSP::CommandListProcessor& processor) {
413 std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
414 std::vector<std::span<s32>> output_buffers(parameter.channel_count);
415
416 for (u32 i = 0; i < parameter.channel_count; i++) {
417 input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
418 processor.sample_count);
419 output_buffers[i] = processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
420 processor.sample_count);
421 }
422
423 auto state_{reinterpret_cast<ReverbInfo::State*>(state)};
424
425 if (effect_enabled) {
426 if (parameter.state == ReverbInfo::ParameterState::Updating) {
427 UpdateReverbEffectParameter(parameter, *state_);
428 } else if (parameter.state == ReverbInfo::ParameterState::Initialized) {
429 InitializeReverbEffect(parameter, *state_, workbuffer, long_size_pre_delay_supported);
430 }
431 }
432 ApplyReverbEffect(parameter, *state_, effect_enabled, input_buffers, output_buffers,
433 processor.sample_count);
434}
435
436bool ReverbCommand::Verify(const ADSP::CommandListProcessor& processor) {
437 return true;
438}
439
440} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/effect/reverb.h b/src/audio_core/renderer/command/effect/reverb.h
new file mode 100644
index 000000000..328756150
--- /dev/null
+++ b/src/audio_core/renderer/command/effect/reverb.h
@@ -0,0 +1,62 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8
9#include "audio_core/renderer/command/icommand.h"
10#include "audio_core/renderer/effect/reverb.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for a Reverb effect. Apply a reverb to inputs mix buffer, outputs receives
20 * the results.
21 */
22struct ReverbCommand : ICommand {
23 /**
24 * Print this command's information to a string.
25 *
26 * @param processor - The CommandListProcessor processing this command.
27 * @param string - The string to print into.
28 */
29 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
30
31 /**
32 * Process this command.
33 *
34 * @param processor - The CommandListProcessor processing this command.
35 */
36 void Process(const ADSP::CommandListProcessor& processor) override;
37
38 /**
39 * Verify this command's data is valid.
40 *
41 * @param processor - The CommandListProcessor processing this command.
42 * @return True if the command is valid, otherwise false.
43 */
44 bool Verify(const ADSP::CommandListProcessor& processor) override;
45
46 /// Input mix buffer offsets for each channel
47 std::array<s16, MaxChannels> inputs;
48 /// Output mix buffer offsets for each channel
49 std::array<s16, MaxChannels> outputs;
50 /// Input parameters
51 ReverbInfo::ParameterVersion2 parameter;
52 /// State, updated each call
53 CpuAddr state;
54 /// Game-supplied workbuffer (Unused)
55 CpuAddr workbuffer;
56 /// Is this effect enabled?
57 bool effect_enabled;
58 /// Is a longer pre-delay time supported?
59 bool long_size_pre_delay_supported;
60};
61
62} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/icommand.h b/src/audio_core/renderer/command/icommand.h
new file mode 100644
index 000000000..f2dd41254
--- /dev/null
+++ b/src/audio_core/renderer/command/icommand.h
@@ -0,0 +1,93 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "audio_core/common/common.h"
7#include "common/common_types.h"
8
9namespace AudioCore::AudioRenderer {
10namespace ADSP {
11class CommandListProcessor;
12}
13
14enum class CommandId : u8 {
15 /* 0x00 */ Invalid,
16 /* 0x01 */ DataSourcePcmInt16Version1,
17 /* 0x02 */ DataSourcePcmInt16Version2,
18 /* 0x03 */ DataSourcePcmFloatVersion1,
19 /* 0x04 */ DataSourcePcmFloatVersion2,
20 /* 0x05 */ DataSourceAdpcmVersion1,
21 /* 0x06 */ DataSourceAdpcmVersion2,
22 /* 0x07 */ Volume,
23 /* 0x08 */ VolumeRamp,
24 /* 0x09 */ BiquadFilter,
25 /* 0x0A */ Mix,
26 /* 0x0B */ MixRamp,
27 /* 0x0C */ MixRampGrouped,
28 /* 0x0D */ DepopPrepare,
29 /* 0x0E */ DepopForMixBuffers,
30 /* 0x0F */ Delay,
31 /* 0x10 */ Upsample,
32 /* 0x11 */ DownMix6chTo2ch,
33 /* 0x12 */ Aux,
34 /* 0x13 */ DeviceSink,
35 /* 0x14 */ CircularBufferSink,
36 /* 0x15 */ Reverb,
37 /* 0x16 */ I3dl2Reverb,
38 /* 0x17 */ Performance,
39 /* 0x18 */ ClearMixBuffer,
40 /* 0x19 */ CopyMixBuffer,
41 /* 0x1A */ LightLimiterVersion1,
42 /* 0x1B */ LightLimiterVersion2,
43 /* 0x1C */ MultiTapBiquadFilter,
44 /* 0x1D */ Capture,
45 /* 0x1E */ Compressor,
46};
47
48constexpr u32 CommandMagic{0xCAFEBABE};
49
50/**
51 * A command, generated by the host, and processed by the ADSP's AudioRenderer.
52 */
53struct ICommand {
54 virtual ~ICommand() = default;
55
56 /**
57 * Print this command's information to a string.
58 *
59 * @param processor - The CommandListProcessor processing this command.
60 * @param string - The string to print into.
61 */
62 virtual void Dump(const ADSP::CommandListProcessor& processor, std::string& string) = 0;
63
64 /**
65 * Process this command.
66 *
67 * @param processor - The CommandListProcessor processing this command.
68 */
69 virtual void Process(const ADSP::CommandListProcessor& processor) = 0;
70
71 /**
72 * Verify this command's data is valid.
73 *
74 * @param processor - The CommandListProcessor processing this command.
75 * @return True if the command is valid, otherwise false.
76 */
77 virtual bool Verify(const ADSP::CommandListProcessor& processor) = 0;
78
79 /// Command magic 0xCAFEBABE
80 u32 magic{};
81 /// Command enabled
82 bool enabled{};
83 /// Type of this command (see CommandId)
84 CommandId type{};
85 /// Size of this command
86 s16 size{};
87 /// Estimated processing time for this command
88 u32 estimated_process_time{};
89 /// Node id of the voice or mix this command was generated from
90 u32 node_id{};
91};
92
93} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/clear_mix.cpp b/src/audio_core/renderer/command/mix/clear_mix.cpp
new file mode 100644
index 000000000..4f649d6a8
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/clear_mix.cpp
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <string>
5
6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/mix/clear_mix.h"
8
9namespace AudioCore::AudioRenderer {
10
11void ClearMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
12 std::string& string) {
13 string += fmt::format("ClearMixBufferCommand\n");
14}
15
16void ClearMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) {
17 memset(processor.mix_buffers.data(), 0, processor.mix_buffers.size_bytes());
18}
19
20bool ClearMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) {
21 return true;
22}
23
24} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/clear_mix.h b/src/audio_core/renderer/command/mix/clear_mix.h
new file mode 100644
index 000000000..956ec0b65
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/clear_mix.h
@@ -0,0 +1,45 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for a clearing the mix buffers.
18 * Used at the start of each command list.
19 */
20struct ClearMixBufferCommand : ICommand {
21 /**
22 * Print this command's information to a string.
23 *
24 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into.
26 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
28
29 /**
30 * Process this command.
31 *
32 * @param processor - The CommandListProcessor processing this command.
33 */
34 void Process(const ADSP::CommandListProcessor& processor) override;
35
36 /**
37 * Verify this command's data is valid.
38 *
39 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false.
41 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override;
43};
44
45} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/copy_mix.cpp b/src/audio_core/renderer/command/mix/copy_mix.cpp
new file mode 100644
index 000000000..1d49f1644
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/copy_mix.cpp
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/mix/copy_mix.h"
6
7namespace AudioCore::AudioRenderer {
8
9void CopyMixBufferCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
10 std::string& string) {
11 string += fmt::format("CopyMixBufferCommand\n\tinput {:02X} output {:02X}\n", input_index,
12 output_index);
13}
14
15void CopyMixBufferCommand::Process(const ADSP::CommandListProcessor& processor) {
16 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
17 processor.sample_count)};
18 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
19 processor.sample_count)};
20 std::memcpy(output.data(), input.data(), processor.sample_count * sizeof(s32));
21}
22
23bool CopyMixBufferCommand::Verify(const ADSP::CommandListProcessor& processor) {
24 return true;
25}
26
27} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/copy_mix.h b/src/audio_core/renderer/command/mix/copy_mix.h
new file mode 100644
index 000000000..a59007fb6
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/copy_mix.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for a copying a mix buffer from input to output.
18 */
19struct CopyMixBufferCommand : ICommand {
20 /**
21 * Print this command's information to a string.
22 *
23 * @param processor - The CommandListProcessor processing this command.
24 * @param string - The string to print into.
25 */
26 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
27
28 /**
29 * Process this command.
30 *
31 * @param processor - The CommandListProcessor processing this command.
32 */
33 void Process(const ADSP::CommandListProcessor& processor) override;
34
35 /**
36 * Verify this command's data is valid.
37 *
38 * @param processor - The CommandListProcessor processing this command.
39 * @return True if the command is valid, otherwise false.
40 */
41 bool Verify(const ADSP::CommandListProcessor& processor) override;
42
43 /// Input mix buffer index
44 s16 input_index;
45 /// Output mix buffer index
46 s16 output_index;
47};
48
49} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp
new file mode 100644
index 000000000..c2bc10061
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.cpp
@@ -0,0 +1,64 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/common/common.h"
5#include "audio_core/renderer/adsp/command_list_processor.h"
6#include "audio_core/renderer/command/mix/depop_for_mix_buffers.h"
7
8namespace AudioCore::AudioRenderer {
9/**
10 * Apply depopping. Add the depopped sample to each incoming new sample, decaying it each time
11 * according to decay.
12 *
13 * @param output - Output buffer to be depopped.
14 * @param depop_sample - Depopped sample to apply to output samples.
15 * @param decay_ - Amount to decay the depopped sample for every output sample.
16 * @param sample_count - Samples to process.
17 * @return Final decayed depop sample.
18 */
19static s32 ApplyDepopMix(std::span<s32> output, const s32 depop_sample,
20 Common::FixedPoint<49, 15>& decay_, const u32 sample_count) {
21 auto sample{std::abs(depop_sample)};
22 auto decay{decay_.to_raw()};
23
24 if (depop_sample <= 0) {
25 for (u32 i = 0; i < sample_count; i++) {
26 sample = static_cast<s32>((static_cast<s64>(sample) * decay) >> 15);
27 output[i] -= sample;
28 }
29 return -sample;
30 } else {
31 for (u32 i = 0; i < sample_count; i++) {
32 sample = static_cast<s32>((static_cast<s64>(sample) * decay) >> 15);
33 output[i] += sample;
34 }
35 return sample;
36 }
37}
38
39void DepopForMixBuffersCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
40 std::string& string) {
41 string += fmt::format("DepopForMixBuffersCommand\n\tinput {:02X} count {} decay {}\n", input,
42 count, decay.to_float());
43}
44
45void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& processor) {
46 auto end_index{std::min(processor.buffer_count, input + count)};
47 std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index};
48
49 for (u32 index = input; index < end_index; index++) {
50 const auto depop_sample{depop_buff[index]};
51 if (depop_sample != 0) {
52 auto input_buffer{processor.mix_buffers.subspan(index * processor.sample_count,
53 processor.sample_count)};
54 depop_buff[index] =
55 ApplyDepopMix(input_buffer, depop_sample, decay, processor.sample_count);
56 }
57 }
58}
59
60bool DepopForMixBuffersCommand::Verify(const ADSP::CommandListProcessor& processor) {
61 return true;
62}
63
64} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h
new file mode 100644
index 000000000..e7268ff27
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/depop_for_mix_buffers.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10#include "common/fixed_point.h"
11
12namespace AudioCore::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor;
15}
16
17/**
18 * AudioRenderer command for depopping a mix buffer.
19 * Adds a cumulation of previous samples to the current mix buffer with a decay.
20 */
21struct DepopForMixBuffersCommand : ICommand {
22 /**
23 * Print this command's information to a string.
24 *
25 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into.
27 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
29
30 /**
31 * Process this command.
32 *
33 * @param processor - The CommandListProcessor processing this command.
34 */
35 void Process(const ADSP::CommandListProcessor& processor) override;
36
37 /**
38 * Verify this command's data is valid.
39 *
40 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false.
42 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override;
44
45 /// Starting input mix buffer index
46 u32 input;
47 /// Number of mix buffers to depop
48 u32 count;
49 /// Amount to decay the depop sample for each new sample
50 Common::FixedPoint<49, 15> decay;
51 /// Address of the depop buffer, holding the last sample for every mix buffer
52 CpuAddr depop_buffer;
53};
54
55} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/depop_prepare.cpp b/src/audio_core/renderer/command/mix/depop_prepare.cpp
new file mode 100644
index 000000000..2ee076ef6
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/depop_prepare.cpp
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/mix/depop_prepare.h"
6#include "audio_core/renderer/voice/voice_state.h"
7#include "common/fixed_point.h"
8
9namespace AudioCore::AudioRenderer {
10
11void DepopPrepareCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
12 std::string& string) {
13 string += fmt::format("DepopPrepareCommand\n\tinputs: ");
14 for (u32 i = 0; i < buffer_count; i++) {
15 string += fmt::format("{:02X}, ", inputs[i]);
16 }
17 string += "\n";
18}
19
20void DepopPrepareCommand::Process(const ADSP::CommandListProcessor& processor) {
21 auto samples{reinterpret_cast<s32*>(previous_samples)};
22 auto buffer{std::span(reinterpret_cast<s32*>(depop_buffer), buffer_count)};
23
24 for (u32 i = 0; i < buffer_count; i++) {
25 if (samples[i]) {
26 buffer[inputs[i]] += samples[i];
27 samples[i] = 0;
28 }
29 }
30}
31
32bool DepopPrepareCommand::Verify(const ADSP::CommandListProcessor& processor) {
33 return true;
34}
35
36} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/depop_prepare.h b/src/audio_core/renderer/command/mix/depop_prepare.h
new file mode 100644
index 000000000..a5465da9a
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/depop_prepare.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for preparing depop.
18 * Adds the previusly output last samples to the depop buffer.
19 */
20struct DepopPrepareCommand : ICommand {
21 /**
22 * Print this command's information to a string.
23 *
24 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into.
26 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
28
29 /**
30 * Process this command.
31 *
32 * @param processor - The CommandListProcessor processing this command.
33 */
34 void Process(const ADSP::CommandListProcessor& processor) override;
35
36 /**
37 * Verify this command's data is valid.
38 *
39 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false.
41 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override;
43
44 /// Depop buffer offset for each mix buffer
45 std::array<s16, MaxMixBuffers> inputs;
46 /// Pointer to the previous mix buffer samples
47 CpuAddr previous_samples;
48 /// Number of mix buffers to use
49 u32 buffer_count;
50 /// Pointer to the current depop values
51 CpuAddr depop_buffer;
52};
53
54} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/mix.cpp b/src/audio_core/renderer/command/mix/mix.cpp
new file mode 100644
index 000000000..8ecf9b05a
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/mix.cpp
@@ -0,0 +1,70 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <limits>
6#include <span>
7
8#include "audio_core/renderer/adsp/command_list_processor.h"
9#include "audio_core/renderer/command/mix/mix.h"
10#include "common/fixed_point.h"
11
12namespace AudioCore::AudioRenderer {
13/**
14 * Mix input mix buffer into output mix buffer, with volume applied to the input.
15 *
16 * @tparam Q - Number of bits for fixed point operations.
17 * @param output - Output mix buffer.
18 * @param input - Input mix buffer.
19 * @param volume - Volume applied to the input.
20 * @param sample_count - Number of samples to process.
21 */
22template <size_t Q>
23static void ApplyMix(std::span<s32> output, std::span<const s32> input, const f32 volume_,
24 const u32 sample_count) {
25 const Common::FixedPoint<64 - Q, Q> volume{volume_};
26 for (u32 i = 0; i < sample_count; i++) {
27 output[i] = (output[i] + input[i] * volume).to_int();
28 }
29}
30
31void MixCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
32 std::string& string) {
33 string += fmt::format("MixCommand");
34 string += fmt::format("\n\tinput {:02X}", input_index);
35 string += fmt::format("\n\toutput {:02X}", output_index);
36 string += fmt::format("\n\tvolume {:.8f}", volume);
37 string += "\n";
38}
39
40void MixCommand::Process(const ADSP::CommandListProcessor& processor) {
41 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
42 processor.sample_count)};
43 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
44 processor.sample_count)};
45
46 // If volume is 0, nothing will be added to the output, so just skip.
47 if (volume == 0.0f) {
48 return;
49 }
50
51 switch (precision) {
52 case 15:
53 ApplyMix<15>(output, input, volume, processor.sample_count);
54 break;
55
56 case 23:
57 ApplyMix<23>(output, input, volume, processor.sample_count);
58 break;
59
60 default:
61 LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
62 break;
63 }
64}
65
66bool MixCommand::Verify(const ADSP::CommandListProcessor& processor) {
67 return true;
68}
69
70} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/mix.h b/src/audio_core/renderer/command/mix/mix.h
new file mode 100644
index 000000000..0201cf171
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/mix.h
@@ -0,0 +1,54 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume
18 * applied to the input.
19 */
20struct MixCommand : ICommand {
21 /**
22 * Print this command's information to a string.
23 *
24 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into.
26 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
28
29 /**
30 * Process this command.
31 *
32 * @param processor - The CommandListProcessor processing this command.
33 */
34 void Process(const ADSP::CommandListProcessor& processor) override;
35
36 /**
37 * Verify this command's data is valid.
38 *
39 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false.
41 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override;
43
44 /// Fixed point precision
45 u8 precision;
46 /// Input mix buffer index
47 s16 input_index;
48 /// Output mix buffer index
49 s16 output_index;
50 /// Mix volume applied to the input
51 f32 volume;
52};
53
54} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/mix_ramp.cpp b/src/audio_core/renderer/command/mix/mix_ramp.cpp
new file mode 100644
index 000000000..ffdafa1c8
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/mix_ramp.cpp
@@ -0,0 +1,94 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/mix/mix_ramp.h"
6#include "common/fixed_point.h"
7#include "common/logging/log.h"
8
9namespace AudioCore::AudioRenderer {
10/**
11 * Mix input mix buffer into output mix buffer, with volume applied to the input.
12 *
13 * @tparam Q - Number of bits for fixed point operations.
14 * @param output - Output mix buffer.
15 * @param input - Input mix buffer.
16 * @param volume - Volume applied to the input.
17 * @param ramp - Ramp applied to volume every sample.
18 * @param sample_count - Number of samples to process.
19 * @return The final gained input sample, used for depopping.
20 */
21template <size_t Q>
22s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_,
23 const f32 ramp_, const u32 sample_count) {
24 Common::FixedPoint<64 - Q, Q> volume{volume_};
25 Common::FixedPoint<64 - Q, Q> sample{0};
26
27 if (ramp_ == 0.0f) {
28 for (u32 i = 0; i < sample_count; i++) {
29 sample = input[i] * volume;
30 output[i] = (output[i] + sample).to_int();
31 }
32 } else {
33 Common::FixedPoint<64 - Q, Q> ramp{ramp_};
34 for (u32 i = 0; i < sample_count; i++) {
35 sample = input[i] * volume;
36 output[i] = (output[i] + sample).to_int();
37 volume += ramp;
38 }
39 }
40 return sample.to_int();
41}
42
43template s32 ApplyMixRamp<15>(std::span<s32>, std::span<const s32>, const f32, const f32,
44 const u32);
45template s32 ApplyMixRamp<23>(std::span<s32>, std::span<const s32>, const f32, const f32,
46 const u32);
47
48void MixRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) {
49 const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
50 string += fmt::format("MixRampCommand");
51 string += fmt::format("\n\tinput {:02X}", input_index);
52 string += fmt::format("\n\toutput {:02X}", output_index);
53 string += fmt::format("\n\tvolume {:.8f}", volume);
54 string += fmt::format("\n\tprev_volume {:.8f}", prev_volume);
55 string += fmt::format("\n\tramp {:.8f}", ramp);
56 string += "\n";
57}
58
59void MixRampCommand::Process(const ADSP::CommandListProcessor& processor) {
60 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
61 processor.sample_count)};
62 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
63 processor.sample_count)};
64 const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
65 auto prev_sample_ptr{reinterpret_cast<s32*>(previous_sample)};
66
67 // If previous volume and ramp are both 0, nothing will be added to the output, so just skip.
68 if (prev_volume == 0.0f && ramp == 0.0f) {
69 *prev_sample_ptr = 0;
70 return;
71 }
72
73 switch (precision) {
74 case 15:
75 *prev_sample_ptr =
76 ApplyMixRamp<15>(output, input, prev_volume, ramp, processor.sample_count);
77 break;
78
79 case 23:
80 *prev_sample_ptr =
81 ApplyMixRamp<23>(output, input, prev_volume, ramp, processor.sample_count);
82 break;
83
84 default:
85 LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
86 break;
87 }
88}
89
90bool MixRampCommand::Verify(const ADSP::CommandListProcessor& processor) {
91 return true;
92}
93
94} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/mix_ramp.h b/src/audio_core/renderer/command/mix/mix_ramp.h
new file mode 100644
index 000000000..770f57e80
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/mix_ramp.h
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7#include <string>
8
9#include "audio_core/renderer/command/icommand.h"
10#include "common/common_types.h"
11
12namespace AudioCore::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor;
15}
16
17/**
18 * AudioRenderer command for mixing an input mix buffer to an output mix buffer, with a volume
19 * applied to the input, and volume ramping to smooth out the transition.
20 */
21struct MixRampCommand : ICommand {
22 /**
23 * Print this command's information to a string.
24 *
25 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into.
27 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
29
30 /**
31 * Process this command.
32 *
33 * @param processor - The CommandListProcessor processing this command.
34 */
35 void Process(const ADSP::CommandListProcessor& processor) override;
36
37 /**
38 * Verify this command's data is valid.
39 *
40 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false.
42 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override;
44
45 /// Fixed point precision
46 u8 precision;
47 /// Input mix buffer index
48 s16 input_index;
49 /// Output mix buffer index
50 s16 output_index;
51 /// Previous mix volume
52 f32 prev_volume;
53 /// Current mix volume
54 f32 volume;
55 /// Pointer to the previous sample buffer, used for depopping
56 CpuAddr previous_sample;
57};
58
59/**
60 * Mix input mix buffer into output mix buffer, with volume applied to the input.
61 * @tparam Q - Number of bits for fixed point operations.
62 * @param output - Output mix buffer.
63 * @param input - Input mix buffer.
64 * @param volume - Volume applied to the input.
65 * @param ramp - Ramp applied to volume every sample.
66 * @param sample_count - Number of samples to process.
67 * @return The final gained input sample, used for depopping.
68 */
69template <size_t Q>
70s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, const f32 volume_,
71 const f32 ramp_, const u32 sample_count);
72
73} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp b/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp
new file mode 100644
index 000000000..43dbef9fc
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.cpp
@@ -0,0 +1,65 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/mix/mix_ramp.h"
6#include "audio_core/renderer/command/mix/mix_ramp_grouped.h"
7
8namespace AudioCore::AudioRenderer {
9
10void MixRampGroupedCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) {
11 string += "MixRampGroupedCommand";
12 for (u32 i = 0; i < buffer_count; i++) {
13 string += fmt::format("\n\t{}", i);
14 const auto ramp{(volumes[i] - prev_volumes[i]) / static_cast<f32>(processor.sample_count)};
15 string += fmt::format("\n\t\tinput {:02X}", inputs[i]);
16 string += fmt::format("\n\t\toutput {:02X}", outputs[i]);
17 string += fmt::format("\n\t\tvolume {:.8f}", volumes[i]);
18 string += fmt::format("\n\t\tprev_volume {:.8f}", prev_volumes[i]);
19 string += fmt::format("\n\t\tramp {:.8f}", ramp);
20 string += "\n";
21 }
22}
23
24void MixRampGroupedCommand::Process(const ADSP::CommandListProcessor& processor) {
25 std::span<s32> prev_samples = {reinterpret_cast<s32*>(previous_samples), MaxMixBuffers};
26
27 for (u32 i = 0; i < buffer_count; i++) {
28 auto last_sample{0};
29 if (prev_volumes[i] != 0.0f || volumes[i] != 0.0f) {
30 const auto output{processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
31 processor.sample_count)};
32 const auto input{processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
33 processor.sample_count)};
34 const auto ramp{(volumes[i] - prev_volumes[i]) /
35 static_cast<f32>(processor.sample_count)};
36
37 if (prev_volumes[i] == 0.0f && ramp == 0.0f) {
38 prev_samples[i] = 0;
39 continue;
40 }
41
42 switch (precision) {
43 case 15:
44 last_sample =
45 ApplyMixRamp<15>(output, input, prev_volumes[i], ramp, processor.sample_count);
46 break;
47 case 23:
48 last_sample =
49 ApplyMixRamp<23>(output, input, prev_volumes[i], ramp, processor.sample_count);
50 break;
51 default:
52 LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
53 break;
54 }
55 }
56
57 prev_samples[i] = last_sample;
58 }
59}
60
61bool MixRampGroupedCommand::Verify(const ADSP::CommandListProcessor& processor) {
62 return true;
63}
64
65} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/mix_ramp_grouped.h b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h
new file mode 100644
index 000000000..027276e5a
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/mix_ramp_grouped.h
@@ -0,0 +1,61 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <string>
8
9#include "audio_core/renderer/command/icommand.h"
10#include "common/common_types.h"
11
12namespace AudioCore::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor;
15}
16
17/**
18 * AudioRenderer command for mixing multiple input mix buffers to multiple output mix buffers, with
19 * a volume applied to the input, and volume ramping to smooth out the transition.
20 */
21struct MixRampGroupedCommand : ICommand {
22 /**
23 * Print this command's information to a string.
24 *
25 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into.
27 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
29
30 /**
31 * Process this command.
32 *
33 * @param processor - The CommandListProcessor processing this command.
34 */
35 void Process(const ADSP::CommandListProcessor& processor) override;
36
37 /**
38 * Verify this command's data is valid.
39 *
40 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false.
42 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override;
44
45 /// Fixed point precision
46 u8 precision;
47 /// Number of mix buffers to mix
48 u32 buffer_count;
49 /// Input mix buffer indexes for each mix buffer
50 std::array<s16, MaxMixBuffers> inputs;
51 /// Output mix buffer indexes for each mix buffer
52 std::array<s16, MaxMixBuffers> outputs;
53 /// Previous mix vloumes for each mix buffer
54 std::array<f32, MaxMixBuffers> prev_volumes;
55 /// Current mix vloumes for each mix buffer
56 std::array<f32, MaxMixBuffers> volumes;
57 /// Pointer to the previous sample buffer, used for depop
58 CpuAddr previous_samples;
59};
60
61} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/volume.cpp b/src/audio_core/renderer/command/mix/volume.cpp
new file mode 100644
index 000000000..b045fb062
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/volume.cpp
@@ -0,0 +1,72 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/mix/volume.h"
6#include "common/fixed_point.h"
7#include "common/logging/log.h"
8
9namespace AudioCore::AudioRenderer {
10/**
11 * Apply volume to the input mix buffer, saving to the output buffer.
12 *
13 * @tparam Q - Number of bits for fixed point operations.
14 * @param output - Output mix buffer.
15 * @param input - Input mix buffer.
16 * @param volume - Volume applied to the input.
17 * @param sample_count - Number of samples to process.
18 */
19template <size_t Q>
20static void ApplyUniformGain(std::span<s32> output, std::span<const s32> input, const f32 volume,
21 const u32 sample_count) {
22 if (volume == 1.0f) {
23 std::memcpy(output.data(), input.data(), input.size_bytes());
24 } else {
25 const Common::FixedPoint<64 - Q, Q> gain{volume};
26 for (u32 i = 0; i < sample_count; i++) {
27 output[i] = (input[i] * gain).to_int();
28 }
29 }
30}
31
32void VolumeCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
33 std::string& string) {
34 string += fmt::format("VolumeCommand");
35 string += fmt::format("\n\tinput {:02X}", input_index);
36 string += fmt::format("\n\toutput {:02X}", output_index);
37 string += fmt::format("\n\tvolume {:.8f}", volume);
38 string += "\n";
39}
40
41void VolumeCommand::Process(const ADSP::CommandListProcessor& processor) {
42 // If input and output buffers are the same, and the volume is 1.0f, this won't do
43 // anything, so just skip.
44 if (input_index == output_index && volume == 1.0f) {
45 return;
46 }
47
48 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
49 processor.sample_count)};
50 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
51 processor.sample_count)};
52
53 switch (precision) {
54 case 15:
55 ApplyUniformGain<15>(output, input, volume, processor.sample_count);
56 break;
57
58 case 23:
59 ApplyUniformGain<23>(output, input, volume, processor.sample_count);
60 break;
61
62 default:
63 LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
64 break;
65 }
66}
67
68bool VolumeCommand::Verify(const ADSP::CommandListProcessor& processor) {
69 return true;
70}
71
72} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/volume.h b/src/audio_core/renderer/command/mix/volume.h
new file mode 100644
index 000000000..6ae9fb794
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/volume.h
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for applying volume to a mix buffer.
18 */
19struct VolumeCommand : ICommand {
20 /**
21 * Print this command's information to a string.
22 *
23 * @param processor - The CommandListProcessor processing this command.
24 * @param string - The string to print into.
25 */
26 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
27
28 /**
29 * Process this command.
30 *
31 * @param processor - The CommandListProcessor processing this command.
32 */
33 void Process(const ADSP::CommandListProcessor& processor) override;
34
35 /**
36 * Verify this command's data is valid.
37 *
38 * @param processor - The CommandListProcessor processing this command.
39 * @return True if the command is valid, otherwise false.
40 */
41 bool Verify(const ADSP::CommandListProcessor& processor) override;
42
43 /// Fixed point precision
44 u8 precision;
45 /// Input mix buffer index
46 s16 input_index;
47 /// Output mix buffer index
48 s16 output_index;
49 /// Mix volume applied to the input
50 f32 volume;
51};
52
53} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/volume_ramp.cpp b/src/audio_core/renderer/command/mix/volume_ramp.cpp
new file mode 100644
index 000000000..424307148
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/volume_ramp.cpp
@@ -0,0 +1,84 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/mix/volume_ramp.h"
6#include "common/fixed_point.h"
7
8namespace AudioCore::AudioRenderer {
9/**
10 * Apply volume with ramping to the input mix buffer, saving to the output buffer.
11 *
12 * @tparam Q - Number of bits for fixed point operations.
13 * @param output - Output mix buffers.
14 * @param input - Input mix buffers.
15 * @param volume - Volume applied to the input.
16 * @param ramp - Ramp applied to volume every sample.
17 * @param sample_count - Number of samples to process.
18 */
19template <size_t Q>
20static void ApplyLinearEnvelopeGain(std::span<s32> output, std::span<const s32> input,
21 const f32 volume, const f32 ramp_, const u32 sample_count) {
22 if (volume == 0.0f && ramp_ == 0.0f) {
23 std::memset(output.data(), 0, output.size_bytes());
24 } else if (volume == 1.0f && ramp_ == 0.0f) {
25 std::memcpy(output.data(), input.data(), output.size_bytes());
26 } else if (ramp_ == 0.0f) {
27 const Common::FixedPoint<64 - Q, Q> gain{volume};
28 for (u32 i = 0; i < sample_count; i++) {
29 output[i] = (input[i] * gain).to_int();
30 }
31 } else {
32 Common::FixedPoint<64 - Q, Q> gain{volume};
33 const Common::FixedPoint<64 - Q, Q> ramp{ramp_};
34 for (u32 i = 0; i < sample_count; i++) {
35 output[i] = (input[i] * gain).to_int();
36 gain += ramp;
37 }
38 }
39}
40
41void VolumeRampCommand::Dump(const ADSP::CommandListProcessor& processor, std::string& string) {
42 const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
43 string += fmt::format("VolumeRampCommand");
44 string += fmt::format("\n\tinput {:02X}", input_index);
45 string += fmt::format("\n\toutput {:02X}", output_index);
46 string += fmt::format("\n\tvolume {:.8f}", volume);
47 string += fmt::format("\n\tprev_volume {:.8f}", prev_volume);
48 string += fmt::format("\n\tramp {:.8f}", ramp);
49 string += "\n";
50}
51
52void VolumeRampCommand::Process(const ADSP::CommandListProcessor& processor) {
53 auto output{processor.mix_buffers.subspan(output_index * processor.sample_count,
54 processor.sample_count)};
55 auto input{processor.mix_buffers.subspan(input_index * processor.sample_count,
56 processor.sample_count)};
57 const auto ramp{(volume - prev_volume) / static_cast<f32>(processor.sample_count)};
58
59 // If input and output buffers are the same, and the volume is 1.0f, and there's no ramping,
60 // this won't do anything, so just skip.
61 if (input_index == output_index && prev_volume == 1.0f && ramp == 0.0f) {
62 return;
63 }
64
65 switch (precision) {
66 case 15:
67 ApplyLinearEnvelopeGain<15>(output, input, prev_volume, ramp, processor.sample_count);
68 break;
69
70 case 23:
71 ApplyLinearEnvelopeGain<23>(output, input, prev_volume, ramp, processor.sample_count);
72 break;
73
74 default:
75 LOG_ERROR(Service_Audio, "Invalid precision {}", precision);
76 break;
77 }
78}
79
80bool VolumeRampCommand::Verify(const ADSP::CommandListProcessor& processor) {
81 return true;
82}
83
84} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/mix/volume_ramp.h b/src/audio_core/renderer/command/mix/volume_ramp.h
new file mode 100644
index 000000000..77b61547e
--- /dev/null
+++ b/src/audio_core/renderer/command/mix/volume_ramp.h
@@ -0,0 +1,56 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for applying volume to a mix buffer, with ramping for the volume to smooth
18 * out the transition.
19 */
20struct VolumeRampCommand : ICommand {
21 /**
22 * Print this command's information to a string.
23 *
24 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into.
26 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
28
29 /**
30 * Process this command.
31 *
32 * @param processor - The CommandListProcessor processing this command.
33 */
34 void Process(const ADSP::CommandListProcessor& processor) override;
35
36 /**
37 * Verify this command's data is valid.
38 *
39 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false.
41 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override;
43
44 /// Fixed point precision
45 u8 precision;
46 /// Input mix buffer index
47 s16 input_index;
48 /// Output mix buffer index
49 s16 output_index;
50 /// Previous mix volume applied to the input
51 f32 prev_volume;
52 /// Current mix volume applied to the input
53 f32 volume;
54};
55
56} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/performance/performance.cpp b/src/audio_core/renderer/command/performance/performance.cpp
new file mode 100644
index 000000000..985958b03
--- /dev/null
+++ b/src/audio_core/renderer/command/performance/performance.cpp
@@ -0,0 +1,43 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/performance/performance.h"
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9
10namespace AudioCore::AudioRenderer {
11
12void PerformanceCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
13 std::string& string) {
14 string += fmt::format("PerformanceCommand\n\tstate {}\n", static_cast<u32>(state));
15}
16
17void PerformanceCommand::Process(const ADSP::CommandListProcessor& processor) {
18 auto base{entry_address.translated_address};
19 if (state == PerformanceState::Start) {
20 auto start_time_ptr{reinterpret_cast<u32*>(base + entry_address.entry_start_time_offset)};
21 *start_time_ptr = static_cast<u32>(
22 Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() -
23 processor.start_time - processor.current_processing_time)
24 .count());
25 } else if (state == PerformanceState::Stop) {
26 auto processed_time_ptr{
27 reinterpret_cast<u32*>(base + entry_address.entry_processed_time_offset)};
28 auto entry_count_ptr{
29 reinterpret_cast<u32*>(base + entry_address.header_entry_count_offset)};
30
31 *processed_time_ptr = static_cast<u32>(
32 Core::Timing::CyclesToUs(processor.system->CoreTiming().GetClockTicks() -
33 processor.start_time - processor.current_processing_time)
34 .count());
35 (*entry_count_ptr)++;
36 }
37}
38
39bool PerformanceCommand::Verify(const ADSP::CommandListProcessor& processor) {
40 return true;
41}
42
43} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/performance/performance.h b/src/audio_core/renderer/command/performance/performance.h
new file mode 100644
index 000000000..11a7d6c08
--- /dev/null
+++ b/src/audio_core/renderer/command/performance/performance.h
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "audio_core/renderer/performance/performance_entry_addresses.h"
10#include "audio_core/renderer/performance/performance_manager.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for writing AudioRenderer performance metrics back to the sysmodule.
20 */
21struct PerformanceCommand : ICommand {
22 /**
23 * Print this command's information to a string.
24 *
25 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into.
27 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
29
30 /**
31 * Process this command.
32 *
33 * @param processor - The CommandListProcessor processing this command.
34 */
35 void Process(const ADSP::CommandListProcessor& processor) override;
36
37 /**
38 * Verify this command's data is valid.
39 *
40 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false.
42 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override;
44
45 /// State of the performance
46 PerformanceState state;
47 /// Pointers to be written
48 PerformanceEntryAddresses entry_address;
49};
50
51} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp
new file mode 100644
index 000000000..1fd90308a
--- /dev/null
+++ b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.cpp
@@ -0,0 +1,74 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/resample/downmix_6ch_to_2ch.h"
6
7namespace AudioCore::AudioRenderer {
8
9void DownMix6chTo2chCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
10 std::string& string) {
11 string += fmt::format("DownMix6chTo2chCommand\n\tinputs: ");
12 for (u32 i = 0; i < MaxChannels; i++) {
13 string += fmt::format("{:02X}, ", inputs[i]);
14 }
15 string += "\n\toutputs: ";
16 for (u32 i = 0; i < MaxChannels; i++) {
17 string += fmt::format("{:02X}, ", outputs[i]);
18 }
19 string += "\n";
20}
21
22void DownMix6chTo2chCommand::Process(const ADSP::CommandListProcessor& processor) {
23 auto in_front_left{
24 processor.mix_buffers.subspan(inputs[0] * processor.sample_count, processor.sample_count)};
25 auto in_front_right{
26 processor.mix_buffers.subspan(inputs[1] * processor.sample_count, processor.sample_count)};
27 auto in_center{
28 processor.mix_buffers.subspan(inputs[2] * processor.sample_count, processor.sample_count)};
29 auto in_lfe{
30 processor.mix_buffers.subspan(inputs[3] * processor.sample_count, processor.sample_count)};
31 auto in_back_left{
32 processor.mix_buffers.subspan(inputs[4] * processor.sample_count, processor.sample_count)};
33 auto in_back_right{
34 processor.mix_buffers.subspan(inputs[5] * processor.sample_count, processor.sample_count)};
35
36 auto out_front_left{
37 processor.mix_buffers.subspan(outputs[0] * processor.sample_count, processor.sample_count)};
38 auto out_front_right{
39 processor.mix_buffers.subspan(outputs[1] * processor.sample_count, processor.sample_count)};
40 auto out_center{
41 processor.mix_buffers.subspan(outputs[2] * processor.sample_count, processor.sample_count)};
42 auto out_lfe{
43 processor.mix_buffers.subspan(outputs[3] * processor.sample_count, processor.sample_count)};
44 auto out_back_left{
45 processor.mix_buffers.subspan(outputs[4] * processor.sample_count, processor.sample_count)};
46 auto out_back_right{
47 processor.mix_buffers.subspan(outputs[5] * processor.sample_count, processor.sample_count)};
48
49 for (u32 i = 0; i < processor.sample_count; i++) {
50 const auto left_sample{(in_front_left[i] * down_mix_coeff[0] +
51 in_center[i] * down_mix_coeff[1] + in_lfe[i] * down_mix_coeff[2] +
52 in_back_left[i] * down_mix_coeff[3])
53 .to_int()};
54
55 const auto right_sample{(in_front_right[i] * down_mix_coeff[0] +
56 in_center[i] * down_mix_coeff[1] + in_lfe[i] * down_mix_coeff[2] +
57 in_back_right[i] * down_mix_coeff[3])
58 .to_int()};
59
60 out_front_left[i] = left_sample;
61 out_front_right[i] = right_sample;
62 }
63
64 std::memset(out_center.data(), 0, out_center.size_bytes());
65 std::memset(out_lfe.data(), 0, out_lfe.size_bytes());
66 std::memset(out_back_left.data(), 0, out_back_left.size_bytes());
67 std::memset(out_back_right.data(), 0, out_back_right.size_bytes());
68}
69
70bool DownMix6chTo2chCommand::Verify(const ADSP::CommandListProcessor& processor) {
71 return true;
72}
73
74} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h
new file mode 100644
index 000000000..dc133a73b
--- /dev/null
+++ b/src/audio_core/renderer/command/resample/downmix_6ch_to_2ch.h
@@ -0,0 +1,59 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10#include "common/fixed_point.h"
11
12namespace AudioCore::AudioRenderer {
13namespace ADSP {
14class CommandListProcessor;
15}
16
17/**
18 * AudioRenderer command for downmixing 6 channels to 2.
19 * Channel layout (SMPTE):
20 * 0 - front left
21 * 1 - front right
22 * 2 - center
23 * 3 - lfe
24 * 4 - back left
25 * 5 - back right
26 */
27struct DownMix6chTo2chCommand : ICommand {
28 /**
29 * Print this command's information to a string.
30 *
31 * @param processor - The CommandListProcessor processing this command.
32 * @param string - The string to print into.
33 */
34 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
35
36 /**
37 * Process this command.
38 *
39 * @param processor - The CommandListProcessor processing this command.
40 */
41 void Process(const ADSP::CommandListProcessor& processor) override;
42
43 /**
44 * Verify this command's data is valid.
45 *
46 * @param processor - The CommandListProcessor processing this command.
47 * @return True if the command is valid, otherwise false.
48 */
49 bool Verify(const ADSP::CommandListProcessor& processor) override;
50
51 /// Input mix buffer offsets for each channel
52 std::array<s16, MaxChannels> inputs;
53 /// Output mix buffer offsets for each channel
54 std::array<s16, MaxChannels> outputs;
55 /// Coefficients used for downmixing
56 std::array<Common::FixedPoint<48, 16>, 4> down_mix_coeff;
57};
58
59} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/resample/resample.cpp b/src/audio_core/renderer/command/resample/resample.cpp
new file mode 100644
index 000000000..070c9d2b8
--- /dev/null
+++ b/src/audio_core/renderer/command/resample/resample.cpp
@@ -0,0 +1,883 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/command/resample/resample.h"
5
6namespace AudioCore::AudioRenderer {
7
8static void ResampleLowQuality(std::span<s32> output, std::span<const s16> input,
9 const Common::FixedPoint<49, 15>& sample_rate_ratio,
10 Common::FixedPoint<49, 15>& fraction, const u32 samples_to_write) {
11 if (sample_rate_ratio == 1.0f) {
12 for (u32 i = 0; i < samples_to_write; i++) {
13 output[i] = input[i];
14 }
15 } else {
16 u32 read_index{0};
17 for (u32 i = 0; i < samples_to_write; i++) {
18 output[i] = input[read_index + (fraction >= 0.5f)];
19 fraction += sample_rate_ratio;
20 read_index += static_cast<u32>(fraction.to_int_floor());
21 fraction.clear_int();
22 }
23 }
24}
25
26static void ResampleNormalQuality(std::span<s32> output, std::span<const s16> input,
27 const Common::FixedPoint<49, 15>& sample_rate_ratio,
28 Common::FixedPoint<49, 15>& fraction,
29 const u32 samples_to_write) {
30 static constexpr std::array<f32, 512> lut0 = {
31 0.20141602f, 0.59283447f, 0.20513916f, 0.00009155f, 0.19772339f, 0.59277344f, 0.20889282f,
32 0.00027466f, 0.19406128f, 0.59262085f, 0.21264648f, 0.00045776f, 0.19039917f, 0.59240723f,
33 0.21646118f, 0.00067139f, 0.18679810f, 0.59213257f, 0.22030640f, 0.00085449f, 0.18322754f,
34 0.59176636f, 0.22415161f, 0.00103760f, 0.17968750f, 0.59133911f, 0.22802734f, 0.00125122f,
35 0.17617798f, 0.59085083f, 0.23193359f, 0.00146484f, 0.17269897f, 0.59027100f, 0.23583984f,
36 0.00167847f, 0.16925049f, 0.58963013f, 0.23977661f, 0.00189209f, 0.16583252f, 0.58892822f,
37 0.24374390f, 0.00210571f, 0.16244507f, 0.58816528f, 0.24774170f, 0.00234985f, 0.15908813f,
38 0.58731079f, 0.25173950f, 0.00256348f, 0.15576172f, 0.58639526f, 0.25576782f, 0.00280762f,
39 0.15249634f, 0.58541870f, 0.25979614f, 0.00308228f, 0.14923096f, 0.58435059f, 0.26385498f,
40 0.00332642f, 0.14602661f, 0.58325195f, 0.26794434f, 0.00360107f, 0.14285278f, 0.58206177f,
41 0.27203369f, 0.00387573f, 0.13973999f, 0.58078003f, 0.27612305f, 0.00418091f, 0.13662720f,
42 0.57946777f, 0.28024292f, 0.00448608f, 0.13357544f, 0.57806396f, 0.28436279f, 0.00479126f,
43 0.13052368f, 0.57662964f, 0.28851318f, 0.00512695f, 0.12753296f, 0.57510376f, 0.29266357f,
44 0.00546265f, 0.12460327f, 0.57351685f, 0.29681396f, 0.00579834f, 0.12167358f, 0.57183838f,
45 0.30099487f, 0.00616455f, 0.11880493f, 0.57012939f, 0.30517578f, 0.00656128f, 0.11596680f,
46 0.56835938f, 0.30935669f, 0.00695801f, 0.11318970f, 0.56649780f, 0.31353760f, 0.00735474f,
47 0.11041260f, 0.56457520f, 0.31771851f, 0.00778198f, 0.10769653f, 0.56262207f, 0.32192993f,
48 0.00823975f, 0.10501099f, 0.56057739f, 0.32614136f, 0.00869751f, 0.10238647f, 0.55847168f,
49 0.33032227f, 0.00915527f, 0.09976196f, 0.55633545f, 0.33453369f, 0.00967407f, 0.09722900f,
50 0.55410767f, 0.33874512f, 0.01019287f, 0.09469604f, 0.55181885f, 0.34295654f, 0.01071167f,
51 0.09222412f, 0.54949951f, 0.34713745f, 0.01126099f, 0.08978271f, 0.54708862f, 0.35134888f,
52 0.01184082f, 0.08737183f, 0.54464722f, 0.35552979f, 0.01245117f, 0.08499146f, 0.54214478f,
53 0.35974121f, 0.01306152f, 0.08267212f, 0.53958130f, 0.36392212f, 0.01370239f, 0.08041382f,
54 0.53695679f, 0.36810303f, 0.01437378f, 0.07815552f, 0.53427124f, 0.37225342f, 0.01507568f,
55 0.07595825f, 0.53155518f, 0.37640381f, 0.01577759f, 0.07379150f, 0.52877808f, 0.38055420f,
56 0.01651001f, 0.07165527f, 0.52593994f, 0.38470459f, 0.01727295f, 0.06958008f, 0.52307129f,
57 0.38882446f, 0.01806641f, 0.06753540f, 0.52014160f, 0.39294434f, 0.01889038f, 0.06552124f,
58 0.51715088f, 0.39703369f, 0.01974487f, 0.06356812f, 0.51409912f, 0.40112305f, 0.02059937f,
59 0.06164551f, 0.51101685f, 0.40518188f, 0.02148438f, 0.05975342f, 0.50790405f, 0.40921021f,
60 0.02243042f, 0.05789185f, 0.50473022f, 0.41323853f, 0.02337646f, 0.05609131f, 0.50152588f,
61 0.41726685f, 0.02435303f, 0.05432129f, 0.49826050f, 0.42123413f, 0.02539062f, 0.05258179f,
62 0.49493408f, 0.42520142f, 0.02642822f, 0.05087280f, 0.49160767f, 0.42913818f, 0.02749634f,
63 0.04922485f, 0.48822021f, 0.43307495f, 0.02859497f, 0.04760742f, 0.48477173f, 0.43695068f,
64 0.02975464f, 0.04602051f, 0.48132324f, 0.44082642f, 0.03091431f, 0.04446411f, 0.47781372f,
65 0.44467163f, 0.03210449f, 0.04293823f, 0.47424316f, 0.44845581f, 0.03335571f, 0.04147339f,
66 0.47067261f, 0.45223999f, 0.03460693f, 0.04003906f, 0.46704102f, 0.45599365f, 0.03591919f,
67 0.03863525f, 0.46340942f, 0.45971680f, 0.03726196f, 0.03726196f, 0.45971680f, 0.46340942f,
68 0.03863525f, 0.03591919f, 0.45599365f, 0.46704102f, 0.04003906f, 0.03460693f, 0.45223999f,
69 0.47067261f, 0.04147339f, 0.03335571f, 0.44845581f, 0.47424316f, 0.04293823f, 0.03210449f,
70 0.44467163f, 0.47781372f, 0.04446411f, 0.03091431f, 0.44082642f, 0.48132324f, 0.04602051f,
71 0.02975464f, 0.43695068f, 0.48477173f, 0.04760742f, 0.02859497f, 0.43307495f, 0.48822021f,
72 0.04922485f, 0.02749634f, 0.42913818f, 0.49160767f, 0.05087280f, 0.02642822f, 0.42520142f,
73 0.49493408f, 0.05258179f, 0.02539062f, 0.42123413f, 0.49826050f, 0.05432129f, 0.02435303f,
74 0.41726685f, 0.50152588f, 0.05609131f, 0.02337646f, 0.41323853f, 0.50473022f, 0.05789185f,
75 0.02243042f, 0.40921021f, 0.50790405f, 0.05975342f, 0.02148438f, 0.40518188f, 0.51101685f,
76 0.06164551f, 0.02059937f, 0.40112305f, 0.51409912f, 0.06356812f, 0.01974487f, 0.39703369f,
77 0.51715088f, 0.06552124f, 0.01889038f, 0.39294434f, 0.52014160f, 0.06753540f, 0.01806641f,
78 0.38882446f, 0.52307129f, 0.06958008f, 0.01727295f, 0.38470459f, 0.52593994f, 0.07165527f,
79 0.01651001f, 0.38055420f, 0.52877808f, 0.07379150f, 0.01577759f, 0.37640381f, 0.53155518f,
80 0.07595825f, 0.01507568f, 0.37225342f, 0.53427124f, 0.07815552f, 0.01437378f, 0.36810303f,
81 0.53695679f, 0.08041382f, 0.01370239f, 0.36392212f, 0.53958130f, 0.08267212f, 0.01306152f,
82 0.35974121f, 0.54214478f, 0.08499146f, 0.01245117f, 0.35552979f, 0.54464722f, 0.08737183f,
83 0.01184082f, 0.35134888f, 0.54708862f, 0.08978271f, 0.01126099f, 0.34713745f, 0.54949951f,
84 0.09222412f, 0.01071167f, 0.34295654f, 0.55181885f, 0.09469604f, 0.01019287f, 0.33874512f,
85 0.55410767f, 0.09722900f, 0.00967407f, 0.33453369f, 0.55633545f, 0.09976196f, 0.00915527f,
86 0.33032227f, 0.55847168f, 0.10238647f, 0.00869751f, 0.32614136f, 0.56057739f, 0.10501099f,
87 0.00823975f, 0.32192993f, 0.56262207f, 0.10769653f, 0.00778198f, 0.31771851f, 0.56457520f,
88 0.11041260f, 0.00735474f, 0.31353760f, 0.56649780f, 0.11318970f, 0.00695801f, 0.30935669f,
89 0.56835938f, 0.11596680f, 0.00656128f, 0.30517578f, 0.57012939f, 0.11880493f, 0.00616455f,
90 0.30099487f, 0.57183838f, 0.12167358f, 0.00579834f, 0.29681396f, 0.57351685f, 0.12460327f,
91 0.00546265f, 0.29266357f, 0.57510376f, 0.12753296f, 0.00512695f, 0.28851318f, 0.57662964f,
92 0.13052368f, 0.00479126f, 0.28436279f, 0.57806396f, 0.13357544f, 0.00448608f, 0.28024292f,
93 0.57946777f, 0.13662720f, 0.00418091f, 0.27612305f, 0.58078003f, 0.13973999f, 0.00387573f,
94 0.27203369f, 0.58206177f, 0.14285278f, 0.00360107f, 0.26794434f, 0.58325195f, 0.14602661f,
95 0.00332642f, 0.26385498f, 0.58435059f, 0.14923096f, 0.00308228f, 0.25979614f, 0.58541870f,
96 0.15249634f, 0.00280762f, 0.25576782f, 0.58639526f, 0.15576172f, 0.00256348f, 0.25173950f,
97 0.58731079f, 0.15908813f, 0.00234985f, 0.24774170f, 0.58816528f, 0.16244507f, 0.00210571f,
98 0.24374390f, 0.58892822f, 0.16583252f, 0.00189209f, 0.23977661f, 0.58963013f, 0.16925049f,
99 0.00167847f, 0.23583984f, 0.59027100f, 0.17269897f, 0.00146484f, 0.23193359f, 0.59085083f,
100 0.17617798f, 0.00125122f, 0.22802734f, 0.59133911f, 0.17968750f, 0.00103760f, 0.22415161f,
101 0.59176636f, 0.18322754f, 0.00085449f, 0.22030640f, 0.59213257f, 0.18679810f, 0.00067139f,
102 0.21646118f, 0.59240723f, 0.19039917f, 0.00045776f, 0.21264648f, 0.59262085f, 0.19406128f,
103 0.00027466f, 0.20889282f, 0.59277344f, 0.19772339f, 0.00009155f, 0.20513916f, 0.59283447f,
104 0.20141602f,
105 };
106
107 static constexpr std::array<f32, 512> lut1 = {
108 0.00207520f, 0.99606323f, 0.00210571f, -0.00015259f, -0.00610352f, 0.99578857f,
109 0.00646973f, -0.00045776f, -0.01000977f, 0.99526978f, 0.01095581f, -0.00079346f,
110 -0.01373291f, 0.99444580f, 0.01562500f, -0.00109863f, -0.01733398f, 0.99337769f,
111 0.02041626f, -0.00143433f, -0.02075195f, 0.99203491f, 0.02539062f, -0.00177002f,
112 -0.02404785f, 0.99041748f, 0.03051758f, -0.00210571f, -0.02719116f, 0.98855591f,
113 0.03582764f, -0.00244141f, -0.03021240f, 0.98641968f, 0.04125977f, -0.00280762f,
114 -0.03308105f, 0.98400879f, 0.04687500f, -0.00314331f, -0.03579712f, 0.98135376f,
115 0.05261230f, -0.00350952f, -0.03839111f, 0.97842407f, 0.05856323f, -0.00390625f,
116 -0.04083252f, 0.97521973f, 0.06463623f, -0.00427246f, -0.04315186f, 0.97180176f,
117 0.07086182f, -0.00466919f, -0.04534912f, 0.96810913f, 0.07727051f, -0.00509644f,
118 -0.04742432f, 0.96414185f, 0.08383179f, -0.00549316f, -0.04934692f, 0.95996094f,
119 0.09054565f, -0.00592041f, -0.05114746f, 0.95550537f, 0.09741211f, -0.00637817f,
120 -0.05285645f, 0.95083618f, 0.10443115f, -0.00683594f, -0.05441284f, 0.94589233f,
121 0.11160278f, -0.00732422f, -0.05584717f, 0.94073486f, 0.11892700f, -0.00781250f,
122 -0.05718994f, 0.93533325f, 0.12643433f, -0.00830078f, -0.05841064f, 0.92968750f,
123 0.13406372f, -0.00881958f, -0.05953979f, 0.92382812f, 0.14184570f, -0.00936890f,
124 -0.06054688f, 0.91772461f, 0.14978027f, -0.00991821f, -0.06146240f, 0.91143799f,
125 0.15783691f, -0.01046753f, -0.06225586f, 0.90490723f, 0.16607666f, -0.01104736f,
126 -0.06295776f, 0.89816284f, 0.17443848f, -0.01165771f, -0.06356812f, 0.89120483f,
127 0.18292236f, -0.01229858f, -0.06408691f, 0.88403320f, 0.19155884f, -0.01293945f,
128 -0.06451416f, 0.87667847f, 0.20034790f, -0.01358032f, -0.06484985f, 0.86914062f,
129 0.20925903f, -0.01428223f, -0.06509399f, 0.86138916f, 0.21829224f, -0.01495361f,
130 -0.06527710f, 0.85345459f, 0.22744751f, -0.01568604f, -0.06536865f, 0.84533691f,
131 0.23675537f, -0.01641846f, -0.06536865f, 0.83703613f, 0.24615479f, -0.01718140f,
132 -0.06533813f, 0.82858276f, 0.25567627f, -0.01794434f, -0.06518555f, 0.81991577f,
133 0.26531982f, -0.01873779f, -0.06500244f, 0.81112671f, 0.27505493f, -0.01956177f,
134 -0.06472778f, 0.80215454f, 0.28491211f, -0.02038574f, -0.06442261f, 0.79306030f,
135 0.29489136f, -0.02124023f, -0.06402588f, 0.78378296f, 0.30496216f, -0.02209473f,
136 -0.06359863f, 0.77438354f, 0.31512451f, -0.02297974f, -0.06307983f, 0.76486206f,
137 0.32537842f, -0.02389526f, -0.06253052f, 0.75518799f, 0.33569336f, -0.02481079f,
138 -0.06195068f, 0.74539185f, 0.34613037f, -0.02575684f, -0.06130981f, 0.73547363f,
139 0.35662842f, -0.02670288f, -0.06060791f, 0.72543335f, 0.36721802f, -0.02767944f,
140 -0.05987549f, 0.71527100f, 0.37786865f, -0.02865601f, -0.05911255f, 0.70504761f,
141 0.38858032f, -0.02966309f, -0.05831909f, 0.69470215f, 0.39935303f, -0.03067017f,
142 -0.05746460f, 0.68426514f, 0.41018677f, -0.03170776f, -0.05661011f, 0.67373657f,
143 0.42108154f, -0.03271484f, -0.05569458f, 0.66311646f, 0.43200684f, -0.03378296f,
144 -0.05477905f, 0.65246582f, 0.44299316f, -0.03482056f, -0.05383301f, 0.64169312f,
145 0.45401001f, -0.03588867f, -0.05285645f, 0.63088989f, 0.46505737f, -0.03695679f,
146 -0.05187988f, 0.62002563f, 0.47613525f, -0.03802490f, -0.05087280f, 0.60910034f,
147 0.48721313f, -0.03912354f, -0.04983521f, 0.59814453f, 0.49832153f, -0.04019165f,
148 -0.04879761f, 0.58712769f, 0.50946045f, -0.04129028f, -0.04772949f, 0.57611084f,
149 0.52056885f, -0.04235840f, -0.04669189f, 0.56503296f, 0.53170776f, -0.04345703f,
150 -0.04562378f, 0.55392456f, 0.54281616f, -0.04452515f, -0.04452515f, 0.54281616f,
151 0.55392456f, -0.04562378f, -0.04345703f, 0.53170776f, 0.56503296f, -0.04669189f,
152 -0.04235840f, 0.52056885f, 0.57611084f, -0.04772949f, -0.04129028f, 0.50946045f,
153 0.58712769f, -0.04879761f, -0.04019165f, 0.49832153f, 0.59814453f, -0.04983521f,
154 -0.03912354f, 0.48721313f, 0.60910034f, -0.05087280f, -0.03802490f, 0.47613525f,
155 0.62002563f, -0.05187988f, -0.03695679f, 0.46505737f, 0.63088989f, -0.05285645f,
156 -0.03588867f, 0.45401001f, 0.64169312f, -0.05383301f, -0.03482056f, 0.44299316f,
157 0.65246582f, -0.05477905f, -0.03378296f, 0.43200684f, 0.66311646f, -0.05569458f,
158 -0.03271484f, 0.42108154f, 0.67373657f, -0.05661011f, -0.03170776f, 0.41018677f,
159 0.68426514f, -0.05746460f, -0.03067017f, 0.39935303f, 0.69470215f, -0.05831909f,
160 -0.02966309f, 0.38858032f, 0.70504761f, -0.05911255f, -0.02865601f, 0.37786865f,
161 0.71527100f, -0.05987549f, -0.02767944f, 0.36721802f, 0.72543335f, -0.06060791f,
162 -0.02670288f, 0.35662842f, 0.73547363f, -0.06130981f, -0.02575684f, 0.34613037f,
163 0.74539185f, -0.06195068f, -0.02481079f, 0.33569336f, 0.75518799f, -0.06253052f,
164 -0.02389526f, 0.32537842f, 0.76486206f, -0.06307983f, -0.02297974f, 0.31512451f,
165 0.77438354f, -0.06359863f, -0.02209473f, 0.30496216f, 0.78378296f, -0.06402588f,
166 -0.02124023f, 0.29489136f, 0.79306030f, -0.06442261f, -0.02038574f, 0.28491211f,
167 0.80215454f, -0.06472778f, -0.01956177f, 0.27505493f, 0.81112671f, -0.06500244f,
168 -0.01873779f, 0.26531982f, 0.81991577f, -0.06518555f, -0.01794434f, 0.25567627f,
169 0.82858276f, -0.06533813f, -0.01718140f, 0.24615479f, 0.83703613f, -0.06536865f,
170 -0.01641846f, 0.23675537f, 0.84533691f, -0.06536865f, -0.01568604f, 0.22744751f,
171 0.85345459f, -0.06527710f, -0.01495361f, 0.21829224f, 0.86138916f, -0.06509399f,
172 -0.01428223f, 0.20925903f, 0.86914062f, -0.06484985f, -0.01358032f, 0.20034790f,
173 0.87667847f, -0.06451416f, -0.01293945f, 0.19155884f, 0.88403320f, -0.06408691f,
174 -0.01229858f, 0.18292236f, 0.89120483f, -0.06356812f, -0.01165771f, 0.17443848f,
175 0.89816284f, -0.06295776f, -0.01104736f, 0.16607666f, 0.90490723f, -0.06225586f,
176 -0.01046753f, 0.15783691f, 0.91143799f, -0.06146240f, -0.00991821f, 0.14978027f,
177 0.91772461f, -0.06054688f, -0.00936890f, 0.14184570f, 0.92382812f, -0.05953979f,
178 -0.00881958f, 0.13406372f, 0.92968750f, -0.05841064f, -0.00830078f, 0.12643433f,
179 0.93533325f, -0.05718994f, -0.00781250f, 0.11892700f, 0.94073486f, -0.05584717f,
180 -0.00732422f, 0.11160278f, 0.94589233f, -0.05441284f, -0.00683594f, 0.10443115f,
181 0.95083618f, -0.05285645f, -0.00637817f, 0.09741211f, 0.95550537f, -0.05114746f,
182 -0.00592041f, 0.09054565f, 0.95996094f, -0.04934692f, -0.00549316f, 0.08383179f,
183 0.96414185f, -0.04742432f, -0.00509644f, 0.07727051f, 0.96810913f, -0.04534912f,
184 -0.00466919f, 0.07086182f, 0.97180176f, -0.04315186f, -0.00427246f, 0.06463623f,
185 0.97521973f, -0.04083252f, -0.00390625f, 0.05856323f, 0.97842407f, -0.03839111f,
186 -0.00350952f, 0.05261230f, 0.98135376f, -0.03579712f, -0.00314331f, 0.04687500f,
187 0.98400879f, -0.03308105f, -0.00280762f, 0.04125977f, 0.98641968f, -0.03021240f,
188 -0.00244141f, 0.03582764f, 0.98855591f, -0.02719116f, -0.00210571f, 0.03051758f,
189 0.99041748f, -0.02404785f, -0.00177002f, 0.02539062f, 0.99203491f, -0.02075195f,
190 -0.00143433f, 0.02041626f, 0.99337769f, -0.01733398f, -0.00109863f, 0.01562500f,
191 0.99444580f, -0.01373291f, -0.00079346f, 0.01095581f, 0.99526978f, -0.01000977f,
192 -0.00045776f, 0.00646973f, 0.99578857f, -0.00610352f, -0.00015259f, 0.00210571f,
193 0.99606323f, -0.00207520f,
194 };
195
196 static constexpr std::array<f32, 512> lut2 = {
197 0.09750366f, 0.80221558f, 0.10159302f, -0.00097656f, 0.09350586f, 0.80203247f,
198 0.10580444f, -0.00103760f, 0.08959961f, 0.80169678f, 0.11010742f, -0.00115967f,
199 0.08578491f, 0.80117798f, 0.11447144f, -0.00128174f, 0.08203125f, 0.80047607f,
200 0.11892700f, -0.00140381f, 0.07836914f, 0.79962158f, 0.12347412f, -0.00152588f,
201 0.07479858f, 0.79861450f, 0.12814331f, -0.00164795f, 0.07135010f, 0.79742432f,
202 0.13287354f, -0.00177002f, 0.06796265f, 0.79605103f, 0.13769531f, -0.00192261f,
203 0.06469727f, 0.79452515f, 0.14260864f, -0.00204468f, 0.06149292f, 0.79284668f,
204 0.14761353f, -0.00219727f, 0.05834961f, 0.79098511f, 0.15270996f, -0.00231934f,
205 0.05532837f, 0.78894043f, 0.15789795f, -0.00247192f, 0.05236816f, 0.78674316f,
206 0.16317749f, -0.00265503f, 0.04949951f, 0.78442383f, 0.16851807f, -0.00280762f,
207 0.04672241f, 0.78189087f, 0.17398071f, -0.00299072f, 0.04400635f, 0.77920532f,
208 0.17950439f, -0.00314331f, 0.04141235f, 0.77636719f, 0.18511963f, -0.00332642f,
209 0.03887939f, 0.77337646f, 0.19082642f, -0.00350952f, 0.03640747f, 0.77023315f,
210 0.19659424f, -0.00369263f, 0.03402710f, 0.76693726f, 0.20248413f, -0.00387573f,
211 0.03173828f, 0.76348877f, 0.20843506f, -0.00405884f, 0.02951050f, 0.75985718f,
212 0.21444702f, -0.00427246f, 0.02737427f, 0.75610352f, 0.22055054f, -0.00445557f,
213 0.02529907f, 0.75219727f, 0.22674561f, -0.00466919f, 0.02331543f, 0.74816895f,
214 0.23300171f, -0.00485229f, 0.02139282f, 0.74398804f, 0.23931885f, -0.00506592f,
215 0.01956177f, 0.73965454f, 0.24572754f, -0.00531006f, 0.01779175f, 0.73519897f,
216 0.25219727f, -0.00552368f, 0.01605225f, 0.73059082f, 0.25872803f, -0.00570679f,
217 0.01440430f, 0.72586060f, 0.26535034f, -0.00592041f, 0.01281738f, 0.72100830f,
218 0.27203369f, -0.00616455f, 0.01132202f, 0.71600342f, 0.27877808f, -0.00637817f,
219 0.00988770f, 0.71090698f, 0.28558350f, -0.00656128f, 0.00851440f, 0.70565796f,
220 0.29244995f, -0.00677490f, 0.00720215f, 0.70031738f, 0.29934692f, -0.00701904f,
221 0.00592041f, 0.69485474f, 0.30633545f, -0.00723267f, 0.00469971f, 0.68927002f,
222 0.31338501f, -0.00741577f, 0.00357056f, 0.68356323f, 0.32046509f, -0.00762939f,
223 0.00247192f, 0.67773438f, 0.32760620f, -0.00787354f, 0.00143433f, 0.67184448f,
224 0.33477783f, -0.00808716f, 0.00045776f, 0.66583252f, 0.34197998f, -0.00827026f,
225 -0.00048828f, 0.65972900f, 0.34924316f, -0.00845337f, -0.00134277f, 0.65353394f,
226 0.35656738f, -0.00863647f, -0.00216675f, 0.64721680f, 0.36389160f, -0.00885010f,
227 -0.00296021f, 0.64083862f, 0.37127686f, -0.00903320f, -0.00369263f, 0.63433838f,
228 0.37869263f, -0.00921631f, -0.00436401f, 0.62777710f, 0.38613892f, -0.00933838f,
229 -0.00497437f, 0.62115479f, 0.39361572f, -0.00949097f, -0.00558472f, 0.61444092f,
230 0.40109253f, -0.00964355f, -0.00613403f, 0.60763550f, 0.40859985f, -0.00979614f,
231 -0.00665283f, 0.60076904f, 0.41610718f, -0.00991821f, -0.00714111f, 0.59384155f,
232 0.42364502f, -0.01000977f, -0.00756836f, 0.58685303f, 0.43121338f, -0.01013184f,
233 -0.00796509f, 0.57977295f, 0.43875122f, -0.01022339f, -0.00833130f, 0.57266235f,
234 0.44631958f, -0.01028442f, -0.00866699f, 0.56552124f, 0.45388794f, -0.01034546f,
235 -0.00897217f, 0.55831909f, 0.46145630f, -0.01040649f, -0.00921631f, 0.55105591f,
236 0.46902466f, -0.01040649f, -0.00946045f, 0.54373169f, 0.47659302f, -0.01040649f,
237 -0.00967407f, 0.53640747f, 0.48413086f, -0.01037598f, -0.00985718f, 0.52902222f,
238 0.49166870f, -0.01037598f, -0.01000977f, 0.52160645f, 0.49917603f, -0.01031494f,
239 -0.01013184f, 0.51416016f, 0.50668335f, -0.01025391f, -0.01025391f, 0.50668335f,
240 0.51416016f, -0.01013184f, -0.01031494f, 0.49917603f, 0.52160645f, -0.01000977f,
241 -0.01037598f, 0.49166870f, 0.52902222f, -0.00985718f, -0.01037598f, 0.48413086f,
242 0.53640747f, -0.00967407f, -0.01040649f, 0.47659302f, 0.54373169f, -0.00946045f,
243 -0.01040649f, 0.46902466f, 0.55105591f, -0.00921631f, -0.01040649f, 0.46145630f,
244 0.55831909f, -0.00897217f, -0.01034546f, 0.45388794f, 0.56552124f, -0.00866699f,
245 -0.01028442f, 0.44631958f, 0.57266235f, -0.00833130f, -0.01022339f, 0.43875122f,
246 0.57977295f, -0.00796509f, -0.01013184f, 0.43121338f, 0.58685303f, -0.00756836f,
247 -0.01000977f, 0.42364502f, 0.59384155f, -0.00714111f, -0.00991821f, 0.41610718f,
248 0.60076904f, -0.00665283f, -0.00979614f, 0.40859985f, 0.60763550f, -0.00613403f,
249 -0.00964355f, 0.40109253f, 0.61444092f, -0.00558472f, -0.00949097f, 0.39361572f,
250 0.62115479f, -0.00497437f, -0.00933838f, 0.38613892f, 0.62777710f, -0.00436401f,
251 -0.00921631f, 0.37869263f, 0.63433838f, -0.00369263f, -0.00903320f, 0.37127686f,
252 0.64083862f, -0.00296021f, -0.00885010f, 0.36389160f, 0.64721680f, -0.00216675f,
253 -0.00863647f, 0.35656738f, 0.65353394f, -0.00134277f, -0.00845337f, 0.34924316f,
254 0.65972900f, -0.00048828f, -0.00827026f, 0.34197998f, 0.66583252f, 0.00045776f,
255 -0.00808716f, 0.33477783f, 0.67184448f, 0.00143433f, -0.00787354f, 0.32760620f,
256 0.67773438f, 0.00247192f, -0.00762939f, 0.32046509f, 0.68356323f, 0.00357056f,
257 -0.00741577f, 0.31338501f, 0.68927002f, 0.00469971f, -0.00723267f, 0.30633545f,
258 0.69485474f, 0.00592041f, -0.00701904f, 0.29934692f, 0.70031738f, 0.00720215f,
259 -0.00677490f, 0.29244995f, 0.70565796f, 0.00851440f, -0.00656128f, 0.28558350f,
260 0.71090698f, 0.00988770f, -0.00637817f, 0.27877808f, 0.71600342f, 0.01132202f,
261 -0.00616455f, 0.27203369f, 0.72100830f, 0.01281738f, -0.00592041f, 0.26535034f,
262 0.72586060f, 0.01440430f, -0.00570679f, 0.25872803f, 0.73059082f, 0.01605225f,
263 -0.00552368f, 0.25219727f, 0.73519897f, 0.01779175f, -0.00531006f, 0.24572754f,
264 0.73965454f, 0.01956177f, -0.00506592f, 0.23931885f, 0.74398804f, 0.02139282f,
265 -0.00485229f, 0.23300171f, 0.74816895f, 0.02331543f, -0.00466919f, 0.22674561f,
266 0.75219727f, 0.02529907f, -0.00445557f, 0.22055054f, 0.75610352f, 0.02737427f,
267 -0.00427246f, 0.21444702f, 0.75985718f, 0.02951050f, -0.00405884f, 0.20843506f,
268 0.76348877f, 0.03173828f, -0.00387573f, 0.20248413f, 0.76693726f, 0.03402710f,
269 -0.00369263f, 0.19659424f, 0.77023315f, 0.03640747f, -0.00350952f, 0.19082642f,
270 0.77337646f, 0.03887939f, -0.00332642f, 0.18511963f, 0.77636719f, 0.04141235f,
271 -0.00314331f, 0.17950439f, 0.77920532f, 0.04400635f, -0.00299072f, 0.17398071f,
272 0.78189087f, 0.04672241f, -0.00280762f, 0.16851807f, 0.78442383f, 0.04949951f,
273 -0.00265503f, 0.16317749f, 0.78674316f, 0.05236816f, -0.00247192f, 0.15789795f,
274 0.78894043f, 0.05532837f, -0.00231934f, 0.15270996f, 0.79098511f, 0.05834961f,
275 -0.00219727f, 0.14761353f, 0.79284668f, 0.06149292f, -0.00204468f, 0.14260864f,
276 0.79452515f, 0.06469727f, -0.00192261f, 0.13769531f, 0.79605103f, 0.06796265f,
277 -0.00177002f, 0.13287354f, 0.79742432f, 0.07135010f, -0.00164795f, 0.12814331f,
278 0.79861450f, 0.07479858f, -0.00152588f, 0.12347412f, 0.79962158f, 0.07836914f,
279 -0.00140381f, 0.11892700f, 0.80047607f, 0.08203125f, -0.00128174f, 0.11447144f,
280 0.80117798f, 0.08578491f, -0.00115967f, 0.11010742f, 0.80169678f, 0.08959961f,
281 -0.00103760f, 0.10580444f, 0.80203247f, 0.09350586f, -0.00097656f, 0.10159302f,
282 0.80221558f, 0.09750366f,
283 };
284
285 const auto get_lut = [&]() -> std::span<const f32> {
286 if (sample_rate_ratio <= 1.0f) {
287 return std::span<const f32>(lut2.data(), lut2.size());
288 } else if (sample_rate_ratio < 1.3f) {
289 return std::span<const f32>(lut1.data(), lut1.size());
290 } else {
291 return std::span<const f32>(lut0.data(), lut0.size());
292 }
293 };
294
295 auto lut{get_lut()};
296 u32 read_index{0};
297 for (u32 i = 0; i < samples_to_write; i++) {
298 const auto lut_index{(fraction.get_frac() >> 8) * 4};
299 const Common::FixedPoint<56, 8> sample0{input[read_index + 0] * lut[lut_index + 0]};
300 const Common::FixedPoint<56, 8> sample1{input[read_index + 1] * lut[lut_index + 1]};
301 const Common::FixedPoint<56, 8> sample2{input[read_index + 2] * lut[lut_index + 2]};
302 const Common::FixedPoint<56, 8> sample3{input[read_index + 3] * lut[lut_index + 3]};
303 output[i] = (sample0 + sample1 + sample2 + sample3).to_int_floor();
304 fraction += sample_rate_ratio;
305 read_index += static_cast<u32>(fraction.to_int_floor());
306 fraction.clear_int();
307 }
308}
309
310static void ResampleHighQuality(std::span<s32> output, std::span<const s16> input,
311 const Common::FixedPoint<49, 15>& sample_rate_ratio,
312 Common::FixedPoint<49, 15>& fraction, const u32 samples_to_write) {
313 static constexpr std::array<f32, 1024> lut0 = {
314 -0.01776123f, -0.00070190f, 0.26672363f, 0.50006104f, 0.26956177f, 0.00024414f,
315 -0.01800537f, 0.00000000f, -0.01748657f, -0.00164795f, 0.26388550f, 0.50003052f,
316 0.27236938f, 0.00122070f, -0.01824951f, -0.00003052f, -0.01724243f, -0.00256348f,
317 0.26107788f, 0.49996948f, 0.27520752f, 0.00219727f, -0.01849365f, -0.00003052f,
318 -0.01699829f, -0.00344849f, 0.25823975f, 0.49984741f, 0.27801514f, 0.00320435f,
319 -0.01873779f, -0.00006104f, -0.01675415f, -0.00433350f, 0.25543213f, 0.49972534f,
320 0.28085327f, 0.00424194f, -0.01898193f, -0.00006104f, -0.01651001f, -0.00518799f,
321 0.25259399f, 0.49954224f, 0.28366089f, 0.00527954f, -0.01922607f, -0.00009155f,
322 -0.01626587f, -0.00604248f, 0.24978638f, 0.49932861f, 0.28646851f, 0.00634766f,
323 -0.01947021f, -0.00012207f, -0.01602173f, -0.00686646f, 0.24697876f, 0.49908447f,
324 0.28930664f, 0.00744629f, -0.01971436f, -0.00015259f, -0.01574707f, -0.00765991f,
325 0.24414062f, 0.49877930f, 0.29211426f, 0.00854492f, -0.01995850f, -0.00015259f,
326 -0.01550293f, -0.00845337f, 0.24133301f, 0.49847412f, 0.29492188f, 0.00967407f,
327 -0.02020264f, -0.00018311f, -0.01525879f, -0.00921631f, 0.23852539f, 0.49810791f,
328 0.29772949f, 0.01083374f, -0.02044678f, -0.00021362f, -0.01501465f, -0.00997925f,
329 0.23571777f, 0.49774170f, 0.30050659f, 0.01199341f, -0.02069092f, -0.00024414f,
330 -0.01477051f, -0.01071167f, 0.23291016f, 0.49731445f, 0.30331421f, 0.01318359f,
331 -0.02093506f, -0.00027466f, -0.01452637f, -0.01141357f, 0.23010254f, 0.49685669f,
332 0.30609131f, 0.01437378f, -0.02117920f, -0.00030518f, -0.01428223f, -0.01211548f,
333 0.22732544f, 0.49636841f, 0.30886841f, 0.01559448f, -0.02142334f, -0.00033569f,
334 -0.01403809f, -0.01278687f, 0.22451782f, 0.49581909f, 0.31164551f, 0.01684570f,
335 -0.02163696f, -0.00039673f, -0.01379395f, -0.01345825f, 0.22174072f, 0.49526978f,
336 0.31442261f, 0.01809692f, -0.02188110f, -0.00042725f, -0.01358032f, -0.01409912f,
337 0.21896362f, 0.49465942f, 0.31719971f, 0.01937866f, -0.02209473f, -0.00045776f,
338 -0.01333618f, -0.01473999f, 0.21618652f, 0.49404907f, 0.31994629f, 0.02069092f,
339 -0.02233887f, -0.00048828f, -0.01309204f, -0.01535034f, 0.21343994f, 0.49337769f,
340 0.32269287f, 0.02203369f, -0.02255249f, -0.00054932f, -0.01284790f, -0.01596069f,
341 0.21066284f, 0.49267578f, 0.32543945f, 0.02337646f, -0.02279663f, -0.00057983f,
342 -0.01263428f, -0.01654053f, 0.20791626f, 0.49194336f, 0.32818604f, 0.02471924f,
343 -0.02301025f, -0.00064087f, -0.01239014f, -0.01708984f, 0.20516968f, 0.49118042f,
344 0.33090210f, 0.02612305f, -0.02322388f, -0.00067139f, -0.01214600f, -0.01763916f,
345 0.20242310f, 0.49035645f, 0.33361816f, 0.02752686f, -0.02343750f, -0.00073242f,
346 -0.01193237f, -0.01818848f, 0.19970703f, 0.48953247f, 0.33633423f, 0.02896118f,
347 -0.02365112f, -0.00079346f, -0.01168823f, -0.01867676f, 0.19696045f, 0.48864746f,
348 0.33901978f, 0.03039551f, -0.02386475f, -0.00082397f, -0.01147461f, -0.01919556f,
349 0.19427490f, 0.48776245f, 0.34170532f, 0.03186035f, -0.02407837f, -0.00088501f,
350 -0.01123047f, -0.01968384f, 0.19155884f, 0.48681641f, 0.34439087f, 0.03335571f,
351 -0.02429199f, -0.00094604f, -0.01101685f, -0.02014160f, 0.18887329f, 0.48583984f,
352 0.34704590f, 0.03485107f, -0.02447510f, -0.00100708f, -0.01080322f, -0.02059937f,
353 0.18615723f, 0.48483276f, 0.34970093f, 0.03637695f, -0.02468872f, -0.00106812f,
354 -0.01058960f, -0.02102661f, 0.18350220f, 0.48379517f, 0.35235596f, 0.03793335f,
355 -0.02487183f, -0.00112915f, -0.01034546f, -0.02145386f, 0.18081665f, 0.48272705f,
356 0.35498047f, 0.03948975f, -0.02505493f, -0.00119019f, -0.01013184f, -0.02188110f,
357 0.17816162f, 0.48162842f, 0.35760498f, 0.04107666f, -0.02523804f, -0.00125122f,
358 -0.00991821f, -0.02227783f, 0.17550659f, 0.48049927f, 0.36019897f, 0.04269409f,
359 -0.02542114f, -0.00131226f, -0.00970459f, -0.02264404f, 0.17288208f, 0.47933960f,
360 0.36279297f, 0.04431152f, -0.02560425f, -0.00140381f, -0.00952148f, -0.02301025f,
361 0.17025757f, 0.47814941f, 0.36538696f, 0.04595947f, -0.02578735f, -0.00146484f,
362 -0.00930786f, -0.02337646f, 0.16763306f, 0.47689819f, 0.36795044f, 0.04763794f,
363 -0.02593994f, -0.00152588f, -0.00909424f, -0.02371216f, 0.16503906f, 0.47564697f,
364 0.37048340f, 0.04931641f, -0.02609253f, -0.00161743f, -0.00888062f, -0.02401733f,
365 0.16244507f, 0.47436523f, 0.37304688f, 0.05102539f, -0.02627563f, -0.00170898f,
366 -0.00869751f, -0.02435303f, 0.15988159f, 0.47302246f, 0.37554932f, 0.05276489f,
367 -0.02642822f, -0.00177002f, -0.00848389f, -0.02462769f, 0.15731812f, 0.47167969f,
368 0.37805176f, 0.05450439f, -0.02658081f, -0.00186157f, -0.00830078f, -0.02493286f,
369 0.15475464f, 0.47027588f, 0.38055420f, 0.05627441f, -0.02670288f, -0.00195312f,
370 -0.00808716f, -0.02520752f, 0.15222168f, 0.46887207f, 0.38302612f, 0.05804443f,
371 -0.02685547f, -0.00204468f, -0.00790405f, -0.02545166f, 0.14968872f, 0.46743774f,
372 0.38546753f, 0.05987549f, -0.02697754f, -0.00213623f, -0.00772095f, -0.02569580f,
373 0.14718628f, 0.46594238f, 0.38790894f, 0.06170654f, -0.02709961f, -0.00222778f,
374 -0.00753784f, -0.02593994f, 0.14468384f, 0.46444702f, 0.39031982f, 0.06353760f,
375 -0.02722168f, -0.00231934f, -0.00735474f, -0.02615356f, 0.14218140f, 0.46289062f,
376 0.39273071f, 0.06539917f, -0.02734375f, -0.00241089f, -0.00717163f, -0.02636719f,
377 0.13970947f, 0.46133423f, 0.39511108f, 0.06729126f, -0.02743530f, -0.00250244f,
378 -0.00698853f, -0.02655029f, 0.13726807f, 0.45974731f, 0.39749146f, 0.06918335f,
379 -0.02755737f, -0.00259399f, -0.00680542f, -0.02673340f, 0.13479614f, 0.45812988f,
380 0.39984131f, 0.07113647f, -0.02764893f, -0.00271606f, -0.00662231f, -0.02691650f,
381 0.13238525f, 0.45648193f, 0.40216064f, 0.07305908f, -0.02774048f, -0.00280762f,
382 -0.00643921f, -0.02706909f, 0.12997437f, 0.45480347f, 0.40447998f, 0.07504272f,
383 -0.02780151f, -0.00292969f, -0.00628662f, -0.02722168f, 0.12756348f, 0.45309448f,
384 0.40676880f, 0.07699585f, -0.02789307f, -0.00305176f, -0.00610352f, -0.02734375f,
385 0.12518311f, 0.45135498f, 0.40902710f, 0.07901001f, -0.02795410f, -0.00314331f,
386 -0.00595093f, -0.02746582f, 0.12280273f, 0.44958496f, 0.41128540f, 0.08102417f,
387 -0.02801514f, -0.00326538f, -0.00579834f, -0.02758789f, 0.12045288f, 0.44778442f,
388 0.41351318f, 0.08306885f, -0.02804565f, -0.00338745f, -0.00561523f, -0.02770996f,
389 0.11813354f, 0.44598389f, 0.41571045f, 0.08511353f, -0.02810669f, -0.00350952f,
390 -0.00546265f, -0.02780151f, 0.11581421f, 0.44412231f, 0.41787720f, 0.08718872f,
391 -0.02813721f, -0.00363159f, -0.00531006f, -0.02786255f, 0.11349487f, 0.44226074f,
392 0.42004395f, 0.08929443f, -0.02816772f, -0.00375366f, -0.00515747f, -0.02795410f,
393 0.11120605f, 0.44036865f, 0.42218018f, 0.09140015f, -0.02816772f, -0.00387573f,
394 -0.00500488f, -0.02801514f, 0.10894775f, 0.43844604f, 0.42431641f, 0.09353638f,
395 -0.02819824f, -0.00402832f, -0.00485229f, -0.02807617f, 0.10668945f, 0.43649292f,
396 0.42639160f, 0.09570312f, -0.02819824f, -0.00415039f, -0.00469971f, -0.02810669f,
397 0.10446167f, 0.43453979f, 0.42846680f, 0.09786987f, -0.02819824f, -0.00427246f,
398 -0.00457764f, -0.02813721f, 0.10223389f, 0.43252563f, 0.43051147f, 0.10003662f,
399 -0.02816772f, -0.00442505f, -0.00442505f, -0.02816772f, 0.10003662f, 0.43051147f,
400 0.43252563f, 0.10223389f, -0.02813721f, -0.00457764f, -0.00427246f, -0.02819824f,
401 0.09786987f, 0.42846680f, 0.43453979f, 0.10446167f, -0.02810669f, -0.00469971f,
402 -0.00415039f, -0.02819824f, 0.09570312f, 0.42639160f, 0.43649292f, 0.10668945f,
403 -0.02807617f, -0.00485229f, -0.00402832f, -0.02819824f, 0.09353638f, 0.42431641f,
404 0.43844604f, 0.10894775f, -0.02801514f, -0.00500488f, -0.00387573f, -0.02816772f,
405 0.09140015f, 0.42218018f, 0.44036865f, 0.11120605f, -0.02795410f, -0.00515747f,
406 -0.00375366f, -0.02816772f, 0.08929443f, 0.42004395f, 0.44226074f, 0.11349487f,
407 -0.02786255f, -0.00531006f, -0.00363159f, -0.02813721f, 0.08718872f, 0.41787720f,
408 0.44412231f, 0.11581421f, -0.02780151f, -0.00546265f, -0.00350952f, -0.02810669f,
409 0.08511353f, 0.41571045f, 0.44598389f, 0.11813354f, -0.02770996f, -0.00561523f,
410 -0.00338745f, -0.02804565f, 0.08306885f, 0.41351318f, 0.44778442f, 0.12045288f,
411 -0.02758789f, -0.00579834f, -0.00326538f, -0.02801514f, 0.08102417f, 0.41128540f,
412 0.44958496f, 0.12280273f, -0.02746582f, -0.00595093f, -0.00314331f, -0.02795410f,
413 0.07901001f, 0.40902710f, 0.45135498f, 0.12518311f, -0.02734375f, -0.00610352f,
414 -0.00305176f, -0.02789307f, 0.07699585f, 0.40676880f, 0.45309448f, 0.12756348f,
415 -0.02722168f, -0.00628662f, -0.00292969f, -0.02780151f, 0.07504272f, 0.40447998f,
416 0.45480347f, 0.12997437f, -0.02706909f, -0.00643921f, -0.00280762f, -0.02774048f,
417 0.07305908f, 0.40216064f, 0.45648193f, 0.13238525f, -0.02691650f, -0.00662231f,
418 -0.00271606f, -0.02764893f, 0.07113647f, 0.39984131f, 0.45812988f, 0.13479614f,
419 -0.02673340f, -0.00680542f, -0.00259399f, -0.02755737f, 0.06918335f, 0.39749146f,
420 0.45974731f, 0.13726807f, -0.02655029f, -0.00698853f, -0.00250244f, -0.02743530f,
421 0.06729126f, 0.39511108f, 0.46133423f, 0.13970947f, -0.02636719f, -0.00717163f,
422 -0.00241089f, -0.02734375f, 0.06539917f, 0.39273071f, 0.46289062f, 0.14218140f,
423 -0.02615356f, -0.00735474f, -0.00231934f, -0.02722168f, 0.06353760f, 0.39031982f,
424 0.46444702f, 0.14468384f, -0.02593994f, -0.00753784f, -0.00222778f, -0.02709961f,
425 0.06170654f, 0.38790894f, 0.46594238f, 0.14718628f, -0.02569580f, -0.00772095f,
426 -0.00213623f, -0.02697754f, 0.05987549f, 0.38546753f, 0.46743774f, 0.14968872f,
427 -0.02545166f, -0.00790405f, -0.00204468f, -0.02685547f, 0.05804443f, 0.38302612f,
428 0.46887207f, 0.15222168f, -0.02520752f, -0.00808716f, -0.00195312f, -0.02670288f,
429 0.05627441f, 0.38055420f, 0.47027588f, 0.15475464f, -0.02493286f, -0.00830078f,
430 -0.00186157f, -0.02658081f, 0.05450439f, 0.37805176f, 0.47167969f, 0.15731812f,
431 -0.02462769f, -0.00848389f, -0.00177002f, -0.02642822f, 0.05276489f, 0.37554932f,
432 0.47302246f, 0.15988159f, -0.02435303f, -0.00869751f, -0.00170898f, -0.02627563f,
433 0.05102539f, 0.37304688f, 0.47436523f, 0.16244507f, -0.02401733f, -0.00888062f,
434 -0.00161743f, -0.02609253f, 0.04931641f, 0.37048340f, 0.47564697f, 0.16503906f,
435 -0.02371216f, -0.00909424f, -0.00152588f, -0.02593994f, 0.04763794f, 0.36795044f,
436 0.47689819f, 0.16763306f, -0.02337646f, -0.00930786f, -0.00146484f, -0.02578735f,
437 0.04595947f, 0.36538696f, 0.47814941f, 0.17025757f, -0.02301025f, -0.00952148f,
438 -0.00140381f, -0.02560425f, 0.04431152f, 0.36279297f, 0.47933960f, 0.17288208f,
439 -0.02264404f, -0.00970459f, -0.00131226f, -0.02542114f, 0.04269409f, 0.36019897f,
440 0.48049927f, 0.17550659f, -0.02227783f, -0.00991821f, -0.00125122f, -0.02523804f,
441 0.04107666f, 0.35760498f, 0.48162842f, 0.17816162f, -0.02188110f, -0.01013184f,
442 -0.00119019f, -0.02505493f, 0.03948975f, 0.35498047f, 0.48272705f, 0.18081665f,
443 -0.02145386f, -0.01034546f, -0.00112915f, -0.02487183f, 0.03793335f, 0.35235596f,
444 0.48379517f, 0.18350220f, -0.02102661f, -0.01058960f, -0.00106812f, -0.02468872f,
445 0.03637695f, 0.34970093f, 0.48483276f, 0.18615723f, -0.02059937f, -0.01080322f,
446 -0.00100708f, -0.02447510f, 0.03485107f, 0.34704590f, 0.48583984f, 0.18887329f,
447 -0.02014160f, -0.01101685f, -0.00094604f, -0.02429199f, 0.03335571f, 0.34439087f,
448 0.48681641f, 0.19155884f, -0.01968384f, -0.01123047f, -0.00088501f, -0.02407837f,
449 0.03186035f, 0.34170532f, 0.48776245f, 0.19427490f, -0.01919556f, -0.01147461f,
450 -0.00082397f, -0.02386475f, 0.03039551f, 0.33901978f, 0.48864746f, 0.19696045f,
451 -0.01867676f, -0.01168823f, -0.00079346f, -0.02365112f, 0.02896118f, 0.33633423f,
452 0.48953247f, 0.19970703f, -0.01818848f, -0.01193237f, -0.00073242f, -0.02343750f,
453 0.02752686f, 0.33361816f, 0.49035645f, 0.20242310f, -0.01763916f, -0.01214600f,
454 -0.00067139f, -0.02322388f, 0.02612305f, 0.33090210f, 0.49118042f, 0.20516968f,
455 -0.01708984f, -0.01239014f, -0.00064087f, -0.02301025f, 0.02471924f, 0.32818604f,
456 0.49194336f, 0.20791626f, -0.01654053f, -0.01263428f, -0.00057983f, -0.02279663f,
457 0.02337646f, 0.32543945f, 0.49267578f, 0.21066284f, -0.01596069f, -0.01284790f,
458 -0.00054932f, -0.02255249f, 0.02203369f, 0.32269287f, 0.49337769f, 0.21343994f,
459 -0.01535034f, -0.01309204f, -0.00048828f, -0.02233887f, 0.02069092f, 0.31994629f,
460 0.49404907f, 0.21618652f, -0.01473999f, -0.01333618f, -0.00045776f, -0.02209473f,
461 0.01937866f, 0.31719971f, 0.49465942f, 0.21896362f, -0.01409912f, -0.01358032f,
462 -0.00042725f, -0.02188110f, 0.01809692f, 0.31442261f, 0.49526978f, 0.22174072f,
463 -0.01345825f, -0.01379395f, -0.00039673f, -0.02163696f, 0.01684570f, 0.31164551f,
464 0.49581909f, 0.22451782f, -0.01278687f, -0.01403809f, -0.00033569f, -0.02142334f,
465 0.01559448f, 0.30886841f, 0.49636841f, 0.22732544f, -0.01211548f, -0.01428223f,
466 -0.00030518f, -0.02117920f, 0.01437378f, 0.30609131f, 0.49685669f, 0.23010254f,
467 -0.01141357f, -0.01452637f, -0.00027466f, -0.02093506f, 0.01318359f, 0.30331421f,
468 0.49731445f, 0.23291016f, -0.01071167f, -0.01477051f, -0.00024414f, -0.02069092f,
469 0.01199341f, 0.30050659f, 0.49774170f, 0.23571777f, -0.00997925f, -0.01501465f,
470 -0.00021362f, -0.02044678f, 0.01083374f, 0.29772949f, 0.49810791f, 0.23852539f,
471 -0.00921631f, -0.01525879f, -0.00018311f, -0.02020264f, 0.00967407f, 0.29492188f,
472 0.49847412f, 0.24133301f, -0.00845337f, -0.01550293f, -0.00015259f, -0.01995850f,
473 0.00854492f, 0.29211426f, 0.49877930f, 0.24414062f, -0.00765991f, -0.01574707f,
474 -0.00015259f, -0.01971436f, 0.00744629f, 0.28930664f, 0.49908447f, 0.24697876f,
475 -0.00686646f, -0.01602173f, -0.00012207f, -0.01947021f, 0.00634766f, 0.28646851f,
476 0.49932861f, 0.24978638f, -0.00604248f, -0.01626587f, -0.00009155f, -0.01922607f,
477 0.00527954f, 0.28366089f, 0.49954224f, 0.25259399f, -0.00518799f, -0.01651001f,
478 -0.00006104f, -0.01898193f, 0.00424194f, 0.28085327f, 0.49972534f, 0.25543213f,
479 -0.00433350f, -0.01675415f, -0.00006104f, -0.01873779f, 0.00320435f, 0.27801514f,
480 0.49984741f, 0.25823975f, -0.00344849f, -0.01699829f, -0.00003052f, -0.01849365f,
481 0.00219727f, 0.27520752f, 0.49996948f, 0.26107788f, -0.00256348f, -0.01724243f,
482 -0.00003052f, -0.01824951f, 0.00122070f, 0.27236938f, 0.50003052f, 0.26388550f,
483 -0.00164795f, -0.01748657f, 0.00000000f, -0.01800537f, 0.00024414f, 0.26956177f,
484 0.50006104f, 0.26672363f, -0.00070190f, -0.01776123f,
485 };
486
487 static constexpr std::array<f32, 1024> lut1 = {
488 0.01275635f, -0.07745361f, 0.18670654f, 0.75119019f, 0.19219971f, -0.07821655f,
489 0.01272583f, 0.00000000f, 0.01281738f, -0.07666016f, 0.18124390f, 0.75106812f,
490 0.19772339f, -0.07897949f, 0.01266479f, 0.00003052f, 0.01284790f, -0.07583618f,
491 0.17581177f, 0.75088501f, 0.20330811f, -0.07971191f, 0.01257324f, 0.00006104f,
492 0.01287842f, -0.07501221f, 0.17044067f, 0.75057983f, 0.20892334f, -0.08041382f,
493 0.01248169f, 0.00009155f, 0.01290894f, -0.07415771f, 0.16510010f, 0.75018311f,
494 0.21453857f, -0.08111572f, 0.01239014f, 0.00012207f, 0.01290894f, -0.07330322f,
495 0.15979004f, 0.74966431f, 0.22021484f, -0.08178711f, 0.01229858f, 0.00015259f,
496 0.01290894f, -0.07241821f, 0.15454102f, 0.74908447f, 0.22592163f, -0.08242798f,
497 0.01217651f, 0.00018311f, 0.01290894f, -0.07150269f, 0.14932251f, 0.74838257f,
498 0.23165894f, -0.08303833f, 0.01205444f, 0.00021362f, 0.01290894f, -0.07058716f,
499 0.14416504f, 0.74755859f, 0.23742676f, -0.08364868f, 0.01193237f, 0.00024414f,
500 0.01287842f, -0.06967163f, 0.13903809f, 0.74667358f, 0.24322510f, -0.08419800f,
501 0.01177979f, 0.00027466f, 0.01284790f, -0.06872559f, 0.13397217f, 0.74566650f,
502 0.24905396f, -0.08474731f, 0.01162720f, 0.00033569f, 0.01281738f, -0.06777954f,
503 0.12893677f, 0.74456787f, 0.25491333f, -0.08526611f, 0.01147461f, 0.00036621f,
504 0.01278687f, -0.06683350f, 0.12396240f, 0.74337769f, 0.26077271f, -0.08575439f,
505 0.01129150f, 0.00042725f, 0.01275635f, -0.06585693f, 0.11901855f, 0.74206543f,
506 0.26669312f, -0.08621216f, 0.01110840f, 0.00045776f, 0.01269531f, -0.06488037f,
507 0.11413574f, 0.74069214f, 0.27261353f, -0.08663940f, 0.01092529f, 0.00051880f,
508 0.01263428f, -0.06387329f, 0.10931396f, 0.73919678f, 0.27853394f, -0.08700562f,
509 0.01071167f, 0.00057983f, 0.01257324f, -0.06286621f, 0.10452271f, 0.73760986f,
510 0.28451538f, -0.08737183f, 0.01049805f, 0.00064087f, 0.01251221f, -0.06185913f,
511 0.09979248f, 0.73593140f, 0.29049683f, -0.08770752f, 0.01025391f, 0.00067139f,
512 0.01242065f, -0.06082153f, 0.09512329f, 0.73413086f, 0.29647827f, -0.08801270f,
513 0.01000977f, 0.00073242f, 0.01232910f, -0.05981445f, 0.09051514f, 0.73226929f,
514 0.30249023f, -0.08828735f, 0.00973511f, 0.00079346f, 0.01226807f, -0.05877686f,
515 0.08593750f, 0.73028564f, 0.30853271f, -0.08850098f, 0.00949097f, 0.00088501f,
516 0.01214600f, -0.05773926f, 0.08142090f, 0.72824097f, 0.31457520f, -0.08871460f,
517 0.00918579f, 0.00094604f, 0.01205444f, -0.05670166f, 0.07696533f, 0.72607422f,
518 0.32061768f, -0.08886719f, 0.00891113f, 0.00100708f, 0.01196289f, -0.05563354f,
519 0.07257080f, 0.72381592f, 0.32669067f, -0.08898926f, 0.00860596f, 0.00106812f,
520 0.01187134f, -0.05459595f, 0.06820679f, 0.72146606f, 0.33276367f, -0.08908081f,
521 0.00827026f, 0.00115967f, 0.01174927f, -0.05352783f, 0.06393433f, 0.71902466f,
522 0.33883667f, -0.08911133f, 0.00796509f, 0.00122070f, 0.01162720f, -0.05245972f,
523 0.05969238f, 0.71649170f, 0.34494019f, -0.08914185f, 0.00759888f, 0.00131226f,
524 0.01150513f, -0.05139160f, 0.05551147f, 0.71389771f, 0.35101318f, -0.08911133f,
525 0.00726318f, 0.00137329f, 0.01138306f, -0.05032349f, 0.05139160f, 0.71118164f,
526 0.35711670f, -0.08901978f, 0.00686646f, 0.00146484f, 0.01126099f, -0.04928589f,
527 0.04733276f, 0.70837402f, 0.36322021f, -0.08892822f, 0.00650024f, 0.00155640f,
528 0.01113892f, -0.04821777f, 0.04333496f, 0.70550537f, 0.36932373f, -0.08877563f,
529 0.00610352f, 0.00164795f, 0.01101685f, -0.04714966f, 0.03939819f, 0.70251465f,
530 0.37542725f, -0.08856201f, 0.00567627f, 0.00173950f, 0.01086426f, -0.04608154f,
531 0.03549194f, 0.69946289f, 0.38153076f, -0.08834839f, 0.00527954f, 0.00183105f,
532 0.01074219f, -0.04501343f, 0.03167725f, 0.69631958f, 0.38763428f, -0.08804321f,
533 0.00482178f, 0.00192261f, 0.01058960f, -0.04394531f, 0.02792358f, 0.69308472f,
534 0.39370728f, -0.08773804f, 0.00436401f, 0.00201416f, 0.01043701f, -0.04287720f,
535 0.02420044f, 0.68975830f, 0.39981079f, -0.08737183f, 0.00390625f, 0.00210571f,
536 0.01031494f, -0.04180908f, 0.02056885f, 0.68637085f, 0.40588379f, -0.08694458f,
537 0.00344849f, 0.00222778f, 0.01016235f, -0.04074097f, 0.01699829f, 0.68289185f,
538 0.41195679f, -0.08648682f, 0.00296021f, 0.00231934f, 0.01000977f, -0.03970337f,
539 0.01345825f, 0.67932129f, 0.41802979f, -0.08596802f, 0.00244141f, 0.00244141f,
540 0.00985718f, -0.03863525f, 0.01000977f, 0.67568970f, 0.42407227f, -0.08541870f,
541 0.00192261f, 0.00253296f, 0.00970459f, -0.03759766f, 0.00662231f, 0.67196655f,
542 0.43011475f, -0.08480835f, 0.00140381f, 0.00265503f, 0.00955200f, -0.03652954f,
543 0.00326538f, 0.66815186f, 0.43612671f, -0.08416748f, 0.00085449f, 0.00277710f,
544 0.00936890f, -0.03549194f, 0.00000000f, 0.66427612f, 0.44213867f, -0.08346558f,
545 0.00027466f, 0.00289917f, 0.00921631f, -0.03445435f, -0.00320435f, 0.66030884f,
546 0.44812012f, -0.08270264f, -0.00027466f, 0.00299072f, 0.00906372f, -0.03344727f,
547 -0.00634766f, 0.65631104f, 0.45407104f, -0.08190918f, -0.00088501f, 0.00311279f,
548 0.00891113f, -0.03240967f, -0.00946045f, 0.65219116f, 0.46002197f, -0.08105469f,
549 -0.00146484f, 0.00323486f, 0.00872803f, -0.03140259f, -0.01248169f, 0.64801025f,
550 0.46594238f, -0.08013916f, -0.00210571f, 0.00338745f, 0.00857544f, -0.03039551f,
551 -0.01544189f, 0.64376831f, 0.47183228f, -0.07919312f, -0.00271606f, 0.00350952f,
552 0.00842285f, -0.02938843f, -0.01834106f, 0.63946533f, 0.47772217f, -0.07818604f,
553 -0.00335693f, 0.00363159f, 0.00823975f, -0.02838135f, -0.02117920f, 0.63507080f,
554 0.48358154f, -0.07711792f, -0.00402832f, 0.00375366f, 0.00808716f, -0.02740479f,
555 -0.02395630f, 0.63061523f, 0.48937988f, -0.07598877f, -0.00469971f, 0.00390625f,
556 0.00793457f, -0.02642822f, -0.02667236f, 0.62609863f, 0.49517822f, -0.07482910f,
557 -0.00537109f, 0.00402832f, 0.00775146f, -0.02545166f, -0.02932739f, 0.62152100f,
558 0.50094604f, -0.07357788f, -0.00607300f, 0.00418091f, 0.00759888f, -0.02450562f,
559 -0.03192139f, 0.61685181f, 0.50665283f, -0.07229614f, -0.00677490f, 0.00430298f,
560 0.00741577f, -0.02352905f, -0.03445435f, 0.61215210f, 0.51235962f, -0.07098389f,
561 -0.00750732f, 0.00445557f, 0.00726318f, -0.02258301f, -0.03689575f, 0.60736084f,
562 0.51800537f, -0.06958008f, -0.00823975f, 0.00460815f, 0.00711060f, -0.02166748f,
563 -0.03930664f, 0.60253906f, 0.52362061f, -0.06811523f, -0.00897217f, 0.00476074f,
564 0.00692749f, -0.02075195f, -0.04165649f, 0.59762573f, 0.52920532f, -0.06661987f,
565 -0.00973511f, 0.00488281f, 0.00677490f, -0.01983643f, -0.04394531f, 0.59268188f,
566 0.53475952f, -0.06506348f, -0.01052856f, 0.00503540f, 0.00662231f, -0.01892090f,
567 -0.04617310f, 0.58767700f, 0.54025269f, -0.06344604f, -0.01129150f, 0.00518799f,
568 0.00643921f, -0.01803589f, -0.04830933f, 0.58261108f, 0.54571533f, -0.06173706f,
569 -0.01208496f, 0.00534058f, 0.00628662f, -0.01715088f, -0.05041504f, 0.57748413f,
570 0.55111694f, -0.05999756f, -0.01290894f, 0.00549316f, 0.00613403f, -0.01626587f,
571 -0.05245972f, 0.57232666f, 0.55648804f, -0.05819702f, -0.01373291f, 0.00564575f,
572 0.00598145f, -0.01541138f, -0.05444336f, 0.56707764f, 0.56182861f, -0.05636597f,
573 -0.01455688f, 0.00582886f, 0.00582886f, -0.01455688f, -0.05636597f, 0.56182861f,
574 0.56707764f, -0.05444336f, -0.01541138f, 0.00598145f, 0.00564575f, -0.01373291f,
575 -0.05819702f, 0.55648804f, 0.57232666f, -0.05245972f, -0.01626587f, 0.00613403f,
576 0.00549316f, -0.01290894f, -0.05999756f, 0.55111694f, 0.57748413f, -0.05041504f,
577 -0.01715088f, 0.00628662f, 0.00534058f, -0.01208496f, -0.06173706f, 0.54571533f,
578 0.58261108f, -0.04830933f, -0.01803589f, 0.00643921f, 0.00518799f, -0.01129150f,
579 -0.06344604f, 0.54025269f, 0.58767700f, -0.04617310f, -0.01892090f, 0.00662231f,
580 0.00503540f, -0.01052856f, -0.06506348f, 0.53475952f, 0.59268188f, -0.04394531f,
581 -0.01983643f, 0.00677490f, 0.00488281f, -0.00973511f, -0.06661987f, 0.52920532f,
582 0.59762573f, -0.04165649f, -0.02075195f, 0.00692749f, 0.00476074f, -0.00897217f,
583 -0.06811523f, 0.52362061f, 0.60253906f, -0.03930664f, -0.02166748f, 0.00711060f,
584 0.00460815f, -0.00823975f, -0.06958008f, 0.51800537f, 0.60736084f, -0.03689575f,
585 -0.02258301f, 0.00726318f, 0.00445557f, -0.00750732f, -0.07098389f, 0.51235962f,
586 0.61215210f, -0.03445435f, -0.02352905f, 0.00741577f, 0.00430298f, -0.00677490f,
587 -0.07229614f, 0.50665283f, 0.61685181f, -0.03192139f, -0.02450562f, 0.00759888f,
588 0.00418091f, -0.00607300f, -0.07357788f, 0.50094604f, 0.62152100f, -0.02932739f,
589 -0.02545166f, 0.00775146f, 0.00402832f, -0.00537109f, -0.07482910f, 0.49517822f,
590 0.62609863f, -0.02667236f, -0.02642822f, 0.00793457f, 0.00390625f, -0.00469971f,
591 -0.07598877f, 0.48937988f, 0.63061523f, -0.02395630f, -0.02740479f, 0.00808716f,
592 0.00375366f, -0.00402832f, -0.07711792f, 0.48358154f, 0.63507080f, -0.02117920f,
593 -0.02838135f, 0.00823975f, 0.00363159f, -0.00335693f, -0.07818604f, 0.47772217f,
594 0.63946533f, -0.01834106f, -0.02938843f, 0.00842285f, 0.00350952f, -0.00271606f,
595 -0.07919312f, 0.47183228f, 0.64376831f, -0.01544189f, -0.03039551f, 0.00857544f,
596 0.00338745f, -0.00210571f, -0.08013916f, 0.46594238f, 0.64801025f, -0.01248169f,
597 -0.03140259f, 0.00872803f, 0.00323486f, -0.00146484f, -0.08105469f, 0.46002197f,
598 0.65219116f, -0.00946045f, -0.03240967f, 0.00891113f, 0.00311279f, -0.00088501f,
599 -0.08190918f, 0.45407104f, 0.65631104f, -0.00634766f, -0.03344727f, 0.00906372f,
600 0.00299072f, -0.00027466f, -0.08270264f, 0.44812012f, 0.66030884f, -0.00320435f,
601 -0.03445435f, 0.00921631f, 0.00289917f, 0.00027466f, -0.08346558f, 0.44213867f,
602 0.66427612f, 0.00000000f, -0.03549194f, 0.00936890f, 0.00277710f, 0.00085449f,
603 -0.08416748f, 0.43612671f, 0.66815186f, 0.00326538f, -0.03652954f, 0.00955200f,
604 0.00265503f, 0.00140381f, -0.08480835f, 0.43011475f, 0.67196655f, 0.00662231f,
605 -0.03759766f, 0.00970459f, 0.00253296f, 0.00192261f, -0.08541870f, 0.42407227f,
606 0.67568970f, 0.01000977f, -0.03863525f, 0.00985718f, 0.00244141f, 0.00244141f,
607 -0.08596802f, 0.41802979f, 0.67932129f, 0.01345825f, -0.03970337f, 0.01000977f,
608 0.00231934f, 0.00296021f, -0.08648682f, 0.41195679f, 0.68289185f, 0.01699829f,
609 -0.04074097f, 0.01016235f, 0.00222778f, 0.00344849f, -0.08694458f, 0.40588379f,
610 0.68637085f, 0.02056885f, -0.04180908f, 0.01031494f, 0.00210571f, 0.00390625f,
611 -0.08737183f, 0.39981079f, 0.68975830f, 0.02420044f, -0.04287720f, 0.01043701f,
612 0.00201416f, 0.00436401f, -0.08773804f, 0.39370728f, 0.69308472f, 0.02792358f,
613 -0.04394531f, 0.01058960f, 0.00192261f, 0.00482178f, -0.08804321f, 0.38763428f,
614 0.69631958f, 0.03167725f, -0.04501343f, 0.01074219f, 0.00183105f, 0.00527954f,
615 -0.08834839f, 0.38153076f, 0.69946289f, 0.03549194f, -0.04608154f, 0.01086426f,
616 0.00173950f, 0.00567627f, -0.08856201f, 0.37542725f, 0.70251465f, 0.03939819f,
617 -0.04714966f, 0.01101685f, 0.00164795f, 0.00610352f, -0.08877563f, 0.36932373f,
618 0.70550537f, 0.04333496f, -0.04821777f, 0.01113892f, 0.00155640f, 0.00650024f,
619 -0.08892822f, 0.36322021f, 0.70837402f, 0.04733276f, -0.04928589f, 0.01126099f,
620 0.00146484f, 0.00686646f, -0.08901978f, 0.35711670f, 0.71118164f, 0.05139160f,
621 -0.05032349f, 0.01138306f, 0.00137329f, 0.00726318f, -0.08911133f, 0.35101318f,
622 0.71389771f, 0.05551147f, -0.05139160f, 0.01150513f, 0.00131226f, 0.00759888f,
623 -0.08914185f, 0.34494019f, 0.71649170f, 0.05969238f, -0.05245972f, 0.01162720f,
624 0.00122070f, 0.00796509f, -0.08911133f, 0.33883667f, 0.71902466f, 0.06393433f,
625 -0.05352783f, 0.01174927f, 0.00115967f, 0.00827026f, -0.08908081f, 0.33276367f,
626 0.72146606f, 0.06820679f, -0.05459595f, 0.01187134f, 0.00106812f, 0.00860596f,
627 -0.08898926f, 0.32669067f, 0.72381592f, 0.07257080f, -0.05563354f, 0.01196289f,
628 0.00100708f, 0.00891113f, -0.08886719f, 0.32061768f, 0.72607422f, 0.07696533f,
629 -0.05670166f, 0.01205444f, 0.00094604f, 0.00918579f, -0.08871460f, 0.31457520f,
630 0.72824097f, 0.08142090f, -0.05773926f, 0.01214600f, 0.00088501f, 0.00949097f,
631 -0.08850098f, 0.30853271f, 0.73028564f, 0.08593750f, -0.05877686f, 0.01226807f,
632 0.00079346f, 0.00973511f, -0.08828735f, 0.30249023f, 0.73226929f, 0.09051514f,
633 -0.05981445f, 0.01232910f, 0.00073242f, 0.01000977f, -0.08801270f, 0.29647827f,
634 0.73413086f, 0.09512329f, -0.06082153f, 0.01242065f, 0.00067139f, 0.01025391f,
635 -0.08770752f, 0.29049683f, 0.73593140f, 0.09979248f, -0.06185913f, 0.01251221f,
636 0.00064087f, 0.01049805f, -0.08737183f, 0.28451538f, 0.73760986f, 0.10452271f,
637 -0.06286621f, 0.01257324f, 0.00057983f, 0.01071167f, -0.08700562f, 0.27853394f,
638 0.73919678f, 0.10931396f, -0.06387329f, 0.01263428f, 0.00051880f, 0.01092529f,
639 -0.08663940f, 0.27261353f, 0.74069214f, 0.11413574f, -0.06488037f, 0.01269531f,
640 0.00045776f, 0.01110840f, -0.08621216f, 0.26669312f, 0.74206543f, 0.11901855f,
641 -0.06585693f, 0.01275635f, 0.00042725f, 0.01129150f, -0.08575439f, 0.26077271f,
642 0.74337769f, 0.12396240f, -0.06683350f, 0.01278687f, 0.00036621f, 0.01147461f,
643 -0.08526611f, 0.25491333f, 0.74456787f, 0.12893677f, -0.06777954f, 0.01281738f,
644 0.00033569f, 0.01162720f, -0.08474731f, 0.24905396f, 0.74566650f, 0.13397217f,
645 -0.06872559f, 0.01284790f, 0.00027466f, 0.01177979f, -0.08419800f, 0.24322510f,
646 0.74667358f, 0.13903809f, -0.06967163f, 0.01287842f, 0.00024414f, 0.01193237f,
647 -0.08364868f, 0.23742676f, 0.74755859f, 0.14416504f, -0.07058716f, 0.01290894f,
648 0.00021362f, 0.01205444f, -0.08303833f, 0.23165894f, 0.74838257f, 0.14932251f,
649 -0.07150269f, 0.01290894f, 0.00018311f, 0.01217651f, -0.08242798f, 0.22592163f,
650 0.74908447f, 0.15454102f, -0.07241821f, 0.01290894f, 0.00015259f, 0.01229858f,
651 -0.08178711f, 0.22021484f, 0.74966431f, 0.15979004f, -0.07330322f, 0.01290894f,
652 0.00012207f, 0.01239014f, -0.08111572f, 0.21453857f, 0.75018311f, 0.16510010f,
653 -0.07415771f, 0.01290894f, 0.00009155f, 0.01248169f, -0.08041382f, 0.20892334f,
654 0.75057983f, 0.17044067f, -0.07501221f, 0.01287842f, 0.00006104f, 0.01257324f,
655 -0.07971191f, 0.20330811f, 0.75088501f, 0.17581177f, -0.07583618f, 0.01284790f,
656 0.00003052f, 0.01266479f, -0.07897949f, 0.19772339f, 0.75106812f, 0.18124390f,
657 -0.07666016f, 0.01281738f, 0.00000000f, 0.01272583f, -0.07821655f, 0.19219971f,
658 0.75119019f, 0.18670654f, -0.07745361f, 0.01275635f,
659 };
660
661 static constexpr std::array<f32, 1024> lut2 = {
662 -0.00036621f, 0.00143433f, -0.00408936f, 0.99996948f, 0.00247192f, -0.00048828f,
663 0.00006104f, 0.00000000f, -0.00079346f, 0.00329590f, -0.01052856f, 0.99975586f,
664 0.00918579f, -0.00241089f, 0.00051880f, -0.00003052f, -0.00122070f, 0.00512695f,
665 -0.01684570f, 0.99929810f, 0.01605225f, -0.00439453f, 0.00097656f, -0.00006104f,
666 -0.00161743f, 0.00689697f, -0.02297974f, 0.99862671f, 0.02304077f, -0.00640869f,
667 0.00143433f, -0.00009155f, -0.00201416f, 0.00866699f, -0.02899170f, 0.99774170f,
668 0.03018188f, -0.00845337f, 0.00192261f, -0.00015259f, -0.00238037f, 0.01037598f,
669 -0.03488159f, 0.99664307f, 0.03741455f, -0.01055908f, 0.00241089f, -0.00018311f,
670 -0.00274658f, 0.01202393f, -0.04061890f, 0.99533081f, 0.04483032f, -0.01266479f,
671 0.00292969f, -0.00024414f, -0.00308228f, 0.01364136f, -0.04620361f, 0.99377441f,
672 0.05233765f, -0.01483154f, 0.00344849f, -0.00027466f, -0.00341797f, 0.01522827f,
673 -0.05163574f, 0.99200439f, 0.05999756f, -0.01699829f, 0.00396729f, -0.00033569f,
674 -0.00375366f, 0.01678467f, -0.05691528f, 0.99002075f, 0.06777954f, -0.01922607f,
675 0.00451660f, -0.00039673f, -0.00405884f, 0.01828003f, -0.06207275f, 0.98782349f,
676 0.07568359f, -0.02145386f, 0.00506592f, -0.00042725f, -0.00436401f, 0.01971436f,
677 -0.06707764f, 0.98541260f, 0.08370972f, -0.02374268f, 0.00564575f, -0.00048828f,
678 -0.00463867f, 0.02114868f, -0.07192993f, 0.98278809f, 0.09185791f, -0.02603149f,
679 0.00622559f, -0.00054932f, -0.00494385f, 0.02252197f, -0.07666016f, 0.97991943f,
680 0.10012817f, -0.02835083f, 0.00680542f, -0.00061035f, -0.00518799f, 0.02383423f,
681 -0.08123779f, 0.97686768f, 0.10848999f, -0.03073120f, 0.00738525f, -0.00070190f,
682 -0.00543213f, 0.02511597f, -0.08566284f, 0.97360229f, 0.11700439f, -0.03308105f,
683 0.00799561f, -0.00076294f, -0.00567627f, 0.02636719f, -0.08993530f, 0.97012329f,
684 0.12561035f, -0.03549194f, 0.00860596f, -0.00082397f, -0.00592041f, 0.02755737f,
685 -0.09405518f, 0.96643066f, 0.13436890f, -0.03790283f, 0.00924683f, -0.00091553f,
686 -0.00613403f, 0.02868652f, -0.09805298f, 0.96252441f, 0.14318848f, -0.04034424f,
687 0.00985718f, -0.00097656f, -0.00631714f, 0.02981567f, -0.10189819f, 0.95843506f,
688 0.15213013f, -0.04281616f, 0.01049805f, -0.00106812f, -0.00653076f, 0.03085327f,
689 -0.10559082f, 0.95413208f, 0.16119385f, -0.04528809f, 0.01113892f, -0.00112915f,
690 -0.00671387f, 0.03189087f, -0.10916138f, 0.94961548f, 0.17034912f, -0.04779053f,
691 0.01181030f, -0.00122070f, -0.00686646f, 0.03286743f, -0.11254883f, 0.94491577f,
692 0.17959595f, -0.05029297f, 0.01248169f, -0.00131226f, -0.00701904f, 0.03378296f,
693 -0.11584473f, 0.94000244f, 0.18893433f, -0.05279541f, 0.01315308f, -0.00140381f,
694 -0.00717163f, 0.03466797f, -0.11895752f, 0.93490601f, 0.19839478f, -0.05532837f,
695 0.01382446f, -0.00149536f, -0.00732422f, 0.03552246f, -0.12194824f, 0.92962646f,
696 0.20791626f, -0.05786133f, 0.01449585f, -0.00158691f, -0.00744629f, 0.03631592f,
697 -0.12478638f, 0.92413330f, 0.21752930f, -0.06042480f, 0.01519775f, -0.00167847f,
698 -0.00753784f, 0.03707886f, -0.12750244f, 0.91848755f, 0.22723389f, -0.06298828f,
699 0.01586914f, -0.00177002f, -0.00765991f, 0.03781128f, -0.13006592f, 0.91262817f,
700 0.23703003f, -0.06555176f, 0.01657104f, -0.00189209f, -0.00775146f, 0.03848267f,
701 -0.13250732f, 0.90658569f, 0.24691772f, -0.06808472f, 0.01727295f, -0.00198364f,
702 -0.00784302f, 0.03909302f, -0.13479614f, 0.90036011f, 0.25683594f, -0.07064819f,
703 0.01797485f, -0.00210571f, -0.00790405f, 0.03970337f, -0.13696289f, 0.89395142f,
704 0.26687622f, -0.07321167f, 0.01870728f, -0.00219727f, -0.00796509f, 0.04025269f,
705 -0.13900757f, 0.88739014f, 0.27694702f, -0.07577515f, 0.01940918f, -0.00231934f,
706 -0.00802612f, 0.04077148f, -0.14089966f, 0.88064575f, 0.28710938f, -0.07833862f,
707 0.02011108f, -0.00244141f, -0.00808716f, 0.04122925f, -0.14263916f, 0.87374878f,
708 0.29733276f, -0.08090210f, 0.02084351f, -0.00253296f, -0.00811768f, 0.04165649f,
709 -0.14428711f, 0.86666870f, 0.30761719f, -0.08343506f, 0.02154541f, -0.00265503f,
710 -0.00814819f, 0.04205322f, -0.14578247f, 0.85940552f, 0.31793213f, -0.08596802f,
711 0.02227783f, -0.00277710f, -0.00814819f, 0.04238892f, -0.14715576f, 0.85202026f,
712 0.32833862f, -0.08847046f, 0.02297974f, -0.00289917f, -0.00817871f, 0.04272461f,
713 -0.14840698f, 0.84445190f, 0.33874512f, -0.09097290f, 0.02371216f, -0.00302124f,
714 -0.00817871f, 0.04299927f, -0.14953613f, 0.83673096f, 0.34924316f, -0.09347534f,
715 0.02441406f, -0.00314331f, -0.00817871f, 0.04321289f, -0.15054321f, 0.82888794f,
716 0.35977173f, -0.09594727f, 0.02514648f, -0.00326538f, -0.00814819f, 0.04342651f,
717 -0.15142822f, 0.82086182f, 0.37033081f, -0.09838867f, 0.02584839f, -0.00341797f,
718 -0.00814819f, 0.04357910f, -0.15219116f, 0.81271362f, 0.38092041f, -0.10079956f,
719 0.02655029f, -0.00354004f, -0.00811768f, 0.04373169f, -0.15283203f, 0.80441284f,
720 0.39154053f, -0.10321045f, 0.02725220f, -0.00366211f, -0.00808716f, 0.04382324f,
721 -0.15338135f, 0.79598999f, 0.40219116f, -0.10559082f, 0.02795410f, -0.00381470f,
722 -0.00805664f, 0.04388428f, -0.15377808f, 0.78741455f, 0.41287231f, -0.10794067f,
723 0.02865601f, -0.00393677f, -0.00799561f, 0.04388428f, -0.15408325f, 0.77871704f,
724 0.42358398f, -0.11026001f, 0.02935791f, -0.00405884f, -0.00793457f, 0.04388428f,
725 -0.15426636f, 0.76989746f, 0.43429565f, -0.11251831f, 0.03002930f, -0.00421143f,
726 -0.00787354f, 0.04385376f, -0.15435791f, 0.76095581f, 0.44500732f, -0.11477661f,
727 0.03070068f, -0.00433350f, -0.00781250f, 0.04379272f, -0.15435791f, 0.75192261f,
728 0.45574951f, -0.11697388f, 0.03137207f, -0.00448608f, -0.00775146f, 0.04367065f,
729 -0.15420532f, 0.74273682f, 0.46649170f, -0.11914062f, 0.03201294f, -0.00460815f,
730 -0.00769043f, 0.04354858f, -0.15399170f, 0.73345947f, 0.47723389f, -0.12127686f,
731 0.03268433f, -0.00473022f, -0.00759888f, 0.04339600f, -0.15365601f, 0.72406006f,
732 0.48794556f, -0.12335205f, 0.03329468f, -0.00488281f, -0.00750732f, 0.04321289f,
733 -0.15322876f, 0.71456909f, 0.49868774f, -0.12539673f, 0.03393555f, -0.00500488f,
734 -0.00741577f, 0.04296875f, -0.15270996f, 0.70498657f, 0.50936890f, -0.12738037f,
735 0.03454590f, -0.00515747f, -0.00732422f, 0.04272461f, -0.15209961f, 0.69528198f,
736 0.52008057f, -0.12930298f, 0.03515625f, -0.00527954f, -0.00723267f, 0.04248047f,
737 -0.15136719f, 0.68551636f, 0.53076172f, -0.13119507f, 0.03573608f, -0.00543213f,
738 -0.00714111f, 0.04217529f, -0.15057373f, 0.67565918f, 0.54138184f, -0.13299561f,
739 0.03631592f, -0.00555420f, -0.00701904f, 0.04183960f, -0.14968872f, 0.66571045f,
740 0.55200195f, -0.13476562f, 0.03689575f, -0.00567627f, -0.00692749f, 0.04150391f,
741 -0.14871216f, 0.65567017f, 0.56259155f, -0.13647461f, 0.03741455f, -0.00582886f,
742 -0.00680542f, 0.04113770f, -0.14767456f, 0.64556885f, 0.57315063f, -0.13812256f,
743 0.03796387f, -0.00595093f, -0.00668335f, 0.04074097f, -0.14651489f, 0.63540649f,
744 0.58364868f, -0.13970947f, 0.03845215f, -0.00607300f, -0.00656128f, 0.04031372f,
745 -0.14529419f, 0.62518311f, 0.59411621f, -0.14120483f, 0.03897095f, -0.00619507f,
746 -0.00643921f, 0.03988647f, -0.14401245f, 0.61486816f, 0.60452271f, -0.14263916f,
747 0.03942871f, -0.00631714f, -0.00631714f, 0.03942871f, -0.14263916f, 0.60452271f,
748 0.61486816f, -0.14401245f, 0.03988647f, -0.00643921f, -0.00619507f, 0.03897095f,
749 -0.14120483f, 0.59411621f, 0.62518311f, -0.14529419f, 0.04031372f, -0.00656128f,
750 -0.00607300f, 0.03845215f, -0.13970947f, 0.58364868f, 0.63540649f, -0.14651489f,
751 0.04074097f, -0.00668335f, -0.00595093f, 0.03796387f, -0.13812256f, 0.57315063f,
752 0.64556885f, -0.14767456f, 0.04113770f, -0.00680542f, -0.00582886f, 0.03741455f,
753 -0.13647461f, 0.56259155f, 0.65567017f, -0.14871216f, 0.04150391f, -0.00692749f,
754 -0.00567627f, 0.03689575f, -0.13476562f, 0.55200195f, 0.66571045f, -0.14968872f,
755 0.04183960f, -0.00701904f, -0.00555420f, 0.03631592f, -0.13299561f, 0.54138184f,
756 0.67565918f, -0.15057373f, 0.04217529f, -0.00714111f, -0.00543213f, 0.03573608f,
757 -0.13119507f, 0.53076172f, 0.68551636f, -0.15136719f, 0.04248047f, -0.00723267f,
758 -0.00527954f, 0.03515625f, -0.12930298f, 0.52008057f, 0.69528198f, -0.15209961f,
759 0.04272461f, -0.00732422f, -0.00515747f, 0.03454590f, -0.12738037f, 0.50936890f,
760 0.70498657f, -0.15270996f, 0.04296875f, -0.00741577f, -0.00500488f, 0.03393555f,
761 -0.12539673f, 0.49868774f, 0.71456909f, -0.15322876f, 0.04321289f, -0.00750732f,
762 -0.00488281f, 0.03329468f, -0.12335205f, 0.48794556f, 0.72406006f, -0.15365601f,
763 0.04339600f, -0.00759888f, -0.00473022f, 0.03268433f, -0.12127686f, 0.47723389f,
764 0.73345947f, -0.15399170f, 0.04354858f, -0.00769043f, -0.00460815f, 0.03201294f,
765 -0.11914062f, 0.46649170f, 0.74273682f, -0.15420532f, 0.04367065f, -0.00775146f,
766 -0.00448608f, 0.03137207f, -0.11697388f, 0.45574951f, 0.75192261f, -0.15435791f,
767 0.04379272f, -0.00781250f, -0.00433350f, 0.03070068f, -0.11477661f, 0.44500732f,
768 0.76095581f, -0.15435791f, 0.04385376f, -0.00787354f, -0.00421143f, 0.03002930f,
769 -0.11251831f, 0.43429565f, 0.76989746f, -0.15426636f, 0.04388428f, -0.00793457f,
770 -0.00405884f, 0.02935791f, -0.11026001f, 0.42358398f, 0.77871704f, -0.15408325f,
771 0.04388428f, -0.00799561f, -0.00393677f, 0.02865601f, -0.10794067f, 0.41287231f,
772 0.78741455f, -0.15377808f, 0.04388428f, -0.00805664f, -0.00381470f, 0.02795410f,
773 -0.10559082f, 0.40219116f, 0.79598999f, -0.15338135f, 0.04382324f, -0.00808716f,
774 -0.00366211f, 0.02725220f, -0.10321045f, 0.39154053f, 0.80441284f, -0.15283203f,
775 0.04373169f, -0.00811768f, -0.00354004f, 0.02655029f, -0.10079956f, 0.38092041f,
776 0.81271362f, -0.15219116f, 0.04357910f, -0.00814819f, -0.00341797f, 0.02584839f,
777 -0.09838867f, 0.37033081f, 0.82086182f, -0.15142822f, 0.04342651f, -0.00814819f,
778 -0.00326538f, 0.02514648f, -0.09594727f, 0.35977173f, 0.82888794f, -0.15054321f,
779 0.04321289f, -0.00817871f, -0.00314331f, 0.02441406f, -0.09347534f, 0.34924316f,
780 0.83673096f, -0.14953613f, 0.04299927f, -0.00817871f, -0.00302124f, 0.02371216f,
781 -0.09097290f, 0.33874512f, 0.84445190f, -0.14840698f, 0.04272461f, -0.00817871f,
782 -0.00289917f, 0.02297974f, -0.08847046f, 0.32833862f, 0.85202026f, -0.14715576f,
783 0.04238892f, -0.00814819f, -0.00277710f, 0.02227783f, -0.08596802f, 0.31793213f,
784 0.85940552f, -0.14578247f, 0.04205322f, -0.00814819f, -0.00265503f, 0.02154541f,
785 -0.08343506f, 0.30761719f, 0.86666870f, -0.14428711f, 0.04165649f, -0.00811768f,
786 -0.00253296f, 0.02084351f, -0.08090210f, 0.29733276f, 0.87374878f, -0.14263916f,
787 0.04122925f, -0.00808716f, -0.00244141f, 0.02011108f, -0.07833862f, 0.28710938f,
788 0.88064575f, -0.14089966f, 0.04077148f, -0.00802612f, -0.00231934f, 0.01940918f,
789 -0.07577515f, 0.27694702f, 0.88739014f, -0.13900757f, 0.04025269f, -0.00796509f,
790 -0.00219727f, 0.01870728f, -0.07321167f, 0.26687622f, 0.89395142f, -0.13696289f,
791 0.03970337f, -0.00790405f, -0.00210571f, 0.01797485f, -0.07064819f, 0.25683594f,
792 0.90036011f, -0.13479614f, 0.03909302f, -0.00784302f, -0.00198364f, 0.01727295f,
793 -0.06808472f, 0.24691772f, 0.90658569f, -0.13250732f, 0.03848267f, -0.00775146f,
794 -0.00189209f, 0.01657104f, -0.06555176f, 0.23703003f, 0.91262817f, -0.13006592f,
795 0.03781128f, -0.00765991f, -0.00177002f, 0.01586914f, -0.06298828f, 0.22723389f,
796 0.91848755f, -0.12750244f, 0.03707886f, -0.00753784f, -0.00167847f, 0.01519775f,
797 -0.06042480f, 0.21752930f, 0.92413330f, -0.12478638f, 0.03631592f, -0.00744629f,
798 -0.00158691f, 0.01449585f, -0.05786133f, 0.20791626f, 0.92962646f, -0.12194824f,
799 0.03552246f, -0.00732422f, -0.00149536f, 0.01382446f, -0.05532837f, 0.19839478f,
800 0.93490601f, -0.11895752f, 0.03466797f, -0.00717163f, -0.00140381f, 0.01315308f,
801 -0.05279541f, 0.18893433f, 0.94000244f, -0.11584473f, 0.03378296f, -0.00701904f,
802 -0.00131226f, 0.01248169f, -0.05029297f, 0.17959595f, 0.94491577f, -0.11254883f,
803 0.03286743f, -0.00686646f, -0.00122070f, 0.01181030f, -0.04779053f, 0.17034912f,
804 0.94961548f, -0.10916138f, 0.03189087f, -0.00671387f, -0.00112915f, 0.01113892f,
805 -0.04528809f, 0.16119385f, 0.95413208f, -0.10559082f, 0.03085327f, -0.00653076f,
806 -0.00106812f, 0.01049805f, -0.04281616f, 0.15213013f, 0.95843506f, -0.10189819f,
807 0.02981567f, -0.00631714f, -0.00097656f, 0.00985718f, -0.04034424f, 0.14318848f,
808 0.96252441f, -0.09805298f, 0.02868652f, -0.00613403f, -0.00091553f, 0.00924683f,
809 -0.03790283f, 0.13436890f, 0.96643066f, -0.09405518f, 0.02755737f, -0.00592041f,
810 -0.00082397f, 0.00860596f, -0.03549194f, 0.12561035f, 0.97012329f, -0.08993530f,
811 0.02636719f, -0.00567627f, -0.00076294f, 0.00799561f, -0.03308105f, 0.11700439f,
812 0.97360229f, -0.08566284f, 0.02511597f, -0.00543213f, -0.00070190f, 0.00738525f,
813 -0.03073120f, 0.10848999f, 0.97686768f, -0.08123779f, 0.02383423f, -0.00518799f,
814 -0.00061035f, 0.00680542f, -0.02835083f, 0.10012817f, 0.97991943f, -0.07666016f,
815 0.02252197f, -0.00494385f, -0.00054932f, 0.00622559f, -0.02603149f, 0.09185791f,
816 0.98278809f, -0.07192993f, 0.02114868f, -0.00463867f, -0.00048828f, 0.00564575f,
817 -0.02374268f, 0.08370972f, 0.98541260f, -0.06707764f, 0.01971436f, -0.00436401f,
818 -0.00042725f, 0.00506592f, -0.02145386f, 0.07568359f, 0.98782349f, -0.06207275f,
819 0.01828003f, -0.00405884f, -0.00039673f, 0.00451660f, -0.01922607f, 0.06777954f,
820 0.99002075f, -0.05691528f, 0.01678467f, -0.00375366f, -0.00033569f, 0.00396729f,
821 -0.01699829f, 0.05999756f, 0.99200439f, -0.05163574f, 0.01522827f, -0.00341797f,
822 -0.00027466f, 0.00344849f, -0.01483154f, 0.05233765f, 0.99377441f, -0.04620361f,
823 0.01364136f, -0.00308228f, -0.00024414f, 0.00292969f, -0.01266479f, 0.04483032f,
824 0.99533081f, -0.04061890f, 0.01202393f, -0.00274658f, -0.00018311f, 0.00241089f,
825 -0.01055908f, 0.03741455f, 0.99664307f, -0.03488159f, 0.01037598f, -0.00238037f,
826 -0.00015259f, 0.00192261f, -0.00845337f, 0.03018188f, 0.99774170f, -0.02899170f,
827 0.00866699f, -0.00201416f, -0.00009155f, 0.00143433f, -0.00640869f, 0.02304077f,
828 0.99862671f, -0.02297974f, 0.00689697f, -0.00161743f, -0.00006104f, 0.00097656f,
829 -0.00439453f, 0.01605225f, 0.99929810f, -0.01684570f, 0.00512695f, -0.00122070f,
830 -0.00003052f, 0.00051880f, -0.00241089f, 0.00918579f, 0.99975586f, -0.01052856f,
831 0.00329590f, -0.00079346f, 0.00000000f, 0.00006104f, -0.00048828f, 0.00247192f,
832 0.99996948f, -0.00408936f, 0.00143433f, -0.00036621f,
833 };
834
835 const auto get_lut = [&]() -> std::span<const f32> {
836 if (sample_rate_ratio <= 1.0f) {
837 return std::span<const f32>(lut2.data(), lut2.size());
838 } else if (sample_rate_ratio < 1.3f) {
839 return std::span<const f32>(lut1.data(), lut1.size());
840 } else {
841 return std::span<const f32>(lut0.data(), lut0.size());
842 }
843 };
844
845 auto lut{get_lut()};
846 u32 read_index{0};
847 for (u32 i = 0; i < samples_to_write; i++) {
848 const auto lut_index{(fraction.get_frac() >> 8) * 8};
849 const Common::FixedPoint<56, 8> sample0{input[read_index + 0] * lut[lut_index + 0]};
850 const Common::FixedPoint<56, 8> sample1{input[read_index + 1] * lut[lut_index + 1]};
851 const Common::FixedPoint<56, 8> sample2{input[read_index + 2] * lut[lut_index + 2]};
852 const Common::FixedPoint<56, 8> sample3{input[read_index + 3] * lut[lut_index + 3]};
853 const Common::FixedPoint<56, 8> sample4{input[read_index + 4] * lut[lut_index + 4]};
854 const Common::FixedPoint<56, 8> sample5{input[read_index + 5] * lut[lut_index + 5]};
855 const Common::FixedPoint<56, 8> sample6{input[read_index + 6] * lut[lut_index + 6]};
856 const Common::FixedPoint<56, 8> sample7{input[read_index + 7] * lut[lut_index + 7]};
857 output[i] = (sample0 + sample1 + sample2 + sample3 + sample4 + sample5 + sample6 + sample7)
858 .to_int_floor();
859 fraction += sample_rate_ratio;
860 read_index += static_cast<u32>(fraction.to_int_floor());
861 fraction.clear_int();
862 }
863}
864
865void Resample(std::span<s32> output, std::span<const s16> input,
866 const Common::FixedPoint<49, 15>& sample_rate_ratio,
867 Common::FixedPoint<49, 15>& fraction, const u32 samples_to_write,
868 const SrcQuality src_quality) {
869
870 switch (src_quality) {
871 case SrcQuality::Low:
872 ResampleLowQuality(output, input, sample_rate_ratio, fraction, samples_to_write);
873 break;
874 case SrcQuality::Medium:
875 ResampleNormalQuality(output, input, sample_rate_ratio, fraction, samples_to_write);
876 break;
877 case SrcQuality::High:
878 ResampleHighQuality(output, input, sample_rate_ratio, fraction, samples_to_write);
879 break;
880 }
881}
882
883} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/resample/resample.h b/src/audio_core/renderer/command/resample/resample.h
new file mode 100644
index 000000000..ba9209b82
--- /dev/null
+++ b/src/audio_core/renderer/command/resample/resample.h
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "audio_core/common/common.h"
9#include "common/common_types.h"
10#include "common/fixed_point.h"
11
12namespace AudioCore::AudioRenderer {
13/**
14 * Resample an input buffer into an output buffer, according to the sample_rate_ratio.
15 *
16 * @param output - Output buffer.
17 * @param input - Input buffer.
18 * @param sample_rate_ratio - Ratio for resampling.
19 e.g 32000/48000 = 0.666 input samples read per output.
20 * @param fraction - Current read fraction, written to and should be passed back in for
21 * multiple calls.
22 * @param samples_to_write - Number of samples to write.
23 * @param src_quality - Resampling quality.
24 */
25void Resample(std::span<s32> output, std::span<const s16> input,
26 const Common::FixedPoint<49, 15>& sample_rate_ratio,
27 Common::FixedPoint<49, 15>& fraction, u32 samples_to_write, SrcQuality src_quality);
28
29} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/resample/upsample.cpp b/src/audio_core/renderer/command/resample/upsample.cpp
new file mode 100644
index 000000000..6c3ff31f7
--- /dev/null
+++ b/src/audio_core/renderer/command/resample/upsample.cpp
@@ -0,0 +1,262 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <array>
5
6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/resample/upsample.h"
8#include "audio_core/renderer/upsampler/upsampler_info.h"
9
10namespace AudioCore::AudioRenderer {
11/**
12 * Upsampling impl. Input must be 8K, 16K or 32K, output is 48K.
13 *
14 * @param output - Output buffer.
15 * @param input - Input buffer.
16 * @param target_sample_count - Number of samples for output.
17 * @param state - Upsampler state, updated each call.
18 */
19static void SrcProcessFrame(std::span<s32> output, std::span<const s32> input,
20 const u32 target_sample_count, const u32 source_sample_count,
21 UpsamplerState* state) {
22 constexpr u32 WindowSize = 10;
23 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow1{
24 51.93359375f, -18.80078125f, 9.73046875f, -5.33203125f, 2.84375f,
25 -1.41015625f, 0.62109375f, -0.2265625f, 0.0625f, -0.00390625f,
26 };
27 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow2{
28 105.35546875f, -24.52734375f, 11.9609375f, -6.515625f, 3.52734375f,
29 -1.796875f, 0.828125f, -0.32421875f, 0.1015625f, -0.015625f,
30 };
31 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow3{
32 122.08203125f, -16.47656250f, 7.68359375f, -4.15625000f, 2.26171875f,
33 -1.16796875f, 0.54687500f, -0.22265625f, 0.07421875f, -0.01171875f,
34 };
35 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow4{
36 23.73437500f, -9.62109375f, 5.07812500f, -2.78125000f, 1.46875000f,
37 -0.71484375f, 0.30859375f, -0.10546875f, 0.02734375f, 0.00000000f,
38 };
39 constexpr std::array<Common::FixedPoint<24, 8>, WindowSize> SincWindow5{
40 80.62500000f, -24.67187500f, 12.44921875f, -6.80859375f, 3.66406250f,
41 -1.83984375f, 0.83203125f, -0.31640625f, 0.09375000f, -0.01171875f,
42 };
43
44 if (!state->initialized) {
45 switch (source_sample_count) {
46 case 40:
47 state->window_size = WindowSize;
48 state->ratio = 6.0f;
49 state->history.fill(0);
50 break;
51
52 case 80:
53 state->window_size = WindowSize;
54 state->ratio = 3.0f;
55 state->history.fill(0);
56 break;
57
58 case 160:
59 state->window_size = WindowSize;
60 state->ratio = 1.5f;
61 state->history.fill(0);
62 break;
63
64 default:
65 LOG_ERROR(Service_Audio, "Invalid upsampling source count {}!", source_sample_count);
66 // This continues anyway, but let's assume 160 for sanity
67 state->window_size = WindowSize;
68 state->ratio = 1.5f;
69 state->history.fill(0);
70 break;
71 }
72
73 state->history_input_index = 0;
74 state->history_output_index = 9;
75 state->history_start_index = 0;
76 state->history_end_index = UpsamplerState::HistorySize - 1;
77 state->initialized = true;
78 }
79
80 if (target_sample_count == 0) {
81 return;
82 }
83
84 u32 read_index{0};
85
86 auto increment = [&]() -> void {
87 state->history[state->history_input_index] = input[read_index++];
88 state->history_input_index =
89 static_cast<u16>((state->history_input_index + 1) % UpsamplerState::HistorySize);
90 state->history_output_index =
91 static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize);
92 };
93
94 auto calculate_sample = [&state](std::span<const Common::FixedPoint<24, 8>> coeffs1,
95 std::span<const Common::FixedPoint<24, 8>> coeffs2) -> s32 {
96 auto output_index{state->history_output_index};
97 auto start_pos{output_index - state->history_start_index + 1U};
98 auto end_pos{10U};
99
100 if (start_pos < 10) {
101 end_pos = start_pos;
102 }
103
104 u64 prev_contrib{0};
105 u32 coeff_index{0};
106 for (; coeff_index < end_pos; coeff_index++, output_index--) {
107 prev_contrib += static_cast<u64>(state->history[output_index].to_raw()) *
108 coeffs1[coeff_index].to_raw();
109 }
110
111 auto end_index{state->history_end_index};
112 for (; start_pos < 9; start_pos++, coeff_index++, end_index--) {
113 prev_contrib += static_cast<u64>(state->history[end_index].to_raw()) *
114 coeffs1[coeff_index].to_raw();
115 }
116
117 output_index =
118 static_cast<u16>((state->history_output_index + 1) % UpsamplerState::HistorySize);
119 start_pos = state->history_end_index - output_index + 1U;
120 end_pos = 10U;
121
122 if (start_pos < 10) {
123 end_pos = start_pos;
124 }
125
126 u64 next_contrib{0};
127 coeff_index = 0;
128 for (; coeff_index < end_pos; coeff_index++, output_index++) {
129 next_contrib += static_cast<u64>(state->history[output_index].to_raw()) *
130 coeffs2[coeff_index].to_raw();
131 }
132
133 auto start_index{state->history_start_index};
134 for (; start_pos < 9; start_pos++, start_index++, coeff_index++) {
135 next_contrib += static_cast<u64>(state->history[start_index].to_raw()) *
136 coeffs2[coeff_index].to_raw();
137 }
138
139 return static_cast<s32>(((prev_contrib >> 15) + (next_contrib >> 15)) >> 8);
140 };
141
142 switch (state->ratio.to_int_floor()) {
143 // 40 -> 240
144 case 6:
145 for (u32 write_index = 0; write_index < target_sample_count; write_index++) {
146 switch (state->sample_index) {
147 case 0:
148 increment();
149 output[write_index] = state->history[state->history_output_index].to_int_floor();
150 break;
151
152 case 1:
153 output[write_index] = calculate_sample(SincWindow3, SincWindow4);
154 break;
155
156 case 2:
157 output[write_index] = calculate_sample(SincWindow2, SincWindow1);
158 break;
159
160 case 3:
161 output[write_index] = calculate_sample(SincWindow5, SincWindow5);
162 break;
163
164 case 4:
165 output[write_index] = calculate_sample(SincWindow1, SincWindow2);
166 break;
167
168 case 5:
169 output[write_index] = calculate_sample(SincWindow4, SincWindow3);
170 break;
171 }
172 state->sample_index = static_cast<u8>((state->sample_index + 1) % 6);
173 }
174 break;
175
176 // 80 -> 240
177 case 3:
178 for (u32 write_index = 0; write_index < target_sample_count; write_index++) {
179 switch (state->sample_index) {
180 case 0:
181 increment();
182 output[write_index] = state->history[state->history_output_index].to_int_floor();
183 break;
184
185 case 1:
186 output[write_index] = calculate_sample(SincWindow2, SincWindow1);
187 break;
188
189 case 2:
190 output[write_index] = calculate_sample(SincWindow1, SincWindow2);
191 break;
192 }
193 state->sample_index = static_cast<u8>((state->sample_index + 1) % 3);
194 }
195 break;
196
197 // 160 -> 240
198 default:
199 for (u32 write_index = 0; write_index < target_sample_count; write_index++) {
200 switch (state->sample_index) {
201 case 0:
202 increment();
203 output[write_index] = state->history[state->history_output_index].to_int_floor();
204 break;
205
206 case 1:
207 output[write_index] = calculate_sample(SincWindow1, SincWindow2);
208 break;
209
210 case 2:
211 increment();
212 output[write_index] = calculate_sample(SincWindow2, SincWindow1);
213 break;
214 }
215 state->sample_index = static_cast<u8>((state->sample_index + 1) % 3);
216 }
217
218 break;
219 }
220}
221
222auto UpsampleCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
223 std::string& string) -> void {
224 string += fmt::format("UpsampleCommand\n\tsource_sample_count {} source_sample_rate {}",
225 source_sample_count, source_sample_rate);
226 const auto upsampler{reinterpret_cast<UpsamplerInfo*>(upsampler_info)};
227 if (upsampler != nullptr) {
228 string += fmt::format("\n\tUpsampler\n\t\tenabled {} sample count {}\n\tinputs: ",
229 upsampler->enabled, upsampler->sample_count);
230 for (u32 i = 0; i < upsampler->input_count; i++) {
231 string += fmt::format("{:02X}, ", upsampler->inputs[i]);
232 }
233 }
234 string += "\n";
235}
236
237void UpsampleCommand::Process(const ADSP::CommandListProcessor& processor) {
238 const auto info{reinterpret_cast<UpsamplerInfo*>(upsampler_info)};
239 const auto input_count{std::min(info->input_count, buffer_count)};
240 const std::span<const s16> inputs_{reinterpret_cast<const s16*>(inputs), input_count};
241
242 for (u32 i = 0; i < input_count; i++) {
243 const auto channel{inputs_[i]};
244
245 if (channel >= 0 && channel < static_cast<s16>(processor.buffer_count)) {
246 auto state{&info->states[i]};
247 std::span<s32> output{
248 reinterpret_cast<s32*>(samples_buffer + info->sample_count * channel * sizeof(s32)),
249 info->sample_count};
250 auto input{processor.mix_buffers.subspan(channel * processor.sample_count,
251 processor.sample_count)};
252
253 SrcProcessFrame(output, input, info->sample_count, source_sample_count, state);
254 }
255 }
256}
257
258bool UpsampleCommand::Verify(const ADSP::CommandListProcessor& processor) {
259 return true;
260}
261
262} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/resample/upsample.h b/src/audio_core/renderer/command/resample/upsample.h
new file mode 100644
index 000000000..bfc94e8af
--- /dev/null
+++ b/src/audio_core/renderer/command/resample/upsample.h
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for upsampling a mix buffer to 48Khz.
18 * Input must be 8Khz, 16Khz or 32Khz, and output will be 48Khz.
19 */
20struct UpsampleCommand : ICommand {
21 /**
22 * Print this command's information to a string.
23 *
24 * @param processor - The CommandListProcessor processing this command.
25 * @param string - The string to print into.
26 */
27 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
28
29 /**
30 * Process this command.
31 *
32 * @param processor - The CommandListProcessor processing this command.
33 */
34 void Process(const ADSP::CommandListProcessor& processor) override;
35
36 /**
37 * Verify this command's data is valid.
38 *
39 * @param processor - The CommandListProcessor processing this command.
40 * @return True if the command is valid, otherwise false.
41 */
42 bool Verify(const ADSP::CommandListProcessor& processor) override;
43
44 /// Pointer to the output samples buffer.
45 CpuAddr samples_buffer;
46 /// Pointer to input mix buffer indexes.
47 CpuAddr inputs;
48 /// Number of input mix buffers.
49 u32 buffer_count;
50 /// Unknown, unused.
51 u32 unk_20;
52 /// Source data sample count.
53 u32 source_sample_count;
54 /// Source data sample rate.
55 u32 source_sample_rate;
56 /// Pointer to the upsampler info for this command.
57 CpuAddr upsampler_info;
58};
59
60} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/sink/circular_buffer.cpp b/src/audio_core/renderer/command/sink/circular_buffer.cpp
new file mode 100644
index 000000000..ded5afc94
--- /dev/null
+++ b/src/audio_core/renderer/command/sink/circular_buffer.cpp
@@ -0,0 +1,48 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <vector>
5
6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/sink/circular_buffer.h"
8#include "core/memory.h"
9
10namespace AudioCore::AudioRenderer {
11
12void CircularBufferSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
13 std::string& string) {
14 string += fmt::format(
15 "CircularBufferSinkCommand\n\tinput_count {} ring size {:04X} ring pos {:04X}\n\tinputs: ",
16 input_count, size, pos);
17 for (u32 i = 0; i < input_count; i++) {
18 string += fmt::format("{:02X}, ", inputs[i]);
19 }
20 string += "\n";
21}
22
23void CircularBufferSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
24 constexpr s32 min{std::numeric_limits<s16>::min()};
25 constexpr s32 max{std::numeric_limits<s16>::max()};
26
27 std::vector<s16> output(processor.sample_count);
28 for (u32 channel = 0; channel < input_count; channel++) {
29 auto input{processor.mix_buffers.subspan(inputs[channel] * processor.sample_count,
30 processor.sample_count)};
31 for (u32 sample_index = 0; sample_index < processor.sample_count; sample_index++) {
32 output[sample_index] = static_cast<s16>(std::clamp(input[sample_index], min, max));
33 }
34
35 processor.memory->WriteBlockUnsafe(address + pos, output.data(),
36 output.size() * sizeof(s16));
37 pos += static_cast<u32>(processor.sample_count * sizeof(s16));
38 if (pos >= size) {
39 pos = 0;
40 }
41 }
42}
43
44bool CircularBufferSinkCommand::Verify(const ADSP::CommandListProcessor& processor) {
45 return true;
46}
47
48} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/sink/circular_buffer.h b/src/audio_core/renderer/command/sink/circular_buffer.h
new file mode 100644
index 000000000..e7d5be26e
--- /dev/null
+++ b/src/audio_core/renderer/command/sink/circular_buffer.h
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "audio_core/renderer/command/icommand.h"
9#include "common/common_types.h"
10
11namespace AudioCore::AudioRenderer {
12namespace ADSP {
13class CommandListProcessor;
14}
15
16/**
17 * AudioRenderer command for sinking samples to a circular buffer.
18 */
19struct CircularBufferSinkCommand : ICommand {
20 /**
21 * Print this command's information to a string.
22 *
23 * @param processor - The CommandListProcessor processing this command.
24 * @param string - The string to print into.
25 */
26 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
27
28 /**
29 * Process this command.
30 *
31 * @param processor - The CommandListProcessor processing this command.
32 */
33 void Process(const ADSP::CommandListProcessor& processor) override;
34
35 /**
36 * Verify this command's data is valid.
37 *
38 * @param processor - The CommandListProcessor processing this command.
39 * @return True if the command is valid, otherwise false.
40 */
41 bool Verify(const ADSP::CommandListProcessor& processor) override;
42
43 /// Number of input mix buffers
44 u32 input_count;
45 /// Input mix buffer indexes
46 std::array<s16, MaxChannels> inputs;
47 /// Circular buffer address
48 CpuAddr address;
49 /// Circular buffer size
50 u32 size;
51 /// Current buffer offset
52 u32 pos;
53};
54
55} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/sink/device.cpp b/src/audio_core/renderer/command/sink/device.cpp
new file mode 100644
index 000000000..47e0c6722
--- /dev/null
+++ b/src/audio_core/renderer/command/sink/device.cpp
@@ -0,0 +1,55 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5
6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/sink/device.h"
8#include "audio_core/sink/sink.h"
9
10namespace AudioCore::AudioRenderer {
11
12void DeviceSinkCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
13 std::string& string) {
14 string += fmt::format("DeviceSinkCommand\n\t{} session {} input_count {}\n\tinputs: ",
15 std::string_view(name), session_id, input_count);
16 for (u32 i = 0; i < input_count; i++) {
17 string += fmt::format("{:02X}, ", inputs[i]);
18 }
19 string += "\n";
20}
21
22void DeviceSinkCommand::Process(const ADSP::CommandListProcessor& processor) {
23 constexpr s32 min = std::numeric_limits<s16>::min();
24 constexpr s32 max = std::numeric_limits<s16>::max();
25
26 auto stream{processor.GetOutputSinkStream()};
27 stream->SetSystemChannels(input_count);
28
29 Sink::SinkBuffer out_buffer{
30 .frames{TargetSampleCount},
31 .frames_played{0},
32 .tag{0},
33 .consumed{false},
34 };
35
36 std::vector<s16> samples(out_buffer.frames * input_count);
37
38 for (u32 channel = 0; channel < input_count; channel++) {
39 const auto offset{inputs[channel] * out_buffer.frames};
40
41 for (u32 index = 0; index < out_buffer.frames; index++) {
42 samples[index * input_count + channel] =
43 static_cast<s16>(std::clamp(sample_buffer[offset + index], min, max));
44 }
45 }
46
47 out_buffer.tag = reinterpret_cast<u64>(samples.data());
48 stream->AppendBuffer(out_buffer, samples);
49}
50
51bool DeviceSinkCommand::Verify(const ADSP::CommandListProcessor& processor) {
52 return true;
53}
54
55} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/command/sink/device.h b/src/audio_core/renderer/command/sink/device.h
new file mode 100644
index 000000000..1099bcf8c
--- /dev/null
+++ b/src/audio_core/renderer/command/sink/device.h
@@ -0,0 +1,57 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8#include <string>
9
10#include "audio_core/renderer/command/icommand.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14namespace ADSP {
15class CommandListProcessor;
16}
17
18/**
19 * AudioRenderer command for sinking samples to an output device.
20 */
21struct DeviceSinkCommand : ICommand {
22 /**
23 * Print this command's information to a string.
24 *
25 * @param processor - The CommandListProcessor processing this command.
26 * @param string - The string to print into.
27 */
28 void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
29
30 /**
31 * Process this command.
32 *
33 * @param processor - The CommandListProcessor processing this command.
34 */
35 void Process(const ADSP::CommandListProcessor& processor) override;
36
37 /**
38 * Verify this command's data is valid.
39 *
40 * @param processor - The CommandListProcessor processing this command.
41 * @return True if the command is valid, otherwise false.
42 */
43 bool Verify(const ADSP::CommandListProcessor& processor) override;
44
45 /// Device name
46 char name[0x100];
47 /// System session id (unused)
48 s32 session_id;
49 /// Sample buffer to sink
50 std::span<s32> sample_buffer;
51 /// Number of input channels
52 u32 input_count;
53 /// Mix buffer indexes for each channel
54 std::array<s16, MaxChannels> inputs;
55};
56
57} // namespace AudioCore::AudioRenderer