SynthLab SDK
|
Copy the synthmodule_nocores.h and synthmodule_nocores.cpp files into your project. You will probably want to rename them to something sensible. Open them in your compiler or a text editor and change the occurrances of SynthModuleNoCores to FilterModule or anything else that makes sense. Here I am calling the object FilterModule.
The synthbase code contains a built-in object called BQAudioFilter that implements an augmented biquad structure that is described and used in detail in my FX book. We will use this object here to implement two filters that are in neither the synth book nor the sample project code: Vicanek's Analog Matched 2nd order lowpass and 2nd order bandpass filters with the "loose fit" algorithms, giving us two filter types that will become the module strings. We will use the parameter structure to test the object and will implement one mod knob control for filter keytrack modulation, that is explained in the next section.
Filter Module Specifications:
Figure 1 shows the frequency responses of the Vicanek loose and tight fit filters compared to the bilinear transform versions.
Figure 1: Vicanek's frequency responses for LPF (left) and BPF (right) from "Designing Audio Effects Plugins in C++ 2nd Ed." by Will Pirkle
The BQAudioFilter object is a thin wrapper for the augmented biquad structure from my FX book. It accepts a structure of coefficients and then runs the augmented biquad routine using the Transposed Canonical form. This object does not need to know about the sample rate or type of filter, it only runs the biquad routine. We will need two of these objects, one for each channel for stereo operation.
We can design this module in the same manner as the ModuleCores by starting with the .h file, adding members, and then modifying the five operational phases in the .cpp file, starting with the constructor and working down through the rest of the functions thinking about those five phases and adding the code to implement them.
We'll need to add two of the BQAudioFilter object to the SynthModule along with a couple of helper members and enumerations. Add this to you class declaration:
We will go through the operational phase methods one at a time, just as with the other objects and following the same paradigm as the synth book. We can start with the constructor and add the support code to create the audio buffers and set the module strings and mod knob labels.
constructor
Here's the code for the constructor; after creating the parameters and buffers, you see the code that is nearly identical to the ModuleCores for setting the module strings and mod knob labels. I am usign the enums to make the indexing a bit easier to read.
reset()
For the reset function, we only need to reset the filters which flushes the state registers. The BQAudioFilter is a simple object that is not a SynthModule and its reset() function does not require a sample rate. We do need to store the sample rate for the update() function later.
update()
The main purpose for the update() function is to recalculate the biquadratic coefficients based on the current filter parameters, fc and Q. We will add code later to modulate the filter cutoff frequency. The code here examines the selected filter index and recalculates the coefficients based on the curoff frequency, then sends that information to the filters. This code is inefficient because it does not check first to see if the parameters have been changed; you might consider adding that code as an exercise. The theory and coefficient calculations may be found in my FX book.
render()
To render the filters, we need to pick up the audio input samples from the input buffers, process them through the filters, and write the outputs into the output buffer over the requested block of audio data. We've already seen much of this code in the previous sections and here you can see how to access the audio input buffers. And, notice how the update() function is called directly from render() to force the sequence of operations of update -> render.
doNoteOn() and doNoteOff()
We only need to alter the doNoteOn() method to pick up the MIDI pitch of the note played. This will be used for key track modulation in the next section. There is nothing to do for the note-off handler.
Testing this object will be the same as the previous objects and you may use your MinSynth voice object, or test it independently following the same familiar programmign pattern.
Create the Filter Instance
Update the object to test Filter [0]
Here we will set the filterIndex to 0 for the "Vic LPF2" filter and then set the fc = 500.0 Hz and Q = 10.0 producing a very resonant filter whose affect on the oscillator will be easy to hear.
Send a Note-On Event
You can get this from a live MIDI source or fake it to test as done here.
Render the Filter
Render the filters and loop over the audio block to process input to output. Remember that we set the blockSize variable at construction time. This code shows both the oscillator render and the transfer of data from oscillator to filter, prior to the filter render.
Test the filter and check to make sure it is functioning correctly. Next, change the filter type to the Vic BPF2 variety and play with the fc and Q values a bit.
Update the object for Filter [1]
Testing Filter [1]
Test the filter to verify operation. Once satisified, move to the next section and add the cutoff modulation code.