BiquadSample/main.c
/* |
Copyright (C) 2015 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
vDSP_biquadm usage example. This sample shows how to use vDSP_biquadm to process a stereo signal through an IIR filter consisting of 4 cascaded biquad sections. Half of the signal will be processed under one set of coefficients and the second half of the signal will be processed using a different set of coefficients. vDSP_biquadm_SetTargetsDouble will be used to update the filter coefficients. The input channels are interleaved. |
*/ |
#include <stdlib.h> |
#include <Accelerate/Accelerate.h> |
// Filter coefficients for each channel (channel 0 and channel 1). |
// B coefficients are the feed-forward coefficients for each section (i.e. b0, b1, b2) |
// A coefficients are the feed-back coefficients for each section (i.e. a0 (always assumed to be 1), a1, a2) |
// B coefficients for channel 0 |
static const float B0[] = { |
0.984764420, -1.969528840, 0.984764420, |
0.977712906, -1.955425811, 0.977712906, |
0.998549912, -1.969041291, 0.986303756, |
0.990002419, -0.908952874, 0.881103728 |
}; |
// A coefficients for channel 0 |
static const float A0[] = { |
1.000000000, -1.969331359, 0.969726321, |
1.000000000, -1.955243388, 0.955608234, |
1.000000000, -1.969041291, 0.984853668, |
1.000000000, -0.908952874, 0.871106147 |
}; |
// B coefficients for channel 1 |
static const float B1[] = { |
0.984764420, -1.969528840, 0.984764420, |
0.977712906, -1.955425811, 0.977712906, |
0.999262098, -1.983019787, 0.993476599, |
0.983015517, -1.728018843, 0.952885462 |
}; |
// A coefficients for channel 1 |
static const float A1[] = { |
1.000000000, -1.969331359, 0.969726321, |
1.000000000, -1.955243388, 0.955608234, |
1.000000000, -1.983019787, 0.992738697, |
1.000000000, -1.728018843, 0.935900979 |
}; |
/* |
We will use vDSP_biquadm_SetTargetsDouble to update the filter coefficients. We will update |
only the coefficients in the second channel (channel 1) for the two middle sections. Unlike |
vDSP_biquadm_SetCoefficientsDouble, SetTargets does not update the coefficients immediately. |
The coefficients are updated during processing on a per-sample basis. |
*/ |
// New coefficients for channel 1 |
static const float B1New[] = { |
/*B1[3*0+0], B1[3*0+1], B1[3*0+2],*/ |
0.985741670, -1.688906290, 0.953772625, |
0.959492493, -1.800065496, 0.930225572, |
/*B1[3*3+0], B1[3*3+1], B1[3*3+2] */ |
}; |
static const float A1New[] = { |
/*A1[3*0+0], A1[3*0+1], A1[3*0+2],*/ |
1.000000000, -1.688906290, 0.939514295, |
1.000000000, -1.800065496, 0.889718065, |
/*A1[3*3+0], A1[3*3+1], A1[3*3+2] */ |
}; |
/* |
The filter coefficients are updated incrementally so we need to know how close we want |
to get to our targets. If we process a sufficiently large number of samples then all of |
the filter coefficients will be within this threshold of the targets. |
*/ |
static const float threshold = 0.05; |
/* |
We also need to know how 'fast' we want to iterate our filter coefficients towards the |
targets. This number, between 0 and 1, will specify that. |
*/ |
static const float rate = 0.4; |
int main() |
{ |
// We're processing a stereo signal, so 2 channels. |
vDSP_Length Channels = 2; |
// Our filter consists of 4 cascaded sections using the filter coefficients specified |
// above, and in the order specified above. |
vDSP_Length Sections = 4; |
// An example input of length 1024, just use random data. |
vDSP_Length N = 1024; |
float *Input = malloc(N * Channels * sizeof *Input); |
// We're filling the input buffer sequentially for demonstration purposes, but conceptually the data is interleaved |
// i.e. Input[2*i], i=0:1023, is the channel 0 data and Input[2*i+1],i=0:1023 is the channel 1 data. |
for (vDSP_Length i = 0; i < N*Channels; ++i) |
Input[i] = (float)rand()/(float)RAND_MAX; |
// A buffer to store our output |
float *Output = malloc(N * Channels * sizeof *Output); |
// vDSP_biquadm requires our filter coefficients in a particular format. |
// First, allocate some memory to hold the coefficients in the right format. There are 5 coefficients |
// per section, 4 sections per channel, and 2 channels, so we need to hold 5*4*2 coefficients total. |
double *F = malloc(5*Channels*Sections*sizeof *F); |
// Coefficients are ordered by section, then channel, i.e. "section-major ordering". Each section holds its coefficients as [b0 b1 b2 a1 a2] |
// (as mentioned above, a0 is always assumed to be 1). So, if you lay it out as a matrix: |
// |
// Channel 0 Channel 1 |
// Section0 [b0 b1 b2 a1 a2 b0 b1 b2 a1 a2 |
// Section1 b0 b1 b2 a1 a2 b0 b1 b2 a1 a2 |
// Section2 b0 b1 b2 a1 a2 b0 b1 b2 a1 a2 |
// Section3 b0 b1 b2 a1 a2 b0 b1 b2 a1 a2] |
// Lay out channel 0: |
for (vDSP_Length j = 0; j < Sections; ++j) |
{ |
F[j*Channels*5 + 0*5 + 0] = B0[3*j + 0]; //b0 |
F[j*Channels*5 + 0*5 + 1] = B0[3*j + 1]; //b1 |
F[j*Channels*5 + 0*5 + 2] = B0[3*j + 2]; //b2 |
F[j*Channels*5 + 0*5 + 3] = A0[3*j + 1]; //a1 |
F[j*Channels*5 + 0*5 + 4] = A0[3*j + 2]; //a2 |
} |
// Lay out channel 1: |
for (vDSP_Length j = 0; j < Sections; ++j) |
{ |
F[j*Channels*5 + 1*5 + 0] = B1[3*j + 0]; //b0 |
F[j*Channels*5 + 1*5 + 1] = B1[3*j + 1]; //b1 |
F[j*Channels*5 + 1*5 + 2] = B1[3*j + 2]; //b2 |
F[j*Channels*5 + 1*5 + 3] = A1[3*j + 1]; //a1 |
F[j*Channels*5 + 1*5 + 4] = A1[3*j + 2]; //a2 |
} |
// vDSP_biquadm takes an array of pointers to each input/output channel. Since our data is |
// interleaved and it is set up so that channel 0 data is in Input[2*i] and channel 1 data is in |
// Input[2*i+1], i=0:1023, our pointer to channel 0 data is Input and our pointer to channel 1 data |
// is Input+1. The stride between samples of a given channel is 2. |
// Set up pointers to input and output pointers as an array: |
float *pInputs[2] = {Input, Input+1}; |
float *pOutputs[2] = {Output, Output+1}; |
// Construct a vDSP_biquadm_Setup object using the filters 'F', the number of sections 'Sections' and |
// the number of channels 'Channels': |
vDSP_biquadm_Setup Setup = vDSP_biquadm_CreateSetup(F, Sections, Channels); |
// We will process the input in three steps. |
// 1: Process a portion of the input under the original coefficients which we |
// just used to construct the filter. |
// 2: Set the targets to the new coefficients B1New and A1New. As noted |
// above, these will apply only to channel 1 and sections 1 and 2. |
// 3: Process the remaining input frames. The coefficients will iterate towards |
// the targets during processing. |
vDSP_Length NPart0 = 300; // Number of frames to process under the original coefficients. |
vDSP_Length NPart1 = N - NPart0; // Number of frames to process under the updated coefficients. |
// Execute the filter using the setup we just created 'Setup', the array of input pointers 'pInput', the |
// stride between samples (2), the array of output pointers 'pOutputs', the stride between output samples (2), |
// and the input sample length 'NPart0'. |
vDSP_biquadm(Setup, (const float**)pInputs, 2, pOutputs, 2, NPart0); |
// Now update the filter coefficients. The coefficients need to be laid out in the same |
// pattern as expected by CreateSetup. In practice we would already have the coefficients |
// laid out in this pattern for efficiency, but for this example we will re-use our 'F' |
// buffer to pass our new coefficients to SetCoefficientsDouble. |
// We also need some parameters to establish the set of channels and sections we are updating. |
vDSP_Length FirstSectionToUpdate = 1; |
vDSP_Length NumberOfSectionsToUpdate = 2; |
vDSP_Length FirstChannelToUpdate = 1; |
vDSP_Length NumberOfChannelsToUpdate = 1; |
// Note that the array of coefficients expected by SetCoefficientsDouble contains |
// only the updated coefficients, i.e. the original 'F' array passed to CreateSetup |
// began with coefficients for channel 0; this 'F' array will NOT begin with coefficients |
// for channel 0, but for channel 1. And only for the updated sections, sections 1 and 2. |
for (vDSP_Length j = 0; j < NumberOfSectionsToUpdate; ++j) |
{ |
F[j*NumberOfChannelsToUpdate*5 + 0*5 + 0] = B1New[3*j + 0]; //b0 |
F[j*NumberOfChannelsToUpdate*5 + 0*5 + 1] = B1New[3*j + 1]; //b1 |
F[j*NumberOfChannelsToUpdate*5 + 0*5 + 2] = B1New[3*j + 2]; //b2 |
F[j*NumberOfChannelsToUpdate*5 + 0*5 + 3] = A1New[3*j + 1]; //a1 |
F[j*NumberOfChannelsToUpdate*5 + 0*5 + 4] = A1New[3*j + 2]; //a2 |
} |
// Now update the filter |
vDSP_biquadm_SetTargetsDouble(Setup, F, rate, threshold, |
FirstSectionToUpdate, |
FirstChannelToUpdate, |
NumberOfSectionsToUpdate, |
NumberOfChannelsToUpdate); |
// And complete the processing. Minor detail here, we need to update |
// the pointers to point to the remaining frames |
pInputs[0] = Input + NPart0*2; |
pInputs[1] = pInputs[0] + 1; |
pOutputs[0] = Output + NPart0*2; |
pOutputs[1] = pOutputs[0] + 1; |
vDSP_biquadm(Setup, (const float**)pInputs, 2, pOutputs, 2, NPart1); |
// We're done with the filter so free any associated resources: |
vDSP_biquadm_DestroySetup(Setup); |
// Free our arrays: |
free(F); |
free(Input); |
free(Output); |
} |
Copyright © 2015 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2015-09-16