1 /**
2 * Delay-line implementation.
3 * Copyright: Copyright Auburn Sounds 2015 and later.
4 * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
5 * Authors:   Guillaume Piolat
6 */
7 module dplug.dsp.delayline;
8 
9 import core.stdc..string;
10 
11 import dplug.core.nogc;
12 import dplug.core.math;
13 import dplug.core.vec;
14 
15 /// Allow to sample signal back in time.
16 struct Delayline(T)
17 {
18 public:
19 
20     /// Initialize the delay line. Can delay up to count samples.
21     void initialize(int numSamples) nothrow @nogc
22     {
23         resize(numSamples);
24     }
25 
26     ~this() nothrow @nogc
27     {
28         _data.reallocBuffer(0);
29     }
30 
31     @disable this(this);
32 
33     /// Resize the delay line. Can delay up to count samples.
34     /// The state is cleared.
35     void resize(int numSamples) nothrow @nogc
36     {
37         if (numSamples < 0)
38             assert(false);
39 
40         // Over-allocate to support POW2 delaylines.
41         // This wastes memory but allows delay-line of length 0 without tests.
42 
43         int toAllocate = nextPowerOf2(numSamples + 1);
44         _data.reallocBuffer(toAllocate);
45         _indexMask = toAllocate - 1;
46         _numSamples = numSamples;
47         _index = _indexMask;
48         _data[] = 0;
49     }
50 
51     /// Combined feed + sampleFull.
52     /// Uses the delay line as a fixed delay of count samples.
53     T nextSample(T incoming) nothrow @nogc
54     {
55         feedSample(incoming);
56         return sampleFull(_numSamples);
57     }
58 
59     /// Combined feed + sampleFull.
60     /// Uses the delay line as a fixed delay of count samples.
61     void nextBuffer(const(T)* input, T* output, int frames) nothrow @nogc
62     {
63         for(int i = 0; i < frames; ++i)
64             output[i] = nextSample(input[i]);
65     }
66 
67     /// Adds a new sample at end of delay.
68     void feedSample(T incoming) nothrow @nogc
69     {
70         _index = (_index + 1) & _indexMask;
71         _data[_index] = incoming;
72     }
73 
74     /// Adds several samples at end of delay.
75     void feedBuffer(const(T)[] incoming) nothrow @nogc
76     {
77         int N = cast(int)(incoming.length);
78 
79         // this buffer must be smaller than the delay line, 
80         // else we may risk dropping samples immediately
81         assert(N < _numSamples);
82 
83         // remaining samples before end of delayline
84         int remain = _indexMask - _index;
85 
86         if (N <= remain)
87         {
88             memcpy( &_data[_index + 1], incoming.ptr, N * T.sizeof );
89             _index += N;
90         }
91         else
92         {
93             memcpy( _data.ptr + (_index + 1), incoming.ptr, remain * T.sizeof );
94             size_t numBytes = (N - remain) * T.sizeof;
95             memcpy( _data.ptr, incoming.ptr + remain, numBytes);
96             _index = (_index + N) & _indexMask;
97         }
98     }
99 
100     /// Random access sampling of the delay-line at integer points.
101     /// Delay 0 = last entered sample with feed().
102     T sampleFull(int delay) nothrow @nogc
103     {
104         assert(delay >= 0);
105         return _data[(_index - delay) & _indexMask];
106     }
107 
108     static if (is(T == float) || is(T == double))
109     {
110         /// Random access sampling of the delay-line with linear interpolation.
111         T sampleLinear(float delay) nothrow @nogc
112         {
113             assert(delay > 0);
114             float sampleLoc = (_index - delay) + 2 * _data.length;
115             assert(sampleLoc >= 1);
116             int iPart = cast(int)(sampleLoc);
117             float fPart = cast(float)(sampleLoc - iPart);
118             T x0  = _data[iPart       & _indexMask];
119             T x1  = _data[(iPart + 1) & _indexMask];
120             return lerp(x0, x1, fPart);
121         }
122 
123         /// Random access sampling of the delay-line with a 3rd order polynomial.
124         T sampleHermite(float delay) nothrow @nogc
125         {
126             assert(delay > 1);
127             float sampleLoc = (_index - delay) + 2 * _data.length;
128             assert(sampleLoc >= 1);
129             int iPart = cast(int)(sampleLoc);
130             float fPart = cast(float)(sampleLoc - iPart);
131             assert(fPart >= 0.0f);
132             assert(fPart <= 1.0f);
133             T xm1 = _data[(iPart - 1) & _indexMask];
134             T x0  = _data[ iPart      & _indexMask];
135             T x1  = _data[(iPart + 1) & _indexMask];
136             T x2  = _data[(iPart + 2) & _indexMask];
137             return hermite!T(fPart, xm1, x0, x1, x2);
138         }
139 
140         /// Third-order splice interpolation
141         /// http://musicdsp.org/showArchiveComment.php?ArchiveID=62
142         T sampleSpline3(float delay)
143         {
144             assert(delay > 1);
145             float sampleLoc = (_index - delay) + 2 * _data.length;
146             assert(sampleLoc >= 1);
147 
148             int iPart = cast(int)(sampleLoc);
149             float fPart = cast(float)(sampleLoc - iPart);
150             assert(fPart >= 0.0f);
151             assert(fPart <= 1.0f);
152             T L1 = _data[(iPart - 1) & _indexMask];
153             T L0  = _data[ iPart      & _indexMask];
154             T H0  = _data[(iPart + 1) & _indexMask];
155             T H1  = _data[(iPart + 2) & _indexMask];
156 
157             return L0 + 0.5f *
158                 fPart*(H0-L1 +
159                 fPart*(H0 + L0*(-2) + L1 +
160                 fPart*( (H0 - L0)*9 + (L1 - H1)*3 +
161                 fPart*((L0 - H0)*15 + (H1 - L1)*5 +
162                 fPart*((H0 - L0)*6 + (L1 - H1)*2 )))));
163         }
164 
165         /// 4th order spline interpolation
166         /// http://musicdsp.org/showArchiveComment.php?ArchiveID=60
167         T sampleSpline4(float delay)
168         {
169             assert(delay > 2);
170             T sampleLoc = (_index - delay) + 2 * _data.length;
171             assert(sampleLoc >= 2);
172 
173             int iPart = cast(int)(sampleLoc);
174             T fPart = cast(T)(sampleLoc - iPart);
175 
176 
177             assert(fPart >= 0.0f);
178             assert(fPart <= 1.0f);
179 
180             T p0 = _data[(iPart-2) & _indexMask];
181             T p1 = _data[(iPart-1) & _indexMask];
182             T p2 = _data[iPart     & _indexMask];
183             T p3 = _data[(iPart+1) & _indexMask];
184             T p4 = _data[(iPart+2) & _indexMask];
185             T p5 = _data[(iPart+3) & _indexMask];
186 
187             return p2 + 0.04166666666f * fPart * ((p3 - p1) * 16 + (p0 - p4) * 2
188             + fPart * ((p3 + p1) * 16 - p0 - p2 * 30 - p4
189             + fPart * (p3 * 66 - p2 * 70 - p4 * 33 + p1 * 39 + p5 * 7- p0 * 9
190             + fPart * ( p2 * 126 - p3 * 124 + p4 * 61 - p1 * 64 - p5 * 12 + p0 * 13
191             + fPart * ((p3-p2) * 50 + (p1-p4) * 25 + (p5-p0)*5)))));
192         };
193     }
194 
195 private:
196     T[] _data;
197     int _index;
198     int _indexMask;
199     int _numSamples;
200 }
201 
202 unittest
203 {
204     Delayline!float line;
205     line.initialize(0); // should be possible
206     assert(line.nextSample(1) == 1);
207 
208     Delayline!double line2;
209 
210     Delayline!int line3;
211     line3.initialize(2);
212     assert(line3.nextSample(1) == 0);
213     assert(line3.nextSample(2) == 0);
214     assert(line3.nextSample(3) == 1);
215 }