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(¤tSample, ¤tQuadPhaseSample); 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 }