1 module ddsp.util.oversample; 2 3 import core.stdc.stdlib; 4 5 import ddsp.effect.effect : AudioEffect; 6 import ddsp.util.memory : calloc; 7 import ddsp.util.functions : linearInterp; 8 import ddsp.filter.lowpass : LinkwitzRileyLP; 9 10 import dplug.core.vec; 11 12 class OverSampler(T) : AudioEffect 13 { 14 public: 15 nothrow: 16 @nogc: 17 18 this() 19 { 20 lowpassIn = calloc!LinkwitzRileyLP.init(); 21 lowpassOut = calloc!LinkwitzRileyLP.init(); 22 23 effects = makeVec!AudioEffect(); 24 } 25 26 this(uint factor) 27 { 28 setSampleFactor(factor); 29 this(); 30 } 31 32 // Power of 2 33 void setSampleFactor(uint factor) 34 { 35 assert(factor >= 0 && factor < 7, "OverSampler factor must be 0 or greater"); 36 _bufferSize = (1 << factor) + 1; 37 initializeBuffer(); 38 } 39 40 override void setSampleRate(float sampleRate) 41 { 42 assert(_bufferSize > 0, "setSampleFactor must be called or have factor passed in constructor"); 43 _sampleRate = sampleRate * (_bufferSize - 1); 44 nyquistFrequency = cast(long)(sampleRate / 2); 45 46 lowpassIn.setSampleRate(_sampleRate); 47 lowpassOut.setSampleRate(_sampleRate); 48 lowpassIn.setFrequency(nyquistFrequency); 49 lowpassOut.setFrequency(nyquistFrequency); 50 51 foreach(effect; effects) 52 { 53 effect.setSampleRate(_sampleRate); 54 } 55 } 56 57 override float getNextSample(const float input) 58 { 59 upSample(input); 60 doLowpassIn(); 61 foreach(effect; effects) 62 { 63 for(int i = 0; i < _bufferSize; ++i) 64 { 65 buffer[i] = effect.getNextSample(buffer[i]); 66 } 67 } 68 doLowpassOut(); 69 return buffer[0]; 70 } 71 72 void upSample(const float input) 73 { 74 buffer[0] = buffer[_bufferSize - 1]; 75 buffer[_bufferSize - 1] = input; 76 for(int i = 1; i < _bufferSize - 1; ++i) 77 { 78 buffer[i] = linearInterp(0, _bufferSize - 1, buffer[0], input, i); 79 } 80 } 81 82 override void reset() 83 { 84 lowpassIn.reset(); 85 lowpassOut.reset(); 86 foreach(effect; effects) 87 { 88 effect.reset(); 89 } 90 } 91 92 /// Note that buffer includes previous sample and current sample so its size is 2^factor + 1 93 T[] Buffer() @property 94 { 95 return buffer[0.._bufferSize]; 96 } 97 98 float Nyquist() @property 99 { 100 return nyquistFrequency; 101 } 102 103 void insertEffect(AudioEffect effect) 104 { 105 effects.pushBack(effect); 106 } 107 108 109 private: 110 uint _bufferSize; 111 T* buffer; 112 long nyquistFrequency; 113 114 LinkwitzRileyLP lowpassIn; 115 LinkwitzRileyLP lowpassOut; 116 117 Vec!AudioEffect effects; 118 119 void initializeBuffer() 120 { 121 assert(_bufferSize >= 1, "Must set oversample factor"); 122 buffer = cast(float*)malloc(float.sizeof * _bufferSize); 123 buffer[0] = 0; 124 } 125 126 void doLowpassIn() 127 { 128 for(int i = 1; i < _bufferSize; ++i) 129 { 130 buffer[i] = lowpassIn.getNextSample(buffer[i]); 131 } 132 } 133 134 void doLowpassOut() 135 { 136 for(int i = 1; i < _bufferSize; ++i) 137 { 138 buffer[i] = lowpassOut.getNextSample(buffer[i]); 139 } 140 } 141 } 142 143 unittest 144 { 145 //Oversampler pushBackSampleTest 146 import std.stdio; 147 148 OverSampler!float sampler = new OverSampler!float(); 149 sampler.setSampleFactor(2); 150 sampler.setSampleRate(44100); 151 152 153 sampler.upSample(0.5); 154 sampler.upSample(1.0); 155 156 auto expected = [0.5, 0.6250, 0.7500, 0.8750, 1.0]; 157 auto actual = sampler.Buffer(); 158 159 assert(expected == actual, "Failed Test - Oversampler pushBackSampleTest"); 160 assert(sampler.SampleRate == 176400f, "Failed Test - Oversampler setSampleRate"); 161 162 /// 163 /// 164 /// 165 166 import ddsp.effect.effect : testEffect; 167 168 class Distorter : AudioEffect 169 { 170 private import std.math; 171 override float getNextSample(const float input) { return sin(input * PI_2);} 172 override void reset() {} 173 } 174 175 Distorter distorter = calloc!Distorter.init(); 176 177 sampler = new OverSampler!float(); 178 sampler.setSampleFactor(2); 179 sampler.setSampleRate(44100); 180 sampler.insertEffect(distorter); 181 testEffect(sampler, "Oversampler", 20000 * 4, false); 182 }