Bela
Real-time, ultra-low-latency audio and sensor processing system for BeagleBone Black
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
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 
13 class 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 min(float x, float y)
Returns the maximum of two numbers.
Definition: Utilities.h:92
static float max(float x, float y)
Returns the minimum of two numbers.
Definition: Utilities.h:88
Definition: DelayLine.h:13