MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
BufferSupplyMixing.cpp
Go to the documentation of this file.
2
9
10namespace MayaFlux::Buffers {
11
13 TokenUnitManager& unit_manager,
14 BufferAccessControl& access_control)
15 : m_unit_manager(unit_manager)
16 , m_access_control(access_control)
17{
18}
19
20// ============================================================================
21// Buffer Supply and Mixing
22// ============================================================================
23
25 const std::shared_ptr<AudioBuffer>& buffer,
26 ProcessingToken token,
27 uint32_t channel,
28 double mix)
29{
30 if (!buffer) {
32 "BufferSupplyMixing: Invalid buffer for supplying");
33 return false;
34 }
35
36 if (buffer->get_channel_id() == channel) {
38 "BufferSupplyMixing: Buffer already has the correct channel ID {}", channel);
39 return false;
40 }
41
44 "BufferSupplyMixing: Token/channel combination out of range for supplying (token: {}, channel: {})",
45 static_cast<int>(token), channel);
46 return false;
47 }
48
49 auto& unit = m_unit_manager.get_audio_unit_mutable(token);
50 auto root_buffer = unit.get_buffer(channel);
51 auto processing_chain = unit.get_chain(channel);
52
53 std::shared_ptr<MixProcessor> mix_processor = processing_chain->get_processor<MixProcessor>(root_buffer);
54
55 if (!mix_processor) {
56 mix_processor = std::make_shared<MixProcessor>();
57 processing_chain->add_processor(mix_processor, root_buffer);
58 }
59
60 return mix_processor->register_source(buffer, mix, false);
61}
62
64 const std::shared_ptr<AudioBuffer>& buffer,
65 ProcessingToken token,
66 uint32_t channel)
67{
68 if (!buffer) {
70 "BufferSupplyMixing: Invalid buffer for removal");
71 return false;
72 }
73
76 "BufferSupplyMixing: Token/channel combination out of range for removal (token: {}, channel: {})",
77 static_cast<int>(token), channel);
78 return false;
79 }
80
81 auto& unit = m_unit_manager.get_audio_unit_mutable(token);
82 auto root_buffer = unit.get_buffer(channel);
83 auto processing_chain = unit.get_chain(channel);
84
85 if (std::shared_ptr<MixProcessor> mix_processor = processing_chain->get_processor<MixProcessor>(root_buffer)) {
86 return mix_processor->remove_source(buffer);
87 }
88 return false;
89}
90
91// ============================================================================
92// Interleaved Data I/O
93// ============================================================================
94
96 const double* interleaved_data,
97 uint32_t num_frames,
98 ProcessingToken token,
99 uint32_t num_channels)
100{
101 if (!m_unit_manager.has_audio_unit(token)) {
102 return;
103 }
104
105 auto& unit = m_unit_manager.get_audio_unit_mutable(token);
106 uint32_t channels_to_process = std::min(num_channels, unit.channel_count);
107
108 for (uint32_t channel = 0; channel < channels_to_process; ++channel) {
109 auto& buffer_data = unit.get_buffer(channel)->get_data();
110 uint32_t frames_to_copy = std::min(num_frames, static_cast<uint32_t>(buffer_data.size()));
111
112 for (uint32_t frame = 0; frame < frames_to_copy; ++frame) {
113 buffer_data[frame] = interleaved_data[frame * num_channels + channel];
114 }
115 }
116}
117
119 double* interleaved_data,
120 uint32_t num_frames,
121 ProcessingToken token,
122 uint32_t num_channels) const
123{
124 if (!m_unit_manager.has_audio_unit(token)) {
125 return;
126 }
127
128 const auto& unit = m_unit_manager.get_audio_unit(token);
129 uint32_t channels_to_process = std::min(num_channels, unit.channel_count);
130
131 for (uint32_t channel = 0; channel < channels_to_process; ++channel) {
132 const auto& buffer_data = unit.get_buffer(channel)->get_data();
133 uint32_t frames_to_copy = std::min(num_frames, static_cast<uint32_t>(buffer_data.size()));
134
135 for (uint32_t frame = 0; frame < frames_to_copy; ++frame) {
136 interleaved_data[frame * num_channels + channel] = buffer_data[frame];
137 }
138 }
139}
140
141// ============================================================================
142// Buffer Cloning
143// ============================================================================
144
145std::vector<std::shared_ptr<AudioBuffer>> BufferSupplyMixing::clone_audio_buffer_for_channels(
146 const std::shared_ptr<AudioBuffer>& buffer,
147 const std::vector<uint32_t>& channels,
148 ProcessingToken token)
149{
150 if (channels.empty()) {
152 "BufferSupplyMixing: No channels specified for cloning");
153 return {};
154 }
155 if (!buffer) {
157 "BufferSupplyMixing: Invalid buffer for cloning");
158 return {};
159 }
160
161 if (!m_unit_manager.has_audio_unit(token)) {
163 "BufferSupplyMixing: Token not found for cloning");
164 return {};
165 }
166
167 auto& unit = m_unit_manager.get_audio_unit_mutable(token);
168
169 std::vector<std::shared_ptr<AudioBuffer>> cloned_buffers {};
170
171 for (const auto channel : channels) {
172 if (channel >= unit.channel_count) {
174 "BufferSupplyMixing: Channel {} out of range for cloning", channel);
175 return cloned_buffers;
176 }
177
178 auto cloned_buffer = buffer->clone_to(channel);
179 m_access_control.add_audio_buffer(cloned_buffer, token, channel);
180 cloned_buffers.push_back(cloned_buffer);
181 }
182
183 return cloned_buffers;
184}
185
187 const std::shared_ptr<AudioBuffer>& buffer,
188 uint32_t target_channel,
189 uint32_t fade_cycles,
190 ProcessingToken token)
191{
192 if (!buffer) {
194 "BufferSupplyMixing: Invalid buffer for routing");
195 return;
196 }
197
198 uint32_t current_channel = buffer->get_channel_id();
199
200 if (current_channel == target_channel) {
202 "BufferSupplyMixing: Buffer already on target channel {}", target_channel);
203 return;
204 }
205
206 if (!m_unit_manager.has_audio_unit(token)) {
208 "BufferSupplyMixing: Invalid token for routing");
209 return;
210 }
211
212 auto& unit = m_unit_manager.get_audio_unit_mutable(token);
213 if (target_channel >= unit.channel_count) {
215 "BufferSupplyMixing: Target channel {} out of range", target_channel);
216 return;
217 }
218
219 uint32_t fade_blocks = std::max(1U, fade_cycles);
220
221 BufferRoutingState state;
222 state.from_channel = current_channel;
223 state.to_channel = target_channel;
224 state.fade_cycles = fade_blocks;
225 state.from_amount = 1.0;
226 state.to_amount = 0.0;
227 state.cycles_elapsed = 0;
228 state.phase = BufferRoutingState::ACTIVE;
229
230 buffer->get_routing_state() = state;
231
232 supply_audio_buffer_to(buffer, token, target_channel, 0.0);
233}
234
236{
237 if (!m_unit_manager.has_audio_unit(token)) {
238 return;
239 }
240
241 auto& unit = m_unit_manager.get_audio_unit_mutable(token);
242
243 for (uint32_t channel = 0; channel < unit.channel_count; ++channel) {
244 auto root_buffer = unit.get_buffer(channel);
245
246 for (auto& child : root_buffer->get_child_buffers()) {
247 if (!child->needs_routing()) {
248 continue;
249 }
250
251 auto& state = child->get_routing_state();
252
253 if (state.phase != BufferRoutingState::ACTIVE) {
254 continue;
255 }
256
258
259 auto root_target = unit.get_buffer(state.to_channel);
260 auto chain_target = unit.get_chain(state.to_channel);
261
262 if (auto mix_proc = chain_target->get_processor<MixProcessor>(root_target)) {
263 mix_proc->update_source_mix(child, state.to_amount);
264 }
265 }
266 }
267}
268
270{
271 if (!m_unit_manager.has_audio_unit(token)) {
272 return;
273 }
274
275 auto& unit = m_unit_manager.get_audio_unit_mutable(token);
276
277 std::vector<std::tuple<std::shared_ptr<AudioBuffer>, uint32_t, uint32_t>> buffers_to_move;
278
279 for (uint32_t channel = 0; channel < unit.channel_count; ++channel) {
280 auto root_buffer = unit.get_buffer(channel);
281
282 for (auto& child : root_buffer->get_child_buffers()) {
283 if (!child->needs_routing()) {
284 continue;
285 }
286
287 auto& state = child->get_routing_state();
288
289 if (state.phase == BufferRoutingState::COMPLETED) {
290 buffers_to_move.emplace_back(child, state.from_channel, state.to_channel);
291 }
292 }
293 }
294
295 for (auto& [buffer, from_ch, to_ch] : buffers_to_move) {
296 auto root_from = unit.get_buffer(from_ch);
297 auto root_to = unit.get_buffer(to_ch);
298
299 root_from->remove_child_buffer(buffer);
300
301 buffer->set_channel_id(to_ch);
302 root_to->add_child_buffer(buffer);
303
304 remove_supplied_audio_buffer(buffer, token, to_ch);
305
306 buffer->get_routing_state() = BufferRoutingState {};
307 }
308}
309
311{
312 state.cycles_elapsed++;
313
314 double progress = 0.0;
315 if (state.fade_cycles > 0) {
316 progress = std::min(1.0, static_cast<double>(state.cycles_elapsed) / state.fade_cycles);
317 } else {
318 state.from_amount = 0.0;
319 state.to_amount = 1.0;
320 state.phase = BufferRoutingState::COMPLETED;
321 return;
322 }
323
324 state.from_amount = 1.0 - progress;
325 state.to_amount = progress;
326
327 if (state.cycles_elapsed >= state.fade_cycles) {
328 state.phase = BufferRoutingState::COMPLETED;
329 }
330}
331
332} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
void add_audio_buffer(const std::shared_ptr< AudioBuffer > &buffer, ProcessingToken token, uint32_t channel)
Adds an audio buffer to a token and channel.
Token-aware buffer and unit access patterns.
bool supply_audio_buffer_to(const std::shared_ptr< AudioBuffer > &buffer, ProcessingToken token, uint32_t channel, double mix=1.0)
Supplies an external audio buffer to a specific token and channel.
void route_buffer_to_channel(const std::shared_ptr< AudioBuffer > &buffer, uint32_t target_channel, uint32_t fade_cycles, ProcessingToken token)
Routes a buffer's processing from one channel to another with fade.
bool remove_supplied_audio_buffer(const std::shared_ptr< AudioBuffer > &buffer, ProcessingToken token, uint32_t channel)
Removes a previously supplied buffer from a token and channel.
void fill_audio_interleaved(double *interleaved_data, uint32_t num_frames, ProcessingToken token, uint32_t num_channels) const
Fills interleaved buffer from audio token channels.
void update_routing_state(BufferRoutingState &state)
Updates a single buffer's routing state.
TokenUnitManager & m_unit_manager
Reference to the token/unit manager.
BufferSupplyMixing(TokenUnitManager &unit_manager, BufferAccessControl &access_control)
Creates a new supply/mixing control handler.
std::vector< std::shared_ptr< AudioBuffer > > clone_audio_buffer_for_channels(const std::shared_ptr< AudioBuffer > &buffer, const std::vector< uint32_t > &channels, ProcessingToken token)
Clones an audio buffer for each channel in the specified list.
void update_routing_states_for_cycle(ProcessingToken token)
Updates routing states for all buffers undergoing transitions.
void cleanup_completed_routing(ProcessingToken token)
Cleans up completed routing transitions.
BufferAccessControl & m_access_control
Reference to the buffer access control.
void fill_audio_from_interleaved(const double *interleaved_data, uint32_t num_frames, ProcessingToken token, uint32_t num_channels)
Fills audio token channels from interleaved source data.
Processes multiple audio buffers and mixes their data into a single output buffer.
uint32_t get_audio_channel_count(ProcessingToken token) const
Gets the number of channels in an audio unit.
const RootAudioUnit & get_audio_unit(ProcessingToken token) const
Gets an existing audio unit without creating if missing.
bool has_audio_unit(ProcessingToken token) const
Checks if an audio unit exists for the given token.
RootAudioUnit & get_audio_unit_mutable(ProcessingToken token)
Gets an existing audio unit without creating if missing (mutable)
Token-scoped unit storage and lifecycle management.
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
@ BufferManagement
Buffer Management (Buffers::BufferManager, creating buffers)
@ Buffers
Buffers, Managers, processors and processing chains.
@ Core
Core engine, backend, subsystems.
std::vector< double > mix(const std::vector< std::vector< double > > &streams)
Mix multiple data streams with equal weighting.
Definition Yantra.cpp:1021
Represents the state of routing transitions for a buffer.
std::shared_ptr< RootAudioBuffer > get_buffer(uint32_t channel) const