summaryrefslogtreecommitdiff
path: root/src/audio_core/mix_context.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2020-09-11 10:57:27 -0400
committerGravatar GitHub2020-09-11 10:57:27 -0400
commit324029d4f9fd2381f474e608a2859360324161e5 (patch)
treed2dc348235f05f20686c526f7092590f596f65c2 /src/audio_core/mix_context.cpp
parentMerge pull request #4597 from Morph1984/mjolnir-p2 (diff)
parentPreliminary effects (diff)
downloadyuzu-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.cpp296
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
11namespace AudioCore {
12MixContext::MixContext() = default;
13MixContext::~MixContext() = default;
14
15void 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
37void 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
88void 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
101void 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
116bool 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
140std::size_t MixContext::GetCount() const {
141 return info_count;
142}
143
144ServerMixInfo& MixContext::GetInfo(std::size_t i) {
145 ASSERT(i < info_count);
146 return infos.at(i);
147}
148
149const ServerMixInfo& MixContext::GetInfo(std::size_t i) const {
150 ASSERT(i < info_count);
151 return infos.at(i);
152}
153
154ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) {
155 ASSERT(i < info_count);
156 return *sorted_info.at(i);
157}
158
159const ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) const {
160 ASSERT(i < info_count);
161 return *sorted_info.at(i);
162}
163
164ServerMixInfo& MixContext::GetFinalMixInfo() {
165 return infos.at(AudioCommon::FINAL_MIX);
166}
167
168const ServerMixInfo& MixContext::GetFinalMixInfo() const {
169 return infos.at(AudioCommon::FINAL_MIX);
170}
171
172EdgeMatrix& MixContext::GetEdgeMatrix() {
173 return edge_matrix;
174}
175
176const EdgeMatrix& MixContext::GetEdgeMatrix() const {
177 return edge_matrix;
178}
179
180ServerMixInfo::ServerMixInfo() {
181 Cleanup();
182}
183ServerMixInfo::~ServerMixInfo() = default;
184
185const ServerMixInfo::InParams& ServerMixInfo::GetInParams() const {
186 return in_params;
187}
188
189ServerMixInfo::InParams& ServerMixInfo::GetInParams() {
190 return in_params;
191}
192
193bool 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
229bool ServerMixInfo::HasAnyConnection() const {
230 return in_params.splitter_id != AudioCommon::NO_SPLITTER ||
231 in_params.mix_id != AudioCommon::NO_MIX;
232}
233
234void 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
247void ServerMixInfo::SetEffectCount(std::size_t count) {
248 effect_processing_order.resize(count);
249 ResetEffectProcessingOrder();
250}
251
252void ServerMixInfo::ResetEffectProcessingOrder() {
253 for (auto& order : effect_processing_order) {
254 order = AudioCommon::NO_EFFECT_ORDER;
255 }
256}
257
258s32 ServerMixInfo::GetEffectOrder(std::size_t i) const {
259 return effect_processing_order.at(i);
260}
261
262bool 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