Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
oscillator.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4
5namespace pulp::signal {
6
7// Band-limited oscillator with polyBLEP anti-aliasing.
8// RT contract: setters, reset, and next() allocate no memory. Smooth frequency
9// externally if zipper-free retuning is required.
10// Supports sine, saw, square, and triangle waveforms.
11//
12// Superseded by `pulp::signal::osc` (osc/va.hpp) — prefer it for new work, and
13// extend that module rather than this one. This stays for existing callers and
14// is not a foundation: its phase is float, it wraps only at the top (so a
15// negative increment, which through-zero FM requires, is impossible), and its
16// triangle integrates a leaky square, which makes level fall with pitch. The
17// newer module fixes all three and adds hard sync, TZFM, and pulse width.
18template <typename SampleType = float>
20public:
21 enum class Waveform { sine, saw, square, triangle };
22
23 void set_sample_rate(SampleType sr) { sample_rate_ = sr; }
24 void set_frequency(SampleType hz) { freq_ = hz; }
25 void set_waveform(Waveform w) { waveform_ = w; }
26
27 // Reset phase to 0.
28 void reset() noexcept { phase_ = SampleType{0}; }
29
30 // Set a normalized phase without allowing invalid state into the audio
31 // path. The phase is accepted in [0, 1); failure leaves state unchanged.
32 bool reset_phase(SampleType normalized_phase) noexcept {
33 if (!std::isfinite(normalized_phase) || normalized_phase < SampleType{0} ||
34 normalized_phase >= SampleType{1}) {
35 return false;
36 }
37 phase_ = normalized_phase;
38 tri_state_ = SampleType{0};
39 return true;
40 }
41
42 // Generate next sample
43 SampleType next() {
44 SampleType dt = freq_ / sample_rate_;
45 SampleType out = 0;
46
47 switch (waveform_) {
48 case Waveform::sine:
49 out = std::sin(SampleType{2.0f} * pi * phase_);
50 break;
51
52 case Waveform::saw:
53 out = SampleType{2.0f} * phase_ - SampleType{1.0f};
54 out -= poly_blep(phase_, dt);
55 break;
56
57 case Waveform::square: {
58 out = phase_ < SampleType{0.5f} ? SampleType{1.0f} : SampleType{-1.0f};
59 out += poly_blep(phase_, dt);
60 out -= poly_blep(std::fmod(phase_ + SampleType{0.5f}, SampleType{1.0f}), dt);
61 break;
62 }
63
65 // Integrated square wave
66 out = phase_ < SampleType{0.5f} ? SampleType{1.0f} : SampleType{-1.0f};
67 out += poly_blep(phase_, dt);
68 out -= poly_blep(std::fmod(phase_ + SampleType{0.5f}, SampleType{1.0f}), dt);
69 // Integrate: leaky integrator
70 tri_state_ = dt * out + (SampleType{1.0f} - dt) * tri_state_;
71 // Scale to -1..1 range
72 out = tri_state_ * SampleType{4.0f};
73 break;
74 }
75
76 // Advance phase
77 phase_ += dt;
78 if (phase_ >= SampleType{1.0f}) phase_ -= SampleType{1.0f};
79
80 return out;
81 }
82
83 SampleType phase() const { return phase_; }
84 SampleType frequency() const { return freq_; }
85
86private:
87 static constexpr SampleType pi = SampleType{3.14159265358979323846f};
88
89 SampleType sample_rate_ = SampleType{44100.0f};
90 SampleType freq_ = SampleType{440.0f};
91 SampleType phase_ = 0;
92 SampleType tri_state_ = 0; // For triangle wave integration
93 Waveform waveform_ = Waveform::sine;
94
95 // PolyBLEP anti-aliasing residual
96 static SampleType poly_blep(SampleType t, SampleType dt) {
97 if (t < dt) {
98 t /= dt;
99 return t + t - t * t - SampleType{1.0f};
100 }
101 if (t > SampleType{1.0f} - dt) {
102 t = (t - SampleType{1.0f}) / dt;
103 return t * t + t + t + SampleType{1.0f};
104 }
105 return SampleType{0.0f};
106 }
107};
108
111
112} // namespace pulp::signal
Definition oscillator.hpp:19
void set_frequency(SampleType hz)
Definition oscillator.hpp:24
void set_waveform(Waveform w)
Definition oscillator.hpp:25
void set_sample_rate(SampleType sr)
Definition oscillator.hpp:23
Waveform
Definition oscillator.hpp:21
@ square
Definition oscillator.hpp:21
@ sine
Definition oscillator.hpp:21
@ saw
Definition oscillator.hpp:21
@ triangle
Definition oscillator.hpp:21
SampleType frequency() const
Definition oscillator.hpp:84
SampleType phase() const
Definition oscillator.hpp:83
SampleType next()
Definition oscillator.hpp:43
void reset() noexcept
Definition oscillator.hpp:28
bool reset_phase(SampleType normalized_phase) noexcept
Definition oscillator.hpp:32
Definition additive_bank.hpp:218
OscillatorT< double > Oscillator64
Definition oscillator.hpp:110
OscillatorT< float > Oscillator
Definition oscillator.hpp:109
@ t
Definition input_events.hpp:41