MayaFlux 0.1.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(std::shared_ptr<Buffer> root_buffer)
6 : m_root_buffer(std::dynamic_pointer_cast<RootAudioBuffer>(root_buffer))
7{
9}
10
11void ChannelProcessor::processing_function(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 const auto& child_data = child->get_data();
51 for (size_t i = 0; i < std::min(child_data.size(), output_data.size()); i++) {
52 output_data[i] += child_data[i] / active_buffers;
53 }
54 }
55 }
56 }
57
58 for (auto& child : buffers_to_remove) {
59 m_root_buffer->remove_child_buffer(child);
60 }
61}
62
63void ChannelProcessor::on_attach(std::shared_ptr<Buffer> buffer)
64{
65 auto root_audio_buffer = std::dynamic_pointer_cast<RootAudioBuffer>(buffer);
66 if (!root_audio_buffer) {
67 throw std::runtime_error("ChannelProcessor can only be attached to RootAudioBuffer");
68 }
69
71 throw std::runtime_error("ChannelProcessor token incompatible with RootAudioBuffer requirements");
72 }
73}
74
75bool ChannelProcessor::is_compatible_with(std::shared_ptr<Buffer> buffer) const
76{
77 auto root_audio_buffer = std::dynamic_pointer_cast<RootAudioBuffer>(buffer);
78 return root_audio_buffer != nullptr;
79}
80
81RootAudioBuffer::RootAudioBuffer(uint32_t channel_id, uint32_t num_samples)
82 : RootBuffer<AudioBuffer>(channel_id, num_samples)
83 , m_has_node_output(false)
84{
87}
88
90{
91 auto channel_processor = create_default_processor();
92 if (channel_processor) {
93 set_default_processor(channel_processor);
94 }
95}
96
97void RootAudioBuffer::set_node_output(const std::vector<double>& data)
98{
99 if (m_node_output.size() != data.size()) {
100 m_node_output.resize(data.size());
101 }
102 std::ranges::copy(data, m_node_output.begin());
103 m_has_node_output = true;
104}
105
107{
108 if (this->has_pending_operations()) {
110 }
111
113 m_default_processor->process(shared_from_this());
114 }
115}
116
117void RootAudioBuffer::resize(uint32_t num_samples)
118{
119 AudioBuffer::resize(num_samples);
120
121 m_node_output.resize(num_samples, 0.0);
122
123 for (auto& child : m_child_buffers) {
124 if (child) {
125 child->resize(num_samples);
126 }
127 }
128}
129
130std::shared_ptr<BufferProcessor> RootAudioBuffer::create_default_processor()
131{
132 return std::make_shared<ChannelProcessor>(shared_from_this());
133}
134
139
140void FinalLimiterProcessor::on_attach(std::shared_ptr<Buffer> buffer)
141{
142 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
143 if (!audio_buffer) {
144 throw std::runtime_error("FinalLimiterProcessor can only be attached to AudioBuffer-derived types");
145 }
146
148 throw std::runtime_error("FinalLimiterProcessor token incompatible with audio processing requirements");
149 }
150}
151
152bool FinalLimiterProcessor::is_compatible_with(std::shared_ptr<Buffer> buffer) const
153{
154 return std::dynamic_pointer_cast<AudioBuffer>(buffer) != nullptr;
155}
156
157void FinalLimiterProcessor::processing_function(std::shared_ptr<Buffer> buffer)
158{
159 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
160 if (!audio_buffer) {
161 return;
162 }
163
164 auto& data = audio_buffer->get_data();
165
166 constexpr double threshold = 0.95;
167 constexpr double knee = 0.1;
168
169 for (double& sample : data) {
170 const double abs_sample = std::abs(sample);
171
172 if (abs_sample > threshold) {
173 const double excess = abs_sample - threshold;
174 const double compressed_excess = std::tanh(excess / knee) * knee;
175 const double limited_abs = threshold + compressed_excess;
176
177 sample = (sample >= 0.0) ? limited_abs : -limited_abs;
178 }
179 }
180}
181}
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.
virtual void set_default_processor(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(std::shared_ptr< Buffer > root_buffer)
Creates a new channel aggregation processor.
void processing_function(std::shared_ptr< Buffer > buffer) override
Processes a buffer by combining tributary buffers and node network output.
std::shared_ptr< RootAudioBuffer > m_root_buffer
Shared pointer to the root buffer this processor manages.
bool is_compatible_with(std::shared_ptr< Buffer > buffer) const override
Checks compatibility with a specific buffer type.
void on_attach(std::shared_ptr< Buffer > buffer) override
Called when processor is attached to a buffer.
void on_attach(std::shared_ptr< Buffer > buffer) override
Called when processor is attached to a buffer.
FinalLimiterProcessor()
Creates a new final limiter processor.
void processing_function(std::shared_ptr< Buffer > buffer) override
Processes a buffer by enforcing boundary conditions.
bool is_compatible_with(std::shared_ptr< Buffer > buffer) const override
Checks compatibility with a specific buffer type.
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.
virtual 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.
virtual void resize(uint32_t num_samples) override
Resizes this buffer and all tributary buffers.
virtual 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.