Vital
Loading...
Searching...
No Matches
diode_filter.cpp
Go to the documentation of this file.
1#include "diode_filter.h"
2#include "futils.h"
3
4namespace vital {
5
10 hardReset();
11 }
12
17 void DiodeFilter::reset(poly_mask reset_mask) {
18 high_pass_1_.reset(reset_mask);
19 high_pass_2_.reset(reset_mask);
20 stage1_.reset(reset_mask);
21 stage2_.reset(reset_mask);
22 stage3_.reset(reset_mask);
23 stage4_.reset(reset_mask);
24 }
25
34 resonance_ = 0.0f;
35 drive_ = 0.0f;
36 post_multiply_ = 0.0f;
37 }
38
46 void DiodeFilter::process(int num_samples) {
48
49 // Cache internal parameters
50 poly_float current_resonance = resonance_;
51 poly_float current_drive = drive_;
52 poly_float current_post_multiply = post_multiply_;
53 poly_float current_high_pass_ratio = high_pass_ratio_;
54 poly_float current_high_pass_amount = high_pass_amount_;
55
56 // Fetch filter settings and apply them
59
60 // Check if any voices need resetting
61 poly_mask reset_mask = getResetMask(kReset);
62 if (reset_mask.anyMask()) {
63 reset(reset_mask);
64
65 current_resonance =
66 utils::maskLoad(current_resonance, resonance_, reset_mask);
67 current_drive =
68 utils::maskLoad(current_drive, drive_, reset_mask);
69 current_post_multiply =
70 utils::maskLoad(current_post_multiply, post_multiply_, reset_mask);
71 current_high_pass_ratio =
72 utils::maskLoad(current_high_pass_ratio, high_pass_ratio_, reset_mask);
73 current_high_pass_amount =
74 utils::maskLoad(current_high_pass_amount, high_pass_amount_, reset_mask);
75 }
76
77 // Calculate per-sample increments for parameter smoothing
78 mono_float tick_increment = 1.0f / num_samples;
79 poly_float delta_resonance = (resonance_ - current_resonance) * tick_increment;
80 poly_float delta_drive = (drive_ - current_drive) * tick_increment;
81 poly_float delta_post_multiply = (post_multiply_ - current_post_multiply) * tick_increment;
82 poly_float delta_high_pass_ratio = (high_pass_ratio_ - current_high_pass_ratio) * tick_increment;
83 poly_float delta_high_pass_amount = (high_pass_amount_ - current_high_pass_amount) * tick_increment;
84
85 // Get buffers
86 const poly_float* audio_in = input(kAudio)->source->buffer;
87 poly_float* audio_out = output()->buffer;
88
89 // Retrieve the coefficient lookup table and MIDI note-based filter control
90 const CoefficientLookup* coefficient_lookup = getCoefficientLookup();
91 const poly_float* midi_cutoff_buffer = filter_state_.midi_cutoff_buffer;
92 poly_float base_midi = midi_cutoff_buffer[num_samples - 1];
93 poly_float base_frequency =
94 utils::midiNoteToFrequency(base_midi) * (1.0f / getSampleRate());
95 poly_float high_pass_frequency_ratio =
97 poly_float high_pass_feedback_coefficient =
98 coefficient_lookup->cubicLookup(high_pass_frequency_ratio);
99
100 // Main processing loop
101 for (int i = 0; i < num_samples; ++i) {
102 poly_float midi_delta = midi_cutoff_buffer[i] - base_midi;
103 // Calculate the current sample's frequency from MIDI
104 poly_float frequency =
105 utils::min(base_frequency * futils::midiOffsetToRatio(midi_delta), 1.0f);
106 poly_float coefficient = coefficient_lookup->cubicLookup(frequency);
107
108 // Smoothly update parameters
109 current_resonance += delta_resonance;
110 current_drive += delta_drive;
111 current_post_multiply += delta_post_multiply;
112 current_high_pass_ratio += delta_high_pass_ratio;
113 current_high_pass_amount += delta_high_pass_amount;
114
115 // Apply the diode filter algorithm per sample
116 tick(audio_in[i], coefficient, current_high_pass_ratio, current_high_pass_amount,
117 high_pass_feedback_coefficient, current_resonance, current_drive);
118
119 // Write output (final stage multiplied by the post_multiply factor)
120 audio_out[i] = stage4_.getCurrentState() * current_post_multiply;
121 }
122 }
123
131 void DiodeFilter::setupFilter(const FilterState& filter_state) {
132 static constexpr float kHighPassStart = -9.0f;
133 static constexpr float kHighPassEnd = -1.0f;
134 static constexpr float kHighPassRange = kHighPassEnd - kHighPassStart;
135
136 // Compute resonance (Q)
137 poly_float resonance_percent = utils::clamp(filter_state.resonance_percent, 0.0f, 1.0f);
138 // Emphasize higher resonance values
139 resonance_percent *= resonance_percent * resonance_percent;
140
141 resonance_ = utils::interpolate(kMinResonance, kMaxResonance, resonance_percent);
142
143 // Compute drive (scales with resonance)
144 drive_ = (resonance_ * 0.5f + 1.0f) * filter_state.drive;
145
146 // Post-multiply factor compensates for changes in volume due to drive
147 post_multiply_ = poly_float(1.0f) / utils::sqrt(filter_state.drive);
148
149 // Compute the high-pass ratio and amount depending on the style
150 poly_float blend_amount = filter_state.pass_blend * 0.5f;
151
152 if (filter_state.style == k12Db) {
153 high_pass_ratio_ = futils::exp2(kHighPassEnd);
154 high_pass_amount_ = blend_amount * blend_amount;
155 }
156 else {
157 high_pass_ratio_ = futils::exp2(blend_amount * kHighPassRange + kHighPassStart);
158 high_pass_amount_ = 1.0f;
159 }
160 }
161
176 poly_float coefficient,
177 poly_float high_pass_ratio,
178 poly_float high_pass_amount,
179 poly_float high_pass_feedback_coefficient,
181 poly_float drive) {
182 // Compute high-pass filter coefficients
183 poly_float high_pass_coefficient = coefficient * high_pass_ratio;
184 poly_float high_pass_coefficient2 = high_pass_coefficient * 2.0f;
185 poly_float high_pass_coefficient_squared = high_pass_coefficient * high_pass_coefficient;
186 poly_float high_pass_coefficient_diff = high_pass_coefficient_squared - high_pass_coefficient;
187 poly_float high_pass_feedback_mult = high_pass_coefficient2 - high_pass_coefficient_squared - 1.0f;
188 poly_float high_pass_normalizer = poly_float(1.0f) / (high_pass_coefficient_diff + 1.0f);
189
190 poly_float high_pass_mult_stage2 = -high_pass_coefficient + 1.0f;
191 poly_float high_pass_feedback =
192 high_pass_feedback_mult * high_pass_1_.getNextState() +
193 high_pass_mult_stage2 * high_pass_2_.getNextState();
194
195 // Stage 1: high-pass input
196 poly_float high_pass_input = (audio_in - high_pass_feedback) * high_pass_normalizer;
197
198 // Tick through the high-pass filters
199 poly_float high_pass_1_out = high_pass_1_.tickBasic(high_pass_input, high_pass_coefficient);
200 poly_float high_pass_2_out = high_pass_2_.tickBasic(high_pass_1_out, high_pass_coefficient);
201
202 // Sum to get the high-pass output
203 poly_float high_pass_out = high_pass_input - high_pass_1_out * 2.0f + high_pass_2_out;
204
205 // Blend the high-pass output with the original input
206 high_pass_out = utils::interpolate(audio_in, high_pass_out, high_pass_amount);
207
208 // Prepare for main diode ladder stages
209 poly_float filter_state = stage4_.getNextSatState();
210 poly_float filter_input = (drive * high_pass_out - resonance * filter_state) * 0.5f;
211
212 // Nonlinear saturation (tanh)
213 poly_float sat_input = futils::tanh(filter_input);
214
215 // Combine with Stage 2 output for feedback
216 poly_float feedback_input = sat_input + stage2_.getNextSatState();
217
218 // Tick feedback through its high-pass filter
219 poly_float feedback = high_pass_feedback_.tickBasic(feedback_input, high_pass_feedback_coefficient);
220
221 // Cascade through each stage
222 stage1_.tick(feedback_input - feedback, coefficient);
223 stage2_.tick((stage1_.getCurrentState() + stage3_.getNextSatState()) * 0.5f, coefficient);
224 stage3_.tick((stage2_.getCurrentState() + stage4_.getNextSatState()) * 0.5f, coefficient);
225
226 // Final stage with clamping saturation
227 stage4_.tick(stage3_.getCurrentState(), coefficient);
228 }
229
230} // namespace vital
A diode ladder filter implementation for the Vital synthesizer.
Definition diode_filter.h:20
void hardReset() override
Performs a complete hard reset of all internal states (for all voices).
Definition diode_filter.cpp:32
force_inline void tick(poly_float audio_in, poly_float coefficient, poly_float high_pass_ratio, poly_float high_pass_amount, poly_float high_pass_feedback_coefficient, poly_float resonance, poly_float drive)
Processes a single sample of audio through the diode ladder filter stages.
Definition diode_filter.cpp:175
void reset(poly_mask reset_mask) override
Resets internal filter states (such as one-pole filters) with the given reset mask.
Definition diode_filter.cpp:17
static constexpr mono_float kMinResonance
Minimum resonance value for the diode filter.
Definition diode_filter.h:25
void setupFilter(const FilterState &filter_state) override
Sets up filter parameters from the given FilterState.
Definition diode_filter.cpp:131
DiodeFilter()
Default constructor.
Definition diode_filter.cpp:9
virtual void process(int num_samples) override
Processes audio data through the diode filter.
Definition diode_filter.cpp:46
static constexpr mono_float kHighPassFrequency
High-pass cutoff frequency in Hz (used internally).
Definition diode_filter.h:40
static constexpr mono_float kMaxResonance
Maximum resonance value for the diode filter.
Definition diode_filter.h:30
A one-dimensional lookup table for a given function with a specified resolution.
Definition lookup_table.h:31
force_inline poly_float cubicLookup(poly_float value) const
Performs a cubic interpolation lookup on the precomputed data.
Definition lookup_table.h:61
force_inline poly_float getNextState()
Gets the next filter state value (without saturation).
Definition one_pole_filter.h:120
force_inline void reset(poly_mask reset_mask)
Resets the filter state for the voices indicated by a mask.
Definition one_pole_filter.h:36
force_inline poly_float getCurrentState()
Gets the current state of the filter output.
Definition one_pole_filter.h:106
force_inline poly_float getNextSatState()
Gets the next saturated filter state value.
Definition one_pole_filter.h:113
force_inline poly_float tick(poly_float audio_in, poly_float coefficient)
Processes a single sample, applying the saturation function at each step.
Definition one_pole_filter.h:73
force_inline poly_float tickBasic(poly_float audio_in, poly_float coefficient)
Processes a single sample in a basic (non-saturating) manner.
Definition one_pole_filter.h:56
Base class for all signal-processing units in Vital.
Definition processor.h:212
force_inline Input * input(unsigned int index=0) const
Retrieves the Input pointer at a given index.
Definition processor.h:587
force_inline int getSampleRate() const
Retrieves the current (effective) sample rate.
Definition processor.h:326
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
force_inline Output * output(unsigned int index=0) const
Retrieves the Output pointer at a given index.
Definition processor.h:616
Holds the parameters necessary to configure a SynthFilter at runtime.
Definition synth_filter.h:92
void loadSettings(Processor *processor)
Loads state from a Processor’s input signals (MIDI cutoff, drive, style, etc.).
Definition synth_filter.cpp:30
const poly_float * midi_cutoff_buffer
Pointer to the buffer storing per-sample MIDI cutoff.
Definition synth_filter.h:111
poly_float pass_blend
Blend parameter in [0..2], controlling pass type.
Definition synth_filter.h:117
poly_float drive
Drive in linear magnitude.
Definition synth_filter.h:113
int style
Filter style enum (e.g., k12Db, k24Db)
Definition synth_filter.h:116
poly_float resonance_percent
Resonance parameter in [0..1].
Definition synth_filter.h:112
@ kReset
Reset signal.
Definition synth_filter.h:56
@ kAudio
Audio input index.
Definition synth_filter.h:55
FilterState filter_state_
Internal storage of the most recent FilterState, used by derived filters.
Definition synth_filter.h:151
static const CoefficientLookup * getCoefficientLookup()
Retrieves a pointer to the static coefficient lookup table.
Definition synth_filter.h:48
@ k12Db
Definition synth_filter.h:75
#define VITAL_ASSERT(x)
Definition common.h:11
#define force_inline
Definition common.h:23
cr::Value resonance
Resonance factor for this formant.
Definition formant_filter.cpp:18
Contains faster but less accurate versions of utility math functions, such as exponential,...
const poly_mask kFullMask
A mask covering all lanes of a poly_float vector.
Definition synth_constants.h:257
force_inline poly_float exp2(poly_float exponent)
Approximates 2^exponent for poly_float values using a polynomial approximation.
Definition futils.h:45
force_inline poly_float midiOffsetToRatio(poly_float note_offset)
Converts a MIDI note offset to a frequency ratio.
Definition futils.h:184
force_inline poly_float tanh(poly_float value)
Approximates tanh function using a complex polynomial.
Definition futils.h:347
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 poly_float min(poly_float left, poly_float right)
Returns the minimum of two poly_floats lane-by-lane.
Definition poly_utils.h:334
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_float sqrt(poly_float value)
Computes the square root of each element in a poly_float.
Definition poly_utils.h:169
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
Contains classes and functions used within the Vital synthesizer framework.
float mono_float
Definition common.h:33
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
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
Represents a vector of integer values using SIMD instructions.
Definition poly_values.h:56
static force_inline uint32_t vector_call anyMask(simd_type value)
Returns a bitmask that indicates which bytes/elements in the register are non-zero.
Definition poly_values.h:352