| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- // Copyright 2020 yuzu Emulator Project
- // Licensed under GPLv2 or any later version
- // Refer to the license.txt file included.
- #include "audio_core/behavior_info.h"
- #include "audio_core/splitter_context.h"
- #include "common/alignment.h"
- #include "common/assert.h"
- #include "common/logging/log.h"
- namespace AudioCore {
- ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id) : id(id) {}
- ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
- void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
- // Log error as these are not actually failure states
- if (header.magic != SplitterMagic::DataHeader) {
- LOG_ERROR(Audio, "Splitter destination header is invalid!");
- return;
- }
- // Incorrect splitter id
- if (header.splitter_id != id) {
- LOG_ERROR(Audio, "Splitter destination ids do not match!");
- return;
- }
- mix_id = header.mix_id;
- // Copy our mix volumes
- std::copy(header.mix_volumes.begin(), header.mix_volumes.end(), current_mix_volumes.begin());
- if (!in_use && header.in_use) {
- // Update mix volumes
- std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
- needs_update = false;
- }
- in_use = header.in_use;
- }
- ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() {
- return next;
- }
- const ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() const {
- return next;
- }
- void ServerSplitterDestinationData::SetNextDestination(ServerSplitterDestinationData* dest) {
- next = dest;
- }
- bool ServerSplitterDestinationData::ValidMixId() const {
- return GetMixId() != AudioCommon::NO_MIX;
- }
- s32 ServerSplitterDestinationData::GetMixId() const {
- return mix_id;
- }
- bool ServerSplitterDestinationData::IsConfigured() const {
- return in_use && ValidMixId();
- }
- float ServerSplitterDestinationData::GetMixVolume(std::size_t i) const {
- ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
- return current_mix_volumes.at(i);
- }
- const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
- ServerSplitterDestinationData::CurrentMixVolumes() const {
- return current_mix_volumes;
- }
- const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
- ServerSplitterDestinationData::LastMixVolumes() const {
- return last_mix_volumes;
- }
- void ServerSplitterDestinationData::MarkDirty() {
- needs_update = true;
- }
- void ServerSplitterDestinationData::UpdateInternalState() {
- if (in_use && needs_update) {
- std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
- }
- needs_update = false;
- }
- ServerSplitterInfo::ServerSplitterInfo(s32 id) : id(id) {}
- ServerSplitterInfo::~ServerSplitterInfo() = default;
- void ServerSplitterInfo::InitializeInfos() {
- send_length = 0;
- head = nullptr;
- new_connection = true;
- }
- void ServerSplitterInfo::ClearNewConnectionFlag() {
- new_connection = false;
- }
- std::size_t ServerSplitterInfo::Update(SplitterInfo::InInfoPrams& header) {
- if (header.send_id != id) {
- return 0;
- }
- sample_rate = header.sample_rate;
- new_connection = true;
- // We need to update the size here due to the splitter bug being present and providing an
- // incorrect size. We're suppose to also update the header here but we just ignore and continue
- return (sizeof(s32_le) * (header.length - 1)) + (sizeof(s32_le) * 3);
- }
- ServerSplitterDestinationData* ServerSplitterInfo::GetHead() {
- return head;
- }
- const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const {
- return head;
- }
- ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
- auto current_head = head;
- for (std::size_t i = 0; i < depth; i++) {
- if (current_head == nullptr) {
- return nullptr;
- }
- current_head = current_head->GetNextDestination();
- }
- return current_head;
- }
- const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
- auto current_head = head;
- for (std::size_t i = 0; i < depth; i++) {
- if (current_head == nullptr) {
- return nullptr;
- }
- current_head = current_head->GetNextDestination();
- }
- return current_head;
- }
- bool ServerSplitterInfo::HasNewConnection() const {
- return new_connection;
- }
- s32 ServerSplitterInfo::GetLength() const {
- return send_length;
- }
- void ServerSplitterInfo::SetHead(ServerSplitterDestinationData* new_head) {
- head = new_head;
- }
- void ServerSplitterInfo::SetHeadDepth(s32 length) {
- send_length = length;
- }
- SplitterContext::SplitterContext() = default;
- SplitterContext::~SplitterContext() = default;
- void SplitterContext::Initialize(BehaviorInfo& behavior_info, std::size_t _info_count,
- std::size_t _data_count) {
- if (!behavior_info.IsSplitterSupported() || _data_count == 0 || _info_count == 0) {
- Setup(0, 0, false);
- return;
- }
- // Only initialize if we're using splitters
- Setup(_info_count, _data_count, behavior_info.IsSplitterBugFixed());
- }
- bool SplitterContext::Update(const std::vector<u8>& input, std::size_t& input_offset,
- std::size_t& bytes_read) {
- const auto UpdateOffsets = [&](std::size_t read) {
- input_offset += read;
- bytes_read += read;
- };
- if (info_count == 0 || data_count == 0) {
- bytes_read = 0;
- return true;
- }
- if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
- sizeof(SplitterInfo::InHeader))) {
- LOG_ERROR(Audio, "Buffer is an invalid size!");
- return false;
- }
- SplitterInfo::InHeader header{};
- std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InHeader));
- UpdateOffsets(sizeof(SplitterInfo::InHeader));
- if (header.magic != SplitterMagic::SplitterHeader) {
- LOG_ERROR(Audio, "Invalid header magic! Expecting {:X} but got {:X}",
- SplitterMagic::SplitterHeader, header.magic);
- return false;
- }
- // Clear all connections
- for (auto& info : infos) {
- info.ClearNewConnectionFlag();
- }
- UpdateInfo(input, input_offset, bytes_read, header.info_count);
- UpdateData(input, input_offset, bytes_read, header.data_count);
- const auto aligned_bytes_read = Common::AlignUp(bytes_read, 16);
- input_offset += aligned_bytes_read - bytes_read;
- bytes_read = aligned_bytes_read;
- return true;
- }
- bool SplitterContext::UsingSplitter() const {
- return info_count > 0 && data_count > 0;
- }
- ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) {
- ASSERT(i < info_count);
- return infos.at(i);
- }
- const ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) const {
- ASSERT(i < info_count);
- return infos.at(i);
- }
- ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) {
- ASSERT(i < data_count);
- return datas.at(i);
- }
- const ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) const {
- ASSERT(i < data_count);
- return datas.at(i);
- }
- ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
- std::size_t data) {
- ASSERT(info < info_count);
- auto& cur_info = GetInfo(info);
- return cur_info.GetData(data);
- }
- const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
- std::size_t data) const {
- ASSERT(info < info_count);
- auto& cur_info = GetInfo(info);
- return cur_info.GetData(data);
- }
- void SplitterContext::UpdateInternalState() {
- if (data_count == 0) {
- return;
- }
- for (auto& data : datas) {
- data.UpdateInternalState();
- }
- }
- std::size_t SplitterContext::GetInfoCount() const {
- return info_count;
- }
- std::size_t SplitterContext::GetDataCount() const {
- return data_count;
- }
- void SplitterContext::Setup(std::size_t _info_count, std::size_t _data_count,
- bool is_splitter_bug_fixed) {
- info_count = _info_count;
- data_count = _data_count;
- for (std::size_t i = 0; i < info_count; i++) {
- auto& splitter = infos.emplace_back(static_cast<s32>(i));
- splitter.InitializeInfos();
- }
- for (std::size_t i = 0; i < data_count; i++) {
- datas.emplace_back(static_cast<s32>(i));
- }
- bug_fixed = is_splitter_bug_fixed;
- }
- bool SplitterContext::UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
- std::size_t& bytes_read, s32 in_splitter_count) {
- const auto UpdateOffsets = [&](std::size_t read) {
- input_offset += read;
- bytes_read += read;
- };
- for (s32 i = 0; i < in_splitter_count; i++) {
- if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
- sizeof(SplitterInfo::InInfoPrams))) {
- LOG_ERROR(Audio, "Buffer is an invalid size!");
- return false;
- }
- SplitterInfo::InInfoPrams header{};
- std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InInfoPrams));
- // Logged as warning as these don't actually cause a bailout for some reason
- if (header.magic != SplitterMagic::InfoHeader) {
- LOG_ERROR(Audio, "Bad splitter data header");
- break;
- }
- if (header.send_id < 0 || static_cast<std::size_t>(header.send_id) > info_count) {
- LOG_ERROR(Audio, "Bad splitter data id");
- break;
- }
- UpdateOffsets(sizeof(SplitterInfo::InInfoPrams));
- auto& info = GetInfo(header.send_id);
- if (!RecomposeDestination(info, header, input, input_offset)) {
- LOG_ERROR(Audio, "Failed to recompose destination for splitter!");
- return false;
- }
- const std::size_t read = info.Update(header);
- bytes_read += read;
- input_offset += read;
- }
- return true;
- }
- bool SplitterContext::UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
- std::size_t& bytes_read, s32 in_data_count) {
- const auto UpdateOffsets = [&](std::size_t read) {
- input_offset += read;
- bytes_read += read;
- };
- for (s32 i = 0; i < in_data_count; i++) {
- if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
- sizeof(SplitterInfo::InDestinationParams))) {
- LOG_ERROR(Audio, "Buffer is an invalid size!");
- return false;
- }
- SplitterInfo::InDestinationParams header{};
- std::memcpy(&header, input.data() + input_offset,
- sizeof(SplitterInfo::InDestinationParams));
- UpdateOffsets(sizeof(SplitterInfo::InDestinationParams));
- // Logged as warning as these don't actually cause a bailout for some reason
- if (header.magic != SplitterMagic::DataHeader) {
- LOG_ERROR(Audio, "Bad splitter data header");
- break;
- }
- if (header.splitter_id < 0 || static_cast<std::size_t>(header.splitter_id) > data_count) {
- LOG_ERROR(Audio, "Bad splitter data id");
- break;
- }
- GetData(header.splitter_id).Update(header);
- }
- return true;
- }
- bool SplitterContext::RecomposeDestination(ServerSplitterInfo& info,
- SplitterInfo::InInfoPrams& header,
- const std::vector<u8>& input,
- const std::size_t& input_offset) {
- // Clear our current destinations
- auto* current_head = info.GetHead();
- while (current_head != nullptr) {
- auto next_head = current_head->GetNextDestination();
- current_head->SetNextDestination(nullptr);
- current_head = next_head;
- }
- info.SetHead(nullptr);
- s32 size = header.length;
- // If the splitter bug is present, calculate fixed size
- if (!bug_fixed) {
- if (info_count > 0) {
- const auto factor = data_count / info_count;
- size = std::min(header.length, static_cast<s32>(factor));
- } else {
- size = 0;
- }
- }
- if (size < 1) {
- LOG_ERROR(Audio, "Invalid splitter info size! size={:X}", size);
- return true;
- }
- auto* start_head = &GetData(header.resource_id_base);
- current_head = start_head;
- std::vector<s32_le> resource_ids(size - 1);
- if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
- resource_ids.size() * sizeof(s32_le))) {
- LOG_ERROR(Audio, "Buffer is an invalid size!");
- return false;
- }
- std::memcpy(resource_ids.data(), input.data() + input_offset,
- resource_ids.size() * sizeof(s32_le));
- for (auto resource_id : resource_ids) {
- auto* head = &GetData(resource_id);
- current_head->SetNextDestination(head);
- current_head = head;
- }
- info.SetHead(start_head);
- info.SetHeadDepth(size);
- return true;
- }
- NodeStates::NodeStates() = default;
- NodeStates::~NodeStates() = default;
- void NodeStates::Initialize(std::size_t node_count_) {
- // Setup our work parameters
- node_count = node_count_;
- was_node_found.resize(node_count);
- was_node_completed.resize(node_count);
- index_list.resize(node_count);
- index_stack.Reset(node_count * node_count);
- }
- bool NodeStates::Tsort(EdgeMatrix& edge_matrix) {
- return DepthFirstSearch(edge_matrix);
- }
- std::size_t NodeStates::GetIndexPos() const {
- return index_pos;
- }
- const std::vector<s32>& NodeStates::GetIndexList() const {
- return index_list;
- }
- void NodeStates::PushTsortResult(s32 index) {
- ASSERT(index < static_cast<s32>(node_count));
- index_list[index_pos++] = index;
- }
- bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
- ResetState();
- for (std::size_t i = 0; i < node_count; i++) {
- const auto node_id = static_cast<s32>(i);
- // If we don't have a state, send to our index stack for work
- if (GetState(i) == NodeStates::State::NoState) {
- index_stack.push(node_id);
- }
- // While we have work to do in our stack
- while (index_stack.Count() > 0) {
- // Get the current node
- const auto current_stack_index = index_stack.top();
- // Check if we've seen the node yet
- const auto index_state = GetState(current_stack_index);
- if (index_state == NodeStates::State::NoState) {
- // Mark the node as seen
- UpdateState(NodeStates::State::InFound, current_stack_index);
- } else if (index_state == NodeStates::State::InFound) {
- // We've seen this node before, mark it as completed
- UpdateState(NodeStates::State::InCompleted, current_stack_index);
- // Update our index list
- PushTsortResult(current_stack_index);
- // Pop the stack
- index_stack.pop();
- continue;
- } else if (index_state == NodeStates::State::InCompleted) {
- // If our node is already sorted, clear it
- index_stack.pop();
- continue;
- }
- const auto node_count = edge_matrix.GetNodeCount();
- for (s32 j = 0; j < static_cast<s32>(node_count); j++) {
- // Check if our node is connected to our edge matrix
- if (!edge_matrix.Connected(current_stack_index, j)) {
- continue;
- }
- // Check if our node exists
- const auto node_state = GetState(j);
- if (node_state == NodeStates::State::NoState) {
- // Add more work
- index_stack.push(j);
- } else if (node_state == NodeStates::State::InFound) {
- UNREACHABLE_MSG("Node start marked as found");
- ResetState();
- return false;
- }
- }
- }
- }
- return true;
- }
- void NodeStates::ResetState() {
- // Reset to the start of our index stack
- index_pos = 0;
- for (std::size_t i = 0; i < node_count; i++) {
- // Mark all nodes as not found
- was_node_found[i] = false;
- // Mark all nodes as uncompleted
- was_node_completed[i] = false;
- // Mark all indexes as invalid
- index_list[i] = -1;
- }
- }
- void NodeStates::UpdateState(NodeStates::State state, std::size_t i) {
- switch (state) {
- case NodeStates::State::NoState:
- was_node_found[i] = false;
- was_node_completed[i] = false;
- break;
- case NodeStates::State::InFound:
- was_node_found[i] = true;
- was_node_completed[i] = false;
- break;
- case NodeStates::State::InCompleted:
- was_node_found[i] = false;
- was_node_completed[i] = true;
- break;
- }
- }
- NodeStates::State NodeStates::GetState(std::size_t i) {
- ASSERT(i < node_count);
- if (was_node_found[i]) {
- // If our node exists in our found list
- return NodeStates::State::InFound;
- } else if (was_node_completed[i]) {
- // If node is in the completed list
- return NodeStates::State::InCompleted;
- } else {
- // If in neither
- return NodeStates::State::NoState;
- }
- }
- NodeStates::Stack::Stack() = default;
- NodeStates::Stack::~Stack() = default;
- void NodeStates::Stack::Reset(std::size_t size) {
- // Mark our stack as empty
- stack.resize(size);
- stack_size = size;
- stack_pos = 0;
- std::fill(stack.begin(), stack.end(), 0);
- }
- void NodeStates::Stack::push(s32 val) {
- ASSERT(stack_pos < stack_size);
- stack[stack_pos++] = val;
- }
- std::size_t NodeStates::Stack::Count() const {
- return stack_pos;
- }
- s32 NodeStates::Stack::top() const {
- ASSERT(stack_pos > 0);
- return stack[stack_pos - 1];
- }
- s32 NodeStates::Stack::pop() {
- ASSERT(stack_pos > 0);
- stack_pos--;
- return stack[stack_pos];
- }
- EdgeMatrix::EdgeMatrix() = default;
- EdgeMatrix::~EdgeMatrix() = default;
- void EdgeMatrix::Initialize(std::size_t _node_count) {
- node_count = _node_count;
- edge_matrix.resize(node_count * node_count);
- }
- bool EdgeMatrix::Connected(s32 a, s32 b) {
- return GetState(a, b);
- }
- void EdgeMatrix::Connect(s32 a, s32 b) {
- SetState(a, b, true);
- }
- void EdgeMatrix::Disconnect(s32 a, s32 b) {
- SetState(a, b, false);
- }
- void EdgeMatrix::RemoveEdges(s32 edge) {
- for (std::size_t i = 0; i < node_count; i++) {
- SetState(edge, static_cast<s32>(i), false);
- }
- }
- std::size_t EdgeMatrix::GetNodeCount() const {
- return node_count;
- }
- void EdgeMatrix::SetState(s32 a, s32 b, bool state) {
- ASSERT(InRange(a, b));
- edge_matrix.at(a * node_count + b) = state;
- }
- bool EdgeMatrix::GetState(s32 a, s32 b) {
- ASSERT(InRange(a, b));
- return edge_matrix.at(a * node_count + b);
- }
- bool EdgeMatrix::InRange(s32 a, s32 b) const {
- const std::size_t pos = a * node_count + b;
- return pos < (node_count * node_count);
- }
- } // namespace AudioCore
|