MayaFlux 0.1.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 Prints a visual representation of the phasor waveform
108 *
109 * Outputs a text-based graph of the phasor pattern over time,
110 * useful for debugging and visualization.
111 */
112 inline void printGraph() override { }
113
114 /**
115 * @brief Prints the current parameters of the phasor generator
116 *
117 * Outputs the current frequency, amplitude, offset, and modulation
118 * settings of the generator.
119 */
120 inline void printCurrent() override { }
121
122 /**
123 * @brief Sets the generator's frequency
124 * @param frequency New frequency in Hz
125 *
126 * Updates the generator's frequency, which controls how fast the phase ramps.
127 */
128 void set_frequency(float frequency) override;
129
130 /**
131 * @brief Gets the current base frequency
132 * @return Current frequency in Hz
133 */
134 inline float get_frequency() const { return m_frequency; }
135
136 /**
137 * @brief Sets all basic parameters at once
138 * @param frequency New frequency in Hz
139 * @param amplitude New amplitude
140 * @param offset New DC offset
141 *
142 * This is more efficient than setting parameters individually
143 * when multiple parameters need to be changed.
144 */
145 inline void set_params(float frequency, double amplitude, float offset)
146 {
147 m_amplitude = amplitude;
148 m_offset = offset;
149 set_frequency(frequency);
150 }
151
152 /**
153 * @brief Sets a node to modulate the generator's frequency
154 * @param modulator Node that will modulate the frequency
155 *
156 * The modulator's output is added to the base frequency,
157 * enabling dynamic control of phasor rate.
158 */
159 void set_frequency_modulator(const std::shared_ptr<Node>& modulator);
160
161 /**
162 * @brief Sets a node to modulate the generator's amplitude
163 * @param modulator Node that will modulate the amplitude
164 *
165 * The modulator's output is multiplied with the base amplitude,
166 * enabling dynamic control of phasor height.
167 */
168 void set_amplitude_modulator(const std::shared_ptr<Node>& modulator);
169
170 /**
171 * @brief Removes all modulation connections
172 *
173 * After calling this method, the generator will use only its
174 * base frequency and amplitude without any modulation.
175 */
176 void clear_modulators();
177
178 /**
179 * @brief Resets the generator's phase and parameters
180 * @param frequency New frequency in Hz (default: 1Hz)
181 * @param amplitude New amplitude (default: 1.0)
182 * @param offset New DC offset (default: 0)
183 * @param phase Initial phase value (default: 0.0)
184 *
185 * This method resets the generator's internal state and parameters,
186 * effectively restarting it from the specified phase.
187 */
188 void reset(float frequency = 1, float amplitude = 1.0, float offset = 0, double phase = 0.0);
189
190 /**
191 * @brief Sets the current phase of the phasor
192 * @param phase New phase value (0.0 to 1.0)
193 *
194 * This allows direct control of the phasor's position in its cycle,
195 * useful for synchronizing with other generators or external events.
196 */
197 inline void set_phase(double phase)
198 {
199 m_phase = phase;
200 // Keep phase in [0, 1) range
201 while (m_phase >= 1.0)
202 m_phase -= 1.0;
203 while (m_phase < 0.0)
204 m_phase += 1.0;
205 }
206
207 /**
208 * @brief Gets the current phase of the phasor
209 * @return Current phase (0.0 to 1.0)
210 */
211 inline double get_phase() const { return m_phase; }
212
213 /**
214 * @brief Registers a callback for every phase wrap
215 * @param callback Function to call when the phase wraps
216 *
217 * This method allows external components to monitor or react to
218 * every time the phasor completes a cycle and wraps back to 0.
219 * The callback receives a GeneratorContext containing the generated
220 * value and generator parameters like frequency, amplitude, and phase.
221 */
222 void on_phase_wrap(const NodeHook& callback);
223
224 /**
225 * @brief Registers a callback for every time the phasor crosses a threshold
226 * @param callback Function to call when the threshold is crossed
227 * @param threshold Value at which to trigger the callback
228 * @param rising Whether to trigger on rising (true) or falling (false) edges
229 *
230 * This method allows external components to monitor or react to
231 * every time the phasor crosses a specific threshold value. The callback
232 * receives a GeneratorContext containing the generated value and
233 * generator parameters like frequency, amplitude, and phase.
234 */
235 void on_threshold(const NodeHook& callback, double threshold, bool rising = true);
236
237 /**
238 * @brief Removes a previously registered callback
239 * @param callback The callback function to remove
240 * @return True if the callback was found and removed, false otherwise
241 *
242 * Unregisters a callback previously added with on_tick(), stopping
243 * it from receiving further notifications about generated samples.
244 */
245 bool remove_hook(const NodeHook& callback) override;
246
247 /**
248 * @brief Removes all registered callbacks
249 *
250 * Clears all standard and conditional callbacks, effectively
251 * disconnecting all external components from this generator's
252 * notification system. Useful when reconfiguring the processing
253 * graph or shutting down components.
254 */
255 inline void remove_all_hooks() override
256 {
257 m_callbacks.clear();
258 m_conditional_callbacks.clear();
259 m_phase_wrap_callbacks.clear();
260 m_threshold_callbacks.clear();
261 }
262
263 void save_state() override;
264 void restore_state() override;
265
266protected:
267 /**
268 * @brief Notifies all registered callbacks about a new sample
269 * @param value The newly generated sample
270 *
271 * This method is called internally whenever a new sample is generated,
272 * creating the appropriate context and invoking all registered callbacks
273 * that should receive notification about this sample.
274 */
275 void notify_tick(double value) override;
276
277 /**
278 * @brief Removes a previously registered phase wrap callback
279 * @param callback The callback function to remove
280 * @return True if the callback was found and removed, false otherwise
281 *
282 * Unregisters a callback previously added with on_phase_wrap(),
283 * stopping it from receiving further notifications about phase wraps.
284 */
285 bool remove_threshold_callback(const NodeHook& callback);
286
287private:
288 /**
289 * @brief Phase increment per sample
290 *
291 * This value determines how much the phase advances with each sample,
292 * controlling the generator's frequency.
293 */
294 double m_phase_inc {};
295
296 /**
297 * @brief DC offset added to the output
298 */
299 float m_offset;
300
301 /**
302 * @brief Node that modulates the frequency
303 */
304 std::shared_ptr<Node> m_frequency_modulator;
305
306 /**
307 * @brief Node that modulates the amplitude
308 */
309 std::shared_ptr<Node> m_amplitude_modulator;
310
311 /**
312 * @brief Updates the phase increment based on a new frequency
313 * @param frequency New frequency in Hz
314 *
315 * This method calculates the phase increment needed to produce
316 * a complete cycle at the specified frequency at the current sample rate.
317 */
318 void update_phase_increment(double frequency);
319
320 /**
321 * @brief Collection of phase wrap-specific callback functions
322 */
323 std::vector<NodeHook> m_phase_wrap_callbacks;
324
325 /**
326 * @brief Collection of threshold-specific callback functions with their thresholds
327 */
328 std::vector<std::pair<NodeHook, double>> m_threshold_callbacks;
329
330 /**
331 * @brief Flag indicating whether the phase has wrapped in the current sample
332 */
334
335 /**
336 * @brief Flag indicating whether the threshold has been crossed in the current sample
337 */
339
340 double m_saved_phase {};
341 float m_saved_frequency {};
342 float m_saved_offset {};
343 double m_saved_phase_inc {};
344 double m_saved_last_output {};
345
346 bool m_state_saved {};
347};
348}
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:304
float get_frequency() const
Gets the current base frequency.
Definition Phasor.hpp:134
bool m_threshold_crossed
Flag indicating whether the threshold has been crossed in the current sample.
Definition Phasor.hpp:338
bool m_phase_wrapped
Flag indicating whether the phase has wrapped in the current sample.
Definition Phasor.hpp:333
void set_phase(double phase)
Sets the current phase of the phasor.
Definition Phasor.hpp:197
std::shared_ptr< Node > m_amplitude_modulator
Node that modulates the amplitude.
Definition Phasor.hpp:309
std::vector< NodeHook > m_phase_wrap_callbacks
Collection of phase wrap-specific callback functions.
Definition Phasor.hpp:323
double get_phase() const
Gets the current phase of the phasor.
Definition Phasor.hpp:211
void remove_all_hooks() override
Removes all registered callbacks.
Definition Phasor.hpp:255
std::vector< std::pair< NodeHook, double > > m_threshold_callbacks
Collection of threshold-specific callback functions with their thresholds.
Definition Phasor.hpp:328
void set_params(float frequency, double amplitude, float offset)
Sets all basic parameters at once.
Definition Phasor.hpp:145
void printCurrent() override
Prints the current parameters of the phasor generator.
Definition Phasor.hpp:120
void printGraph() override
Prints a visual representation of the phasor waveform.
Definition Phasor.hpp:112
~Phasor() override=default
Virtual destructor.
float m_offset
DC offset added to the output.
Definition Phasor.hpp:299
Phase ramp generator node.
Definition Phasor.hpp:31
std::function< void(NodeContext &)> NodeHook
Callback function type for node processing events.
Definition NodeUtils.hpp:25