SynthLab SDK
SynthFilter

The SynthLab::SynthFilter object processes audio samples from its AudioBuffer input array to its AudioBuffer output array. It also reads its modulation input array during the update( ) phase to allow parameter modulation.

You will need to add the following files for the SynthFilter

Follow the same pattern and start with the class documentation. The SynthFilter:

  • uses a FilterParameters structure for control, both GUI and programatically
  • reads values from its own AudioBuffer input
  • renders values into its own AudioBuffer output
  • includes 2 filter cores, virtual analog and biquad

We'll choose the VACore object (index = 0, see docs for SynthFilter class) and jump to its documentation. There we can find the module strings and mod knob assignments. The ModuleStrings are the two implemented filter types. We'll choose the 2nd order state-variable lowpass filter (SVFLPF2)

Module Strings, zero-indexed for your GUI Control:

  • No_Filter, LPF1, HPF1, APF1, SVF_LP, SVF_HP, SVF_BP, SVF_BS, Korg35_LP, Korg35_HP, Moog_LP1, Moog_LP2, Moog_LP3, Moog_LP4, Diode_LP4

We will choose the SVF_LP, which as a zero-indexed string entry is [4]. Next, look at the documentation to find the output data (render) location

From the docs on VAFilterCore:

  • reads from the input buffer
  • renders into the output buffer
  • renders one block of audio per render cycle

Processing AudioBuffers

The SynthFilter processes audio samples rather than rendering them. For the sample code here, we are going to use the wavetable oscillator's output buffer as input samples for the filter. To do this, you will use a helper function form synthfunctions.h that does fast memcpy( ) operations on bulk memory chunks to transfer the audio data from the oscillator output to the filter input. These functions take the following channel count constants:

  • MONO_TO_MONO: left channel to left channel
  • MONO_TO_STEREO: left channel to left and right channels (dual mono)
  • STEREO_TO_STEREO: left to left and right to right channels

They use the following constants that you can also use for your buffer access code for better readability:

  • LEFT_CHANNEL: array slot [0] in the buffer
  • RIGHT_CHANNEL: array slot [1] in the buffer
// --- one of several buffer copy functions
void copyOutputToInput(std::shared_ptr<AudioBuffer> source, /* the AudioBuffer output */
std::shared_ptr<AudioBuffer> destination, /* the AudioBuffer input */
uint32_t channel, /* channel code */
uint32_t samplesToCopy) /* size of copy*/
//

There are more of these fast-copy helper functions:

Function Copies Block
copyOutputToInput from an AudioBuffer output to an AudioBuffer input
copyInputToOutput from an AudioBuffer input to an AudioBuffer output
copyOutputToOutput from an AudioBuffer output to an AudioBuffer output
copyOutputToOutput from an AudioBuffer output to a SynthProcessInfo output (see SynthVoice)
copyAudioBufferOutputToSynthOutput from an AudioBuffer output to a SynthProcessInfo output (see SynthEngine)
copyBufferToInput same as copyOutputToInput, used with mix buffers and other temporary buffers

Note that the SynthFilter documentation also shows you how to create the object in standalone mode, we can use this code verbatim because we will setup a blocksize of 64 for this filter.

// --- standalone construction, block size = 64
SynthFilter(nullptr, nullptr, 64);
//

So, we will do the following:

  1. create the filter and pass nullptr for the MIDI input and parameters arguments
  2. reset it with the sample rate
  3. select the VAFilterCore (index = 0 – it is the default)
  4. create a MIDI event and call the note-on handler
  5. use getParameters: set the filter to SVF LP ([4], set the filter fc = 880.0 Hz and Q = 5.0, output amplitude = +2.0 dB
  6. transfer audio from the wavetable oscillator (in the previous section) to the filter
  7. call the render function to process the data (send that to your plugin framework output buffer)
  8. call the note-off handler when note event is done
#include "synthfilter.h"
// --- create a LFO via shared pointer; here is the definition
std::unique_ptr<SynthLab::SynthFilter> filter = nullptr;
// --- here is the creation
filter.reset(new SynthLab::SynthFilter(nullptr, /* MIDI input data */
nullptr, /* filter parameters */
64)); /* block size */
// --- initialize
filter->reset(44100.0); //<- get fs from your framework
// --- select core #0: VA Filter core (no harm in selecting it, we know it is default)
filter->selectModuleCore(0);
// --- get the parameter struct pointer;
// it was synthesized when we passed a nullptr into the constructor
std::shared_ptr<SynthLab::FilterParameters> filterParameters = filter->getParameters();
if (filterParameters) // should never fail
{
// --- set the variables
filterParameters->filterIndex = 4; // SVF LP
filterParameters->fc = 880.0;
filterParameters->Q = 5;
filterParameters->filterOutputGain_dB = +2.0;
// --- call the update function (only needs to be done once per render cycle)
filter->update();
}
// ---

Now, in response to a MIDI note-on message, we setup a MIDIEvent structure (NOTE: we added the call to the pitch calculaton prior to the note-on function call):

// --- prepare a MIDI event for note-on
midiEvent.midiNoteNumber = 60;
midiEvent.midiPitch = SynthLab::midiNoteNumberToOscFrequency(midiEvent.midiNoteNumber); //< PITCH Calculation
midiEvent.midiNoteVelocity = 127;
// --- send note event
wtOsc->doNoteOn(midiEvent);
// --- filter note on handler
filter->doNoteOn(midiEvent);
// ---

Now you render values from the oscillator and send them to the filter. Once rendered, you may send the block of data to your plugin framework's output buffers;

// --- render OSC output
wtOsc->render(64);
// --- transfer information from OSC output to filter input
SynthLab::copyOutputToInput(wtOsc->getAudioBuffers(), /* FROM osc output */
filter->getAudioBuffers(), /* TO filter input */
SynthLab::STEREO_TO_STEREO, /* stereo copy */
64); /* block size in frames */
// --- render the filter
filter->render(64); // blocksize is 64
// --- get pointers to output buffers
float* leftOutBuffer = filter->getAudioBuffer()->getOutputBuffer(SynthLab::LEFT_CHANNEL);
float* rightOutBuffer = filter->getAudioBuffer()->getOutputBuffer(SynthLab::RIGHT_CHANNEL);
// --- loop over buffers to access samples; loop maximum (64) set manually here
for (uint32_t i = 0; i < 64; i++)
{
float leftSample = leftOutBuffer[i];
float rightSample = rightOutBuffer[i];
// --- SEND THESE OUTPUTS TO YOUR FRAMEWORK or play with them
}
// ---

From here you can keep rendering until the note-off message arrives.

// --- prepare a MIDI event for note-off
midiEvent.midiNoteNumber = 60; // <- get from your framework
midiEvent.midiPitch = SynthLab::midiNoteNumberToOscFrequency(midiEvent.midiNoteNumber); //< PITCH Calculation
midiEvent.midiNoteVelocity = 0; // <- get from your framework
// --- send note event
filter->doNoteOff(midiEvent);
// ---

Now, experiment with changing the filter parameters (see the FilterParameter documentation to see what you can change). Also, try experimenting with the mod knobs (see SynthLFO tutorial above)


synthlab_4.png