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