Skip to content

Commit

Permalink
Merge pull request #286 from sensorium/dev/AudioDelay_template
Browse files Browse the repository at this point in the history
Add a type template parameter to `AudioDelayFeedback`
  • Loading branch information
tomcombriat authored Nov 14, 2024
2 parents 7b2adcd + 043d545 commit 0ca34ef
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 46 deletions.
2 changes: 1 addition & 1 deletion AudioDelay.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class AudioDelay

// why does delay jump if I read it before writing?
delay_array[_write_pos] = in_value; // write to buffer
int8_t delay_sig = delay_array[read_pos] ; // read the delay buffer
T delay_sig = delay_array[read_pos] ; // read the delay buffer

return (T)delay_sig;
}
Expand Down
84 changes: 50 additions & 34 deletions AudioDelayFeedback.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "mozzi_utils.h"
#include "meta.h"
#include "IntegerType.h"

enum interpolation_types {LINEAR,ALLPASS};

Expand All @@ -31,11 +32,14 @@ with the input, do it in your sketch. AudioDelayFeedback uses more processing an
than a plain AudioDelay, but allows for more dramatic effects with feedback.
@tparam INTERP_TYPE a choice of LINEAR (default) or ALLPASS interpolation. LINEAR is better
for sweeping delay times, ALLPASS may be better for reverb-like effects.
@tparam the type of numbers to use for the signal in the delay. The default is int8_t, but int16_t could be useful. Larger types (int32_t) might produce overflows as of v2.0.2.
*/
template <uint16_t NUM_BUFFER_SAMPLES, int8_t INTERP_TYPE = LINEAR>
template <uint16_t NUM_BUFFER_SAMPLES, int8_t INTERP_TYPE = LINEAR, typename su=int8_t>
class AudioDelayFeedback
{

typedef typename IntegerType<sizeof(su)+sizeof(su)>::signed_type return_type;

public:
/** Constructor.
*/
Expand Down Expand Up @@ -68,7 +72,7 @@ class AudioDelayFeedback
@note slower than next(int8_t input, uint16_t delaytime_cells)
*/
inline
int16_t next(int8_t input)
return_type next(su input)
{
// chooses a different next() function depending on whether the
// the template parameter is LINEAR(default if none provided) or ALLPASS.
Expand All @@ -85,18 +89,19 @@ class AudioDelayFeedback
@note Timing: 4us
*/
inline
int16_t next(int8_t input, uint16_t delaytime_cells)
return_type next(su input, uint16_t delaytime_cells)
{
//setPin13High();
++write_pos &= (NUM_BUFFER_SAMPLES - 1);
uint16_t read_pos = (write_pos - delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
// < 1us to here
int16_t delay_sig = delay_array[read_pos]; // read the delay buffer
return_type delay_sig = delay_array[read_pos]; // read the delay buffer
// with this line, the method takes 18us
//int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)/128),-128),127); // feedback clipped
// this line, the whole method takes 4us... Compiler doesn't optimise pow2 divides. Why?
int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)>>7),-128),127); // feedback clipped
delay_array[write_pos] = (int16_t) input + feedback_sig; // write to buffer
//int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)>>7),-128),127); // feedback clipped
su feedback_sig = (su) constrain( ((delay_sig * _feedback_level)>>7), -(1<<((sizeof(su)<<3)-1)), (1<<((sizeof(su)<<3)-1))-1);
delay_array[write_pos] = (return_type)input + feedback_sig; // write to buffer
//setPin13Low();
return delay_sig;
}
Expand All @@ -110,7 +115,7 @@ class AudioDelayFeedback
value of _delaytime_cells.
*/
inline
int16_t next(int8_t input, Q16n16 delaytime_cells)
return_type next(su input, Q16n16 delaytime_cells)
{
//setPin13High();
++write_pos &= (NUM_BUFFER_SAMPLES - 1);
Expand All @@ -119,21 +124,23 @@ class AudioDelayFeedback
uint16_t fraction = (uint16_t) delaytime_cells; // keeps low word

uint16_t read_pos1 = (write_pos - index) & (NUM_BUFFER_SAMPLES - 1);
int16_t delay_sig1 = delay_array[read_pos1]; // read the delay buffer
return_type delay_sig1 = delay_array[read_pos1]; // read the delay buffer

uint16_t read_pos2 = (write_pos - (index+1)) & (NUM_BUFFER_SAMPLES - 1);
int16_t delay_sig2 = delay_array[read_pos2]; // read the delay buffer
return_type delay_sig2 = delay_array[read_pos2]; // read the delay buffer


int16_t difference = delay_sig2 - delay_sig1;
int16_t delay_sig_fraction = (int16_t)((int32_t)((int32_t) fraction * difference) >> 16);
return_type difference = delay_sig2 - delay_sig1;
//int16_t delay_sig_fraction = (int16_t)((int32_t)((int32_t) fraction * difference) >> 16);
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);

