summaryrefslogtreecommitdiff
path: root/src/audio_core/voice_context.cpp
diff options
context:
space:
mode:
authorGravatar David Marcec2020-07-12 21:59:14 +1000
committerGravatar David Marcec2020-07-25 12:39:34 +1000
commit380658c21d39cf05ac765a9284da246388cca2a4 (patch)
tree1416cd7e9aee96ec40675078d16a8240d410d04b /src/audio_core/voice_context.cpp
parentMerge pull request #4377 from Morph1984/dark-themes (diff)
downloadyuzu-380658c21d39cf05ac765a9284da246388cca2a4.tar.gz
yuzu-380658c21d39cf05ac765a9284da246388cca2a4.tar.xz
yuzu-380658c21d39cf05ac765a9284da246388cca2a4.zip
audio_core: Apollo Part 1, AudioRenderer refactor
Diffstat (limited to 'src/audio_core/voice_context.cpp')
-rw-r--r--src/audio_core/voice_context.cpp531
1 files changed, 531 insertions, 0 deletions
diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp
new file mode 100644
index 000000000..038595ae0
--- /dev/null
+++ b/src/audio_core/voice_context.cpp
@@ -0,0 +1,531 @@
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 std::copy(in_params.mix_volume.begin(), in_params.mix_volume.end(), mix_volume.begin());
33 }
34}
35
36void ServerVoiceChannelResource::UpdateLastMixVolumes() {
37 std::copy(mix_volume.begin(), mix_volume.end(), last_mix_volume.begin());
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 std::memset(in_params.biquad_filter.data(), 0,
68 sizeof(BiquadFilterParameter) * in_params.biquad_filter.size());
69 in_params.wave_buffer_count = 0;
70 in_params.wave_bufffer_head = 0;
71 in_params.mix_id = AudioCommon::NO_MIX;
72 in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
73 in_params.additional_params_address = 0;
74 in_params.additional_params_size = 0;
75 in_params.is_new = false;
76 out_params.played_sample_count = 0;
77 out_params.wave_buffer_consumed = 0;
78 in_params.voice_drop_flag = false;
79 in_params.buffer_mapped = false;
80 in_params.wave_buffer_flush_request_count = 0;
81 std::fill(in_params.was_biquad_filter_enabled.begin(),
82 in_params.was_biquad_filter_enabled.end(), false);
83
84 for (auto& wave_buffer : in_params.wave_buffer) {
85 wave_buffer.start_sample_offset = 0;
86 wave_buffer.end_sample_offset = 0;
87 wave_buffer.is_looping = false;
88 wave_buffer.end_of_stream = false;
89 wave_buffer.buffer_address = 0;
90 wave_buffer.buffer_size = 0;
91 wave_buffer.context_address = 0;
92 wave_buffer.context_size = 0;
93 wave_buffer.sent_to_dsp = true;
94 }
95
96 stored_samples.clear();
97}
98
99void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
100 BehaviorInfo& behavior_info) {
101 in_params.in_use = voice_in.is_in_use;
102 in_params.id = voice_in.id;
103 in_params.node_id = voice_in.node_id;
104 in_params.last_playstate = in_params.current_playstate;
105 switch (voice_in.play_state) {
106 case PlayState::Paused:
107 in_params.current_playstate = ServerPlayState::Paused;
108 break;
109 case PlayState::Stopped:
110 if (in_params.current_playstate != ServerPlayState::Stop) {
111 in_params.current_playstate = ServerPlayState::RequestStop;
112 }
113 break;
114 case PlayState::Started:
115 in_params.current_playstate = ServerPlayState::Play;
116 break;
117 default:
118 UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state);
119 break;
120 }
121
122 in_params.priority = voice_in.priority;
123 in_params.sorting_order = voice_in.sorting_order;
124 in_params.sample_rate = voice_in.sample_rate;
125 in_params.sample_format = voice_in.sample_format;
126 in_params.channel_count = voice_in.channel_count;
127 in_params.pitch = voice_in.pitch;
128 in_params.volume = voice_in.volume;
129 std::memcpy(in_params.biquad_filter.data(), voice_in.biquad_filter.data(),
130 sizeof(BiquadFilterParameter) * voice_in.biquad_filter.size());
131 in_params.wave_buffer_count = voice_in.wave_buffer_count;
132 in_params.wave_bufffer_head = voice_in.wave_buffer_head;
133 if (behavior_info.IsFlushVoiceWaveBuffersSupported()) {
134 in_params.wave_buffer_flush_request_count += voice_in.wave_buffer_flush_request_count;
135 }
136 in_params.mix_id = voice_in.mix_id;
137 if (behavior_info.IsSplitterSupported()) {
138 in_params.splitter_info_id = voice_in.splitter_info_id;
139 } else {
140 in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
141 }
142
143 std::memcpy(in_params.voice_channel_resource_id.data(),
144 voice_in.voice_channel_resource_ids.data(),
145 sizeof(s32) * in_params.voice_channel_resource_id.size());
146
147 if (behavior_info.IsVoicePlayedSampleCountResetAtLoopPointSupported()) {
148 in_params.behavior_flags.is_played_samples_reset_at_loop_point =
149 voice_in.behavior_flags.is_played_samples_reset_at_loop_point;
150 } else {
151 in_params.behavior_flags.is_played_samples_reset_at_loop_point.Assign(0);
152 }
153 if (behavior_info.IsVoicePitchAndSrcSkippedSupported()) {
154 in_params.behavior_flags.is_pitch_and_src_skipped =
155 voice_in.behavior_flags.is_pitch_and_src_skipped;
156 } else {
157 in_params.behavior_flags.is_pitch_and_src_skipped.Assign(0);
158 }
159
160 if (voice_in.is_voice_drop_flag_clear_requested) {
161 in_params.voice_drop_flag = false;
162 }
163
164 if (in_params.additional_params_address != voice_in.additional_params_address ||
165 in_params.additional_params_size != voice_in.additional_params_size) {
166 in_params.additional_params_address = voice_in.additional_params_address;
167 in_params.additional_params_size = voice_in.additional_params_size;
168 // TODO(ogniK): Reattach buffer, do we actually need to? Maybe just signal to the DSP that
169 // our context is new
170 }
171}
172
173void ServerVoiceInfo::UpdateWaveBuffers(
174 const VoiceInfo::InParams& voice_in,
175 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states,
176 BehaviorInfo& behavior_info) {
177 if (voice_in.is_new) {
178 // Initialize our wave buffers
179 for (auto& wave_buffer : in_params.wave_buffer) {
180 wave_buffer.start_sample_offset = 0;
181 wave_buffer.end_sample_offset = 0;
182 wave_buffer.is_looping = false;
183 wave_buffer.end_of_stream = false;
184 wave_buffer.buffer_address = 0;
185 wave_buffer.buffer_size = 0;
186 wave_buffer.context_address = 0;
187 wave_buffer.context_size = 0;
188 wave_buffer.sent_to_dsp = true;
189 }
190
191 // Mark all our wave buffers as invalid
192 for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count);
193 channel++) {
194 for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) {
195 is_valid = false;
196 }
197 }
198 }
199
200 // Update our wave buffers
201 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
202 // Assume that we have at least 1 channel voice state
203 const auto have_valid_wave_buffer = voice_states[0]->is_wave_buffer_valid[i];
204
205 UpdateWaveBuffer(in_params.wave_buffer[i], voice_in.wave_buffer[i], in_params.sample_format,
206 have_valid_wave_buffer, behavior_info);
207 }
208}
209
210void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
211 const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
212 bool is_buffer_valid, BehaviorInfo& behavior_info) {
213 if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) {
214 out_wavebuffer.buffer_address = 0;
215 out_wavebuffer.buffer_size = 0;
216 }
217
218 if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) {
219 // Validate sample offset sizings
220 if (sample_format == SampleFormat::Pcm16) {
221 const auto buffer_size = in_wave_buffer.buffer_size;
222 if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 ||
223 (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) ||
224 (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) {
225 // TODO(ogniK): Write error info
226 return;
227 }
228 }
229 // TODO(ogniK): ADPCM Size error
230
231 out_wavebuffer.sent_to_dsp = false;
232 out_wavebuffer.start_sample_offset = in_wave_buffer.start_sample_offset;
233 out_wavebuffer.end_sample_offset = in_wave_buffer.end_sample_offset;
234 out_wavebuffer.is_looping = in_wave_buffer.is_looping;
235 out_wavebuffer.end_of_stream = in_wave_buffer.end_of_stream;
236
237 out_wavebuffer.buffer_address = in_wave_buffer.buffer_address;
238 out_wavebuffer.buffer_size = in_wave_buffer.buffer_size;
239 out_wavebuffer.context_address = in_wave_buffer.context_address;
240 out_wavebuffer.context_size = in_wave_buffer.context_size;
241 in_params.buffer_mapped =
242 in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0;
243 // TODO(ogniK): Pool mapper attachment
244 // TODO(ogniK): IsAdpcmLoopContextBugFixed
245 }
246}
247
248void ServerVoiceInfo::WriteOutStatus(
249 VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
250 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) {
251 if (voice_in.is_new) {
252 in_params.is_new = true;
253 voice_out.wave_buffer_consumed = 0;
254 voice_out.played_sample_count = 0;
255 voice_out.voice_dropped = false;
256 } else if (!in_params.is_new) {
257 voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed;
258 voice_out.played_sample_count = voice_states[0]->played_sample_count;
259 voice_out.voice_dropped = in_params.voice_drop_flag;
260 } else {
261 voice_out.wave_buffer_consumed = 0;
262 voice_out.played_sample_count = 0;
263 voice_out.voice_dropped = false;
264 }
265}
266
267const ServerVoiceInfo::InParams& ServerVoiceInfo::GetInParams() const {
268 return in_params;
269}
270
271ServerVoiceInfo::InParams& ServerVoiceInfo::GetInParams() {
272 return in_params;
273}
274
275const ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() const {
276 return out_params;
277}
278
279ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() {
280 return out_params;
281}
282
283bool ServerVoiceInfo::ShouldSkip() const {
284 // TODO(ogniK): Handle unmapped wave buffers or parameters
285 return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag;
286}
287
288bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) {
289 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> dsp_voice_states{};
290 if (in_params.is_new) {
291 ResetResources(voice_context);
292 in_params.last_volume = in_params.volume;
293 in_params.is_new = false;
294 }
295
296 const s32 channel_count = in_params.channel_count;
297 for (s32 i = 0; i < channel_count; i++) {
298 const auto channel_resource = in_params.voice_channel_resource_id[i];
299 dsp_voice_states[i] =
300 &voice_context.GetDspSharedState(static_cast<std::size_t>(channel_resource));
301 }
302 return UpdateParametersForCommandGeneration(dsp_voice_states);
303}
304
305void ServerVoiceInfo::ResetResources(VoiceContext& voice_context) {
306 const s32 channel_count = in_params.channel_count;
307 for (s32 i = 0; i < channel_count; i++) {
308 const auto channel_resource = in_params.voice_channel_resource_id[i];
309 auto& dsp_state =
310 voice_context.GetDspSharedState(static_cast<std::size_t>(channel_resource));
311 std::memset(&dsp_state, 0, sizeof(VoiceState));
312 voice_context.GetChannelResource(static_cast<std::size_t>(channel_resource))
313 .UpdateLastMixVolumes();
314 }
315}
316
317bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
318 std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states) {
319 const s32 channel_count = in_params.channel_count;
320 if (in_params.wave_buffer_flush_request_count > 0) {
321 FlushWaveBuffers(in_params.wave_buffer_flush_request_count, dsp_voice_states,
322 channel_count);
323 in_params.wave_buffer_flush_request_count = 0;
324 }
325
326 switch (in_params.current_playstate) {
327 case ServerPlayState::Play: {
328 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
329 if (!in_params.wave_buffer[i].sent_to_dsp) {
330 for (s32 channel = 0; channel < channel_count; channel++) {
331 dsp_voice_states[channel]->is_wave_buffer_valid[i] = true;
332 }
333 in_params.wave_buffer[i].sent_to_dsp = true;
334 }
335 }
336 in_params.should_depop = false;
337 return HasValidWaveBuffer(dsp_voice_states[0]);
338 }
339 case ServerPlayState::Paused:
340 case ServerPlayState::Stop: {
341 in_params.should_depop = in_params.last_playstate == ServerPlayState::Play;
342 return in_params.should_depop;
343 }
344 case ServerPlayState::RequestStop: {
345 for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
346 in_params.wave_buffer[i].sent_to_dsp = true;
347 for (s32 channel = 0; channel < channel_count; channel++) {
348 auto* dsp_state = dsp_voice_states[channel];
349
350 if (dsp_state->is_wave_buffer_valid[i]) {
351 dsp_state->wave_buffer_index =
352 (dsp_state->wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
353 dsp_state->wave_buffer_consumed++;
354 }
355
356 dsp_state->is_wave_buffer_valid[i] = false;
357 }
358 }
359
360 for (s32 channel = 0; channel < channel_count; channel++) {
361 auto* dsp_state = dsp_voice_states[channel];
362 dsp_state->offset = 0;
363 dsp_state->played_sample_count = 0;
364 dsp_state->fraction = 0;
365 std::memset(dsp_state->sample_history.data(), 0,
366 sizeof(s32) * dsp_state->sample_history.size());
367 std::memset(&dsp_state->context, 0, sizeof(dsp_state->context));
368 }
369
370 in_params.current_playstate = ServerPlayState::Stop;
371 in_params.should_depop = in_params.last_playstate == ServerPlayState::Play;
372 return in_params.should_depop;
373 }
374 default:
375 UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate);
376 }
377
378 return false;
379}
380
381void ServerVoiceInfo::FlushWaveBuffers(
382 u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
383 s32 channel_count) {
384 auto wave_head = in_params.wave_bufffer_head;
385
386 for (u8 i = 0; i < flush_count; i++) {
387 in_params.wave_buffer[wave_head].sent_to_dsp = true;
388 for (s32 channel = 0; channel < channel_count; channel++) {
389 auto* dsp_state = dsp_voice_states[channel];
390 dsp_state->wave_buffer_consumed++;
391 dsp_state->is_wave_buffer_valid[wave_head] = false;
392 dsp_state->wave_buffer_index =
393 (dsp_state->wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
394 }
395 wave_head = (wave_head + 1) % AudioCommon::MAX_WAVE_BUFFERS;
396 }
397}
398
399bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const {
400 const auto& valid_wb = state->is_wave_buffer_valid;
401 return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
402}
403
404VoiceContext::VoiceContext(std::size_t voice_count) : voice_count(voice_count) {
405 for (std::size_t i = 0; i < voice_count; i++) {
406 voice_channel_resources.emplace_back(static_cast<s32>(i));
407 sorted_voice_info.push_back(&voice_info.emplace_back());
408 voice_states.emplace_back();
409 dsp_voice_states.emplace_back();
410 }
411}
412
413VoiceContext::~VoiceContext() {
414 sorted_voice_info.clear();
415}
416
417std::size_t VoiceContext::GetVoiceCount() const {
418 return voice_count;
419}
420
421ServerVoiceChannelResource& VoiceContext::GetChannelResource(std::size_t i) {
422 ASSERT(i < voice_count);
423 return voice_channel_resources.at(i);
424}
425
426const ServerVoiceChannelResource& VoiceContext::GetChannelResource(std::size_t i) const {
427 ASSERT(i < voice_count);
428 return voice_channel_resources.at(i);
429}
430
431VoiceState& VoiceContext::GetState(std::size_t i) {
432 ASSERT(i < voice_count);
433 return voice_states.at(i);
434}
435
436const VoiceState& VoiceContext::GetState(std::size_t i) const {
437 ASSERT(i < voice_count);
438 return voice_states.at(i);
439}
440
441VoiceState& VoiceContext::GetDspSharedState(std::size_t i) {
442 ASSERT(i < voice_count);
443 return dsp_voice_states.at(i);
444}
445
446const VoiceState& VoiceContext::GetDspSharedState(std::size_t i) const {
447 ASSERT(i < voice_count);
448 return dsp_voice_states.at(i);
449}
450
451ServerVoiceInfo& VoiceContext::GetInfo(std::size_t i) {
452 ASSERT(i < voice_count);
453 return voice_info.at(i);
454}
455
456const ServerVoiceInfo& VoiceContext::GetInfo(std::size_t i) const {
457 ASSERT(i < voice_count);
458 return voice_info.at(i);
459}
460
461ServerVoiceInfo& VoiceContext::GetSortedInfo(std::size_t i) {
462 ASSERT(i < voice_count);
463 return *sorted_voice_info.at(i);
464}
465
466const ServerVoiceInfo& VoiceContext::GetSortedInfo(std::size_t i) const {
467 ASSERT(i < voice_count);
468 return *sorted_voice_info.at(i);
469}
470
471s32 VoiceContext::DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer, s32 channel,
472 s32 channel_count, s32 buffer_offset, s32 sample_count,
473 Core::Memory::Memory& memory) {
474 if (wave_buffer->buffer_address == 0) {
475 return 0;
476 }
477 if (wave_buffer->buffer_size == 0) {
478 return 0;
479 }
480 if (wave_buffer->end_sample_offset < wave_buffer->start_sample_offset) {
481 return 0;
482 }
483
484 const auto samples_remaining =
485 (wave_buffer->end_sample_offset - wave_buffer->start_sample_offset) - buffer_offset;
486 const auto start_offset = (wave_buffer->start_sample_offset + buffer_offset) * channel_count;
487 const auto buffer_pos = wave_buffer->buffer_address + start_offset;
488
489 s16* buffer_data = reinterpret_cast<s16*>(memory.GetPointer(buffer_pos));
490
491 const auto samples_processed = std::min(sample_count, samples_remaining);
492
493 // Fast path
494 if (channel_count == 1) {
495 for (std::size_t i = 0; i < samples_processed; i++) {
496 output_buffer[i] = buffer_data[i];
497 }
498 } else {
499 for (std::size_t i = 0; i < samples_processed; i++) {
500 output_buffer[i] = buffer_data[i * channel_count + channel];
501 }
502 }
503
504 return samples_processed;
505}
506
507void VoiceContext::SortInfo() {
508 for (std::size_t i = 0; i < voice_count; i++) {
509 sorted_voice_info[i] = &voice_info[i];
510 }
511
512 std::sort(sorted_voice_info.begin(), sorted_voice_info.end(),
513 [](const ServerVoiceInfo* lhs, const ServerVoiceInfo* rhs) {
514 const auto& lhs_in = lhs->GetInParams();
515 const auto& rhs_in = rhs->GetInParams();
516 // Sort by priority
517 if (lhs_in.priority != rhs_in.priority) {
518 return lhs_in.priority > rhs_in.priority;
519 } else {
520 // If the priorities match, sort by sorting order
521 return lhs_in.sorting_order > rhs_in.sorting_order;
522 }
523 });
524}
525
526void VoiceContext::UpdateStateByDspShared() {
527 std::memcpy(voice_states.data(), dsp_voice_states.data(),
528 sizeof(VoiceState) * dsp_voice_states.size());
529}
530
531} // namespace AudioCore