1 /** 2 * Copyright 2017 Cut Through Recordings 3 * License: MIT License 4 * Author(s): Ethan Reker 5 */ 6 module ddsp.effect.digitaldelay; 7 8 import ddsp.effect.effect; 9 import ddsp.util.functions; 10 11 import dplug.core.nogc; 12 13 /** 14 * A general purpose Digital Delay with support for external feedback, 15 fractional delay, and feedback path effects. 16 */ 17 class DigitalDelay(T) : AudioEffect!T 18 { 19 import core.stdc.stdlib : malloc, free; 20 import core.stdc.string : memset; 21 import dplug.core.vec; 22 23 public: 24 25 this() nothrow @nogc 26 { 27 buffer = null; 28 _size = 0; 29 _feedback = 0.0f; 30 _mix = 0.0f; 31 _useExternalFeedback = false; 32 33 _readIndex = 1; 34 _writeIndex = 0; 35 36 feedbackFX = makeVec!(AudioEffect!T); 37 } 38 39 ~this() nothrow @nogc { free(buffer); } 40 41 /// Set the sample rate and initialize the buffer. 42 override void setSampleRate(float sampleRate) nothrow @nogc 43 { 44 _sampleRate = sampleRate; 45 _feedback = 0; 46 _mix = 0; 47 _size = cast(uint)sampleRate + 1; 48 49 if(buffer != null) 50 free(buffer); 51 52 buffer = cast(float*) mallocSlice!float(_size); 53 reset(); 54 } 55 56 /// calculates and sets the number of samples required. 57 void setDelay(float msDelay) nothrow @nogc 58 { 59 _delayInSamples = msToSamples(msDelay, _sampleRate); 60 if(_delayInSamples > _size) 61 { 62 _size = cast(size_t)_delayInSamples + 1; 63 resizeOnNextReset = true; 64 reset(); 65 } 66 else 67 { 68 resetIndices(); 69 } 70 } 71 72 /// Sets delay time, feedback, and mix 73 void setParams(float msDelay, float feedback, float mix) nothrow @nogc 74 { 75 setDelay(msDelay); 76 setFeedbackAmount(feedback); 77 setMixAmount(mix); 78 } 79 80 override T getNextSample(const T input) nothrow @nogc 81 { 82 float xn = input; 83 float yn = buffer[cast(size_t)_readIndex]; 84 85 if(_readIndex == _writeIndex && _delayInSamples < 1.0f) 86 { 87 yn = xn; 88 } 89 90 size_t _readIndex_1 = cast(size_t)(_readIndex - 1); 91 if(_readIndex_1 < 0) 92 _readIndex_1 = _size - 1; 93 94 float yn_1 = buffer[_readIndex_1]; 95 96 float fracDelay = _delayInSamples - cast(int)_delayInSamples; 97 98 float interp = linearInterp(0, 1, yn, yn_1, fracDelay); 99 100 if(_delayInSamples == 0) 101 yn = xn; 102 else 103 yn = interp; 104 105 if(!_useExternalFeedback) 106 buffer[cast(size_t)_writeIndex] = xn + _feedback * yn; 107 else 108 buffer[cast(size_t)_writeIndex] = xn + _feedbackIn * _feedback; 109 110 float output = _mix * yn + (1.0 - _mix) * xn; 111 112 if(++_writeIndex >= _size) 113 _writeIndex = 0; 114 if(++_readIndex >= _size) 115 _readIndex = 0; 116 117 return output; 118 } 119 120 //set all elements in the buffer to 0, and reset indices. 121 override void reset() nothrow @nogc 122 { 123 if(resizeOnNextReset) { 124 resizeOnNextReset = false; 125 buffer = cast(float*) mallocSlice!float(_size); 126 } 127 memset(buffer, 0, _size * float.sizeof); 128 129 resetIndices(); 130 } 131 132 void resetIndices() nothrow @nogc 133 { 134 _readIndex = cast(long)(_writeIndex - cast(long)_delayInSamples); 135 if(_readIndex < 0) 136 _readIndex += _size; 137 } 138 139 float getCurrentFeedbackOutput() nothrow @nogc { return _feedback * buffer[cast(size_t)_readIndex]; } 140 141 float getFeedbackAmount() nothrow @nogc { return _feedback; } 142 143 float getMixAmount() nothrow @nogc { return _mix; } 144 145 void setCurrentFeedbackInput(float f) nothrow @nogc { _feedbackIn = f; } 146 147 void setUseExternalFeedback(bool b) nothrow @nogc { _useExternalFeedback = b; } 148 149 void addFeedbackEffect(AudioEffect!T effect) nothrow @nogc { feedbackFX.pushBack(effect); } 150 151 void setFeedbackAmount(float feedback) nothrow @nogc { _feedback = feedback; } 152 153 void setMixAmount(float mix) nothrow @nogc { _mix = mix; } 154 155 /// used for debuging purposes. 156 override string toString() 157 { 158 import std.conv; 159 string output = "Rindex " ~ to!string(_readIndex) ~ " Windex " ~ to!string(_writeIndex) ~ " Size " ~ to!string(_size) ~ " Delay " ~ to!string(_delayInSamples); 160 return output; 161 } 162 163 protected: 164 165 float *buffer; 166 float _sampleRate; 167 float _feedback; 168 float _mix; 169 170 size_t _size; 171 float _delayInSamples; 172 float _delayInMS; 173 174 long _writeIndex; 175 long _readIndex; 176 177 bool _useExternalFeedback; 178 bool resizeOnNextReset; 179 float _feedbackIn; 180 181 Vec!(AudioEffect!T) feedbackFX; 182 183 float maxDelayTime = 3.0f; 184 185 } 186 187 unittest 188 { 189 import dplug.core.nogc; 190 191 DigitalDelay!float d = mallocNew!(DigitalDelay!float)(); 192 //d.initialize(44100); 193 //d.update(22050, 0.5, 0.5); 194 //testEffect(d, "DDelay", 44100 * 2, false); 195 }