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