int16_t delay_sig = delay_sig1+delay_sig_fraction;
return_type delay_sig = delay_sig1+delay_sig_fraction;

//int16_t delay_sig = delay_sig1 + ((int32_t)delay_sig2*fraction)>>16;

int8_t feedback_sig = (int8_t) min(max((((int16_t)(delay_sig * _feedback_level))>>7),-128),127); // feedback clipped
delay_array[write_pos] = (int16_t) input + feedback_sig; // write to buffer
//int8_t feedback_sig = (int8_t) min(max((((int16_t)(delay_sig * _feedback_level))>>7),-128),127); // feedback clipped
su feedback_sig = (su) constrain(((delay_sig * _feedback_level)>>7), -(1<<((sizeof(su)<<3)-1)), (1<<((sizeof(su)<<3)-1))-1);
delay_array[write_pos] = (return_type) input + feedback_sig; // write to buffer
//setPin13Low();
return delay_sig;
}
Expand All @@ -143,7 +150,7 @@ class AudioDelayFeedback
@param input the signal input.
*/
inline
void write(int8_t input)
void write(su input)
{
++write_pos &= (NUM_BUFFER_SAMPLES - 1);
delay_array[write_pos] = input;
Expand All @@ -155,7 +162,7 @@ class AudioDelayFeedback
@param input the signal input.
*/
inline
void writeFeedback(int8_t input)
void writeFeedback(su input)
{
delay_array[write_pos] = input;
}
Expand All @@ -167,7 +174,7 @@ class AudioDelayFeedback
@param offset the number of cells behind the ordinary write position where the input will be written.
*/
inline
void write(int8_t input, uint16_t offset)
void write(su input, uint16_t offset)
{
uint16_t _pos = (write_pos + offset) & (NUM_BUFFER_SAMPLES - 1);
delay_array[_pos] = input;
Expand All @@ -179,7 +186,7 @@ class AudioDelayFeedback
@param delaytime_cells indicates the delay time in terms of cells in the delay buffer.
*/
inline
int16_t read(Q16n16 delaytime_cells)
return_type read(Q16n16 delaytime_cells)
{
return read(delaytime_cells, Int2Type<INTERP_TYPE>());
}
Expand All @@ -189,7 +196,7 @@ class AudioDelayFeedback
It doesn't change the stored internal value of _delaytime_cells or feedback the output to the input.
*/
inline
int16_t read()
return_type read()
{
return read(Int2Type<INTERP_TYPE>());
}
Expand Down Expand Up @@ -243,26 +250,29 @@ class AudioDelayFeedback


private:
int16_t delay_array[NUM_BUFFER_SAMPLES];
return_type delay_array[NUM_BUFFER_SAMPLES];
uint16_t write_pos;
int8_t _feedback_level;
uint16_t _delaytime_cells;
Q15n16 _coeff; // for allpass interpolation
su last_in;
return_type last_out;



/** Input a value to the delay and retrieve the signal in the delay line at the position delaytime_cells.
@param in_value the signal input.
*/
inline
int16_t next(int8_t in_value, Int2Type<LINEAR>)
return_type next(su in_value, Int2Type<LINEAR>)
{
++write_pos &= (NUM_BUFFER_SAMPLES - 1);
uint16_t read_pos = (write_pos - _delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);

int16_t delay_sig = delay_array[read_pos]; // read the delay buffer
int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)/128),-128),127); // feedback clipped
delay_array[write_pos] = (int16_t) in_value + feedback_sig; // write to buffer
return_type delay_sig = delay_array[read_pos]; // read the delay buffer
//int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)/128),-128),127); // feedback clipped
su feedback_sig = (su) constrain(((delay_sig * _feedback_level)>>7), -(1<<((sizeof(su)<<3)-1)), (1<<((sizeof(su)<<3)-1))-1);
delay_array[write_pos] = (return_type) in_value + feedback_sig; // write to buffer

