MayaFlux 0.4.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 error<std::runtime_error>(Journal::Component::Buffers, Journal::Context::BufferProcessing, std::source_location::current(),
75 "ChannelProcessor can only be attached to RootAudioBuffer");
76 }
77
79 error<std::runtime_error>(Journal::Component::Buffers, Journal::Context::BufferProcessing, std::source_location::current(),
80 "ChannelProcessor token incompatible with RootAudioBuffer requirements");
81 }
82}
83
84bool ChannelProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
85{
86 auto root_audio_buffer = std::dynamic_pointer_cast<RootAudioBuffer>(buffer);
87 return root_audio_buffer != nullptr;
88}
89
90RootAudioBuffer::RootAudioBuffer(uint32_t channel_id, uint32_t num_samples)
91 : RootBuffer<AudioBuffer>(channel_id, num_samples)
92 , m_has_node_output(false)
93{
96}
97
99{
100 auto channel_processor = create_default_processor();
101 if (channel_processor) {
102 set_default_processor(channel_processor);
103 }
104}
105
106void RootAudioBuffer::set_node_output(const std::vector<double>& data)
107{
108 if (m_node_output.size() != data.size()) {
109 m_node_output.resize(data.size());
110 }
111 std::ranges::copy(data, m_node_output.begin());
112 m_has_node_output = true;
113}
114
116{
117 if (this->has_pending_operations()) {
119 }
120
122 m_default_processor->process(shared_from_this());
123 }
124}
125
126void RootAudioBuffer::resize(uint32_t num_samples)
127{
128 AudioBuffer::resize(num_samples);
129
130 m_node_output.resize(num_samples, 0.0);
131
132 for (auto& child : m_child_buffers) {
133 if (child) {
134 child->resize(num_samples);
135 }
136 }
137}
138
139std::shared_ptr<BufferProcessor> RootAudioBuffer::create_default_processor()
140{
141 return std::make_shared<ChannelProcessor>(shared_from_this());
142}
143
148
149void FinalLimiterProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
150{
151 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
152 if (!audio_buffer) {
153 error<std::runtime_error>(Journal::Component::Buffers, Journal::Context::BufferProcessing, std::source_location::current(),
154 "FinalLimiterProcessor can only be attached to AudioBuffer-derived types");
155 }
156
158 error<std::runtime_error>(Journal::Component::Buffers, Journal::Context::BufferProcessing, std::source_location::current(),
159 "FinalLimiterProcessor token incompatible with audio processing requirements");
160 }
161}
162
163bool FinalLimiterProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
164{
165 return std::dynamic_pointer_cast<AudioBuffer>(buffer) != nullptr;
166}
167
168void FinalLimiterProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
169{
170 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
171 if (!audio_buffer) {
172 return;
173 }
174
175 auto& data = audio_buffer->get_data();
176
177 constexpr double threshold = 0.95;
178 constexpr double knee = 0.1;
179
180 for (double& sample : data) {
181 const double abs_sample = std::abs(sample);
182
183 if (abs_sample > threshold) {
184 const double excess = abs_sample - threshold;
185 const double compressed_excess = std::tanh(excess / knee) * knee;
186 const double limited_abs = threshold + compressed_excess;
187
188 sample = (sample >= 0.0) ? limited_abs : -limited_abs;
189 }
190 }
191}
192}
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.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.