DSP Question: Implementing simple Low Pass in processBlock


#1

Hi,

I’m very new to DSP programming in c++ and so I thought it would be a good idea, to implement a very simple P-T1 (first order low-pass).

The formula I’ve used is the following:
y[n] = y[n-1] + ( k * x[n-1] - y[n-1] ) * dt / T

with
x: input
y: output
k: gain factor
dt: 1/SampleRate
T: 1/(2pif)

I’ve implemented this filter in the processBlock of my audio plugin (I get the frequency from a slider with values between 0 and 1000. k is between 0 and 1):

// This is the place where you'd normally do the guts of your plugin's
// audio processing...
for (int channel = 0; channel < getNumInputChannels(); ++channel)
{
        float* channelData = buffer.getSampleData (channel);
        float out = 0;

        // ..do something to the data...
		for (int i = 0; i < buffer.getNumSamples(); ++i)
		{
			// P_T1 lowpass:
			channelData[i] = out + (k * channelData[i] - out) * 2.0 * 3.14 * (double) frequency / (double) SampleRate;			
			out = channelData[i];
		}
}

The filter works, but there’s a very ugly crackling on the sound. The crackling is getting worse, the lower the frequency is.
Is this maybe the wrongest possible way to implement dsp stuff, or did I miss anything else?

Would be great if someone could help me.

Best,
Philipp.


#2

You know there’s an IIRFilter class that’ll do a low-pass for you?


#3

Yes, I know. But since I want to lern to implement dsp algorithms, I thougt it would be a good exercise to do this by myself…


#4

You’re reseting the buffer (out) every block. Instead of a local variable use an AudioProcessor member as buffer.

Chris


#5

Pi is way too truncated. 3.14 isn’t precise enough for the calculations, use the version that’s in Juce. It’s “double_Pi” or “float_Pi” in juce_mathsfunctions.h.

You probably shouldn’t need to cast to (double) for frequency or SampleRate either.


#6

Hi,

thanks a lot for your replies!

[quote=“ckk”]You’re reseting the buffer (out) every block. Instead of a local variable use an AudioProcessor member as buffer.

Chris[/quote]

I’ve already tried this: I declared “out” in PluginProcessor.h and resetted it before the loops. But the result is the same. The filtered sound is crackling.

[quote=“Anima”]Pi is way too truncated. 3.14 isn’t precise enough for the calculations, use the version that’s in Juce. It’s “double_Pi” or “float_Pi” in juce_mathsfunctions.h.

You probably shouldn’t need to cast to (double) for frequency or SampleRate either.[/quote]

Maybe Pi is too truncated, but I think that’s not the reason for the crackling. In this case, I use pi to convert the angular frequency into the cut-off frequency. (w = 2pif). So in the worst case, the cut-off frequency is imprecise.

By the way, I’ve tested the plugin with both Cubase and The Juce Plugin Host.

Best,
Philipp.


#7

Yeah the crackling is probably the reason ckk outlined but I was just saying that anyways.

You could try smoothing your values from the slider also.


#8

[quote=“Anima”]

You could try smoothing your values from the slider also.[/quote]

Yes, I’ve already thought about this but I haven’t found any documentation or examples yet. How is this done in Juce?
Furthermore, it’s strange, that the juce audio plugin demo doesn’t have problems with the slider handling… Afaik, the sliders are not smoothed here, too?!

Edit: It’s also crackling, when I’m using constant values for the frequency, so slider smoothing is not the problem…


#9

ckk’s answer was correct, but you’re misunderstanding it. You need to maintain your filter state between callbacks, not start from 0 each time you process a block.


#10

Okay, thanks. I think I got it… I’ve made another AudioSampleBuffer (like the delayBuffer in the demo plugin), where I store the values and now it works.

But it only works, when I’m setting up default values greater than zero in the constructor of the AudioProcessor. What’s the reason for this?


#11

You shouldn’t need an entire buffer. You just need to store one value between callbacks. The problem is that when a new block is called for, you don’t have access to the last sample of the last buffer. So if your filter needed the last two samples, you’d need to store another variable (y[n-2]) as well as the y[n-1] sample.

Class Filter : AudioProcessor
{
....
    void prepareToPlay()
    {
         lastOut = 0.0;
    }

    void processBlock( AudioSampleBuffer buffer.... )
    {
        ... your algorithm ...

        lastOut = channelData[i];
    }
private:
    double lastOut;
};

#12

DSPFiltersDemo is a perfectly working example of implementing a low pass (or any type) of filter, which works in processblock, allows time-varying parameters, and has no clicks.


#13

[quote=“Anima”]You shouldn’t need an entire buffer. You just need to store one value between callbacks. The problem is that when a new block is called for, you don’t have access to the last sample of the last buffer. So if your filter needed the last two samples, you’d need to store another variable (y[n-2]) as well as the y[n-1] sample.

