diff options
| author | 2020-09-11 10:57:27 -0400 | |
|---|---|---|
| committer | 2020-09-11 10:57:27 -0400 | |
| commit | 324029d4f9fd2381f474e608a2859360324161e5 (patch) | |
| tree | d2dc348235f05f20686c526f7092590f596f65c2 /src/audio_core/mix_context.cpp | |
| parent | Merge pull request #4597 from Morph1984/mjolnir-p2 (diff) | |
| parent | Preliminary effects (diff) | |
| download | yuzu-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/mix_context.cpp')
| -rw-r--r-- | src/audio_core/mix_context.cpp | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp new file mode 100644 index 000000000..042891490 --- /dev/null +++ b/src/audio_core/mix_context.cpp | |||
| @@ -0,0 +1,296 @@ | |||
| 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/common.h" | ||
| 7 | #include "audio_core/effect_context.h" | ||
| 8 | #include "audio_core/mix_context.h" | ||
| 9 | #include "audio_core/splitter_context.h" | ||
| 10 | |||
| 11 | namespace AudioCore { | ||
| 12 | MixContext::MixContext() = default; | ||
| 13 | MixContext::~MixContext() = default; | ||
| 14 | |||
| 15 | void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count, | ||
| 16 | std::size_t effect_count) { | ||
| 17 | info_count = mix_count; | ||
| 18 | infos.resize(info_count); | ||
| 19 | auto& final_mix = GetInfo(AudioCommon::FINAL_MIX); | ||
| 20 | final_mix.GetInParams().mix_id = AudioCommon::FINAL_MIX; | ||
| 21 | sorted_info.reserve(infos.size()); | ||
| 22 | for (auto& info : infos) { | ||
| 23 | sorted_info.push_back(&info); | ||
| 24 | } | ||
| 25 | |||
| 26 | for (auto& info : infos) { | ||
| 27 | info.SetEffectCount(effect_count); | ||
| 28 | } | ||
| 29 | |||
| 30 | // Only initialize our edge matrix and node states if splitters are supported | ||
| 31 | if (behavior_info.IsSplitterSupported()) { | ||
| 32 | node_states.Initialize(mix_count); | ||
| 33 | edge_matrix.Initialize(mix_count); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | void MixContext::UpdateDistancesFromFinalMix() { | ||
| 38 | // Set all distances to be invalid | ||
| 39 | for (std::size_t i = 0; i < info_count; i++) { | ||
| 40 | GetInfo(i).GetInParams().final_mix_distance = AudioCommon::NO_FINAL_MIX; | ||
| 41 | } | ||
| 42 | |||
| 43 | for (std::size_t i = 0; i < info_count; i++) { | ||
| 44 | auto& info = GetInfo(i); | ||
| 45 | auto& in_params = info.GetInParams(); | ||
| 46 | // Populate our sorted info | ||
| 47 | sorted_info[i] = &info; | ||
| 48 | |||
| 49 | if (!in_params.in_use) { | ||
| 50 | continue; | ||
| 51 | } | ||
| 52 | |||
| 53 | auto mix_id = in_params.mix_id; | ||
| 54 | // Needs to be referenced out of scope | ||
| 55 | s32 distance_to_final_mix{AudioCommon::FINAL_MIX}; | ||
| 56 | for (; distance_to_final_mix < info_count; distance_to_final_mix++) { | ||
| 57 | if (mix_id == AudioCommon::FINAL_MIX) { | ||
| 58 | // If we're at the final mix, we're done | ||
| 59 | break; | ||
| 60 | } else if (mix_id == AudioCommon::NO_MIX) { | ||
| 61 | // If we have no more mix ids, we're done | ||
| 62 | distance_to_final_mix = AudioCommon::NO_FINAL_MIX; | ||
| 63 | break; | ||
| 64 | } else { | ||
| 65 | const auto& dest_mix = GetInfo(mix_id); | ||
| 66 | const auto dest_mix_distance = dest_mix.GetInParams().final_mix_distance; | ||
| 67 | |||
| 68 | if (dest_mix_distance == AudioCommon::NO_FINAL_MIX) { | ||
| 69 | // If our current mix isn't pointing to a final mix, follow through | ||
| 70 | mix_id = dest_mix.GetInParams().dest_mix_id; | ||
| 71 | } else { | ||
| 72 | // Our current mix + 1 = final distance | ||
| 73 | distance_to_final_mix = dest_mix_distance + 1; | ||
| 74 | break; | ||
| 75 | } | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | // If we're out of range for our distance, mark it as no final mix | ||
| 80 | if (distance_to_final_mix >= info_count) { | ||
| 81 | distance_to_final_mix = AudioCommon::NO_FINAL_MIX; | ||
| 82 | } | ||
| 83 | |||
| 84 | in_params.final_mix_distance = distance_to_final_mix; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | void MixContext::CalcMixBufferOffset() { | ||
| 89 | s32 offset{}; | ||
| 90 | for (std::size_t i = 0; i < info_count; i++) { | ||
| 91 | auto& info = GetSortedInfo(i); | ||
| 92 | auto& in_params = info.GetInParams(); | ||
| 93 | if (in_params.in_use) { | ||
| 94 | // Only update if in use | ||
| 95 | in_params.buffer_offset = offset; | ||
| 96 | offset += in_params.buffer_count; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | void MixContext::SortInfo() { | ||
| 102 | // Get the distance to the final mix | ||
| 103 | UpdateDistancesFromFinalMix(); | ||
| 104 | |||
| 105 | // Sort based on the distance to the final mix | ||
| 106 | std::sort(sorted_info.begin(), sorted_info.end(), | ||
| 107 | [](const ServerMixInfo* lhs, const ServerMixInfo* rhs) { | ||
| 108 | return lhs->GetInParams().final_mix_distance > | ||
| 109 | rhs->GetInParams().final_mix_distance; | ||
| 110 | }); | ||
| 111 | |||
| 112 | // Calculate the mix buffer offset | ||
| 113 | CalcMixBufferOffset(); | ||
| 114 | } | ||
| 115 | |||
| 116 | bool MixContext::TsortInfo(SplitterContext& splitter_context) { | ||
| 117 | // If we're not using mixes, just calculate the mix buffer offset | ||
| 118 | if (!splitter_context.UsingSplitter()) { | ||
| 119 | CalcMixBufferOffset(); | ||
| 120 | return true; | ||
| 121 | } | ||
| 122 | // Sort our node states | ||
| 123 | if (!node_states.Tsort(edge_matrix)) { | ||
| 124 | return false; | ||
| 125 | } | ||
| 126 | |||
| 127 | // Get our sorted list | ||
| 128 | const auto sorted_list = node_states.GetIndexList(); | ||
| 129 | std::size_t info_id{}; | ||
| 130 | for (auto itr = sorted_list.rbegin(); itr != sorted_list.rend(); ++itr) { | ||
| 131 | // Set our sorted info | ||
| 132 | sorted_info[info_id++] = &GetInfo(*itr); | ||
| 133 | } | ||
| 134 | |||
| 135 | // Calculate the mix buffer offset | ||
| 136 | CalcMixBufferOffset(); | ||
| 137 | return true; | ||
| 138 | } | ||
| 139 | |||
| 140 | std::size_t MixContext::GetCount() const { | ||
| 141 | return info_count; | ||
| 142 | } | ||
| 143 | |||
| 144 | ServerMixInfo& MixContext::GetInfo(std::size_t i) { | ||
| 145 | ASSERT(i < info_count); | ||
| 146 | return infos.at(i); | ||
| 147 | } | ||
| 148 | |||
| 149 | const ServerMixInfo& MixContext::GetInfo(std::size_t i) const { | ||
| 150 | ASSERT(i < info_count); | ||
| 151 | return infos.at(i); | ||
| 152 | } | ||
| 153 | |||
| 154 | ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) { | ||
| 155 | ASSERT(i < info_count); | ||
| 156 | return *sorted_info.at(i); | ||
| 157 | } | ||
| 158 | |||
| 159 | const ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) const { | ||
| 160 | ASSERT(i < info_count); | ||
| 161 | return *sorted_info.at(i); | ||
| 162 | } | ||
| 163 | |||
| 164 | ServerMixInfo& MixContext::GetFinalMixInfo() { | ||
| 165 | return infos.at(AudioCommon::FINAL_MIX); | ||
| 166 | } | ||
| 167 | |||
| 168 | const ServerMixInfo& MixContext::GetFinalMixInfo() const { | ||
| 169 | return infos.at(AudioCommon::FINAL_MIX); | ||
| 170 | } | ||
| 171 | |||
| 172 | EdgeMatrix& MixContext::GetEdgeMatrix() { | ||
| 173 | return edge_matrix; | ||
| 174 | } | ||
| 175 | |||
| 176 | const EdgeMatrix& MixContext::GetEdgeMatrix() const { | ||
| 177 | return edge_matrix; | ||
| 178 | } | ||
| 179 | |||
| 180 | ServerMixInfo::ServerMixInfo() { | ||
| 181 | Cleanup(); | ||
| 182 | } | ||
| 183 | ServerMixInfo::~ServerMixInfo() = default; | ||
| 184 | |||
| 185 | const ServerMixInfo::InParams& ServerMixInfo::GetInParams() const { | ||
| 186 | return in_params; | ||
| 187 | } | ||
| 188 | |||
| 189 | ServerMixInfo::InParams& ServerMixInfo::GetInParams() { | ||
| 190 | return in_params; | ||
| 191 | } | ||
| 192 | |||
| 193 | bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, | ||
| 194 | BehaviorInfo& behavior_info, SplitterContext& splitter_context, | ||
| 195 | EffectContext& effect_context) { | ||
| 196 | in_params.volume = mix_in.volume; | ||
| 197 | in_params.sample_rate = mix_in.sample_rate; | ||
| 198 | in_params.buffer_count = mix_in.buffer_count; | ||
| 199 | in_params.in_use = mix_in.in_use; | ||
| 200 | in_params.mix_id = mix_in.mix_id; | ||
| 201 | in_params.node_id = mix_in.node_id; | ||
| 202 | for (std::size_t i = 0; i < mix_in.mix_volume.size(); i++) { | ||
| 203 | std::copy(mix_in.mix_volume[i].begin(), mix_in.mix_volume[i].end(), | ||
| 204 | in_params.mix_volume[i].begin()); | ||
| 205 | } | ||
| 206 | |||
| 207 | bool require_sort = false; | ||
| 208 | |||
| 209 | if (behavior_info.IsSplitterSupported()) { | ||
| 210 | require_sort = UpdateConnection(edge_matrix, mix_in, splitter_context); | ||
| 211 | } else { | ||
| 212 | in_params.dest_mix_id = mix_in.dest_mix_id; | ||
| 213 | in_params.splitter_id = AudioCommon::NO_SPLITTER; | ||
| 214 | } | ||
| 215 | |||
| 216 | ResetEffectProcessingOrder(); | ||
| 217 | const auto effect_count = effect_context.GetCount(); | ||
| 218 | for (std::size_t i = 0; i < effect_count; i++) { | ||
| 219 | auto* effect_info = effect_context.GetInfo(i); | ||
| 220 | if (effect_info->GetMixID() == in_params.mix_id) { | ||
| 221 | effect_processing_order[effect_info->GetProcessingOrder()] = static_cast<s32>(i); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | // TODO(ogniK): Update effect processing order | ||
| 226 | return require_sort; | ||
| 227 | } | ||
| 228 | |||
| 229 | bool ServerMixInfo::HasAnyConnection() const { | ||
| 230 | return in_params.splitter_id != AudioCommon::NO_SPLITTER || | ||
| 231 | in_params.mix_id != AudioCommon::NO_MIX; | ||
| 232 | } | ||
| 233 | |||
| 234 | void ServerMixInfo::Cleanup() { | ||
| 235 | in_params.volume = 0.0f; | ||
| 236 | in_params.sample_rate = 0; | ||
| 237 | in_params.buffer_count = 0; | ||
| 238 | in_params.in_use = false; | ||
| 239 | in_params.mix_id = AudioCommon::NO_MIX; | ||
| 240 | in_params.node_id = 0; | ||
| 241 | in_params.buffer_offset = 0; | ||
| 242 | in_params.dest_mix_id = AudioCommon::NO_MIX; | ||
| 243 | in_params.splitter_id = AudioCommon::NO_SPLITTER; | ||
| 244 | std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size()); | ||
| 245 | } | ||
| 246 | |||
| 247 | void ServerMixInfo::SetEffectCount(std::size_t count) { | ||
| 248 | effect_processing_order.resize(count); | ||
| 249 | ResetEffectProcessingOrder(); | ||
| 250 | } | ||
| 251 | |||
| 252 | void ServerMixInfo::ResetEffectProcessingOrder() { | ||
| 253 | for (auto& order : effect_processing_order) { | ||
| 254 | order = AudioCommon::NO_EFFECT_ORDER; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | s32 ServerMixInfo::GetEffectOrder(std::size_t i) const { | ||
| 259 | return effect_processing_order.at(i); | ||
| 260 | } | ||
| 261 | |||
| 262 | bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, | ||
| 263 | SplitterContext& splitter_context) { | ||
| 264 | // Mixes are identical | ||
| 265 | if (in_params.dest_mix_id == mix_in.dest_mix_id && | ||
| 266 | in_params.splitter_id == mix_in.splitter_id && | ||
| 267 | ((in_params.splitter_id == AudioCommon::NO_SPLITTER) || | ||
| 268 | !splitter_context.GetInfo(in_params.splitter_id).HasNewConnection())) { | ||
| 269 | return false; | ||
| 270 | } | ||
| 271 | // Remove current edges for mix id | ||
| 272 | edge_matrix.RemoveEdges(in_params.mix_id); | ||
| 273 | if (mix_in.dest_mix_id != AudioCommon::NO_MIX) { | ||
| 274 | // If we have a valid destination mix id, set our edge matrix | ||
| 275 | edge_matrix.Connect(in_params.mix_id, mix_in.dest_mix_id); | ||
| 276 | } else if (mix_in.splitter_id != AudioCommon::NO_SPLITTER) { | ||
| 277 | // Recurse our splitter linked and set our edges | ||
| 278 | auto& splitter_info = splitter_context.GetInfo(mix_in.splitter_id); | ||
| 279 | const auto length = splitter_info.GetLength(); | ||
| 280 | for (s32 i = 0; i < length; i++) { | ||
| 281 | const auto* splitter_destination = | ||
| 282 | splitter_context.GetDestinationData(mix_in.splitter_id, i); | ||
| 283 | if (splitter_destination == nullptr) { | ||
| 284 | continue; | ||
| 285 | } | ||
| 286 | if (splitter_destination->ValidMixId()) { | ||
| 287 | edge_matrix.Connect(in_params.mix_id, splitter_destination->GetMixId()); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | in_params.dest_mix_id = mix_in.dest_mix_id; | ||
| 292 | in_params.splitter_id = mix_in.splitter_id; | ||
| 293 | return true; | ||
| 294 | } | ||
| 295 | |||
| 296 | } // namespace AudioCore | ||