Bela
Real-time, ultra-low-latency audio and sensor processing system for BeagleBone Black
Loading...
Searching...
No Matches
DelayLine.h
1#pragma once
2/*
3 * \brief Basic multi-tap Delay Line Implementation with arbitrary tap number and maximum delay buffer length.
4 * Feedback can either be taken from the main tap or external via the use of set and get feedback loop methods.
5 * Mix of wet and dry signals can be set by using wet/dry mix or independent gains for each signal path.
6 * Process method works sample by sample.
7 *
8 * October 2021
9 * Author: Adan L. Benito
10 */
11#include <vector>
12
13class DelayLine
14{
15 public:
16 DelayLine();
17 ~DelayLine();
18 /*
19 * Initialise delay line.
20 *
21 * @param maxDelayTime Maximum size of delay that the delay buffer can hold (in milliseconds)
22 * @param fs Sample frequency
23 * @param nTaps Number of taps for the delay line.
24 */
25 DelayLine(float maxDelayTime, float fs, unsigned int nTaps = 1);
26 /*
27 * \copydoc DelayLine::DelayLine(float, float, unsigned int)
28 *
29 * @return 0 upon success, error otherwise
30 */
31 int setup(float maxDelayTime, float fs, unsigned int nTaps);
32 /*
33 * Free/dealocate any used memory.
34 */
35 int cleanup();
36 /*
37 * Process input and generate delay based on current parameters.
38 *
39 * @param input Input sample.
40 * @return Processed sample
41 */
42 float process(float input);
43 /*
44 * Set delay time for an specific tap
45 *
46 * @param delayTime Delay time in milliseconds (should be less than maximum delay time.
47 * @param tap Tap index. Defaults to main tap.
48 */
49 void setDelayTime(float delayTime, unsigned int tap = 0)
50 {
51 tap = this->constrain<unsigned int>(tap, 0, _nTaps-1);
52 _delaySamples[tap] = this->constrain<float>(_fs * delayTime / 1000.0, 0.0, (float)(_delayBuffer.size()));
53 }
54 /*
55 * Get current delay time (in milliseconds) for specified tap
56 *
57 * @param tap Tap index. Defaults to main tap.
58 */
59 float getDelayTime(unsigned int tap = 0) { return 1000.0 *_delaySamples[tap] / _fs; };
60
61 /* Set feedback gain */
62 void setFeedbackGain(float feedbackGain)
63 {
64 _feedbackGain = this->constrain<float>(feedbackGain, 0.0, 0.99);
65 }
66 /* Get feedback gain */
67 float getFeedbackGain(){ return _feedbackGain; };
68
69 /* Set dry level value independenty from wet level */
70 void setDryLevel(float dryLevel)
71 {
72 _dryLevel = this->constrain<float>(dryLevel, 0.0, 1.0);
73 }
74 /* Get dry level value */
75 float getDryLevel(){ return _dryLevel; };
76
77 /* Set wet level value independenty from dry level */
78 void setWetLevel(float wetLevel)
79 {
80 _wetLevel = this->constrain<float>(wetLevel, 0.0, 1.0);
81 }
82 /* Get wet level value */
83 float getWetLevel(){ return _wetLevel; };
84 /*
85 * Set wet/dry mix
86 *
87 * @param wetDryMix Mix value, constrained from 0 to 1 ( 0 -> only dry, 1 -> only wet)
88 */
89 void setWetDryMix(float wetDryMix)
90 {
91 _wetLevel = this->constrain<float>(wetDryMix, 0.0, 1.0);
92 _dryLevel = 1.0 - _wetLevel;
93 }
94 /* Get wet/dry mix value from dry and wet levels */
95 float getWetDryMix();
96 /*
97 * Constrain input to a given range
98 *
99 * @param val input value
100 * @param min minimum value in constrained range
101 * @param max maximum value in constrained range
102 */
103 template <typename T>
104 static T constrain(T val, T min, T max)
105 {
106 T retVal = val;
107 if(retVal < min)
108 retVal = min;
109 if(retVal > max)
110 retVal = max;
111 return retVal;
112 }
113 /*
114 * Simple linear interpolation method using previous and next value.
115 * This method is used to interpolate read pointers for each delay tap.
116 *
117 * @param index Interpolation index
118 * @param pVal Previous value
119 * @param nVal Next value
120 * @return Interpolated value
121 */
122 static float lerp(float index, float pVal, float nVal);
123 /* Get feedback send value as set during delay processing based on the interpolated
124 * reading of the delay line.
125 */
126 float getFeedbackSend() { return _feedbackSend; };
127 /*
128 * Set feedback return value.
129 * This can be either the feedback send value after processing or any other desired input.
130 *
131 * @param feedbackReturn Input value for feedback return path summed to the delay.
132 * @param preGain Boolean flag indicating wether feedback gain is applied to feedback input (post-gain) or not (pre-gain).
133 */
134 void setFeedbackReturn(float feedbackReturn, bool preGain = false)
135 {
136 _feedbackReturn = (preGain) ? feedbackReturn : _feedbackGain * feedbackReturn;
137 }
138 /*
139 * Flag to indicate wether the internal or external feedback path is being used.
140 *
141 * @param doUse Flag value, if true use external feedback path.
142 */
143 void useExternalFeedback(bool doUse) { _externalFeedback = doUse; }
144 /*
145 * Update pointer and interpolate to get delay output for an specific tap.
146 *
147 * @param tap Tap index.
148 * @param preGain Flag. If true output is taken before applying wet gain.
149 */
150 float getTapOutput(unsigned int tap, bool preGain = true)
151 {
152 updateReadPointer(tap);
153 return (preGain) ? interpolatedRead(_readPtr[tap]) : interpolatedRead(_readPtr[tap] * _wetLevel);
154 }
155 /*
156 * Flag to indicate wether taps are taken independently from the main tap or summed together and written back into the buffer.
157 *
158 * @param doUse Flag value, if true do not add taps to the main tap in processing() and allow to use independently..
159 */
160 void useSeparateTaps(bool doUse) { _separateTaps = doUse; };
161 /* Empty delay buffer */
162 void flush() { std::fill(_delayBuffer.begin(), _delayBuffer.end(), 0); }
163 /* Reset delay value and read pointer for all taps to the values indicated by the main tap */
164 void resetTaps()
165 {
166 for(unsigned int t = 1; t<_nTaps; t++)
167 {
168 _delaySamples[t] = _delaySamples[0];
169 _readPtr[t] = _readPtr[0];
170 }
171 }
172
173 private:
174 float _fs = 0.0; // Sample frequency
175 float * _delaySamples = nullptr; // Pointer to array of delay times (in samples) for delay taps
176 float _feedbackGain = 0.0; // Feedback gain
177 float _dryLevel = 1.0; // Dry level
178 float _wetLevel = 0.0; // Wet level
179
180 float _feedbackSend = 0.0; // Feedback loop send value
181 float _feedbackReturn = 0.0; // Feebback loop return value
182 bool _externalFeedback = false; // Use external feedback flag
183
184 unsigned int _writePtr = 0; // Write pointer for delay line
185
186 unsigned int _nTaps = 1; // Number of taps
187 float *_readPtr = nullptr; // Pointer to array of read pointers for delay taps
188 bool _separateTaps = false; // Use separate taps flag
189
190 std::vector<float> _delayBuffer; // Delay buffer
191
192 /*
193 * Update write pointer and wrap around delay buffer size
194 */
195 void updateWritePointer()
196 {
197 if(++_writePtr >= _delayBuffer.size())
198 _writePtr = 0;
199 }
200
201 /*
202 * Update read pointer for specified tap adn wrap around delay buffer size
203 */
204 void updateReadPointer(unsigned int tap = 0);
205 /*
206 * Interpolate reading for a float index
207 */
208 float interpolatedRead(float index);
209
210};
static float max(float x, float y)
Returns the minimum of two numbers.
Definition Utilities.h:88
static float min(float x, float y)
Returns the maximum of two numbers.
Definition Utilities.h:92