summaryrefslogtreecommitdiff
path: root/src/audio_core/renderer/mix
diff options
context:
space:
mode:
authorGravatar Kelebek12022-07-16 23:48:45 +0100
committerGravatar Kelebek12022-07-22 01:11:32 +0100
commit458da8a94877677f086f06cdeecf959ec4283a33 (patch)
tree583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/renderer/mix
parentMerge pull request #8598 from Link4565/recv-dontwait (diff)
downloadyuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz
yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz
yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip
Project Andio
Diffstat (limited to 'src/audio_core/renderer/mix')
-rw-r--r--src/audio_core/renderer/mix/mix_context.cpp141
-rw-r--r--src/audio_core/renderer/mix/mix_context.h124
-rw-r--r--src/audio_core/renderer/mix/mix_info.cpp120
-rw-r--r--src/audio_core/renderer/mix/mix_info.h124
4 files changed, 509 insertions, 0 deletions
diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp
new file mode 100644
index 000000000..2427c83ed
--- /dev/null
+++ b/src/audio_core/renderer/mix/mix_context.cpp
@@ -0,0 +1,141 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <ranges>
5
6#include "audio_core/renderer/mix/mix_context.h"
7#include "audio_core/renderer/splitter/splitter_context.h"
8
9namespace AudioCore::AudioRenderer {
10
11void MixContext::Initialize(std::span<MixInfo*> sorted_mix_infos_, std::span<MixInfo> mix_infos_,
12 const u32 count_, std::span<s32> effect_process_order_buffer_,
13 const u32 effect_count_, std::span<u8> node_states_workbuffer,
14 const u64 node_buffer_size, std::span<u8> edge_matrix_workbuffer,
15 const u64 edge_matrix_size) {
16 count = count_;
17 sorted_mix_infos = sorted_mix_infos_;
18 mix_infos = mix_infos_;
19 effect_process_order_buffer = effect_process_order_buffer_;
20 effect_count = effect_count_;
21
22 if (node_states_workbuffer.size() > 0 && edge_matrix_workbuffer.size() > 0) {
23 node_states.Initialize(node_states_workbuffer, node_buffer_size, count);
24 edge_matrix.Initialize(edge_matrix_workbuffer, edge_matrix_size, count);
25 }
26
27 for (s32 i = 0; i < count; i++) {
28 sorted_mix_infos[i] = &mix_infos[i];
29 }
30}
31
32MixInfo* MixContext::GetSortedInfo(const s32 index) {
33 return sorted_mix_infos[index];
34}
35
36void MixContext::SetSortedInfo(const s32 index, MixInfo& mix_info) {
37 sorted_mix_infos[index] = &mix_info;
38}
39
40MixInfo* MixContext::GetInfo(const s32 index) {
41 return &mix_infos[index];
42}
43
44MixInfo* MixContext::GetFinalMixInfo() {
45 return &mix_infos[0];
46}
47
48s32 MixContext::GetCount() const {
49 return count;
50}
51
52void MixContext::UpdateDistancesFromFinalMix() {
53 for (s32 i = 0; i < count; i++) {
54 mix_infos[i].distance_from_final_mix = InvalidDistanceFromFinalMix;
55 }
56
57 for (s32 i = 0; i < count; i++) {
58 auto& mix_info{mix_infos[i]};
59 sorted_mix_infos[i] = &mix_info;
60
61 if (!mix_info.in_use) {
62 continue;
63 }
64
65 auto mix_id{mix_info.mix_id};
66 auto distance_to_final_mix{FinalMixId};
67
68 while (distance_to_final_mix < count) {
69 if (mix_id == FinalMixId) {
70 break;
71 }
72
73 if (mix_id == UnusedMixId) {
74 distance_to_final_mix = InvalidDistanceFromFinalMix;
75 break;
76 }
77
78 auto distance_from_final_mix{mix_infos[mix_id].distance_from_final_mix};
79 if (distance_from_final_mix != InvalidDistanceFromFinalMix) {
80 distance_to_final_mix = distance_from_final_mix + 1;
81 break;
82 }
83
84 distance_to_final_mix++;
85 mix_id = mix_infos[mix_id].dst_mix_id;
86 }
87
88 if (distance_to_final_mix >= count) {
89 distance_to_final_mix = InvalidDistanceFromFinalMix;
90 }
91 mix_info.distance_from_final_mix = distance_to_final_mix;
92 }
93}
94
95void MixContext::SortInfo() {
96 UpdateDistancesFromFinalMix();
97
98 std::ranges::sort(sorted_mix_infos, [](const MixInfo* lhs, const MixInfo* rhs) {
99 return lhs->distance_from_final_mix > rhs->distance_from_final_mix;
100 });
101
102 CalcMixBufferOffset();
103}
104
105void MixContext::CalcMixBufferOffset() {
106 s16 offset{0};
107 for (s32 i = 0; i < count; i++) {
108 auto mix_info{sorted_mix_infos[i]};
109 if (mix_info->in_use) {
110 const auto buffer_count{mix_info->buffer_count};
111 mix_info->buffer_offset = offset;
112 offset += buffer_count;
113 }
114 }
115}
116
117bool MixContext::TSortInfo(const SplitterContext& splitter_context) {
118 if (!splitter_context.UsingSplitter()) {
119 CalcMixBufferOffset();
120 return true;
121 }
122
123 if (!node_states.Tsort(edge_matrix)) {
124 return false;
125 }
126
127 std::vector<s32> sorted_results{node_states.GetSortedResuls()};
128 const auto result_size{std::min(count, static_cast<s32>(sorted_results.size()))};
129 for (s32 i = 0; i < result_size; i++) {
130 sorted_mix_infos[i] = &mix_infos[sorted_results[i]];
131 }
132
133 CalcMixBufferOffset();
134 return true;
135}
136
137EdgeMatrix& MixContext::GetEdgeMatrix() {
138 return edge_matrix;
139}
140
141} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/mix/mix_context.h b/src/audio_core/renderer/mix/mix_context.h
new file mode 100644
index 000000000..da3aa2829
--- /dev/null
+++ b/src/audio_core/renderer/mix/mix_context.h
@@ -0,0 +1,124 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <span>
7
8#include "audio_core/renderer/mix/mix_info.h"
9#include "audio_core/renderer/nodes/edge_matrix.h"
10#include "audio_core/renderer/nodes/node_states.h"
11#include "common/common_types.h"
12
13namespace AudioCore::AudioRenderer {
14class SplitterContext;
15
16/*
17 * Manages mixing states, sorting and building a node graph to describe a mix order.
18 */
19class MixContext {
20public:
21 /**
22 * Initialize the mix context.
23 *
24 * @param sorted_mix_infos - Buffer for the sorted mix infos.
25 * @param mix_infos - Buffer for the mix infos.
26 * @param effect_process_order_buffer - Buffer for the effect process orders.
27 * @param effect_count - Number of effects in the buffer.
28 * @param node_states_workbuffer - Buffer for node states.
29 * @param node_buffer_size - Size of the node states buffer.
30 * @param edge_matrix_workbuffer - Buffer for edge matrix.
31 * @param edge_matrix_size - Size of the edge matrix buffer.
32 */
33 void Initialize(std::span<MixInfo*> sorted_mix_infos, std::span<MixInfo> mix_infos, u32 count_,
34 std::span<s32> effect_process_order_buffer, u32 effect_count,
35 std::span<u8> node_states_workbuffer, u64 node_buffer_size,
36 std::span<u8> edge_matrix_workbuffer, u64 edge_matrix_size);
37
38 /**
39 * Get a sorted mix at the given index.
40 *
41 * @param index - Index of sorted mix.
42 * @return The sorted mix.
43 */
44 MixInfo* GetSortedInfo(s32 index);
45
46 /**
47 * Set the sorted info at the given index.
48 *
49 * @param index - Index of sorted mix.
50 * @param mix_info - The new mix for this index.
51 */
52 void SetSortedInfo(s32 index, MixInfo& mix_info);
53
54 /**
55 * Get a mix at the given index.
56 *
57 * @param index - Index of mix.
58 * @return The mix.
59 */
60 MixInfo* GetInfo(s32 index);
61
62 /**
63 * Get the final mix.
64 *
65 * @return The final mix.
66 */
67 MixInfo* GetFinalMixInfo();
68
69 /**
70 * Get the current number of mixes.
71 *
72 * @return The number of active mixes.
73 */
74 s32 GetCount() const;
75
76 /**
77 * Update all of the mixes' distance from the final mix.
78 * Needs to be called after altering the mix graph.
79 */
80 void UpdateDistancesFromFinalMix();
81
82 /**
83 * Non-splitter sort, sorts the sorted mixes based on their distance from the final mix.
84 */
85 void SortInfo();
86
87 /**
88 * Re-calculate the mix buffer offsets for each mix after altering the mix.
89 */
90 void CalcMixBufferOffset();
91
92 /**
93 * Splitter sort, traverse the splitter node graph and sort the sorted mixes from results.
94 *
95 * @param splitter_context - Splitter context for the sort.
96 * @return True if the sort was successful, othewise false.
97 */
98 bool TSortInfo(const SplitterContext& splitter_context);
99
100 /**
101 * Get the edge matrix used for the mix graph.
102 *
103 * @return The edge matrix used.
104 */
105 EdgeMatrix& GetEdgeMatrix();
106
107private:
108 /// Array of sorted mixes
109 std::span<MixInfo*> sorted_mix_infos{};
110 /// Array of mixes
111 std::span<MixInfo> mix_infos{};
112 /// Number of active mixes
113 s32 count{};
114 /// Array of effect process orderings
115 std::span<s32> effect_process_order_buffer{};
116 /// Number of effects in the process ordering buffer
117 u64 effect_count{};
118 /// Node states used in splitter sort
119 NodeStates node_states{};
120 /// Edge matrix for connected nodes used in splitter sort
121 EdgeMatrix edge_matrix{};
122};
123
124} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/mix/mix_info.cpp b/src/audio_core/renderer/mix/mix_info.cpp
new file mode 100644
index 000000000..cc18e57ee
--- /dev/null
+++ b/src/audio_core/renderer/mix/mix_info.cpp
@@ -0,0 +1,120 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "audio_core/renderer/behavior/behavior_info.h"
5#include "audio_core/renderer/effect/effect_context.h"
6#include "audio_core/renderer/mix/mix_info.h"
7#include "audio_core/renderer/nodes/edge_matrix.h"
8#include "audio_core/renderer/splitter/splitter_context.h"
9
10namespace AudioCore::AudioRenderer {
11
12MixInfo::MixInfo(std::span<s32> effect_order_buffer_, s32 effect_count_, BehaviorInfo& behavior)
13 : effect_order_buffer{effect_order_buffer_}, effect_count{effect_count_},
14 long_size_pre_delay_supported{behavior.IsLongSizePreDelaySupported()} {
15 ClearEffectProcessingOrder();
16}
17
18void MixInfo::Cleanup() {
19 mix_id = UnusedMixId;
20 dst_mix_id = UnusedMixId;
21 dst_splitter_id = UnusedSplitterId;
22}
23
24void MixInfo::ClearEffectProcessingOrder() {
25 for (s32 i = 0; i < effect_count; i++) {
26 effect_order_buffer[i] = -1;
27 }
28}
29
30bool MixInfo::Update(EdgeMatrix& edge_matrix, const InParameter& in_params,
31 EffectContext& effect_context, SplitterContext& splitter_context,
32 const BehaviorInfo& behavior) {
33 volume = in_params.volume;
34 sample_rate = in_params.sample_rate;
35 buffer_count = static_cast<s16>(in_params.buffer_count);
36 in_use = in_params.in_use;
37 mix_id = in_params.mix_id;
38 node_id = in_params.node_id;
39 mix_volumes = in_params.mix_volumes;
40
41 bool sort_required{false};
42 if (behavior.IsSplitterSupported()) {
43 sort_required = UpdateConnection(edge_matrix, in_params, splitter_context);
44 } else {
45 if (dst_mix_id != in_params.dest_mix_id) {
46 dst_mix_id = in_params.dest_mix_id;
47 sort_required = true;
48 }
49 dst_splitter_id = UnusedSplitterId;
50 }
51
52 ClearEffectProcessingOrder();
53
54 // Check all effects, and set their order if they belong to this mix.
55 const auto count{effect_context.GetCount()};
56 for (u32 i = 0; i < count; i++) {
57 const auto& info{effect_context.GetInfo(i)};
58 if (mix_id == info.GetMixId()) {
59 const auto processing_order{info.GetProcessingOrder()};
60 if (processing_order > effect_count) {
61 break;
62 }
63 effect_order_buffer[processing_order] = i;
64 }
65 }
66
67 return sort_required;
68}
69
70bool MixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const InParameter& in_params,
71 SplitterContext& splitter_context) {
72 auto has_new_connection{false};
73 if (dst_splitter_id != UnusedSplitterId) {
74 auto& splitter_info{splitter_context.GetInfo(dst_splitter_id)};
75 has_new_connection = splitter_info.HasNewConnection();
76 }
77
78 // Check if this mix matches the input parameters.
79 // If everything is the same, don't bother updating.
80 if (dst_mix_id == in_params.dest_mix_id && dst_splitter_id == in_params.dest_splitter_id &&
81 !has_new_connection) {
82 return false;
83 }
84
85 // Reset the mix in the graph, as we're about to update it.
86 edge_matrix.RemoveEdges(mix_id);
87
88 if (in_params.dest_mix_id == UnusedMixId) {
89 if (in_params.dest_splitter_id != UnusedSplitterId) {
90 // If the splitter is used, connect this mix to each active destination.
91 auto& splitter_info{splitter_context.GetInfo(in_params.dest_splitter_id)};
92 auto const destination_count{splitter_info.GetDestinationCount()};
93
94 for (u32 i = 0; i < destination_count; i++) {
95 auto destination{
96 splitter_context.GetDesintationData(in_params.dest_splitter_id, i)};
97
98 if (destination) {
99 const auto destination_id{destination->GetMixId()};
100 if (destination_id != UnusedMixId) {
101 edge_matrix.Connect(mix_id, destination_id);
102 }
103 }
104 }
105 }
106 } else {
107 // If the splitter is not used, only connect this mix to its destination.
108 edge_matrix.Connect(mix_id, in_params.dest_mix_id);
109 }
110
111 dst_mix_id = in_params.dest_mix_id;
112 dst_splitter_id = in_params.dest_splitter_id;
113 return true;
114}
115
116bool MixInfo::HasAnyConnection() const {
117 return dst_mix_id != UnusedMixId || dst_splitter_id != UnusedSplitterId;
118}
119
120} // namespace AudioCore::AudioRenderer
diff --git a/src/audio_core/renderer/mix/mix_info.h b/src/audio_core/renderer/mix/mix_info.h
new file mode 100644
index 000000000..b5fa4c0c7
--- /dev/null
+++ b/src/audio_core/renderer/mix/mix_info.h
@@ -0,0 +1,124 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <array>
7#include <span>
8
9#include "audio_core/common/common.h"
10#include "common/common_types.h"
11
12namespace AudioCore::AudioRenderer {
13class EdgeMatrix;
14class SplitterContext;
15class EffectContext;
16class BehaviorInfo;
17
18/**
19 * A single mix, which may feed through other mixes in a chain until reaching the final output mix.
20 */
21class MixInfo {
22public:
23 struct InParameter {
24 /* 0x000 */ f32 volume;
25 /* 0x004 */ u32 sample_rate;
26 /* 0x008 */ u32 buffer_count;
27 /* 0x00C */ bool in_use;
28 /* 0x00D */ bool is_dirty;
29 /* 0x010 */ s32 mix_id;
30 /* 0x014 */ u32 effect_count;
31 /* 0x018 */ s32 node_id;
32 /* 0x01C */ char unk01C[0x8];
33 /* 0x024 */ std::array<std::array<f32, MaxMixBuffers>, MaxMixBuffers> mix_volumes;
34 /* 0x924 */ s32 dest_mix_id;
35 /* 0x928 */ s32 dest_splitter_id;
36 /* 0x92C */ char unk92C[0x4];
37 };
38 static_assert(sizeof(InParameter) == 0x930, "MixInfo::InParameter has the wrong size!");
39
40 struct InDirtyParameter {
41 /* 0x00 */ u32 magic;
42 /* 0x04 */ s32 count;
43 /* 0x08 */ char unk08[0x18];
44 };
45 static_assert(sizeof(InDirtyParameter) == 0x20,
46 "MixInfo::InDirtyParameter has the wrong size!");
47
48 MixInfo(std::span<s32> effect_order_buffer, s32 effect_count, BehaviorInfo& behavior);
49
50 /**
51 * Clean up the mix, resetting it to a default state.
52 */
53 void Cleanup();
54
55 /**
56 * Clear the effect process order for all effects in this mix.
57 */
58 void ClearEffectProcessingOrder();
59
60 /**
61 * Update the mix according to the given parameters.
62 *
63 * @param edge_matrix - Updated with new splitter node connections, if supported.
64 * @param in_params - Input parameters.
65 * @param effect_context - Used to update the effect orderings.
66 * @param splitter_context - Used to update the mix graph if supported.
67 * @param behavior - Used for checking which features are supported.
68 * @return True if the mix was updated and a sort is required, otherwise false.
69 */
70 bool Update(EdgeMatrix& edge_matrix, const InParameter& in_params,
71 EffectContext& effect_context, SplitterContext& splitter_context,
72 const BehaviorInfo& behavior);
73
74 /**
75 * Update the mix's connection in the node graph according to the given parameters.
76 *
77 * @param edge_matrix - Updated with new splitter node connections, if supported.
78 * @param in_params - Input parameters.
79 * @param splitter_context - Used to update the mix graph if supported.
80 * @return True if the mix was updated and a sort is required, otherwise false.
81 */
82 bool UpdateConnection(EdgeMatrix& edge_matrix, const InParameter& in_params,
83 SplitterContext& splitter_context);
84
85 /**
86 * Check if this mix is connected to any other.
87 *
88 * @return True if the mix has a connection, otherwise false.
89 */
90 bool HasAnyConnection() const;
91
92 /// Volume of this mix
93 f32 volume{};
94 /// Sample rate of this mix
95 u32 sample_rate{};
96 /// Number of buffers in this mix
97 s16 buffer_count{};
98 /// Is this mix in use?
99 bool in_use{};
100 /// Is this mix enabled?
101 bool enabled{};
102 /// Id of this mix
103 s32 mix_id{UnusedMixId};
104 /// Node id of this mix
105 s32 node_id{};
106 /// Buffer offset for this mix
107 s16 buffer_offset{};
108 /// Distance to the final mix
109 s32 distance_from_final_mix{InvalidDistanceFromFinalMix};
110 /// Array of effect orderings of all effects in this mix
111 std::span<s32> effect_order_buffer;
112 /// Number of effects in this mix
113 const s32 effect_count;
114 /// Id for next mix in the chain
115 s32 dst_mix_id{UnusedMixId};
116 /// Mixing volumes for this mix used when this mix is chained with another
117 std::array<std::array<f32, MaxMixBuffers>, MaxMixBuffers> mix_volumes{};
118 /// Id for next mix in the graph when splitter is used
119 s32 dst_splitter_id{UnusedSplitterId};
120 /// Is a longer pre-delay time supported for the reverb effect?
121 const bool long_size_pre_delay_supported;
122};
123
124} // namespace AudioCore::AudioRenderer