summaryrefslogtreecommitdiff
path: root/src/audio_core/splitter_context.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2020-09-11 10:57:27 -0400
committerGravatar GitHub2020-09-11 10:57:27 -0400
commit324029d4f9fd2381f474e608a2859360324161e5 (patch)
treed2dc348235f05f20686c526f7092590f596f65c2 /src/audio_core/splitter_context.cpp
parentMerge pull request #4597 from Morph1984/mjolnir-p2 (diff)
parentPreliminary effects (diff)
downloadyuzu-324029d4f9fd2381f474e608a2859360324161e5.tar.gz
yuzu-324029d4f9fd2381f474e608a2859360324161e5.tar.xz
yuzu-324029d4f9fd2381f474e608a2859360324161e5.zip
Merge pull request #4310 from ogniK5377/apollo-1-prod
audio_core: Apollo Part 1, AudioRenderer refactor
Diffstat (limited to 'src/audio_core/splitter_context.cpp')
-rw-r--r--src/audio_core/splitter_context.cpp617
1 files changed, 617 insertions, 0 deletions
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