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 }