MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Phasor.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Generator.hpp"
4
6
7/**
8 * @class Phasor
9 * @brief Phase ramp generator node
10 *
11 * The Phasor class generates a linearly increasing ramp from 0 to 1 that wraps around,
12 * creating a sawtooth-like waveform. This is a fundamental building block for many
13 * synthesis techniques and can be used to derive other waveforms.
14 *
15 * Key features:
16 * - Configurable frequency, amplitude, and DC offset
17 * - Support for frequency modulation (changing ramp rate)
18 * - Support for amplitude modulation (changing ramp height)
19 * - Automatic registration with the node graph manager (optional)
20 * - Precise phase control for synchronization with other generators
21 *
22 * Phasor generators are used extensively in audio and signal processing for:
23 * - Creating time-based effects and modulations
24 * - Serving as the basis for other waveforms (triangle, pulse, etc.)
25 * - Synchronizing multiple oscillators
26 * - Driving sample playback positions
27 *
28 * The implementation uses a simple phase accumulation approach that ensures
29 * sample-accurate timing and smooth transitions.
30 */
31class MAYAFLUX_API Phasor : public Generator, public std::enable_shared_from_this<Phasor> {
32public:
33 /**
34 * @brief Basic constructor with fixed parameters
35 * @param frequency Phasor cycle rate in Hz (default: 1Hz, one cycle per second)
36 * @param amplitude Phasor amplitude (default: 1.0)
37 * @param offset DC offset added to the output (default: 0.0)
38 *
39 * Creates a phasor generator with fixed frequency and amplitude.
40 */
41 Phasor(float frequency = 1, double amplitude = 1, float offset = 0);
42
43 /**
44 * @brief Constructor with frequency modulation
45 * @param frequency_modulator Node that modulates the frequency
46 * @param frequency Base frequency in Hz (default: 1Hz)
47 * @param amplitude Phasor amplitude (default: 1.0)
48 * @param offset DC offset added to the output (default: 0.0)
49 *
50 * Creates a phasor generator with frequency modulation, where the actual frequency
51 * is the base frequency plus the output of the modulator node.
52 */
53 Phasor(const std::shared_ptr<Node>& frequency_modulator, float frequency = 1, double amplitude = 1, float offset = 0);
54
55 /**
56 * @brief Constructor with amplitude modulation
57 * @param frequency Phasor cycle rate in Hz
58 * @param amplitude_modulator Node that modulates the amplitude
59 * @param amplitude Base amplitude (default: 1.0)
60 * @param offset DC offset added to the output (default: 0.0)
61 *
62 * Creates a phasor generator with amplitude modulation, where the actual amplitude
63 * is the base amplitude multiplied by the output of the modulator node.
64 */
65 Phasor(float frequency, const std::shared_ptr<Node>& amplitude_modulator, double amplitude = 1, float offset = 0);
66
67 /**
68 * @brief Constructor with both frequency and amplitude modulation
69 * @param frequency_modulator Node that modulates the frequency
70 * @param amplitude_modulator Node that modulates the amplitude
71 * @param frequency Base frequency in Hz (default: 1Hz)
72 * @param amplitude Base amplitude (default: 1.0)
73 * @param offset DC offset added to the output (default: 0.0)
74 *
75 * Creates a phasor generator with both frequency and amplitude modulation,
76 * enabling complex timing and amplitude control.
77 */
78 Phasor(const std::shared_ptr<Node>& frequency_modulator, const std::shared_ptr<Node>& amplitude_modulator,
79 float frequency = 1, double amplitude = 1, float offset = 0);
80
81 /**
82 * @brief Virtual destructor
83 */
84 ~Phasor() override = default;
85
86 /**
87 * @brief Processes a single input sample and generates a phasor sample
88 * @param input Input sample (used for modulation when modulators are connected)
89 * @return Generated phasor sample (ramp from 0 to amplitude)
90 *
91 * This method advances the generator's phase and computes the next
92 * sample of the phasor ramp, applying any modulation from connected nodes.
93 */
94 double process_sample(double input = 0.) override;
95
96 /**
97 * @brief Processes multiple samples at once
98 * @param num_samples Number of samples to generate
99 * @return Vector of generated phasor samples
100 *
101 * This method is more efficient than calling process_sample() repeatedly
102 * when generating multiple samples at once.
103 */
104 std::vector<double> process_batch(unsigned int num_samples) override;
105
106 /**
107 * @brief Sets the generator's frequency
108 * @param frequency New frequency in Hz
109 *
110 * Updates the generator's frequency, which controls how fast the phase ramps.
111 */
112 void set_frequency(float frequency) override;
113
114 /**
115 * @brief Gets the current base frequency
116 * @return Current frequency in Hz
117 */
118 inline float get_frequency() const { return m_frequency; }
119
120 /**
121 * @brief Sets all basic parameters at once
122 * @param frequency New frequency in Hz
123 * @param amplitude New amplitude
124 * @param offset New DC offset
125 *
126 * This is more efficient than setting parameters individually
127 * when multiple parameters need to be changed.
128 */
129 inline void set_params(float frequency, double amplitude, float offset)
130 {
131 m_amplitude = amplitude;
132 m_offset = offset;
133 set_frequency(frequency);
134 }
135
136 /**
137 * @brief Sets a node to modulate the generator's frequency
138 * @param modulator Node that will modulate the frequency
139 *
140 * The modulator's output is added to the base frequency,
141 * enabling dynamic control of phasor rate.
142 */
143 void set_frequency_modulator(const std::shared_ptr<Node>& modulator);
144
145 /**
146 * @brief Sets a node to modulate the generator's amplitude
147 * @param modulator Node that will modulate the amplitude
148 *
149 * The modulator's output is multiplied with the base amplitude,
150 * enabling dynamic control of phasor height.
151 */
152 void set_amplitude_modulator(const std::shared_ptr<Node>& modulator);
153
154 /**
155 * @brief Removes all modulation connections
156 *
157 * After calling this method, the generator will use only its
158 * base frequency and amplitude without any modulation.
159 */
160 void clear_modulators();
161
162 /**
163 * @brief Resets the generator's phase and parameters
164 * @param frequency New frequency in Hz (default: 1Hz)
165 * @param amplitude New amplitude (default: 1.0)
166 * @param offset New DC offset (default: 0)
167 * @param phase Initial phase value (default: 0.0)
168 *
169 * This method resets the generator's internal state and parameters,
170 * effectively restarting it from the specified phase.
171 */
172 void reset(float frequency = 1, float amplitude = 1.0, float offset = 0, double phase = 0.0);
173
174 /**
175 * @brief Sets the current phase of the phasor
176 * @param phase New phase value (0.0 to 1.0)
177 *
178 * This allows direct control of the phasor's position in its cycle,
179 * useful for synchronizing with other generators or external events.
180 */
181 inline void set_phase(double phase)
182 {
183 m_phase = phase;
184 // Keep phase in [0, 1) range
185 while (m_phase >= 1.0)
186 m_phase -= 1.0;
187 while (m_phase < 0.0)
188 m_phase += 1.0;
189 }
190
191 /**
192 * @brief Gets the current phase of the phasor
193 * @return Current phase (0.0 to 1.0)
194 */
195 inline double get_phase() const { return m_phase; }
196
197 /**
198 * @brief Registers a callback for every phase wrap
199 * @param callback Function to call when the phase wraps
200 *
201 * This method allows external components to monitor or react to
202 * every time the phasor completes a cycle and wraps back to 0.
203 * The callback receives a GeneratorContext containing the generated
204 * value and generator parameters like frequency, amplitude, and phase.
205 */
206 void on_phase_wrap(const TypedHook<GeneratorContext>& callback);
207
208 /**
209 * @brief Registers a callback for every time the phasor crosses a threshold
210 * @param callback Function to call when the threshold is crossed
211 * @param threshold Value at which to trigger the callback
212 * @param rising Whether to trigger on rising (true) or falling (false) edges
213 *
214 * This method allows external components to monitor or react to
215 * every time the phasor crosses a specific threshold value. The callback
216 * receives a GeneratorContext containing the generated value and
217 * generator parameters like frequency, amplitude, and phase.
218 */
219 void on_threshold(const TypedHook<GeneratorContext>& callback, double threshold, bool rising = true);
220
221 /**
222 * @brief Removes a previously registered callback
223 * @param callback The callback function to remove
224 * @return True if the callback was found and removed, false otherwise
225 *
226 * Unregisters a callback previously added with on_tick(), stopping
227 * it from receiving further notifications about generated samples.
228 */
229 bool remove_hook(const TypedHook<GeneratorContext>& callback);
230
231 /**
232 * @brief Removes all registered callbacks
233 *
234 * Clears all standard and conditional callbacks, effectively
235 * disconnecting all external components from this generator's
236 * notification system. Useful when reconfiguring the processing
237 * graph or shutting down components.
238 */
239 inline void remove_all_hooks() override
240 {
241 m_callbacks.clear();
242 m_conditional_callbacks.clear();
243 m_phase_wrap_callbacks.clear();
244 m_threshold_callbacks.clear();
245 }
246
247 void save_state() override;
248 void restore_state() override;
249
250 /**
251 * @brief Retrieves the current modulators connected to this node
252 * @return Vector of pairs containing the modulator role and the corresponding node
253 */
254 [[nodiscard]] std::vector<std::pair<ModulatorRole, std::shared_ptr<Node>>> get_modulators() const override;
255
256protected:
257 /**
258 * @brief Notifies all registered callbacks about a new sample
259 * @param value The newly generated sample
260 *
261 * This method is called internally whenever a new sample is generated,
262 * creating the appropriate context and invoking all registered callbacks
263 * that should receive notification about this sample.
264 */
265 void notify_tick(double value) override;
266
267 /**
268 * @brief Removes a previously registered phase wrap callback
269 * @param callback The callback function to remove
270 * @return True if the callback was found and removed, false otherwise
271 *
272 * Unregisters a callback previously added with on_phase_wrap(),
273 * stopping it from receiving further notifications about phase wraps.
274 */
275 bool remove_threshold_callback(const TypedHook<GeneratorContext>& callback);
276
277private:
278 /**
279 * @brief Phase increment per sample
280 *
281 * This value determines how much the phase advances with each sample,
282 * controlling the generator's frequency.
283 */
284 double m_phase_inc {};
285
286 /**
287 * @brief DC offset added to the output
288 */
289 float m_offset;
290
291 /**
292 * @brief Node that modulates the frequency
293 */
294 std::shared_ptr<Node> m_frequency_modulator;
295
296 /**
297 * @brief Node that modulates the amplitude
298 */
299 std::shared_ptr<Node> m_amplitude_modulator;
300
301 /**
302 * @brief Updates the phase increment based on a new frequency
303 * @param frequency New frequency in Hz
304 *
305 * This method calculates the phase increment needed to produce
306 * a complete cycle at the specified frequency at the current sample rate.
307 */
308 void update_phase_increment(double frequency);
309
310 /**
311 * @brief Collection of phase wrap-specific callback functions
312 */
313 std::vector<TypedHook<GeneratorContext>> m_phase_wrap_callbacks;
314
315 /**
316 * @brief Collection of threshold-specific callback functions with their thresholds
317 */
318 std::vector<std::pair<TypedHook<GeneratorContext>, double>> m_threshold_callbacks;
319
320 /**
321 * @brief Flag indicating whether the phase has wrapped in the current sample
322 */
324
325 /**
326 * @brief Flag indicating whether the threshold has been crossed in the current sample
327 */
329
330 double m_saved_phase {};
331 float m_saved_frequency {};
332 float m_saved_offset {};
333 double m_saved_phase_inc {};
334 double m_saved_last_output {};
335};
336}
Core::GlobalInputConfig input
Definition Config.cpp:36
double frequency
Base class for all signal and pattern generators in Maya Flux.
std::shared_ptr< Node > m_frequency_modulator
Node that modulates the frequency.
Definition Phasor.hpp:294
float get_frequency() const
Gets the current base frequency.
Definition Phasor.hpp:118
bool m_threshold_crossed
Flag indicating whether the threshold has been crossed in the current sample.
Definition Phasor.hpp:328
bool m_phase_wrapped
Flag indicating whether the phase has wrapped in the current sample.
Definition Phasor.hpp:323
void set_phase(double phase)
Sets the current phase of the phasor.
Definition Phasor.hpp:181
std::shared_ptr< Node > m_amplitude_modulator
Node that modulates the amplitude.
Definition Phasor.hpp:299
std::vector< std::pair< TypedHook< GeneratorContext >, double > > m_threshold_callbacks
Collection of threshold-specific callback functions with their thresholds.
Definition Phasor.hpp:318
double get_phase() const
Gets the current phase of the phasor.
Definition Phasor.hpp:195
void remove_all_hooks() override
Removes all registered callbacks.
Definition Phasor.hpp:239
void set_params(float frequency, double amplitude, float offset)
Sets all basic parameters at once.
Definition Phasor.hpp:129
~Phasor() override=default
Virtual destructor.
std::vector< TypedHook< GeneratorContext > > m_phase_wrap_callbacks
Collection of phase wrap-specific callback functions.
Definition Phasor.hpp:313
float m_offset
DC offset added to the output.
Definition Phasor.hpp:289
Phase ramp generator node.
Definition Phasor.hpp:31
std::function< void(ContextT &)> TypedHook
Callback function type for node processing events, parameterised on context type.
Definition NodeUtils.hpp:28