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 }