Vital
Loading...
Searching...
No Matches
phaser_filter.h
Go to the documentation of this file.
1#pragma once
2
3#include "processor.h"
4#include "synth_filter.h"
5
6#include "one_pole_filter.h"
7
8namespace vital {
9
19 class PhaserFilter : public Processor, public SynthFilter {
20 public:
24 static constexpr mono_float kMinResonance = 0.0f;
25
29 static constexpr mono_float kMaxResonance = 1.0f;
30
34 static constexpr mono_float kMinCutoff = 1.0f;
35
39 static constexpr mono_float kClearRatio = 20.0f;
40
44 static constexpr int kPeakStage = 4;
45
49 static constexpr int kMaxStages = 3 * kPeakStage;
50
55 PhaserFilter(bool clean);
56
60 virtual ~PhaserFilter() { }
61
66 virtual Processor* clone() const override { return new PhaserFilter(*this); }
67
72 virtual void process(int num_samples) override;
73
79 void processWithInput(const poly_float* audio_in, int num_samples) override;
80
85 void setupFilter(const FilterState& filter_state) override;
86
91 void reset(poly_mask reset_mask) override;
92
96 void hardReset() override;
97
102 void setClean(bool clean) { clean_ = clean; }
103
108 poly_float getResonance() { return resonance_; }
109
114 poly_float getDrive() { return drive_; }
115
120 poly_float getPeak1Amount() { return peak1_amount_; }
121
126 poly_float getPeak3Amount() { return peak3_amount_; }
127
132 poly_float getPeak5Amount() { return peak5_amount_; }
133
134 private:
145 template <poly_float(*saturateResonance)(poly_float), poly_float(*saturateInput)(poly_float)>
146 void process(const poly_float* audio_in, int num_samples) {
147 // Cache current parameters
148 poly_float current_resonance = resonance_;
149 poly_float current_drive = drive_;
150 poly_float current_peak1 = peak1_amount_;
151 poly_float current_peak3 = peak3_amount_;
152 poly_float current_peak5 = peak5_amount_;
153
154 // Reload FilterState settings (in case they changed)
157
158 // Check for resets
159 poly_mask reset_mask = getResetMask(kReset);
160 if (reset_mask.anyMask()) {
161 reset(reset_mask);
162
163 current_resonance = utils::maskLoad(current_resonance, resonance_, reset_mask);
164 current_drive = utils::maskLoad(current_drive, drive_, reset_mask);
165 current_peak1 = utils::maskLoad(current_peak1, peak1_amount_, reset_mask);
166 current_peak3 = utils::maskLoad(current_peak3, peak3_amount_, reset_mask);
167 current_peak5 = utils::maskLoad(current_peak5, peak5_amount_, reset_mask);
168 }
169
170 // Calculate per-sample increments for parameter smoothing
171 mono_float tick_increment = 1.0f / num_samples;
172 poly_float delta_resonance = (resonance_ - current_resonance) * tick_increment;
173 poly_float delta_drive = (drive_ - current_drive) * tick_increment;
174 poly_float delta_peak1 = (peak1_amount_ - current_peak1) * tick_increment;
175 poly_float delta_peak3 = (peak3_amount_ - current_peak3) * tick_increment;
176 poly_float delta_peak5 = (peak5_amount_ - current_peak5) * tick_increment;
177
178 poly_float* audio_out = output()->buffer;
179 const CoefficientLookup* coefficient_lookup = getCoefficientLookup();
180 const poly_float* midi_cutoff_buffer = filter_state_.midi_cutoff_buffer;
181 poly_float base_midi = midi_cutoff_buffer[num_samples - 1];
182 poly_float base_frequency = utils::midiNoteToFrequency(base_midi) * (1.0f / getSampleRate());
183
184 // Process each sample
185 for (int i = 0; i < num_samples; ++i) {
186 poly_float midi_delta = midi_cutoff_buffer[i] - base_midi;
187 poly_float frequency = utils::min(base_frequency * futils::midiOffsetToRatio(midi_delta), 1.0f);
188 poly_float coefficient = coefficient_lookup->cubicLookup(frequency);
189
190 // Smoothly update parameters
191 current_resonance += delta_resonance;
192 current_drive += delta_drive;
193 current_peak1 += delta_peak1;
194 current_peak3 += delta_peak3;
195 current_peak5 += delta_peak5;
196
197 // Process a single sample through phaser stages
198 tick<saturateResonance, saturateInput>(audio_in[i], coefficient,
199 current_resonance, current_drive,
200 current_peak1, current_peak3, current_peak5);
201
202 // The phaser output is a blend of the allpass output and the original signal
203 audio_out[i] = (audio_in[i] + invert_mult_ * allpass_output_) * 0.5f;
204 }
205 }
206
220 template <poly_float(*saturateResonance)(poly_float), poly_float(*saturateInput)(poly_float)>
221 force_inline void tick(poly_float audio_in, poly_float coefficient,
222 poly_float resonance, poly_float drive,
223 poly_float peak1, poly_float peak3, poly_float peak5) {
224 // Remove some lows, remove some highs
225 poly_float filter_state_lows = remove_lows_stage_.tickBasic(allpass_output_,
226 utils::min(coefficient * kClearRatio, 0.9f));
227 poly_float filter_state_highs = remove_highs_stage_.tickBasic(filter_state_lows,
228 coefficient * (1.0f / kClearRatio));
229
230 // Saturate resonance feedback
231 poly_float filter_state = saturateResonance(resonance * (filter_state_lows - filter_state_highs));
232
233 // Combine input with negative feedback
234 poly_float filter_input = utils::mulAdd(drive * audio_in, invert_mult_, filter_state);
235 poly_float all_pass_input = saturateInput(filter_input);
236
237 // Pass through kPeakStage (4) all-pass stages for peak 1
238 poly_float stage_out;
239 for (int i = 0; i < kPeakStage; ++i) {
240 stage_out = stages_[i].tickBasic(all_pass_input, coefficient);
241 all_pass_input = utils::mulAdd(all_pass_input, stage_out, -2.0f);
242 }
243 poly_float peak1_out = all_pass_input;
244
245 // Next kPeakStage all-pass stages for peak 3
246 for (int i = kPeakStage; i < 2 * kPeakStage; ++i) {
247 stage_out = stages_[i].tickBasic(all_pass_input, coefficient);
248 all_pass_input = utils::mulAdd(all_pass_input, stage_out, -2.0f);
249 }
250 poly_float peak3_out = all_pass_input;
251
252 // Final kPeakStage all-pass stages for peak 5
253 for (int i = 2 * kPeakStage; i < 3 * kPeakStage; ++i) {
254 stage_out = stages_[i].tickBasic(all_pass_input, coefficient);
255 all_pass_input = utils::mulAdd(all_pass_input, stage_out, -2.0f);
256 }
257 poly_float peak5_out = all_pass_input;
258
259 // Combine the outputs of each peak cluster
260 poly_float all_pass_output_1_3 = utils::mulAdd(peak1 * peak1_out, peak3, peak3_out);
261 allpass_output_ = utils::mulAdd(all_pass_output_1_3, peak5, peak5_out);
262 }
263
267 bool clean_;
268
272 poly_float resonance_;
273
277 poly_float drive_;
278
282 poly_float peak1_amount_;
283
287 poly_float peak3_amount_;
288
292 poly_float peak5_amount_;
293
297 poly_float invert_mult_;
298
302 OnePoleFilter<> stages_[kMaxStages];
303
307 OnePoleFilter<> remove_lows_stage_;
308 OnePoleFilter<> remove_highs_stage_;
309
313 poly_float allpass_output_;
314
315 JUCE_LEAK_DETECTOR(PhaserFilter)
316 };
317} // namespace vital
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
A multi-stage phaser filter for the Vital synthesizer.
Definition phaser_filter.h:19
PhaserFilter(bool clean)
Constructs a PhaserFilter object.
Definition phaser_filter.cpp:11
static constexpr int kMaxStages
Maximum number of stages (3 clusters of 4 stages = 12 total).
Definition phaser_filter.h:49
static constexpr mono_float kMinCutoff
Minimum cutoff frequency in Hz (used internally).
Definition phaser_filter.h:34
static constexpr mono_float kClearRatio
A ratio used to remove/clear low frequencies or high frequencies in the phaser path.
Definition phaser_filter.h:39
static constexpr mono_float kMaxResonance
Maximum resonance value.
Definition phaser_filter.h:29
static constexpr mono_float kMinResonance
Minimum resonance value.
Definition phaser_filter.h:24
poly_float getDrive()
Gets the current drive setting.
Definition phaser_filter.h:114
static constexpr int kPeakStage
Number of all-pass stages per peak cluster. (4 stages per cluster)
Definition phaser_filter.h:44
void reset(poly_mask reset_mask) override
Resets internal filter states for the specified voices.
Definition phaser_filter.cpp:22
virtual ~PhaserFilter()
Virtual destructor.
Definition phaser_filter.h:60
poly_float getPeak1Amount()
Gets the current peak1 (lowest peak cluster) mix amount.
Definition phaser_filter.h:120
poly_float getResonance()
Gets the current resonance value.
Definition phaser_filter.h:108
poly_float getPeak3Amount()
Gets the current peak3 (second peak cluster) mix amount.
Definition phaser_filter.h:126
void hardReset() override
Performs a full reset of the filter states (for all voices).
Definition phaser_filter.cpp:34
virtual Processor * clone() const override
Creates a clone (deep copy) of the PhaserFilter.
Definition phaser_filter.h:66
virtual void process(int num_samples) override
Processes the audio buffer through the phaser effect.
Definition phaser_filter.cpp:48
void setClean(bool clean)
Toggles between a clean or distorted phaser mode.
Definition phaser_filter.h:102
void processWithInput(const poly_float *audio_in, int num_samples) override
Processes a given input buffer through the phaser effect.
Definition phaser_filter.cpp:58
poly_float getPeak5Amount()
Gets the current peak5 (highest peak cluster) mix amount.
Definition phaser_filter.h:132
void setupFilter(const FilterState &filter_state) override
Sets up the filter parameters (resonance, drive, peaks) based on the FilterState.
Definition phaser_filter.cpp:71
Base class for all signal-processing units in Vital.
Definition processor.h:212
force_inline int getSampleRate() const
Retrieves the current (effective) sample rate.
Definition processor.h:326
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
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
Abstract base class for Vital’s synthesizer filters.
Definition synth_filter.h:19
@ kReset
Reset signal.
Definition synth_filter.h:56
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
OneDimLookup< computeOnePoleFilterCoefficient, 2048 > CoefficientLookup
A lookup table for quick computation of one-pole filter coefficients.
Definition synth_filter.h:37
#define force_inline
Definition common.h:23
cr::Value resonance
Resonance factor for this formant.
Definition formant_filter.cpp:18
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 mulAdd(poly_float a, poly_float b, poly_float c)
Performs a fused multiply-add on SIMD data: (a * b) + c.
Definition poly_utils.h:61
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
Contains classes and functions used within the Vital synthesizer framework.
float mono_float
Definition common.h:33
Declares the Processor class and related structures for handling audio processing in a polyphonic con...
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