From 380658c21d39cf05ac765a9284da246388cca2a4 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Sun, 12 Jul 2020 21:59:14 +1000 Subject: audio_core: Apollo Part 1, AudioRenderer refactor --- src/audio_core/mix_context.cpp | 264 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 src/audio_core/mix_context.cpp (limited to 'src/audio_core/mix_context.cpp') diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp new file mode 100644 index 000000000..8e150db03 --- /dev/null +++ b/src/audio_core/mix_context.cpp @@ -0,0 +1,264 @@ +// 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/common.h" +#include "audio_core/mix_context.h" +#include "audio_core/splitter_context.h" + +namespace AudioCore { +MixContext::MixContext() = default; +MixContext::~MixContext() = default; + +void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count) { + info_count = mix_count; + infos.resize(info_count); + auto& final_mix = GetInfo(AudioCommon::FINAL_MIX); + final_mix.GetInParams().mix_id = AudioCommon::FINAL_MIX; + for (auto& info : infos) { + sorted_info.push_back(&info); + } + + // Only initialize our edge matrix and node states if splitters are supported + if (behavior_info.IsSplitterSupported()) { + node_states.Initialize(mix_count); + edge_matrix.Initialize(mix_count); + } +} + +void MixContext::UpdateDistancesFromFinalMix() { + // Set all distances to be invalid + for (std::size_t i = 0; i < info_count; i++) { + GetInfo(i).GetInParams().final_mix_distance = AudioCommon::NO_FINAL_MIX; + } + + for (std::size_t i = 0; i < info_count; i++) { + auto& info = GetInfo(i); + auto& in_params = info.GetInParams(); + // Populate our sorted info + sorted_info[i] = &info; + + if (!in_params.in_use) { + continue; + } + + auto mix_id = in_params.mix_id; + // Needs to be referenced out of scope + s32 distance_to_final_mix{AudioCommon::FINAL_MIX}; + for (; distance_to_final_mix < info_count; distance_to_final_mix++) { + if (mix_id == AudioCommon::FINAL_MIX) { + // If we're at the final mix, we're done + break; + } else if (mix_id == AudioCommon::NO_MIX) { + // If we have no more mix ids, we're done + distance_to_final_mix = AudioCommon::NO_FINAL_MIX; + break; + } else { + const auto& dest_mix = GetInfo(mix_id); + const auto dest_mix_distance = dest_mix.GetInParams().final_mix_distance; + + if (dest_mix_distance == AudioCommon::NO_FINAL_MIX) { + // If our current mix isn't pointing to a final mix, follow through + mix_id = dest_mix.GetInParams().dest_mix_id; + } else { + // Our current mix + 1 = final distance + distance_to_final_mix = dest_mix_distance + 1; + break; + } + } + } + + // If we're out of range for our distance, mark it as no final mix + if (distance_to_final_mix >= info_count) { + distance_to_final_mix = AudioCommon::NO_FINAL_MIX; + } + + in_params.final_mix_distance = distance_to_final_mix; + } +} + +void MixContext::CalcMixBufferOffset() { + s32 offset{}; + for (std::size_t i = 0; i < info_count; i++) { + auto& info = GetSortedInfo(i); + auto& in_params = info.GetInParams(); + if (in_params.in_use) { + // Only update if in use + in_params.buffer_offset = offset; + offset += in_params.buffer_count; + } + } +} + +void MixContext::SortInfo() { + // Get the distance to the final mix + UpdateDistancesFromFinalMix(); + + // Sort based on the distance to the final mix + std::sort(sorted_info.begin(), sorted_info.end(), + [](const ServerMixInfo* lhs, const ServerMixInfo* rhs) { + return lhs->GetInParams().final_mix_distance > + rhs->GetInParams().final_mix_distance; + }); + + // Calculate the mix buffer offset + CalcMixBufferOffset(); +} + +bool MixContext::TsortInfo(SplitterContext& splitter_context) { + // If we're not using mixes, just calculate the mix buffer offset + if (!splitter_context.UsingSplitter()) { + CalcMixBufferOffset(); + return true; + } + // Sort our node states + if (!node_states.Tsort(edge_matrix)) { + return false; + } + + // Get our sorted list + const auto sorted_list = node_states.GetIndexList(); + std::size_t info_id{}; + for (auto itr = sorted_list.rbegin(); itr != sorted_list.rend(); ++itr) { + // Set our sorted info + sorted_info[info_id++] = &GetInfo(*itr); + } + + // Calculate the mix buffer offset + CalcMixBufferOffset(); + return true; +} + +std::size_t MixContext::GetCount() const { + return info_count; +} + +ServerMixInfo& MixContext::GetInfo(std::size_t i) { + ASSERT(i < info_count); + return infos.at(i); +} + +const ServerMixInfo& MixContext::GetInfo(std::size_t i) const { + ASSERT(i < info_count); + return infos.at(i); +} + +ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) { + ASSERT(i < info_count); + return *sorted_info.at(i); +} + +const ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) const { + ASSERT(i < info_count); + return *sorted_info.at(i); +} + +ServerMixInfo& MixContext::GetFinalMixInfo() { + return infos.at(AudioCommon::FINAL_MIX); +} + +const ServerMixInfo& MixContext::GetFinalMixInfo() const { + return infos.at(AudioCommon::FINAL_MIX); +} + +EdgeMatrix& MixContext::GetEdgeMatrix() { + return edge_matrix; +} + +const EdgeMatrix& MixContext::GetEdgeMatrix() const { + return edge_matrix; +} + +ServerMixInfo::ServerMixInfo() { + Cleanup(); +} +ServerMixInfo::~ServerMixInfo() = default; + +const ServerMixInfo::InParams& ServerMixInfo::GetInParams() const { + return in_params; +} + +ServerMixInfo::InParams& ServerMixInfo::GetInParams() { + return in_params; +} + +bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, + BehaviorInfo& behavior_info, SplitterContext& splitter_context) { + in_params.volume = mix_in.volume; + in_params.sample_rate = mix_in.sample_rate; + in_params.buffer_count = mix_in.buffer_count; + in_params.in_use = mix_in.in_use; + in_params.mix_id = mix_in.mix_id; + in_params.node_id = mix_in.node_id; + for (std::size_t i = 0; i < mix_in.mix_volume.size(); i++) { + std::copy(mix_in.mix_volume[i].begin(), mix_in.mix_volume[i].end(), + in_params.mix_volume[i].begin()); + } + + bool require_sort = false; + + if (behavior_info.IsSplitterSupported()) { + require_sort = UpdateConnection(edge_matrix, mix_in, splitter_context); + } else { + in_params.dest_mix_id = mix_in.dest_mix_id; + in_params.splitter_id = AudioCommon::NO_SPLITTER; + } + + // TODO(ogniK): Update effect processing order + return require_sort; +} + +bool ServerMixInfo::HasAnyConnection() const { + return in_params.splitter_id != AudioCommon::NO_SPLITTER || + in_params.mix_id != AudioCommon::NO_MIX; +} + +void ServerMixInfo::Cleanup() { + in_params.volume = 0.0f; + in_params.sample_rate = 0; + in_params.buffer_count = 0; + in_params.in_use = false; + in_params.mix_id = AudioCommon::NO_MIX; + in_params.node_id = 0; + in_params.buffer_offset = 0; + in_params.dest_mix_id = AudioCommon::NO_MIX; + in_params.splitter_id = AudioCommon::NO_SPLITTER; + std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size()); +} + +bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, + SplitterContext& splitter_context) { + // Mixes are identical + if (in_params.dest_mix_id == mix_in.dest_mix_id && + in_params.splitter_id == mix_in.splitter_id && + ((in_params.splitter_id == AudioCommon::NO_SPLITTER) || + !splitter_context.GetInfo(in_params.splitter_id).HasNewConnection())) { + return false; + } + // Remove current edges for mix id + edge_matrix.RemoveEdges(in_params.mix_id); + if (mix_in.dest_mix_id != AudioCommon::NO_MIX) { + // If we have a valid destination mix id, set our edge matrix + edge_matrix.Connect(in_params.mix_id, mix_in.dest_mix_id); + } else if (mix_in.splitter_id != AudioCommon::NO_SPLITTER) { + // Recurse our splitter linked and set our edges + auto& splitter_info = splitter_context.GetInfo(mix_in.splitter_id); + const auto length = splitter_info.GetLength(); + for (s32 i = 0; i < length; i++) { + const auto* splitter_destination = + splitter_context.GetDestinationData(mix_in.splitter_id, i); + if (splitter_destination == nullptr) { + continue; + } + if (splitter_destination->ValidMixId()) { + edge_matrix.Connect(in_params.mix_id, splitter_destination->GetMixId()); + } + } + } + in_params.dest_mix_id = mix_in.dest_mix_id; + in_params.splitter_id = mix_in.splitter_id; + return true; +} + +} // namespace AudioCore -- cgit v1.2.3 From b924c71822225b6de396b687debb93c1af59e2d6 Mon Sep 17 00:00:00 2001 From: David Marcec Date: Sat, 25 Jul 2020 12:32:05 +1000 Subject: Address issues --- src/audio_core/mix_context.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/audio_core/mix_context.cpp') diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp index 8e150db03..f6f119a11 100644 --- a/src/audio_core/mix_context.cpp +++ b/src/audio_core/mix_context.cpp @@ -16,6 +16,7 @@ void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_c infos.resize(info_count); auto& final_mix = GetInfo(AudioCommon::FINAL_MIX); final_mix.GetInParams().mix_id = AudioCommon::FINAL_MIX; + sorted_info.reserve(infos.size()); for (auto& info : infos) { sorted_info.push_back(&info); } -- cgit v1.2.3 From 80ac1331b545d993aa7c205dc24f8b20a4d6d44e Mon Sep 17 00:00:00 2001 From: David Marcec Date: Mon, 17 Aug 2020 01:23:55 +1000 Subject: Preliminary effects --- src/audio_core/mix_context.cpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) (limited to 'src/audio_core/mix_context.cpp') diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp index f6f119a11..042891490 100644 --- a/src/audio_core/mix_context.cpp +++ b/src/audio_core/mix_context.cpp @@ -4,6 +4,7 @@ #include "audio_core/behavior_info.h" #include "audio_core/common.h" +#include "audio_core/effect_context.h" #include "audio_core/mix_context.h" #include "audio_core/splitter_context.h" @@ -11,7 +12,8 @@ namespace AudioCore { MixContext::MixContext() = default; MixContext::~MixContext() = default; -void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count) { +void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count, + std::size_t effect_count) { info_count = mix_count; infos.resize(info_count); auto& final_mix = GetInfo(AudioCommon::FINAL_MIX); @@ -21,6 +23,10 @@ void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_c sorted_info.push_back(&info); } + for (auto& info : infos) { + info.SetEffectCount(effect_count); + } + // Only initialize our edge matrix and node states if splitters are supported if (behavior_info.IsSplitterSupported()) { node_states.Initialize(mix_count); @@ -185,7 +191,8 @@ ServerMixInfo::InParams& ServerMixInfo::GetInParams() { } bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, - BehaviorInfo& behavior_info, SplitterContext& splitter_context) { + BehaviorInfo& behavior_info, SplitterContext& splitter_context, + EffectContext& effect_context) { in_params.volume = mix_in.volume; in_params.sample_rate = mix_in.sample_rate; in_params.buffer_count = mix_in.buffer_count; @@ -206,6 +213,15 @@ bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix in_params.splitter_id = AudioCommon::NO_SPLITTER; } + ResetEffectProcessingOrder(); + const auto effect_count = effect_context.GetCount(); + for (std::size_t i = 0; i < effect_count; i++) { + auto* effect_info = effect_context.GetInfo(i); + if (effect_info->GetMixID() == in_params.mix_id) { + effect_processing_order[effect_info->GetProcessingOrder()] = static_cast(i); + } + } + // TODO(ogniK): Update effect processing order return require_sort; } @@ -228,6 +244,21 @@ void ServerMixInfo::Cleanup() { std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size()); } +void ServerMixInfo::SetEffectCount(std::size_t count) { + effect_processing_order.resize(count); + ResetEffectProcessingOrder(); +} + +void ServerMixInfo::ResetEffectProcessingOrder() { + for (auto& order : effect_processing_order) { + order = AudioCommon::NO_EFFECT_ORDER; + } +} + +s32 ServerMixInfo::GetEffectOrder(std::size_t i) const { + return effect_processing_order.at(i); +} + bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, SplitterContext& splitter_context) { // Mixes are identical -- cgit v1.2.3