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