summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/CMakeLists.txt16
-rw-r--r--src/audio_core/algorithm/interpolate.cpp32
-rw-r--r--src/audio_core/algorithm/interpolate.h3
-rw-r--r--src/audio_core/audio_renderer.cpp543
-rw-r--r--src/audio_core/audio_renderer.h224
-rw-r--r--src/audio_core/behavior_info.cpp79
-rw-r--r--src/audio_core/behavior_info.h52
-rw-r--r--src/audio_core/command_generator.cpp976
-rw-r--r--src/audio_core/command_generator.h103
-rw-r--r--src/audio_core/common.h65
-rw-r--r--src/audio_core/cubeb_sink.cpp18
-rw-r--r--src/audio_core/effect_context.cpp299
-rw-r--r--src/audio_core/effect_context.h322
-rw-r--r--src/audio_core/info_updater.cpp517
-rw-r--r--src/audio_core/info_updater.h58
-rw-r--r--src/audio_core/memory_pool.cpp62
-rw-r--r--src/audio_core/memory_pool.h53
-rw-r--r--src/audio_core/mix_context.cpp296
-rw-r--r--src/audio_core/mix_context.h114
-rw-r--r--src/audio_core/sink_context.cpp31
-rw-r--r--src/audio_core/sink_context.h89
-rw-r--r--src/audio_core/splitter_context.cpp617
-rw-r--r--src/audio_core/splitter_context.h221
-rw-r--r--src/audio_core/stream.cpp6
-rw-r--r--src/audio_core/voice_context.cpp526
-rw-r--r--src/audio_core/voice_context.h296
-rw-r--r--src/core/hle/service/audio/audren_u.cpp149
27 files changed, 5048 insertions, 719 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 5ef38a337..cb00ef60e 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -12,16 +12,32 @@ add_library(audio_core STATIC
12 buffer.h 12 buffer.h
13 codec.cpp 13 codec.cpp
14 codec.h 14 codec.h
15 command_generator.cpp
16 command_generator.h
15 common.h 17 common.h
18 effect_context.cpp
19 effect_context.h
20 info_updater.cpp
21 info_updater.h
22 memory_pool.cpp
23 memory_pool.h
24 mix_context.cpp
25 mix_context.h
16 null_sink.h 26 null_sink.h
17 sink.h 27 sink.h
28 sink_context.cpp
29 sink_context.h
18 sink_details.cpp 30 sink_details.cpp
19 sink_details.h 31 sink_details.h
20 sink_stream.h 32 sink_stream.h
33 splitter_context.cpp
34 splitter_context.h
21 stream.cpp 35 stream.cpp
22 stream.h 36 stream.h
23 time_stretch.cpp 37 time_stretch.cpp
24 time_stretch.h 38 time_stretch.h
39 voice_context.cpp
40 voice_context.h
25 41
26 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h> 42 $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
27) 43)
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp
index 49ab9d3e1..689a54508 100644
--- a/src/audio_core/algorithm/interpolate.cpp
+++ b/src/audio_core/algorithm/interpolate.cpp
@@ -197,4 +197,36 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
197 return output; 197 return output;
198} 198}
199 199
200void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size_t sample_count) {
201 const std::array<s16, 512>& lut = [pitch] {
202 if (pitch > 0xaaaa) {
203 return curve_lut0;
204 }
205 if (pitch <= 0x8000) {
206 return curve_lut1;
207 }
208 return curve_lut2;
209 }();
210
211 std::size_t index{};
212
213 for (std::size_t i = 0; i < sample_count; i++) {
214 const std::size_t lut_index{(static_cast<std::size_t>(fraction) >> 8) * 4};
215 const auto l0 = lut[lut_index + 0];
216 const auto l1 = lut[lut_index + 1];
217 const auto l2 = lut[lut_index + 2];
218 const auto l3 = lut[lut_index + 3];
219
220 const auto s0 = static_cast<s32>(input[index]);
221 const auto s1 = static_cast<s32>(input[index + 1]);
222 const auto s2 = static_cast<s32>(input[index + 2]);
223 const auto s3 = static_cast<s32>(input[index + 3]);
224
225 output[i] = (l0 * s0 + l1 * s1 + l2 * s2 + l3 * s3) >> 15;
226 fraction += pitch;
227 index += (fraction >> 15);
228 fraction &= 0x7fff;
229 }
230}
231
200} // namespace AudioCore 232} // namespace AudioCore
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h
index ab1a31754..d534077af 100644
--- a/src/audio_core/algorithm/interpolate.h
+++ b/src/audio_core/algorithm/interpolate.h
@@ -38,4 +38,7 @@ inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16>
38 return Interpolate(state, std::move(input), ratio); 38 return Interpolate(state, std::move(input), ratio);
39} 39}
40 40
41/// Nintendo Switchs DSP resampling algorithm. Based on a single channel
42void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size_t sample_count);
43
41} // namespace AudioCore 44} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index d64452617..56dc892b1 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -2,95 +2,49 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <vector>
5#include "audio_core/algorithm/interpolate.h" 6#include "audio_core/algorithm/interpolate.h"
6#include "audio_core/audio_out.h" 7#include "audio_core/audio_out.h"
7#include "audio_core/audio_renderer.h" 8#include "audio_core/audio_renderer.h"
8#include "audio_core/codec.h" 9#include "audio_core/codec.h"
9#include "audio_core/common.h" 10#include "audio_core/common.h"
11#include "audio_core/info_updater.h"
12#include "audio_core/voice_context.h"
10#include "common/assert.h" 13#include "common/assert.h"
11#include "common/logging/log.h" 14#include "common/logging/log.h"
12#include "core/core.h" 15#include "core/core.h"
13#include "core/hle/kernel/writable_event.h" 16#include "core/hle/kernel/writable_event.h"
14#include "core/memory.h" 17#include "core/memory.h"
18#include "core/settings.h"
15 19
16namespace AudioCore { 20namespace AudioCore {
17
18constexpr u32 STREAM_SAMPLE_RATE{48000};
19constexpr u32 STREAM_NUM_CHANNELS{2};
20using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>;
21class AudioRenderer::VoiceState {
22public:
23 bool IsPlaying() const {
24 return is_in_use && info.play_state == PlayState::Started;
25 }
26
27 const VoiceOutStatus& GetOutStatus() const {
28 return out_status;
29 }
30
31 const VoiceInfo& GetInfo() const {
32 return info;
33 }
34
35 VoiceInfo& GetInfo() {
36 return info;
37 }
38
39 void SetWaveIndex(std::size_t index);
40 std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory,
41 const VoiceChannelHolder& voice_resources);
42 void UpdateState();
43 void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources);
44
45private:
46 bool is_in_use{};
47 bool is_refresh_pending{};
48 std::size_t wave_index{};
49 std::size_t offset{};
50 Codec::ADPCMState adpcm_state{};
51 InterpolationState interp_state{};
52 std::vector<s16> samples;
53 VoiceOutStatus out_status{};
54 VoiceInfo info{};
55};
56
57class AudioRenderer::EffectState {
58public:
59 const EffectOutStatus& GetOutStatus() const {
60 return out_status;
61 }
62
63 const EffectInStatus& GetInfo() const {
64 return info;
65 }
66
67 EffectInStatus& GetInfo() {
68 return info;
69 }
70
71 void UpdateState(Core::Memory::Memory& memory);
72
73private:
74 EffectOutStatus out_status{};
75 EffectInStatus info{};
76};
77
78AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 21AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
79 AudioRendererParameter params, 22 AudioCommon::AudioRendererParameter params,
80 std::shared_ptr<Kernel::WritableEvent> buffer_event, 23 std::shared_ptr<Kernel::WritableEvent> buffer_event,
81 std::size_t instance_number) 24 std::size_t instance_number)
82 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), 25 : worker_params{params}, buffer_event{buffer_event},
83 voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} { 26 memory_pool_info(params.effect_count + params.voice_count * 4),
27 voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
28 sink_context(params.sink_count), splitter_context(),
29 voices(params.voice_count), memory{memory_},
30 command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
31 memory),
32 temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) {
84 behavior_info.SetUserRevision(params.revision); 33 behavior_info.SetUserRevision(params.revision);
34 splitter_context.Initialize(behavior_info, params.splitter_count,
35 params.num_splitter_send_channels);
36 mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
85 audio_out = std::make_unique<AudioCore::AudioOut>(); 37 audio_out = std::make_unique<AudioCore::AudioOut>();
86 stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, 38 stream =
87 fmt::format("AudioRenderer-Instance{}", instance_number), 39 audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
88 [=]() { buffer_event->Signal(); }); 40 fmt::format("AudioRenderer-Instance{}", instance_number),
41 [=]() { buffer_event->Signal(); });
89 audio_out->StartStream(stream); 42 audio_out->StartStream(stream);
90 43
91 QueueMixedBuffer(0); 44 QueueMixedBuffer(0);
92 QueueMixedBuffer(1); 45 QueueMixedBuffer(1);
93 QueueMixedBuffer(2); 46 QueueMixedBuffer(2);
47 QueueMixedBuffer(3);
94} 48}
95 49
96AudioRenderer::~AudioRenderer() = default; 50AudioRenderer::~AudioRenderer() = default;
@@ -111,355 +65,200 @@ Stream::State AudioRenderer::GetStreamState() const {
111 return stream->GetState(); 65 return stream->GetState();
112} 66}
113 67
114ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { 68static constexpr s16 ClampToS16(s32 value) {
115 // Copy UpdateDataHeader struct 69 return static_cast<s16>(std::clamp(value, -32768, 32767));
116 UpdateDataHeader config{}; 70}
117 std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
118 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
119
120 if (!behavior_info.UpdateInput(input_params, sizeof(UpdateDataHeader))) {
121 LOG_ERROR(Audio, "Failed to update behavior info input parameters");
122 return Audren::ERR_INVALID_PARAMETERS;
123 }
124
125 // Copy MemoryPoolInfo structs
126 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
127 std::memcpy(mem_pool_info.data(),
128 input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
129 memory_pool_count * sizeof(MemoryPoolInfo));
130
131 // Copy voice resources
132 const std::size_t voice_resource_offset{sizeof(UpdateDataHeader) + config.behavior_size +
133 config.memory_pools_size};
134 std::memcpy(voice_resources.data(), input_params.data() + voice_resource_offset,
135 sizeof(VoiceResourceInformation) * voice_resources.size());
136
137 // Copy VoiceInfo structs
138 std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
139 config.memory_pools_size + config.voice_resource_size};
140 for (auto& voice : voices) {
141 std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo));
142 voice_offset += sizeof(VoiceInfo);
143 }
144
145 std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size +
146 config.memory_pools_size + config.voice_resource_size +
147 config.voices_size};
148 for (auto& effect : effects) {
149 std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus));
150 effect_offset += sizeof(EffectInStatus);
151 }
152
153 // Update memory pool state
154 std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
155 for (std::size_t index = 0; index < memory_pool.size(); ++index) {
156 if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
157 memory_pool[index].state = MemoryPoolStates::Attached;
158 } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
159 memory_pool[index].state = MemoryPoolStates::Detached;
160 }
161 }
162
163 // Update voices
164 for (auto& voice : voices) {
165 voice.UpdateState();
166 if (!voice.GetInfo().is_in_use) {
167 continue;
168 }
169 if (voice.GetInfo().is_new) {
170 voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
171 }
172 }
173
174 for (auto& effect : effects) {
175 effect.UpdateState(memory);
176 }
177
178 // Release previous buffers and queue next ones for playback
179 ReleaseAndQueueBuffers();
180
181 // Copy output header
182 UpdateDataHeader response_data{worker_params};
183 if (behavior_info.IsElapsedFrameCountSupported()) {
184 response_data.render_info = sizeof(RendererInfo);
185 response_data.total_size += sizeof(RendererInfo);
186 }
187
188 std::vector<u8> output_params(response_data.total_size);
189 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
190
191 // Copy output memory pool entries
192 std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
193 response_data.memory_pools_size);
194
195 // Copy output voice status
196 std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
197 for (const auto& voice : voices) {
198 std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
199 sizeof(VoiceOutStatus));
200 voice_out_status_offset += sizeof(VoiceOutStatus);
201 }
202 71
203 std::size_t effect_out_status_offset{ 72ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
204 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + 73 std::vector<u8>& output_params) {
205 response_data.voice_resource_size};
206 for (const auto& effect : effects) {
207 std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(),
208 sizeof(EffectOutStatus));
209 effect_out_status_offset += sizeof(EffectOutStatus);
210 }
211 74
212 // Update behavior info output 75 InfoUpdater info_updater{input_params, output_params, behavior_info};
213 const std::size_t behavior_out_status_offset{
214 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
215 response_data.effects_size + response_data.sinks_size +
216 response_data.performance_manager_size};
217 76
218 if (!behavior_info.UpdateOutput(output_params, behavior_out_status_offset)) { 77 if (!info_updater.UpdateBehaviorInfo(behavior_info)) {
219 LOG_ERROR(Audio, "Failed to update behavior info output parameters"); 78 LOG_ERROR(Audio, "Failed to update behavior info input parameters");
220 return Audren::ERR_INVALID_PARAMETERS; 79 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
221 } 80 }
222 81
223 if (behavior_info.IsElapsedFrameCountSupported()) { 82 if (!info_updater.UpdateMemoryPools(memory_pool_info)) {
224 const std::size_t renderer_info_offset{ 83 LOG_ERROR(Audio, "Failed to update memory pool parameters");
225 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + 84 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
226 response_data.effects_size + response_data.sinks_size +
227 response_data.performance_manager_size + response_data.behavior_size};
228 RendererInfo renderer_info{};
229 renderer_info.elasped_frame_count = elapsed_frame_count;
230 std::memcpy(output_params.data() + renderer_info_offset, &renderer_info,
231 sizeof(RendererInfo));
232 } 85 }
233 86
234 return MakeResult(output_params); 87 if (!info_updater.UpdateVoiceChannelResources(voice_context)) {
235} 88 LOG_ERROR(Audio, "Failed to update voice channel resource parameters");
236 89 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
237void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
238 wave_index = index & 3;
239 is_refresh_pending = true;
240}
241
242std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(
243 std::size_t sample_count, Core::Memory::Memory& memory,
244 const VoiceChannelHolder& voice_resources) {
245 if (!IsPlaying()) {
246 return {};
247 } 90 }
248 91
249 if (is_refresh_pending) { 92 if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) {
250 RefreshBuffer(memory, voice_resources); 93 LOG_ERROR(Audio, "Failed to update voice parameters");
94 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
251 } 95 }
252 96
253 const std::size_t max_size{samples.size() - offset}; 97 // TODO(ogniK): Deal with stopped audio renderer but updates still taking place
254 const std::size_t dequeue_offset{offset}; 98 if (!info_updater.UpdateEffects(effect_context, true)) {
255 std::size_t size{sample_count * STREAM_NUM_CHANNELS}; 99 LOG_ERROR(Audio, "Failed to update effect parameters");
256 if (size > max_size) { 100 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
257 size = max_size;
258 } 101 }
259 102
260 out_status.played_sample_count += size / STREAM_NUM_CHANNELS; 103 if (behavior_info.IsSplitterSupported()) {
261 offset += size; 104 if (!info_updater.UpdateSplitterInfo(splitter_context)) {
262 105 LOG_ERROR(Audio, "Failed to update splitter parameters");
263 const auto& wave_buffer{info.wave_buffer[wave_index]}; 106 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
264 if (offset == samples.size()) {
265 offset = 0;
266
267 if (!wave_buffer.is_looping && wave_buffer.buffer_sz) {
268 SetWaveIndex(wave_index + 1);
269 }
270
271 if (wave_buffer.buffer_sz) {
272 out_status.wave_buffer_consumed++;
273 }
274
275 if (wave_buffer.end_of_stream || wave_buffer.buffer_sz == 0) {
276 info.play_state = PlayState::Paused;
277 } 107 }
278 } 108 }
279 109
280 return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size}; 110 auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
281} 111 splitter_context, effect_context);
282 112
283void AudioRenderer::VoiceState::UpdateState() { 113 if (mix_result.IsError()) {
284 if (is_in_use && !info.is_in_use) { 114 LOG_ERROR(Audio, "Failed to update mix parameters");
285 // No longer in use, reset state 115 return mix_result;
286 is_refresh_pending = true;
287 wave_index = 0;
288 offset = 0;
289 out_status = {};
290 } 116 }
291 is_in_use = info.is_in_use;
292}
293 117
294void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory, 118 // TODO(ogniK): Sinks
295 const VoiceChannelHolder& voice_resources) { 119 if (!info_updater.UpdateSinks(sink_context)) {
296 const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; 120 LOG_ERROR(Audio, "Failed to update sink parameters");
297 const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; 121 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
298 std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
299 memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size);
300
301 switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
302 case Codec::PcmFormat::Int16: {
303 // PCM16 is played as-is
304 break;
305 }
306 case Codec::PcmFormat::Adpcm: {
307 // Decode ADPCM to PCM16
308 Codec::ADPCM_Coeff coeffs;
309 memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
310 new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
311 new_samples.size() * sizeof(s16), coeffs, adpcm_state);
312 break;
313 } 122 }
314 default:
315 UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
316 break;
317 }
318
319 switch (info.channel_count) {
320 case 1: {
321 // 1 channel is upsampled to 2 channel
322 samples.resize(new_samples.size() * 2);
323 123
324 for (std::size_t index = 0; index < new_samples.size(); ++index) { 124 // TODO(ogniK): Performance buffer
325 auto sample = static_cast<float>(new_samples[index]); 125 if (!info_updater.UpdatePerformanceBuffer()) {
326 if (voice_resources[0]->in_use) { 126 LOG_ERROR(Audio, "Failed to update performance buffer parameters");
327 sample *= voice_resources[0]->mix_volumes[0]; 127 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
328 }
329
330 samples[index * 2] = static_cast<s16>(sample * info.volume);
331 samples[index * 2 + 1] = static_cast<s16>(sample * info.volume);
332 }
333 break;
334 } 128 }
335 case 2: {
336 // 2 channel is played as is
337 samples = std::move(new_samples);
338 const std::size_t sample_count = (samples.size() / 2);
339 for (std::size_t index = 0; index < sample_count; ++index) {
340 const std::size_t index_l = index * 2;
341 const std::size_t index_r = index * 2 + 1;
342
343 auto sample_l = static_cast<float>(samples[index_l]);
344 auto sample_r = static_cast<float>(samples[index_r]);
345
346 if (voice_resources[0]->in_use) {
347 sample_l *= voice_resources[0]->mix_volumes[0];
348 }
349
350 if (voice_resources[1]->in_use) {
351 sample_r *= voice_resources[1]->mix_volumes[1];
352 }
353 129
354 samples[index_l] = static_cast<s16>(sample_l * info.volume); 130 if (!info_updater.UpdateErrorInfo(behavior_info)) {
355 samples[index_r] = static_cast<s16>(sample_r * info.volume); 131 LOG_ERROR(Audio, "Failed to update error info");
356 } 132 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
357 break;
358 } 133 }
359 case 6: {
360 samples.resize((new_samples.size() / 6) * 2);
361 const std::size_t sample_count = samples.size() / 2;
362
363 for (std::size_t index = 0; index < sample_count; ++index) {
364 auto FL = static_cast<float>(new_samples[index * 6]);
365 auto FR = static_cast<float>(new_samples[index * 6 + 1]);
366 auto FC = static_cast<float>(new_samples[index * 6 + 2]);
367 auto BL = static_cast<float>(new_samples[index * 6 + 4]);
368 auto BR = static_cast<float>(new_samples[index * 6 + 5]);
369
370 if (voice_resources[0]->in_use) {
371 FL *= voice_resources[0]->mix_volumes[0];
372 }
373 if (voice_resources[1]->in_use) {
374 FR *= voice_resources[1]->mix_volumes[1];
375 }
376 if (voice_resources[2]->in_use) {
377 FC *= voice_resources[2]->mix_volumes[2];
378 }
379 if (voice_resources[4]->in_use) {
380 BL *= voice_resources[4]->mix_volumes[4];
381 }
382 if (voice_resources[5]->in_use) {
383 BR *= voice_resources[5]->mix_volumes[5];
384 }
385 134
386 samples[index * 2] = 135 if (behavior_info.IsElapsedFrameCountSupported()) {
387 static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume); 136 if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) {
388 samples[index * 2 + 1] = 137 LOG_ERROR(Audio, "Failed to update renderer info");
389 static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume); 138 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
390 } 139 }
391 break;
392 }
393 default:
394 UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
395 break;
396 } 140 }
141 // TODO(ogniK): Statistics
397 142
398 // Only interpolate when necessary, expensive. 143 if (!info_updater.WriteOutputHeader()) {
399 if (GetInfo().sample_rate != STREAM_SAMPLE_RATE) { 144 LOG_ERROR(Audio, "Failed to write output header");
400 samples = Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, 145 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
401 STREAM_SAMPLE_RATE);
402 } 146 }
403 147
404 is_refresh_pending = false; 148 // TODO(ogniK): Check when all sections are implemented
405}
406 149
407void AudioRenderer::EffectState::UpdateState(Core::Memory::Memory& memory) { 150 if (!info_updater.CheckConsumedSize()) {
408 if (info.is_new) { 151 LOG_ERROR(Audio, "Audio buffers were not consumed!");
409 out_status.state = EffectStatus::New; 152 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
410 } else {
411 if (info.type == Effect::Aux) {
412 ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0,
413 "Aux buffers tried to update");
414 ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0,
415 "Aux buffers tried to update");
416 ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0,
417 "Aux buffers tried to update");
418 ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0,
419 "Aux buffers tried to update");
420 }
421 } 153 }
422}
423 154
424static constexpr s16 ClampToS16(s32 value) { 155 ReleaseAndQueueBuffers();
425 return static_cast<s16>(std::clamp(value, -32768, 32767)); 156
157 return RESULT_SUCCESS;
426} 158}
427 159
428void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { 160void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
429 constexpr std::size_t BUFFER_SIZE{512}; 161 command_generator.PreCommand();
162 // Clear mix buffers before our next operation
163 command_generator.ClearMixBuffers();
164
165 // If the splitter is not in use, sort our mixes
166 if (!splitter_context.UsingSplitter()) {
167 mix_context.SortInfo();
168 }
169 // Sort our voices
170 voice_context.SortInfo();
171
172 // Handle samples
173 command_generator.GenerateVoiceCommands();
174 command_generator.GenerateSubMixCommands();
175 command_generator.GenerateFinalMixCommands();
176
177 command_generator.PostCommand();
178 // Base sample size
179 std::size_t BUFFER_SIZE{worker_params.sample_count};
180 // Samples
430 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); 181 std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
431 182 // Make sure to clear our samples
432 for (auto& voice : voices) { 183 std::memset(buffer.data(), 0, buffer.size() * sizeof(s16));
433 if (!voice.IsPlaying()) { 184
434 continue; 185 if (sink_context.InUse()) {
435 } 186 const auto stream_channel_count = stream->GetNumChannels();
436 VoiceChannelHolder resources{}; 187 const auto buffer_offsets = sink_context.OutputBuffers();
437 for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) { 188 const auto channel_count = buffer_offsets.size();
438 const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel]; 189 const auto& final_mix = mix_context.GetFinalMixInfo();
439 resources[channel] = &voice_resources[channel_resource_id]; 190 const auto& in_params = final_mix.GetInParams();
191 std::vector<s32*> mix_buffers(channel_count);
192 for (std::size_t i = 0; i < channel_count; i++) {
193 mix_buffers[i] =
194 command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]);
440 } 195 }
441 196
442 std::size_t offset{}; 197 for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
443 s64 samples_remaining{BUFFER_SIZE}; 198 if (channel_count == 1) {
444 while (samples_remaining > 0) { 199 const auto sample = ClampToS16(mix_buffers[0][i]);
445 const std::vector<s16> samples{ 200 buffer[i * stream_channel_count + 0] = sample;
446 voice.DequeueSamples(samples_remaining, memory, resources)}; 201 if (stream_channel_count > 1) {
447 202 buffer[i * stream_channel_count + 1] = sample;
448 if (samples.empty()) { 203 }
449 break; 204 if (stream_channel_count == 6) {
450 } 205 buffer[i * stream_channel_count + 2] = sample;
451 206 buffer[i * stream_channel_count + 4] = sample;
452 samples_remaining -= samples.size() / stream->GetNumChannels(); 207 buffer[i * stream_channel_count + 5] = sample;
453 208 }
454 for (const auto& sample : samples) { 209 } else if (channel_count == 2) {
455 const s32 buffer_sample{buffer[offset]}; 210 const auto l_sample = ClampToS16(mix_buffers[0][i]);
456 buffer[offset++] = 211 const auto r_sample = ClampToS16(mix_buffers[1][i]);
457 ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume)); 212 if (stream_channel_count == 1) {
213 buffer[i * stream_channel_count + 0] = l_sample;
214 } else if (stream_channel_count == 2) {
215 buffer[i * stream_channel_count + 0] = l_sample;
216 buffer[i * stream_channel_count + 1] = r_sample;
217 } else if (stream_channel_count == 6) {
218 buffer[i * stream_channel_count + 0] = l_sample;
219 buffer[i * stream_channel_count + 1] = r_sample;
220
221 buffer[i * stream_channel_count + 2] =
222 ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2);
223
224 buffer[i * stream_channel_count + 4] = l_sample;
225 buffer[i * stream_channel_count + 5] = r_sample;
226 }
227
228 } else if (channel_count == 6) {
229 const auto fl_sample = ClampToS16(mix_buffers[0][i]);
230 const auto fr_sample = ClampToS16(mix_buffers[1][i]);
231 const auto fc_sample = ClampToS16(mix_buffers[2][i]);
232 const auto lf_sample = ClampToS16(mix_buffers[3][i]);
233 const auto bl_sample = ClampToS16(mix_buffers[4][i]);
234 const auto br_sample = ClampToS16(mix_buffers[5][i]);
235
236 if (stream_channel_count == 1) {
237 buffer[i * stream_channel_count + 0] = fc_sample;
238 } else if (stream_channel_count == 2) {
239 buffer[i * stream_channel_count + 0] =
240 static_cast<s16>(0.3694f * static_cast<float>(fl_sample) +
241 0.2612f * static_cast<float>(fc_sample) +
242 0.3694f * static_cast<float>(bl_sample));
243 buffer[i * stream_channel_count + 1] =
244 static_cast<s16>(0.3694f * static_cast<float>(fr_sample) +
245 0.2612f * static_cast<float>(fc_sample) +
246 0.3694f * static_cast<float>(br_sample));
247 } else if (stream_channel_count == 6) {
248 buffer[i * stream_channel_count + 0] = fl_sample;
249 buffer[i * stream_channel_count + 1] = fr_sample;
250 buffer[i * stream_channel_count + 2] = fc_sample;
251 buffer[i * stream_channel_count + 3] = lf_sample;
252 buffer[i * stream_channel_count + 4] = bl_sample;
253 buffer[i * stream_channel_count + 5] = br_sample;
254 }
458 } 255 }
459 } 256 }
460 } 257 }
258
461 audio_out->QueueBuffer(stream, tag, std::move(buffer)); 259 audio_out->QueueBuffer(stream, tag, std::move(buffer));
462 elapsed_frame_count++; 260 elapsed_frame_count++;
261 voice_context.UpdateStateByDspShared();
463} 262}
464 263
465void AudioRenderer::ReleaseAndQueueBuffers() { 264void AudioRenderer::ReleaseAndQueueBuffers() {
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index f0b691a86..2bca795ba 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -9,8 +9,15 @@
9#include <vector> 9#include <vector>
10 10
11#include "audio_core/behavior_info.h" 11#include "audio_core/behavior_info.h"
12#include "audio_core/command_generator.h"
12#include "audio_core/common.h" 13#include "audio_core/common.h"
14#include "audio_core/effect_context.h"
15#include "audio_core/memory_pool.h"
16#include "audio_core/mix_context.h"
17#include "audio_core/sink_context.h"
18#include "audio_core/splitter_context.h"
13#include "audio_core/stream.h" 19#include "audio_core/stream.h"
20#include "audio_core/voice_context.h"
14#include "common/common_funcs.h" 21#include "common/common_funcs.h"
15#include "common/common_types.h" 22#include "common/common_types.h"
16#include "common/swap.h" 23#include "common/swap.h"
@@ -30,220 +37,25 @@ class Memory;
30} 37}
31 38
32namespace AudioCore { 39namespace AudioCore {
40using DSPStateHolder = std::array<VoiceState*, 6>;
33 41
34class AudioOut; 42class AudioOut;
35 43
36enum class PlayState : u8 {
37 Started = 0,
38 Stopped = 1,
39 Paused = 2,
40};
41
42enum class Effect : u8 {
43 None = 0,
44 Aux = 2,
45};
46
47enum class EffectStatus : u8 {
48 None = 0,
49 New = 1,
50};
51
52struct AudioRendererParameter {
53 u32_le sample_rate;
54 u32_le sample_count;
55 u32_le mix_buffer_count;
56 u32_le submix_count;
57 u32_le voice_count;
58 u32_le sink_count;
59 u32_le effect_count;
60 u32_le performance_frame_count;
61 u8 is_voice_drop_enabled;
62 u8 unknown_21;
63 u8 unknown_22;
64 u8 execution_mode;
65 u32_le splitter_count;
66 u32_le num_splitter_send_channels;
67 u32_le unknown_30;
68 u32_le revision;
69};
70static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
71
72enum class MemoryPoolStates : u32 { // Should be LE
73 Invalid = 0x0,
74 Unknown = 0x1,
75 RequestDetach = 0x2,
76 Detached = 0x3,
77 RequestAttach = 0x4,
78 Attached = 0x5,
79 Released = 0x6,
80};
81
82struct MemoryPoolEntry {
83 MemoryPoolStates state;
84 u32_le unknown_4;
85 u32_le unknown_8;
86 u32_le unknown_c;
87};
88static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
89
90struct MemoryPoolInfo {
91 u64_le pool_address;
92 u64_le pool_size;
93 MemoryPoolStates pool_state;
94 INSERT_PADDING_WORDS(3); // Unknown
95};
96static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
97struct BiquadFilter {
98 u8 enable;
99 INSERT_PADDING_BYTES(1);
100 std::array<s16_le, 3> numerator;
101 std::array<s16_le, 2> denominator;
102};
103static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
104
105struct WaveBuffer {
106 u64_le buffer_addr;
107 u64_le buffer_sz;
108 s32_le start_sample_offset;
109 s32_le end_sample_offset;
110 u8 is_looping;
111 u8 end_of_stream;
112 u8 sent_to_server;
113 INSERT_PADDING_BYTES(5);
114 u64 context_addr;
115 u64 context_sz;
116 INSERT_PADDING_BYTES(8);
117};
118static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
119
120struct VoiceResourceInformation {
121 s32_le id{};
122 std::array<float_le, MAX_MIX_BUFFERS> mix_volumes{};
123 bool in_use{};
124 INSERT_PADDING_BYTES(11);
125};
126static_assert(sizeof(VoiceResourceInformation) == 0x70, "VoiceResourceInformation has wrong size");
127
128struct VoiceInfo {
129 u32_le id;
130 u32_le node_id;
131 u8 is_new;
132 u8 is_in_use;
133 PlayState play_state;
134 u8 sample_format;
135 u32_le sample_rate;
136 u32_le priority;
137 u32_le sorting_order;
138 u32_le channel_count;
139 float_le pitch;
140 float_le volume;
141 std::array<BiquadFilter, 2> biquad_filter;
142 u32_le wave_buffer_count;
143 u32_le wave_buffer_head;
144 INSERT_PADDING_WORDS(1);
145 u64_le additional_params_addr;
146 u64_le additional_params_sz;
147 u32_le mix_id;
148 u32_le splitter_info_id;
149 std::array<WaveBuffer, 4> wave_buffer;
150 std::array<u32_le, 6> voice_channel_resource_ids;
151 INSERT_PADDING_BYTES(24);
152};
153static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
154
155struct VoiceOutStatus {
156 u64_le played_sample_count;
157 u32_le wave_buffer_consumed;
158 u32_le voice_drops_count;
159};
160static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
161
162struct AuxInfo {
163 std::array<u8, 24> input_mix_buffers;
164 std::array<u8, 24> output_mix_buffers;
165 u32_le mix_buffer_count;
166 u32_le sample_rate; // Stored in the aux buffer currently
167 u32_le sample_count;
168 u64_le send_buffer_info;
169 u64_le send_buffer_base;
170
171 u64_le return_buffer_info;
172 u64_le return_buffer_base;
173};
174static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
175
176struct EffectInStatus {
177 Effect type;
178 u8 is_new;
179 u8 is_enabled;
180 INSERT_PADDING_BYTES(1);
181 u32_le mix_id;
182 u64_le buffer_base;
183 u64_le buffer_sz;
184 s32_le priority;
185 INSERT_PADDING_BYTES(4);
186 union {
187 std::array<u8, 0xa0> raw;
188 AuxInfo aux_info;
189 };
190};
191static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size");
192
193struct EffectOutStatus {
194 EffectStatus state;
195 INSERT_PADDING_BYTES(0xf);
196};
197static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
198
199struct RendererInfo { 44struct RendererInfo {
200 u64_le elasped_frame_count{}; 45 u64_le elasped_frame_count{};
201 INSERT_PADDING_WORDS(2); 46 INSERT_PADDING_WORDS(2);
202}; 47};
203static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size"); 48static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
204 49
205struct UpdateDataHeader {
206 UpdateDataHeader() {}
207
208 explicit UpdateDataHeader(const AudioRendererParameter& config) {
209 revision = Common::MakeMagic('R', 'E', 'V', '8'); // 9.2.0 Revision
210 behavior_size = 0xb0;
211 memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
212 voices_size = config.voice_count * 0x10;
213 voice_resource_size = 0x0;
214 effects_size = config.effect_count * 0x10;
215 mixes_size = 0x0;
216 sinks_size = config.sink_count * 0x20;
217 performance_manager_size = 0x10;
218 render_info = 0;
219 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
220 effects_size + sinks_size + performance_manager_size;
221 }
222
223 u32_le revision{};
224 u32_le behavior_size{};
225 u32_le memory_pools_size{};
226 u32_le voices_size{};
227 u32_le voice_resource_size{};
228 u32_le effects_size{};
229 u32_le mixes_size{};
230 u32_le sinks_size{};
231 u32_le performance_manager_size{};
232 u32_le splitter_size{};
233 u32_le render_info{};
234 INSERT_PADDING_WORDS(4);
235 u32_le total_size{};
236};
237static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
238
239class AudioRenderer { 50class AudioRenderer {
240public: 51public:
241 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, 52 AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
242 AudioRendererParameter params, 53 AudioCommon::AudioRendererParameter params,
243 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number); 54 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
244 ~AudioRenderer(); 55 ~AudioRenderer();
245 56
246 ResultVal<std::vector<u8>> UpdateAudioRenderer(const std::vector<u8>& input_params); 57 ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
58 std::vector<u8>& output_params);
247 void QueueMixedBuffer(Buffer::Tag tag); 59 void QueueMixedBuffer(Buffer::Tag tag);
248 void ReleaseAndQueueBuffers(); 60 void ReleaseAndQueueBuffers();
249 u32 GetSampleRate() const; 61 u32 GetSampleRate() const;
@@ -252,19 +64,23 @@ public:
252 Stream::State GetStreamState() const; 64 Stream::State GetStreamState() const;
253 65
254private: 66private:
255 class EffectState;
256 class VoiceState;
257 BehaviorInfo behavior_info{}; 67 BehaviorInfo behavior_info{};
258 68
259 AudioRendererParameter worker_params; 69 AudioCommon::AudioRendererParameter worker_params;
260 std::shared_ptr<Kernel::WritableEvent> buffer_event; 70 std::shared_ptr<Kernel::WritableEvent> buffer_event;
71 std::vector<ServerMemoryPoolInfo> memory_pool_info;
72 VoiceContext voice_context;
73 EffectContext effect_context;
74 MixContext mix_context;
75 SinkContext sink_context;
76 SplitterContext splitter_context;
261 std::vector<VoiceState> voices; 77 std::vector<VoiceState> voices;
262 std::vector<VoiceResourceInformation> voice_resources;
263 std::vector<EffectState> effects;
264 std::unique_ptr<AudioOut> audio_out; 78 std::unique_ptr<AudioOut> audio_out;
265 StreamPtr stream; 79 StreamPtr stream;
266 Core::Memory::Memory& memory; 80 Core::Memory::Memory& memory;
81 CommandGenerator command_generator;
267 std::size_t elapsed_frame_count{}; 82 std::size_t elapsed_frame_count{};
83 std::vector<s32> temp_mix_buffer{};
268}; 84};
269 85
270} // namespace AudioCore 86} // namespace AudioCore
diff --git a/src/audio_core/behavior_info.cpp b/src/audio_core/behavior_info.cpp
index 94b7a3bf1..5d62adb0b 100644
--- a/src/audio_core/behavior_info.cpp
+++ b/src/audio_core/behavior_info.cpp
@@ -9,39 +9,11 @@
9 9
10namespace AudioCore { 10namespace AudioCore {
11 11
12BehaviorInfo::BehaviorInfo() : process_revision(CURRENT_PROCESS_REVISION) {} 12BehaviorInfo::BehaviorInfo() : process_revision(AudioCommon::CURRENT_PROCESS_REVISION) {}
13BehaviorInfo::~BehaviorInfo() = default; 13BehaviorInfo::~BehaviorInfo() = default;
14 14
15bool BehaviorInfo::UpdateInput(const std::vector<u8>& buffer, std::size_t offset) {
16 if (!CanConsumeBuffer(buffer.size(), offset, sizeof(InParams))) {
17 LOG_ERROR(Audio, "Buffer is an invalid size!");
18 return false;
19 }
20 InParams params{};
21 std::memcpy(&params, buffer.data() + offset, sizeof(InParams));
22
23 if (!IsValidRevision(params.revision)) {
24 LOG_ERROR(Audio, "Invalid input revision, revision=0x{:08X}", params.revision);
25 return false;
26 }
27
28 if (user_revision != params.revision) {
29 LOG_ERROR(Audio,
30 "User revision differs from input revision, expecting 0x{:08X} but got 0x{:08X}",
31 user_revision, params.revision);
32 return false;
33 }
34
35 ClearError();
36 UpdateFlags(params.flags);
37
38 // TODO(ogniK): Check input params size when InfoUpdater is used
39
40 return true;
41}
42
43bool BehaviorInfo::UpdateOutput(std::vector<u8>& buffer, std::size_t offset) { 15bool BehaviorInfo::UpdateOutput(std::vector<u8>& buffer, std::size_t offset) {
44 if (!CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) { 16 if (!AudioCommon::CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) {
45 LOG_ERROR(Audio, "Buffer is an invalid size!"); 17 LOG_ERROR(Audio, "Buffer is an invalid size!");
46 return false; 18 return false;
47 } 19 }
@@ -65,36 +37,69 @@ void BehaviorInfo::SetUserRevision(u32_le revision) {
65 user_revision = revision; 37 user_revision = revision;
66} 38}
67 39
40u32_le BehaviorInfo::GetUserRevision() const {
41 return user_revision;
42}
43
44u32_le BehaviorInfo::GetProcessRevision() const {
45 return process_revision;
46}
47
68bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const { 48bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const {
69 return IsRevisionSupported(2, user_revision); 49 return AudioCommon::IsRevisionSupported(2, user_revision);
70} 50}
71 51
72bool BehaviorInfo::IsSplitterSupported() const { 52bool BehaviorInfo::IsSplitterSupported() const {
73 return IsRevisionSupported(2, user_revision); 53 return AudioCommon::IsRevisionSupported(2, user_revision);
74} 54}
75 55
76bool BehaviorInfo::IsLongSizePreDelaySupported() const { 56bool BehaviorInfo::IsLongSizePreDelaySupported() const {
77 return IsRevisionSupported(3, user_revision); 57 return AudioCommon::IsRevisionSupported(3, user_revision);
78} 58}
79 59
80bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const { 60bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const {
81 return IsRevisionSupported(5, user_revision); 61 return AudioCommon::IsRevisionSupported(5, user_revision);
82} 62}
83 63
84bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const { 64bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const {
85 return IsRevisionSupported(4, user_revision); 65 return AudioCommon::IsRevisionSupported(4, user_revision);
86} 66}
87 67
88bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const { 68bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const {
89 return IsRevisionSupported(1, user_revision); 69 return AudioCommon::IsRevisionSupported(1, user_revision);
90} 70}
91 71
92bool BehaviorInfo::IsElapsedFrameCountSupported() const { 72bool BehaviorInfo::IsElapsedFrameCountSupported() const {
93 return IsRevisionSupported(5, user_revision); 73 return AudioCommon::IsRevisionSupported(5, user_revision);
94} 74}
95 75
96bool BehaviorInfo::IsMemoryPoolForceMappingEnabled() const { 76bool BehaviorInfo::IsMemoryPoolForceMappingEnabled() const {
97 return (flags & 1) != 0; 77 return (flags & 1) != 0;
98} 78}
99 79
80bool BehaviorInfo::IsFlushVoiceWaveBuffersSupported() const {
81 return AudioCommon::IsRevisionSupported(5, user_revision);
82}
83
84bool BehaviorInfo::IsVoicePlayedSampleCountResetAtLoopPointSupported() const {
85 return AudioCommon::IsRevisionSupported(5, user_revision);
86}
87
88bool BehaviorInfo::IsVoicePitchAndSrcSkippedSupported() const {
89 return AudioCommon::IsRevisionSupported(5, user_revision);
90}
91
92bool BehaviorInfo::IsMixInParameterDirtyOnlyUpdateSupported() const {
93 return AudioCommon::IsRevisionSupported(7, user_revision);
94}
95
96bool BehaviorInfo::IsSplitterBugFixed() const {
97 return AudioCommon::IsRevisionSupported(5, user_revision);
98}
99
100void BehaviorInfo::CopyErrorInfo(BehaviorInfo::OutParams& dst) {
101 dst.error_count = static_cast<u32>(error_count);
102 std::copy(errors.begin(), errors.begin() + error_count, dst.errors.begin());
103}
104
100} // namespace AudioCore 105} // namespace AudioCore
diff --git a/src/audio_core/behavior_info.h b/src/audio_core/behavior_info.h
index c5e91ab39..50948e8df 100644
--- a/src/audio_core/behavior_info.h
+++ b/src/audio_core/behavior_info.h
@@ -14,15 +14,37 @@
14namespace AudioCore { 14namespace AudioCore {
15class BehaviorInfo { 15class BehaviorInfo {
16public: 16public:
17 struct ErrorInfo {
18 u32_le result{};
19 INSERT_PADDING_WORDS(1);
20 u64_le result_info{};
21 };
22 static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size");
23
24 struct InParams {
25 u32_le revision{};
26 u32_le padding{};
27 u64_le flags{};
28 };
29 static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size");
30
31 struct OutParams {
32 std::array<ErrorInfo, 10> errors{};
33 u32_le error_count{};
34 INSERT_PADDING_BYTES(12);
35 };
36 static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size");
37
17 explicit BehaviorInfo(); 38 explicit BehaviorInfo();
18 ~BehaviorInfo(); 39 ~BehaviorInfo();
19 40
20 bool UpdateInput(const std::vector<u8>& buffer, std::size_t offset);
21 bool UpdateOutput(std::vector<u8>& buffer, std::size_t offset); 41 bool UpdateOutput(std::vector<u8>& buffer, std::size_t offset);
22 42
23 void ClearError(); 43 void ClearError();
24 void UpdateFlags(u64_le dest_flags); 44 void UpdateFlags(u64_le dest_flags);
25 void SetUserRevision(u32_le revision); 45 void SetUserRevision(u32_le revision);
46 u32_le GetUserRevision() const;
47 u32_le GetProcessRevision() const;
26 48
27 bool IsAdpcmLoopContextBugFixed() const; 49 bool IsAdpcmLoopContextBugFixed() const;
28 bool IsSplitterSupported() const; 50 bool IsSplitterSupported() const;
@@ -32,35 +54,19 @@ public:
32 bool IsAudioRenererProcessingTimeLimit70PercentSupported() const; 54 bool IsAudioRenererProcessingTimeLimit70PercentSupported() const;
33 bool IsElapsedFrameCountSupported() const; 55 bool IsElapsedFrameCountSupported() const;
34 bool IsMemoryPoolForceMappingEnabled() const; 56 bool IsMemoryPoolForceMappingEnabled() const;
57 bool IsFlushVoiceWaveBuffersSupported() const;
58 bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
59 bool IsVoicePitchAndSrcSkippedSupported() const;
60 bool IsMixInParameterDirtyOnlyUpdateSupported() const;
61 bool IsSplitterBugFixed() const;
62 void CopyErrorInfo(OutParams& dst);
35 63
36private: 64private:
37 u32_le process_revision{}; 65 u32_le process_revision{};
38 u32_le user_revision{}; 66 u32_le user_revision{};
39 u64_le flags{}; 67 u64_le flags{};
40
41 struct ErrorInfo {
42 u32_le result{};
43 INSERT_PADDING_WORDS(1);
44 u64_le result_info{};
45 };
46 static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size");
47
48 std::array<ErrorInfo, 10> errors{}; 68 std::array<ErrorInfo, 10> errors{};
49 std::size_t error_count{}; 69 std::size_t error_count{};
50
51 struct InParams {
52 u32_le revision{};
53 u32_le padding{};
54 u64_le flags{};
55 };
56 static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size");
57
58 struct OutParams {
59 std::array<ErrorInfo, 10> errors{};
60 u32_le error_count{};
61 INSERT_PADDING_BYTES(12);
62 };
63 static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size");
64}; 70};
65 71
66} // namespace AudioCore 72} // namespace AudioCore
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
new file mode 100644
index 000000000..84782cde6
--- /dev/null
+++ b/src/audio_core/command_generator.cpp
@@ -0,0 +1,976 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/algorithm/interpolate.h"
6#include "audio_core/command_generator.h"
7#include "audio_core/effect_context.h"
8#include "audio_core/mix_context.h"
9#include "audio_core/voice_context.h"
10#include "core/memory.h"
11
12namespace AudioCore {
13namespace {
14constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
15constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
16
17template <std::size_t N>
18void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
19 for (std::size_t i = 0; i < static_cast<std::size_t>(sample_count); i += N) {
20 for (std::size_t j = 0; j < N; j++) {
21 output[i + j] +=
22 static_cast<s32>((static_cast<s64>(input[i + j]) * gain + 0x4000) >> 15);
23 }
24 }
25}
26
27s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sample_count) {
28 s32 x = 0;
29 for (s32 i = 0; i < sample_count; i++) {
30 x = static_cast<s32>(static_cast<float>(input[i]) * gain);
31 output[i] += x;
32 gain += delta;
33 }
34 return x;
35}
36
37void ApplyGain(s32* output, const s32* input, s32 gain, s32 delta, s32 sample_count) {
38 for (s32 i = 0; i < sample_count; i++) {
39 output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
40 gain += delta;
41 }
42}
43
44void ApplyGainWithoutDelta(s32* output, const s32* input, s32 gain, s32 sample_count) {
45 for (s32 i = 0; i < sample_count; i++) {
46 output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
47 }
48}
49
50s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
51 const bool positive = first_sample > 0;
52 auto final_sample = std::abs(first_sample);
53 for (s32 i = 0; i < sample_count; i++) {
54 final_sample = static_cast<s32>((static_cast<s64>(final_sample) * delta) >> 15);
55 if (positive) {
56 output[i] += final_sample;
57 } else {
58 output[i] -= final_sample;
59 }
60 }
61 if (positive) {
62 return final_sample;
63 } else {
64 return -final_sample;
65 }
66}
67
68} // namespace
69
70CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
71 VoiceContext& voice_context, MixContext& mix_context,
72 SplitterContext& splitter_context, EffectContext& effect_context,
73 Core::Memory::Memory& memory)
74 : worker_params(worker_params), voice_context(voice_context), mix_context(mix_context),
75 splitter_context(splitter_context), effect_context(effect_context), memory(memory),
76 mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
77 worker_params.sample_count),
78 sample_buffer(MIX_BUFFER_SIZE),
79 depop_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
80 worker_params.sample_count) {}
81CommandGenerator::~CommandGenerator() = default;
82
83void CommandGenerator::ClearMixBuffers() {
84 std::fill(mix_buffer.begin(), mix_buffer.end(), 0);
85 std::fill(sample_buffer.begin(), sample_buffer.end(), 0);
86 // std::fill(depop_buffer.begin(), depop_buffer.end(), 0);
87}
88
89void CommandGenerator::GenerateVoiceCommands() {
90 if (dumping_frame) {
91 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateVoiceCommands");
92 }
93 // Grab all our voices
94 const auto voice_count = voice_context.GetVoiceCount();
95 for (std::size_t i = 0; i < voice_count; i++) {
96 auto& voice_info = voice_context.GetSortedInfo(i);
97 // Update voices and check if we should queue them
98 if (voice_info.ShouldSkip() || !voice_info.UpdateForCommandGeneration(voice_context)) {
99 continue;
100 }
101
102 // Queue our voice
103 GenerateVoiceCommand(voice_info);
104 }
105 // Update our splitters
106 splitter_context.UpdateInternalState();
107}
108
109void CommandGenerator::GenerateVoiceCommand(ServerVoiceInfo& voice_info) {
110 auto& in_params = voice_info.GetInParams();
111 const auto channel_count = in_params.channel_count;
112
113 for (s32 channel = 0; channel < channel_count; channel++) {
114 const auto resource_id = in_params.voice_channel_resource_id[channel];
115 auto& dsp_state = voice_context.GetDspSharedState(resource_id);
116 auto& channel_resource = voice_context.GetChannelResource(resource_id);
117
118 // Decode our samples for our channel
119 GenerateDataSourceCommand(voice_info, dsp_state, channel);
120
121 if (in_params.should_depop) {
122 in_params.last_volume = 0.0f;
123 } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER ||
124 in_params.mix_id != AudioCommon::NO_MIX) {
125 // Apply a biquad filter if needed
126 GenerateBiquadFilterCommandForVoice(voice_info, dsp_state,
127 worker_params.mix_buffer_count, channel);
128 // Base voice volume ramping
129 GenerateVolumeRampCommand(in_params.last_volume, in_params.volume, channel,
130 in_params.node_id);
131 in_params.last_volume = in_params.volume;
132
133 if (in_params.mix_id != AudioCommon::NO_MIX) {
134 // If we're using a mix id
135 auto& mix_info = mix_context.GetInfo(in_params.mix_id);
136 const auto& dest_mix_params = mix_info.GetInParams();
137
138 // Voice Mixing
139 GenerateVoiceMixCommand(
140 channel_resource.GetCurrentMixVolume(), channel_resource.GetLastMixVolume(),
141 dsp_state, dest_mix_params.buffer_offset, dest_mix_params.buffer_count,
142 worker_params.mix_buffer_count + channel, in_params.node_id);
143
144 // Update last mix volumes
145 channel_resource.UpdateLastMixVolumes();
146 } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) {
147 s32 base = channel;
148 while (auto* destination_data =
149 GetDestinationData(in_params.splitter_info_id, base)) {
150 base += channel_count;
151
152 if (!destination_data->IsConfigured()) {
153 continue;
154 }
155 if (destination_data->GetMixId() >= mix_context.GetCount()) {
156 continue;
157 }
158
159 const auto& mix_info = mix_context.GetInfo(destination_data->GetMixId());
160 const auto& dest_mix_params = mix_info.GetInParams();
161 GenerateVoiceMixCommand(
162 destination_data->CurrentMixVolumes(), destination_data->LastMixVolumes(),
163 dsp_state, dest_mix_params.buffer_offset, dest_mix_params.buffer_count,
164 worker_params.mix_buffer_count + channel, in_params.node_id);
165 destination_data->MarkDirty();
166 }
167 }
168 // Update biquad filter enabled states
169 for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
170 in_params.was_biquad_filter_enabled[i] = in_params.biquad_filter[i].enabled;
171 }
172 }
173 }
174}
175
176void CommandGenerator::GenerateSubMixCommands() {
177 const auto mix_count = mix_context.GetCount();
178 for (std::size_t i = 0; i < mix_count; i++) {
179 auto& mix_info = mix_context.GetSortedInfo(i);
180 const auto& in_params = mix_info.GetInParams();
181 if (!in_params.in_use || in_params.mix_id == AudioCommon::FINAL_MIX) {
182 continue;
183 }
184 GenerateSubMixCommand(mix_info);
185 }
186}
187
188void CommandGenerator::GenerateFinalMixCommands() {
189 GenerateFinalMixCommand();
190}
191
192void CommandGenerator::PreCommand() {
193 if (!dumping_frame) {
194 return;
195 }
196 for (std::size_t i = 0; i < splitter_context.GetInfoCount(); i++) {
197 const auto& base = splitter_context.GetInfo(i);
198 std::string graph = fmt::format("b[{}]", i);
199 auto* head = base.GetHead();
200 while (head != nullptr) {
201 graph += fmt::format("->{}", head->GetMixId());
202 head = head->GetNextDestination();
203 }
204 LOG_DEBUG(Audio, "(DSP_TRACE) SplitterGraph splitter_info={}, {}", i, graph);
205 }
206}
207
208void CommandGenerator::PostCommand() {
209 if (!dumping_frame) {
210 return;
211 }
212 dumping_frame = false;
213}
214
215void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
216 s32 channel) {
217 auto& in_params = voice_info.GetInParams();
218 const auto depop = in_params.should_depop;
219
220 if (depop) {
221 if (in_params.mix_id != AudioCommon::NO_MIX) {
222 auto& mix_info = mix_context.GetInfo(in_params.mix_id);
223 const auto& mix_in = mix_info.GetInParams();
224 GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset);
225 } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) {
226 s32 index{};
227 while (const auto* destination =
228 GetDestinationData(in_params.splitter_info_id, index++)) {
229 if (!destination->IsConfigured()) {
230 continue;
231 }
232 auto& mix_info = mix_context.GetInfo(destination->GetMixId());
233 const auto& mix_in = mix_info.GetInParams();
234 GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset);
235 }
236 }
237 } else {
238 switch (in_params.sample_format) {
239 case SampleFormat::Pcm16:
240 DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(channel), dsp_state, channel,
241 worker_params.sample_rate, worker_params.sample_count,
242 in_params.node_id);
243 break;
244 case SampleFormat::Adpcm:
245 ASSERT(channel == 0 && in_params.channel_count == 1);
246 DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(0), dsp_state, 0,
247 worker_params.sample_rate, worker_params.sample_count,
248 in_params.node_id);
249 break;
250 default:
251 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
252 }
253 }
254}
255
256void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info,
257 VoiceState& dsp_state,
258 s32 mix_buffer_count, s32 channel) {
259 for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
260 const auto& in_params = voice_info.GetInParams();
261 auto& biquad_filter = in_params.biquad_filter[i];
262 // Check if biquad filter is actually used
263 if (!biquad_filter.enabled) {
264 continue;
265 }
266
267 // Reinitialize our biquad filter state if it was enabled previously
268 if (!in_params.was_biquad_filter_enabled[i]) {
269 dsp_state.biquad_filter_state.fill(0);
270 }
271
272 // Generate biquad filter
273 // GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
274 // dsp_state.biquad_filter_state,
275 // mix_buffer_count + channel, mix_buffer_count +
276 // channel, worker_params.sample_count,
277 // voice_info.GetInParams().node_id);
278 }
279}
280
281void AudioCore::CommandGenerator::GenerateBiquadFilterCommand(
282 s32 mix_buffer, const BiquadFilterParameter& params, std::array<s64, 2>& state,
283 std::size_t input_offset, std::size_t output_offset, s32 sample_count, s32 node_id) {
284 if (dumping_frame) {
285 LOG_DEBUG(Audio,
286 "(DSP_TRACE) GenerateBiquadFilterCommand node_id={}, "
287 "input_mix_buffer={}, output_mix_buffer={}",
288 node_id, input_offset, output_offset);
289 }
290 const auto* input = GetMixBuffer(input_offset);
291 auto* output = GetMixBuffer(output_offset);
292
293 // Biquad filter parameters
294 const auto [n0, n1, n2] = params.numerator;
295 const auto [d0, d1] = params.denominator;
296
297 // Biquad filter states
298 auto [s0, s1] = state;
299
300 constexpr s64 int32_min = std::numeric_limits<s32>::min();
301 constexpr s64 int32_max = std::numeric_limits<s32>::max();
302
303 for (int i = 0; i < sample_count; ++i) {
304 const auto sample = static_cast<s64>(input[i]);
305 const auto f = (sample * n0 + s0 + 0x4000) >> 15;
306 const auto y = std::clamp(f, int32_min, int32_max);
307 s0 = sample * n1 + y * d0 + s1;
308 s1 = sample * n2 + y * d1;
309 output[i] = static_cast<s32>(y);
310 }
311
312 state = {s0, s1};
313}
314
315void CommandGenerator::GenerateDepopPrepareCommand(VoiceState& dsp_state,
316 std::size_t mix_buffer_count,
317 std::size_t mix_buffer_offset) {
318 for (std::size_t i = 0; i < mix_buffer_count; i++) {
319 auto& sample = dsp_state.previous_samples[i];
320 if (sample != 0) {
321 depop_buffer[mix_buffer_offset + i] += sample;
322 sample = 0;
323 }
324 }
325}
326
327void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
328 std::size_t mix_buffer_offset,
329 s32 sample_rate) {
330 const std::size_t end_offset =
331 std::min(mix_buffer_offset + mix_buffer_count, GetTotalMixBufferCount());
332 const s32 delta = sample_rate == 48000 ? 0x7B29 : 0x78CB;
333 for (std::size_t i = mix_buffer_offset; i < end_offset; i++) {
334 if (depop_buffer[i] == 0) {
335 continue;
336 }
337
338 depop_buffer[i] =
339 ApplyMixDepop(GetMixBuffer(i), depop_buffer[i], delta, worker_params.sample_count);
340 }
341}
342
343void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
344 const std::size_t effect_count = effect_context.GetCount();
345 const auto buffer_offset = mix_info.GetInParams().buffer_offset;
346 for (std::size_t i = 0; i < effect_count; i++) {
347 const auto index = mix_info.GetEffectOrder(i);
348 if (index == AudioCommon::NO_EFFECT_ORDER) {
349 break;
350 }
351 auto* info = effect_context.GetInfo(index);
352 const auto type = info->GetType();
353
354 // TODO(ogniK): Finish remaining effects
355 switch (type) {
356 case EffectType::Aux:
357 GenerateAuxCommand(buffer_offset, info, info->IsEnabled());
358 break;
359 case EffectType::I3dl2Reverb:
360 GenerateI3dl2ReverbEffectCommand(buffer_offset, info, info->IsEnabled());
361 break;
362 case EffectType::BiquadFilter:
363 GenerateBiquadFilterEffectCommand(buffer_offset, info, info->IsEnabled());
364 break;
365 default:
366 break;
367 }
368
369 info->UpdateForCommandGeneration();
370 }
371}
372
373void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
374 bool enabled) {
375 if (!enabled) {
376 return;
377 }
378 const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
379 const auto channel_count = params.channel_count;
380 for (s32 i = 0; i < channel_count; i++) {
381 // TODO(ogniK): Actually implement reverb
382 if (params.input[i] != params.output[i]) {
383 const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
384 auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
385 ApplyMix<1>(output, input, 32768, worker_params.sample_count);
386 }
387 }
388}
389
390void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info,
391 bool enabled) {
392 if (!enabled) {
393 return;
394 }
395 const auto& params = dynamic_cast<EffectBiquadFilter*>(info)->GetParams();
396 const auto channel_count = params.channel_count;
397 for (s32 i = 0; i < channel_count; i++) {
398 // TODO(ogniK): Actually implement biquad filter
399 if (params.input[i] != params.output[i]) {
400 const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
401 auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
402 ApplyMix<1>(output, input, 32768, worker_params.sample_count);
403 }
404 }
405}
406
407void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) {
408 auto aux = dynamic_cast<EffectAuxInfo*>(info);
409 const auto& params = aux->GetParams();
410 if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) {
411 const auto max_channels = params.count;
412 u32 offset{};
413 for (u32 channel = 0; channel < max_channels; channel++) {
414 u32 write_count = 0;
415 if (channel == (max_channels - 1)) {
416 write_count = offset + worker_params.sample_count;
417 }
418
419 const auto input_index = params.input_mix_buffers[channel] + mix_buffer_offset;
420 const auto output_index = params.output_mix_buffers[channel] + mix_buffer_offset;
421
422 if (enabled) {
423 AuxInfoDSP send_info{};
424 AuxInfoDSP recv_info{};
425 memory.ReadBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
426 memory.ReadBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
427
428 WriteAuxBuffer(send_info, aux->GetSendBuffer(), params.sample_count,
429 GetMixBuffer(input_index), worker_params.sample_count, offset,
430 write_count);
431 memory.WriteBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
432
433 const auto samples_read = ReadAuxBuffer(
434 recv_info, aux->GetRecvBuffer(), params.sample_count,
435 GetMixBuffer(output_index), worker_params.sample_count, offset, write_count);
436 memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
437
438 if (samples_read != worker_params.sample_count &&
439 samples_read <= params.sample_count) {
440 std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read);
441 }
442 } else {
443 AuxInfoDSP empty{};
444 memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
445 memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
446 if (output_index != input_index) {
447 std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index),
448 worker_params.sample_count * sizeof(s32));
449 }
450 }
451
452 offset += worker_params.sample_count;
453 }
454 }
455}
456
457ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) {
458 if (splitter_id == AudioCommon::NO_SPLITTER) {
459 return nullptr;
460 }
461 return splitter_context.GetDestinationData(splitter_id, index);
462}
463
464s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
465 const s32* data, u32 sample_count, u32 write_offset,
466 u32 write_count) {
467 if (max_samples == 0) {
468 return 0;
469 }
470 u32 offset = dsp_info.write_offset + write_offset;
471 if (send_buffer == 0 || offset > max_samples) {
472 return 0;
473 }
474
475 std::size_t data_offset{};
476 u32 remaining = sample_count;
477 while (remaining > 0) {
478 // Get position in buffer
479 const auto base = send_buffer + (offset * sizeof(u32));
480 const auto samples_to_grab = std::min(max_samples - offset, remaining);
481 // Write to output
482 memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32));
483 offset = (offset + samples_to_grab) % max_samples;
484 remaining -= samples_to_grab;
485 data_offset += samples_to_grab;
486 }
487
488 if (write_count != 0) {
489 dsp_info.write_offset = (dsp_info.write_offset + write_count) % max_samples;
490 }
491 return sample_count;
492}
493
494s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples,
495 s32* out_data, u32 sample_count, u32 read_offset,
496 u32 read_count) {
497 if (max_samples == 0) {
498 return 0;
499 }
500
501 u32 offset = recv_info.read_offset + read_offset;
502 if (recv_buffer == 0 || offset > max_samples) {
503 return 0;
504 }
505
506 u32 remaining = sample_count;
507 while (remaining > 0) {
508 const auto base = recv_buffer + (offset * sizeof(u32));
509 const auto samples_to_grab = std::min(max_samples - offset, remaining);
510 std::vector<s32> buffer(samples_to_grab);
511 memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
512 std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32));
513 out_data += samples_to_grab;
514 offset = (offset + samples_to_grab) % max_samples;
515 remaining -= samples_to_grab;
516 }
517
518 if (read_count != 0) {
519 recv_info.read_offset = (recv_info.read_offset + read_count) % max_samples;
520 }
521 return sample_count;
522}
523
524void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
525 s32 channel, s32 node_id) {
526 const auto last = static_cast<s32>(last_volume * 32768.0f);
527 const auto current = static_cast<s32>(current_volume * 32768.0f);
528 const auto delta = static_cast<s32>((static_cast<float>(current) - static_cast<float>(last)) /
529 static_cast<float>(worker_params.sample_count));
530
531 if (dumping_frame) {
532 LOG_DEBUG(Audio,
533 "(DSP_TRACE) GenerateVolumeRampCommand node_id={}, input={}, output={}, "
534 "last_volume={}, current_volume={}",
535 node_id, GetMixChannelBufferOffset(channel), GetMixChannelBufferOffset(channel),
536 last_volume, current_volume);
537 }
538 // Apply generic gain on samples
539 ApplyGain(GetChannelMixBuffer(channel), GetChannelMixBuffer(channel), last, delta,
540 worker_params.sample_count);
541}
542
543void CommandGenerator::GenerateVoiceMixCommand(const MixVolumeBuffer& mix_volumes,
544 const MixVolumeBuffer& last_mix_volumes,
545 VoiceState& dsp_state, s32 mix_buffer_offset,
546 s32 mix_buffer_count, s32 voice_index, s32 node_id) {
547 // Loop all our mix buffers
548 for (s32 i = 0; i < mix_buffer_count; i++) {
549 if (last_mix_volumes[i] != 0.0f || mix_volumes[i] != 0.0f) {
550 const auto delta = static_cast<float>((mix_volumes[i] - last_mix_volumes[i])) /
551 static_cast<float>(worker_params.sample_count);
552
553 if (dumping_frame) {
554 LOG_DEBUG(Audio,
555 "(DSP_TRACE) GenerateVoiceMixCommand node_id={}, input={}, "
556 "output={}, last_volume={}, current_volume={}",
557 node_id, voice_index, mix_buffer_offset + i, last_mix_volumes[i],
558 mix_volumes[i]);
559 }
560
561 dsp_state.previous_samples[i] =
562 ApplyMixRamp(GetMixBuffer(mix_buffer_offset + i), GetMixBuffer(voice_index),
563 last_mix_volumes[i], delta, worker_params.sample_count);
564 } else {
565 dsp_state.previous_samples[i] = 0;
566 }
567 }
568}
569
570void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) {
571 if (dumping_frame) {
572 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand");
573 }
574 auto& in_params = mix_info.GetInParams();
575 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
576 in_params.sample_rate);
577
578 GenerateEffectCommand(mix_info);
579
580 GenerateMixCommands(mix_info);
581}
582
583void CommandGenerator::GenerateMixCommands(ServerMixInfo& mix_info) {
584 if (!mix_info.HasAnyConnection()) {
585 return;
586 }
587 const auto& in_params = mix_info.GetInParams();
588 if (in_params.dest_mix_id != AudioCommon::NO_MIX) {
589 const auto& dest_mix = mix_context.GetInfo(in_params.dest_mix_id);
590 const auto& dest_in_params = dest_mix.GetInParams();
591
592 const auto buffer_count = in_params.buffer_count;
593
594 for (s32 i = 0; i < buffer_count; i++) {
595 for (s32 j = 0; j < dest_in_params.buffer_count; j++) {
596 const auto mixed_volume = in_params.volume * in_params.mix_volume[i][j];
597 if (mixed_volume != 0.0f) {
598 GenerateMixCommand(dest_in_params.buffer_offset + j,
599 in_params.buffer_offset + i, mixed_volume,
600 in_params.node_id);
601 }
602 }
603 }
604 } else if (in_params.splitter_id != AudioCommon::NO_SPLITTER) {
605 s32 base{};
606 while (const auto* destination_data = GetDestinationData(in_params.splitter_id, base++)) {
607 if (!destination_data->IsConfigured()) {
608 continue;
609 }
610
611 const auto& dest_mix = mix_context.GetInfo(destination_data->GetMixId());
612 const auto& dest_in_params = dest_mix.GetInParams();
613 const auto mix_index = (base - 1) % in_params.buffer_count + in_params.buffer_offset;
614 for (std::size_t i = 0; i < dest_in_params.buffer_count; i++) {
615 const auto mixed_volume = in_params.volume * destination_data->GetMixVolume(i);
616 if (mixed_volume != 0.0f) {
617 GenerateMixCommand(dest_in_params.buffer_offset + i, mix_index, mixed_volume,
618 in_params.node_id);
619 }
620 }
621 }
622 }
623}
624
625void CommandGenerator::GenerateMixCommand(std::size_t output_offset, std::size_t input_offset,
626 float volume, s32 node_id) {
627
628 if (dumping_frame) {
629 LOG_DEBUG(Audio,
630 "(DSP_TRACE) GenerateMixCommand node_id={}, input={}, output={}, volume={}",
631 node_id, input_offset, output_offset, volume);
632 }
633
634 auto* output = GetMixBuffer(output_offset);
635 const auto* input = GetMixBuffer(input_offset);
636
637 const s32 gain = static_cast<s32>(volume * 32768.0f);
638 // Mix with loop unrolling
639 if (worker_params.sample_count % 4 == 0) {
640 ApplyMix<4>(output, input, gain, worker_params.sample_count);
641 } else if (worker_params.sample_count % 2 == 0) {
642 ApplyMix<2>(output, input, gain, worker_params.sample_count);
643 } else {
644 ApplyMix<1>(output, input, gain, worker_params.sample_count);
645 }
646}
647
648void CommandGenerator::GenerateFinalMixCommand() {
649 if (dumping_frame) {
650 LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand");
651 }
652 auto& mix_info = mix_context.GetFinalMixInfo();
653 const auto in_params = mix_info.GetInParams();
654
655 GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
656 in_params.sample_rate);
657
658 GenerateEffectCommand(mix_info);
659
660 for (s32 i = 0; i < in_params.buffer_count; i++) {
661 const s32 gain = static_cast<s32>(in_params.volume * 32768.0f);
662 if (dumping_frame) {
663 LOG_DEBUG(
664 Audio,
665 "(DSP_TRACE) ApplyGainWithoutDelta node_id={}, input={}, output={}, volume={}",
666 in_params.node_id, in_params.buffer_offset + i, in_params.buffer_offset + i,
667 in_params.volume);
668 }
669 ApplyGainWithoutDelta(GetMixBuffer(in_params.buffer_offset + i),
670 GetMixBuffer(in_params.buffer_offset + i), gain,
671 worker_params.sample_count);
672 }
673}
674
675s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
676 s32 sample_count, s32 channel, std::size_t mix_offset) {
677 auto& in_params = voice_info.GetInParams();
678 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
679 if (wave_buffer.buffer_address == 0) {
680 return 0;
681 }
682 if (wave_buffer.buffer_size == 0) {
683 return 0;
684 }
685 if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
686 return 0;
687 }
688 const auto samples_remaining =
689 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
690 const auto start_offset =
691 ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) *
692 sizeof(s16);
693 const auto buffer_pos = wave_buffer.buffer_address + start_offset;
694 const auto samples_processed = std::min(sample_count, samples_remaining);
695
696 if (in_params.channel_count == 1) {
697 std::vector<s16> buffer(samples_processed);
698 memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
699 for (std::size_t i = 0; i < buffer.size(); i++) {
700 sample_buffer[mix_offset + i] = buffer[i];
701 }
702 } else {
703 const auto channel_count = in_params.channel_count;
704 std::vector<s16> buffer(samples_processed * channel_count);
705 memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
706
707 for (std::size_t i = 0; i < samples_processed; i++) {
708 sample_buffer[mix_offset + i] = buffer[i * channel_count + channel];
709 }
710 }
711
712 return samples_processed;
713}
714
715s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
716 s32 sample_count, s32 channel, std::size_t mix_offset) {
717 auto& in_params = voice_info.GetInParams();
718 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
719 if (wave_buffer.buffer_address == 0) {
720 return 0;
721 }
722 if (wave_buffer.buffer_size == 0) {
723 return 0;
724 }
725 if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
726 return 0;
727 }
728
729 constexpr std::array<int, 16> SIGNED_NIBBLES = {
730 {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
731
732 constexpr std::size_t FRAME_LEN = 8;
733 constexpr std::size_t NIBBLES_PER_SAMPLE = 16;
734 constexpr std::size_t SAMPLES_PER_FRAME = 14;
735
736 auto frame_header = dsp_state.context.header;
737 s32 idx = (frame_header >> 4) & 0xf;
738 s32 scale = frame_header & 0xf;
739 s16 yn1 = dsp_state.context.yn1;
740 s16 yn2 = dsp_state.context.yn2;
741
742 Codec::ADPCM_Coeff coeffs;
743 memory.ReadBlock(in_params.additional_params_address, coeffs.data(),
744 sizeof(Codec::ADPCM_Coeff));
745
746 s32 coef1 = coeffs[idx * 2];
747 s32 coef2 = coeffs[idx * 2 + 1];
748
749 const auto samples_remaining =
750 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
751 const auto samples_processed = std::min(sample_count, samples_remaining);
752 const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset;
753
754 const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME;
755 auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) +
756 samples_remaining_in_frame + (samples_remaining_in_frame != 0 ? 2 : 0);
757
758 const auto decode_sample = [&](const int nibble) -> s16 {
759 const int xn = nibble * (1 << scale);
760 // We first transform everything into 11 bit fixed point, perform the second order
761 // digital filter, then transform back.
762 // 0x400 == 0.5 in 11 bit fixed point.
763 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
764 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
765 // Clamp to output range.
766 val = std::clamp<s32>(val, -32768, 32767);
767 // Advance output feedback.
768 yn2 = yn1;
769 yn1 = val;
770 return static_cast<s16>(val);
771 };
772
773 std::size_t buffer_offset{};
774 std::vector<u8> buffer(
775 std::max((samples_processed / FRAME_LEN) * SAMPLES_PER_FRAME, FRAME_LEN));
776 memory.ReadBlock(wave_buffer.buffer_address + (position_in_frame / 2), buffer.data(),
777 buffer.size());
778 std::size_t cur_mix_offset = mix_offset;
779
780 auto remaining_samples = samples_processed;
781 while (remaining_samples > 0) {
782 if (position_in_frame % NIBBLES_PER_SAMPLE == 0) {
783 // Read header
784 frame_header = buffer[buffer_offset++];
785 idx = (frame_header >> 4) & 0xf;
786 scale = frame_header & 0xf;
787 coef1 = coeffs[idx * 2];
788 coef2 = coeffs[idx * 2 + 1];
789 position_in_frame += 2;
790
791 // Decode entire frame
792 if (remaining_samples >= SAMPLES_PER_FRAME) {
793 for (std::size_t i = 0; i < SAMPLES_PER_FRAME / 2; i++) {
794
795 // Sample 1
796 const s32 s0 = SIGNED_NIBBLES[buffer[buffer_offset] >> 4];
797 const s32 s1 = SIGNED_NIBBLES[buffer[buffer_offset++] & 0xf];
798 const s16 sample_1 = decode_sample(s0);
799 const s16 sample_2 = decode_sample(s1);
800 sample_buffer[cur_mix_offset++] = sample_1;
801 sample_buffer[cur_mix_offset++] = sample_2;
802 }
803 remaining_samples -= SAMPLES_PER_FRAME;
804 position_in_frame += SAMPLES_PER_FRAME;
805 continue;
806 }
807 }
808 // Decode mid frame
809 s32 current_nibble = buffer[buffer_offset];
810 if (position_in_frame++ & 0x1) {
811 current_nibble &= 0xf;
812 buffer_offset++;
813 } else {
814 current_nibble >>= 4;
815 }
816 const s16 sample = decode_sample(SIGNED_NIBBLES[current_nibble]);
817 sample_buffer[cur_mix_offset++] = sample;
818 remaining_samples--;
819 }
820
821 dsp_state.context.header = frame_header;
822 dsp_state.context.yn1 = yn1;
823 dsp_state.context.yn2 = yn2;
824
825 return samples_processed;
826}
827
828s32* CommandGenerator::GetMixBuffer(std::size_t index) {
829 return mix_buffer.data() + (index * worker_params.sample_count);
830}
831
832const s32* CommandGenerator::GetMixBuffer(std::size_t index) const {
833 return mix_buffer.data() + (index * worker_params.sample_count);
834}
835
836std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const {
837 return worker_params.mix_buffer_count + channel;
838}
839
840std::size_t CommandGenerator::GetTotalMixBufferCount() const {
841 return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT;
842}
843
844s32* CommandGenerator::GetChannelMixBuffer(s32 channel) {
845 return GetMixBuffer(worker_params.mix_buffer_count + channel);
846}
847
848const s32* CommandGenerator::GetChannelMixBuffer(s32 channel) const {
849 return GetMixBuffer(worker_params.mix_buffer_count + channel);
850}
851
852void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output,
853 VoiceState& dsp_state, s32 channel,
854 s32 target_sample_rate, s32 sample_count,
855 s32 node_id) {
856 auto& in_params = voice_info.GetInParams();
857 if (dumping_frame) {
858 LOG_DEBUG(Audio,
859 "(DSP_TRACE) DecodeFromWaveBuffers, node_id={}, channel={}, "
860 "format={}, sample_count={}, sample_rate={}, mix_id={}, splitter_id={}",
861 node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate,
862 in_params.mix_id, in_params.splitter_info_id);
863 }
864 ASSERT_OR_EXECUTE(output != nullptr, { return; });
865
866 const auto resample_rate = static_cast<s32>(
867 static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
868 static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
869 auto* output_base = output;
870 if ((dsp_state.fraction + sample_count * resample_rate) > (SCALED_MIX_BUFFER_SIZE - 4ULL)) {
871 return;
872 }
873
874 auto min_required_samples =
875 std::min(static_cast<s32>(SCALED_MIX_BUFFER_SIZE) - dsp_state.fraction, resample_rate);
876 if (min_required_samples >= sample_count) {
877 min_required_samples = sample_count;
878 }
879
880 std::size_t temp_mix_offset{};
881 bool is_buffer_completed{false};
882 auto samples_remaining = sample_count;
883 while (samples_remaining > 0 && !is_buffer_completed) {
884 const auto samples_to_output = std::min(samples_remaining, min_required_samples);
885 const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15;
886
887 if (!in_params.behavior_flags.is_pitch_and_src_skipped) {
888 // Append sample histtory for resampler
889 for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
890 sample_buffer[temp_mix_offset + i] = dsp_state.sample_history[i];
891 }
892 temp_mix_offset += 4;
893 }
894
895 s32 samples_read{};
896 while (samples_read < samples_to_read) {
897 const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
898 // No more data can be read
899 if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) {
900 is_buffer_completed = true;
901 break;
902 }
903
904 if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 &&
905 wave_buffer.context_address != 0 && wave_buffer.context_size != 0) {
906 // TODO(ogniK): ADPCM loop context
907 }
908
909 s32 samples_decoded{0};
910 switch (in_params.sample_format) {
911 case SampleFormat::Pcm16:
912 samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read,
913 channel, temp_mix_offset);
914 break;
915 case SampleFormat::Adpcm:
916 samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read,
917 channel, temp_mix_offset);
918 break;
919 default:
920 UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
921 }
922
923 temp_mix_offset += samples_decoded;
924 samples_read += samples_decoded;
925 dsp_state.offset += samples_decoded;
926 dsp_state.played_sample_count += samples_decoded;
927
928 if (dsp_state.offset >=
929 (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) ||
930 samples_decoded == 0) {
931 // Reset our sample offset
932 dsp_state.offset = 0;
933 if (wave_buffer.is_looping) {
934 if (samples_decoded == 0) {
935 // End of our buffer
936 is_buffer_completed = true;
937 break;
938 }
939
940 if (in_params.behavior_flags.is_played_samples_reset_at_loop_point.Value()) {
941 dsp_state.played_sample_count = 0;
942 }
943 } else {
944
945 // Update our wave buffer states
946 dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false;
947 dsp_state.wave_buffer_consumed++;
948 dsp_state.wave_buffer_index =
949 (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
950 if (wave_buffer.end_of_stream) {
951 dsp_state.played_sample_count = 0;
952 }
953 }
954 }
955 }
956
957 if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) {
958 // No need to resample
959 std::memcpy(output, sample_buffer.data(), samples_read * sizeof(s32));
960 } else {
961 std::fill(sample_buffer.begin() + temp_mix_offset,
962 sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read),
963 0);
964 AudioCore::Resample(output, sample_buffer.data(), resample_rate, dsp_state.fraction,
965 samples_to_output);
966 // Resample
967 for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
968 dsp_state.sample_history[i] = sample_buffer[samples_to_read + i];
969 }
970 }
971 output += samples_to_output;
972 samples_remaining -= samples_to_output;
973 }
974}
975
976} // namespace AudioCore
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h
new file mode 100644
index 000000000..967d24078
--- /dev/null
+++ b/src/audio_core/command_generator.h
@@ -0,0 +1,103 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "audio_core/common.h"
9#include "audio_core/voice_context.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12
13namespace Core::Memory {
14class Memory;
15}
16
17namespace AudioCore {
18class MixContext;
19class SplitterContext;
20class ServerSplitterDestinationData;
21class ServerMixInfo;
22class EffectContext;
23class EffectBase;
24struct AuxInfoDSP;
25using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
26
27class CommandGenerator {
28public:
29 explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
30 VoiceContext& voice_context, MixContext& mix_context,
31 SplitterContext& splitter_context, EffectContext& effect_context,
32 Core::Memory::Memory& memory);
33 ~CommandGenerator();
34
35 void ClearMixBuffers();
36 void GenerateVoiceCommands();
37 void GenerateVoiceCommand(ServerVoiceInfo& voice_info);
38 void GenerateSubMixCommands();
39 void GenerateFinalMixCommands();
40 void PreCommand();
41 void PostCommand();
42
43 s32* GetChannelMixBuffer(s32 channel);
44 const s32* GetChannelMixBuffer(s32 channel) const;
45 s32* GetMixBuffer(std::size_t index);
46 const s32* GetMixBuffer(std::size_t index) const;
47 std::size_t GetMixChannelBufferOffset(s32 channel) const;
48
49 std::size_t GetTotalMixBufferCount() const;
50
51private:
52 void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
53 void GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
54 s32 mix_buffer_count, s32 channel);
55 void GenerateVolumeRampCommand(float last_volume, float current_volume, s32 channel,
56 s32 node_id);
57 void GenerateVoiceMixCommand(const MixVolumeBuffer& mix_volumes,
58 const MixVolumeBuffer& last_mix_volumes, VoiceState& dsp_state,
59 s32 mix_buffer_offset, s32 mix_buffer_count, s32 voice_index,
60 s32 node_id);
61 void GenerateSubMixCommand(ServerMixInfo& mix_info);
62 void GenerateMixCommands(ServerMixInfo& mix_info);
63 void GenerateMixCommand(std::size_t output_offset, std::size_t input_offset, float volume,
64 s32 node_id);
65 void GenerateFinalMixCommand();
66 void GenerateBiquadFilterCommand(s32 mix_buffer, const BiquadFilterParameter& params,
67 std::array<s64, 2>& state, std::size_t input_offset,
68 std::size_t output_offset, s32 sample_count, s32 node_id);
69 void GenerateDepopPrepareCommand(VoiceState& dsp_state, std::size_t mix_buffer_count,
70 std::size_t mix_buffer_offset);
71 void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
72 std::size_t mix_buffer_offset, s32 sample_rate);
73 void GenerateEffectCommand(ServerMixInfo& mix_info);
74 void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
75 void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
76 void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
77 ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
78
79 s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
80 u32 sample_count, u32 write_offset, u32 write_count);
81 s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
82 u32 sample_count, u32 read_offset, u32 read_count);
83
84 // DSP Code
85 s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
86 s32 channel, std::size_t mix_offset);
87 s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
88 s32 channel, std::size_t mix_offset);
89 void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state,
90 s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id);
91
92 AudioCommon::AudioRendererParameter& worker_params;
93 VoiceContext& voice_context;
94 MixContext& mix_context;
95 SplitterContext& splitter_context;
96 EffectContext& effect_context;
97 Core::Memory::Memory& memory;
98 std::vector<s32> mix_buffer{};
99 std::vector<s32> sample_buffer{};
100 std::vector<s32> depop_buffer{};
101 bool dumping_frame{false};
102};
103} // namespace AudioCore
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index 7bb145c53..72ebce221 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -8,13 +8,30 @@
8#include "common/swap.h" 8#include "common/swap.h"
9#include "core/hle/result.h" 9#include "core/hle/result.h"
10 10
11namespace AudioCore { 11namespace AudioCommon {
12namespace Audren { 12namespace Audren {
13constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; 13constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
14} 14constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
15} // namespace Audren
15 16
16constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); 17constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8');
17constexpr std::size_t MAX_MIX_BUFFERS = 24; 18constexpr std::size_t MAX_MIX_BUFFERS = 24;
19constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
20constexpr std::size_t MAX_CHANNEL_COUNT = 6;
21constexpr std::size_t MAX_WAVE_BUFFERS = 4;
22constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
23constexpr u32 STREAM_SAMPLE_RATE = 48000;
24constexpr u32 STREAM_NUM_CHANNELS = 6;
25constexpr s32 NO_SPLITTER = -1;
26constexpr s32 NO_MIX = 0x7fffffff;
27constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
28constexpr s32 FINAL_MIX = 0;
29constexpr s32 NO_EFFECT_ORDER = -1;
30constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant
31// Any size checks seem to take the sample history into account
32// and our const ends up being 0x3f04, the 4 bytes are most
33// likely the sample history
34constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
18 35
19static constexpr u32 VersionFromRevision(u32_le rev) { 36static constexpr u32 VersionFromRevision(u32_le rev) {
20 // "REV7" -> 7 37 // "REV7" -> 7
@@ -45,4 +62,46 @@ static constexpr bool CanConsumeBuffer(std::size_t size, std::size_t offset, std
45 return true; 62 return true;
46} 63}
47 64
48} // namespace AudioCore 65struct UpdateDataSizes {
66 u32_le behavior{};
67 u32_le memory_pool{};
68 u32_le voice{};
69 u32_le voice_channel_resource{};
70 u32_le effect{};
71 u32_le mixer{};
72 u32_le sink{};
73 u32_le performance{};
74 u32_le splitter{};
75 u32_le render_info{};
76 INSERT_PADDING_WORDS(4);
77};
78static_assert(sizeof(UpdateDataSizes) == 0x38, "UpdateDataSizes is an invalid size");
79
80struct UpdateDataHeader {
81 u32_le revision{};
82 UpdateDataSizes size{};
83 u32_le total_size{};
84};
85static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader is an invalid size");
86
87struct AudioRendererParameter {
88 u32_le sample_rate;
89 u32_le sample_count;
90 u32_le mix_buffer_count;
91 u32_le submix_count;
92 u32_le voice_count;
93 u32_le sink_count;
94 u32_le effect_count;
95 u32_le performance_frame_count;
96 u8 is_voice_drop_enabled;
97 u8 unknown_21;
98 u8 unknown_22;
99 u8 execution_mode;
100 u32_le splitter_count;
101 u32_le num_splitter_send_channels;
102 u32_le unknown_30;
103 u32_le revision;
104};
105static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
106
107} // namespace AudioCommon
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index c27df946c..83c06c0ed 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -23,14 +23,24 @@ class CubebSinkStream final : public SinkStream {
23public: 23public:
24 CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device, 24 CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
25 const std::string& name) 25 const std::string& name)
26 : ctx{ctx}, num_channels{std::min(num_channels_, 2u)}, time_stretch{sample_rate, 26 : ctx{ctx}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
27 num_channels} { 27 num_channels} {
28 28
29 cubeb_stream_params params{}; 29 cubeb_stream_params params{};
30 params.rate = sample_rate; 30 params.rate = sample_rate;
31 params.channels = num_channels; 31 params.channels = num_channels;
32 params.format = CUBEB_SAMPLE_S16NE; 32 params.format = CUBEB_SAMPLE_S16NE;
33 params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO; 33 switch (num_channels) {
34 case 1:
35 params.layout = CUBEB_LAYOUT_MONO;
36 break;
37 case 2:
38 params.layout = CUBEB_LAYOUT_STEREO;
39 break;
40 case 6:
41 params.layout = CUBEB_LAYOUT_3F2_LFE;
42 break;
43 }
34 44
35 u32 minimum_latency{}; 45 u32 minimum_latency{};
36 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) { 46 if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
@@ -193,6 +203,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
193 const std::size_t samples_to_write = num_channels * num_frames; 203 const std::size_t samples_to_write = num_channels * num_frames;
194 std::size_t samples_written; 204 std::size_t samples_written;
195 205
206 /*
196 if (Settings::values.enable_audio_stretching.GetValue()) { 207 if (Settings::values.enable_audio_stretching.GetValue()) {
197 const std::vector<s16> in{impl->queue.Pop()}; 208 const std::vector<s16> in{impl->queue.Pop()};
198 const std::size_t num_in{in.size() / num_channels}; 209 const std::size_t num_in{in.size() / num_channels};
@@ -207,7 +218,8 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
207 } 218 }
208 } else { 219 } else {
209 samples_written = impl->queue.Pop(buffer, samples_to_write); 220 samples_written = impl->queue.Pop(buffer, samples_to_write);
210 } 221 }*/
222 samples_written = impl->queue.Pop(buffer, samples_to_write);
211 223
212 if (samples_written >= num_channels) { 224 if (samples_written >= num_channels) {
213 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16), 225 std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
diff --git a/src/audio_core/effect_context.cpp b/src/audio_core/effect_context.cpp
new file mode 100644
index 000000000..adfec3df5
--- /dev/null
+++ b/src/audio_core/effect_context.cpp
@@ -0,0 +1,299 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include "audio_core/effect_context.h"
7
8namespace AudioCore {
9namespace {
10bool ValidChannelCountForEffect(s32 channel_count) {
11 return channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6;
12}
13} // namespace
14
15EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) {
16 effects.reserve(effect_count);
17 std::generate_n(std::back_inserter(effects), effect_count,
18 [] { return std::make_unique<EffectStubbed>(); });
19}
20EffectContext::~EffectContext() = default;
21
22std::size_t EffectContext::GetCount() const {
23 return effect_count;
24}
25
26EffectBase* EffectContext::GetInfo(std::size_t i) {
27 return effects.at(i).get();
28}
29
30EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
31 switch (effect) {
32 case EffectType::Invalid:
33 effects[i] = std::make_unique<EffectStubbed>();
34 break;
35 case EffectType::BufferMixer:
36 effects[i] = std::make_unique<EffectBufferMixer>();
37 break;
38 case EffectType::Aux:
39 effects[i] = std::make_unique<EffectAuxInfo>();
40 break;
41 case EffectType::Delay:
42 effects[i] = std::make_unique<EffectDelay>();
43 break;
44 case EffectType::Reverb:
45 effects[i] = std::make_unique<EffectReverb>();
46 break;
47 case EffectType::I3dl2Reverb:
48 effects[i] = std::make_unique<EffectI3dl2Reverb>();
49 break;
50 case EffectType::BiquadFilter:
51 effects[i] = std::make_unique<EffectBiquadFilter>();
52 break;
53 default:
54 UNREACHABLE_MSG("Unimplemented effect {}", effect);
55 effects[i] = std::make_unique<EffectStubbed>();
56 }
57 return GetInfo(i);
58}
59
60const EffectBase* EffectContext::GetInfo(std::size_t i) const {
61 return effects.at(i).get();
62}
63
64EffectStubbed::EffectStubbed() : EffectBase::EffectBase(EffectType::Invalid) {}
65EffectStubbed::~EffectStubbed() = default;
66
67void EffectStubbed::Update(EffectInfo::InParams& in_params) {}
68void EffectStubbed::UpdateForCommandGeneration() {}
69
70EffectBase::EffectBase(EffectType effect_type) : effect_type(effect_type) {}
71EffectBase::~EffectBase() = default;
72
73UsageState EffectBase::GetUsage() const {
74 return usage;
75}
76
77EffectType EffectBase::GetType() const {
78 return effect_type;
79}
80
81bool EffectBase::IsEnabled() const {
82 return enabled;
83}
84
85s32 EffectBase::GetMixID() const {
86 return mix_id;
87}
88
89s32 EffectBase::GetProcessingOrder() const {
90 return processing_order;
91}
92
93EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric::EffectGeneric(EffectType::I3dl2Reverb) {}
94EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
95
96void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
97 auto& internal_params = GetParams();
98 const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
99 if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
100 UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
101 return;
102 }
103
104 const auto last_status = internal_params.status;
105 mix_id = in_params.mix_id;
106 processing_order = in_params.processing_order;
107 internal_params = *reverb_params;
108 if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
109 internal_params.channel_count = internal_params.max_channels;
110 }
111 enabled = in_params.is_enabled;
112 if (last_status != ParameterStatus::Updated) {
113 internal_params.status = last_status;
114 }
115
116 if (in_params.is_new || skipped) {
117 usage = UsageState::Initialized;
118 internal_params.status = ParameterStatus::Initialized;
119 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
120 }
121}
122
123void EffectI3dl2Reverb::UpdateForCommandGeneration() {
124 if (enabled) {
125 usage = UsageState::Running;
126 } else {
127 usage = UsageState::Stopped;
128 }
129 GetParams().status = ParameterStatus::Updated;
130}
131
132EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric::EffectGeneric(EffectType::BiquadFilter) {}
133EffectBiquadFilter::~EffectBiquadFilter() = default;
134
135void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
136 auto& internal_params = GetParams();
137 const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data());
138 mix_id = in_params.mix_id;
139 processing_order = in_params.processing_order;
140 internal_params = *biquad_params;
141 enabled = in_params.is_enabled;
142}
143
144void EffectBiquadFilter::UpdateForCommandGeneration() {
145 if (enabled) {
146 usage = UsageState::Running;
147 } else {
148 usage = UsageState::Stopped;
149 }
150 GetParams().status = ParameterStatus::Updated;
151}
152
153EffectAuxInfo::EffectAuxInfo() : EffectGeneric::EffectGeneric(EffectType::Aux) {}
154EffectAuxInfo::~EffectAuxInfo() = default;
155
156void EffectAuxInfo::Update(EffectInfo::InParams& in_params) {
157 const auto* aux_params = reinterpret_cast<AuxInfo*>(in_params.raw.data());
158 mix_id = in_params.mix_id;
159 processing_order = in_params.processing_order;
160 GetParams() = *aux_params;
161 enabled = in_params.is_enabled;
162
163 if (in_params.is_new || skipped) {
164 skipped = aux_params->send_buffer_info == 0 || aux_params->return_buffer_info == 0;
165 if (skipped) {
166 return;
167 }
168
169 // There's two AuxInfos which are an identical size, the first one is managed by the cpu,
170 // the second is managed by the dsp. All we care about is managing the DSP one
171 send_info = aux_params->send_buffer_info + sizeof(AuxInfoDSP);
172 send_buffer = aux_params->send_buffer_info + (sizeof(AuxInfoDSP) * 2);
173
174 recv_info = aux_params->return_buffer_info + sizeof(AuxInfoDSP);
175 recv_buffer = aux_params->return_buffer_info + (sizeof(AuxInfoDSP) * 2);
176 }
177}
178
179void EffectAuxInfo::UpdateForCommandGeneration() {
180 if (enabled) {
181 usage = UsageState::Running;
182 } else {
183 usage = UsageState::Stopped;
184 }
185}
186
187const VAddr EffectAuxInfo::GetSendInfo() const {
188 return send_info;
189}
190
191const VAddr EffectAuxInfo::GetSendBuffer() const {
192 return send_buffer;
193}
194
195const VAddr EffectAuxInfo::GetRecvInfo() const {
196 return recv_info;
197}
198
199const VAddr EffectAuxInfo::GetRecvBuffer() const {
200 return recv_buffer;
201}
202
203EffectDelay::EffectDelay() : EffectGeneric::EffectGeneric(EffectType::Delay) {}
204EffectDelay::~EffectDelay() = default;
205
206void EffectDelay::Update(EffectInfo::InParams& in_params) {
207 const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data());
208 auto& internal_params = GetParams();
209 if (!ValidChannelCountForEffect(delay_params->max_channels)) {
210 return;
211 }
212
213 const auto last_status = internal_params.status;
214 mix_id = in_params.mix_id;
215 processing_order = in_params.processing_order;
216 internal_params = *delay_params;
217 if (!ValidChannelCountForEffect(delay_params->channels)) {
218 internal_params.channels = internal_params.max_channels;
219 }
220 enabled = in_params.is_enabled;
221
222 if (last_status != ParameterStatus::Updated) {
223 internal_params.status = last_status;
224 }
225
226 if (in_params.is_new || skipped) {
227 usage = UsageState::Initialized;
228 internal_params.status = ParameterStatus::Initialized;
229 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
230 }
231}
232
233void EffectDelay::UpdateForCommandGeneration() {
234 if (enabled) {
235 usage = UsageState::Running;
236 } else {
237 usage = UsageState::Stopped;
238 }
239 GetParams().status = ParameterStatus::Updated;
240}
241
242EffectBufferMixer::EffectBufferMixer() : EffectGeneric::EffectGeneric(EffectType::BufferMixer) {}
243EffectBufferMixer::~EffectBufferMixer() = default;
244
245void EffectBufferMixer::Update(EffectInfo::InParams& in_params) {
246 mix_id = in_params.mix_id;
247 processing_order = in_params.processing_order;
248 GetParams() = *reinterpret_cast<BufferMixerParams*>(in_params.raw.data());
249 enabled = in_params.is_enabled;
250}
251
252void EffectBufferMixer::UpdateForCommandGeneration() {
253 if (enabled) {
254 usage = UsageState::Running;
255 } else {
256 usage = UsageState::Stopped;
257 }
258}
259
260EffectReverb::EffectReverb() : EffectGeneric::EffectGeneric(EffectType::Reverb) {}
261EffectReverb::~EffectReverb() = default;
262
263void EffectReverb::Update(EffectInfo::InParams& in_params) {
264 const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data());
265 auto& internal_params = GetParams();
266 if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
267 return;
268 }
269
270 const auto last_status = internal_params.status;
271 mix_id = in_params.mix_id;
272 processing_order = in_params.processing_order;
273 internal_params = *reverb_params;
274 if (!ValidChannelCountForEffect(reverb_params->channels)) {
275 internal_params.channels = internal_params.max_channels;
276 }
277 enabled = in_params.is_enabled;
278
279 if (last_status != ParameterStatus::Updated) {
280 internal_params.status = last_status;
281 }
282
283 if (in_params.is_new || skipped) {
284 usage = UsageState::Initialized;
285 internal_params.status = ParameterStatus::Initialized;
286 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
287 }
288}
289
290void EffectReverb::UpdateForCommandGeneration() {
291 if (enabled) {
292 usage = UsageState::Running;
293 } else {
294 usage = UsageState::Stopped;
295 }
296 GetParams().status = ParameterStatus::Updated;
297}
298
299} // namespace AudioCore
diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h
new file mode 100644
index 000000000..2f2da72dd
--- /dev/null
+++ b/src/audio_core/effect_context.h
@@ -0,0 +1,322 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <memory>
9#include <vector>
10#include "audio_core/common.h"
11#include "common/common_funcs.h"
12#include "common/common_types.h"
13#include "common/swap.h"
14
15namespace AudioCore {
16enum class EffectType : u8 {
17 Invalid = 0,
18 BufferMixer = 1,
19 Aux = 2,
20 Delay = 3,
21 Reverb = 4,
22 I3dl2Reverb = 5,
23 BiquadFilter = 6,
24};
25
26enum class UsageStatus : u8 {
27 Invalid = 0,
28 New = 1,
29 Initialized = 2,
30 Used = 3,
31 Removed = 4,
32};
33
34enum class UsageState {
35 Invalid = 0,
36 Initialized = 1,
37 Running = 2,
38 Stopped = 3,
39};
40
41enum class ParameterStatus : u8 {
42 Initialized = 0,
43 Updating = 1,
44 Updated = 2,
45};
46
47struct BufferMixerParams {
48 std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input{};
49 std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output{};
50 std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> volume{};
51 s32_le count{};
52};
53static_assert(sizeof(BufferMixerParams) == 0x94, "BufferMixerParams is an invalid size");
54
55struct AuxInfoDSP {
56 u32_le read_offset{};
57 u32_le write_offset{};
58 u32_le remaining{};
59 INSERT_PADDING_WORDS(13);
60};
61static_assert(sizeof(AuxInfoDSP) == 0x40, "AuxInfoDSP is an invalid size");
62
63struct AuxInfo {
64 std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input_mix_buffers{};
65 std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output_mix_buffers{};
66 u32_le count{};
67 s32_le sample_rate{};
68 s32_le sample_count{};
69 s32_le mix_buffer_count{};
70 u64_le send_buffer_info{};
71 u64_le send_buffer_base{};
72
73 u64_le return_buffer_info{};
74 u64_le return_buffer_base{};
75};
76static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
77
78struct I3dl2ReverbParams {
79 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
80 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
81 u16_le max_channels{};
82 u16_le channel_count{};
83 INSERT_PADDING_BYTES(1);
84 u32_le sample_rate{};
85 f32 room_hf{};
86 f32 hf_reference{};
87 f32 decay_time{};
88 f32 hf_decay_ratio{};
89 f32 room{};
90 f32 reflection{};
91 f32 reverb{};
92 f32 diffusion{};
93 f32 reflection_delay{};
94 f32 reverb_delay{};
95 f32 density{};
96 f32 dry_gain{};
97 ParameterStatus status{};
98 INSERT_PADDING_BYTES(3);
99};
100static_assert(sizeof(I3dl2ReverbParams) == 0x4c, "I3dl2ReverbParams is an invalid size");
101
102struct BiquadFilterParams {
103 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
104 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
105 std::array<s16_le, 3> numerator;
106 std::array<s16_le, 2> denominator;
107 s8 channel_count{};
108 ParameterStatus status{};
109};
110static_assert(sizeof(BiquadFilterParams) == 0x18, "BiquadFilterParams is an invalid size");
111
112struct DelayParams {
113 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
114 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
115 u16_le max_channels{};
116 u16_le channels{};
117 s32_le max_delay{};
118 s32_le delay{};
119 s32_le sample_rate{};
120 s32_le gain{};
121 s32_le feedback_gain{};
122 s32_le out_gain{};
123 s32_le dry_gain{};
124 s32_le channel_spread{};
125 s32_le low_pass{};
126 ParameterStatus status{};
127 INSERT_PADDING_BYTES(3);
128};
129static_assert(sizeof(DelayParams) == 0x38, "DelayParams is an invalid size");
130
131struct ReverbParams {
132 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
133 std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
134 u16_le max_channels{};
135 u16_le channels{};
136 s32_le sample_rate{};
137 s32_le mode0{};
138 s32_le mode0_gain{};
139 s32_le pre_delay{};
140 s32_le mode1{};
141 s32_le mode1_gain{};
142 s32_le decay{};
143 s32_le hf_decay_ratio{};
144 s32_le coloration{};
145 s32_le reverb_gain{};
146 s32_le out_gain{};
147 s32_le dry_gain{};
148 ParameterStatus status{};
149 INSERT_PADDING_BYTES(3);
150};
151static_assert(sizeof(ReverbParams) == 0x44, "ReverbParams is an invalid size");
152
153class EffectInfo {
154public:
155 struct InParams {
156 EffectType type{};
157 u8 is_new{};
158 u8 is_enabled{};
159 INSERT_PADDING_BYTES(1);
160 s32_le mix_id{};
161 u64_le buffer_address{};
162 u64_le buffer_size{};
163 s32_le processing_order{};
164 INSERT_PADDING_BYTES(4);
165 union {
166 std::array<u8, 0xa0> raw;
167 };
168 };
169 static_assert(sizeof(EffectInfo::InParams) == 0xc0, "InParams is an invalid size");
170
171 struct OutParams {
172 UsageStatus status{};
173 INSERT_PADDING_BYTES(15);
174 };
175 static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size");
176};
177
178struct AuxAddress {
179 VAddr send_dsp_info{};
180 VAddr send_buffer_base{};
181 VAddr return_dsp_info{};
182 VAddr return_buffer_base{};
183};
184
185class EffectBase {
186public:
187 EffectBase(EffectType effect_type);
188 ~EffectBase();
189
190 virtual void Update(EffectInfo::InParams& in_params) = 0;
191 virtual void UpdateForCommandGeneration() = 0;
192 UsageState GetUsage() const;
193 EffectType GetType() const;
194 bool IsEnabled() const;
195 s32 GetMixID() const;
196 s32 GetProcessingOrder() const;
197
198protected:
199 UsageState usage{UsageState::Invalid};
200 EffectType effect_type{};
201 s32 mix_id{};
202 s32 processing_order{};
203 bool enabled = false;
204};
205
206template <typename T>
207class EffectGeneric : public EffectBase {
208public:
209 EffectGeneric(EffectType effect_type) : EffectBase::EffectBase(effect_type) {}
210 ~EffectGeneric() = default;
211
212 T& GetParams() {
213 return internal_params;
214 }
215
216 const I3dl2ReverbParams& GetParams() const {
217 return internal_params;
218 }
219
220private:
221 T internal_params{};
222};
223
224class EffectStubbed : public EffectBase {
225public:
226 explicit EffectStubbed();
227 ~EffectStubbed();
228
229 void Update(EffectInfo::InParams& in_params) override;
230 void UpdateForCommandGeneration() override;
231};
232
233class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
234public:
235 explicit EffectI3dl2Reverb();
236 ~EffectI3dl2Reverb();
237
238 void Update(EffectInfo::InParams& in_params) override;
239 void UpdateForCommandGeneration() override;
240
241private:
242 bool skipped = false;
243};
244
245class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
246public:
247 explicit EffectBiquadFilter();
248 ~EffectBiquadFilter();
249
250 void Update(EffectInfo::InParams& in_params) override;
251 void UpdateForCommandGeneration() override;
252};
253
254class EffectAuxInfo : public EffectGeneric<AuxInfo> {
255public:
256 explicit EffectAuxInfo();
257 ~EffectAuxInfo();
258
259 void Update(EffectInfo::InParams& in_params) override;
260 void UpdateForCommandGeneration() override;
261 const VAddr GetSendInfo() const;
262 const VAddr GetSendBuffer() const;
263 const VAddr GetRecvInfo() const;
264 const VAddr GetRecvBuffer() const;
265
266private:
267 VAddr send_info{};
268 VAddr send_buffer{};
269 VAddr recv_info{};
270 VAddr recv_buffer{};
271 bool skipped = false;
272 AuxAddress addresses{};
273};
274
275class EffectDelay : public EffectGeneric<DelayParams> {
276public:
277 explicit EffectDelay();
278 ~EffectDelay();
279
280 void Update(EffectInfo::InParams& in_params) override;
281 void UpdateForCommandGeneration() override;
282
283private:
284 bool skipped = false;
285};
286
287class EffectBufferMixer : public EffectGeneric<BufferMixerParams> {
288public:
289 explicit EffectBufferMixer();
290 ~EffectBufferMixer();
291
292 void Update(EffectInfo::InParams& in_params) override;
293 void UpdateForCommandGeneration() override;
294};
295
296class EffectReverb : public EffectGeneric<ReverbParams> {
297public:
298 explicit EffectReverb();
299 ~EffectReverb();
300
301 void Update(EffectInfo::InParams& in_params) override;
302 void UpdateForCommandGeneration() override;
303
304private:
305 bool skipped = false;
306};
307
308class EffectContext {
309public:
310 explicit EffectContext(std::size_t effect_count);
311 ~EffectContext();
312
313 std::size_t GetCount() const;
314 EffectBase* GetInfo(std::size_t i);
315 EffectBase* RetargetEffect(std::size_t i, EffectType effect);
316 const EffectBase* GetInfo(std::size_t i) const;
317
318private:
319 std::size_t effect_count{};
320 std::vector<std::unique_ptr<EffectBase>> effects;
321};
322} // namespace AudioCore
diff --git a/src/audio_core/info_updater.cpp b/src/audio_core/info_updater.cpp
new file mode 100644
index 000000000..f53ce21a5
--- /dev/null
+++ b/src/audio_core/info_updater.cpp
@@ -0,0 +1,517 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/behavior_info.h"
6#include "audio_core/effect_context.h"
7#include "audio_core/info_updater.h"
8#include "audio_core/memory_pool.h"
9#include "audio_core/mix_context.h"
10#include "audio_core/sink_context.h"
11#include "audio_core/splitter_context.h"
12#include "audio_core/voice_context.h"
13#include "common/logging/log.h"
14
15namespace AudioCore {
16
17InfoUpdater::InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
18 BehaviorInfo& behavior_info)
19 : in_params(in_params), out_params(out_params), behavior_info(behavior_info) {
20 ASSERT(
21 AudioCommon::CanConsumeBuffer(in_params.size(), 0, sizeof(AudioCommon::UpdateDataHeader)));
22 std::memcpy(&input_header, in_params.data(), sizeof(AudioCommon::UpdateDataHeader));
23 output_header.total_size = sizeof(AudioCommon::UpdateDataHeader);
24}
25
26InfoUpdater::~InfoUpdater() = default;
27
28bool InfoUpdater::UpdateBehaviorInfo(BehaviorInfo& in_behavior_info) {
29 if (input_header.size.behavior != sizeof(BehaviorInfo::InParams)) {
30 LOG_ERROR(Audio, "Behavior info is an invalid size, expecting 0x{:X} but got 0x{:X}",
31 sizeof(BehaviorInfo::InParams), input_header.size.behavior);
32 return false;
33 }
34
35 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset,
36 sizeof(BehaviorInfo::InParams))) {
37 LOG_ERROR(Audio, "Buffer is an invalid size!");
38 return false;
39 }
40
41 BehaviorInfo::InParams behavior_in{};
42 std::memcpy(&behavior_in, in_params.data() + input_offset, sizeof(BehaviorInfo::InParams));
43 input_offset += sizeof(BehaviorInfo::InParams);
44
45 // Make sure it's an audio revision we can actually support
46 if (!AudioCommon::IsValidRevision(behavior_in.revision)) {
47 LOG_ERROR(Audio, "Invalid input revision, revision=0x{:08X}", behavior_in.revision);
48 return false;
49 }
50
51 // Make sure that our behavior info revision matches the input
52 if (in_behavior_info.GetUserRevision() != behavior_in.revision) {
53 LOG_ERROR(Audio,
54 "User revision differs from input revision, expecting 0x{:08X} but got 0x{:08X}",
55 in_behavior_info.GetUserRevision(), behavior_in.revision);
56 return false;
57 }
58
59 // Update behavior info flags
60 in_behavior_info.ClearError();
61 in_behavior_info.UpdateFlags(behavior_in.flags);
62
63 return true;
64}
65
66bool InfoUpdater::UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info) {
67 const auto force_mapping = behavior_info.IsMemoryPoolForceMappingEnabled();
68 const auto memory_pool_count = memory_pool_info.size();
69 const auto total_memory_pool_in = sizeof(ServerMemoryPoolInfo::InParams) * memory_pool_count;
70 const auto total_memory_pool_out = sizeof(ServerMemoryPoolInfo::OutParams) * memory_pool_count;
71
72 if (input_header.size.memory_pool != total_memory_pool_in) {
73 LOG_ERROR(Audio, "Memory pools are an invalid size, expecting 0x{:X} but got 0x{:X}",
74 total_memory_pool_in, input_header.size.memory_pool);
75 return false;
76 }
77
78 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_memory_pool_in)) {
79 LOG_ERROR(Audio, "Buffer is an invalid size!");
80 return false;
81 }
82
83 std::vector<ServerMemoryPoolInfo::InParams> mempool_in(memory_pool_count);
84 std::vector<ServerMemoryPoolInfo::OutParams> mempool_out(memory_pool_count);
85
86 std::memcpy(mempool_in.data(), in_params.data() + input_offset, total_memory_pool_in);
87 input_offset += total_memory_pool_in;
88
89 // Update our memory pools
90 for (std::size_t i = 0; i < memory_pool_count; i++) {
91 if (!memory_pool_info[i].Update(mempool_in[i], mempool_out[i])) {
92 LOG_ERROR(Audio, "Failed to update memory pool {}!", i);
93 return false;
94 }
95 }
96
97 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset,
98 sizeof(BehaviorInfo::InParams))) {
99 LOG_ERROR(Audio, "Buffer is an invalid size!");
100 return false;
101 }
102
103 std::memcpy(out_params.data() + output_offset, mempool_out.data(), total_memory_pool_out);
104 output_offset += total_memory_pool_out;
105 output_header.size.memory_pool = static_cast<u32>(total_memory_pool_out);
106 return true;
107}
108
109bool InfoUpdater::UpdateVoiceChannelResources(VoiceContext& voice_context) {
110 const auto voice_count = voice_context.GetVoiceCount();
111 const auto voice_size = voice_count * sizeof(VoiceChannelResource::InParams);
112 std::vector<VoiceChannelResource::InParams> resources_in(voice_count);
113
114 if (input_header.size.voice_channel_resource != voice_size) {
115 LOG_ERROR(Audio, "VoiceChannelResource is an invalid size, expecting 0x{:X} but got 0x{:X}",
116 voice_size, input_header.size.voice_channel_resource);
117 return false;
118 }
119
120 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, voice_size)) {
121 LOG_ERROR(Audio, "Buffer is an invalid size!");
122 return false;
123 }
124
125 std::memcpy(resources_in.data(), in_params.data() + input_offset, voice_size);
126 input_offset += voice_size;
127
128 // Update our channel resources
129 for (std::size_t i = 0; i < voice_count; i++) {
130 // Grab our channel resource
131 auto& resource = voice_context.GetChannelResource(i);
132 resource.Update(resources_in[i]);
133 }
134
135 return true;
136}
137
138bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
139 std::vector<ServerMemoryPoolInfo>& memory_pool_info,
140 VAddr audio_codec_dsp_addr) {
141 const auto voice_count = voice_context.GetVoiceCount();
142 std::vector<VoiceInfo::InParams> voice_in(voice_count);
143 std::vector<VoiceInfo::OutParams> voice_out(voice_count);
144
145 const auto voice_in_size = voice_count * sizeof(VoiceInfo::InParams);
146 const auto voice_out_size = voice_count * sizeof(VoiceInfo::OutParams);
147
148 if (input_header.size.voice != voice_in_size) {
149 LOG_ERROR(Audio, "Voices are an invalid size, expecting 0x{:X} but got 0x{:X}",
150 voice_in_size, input_header.size.voice);
151 return false;
152 }
153
154 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, voice_in_size)) {
155 LOG_ERROR(Audio, "Buffer is an invalid size!");
156 return false;
157 }
158
159 std::memcpy(voice_in.data(), in_params.data() + input_offset, voice_in_size);
160 input_offset += voice_in_size;
161
162 // Set all voices to not be in use
163 for (std::size_t i = 0; i < voice_count; i++) {
164 voice_context.GetInfo(i).GetInParams().in_use = false;
165 }
166
167 // Update our voices
168 for (std::size_t i = 0; i < voice_count; i++) {
169 auto& in_params = voice_in[i];
170 const auto channel_count = static_cast<std::size_t>(in_params.channel_count);
171 // Skip if it's not currently in use
172 if (!in_params.is_in_use) {
173 continue;
174 }
175 // Voice states for each channel
176 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> voice_states{};
177 ASSERT(in_params.id < voice_count);
178
179 // Grab our current voice info
180 auto& voice_info = voice_context.GetInfo(static_cast<std::size_t>(in_params.id));
181
182 ASSERT(channel_count <= AudioCommon::MAX_CHANNEL_COUNT);
183
184 // Get all our channel voice states
185 for (std::size_t channel = 0; channel < channel_count; channel++) {
186 voice_states[channel] =
187 &voice_context.GetState(in_params.voice_channel_resource_ids[channel]);
188 }
189
190 if (in_params.is_new) {
191 // Default our values for our voice
192 voice_info.Initialize();
193 if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) {
194 continue;
195 }
196
197 // Zero out our voice states
198 for (std::size_t channel = 0; channel < channel_count; channel++) {
199 std::memset(voice_states[channel], 0, sizeof(VoiceState));
200 }
201 }
202
203 // Update our voice
204 voice_info.UpdateParameters(in_params, behavior_info);
205 // TODO(ogniK): Handle mapping errors with behavior info based on in params response
206
207 // Update our wave buffers
208 voice_info.UpdateWaveBuffers(in_params, voice_states, behavior_info);
209 voice_info.WriteOutStatus(voice_out[i], in_params, voice_states);
210 }
211
212 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, voice_out_size)) {
213 LOG_ERROR(Audio, "Buffer is an invalid size!");
214 return false;
215 }
216 std::memcpy(out_params.data() + output_offset, voice_out.data(), voice_out_size);
217 output_offset += voice_out_size;
218 output_header.size.voice = static_cast<u32>(voice_out_size);
219 return true;
220}
221
222bool InfoUpdater::UpdateEffects(EffectContext& effect_context, bool is_active) {
223 const auto effect_count = effect_context.GetCount();
224 std::vector<EffectInfo::InParams> effect_in(effect_count);
225 std::vector<EffectInfo::OutParams> effect_out(effect_count);
226
227 const auto total_effect_in = effect_count * sizeof(EffectInfo::InParams);
228 const auto total_effect_out = effect_count * sizeof(EffectInfo::OutParams);
229
230 if (input_header.size.effect != total_effect_in) {
231 LOG_ERROR(Audio, "Effects are an invalid size, expecting 0x{:X} but got 0x{:X}",
232 total_effect_in, input_header.size.effect);
233 return false;
234 }
235
236 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_effect_in)) {
237 LOG_ERROR(Audio, "Buffer is an invalid size!");
238 return false;
239 }
240
241 std::memcpy(effect_in.data(), in_params.data() + input_offset, total_effect_in);
242 input_offset += total_effect_in;
243
244 // Update effects
245 for (std::size_t i = 0; i < effect_count; i++) {
246 auto* info = effect_context.GetInfo(i);
247 if (effect_in[i].type != info->GetType()) {
248 info = effect_context.RetargetEffect(i, effect_in[i].type);
249 }
250
251 info->Update(effect_in[i]);
252
253 if ((!is_active && info->GetUsage() != UsageState::Initialized) ||
254 info->GetUsage() == UsageState::Stopped) {
255 effect_out[i].status = UsageStatus::Removed;
256 } else {
257 effect_out[i].status = UsageStatus::Used;
258 }
259 }
260
261 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_effect_out)) {
262 LOG_ERROR(Audio, "Buffer is an invalid size!");
263 return false;
264 }
265
266 std::memcpy(out_params.data() + output_offset, effect_out.data(), total_effect_out);
267 output_offset += total_effect_out;
268 output_header.size.effect = static_cast<u32>(total_effect_out);
269
270 return true;
271}
272
273bool InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) {
274 std::size_t start_offset = input_offset;
275 std::size_t bytes_read{};
276 // Update splitter context
277 if (!splitter_context.Update(in_params, input_offset, bytes_read)) {
278 LOG_ERROR(Audio, "Failed to update splitter context!");
279 return false;
280 }
281
282 const auto consumed = input_offset - start_offset;
283
284 if (input_header.size.splitter != consumed) {
285 LOG_ERROR(Audio, "Splitters is an invalid size, expecting 0x{:X} but got 0x{:X}",
286 bytes_read, input_header.size.splitter);
287 return false;
288 }
289
290 return true;
291}
292
293ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
294 SplitterContext& splitter_context,
295 EffectContext& effect_context) {
296 std::vector<MixInfo::InParams> mix_in_params;
297
298 if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
299 // If we're not dirty, get ALL mix in parameters
300 const auto context_mix_count = mix_context.GetCount();
301 const auto total_mix_in = context_mix_count * sizeof(MixInfo::InParams);
302 if (input_header.size.mixer != total_mix_in) {
303 LOG_ERROR(Audio, "Mixer is an invalid size, expecting 0x{:X} but got 0x{:X}",
304 total_mix_in, input_header.size.mixer);
305 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
306 }
307
308 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_mix_in)) {
309 LOG_ERROR(Audio, "Buffer is an invalid size!");
310 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
311 }
312
313 mix_in_params.resize(context_mix_count);
314 std::memcpy(mix_in_params.data(), in_params.data() + input_offset, total_mix_in);
315
316 input_offset += total_mix_in;
317 } else {
318 // Only update the "dirty" mixes
319 MixInfo::DirtyHeader dirty_header{};
320 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset,
321 sizeof(MixInfo::DirtyHeader))) {
322 LOG_ERROR(Audio, "Buffer is an invalid size!");
323 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
324 }
325
326 std::memcpy(&dirty_header, in_params.data() + input_offset, sizeof(MixInfo::DirtyHeader));
327 input_offset += sizeof(MixInfo::DirtyHeader);
328
329 const auto total_mix_in =
330 dirty_header.mixer_count * sizeof(MixInfo::InParams) + sizeof(MixInfo::DirtyHeader);
331
332 if (input_header.size.mixer != total_mix_in) {
333 LOG_ERROR(Audio, "Mixer is an invalid size, expecting 0x{:X} but got 0x{:X}",
334 total_mix_in, input_header.size.mixer);
335 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
336 }
337
338 if (dirty_header.mixer_count != 0) {
339 mix_in_params.resize(dirty_header.mixer_count);
340 std::memcpy(mix_in_params.data(), in_params.data() + input_offset,
341 mix_in_params.size() * sizeof(MixInfo::InParams));
342 input_offset += mix_in_params.size() * sizeof(MixInfo::InParams);
343 }
344 }
345
346 // Get our total input count
347 const auto mix_count = mix_in_params.size();
348
349 if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
350 // Only verify our buffer count if we're not dirty
351 std::size_t total_buffer_count{};
352 for (std::size_t i = 0; i < mix_count; i++) {
353 const auto& in = mix_in_params[i];
354 total_buffer_count += in.buffer_count;
355 if (in.dest_mix_id > mix_count && in.dest_mix_id != AudioCommon::NO_MIX &&
356 in.mix_id != AudioCommon::FINAL_MIX) {
357 LOG_ERROR(
358 Audio,
359 "Invalid mix destination, mix_id={:X}, dest_mix_id={:X}, mix_buffer_count={:X}",
360 in.mix_id, in.dest_mix_id, mix_buffer_count);
361 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
362 }
363 }
364
365 if (total_buffer_count > mix_buffer_count) {
366 LOG_ERROR(Audio,
367 "Too many mix buffers used! mix_buffer_count={:X}, requesting_buffers={:X}",
368 mix_buffer_count, total_buffer_count);
369 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
370 }
371 }
372
373 if (mix_buffer_count == 0) {
374 LOG_ERROR(Audio, "No mix buffers!");
375 return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
376 }
377
378 bool should_sort = false;
379 for (std::size_t i = 0; i < mix_count; i++) {
380 const auto& mix_in = mix_in_params[i];
381 std::size_t target_mix{};
382 if (behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
383 target_mix = mix_in.mix_id;
384 } else {
385 // Non dirty supported games just use i instead of the actual mix_id
386 target_mix = i;
387 }
388 auto& mix_info = mix_context.GetInfo(target_mix);
389 auto& mix_info_params = mix_info.GetInParams();
390 if (mix_info_params.in_use != mix_in.in_use) {
391 mix_info_params.in_use = mix_in.in_use;
392 mix_info.ResetEffectProcessingOrder();
393 should_sort = true;
394 }
395
396 if (mix_in.in_use) {
397 should_sort |= mix_info.Update(mix_context.GetEdgeMatrix(), mix_in, behavior_info,
398 splitter_context, effect_context);
399 }
400 }
401
402 if (should_sort && behavior_info.IsSplitterSupported()) {
403 // Sort our splitter data
404 if (!mix_context.TsortInfo(splitter_context)) {
405 return AudioCommon::Audren::ERR_SPLITTER_SORT_FAILED;
406 }
407 }
408
409 // TODO(ogniK): Sort when splitter is suppoorted
410
411 return RESULT_SUCCESS;
412}
413
414bool InfoUpdater::UpdateSinks(SinkContext& sink_context) {
415 const auto sink_count = sink_context.GetCount();
416 std::vector<SinkInfo::InParams> sink_in_params(sink_count);
417 const auto total_sink_in = sink_count * sizeof(SinkInfo::InParams);
418
419 if (input_header.size.sink != total_sink_in) {
420 LOG_ERROR(Audio, "Sinks are an invalid size, expecting 0x{:X} but got 0x{:X}",
421 total_sink_in, input_header.size.effect);
422 return false;
423 }
424
425 if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_sink_in)) {
426 LOG_ERROR(Audio, "Buffer is an invalid size!");
427 return false;
428 }
429
430 std::memcpy(sink_in_params.data(), in_params.data() + input_offset, total_sink_in);
431 input_offset += total_sink_in;
432
433 // TODO(ogniK): Properly update sinks
434 if (!sink_in_params.empty()) {
435 sink_context.UpdateMainSink(sink_in_params[0]);
436 }
437
438 output_header.size.sink = static_cast<u32>(0x20 * sink_count);
439 output_offset += 0x20 * sink_count;
440 return true;
441}
442
443bool InfoUpdater::UpdatePerformanceBuffer() {
444 output_header.size.performance = 0x10;
445 output_offset += 0x10;
446 return true;
447}
448
449bool InfoUpdater::UpdateErrorInfo(BehaviorInfo& in_behavior_info) {
450 const auto total_beahvior_info_out = sizeof(BehaviorInfo::OutParams);
451
452 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_beahvior_info_out)) {
453 LOG_ERROR(Audio, "Buffer is an invalid size!");
454 return false;
455 }
456
457 BehaviorInfo::OutParams behavior_info_out{};
458 behavior_info.CopyErrorInfo(behavior_info_out);
459
460 std::memcpy(out_params.data() + output_offset, &behavior_info_out, total_beahvior_info_out);
461 output_offset += total_beahvior_info_out;
462 output_header.size.behavior = total_beahvior_info_out;
463
464 return true;
465}
466
467struct RendererInfo {
468 u64_le elasped_frame_count{};
469 INSERT_PADDING_WORDS(2);
470};
471static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
472
473bool InfoUpdater::UpdateRendererInfo(std::size_t elapsed_frame_count) {
474 const auto total_renderer_info_out = sizeof(RendererInfo);
475 if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_renderer_info_out)) {
476 LOG_ERROR(Audio, "Buffer is an invalid size!");
477 return false;
478 }
479 RendererInfo out{};
480 out.elasped_frame_count = elapsed_frame_count;
481 std::memcpy(out_params.data() + output_offset, &out, total_renderer_info_out);
482 output_offset += total_renderer_info_out;
483 output_header.size.render_info = total_renderer_info_out;
484
485 return true;
486}
487
488bool InfoUpdater::CheckConsumedSize() const {
489 if (output_offset != out_params.size()) {
490 LOG_ERROR(Audio, "Output is not consumed! Consumed {}, but requires {}. {} bytes remaining",
491 output_offset, out_params.size(), out_params.size() - output_offset);
492 return false;
493 }
494 /*if (input_offset != in_params.size()) {
495 LOG_ERROR(Audio, "Input is not consumed!");
496 return false;
497 }*/
498 return true;
499}
500
501bool InfoUpdater::WriteOutputHeader() {
502 if (!AudioCommon::CanConsumeBuffer(out_params.size(), 0,
503 sizeof(AudioCommon::UpdateDataHeader))) {
504 LOG_ERROR(Audio, "Buffer is an invalid size!");
505 return false;
506 }
507 output_header.revision = AudioCommon::CURRENT_PROCESS_REVISION;
508 const auto& sz = output_header.size;
509 output_header.total_size += sz.behavior + sz.memory_pool + sz.voice +
510 sz.voice_channel_resource + sz.effect + sz.mixer + sz.sink +
511 sz.performance + sz.splitter + sz.render_info;
512
513 std::memcpy(out_params.data(), &output_header, sizeof(AudioCommon::UpdateDataHeader));
514 return true;
515}
516
517} // namespace AudioCore
diff --git a/src/audio_core/info_updater.h b/src/audio_core/info_updater.h
new file mode 100644
index 000000000..06f9d770f
--- /dev/null
+++ b/src/audio_core/info_updater.h
@@ -0,0 +1,58 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "audio_core/common.h"
9#include "common/common_types.h"
10
11namespace AudioCore {
12
13class BehaviorInfo;
14class ServerMemoryPoolInfo;
15class VoiceContext;
16class EffectContext;
17class MixContext;
18class SinkContext;
19class SplitterContext;
20
21class InfoUpdater {
22public:
23 // TODO(ogniK): Pass process handle when we support it
24 InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
25 BehaviorInfo& behavior_info);
26 ~InfoUpdater();
27
28 bool UpdateBehaviorInfo(BehaviorInfo& in_behavior_info);
29 bool UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info);
30 bool UpdateVoiceChannelResources(VoiceContext& voice_context);
31 bool UpdateVoices(VoiceContext& voice_context,
32 std::vector<ServerMemoryPoolInfo>& memory_pool_info,
33 VAddr audio_codec_dsp_addr);
34 bool UpdateEffects(EffectContext& effect_context, bool is_active);
35 bool UpdateSplitterInfo(SplitterContext& splitter_context);
36 ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
37 SplitterContext& splitter_context, EffectContext& effect_context);
38 bool UpdateSinks(SinkContext& sink_context);
39 bool UpdatePerformanceBuffer();
40 bool UpdateErrorInfo(BehaviorInfo& in_behavior_info);
41 bool UpdateRendererInfo(std::size_t elapsed_frame_count);
42 bool CheckConsumedSize() const;
43
44 bool WriteOutputHeader();
45
46private:
47 const std::vector<u8>& in_params;
48 std::vector<u8>& out_params;
49 BehaviorInfo& behavior_info;
50
51 AudioCommon::UpdateDataHeader input_header{};
52 AudioCommon::UpdateDataHeader output_header{};
53
54 std::size_t input_offset{sizeof(AudioCommon::UpdateDataHeader)};
55 std::size_t output_offset{sizeof(AudioCommon::UpdateDataHeader)};
56};
57
58} // namespace AudioCore
diff --git a/src/audio_core/memory_pool.cpp b/src/audio_core/memory_pool.cpp
new file mode 100644
index 000000000..5a3453063
--- /dev/null
+++ b/src/audio_core/memory_pool.cpp
@@ -0,0 +1,62 @@
1
2// Copyright 2020 yuzu Emulator Project
3// Licensed under GPLv2 or any later version
4// Refer to the license.txt file included.
5
6#include "audio_core/memory_pool.h"
7#include "common/logging/log.h"
8
9namespace AudioCore {
10
11ServerMemoryPoolInfo::ServerMemoryPoolInfo() = default;
12ServerMemoryPoolInfo::~ServerMemoryPoolInfo() = default;
13bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_params,
14 ServerMemoryPoolInfo::OutParams& out_params) {
15 // Our state does not need to be changed
16 if (in_params.state != ServerMemoryPoolInfo::State::RequestAttach &&
17 in_params.state != ServerMemoryPoolInfo::State::RequestDetach) {
18 return true;
19 }
20
21 // Address or size is null
22 if (in_params.address == 0 || in_params.size == 0) {
23 LOG_ERROR(Audio, "Memory pool address or size is zero! address={:X}, size={:X}",
24 in_params.address, in_params.size);
25 return false;
26 }
27
28 // Address or size is not aligned
29 if ((in_params.address % 0x1000) != 0 || (in_params.size % 0x1000) != 0) {
30 LOG_ERROR(Audio, "Memory pool address or size is not aligned! address={:X}, size={:X}",
31 in_params.address, in_params.size);
32 return false;
33 }
34
35 if (in_params.state == ServerMemoryPoolInfo::State::RequestAttach) {
36 cpu_address = in_params.address;
37 size = in_params.size;
38 used = true;
39 out_params.state = ServerMemoryPoolInfo::State::Attached;
40 } else {
41 // Unexpected address
42 if (cpu_address != in_params.address) {
43 LOG_ERROR(Audio, "Memory pool address differs! Expecting {:X} but address is {:X}",
44 cpu_address, in_params.address);
45 return false;
46 }
47
48 if (size != in_params.size) {
49 LOG_ERROR(Audio, "Memory pool size differs! Expecting {:X} but size is {:X}", size,
50 in_params.size);
51 return false;
52 }
53
54 cpu_address = 0;
55 size = 0;
56 used = false;
57 out_params.state = ServerMemoryPoolInfo::State::Detached;
58 }
59 return true;
60}
61
62} // namespace AudioCore
diff --git a/src/audio_core/memory_pool.h b/src/audio_core/memory_pool.h
new file mode 100644
index 000000000..8ac503f1c
--- /dev/null
+++ b/src/audio_core/memory_pool.h
@@ -0,0 +1,53 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/common_types.h"
9#include "common/swap.h"
10
11namespace AudioCore {
12
13class ServerMemoryPoolInfo {
14public:
15 ServerMemoryPoolInfo();
16 ~ServerMemoryPoolInfo();
17
18 enum class State : u32_le {
19 Invalid = 0x0,
20 Aquired = 0x1,
21 RequestDetach = 0x2,
22 Detached = 0x3,
23 RequestAttach = 0x4,
24 Attached = 0x5,
25 Released = 0x6,
26 };
27
28 struct InParams {
29 u64_le address{};
30 u64_le size{};
31 ServerMemoryPoolInfo::State state{};
32 INSERT_PADDING_WORDS(3);
33 };
34 static_assert(sizeof(ServerMemoryPoolInfo::InParams) == 0x20, "InParams are an invalid size");
35
36 struct OutParams {
37 ServerMemoryPoolInfo::State state{};
38 INSERT_PADDING_WORDS(3);
39 };
40 static_assert(sizeof(ServerMemoryPoolInfo::OutParams) == 0x10, "OutParams are an invalid size");
41
42 bool Update(const ServerMemoryPoolInfo::InParams& in_params,
43 ServerMemoryPoolInfo::OutParams& out_params);
44
45private:
46 // There's another entry here which is the DSP address, however since we're not talking to the
47 // DSP we can just use the same address provided by the guest without needing to remap
48 u64_le cpu_address{};
49 u64_le size{};
50 bool used{};
51};
52
53} // namespace AudioCore
diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp
new file mode 100644
index 000000000..042891490
--- /dev/null
+++ b/src/audio_core/mix_context.cpp
@@ -0,0 +1,296 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/behavior_info.h"
6#include "audio_core/common.h"
7#include "audio_core/effect_context.h"
8#include "audio_core/mix_context.h"
9#include "audio_core/splitter_context.h"
10
11namespace AudioCore {
12MixContext::MixContext() = default;
13MixContext::~MixContext() = default;
14
15void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
16 std::size_t effect_count) {
17 info_count = mix_count;
18 infos.resize(info_count);
19 auto& final_mix = GetInfo(AudioCommon::FINAL_MIX);
20 final_mix.GetInParams().mix_id = AudioCommon::FINAL_MIX;
21 sorted_info.reserve(infos.size());
22 for (auto& info : infos) {
23 sorted_info.push_back(&info);
24 }
25
26 for (auto& info : infos) {
27 info.SetEffectCount(effect_count);
28 }
29
30 // Only initialize our edge matrix and node states if splitters are supported
31 if (behavior_info.IsSplitterSupported()) {
32 node_states.Initialize(mix_count);
33 edge_matrix.Initialize(mix_count);
34 }
35}
36
37void MixContext::UpdateDistancesFromFinalMix() {
38 // Set all distances to be invalid
39 for (std::size_t i = 0; i < info_count; i++) {
40 GetInfo(i).GetInParams().final_mix_distance = AudioCommon::NO_FINAL_MIX;
41 }
42
43 for (std::size_t i = 0; i < info_count; i++) {
44 auto& info = GetInfo(i);
45 auto& in_params = info.GetInParams();
46 // Populate our sorted info
47 sorted_info[i] = &info;
48
49 if (!in_params.in_use) {
50 continue;
51 }
52
53 auto mix_id = in_params.mix_id;
54 // Needs to be referenced out of scope
55 s32 distance_to_final_mix{AudioCommon::FINAL_MIX};
56 for (; distance_to_final_mix < info_count; distance_to_final_mix++) {
57 if (mix_id == AudioCommon::FINAL_MIX) {
58 // If we're at the final mix, we're done
59 break;
60 } else if (mix_id == AudioCommon::NO_MIX) {
61 // If we have no more mix ids, we're done
62 distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
63 break;
64 } else {
65 const auto& dest_mix = GetInfo(mix_id);
66 const auto dest_mix_distance = dest_mix.GetInParams().final_mix_distance;
67
68 if (dest_mix_distance == AudioCommon::NO_FINAL_MIX) {
69 // If our current mix isn't pointing to a final mix, follow through
70 mix_id = dest_mix.GetInParams().dest_mix_id;
71 } else {
72 // Our current mix + 1 = final distance
73 distance_to_final_mix = dest_mix_distance + 1;
74 break;
75 }
76 }
77 }
78
79 // If we're out of range for our distance, mark it as no final mix
80 if (distance_to_final_mix >= info_count) {
81 distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
82 }
83
84 in_params.final_mix_distance = distance_to_final_mix;
85 }
86}
87
88void MixContext::CalcMixBufferOffset() {
89 s32 offset{};
90 for (std::size_t i = 0; i < info_count; i++) {
91 auto& info = GetSortedInfo(i);
92 auto& in_params = info.GetInParams();
93 if (in_params.in_use) {
94 // Only update if in use
95 in_params.buffer_offset = offset;
96 offset += in_params.buffer_count;
97 }
98 }
99}
100
101void MixContext::SortInfo() {
102 // Get the distance to the final mix
103 UpdateDistancesFromFinalMix();
104
105 // Sort based on the distance to the final mix
106 std::sort(sorted_info.begin(), sorted_info.end(),
107 [](const ServerMixInfo* lhs, const ServerMixInfo* rhs) {
108 return lhs->GetInParams().final_mix_distance >
109 rhs->GetInParams().final_mix_distance;
110 });
111
112 // Calculate the mix buffer offset
113 CalcMixBufferOffset();
114}
115
116bool MixContext::TsortInfo(SplitterContext& splitter_context) {
117 // If we're not using mixes, just calculate the mix buffer offset
118 if (!splitter_context.UsingSplitter()) {
119 CalcMixBufferOffset();
120 return true;
121 }
122 // Sort our node states
123 if (!node_states.Tsort(edge_matrix)) {
124 return false;
125 }
126
127 // Get our sorted list
128 const auto sorted_list = node_states.GetIndexList();
129 std::size_t info_id{};
130 for (auto itr = sorted_list.rbegin(); itr != sorted_list.rend(); ++itr) {
131 // Set our sorted info
132 sorted_info[info_id++] = &GetInfo(*itr);
133 }
134
135 // Calculate the mix buffer offset
136 CalcMixBufferOffset();
137 return true;
138}
139
140std::size_t MixContext::GetCount() const {
141 return info_count;
142}
143
144ServerMixInfo& MixContext::GetInfo(std::size_t i) {
145 ASSERT(i < info_count);
146 return infos.at(i);
147}
148
149const ServerMixInfo& MixContext::GetInfo(std::size_t i) const {
150 ASSERT(i < info_count);
151 return infos.at(i);
152}
153
154ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) {
155 ASSERT(i < info_count);
156 return *sorted_info.at(i);
157}
158
159const ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) const {
160 ASSERT(i < info_count);
161 return *sorted_info.at(i);
162}
163
164ServerMixInfo& MixContext::GetFinalMixInfo() {
165 return infos.at(AudioCommon::FINAL_MIX);
166}
167
168const ServerMixInfo& MixContext::GetFinalMixInfo() const {
169 return infos.at(AudioCommon::FINAL_MIX);
170}
171
172EdgeMatrix& MixContext::GetEdgeMatrix() {
173 return edge_matrix;
174}
175
176const EdgeMatrix& MixContext::GetEdgeMatrix() const {
177 return edge_matrix;
178}
179
180ServerMixInfo::ServerMixInfo() {
181 Cleanup();
182}
183ServerMixInfo::~ServerMixInfo() = default;
184
185const ServerMixInfo::InParams& ServerMixInfo::GetInParams() const {
186 return in_params;
187}
188
189ServerMixInfo::InParams& ServerMixInfo::GetInParams() {
190 return in_params;
191}
192
193bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
194 BehaviorInfo& behavior_info, SplitterContext& splitter_context,
195 EffectContext& effect_context) {
196 in_params.volume = mix_in.volume;
197 in_params.sample_rate = mix_in.sample_rate;
198 in_params.buffer_count = mix_in.buffer_count;
199 in_params.in_use = mix_in.in_use;
200 in_params.mix_id = mix_in.mix_id;
201 in_params.node_id = mix_in.node_id;
202 for (std::size_t i = 0; i < mix_in.mix_volume.size(); i++) {
203 std::copy(mix_in.mix_volume[i].begin(), mix_in.mix_volume[i].end(),
204 in_params.mix_volume[i].begin());
205 }
206
207 bool require_sort = false;
208
209 if (behavior_info.IsSplitterSupported()) {
210 require_sort = UpdateConnection(edge_matrix, mix_in, splitter_context);
211 } else {
212 in_params.dest_mix_id = mix_in.dest_mix_id;
213 in_params.splitter_id = AudioCommon::NO_SPLITTER;
214 }
215
216 ResetEffectProcessingOrder();
217 const auto effect_count = effect_context.GetCount();
218 for (std::size_t i = 0; i < effect_count; i++) {
219 auto* effect_info = effect_context.GetInfo(i);
220 if (effect_info->GetMixID() == in_params.mix_id) {
221 effect_processing_order[effect_info->GetProcessingOrder()] = static_cast<s32>(i);
222 }
223 }
224
225 // TODO(ogniK): Update effect processing order
226 return require_sort;
227}
228
229bool ServerMixInfo::HasAnyConnection() const {
230 return in_params.splitter_id != AudioCommon::NO_SPLITTER ||
231 in_params.mix_id != AudioCommon::NO_MIX;
232}
233
234void ServerMixInfo::Cleanup() {
235 in_params.volume = 0.0f;
236 in_params.sample_rate = 0;
237 in_params.buffer_count = 0;
238 in_params.in_use = false;
239 in_params.mix_id = AudioCommon::NO_MIX;
240 in_params.node_id = 0;
241 in_params.buffer_offset = 0;
242 in_params.dest_mix_id = AudioCommon::NO_MIX;
243 in_params.splitter_id = AudioCommon::NO_SPLITTER;
244 std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size());
245}
246
247void ServerMixInfo::SetEffectCount(std::size_t count) {
248 effect_processing_order.resize(count);
249 ResetEffectProcessingOrder();
250}
251
252void ServerMixInfo::ResetEffectProcessingOrder() {
253 for (auto& order : effect_processing_order) {
254 order = AudioCommon::NO_EFFECT_ORDER;
255 }
256}
257
258s32 ServerMixInfo::GetEffectOrder(std::size_t i) const {
259 return effect_processing_order.at(i);
260}
261
262bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
263 SplitterContext& splitter_context) {
264 // Mixes are identical
265 if (in_params.dest_mix_id == mix_in.dest_mix_id &&
266 in_params.splitter_id == mix_in.splitter_id &&
267 ((in_params.splitter_id == AudioCommon::NO_SPLITTER) ||
268 !splitter_context.GetInfo(in_params.splitter_id).HasNewConnection())) {
269 return false;
270 }
271 // Remove current edges for mix id
272 edge_matrix.RemoveEdges(in_params.mix_id);
273 if (mix_in.dest_mix_id != AudioCommon::NO_MIX) {
274 // If we have a valid destination mix id, set our edge matrix
275 edge_matrix.Connect(in_params.mix_id, mix_in.dest_mix_id);
276 } else if (mix_in.splitter_id != AudioCommon::NO_SPLITTER) {
277 // Recurse our splitter linked and set our edges
278 auto& splitter_info = splitter_context.GetInfo(mix_in.splitter_id);
279 const auto length = splitter_info.GetLength();
280 for (s32 i = 0; i < length; i++) {
281 const auto* splitter_destination =
282 splitter_context.GetDestinationData(mix_in.splitter_id, i);
283 if (splitter_destination == nullptr) {
284 continue;
285 }
286 if (splitter_destination->ValidMixId()) {
287 edge_matrix.Connect(in_params.mix_id, splitter_destination->GetMixId());
288 }
289 }
290 }
291 in_params.dest_mix_id = mix_in.dest_mix_id;
292 in_params.splitter_id = mix_in.splitter_id;
293 return true;
294}
295
296} // namespace AudioCore
diff --git a/src/audio_core/mix_context.h b/src/audio_core/mix_context.h
new file mode 100644
index 000000000..6a588eeb4
--- /dev/null
+++ b/src/audio_core/mix_context.h
@@ -0,0 +1,114 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <vector>
9#include "audio_core/common.h"
10#include "audio_core/splitter_context.h"
11#include "common/common_funcs.h"
12#include "common/common_types.h"
13
14namespace AudioCore {
15class BehaviorInfo;
16class EffectContext;
17
18class MixInfo {
19public:
20 struct DirtyHeader {
21 u32_le magic{};
22 u32_le mixer_count{};
23 INSERT_PADDING_BYTES(0x18);
24 };
25 static_assert(sizeof(DirtyHeader) == 0x20, "MixInfo::DirtyHeader is an invalid size");
26
27 struct InParams {
28 float_le volume{};
29 s32_le sample_rate{};
30 s32_le buffer_count{};
31 bool in_use{};
32 INSERT_PADDING_BYTES(3);
33 s32_le mix_id{};
34 s32_le effect_count{};
35 u32_le node_id{};
36 INSERT_PADDING_WORDS(2);
37 std::array<std::array<float_le, AudioCommon::MAX_MIX_BUFFERS>, AudioCommon::MAX_MIX_BUFFERS>
38 mix_volume{};
39 s32_le dest_mix_id{};
40 s32_le splitter_id{};
41 INSERT_PADDING_WORDS(1);
42 };
43 static_assert(sizeof(MixInfo::InParams) == 0x930, "MixInfo::InParams is an invalid size");
44};
45
46class ServerMixInfo {
47public:
48 struct InParams {
49 float volume{};
50 s32 sample_rate{};
51 s32 buffer_count{};
52 bool in_use{};
53 s32 mix_id{};
54 u32 node_id{};
55 std::array<std::array<float_le, AudioCommon::MAX_MIX_BUFFERS>, AudioCommon::MAX_MIX_BUFFERS>
56 mix_volume{};
57 s32 dest_mix_id{};
58 s32 splitter_id{};
59 s32 buffer_offset{};
60 s32 final_mix_distance{};
61 };
62 ServerMixInfo();
63 ~ServerMixInfo();
64
65 const ServerMixInfo::InParams& GetInParams() const;
66 ServerMixInfo::InParams& GetInParams();
67
68 bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
69 BehaviorInfo& behavior_info, SplitterContext& splitter_context,
70 EffectContext& effect_context);
71 bool HasAnyConnection() const;
72 void Cleanup();
73 void SetEffectCount(std::size_t count);
74 void ResetEffectProcessingOrder();
75 s32 GetEffectOrder(std::size_t i) const;
76
77private:
78 std::vector<s32> effect_processing_order;
79 InParams in_params{};
80 bool UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
81 SplitterContext& splitter_context);
82};
83
84class MixContext {
85public:
86 MixContext();
87 ~MixContext();
88
89 void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
90 std::size_t effect_count);
91 void SortInfo();
92 bool TsortInfo(SplitterContext& splitter_context);
93
94 std::size_t GetCount() const;
95 ServerMixInfo& GetInfo(std::size_t i);
96 const ServerMixInfo& GetInfo(std::size_t i) const;
97 ServerMixInfo& GetSortedInfo(std::size_t i);
98 const ServerMixInfo& GetSortedInfo(std::size_t i) const;
99 ServerMixInfo& GetFinalMixInfo();
100 const ServerMixInfo& GetFinalMixInfo() const;
101 EdgeMatrix& GetEdgeMatrix();
102 const EdgeMatrix& GetEdgeMatrix() const;
103
104private:
105 void CalcMixBufferOffset();
106 void UpdateDistancesFromFinalMix();
107
108 NodeStates node_states{};
109 EdgeMatrix edge_matrix{};
110 std::size_t info_count{};
111 std::vector<ServerMixInfo> infos{};
112 std::vector<ServerMixInfo*> sorted_info{};
113};
114} // namespace AudioCore
diff --git a/src/audio_core/sink_context.cpp b/src/audio_core/sink_context.cpp
new file mode 100644
index 000000000..0882b411a
--- /dev/null
+++ b/src/audio_core/sink_context.cpp
@@ -0,0 +1,31 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/sink_context.h"
6
7namespace AudioCore {
8SinkContext::SinkContext(std::size_t sink_count) : sink_count(sink_count) {}
9SinkContext::~SinkContext() = default;
10
11std::size_t SinkContext::GetCount() const {
12 return sink_count;
13}
14
15void SinkContext::UpdateMainSink(SinkInfo::InParams& in) {
16 in_use = in.in_use;
17 use_count = in.device.input_count;
18 std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT);
19}
20
21bool SinkContext::InUse() const {
22 return in_use;
23}
24
25std::vector<u8> SinkContext::OutputBuffers() const {
26 std::vector<u8> buffer_ret(use_count);
27 std::memcpy(buffer_ret.data(), buffers.data(), use_count);
28 return buffer_ret;
29}
30
31} // namespace AudioCore
diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h
new file mode 100644
index 000000000..d7aa72ba7
--- /dev/null
+++ b/src/audio_core/sink_context.h
@@ -0,0 +1,89 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "audio_core/common.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h"
10#include "common/swap.h"
11
12namespace AudioCore {
13
14enum class SinkTypes : u8 {
15 Invalid = 0,
16 Device = 1,
17 Circular = 2,
18};
19
20enum class SinkSampleFormat : u32_le {
21 None = 0,
22 Pcm8 = 1,
23 Pcm16 = 2,
24 Pcm24 = 3,
25 Pcm32 = 4,
26 PcmFloat = 5,
27 Adpcm = 6,
28};
29
30class SinkInfo {
31public:
32 struct CircularBufferIn {
33 u64_le address;
34 u32_le size;
35 u32_le input_count;
36 u32_le sample_count;
37 u32_le previous_position;
38 SinkSampleFormat sample_format;
39 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
40 bool in_use;
41 INSERT_UNION_PADDING_BYTES(5);
42 };
43 static_assert(sizeof(SinkInfo::CircularBufferIn) == 0x28,
44 "SinkInfo::CircularBufferIn is in invalid size");
45
46 struct DeviceIn {
47 std::array<u8, 255> device_name;
48 INSERT_UNION_PADDING_BYTES(1);
49 s32_le input_count;
50 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
51 INSERT_UNION_PADDING_BYTES(1);
52 bool down_matrix_enabled;
53 std::array<float_le, 4> down_matrix_coef;
54 };
55 static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
56
57 struct InParams {
58 SinkTypes type{};
59 bool in_use{};
60 INSERT_PADDING_BYTES(2);
61 u32_le node_id{};
62 INSERT_PADDING_WORDS(6);
63 union {
64 // std::array<u8, 0x120> raw{};
65 SinkInfo::DeviceIn device;
66 SinkInfo::CircularBufferIn circular_buffer;
67 };
68 };
69 static_assert(sizeof(SinkInfo::InParams) == 0x140, "SinkInfo::InParams are an invalid size!");
70};
71
72class SinkContext {
73public:
74 explicit SinkContext(std::size_t sink_count);
75 ~SinkContext();
76
77 std::size_t GetCount() const;
78
79 void UpdateMainSink(SinkInfo::InParams& in);
80 bool InUse() const;
81 std::vector<u8> OutputBuffers() const;
82
83private:
84 bool in_use{false};
85 s32 use_count{};
86 std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
87 std::size_t sink_count{};
88};
89} // namespace AudioCore
diff --git a/src/audio_core/splitter_context.cpp b/src/audio_core/splitter_context.cpp
new file mode 100644
index 000000000..79bb2f516
--- /dev/null
+++ b/src/audio_core/splitter_context.cpp
@@ -0,0 +1,617 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/behavior_info.h"
6#include "audio_core/splitter_context.h"
7#include "common/alignment.h"
8#include "common/assert.h"
9#include "common/logging/log.h"
10
11namespace AudioCore {
12
13ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id) : id(id) {}
14ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
15
16void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
17 // Log error as these are not actually failure states
18 if (header.magic != SplitterMagic::DataHeader) {
19 LOG_ERROR(Audio, "Splitter destination header is invalid!");
20 return;
21 }
22
23 // Incorrect splitter id
24 if (header.splitter_id != id) {
25 LOG_ERROR(Audio, "Splitter destination ids do not match!");
26 return;
27 }
28
29 mix_id = header.mix_id;
30 // Copy our mix volumes
31 std::copy(header.mix_volumes.begin(), header.mix_volumes.end(), current_mix_volumes.begin());
32 if (!in_use && header.in_use) {
33 // Update mix volumes
34 std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
35 needs_update = false;
36 }
37 in_use = header.in_use;
38}
39
40ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() {
41 return next;
42}
43
44const ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() const {
45 return next;
46}
47
48void ServerSplitterDestinationData::SetNextDestination(ServerSplitterDestinationData* dest) {
49 next = dest;
50}
51
52bool ServerSplitterDestinationData::ValidMixId() const {
53 return GetMixId() != AudioCommon::NO_MIX;
54}
55
56s32 ServerSplitterDestinationData::GetMixId() const {
57 return mix_id;
58}
59
60bool ServerSplitterDestinationData::IsConfigured() const {
61 return in_use && ValidMixId();
62}
63
64float ServerSplitterDestinationData::GetMixVolume(std::size_t i) const {
65 ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
66 return current_mix_volumes.at(i);
67}
68
69const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
70ServerSplitterDestinationData::CurrentMixVolumes() const {
71 return current_mix_volumes;
72}
73
74const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
75ServerSplitterDestinationData::LastMixVolumes() const {
76 return last_mix_volumes;
77}
78
79void ServerSplitterDestinationData::MarkDirty() {
80 needs_update = true;
81}
82
83void ServerSplitterDestinationData::UpdateInternalState() {
84 if (in_use && needs_update) {
85 std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
86 }
87 needs_update = false;
88}
89
90ServerSplitterInfo::ServerSplitterInfo(s32 id) : id(id) {}
91ServerSplitterInfo::~ServerSplitterInfo() = default;
92
93void ServerSplitterInfo::InitializeInfos() {
94 send_length = 0;
95 head = nullptr;
96 new_connection = true;
97}
98
99void ServerSplitterInfo::ClearNewConnectionFlag() {
100 new_connection = false;
101}
102
103std::size_t ServerSplitterInfo::Update(SplitterInfo::InInfoPrams& header) {
104 if (header.send_id != id) {
105 return 0;
106 }
107
108 sample_rate = header.sample_rate;
109 new_connection = true;
110 // We need to update the size here due to the splitter bug being present and providing an
111 // incorrect size. We're suppose to also update the header here but we just ignore and continue
112 return (sizeof(s32_le) * (header.length - 1)) + (sizeof(s32_le) * 3);
113}
114
115ServerSplitterDestinationData* ServerSplitterInfo::GetHead() {
116 return head;
117}
118
119const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const {
120 return head;
121}
122
123ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
124 auto current_head = head;
125 for (std::size_t i = 0; i < depth; i++) {
126 if (current_head == nullptr) {
127 return nullptr;
128 }
129 current_head = current_head->GetNextDestination();
130 }
131 return current_head;
132}
133
134const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
135 auto current_head = head;
136 for (std::size_t i = 0; i < depth; i++) {
137 if (current_head == nullptr) {
138 return nullptr;
139 }
140 current_head = current_head->GetNextDestination();
141 }
142 return current_head;
143}
144
145bool ServerSplitterInfo::HasNewConnection() const {
146 return new_connection;
147}
148
149s32 ServerSplitterInfo::GetLength() const {
150 return send_length;
151}
152
153void ServerSplitterInfo::SetHead(ServerSplitterDestinationData* new_head) {
154 head = new_head;
155}
156
157void ServerSplitterInfo::SetHeadDepth(s32 length) {
158 send_length = length;
159}
160
161SplitterContext::SplitterContext() = default;
162SplitterContext::~SplitterContext() = default;
163
164void SplitterContext::Initialize(BehaviorInfo& behavior_info, std::size_t _info_count,
165 std::size_t _data_count) {
166 if (!behavior_info.IsSplitterSupported() || _data_count == 0 || _info_count == 0) {
167 Setup(0, 0, false);
168 return;
169 }
170 // Only initialize if we're using splitters
171 Setup(_info_count, _data_count, behavior_info.IsSplitterBugFixed());
172}
173
174bool SplitterContext::Update(const std::vector<u8>& input, std::size_t& input_offset,
175 std::size_t& bytes_read) {
176 const auto UpdateOffsets = [&](std::size_t read) {
177 input_offset += read;
178 bytes_read += read;
179 };
180
181 if (info_count == 0 || data_count == 0) {
182 bytes_read = 0;
183 return true;
184 }
185
186 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
187 sizeof(SplitterInfo::InHeader))) {
188 LOG_ERROR(Audio, "Buffer is an invalid size!");
189 return false;
190 }
191 SplitterInfo::InHeader header{};
192 std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InHeader));
193 UpdateOffsets(sizeof(SplitterInfo::InHeader));
194
195 if (header.magic != SplitterMagic::SplitterHeader) {
196 LOG_ERROR(Audio, "Invalid header magic! Expecting {:X} but got {:X}",
197 SplitterMagic::SplitterHeader, header.magic);
198 return false;
199 }
200
201 // Clear all connections
202 for (auto& info : infos) {
203 info.ClearNewConnectionFlag();
204 }
205
206 UpdateInfo(input, input_offset, bytes_read, header.info_count);
207 UpdateData(input, input_offset, bytes_read, header.data_count);
208 const auto aligned_bytes_read = Common::AlignUp(bytes_read, 16);
209 input_offset += aligned_bytes_read - bytes_read;
210 bytes_read = aligned_bytes_read;
211 return true;
212}
213
214bool SplitterContext::UsingSplitter() const {
215 return info_count > 0 && data_count > 0;
216}
217
218ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) {
219 ASSERT(i < info_count);
220 return infos.at(i);
221}
222
223const ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) const {
224 ASSERT(i < info_count);
225 return infos.at(i);
226}
227
228ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) {
229 ASSERT(i < data_count);
230 return datas.at(i);
231}
232
233const ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) const {
234 ASSERT(i < data_count);
235 return datas.at(i);
236}
237
238ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
239 std::size_t data) {
240 ASSERT(info < info_count);
241 auto& cur_info = GetInfo(info);
242 return cur_info.GetData(data);
243}
244
245const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
246 std::size_t data) const {
247 ASSERT(info < info_count);
248 auto& cur_info = GetInfo(info);
249 return cur_info.GetData(data);
250}
251
252void SplitterContext::UpdateInternalState() {
253 if (data_count == 0) {
254 return;
255 }
256
257 for (auto& data : datas) {
258 data.UpdateInternalState();
259 }
260}
261
262std::size_t SplitterContext::GetInfoCount() const {
263 return info_count;
264}
265
266std::size_t SplitterContext::GetDataCount() const {
267 return data_count;
268}
269
270void SplitterContext::Setup(std::size_t _info_count, std::size_t _data_count,
271 bool is_splitter_bug_fixed) {
272
273 info_count = _info_count;
274 data_count = _data_count;
275
276 for (std::size_t i = 0; i < info_count; i++) {
277 auto& splitter = infos.emplace_back(static_cast<s32>(i));
278 splitter.InitializeInfos();
279 }
280 for (std::size_t i = 0; i < data_count; i++) {
281 datas.emplace_back(static_cast<s32>(i));
282 }
283
284 bug_fixed = is_splitter_bug_fixed;
285}
286
287bool SplitterContext::UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
288 std::size_t& bytes_read, s32 in_splitter_count) {
289 const auto UpdateOffsets = [&](std::size_t read) {
290 input_offset += read;
291 bytes_read += read;
292 };
293
294 for (s32 i = 0; i < in_splitter_count; i++) {
295 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
296 sizeof(SplitterInfo::InInfoPrams))) {
297 LOG_ERROR(Audio, "Buffer is an invalid size!");
298 return false;
299 }
300 SplitterInfo::InInfoPrams header{};
301 std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InInfoPrams));
302
303 // Logged as warning as these don't actually cause a bailout for some reason
304 if (header.magic != SplitterMagic::InfoHeader) {
305 LOG_ERROR(Audio, "Bad splitter data header");
306 break;
307 }
308
309 if (header.send_id < 0 || header.send_id > info_count) {
310 LOG_ERROR(Audio, "Bad splitter data id");
311 break;
312 }
313
314 UpdateOffsets(sizeof(SplitterInfo::InInfoPrams));
315 auto& info = GetInfo(header.send_id);
316 if (!RecomposeDestination(info, header, input, input_offset)) {
317 LOG_ERROR(Audio, "Failed to recompose destination for splitter!");
318 return false;
319 }
320 const std::size_t read = info.Update(header);
321 bytes_read += read;
322 input_offset += read;
323 }
324 return true;
325}
326
327bool SplitterContext::UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
328 std::size_t& bytes_read, s32 in_data_count) {
329 const auto UpdateOffsets = [&](std::size_t read) {
330 input_offset += read;
331 bytes_read += read;
332 };
333
334 for (s32 i = 0; i < in_data_count; i++) {
335 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
336 sizeof(SplitterInfo::InDestinationParams))) {
337 LOG_ERROR(Audio, "Buffer is an invalid size!");
338 return false;
339 }
340 SplitterInfo::InDestinationParams header{};
341 std::memcpy(&header, input.data() + input_offset,
342 sizeof(SplitterInfo::InDestinationParams));
343 UpdateOffsets(sizeof(SplitterInfo::InDestinationParams));
344
345 // Logged as warning as these don't actually cause a bailout for some reason
346 if (header.magic != SplitterMagic::DataHeader) {
347 LOG_ERROR(Audio, "Bad splitter data header");
348 break;
349 }
350
351 if (header.splitter_id < 0 || header.splitter_id > data_count) {
352 LOG_ERROR(Audio, "Bad splitter data id");
353 break;
354 }
355 GetData(header.splitter_id).Update(header);
356 }
357 return true;
358}
359
360bool SplitterContext::RecomposeDestination(ServerSplitterInfo& info,
361 SplitterInfo::InInfoPrams& header,
362 const std::vector<u8>& input,
363 const std::size_t& input_offset) {
364 // Clear our current destinations
365 auto* current_head = info.GetHead();
366 while (current_head != nullptr) {
367 auto next_head = current_head->GetNextDestination();
368 current_head->SetNextDestination(nullptr);
369 current_head = next_head;
370 }
371 info.SetHead(nullptr);
372
373 s32 size = header.length;
374 // If the splitter bug is present, calculate fixed size
375 if (!bug_fixed) {
376 if (info_count > 0) {
377 const auto factor = data_count / info_count;
378 size = std::min(header.length, static_cast<s32>(factor));
379 } else {
380 size = 0;
381 }
382 }
383
384 if (size < 1) {
385 LOG_ERROR(Audio, "Invalid splitter info size! size={:X}", size);
386 return true;
387 }
388
389 auto* start_head = &GetData(header.resource_id_base);
390 current_head = start_head;
391 std::vector<s32_le> resource_ids(size - 1);
392 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
393 resource_ids.size() * sizeof(s32_le))) {
394 LOG_ERROR(Audio, "Buffer is an invalid size!");
395 return false;
396 }
397 std::memcpy(resource_ids.data(), input.data() + input_offset,
398 resource_ids.size() * sizeof(s32_le));
399
400 for (auto resource_id : resource_ids) {
401 auto* head = &GetData(resource_id);
402 current_head->SetNextDestination(head);
403 current_head = head;
404 }
405
406 info.SetHead(start_head);
407 info.SetHeadDepth(size);
408
409 return true;
410}
411
412NodeStates::NodeStates() = default;
413NodeStates::~NodeStates() = default;
414
415void NodeStates::Initialize(std::size_t node_count_) {
416 // Setup our work parameters
417 node_count = node_count_;
418 was_node_found.resize(node_count);
419 was_node_completed.resize(node_count);
420 index_list.resize(node_count);
421 index_stack.Reset(node_count * node_count);
422}
423
424bool NodeStates::Tsort(EdgeMatrix& edge_matrix) {
425 return DepthFirstSearch(edge_matrix);
426}
427
428std::size_t NodeStates::GetIndexPos() const {
429 return index_pos;
430}
431
432const std::vector<s32>& NodeStates::GetIndexList() const {
433 return index_list;
434}
435
436void NodeStates::PushTsortResult(s32 index) {
437 ASSERT(index < node_count);
438 index_list[index_pos++] = index;
439}
440
441bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
442 ResetState();
443 for (std::size_t i = 0; i < node_count; i++) {
444 const auto node_id = static_cast<s32>(i);
445
446 // If we don't have a state, send to our index stack for work
447 if (GetState(i) == NodeStates::State::NoState) {
448 index_stack.push(node_id);
449 }
450
451 // While we have work to do in our stack
452 while (index_stack.Count() > 0) {
453 // Get the current node
454 const auto current_stack_index = index_stack.top();
455 // Check if we've seen the node yet
456 const auto index_state = GetState(current_stack_index);
457 if (index_state == NodeStates::State::NoState) {
458 // Mark the node as seen
459 UpdateState(NodeStates::State::InFound, current_stack_index);
460 } else if (index_state == NodeStates::State::InFound) {
461 // We've seen this node before, mark it as completed
462 UpdateState(NodeStates::State::InCompleted, current_stack_index);
463 // Update our index list
464 PushTsortResult(current_stack_index);
465 // Pop the stack
466 index_stack.pop();
467 continue;
468 } else if (index_state == NodeStates::State::InCompleted) {
469 // If our node is already sorted, clear it
470 index_stack.pop();
471 continue;
472 }
473
474 const auto node_count = edge_matrix.GetNodeCount();
475 for (s32 j = 0; j < static_cast<s32>(node_count); j++) {
476 // Check if our node is connected to our edge matrix
477 if (!edge_matrix.Connected(current_stack_index, j)) {
478 continue;
479 }
480
481 // Check if our node exists
482 const auto node_state = GetState(j);
483 if (node_state == NodeStates::State::NoState) {
484 // Add more work
485 index_stack.push(j);
486 } else if (node_state == NodeStates::State::InFound) {
487 UNREACHABLE_MSG("Node start marked as found");
488 ResetState();
489 return false;
490 }
491 }
492 }
493 }
494 return true;
495}
496
497void NodeStates::ResetState() {
498 // Reset to the start of our index stack
499 index_pos = 0;
500 for (std::size_t i = 0; i < node_count; i++) {
501 // Mark all nodes as not found
502 was_node_found[i] = false;
503 // Mark all nodes as uncompleted
504 was_node_completed[i] = false;
505 // Mark all indexes as invalid
506 index_list[i] = -1;
507 }
508}
509
510void NodeStates::UpdateState(NodeStates::State state, std::size_t i) {
511 switch (state) {
512 case NodeStates::State::NoState:
513 was_node_found[i] = false;
514 was_node_completed[i] = false;
515 break;
516 case NodeStates::State::InFound:
517 was_node_found[i] = true;
518 was_node_completed[i] = false;
519 break;
520 case NodeStates::State::InCompleted:
521 was_node_found[i] = false;
522 was_node_completed[i] = true;
523 break;
524 }
525}
526
527NodeStates::State NodeStates::GetState(std::size_t i) {
528 ASSERT(i < node_count);
529 if (was_node_found[i]) {
530 // If our node exists in our found list
531 return NodeStates::State::InFound;
532 } else if (was_node_completed[i]) {
533 // If node is in the completed list
534 return NodeStates::State::InCompleted;
535 } else {
536 // If in neither
537 return NodeStates::State::NoState;
538 }
539}
540
541NodeStates::Stack::Stack() = default;
542NodeStates::Stack::~Stack() = default;
543
544void NodeStates::Stack::Reset(std::size_t size) {
545 // Mark our stack as empty
546 stack.resize(size);
547 stack_size = size;
548 stack_pos = 0;
549 std::fill(stack.begin(), stack.end(), 0);
550}
551
552void NodeStates::Stack::push(s32 val) {
553 ASSERT(stack_pos < stack_size);
554 stack[stack_pos++] = val;
555}
556
557std::size_t NodeStates::Stack::Count() const {
558 return stack_pos;
559}
560
561s32 NodeStates::Stack::top() const {
562 ASSERT(stack_pos > 0);
563 return stack[stack_pos - 1];
564}
565
566s32 NodeStates::Stack::pop() {
567 ASSERT(stack_pos > 0);
568 stack_pos--;
569 return stack[stack_pos];
570}
571
572EdgeMatrix::EdgeMatrix() = default;
573EdgeMatrix::~EdgeMatrix() = default;
574
575void EdgeMatrix::Initialize(std::size_t _node_count) {
576 node_count = _node_count;
577 edge_matrix.resize(node_count * node_count);
578}
579
580bool EdgeMatrix::Connected(s32 a, s32 b) {
581 return GetState(a, b);
582}
583
584void EdgeMatrix::Connect(s32 a, s32 b) {
585 SetState(a, b, true);
586}
587
588void EdgeMatrix::Disconnect(s32 a, s32 b) {
589 SetState(a, b, false);
590}
591
592void EdgeMatrix::RemoveEdges(s32 edge) {
593 for (std::size_t i = 0; i < node_count; i++) {
594 SetState(edge, static_cast<s32>(i), false);
595 }
596}
597
598std::size_t EdgeMatrix::GetNodeCount() const {
599 return node_count;
600}
601
602void EdgeMatrix::SetState(s32 a, s32 b, bool state) {
603 ASSERT(InRange(a, b));
604 edge_matrix.at(a * node_count + b) = state;
605}
606
607bool EdgeMatrix::GetState(s32 a, s32 b) {
608 ASSERT(InRange(a, b));
609 return edge_matrix.at(a * node_count + b);
610}
611
612bool EdgeMatrix::InRange(s32 a, s32 b) const {
613 const std::size_t pos = a * node_count + b;
614 return pos < (node_count * node_count);
615}
616
617} // namespace AudioCore
diff --git a/src/audio_core/splitter_context.h b/src/audio_core/splitter_context.h
new file mode 100644
index 000000000..ea6239fdb
--- /dev/null
+++ b/src/audio_core/splitter_context.h
@@ -0,0 +1,221 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <stack>
8#include <vector>
9#include "audio_core/common.h"
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "common/swap.h"
13
14namespace AudioCore {
15class BehaviorInfo;
16
17class EdgeMatrix {
18public:
19 EdgeMatrix();
20 ~EdgeMatrix();
21
22 void Initialize(std::size_t _node_count);
23 bool Connected(s32 a, s32 b);
24 void Connect(s32 a, s32 b);
25 void Disconnect(s32 a, s32 b);
26 void RemoveEdges(s32 edge);
27 std::size_t GetNodeCount() const;
28
29private:
30 void SetState(s32 a, s32 b, bool state);
31 bool GetState(s32 a, s32 b);
32
33 bool InRange(s32 a, s32 b) const;
34 std::vector<bool> edge_matrix{};
35 std::size_t node_count{};
36};
37
38class NodeStates {
39public:
40 enum class State {
41 NoState = 0,
42 InFound = 1,
43 InCompleted = 2,
44 };
45
46 // Looks to be a fixed size stack. Placed within the NodeStates class based on symbols
47 class Stack {
48 public:
49 Stack();
50 ~Stack();
51
52 void Reset(std::size_t size);
53 void push(s32 val);
54 std::size_t Count() const;
55 s32 top() const;
56 s32 pop();
57
58 private:
59 std::vector<s32> stack{};
60 std::size_t stack_size{};
61 std::size_t stack_pos{};
62 };
63 NodeStates();
64 ~NodeStates();
65
66 void Initialize(std::size_t _node_count);
67 bool Tsort(EdgeMatrix& edge_matrix);
68 std::size_t GetIndexPos() const;
69 const std::vector<s32>& GetIndexList() const;
70
71private:
72 void PushTsortResult(s32 index);
73 bool DepthFirstSearch(EdgeMatrix& edge_matrix);
74 void ResetState();
75 void UpdateState(NodeStates::State state, std::size_t i);
76 NodeStates::State GetState(std::size_t i);
77
78 std::size_t node_count{};
79 std::vector<bool> was_node_found{};
80 std::vector<bool> was_node_completed{};
81 std::size_t index_pos{};
82 std::vector<s32> index_list{};
83 NodeStates::Stack index_stack{};
84};
85
86enum class SplitterMagic : u32_le {
87 SplitterHeader = Common::MakeMagic('S', 'N', 'D', 'H'),
88 DataHeader = Common::MakeMagic('S', 'N', 'D', 'D'),
89 InfoHeader = Common::MakeMagic('S', 'N', 'D', 'I'),
90};
91
92class SplitterInfo {
93public:
94 struct InHeader {
95 SplitterMagic magic{};
96 s32_le info_count{};
97 s32_le data_count{};
98 INSERT_PADDING_WORDS(5);
99 };
100 static_assert(sizeof(SplitterInfo::InHeader) == 0x20,
101 "SplitterInfo::InHeader is an invalid size");
102
103 struct InInfoPrams {
104 SplitterMagic magic{};
105 s32_le send_id{};
106 s32_le sample_rate{};
107 s32_le length{};
108 s32_le resource_id_base{};
109 };
110 static_assert(sizeof(SplitterInfo::InInfoPrams) == 0x14,
111 "SplitterInfo::InInfoPrams is an invalid size");
112
113 struct InDestinationParams {
114 SplitterMagic magic{};
115 s32_le splitter_id{};
116 std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> mix_volumes{};
117 s32_le mix_id{};
118 bool in_use{};
119 INSERT_PADDING_BYTES(3);
120 };
121 static_assert(sizeof(SplitterInfo::InDestinationParams) == 0x70,
122 "SplitterInfo::InDestinationParams is an invalid size");
123};
124
125class ServerSplitterDestinationData {
126public:
127 explicit ServerSplitterDestinationData(s32 id);
128 ~ServerSplitterDestinationData();
129
130 void Update(SplitterInfo::InDestinationParams& header);
131
132 ServerSplitterDestinationData* GetNextDestination();
133 const ServerSplitterDestinationData* GetNextDestination() const;
134 void SetNextDestination(ServerSplitterDestinationData* dest);
135 bool ValidMixId() const;
136 s32 GetMixId() const;
137 bool IsConfigured() const;
138 float GetMixVolume(std::size_t i) const;
139 const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& CurrentMixVolumes() const;
140 const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& LastMixVolumes() const;
141 void MarkDirty();
142 void UpdateInternalState();
143
144private:
145 bool needs_update{};
146 bool in_use{};
147 s32 id{};
148 s32 mix_id{};
149 std::array<float, AudioCommon::MAX_MIX_BUFFERS> current_mix_volumes{};
150 std::array<float, AudioCommon::MAX_MIX_BUFFERS> last_mix_volumes{};
151 ServerSplitterDestinationData* next = nullptr;
152};
153
154class ServerSplitterInfo {
155public:
156 explicit ServerSplitterInfo(s32 id);
157 ~ServerSplitterInfo();
158
159 void InitializeInfos();
160 void ClearNewConnectionFlag();
161 std::size_t Update(SplitterInfo::InInfoPrams& header);
162
163 ServerSplitterDestinationData* GetHead();
164 const ServerSplitterDestinationData* GetHead() const;
165 ServerSplitterDestinationData* GetData(std::size_t depth);
166 const ServerSplitterDestinationData* GetData(std::size_t depth) const;
167
168 bool HasNewConnection() const;
169 s32 GetLength() const;
170
171 void SetHead(ServerSplitterDestinationData* new_head);
172 void SetHeadDepth(s32 length);
173
174private:
175 s32 sample_rate{};
176 s32 id{};
177 s32 send_length{};
178 ServerSplitterDestinationData* head = nullptr;
179 bool new_connection{};
180};
181
182class SplitterContext {
183public:
184 SplitterContext();
185 ~SplitterContext();
186
187 void Initialize(BehaviorInfo& behavior_info, std::size_t splitter_count,
188 std::size_t data_count);
189
190 bool Update(const std::vector<u8>& input, std::size_t& input_offset, std::size_t& bytes_read);
191 bool UsingSplitter() const;
192
193 ServerSplitterInfo& GetInfo(std::size_t i);
194 const ServerSplitterInfo& GetInfo(std::size_t i) const;
195 ServerSplitterDestinationData& GetData(std::size_t i);
196 const ServerSplitterDestinationData& GetData(std::size_t i) const;
197 ServerSplitterDestinationData* GetDestinationData(std::size_t info, std::size_t data);
198 const ServerSplitterDestinationData* GetDestinationData(std::size_t info,
199 std::size_t data) const;
200 void UpdateInternalState();
201
202 std::size_t GetInfoCount() const;
203 std::size_t GetDataCount() const;
204
205private:
206 void Setup(std::size_t info_count, std::size_t data_count, bool is_splitter_bug_fixed);
207 bool UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
208 std::size_t& bytes_read, s32 in_splitter_count);
209 bool UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
210 std::size_t& bytes_read, s32 in_data_count);
211 bool RecomposeDestination(ServerSplitterInfo& info, SplitterInfo::InInfoPrams& header,
212 const std::vector<u8>& input, const std::size_t& input_offset);
213
214 std::vector<ServerSplitterInfo> infos{};
215 std::vector<ServerSplitterDestinationData> datas{};
216
217 std::size_t info_count{};
218 std::size_t data_count{};
219 bool bug_fixed{};
220};
221} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 7be5d5087..cb33926bc 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -104,11 +104,7 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
104 104
105 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 105 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
106 106
107 const auto time_stretch_delta = Settings::values.enable_audio_stretching.GetValue() 107 core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer) - ns_late, release_event, {});
108 ? std::chrono::nanoseconds::zero()
109 : ns_late;
110 const auto future_time = GetBufferReleaseNS(*active_buffer) - time_stretch_delta;
111 core_timing.ScheduleEvent(future_time, release_event, {});
112} 108}
113 109
114void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) { 110void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) {
diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp
new file mode 100644
index 000000000..1d8f69844
--- /dev/null
+++ b/src/audio_core/voice_context.cpp
@@ -0,0 +1,526 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "audio_core/behavior_info.h"
6#include "audio_core/voice_context.h"
7#include "core/memory.h"
8
9namespace AudioCore {
10
11ServerVoiceChannelResource::ServerVoiceChannelResource(s32 id) : id(id) {}
12ServerVoiceChannelResource::~ServerVoiceChannelResource() = default;
13
14bool ServerVoiceChannelResource::InUse() const {
15 return in_use;
16}
17
18float ServerVoiceChannelResource::GetCurrentMixVolumeAt(std::size_t i) const {
19 ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
20 return mix_volume.at(i);
21}
22
23float ServerVoiceChannelResource::GetLastMixVolumeAt(std::size_t i) const {
24 ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
25 return last_mix_volume.at(i);
26}
27
28void ServerVoiceChannelResource::Update(VoiceChannelResource::InParams& in_params) {
29 in_use = in_params.in_use;
30 // Update our mix volumes only if it's in use
31 if (in_params.in_use) {
32 mix_volume = in_params.mix_volume;
33 }
34}
35
36void ServerVoiceChannelResource::UpdateLastMixVolumes() {
37 last_mix_volume = mix_volume;
38}
39
40const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
41ServerVoiceChannelResource::GetCurrentMixVolume() const {
42 return mix_volume;
43}
44
45const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
46ServerVoiceChannelResource::GetLastMixVolume() const {
47 return last_mix_volume;
48}
49
50ServerVoiceInfo::ServerVoiceInfo() {
51 Initialize();
52}
53ServerVoiceInfo::~ServerVoiceInfo() = default;
54
55void ServerVoiceInfo::Initialize() {
56 in_params.in_use = false;
57 in_params.node_id = 0;
58 in_params.id = 0;
59 in_params.current_playstate = ServerPlayState::Stop;
60 in_params.priority = 255;
61 in_params.sample_rate = 0;
62 in_params.sample_format = SampleFormat::Invalid;
63 in_params.channel_count = 0;
64 in_params.pitch = 0.0f;
65 in_params.volume = 0.0f;
66 in_params.last_volume = 0.0f;
67 in_params.biquad_filter.fill({});
68 in_params.wave_buffer_count = 0;
69 in_params.wave_bufffer_head = 0;
70 in_params.mix_id = AudioCommon::NO_MIX;
71 in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
72 in_params.additional_params_address = 0;
73 in_params.additional_params_size = 0;
74 in_params.is_new = false;
75 out_params.played_sample_count = 0;
76 out_params.wave_buffer_consumed = 0;
77 in_params.voice_drop_flag = false;
78 in_params.buffer_mapped = false;
79 in_params.wave_buffer_flush_request_count = 0;
80 in_params.was_biquad_filter_enabled.fill(false);
81
82 for (auto& wave_buffer : in_params.wave_buffer) {
83 wave_buffer.start_sample_offset = 0;
84 wave_buffer.end_sample_offset = 0;
85 wave_buffer.is_looping = false;
86 wave_buffer.end_of_stream = false;
87 wave_buffer.buffer_address = 0;
88 wave_buffer.buffer_size = 0;
89 wave_buffer.context_address = 0;
90 wave_buffer.context_size = 0;
91 wave_buffer.sent_to_dsp = true;
92 }
93
94 stored_samples.clear();
95}
96
97void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
98 BehaviorInfo& behavior_info) {
99 in_params.in_use = voice_in.is_in_use;
100 in_params.id = voice_in.id;
101 in_params.node_id = voice_in.node_id;
102 in_params.last_playstate = in_params.current_playstate;
103 switch (voice_in.play_state) {
104 case PlayState::Paused:
105 in_params.current_playstate = ServerPlayState::Paused;
106 break;
107 case PlayState::Stopped:
108 if (in_params.current_playstate != ServerPlayState::Stop) {
109 in_params.current_playstate = ServerPlayState::RequestStop;
110 }
111 break;
112 case PlayState::Started:
113 in_params.current_playstate = ServerPlayState::Play;
114 break;
115 default:
116 UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state);
117 break;
118 }
119
120 in_params.priority = voice_in.priority;
121 in_params.sorting_order = voice_in.sorting_order;
122 in_params.sample_rate = voice_in.sample_rate;
123 in_params.sample_format = voice_in.sample_format;
124 in_params.channel_count = voice_in.channel_count;
125 in_params.pitch = voice_in.pitch;
126 in_params.volume = voice_in.volume;
127 in_params.biquad_filter = voice_in.biquad_filter;
128 in_params.wave_buffer_count = voice_in.wave_buffer_count;
129 in_params.wave_bufffer_head = voice_in.wave_buffer_head;
130 if (behavior_info.IsFlushVoiceWaveBuffersSupported()) {
131 in_params.wave_buffer_flush_request_count += voice_in.wave_buffer_flush_request_count;
132 }
133 in_params.mix_id = voice_in.mix_id;
134 if (behavior_info.IsSplitterSupported()) {
135 in_params.splitter_info_id = voice_in.splitter_info_id;
136 } else {
137 in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
138 }
139
140 std::memcpy(in_params.voice_channel_resource_id.data(),
141 voice_in.voice_channel_resource_ids.data(),
142 sizeof(s32) * in_params.voice_channel_resource_id.size());
143
144 if (behavior_info.IsVoicePlayedSampleCountResetAtLoopPointSupported()) {
145 in_params.behavior_flags.is_played_samples_reset_at_loop_point =
146 voice_in.behavior_flags.is_played_samples_reset_at_loop_point;
147 } else {
148 in_params.behavior_flags.is_played_samples_reset_at_loop_point.Assign(0);
149 }
150 if (behavior_info.IsVoicePitchAndSrcSkippedSupported()) {
151 in_params.behavior_flags.is_pitch_and_src_skipped =
152 voice_in.behavior_flags.is_pitch_and_src_skipped;
153 } else {
154 in_params.behavior_flags.is_pitch_and_src_skipped.Assign(0);
155 }
156
157 if (voice_in.is_voice_drop_flag_clear_requested) {
158 in_params.voice_drop_flag = false;
159 }
160
161 if (in_params.additional_params_address != voice_in.additional_params_address ||
162 in_params.additional_params_size != voice_in.additional_params_size) {
163 in_params.additional_params_address = voice_in.additional_params_address;
164 in_params.additional_params_size = voice_in.additional_params_size;
165 // TODO(ogniK): Reattach buffer, do we actually need to? Maybe just signal to the DSP that
166 // our context is new
167 }
168}
169
170void ServerVoiceInfo::UpdateWaveBuffers(
171 const VoiceInfo::InParams& voice_in,
172 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states,
173 BehaviorInfo& behavior_info) {
174 if (voice_in.is_new) {
175 // Initialize our wave buffers
176 for (auto& wave_buffer : in_params.wave_buffer) {
177 wave_buffer.start_sample_offset = 0;
178 wave_buffer.end_sample_offset = 0;
179 wave_buffer.is_looping = false;
180 wave_buffer.end_of_stream = false;
181 wave_buffer.buffer_address = 0;
182 wave_buffer.buffer_size = 0;
183 wave_buffer.context_address = 0;
184 wave_buffer.context_size = 0;
185 wave_buffer.sent_to_dsp = true;
186 }
187
188 // Mark all our wave buffers as invalid
189 for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count);
190 channel++) {
191 for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) {
192 is_valid = false;
193 }
194 }
195 }
196
197 // Update our wave buffers
198 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
199 // Assume that we have at least 1 channel voice state
200 const auto have_valid_wave_buffer = voice_states[0]->is_wave_buffer_valid[i];
201
202 UpdateWaveBuffer(in_params.wave_buffer[i], voice_in.wave_buffer[i], in_params.sample_format,
203 have_valid_wave_buffer, behavior_info);
204 }
205}
206
207void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
208 const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
209 bool is_buffer_valid, BehaviorInfo& behavior_info) {
210 if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) {
211 out_wavebuffer.buffer_address = 0;
212 out_wavebuffer.buffer_size = 0;
213 }
214
215 if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) {
216 // Validate sample offset sizings
217 if (sample_format == SampleFormat::Pcm16) {
218 const auto buffer_size = in_wave_buffer.buffer_size;
219 if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 ||
220 (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) ||
221 (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) {
222 // TODO(ogniK): Write error info
223 return;
224 }
225 }
226 // TODO(ogniK): ADPCM Size error
227
228 out_wavebuffer.sent_to_dsp = false;
229 out_wavebuffer.start_sample_offset = in_wave_buffer.start_sample_offset;
230 out_wavebuffer.end_sample_offset = in_wave_buffer.end_sample_offset;
231 out_wavebuffer.is_looping = in_wave_buffer.is_looping;
232 out_wavebuffer.end_of_stream = in_wave_buffer.end_of_stream;
233
234 out_wavebuffer.buffer_address = in_wave_buffer.buffer_address;
235 out_wavebuffer.buffer_size = in_wave_buffer.buffer_size;
236 out_wavebuffer.context_address = in_wave_buffer.context_address;
237 out_wavebuffer.context_size = in_wave_buffer.context_size;
238 in_params.buffer_mapped =
239 in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0;
240 // TODO(ogniK): Pool mapper attachment
241 // TODO(ogniK): IsAdpcmLoopContextBugFixed
242 }
243}
244
245void ServerVoiceInfo::WriteOutStatus(
246 VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
247 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) {
248 if (voice_in.is_new) {
249 in_params.is_new = true;
250 voice_out.wave_buffer_consumed = 0;
251 voice_out.played_sample_count = 0;
252 voice_out.voice_dropped = false;
253 } else if (!in_params.is_new) {
254 voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed;
255 voice_out.played_sample_count = voice_states[0]->played_sample_count;
256 voice_out.voice_dropped = in_params.voice_drop_flag;
257 } else {
258 voice_out.wave_buffer_consumed = 0;
259 voice_out.played_sample_count = 0;
260 voice_out.voice_dropped = false;
261 }
262}
263
264const ServerVoiceInfo::InParams& ServerVoiceInfo::GetInParams() const {
265 return in_params;
266}
267
268ServerVoiceInfo::InParams& ServerVoiceInfo::GetInParams() {
269 return in_params;
270}
271
272const ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() const {
273 return out_params;
274}
275
276ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() {
277 return out_params;
278}
279
280bool ServerVoiceInfo::ShouldSkip() const {
281 // TODO(ogniK): Handle unmapped wave buffers or parameters
282 return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag;
283}
284
285bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) {
286 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> dsp_voice_states{};
287 if (in_params.is_new) {
288 ResetResources(voice_context);
289 in_params.last_volume = in_params.volume;
290 in_params.is_new = false;
291 }
292
293 const s32 channel_count = in_params.channel_count;
294 for (s32 i = 0; i < channel_count; i++) {
295 const auto channel_resource = in_params.voice_channel_resource_id[i];
296 dsp_voice_states[i] =
297 &voice_context.GetDspSharedState(static_cast<std::size_t>(channel_resource));
298 }
299 return UpdateParametersForCommandGeneration(dsp_voice_states);
300}
301
302void ServerVoiceInfo::ResetResources(VoiceContext& voice_context) {
303 const s32 channel_count = in_params.channel_count;
304 for (s32 i = 0; i < channel_count; i++) {
305 const auto channel_resource = in_params.voice_channel_resource_id[i];
306 auto& dsp_state =
307 voice_context.GetDspSharedState(static_cast<std::size_t>(channel_resource));
308 dsp_state = {};
309 voice_context.GetChannelResource(static_cast<std::size_t>(channel_resource))
310 .UpdateLastMixVolumes();
311 }
312}
313
314bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
315 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states) {
316 const s32 channel_count = in_params.channel_count;
317 if (in_params.wave_buffer_flush_request_count > 0) {
318 FlushWaveBuffers(in_params.wave_buffer_flush_request_count, dsp_voice_states,
319 channel_count);
320 in_params.wave_buffer_flush_request_count = 0;
321 }
322
323 switch (in_params.current_playstate) {
324 case ServerPlayState::Play: {
325 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
326 if (!in_params.wave_buffer[i].sent_to_dsp) {
327 for (s32 channel = 0; channel < channel_count; channel++) {
328 dsp_voice_states[channel]->is_wave_buffer_valid[i] = true;
329 }
330 in_params.wave_buffer[i].sent_to_dsp = true;
331 }
332 }
333 in_params.should_depop = false;
334 return HasValidWaveBuffer(dsp_voice_states[0]);
335 }
336 case ServerPlayState::Paused:
337 case ServerPlayState::Stop: {
338 in_params.should_depop = in_params.last_playstate == ServerPlayState::Play;
339 return in_params.should_depop;
340 }
341 case ServerPlayState::RequestStop: {
342 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
343 in_params.wave_buffer[i].sent_to_dsp = true;
344 for (s32 channel = 0; channel < channel_count; channel++) {
345 auto* dsp_state = dsp_voice_states[channel];
346
347 if (dsp_state->is_wave_buffer_valid[i]) {
348 dsp_state->wave_buffer_index =
349 (dsp_state->wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
350 dsp_state->wave_buffer_consumed++;
351 }
352
353 dsp_state->is_wave_buffer_valid[i] = false;
354 }
355 }
356
357 for (s32 channel = 0; channel < channel_count; channel++) {
358 auto* dsp_state = dsp_voice_states[channel];
359 dsp_state->offset = 0;
360 dsp_state->played_sample_count = 0;
361 dsp_state->fraction = 0;
362 dsp_state->sample_history.fill(0);
363 dsp_state->context = {};
364 }
365
366 in_params.current_playstate = ServerPlayState::Stop;
367 in_params.should_depop = in_params.last_playstate == ServerPlayState::Play;
368 return in_params.should_depop;
369 }
370 default:
371 UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate);
372 }
373
374 return false;
375}
376
377void ServerVoiceInfo::FlushWaveBuffers(
378 u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
379 s32 channel_count) {
380 auto wave_head = in_params.wave_bufffer_head;
381
382 for (u8 i = 0; i < flush_count; i++) {
383 in_params.wave_buffer[wave_head].sent_to_dsp = true;
384 for (s32 channel = 0; channel < channel_count; channel++) {
385 auto* dsp_state = dsp_voice_states[channel];
386 dsp_state->wave_buffer_consumed++;
387 dsp_state->is_wave_buffer_valid[wave_head] = false;
388 dsp_state->wave_buffer_index =
389 (dsp_state->wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
390 }
391 wave_head = (wave_head + 1) % AudioCommon::MAX_WAVE_BUFFERS;
392 }
393}
394
395bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const {
396 const auto& valid_wb = state->is_wave_buffer_valid;
397 return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
398}
399
400VoiceContext::VoiceContext(std::size_t voice_count) : voice_count(voice_count) {
401 for (std::size_t i = 0; i < voice_count; i++) {
402 voice_channel_resources.emplace_back(static_cast<s32>(i));
403 sorted_voice_info.push_back(&voice_info.emplace_back());
404 voice_states.emplace_back();
405 dsp_voice_states.emplace_back();
406 }
407}
408
409VoiceContext::~VoiceContext() {
410 sorted_voice_info.clear();
411}
412
413std::size_t VoiceContext::GetVoiceCount() const {
414 return voice_count;
415}
416
417ServerVoiceChannelResource& VoiceContext::GetChannelResource(std::size_t i) {
418 ASSERT(i < voice_count);
419 return voice_channel_resources.at(i);
420}
421
422const ServerVoiceChannelResource& VoiceContext::GetChannelResource(std::size_t i) const {
423 ASSERT(i < voice_count);
424 return voice_channel_resources.at(i);
425}
426
427VoiceState& VoiceContext::GetState(std::size_t i) {
428 ASSERT(i < voice_count);
429 return voice_states.at(i);
430}
431
432const VoiceState& VoiceContext::GetState(std::size_t i) const {
433 ASSERT(i < voice_count);
434 return voice_states.at(i);
435}
436
437VoiceState& VoiceContext::GetDspSharedState(std::size_t i) {
438 ASSERT(i < voice_count);
439 return dsp_voice_states.at(i);
440}
441
442const VoiceState& VoiceContext::GetDspSharedState(std::size_t i) const {
443 ASSERT(i < voice_count);
444 return dsp_voice_states.at(i);
445}
446
447ServerVoiceInfo& VoiceContext::GetInfo(std::size_t i) {
448 ASSERT(i < voice_count);
449 return voice_info.at(i);
450}
451
452const ServerVoiceInfo& VoiceContext::GetInfo(std::size_t i) const {
453 ASSERT(i < voice_count);
454 return voice_info.at(i);
455}
456
457ServerVoiceInfo& VoiceContext::GetSortedInfo(std::size_t i) {
458 ASSERT(i < voice_count);
459 return *sorted_voice_info.at(i);
460}
461
462const ServerVoiceInfo& VoiceContext::GetSortedInfo(std::size_t i) const {
463 ASSERT(i < voice_count);
464 return *sorted_voice_info.at(i);
465}
466
467s32 VoiceContext::DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer, s32 channel,
468 s32 channel_count, s32 buffer_offset, s32 sample_count,
469 Core::Memory::Memory& memory) {
470 if (wave_buffer->buffer_address == 0) {
471 return 0;
472 }
473 if (wave_buffer->buffer_size == 0) {
474 return 0;
475 }
476 if (wave_buffer->end_sample_offset < wave_buffer->start_sample_offset) {
477 return 0;
478 }
479
480 const auto samples_remaining =
481 (wave_buffer->end_sample_offset - wave_buffer->start_sample_offset) - buffer_offset;
482 const auto start_offset = (wave_buffer->start_sample_offset + buffer_offset) * channel_count;
483 const auto buffer_pos = wave_buffer->buffer_address + start_offset;
484
485 s16* buffer_data = reinterpret_cast<s16*>(memory.GetPointer(buffer_pos));
486
487 const auto samples_processed = std::min(sample_count, samples_remaining);
488
489 // Fast path
490 if (channel_count == 1) {
491 for (std::size_t i = 0; i < samples_processed; i++) {
492 output_buffer[i] = buffer_data[i];
493 }
494 } else {
495 for (std::size_t i = 0; i < samples_processed; i++) {
496 output_buffer[i] = buffer_data[i * channel_count + channel];
497 }
498 }
499
500 return samples_processed;
501}
502
503void VoiceContext::SortInfo() {
504 for (std::size_t i = 0; i < voice_count; i++) {
505 sorted_voice_info[i] = &voice_info[i];
506 }
507
508 std::sort(sorted_voice_info.begin(), sorted_voice_info.end(),
509 [](const ServerVoiceInfo* lhs, const ServerVoiceInfo* rhs) {
510 const auto& lhs_in = lhs->GetInParams();
511 const auto& rhs_in = rhs->GetInParams();
512 // Sort by priority
513 if (lhs_in.priority != rhs_in.priority) {
514 return lhs_in.priority > rhs_in.priority;
515 } else {
516 // If the priorities match, sort by sorting order
517 return lhs_in.sorting_order > rhs_in.sorting_order;
518 }
519 });
520}
521
522void VoiceContext::UpdateStateByDspShared() {
523 voice_states = dsp_voice_states;
524}
525
526} // namespace AudioCore
diff --git a/src/audio_core/voice_context.h b/src/audio_core/voice_context.h
new file mode 100644
index 000000000..59d3d7dfb
--- /dev/null
+++ b/src/audio_core/voice_context.h
@@ -0,0 +1,296 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "audio_core/algorithm/interpolate.h"
9#include "audio_core/codec.h"
10#include "audio_core/common.h"
11#include "common/bit_field.h"
12#include "common/common_funcs.h"
13#include "common/common_types.h"
14
15namespace Core::Memory {
16class Memory;
17}
18
19namespace AudioCore {
20
21class BehaviorInfo;
22class VoiceContext;
23
24enum class SampleFormat : u8 {
25 Invalid = 0,
26 Pcm8 = 1,
27 Pcm16 = 2,
28 Pcm24 = 3,
29 Pcm32 = 4,
30 PcmFloat = 5,
31 Adpcm = 6,
32};
33
34enum class PlayState : u8 {
35 Started = 0,
36 Stopped = 1,
37 Paused = 2,
38};
39
40enum class ServerPlayState {
41 Play = 0,
42 Stop = 1,
43 RequestStop = 2,
44 Paused = 3,
45};
46
47struct BiquadFilterParameter {
48 bool enabled{};
49 INSERT_PADDING_BYTES(1);
50 std::array<s16, 3> numerator{};
51 std::array<s16, 2> denominator{};
52};
53static_assert(sizeof(BiquadFilterParameter) == 0xc, "BiquadFilterParameter is an invalid size");
54
55struct WaveBuffer {
56 u64_le buffer_address{};
57 u64_le buffer_size{};
58 s32_le start_sample_offset{};
59 s32_le end_sample_offset{};
60 u8 is_looping{};
61 u8 end_of_stream{};
62 u8 sent_to_server{};
63 INSERT_PADDING_BYTES(5);
64 u64 context_address{};
65 u64 context_size{};
66 INSERT_PADDING_BYTES(8);
67};
68static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size");
69
70struct ServerWaveBuffer {
71 VAddr buffer_address{};
72 std::size_t buffer_size{};
73 s32 start_sample_offset{};
74 s32 end_sample_offset{};
75 bool is_looping{};
76 bool end_of_stream{};
77 VAddr context_address{};
78 std::size_t context_size{};
79 bool sent_to_dsp{true};
80};
81
82struct BehaviorFlags {
83 BitField<0, 1, u16> is_played_samples_reset_at_loop_point;
84 BitField<1, 1, u16> is_pitch_and_src_skipped;
85};
86static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
87
88struct ADPCMContext {
89 u16 header{};
90 s16 yn1{};
91 s16 yn2{};
92};
93static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
94
95struct VoiceState {
96 s64 played_sample_count{};
97 s32 offset{};
98 s32 wave_buffer_index{};
99 std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{};
100 s32 wave_buffer_consumed{};
101 std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{};
102 s32 fraction{};
103 VAddr context_address{};
104 Codec::ADPCM_Coeff coeff{};
105 ADPCMContext context{};
106 std::array<s64, 2> biquad_filter_state{};
107 std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{};
108 u32 external_context_size{};
109 bool is_external_context_used{};
110 bool voice_dropped{};
111};
112
113class VoiceChannelResource {
114public:
115 struct InParams {
116 s32_le id{};
117 std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> mix_volume{};
118 bool in_use{};
119 INSERT_PADDING_BYTES(11);
120 };
121 static_assert(sizeof(VoiceChannelResource::InParams) == 0x70, "InParams is an invalid size");
122};
123
124class ServerVoiceChannelResource {
125public:
126 explicit ServerVoiceChannelResource(s32 id);
127 ~ServerVoiceChannelResource();
128
129 bool InUse() const;
130 float GetCurrentMixVolumeAt(std::size_t i) const;
131 float GetLastMixVolumeAt(std::size_t i) const;
132 void Update(VoiceChannelResource::InParams& in_params);
133 void UpdateLastMixVolumes();
134
135 const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& GetCurrentMixVolume() const;
136 const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& GetLastMixVolume() const;
137
138private:
139 s32 id{};
140 std::array<float, AudioCommon::MAX_MIX_BUFFERS> mix_volume{};
141 std::array<float, AudioCommon::MAX_MIX_BUFFERS> last_mix_volume{};
142 bool in_use{};
143};
144
145class VoiceInfo {
146public:
147 struct InParams {
148 s32_le id{};
149 u32_le node_id{};
150 u8 is_new{};
151 u8 is_in_use{};
152 PlayState play_state{};
153 SampleFormat sample_format{};
154 s32_le sample_rate{};
155 s32_le priority{};
156 s32_le sorting_order{};
157 s32_le channel_count{};
158 float_le pitch{};
159 float_le volume{};
160 std::array<BiquadFilterParameter, 2> biquad_filter{};
161 s32_le wave_buffer_count{};
162 s16_le wave_buffer_head{};
163 INSERT_PADDING_BYTES(6);
164 u64_le additional_params_address{};
165 u64_le additional_params_size{};
166 s32_le mix_id{};
167 s32_le splitter_info_id{};
168 std::array<WaveBuffer, 4> wave_buffer{};
169 std::array<u32_le, 6> voice_channel_resource_ids{};
170 // TODO(ogniK): Remaining flags
171 u8 is_voice_drop_flag_clear_requested{};
172 u8 wave_buffer_flush_request_count{};
173 INSERT_PADDING_BYTES(2);
174 BehaviorFlags behavior_flags{};
175 INSERT_PADDING_BYTES(16);
176 };
177 static_assert(sizeof(VoiceInfo::InParams) == 0x170, "InParams is an invalid size");
178
179 struct OutParams {
180 u64_le played_sample_count{};
181 u32_le wave_buffer_consumed{};
182 u8 voice_dropped{};
183 INSERT_PADDING_BYTES(3);
184 };
185 static_assert(sizeof(VoiceInfo::OutParams) == 0x10, "OutParams is an invalid size");
186};
187
188class ServerVoiceInfo {
189public:
190 struct InParams {
191 bool in_use{};
192 bool is_new{};
193 bool should_depop{};
194 SampleFormat sample_format{};
195 s32 sample_rate{};
196 s32 channel_count{};
197 s32 id{};
198 s32 node_id{};
199 s32 mix_id{};
200 ServerPlayState current_playstate{};
201 ServerPlayState last_playstate{};
202 s32 priority{};
203 s32 sorting_order{};
204 float pitch{};
205 float volume{};
206 float last_volume{};
207 std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{};
208 s32 wave_buffer_count{};
209 s16 wave_bufffer_head{};
210 INSERT_PADDING_BYTES(2);
211 BehaviorFlags behavior_flags{};
212 VAddr additional_params_address{};
213 std::size_t additional_params_size{};
214 std::array<ServerWaveBuffer, AudioCommon::MAX_WAVE_BUFFERS> wave_buffer{};
215 std::array<s32, AudioCommon::MAX_CHANNEL_COUNT> voice_channel_resource_id{};
216 s32 splitter_info_id{};
217 u8 wave_buffer_flush_request_count{};
218 bool voice_drop_flag{};
219 bool buffer_mapped{};
220 std::array<bool, AudioCommon::MAX_BIQUAD_FILTERS> was_biquad_filter_enabled{};
221 };
222
223 struct OutParams {
224 s64 played_sample_count{};
225 s32 wave_buffer_consumed{};
226 };
227
228 ServerVoiceInfo();
229 ~ServerVoiceInfo();
230 void Initialize();
231 void UpdateParameters(const VoiceInfo::InParams& voice_in, BehaviorInfo& behavior_info);
232 void UpdateWaveBuffers(const VoiceInfo::InParams& voice_in,
233 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states,
234 BehaviorInfo& behavior_info);
235 void UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, const WaveBuffer& in_wave_buffer,
236 SampleFormat sample_format, bool is_buffer_valid,
237 BehaviorInfo& behavior_info);
238 void WriteOutStatus(VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
239 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states);
240
241 const InParams& GetInParams() const;
242 InParams& GetInParams();
243
244 const OutParams& GetOutParams() const;
245 OutParams& GetOutParams();
246
247 bool ShouldSkip() const;
248 bool UpdateForCommandGeneration(VoiceContext& voice_context);
249 void ResetResources(VoiceContext& voice_context);
250 bool UpdateParametersForCommandGeneration(
251 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states);
252 void FlushWaveBuffers(u8 flush_count,
253 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
254 s32 channel_count);
255
256private:
257 std::vector<s16> stored_samples;
258 InParams in_params{};
259 OutParams out_params{};
260
261 bool HasValidWaveBuffer(const VoiceState* state) const;
262};
263
264class VoiceContext {
265public:
266 VoiceContext(std::size_t voice_count);
267 ~VoiceContext();
268
269 std::size_t GetVoiceCount() const;
270 ServerVoiceChannelResource& GetChannelResource(std::size_t i);
271 const ServerVoiceChannelResource& GetChannelResource(std::size_t i) const;
272 VoiceState& GetState(std::size_t i);
273 const VoiceState& GetState(std::size_t i) const;
274 VoiceState& GetDspSharedState(std::size_t i);
275 const VoiceState& GetDspSharedState(std::size_t i) const;
276 ServerVoiceInfo& GetInfo(std::size_t i);
277 const ServerVoiceInfo& GetInfo(std::size_t i) const;
278 ServerVoiceInfo& GetSortedInfo(std::size_t i);
279 const ServerVoiceInfo& GetSortedInfo(std::size_t i) const;
280
281 s32 DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer, s32 channel,
282 s32 channel_count, s32 buffer_offset, s32 sample_count,
283 Core::Memory::Memory& memory);
284 void SortInfo();
285 void UpdateStateByDspShared();
286
287private:
288 std::size_t voice_count{};
289 std::vector<ServerVoiceChannelResource> voice_channel_resources{};
290 std::vector<VoiceState> voice_states{};
291 std::vector<VoiceState> dsp_voice_states{};
292 std::vector<ServerVoiceInfo> voice_info{};
293 std::vector<ServerVoiceInfo*> sorted_voice_info{};
294};
295
296} // namespace AudioCore
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index d8359abaa..a2d3ded7b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -26,7 +26,7 @@ namespace Service::Audio {
26 26
27class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { 27class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
28public: 28public:
29 explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params, 29 explicit IAudioRenderer(Core::System& system, AudioCommon::AudioRendererParameter audren_params,
30 const std::size_t instance_number) 30 const std::size_t instance_number)
31 : ServiceFramework("IAudioRenderer") { 31 : ServiceFramework("IAudioRenderer") {
32 // clang-format off 32 // clang-format off
@@ -94,14 +94,15 @@ private:
94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { 94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
95 LOG_DEBUG(Service_Audio, "(STUBBED) called"); 95 LOG_DEBUG(Service_Audio, "(STUBBED) called");
96 96
97 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer()); 97 std::vector<u8> output_params(ctx.GetWriteBufferSize());
98 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
98 99
99 if (result.Succeeded()) { 100 if (result.IsSuccess()) {
100 ctx.WriteBuffer(result.Unwrap()); 101 ctx.WriteBuffer(output_params);
101 } 102 }
102 103
103 IPC::ResponseBuilder rb{ctx, 2}; 104 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(result.Code()); 105 rb.Push(result);
105 } 106 }
106 107
107 void Start(Kernel::HLERequestContext& ctx) { 108 void Start(Kernel::HLERequestContext& ctx) {
@@ -346,7 +347,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
346 OpenAudioRendererImpl(ctx); 347 OpenAudioRendererImpl(ctx);
347} 348}
348 349
349static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) { 350static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
350 // +1 represents the final mix. 351 // +1 represents the final mix.
351 return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + 352 return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
352 1; 353 1;
@@ -375,7 +376,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
375 constexpr u64 upsampler_manager_size = 0x48; 376 constexpr u64 upsampler_manager_size = 0x48;
376 377
377 // Calculates the part of the size that relates to mix buffers. 378 // Calculates the part of the size that relates to mix buffers.
378 const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) { 379 const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
379 // As of 8.0.0 this is the maximum on voice channels. 380 // As of 8.0.0 this is the maximum on voice channels.
380 constexpr u64 max_voice_channels = 6; 381 constexpr u64 max_voice_channels = 6;
381 382
@@ -397,7 +398,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
397 }; 398 };
398 399
399 // Calculates the portion of the size related to the mix data (and the sorting thereof). 400 // Calculates the portion of the size related to the mix data (and the sorting thereof).
400 const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) { 401 const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
401 // The size of the mixing info data structure. 402 // The size of the mixing info data structure.
402 constexpr u64 mix_info_size = 0x940; 403 constexpr u64 mix_info_size = 0x940;
403 404
@@ -447,7 +448,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
447 }; 448 };
448 449
449 // Calculates the part of the size related to voice channel info. 450 // Calculates the part of the size related to voice channel info.
450 const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) { 451 const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
451 constexpr u64 voice_info_size = 0x220; 452 constexpr u64 voice_info_size = 0x220;
452 constexpr u64 voice_resource_size = 0xD0; 453 constexpr u64 voice_resource_size = 0xD0;
453 454
@@ -461,7 +462,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
461 }; 462 };
462 463
463 // Calculates the part of the size related to memory pools. 464 // Calculates the part of the size related to memory pools.
464 const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) { 465 const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
465 const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); 466 const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
466 const u64 memory_pool_info_size = 0x20; 467 const u64 memory_pool_info_size = 0x20;
467 return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); 468 return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
@@ -469,7 +470,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
469 470
470 // Calculates the part of the size related to the splitter context. 471 // Calculates the part of the size related to the splitter context.
471 const auto calculate_splitter_context_size = 472 const auto calculate_splitter_context_size =
472 [](const AudioCore::AudioRendererParameter& params) -> u64 { 473 [](const AudioCommon::AudioRendererParameter& params) -> u64 {
473 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { 474 if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
474 return 0; 475 return 0;
475 } 476 }
@@ -488,27 +489,29 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
488 }; 489 };
489 490
490 // Calculates the part of the size related to the upsampler info. 491 // Calculates the part of the size related to the upsampler info.
491 const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) { 492 const auto calculate_upsampler_info_size =
492 constexpr u64 upsampler_info_size = 0x280; 493 [](const AudioCommon::AudioRendererParameter& params) {
493 // Yes, using the buffer size over info alignment size is intentional here. 494 constexpr u64 upsampler_info_size = 0x280;
494 return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count), 495 // Yes, using the buffer size over info alignment size is intentional here.
495 buffer_alignment_size); 496 return Common::AlignUp(upsampler_info_size *
496 }; 497 (u64{params.submix_count} + params.sink_count),
498 buffer_alignment_size);
499 };
497 500
498 // Calculates the part of the size related to effect info. 501 // Calculates the part of the size related to effect info.
499 const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) { 502 const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
500 constexpr u64 effect_info_size = 0x2B0; 503 constexpr u64 effect_info_size = 0x2B0;
501 return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); 504 return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
502 }; 505 };
503 506
504 // Calculates the part of the size related to audio sink info. 507 // Calculates the part of the size related to audio sink info.
505 const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) { 508 const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
506 const u64 sink_info_size = 0x170; 509 const u64 sink_info_size = 0x170;
507 return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); 510 return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
508 }; 511 };
509 512
510 // Calculates the part of the size related to voice state info. 513 // Calculates the part of the size related to voice state info.
511 const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) { 514 const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
512 const u64 voice_state_size = 0x100; 515 const u64 voice_state_size = 0x100;
513 const u64 additional_size = buffer_alignment_size - 1; 516 const u64 additional_size = buffer_alignment_size - 1;
514 return Common::AlignUp(voice_state_size * params.voice_count + additional_size, 517 return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
@@ -516,7 +519,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
516 }; 519 };
517 520
518 // Calculates the part of the size related to performance statistics. 521 // Calculates the part of the size related to performance statistics.
519 const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) { 522 const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
520 // Extra size value appended to the end of the calculation. 523 // Extra size value appended to the end of the calculation.
521 constexpr u64 appended = 128; 524 constexpr u64 appended = 128;
522 525
@@ -543,79 +546,81 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
543 }; 546 };
544 547
545 // Calculates the part of the size that relates to the audio command buffer. 548 // Calculates the part of the size that relates to the audio command buffer.
546 const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) { 549 const auto calculate_command_buffer_size =
547 constexpr u64 alignment = (buffer_alignment_size - 1) * 2; 550 [](const AudioCommon::AudioRendererParameter& params) {
551 constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
548 552
549 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { 553 if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
550 constexpr u64 command_buffer_size = 0x18000; 554 constexpr u64 command_buffer_size = 0x18000;
551 555
552 return command_buffer_size + alignment; 556 return command_buffer_size + alignment;
553 } 557 }
554 558
555 // When the variadic command buffer is supported, this means 559 // When the variadic command buffer is supported, this means
556 // the command generator for the audio renderer can issue commands 560 // the command generator for the audio renderer can issue commands
557 // that are (as one would expect), variable in size. So what we need to do 561 // that are (as one would expect), variable in size. So what we need to do
558 // is determine the maximum possible size for a few command data structures 562 // is determine the maximum possible size for a few command data structures
559 // then multiply them by the amount of present commands indicated by the given 563 // then multiply them by the amount of present commands indicated by the given
560 // respective audio parameters. 564 // respective audio parameters.
561 565
562 constexpr u64 max_biquad_filters = 2; 566 constexpr u64 max_biquad_filters = 2;
563 constexpr u64 max_mix_buffers = 24; 567 constexpr u64 max_mix_buffers = 24;
564 568
565 constexpr u64 biquad_filter_command_size = 0x2C; 569 constexpr u64 biquad_filter_command_size = 0x2C;
566 570
567 constexpr u64 depop_mix_command_size = 0x24; 571 constexpr u64 depop_mix_command_size = 0x24;
568 constexpr u64 depop_setup_command_size = 0x50; 572 constexpr u64 depop_setup_command_size = 0x50;
569 573
570 constexpr u64 effect_command_max_size = 0x540; 574 constexpr u64 effect_command_max_size = 0x540;
571 575
572 constexpr u64 mix_command_size = 0x1C; 576 constexpr u64 mix_command_size = 0x1C;
573 constexpr u64 mix_ramp_command_size = 0x24; 577 constexpr u64 mix_ramp_command_size = 0x24;
574 constexpr u64 mix_ramp_grouped_command_size = 0x13C; 578 constexpr u64 mix_ramp_grouped_command_size = 0x13C;
575 579
576 constexpr u64 perf_command_size = 0x28; 580 constexpr u64 perf_command_size = 0x28;
577 581
578 constexpr u64 sink_command_size = 0x130; 582 constexpr u64 sink_command_size = 0x130;
579 583
580 constexpr u64 submix_command_max_size = 584 constexpr u64 submix_command_max_size =
581 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; 585 depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
582 586
583 constexpr u64 volume_command_size = 0x1C; 587 constexpr u64 volume_command_size = 0x1C;
584 constexpr u64 volume_ramp_command_size = 0x20; 588 constexpr u64 volume_ramp_command_size = 0x20;
585 589
586 constexpr u64 voice_biquad_filter_command_size = 590 constexpr u64 voice_biquad_filter_command_size =
587 biquad_filter_command_size * max_biquad_filters; 591 biquad_filter_command_size * max_biquad_filters;
588 constexpr u64 voice_data_command_size = 0x9C; 592 constexpr u64 voice_data_command_size = 0x9C;
589 const u64 voice_command_max_size = 593 const u64 voice_command_max_size =
590 (params.splitter_count * depop_setup_command_size) + 594 (params.splitter_count * depop_setup_command_size) +
591 (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size + 595 (voice_data_command_size + voice_biquad_filter_command_size +
592 mix_ramp_grouped_command_size); 596 volume_ramp_command_size + mix_ramp_grouped_command_size);
593 597
594 // Now calculate the individual elements that comprise the size and add them together. 598 // Now calculate the individual elements that comprise the size and add them together.
595 const u64 effect_commands_size = params.effect_count * effect_command_max_size; 599 const u64 effect_commands_size = params.effect_count * effect_command_max_size;
596 600
597 const u64 final_mix_commands_size = 601 const u64 final_mix_commands_size =
598 depop_mix_command_size + volume_command_size * max_mix_buffers; 602 depop_mix_command_size + volume_command_size * max_mix_buffers;
599 603
600 const u64 perf_commands_size = 604 const u64 perf_commands_size =
601 perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); 605 perf_command_size *
606 (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
602 607
603 const u64 sink_commands_size = params.sink_count * sink_command_size; 608 const u64 sink_commands_size = params.sink_count * sink_command_size;
604 609
605 const u64 splitter_commands_size = 610 const u64 splitter_commands_size =
606 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; 611 params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
607 612
608 const u64 submix_commands_size = params.submix_count * submix_command_max_size; 613 const u64 submix_commands_size = params.submix_count * submix_command_max_size;
609 614
610 const u64 voice_commands_size = params.voice_count * voice_command_max_size; 615 const u64 voice_commands_size = params.voice_count * voice_command_max_size;
611 616
612 return effect_commands_size + final_mix_commands_size + perf_commands_size + 617 return effect_commands_size + final_mix_commands_size + perf_commands_size +
613 sink_commands_size + splitter_commands_size + submix_commands_size + 618 sink_commands_size + splitter_commands_size + submix_commands_size +
614 voice_commands_size + alignment; 619 voice_commands_size + alignment;
615 }; 620 };
616 621
617 IPC::RequestParser rp{ctx}; 622 IPC::RequestParser rp{ctx};
618 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); 623 const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
619 624
620 u64 size = 0; 625 u64 size = 0;
621 size += calculate_mix_buffer_sizes(params); 626 size += calculate_mix_buffer_sizes(params);
@@ -681,7 +686,7 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c
681 686
682void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { 687void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
683 IPC::RequestParser rp{ctx}; 688 IPC::RequestParser rp{ctx};
684 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); 689 const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
685 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 690 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
686 691
687 rb.Push(RESULT_SUCCESS); 692 rb.Push(RESULT_SUCCESS);