summaryrefslogtreecommitdiff
path: root/src/audio_core/splitter_context.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core/splitter_context.cpp')
-rw-r--r--src/audio_core/splitter_context.cpp616
1 files changed, 0 insertions, 616 deletions
diff --git a/src/audio_core/splitter_context.cpp b/src/audio_core/splitter_context.cpp
deleted file mode 100644
index 10646dc05..000000000
--- a/src/audio_core/splitter_context.cpp
+++ /dev/null
@@ -1,616 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/behavior_info.h"
5#include "audio_core/splitter_context.h"
6#include "common/alignment.h"
7#include "common/assert.h"
8#include "common/logging/log.h"
9
10namespace AudioCore {
11
12ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id_) : id{id_} {}
13ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
14
15void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
16 // Log error as these are not actually failure states
17 if (header.magic != SplitterMagic::DataHeader) {
18 LOG_ERROR(Audio, "Splitter destination header is invalid!");
19 return;
20 }
21
22 // Incorrect splitter id
23 if (header.splitter_id != id) {
24 LOG_ERROR(Audio, "Splitter destination ids do not match!");
25 return;
26 }
27
28 mix_id = header.mix_id;
29 // Copy our mix volumes
30 std::copy(header.mix_volumes.begin(), header.mix_volumes.end(), current_mix_volumes.begin());
31 if (!in_use && header.in_use) {
32 // Update mix volumes
33 std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
34 needs_update = false;
35 }
36 in_use = header.in_use;
37}
38
39ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() {
40 return next;
41}
42
43const ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() const {
44 return next;
45}
46
47void ServerSplitterDestinationData::SetNextDestination(ServerSplitterDestinationData* dest) {
48 next = dest;
49}
50
51bool ServerSplitterDestinationData::ValidMixId() const {
52 return GetMixId() != AudioCommon::NO_MIX;
53}
54
55s32 ServerSplitterDestinationData::GetMixId() const {
56 return mix_id;
57}
58
59bool ServerSplitterDestinationData::IsConfigured() const {
60 return in_use && ValidMixId();
61}
62
63float ServerSplitterDestinationData::GetMixVolume(std::size_t i) const {
64 ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
65 return current_mix_volumes.at(i);
66}
67
68const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
69ServerSplitterDestinationData::CurrentMixVolumes() const {
70 return current_mix_volumes;
71}
72
73const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
74ServerSplitterDestinationData::LastMixVolumes() const {
75 return last_mix_volumes;
76}
77
78void ServerSplitterDestinationData::MarkDirty() {
79 needs_update = true;
80}
81
82void ServerSplitterDestinationData::UpdateInternalState() {
83 if (in_use && needs_update) {
84 std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
85 }
86 needs_update = false;
87}
88
89ServerSplitterInfo::ServerSplitterInfo(s32 id_) : id(id_) {}
90ServerSplitterInfo::~ServerSplitterInfo() = default;
91
92void ServerSplitterInfo::InitializeInfos() {
93 send_length = 0;
94 head = nullptr;
95 new_connection = true;
96}
97
98void ServerSplitterInfo::ClearNewConnectionFlag() {
99 new_connection = false;
100}
101
102std::size_t ServerSplitterInfo::Update(SplitterInfo::InInfoPrams& header) {
103 if (header.send_id != id) {
104 return 0;
105 }
106
107 sample_rate = header.sample_rate;
108 new_connection = true;
109 // We need to update the size here due to the splitter bug being present and providing an
110 // incorrect size. We're suppose to also update the header here but we just ignore and continue
111 return (sizeof(s32_le) * (header.length - 1)) + (sizeof(s32_le) * 3);
112}
113
114ServerSplitterDestinationData* ServerSplitterInfo::GetHead() {
115 return head;
116}
117
118const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const {
119 return head;
120}
121
122ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
123 auto* current_head = head;
124 for (std::size_t i = 0; i < depth; i++) {
125 if (current_head == nullptr) {
126 return nullptr;
127 }
128 current_head = current_head->GetNextDestination();
129 }
130 return current_head;
131}
132
133const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
134 auto* current_head = head;
135 for (std::size_t i = 0; i < depth; i++) {
136 if (current_head == nullptr) {
137 return nullptr;
138 }
139 current_head = current_head->GetNextDestination();
140 }
141 return current_head;
142}
143
144bool ServerSplitterInfo::HasNewConnection() const {
145 return new_connection;
146}
147
148s32 ServerSplitterInfo::GetLength() const {
149 return send_length;
150}
151
152void ServerSplitterInfo::SetHead(ServerSplitterDestinationData* new_head) {
153 head = new_head;
154}
155
156void ServerSplitterInfo::SetHeadDepth(s32 length) {
157 send_length = length;
158}
159
160SplitterContext::SplitterContext() = default;
161SplitterContext::~SplitterContext() = default;
162
163void SplitterContext::Initialize(BehaviorInfo& behavior_info, std::size_t _info_count,
164 std::size_t _data_count) {
165 if (!behavior_info.IsSplitterSupported() || _data_count == 0 || _info_count == 0) {
166 Setup(0, 0, false);
167 return;
168 }
169 // Only initialize if we're using splitters
170 Setup(_info_count, _data_count, behavior_info.IsSplitterBugFixed());
171}
172
173bool SplitterContext::Update(const std::vector<u8>& input, std::size_t& input_offset,
174 std::size_t& bytes_read) {
175 const auto UpdateOffsets = [&](std::size_t read) {
176 input_offset += read;
177 bytes_read += read;
178 };
179
180 if (info_count == 0 || data_count == 0) {
181 bytes_read = 0;
182 return true;
183 }
184
185 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
186 sizeof(SplitterInfo::InHeader))) {
187 LOG_ERROR(Audio, "Buffer is an invalid size!");
188 return false;
189 }
190 SplitterInfo::InHeader header{};
191 std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InHeader));
192 UpdateOffsets(sizeof(SplitterInfo::InHeader));
193
194 if (header.magic != SplitterMagic::SplitterHeader) {
195 LOG_ERROR(Audio, "Invalid header magic! Expecting {:X} but got {:X}",
196 SplitterMagic::SplitterHeader, header.magic);
197 return false;
198 }
199
200 // Clear all connections
201 for (auto& info : infos) {
202 info.ClearNewConnectionFlag();
203 }
204
205 UpdateInfo(input, input_offset, bytes_read, header.info_count);
206 UpdateData(input, input_offset, bytes_read, header.data_count);
207 const auto aligned_bytes_read = Common::AlignUp(bytes_read, 16);
208 input_offset += aligned_bytes_read - bytes_read;
209 bytes_read = aligned_bytes_read;
210 return true;
211}
212
213bool SplitterContext::UsingSplitter() const {
214 return info_count > 0 && data_count > 0;
215}
216
217ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) {
218 ASSERT(i < info_count);
219 return infos.at(i);
220}
221
222const ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) const {
223 ASSERT(i < info_count);
224 return infos.at(i);
225}
226
227ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) {
228 ASSERT(i < data_count);
229 return datas.at(i);
230}
231
232const ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) const {
233 ASSERT(i < data_count);
234 return datas.at(i);
235}
236
237ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
238 std::size_t data) {
239 ASSERT(info < info_count);
240 auto& cur_info = GetInfo(info);
241 return cur_info.GetData(data);
242}
243
244const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
245 std::size_t data) const {
246 ASSERT(info < info_count);
247 const auto& cur_info = GetInfo(info);
248 return cur_info.GetData(data);
249}
250
251void SplitterContext::UpdateInternalState() {
252 if (data_count == 0) {
253 return;
254 }
255
256 for (auto& data : datas) {
257 data.UpdateInternalState();
258 }
259}
260
261std::size_t SplitterContext::GetInfoCount() const {
262 return info_count;
263}
264
265std::size_t SplitterContext::GetDataCount() const {
266 return data_count;
267}
268
269void SplitterContext::Setup(std::size_t info_count_, std::size_t data_count_,
270 bool is_splitter_bug_fixed) {
271
272 info_count = info_count_;
273 data_count = data_count_;
274
275 for (std::size_t i = 0; i < info_count; i++) {
276 auto& splitter = infos.emplace_back(static_cast<s32>(i));
277 splitter.InitializeInfos();
278 }
279 for (std::size_t i = 0; i < data_count; i++) {
280 datas.emplace_back(static_cast<s32>(i));
281 }
282
283 bug_fixed = is_splitter_bug_fixed;
284}
285
286bool SplitterContext::UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
287 std::size_t& bytes_read, s32 in_splitter_count) {
288 const auto UpdateOffsets = [&](std::size_t read) {
289 input_offset += read;
290 bytes_read += read;
291 };
292
293 for (s32 i = 0; i < in_splitter_count; i++) {
294 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
295 sizeof(SplitterInfo::InInfoPrams))) {
296 LOG_ERROR(Audio, "Buffer is an invalid size!");
297 return false;
298 }
299 SplitterInfo::InInfoPrams header{};
300 std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InInfoPrams));
301
302 // Logged as warning as these don't actually cause a bailout for some reason
303 if (header.magic != SplitterMagic::InfoHeader) {
304 LOG_ERROR(Audio, "Bad splitter data header");
305 break;
306 }
307
308 if (header.send_id < 0 || static_cast<std::size_t>(header.send_id) > info_count) {
309 LOG_ERROR(Audio, "Bad splitter data id");
310 break;
311 }
312
313 UpdateOffsets(sizeof(SplitterInfo::InInfoPrams));
314 auto& info = GetInfo(header.send_id);
315 if (!RecomposeDestination(info, header, input, input_offset)) {
316 LOG_ERROR(Audio, "Failed to recompose destination for splitter!");
317 return false;
318 }
319 const std::size_t read = info.Update(header);
320 bytes_read += read;
321 input_offset += read;
322 }
323 return true;
324}
325
326bool SplitterContext::UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
327 std::size_t& bytes_read, s32 in_data_count) {
328 const auto UpdateOffsets = [&](std::size_t read) {
329 input_offset += read;
330 bytes_read += read;
331 };
332
333 for (s32 i = 0; i < in_data_count; i++) {
334 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
335 sizeof(SplitterInfo::InDestinationParams))) {
336 LOG_ERROR(Audio, "Buffer is an invalid size!");
337 return false;
338 }
339 SplitterInfo::InDestinationParams header{};
340 std::memcpy(&header, input.data() + input_offset,
341 sizeof(SplitterInfo::InDestinationParams));
342 UpdateOffsets(sizeof(SplitterInfo::InDestinationParams));
343
344 // Logged as warning as these don't actually cause a bailout for some reason
345 if (header.magic != SplitterMagic::DataHeader) {
346 LOG_ERROR(Audio, "Bad splitter data header");
347 break;
348 }
349
350 if (header.splitter_id < 0 || static_cast<std::size_t>(header.splitter_id) > data_count) {
351 LOG_ERROR(Audio, "Bad splitter data id");
352 break;
353 }
354 GetData(header.splitter_id).Update(header);
355 }
356 return true;
357}
358
359bool SplitterContext::RecomposeDestination(ServerSplitterInfo& info,
360 SplitterInfo::InInfoPrams& header,
361 const std::vector<u8>& input,
362 const std::size_t& input_offset) {
363 // Clear our current destinations
364 auto* current_head = info.GetHead();
365 while (current_head != nullptr) {
366 auto* next_head = current_head->GetNextDestination();
367 current_head->SetNextDestination(nullptr);
368 current_head = next_head;
369 }
370 info.SetHead(nullptr);
371
372 s32 size = header.length;
373 // If the splitter bug is present, calculate fixed size
374 if (!bug_fixed) {
375 if (info_count > 0) {
376 const auto factor = data_count / info_count;
377 size = std::min(header.length, static_cast<s32>(factor));
378 } else {
379 size = 0;
380 }
381 }
382
383 if (size < 1) {
384 LOG_ERROR(Audio, "Invalid splitter info size! size={:X}", size);
385 return true;
386 }
387
388 auto* start_head = &GetData(header.resource_id_base);
389 current_head = start_head;
390 std::vector<s32_le> resource_ids(size - 1);
391 if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
392 resource_ids.size() * sizeof(s32_le))) {
393 LOG_ERROR(Audio, "Buffer is an invalid size!");
394 return false;
395 }
396 std::memcpy(resource_ids.data(), input.data() + input_offset,
397 resource_ids.size() * sizeof(s32_le));
398
399 for (auto resource_id : resource_ids) {
400 auto* head = &GetData(resource_id);
401 current_head->SetNextDestination(head);
402 current_head = head;
403 }
404
405 info.SetHead(start_head);
406 info.SetHeadDepth(size);
407
408 return true;
409}
410
411NodeStates::NodeStates() = default;
412NodeStates::~NodeStates() = default;
413
414void NodeStates::Initialize(std::size_t node_count_) {
415 // Setup our work parameters
416 node_count = node_count_;
417 was_node_found.resize(node_count);
418 was_node_completed.resize(node_count);
419 index_list.resize(node_count);
420 index_stack.Reset(node_count * node_count);
421}
422
423bool NodeStates::Tsort(EdgeMatrix& edge_matrix) {
424 return DepthFirstSearch(edge_matrix);
425}
426
427std::size_t NodeStates::GetIndexPos() const {
428 return index_pos;
429}
430
431const std::vector<s32>& NodeStates::GetIndexList() const {
432 return index_list;
433}
434
435void NodeStates::PushTsortResult(s32 index) {
436 ASSERT(index < static_cast<s32>(node_count));
437 index_list[index_pos++] = index;
438}
439
440bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
441 ResetState();
442 for (std::size_t i = 0; i < node_count; i++) {
443 const auto node_id = static_cast<s32>(i);
444
445 // If we don't have a state, send to our index stack for work
446 if (GetState(i) == NodeStates::State::NoState) {
447 index_stack.push(node_id);
448 }
449
450 // While we have work to do in our stack
451 while (index_stack.Count() > 0) {
452 // Get the current node
453 const auto current_stack_index = index_stack.top();
454 // Check if we've seen the node yet
455 const auto index_state = GetState(current_stack_index);
456 if (index_state == NodeStates::State::NoState) {
457 // Mark the node as seen
458 UpdateState(NodeStates::State::InFound, current_stack_index);
459 } else if (index_state == NodeStates::State::InFound) {
460 // We've seen this node before, mark it as completed
461 UpdateState(NodeStates::State::InCompleted, current_stack_index);
462 // Update our index list
463 PushTsortResult(current_stack_index);
464 // Pop the stack
465 index_stack.pop();
466 continue;
467 } else if (index_state == NodeStates::State::InCompleted) {
468 // If our node is already sorted, clear it
469 index_stack.pop();
470 continue;
471 }
472
473 const auto edge_node_count = edge_matrix.GetNodeCount();
474 for (s32 j = 0; j < static_cast<s32>(edge_node_count); j++) {
475 // Check if our node is connected to our edge matrix
476 if (!edge_matrix.Connected(current_stack_index, j)) {
477 continue;
478 }
479
480 // Check if our node exists
481 const auto node_state = GetState(j);
482 if (node_state == NodeStates::State::NoState) {
483 // Add more work
484 index_stack.push(j);
485 } else if (node_state == NodeStates::State::InFound) {
486 ASSERT_MSG(false, "Node start marked as found");
487 ResetState();
488 return false;
489 }
490 }
491 }
492 }
493 return true;
494}
495
496void NodeStates::ResetState() {
497 // Reset to the start of our index stack
498 index_pos = 0;
499 for (std::size_t i = 0; i < node_count; i++) {
500 // Mark all nodes as not found
501 was_node_found[i] = false;
502 // Mark all nodes as uncompleted
503 was_node_completed[i] = false;
504 // Mark all indexes as invalid
505 index_list[i] = -1;
506 }
507}
508
509void NodeStates::UpdateState(NodeStates::State state, std::size_t i) {
510 switch (state) {
511 case NodeStates::State::NoState:
512 was_node_found[i] = false;
513 was_node_completed[i] = false;
514 break;
515 case NodeStates::State::InFound:
516 was_node_found[i] = true;
517 was_node_completed[i] = false;
518 break;
519 case NodeStates::State::InCompleted:
520 was_node_found[i] = false;
521 was_node_completed[i] = true;
522 break;
523 }
524}
525
526NodeStates::State NodeStates::GetState(std::size_t i) {
527 ASSERT(i < node_count);
528 if (was_node_found[i]) {
529 // If our node exists in our found list
530 return NodeStates::State::InFound;
531 } else if (was_node_completed[i]) {
532 // If node is in the completed list
533 return NodeStates::State::InCompleted;
534 } else {
535 // If in neither
536 return NodeStates::State::NoState;
537 }
538}
539
540NodeStates::Stack::Stack() = default;
541NodeStates::Stack::~Stack() = default;
542
543void NodeStates::Stack::Reset(std::size_t size) {
544 // Mark our stack as empty
545 stack.resize(size);
546 stack_size = size;
547 stack_pos = 0;
548 std::fill(stack.begin(), stack.end(), 0);
549}
550
551void NodeStates::Stack::push(s32 val) {
552 ASSERT(stack_pos < stack_size);
553 stack[stack_pos++] = val;
554}
555
556std::size_t NodeStates::Stack::Count() const {
557 return stack_pos;
558}
559
560s32 NodeStates::Stack::top() const {
561 ASSERT(stack_pos > 0);
562 return stack[stack_pos - 1];
563}
564
565s32 NodeStates::Stack::pop() {
566 ASSERT(stack_pos > 0);
567 stack_pos--;
568 return stack[stack_pos];
569}
570
571EdgeMatrix::EdgeMatrix() = default;
572EdgeMatrix::~EdgeMatrix() = default;
573
574void EdgeMatrix::Initialize(std::size_t _node_count) {
575 node_count = _node_count;
576 edge_matrix.resize(node_count * node_count);
577}
578
579bool EdgeMatrix::Connected(s32 a, s32 b) {
580 return GetState(a, b);
581}
582
583void EdgeMatrix::Connect(s32 a, s32 b) {
584 SetState(a, b, true);
585}
586
587void EdgeMatrix::Disconnect(s32 a, s32 b) {
588 SetState(a, b, false);
589}
590
591void EdgeMatrix::RemoveEdges(s32 edge) {
592 for (std::size_t i = 0; i < node_count; i++) {
593 SetState(edge, static_cast<s32>(i), false);
594 }
595}
596
597std::size_t EdgeMatrix::GetNodeCount() const {
598 return node_count;
599}
600
601void EdgeMatrix::SetState(s32 a, s32 b, bool state) {
602 ASSERT(InRange(a, b));
603 edge_matrix.at(a * node_count + b) = state;
604}
605
606bool EdgeMatrix::GetState(s32 a, s32 b) {
607 ASSERT(InRange(a, b));
608 return edge_matrix.at(a * node_count + b);
609}
610
611bool EdgeMatrix::InRange(s32 a, s32 b) const {
612 const std::size_t pos = a * node_count + b;
613 return pos < (node_count * node_count);
614}
615
616} // namespace AudioCore