MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RootAudioBuffer.cpp
Go to the documentation of this file.
1#include "RootAudioBuffer.hpp"
2
3namespace MayaFlux::Buffers {
4
5ChannelProcessor::ChannelProcessor(const std::shared_ptr<Buffer>& root_buffer)
6 : m_root_buffer(std::dynamic_pointer_cast<RootAudioBuffer>(root_buffer))
7{
9}
10
11void ChannelProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
12{
13
14 auto root_audio_buffer = std::dynamic_pointer_cast<RootAudioBuffer>(buffer);
15 if (!root_audio_buffer || root_audio_buffer != m_root_buffer) {
16 return;
17 }
18
19 auto& output_data = root_audio_buffer->get_data();
20 const size_t buffer_size = output_data.size();
21 std::ranges::fill(output_data, 0.0);
22
23 if (root_audio_buffer->has_node_output()) {
24 const auto& node_output = root_audio_buffer->get_node_output();
25 const size_t node_size = node_output.size();
26 const size_t add_size = std::min(buffer_size, node_size);
27
28 for (size_t i = 0; i < add_size; ++i) {
29 output_data[i] += node_output[i];
30 }
31 }
32
33 std::vector<std::shared_ptr<AudioBuffer>> buffers_to_remove;
34 for (auto& child : m_root_buffer->get_child_buffers()) {
35 if (child->needs_removal()) {
36 buffers_to_remove.push_back(child);
37 }
38 }
39
40 uint32_t active_buffers = 0;
41 for (auto& child : m_root_buffer->get_child_buffers()) {
42 if (child->has_data_for_cycle() && !child->needs_removal() && !child->is_internal_only()) {
43 active_buffers++;
44 }
45 }
46
47 if (active_buffers > 0) {
48 for (auto& child : m_root_buffer->get_child_buffers()) {
49 if (child->has_data_for_cycle() && !child->needs_removal() && !child->is_internal_only()) {
50 double scale = 1.0;
51 if (child->needs_routing()) {
52 const auto& state = child->get_routing_state();
53 if (state.from_channel == m_root_buffer->get_channel_id()) {
54 scale = state.from_amount;
55 }
56 }
57 const auto& child_data = child->get_data();
58 for (size_t i = 0; i < std::min(child_data.size(), output_data.size()); i++) {
59 output_data[i] += (child_data[i] * scale) / active_buffers;
60 }
61 }
62 }
63 }
64
65 for (auto& child : buffers_to_remove) {
66 m_root_buffer->remove_child_buffer(child);
67 }
68}
69
70void ChannelProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
71{
72 auto root_audio_buffer = std::dynamic_pointer_cast<RootAudioBuffer>(buffer);
73 if (!root_audio_buffer) {
74 throw std::runtime_error("ChannelProcessor can only be attached to RootAudioBuffer");
75 }
76
78 throw std::runtime_error("ChannelProcessor token incompatible with RootAudioBuffer requirements");
79 }
80}
81
82bool ChannelProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
83{
84 auto root_audio_buffer = std::dynamic_pointer_cast<RootAudioBuffer>(buffer);
85 return root_audio_buffer != nullptr;
86}
87
88RootAudioBuffer::RootAudioBuffer(uint32_t channel_id, uint32_t num_samples)
89 : RootBuffer<AudioBuffer>(channel_id, num_samples)
90 , m_has_node_output(false)
91{
94}
95
97{
98 auto channel_processor = create_default_processor();
99 if (channel_processor) {
100 set_default_processor(channel_processor);
101 }
102}
103
104void RootAudioBuffer::set_node_output(const std::vector<double>& data)
105{
106 if (m_node_output.size() != data.size()) {
107 m_node_output.resize(data.size());
108 }
109 std::ranges::copy(data, m_node_output.begin());
110 m_has_node_output = true;
111}
112
114{
115 if (this->has_pending_operations()) {
117 }
118
120 m_default_processor->process(shared_from_this());
121 }
122}
123
124void RootAudioBuffer::resize(uint32_t num_samples)
125{
126 AudioBuffer::resize(num_samples);
127
128 m_node_output.resize(num_samples, 0.0);
129
130 for (auto& child : m_child_buffers) {
131 if (child) {
132 child->resize(num_samples);
133 }
134 }
135}
136
137std::shared_ptr<BufferProcessor> RootAudioBuffer::create_default_processor()
138{
139 return std::make_shared<ChannelProcessor>(shared_from_this());
140}
141
146
147void FinalLimiterProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
148{
149 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
150 if (!audio_buffer) {
151 throw std::runtime_error("FinalLimiterProcessor can only be attached to AudioBuffer-derived types");
152 }
153
155 throw std::runtime_error("FinalLimiterProcessor token incompatible with audio processing requirements");
156 }
157}
158
159bool FinalLimiterProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
160{
161 return std::dynamic_pointer_cast<AudioBuffer>(buffer) != nullptr;
162}
163
164void FinalLimiterProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
165{
166 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
167 if (!audio_buffer) {
168 return;
169 }
170
171 auto& data = audio_buffer->get_data();
172
173 constexpr double threshold = 0.95;
174 constexpr double knee = 0.1;
175
176 for (double& sample : data) {
177 const double abs_sample = std::abs(sample);
178
179 if (abs_sample > threshold) {
180 const double excess = abs_sample - threshold;
181 const double compressed_excess = std::tanh(excess / knee) * knee;
182 const double limited_abs = threshold + compressed_excess;
183
184 sample = (sample >= 0.0) ? limited_abs : -limited_abs;
185 }
186 }
187}
188}
virtual void resize(uint32_t num_samples)
Adjusts the audio buffer's sample capacity.
std::shared_ptr< BufferProcessor > m_default_processor
Default audio transformation processor for this buffer.
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Sets the default audio transformation processor for this buffer.
bool m_process_default
Whether the audio buffer should be processed using its default processor.
Concrete audio implementation of the Buffer interface for double-precision audio data.
ChannelProcessor(const std::shared_ptr< Buffer > &root_buffer)
Creates a new channel aggregation processor.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when processor is attached to a buffer.
std::shared_ptr< RootAudioBuffer > m_root_buffer
Shared pointer to the root buffer this processor manages.
bool is_compatible_with(const std::shared_ptr< Buffer > &buffer) const override
Checks compatibility with a specific buffer type.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
Processes a buffer by combining tributary buffers and node network output.
FinalLimiterProcessor()
Creates a new final limiter processor.
bool is_compatible_with(const std::shared_ptr< Buffer > &buffer) const override
Checks compatibility with a specific buffer type.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
Processes a buffer by enforcing boundary conditions.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when processor is attached to a buffer.
std::vector< double > m_node_output
Data received directly from computational node networks.
bool m_has_node_output
Flag indicating if node network output data is present.
void set_node_output(const std::vector< double > &data)
Sets direct node network output data for this buffer.
std::shared_ptr< BufferProcessor > create_default_processor() override
Creates the default processor for this buffer type.
RootAudioBuffer(uint32_t channel_id, uint32_t num_samples=512)
Creates a new root aggregation buffer for a channel.
void resize(uint32_t num_samples) override
Resizes this buffer and all tributary buffers.
void process_default() override
Processes this buffer using its default aggregation processor.
Top-level aggregation buffer for computational data streams.
void process_pending_buffer_operations()
Process pending operations - call this at start of processing cycles.
ProcessingToken m_preferred_processing_token
Preferred processing token for this root buffer.
TokenEnforcementStrategy m_token_enforcement_strategy
Current token enforcement strategy for this root buffer.
std::vector< std::shared_ptr< AudioBuffer > > m_child_buffers
Vector of tributary buffers that contribute to this root buffer.
bool are_tokens_compatible(ProcessingToken preferred, ProcessingToken current)
Determines if two processing tokens are compatible for joint execution.
@ AUDIO_BACKEND
Standard audio processing backend configuration.
@ STRICT
Strictly enforces token assignment with no cross-token sharing.