Bass Treble Mid Equaliser

Hi,

I have and audio program built in juce that loads and plays songs, I can increase or decrease the volume on the tracks using a slider.

I have searched the tutorials on here and checked out the demo’s on the projucer but i cant find anyting that explains how i would control the base, treble and mid using sliders.

I am very new to juce and have followed some of the examples to build a simple player that loads, plays and controls the volume but i have no idea where i would start to do the base, treble and mid. Any help is much appreciated.

This is typically done with IIR filters. JUCE has two IIR filter implementations, the one in the dsp module is the newer one. A Bass Middle Treble EQ is basically a chain of three IIR instances. To make a filter actually do something to the frequencies in the audio signal you need to apply filter coefficients that can be computed by filter design helper functions. A suitable choice for your application would be a low shelf for bass, a bell filter for mid and s high shelf for treble. Your controls will set the gain for the filter design, the other parameters should be constants.

This was a very broad overview, you might want to do some further research on filters on your own :slightly_smiling_face:

Thank you Very Much @PluginPenguin. This has been helpful. I now know where I need to start looking at.

I have managed to Setup 3 IIRCoefficients filters, high, Low and band. They all seem to operate independently, when u se my sliders it doesnt keep the previous filter settings. I have tried to pass the 3 filters into a MIxerAudioSource but this seems to speed up the track for some reason. I have search the forum and there are a few other post about this same issue. I am wondering why does this happen if you add 2 or more IIRAudioSource filter into a MixerAudioSource it speed up the track. I have been tryin to find the fix by using BufferingAudioSource but nothing i do seems to work. Does anybody have an Idea about this issue and know a fix?

From your description I’m not entirely sure how your implementation would look like. Could you share some code that shows how you try to use the filters?

Thanks in advance if you are able to help

#pragma once

#include <JuceHeader.h>

class Equaliser : public AudioSource
{
    public:

        Equaliser(AudioFormatManager& _formatManager);
        ~Equaliser();

        //==============================================================================

        void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override;

        void getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill) override;

        void releaseResources() override;

        //==============================================================================
        

        void loadURL(URL audioURL); 

        void start();

        void stop();  
      //===========================================================================

        void setLowPass(double hertz);
        void setHighPass(double hertz);
    
private:
    AudioFormatManager& formatManager;
    std::unique_ptr<AudioFormatReaderSource> readerSource;
    
    AudioTransportSource transportSource;

    IIRFilterAudioSource basefilterSource{ &transportSource, false };
    IIRFilterAudioSource treblefilterSource{ &transportSource, false };

   MixerAudioSource mixerSource;    

   ResamplingAudioSource resampleSource{ &mixerSource, false, 2 };    

};
#include "Equaliser.h"

Equaliser::Equaliser(AudioFormatManager& _formatManager)
: formatManager(_formatManager)
{
     mixerSource.addInputSource(&basefilterSource, false);
     mixerSource.addInputSource(&treblefilterSource, false);
}

Equaliser::~Equaliser()
{

}

//==============================================================================
void Equaliser::prepareToPlay(int samplesPerBlockExpected, double sampleRate)
{
    
    transportSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
    basefilterSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
    treblefilterSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
    mixerSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
    resampleSource.prepareToPlay(samplesPerBlockExpected, sampleRate);
    
}
void Equaliser::getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill)
{
    if (readerSource.get() == nullptr)
    {
        bufferToFill.clearActiveBufferRegion();
        return;
    }
    resampleSource.getNextAudioBlock(bufferToFill);

}

void Equaliser::releaseResources()
{
    basefilterSource.releaseResources();   
    treblefilterSource.releaseResources();   
    mixerSource.releaseResources();
    resampleSource.releaseResources();    
}

//==============================================================================

void Equaliser::loadURL(URL audioURL)
{
    auto* reader = formatManager.createReaderFor(audioURL.createInputStream(false));
    if (reader != nullptr) // good file!
    {
        std::unique_ptr<AudioFormatReaderSource> newSource
        (new AudioFormatReaderSource(reader, true));
        transportSource.setSource(
            newSource.get(), 0, nullptr, reader->sampleRate);
        readerSource.reset(newSource.release());
    }
}


void Equaliser::start()
{
    transportSource.start();     
}
void Equaliser::stop()
{
    transportSource.stop();
}

void Equaliser::setLowPass(double hertz)
{
    auto* reader = readerSource->getAudioFormatReader();

    basefilterSource.setCoefficients(IIRCoefficients::makeLowPass(reader->sampleRate, hertz));

}

void Equaliser::setHighPass(double hertz)
{
    auto* reader = readerSource->getAudioFormatReader();

    treblefilterSource.setCoefficients(IIRCoefficients::makeBandPass(reader->sampleRate, hertz));


}



Both your filter sources pull samples from the same source. What happens is you chop the signal into blocks and mix them together in the end. That way the signal advances twice as fast as it should.

For your equaliser you should send the signal serially through the filters, e.g. like this:

AudioTransportSource transportSource;

IIRFilterAudioSource basefilterSource   { &transportSource, false };
IIRFilterAudioSource treblefilterSource { &basefilterSource, false };

But eventually I would recommend to look into juce::dsp::ProcessorChain…

Thank you for the reply. I have now put the signal serially through the filters as @daniel suggested. If however I add both the basefilter and Treblefilter to the mixerSource i still the the track speeding up. So I tried to just add the Treblefilter source on it own through the mixer. It does work somewhat but doesnt really give the effect I am looking for. I would like to turn the Bass and Treble up and down on the source like you would in a car radio for example. Maybe it cant be done this way and Ill have to use the DSP modules, which i have looked into and spent many hours reading but just cant wrap my head around. I’ve looked at the tutorials and examples but find it hard to understrand how i would implement that. I thought something like bass and treble would be one of the simplest things to implement. But it seems I can’t apply 2 filters Coefficients to an audiosource and adjust them individually.

I forgot to mention:
When you route the signal serially through the both filter sources you should get rid of the mixer source as well. That is the point where the speeding up happens (because it pulls twice from the same source, just through different signal paths.
And it’s also the point where the unfiltered signal from one of the filters spills in.

OK thank you, I have it working how I want now, also what didnt help was one of the sliders had some old code in it that i was using when i was messing around with it to try and get it working and wasnt updating the source correctly. But now with the chaining in place that you suggested it all seem to be working. Brillaint thank you

You can follow what’s shown in this tutorial and incorporate it into your plugin:

It’s a 3-band EQ using juce::dsp::IIRFilter.
The code is available here: GitHub - matkatmusic/SimpleEQ: The code for the SimpleEQ C++ Plugin Project featured on FreeCodeCamp YT channel