Vital
Loading...
Searching...
No Matches
operators.cpp
Go to the documentation of this file.
1#include "operators.h"
2#include "synth_constants.h"
3
4namespace vital {
5
10 void Clamp::process(int num_samples) {
12
13 const poly_float* source = input()->source->buffer;
14 poly_float* dest = output()->buffer;
15
16 for (int i = 0; i < num_samples; ++i)
17 dest[i] = utils::clamp(source[i], min_, max_);
18 }
19
24 void Negate::process(int num_samples) {
26
27 const poly_float* source = input()->source->buffer;
28 poly_float* dest = output()->buffer;
29
30 for (int i = 0; i < num_samples; ++i)
31 dest[i] = -source[i];
32 }
33
38 void Inverse::process(int num_samples) {
40
41 const poly_float* source = input()->source->buffer;
42 poly_float* dest = output()->buffer;
43
44 for (int i = 0; i < num_samples; ++i)
45 dest[i] = poly_float(1.0f) / source[i];
46 }
47
52 void LinearScale::process(int num_samples) {
54
55 const poly_float* source = input()->source->buffer;
56 poly_float* dest = output()->buffer;
57
58 for (int i = 0; i < num_samples; ++i)
59 dest[i] = source[i] * scale_;
60 }
61
66 void Square::process(int num_samples) {
68 poly_float* dest = output()->buffer;
69 const poly_float* source = input()->source->buffer;
70
71 for (int i = 0; i < num_samples; ++i) {
72 poly_float value = source[i];
73 dest[i] = value * value;
74 }
75 }
76
81 void Add::process(int num_samples) {
84
85 poly_float* dest = output()->buffer;
86 const poly_float* source_left = input(0)->source->buffer;
87 const poly_float* source_right = input(1)->source->buffer;
88
89 for (int i = 0; i < num_samples; ++i)
90 dest[i] = source_left[i] + source_right[i];
91 }
92
97 void Subtract::process(int num_samples) {
100
101 poly_float* dest = output()->buffer;
102 const poly_float* source_left = input(0)->source->buffer;
103 const poly_float* source_right = input(1)->source->buffer;
104
105 for (int i = 0; i < num_samples; ++i)
106 dest[i] = source_left[i] - source_right[i];
107 }
108
113 void Multiply::process(int num_samples) {
116
117 poly_float* dest = output()->buffer;
118 const poly_float* source_left = input(0)->source->buffer;
119 const poly_float* source_right = input(1)->source->buffer;
120
121 for (int i = 0; i < num_samples; ++i)
122 dest[i] = source_left[i] * source_right[i];
123 }
124
129 void SmoothMultiply::process(int num_samples) {
130 processMultiply(num_samples, input(kControlRate)->at(0));
131 }
132
138 void SmoothMultiply::processMultiply(int num_samples, poly_float multiply) {
140
141 poly_float* audio_out = output()->buffer;
142 const poly_float* audio_in = input(kAudioRate)->source->buffer;
143
144 poly_float current_multiply = multiply_;
145 multiply_ = multiply;
146
147 // If we need to reset multiplier for specific voices
148 current_multiply = utils::maskLoad(current_multiply, multiply_, getResetMask(kReset));
149 poly_float delta_multiply = (multiply_ - current_multiply) * (1.0f / num_samples);
150
151 for (int i = 0; i < num_samples; ++i) {
152 current_multiply += delta_multiply;
153 audio_out[i] = audio_in[i] * current_multiply;
154 }
155 }
156
161 void SmoothVolume::process(int num_samples) {
162 poly_float db = utils::clamp(input(kDb)->at(0), kMinDb, max_db_);
164
165 poly_float amplitude = futils::dbToMagnitude(db);
166 // If dB is at or below kMinDb, amplitude becomes 0
167 amplitude = utils::maskLoad(amplitude, 0.0f, zero_mask);
168
169 processMultiply(num_samples, amplitude);
170 }
171
176 void Interpolate::process(int num_samples) {
179
180 poly_float* dest = output()->buffer;
181 const poly_float* from = input(kFrom)->source->buffer;
182 const poly_float* to = input(kTo)->source->buffer;
183 const poly_float* fractional = input(kFractional)->source->buffer;
184
185 // If fractional is control rate, we smooth it over the block
186 if (input(kFractional)->source->isControlRate()) {
187 poly_float current_fraction = fraction_;
188 fraction_ = fractional[0];
189 current_fraction = utils::maskLoad(current_fraction, fraction_, getResetMask(kReset));
190 poly_float delta_fraction = (fraction_ - current_fraction) * (1.0f / num_samples);
191
192 for (int i = 0; i < num_samples; ++i) {
193 current_fraction += delta_fraction;
194 dest[i] = utils::interpolate(from[i], to[i], current_fraction);
195 }
196 }
197 else {
198 // Fractional is audio-rate or per-sample
199 for (int i = 0; i < num_samples; ++i)
200 dest[i] = utils::interpolate(from[i], to[i], fractional[i]);
201 }
202 }
203
208 void BilinearInterpolate::process(int num_samples) {
209 static constexpr float kMaxOffset = 1.0f;
210
213
214 poly_float top_left = input(kTopLeft)->at(0);
215 poly_float top_right = input(kTopRight)->at(0);
216 poly_float bottom_left = input(kBottomLeft)->at(0);
217 poly_float bottom_right = input(kBottomRight)->at(0);
218
219 const poly_float* x_position = input(kXPosition)->source->buffer;
220 const poly_float* y_position = input(kYPosition)->source->buffer;
221 poly_float* dest = output()->buffer;
222
223 for (int i = 0; i < num_samples; ++i) {
224 poly_float x = utils::clamp(x_position[i], -kMaxOffset, 1.0f + kMaxOffset);
225 poly_float y = utils::clamp(y_position[i], -kMaxOffset, 1.0f + kMaxOffset);
226 poly_float top = utils::interpolate(top_left, top_right, x);
227 poly_float bottom = utils::interpolate(bottom_left, bottom_right, x);
228 dest[i] = utils::interpolate(top, bottom, y);
229 }
230 }
231
236 void VariableAdd::process(int num_samples) {
237#if DEBUG
238 // Verify each input buffer size matches
239 for (int i = 0; i < inputs_->size(); ++i)
241#endif
242
243 poly_float* dest = output()->buffer;
244 int num_inputs = static_cast<int>(inputs_->size());
245
246 if (isControlRate()) {
247 // Summation for single-sample control-rate
248 dest[0] = 0.0f;
249 for (int i = 0; i < num_inputs; ++i)
250 dest[0] += input(i)->at(0);
251 }
252 else {
253 // Summation for audio-rate
254 utils::zeroBuffer(dest, num_samples);
255
256 for (int i = 0; i < num_inputs; ++i) {
257 if (input(i)->source != &Processor::null_source_) {
258 const poly_float* source = input(i)->source->buffer;
259 for (int s = 0; s < num_samples; ++s)
260 dest[s] += source[s];
261 }
262 }
263 }
264 }
265
270 void ModulationSum::process(int num_samples) {
271 VITAL_ASSERT(output()->buffer_size >= num_samples);
272
273 poly_float* dest = output()->buffer;
274 int num_inputs = static_cast<int>(inputs_->size());
275
276 // Smooth control-rate inputs
277 poly_float current_control_value = control_value_;
278 control_value_ = 0.0f;
279
280 for (int i = kNumStaticInputs; i < num_inputs; ++i) {
281 if (input(i)->source != &Processor::null_source_ && input(i)->source->owner->isControlRate())
282 control_value_ += input(i)->source->buffer[0];
283 }
284
285 // Smoothing the control value
286 current_control_value = utils::maskLoad(current_control_value, control_value_, getResetMask(kReset));
287 poly_float delta_control_value = (control_value_ - current_control_value) * (1.0f / num_samples);
288
289 for (int s = 0; s < num_samples; ++s) {
290 current_control_value += delta_control_value;
291 dest[s] = current_control_value;
292 }
293
294 // Add audio-rate inputs
295 for (int i = kNumStaticInputs; i < num_inputs; ++i) {
296 if (input(i)->source != &Processor::null_source_ && !input(i)->source->owner->isControlRate()) {
298 const poly_float* source = input(i)->source->buffer;
299
300 for (int s = 0; s < num_samples; ++s) {
301 dest[s] += source[s];
302 }
303 }
304 }
305
306 output()->trigger_value = dest[0];
307 }
308
313 void SampleAndHoldBuffer::process(int num_samples) {
314 poly_float value = input()->source->buffer[0];
315 poly_float* dest = output()->buffer;
316
317 // If value is already the same, no change needed
318 if (utils::equal(value, dest[0]))
319 return;
320
321 for (int i = 0; i < num_samples; ++i)
322 dest[i] = value;
323 }
324
329 void StereoEncoder::process(int num_samples) {
330 if (input(kMode)->at(0)[0])
331 processRotate(num_samples);
332 else
333 processCenter(num_samples);
334 }
335
340 void StereoEncoder::processRotate(int num_samples) {
341 static const poly_float kSign(1.0f, -1.0f);
343
344 poly_float current_cos_mult = cos_mult_;
345 poly_float current_sin_mult = sin_mult_;
346 poly_float encoding = utils::clamp(input(kEncodingValue)->at(0), 0.0f, 1.0f)
347 * decoding_mult_ * (2.0f * kPi);
348
349 // Compute target cos/sin
350 cos_mult_ = utils::cos(encoding);
351 sin_mult_ = utils::sin(encoding);
352
353 mono_float delta_tick = 1.0f / num_samples;
354 poly_float delta_cos = (cos_mult_ - current_cos_mult) * delta_tick;
355 poly_float delta_sin = (sin_mult_ - current_sin_mult) * delta_tick;
356
357 const poly_float* source = input()->source->buffer;
358 poly_float* dest = output()->buffer;
359
360 for (int i = 0; i < num_samples; ++i) {
361 current_cos_mult += delta_cos;
362 current_sin_mult += delta_sin;
363 // swapStereo swaps L and R, then multiply by (1, -1) for sign inversion
364 poly_float swap = kSign * utils::swapStereo(source[i]);
365 dest[i] = source[i] * current_cos_mult + swap * current_sin_mult;
366 }
367 }
368
373 void StereoEncoder::processCenter(int num_samples) {
374 poly_float current_cos_mult = cos_mult_;
375 poly_float current_sin_mult = sin_mult_;
376 // encoding ∈ [0..1], mapped to an angle in [π/4..0].
377 poly_float encoding = utils::clamp(input(kEncodingValue)->at(0), 0.0f, 1.0f);
378 poly_float phase = (poly_float(1.0f) - encoding) * (0.25f * kPi);
379
380 cos_mult_ = utils::cos(phase);
381 sin_mult_ = utils::sin(phase);
382
383 mono_float delta_tick = 1.0f / num_samples;
384 poly_float delta_cos = (cos_mult_ - current_cos_mult) * delta_tick;
385 poly_float delta_sin = (sin_mult_ - current_sin_mult) * delta_tick;
386
387 const poly_float* source = input()->source->buffer;
388 poly_float* dest = output()->buffer;
389
390 for (int i = 0; i < num_samples; ++i) {
391 current_cos_mult += delta_cos;
392 current_sin_mult += delta_sin;
393 poly_float swap = utils::swapStereo(source[i]);
394 dest[i] = source[i] * current_cos_mult + swap * current_sin_mult;
395 }
396 }
397
402 void TempoChooser::process(int num_samples) {
403 static const poly_float dotted_ratio = 2.0f / 3.0f;
404 static const poly_float triplet_ratio = 3.0f / 2.0f;
405
407 poly_int tempo_index = utils::toInt(tempo + 0.3f);
408
409 // Retrieve the base ratio from the frequency table
410 poly_float tempo_value = 0.0f;
411 for (int i = 0; i < poly_float::kSize; ++i)
412 tempo_value.set(i, constants::kSyncedFrequencyRatios[tempo_index[i]]);
413
414 poly_float beats_per_second = input(kBeatsPerSecond)->at(0);
415 tempo_value *= beats_per_second;
416
417 poly_float sync = input(kSync)->at(0);
418 poly_mask triplet_mask = poly_float::equal(sync, kTripletMode);
419 poly_mask dotted_mask = poly_float::equal(sync, kDottedMode) & ~triplet_mask;
420
421 // Apply triplet or dotted multipliers
422 poly_float triplet_mult = utils::maskLoad(1.0f, triplet_ratio, triplet_mask);
423 poly_float dotted_mult = utils::maskLoad(1.0f, dotted_ratio, dotted_mask);
424 poly_float tempo_adjusted = triplet_mult * dotted_mult * tempo_value;
425
426 poly_mask frequency_mask = poly_float::equal(sync, kFrequencyMode);
427 poly_mask keytrack_mask = poly_float::equal(sync, kKeytrack);
428
429 // Keytrack uses MIDI to produce a frequency
431 + input(kKeytrackTune)->at(0)
432 + input(kMidi)->at(0);
433 poly_float keytrack_frequency = utils::midiNoteToFrequency(midi);
434
435 // If in frequency mode, just return frequency. Otherwise, use tempo-based calculation.
436 poly_float result = utils::maskLoad(tempo_adjusted, input(kFrequency)->at(0), frequency_mask);
437
438 // If in keytrack mode, override with keytrack frequency
439 output()->buffer[0] = utils::maskLoad(result, keytrack_frequency, keytrack_mask);
440 }
441
442} // namespace vital
void process(int num_samples) override
Processes two input buffers and writes their sum to the output.
Definition operators.cpp:81
@ kTopRight
Definition operators.h:455
@ kYPosition
Definition operators.h:459
@ kBottomRight
Definition operators.h:457
@ kBottomLeft
Definition operators.h:456
@ kXPosition
Definition operators.h:458
@ kTopLeft
Definition operators.h:454
void process(int num_samples) override
Processes the corner values, interpolating horizontally and then vertically.
Definition operators.cpp:208
void process(int num_samples) override
Processes the input buffer, clamping each sample between min_ and max_.
Definition operators.cpp:10
void process(int num_samples) override
Processes two input buffers and interpolates them by a fractional control input.
Definition operators.cpp:176
@ kReset
Definition operators.h:425
@ kTo
Definition operators.h:423
@ kFrom
Definition operators.h:422
@ kFractional
Definition operators.h:424
void process(int num_samples) override
Processes the input buffer, storing the inverse of each sample.
Definition operators.cpp:38
void process(int num_samples) override
Processes each sample, multiplying by the scale factor.
Definition operators.cpp:52
void process(int num_samples) override
Processes control-rate inputs by smoothing them over the block, and adds audio-rate inputs sample by ...
Definition operators.cpp:270
@ kNumStaticInputs
Definition operators.h:260
@ kReset
Definition operators.h:259
void process(int num_samples) override
Processes two inputs, storing the product of each sample pair.
Definition operators.cpp:113
void process(int num_samples) override
Processes the input buffer, negating each sample.
Definition operators.cpp:24
force_inline Input * input(unsigned int index=0) const
Retrieves the Input pointer at a given index.
Definition processor.h:587
force_inline bool isControlRate() const
Checks if this Processor is running at control rate (buffer_size == 1).
Definition processor.h:342
bool inputMatchesBufferSize(int input=0)
Checks whether the buffer size of a particular input matches the size needed by this Processor.
Definition processor.cpp:42
force_inline poly_mask getResetMask(int input_index) const
Retrieves a mask indicating which voices triggered a note-on event. Compares the input's trigger_valu...
Definition processor.h:360
static const Output null_source_
A null (dummy) source used for unconnected inputs.
Definition processor.h:665
std::shared_ptr< std::vector< Input * > > inputs_
All inputs, owned or external.
Definition processor.h:660
force_inline Output * output(unsigned int index=0) const
Retrieves the Output pointer at a given index.
Definition processor.h:616
void process(int num_samples) override
Processes the input buffer, outputting a constant value (first sample) for the entire block.
Definition operators.cpp:313
virtual void process(int num_samples) override
Processes the audio input, multiplying by a smoothed control-rate value.
Definition operators.cpp:129
@ kControlRate
Definition operators.h:346
@ kReset
Definition operators.h:347
@ kAudioRate
Definition operators.h:345
void processMultiply(int num_samples, poly_float multiply)
Internal function to perform the per-sample smoothing of the multiplier and multiplication.
Definition operators.cpp:138
poly_float multiply_
Definition operators.h:376
static constexpr mono_float kMinDb
Definition operators.h:391
void process(int num_samples) override
Processes volume in dB, smoothing across one audio block before multiplying the audio input.
Definition operators.cpp:161
static constexpr int kDb
Definition operators.h:390
void process(int num_samples) override
Processes the input buffer, squaring each sample.
Definition operators.cpp:66
poly_float cos_mult_
Definition operators.h:566
@ kMode
Definition operators.h:517
@ kEncodingValue
Definition operators.h:516
void processRotate(int num_samples)
Processes the stereo signal in rotate mode (phase rotation).
Definition operators.cpp:340
poly_float sin_mult_
Definition operators.h:567
mono_float decoding_mult_
Definition operators.h:568
void processCenter(int num_samples)
Processes the stereo signal in spread/center mode.
Definition operators.cpp:373
void process(int num_samples) override
Processes the stereo input, either applying rotation or a center/spread mix.
Definition operators.cpp:329
void process(int num_samples) override
Processes two inputs, storing the result of left - right.
Definition operators.cpp:97
@ kTempoIndex
Definition operators.h:593
@ kSync
Definition operators.h:595
@ kKeytrackTune
Definition operators.h:598
@ kKeytrackTranspose
Definition operators.h:597
@ kFrequency
Definition operators.h:592
@ kMidi
Definition operators.h:596
@ kBeatsPerSecond
Definition operators.h:594
@ kTripletMode
Definition operators.h:586
@ kFrequencyMode
Definition operators.h:583
@ kKeytrack
Definition operators.h:587
@ kDottedMode
Definition operators.h:585
void process(int num_samples) override
Reads sync mode and other parameters, producing a final frequency.
Definition operators.cpp:402
void process(int num_samples) override
Sums all input channels for each sample and writes the result to the output.
Definition operators.cpp:236
#define VITAL_ASSERT(x)
Definition common.h:11
constexpr vital::mono_float kSyncedFrequencyRatios[kNumSyncedFrequencyRatios]
Predefined list of frequency ratios for synced parameters (from 1/128th to 16x speed).
Definition synth_constants.h:221
constexpr int kNumSyncedFrequencyRatios
Number of frequency ratios used when syncing parameters (e.g., LFO speed) to tempo.
Definition synth_constants.h:218
force_inline mono_float dbToMagnitude(mono_float decibels)
Converts decibels (dB) to magnitude (linear).
Definition futils.h:217
force_inline poly_float cos(poly_float value)
Computes the cosine of each element (in radians).
Definition poly_utils.h:164
force_inline poly_float clamp(poly_float value, mono_float min, mono_float max)
Clamps each lane of a vector to [min, max].
Definition poly_utils.h:306
force_inline void zeroBuffer(mono_float *buffer, int size)
Zeros a mono buffer (standard array).
Definition poly_utils.h:570
force_inline bool equal(poly_float left, poly_float right)
Checks if two poly_floats are equal lane-by-lane. Returns true if all lanes match.
Definition poly_utils.h:341
force_inline poly_float maskLoad(poly_float zero_value, poly_float one_value, poly_mask reset_mask)
Selects between two values (zero_value or one_value) based on a mask in each lane.
Definition poly_utils.h:351
force_inline poly_float midiNoteToFrequency(poly_float value)
Converts a MIDI note to a frequency (vectorized).
Definition poly_utils.h:123
force_inline poly_int toInt(poly_float floats)
Casts a poly_float to poly_int by truncation.
Definition poly_utils.h:748
force_inline poly_float sin(poly_float value)
Computes the sine of each element (in radians).
Definition poly_utils.h:159
force_inline poly_float interpolate(poly_float from, poly_float to, mono_float t)
Performs a linear interpolation between two poly_floats using a scalar t in [0..1].
Definition poly_utils.h:182
force_inline poly_float swapStereo(poly_float value)
Swaps the left and right channels of a stereo poly_float.
Definition poly_utils.h:411
Contains classes and functions used within the Vital synthesizer framework.
constexpr mono_float kPi
Pi constant.
Definition common.h:36
float mono_float
Definition common.h:33
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
poly_float * buffer
Pointer to the output buffer.
Definition processor.h:110
poly_float trigger_value
Trigger values for voices.
Definition processor.h:116
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
static force_inline mask_simd_type vector_call equal(simd_type one, simd_type two)
Compares two SIMD float registers for equality, element-wise.
Definition poly_values.h:954
static force_inline poly_mask vector_call lessThanOrEqual(poly_float one, poly_float two)
Definition poly_values.h:1108
force_inline void vector_call set(size_t index, float new_value) noexcept
Sets a specific element in the SIMD register.
Definition poly_values.h:1182
Represents a vector of integer values using SIMD instructions.
Definition poly_values.h:56