Daisy - Biquad HPF Test

Hi - here’s my Biquad HPF implementation for the petal. However I am not getting any output… Can someone identify why?
Edit: Actually it works for some values of fc like 6.5kHz - however the audio does not sound high-passed.

#include "daisy_petal.h"
#include "daisysp.h"

using namespace daisy;
using namespace daisysp;

DaisyPetal hw;

// High-pass filter coefficients
float h_a0, h_a1, h_a2, h_b1, h_b2;

// Filter state variables
float h_z1 = 0.0f, h_z2 = 0.0f;

// Function to initialize high-pass filter coefficients
void init_high_pass(float sampleRate, float fc, float Q, float gain_dB)
{
    float K = tan(M_PI * fc / sampleRate);
    float norm = 1 / (1 + K / Q + K * K);
    float V0 = pow(10, gain_dB / 40.0);

    h_a0 = K * K * norm * V0;
    h_a1 = -2 * h_a0;
    h_a2 = h_a0;
    h_b1 = 2 * (K * K - 1) * norm;
    h_b2 = (1 - K / Q + K * K) * norm;
}

// High-pass filter function
float high_pass(float inSample)
{
    float outSampleF = h_a0 * inSample + h_a1 * h_z1 + h_a2 * h_z2 - h_b1 * h_z1 - h_b2 * h_z2;
    h_z2 = h_z1;
    h_z1 = inSample;
    return outSampleF;
}

void AudioCallback(AudioHandle::InputBuffer  in,
                   AudioHandle::OutputBuffer out,
                   size_t                    size)
{
    hw.ProcessAllControls();
    for(size_t i = 0; i < size; i++)
    {
        // Apply high-pass filter
        out[0][i] = high_pass(in[0][i]);
    }
}

int main(void)
{
    // Initialize high-pass filter coefficients
    init_high_pass(48000, 10729, 0.7071, 6);

    hw.Init();
    hw.SetAudioBlockSize(4); // number of samples handled per callback
    hw.SetAudioSampleRate(SaiHandle::Config::SampleRate::SAI_48KHZ);
    hw.StartAdc();
    hw.StartAudio(AudioCallback);
    while(1) {}
}

you are processing both channels with the same single filter, you need 2 separate filters for left and right, else you’ll be effectively running the filter at double the sample rate.

you need to code something like this:

   for(int i = 0; i < size; i += 2)
    {
    out[i+0] = high_pass1(in[i+0]);;
    out[i+1] = high_pass2(in[i+1]);
    }

Edited: i deleted an incorrect response here.

I’m using left/mono only. What is the proper way of accessing ins and outs as the petal template does it a bit differently like so:

for(size_t i = 0; i < size; i++)
    {
        out[0][i] = in[0][i];
        out[1][i] = in[1][i];
    }

There are two types of buffer available, AudioBuffer and InterleavedAudioBuffer. They are indexed differently.

@Shabtronic ‘s reply showed how you would index L and R sides of an Interleaved stereo input. Your code is using a buffer which isn’t interleaved.

But I don’t have any idea why the filter doesn’t work as you expect.

I think you’ve written down the filter equation incorrectly: the current output of a digital biquad filter is a linear combo of the current input, previous two inputs, and previous two outputs. I recommend the easier-to-read x (input waveform) and y (output waveform) notation, so that the filter equation becomes

y[n] = a0 x[n] + a1 x[n-1] + a2 x[n-2] - b1 y[n-1] - b2 y[n-2]

In your notation, h_z1 == x[n-1], h_z2 == x[n-2], and outSampleF == y[n], but you don’t have state variables for storing y[n-1] or y[n-2].

1 Like

Thanks for this! Are there any advantages/differences between using either or?

Oh you’re right - not sure why I forgot those. Will revise later today and report back. Cheers