14 auto root_audio_buffer = std::dynamic_pointer_cast<RootAudioBuffer>(buffer);
15 if (!root_audio_buffer || root_audio_buffer !=
m_root_buffer) {
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);
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);
28 for (
size_t i = 0; i < add_size; ++i) {
29 output_data[i] += node_output[i];
33 std::vector<std::shared_ptr<AudioBuffer>> buffers_to_remove;
35 if (child->needs_removal()) {
36 buffers_to_remove.push_back(child);
40 uint32_t active_buffers = 0;
42 if (child->has_data_for_cycle() && !child->needs_removal() && !child->is_internal_only()) {
47 if (active_buffers > 0) {
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;
58 for (
auto& child : buffers_to_remove) {
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");
71 throw std::runtime_error(
"ChannelProcessor token incompatible with RootAudioBuffer requirements");
77 auto root_audio_buffer = std::dynamic_pointer_cast<RootAudioBuffer>(buffer);
78 return root_audio_buffer !=
nullptr;
83 , m_has_node_output(false)
92 if (channel_processor) {
125 child->resize(num_samples);
132 return std::make_shared<ChannelProcessor>(shared_from_this());
142 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
144 throw std::runtime_error(
"FinalLimiterProcessor can only be attached to AudioBuffer-derived types");
148 throw std::runtime_error(
"FinalLimiterProcessor token incompatible with audio processing requirements");
154 return std::dynamic_pointer_cast<AudioBuffer>(buffer) !=
nullptr;
159 auto audio_buffer = std::dynamic_pointer_cast<AudioBuffer>(buffer);
164 auto& data = audio_buffer->get_data();
166 constexpr double threshold = 0.95;
167 constexpr double knee = 0.1;
169 for (
double& sample : data) {
170 const double abs_sample = std::abs(sample);
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;
177 sample = (sample >= 0.0) ? limited_abs : -limited_abs;
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.
ProcessingToken m_processing_token
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.
bool has_pending_operations() const
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.