return delay_sig;
}
Expand All @@ -277,7 +287,7 @@ class AudioDelayFeedback
@note Timing: 10us
*/
inline
int16_t next(int8_t input, Int2Type<ALLPASS>)
return_type next(su input, Int2Type<ALLPASS>)
{
/*
http://www.scandalis.com/Jarrah/Documents/DelayLine.pdf
Expand All @@ -290,19 +300,24 @@ class AudioDelayFeedback
= coeff * (in-last_out) + last_in
*/
//setPin13High();

/* I think these should **not** be static
static int8_t last_in;
static int16_t last_out;

*/

++write_pos &= (NUM_BUFFER_SAMPLES - 1);

uint16_t read_pos1 = (write_pos - _delaytime_cells) & (NUM_BUFFER_SAMPLES - 1);
int16_t delay_sig = delay_array[read_pos1]; // read the delay buffer
return_type delay_sig = delay_array[read_pos1]; // read the delay buffer

int16_t interp = (int16_t)(_coeff * ((int16_t)input - last_out)>>16) + last_in; // Q15n16*Q15n0 + Q15n0 = Q15n16 + Q15n0 = Q15n16
//int16_t interp = (int16_t)(_coeff * ((int16_t)input - last_out)>>16) + last_in; // Q15n16*Q15n0 + Q15n0 = Q15n16 + Q15n0 = Q15n16
return_type interp = (return_type)(_coeff * ((return_type)input - last_out)>>(sizeof(su)<<4)) + last_in;
delay_sig += interp;

int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)>>7),-128),127); // feedback clipped
delay_array[write_pos] = (int16_t) input + feedback_sig; // write to buffer
//int8_t feedback_sig = (int8_t) min(max(((delay_sig * _feedback_level)>>7),-128),127); // feedback clipped
su feedback_sig = (su) constrain(((delay_sig * _feedback_level)>>7), -(1<<((sizeof(su)<<3)-1)), (1<<((sizeof(su)<<3)-1))-1);
delay_array[write_pos] = (return_type) input + feedback_sig; // write to buffer

