SynthLab SDK
Oscillator

The oscillator objects are fundamentally identical except in their rendering operations.

There are two oscillators that do not require any additional files or support:

  • VAOscillator: virtual analog oscillator whose synthesis method is algorithmic
  • KSOscillator: Karplus-Strong plucked string modeling, does not require tables or samples

There are two oscillators that do require additional files:

  • WTOscillator: uses pre-built table files, and the wavetable database to register, store and retrieve them
  • PCMOscillator: uses WAV files for storing samples, and the PCM sample database to register, store and retrieve them

In this section, we'll work with the WTOscillator object, and using the other oscillator objects will work the same way; when in stanalone mode, there is really nothing to do but let the object maintain its own database and data-accesses.

The SynthLab::WTOscillator object renders audio samples into its AudioBuffer output array. Under normal synth operation you will want to render and process blocks of audio data rather than individual samples (see the SynthBook for more reasoning and ramifiations of this). We will setup this standalone object to render audio blocks of 64 samples per channel (called 64 "frames") and you can test this with your plugin framework by piping the output data samples to your framework's audio output buffer.

You will need to add the following files for the WTOscillator (it includes 4 corse plus a bonus core)

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

  • uses a WTOscParameters structure for control, both GUI and programatically
  • renders values into its own AudioBuffer object
  • includes 5 WT Oscillator cores (only four are loaded): ClassicWtCore, DrumWTCore, FourierWTCore, MorphWTCore, and SFXWTcore

We'll choose the SynthLab::FourierWTCore object (index = 2, see docs for WTOscillator class) and jump to its documentation. There we can find the module strings and mod knob assignments. The ModuleStrings are the two Fourier synthesized waveforms:

Module Strings, zero-indexed for your GUI Control:

  • sinewave, parabola

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

From the docs on FourierWTCore:

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

Note that the WTOscillator 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 oscillator.

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

So, we will do the following:

  1. create the Oscillator and pass nullptr for the MIDI input, wavetable database, and parameters arguments
  2. reset it with the sample rate
  3. select the FourierWTCore (index = 2)
  4. use getParameters: set the waveform index (parabola = 1) and outut amplitude = -3dB
  5. create a MIDI event and call the note-on handler
  6. call the render function to generate a block of rendered data (send that to your plugin framework output buffer)
  7. call the note-off handler when note event is done
#include "wtoscillator.h"
// --- create a LFO via shared pointer; here is the definition
std::unique_ptr<SynthLab::WTOscillator> wtOsc = nullptr;
// --- here is the creation
wtOsc.reset(new SynthLab::WTOscillator(nullptr, /* MIDI input data */
nullptr, /* WT parameters */
nullptr, /* wavetable database */
64)); /* audio block size (64 samples per channel)*/
// --- initialize
wtOsc->reset(44100.0); //<- get fs from your framework
// --- select core #2: FourierWTCore
wtOsc->selectModuleCore(2);
// --- get the parameter struct pointer;
// it was synthesized when we passed a nullptr into the constructor
std::shared_ptr<SynthLab::WTOscParameters> wtoParameters = wtOsc->getParameters();
if (wtoParameters) // should never fail
{
// --- set the variables
wtoParameters->waveIndex = 1; // FourierWTCore parabola
wtoParameters->outputAmplitude_dB = -3.0;
// --- call the update function (only needs to be done once per render cycle)
wtOsc->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);
// ---

At this point, we can start to render values from the oscillator. This shows how to grab the left and right samples from the oscillator's output array; notic the use of the constants:

  • LEFT_CHANNEL: array slot [0] in the buffer
  • RIGHT_CHANNEL: array slot [1] in the buffer
// --- render OSC output
wtOsc->render(64); // render a block of 64
// --- get pointers to output buffers
float* leftOutBuffer = wtOsc->getAudioBuffer()->getOutputBuffer(SynthLab::LEFT_CHANNEL);
float* rightOutBuffer = wtOsc->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. Technically, the note-off handler can access the MIDI note number, but in the case of the wavetable oscillator, this value is not used. In fact, most of the objects have note-off handers that are empty. The EGs are a notable exception.

// --- 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
wtOsc->doNoteOff(midiEvent);
// ---

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


synthlab_4.png