Simple delay
This example demonstrates how to apply a feedback delay with an incorporated low-pass filter to an audio signal.
In order to create a delay effect we need to allocate a buffer (i.e. an array of samples) that holds previous samples. Every time we output a sample we need to go back in time and retrieve the sample that occurred n samples ago, where n is our delay in samples. The buffer allows us to do just this. For every incoming sample we write its value into the buffer. We use a so-called write pointer (gDelayBufWritePtr) in order to keep track of the index of the buffer we need to write into. This write pointer is incremented on every sample and wrapped back around to 0 when its reached the last index of the buffer size (this technique is commonly referred to as a circular buffer or a ring buffer).
We go a bit further by applying feedback and filtering to the delay in order to make the effect more interesting. To apply feedback to the delay, we take the sample that occurred gDelayInSamples ago, multiply it by our gDelayFeedbackAmount parameter and add it to the dry input signal that we will write into the buffer. This way, there will always be a trace of the previously delayed sample in the output that will slowly fade away over time.
Next, we apply a low-pass filter. We have pre-calculated the coefficients that are required to apply a Butterworth (or biquad) filter, which is expressed as follows: y = a0*x0 + a1*x1 + a2*x2 + a3*y1 + a4*y2, where x0 and x1 are the previous input (i.e. unfiltered) samples and y0 and y1 are the previous output (i.e. filtered) samples. We keep track of these previous input and output samples for each channel using global variables in order to apply the filter.
Finally we take the processed sample for each channel and write it into the corresponding delay buffer (gDelayBuffer_l and gDelayBuffer_r), so that in the future (after gDelayInSamples samples) we can retrieve it again! Last but not least, we read the sample from the buffer that was written gDelayInSamples ago and add it to the output.
Note that we have to ways of changing the volume of the delay effect. One way is to change the overall gain using the gDelayAmount parameter: this will immediately raise or lower the volume of the delayed signal. The other option is to use the gDelayAmountPre parameter, which will apply gain to the input of the delay line. The advantage of using this parameter is that when turning down the gain we can let the delay ring out while not letting any new input into the effect. Conversely, we can introduce the delay effect naturally without fading in previous output of the effect.
 
 
#define DELAY_BUFFER_SIZE 44100
 
float gDelayBuffer_l[DELAY_BUFFER_SIZE] = {0};
float gDelayBuffer_r[DELAY_BUFFER_SIZE] = {0};
int gDelayBufWritePtr = 0;
float gDelayAmount = 1.0;
float gDelayFeedbackAmount = 0.999;
float gDelayAmountPre = 0.75;
int gDelayInSamples = 22050;
 
float gDel_a0 = 0.1772443606634904;
float gDel_a1 = 0.3544887213269808;
float gDel_a2 = 0.1772443606634904;
float gDel_a3 = -0.5087156198145868;
float gDel_a4 = 0.2176930624685485;
 
float gDel_x1_l = 0;
float gDel_x2_l = 0;
float gDel_y1_l = 0;
float gDel_y2_l = 0;
float gDel_x1_r = 0;
float gDel_x2_r = 0;
float gDel_y1_r = 0;
float gDel_y2_r = 0;
 
{
 
    return true;
}
 
{
 
    for(
unsigned int n = 0; n < context->
audioFrames; n++) {
 
 
        float out_l = 0;
        float out_r = 0;
 
        
 
        
        if(++gDelayBufWritePtr>DELAY_BUFFER_SIZE)
            gDelayBufWritePtr = 0;
 
        
        
        
        float del_input_l = (gDelayAmountPre * out_l + gDelayBuffer_l[(gDelayBufWritePtr-gDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * gDelayFeedbackAmount);
        float del_input_r = (gDelayAmountPre * out_r + gDelayBuffer_r[(gDelayBufWritePtr-gDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * gDelayFeedbackAmount);
 
        
 
        
        float temp_x_l = del_input_l;
        float temp_x_r = del_input_r;
 
        
        del_input_l = gDel_a0*del_input_l
                    + gDel_a1*gDel_x1_l
                    + gDel_a2*gDel_x2_l
                    + gDel_a3*gDelayBuffer_l[(gDelayBufWritePtr-1+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE]
                    + gDel_a4*gDelayBuffer_l[(gDelayBufWritePtr-2+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE];
 
        
        gDel_x2_l = gDel_x1_l;
        gDel_x1_l = temp_x_l;
        gDel_y2_l = gDel_y1_l;
        gDel_y1_l = del_input_l;
 
        
        del_input_r = gDel_a0*del_input_r
                    + gDel_a1*gDel_x1_r
                    + gDel_a2*gDel_x2_r
                    + gDel_a3*gDelayBuffer_r[(gDelayBufWritePtr-1+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE]
                    + gDel_a4*gDelayBuffer_r[(gDelayBufWritePtr-2+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE];
 
        gDel_x2_r = gDel_x1_r;
        gDel_x1_r = temp_x_r;
        gDel_y2_r = gDel_y1_r;
        gDel_y1_r = del_input_r;
 
        
        gDelayBuffer_l[gDelayBufWritePtr] = del_input_l;
        gDelayBuffer_r[gDelayBufWritePtr] = del_input_r;
 
        
        out_l += gDelayBuffer_l[(gDelayBufWritePtr-gDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * gDelayAmount;
        out_r += gDelayBuffer_r[(gDelayBufWritePtr-gDelayInSamples+DELAY_BUFFER_SIZE)%DELAY_BUFFER_SIZE] * gDelayAmount;
 
        
    }
 
}
 
{
 
}
static float audioRead(BelaContext *context, int frame, int channel)
Read an audio input, specifying the frame number (when to read) and the channel.
Definition Bela.h:1458
static void audioWrite(BelaContext *context, int frame, int channel, float value)
Write an audio output, specifying the frame number (when to write) and the channel.
Definition Bela.h:1469
void render(BelaContext *context, void *userData)
User-defined callback function to process audio and sensor data.
Definition render.cpp:68
bool setup(BelaContext *context, void *userData)
User-defined initialisation function which runs before audio rendering begins.
Definition render.cpp:51
void cleanup(BelaContext *context, void *userData)
User-defined cleanup function which runs when the program finishes.
Definition render.cpp:96
Structure holding audio and sensor settings and pointers to I/O data buffers.
Definition Bela.h:231
const uint32_t audioFrames
The number of audio frames per block.
Definition Bela.h:322