robert@464: /* robert@464: ____ _____ _ _ robert@464: | __ )| ____| | / \ robert@464: | _ \| _| | | / _ \ robert@464: | |_) | |___| |___ / ___ \ robert@464: |____/|_____|_____/_/ \_\ robert@464: robert@464: The platform for ultra-low latency audio and sensor processing robert@464: robert@464: http://bela.io robert@464: robert@464: A project of the Augmented Instruments Laboratory within the robert@464: Centre for Digital Music at Queen Mary University of London. robert@464: http://www.eecs.qmul.ac.uk/~andrewm robert@464: robert@464: (c) 2016 Augmented Instruments Laboratory: Andrew McPherson, robert@464: Astrid Bin, Liam Donovan, Christian Heinrichs, Robert Jack, robert@464: Giulio Moro, Laurel Pardue, Victor Zappi. All rights reserved. robert@464: robert@464: The Bela software is distributed under the GNU Lesser General Public License robert@464: (LGPL 3.0), available here: https://www.gnu.org/licenses/lgpl-3.0.txt robert@464: */ robert@464: robert@464: robert@464: #include // to schedule lower prio parallel process robert@464: #include robert@464: #include robert@464: #include robert@464: #include "SampleData.h" robert@464: robert@464: SampleData gSampleData; // User defined structure to get complex data from main robert@464: int gReadPtr; // Position of last read sample from file robert@464: robert@464: // filter vars robert@464: float gLastX[2]; robert@464: float gLastY[2]; robert@464: double lb0, lb1, lb2, la1, la2 = 0.0; robert@464: robert@464: // communication vars between the 2 auxiliary tasks robert@464: int gChangeCoeff = 0; robert@464: int gFreqDelta = 0; robert@464: robert@464: void initialise_filter(float freq); robert@464: robert@464: void calculate_coeff(float cutFreq); robert@464: robert@464: bool initialise_aux_tasks(); robert@464: robert@464: // Task for handling the update of the frequencies using the matrix robert@464: AuxiliaryTask gChangeCoeffTask; robert@464: robert@464: void check_coeff(); robert@464: robert@464: // Task for handling the update of the frequencies using the matrix robert@464: AuxiliaryTask gInputTask; robert@464: robert@464: void read_input(); robert@464: robert@464: robert@464: extern float gCutFreq; robert@464: robert@464: robert@464: bool setup(BelaContext *context, void *userData) robert@464: { robert@464: robert@464: // Retrieve a parameter passed in from the initAudio() call robert@464: gSampleData = *(SampleData *)userData; robert@464: robert@464: gReadPtr = -1; robert@464: robert@464: initialise_filter(200); robert@464: robert@464: // Initialise auxiliary tasks robert@464: if(!initialise_aux_tasks()) robert@464: return false; robert@464: robert@464: return true; robert@464: } robert@464: robert@464: void render(BelaContext *context, void *userData) robert@464: { robert@464: for(unsigned int n = 0; n < context->audioFrames; n++) { robert@464: float sample = 0; robert@464: float out = 0; robert@464: robert@464: // If triggered... robert@464: if(gReadPtr != -1) robert@464: sample += gSampleData.samples[gReadPtr++]; // ...read each sample... robert@464: robert@464: if(gReadPtr >= gSampleData.sampleLen) robert@464: gReadPtr = -1; robert@464: robert@464: out = lb0*sample+lb1*gLastX[0]+lb2*gLastX[1]-la1*gLastY[0]-la2*gLastY[1]; robert@464: robert@464: gLastX[1] = gLastX[0]; robert@464: gLastX[0] = out; robert@464: gLastY[1] = gLastY[0]; robert@464: gLastY[0] = out; robert@464: robert@464: for(unsigned int channel = 0; channel < context->audioChannels; channel++) robert@464: context->audioOut[n * context->audioChannels + channel] = out; // ...and put it in both left and right channel robert@464: robert@464: } robert@464: robert@464: // Request that the lower-priority tasks run at next opportunity robert@464: Bela_scheduleAuxiliaryTask(gChangeCoeffTask); robert@464: Bela_scheduleAuxiliaryTask(gInputTask); robert@464: } robert@464: robert@464: // First calculation of coefficients robert@464: robert@464: void initialise_filter(float freq) robert@464: { robert@464: calculate_coeff(freq); robert@464: } robert@464: robert@464: robert@464: // Calculate the filter coefficients robert@464: // second order low pass butterworth filter robert@464: robert@464: void calculate_coeff(float cutFreq) robert@464: { robert@464: // Initialise any previous state (clearing buffers etc.) robert@464: // to prepare for calls to render() robert@464: float sampleRate = 44100; robert@464: double f = 2*M_PI*cutFreq/sampleRate; robert@464: double denom = 4+2*sqrt(2)*f+f*f; robert@464: lb0 = f*f/denom; robert@464: lb1 = 2*lb0; robert@464: lb2 = lb0; robert@464: la1 = (2*f*f-8)/denom; robert@464: la2 = (f*f+4-2*sqrt(2)*f)/denom; robert@464: gLastX[0] = gLastX [1] = 0; robert@464: gLastY[0] = gLastY[1] = 0; robert@464: robert@464: } robert@464: robert@464: robert@464: // Initialise the auxiliary tasks robert@464: // and print info robert@464: robert@464: bool initialise_aux_tasks() robert@464: { robert@464: if((gChangeCoeffTask = Bela_createAuxiliaryTask(&check_coeff, 90, "bela-check-coeff")) == 0) robert@464: return false; robert@464: robert@464: if((gInputTask = Bela_createAuxiliaryTask(&read_input, 50, "bela-read-input")) == 0) robert@464: return false; robert@464: robert@464: rt_printf("Press 'a' to trigger sample, 's' to stop\n"); robert@464: rt_printf("Press 'z' to low down cut-off freq of 100 Hz, 'x' to raise it up\n"); robert@464: rt_printf("Press 'q' to quit\n"); robert@464: robert@464: return true; robert@464: } robert@464: robert@464: // Check if cut-off freq has been changed robert@464: // and new coefficients are needed robert@464: robert@464: void check_coeff() robert@464: { robert@464: if(gChangeCoeff == 1) robert@464: { robert@464: gCutFreq += gFreqDelta; robert@464: gCutFreq = gCutFreq < 0 ? 0 : gCutFreq; robert@464: gCutFreq = gCutFreq > 22050 ? 22050 : gCutFreq; robert@464: robert@464: rt_printf("Cut-off frequency: %f\n", gCutFreq); robert@464: robert@464: calculate_coeff(gCutFreq); robert@464: gChangeCoeff = 0; robert@464: } robert@464: } robert@464: robert@464: // This is a lower-priority call to periodically read keyboard input robert@464: // and trigger samples. By placing it at a lower priority, robert@464: // it has minimal effect on the audio performance but it will take longer to robert@464: // complete if the system is under heavy audio load. robert@464: robert@464: void read_input() robert@464: { robert@464: // This is not a real-time task! robert@464: // Cos getchar is a system call, not handled by Xenomai. robert@464: // This task will be automatically down graded. robert@464: robert@464: char keyStroke = '.'; robert@464: robert@464: keyStroke = getchar(); robert@464: while(getchar()!='\n'); // to read the first stroke robert@464: robert@464: switch (keyStroke) robert@464: { robert@464: case 'a': robert@464: gReadPtr = 0; robert@464: break; robert@464: case 's': robert@464: gReadPtr = -1; robert@464: break; robert@464: case 'z': robert@464: gChangeCoeff = 1; robert@464: gFreqDelta = -100; robert@464: break; robert@464: case 'x': robert@464: gChangeCoeff = 1; robert@464: gFreqDelta = 100; robert@464: break; robert@464: case 'q': robert@464: gShouldStop = true; robert@464: break; robert@464: default: robert@464: break; robert@464: } robert@464: } robert@464: robert@464: robert@464: void cleanup(BelaContext *context, void *userData) robert@464: { robert@464: delete[] gSampleData.samples; robert@464: } robert@464: robert@464: robert@464: /** robert@500: \example filter-IIR/render.cpp robert@464: robert@464: Infinite Impulse Response Filter robert@464: ------------------------------ robert@464: robert@464: This is an example of a infinite impulse response filter implementation. robert@464: */