last_in = input;
last_out = delay_sig;
Expand Down Expand Up @@ -372,24 +387,25 @@ class AudioDelayFeedback
@param delaytime_cells indicates the delay time in terms of cells in the delay buffer.
*/
inline
int16_t read(Q16n16 delaytime_cells, Int2Type<LINEAR>)
return_type read(Q16n16 delaytime_cells, Int2Type<LINEAR>)
{
uint16_t index = (Q16n16)delaytime_cells >> 16;
uint16_t fraction = (uint16_t) delaytime_cells; // keeps low word

uint16_t read_pos1 = (write_pos - index) & (NUM_BUFFER_SAMPLES - 1);
int16_t delay_sig1 = delay_array[read_pos1]; // read the delay buffer
return_type delay_sig1 = delay_array[read_pos1]; // read the delay buffer

uint16_t read_pos2 = (write_pos - (index+1)) & (NUM_BUFFER_SAMPLES - 1);
int16_t delay_sig2 = delay_array[read_pos2]; // read the delay buffer
return_type delay_sig2 = delay_array[read_pos2]; // read the delay buffer

/*
int16_t difference = delay_sig2 - delay_sig1;
int16_t delay_sig_fraction = ((int32_t) fraction * difference) >> 16;
int16_t delay_sig = delay_sig1+delay_sig_fraction;
*/
int16_t delay_sig = delay_sig1 + ((int32_t)delay_sig2*fraction)>>16;
//int16_t delay_sig = delay_sig1 + ((int32_t)delay_sig2*fraction)>>16;
return_type delay_sig = delay_sig1 + ((typename IntegerType<sizeof(return_type)+sizeof(return_type)>::signed_type)delay_sig2*fraction)>>16;

return delay_sig;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/09.Delays/AudioDelay/AudioDelay.ino
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ void updateControl(){
}

AudioOutput updateAudio(){
char asig = aDel.next(aTriangle.next(), del_samps);
int8_t asig = aDel.next(aTriangle.next(), del_samps);
return MonoOutput::from8Bit(asig);
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void updateControl(){


AudioOutput updateAudio(){
char asig = aTriangle.next(); // get this so it can be used twice without calling next() again
int8_t asig = aTriangle.next(); // get this so it can be used twice without calling next() again
//return asig/8 + aDel.next(asig, (uint16_t) del_samps); // mix some straight signal with the delayed signal
//return aDel.next(aTriangle.next(), (uint16_t) del_samps); // instead of the previous 2 lines for only the delayed signal
return MonoOutput::fromAlmostNBit(9, (asig >> 3) + aDel.next(asig, deltime)); // mix some straight signal with the delayed signal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void updateControl(){
// set random delay parameters
float del_cells = (float)rand(65535u)/256;
aDel.setDelayTimeCells(del_cells); // Allpass interpolation for fractional delay time
char fb = rand(-50,50);
int8_t fb = rand(-50,50);
aDel.setFeedbackLevel(fb);
kDelay.start(duration+500);
}
Expand Down
14 changes: 7 additions & 7 deletions examples/09.Delays/AudioDelayFeedbackX2/AudioDelayFeedbackX2.ino
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,21 @@ void setup(){


void updateControl(){
// delay time range from 0 to 127 samples, @ 16384 samps per sec = 0 to 7 milliseconds
del_samps1 = 64+kDelSamps1.next();
// delay time range from 1 to 128 samples, @ 16384 samps per sec = 0 to 7 milliseconds
del_samps1 = 65+(kDelSamps1.next()>>1);

// delay time range from 1 to 33 samples, @ 16384 samps per sec = 0 to 2 milliseconds
del_samps2 = 17+kDelSamps2.next()/8;
}


AudioOutput updateAudio(){
char asig1 = aTriangle1.next(); // get this so it can be used twice without calling next() again
int aflange1 = (asig1>>3) + aDel1.next(asig1, del_samps1); // mix some straignt signal with the delayed signal
int8_t asig1 = aTriangle1.next(); // get this so it can be used twice without calling next() again
int16_t aflange1 = (asig1>>3) + aDel1.next(asig1, del_samps1); // mix some straignt signal with the delayed signal

char asig2 = aTriangle2.next(); // get this so it can be used twice without calling next() again
int aflange2 = (asig2>>3) + aDel2.next(asig2, del_samps2); // mix some straignt signal with the delayed signal
return MonoOutput::fromAlmostNBit(10, aflange1 + aflange2);
int8_t asig2 = aTriangle2.next(); // get this so it can be used twice without calling next() again
int16_t aflange2 = (asig2>>3) + aDel2.next(asig2, del_samps2); // mix some straignt signal with the delayed signal
return MonoOutput::fromAlmostNBit(10, aflange1 + aflange2).clip();
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void updateControl(){


AudioOutput updateAudio(){
char asig = aTriangle.next(); // get this so it can be used twice without calling next() again
int8_t asig = aTriangle.next(); // get this so it can be used twice without calling next() again
//return asig/8 + aDel.next(asig, del_samps); // mix some straight signal with the delayed signal
//return aDel.next(aTriangle.next(), del_samps); // instead of the previous 2 lines for only the delayed signal
return MonoOutput::fromAlmostNBit(13, asig + (aDel.next(asig, del_samps_fractional)<<4)); // mix some straight signal with the delayed signal
Expand Down

0 comments on commit 0ca34ef

Please sign in to comment.