[code]
Class Filter : AudioProcessor
{

void prepareToPlay()
{
lastOut = 0.0;
}

void processBlock( AudioSampleBuffer buffer.... )
{
    ... your algorithm ...

    lastOut = channelData[i];
}

private:
double lastOut;
};
[/code][/quote]

Yes, that’s exactly what I’ve done. But it crackles anyway.
When I store the values in a new AudioSampleBuffer I’ve got no crackling but the filter doesn’t work correctly. It lets through frequencies of about 460 Hz.

Edit: I think I’ve got the problem. My plugin wants 2 inputs but I’ve connected only one input to the host. The crackling is only on the second output channel. So I have to create 2 lastOuts… one where I store the values of channel 1 and one for channel 2. Logical… Now it works.


#14

hey all

i really tried my best (will keep on trying) i can't really figure out what i did wrongim having the same problem here with the crackles as

i change the Freq

The filter is working fine just the output is 2 cracky


void EarthhfilterAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
    lastout = 0;
}
void EarthhfilterAudioProcessor::releaseResources()
{
    // When playback stops, you can use this as an opportunity to free up any
    // spare memory, etc.
}
void EarthhfilterAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    for (int channel = 0; channel < getNumInputChannels(); ++channel)
    {
        float* channelData = buffer.getSampleData (channel);
        double r, f, c, a1, a2, a3, b1, b2, b3, pi;
        r = f = c = a1 = a2 = a3 = b1 = b2 = b3 = 0;
        pi = 3.14;
        // ..do something to the data...
        
        for (int i = 0; i < buffer.getNumSamples(); i++)
        {
            
            double sample_rate = getSampleRate();
            float in1 = channelData[i];
            float in2 = channelData[i - 1];
            float in3 = channelData[i - 2];
            float out1 = channelData[i - 1];
            float out2 = channelData[i - 2];
            r = Rez;
            f = sample_rate*Freq / 2;
            c = 1.0 / tan(pi * f / sample_rate);
            a1 = 1.0 / (1.0 + r * c + c * c);
            a2 = 2 * a1;
            a3 = a1;
            b1 = 2.0 * (1.0 - c*c) * a1;
            b2 = (1.0 - r * c + c * c) * a1;
            //    The filter algo :
            channelData[i] = (a1 * in1 + a2 * in2 + a3 * in3 - b1*out1 - b2*out2);
        
        }
    
    }
    // In case we have more outputs than inputs, we'll clear any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
    {
        buffer.clear (i, 0, buffer.getNumSamples());
    }
}


#15

float in2 = channelData[i - 1];
float in3 = channelData[i - 2];
float out1 = channelData[i - 1];
float out2 = channelData[i - 2];

You might want to think about what happens when i == 0 ?


#16

i'm trying to get this to work 

 

out(n) = a1 * in + a2 * in(n-1) + a3 * in(n-2) - b1*out(n-1) - b2*out(n-2) //the algoeithim

 

so far it does filter but it has ckracks

so i assum my processing aprotch is faul

so basiclly im trying to do this algorithm and the code is up there so if you could please help me solvethis

i will be greatfull

 

thnx

Avis

 


void EarthhfilterAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    for (int channel = 0; channel < getNumInputChannels(); ++channel)
    {
        float* channelData = buffer.getSampleData (channel);
        
        float r, f, c, a1, a2, a3, b1, b2, b3, pi;
        r = f = c = a1 = a2 = a3 = b1 = b2 = b3 = 0;
        pi = 3.14;
        // ..do something to the data...
        float sample_rate = getSampleRate();
        for (int i = 0; i < buffer.getNumSamples(); i++)
        {
            
            /*
            float in1 = channelData[i];
            float in2 = channelData[i-1];
            float in3 = channelData[i-2];
            float out1 = channelData[i-1];
            float out2 = channelData[i-2];
            float out;*/
            r = Rez;
            f = sample_rate*Freq / 2;
            c = 1.0 / tan(pi * f / sample_rate);
            a1 = 1.0 / (1.0 + r * c + c * c);
            a2 = 2 * a1;
            a3 = a1;
            b1 = 2.0 * (1.0 - c*c) * a1;
            b2 = (1.0 - r * c + c * c) * a1;
            //    The filter algo :
        channelData[i] = (a1 * in + a2 * in2 + a3 * in3 - b1*out1 - b2*out2);
        
        }
    
    }


#17

You access channelData[-2] and channelData[-1] for your first sample which of course is undefined. Also your in- and output is the same.

Try something like this (only first order and initialisation is missing, no denormalisation… ):

class MyFilter
{
public:
    float process(float in)
    {
        const float out = in * a0 + x1 * a1 - y1 * b1;
        y1 = out;
        x1 = in;
        return out;
    }

private:
    float a0, a1, b1;
    float x1, y1;

};

#18

so i did and it sounds a bit better , still i get some noises /// 

 


void EarthhfilterAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    for (int channel = 0; channel < getNumInputChannels(); ++channel)
    {
        float* channelData = buffer.getSampleData (channel);
        
        double r, f, c, a1, a2, a3, b1, b2, b3, pi;
        r = f = c = a1 = a2 = a3 = b1 = b2 = b3 = 0;
        pi = double_Pi;
        // ..do something to the data...
        double sample_rate = getSampleRate();
        float in, in1, in2 , out , out1, out2;
        in = in1 = in2 = out = out1 = out2 = 0;
            r = Rez;
            f = sample_rate*Freq / 2;
            c = 1.0 / tan(pi * f / sample_rate);
            a1 = 1.0 / (1.0 + r * c + c * c);
            a2 = 2 * a1;
            a3 = a1;
            b1 = 2.0 * (1.0 - c*c) * a1;
            b2 = (1.0 - r * c + c * c) * a1;
        for (int i = 0; i < buffer.getNumSamples(); i++)
        {
            
            in = channelData[i];
            
            //    The filter algo :out(n) = a1 * in + a2 * in(n-1) + a3 * in(n-2) - b1*out(n-1) - b2*out(n-2)
            out = (a1*in + a2*in1 + a3*in2 - b1*out1 - b2*out2);
            channelData[i] = out;
            
            in2 = in1;
            in1  = in;
            
            out2 = out1;
            out1 = out; 
            
        
        }
    
    }
    // In case we have more outputs than inputs, we'll clear any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
    {
        buffer.clear (i, 0, buffer.getNumSamples());
    }
}

 

 

is it a noisy algorithm ?? or is it me 

 


#19

You are resetting your filter memory for every audio block:

in = in1 = in2 = out = out1 = out2 = 0;

You will also need two variables for each of them to process stereo data (in1l, in1r…).

Generally, it is better to create a class that does the processing and call a processing function in that class. This way you don’t need to create lots of variables for states (in1, in2…) which will get confusing if you have more than a few filters. The filter coefficients (a0, a1…) also only need to be calculated when they’re changing.


#20

i did change the code again and got all the coef out of the process block 

now i have a new class and a new filter

this is the moog.h class ive made

 

 




#ifndef Moog_H_INCLUDED
#define Moog_H_INCLUDED




class Moog
{

public:
    Moog();
    ~Moog();
    float process(float In, float Cut, float r)
    {

        q = 1.0f - Cut;
        p = Cut + 0.8f * Cut * q;
        f = p + p - 1.0f;
        q = r * (1.0f + 0.5f * q * (1.0f - q + 5.6f * q * q));

        // Filter (in [-1.0...+1.0])
        In -= q * b4;                          //feedback
        t1 = b1;  b1 = (In + b0) * p - b1 * f;
        t2 = b2;  b2 = (b1 + t1) * p - b2 * f;
        t1 = b3;  b3 = (b2 + t2) * p - b3 * f;
        b4 = (b3 + t1) * p - b4 * f;
        b4 = b4 - b4 * b4 * b4 * 0.166667f;    //clipping
        b0 = In;
        // Lowpass  output:  b4
        // Highpass output:  in - b4;
        // Bandpass output:  3.0f * (b3 - b4);

        
        return 3.0f * (b3 - b4);
    }



private:
    float f, p, q;             //filter coefficients
    float b0, b1, b2, b3, b4;  //filter buffers (beware denormals!)
    float t1, t2;              //temporary buffers
    float out;
};
Moog::Moog()
{
    f = p = q = 0;             //filter coefficients
    b0 = b1 = b2 = b3 = b4 = 0;  //filter buffers (beware denormals!)
    t1 = t2 = 0;              //temporary buffers
    

}
Moog::~Moog()
{
}


#endif  // PLUGINPROCESSOR_H_INCLUDED

i send the values like this ///

 

void EarthhfilterAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    int numberOfChannels = getNumInputChannels();
    float rate = getSampleRate();
    Moog moog;
    
        if (numberOfChannels == 2)
        {

            float *DataL = buffer.getSampleData(0, 0);
            float *DataR = buffer.getSampleData(1, 0);


            for (int i = 0; i < buffer.getNumSamples(); i++)
            {
                
                DataL[i] = moog.process(DataL[i], Freq, Rez);
                DataR[i] = moog.process(DataL[i], Freq, Rez);
                lastL = DataL[i];
                lastR = DataR[i];
                
            }
        }
    
    if (numberOfChannels == 1)
    {
        

            float *DataL = buffer.getSampleData(0, 0);
            float *DataR = buffer.getSampleData(0, 0);


            for (int i = 0; i < buffer.getNumSamples(); i++)
            {
                DataL[i] = moog.process(DataL[i], Freq, Rez);
                DataR[i] = moog.process(DataL[i], Freq, Rez);
                lastL = DataL[i];
                lastR = DataR[i];

            }
       
    
    }

Freq and Rez are simple listeners that send values 0-1 in 0.1 scale

 

the algorithm is also very noisy

i tried to figure out how to use another buffer and couldn't understand