diff options
Diffstat (limited to 'src/audio_core/splitter_context.cpp')
| -rw-r--r-- | src/audio_core/splitter_context.cpp | 616 |
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 | |||
| 10 | namespace AudioCore { | ||
| 11 | |||
| 12 | ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id_) : id{id_} {} | ||
| 13 | ServerSplitterDestinationData::~ServerSplitterDestinationData() = default; | ||
| 14 | |||
| 15 | void 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 | |||
| 39 | ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() { | ||
| 40 | return next; | ||
| 41 | } | ||
| 42 | |||
| 43 | const ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() const { | ||
| 44 | return next; | ||
| 45 | } | ||
| 46 | |||
| 47 | void ServerSplitterDestinationData::SetNextDestination(ServerSplitterDestinationData* dest) { | ||
| 48 | next = dest; | ||
| 49 | } | ||
| 50 | |||
| 51 | bool ServerSplitterDestinationData::ValidMixId() const { | ||
| 52 | return GetMixId() != AudioCommon::NO_MIX; | ||
| 53 | } | ||
| 54 | |||
| 55 | s32 ServerSplitterDestinationData::GetMixId() const { | ||
| 56 | return mix_id; | ||
| 57 | } | ||
| 58 | |||
| 59 | bool ServerSplitterDestinationData::IsConfigured() const { | ||
| 60 | return in_use && ValidMixId(); | ||
| 61 | } | ||
| 62 | |||
| 63 | float ServerSplitterDestinationData::GetMixVolume(std::size_t i) const { | ||
| 64 | ASSERT(i < AudioCommon::MAX_MIX_BUFFERS); | ||
| 65 | return current_mix_volumes.at(i); | ||
| 66 | } | ||
| 67 | |||
| 68 | const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& | ||
| 69 | ServerSplitterDestinationData::CurrentMixVolumes() const { | ||
| 70 | return current_mix_volumes; | ||
| 71 | } | ||
| 72 | |||
| 73 | const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& | ||
| 74 | ServerSplitterDestinationData::LastMixVolumes() const { | ||
| 75 | return last_mix_volumes; | ||
| 76 | } | ||
| 77 | |||
| 78 | void ServerSplitterDestinationData::MarkDirty() { | ||
| 79 | needs_update = true; | ||
| 80 | } | ||
| 81 | |||
| 82 | void 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 | |||
| 89 | ServerSplitterInfo::ServerSplitterInfo(s32 id_) : id(id_) {} | ||
| 90 | ServerSplitterInfo::~ServerSplitterInfo() = default; | ||
| 91 | |||
| 92 | void ServerSplitterInfo::InitializeInfos() { | ||
| 93 | send_length = 0; | ||
| 94 | head = nullptr; | ||
| 95 | new_connection = true; | ||
| 96 | } | ||
| 97 | |||
| 98 | void ServerSplitterInfo::ClearNewConnectionFlag() { | ||
| 99 | new_connection = false; | ||
| 100 | } | ||
| 101 | |||
| 102 | std::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 | |||
| 114 | ServerSplitterDestinationData* ServerSplitterInfo::GetHead() { | ||
| 115 | return head; | ||
| 116 | } | ||
| 117 | |||
| 118 | const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const { | ||
| 119 | return head; | ||
| 120 | } | ||
| 121 | |||
| 122 | ServerSplitterDestinationData* 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 | |||
| 133 | const 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 | |||
| 144 | bool ServerSplitterInfo::HasNewConnection() const { | ||
| 145 | return new_connection; | ||
| 146 | } | ||
| 147 | |||
| 148 | s32 ServerSplitterInfo::GetLength() const { | ||
| 149 | return send_length; | ||
| 150 | } | ||
| 151 | |||
| 152 | void ServerSplitterInfo::SetHead(ServerSplitterDestinationData* new_head) { | ||
| 153 | head = new_head; | ||
| 154 | } | ||
| 155 | |||
| 156 | void ServerSplitterInfo::SetHeadDepth(s32 length) { | ||
| 157 | send_length = length; | ||
| 158 | } | ||
| 159 | |||
| 160 | SplitterContext::SplitterContext() = default; | ||
| 161 | SplitterContext::~SplitterContext() = default; | ||
| 162 | |||
| 163 | void 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 | |||
| 173 | bool 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 | |||
| 213 | bool SplitterContext::UsingSplitter() const { | ||
| 214 | return info_count > 0 && data_count > 0; | ||
| 215 | } | ||
| 216 | |||
| 217 | ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) { | ||
| 218 | ASSERT(i < info_count); | ||
| 219 | return infos.at(i); | ||
| 220 | } | ||
| 221 | |||
| 222 | const ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) const { | ||
| 223 | ASSERT(i < info_count); | ||
| 224 | return infos.at(i); | ||
| 225 | } | ||
| 226 | |||
| 227 | ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) { | ||
| 228 | ASSERT(i < data_count); | ||
| 229 | return datas.at(i); | ||
| 230 | } | ||
| 231 | |||
| 232 | const ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) const { | ||
| 233 | ASSERT(i < data_count); | ||
| 234 | return datas.at(i); | ||
| 235 | } | ||
| 236 | |||
| 237 | ServerSplitterDestinationData* 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 | |||
| 244 | const 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 | |||
| 251 | void SplitterContext::UpdateInternalState() { | ||
| 252 | if (data_count == 0) { | ||
| 253 | return; | ||
| 254 | } | ||
| 255 | |||
| 256 | for (auto& data : datas) { | ||
| 257 | data.UpdateInternalState(); | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | std::size_t SplitterContext::GetInfoCount() const { | ||
| 262 | return info_count; | ||
| 263 | } | ||
| 264 | |||
| 265 | std::size_t SplitterContext::GetDataCount() const { | ||
| 266 | return data_count; | ||
| 267 | } | ||
| 268 | |||
| 269 | void 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 | |||
| 286 | bool 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 | |||
| 326 | bool 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 | |||
| 359 | bool 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 | |||
| 411 | NodeStates::NodeStates() = default; | ||
| 412 | NodeStates::~NodeStates() = default; | ||
| 413 | |||
| 414 | void 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 | |||
| 423 | bool NodeStates::Tsort(EdgeMatrix& edge_matrix) { | ||
| 424 | return DepthFirstSearch(edge_matrix); | ||
| 425 | } | ||
| 426 | |||
| 427 | std::size_t NodeStates::GetIndexPos() const { | ||
| 428 | return index_pos; | ||
| 429 | } | ||
| 430 | |||
| 431 | const std::vector<s32>& NodeStates::GetIndexList() const { | ||
| 432 | return index_list; | ||
| 433 | } | ||
| 434 | |||
| 435 | void NodeStates::PushTsortResult(s32 index) { | ||
| 436 | ASSERT(index < static_cast<s32>(node_count)); | ||
| 437 | index_list[index_pos++] = index; | ||
| 438 | } | ||
| 439 | |||
| 440 | bool 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 | |||
| 496 | void 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 | |||
| 509 | void 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 | |||
| 526 | NodeStates::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 | |||
| 540 | NodeStates::Stack::Stack() = default; | ||
| 541 | NodeStates::Stack::~Stack() = default; | ||
| 542 | |||
| 543 | void 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 | |||
| 551 | void NodeStates::Stack::push(s32 val) { | ||
| 552 | ASSERT(stack_pos < stack_size); | ||
| 553 | stack[stack_pos++] = val; | ||
| 554 | } | ||
| 555 | |||
| 556 | std::size_t NodeStates::Stack::Count() const { | ||
| 557 | return stack_pos; | ||
| 558 | } | ||
| 559 | |||
| 560 | s32 NodeStates::Stack::top() const { | ||
| 561 | ASSERT(stack_pos > 0); | ||
| 562 | return stack[stack_pos - 1]; | ||
| 563 | } | ||
| 564 | |||
| 565 | s32 NodeStates::Stack::pop() { | ||
| 566 | ASSERT(stack_pos > 0); | ||
| 567 | stack_pos--; | ||
| 568 | return stack[stack_pos]; | ||
| 569 | } | ||
| 570 | |||
| 571 | EdgeMatrix::EdgeMatrix() = default; | ||
| 572 | EdgeMatrix::~EdgeMatrix() = default; | ||
| 573 | |||
| 574 | void EdgeMatrix::Initialize(std::size_t _node_count) { | ||
| 575 | node_count = _node_count; | ||
| 576 | edge_matrix.resize(node_count * node_count); | ||
| 577 | } | ||
| 578 | |||
| 579 | bool EdgeMatrix::Connected(s32 a, s32 b) { | ||
| 580 | return GetState(a, b); | ||
| 581 | } | ||
| 582 | |||
| 583 | void EdgeMatrix::Connect(s32 a, s32 b) { | ||
| 584 | SetState(a, b, true); | ||
| 585 | } | ||
| 586 | |||
| 587 | void EdgeMatrix::Disconnect(s32 a, s32 b) { | ||
| 588 | SetState(a, b, false); | ||
| 589 | } | ||
| 590 | |||
| 591 | void 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 | |||
| 597 | std::size_t EdgeMatrix::GetNodeCount() const { | ||
| 598 | return node_count; | ||
| 599 | } | ||
| 600 | |||
| 601 | void EdgeMatrix::SetState(s32 a, s32 b, bool state) { | ||
| 602 | ASSERT(InRange(a, b)); | ||
| 603 | edge_matrix.at(a * node_count + b) = state; | ||
| 604 | } | ||
| 605 | |||
| 606 | bool EdgeMatrix::GetState(s32 a, s32 b) { | ||
| 607 | ASSERT(InRange(a, b)); | ||
| 608 | return edge_matrix.at(a * node_count + b); | ||
| 609 | } | ||
| 610 | |||
| 611 | bool 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 | ||