Mozzi  version v2.0
sound synthesis library for Arduino
AudioDelayFeedback.h
1 /*
2  * AudioDelayFeedback.h
3  *
4  * This file is part of Mozzi.
5  *
6  * Copyright 2012-2024 Tim Barrass and the Mozzi Team
7  *
8  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
9  *
10  */
11 
12 #ifndef AUDIODELAY_FEEDBACK_H_
13 #define AUDIODELAY_FEEDBACK_H_
14 
15 #include <Arduino.h>
16 
17 #include "mozzi_utils.h"
18 #include "meta.h"
19 #include "IntegerType.h"
20 
21 enum interpolation_types {LINEAR,ALLPASS};
22 
23 
37 template <uint16_t NUM_BUFFER_SAMPLES, int8_t INTERP_TYPE = LINEAR, typename su=int8_t>
39 {
40 
41  typedef typename IntegerType<sizeof(su)+sizeof(su)>::signed_type return_type;
42 
43 public:
46  AudioDelayFeedback(): write_pos(0), _feedback_level(0), _delaytime_cells(0)
47  {}
48 
49 
55  AudioDelayFeedback(uint16_t delaytime_cells): write_pos(0), _feedback_level(0), _delaytime_cells(delaytime_cells)
56  {}
57 
58 
65  AudioDelayFeedback(uint16_t delaytime_cells, int8_t feedback_level): write_pos(0), _feedback_level(feedback_level), _delaytime_cells(delaytime_cells)
66  {}
67 
68 
69 
74  inline
75  return_type next(su input)
76  {
77  // chooses a different next() function depending on whether the
78  // the template parameter is LINEAR(default if none provided) or ALLPASS.
79  // See meta.h.
80  return next(input, Int2Type<INTERP_TYPE>());
81  }
82 
83 
84 
91  inline
92  return_type next(su input, uint16_t delaytime_cells)
93  {
94  //setPin13High();
95  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
96  uint16_t read_pos = (write_pos - delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
97  // < 1us to here
98  return_type delay_sig = delay_array[read_pos]; // read the delay buffer
99  // with this line, the method takes 18us
100  //int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)/128),-128),127); // feedback clipped
101  // this line, the whole method takes 4us... Compiler doesn't optimise pow2 divides. Why?
102  //int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)>>7),-128),127); // feedback clipped
103  su feedback_sig = (su) constrain( ((delay_sig * _feedback_level)>>7), -(1<<((sizeof(su)<<3)-1)), (1<<((sizeof(su)<<3)-1))-1);
104  delay_array[write_pos] = (return_type)input + feedback_sig; // write to buffer
105  //setPin13Low();
106  return delay_sig;
107  }
108 
109 
110 
117  inline
118  return_type next(su input, Q16n16 delaytime_cells)
119  {
120  //setPin13High();
121  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
122 
123  uint16_t index = Q16n16_to_Q16n0(delaytime_cells);
124  uint16_t fraction = (uint16_t) delaytime_cells; // keeps low word
125 
126  uint16_t read_pos1 = (write_pos - index) & (NUM_BUFFER_SAMPLES - 1);
127  return_type delay_sig1 = delay_array[read_pos1]; // read the delay buffer
128 
129  uint16_t read_pos2 = (write_pos - (index+1)) & (NUM_BUFFER_SAMPLES - 1);
130  return_type delay_sig2 = delay_array[read_pos2]; // read the delay buffer
131 
132 
133  return_type difference = delay_sig2 - delay_sig1;
134  //int16_t delay_sig_fraction = (int16_t)((int32_t)((int32_t) fraction * difference) >> 16);
135  return_type delay_sig_fraction = (return_type)((typename IntegerType<sizeof(return_type)+sizeof(return_type)>::signed_type)((typename IntegerType<sizeof(return_type)+sizeof(return_type)>::signed_type)fraction * difference) >> 16);
136 
137  return_type delay_sig = delay_sig1+delay_sig_fraction;
138 
139  //int16_t delay_sig = delay_sig1 + ((int32_t)delay_sig2*fraction)>>16;
140 
141  //int8_t feedback_sig = (int8_t) min(max((((int16_t)(delay_sig * _feedback_level))>>7),-128),127); // feedback clipped
142  su feedback_sig = (su) constrain(((delay_sig * _feedback_level)>>7), -(1<<((sizeof(su)<<3)-1)), (1<<((sizeof(su)<<3)-1))-1);
143  delay_array[write_pos] = (return_type) input + feedback_sig; // write to buffer
144  //setPin13Low();
145  return delay_sig;
146  }
147 
148 
152  inline
153  void write(su input)
154  {
155  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
156  delay_array[write_pos] = input;
157  }
158 
159 
164  inline
165  void writeFeedback(su input)
166  {
167  delay_array[write_pos] = input;
168  }
169 
170 
176  inline
177  void write(su input, uint16_t offset)
178  {
179  uint16_t _pos = (write_pos + offset) & (NUM_BUFFER_SAMPLES - 1);
180  delay_array[_pos] = input;
181  }
182 
183 
188  inline
189  return_type read(Q16n16 delaytime_cells)
190  {
191  return read(delaytime_cells, Int2Type<INTERP_TYPE>());
192  }
193 
194 
198  inline
199  return_type read()
200  {
201  return read(Int2Type<INTERP_TYPE>());
202  }
203 
204 
210  inline
211  void setDelayTimeCells(uint16_t delaytime_cells)
212  {
213  _delaytime_cells = (uint16_t) delaytime_cells;
214  }
215 
216 
222  inline
223  void setDelayTimeCells(Q16n16 delaytime_cells)
224  {
225  return setDelayTimeCells(delaytime_cells, Int2Type<INTERP_TYPE>());
226  }
227 
228 
234  inline
235  void setDelayTimeCells(float delaytime_cells)
236  {
237  return setDelayTimeCells(delaytime_cells, Int2Type<INTERP_TYPE>());
238  }
239 
240 
244  inline
245  void setFeedbackLevel(int8_t feedback_level)
246  {
247  _feedback_level = feedback_level;
248  }
249 
250 
251 
252 private:
253  return_type delay_array[NUM_BUFFER_SAMPLES];
254  uint16_t write_pos;
255  int8_t _feedback_level;
256  uint16_t _delaytime_cells;
257  Q15n16 _coeff; // for allpass interpolation
258  su last_in;
259  return_type last_out;
260 
261 
262 
266  inline
267  return_type next(su in_value, Int2Type<LINEAR>)
268  {
269  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
270  uint16_t read_pos = (write_pos - _delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
271 
272  return_type delay_sig = delay_array[read_pos]; // read the delay buffer
273  //int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)/128),-128),127); // feedback clipped
274  su feedback_sig = (su) constrain(((delay_sig * _feedback_level)>>7), -(1<<((sizeof(su)<<3)-1)), (1<<((sizeof(su)<<3)-1))-1);
275  delay_array[write_pos] = (return_type) in_value + feedback_sig; // write to buffer
276 
277  return delay_sig;
278  }
279 
280 
281 
289  inline
290  return_type next(su input, Int2Type<ALLPASS>)
291  {
292  /*
293  http://www.scandalis.com/Jarrah/Documents/DelayLine.pdf
294  also https://ccrma.stanford.edu/~jos/Interpolation/Interpolation_4up.pdf
295  for desired fractional delay of d samples,
296  coeff = (1-d)/(1+d)
297  or
298  coeff = ((d-1)>1) + (((d-1)*(d-1))>>2) - (((d-1)*(d-1)*(d-1))>>3)
299  out = coeff * in + last_in - coeff * last_out
300  = coeff * (in-last_out) + last_in
301  */
302  //setPin13High();
303 
304  /* I think these should **not** be static
305  static int8_t last_in;
306  static int16_t last_out;
307  */
308 
309  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
310 
311  uint16_t read_pos1 = (write_pos - _delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
312  return_type delay_sig = delay_array[read_pos1]; // read the delay buffer
313 
314  //int16_t interp = (int16_t)(_coeff * ((int16_t)input - last_out)>>16) + last_in; // Q15n16*Q15n0 + Q15n0 = Q15n16 + Q15n0 = Q15n16
315  return_type interp = (return_type)(_coeff * ((return_type)input - last_out)>>(sizeof(su)<<4)) + last_in;
316  delay_sig += interp;
317 
318  //int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)>>7),-128),127); // feedback clipped
319  su feedback_sig = (su) constrain(((delay_sig * _feedback_level)>>7), -(1<<((sizeof(su)<<3)-1)), (1<<((sizeof(su)<<3)-1))-1);
320  delay_array[write_pos] = (return_type) input + feedback_sig; // write to buffer
321 
322  last_in = input;
323  last_out = delay_sig;
324  //setPin13Low();
325  return delay_sig;
326  }
327 
328 
329 
330  // 20-25us
331  inline
332  void setDelayTimeCells(Q16n16 delaytime_cells, Int2Type<ALLPASS>)
333  {
334  /*
335  integer optimisation/approximation from
336  Van Duyne, Jaffe, Scandalis, Stilson 1997
337  http://www.scandalis.com/Jarrah/Documents/DelayLine.pdf
338  //coeff = -((d-1)>1) + (((d-1)*(d-1))>>2) - (((d-1)*(d-1)*(d-1))>>3) , d is fractional part
339  */
340  _delaytime_cells = delaytime_cells>>16; // whole integer part
341  Q15n16 dminus1 = - Q15n16_FIX1 + (uint16_t) delaytime_cells;
342  Q15n16 dminus1squared = (dminus1)*(dminus1)>>16;
343  _coeff = -(dminus1>>1) + (dminus1squared>>2) - (((dminus1squared*dminus1)>>16)>>3);
344  }
345 
346 
347  // 100us
348  inline
349  void setDelayTimeCells(float delaytime_cells, Int2Type<ALLPASS>)
350  {
351  //coeff = (1-d)/(1+d)
352  _delaytime_cells = (uint16_t) delaytime_cells;
353 
354  float fraction = delaytime_cells - _delaytime_cells;
355 
356  // modified from stk DelayA.cpp
357  float alpha_ = 1.0f + fraction; // fractional part
358  if ( alpha_ < 0.5f ) {
359  // (stk): The optimal range for alpha is about 0.5 - 1.5 in order to
360  // achieve the flattest phase delay response.
361 
362  // something's not right about how I use _delaytime_cells and
363  // NUM_BUFFER_SAMPLES etc. in my ringbuffer compared to stk
364  _delaytime_cells += 1;
365  if ( _delaytime_cells >= NUM_BUFFER_SAMPLES ) _delaytime_cells -= NUM_BUFFER_SAMPLES;
366  alpha_ += 1.0f;
367  }
368  // otherwise this would use fraction instead of alpha
369  _coeff = float_to_Q15n16((1.f-alpha_)/(1.f+alpha_));
370  }
371 
372  // Retrieve the signal in the delay line at the position delaytime_cells.
373  // It doesn't change the stored internal value of _delaytime_cells or feedback the output to the input.
374  // param delaytime_cells indicates the delay time in terms of cells in the delay buffer.
375  //
376  // inline
377  // int16_t read(uint16_t delaytime_cells, Int2Type<LINEAR>)
378  // {
379  // uint16_t read_pos = (write_pos - delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
380  // int16_t delay_sig = delay_array[read_pos]; // read the delay buffer
381  //
382  // return delay_sig;
383  // }
384 
389  inline
390  return_type read(Q16n16 delaytime_cells, Int2Type<LINEAR>)
391  {
392  uint16_t index = (Q16n16)delaytime_cells >> 16;
393  uint16_t fraction = (uint16_t) delaytime_cells; // keeps low word
394 
395  uint16_t read_pos1 = (write_pos - index) & (NUM_BUFFER_SAMPLES - 1);
396  return_type delay_sig1 = delay_array[read_pos1]; // read the delay buffer
397 
398  uint16_t read_pos2 = (write_pos - (index+1)) & (NUM_BUFFER_SAMPLES - 1);
399  return_type delay_sig2 = delay_array[read_pos2]; // read the delay buffer
400 
401  /*
402  int16_t difference = delay_sig2 - delay_sig1;
403  int16_t delay_sig_fraction = ((int32_t) fraction * difference) >> 16;
404 
405  int16_t delay_sig = delay_sig1+delay_sig_fraction;
406  */
407  //int16_t delay_sig = delay_sig1 + ((int32_t)delay_sig2*fraction)>>16;
408  return_type delay_sig = delay_sig1 + ((typename IntegerType<sizeof(return_type)+sizeof(return_type)>::signed_type)delay_sig2*fraction)>>16;
409 
410  return delay_sig;
411  }
412 
413 
414 };
415 
421 #endif // #ifndef AUDIODELAY_FEEDBACK_H_
Audio delay line with feedback for comb filter, flange, chorus and short echo effects.
AudioDelayFeedback(uint16_t delaytime_cells)
Constructor.
void setFeedbackLevel(int8_t feedback_level)
Set the feedback gain.
void setDelayTimeCells(Q16n16 delaytime_cells)
Set delay time expressed in samples, fractional Q16n16 for an interpolating delay.
return_type next(su input, uint16_t delaytime_cells)
Input a value to the delay, retrieve the signal in the delay line at the position delaytime_cells,...
void setDelayTimeCells(uint16_t delaytime_cells)
Set delay time expressed in samples.
void write(su input)
Input a value to the delay but don't change the delay time or retrieve the output signal.
return_type next(su input)
Input a value to the delay and retrieve the signal in the delay line at the position delaytime_cells.
AudioDelayFeedback(uint16_t delaytime_cells, int8_t feedback_level)
Constructor.
void write(su input, uint16_t offset)
Input a value to the delay at an offset from the current write position.
return_type read()
Retrieve the signal in the delay line at the current stored delaytime_cells.
void setDelayTimeCells(float delaytime_cells)
Set delay time expressed in samples, fractional float for an interpolating delay.
return_type read(Q16n16 delaytime_cells)
Retrieve the signal in the delay line at the interpolated fractional position delaytime_cells.
AudioDelayFeedback()
Constructor.
void writeFeedback(su input)
Input a value to the delay but don't advance the write position, change the delay time or retrieve th...
return_type next(su input, Q16n16 delaytime_cells)
Input a value to the delay, retrieve the signal in the delay line at the interpolated fractional posi...
Q15n16 float_to_Q15n16(float a)
Convert float to Q15n16 fix.
int32_t Q15n16
signed fractional number using 15 integer bits and 16 fractional bits, represents -32767....
Definition: mozzi_fixmath.h:46
#define Q15n16_FIX1
1 in Q15n16 format
Definition: mozzi_fixmath.h:69
Q16n0 Q16n16_to_Q16n0(Q16n16 a)
Convert Q16n16 fixed to Q16n0 uint16_t.
uint32_t Q16n16
unsigned fractional number using 16 integer bits and 16 fractional bits, represents 0 to 65535....
Definition: mozzi_fixmath.h:52
Enables you to instantiate a template based on an integer value.
Definition: meta.h:41
Provides appropriate integer types that can bit the given number of bytes on this platform (at most 6...
Definition: IntegerType.h:20