Implementing a DSP class that contains includes and namespaces (Vinne Falco DSPFilters)


#1

I would like to use the Vinne Falco DSP filters in my own JUCE project and would like to find out the best way of going about it.


Here is a link to the Github:  https://github.com/vinniefalco/DSPFilters


In the shared/DSPFilters/source folder, there are .cpp files for each filter type, such as Bessel and Butterworth.  There are #includes and namespaces at the top of the code.  For instance, at the top of the Bessel.cpp file there is:

#include "DspFilters/Common.h"
#include "DspFilters/Bessel.h"
#include "DspFilters/RootFinder.h"

namespace Dsp {
namespace Bessel {

..and then the functions are below.


I go over to the Introjucer and I see Header Search Paths, but I am unsure of exactly what to do to keep with the JUCE workflow and keep everything where it will work multiplatform.  

I've used DSP code, contained in a class which I use from PluginProcessor, however this is the first time I am trying to use something that has its' own dependencies and I want to make sure I go about it in the best way.

Please, help me to know the very best ways to implement the Vinnie Falco DSP Filters using JUCE. 


#2

Hi Gebre, I can spend a bit of time to help you with this on the weekend if you're still stuck.


#3

I’d really appreciate that. Whatever is convenient for you – a post here, phone, Skype…


#4

There a couple of ways to do this, but perhaps the simplest way is as follows:

