iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
Oscillator.h
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
13#include "IPlugPlatform.h"
14
15BEGIN_IPLUG_NAMESPACE
16
17template <typename T>
19{
20public:
21 IOscillator(double startPhase = 0., double startFreq = 1.)
22 : mStartPhase(startPhase)
23 {
24 SetFreqCPS(startFreq);
25 }
26
27 virtual inline T Process(double freqHz) = 0;
28
29 inline void SetFreqCPS(double freqHz)
30 {
31 mPhaseIncr = (1./mSampleRate) * freqHz;
32 }
33
34 void SetSampleRate(double sampleRate)
35 {
36 mSampleRate = sampleRate;
37 }
38
39 void Reset()
40 {
41 mPhase = mStartPhase;
42 }
43
44 void SetPhase(double phase)
45 {
46 mPhase = phase;
47 }
48
49protected:
50 double mPhase = 0.; // float phase (goes between 0. and 1.)
51 double mPhaseIncr = 0.; // how much to add to the phase on each T
52 double mSampleRate = 44100.;
53 double mStartPhase;
54};
55
56template <typename T>
57class SinOscillator : public IOscillator<T>
58{
59public:
60 SinOscillator(double startPhase = 0., double startFreq = 1.)
61 : IOscillator<T>(startPhase, startFreq)
62 {
63 }
64
65 inline T Process()
66 {
68 return std::sin(IOscillator<T>::mPhase * PI * 2.);
69 }
70
71 inline T Process(double freqHz) override
72 {
75 return std::sin(IOscillator<T>::mPhase * PI * 2.);
76 }
77};
78
79/*
80 FastSinOscillator - fast sinusoidal oscillator / table look up, based on an approach and code used by Miller Puckette in Pure Data, originally by Robert Höldrich
81
82 From some correspondence with M.S.P...
83
84 The basic idea is this: if you have a double precision floating point number
85 between -2^19 (-524288) and +2^19 (+524288), and you want the fractional part as an
86 integer, proceed as follows:
87
88 Add 3*2^19 (1572864) to it. The sum will be between 2^20 (1048576) and 2^21 (2097152) and so, in the usual
89 double precision format, the units bit will be the LSB of the higher-order
90 32 bits, and the fractional part will be neatly represented as an unsigned
91 fixed-point number between 0 and 2^32-1.
92
93 You can then use a C union to read these lower 32 bits as an integer. Or, if
94 you want to access then as a floating-point number without having to use
95 fix-to-float conversion, just bash the upper 32 bits to their original
96 state for representing 3*2^19. So far this is what's going on in the wrap~
97 and phasor~ objects in Pd.
98
99 The objects that do interpolating table lookup (e.g., cos~) take this one step
100 further. Here you end up with a number (now supposing it to be between
101 -2^10 and +2^10). Multiply by 2^9 (the size of the table) so that the
102 9-bit table address is in the 9 lowest bits of the upper 32 bit 'word', and
103 the fractional part is in the lower 32 bits as before. Grab the address as
104 an integer (masking to get just the 9 bits we want).
105
106 The you need to re-convert the fractional address into floating-point format
107 so you can use it to interpolate. Just bash the upper 32 bits with the
108 original value for 3*2^19, and the result will be a 64-bit floating-point number
109 between 3*2^19 and 3*2^19+1. Subtracting 3*2^19 from this gives us a floating-
110 point representation of the fractional part.
111 */
112
113#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */
114#define HIOFFSET 1
115#define LOWOFFSET 0
116
117#ifdef _MSC_VER
118#define ALIGN16 __declspec(align(16))
119#define ALIGNED(x)
120#else
121#define ALIGN16 alignas(16)
122#define ALIGNED(x) __attribute__ ((aligned (x)))
123#endif
124
125template <typename T>
127{
128 union tabfudge
129 {
130 double d;
131 int i[2];
132 } ALIGNED(8);
133
134public:
135 FastSinOscillator(double startPhase = 0., double startFreq = 1.)
136 : IOscillator<T>(startPhase * tableSizeM1, startFreq)
137 {
138 }
139
140 inline T Process()
141 {
142 T output = 0.;
143 ProcessBlock(&output, 1);
144
145 return output;
146 }
147
148 inline T Process(double freqCPS) override
149 {
151
152 T output = 0.;
153 ProcessBlock(&output, 1);
154
155 return output;
156 }
157
158 static inline T Lookup(double phaseRadians)
159 {
160 double tPhase = phaseRadians / (PI * 2.) * tableSizeM1;
161
162 tPhase += (double) UNITBIT32;
163
164 union tabfudge tf;
165 tf.d = UNITBIT32;
166 const int normhipart = tf.i[HIOFFSET];
167
168 tf.d = tPhase;
169 const T* addr = mLUT + (tf.i[HIOFFSET] & tableSizeM1);
170 tf.i[HIOFFSET] = normhipart;
171 const double frac = tf.d - UNITBIT32;
172 const T f1 = addr[0];
173 const T f2 = addr[1];
174 return f1 + frac * (f2 - f1);
175 }
176
177 void ProcessBlock(T* pOutput, int nFrames)
178 {
179 double phase = IOscillator<T>::mPhase + (double) UNITBIT32;
180 const double phaseIncr = IOscillator<T>::mPhaseIncr * tableSize;
181
182 union tabfudge tf;
183 tf.d = UNITBIT32;
184 const int normhipart = tf.i[HIOFFSET];
185
186 for (auto s = 0; s < nFrames; s++)
187 {
188 tf.d = phase;
189 phase += phaseIncr;
190 const T* addr = mLUT + (tf.i[HIOFFSET] & tableSizeM1); // Obtain the integer portion
191 tf.i[HIOFFSET] = normhipart; // Force the double to wrap.
192 const double frac = tf.d - UNITBIT32;
193 const T f1 = addr[0];
194 const T f2 = addr[1];
195 mLastOutput = pOutput[s] = T(f1 + frac * (f2 - f1));
196 }
197
198 // Restore mPhase
199 tf.d = UNITBIT32 * tableSize;
200 const int normhipart2 = tf.i[HIOFFSET];
201 tf.d = phase + (UNITBIT32 * tableSize - UNITBIT32); // Remove the offset we introduced at the start of UNITBIT32.
202 tf.i[HIOFFSET] = normhipart2;
203 IOscillator<T>::mPhase = tf.d - UNITBIT32 * tableSize;
204 }
205
206 T mLastOutput = 0.;
207private:
208 static const int tableSize = 512; // 2^9
209 static const int tableSizeM1 = 511; // 2^9 -1
210 static const T mLUT[513];
211} ALIGNED(8);
212
213#include "Oscillator_table.h"
214
215END_IPLUG_NAMESPACE
Include to get consistently named preprocessor macros for different platforms and logging functionali...