Mozzi  version 2016-12-11-17:03
sound synthesis library for Arduino
AudioDelayFeedback.h
1 /*
2  * AudioDelayFeedback.h
3  *
4  * Copyright 2012 Tim Barrass.
5  *
6  * This file is part of Mozzi.
7  *
8  * Mozzi is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
9  *
10  */
11 
12 #ifndef AUDIODELAY_FEEDBACK_H_
13 #define AUDIODELAY_FEEDBACK_H_
14 
15 #if ARDUINO >= 100
16  #include "Arduino.h"
17 #else
18  #include "WProgram.h"
19 #endif
20 
21 #include "mozzi_utils.h"
22 #include "meta.h"
23 
24 enum interpolation_types {LINEAR,ALLPASS};
25 
26 
39 template <uint16_t NUM_BUFFER_SAMPLES, int8_t INTERP_TYPE = LINEAR>
41 {
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  int16_t next(int8_t 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  int16_t next(int8_t 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  int16_t 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  delay_array[write_pos] = (int16_t) input + feedback_sig; // write to buffer
104  //setPin13Low();
105  return delay_sig;
106  }
107 
108 
109 
116  inline
117  int16_t next(int8_t input, Q16n16 delaytime_cells)
118  {
119  //setPin13High();
120  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
121 
122  uint16_t index = Q16n16_to_Q16n0(delaytime_cells);
123  uint16_t fraction = (uint16_t) delaytime_cells; // keeps low word
124 
125  uint16_t read_pos1 = (write_pos - index) & (NUM_BUFFER_SAMPLES - 1);
126  int16_t delay_sig1 = delay_array[read_pos1]; // read the delay buffer
127 
128  uint16_t read_pos2 = (write_pos - (index+1)) & (NUM_BUFFER_SAMPLES - 1);
129  int16_t delay_sig2 = delay_array[read_pos2]; // read the delay buffer
130 
131 
132  int16_t difference = delay_sig2 - delay_sig1;
133  int16_t delay_sig_fraction = (int16_t)((int32_t)((int32_t) fraction * difference) >> 16);
134 
135  int16_t delay_sig = delay_sig1+delay_sig_fraction;
136 
137  //int16_t delay_sig = delay_sig1 + ((int32_t)delay_sig2*fraction)>>16;
138 
139  int8_t feedback_sig = (int8_t) min(max((((int16_t)(delay_sig * _feedback_level))>>7),-128),127); // feedback clipped
140  delay_array[write_pos] = (int16_t) input + feedback_sig; // write to buffer
141  //setPin13Low();
142  return delay_sig;
143  }
144 
145 
149  inline
150  void write(int8_t input)
151  {
152  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
153  delay_array[write_pos] = input;
154  }
155 
156 
161  inline
162  void writeFeedback(int8_t input)
163  {
164  delay_array[write_pos] = input;
165  }
166 
167 
173  inline
174  void write(int8_t input, uint16_t offset)
175  {
176  (write_pos + offset) &= (NUM_BUFFER_SAMPLES - 1);
177  delay_array[write_pos] = input;
178  }
179 
180 
185  inline
186  int16_t read(Q16n16 delaytime_cells)
187  {
188  return read(delaytime_cells, Int2Type<INTERP_TYPE>());
189  }
190 
191 
195  inline
196  int16_t read()
197  {
198  return read(Int2Type<INTERP_TYPE>());
199  }
200 
201 
207  inline
208  void setDelayTimeCells(uint16_t delaytime_cells)
209  {
210  _delaytime_cells = (uint16_t) delaytime_cells;
211  }
212 
213 
219  inline
220  void setDelayTimeCells(Q16n16 delaytime_cells)
221  {
222  return setDelayTimeCells(delaytime_cells, Int2Type<INTERP_TYPE>());
223  }
224 
225 
231  inline
232  void setDelayTimeCells(float delaytime_cells)
233  {
234  return setDelayTimeCells(delaytime_cells, Int2Type<INTERP_TYPE>());
235  }
236 
237 
241  inline
242  void setFeedbackLevel(int8_t feedback_level)
243  {
244  _feedback_level = feedback_level;
245  }
246 
247 
248 
249 private:
250  int16_t delay_array[NUM_BUFFER_SAMPLES];
251  uint16_t write_pos;
252  int8_t _feedback_level;
253  uint16_t _delaytime_cells;
254  Q15n16 _coeff; // for allpass interpolation
255 
256 
257 
261  inline
262  int16_t next(int8_t in_value, Int2Type<LINEAR>)
263  {
264  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
265  uint16_t read_pos = (write_pos - _delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
266 
267  int16_t delay_sig = delay_array[read_pos]; // read the delay buffer
268  int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)/128),-128),127); // feedback clipped
269  delay_array[write_pos] = (int16_t) in_value + feedback_sig; // write to buffer
270 
271  return delay_sig;
272  }
273 
274 
275 
283  inline
284  int16_t next(int8_t input, Int2Type<ALLPASS>)
285  {
286  /*
287  http://www.scandalis.com/Jarrah/Documents/DelayLine.pdf
288  also https://ccrma.stanford.edu/~jos/Interpolation/Interpolation_4up.pdf
289  for desired fractional delay of d samples,
290  coeff = (1-d)/(1+d)
291  or
292  coeff = ((d-1)>1) + (((d-1)*(d-1))>>2) - (((d-1)*(d-1)*(d-1))>>3)
293  out = coeff * in + last_in - coeff * last_out
294  = coeff * (in-last_out) + last_in
295  */
296  //setPin13High();
297  static int8_t last_in;
298  static int16_t last_out;
299 
300  ++write_pos &= (NUM_BUFFER_SAMPLES - 1);
301 
302  uint16_t read_pos1 = (write_pos - _delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
303  int16_t delay_sig = delay_array[read_pos1]; // read the delay buffer
304 
305  int16_t interp = (int16_t)(_coeff * ((int16_t)input - last_out)>>16) + last_in; // Q15n16*Q15n0 + Q15n0 = Q15n16 + Q15n0 = Q15n16
306  delay_sig += interp;
307 
308  int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)>>7),-128),127); // feedback clipped
309  delay_array[write_pos] = (int16_t) input + feedback_sig; // write to buffer
310 
311  last_in = input;
312  last_out = delay_sig;
313  //setPin13Low();
314  return delay_sig;
315  }
316 
317 
318 
319  // 20-25us
320  inline
321  void setDelayTimeCells(Q16n16 delaytime_cells, Int2Type<ALLPASS>)
322  {
323  /*
324  integer optimisation/approximation from
325  Van Duyne, Jaffe, Scandalis, Stilson 1997
326  http://www.scandalis.com/Jarrah/Documents/DelayLine.pdf
327  //coeff = -((d-1)>1) + (((d-1)*(d-1))>>2) - (((d-1)*(d-1)*(d-1))>>3) , d is fractional part
328  */
329  _delaytime_cells = delaytime_cells>>16; // whole integer part
330  Q15n16 dminus1 = - Q15n16_FIX1 + (uint16_t) delaytime_cells;
331  Q15n16 dminus1squared = (dminus1)*(dminus1)>>16;
332  _coeff = -(dminus1>>1) + (dminus1squared>>2) - (((dminus1squared*dminus1)>>16)>>3);
333  }
334 
335 
336  // 100us
337  inline
338  void setDelayTimeCells(float delaytime_cells, Int2Type<ALLPASS>)
339  {
340  //coeff = (1-d)/(1+d)
341  _delaytime_cells = (uint16_t) delaytime_cells;
342 
343  float fraction = delaytime_cells - _delaytime_cells;
344 
345  // modified from stk DelayA.cpp
346  float alpha_ = 1.0f + fraction; // fractional part
347  if ( alpha_ < 0.5f ) {
348  // (stk): The optimal range for alpha is about 0.5 - 1.5 in order to
349  // achieve the flattest phase delay response.
350 
351  // something's not right about how I use _delaytime_cells and
352  // NUM_BUFFER_SAMPLES etc. in my ringbuffer compared to stk
353  _delaytime_cells += 1;
354  if ( _delaytime_cells >= NUM_BUFFER_SAMPLES ) _delaytime_cells -= NUM_BUFFER_SAMPLES;
355  alpha_ += 1.0f;
356  }
357  // otherwise this would use fraction instead of alpha
358  _coeff = float_to_Q15n16((1.f-alpha_)/(1.f+alpha_));
359  }
360 
361  // Retrieve the signal in the delay line at the position delaytime_cells.
362  // It doesn't change the stored internal value of _delaytime_cells or feedback the output to the input.
363  // param delaytime_cells indicates the delay time in terms of cells in the delay buffer.
364  //
365  // inline
366  // int16_t read(uint16_t delaytime_cells, Int2Type<LINEAR>)
367  // {
368  // uint16_t read_pos = (write_pos - delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
369  // int16_t delay_sig = delay_array[read_pos]; // read the delay buffer
370  //
371  // return delay_sig;
372  // }
373 
378  inline
379  int16_t read(Q16n16 delaytime_cells, Int2Type<LINEAR>)
380  {
381  uint16_t index = (Q16n16)delaytime_cells >> 16;
382  uint16_t fraction = (uint16_t) delaytime_cells; // keeps low word
383 
384  uint16_t read_pos1 = (write_pos - index) & (NUM_BUFFER_SAMPLES - 1);
385  int16_t delay_sig1 = delay_array[read_pos1]; // read the delay buffer
386 
387  uint16_t read_pos2 = (write_pos - (index+1)) & (NUM_BUFFER_SAMPLES - 1);
388  int16_t delay_sig2 = delay_array[read_pos2]; // read the delay buffer
389 
390  /*
391  int16_t difference = delay_sig2 - delay_sig1;
392  int16_t delay_sig_fraction = ((int32_t) fraction * difference) >> 16;
393 
394  int16_t delay_sig = delay_sig1+delay_sig_fraction;
395  */
396  int16_t delay_sig = delay_sig1 + ((int32_t)delay_sig2*fraction)>>16;
397 
398  return delay_sig;
399  }
400 
401 
402 };
403 
409 #endif // #ifndef AUDIODELAY_FEEDBACK_H_
int16_t next(int8_t input, uint16_t delaytime_cells)
Input a value to the delay, retrieve the signal in the delay line at the position delaytime_cells...
void write(int8_t input)
Input a value to the delay but don&#39;t change the delay time or retrieve the output signal...
Audio delay line with feedback for comb filter, flange, chorus and short echo effects.
void write(int8_t input, uint16_t offset)
Input a value to the delay at an offset from the current write position.
void setFeedbackLevel(int8_t feedback_level)
Set the feedback gain.
int32_t Q15n16
signed fractional number using 15 integer bits and 16 fractional bits, represents -32767...
Definition: mozzi_fixmath.h:40
int16_t read(Q16n16 delaytime_cells)
Retrieve the signal in the delay line at the interpolated fractional position delaytime_cells.
Q15n16 float_to_Q15n16(float a)
Convert float to Q15n16 fix.
Enables you to instantiate a template based on an integer value.
Definition: meta.h:20
AudioDelayFeedback()
Constructor.
void writeFeedback(int8_t input)
Input a value to the delay but don&#39;t advance the write position, change the delay time or retrieve th...
void setDelayTimeCells(float delaytime_cells)
Set delay time expressed in samples, fractional float for an interpolating delay. ...
#define Q15n16_FIX1
1 in Q15n16 format
Definition: mozzi_fixmath.h:63
int16_t read()
Retrieve the signal in the delay line at the current stored delaytime_cells.
AudioDelayFeedback(uint16_t delaytime_cells)
Constructor.
void setDelayTimeCells(Q16n16 delaytime_cells)
Set delay time expressed in samples, fractional Q16n16 for an interpolating delay.
Q16n0 Q16n16_to_Q16n0(Q16n16 a)
Convert Q16n16 fixed to Q16n0 uint16_t.
int16_t next(int8_t input)
Input a value to the delay and retrieve the signal in the delay line at the position delaytime_cells...
int16_t next(int8_t input, Q16n16 delaytime_cells)
Input a value to the delay, retrieve the signal in the delay line at the interpolated fractional posi...
void setDelayTimeCells(uint16_t delaytime_cells)
Set delay time expressed in samples.
uint32_t Q16n16
unsigned fractional number using 16 integer bits and 16 fractional bits, represents 0 to 65535...
Definition: mozzi_fixmath.h:46
AudioDelayFeedback(uint16_t delaytime_cells, int8_t feedback_level)
Constructor.