  • For each configuration for each IDE target you need to add the path to DSPFilters/shared/DSPFilters/include in your header search paths
    <ul>
    	<li><span>​Remember these paths can be different for different targets (e.g. location might vary between&nbsp;Mac and&nbsp;Windows)</span></li>
    </ul>
    </li>
    <li><span>Add the DspFilter source files to your project - s<span>implest way is to&nbsp;create/add the file I give you below&nbsp;to your project (DspFilters.cpp)</span></span></li>
    <li><span>Lastly, include the Dsp.h header file in your code (<span>​</span><span>#include "DspFilters/Dsp.h")</span></span></li>
    

 

Here's the code for DspFilters.cpp:

//
// Add this as a file to your Introjucer project as an easy way of including all
// the source files for DspFilters
//
// The path to the DSPFilters include directory must be defined in your header search paths
// e.g. /Develop/DSPFilters/shared/DSPFilters/include
//
// This lets you include Dsp.h in your code (#include "DspFilters/Dsp.h") but also enables
// the use of relative paths to the cpp files below
//
// Including the JUCE headers fixes a problem with std::max in RootFinder.cpp and also allows
// us to disable Visual Studio warnings about unreachable code in Param.cpp
#include "../JuceLibraryCode/JuceHeader.h"

// DspFilter Library Source
#include    "../source/Bessel.cpp"
#include    "../source/Biquad.cpp"
#include    "../source/Butterworth.cpp"
#include    "../source/Cascade.cpp"
#include    "../source/ChebyshevI.cpp"
#include    "../source/ChebyshevII.cpp"
#include    "../source/Custom.cpp"
#include    "../source/Design.cpp"
#include    "../source/Documentation.cpp"
#include    "../source/Elliptic.cpp"
#include    "../source/Filter.cpp"
#include    "../source/Legendre.cpp"
#if JUCE_MSVC
#pragma warning (push, 0) 
#pragma warning (disable: 4702)
#endif
    #include    "../source/Param.cpp"
#if JUCE_MSVC
#pragma warning(pop)
#endif
#include    "../source/PoleFilter.cpp"
#include    "../source/RBJ.cpp"
#include    "../source/RootFinder.cpp"
#include    "../source/State.cpp"

#5

Just in case you want to use a high shelf Biquad from DspFilters, I should point out there is an error in the coefficients.

Vinnie never got around to fixing this, so I run a local branch of my own with the following changes to HighShelf::setup which can be found in shared\DSPFilters\source\RBJ.cpp, lines 159-164:

  double b0 =    A*( (A+1) + (A-1)*cs + sq );
  double b1 = -2*A*( (A-1) + (A+1)*cs );
  double b2 =    A*( (A+1) + (A-1)*cs - sq );
  double a0 =        (A+1) - (A-1)*cs + sq;
  double a1 =    2*( (A-1) - (A+1)*cs );
  double a2 =        (A+1) - (A-1)*cs - sq;

#6

Hi Andrew, I greatly appreciate your response, and I was able to get closer to implementing the Vinne Falco DSP Filters.  I now am able to call the library from processBlock.  I wanted to spend due time trying things out myself before getting back in touch with you, or posting on the forum about this.

While DSPFilters does now seem properly included, I have not been successful implmenting the functions, and I've spent more time than I would like to admit.  During my searching, I have seen many posts where people have had this problem over the years.  I even found a post where Vinnie Falco said that when he had some extra time, he would try to improve the way these functions are called, although I'm not sure he was referring to the specific problem I'm having.  Of course, kudos to him for building such an incredible library.

I try this code and I can hear some lowpass being applied, however it has alot of distortion in the signal:


void RevealAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    float* audioData[2];
    int numSamples = buffer.getNumSamples();

    audioData[0] = buffer.getWritePointer(0);
    audioData[1] = buffer.getWritePointer(1);
        
    // create a 2-channel RBJ Low Pass with parameter smoothing
    // and apply it to the audio data
       {
            // "1024" is the number of samples over which to fade parameter changes
            Dsp::Filter* f = new Dsp::SmoothedFilterDesign <Dsp::RBJ::Design::LowPass, 2> (1024);
            Dsp::Params params;
            params[0] = 44100; // sample rate
            params[1] = 4000; // cutoff frequency
            params[2] = 1.25; // Q
            f->setParams (params);
            f->process (numSamples, audioData);
        }            
}

My problem seems to come down to what the DSPFilters process function wants.  The DSPFilters documentation example lists the following as the way to set up audioData to be accepted into the f->process (numSamples, audioData) function:


void UsageExamples ()
{
  // create a two channel audio buffer
  int numSamples = 2000;
  float* audioData[2];
  audioData[0] = new float[numSamples];
  audioData[1] = new float[numSamples];

Seeing that made me think I needed to create a multidimensional array to get audioData set up for the process function.  The line audioData[0] = new float[numSamples]; would seem to create a new array starting at audioData[0][0] with each sample value of channel 0 being held at audioData[0][1] and so on.

I tried that, together with an example I saw in the DSPFilters code, and got something that created even more distortion:


void RevealAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    float* audioData[2];
    int numSamples = buffer.getNumSamples();

    //Make multidimensional array to contain all the samples
    audioData[0] = new float[numSamples];
    audioData[1] = new float[numSamples];
    audioData[0][0] = buffer.getWritePointer(0)[0];
    audioData[1][0] = buffer.getWritePointer(1)[0];
        
    // create a 2-channel RBJ Low Pass with parameter smoothing
    // and apply it to the audio data
       {
            // "1024" is the number of samples over which to fade parameter changes
            Dsp::Filter* f = new Dsp::SmoothedFilterDesign <Dsp::RBJ::Design::LowPass, 2> (1024);
            Dsp::Params params;
            params[0] = 44100; // sample rate
            params[1] = 4000; // cutoff frequency
            params[2] = 1.25; // Q
            f->setParams (params);
            f->process (numSamples, audioData);
        }            
}

Excuse the length of my post and all the pasting -- I wanted to be sure to state my problem clearly and so that if someone else is searching any part of this, this forum post would come up.


#7

I'm not sure what your problem is, but I did notice that your code is allocating memory inside the audio callback, and it should generally not do that. Timur (from JUCE) actually discussed this in his CppCon talk, which is on YouTube. Maybe something like that example doesn't matter though.  I'm really not sure why your even creating those arrays to hold the audio data in the first place. Could you describe that? The audio data is already passed in, so you don't need to create your own channels with buffers. Idk, I could just be misunderstanding this code. I've never tried Vinnie's filters, but I'll have to try them soon and maybe I can help and give an example. 


#8

Hi Gebre,

Jordan is right when he says you shouldn't be allocating memory inside processBlock, but there are a few other things you need to change as well.

What you've done is allocate the filter on the stack – it’s in scope within the braces in the processBlock() function and then it’s discarded just after you’ve called process on the filter itself. This is a problem because IIR filters rely on maintaining the state from previous calls. Also, there’s really no need to redesign the filter for each call to processBlock(), this can be done in your plugin constructor instead.

So what you need to do is define your filter as a member variable in the header file, set it up in the plugin constructor, and then do the processing in processBlock(). It’s also a good idea to define the filter pointer as a ScopedPointer so that JUCE tidies up after you.

The other thing to think about is the filter parameters structure. You can find out the sampleRate in prepareToPlay() – so that’s the best place to set the first argument. However if you plan on letting users vary the filter frequency or Q, then you need to update those parameters in processBlock(). Again, set up your parameters structure as a member variable (also notice that I referred to f1 and params1. If you have more than one filter, then you don’t want to lose track of which is which).

// PluginProcessor.h

private:
       ScopedPointer<Dsp::Filter> f1;
       Dsp::Params params1;

 


// PluginProcessor.cpp

// Constructor
DspFiltersDemoAudioProcessor::DspFiltersDemoAudioProcessor()
{
       // "1024" is the number of samples over which to fade parameter changes
       f1 = new Dsp::SmoothedFilterDesign <Dsp::RBJ::Design::LowPass, 2> (1024);
}

// prepareToPlay()
void DspFiltersDemoAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    // Use this method as the place to do any pre-playback
    // initialisation that you need..
    params1[0] = sampleRate; // sample rate
}

// processBlock()
void DspFiltersDemoAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    // Clear excess output channels
    for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
       buffer.clear (i, 0, buffer.getNumSamples());

    // If these were static you could just do it once in prepareToPlay(), but you're usually going to
    // want to hook these up to parameters so the user can change them at runtime
    params1[1] = 4000; // cutoff frequency
    params1[2] = 1.25; // Q
    f1->setParams (params1);


    // Check that there are at least two channels before we process our 2 channel filter
    jassert (getNumInputChannels() >= 2);

    // Notice that buffer provides us a nice way to get the array of audio data
    f1->process (buffer.getNumSamples(), buffer.getArrayOfWritePointers());
}

Regards,
Andrew


Using DSPFilter by Vinne
#9

This worked perfectly.

I had it in my head that things related to processing needed to happen within processBlock.  I was confused, as I did understand the filter taps would need a continuous flow, and that what I had would reset with each block.

Also, I was trying to force the DSPFilters documentation.cpp example to work as it was presented, which is not how it needs to be done for a plugin.

This makes perfect sense, and makes me realize I need to think outside the processBlock.

Big thanks for your help!