diff options
| author | 2022-07-16 23:48:45 +0100 | |
|---|---|---|
| committer | 2022-07-22 01:11:32 +0100 | |
| commit | 458da8a94877677f086f06cdeecf959ec4283a33 (patch) | |
| tree | 583166d77602ad90a0d552f37de8729ad80fd6c1 /src/audio_core/renderer/splitter | |
| parent | Merge pull request #8598 from Link4565/recv-dontwait (diff) | |
| download | yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.gz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.tar.xz yuzu-458da8a94877677f086f06cdeecf959ec4283a33.zip | |
Project Andio
Diffstat (limited to 'src/audio_core/renderer/splitter')
6 files changed, 814 insertions, 0 deletions
diff --git a/src/audio_core/renderer/splitter/splitter_context.cpp b/src/audio_core/renderer/splitter/splitter_context.cpp new file mode 100644 index 000000000..7a23ba43f --- /dev/null +++ b/src/audio_core/renderer/splitter/splitter_context.cpp | |||
| @@ -0,0 +1,217 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/common/audio_renderer_parameter.h" | ||
| 5 | #include "audio_core/common/workbuffer_allocator.h" | ||
| 6 | #include "audio_core/renderer/behavior/behavior_info.h" | ||
| 7 | #include "audio_core/renderer/splitter/splitter_context.h" | ||
| 8 | #include "common/alignment.h" | ||
| 9 | |||
| 10 | namespace AudioCore::AudioRenderer { | ||
| 11 | |||
| 12 | SplitterDestinationData* SplitterContext::GetDesintationData(const s32 splitter_id, | ||
| 13 | const s32 destination_id) { | ||
| 14 | return splitter_infos[splitter_id].GetData(destination_id); | ||
| 15 | } | ||
| 16 | |||
| 17 | SplitterInfo& SplitterContext::GetInfo(const s32 splitter_id) { | ||
| 18 | return splitter_infos[splitter_id]; | ||
| 19 | } | ||
| 20 | |||
| 21 | u32 SplitterContext::GetDataCount() const { | ||
| 22 | return destinations_count; | ||
| 23 | } | ||
| 24 | |||
| 25 | u32 SplitterContext::GetInfoCount() const { | ||
| 26 | return info_count; | ||
| 27 | } | ||
| 28 | |||
| 29 | SplitterDestinationData& SplitterContext::GetData(const u32 index) { | ||
| 30 | return splitter_destinations[index]; | ||
| 31 | } | ||
| 32 | |||
| 33 | void SplitterContext::Setup(std::span<SplitterInfo> splitter_infos_, const u32 splitter_info_count_, | ||
| 34 | SplitterDestinationData* splitter_destinations_, | ||
| 35 | const u32 destination_count_, const bool splitter_bug_fixed_) { | ||
| 36 | splitter_infos = splitter_infos_; | ||
| 37 | info_count = splitter_info_count_; | ||
| 38 | splitter_destinations = splitter_destinations_; | ||
| 39 | destinations_count = destination_count_; | ||
| 40 | splitter_bug_fixed = splitter_bug_fixed_; | ||
| 41 | } | ||
| 42 | |||
| 43 | bool SplitterContext::UsingSplitter() const { | ||
| 44 | return splitter_infos.size() > 0 && info_count > 0 && splitter_destinations != nullptr && | ||
| 45 | destinations_count > 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | void SplitterContext::ClearAllNewConnectionFlag() { | ||
| 49 | for (s32 i = 0; i < info_count; i++) { | ||
| 50 | splitter_infos[i].SetNewConnectionFlag(); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | bool SplitterContext::Initialize(const BehaviorInfo& behavior, | ||
| 55 | const AudioRendererParameterInternal& params, | ||
| 56 | WorkbufferAllocator& allocator) { | ||
| 57 | if (behavior.IsSplitterSupported() && params.splitter_infos > 0 && | ||
| 58 | params.splitter_destinations > 0) { | ||
| 59 | splitter_infos = allocator.Allocate<SplitterInfo>(params.splitter_infos, 0x10); | ||
| 60 | |||
| 61 | for (u32 i = 0; i < params.splitter_infos; i++) { | ||
| 62 | std::construct_at<SplitterInfo>(&splitter_infos[i], static_cast<s32>(i)); | ||
| 63 | } | ||
| 64 | |||
| 65 | if (splitter_infos.size() == 0) { | ||
| 66 | splitter_infos = {}; | ||
| 67 | return false; | ||
| 68 | } | ||
| 69 | |||
| 70 | splitter_destinations = | ||
| 71 | allocator.Allocate<SplitterDestinationData>(params.splitter_destinations, 0x10).data(); | ||
| 72 | |||
| 73 | for (s32 i = 0; i < params.splitter_destinations; i++) { | ||
| 74 | std::construct_at<SplitterDestinationData>(&splitter_destinations[i], i); | ||
| 75 | } | ||
| 76 | |||
| 77 | if (params.splitter_destinations <= 0) { | ||
| 78 | splitter_infos = {}; | ||
| 79 | splitter_destinations = nullptr; | ||
| 80 | return false; | ||
| 81 | } | ||
| 82 | |||
| 83 | Setup(splitter_infos, params.splitter_infos, splitter_destinations, | ||
| 84 | params.splitter_destinations, behavior.IsSplitterBugFixed()); | ||
| 85 | } | ||
| 86 | return true; | ||
| 87 | } | ||
| 88 | |||
| 89 | bool SplitterContext::Update(const u8* input, u32& consumed_size) { | ||
| 90 | auto in_params{reinterpret_cast<const InParameterHeader*>(input)}; | ||
| 91 | |||
| 92 | if (destinations_count == 0 || info_count == 0) { | ||
| 93 | consumed_size = 0; | ||
| 94 | return true; | ||
| 95 | } | ||
| 96 | |||
| 97 | if (in_params->magic != GetSplitterInParamHeaderMagic()) { | ||
| 98 | consumed_size = 0; | ||
| 99 | return false; | ||
| 100 | } | ||
| 101 | |||
| 102 | for (auto& splitter_info : splitter_infos) { | ||
| 103 | splitter_info.ClearNewConnectionFlag(); | ||
| 104 | } | ||
| 105 | |||
| 106 | u32 offset{sizeof(InParameterHeader)}; | ||
| 107 | offset = UpdateInfo(input, offset, in_params->info_count); | ||
| 108 | offset = UpdateData(input, offset, in_params->destination_count); | ||
| 109 | |||
| 110 | consumed_size = Common::AlignUp(offset, 0x10); | ||
| 111 | return true; | ||
| 112 | } | ||
| 113 | |||
| 114 | u32 SplitterContext::UpdateInfo(const u8* input, u32 offset, const u32 splitter_count) { | ||
| 115 | for (u32 i = 0; i < splitter_count; i++) { | ||
| 116 | auto info_header{reinterpret_cast<const SplitterInfo::InParameter*>(input + offset)}; | ||
| 117 | |||
| 118 | if (info_header->magic != GetSplitterInfoMagic()) { | ||
| 119 | continue; | ||
| 120 | } | ||
| 121 | |||
| 122 | if (info_header->id < 0 || info_header->id > info_count) { | ||
| 123 | break; | ||
| 124 | } | ||
| 125 | |||
| 126 | auto& info{splitter_infos[info_header->id]}; | ||
| 127 | RecomposeDestination(info, info_header); | ||
| 128 | |||
| 129 | offset += info.Update(info_header); | ||
| 130 | } | ||
| 131 | |||
| 132 | return offset; | ||
| 133 | } | ||
| 134 | |||
| 135 | u32 SplitterContext::UpdateData(const u8* input, u32 offset, const u32 count) { | ||
| 136 | for (u32 i = 0; i < count; i++) { | ||
| 137 | auto data_header{ | ||
| 138 | reinterpret_cast<const SplitterDestinationData::InParameter*>(input + offset)}; | ||
| 139 | |||
| 140 | if (data_header->magic != GetSplitterSendDataMagic()) { | ||
| 141 | continue; | ||
| 142 | } | ||
| 143 | |||
| 144 | if (data_header->id < 0 || data_header->id > destinations_count) { | ||
| 145 | continue; | ||
| 146 | } | ||
| 147 | |||
| 148 | splitter_destinations[data_header->id].Update(*data_header); | ||
| 149 | offset += sizeof(SplitterDestinationData::InParameter); | ||
| 150 | } | ||
| 151 | |||
| 152 | return offset; | ||
| 153 | } | ||
| 154 | |||
| 155 | void SplitterContext::UpdateInternalState() { | ||
| 156 | for (s32 i = 0; i < info_count; i++) { | ||
| 157 | splitter_infos[i].UpdateInternalState(); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | void SplitterContext::RecomposeDestination(SplitterInfo& out_info, | ||
| 162 | const SplitterInfo::InParameter* info_header) { | ||
| 163 | auto destination{out_info.GetData(0)}; | ||
| 164 | while (destination != nullptr) { | ||
| 165 | auto dest{destination->GetNext()}; | ||
| 166 | destination->SetNext(nullptr); | ||
| 167 | destination = dest; | ||
| 168 | } | ||
| 169 | out_info.SetDestinations(nullptr); | ||
| 170 | |||
| 171 | auto dest_count{info_header->destination_count}; | ||
| 172 | if (!splitter_bug_fixed) { | ||
| 173 | dest_count = std::min(dest_count, GetDestCountPerInfoForCompat()); | ||
| 174 | } | ||
| 175 | |||
| 176 | if (dest_count == 0) { | ||
| 177 | return; | ||
| 178 | } | ||
| 179 | |||
| 180 | std::span<const u32> destination_ids{reinterpret_cast<const u32*>(&info_header[1]), dest_count}; | ||
| 181 | |||
| 182 | auto head{&splitter_destinations[destination_ids[0]]}; | ||
| 183 | auto current_destination{head}; | ||
| 184 | for (u32 i = 1; i < dest_count; i++) { | ||
| 185 | auto next_destination{&splitter_destinations[destination_ids[i]]}; | ||
| 186 | current_destination->SetNext(next_destination); | ||
| 187 | current_destination = next_destination; | ||
| 188 | } | ||
| 189 | |||
| 190 | out_info.SetDestinations(head); | ||
| 191 | out_info.SetDestinationCount(dest_count); | ||
| 192 | } | ||
| 193 | |||
| 194 | u32 SplitterContext::GetDestCountPerInfoForCompat() const { | ||
| 195 | if (info_count <= 0) { | ||
| 196 | return 0; | ||
| 197 | } | ||
| 198 | return static_cast<u32>(destinations_count / info_count); | ||
| 199 | } | ||
| 200 | |||
| 201 | u64 SplitterContext::CalcWorkBufferSize(const BehaviorInfo& behavior, | ||
| 202 | const AudioRendererParameterInternal& params) { | ||
| 203 | u64 size{0}; | ||
| 204 | if (!behavior.IsSplitterSupported()) { | ||
| 205 | return size; | ||
| 206 | } | ||
| 207 | |||
| 208 | size += params.splitter_destinations * sizeof(SplitterDestinationData) + | ||
| 209 | params.splitter_infos * sizeof(SplitterInfo); | ||
| 210 | |||
| 211 | if (behavior.IsSplitterBugFixed()) { | ||
| 212 | size += Common::AlignUp(params.splitter_destinations * sizeof(u32), 0x10); | ||
| 213 | } | ||
| 214 | return size; | ||
| 215 | } | ||
| 216 | |||
| 217 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/splitter/splitter_context.h b/src/audio_core/renderer/splitter/splitter_context.h new file mode 100644 index 000000000..cfd092b4f --- /dev/null +++ b/src/audio_core/renderer/splitter/splitter_context.h | |||
| @@ -0,0 +1,189 @@ | |||
| 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/splitter/splitter_destinations_data.h" | ||
| 9 | #include "audio_core/renderer/splitter/splitter_info.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore { | ||
| 13 | struct AudioRendererParameterInternal; | ||
| 14 | class WorkbufferAllocator; | ||
| 15 | |||
| 16 | namespace AudioRenderer { | ||
| 17 | class BehaviorInfo; | ||
| 18 | |||
| 19 | /** | ||
| 20 | * The splitter allows much more control over how sound is mixed together. | ||
| 21 | * Previously, one mix can only connect to one other, and you may need | ||
| 22 | * more mixes (and duplicate processing) to achieve the same result. | ||
| 23 | * With the splitter, many-to-one and one-to-many mixing is possible. | ||
| 24 | * This was added in revision 2. | ||
| 25 | * Had a bug with incorrect numbers of destinations, fixed in revision 5. | ||
| 26 | */ | ||
| 27 | class SplitterContext { | ||
| 28 | struct InParameterHeader { | ||
| 29 | /* 0x00 */ u32 magic; // 'SNDH' | ||
| 30 | /* 0x04 */ s32 info_count; | ||
| 31 | /* 0x08 */ s32 destination_count; | ||
| 32 | /* 0x0C */ char unk0C[0x14]; | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(InParameterHeader) == 0x20, | ||
| 35 | "SplitterContext::InParameterHeader has the wrong size!"); | ||
| 36 | |||
| 37 | public: | ||
| 38 | /** | ||
| 39 | * Get a destination mix from the given splitter and destination index. | ||
| 40 | * | ||
| 41 | * @param splitter_id - Splitter index to get from. | ||
| 42 | * @param destination_id - Destination index within the splitter. | ||
| 43 | * @return Pointer to the found destination. May be nullptr. | ||
| 44 | */ | ||
| 45 | SplitterDestinationData* GetDesintationData(s32 splitter_id, s32 destination_id); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Get a splitter from the given index. | ||
| 49 | * | ||
| 50 | * @param index - Index of the desired splitter. | ||
| 51 | * @return Splitter requested. | ||
| 52 | */ | ||
| 53 | SplitterInfo& GetInfo(s32 index); | ||
| 54 | |||
| 55 | /** | ||
| 56 | * Get the total number of splitter destinations. | ||
| 57 | * | ||
| 58 | * @return Number of destiantions. | ||
| 59 | */ | ||
| 60 | u32 GetDataCount() const; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Get the total number of splitters. | ||
| 64 | * | ||
| 65 | * @return Number of splitters. | ||
| 66 | */ | ||
| 67 | u32 GetInfoCount() const; | ||
| 68 | |||
| 69 | /** | ||
| 70 | * Get a specific global destination. | ||
| 71 | * | ||
| 72 | * @param index - Index of the desired destination. | ||
| 73 | * @return The requested destination. | ||
| 74 | */ | ||
| 75 | SplitterDestinationData& GetData(u32 index); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * Check if the splitter is in use. | ||
| 79 | * | ||
| 80 | * @return True if any splitter or destination is in use, otherwise false. | ||
| 81 | */ | ||
| 82 | bool UsingSplitter() const; | ||
| 83 | |||
| 84 | /** | ||
| 85 | * Mark all splitters as having new connections. | ||
| 86 | */ | ||
| 87 | void ClearAllNewConnectionFlag(); | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Initialize the context. | ||
| 91 | * | ||
| 92 | * @param behavior - Used to check for splitter support. | ||
| 93 | * @param params - Input parameters. | ||
| 94 | * @param allocator - Allocator used to allocate workbuffer memory. | ||
| 95 | */ | ||
| 96 | bool Initialize(const BehaviorInfo& behavior, const AudioRendererParameterInternal& params, | ||
| 97 | WorkbufferAllocator& allocator); | ||
| 98 | |||
| 99 | /** | ||
| 100 | * Update the context. | ||
| 101 | * | ||
| 102 | * @param input - Input buffer with the new info, | ||
| 103 | * expected to point to a InParameterHeader. | ||
| 104 | * @param consumed_size - Output with the number of bytes consumed from input. | ||
| 105 | */ | ||
| 106 | bool Update(const u8* input, u32& consumed_size); | ||
| 107 | |||
| 108 | /** | ||
| 109 | * Update the splitters. | ||
| 110 | * | ||
| 111 | * @param input - Input buffer with the new info. | ||
| 112 | * @param offset - Current offset within the input buffer, | ||
| 113 | * input + offset should point to a SplitterInfo::InParameter. | ||
| 114 | * @param splitter_count - Number of splitters in the input buffer. | ||
| 115 | * @return Number of bytes consumed in input. | ||
| 116 | */ | ||
| 117 | u32 UpdateInfo(const u8* input, u32 offset, u32 splitter_count); | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Update the splitters. | ||
| 121 | * | ||
| 122 | * @param input - Input buffer with the new info. | ||
| 123 | * @param offset - Current offset within the input buffer, | ||
| 124 | * input + offset should point to a | ||
| 125 | * SplitterDestinationData::InParameter. | ||
| 126 | * @param destination_count - Number of destinations in the input buffer. | ||
| 127 | * @return Number of bytes consumed in input. | ||
| 128 | */ | ||
| 129 | u32 UpdateData(const u8* input, u32 offset, u32 destination_count); | ||
| 130 | |||
| 131 | /** | ||
| 132 | * Update the state of all destinations in all splitters. | ||
| 133 | */ | ||
| 134 | void UpdateInternalState(); | ||
| 135 | |||
| 136 | /** | ||
| 137 | * Replace the given splitter's destinations with new ones. | ||
| 138 | * | ||
| 139 | * @param out_info - Splitter to recompose. | ||
| 140 | * @param info_header - Input parameters containing new destination ids. | ||
| 141 | */ | ||
| 142 | void RecomposeDestination(SplitterInfo& out_info, const SplitterInfo::InParameter* info_header); | ||
| 143 | |||
| 144 | /** | ||
| 145 | * Old calculation for destinations, this is the thing the splitter bug fixes. | ||
| 146 | * Left for compatibility, and now min'd with the actual count to not bug. | ||
| 147 | * | ||
| 148 | * @return Number of splitter destinations. | ||
| 149 | */ | ||
| 150 | u32 GetDestCountPerInfoForCompat() const; | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Calculate the size of the required workbuffer for splitters and destinations. | ||
| 154 | * | ||
| 155 | * @param behavior - Used to check splitter features. | ||
| 156 | * @param params - Input parameters with splitter/destination counts. | ||
| 157 | * @return Required buffer size. | ||
| 158 | */ | ||
| 159 | static u64 CalcWorkBufferSize(const BehaviorInfo& behavior, | ||
| 160 | const AudioRendererParameterInternal& params); | ||
| 161 | |||
| 162 | private: | ||
| 163 | /** | ||
| 164 | * Setup the context. | ||
| 165 | * | ||
| 166 | * @param splitter_infos - Workbuffer for splitters. | ||
| 167 | * @param splitter_info_count - Number of splitters in the workbuffer. | ||
| 168 | * @param splitter_destinations - Workbuffer for splitter destinations. | ||
| 169 | * @param destination_count - Number of destinations in the workbuffer. | ||
| 170 | * @param splitter_bug_fixed - Is the splitter bug fixed? | ||
| 171 | */ | ||
| 172 | void Setup(std::span<SplitterInfo> splitter_infos, u32 splitter_info_count, | ||
| 173 | SplitterDestinationData* splitter_destinations, u32 destination_count, | ||
| 174 | bool splitter_bug_fixed); | ||
| 175 | |||
| 176 | /// Workbuffer for splitters | ||
| 177 | std::span<SplitterInfo> splitter_infos{}; | ||
| 178 | /// Number of splitters in buffer | ||
| 179 | s32 info_count{}; | ||
| 180 | /// Workbuffer for destinations | ||
| 181 | SplitterDestinationData* splitter_destinations{}; | ||
| 182 | /// Number of destinations in buffer | ||
| 183 | s32 destinations_count{}; | ||
| 184 | /// Is the splitter bug fixed? | ||
| 185 | bool splitter_bug_fixed{}; | ||
| 186 | }; | ||
| 187 | |||
| 188 | } // namespace AudioRenderer | ||
| 189 | } // namespace AudioCore | ||
diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.cpp b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp new file mode 100644 index 000000000..b27d44896 --- /dev/null +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.cpp | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/splitter/splitter_destinations_data.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | SplitterDestinationData::SplitterDestinationData(const s32 id_) : id{id_} {} | ||
| 9 | |||
| 10 | void SplitterDestinationData::ClearMixVolume() { | ||
| 11 | mix_volumes.fill(0.0f); | ||
| 12 | prev_mix_volumes.fill(0.0f); | ||
| 13 | } | ||
| 14 | |||
| 15 | s32 SplitterDestinationData::GetId() const { | ||
| 16 | return id; | ||
| 17 | } | ||
| 18 | |||
| 19 | bool SplitterDestinationData::IsConfigured() const { | ||
| 20 | return in_use && destination_id != UnusedMixId; | ||
| 21 | } | ||
| 22 | |||
| 23 | s32 SplitterDestinationData::GetMixId() const { | ||
| 24 | return destination_id; | ||
| 25 | } | ||
| 26 | |||
| 27 | f32 SplitterDestinationData::GetMixVolume(const u32 index) const { | ||
| 28 | if (index >= mix_volumes.size()) { | ||
| 29 | LOG_ERROR(Service_Audio, "SplitterDestinationData::GetMixVolume Invalid index {}", index); | ||
| 30 | return 0.0f; | ||
| 31 | } | ||
| 32 | return mix_volumes[index]; | ||
| 33 | } | ||
| 34 | |||
| 35 | std::span<f32> SplitterDestinationData::GetMixVolume() { | ||
| 36 | return mix_volumes; | ||
| 37 | } | ||
| 38 | |||
| 39 | f32 SplitterDestinationData::GetMixVolumePrev(const u32 index) const { | ||
| 40 | if (index >= prev_mix_volumes.size()) { | ||
| 41 | LOG_ERROR(Service_Audio, "SplitterDestinationData::GetMixVolumePrev Invalid index {}", | ||
| 42 | index); | ||
| 43 | return 0.0f; | ||
| 44 | } | ||
| 45 | return prev_mix_volumes[index]; | ||
| 46 | } | ||
| 47 | |||
| 48 | std::span<f32> SplitterDestinationData::GetMixVolumePrev() { | ||
| 49 | return prev_mix_volumes; | ||
| 50 | } | ||
| 51 | |||
| 52 | void SplitterDestinationData::Update(const InParameter& params) { | ||
| 53 | if (params.id != id || params.magic != GetSplitterSendDataMagic()) { | ||
| 54 | return; | ||
| 55 | } | ||
| 56 | |||
| 57 | destination_id = params.mix_id; | ||
| 58 | mix_volumes = params.mix_volumes; | ||
| 59 | |||
| 60 | if (!in_use && params.in_use) { | ||
| 61 | prev_mix_volumes = mix_volumes; | ||
| 62 | need_update = false; | ||
| 63 | } | ||
| 64 | |||
| 65 | in_use = params.in_use; | ||
| 66 | } | ||
| 67 | |||
| 68 | void SplitterDestinationData::MarkAsNeedToUpdateInternalState() { | ||
| 69 | need_update = true; | ||
| 70 | } | ||
| 71 | |||
| 72 | void SplitterDestinationData::UpdateInternalState() { | ||
| 73 | if (in_use && need_update) { | ||
| 74 | prev_mix_volumes = mix_volumes; | ||
| 75 | } | ||
| 76 | need_update = false; | ||
| 77 | } | ||
| 78 | |||
| 79 | SplitterDestinationData* SplitterDestinationData::GetNext() const { | ||
| 80 | return next; | ||
| 81 | } | ||
| 82 | |||
| 83 | void SplitterDestinationData::SetNext(SplitterDestinationData* next_) { | ||
| 84 | next = next_; | ||
| 85 | } | ||
| 86 | |||
| 87 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/splitter/splitter_destinations_data.h b/src/audio_core/renderer/splitter/splitter_destinations_data.h new file mode 100644 index 000000000..bd3d55748 --- /dev/null +++ b/src/audio_core/renderer/splitter/splitter_destinations_data.h | |||
| @@ -0,0 +1,135 @@ | |||
| 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 | |||
| 12 | namespace AudioCore::AudioRenderer { | ||
| 13 | /** | ||
| 14 | * Represents a mixing node, can be connected to a previous and next destination forming a chain | ||
| 15 | * that a certain mix buffer will pass through to output. | ||
| 16 | */ | ||
| 17 | class SplitterDestinationData { | ||
| 18 | public: | ||
| 19 | struct InParameter { | ||
| 20 | /* 0x00 */ u32 magic; // 'SNDD' | ||
| 21 | /* 0x04 */ s32 id; | ||
| 22 | /* 0x08 */ std::array<f32, MaxMixBuffers> mix_volumes; | ||
| 23 | /* 0x68 */ u32 mix_id; | ||
| 24 | /* 0x6C */ bool in_use; | ||
| 25 | }; | ||
| 26 | static_assert(sizeof(InParameter) == 0x70, | ||
| 27 | "SplitterDestinationData::InParameter has the wrong size!"); | ||
| 28 | |||
| 29 | SplitterDestinationData(s32 id); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Reset the mix volumes for this destination. | ||
| 33 | */ | ||
| 34 | void ClearMixVolume(); | ||
| 35 | |||
| 36 | /** | ||
| 37 | * Get the id of this destination. | ||
| 38 | * | ||
| 39 | * @return Id for this destination. | ||
| 40 | */ | ||
| 41 | s32 GetId() const; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Check if this destination is correctly configured. | ||
| 45 | * | ||
| 46 | * @return True if configured, otherwise false. | ||
| 47 | */ | ||
| 48 | bool IsConfigured() const; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Get the mix id for this destination. | ||
| 52 | * | ||
| 53 | * @return Mix id for this destination. | ||
| 54 | */ | ||
| 55 | s32 GetMixId() const; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Get the current mix volume of a given index in this destination. | ||
| 59 | * | ||
| 60 | * @param index - Mix buffer index to get the volume for. | ||
| 61 | * @return Current volume of the specified mix. | ||
| 62 | */ | ||
| 63 | f32 GetMixVolume(u32 index) const; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Get the current mix volumes for all mix buffers in this destination. | ||
| 67 | * | ||
| 68 | * @return Span of current mix buffer volumes. | ||
| 69 | */ | ||
| 70 | std::span<f32> GetMixVolume(); | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Get the previous mix volume of a given index in this destination. | ||
| 74 | * | ||
| 75 | * @param index - Mix buffer index to get the volume for. | ||
| 76 | * @return Previous volume of the specified mix. | ||
| 77 | */ | ||
| 78 | f32 GetMixVolumePrev(u32 index) const; | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Get the previous mix volumes for all mix buffers in this destination. | ||
| 82 | * | ||
| 83 | * @return Span of previous mix buffer volumes. | ||
| 84 | */ | ||
| 85 | std::span<f32> GetMixVolumePrev(); | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Update this destination. | ||
| 89 | * | ||
| 90 | * @param params - Inpout parameters to update the destination. | ||
| 91 | */ | ||
| 92 | void Update(const InParameter& params); | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Mark this destination as needing its volumes updated. | ||
| 96 | */ | ||
| 97 | void MarkAsNeedToUpdateInternalState(); | ||
| 98 | |||
| 99 | /** | ||
| 100 | * Copy current volumes to previous if an update is required. | ||
| 101 | */ | ||
| 102 | void UpdateInternalState(); | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Get the next destination in the mix chain. | ||
| 106 | * | ||
| 107 | * @return The next splitter destination, may be nullptr if this is the last in the chain. | ||
| 108 | */ | ||
| 109 | SplitterDestinationData* GetNext() const; | ||
| 110 | |||
| 111 | /** | ||
| 112 | * Set the next destination in the mix chain. | ||
| 113 | * | ||
| 114 | * @param next - Destination this one is to be connected to. | ||
| 115 | */ | ||
| 116 | void SetNext(SplitterDestinationData* next); | ||
| 117 | |||
| 118 | private: | ||
| 119 | /// Id of this destination | ||
| 120 | const s32 id; | ||
| 121 | /// Mix id this destination represents | ||
| 122 | s32 destination_id{UnusedMixId}; | ||
| 123 | /// Current mix volumes | ||
| 124 | std::array<f32, MaxMixBuffers> mix_volumes{0.0f}; | ||
| 125 | /// Previous mix volumes | ||
| 126 | std::array<f32, MaxMixBuffers> prev_mix_volumes{0.0f}; | ||
| 127 | /// Next destination in the mix chain | ||
| 128 | SplitterDestinationData* next{}; | ||
| 129 | /// Is this destiantion in use? | ||
| 130 | bool in_use{}; | ||
| 131 | /// Does this destiantion need its volumes updated? | ||
| 132 | bool need_update{}; | ||
| 133 | }; | ||
| 134 | |||
| 135 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/splitter/splitter_info.cpp b/src/audio_core/renderer/splitter/splitter_info.cpp new file mode 100644 index 000000000..1aee6720b --- /dev/null +++ b/src/audio_core/renderer/splitter/splitter_info.cpp | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/renderer/splitter/splitter_info.h" | ||
| 5 | |||
| 6 | namespace AudioCore::AudioRenderer { | ||
| 7 | |||
| 8 | SplitterInfo::SplitterInfo(const s32 id_) : id{id_} {} | ||
| 9 | |||
| 10 | void SplitterInfo::InitializeInfos(SplitterInfo* splitters, const u32 count) { | ||
| 11 | if (splitters == nullptr) { | ||
| 12 | return; | ||
| 13 | } | ||
| 14 | |||
| 15 | for (u32 i = 0; i < count; i++) { | ||
| 16 | auto& splitter{splitters[i]}; | ||
| 17 | splitter.destinations = nullptr; | ||
| 18 | splitter.destination_count = 0; | ||
| 19 | splitter.has_new_connection = true; | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | u32 SplitterInfo::Update(const InParameter* params) { | ||
| 24 | if (params->id != id) { | ||
| 25 | return 0; | ||
| 26 | } | ||
| 27 | sample_rate = params->sample_rate; | ||
| 28 | has_new_connection = true; | ||
| 29 | return static_cast<u32>((sizeof(InParameter) + 3 * sizeof(s32)) + | ||
| 30 | params->destination_count * sizeof(s32)); | ||
| 31 | } | ||
| 32 | |||
| 33 | SplitterDestinationData* SplitterInfo::GetData(const u32 destination_id) { | ||
| 34 | auto out_destination{destinations}; | ||
| 35 | u32 i{0}; | ||
| 36 | while (i < destination_id) { | ||
| 37 | if (out_destination == nullptr) { | ||
| 38 | break; | ||
| 39 | } | ||
| 40 | out_destination = out_destination->GetNext(); | ||
| 41 | i++; | ||
| 42 | } | ||
| 43 | |||
| 44 | return out_destination; | ||
| 45 | } | ||
| 46 | |||
| 47 | u32 SplitterInfo::GetDestinationCount() const { | ||
| 48 | return destination_count; | ||
| 49 | } | ||
| 50 | |||
| 51 | void SplitterInfo::SetDestinationCount(const u32 count) { | ||
| 52 | destination_count = count; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool SplitterInfo::HasNewConnection() const { | ||
| 56 | return has_new_connection; | ||
| 57 | } | ||
| 58 | |||
| 59 | void SplitterInfo::ClearNewConnectionFlag() { | ||
| 60 | has_new_connection = false; | ||
| 61 | } | ||
| 62 | |||
| 63 | void SplitterInfo::SetNewConnectionFlag() { | ||
| 64 | has_new_connection = true; | ||
| 65 | } | ||
| 66 | |||
| 67 | void SplitterInfo::UpdateInternalState() { | ||
| 68 | auto destination{destinations}; | ||
| 69 | while (destination != nullptr) { | ||
| 70 | destination->UpdateInternalState(); | ||
| 71 | destination = destination->GetNext(); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | void SplitterInfo::SetDestinations(SplitterDestinationData* destinations_) { | ||
| 76 | destinations = destinations_; | ||
| 77 | } | ||
| 78 | |||
| 79 | } // namespace AudioCore::AudioRenderer | ||
diff --git a/src/audio_core/renderer/splitter/splitter_info.h b/src/audio_core/renderer/splitter/splitter_info.h new file mode 100644 index 000000000..d1d75064c --- /dev/null +++ b/src/audio_core/renderer/splitter/splitter_info.h | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/renderer/splitter/splitter_destinations_data.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::AudioRenderer { | ||
| 10 | /** | ||
| 11 | * Represents a splitter, wraps multiple output destinations to split an input mix into. | ||
| 12 | */ | ||
| 13 | class SplitterInfo { | ||
| 14 | public: | ||
| 15 | struct InParameter { | ||
| 16 | /* 0x00 */ u32 magic; // 'SNDI' | ||
| 17 | /* 0x04 */ s32 id; | ||
| 18 | /* 0x08 */ u32 sample_rate; | ||
| 19 | /* 0x0C */ u32 destination_count; | ||
| 20 | }; | ||
| 21 | static_assert(sizeof(InParameter) == 0x10, "SplitterInfo::InParameter has the wrong size!"); | ||
| 22 | |||
| 23 | explicit SplitterInfo(s32 id); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Initialize the given splitters. | ||
| 27 | * | ||
| 28 | * @param splitters - Splitters to initialize. | ||
| 29 | * @param count - Number of splitters given. | ||
| 30 | */ | ||
| 31 | static void InitializeInfos(SplitterInfo* splitters, u32 count); | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Update this splitter. | ||
| 35 | * | ||
| 36 | * @param params - Input parameters to update with. | ||
| 37 | * @return The size in bytes of this splitter. | ||
| 38 | */ | ||
| 39 | u32 Update(const InParameter* params); | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Get a destination in this splitter. | ||
| 43 | * | ||
| 44 | * @param id - Destination id to get. | ||
| 45 | * @return Pointer to the destination, may be nullptr. | ||
| 46 | */ | ||
| 47 | SplitterDestinationData* GetData(u32 id); | ||
| 48 | |||
| 49 | /** | ||
| 50 | * Get the number of destinations in this splitter. | ||
| 51 | * | ||
| 52 | * @return The number of destiantions. | ||
| 53 | */ | ||
| 54 | u32 GetDestinationCount() const; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Set the number of destinations in this splitter. | ||
| 58 | * | ||
| 59 | * @param count - The new number of destiantions. | ||
| 60 | */ | ||
| 61 | void SetDestinationCount(u32 count); | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Check if the splitter has a new connection. | ||
| 65 | * | ||
| 66 | * @return True if there is a new connection, otherwise false. | ||
| 67 | */ | ||
| 68 | bool HasNewConnection() const; | ||
| 69 | |||
| 70 | /** | ||
| 71 | * Reset the new connection flag. | ||
| 72 | */ | ||
| 73 | void ClearNewConnectionFlag(); | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Mark as having a new connection. | ||
| 77 | */ | ||
| 78 | void SetNewConnectionFlag(); | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Update the state of all destinations. | ||
| 82 | */ | ||
| 83 | void UpdateInternalState(); | ||
| 84 | |||
| 85 | /** | ||
| 86 | * Set this splitter's destinations. | ||
| 87 | * | ||
| 88 | * @param destinations - The new destination list for this splitter. | ||
| 89 | */ | ||
| 90 | void SetDestinations(SplitterDestinationData* destinations); | ||
| 91 | |||
| 92 | private: | ||
| 93 | /// Id of this splitter | ||
| 94 | s32 id; | ||
| 95 | /// Sample rate of this splitter | ||
| 96 | u32 sample_rate{}; | ||
| 97 | /// Number of destinations in this splitter | ||
| 98 | u32 destination_count{}; | ||
| 99 | /// Does this splitter have a new connection? | ||
| 100 | bool has_new_connection{true}; | ||
| 101 | /// Pointer to the destinations of this splitter | ||
| 102 | SplitterDestinationData* destinations{}; | ||
| 103 | /// Number of channels this splitter manages | ||
| 104 | u32 channel_count{}; | ||
| 105 | }; | ||
| 106 | |||
| 107 | } // namespace AudioCore::AudioRenderer | ||