Mozzi  version 2015-05-11-20:23
sound synthesis library for Arduino
 All Classes Functions Typedefs Groups
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'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'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.