| Mozzi version v1.1.0 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
27 /** Audio delay line with feedback for comb filter, flange, chorus and short echo effects.
28 @tparam NUM_BUFFER_SAMPLES is the length of the delay buffer in samples, and should be a
29 power of two. The maximum delay length which will fit in an atmega328 is half
30 that of a plain AudioDelay object, in this case 256 cells, or about 15
31 milliseconds. AudioDelayFeedback uses int16_t sized cells to accomodate the higher
32 amplitude of direct input to the delay as well as the feedback, without losing
33 precision. Output is only the delay line signal. If you want to mix the delay
34 with the input, do it in your sketch. AudioDelayFeedback uses more processing and memory
35 than a plain AudioDelay, but allows for more dramatic effects with feedback.
36 @tparam INTERP_TYPE a choice of LINEAR (default) or ALLPASS interpolation. LINEAR is better
37 for sweeping delay times, ALLPASS may be better for reverb-like effects.
38 */
39 template <uint16_t NUM_BUFFER_SAMPLES, int8_t INTERP_TYPE = LINEAR>
40 class AudioDelayFeedback
41 {
42
43 public:
44 /** Constructor.
45 */
46 AudioDelayFeedback(): write_pos(0), _feedback_level(0), _delaytime_cells(0)
47 {}
48
49
50 /** Constructor.
51 @param delaytime_cells delay time expressed in cells.
52 For example, 128 cells delay at AUDIO_RATE 16384 would produce a time delay of 128/16384 = 0.0078125 s = 7.8 ms
53 Put another way, num_cells = delay_seconds * AUDIO_RATE.
54 */
55 AudioDelayFeedback(uint16_t delaytime_cells): write_pos(0), _feedback_level(0), _delaytime_cells(delaytime_cells)
56 {}
57
58
59 /** Constructor.
60 @param delaytime_cells delay time expressed in cells.
61 For example, 128 cells delay at AUDIO_RATE 16384 would produce a time delay of 128/16384 = 0.0078125 s = 7.8 ms
62 Put another way, num_cells = delay_seconds * AUDIO_RATE.
63 @param feedback_level is the feedback level from -128 to 127 (representing -1 to 1).
64 */
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
70 /** Input a value to the delay and retrieve the signal in the delay line at the position delaytime_cells.
71 @param input the signal input.
72 @note slower than next(int8_t input, uint16_t delaytime_cells)
73 */
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
85 /** Input a value to the delay, retrieve the signal in the delay line at the position delaytime_cells, and add feedback from the output to the input.
86 @param input the signal input.
87 @param delaytime_cells indicates the delay time in terms of cells in the delay buffer.
88 It doesn't change the stored internal value of _delaytime_cells.
89 @note Timing: 4us
90 */
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
110 /** Input a value to the delay, retrieve the signal in the delay line at the interpolated fractional position delaytime_cells, and add feedback from the output to the input.
111 @param input the signal input.
112 @param delaytime_cells is a fractional number to set the delay time in terms of cells
113 or partial cells in the delay buffer. It doesn't change the stored internal
114 value of _delaytime_cells.
115 */
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
146 /** Input a value to the delay but don't change the delay time or retrieve the output signal.
147 @param input the signal input.
148 */
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
157 /** Input a value to the delay but don't advance the write position, change the delay time or retrieve the output signal.
158 This can be useful for manually adding feedback to the delay line, "behind" the advancing write head.
159 @param input the signal input.
160 */
161 inline
162 void writeFeedback(int8_t input)
163 {
164 delay_array[write_pos] = input;
165 }
166
167
168 /** Input a value to the delay at an offset from the current write position. Don't advance the main
169 write position or change the stored delay time or retrieve the output signal.
170 @param input the signal input.
171 @param offset the number of cells behind the ordinary write position where the input will be written.
172 */
173 inline
174 void write(int8_t input, uint16_t offset)
175 {
176 uint16_t _pos = (write_pos + offset) & (NUM_BUFFER_SAMPLES - 1);
177 delay_array[_pos] = input;
178 }
179
180
181 /** Retrieve the signal in the delay line at the interpolated fractional position delaytime_cells.
182 It doesn't change the stored internal value of _delaytime_cells or feedback the output to the input.
183 @param delaytime_cells indicates the delay time in terms of cells in the delay buffer.
184 */
185 inline
186 int16_t read(Q16n16 delaytime_cells)
187 {
188 return read(delaytime_cells, Int2Type<INTERP_TYPE>());
189 }
190
191
192 /** Retrieve the signal in the delay line at the current stored delaytime_cells.
193 It doesn't change the stored internal value of _delaytime_cells or feedback the output to the input.
194 */
195 inline
196 int16_t read()
197 {
198 return read(Int2Type<INTERP_TYPE>());
199 }
200
201
202 /** Set delay time expressed in samples.
203 @param delaytime_cells delay time expressed in cells, with each cell played per tick of AUDIO_RATE.
204 For example, 128 cells delay at AUDIO_RATE would produce a time delay of 128/16384 = 0.0078125 s = 7.8 ms
205 Put another way, num_cells = delay_seconds * AUDIO_RATE.
206 */
207 inline
208 void setDelayTimeCells(uint16_t delaytime_cells)
209 {
210 _delaytime_cells = (uint16_t) delaytime_cells;
211 }
212
213
214 /** Set delay time expressed in samples, fractional Q16n16 for an interpolating delay.
215 @param delaytime_cells delay time expressed in cells, with each cell played per tick of AUDIO_RATE.
216 For example, 128 cells delay at AUDIO_RATE would produce a time delay of 128/16384 = 0.0078125 s = 7.8 ms
217 Put another way, num_cells = delay_seconds * AUDIO_RATE.
218 */
219 inline
220 void setDelayTimeCells(Q16n16 delaytime_cells)
221 {
222 return setDelayTimeCells(delaytime_cells, Int2Type<INTERP_TYPE>());
223 }
224
225
226 /** Set delay time expressed in samples, fractional float for an interpolating delay.
227 @param delaytime_cells delay time expressed in cells, with each cell played per tick of AUDIO_RATE.
228 For example, 128 cells delay at AUDIO_RATE would produce a time delay of 128/16384 = 0.0078125 s = 7.8 ms
229 Put another way, num_cells = delay_seconds * AUDIO_RATE.
230 */
231 inline
232 void setDelayTimeCells(float delaytime_cells)
233 {
234 return setDelayTimeCells(delaytime_cells, Int2Type<INTERP_TYPE>());
235 }
236
237
238 /** Set the feedback gain.
239 @param feedback_level is the feedback level from -128 to 127 (representing -1 to 1).
240 */
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
258 /** Input a value to the delay and retrieve the signal in the delay line at the position delaytime_cells.
259 @param in_value the signal input.
260 */
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
276 /** The delaytime_cells has to be set seperately, because it's slowish
277 and in this implementation the allpass interpolation mode doesn't slide
278 nicely from one delay time to another.
279 @param input an audio signal in
280 @return the delayed signal, including feedback
281 @note Timing: 10us
282 */
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
374 /** Retrieve the signal in the delay line at the interpolated fractional position delaytime_cells.
375 It doesn't change the stored internal value of _delaytime_cells or feedback the output to the input.
376 @param delaytime_cells indicates the delay time in terms of cells in the delay buffer.
377 */
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
404 /**
405 @example 09.Delays/AudioDelayFeedback/AudioDelayFeedback.ino
406 This is an example of how to use the AudioDelayFeedback class.
407 */
408
409 #endif // #ifndef AUDIODELAY_FEEDBACK_H_
AudioDelayFeedback::write
void write(int8_t input)
Input a value to the delay but don&#39;t change the delay time or retrieve the output signal...
Definition: AudioDelayFeedback.h:150
AudioDelayFeedback::write
void write(int8_t input, uint16_t offset)
Input a value to the delay at an offset from the current write position.
Definition: AudioDelayFeedback.h:174
AudioDelayFeedback::setFeedbackLevel
void setFeedbackLevel(int8_t feedback_level)
Set the feedback gain.
Definition: AudioDelayFeedback.h:242
AudioDelayFeedback::read
int16_t read(Q16n16 delaytime_cells)
Retrieve the signal in the delay line at the interpolated fractional position delaytime_cells.
Definition: AudioDelayFeedback.h:186
Int2Type
Enables you to instantiate a template based on an integer value.
Definition: meta.h:20
AudioDelayFeedback::AudioDelayFeedback
AudioDelayFeedback()
Constructor.
Definition: AudioDelayFeedback.h:46
AudioDelayFeedback::writeFeedback
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...
Definition: AudioDelayFeedback.h:162
AudioDelayFeedback::setDelayTimeCells
void setDelayTimeCells(float delaytime_cells)
Set delay time expressed in samples, fractional float for an interpolating delay. ...
Definition: AudioDelayFeedback.h:232
AudioDelayFeedback::read
int16_t read()
Retrieve the signal in the delay line at the current stored delaytime_cells.
Definition: AudioDelayFeedback.h:196
AudioDelayFeedback::AudioDelayFeedback
AudioDelayFeedback(uint16_t delaytime_cells)
Constructor.
Definition: AudioDelayFeedback.h:55
AudioDelayFeedback::setDelayTimeCells
void setDelayTimeCells(Q16n16 delaytime_cells)
Set delay time expressed in samples, fractional Q16n16 for an interpolating delay.
Definition: AudioDelayFeedback.h:220
AudioDelayFeedback::next
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...
Definition: AudioDelayFeedback.h:75
AudioDelayFeedback::next
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...
Definition: AudioDelayFeedback.h:117
AudioDelayFeedback::AudioDelayFeedback
AudioDelayFeedback(uint16_t delaytime_cells, int8_t feedback_level)
Constructor.
Definition: AudioDelayFeedback.h:65