iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
LFO.h
Go to the documentation of this file.
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#pragma once
12
18#include "Oscillator.h"
19
20BEGIN_IPLUG_NAMESPACE
21
22#define LFO_TEMPODIV_VALIST "1/64", "1/32", "1/16T", "1/16", "1/16D", "1/8T", "1/8", "1/8D", "1/4", "1/4D", "1/2", "1/1", "2/1", "4/1", "8/1"
23
24#define LFO_SHAPE_VALIST "Triangle", "Square", "Ramp Up", "Ramp Down", "Sine"
25
26template<typename T = double>
27class LFO : public IOscillator<T>
28{
29public:
30 enum ETempoDivison
31 {
32 k64th = 0, // 1 sixty fourth of a beat
33 k32nd, // 1 thirty second of a beat
34 k16thT, // 1 sixteenth note triplet
35 k16th, // 1 sixteenth note
36 k16thD, // 1 dotted sixteenth note
37 k8thT, // 1 eighth note triplet
38 k8th, // 1 eighth note
39 k8thD, // 1 dotted eighth note
40 k4th, // 1 quater note a.k.a 1 beat @ 4/4
41 k4thD, // 1 dotted beat @ 4/4
42 k2th, // 2 beats @ 4/4
43 k1, // 1 bar @ 4/4
44 k2, // 2 bars @ 4/4
45 k4, // 4 bars @ 4/4
46 k8, // 8 bars @ 4/4
47 kNumDivisions
48 };
49
50 enum EShape
51 {
52 kTriangle,
53 kSquare,
54 kRampUp,
55 kRampDown,
56 kSine,
57 kNumShapes
58 };
59
60 enum class EPolarity
61 {
62 kUnipolar,
63 kBipolar
64 };
65
66 enum class ERateMode
67 {
68 kHz,
69 kBPM
70 };
71
73 static T GetQNScalar(ETempoDivison division)
74 {
75 static constexpr T scalars[kNumDivisions] = {
76 64. / 4., // k64th = 0, // 1 sixty fourth of a beat
77 32. / 4., // k32nd, // 1 thirty second of a beat
78 24. / 4., // k16thT, // 1 sixteenth note triplet
79 16. / 4., // k16th, // 1 sixteenth note
80 12. / 4., // k16thD, // 1 dotted sixteenth note
81 9. / 4., // k8thT, // 1 eighth note triplet
82 8. / 4., // k8th, // 1 eighth note
83 6 / 4., // k8thD, // 1 dotted eighth note
84 4. / 4., // k4th, // 1 quater note a.k.a 1 beat @ 4/4
85 3. / 4., // k4thD, // 1 dotted beat @ 4/4
86 2. / 4., // k2th, // 2 beats @ 4/4
87 1. / 4., // k1, // 1 bar @ 4/4
88 0.5 / 4., // k2, // 2 bars @ 4/4
89 0.25 / 4., // k4, // 4 bars @ 4/4
90 0.125 / 4., // k8, // 8 bars @ 4/4
91 };
92
93 return scalars[division];
94 }
95
97 static const char* GetQNDisplay(ETempoDivison division)
98 {
99 static const char* displays[kNumDivisions] = { LFO_TEMPODIV_VALIST };
100 return displays[division];
101 }
102
104 inline T Process(double freqHz) override
105 {
108
109 return DoProcess(IOscillator<T>::mPhase);
110 }
111
112 /* Block process function */
113 void ProcessBlock(T* pOutput, int nFrames, double qnPos = 0., bool transportIsRunning = false, double tempo = 120.)
114 {
115 T oneOverQNScalar = 1./mQNScalar;
116 T phase = IOscillator<T>::mPhase;
117
118 if(mRateMode == ERateMode::kBPM && !transportIsRunning)
120
121 double samplesPerBeat = IOscillator<T>::mSampleRate * (60.0 / (tempo == 0.0 ? 1.0 : tempo)); // samples per beat
122
123 T phaseIncr = IOscillator<T>::mPhaseIncr;
124
125 for (int s=0; s<nFrames; s++)
126 {
127 double sampleAccurateQnPos = qnPos + ((double) s / samplesPerBeat);
128
129 if(mRateMode == ERateMode::kBPM)
130 {
131 if(transportIsRunning)
132 phase = std::fmod(sampleAccurateQnPos, oneOverQNScalar) / oneOverQNScalar;
133 else
134 phase = WrapPhase(phase + (phaseIncr * mQNScalar));
135 }
136 else
137 phase = WrapPhase(phase + phaseIncr);
138
139 pOutput[s] = DoProcess(phase);
140 }
141
143 }
144
145 void SetShape(int lfoShape)
146 {
147 mShape = (EShape) Clip(lfoShape, 0, kNumShapes-1);
148 }
149
150 void SetPolarity(bool bipolar)
151 {
152 mPolarity = bipolar ? EPolarity::kBipolar : EPolarity::kUnipolar;
153 }
154
155 void SetScalar(T scalar)
156 {
157 mLevelScalar = scalar;
158 }
159
160 void SetQNScalar(T scalar)
161 {
162 mQNScalar = scalar;
163 }
164
165 void SetQNScalarFromDivision(int division)
166 {
167 mQNScalar = GetQNScalar(static_cast<ETempoDivison>(Clip(division, 0, (int) kNumDivisions)));
168 }
169
170 void SetRateMode(bool sync)
171 {
172 mRateMode = sync ? ERateMode::kBPM : ERateMode::kHz;
173 }
174
175 T GetLastOutput() const
176 {
177 return mLastOutput;
178 }
179
180private:
181 static inline T WrapPhase (T x, T lo = 0., T hi = 1.)
182 {
183 while (x >= hi)
184 x -= hi;
185 while (x < lo)
186 x += hi - lo;
187 return x;
188 };
189
190 inline T DoProcess(T phase)
191 {
192 auto triangle = [](T x){ return (2. * (1. - std::abs((WrapPhase(x + 0.25) * 2.) -1.))) - 1.; };
193 auto triangleUnipolar = [](T x){ return 1. - std::abs((x * 2.) - 1. ); };
194 auto square = [](T x){ return std::copysign(1., x - 0.5); };
195 auto squareUnipolar = [](T x){ return std::copysign(0.5, x - 0.5) + 0.5; };
196 auto rampup = [](T x){ return (x * 2.) - 1.; };
197 auto rampupUnipolar = [](T x){ return x; };
198 auto rampdown = [](T x){ return ((1. - x) * 2.) - 1.; };
199 auto rampdownUnipolar = [](T x){ return 1. - x; };
200
201 T output = 0.;
202
203 if(mPolarity == EPolarity::kUnipolar)
204 {
205 switch (mShape) {
206 case kTriangle: output = triangleUnipolar(phase); break;
207 case kSquare: output = squareUnipolar(phase); break;
208 case kRampUp: output = rampupUnipolar(phase); break;
209 case kRampDown: output = rampdownUnipolar(phase); break;
210 case kSine: output = (std::sin(phase * 6.283185307179586) * 0.5) + 0.5; break;
211 default: break;
212 }
213 }
214 else
215 {
216 switch (mShape) {
217 case kTriangle: output = triangle(phase); break;
218 case kSquare: output = square(phase); break;
219 case kRampUp: output = rampup(phase); break;
220 case kRampDown: output = rampdown(phase); break;
221 case kSine: output = std::sin(phase * 6.283185307179586); break;
222 default: break;
223 }
224 }
225
226 mLastOutput = output * mLevelScalar;
227
228 return mLastOutput;
229 }
230
231private:
232 T mLastOutput = 0.;
233 T mLevelScalar = 1.; // Non clipped, or smoothed scalar value
234 T mQNScalar = 1.;
235 EShape mShape = EShape::kTriangle;
236 EPolarity mPolarity = EPolarity::kUnipolar;
237 ERateMode mRateMode = ERateMode::kHz;
238};
239
240END_IPLUG_NAMESPACE
Definition: LFO.h:28
static const char * GetQNDisplay(ETempoDivison division)
Get a CString to display the divisor as text.
Definition: LFO.h:97
static T GetQNScalar(ETempoDivison division)
Get the scalar factor to convert a ramp at the host BPM to tempo division value.
Definition: LFO.h:73
T Process(double freqHz) override
Per sample process function, updating frequency per sample.
Definition: LFO.h:104
BEGIN_IPLUG_NAMESPACE T Clip(T x, T lo, T hi)
Clips the value x between lo and hi.