Vital
Loading...
Searching...
No Matches
processor_router.cpp
Go to the documentation of this file.
1
6#include "processor_router.h"
7
8#include "feedback.h"
9#include "synth_constants.h"
10
11#include <algorithm>
12#include <vector>
13
14namespace vital {
15
16 ProcessorRouter::ProcessorRouter(int num_inputs, int num_outputs, bool control_rate) :
17 Processor(num_inputs, num_outputs, control_rate),
20 local_order_(kMaxModulationConnections),
21 global_feedback_order_(new std::vector<const Feedback*>()),
22 global_changes_(new int(0)), local_changes_(0),
23 dependencies_(new CircularQueue<const Processor*>(kMaxModulationConnections)),
24 dependencies_visited_(new CircularQueue<const Processor*>(kMaxModulationConnections)),
25 dependency_inputs_(new CircularQueue<const Processor*>(kMaxModulationConnections)) { }
26
28 Processor(original),
29 global_order_(original.global_order_), global_reorder_(original.global_reorder_),
30 global_feedback_order_(original.global_feedback_order_),
31 global_changes_(original.global_changes_),
32 local_changes_(original.local_changes_) {
33
34 local_order_.reserve(global_order_->capacity());
35 local_order_.assign(global_order_->size(), nullptr);
36 local_feedback_order_.assign(global_feedback_order_->size(), nullptr);
37
38 int num_processors = global_order_->size();
39 for (int i = 0; i < num_processors; ++i) {
40 const Processor* next = global_order_->at(i);
41 std::unique_ptr<Processor> clone(next->clone());
42 local_order_[i] = clone.get();
43 processors_[next] = { 0, std::move(clone) };
44 }
45
46 int num_feedbacks = static_cast<int>(global_feedback_order_->size());
47 for (int i = 0; i < num_feedbacks; ++i) {
48 const Feedback* next = global_feedback_order_->at(i);
49 std::unique_ptr<Feedback> clone((Feedback*)(next->clone()));
51 feedback_processors_[next] = { 0, std::move(clone) };
52 }
53 }
54
56
57 void ProcessorRouter::process(int num_samples) {
58 if (shouldUpdate())
60
61 // Refresh feedback loops
62 int num_feedbacks = static_cast<int>(local_feedback_order_.size());
63 for (int i = 0; i < num_feedbacks; ++i)
64 local_feedback_order_[i]->refreshOutput(num_samples);
65
66 // Process all Processors in order
67 int normal_samples = std::max(1, num_samples / getOversampleAmount());
68 for (Processor* processor : local_order_) {
69 if (processor->enabled()) {
70 int processor_samples = normal_samples * processor->getOversampleAmount();
71 VITAL_ASSERT(processor->checkInputAndOutputSize(processor_samples));
72 processor->process(processor_samples);
73 VITAL_ASSERT(utils::isFinite(processor->output()->buffer, processor->isControlRate() ? 0 : processor_samples));
74 }
75 }
76
77 // Update feedback objects with processed output
78 for (int i = 0; i < num_feedbacks; ++i) {
79 if (global_feedback_order_->at(i)->enabled())
80 local_feedback_order_[i]->process(num_samples);
81 }
82 }
83
86 for (Processor* processor : local_order_)
87 processor->init();
88 }
89
90 void ProcessorRouter::setSampleRate(int sample_rate) {
91 Processor::setSampleRate(sample_rate);
92 if (shouldUpdate())
94
95 int num_processors = static_cast<int>(local_order_.size());
96 for (int i = 0; i < num_processors; ++i)
97 local_order_[i]->setSampleRate(sample_rate);
98
99 int num_feedbacks = static_cast<int>(local_feedback_order_.size());
100 for (int i = 0; i < num_feedbacks; ++i)
101 local_feedback_order_[i]->setSampleRate(sample_rate);
102 }
103
106 if (shouldUpdate())
108
109 for (auto& idle_processor : idle_processors_)
110 idle_processor.second->setOversampleAmount(oversample);
111
112 int num_processors = static_cast<int>(local_order_.size());
113 for (int i = 0; i < num_processors; ++i)
114 local_order_[i]->setOversampleAmount(oversample);
115
116 int num_feedbacks = static_cast<int>(local_feedback_order_.size());
117 for (int i = 0; i < num_feedbacks; ++i)
119 }
120
122 VITAL_ASSERT(processor->router() == nullptr);
123 global_order_->ensureSpace();
124 global_reorder_->ensureCapacity(global_order_->capacity());
125 local_order_.ensureSpace();
126 addProcessorRealTime(processor);
127 }
128
130 VITAL_ASSERT(processor->router() == nullptr);
131 (*global_changes_)++;
133
134 processor->router(this);
135 if (getOversampleAmount() > 1)
137
138 global_order_->push_back(processor);
139 processors_[processor] = { 0, std::unique_ptr<Processor>(processor) };
140 local_order_.push_back(processor);
141
142 for (int i = 0; i < processor->numInputs(); ++i)
143 connect(processor, processor->input(i)->source, i);
144 }
145
147 processor->router(this);
148 idle_processors_[processor] = std::unique_ptr<Processor>(processor);
149 }
150
152 for (int i = 0; i < processor->numInputs(); ++i)
153 disconnect(processor, processor->input(i)->source);
154
155 VITAL_ASSERT(processor->router() == this);
156 (*global_changes_)++;
158 global_order_->remove(processor);
159 local_order_.remove(processor);
160
161 Processor* old_processor = processors_[processor].second.release();
162 VITAL_ASSERT(old_processor == processor);
163 UNUSED(old_processor);
164 processor->router(nullptr);
165 processors_.erase(processor);
166 }
167
168 void ProcessorRouter::connect(Processor* destination, const Output* source, int index) {
169 if (isDownstream(destination, source->owner)) {
170 // Cycle detected - insert feedback
171 Feedback* feedback = new cr::Feedback();
172 feedback->plug(source);
173 destination->plug(feedback, index);
174 addFeedback(feedback);
175 }
176 else {
177 // No cycle, just reorder if needed
178 reorder(destination);
179 }
180 }
181
182 void ProcessorRouter::disconnect(const Processor* destination, const Output* source) {
183 if (isDownstream(destination, source->owner)) {
184 // If there's a cycle, we may need to remove a Feedback node
185 for (int i = 0; i < destination->numInputs(); ++i) {
186 const Processor* owner = destination->input(i)->source->owner;
187 if (feedback_processors_.find(owner) != feedback_processors_.end()) {
188 Feedback* feedback = feedback_processors_[owner].second.get();
189 if (feedback->input()->source == source)
190 removeFeedback(feedback);
191 destination->input(i)->source = &Processor::null_source_;
192 }
193 }
194 }
195 }
196
198 (*global_changes_)++;
200
201 getDependencies(processor);
202 if (dependencies_->size() == 0) {
203 if (router_)
204 router_->reorder(processor);
205 return;
206 }
207
208 int num_processors = static_cast<int>(processors_.size());
209 global_reorder_->clear();
210
211 for (int i = 0; i < num_processors; ++i) {
212 Processor* current_processor = global_order_->at(i);
213 if (current_processor != processor && dependencies_->contains(current_processor))
214 global_reorder_->push_back(current_processor);
215 }
216
217 if (processors_.find(processor) != processors_.end())
218 global_reorder_->push_back(processor);
219
220 for (int i = 0; i < num_processors; ++i) {
221 Processor* current_processor = global_order_->at(i);
222 if (current_processor != processor && !dependencies_->contains(current_processor))
223 global_reorder_->push_back(current_processor);
224 }
225
226 for (int i = 0; i < num_processors; ++i)
227 global_order_->at(i) = global_reorder_->at(i);
228
229 if (router_)
230 router_->reorder(processor);
231 }
232
233 bool ProcessorRouter::isDownstream(const Processor* first, const Processor* second) const {
234 getDependencies(second);
235 return dependencies_->contains(first);
236 }
237
238 bool ProcessorRouter::areOrdered(const Processor* first, const Processor* second) const {
239 const Processor* first_context = getContext(first);
240 const Processor* second_context = getContext(second);
241
242 if (first_context && second_context) {
243 size_t num_processors = global_order_->size();
244 for (size_t i = 0; i < num_processors; ++i) {
245 if (global_order_->at(i) == first_context)
246 return true;
247 else if (global_order_->at(i) == second_context)
248 return false;
249 }
250 }
251 else if (router_)
252 return router_->areOrdered(first, second);
253
254 return true;
255 }
256
257 bool ProcessorRouter::isPolyphonic(const Processor* processor) const {
258 if (router_)
259 return router_->isPolyphonic(this);
260 return false;
261 }
262
264 if (isPolyphonic(this))
265 return router_->getMonoRouter();
266 return this;
267 }
268
272
274 for (auto feedback : local_feedback_order_)
275 feedback->reset(reset_mask);
276 }
277
279 feedback->router(this);
280 global_feedback_order_->push_back(feedback);
281 local_feedback_order_.push_back(feedback);
282 feedback_processors_[feedback] = { 0, std::unique_ptr<Feedback>(feedback) };
283 }
284
286 (*global_changes_)++;
288
289 auto pos = std::find(global_feedback_order_->begin(), global_feedback_order_->end(), feedback);
291 global_feedback_order_->erase(pos, pos + 1);
292
293 auto local_pos = std::find(local_feedback_order_.begin(), local_feedback_order_.end(), feedback);
294 VITAL_ASSERT(local_pos != local_feedback_order_.end());
295 local_feedback_order_.erase(local_pos, local_pos + 1);
296
297 feedback_processors_.erase(feedback);
298 }
299
308
310 if (global_order_->size() > local_order_.capacity())
311 local_order_.reserve(global_order_->capacity());
312
313 local_order_.assign(global_order_->size(), nullptr);
314 local_feedback_order_.assign(global_feedback_order_->size(), nullptr);
315
316 int num_processors = global_order_->size();
317 for (int i = 0; i < num_processors; ++i) {
318 Processor* next = global_order_->at(i);
319 if (next->hasState()) {
320 if (processors_.count(next) == 0)
321 processors_[next] = { 0, std::unique_ptr<Processor>(next->clone()) };
322 local_order_[i] = processors_[next].second.get();
323 }
324 else
325 local_order_[i] = next;
326 }
327
328 int num_feedbacks = static_cast<int>(global_feedback_order_->size());
329 for (int i = 0; i < num_feedbacks; ++i) {
330 const Feedback* next = global_feedback_order_->at(i);
331 if (feedback_processors_.count(next) == 0)
332 feedback_processors_[next] = { 0, std::unique_ptr<Feedback>((Feedback*)(next->clone())) };
333 local_feedback_order_[i] = feedback_processors_[next].second.get();
334 }
335 }
336
338 for (const Processor* global_processor : *global_order_)
339 processors_[global_processor].first = *global_changes_;
340
341 for (auto iter = processors_.cbegin(); iter != processors_.cend();) {
342 if (iter->second.first != *global_changes_)
343 iter = processors_.erase(iter);
344 else
345 ++iter;
346 }
347
348 for (const Feedback* global_feedback : *global_feedback_order_)
349 feedback_processors_[global_feedback].first = *global_changes_;
350
351 for (auto iter = feedback_processors_.cbegin(); iter != feedback_processors_.cend();) {
352 if (iter->second.first != *global_changes_)
353 iter = feedback_processors_.erase(iter);
354 else
355 ++iter;
356 }
357
358 int num_feedbacks = static_cast<int>(global_feedback_order_->size());
359 local_feedback_order_.clear();
360 for (int i = 0; i < num_feedbacks; ++i) {
361 const Feedback* next = global_feedback_order_->at(i);
362 local_feedback_order_.push_back(feedback_processors_[next].second.get());
363 }
364 }
365
366 const Processor* ProcessorRouter::getContext(const Processor* processor) const {
367 const Processor* context = processor;
368 while (context && processors_.find(context) == processors_.end() &&
369 idle_processors_.find(context) == idle_processors_.end()) {
370 context = context->router();
371 }
372
373 return context;
374 }
375
377 return processors_[global_processor].second.get();
378 }
379
380 void ProcessorRouter::getDependencies(const Processor* processor) const {
381 dependencies_->clear();
382 dependencies_visited_->clear();
383 dependency_inputs_->clear();
384 const Processor* context = getContext(processor);
385
386 dependency_inputs_->ensureSpace();
387 dependency_inputs_->push_back(processor);
388 for (size_t i = 0; i < dependency_inputs_->size(); ++i) {
389 const Processor* dep_processor = getContext(dependency_inputs_->at(i));
390
391 if (dep_processor) {
392 if (!dependencies_->contains(dep_processor)) {
393 dependencies_->ensureSpace();
394 dependencies_->push_back(dep_processor);
395 }
396
397 for (int j = 0; j < dependency_inputs_->at(i)->numInputs(); ++j) {
398 const Input* input = dependency_inputs_->at(i)->ownedInput(j);
399 if (input->source && input->source->owner && !dependencies_visited_->contains(input->source->owner)) {
400 dependency_inputs_->ensureSpace();
401 dependency_inputs_->push_back(input->source->owner);
402
403 if (!dependencies_visited_->contains(input->source->owner)) {
404 dependencies_visited_->ensureSpace();
406 }
407 }
408 }
409 }
410 }
411
412 dependencies_->removeAll(context);
413 }
414} // namespace vital
A generic circular buffer (FIFO) data structure that allows adding and removing elements efficiently.
Definition circular_queue.h:32
A processor that buffers and replays audio, providing a feedback loop mechanism.
Definition feedback.h:26
Processor * clone() const override
Clones this Processor for polyphonic expansion. Must be overridden by subclasses.
Definition feedback.h:42
Base class for all signal-processing units in Vital.
Definition processor.h:212
force_inline int numInputs() const
Returns the total number of Input pointers (owned or otherwise).
Definition processor.h:557
virtual void setOversampleAmount(int oversample)
Sets the oversampling amount and updates the effective sample rate.
Definition processor.h:293
virtual bool hasState() const
Indicates whether this Processor requires per-voice state.
Definition processor.h:238
virtual bool isPolyphonic() const
Checks if this Processor is polyphonic by querying its ProcessorRouter.
Definition processor.cpp:73
force_inline Input * input(unsigned int index=0) const
Retrieves the Input pointer at a given index.
Definition processor.h:587
static const Output null_source_
A null (dummy) source used for unconnected inputs.
Definition processor.h:665
force_inline int getOversampleAmount() const
Retrieves the current oversampling factor.
Definition processor.h:334
void plug(const Output *source)
Connects an external Output to this Processor's first input.
Definition processor.cpp:79
virtual void setSampleRate(int sample_rate)
Updates the sample rate of this Processor (scaled by oversampling).
Definition processor.h:285
ProcessorRouter * router_
The ProcessorRouter that manages this Processor.
Definition processor.h:663
virtual Processor * clone() const =0
Clones this Processor for polyphonic expansion. Must be overridden by subclasses.
force_inline void router(ProcessorRouter *router)
Sets the ProcessorRouter that owns or manages this Processor.
Definition processor.h:507
virtual void init()
Called after constructor, used for any additional initialization. Subclasses can override....
Definition processor.h:258
virtual void reset(poly_mask reset_mask)
Called to reset the Processor's per-voice state (e.g., on note-on).
Definition processor.h:267
A specialized Processor that manages a directed graph of Processors and ensures correct processing or...
Definition processor_router.h:34
std::map< const Processor *, std::pair< int, std::unique_ptr< Feedback > > > feedback_processors_
Map of global to local Feedback processors.
Definition processor_router.h:259
virtual void updateAllProcessors()
Updates all processors to match the global order. Called when changes occur.
Definition processor_router.cpp:300
std::shared_ptr< CircularQueue< const Processor * > > dependencies_
Queue for dependencies calculations.
Definition processor_router.h:264
virtual Processor * clone() const override
Creates a copy of this ProcessorRouter.
Definition processor_router.h:59
int local_changes_
Local change counter to track synchronization with global changes.
Definition processor_router.h:262
virtual ~ProcessorRouter()
Destructor.
Definition processor_router.cpp:55
virtual void deleteRemovedProcessors()
Deletes any processors that were removed at the global level but not yet removed locally.
Definition processor_router.cpp:337
void disconnect(const Processor *destination, const Output *source)
Disconnects a source Output from a destination Processor.
Definition processor_router.cpp:182
virtual void removeProcessor(Processor *processor)
Removes a Processor from this router.
Definition processor_router.cpp:151
std::map< const Processor *, std::pair< int, std::unique_ptr< Processor > > > processors_
Map of global to local Processors.
Definition processor_router.h:254
virtual ProcessorRouter * getPolyRouter()
Gets the polyphonic router that corresponds to this ProcessorRouter.
Definition processor_router.cpp:269
ProcessorRouter(int num_inputs=0, int num_outputs=0, bool control_rate=false)
Constructs a ProcessorRouter with a specified number of inputs and outputs.
Definition processor_router.cpp:16
bool isDownstream(const Processor *first, const Processor *second) const
Checks if one Processor is downstream from another, i.e., if there's a path from the second to the fi...
Definition processor_router.cpp:233
std::shared_ptr< int > global_changes_
Global change counter.
Definition processor_router.h:261
virtual void setOversampleAmount(int oversample) override
Sets the oversampling amount for all Processors in this router.
Definition processor_router.cpp:104
virtual bool isPolyphonic(const Processor *processor) const
Determines if a given Processor is polyphonic within this router.
Definition processor_router.cpp:257
virtual void addFeedback(Feedback *feedback)
Adds a Feedback node to handle a feedback loop introduced by a connection.
Definition processor_router.cpp:278
virtual void removeFeedback(Feedback *feedback)
Removes a previously added Feedback node.
Definition processor_router.cpp:285
virtual void init() override
Initializes the ProcessorRouter and all its Processors.
Definition processor_router.cpp:84
virtual ProcessorRouter * getMonoRouter()
Gets the mono router that corresponds to this ProcessorRouter.
Definition processor_router.cpp:263
virtual void setSampleRate(int sample_rate) override
Sets the sample rate for all Processors in this router.
Definition processor_router.cpp:90
std::shared_ptr< std::vector< const Feedback * > > global_feedback_order_
Global order of Feedback nodes.
Definition processor_router.h:257
std::shared_ptr< CircularQueue< Processor * > > global_order_
Global processing order reference.
Definition processor_router.h:250
std::vector< Feedback * > local_feedback_order_
Local copies of Feedback nodes.
Definition processor_router.h:258
virtual void createAddedProcessors()
Creates any processors that were added at the global level but not yet replicated locally.
Definition processor_router.cpp:309
virtual void addIdleProcessor(Processor *processor)
Adds a Processor that should remain idle (not processed) in the router.
Definition processor_router.cpp:146
bool areOrdered(const Processor *first, const Processor *second) const
Checks if the order of two Processors is fixed in the router's processing sequence.
Definition processor_router.cpp:238
std::shared_ptr< CircularQueue< const Processor * > > dependencies_visited_
Queue of visited processors for dependencies calc.
Definition processor_router.h:265
std::shared_ptr< CircularQueue< const Processor * > > dependency_inputs_
Queue of processors to check inputs for dependencies.
Definition processor_router.h:266
std::map< const Processor *, std::unique_ptr< Processor > > idle_processors_
Idle Processors that are not active in the graph.
Definition processor_router.h:255
virtual void resetFeedbacks(poly_mask reset_mask)
Resets all Feedback nodes within this router using a reset mask.
Definition processor_router.cpp:273
void reorder(Processor *processor)
Reorders the internal processing sequence to account for a Processor's dependencies.
Definition processor_router.cpp:197
force_inline bool shouldUpdate()
Checks if local changes need to be synchronized with global changes.
Definition processor_router.h:213
virtual void addProcessorRealTime(Processor *processor)
Adds a Processor to the router in real-time (no memory allocations).
Definition processor_router.cpp:129
void connect(Processor *destination, const Output *source, int index)
Connects a source Output to a destination Processor input by index.
Definition processor_router.cpp:168
CircularQueue< Processor * > local_order_
Local ordering of Processors.
Definition processor_router.h:252
std::shared_ptr< CircularQueue< Processor * > > global_reorder_
Temporary storage for reorder operations.
Definition processor_router.h:251
void getDependencies(const Processor *processor) const
Populates the internal dependencies structure for a given Processor.
Definition processor_router.cpp:380
virtual void process(int num_samples) override
Processes audio through all Processors managed by this router.
Definition processor_router.cpp:57
const Processor * getContext(const Processor *processor) const
Gets the processor context within this router for a global Processor reference.
Definition processor_router.cpp:366
Processor * getLocalProcessor(const Processor *global_processor)
Retrieves the local instance of a globally defined Processor.
Definition processor_router.cpp:376
virtual void addProcessor(Processor *processor)
Adds a Processor to be managed by this router.
Definition processor_router.cpp:121
A control-rate variant of the Feedback processor.
Definition feedback.h:86
#define UNUSED(x)
Definition common.h:15
#define VITAL_ASSERT(x)
Definition common.h:11
force_inline bool isFinite(poly_float value)
Checks if all lanes in a poly_float are finite.
Definition poly_utils.h:610
Contains classes and functions used within the Vital synthesizer framework.
constexpr int kMaxModulationConnections
Maximum number of modulation connections allowed.
Definition synth_constants.h:49
Declares the ProcessorRouter class, which manages a graph of Processors and their dependencies.
Represents a connection to an Output from another Processor.
Definition processor.h:128
force_inline poly_float at(int i) const
Returns the sample at index i from the source buffer.
Definition processor.h:141
const Output * source
The output from which this input reads samples.
Definition processor.h:134
Holds and manages a buffer of samples (poly_float) for a Processor's output.
Definition processor.h:35
Processor * owner
Owning processor.
Definition processor.h:112
Represents a vector of integer values using SIMD instructions.
Definition poly_values.h:56