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 }