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