1 /**
2 * Copyright 2017 Cut Through Recordings
3 * License: MIT License
4 * Author(s): Ethan Reker
5 */
6 module ddsp.osc.wtoscillator;
7 
8 import ddsp.effect.effect;
9 
10 const float pi = 3.14159265;
11 
12 enum : int
13 {
14     wav,
15     sqr,
16     saw,
17     tri
18 }
19 
20 class WTOscillator : AudioEffect
21 {
22     import std.math;
23     import core.stdc.stdlib;
24     import ddsp.util.functions;
25     
26 public:
27 
28     this() nothrow @nogc
29     {
30         _waveTable = cast(float*) malloc(1024 * float.sizeof);
31         m_fReadIndex = 0.0f;
32         m_fQuadPhaseReadIndex = 0.0f;
33         _oscType = wav;
34         _noteOn = true;
35     }
36 
37     void doOscillate(float* pYn, float* pYqn) nothrow @nogc
38     {
39         if(!_noteOn)
40         {
41             *pYn = 0.0;
42             *pYqn = 0.0;
43             return;
44         }
45         
46         //output values for this cycle
47         float fOutSample = 0;
48         float fQuadPhaseOutSample = 0;
49         
50         // get INT part
51         int nReadIndex = cast(int)m_fReadIndex;
52         int nQuadPhaseReadIndex = cast(int)m_fQuadPhaseReadIndex;
53         
54         // get FRAC part - NOTE no quad phase frac is needed because it ill be the same!
55         float fFrac = m_fReadIndex - nReadIndex;
56         
57         //setup second index for interpolation; wrap the buffer if needed
58         int nReadIndexNext = nReadIndex + 1 > 1023 ? 0 : nReadIndex + 1;
59         int nQuadPhaseReadIndexNext = nQuadPhaseReadIndex + 1 > 1023 ? 0 : nQuadPhaseReadIndex + 1;
60         
61         fOutSample = linearInterp(0, 1, _waveTable[nReadIndex], _waveTable[nReadIndexNext], fFrac);
62         fQuadPhaseOutSample = linearInterp(0, 1, _waveTable[nQuadPhaseReadIndex], _waveTable[nQuadPhaseReadIndexNext], fFrac);
63         
64         // add the increment for next time
65         m_fReadIndex += _incVal;
66         m_fQuadPhaseReadIndex += _incVal;
67         
68         // check for wrap
69         if(m_fReadIndex > 1024)
70             m_fReadIndex -= 1024;
71         if(m_fQuadPhaseReadIndex > 1024)
72             m_fQuadPhaseReadIndex -= 1024;
73         
74         //invert?
75         if(_invert)
76         {
77             fOutSample *= -1.0;
78             fQuadPhaseOutSample *= -1.0;
79         }
80         
81         *pYn = fOutSample;
82         *pYqn = fQuadPhaseOutSample;
83         
84     }
85 
86     void setParams(float frequency, int oscType, bool tableMode = true) nothrow @nogc
87     {
88         setFrequency(frequency);
89         prevMode = _tableModeNormal;
90         _tableModeNormal = tableMode;
91         setOscType(oscType);
92     }
93     
94     /**
95     *   Recalculates the values in the sine table based on the oscillator type
96     */
97     void setOscType(int oscType) nothrow @nogc
98     {
99         //Triangle
100         //rising edge1:
101         float mt1 = 1.0f / 256.0f;
102         float bt1 = 0.0f;
103         
104         //rising edge2:
105         float mt2 = 1.0f / 256.0f;
106         float bt2 = -1.0;
107         
108         //falling edge
109         float mtf2 = -2.0f / 512.0f;
110         float btf2 = 1.0f;
111         
112         // Sawtooth
113         // rising edge1:
114         float ms1 = 1.0f / 512.0f;
115         float bs1 = 0.0f;
116         
117         //rising edge2:
118         float ms2 = 1.0f / 512.0f;
119         float bs2 = -1.0f;
120         
121         _oscType = oscType;
122             prevMode = _tableModeNormal;
123             
124             for(int i = 0; i < 1024; ++i)
125             {
126                 float maxVal = 0.0f;
127                 _waveTable[i] = 0;
128                 switch(_oscType)
129                 {
130                     case wav:
131                         //SIN MODE
132                         _waveTable[i] = sin( ( cast(float)i / 1024.0) * (2 * pi));
133                         break;
134                     case saw:
135                         //SAW NORMAL
136                         if(_tableModeNormal)
137                         {
138                             _waveTable[i] = i < 512 ? ms1 * i + bs1 : ms2 * (i - 511) + bs2;
139  
140                         }
141                         //SAW BAND-LIMITED
142                         else
143                         {
144                             for(int g=1; g <= 6; ++g)
145                             {
146                                 double n = cast(double)g;
147                                 _waveTable[i] += pow(cast(float)-1.0f, cast(float)(g+1)) * (1.0 / n) * sin(2.0 * pi * i * n / 1024.0f);
148                             } 
149                         }
150                         break;
151                     case tri:
152                         //TRI NORMAL
153                         if(_tableModeNormal)
154                         {
155                             if(i < 256)
156                                 _waveTable[i] = mt1 * i + bt1;
157                             else if(i >= 256 && i < 768)
158                                 _waveTable[i] = mtf2 * (i - 256) + btf2;
159                             else
160                                 _waveTable[i] = mt2 * (i - 768) + bt2;
161                         }
162                         //TRI BAND-LIMITED
163                         else
164                         {
165                             for(int g = 0; g <=3; ++g)
166                             {
167                                 double n = g;
168                                 _waveTable[i] += pow(cast(float)-1.0, cast(float)n) * (1.0 / pow(cast(float)(2 * n + 1), cast(float)2.0)) * sin(2.0 * pi * (2.0 * n + 1) * i / 1024.0);
169                             }
170                         }
171                         break;
172                     case sqr:
173                         //SQR NORMAL
174                         if(_tableModeNormal)
175                         {
176                             _waveTable[i] = i < 512 ? + 1.0 : -1.0;
177                         }
178                         //SQR BAND-LIMITED
179                         else
180                         {
181                             for(int g = 1; g <= 5; g += 2)
182                             {
183                                 double n = g;
184                                 _waveTable[i] += (1.0 / n) * sin(2.0 * pi * i * n / 1024.0);
185                             }
186                         }
187                         break;
188                     default:
189                         //DEFAULT IS SIN
190                         _waveTable[i] = sin( ( cast(float)i / 1024.0) * (2 * pi));
191                         break;
192                 }
193             }
194     }
195 
196     void setFrequency(float frequency) nothrow @nogc
197     {
198         _incVal = 1024.0f * frequency / _sampleRate;
199     }
200     
201     void setTableModeNormal()
202     {
203         prevMode = _tableModeNormal;
204         _tableModeNormal = true;
205         setOscType(_oscType);
206     }
207     
208     void setTableModeBandLimited()
209     {
210         prevMode = _tableModeNormal;
211         _tableModeNormal = true;
212         setOscType(_oscType);
213     }
214 
215     override void reset() nothrow @nogc
216     {
217         
218     }
219 
220     /**
221     *   Note: only returns single phase output. To get quadrature phase output
222     *   use `void doOscillate(&float, &float)`
223     */
224     override float getNextSample(const float input) nothrow @nogc
225     {
226         currentSample = 0;
227         currentQuadPhaseSample = 0;
228         
229         doOscillate(&currentSample, &currentQuadPhaseSample);
230         
231         return currentSample;
232     }
233 
234     void on() {_noteOn = true;}
235     void off() {_noteOn = false;}
236     
237     override string toString()
238     {
239         import std.conv;
240         return "Inc Val:  " ~ to!string(_incVal) ~ " RIndex: " ~ to!string(m_fReadIndex) ~ " TableVal: " ~ to!string(_waveTable[cast(size_t)m_fReadIndex]);
241     }
242     
243 private:
244     float* _waveTable;
245     float m_fReadIndex;
246     float m_fQuadPhaseReadIndex;
247     float _incVal;
248     
249     float currentSample;
250     float currentQuadPhaseSample;
251     bool _noteOn;
252     bool _invert;
253     
254     /++ true = normal | false = band limited +/
255     bool _tableModeNormal;
256     bool prevMode;
257     
258     int _oscType;
259     
260 }
261 
262 unittest
263 {
264     import dplug.core.nogc;
265     import ddsp.effect.effect;
266 
267     WTOscillator osc = mallocNew!WTOscillator();
268     osc.setSampleRate(44100);
269     osc.setParams(1000, wav, true);
270     testEffect(osc, "WTOscillator", 44100, false);
271 }