Vital
Loading...
Searching...
No Matches
chorus_module.cpp
Go to the documentation of this file.
1#include "chorus_module.h"
2
3#include "delay.h"
4#include "memory.h"
5#include "synth_constants.h"
6
7namespace vital {
8
9 ChorusModule::ChorusModule(const Output* beats_per_second) :
10 SynthModule(0, 1),
11 beats_per_second_(beats_per_second),
12 frequency_(nullptr),
13 delay_time_1_(nullptr),
14 delay_time_2_(nullptr),
15 mod_depth_(nullptr),
16 phase_(0.0f) {
17 wet_ = 0.0f;
18 dry_ = 0.0f;
20 int max_samples = kMaxChorusDelay * kMaxSampleRate + 1;
21
22 // Allocate delay lines and register their outputs for debugging or analysis.
23 for (int i = 0; i < kMaxDelayPairs; ++i) {
25 delays_[i] = new MultiDelay(max_samples);
27 }
28 }
29
31 static const cr::Value kDelayStyle(MultiDelay::kMono);
32
33 voices_ = createBaseControl("chorus_voices");
34
35 // Create mod controls and link them with line generator or value sources.
36 Output* free_frequency = createMonoModControl("chorus_frequency");
37 frequency_ = createTempoSyncSwitch("chorus", free_frequency->owner, beats_per_second_, false);
38 Output* feedback = createMonoModControl("chorus_feedback");
39 wet_output_ = createMonoModControl("chorus_dry_wet");
40 Output* cutoff = createMonoModControl("chorus_cutoff");
41 Output* spread = createMonoModControl("chorus_spread");
42 mod_depth_ = createMonoModControl("chorus_mod_depth");
43
44 delay_time_1_ = createMonoModControl("chorus_delay_1");
45 delay_time_2_ = createMonoModControl("chorus_delay_2");
46
47 // Plug parameters into each delay line.
48 for (int i = 0; i < kMaxDelayPairs; ++i) {
50 delays_[i]->plug(feedback, MultiDelay::kFeedback);
54 delays_[i]->plug(&kDelayStyle, MultiDelay::kStyle);
55 }
56
58 }
59
60 void ChorusModule::enable(bool enable) {
62 process(1); // Ensure one block of processing to initialize states.
63 if (enable) {
64 // Reset wet/dry mix and delay lines on enable.
65 wet_ = 0.0f;
66 dry_ = 0.0f;
67
68 for (int i = 0; i < kMaxDelayPairs; ++i)
69 delays_[i]->hardReset();
70 }
71 }
72
74 int num_voice_pairs = voices_->value();
75
76 // If number of voices increased, reset the newly added voices.
77 for (int i = last_num_voices_; i < num_voice_pairs; ++i)
79
80 last_num_voices_ = num_voice_pairs;
81 return num_voice_pairs;
82 }
83
84 void ChorusModule::processWithInput(const poly_float* audio_in, int num_samples) {
85 // Update any internal parameters
86 SynthModule::process(num_samples);
87
88 // Calculate the modulation phase increment based on frequency.
89 poly_float frequency = frequency_->buffer[0];
90 poly_float delta_phase = (frequency * num_samples) * (1.0f / getSampleRate());
91 phase_ = utils::mod(phase_ + delta_phase);
92
93 poly_float* audio_out = output()->buffer;
94
95 // Start by copying the input audio to output, forming the base (dry) signal.
96 for (int s = 0; s < num_samples; ++s) {
97 poly_float sample = audio_in[s] & constants::kFirstMask;
98 // SwapVoices used to handle stereo processing where left/right are interleaved in poly_float.
99 audio_out[s] = sample + utils::swapVoices(sample);
100 }
101
102 // Get the number of voices and prepare delay parameters.
103 int num_voices = getNextNumVoicePairs();
104
105 poly_float delay1 = delay_time_1_->buffer[0];
106 poly_float delay2 = delay_time_2_->buffer[0];
107 // Uses the first mask to select which delay time to use for left channel, interpolating for other channels.
108 poly_float delay_time = utils::maskLoad(delay2, delay1, constants::kFirstMask);
109 poly_float average_delay = (delay_time + utils::swapVoices(delay_time)) * 0.5f;
110
111 // Apply modulation and set up delays:
112 for (int i = 0; i < num_voices; ++i) {
113 // Stagger phases for different voices to create a richer chorus texture.
114 float pair_offset = i * 0.25f / num_voices;
115 poly_float right_offset = (poly_float(0.25f) & constants::kRightMask);
116 poly_float phase = phase_ + right_offset + (poly_float(0.5f) & ~constants::kFirstMask) + pair_offset;
117
119 // Create a sinusoidal modulation of delay time.
120 poly_float mod = utils::sin(phase * vital::kPi * 2.0f) * 0.5f + 1.0f;
121 float delay_t = 0.0f;
122 if (i > 0)
123 delay_t = i / (num_voices - 1.0f);
124
125 poly_float delay = mod * mod_depth + utils::interpolate(delay_time, average_delay, delay_t);
126
127 // Set the frequency parameter on the delay line (inverse of delay time).
128 vital::poly_float delay_frequency = poly_float(1.0f) / utils::max(0.00001f, delay);
129 delay_frequencies_[i].set(delay_frequency);
130 // Process the audio through the delay line.
131 delays_[i]->processWithInput(audio_out, num_samples);
132
133 // For debugging or analysis, store the delay frequency in the corresponding output.
134 delay_status_outputs_[i].buffer[0] = delay_frequency;
135 }
136
137 // Manage transitions of wet/dry mixing over the block:
138 poly_float current_wet = wet_;
139 poly_float current_dry = dry_;
140
141 poly_float wet_value = utils::clamp(wet_output_->buffer[0], 0.0f, 1.0f);
142 wet_ = futils::equalPowerFade(wet_value);
144
145 mono_float tick_increment = 1.0f / num_samples;
146 poly_float delta_wet = (wet_ - current_wet) * tick_increment;
147 poly_float delta_dry = (dry_ - current_dry) * tick_increment;
148
149 // Clear the output buffer before mixing in the delayed signals.
150 utils::zeroBuffer(audio_out, num_samples);
151
152 // Mix in all delay outputs to create the chorus effect.
153 for (int i = 0; i < num_voices; ++i) {
154 const poly_float* delay_out = delays_[i]->output()->buffer;
155
156 for (int s = 0; s < num_samples; ++s) {
157 poly_float sample_out = delay_out[s] * 0.5f;
158 audio_out[s] += sample_out + utils::swapVoices(sample_out);
159 }
160 }
161
162 // Apply a gradually changing wet/dry mix to smoothly transition over the block.
163 for (int s = 0; s < num_samples; ++s) {
164 current_dry += delta_dry;
165 current_wet += delta_wet;
166 audio_out[s] = current_dry * audio_in[s] + current_wet * audio_out[s];
167 }
168 }
169
170 void ChorusModule::correctToTime(double seconds) {
171 // Recalculate the phase so that the chorus aligns to a given time, for sync or timing-sensitive operations.
173 }
174} // namespace vital
poly_float dry_
Current dry amount (for wet/dry mixing).
Definition chorus_module.h:100
static constexpr mono_float kMaxChorusModulation
The maximum modulation depth in seconds (for delay time modulation).
Definition chorus_module.h:21
Value * voices_
Control for the number of chorus voices.
Definition chorus_module.h:86
poly_float phase_
Current modulation phase.
Definition chorus_module.h:98
static constexpr int kMaxDelayPairs
The maximum number of delay line pairs (voices).
Definition chorus_module.h:25
const Output * beats_per_second_
A reference for tempo synchronization.
Definition chorus_module.h:85
void init() override
Initializes the chorus module, setting up controls and linking parameters.
Definition chorus_module.cpp:30
MultiDelay * delays_[kMaxDelayPairs]
The delay processors that implement the chorus voices.
Definition chorus_module.h:105
Output * wet_output_
Control for the wet/dry mix.
Definition chorus_module.h:96
void processWithInput(const poly_float *audio_in, int num_samples) override
Processes the input audio through the chorus effect.
Definition chorus_module.cpp:84
cr::Output delay_status_outputs_[kMaxDelayPairs]
Outputs for delay status or frequency debug information.
Definition chorus_module.h:90
void enable(bool enable) override
Enables or disables the chorus module.
Definition chorus_module.cpp:60
ChorusModule(const Output *beats_per_second)
Constructs a ChorusModule.
Definition chorus_module.cpp:9
int last_num_voices_
Tracks the last known number of voices to detect changes.
Definition chorus_module.h:88
static constexpr mono_float kMaxChorusDelay
The maximum chorus delay time in seconds.
Definition chorus_module.h:23
Output * frequency_
Control for modulation frequency (can be free-running or tempo-synced).
Definition chorus_module.h:92
Output * delay_time_1_
Control for the first delay time parameter.
Definition chorus_module.h:93
cr::Value delay_frequencies_[kMaxDelayPairs]
Holds frequency parameter values for each delay line.
Definition chorus_module.h:104
Output * delay_time_2_
Control for the second delay time parameter.
Definition chorus_module.h:94
poly_float wet_
Current wet amount (for wet/dry mixing).
Definition chorus_module.h:99
Output * mod_depth_
Control for modulation depth.
Definition chorus_module.h:95
int getNextNumVoicePairs()
Retrieves and updates the number of active voice pairs based on control inputs.
Definition chorus_module.cpp:73
void correctToTime(double seconds) override
Adjusts the internal phase to align with a given time, useful for syncing to host position.
Definition chorus_module.cpp:170
@ kMono
Definition delay.h:82
virtual void processWithInput(const poly_float *audio_in, int num_samples) override
Processes a block of audio from a given input buffer.
Definition delay.cpp:83
@ kFrequency
Base delay frequency.
Definition delay.h:70
@ kFeedback
Feedback amount.
Definition delay.h:72
@ kWet
Wet mix amount.
Definition delay.h:69
@ kFilterCutoff
Filter cutoff (in MIDI note).
Definition delay.h:75
@ kFilterSpread
Filter spread around cutoff.
Definition delay.h:76
@ kStyle
Delay style selection.
Definition delay.h:74
virtual Output * registerOutput(Output *output, int index)
Registers a new Output in the output list at a specified index.
Definition processor.cpp:223
force_inline int getSampleRate() const
Retrieves the current (effective) sample rate.
Definition processor.h:326
virtual void hardReset()
Called to perform a "hard" reset for all voices.
Definition processor.h:272
void plug(const Output *source)
Connects an external Output to this Processor's first input.
Definition processor.cpp:79
virtual void process(int num_samples)=0
Main processing function. Called by the ProcessorRouter.
force_inline Output * output(unsigned int index=0) const
Retrieves the Output pointer at a given index.
Definition processor.h:616
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
virtual void addIdleProcessor(Processor *processor)
Adds a Processor that should remain idle (not processed) in the router.
Definition processor_router.cpp:146
virtual void process(int num_samples) override
Processes audio through all Processors managed by this router.
Definition processor_router.cpp:57
A ProcessorRouter that encapsulates a cohesive unit of functionality in the synthesizer.
Definition synth_module.h:129
Value * createBaseControl(std::string name, bool audio_rate=false, bool smooth_value=false)
Creates a simple control processor for a given parameter name.
Definition synth_module.cpp:22
Output * createTempoSyncSwitch(std::string name, Processor *frequency, const Output *beats_per_second, bool poly, Input *midi=nullptr)
Creates a tempo sync switch that toggles between tempo-based frequency and free-running frequency.
Definition synth_module.cpp:289
virtual void enable(bool enable) override
Enables or disables this SynthModule and its owned processors.
Definition synth_module.cpp:516
Output * createMonoModControl(std::string name, bool audio_rate=false, bool smooth_value=false, Output *internal_modulation=nullptr)
Creates a monophonic mod control, including applying parameter scaling.
Definition synth_module.cpp:104
virtual void set(poly_float value)
Sets the internal value to a new poly_float.
Definition value.cpp:17
force_inline mono_float value() const
Returns the current mono_float value of the first lane.
Definition value.h:60
A control-rate variant of the Value processor.
Definition value.h:82
Declares classes for time-domain memory storage and retrieval with cubic interpolation.
const poly_mask kFullMask
A mask covering all lanes of a poly_float vector.
Definition synth_constants.h:257
const poly_mask kFirstMask
A mask identifying the first voice slots in a polyphonic vector.
Definition synth_constants.h:266
const poly_mask kRightMask
A mask identifying the right channel when comparing to kRightOne.
Definition synth_constants.h:263
const cr::Value kValueOne(1.0f)
force_inline poly_float equalPowerFadeInverse(poly_float t)
The inverse equal-power fade from t to t+1.0.
Definition futils.h:448
force_inline poly_float equalPowerFade(poly_float t)
Produces an equal-power crossfade (sin-based) between 0.0 and 1.0.
Definition futils.h:436
force_inline poly_float mod(poly_float value)
Returns the fractional part of each lane by subtracting the floored value.
Definition poly_utils.h:814
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 poly_float getCycleOffsetFromSeconds(double seconds, poly_float frequency)
Computes a cycle offset given a time in seconds and a frequency.
Definition poly_utils.h:885
force_inline poly_float max(poly_float left, poly_float right)
Returns the maximum of two poly_floats lane-by-lane.
Definition poly_utils.h:327
force_inline poly_float swapVoices(poly_float value)
Swaps the first half of the lanes with the second half.
Definition poly_utils.h:437
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 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
Contains classes and functions used within the Vital synthesizer framework.
Delay< Memory > MultiDelay
MultiDelay is a Delay processor specialized with Memory.
Definition delay.h:310
constexpr mono_float kPi
Pi constant.
Definition common.h:36
constexpr int kMaxSampleRate
Maximum expected sample rate in Hz.
Definition common.h:43
float mono_float
Definition common.h:33
Holds and manages a buffer of samples (poly_float) for a Processor's output.
Definition processor.h:35
poly_float * buffer
Pointer to the output buffer.
Definition processor.h:110
Processor * owner
Owning processor.
Definition processor.h:112
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600