SynthLab SDK
The CoreProcData Structure

Look at the class definition for th ModuleCore to find the five functions that implement the five operational phases of all sytnth components. All functions take the same argument, a reference to a CoreProcData structure. The owning SynthModule prepares this structure in its constructor, and passes it to the cores when it calls their functions.

// abstract base class functions: see my synth book for theory details
virtual bool reset(CoreProcData& processInfo) = 0;
virtual bool update(CoreProcData& processInfo) = 0;
virtual bool render(CoreProcData& processInfo) = 0;
virtual bool doNoteOn(CoreProcData& processInfo) = 0;
virtual bool doNoteOff(CoreProcData& processInfo) = 0;
//

This structure is different from the others in SynthLab as it uses naked pointers (rather than std::unique_ptr and std::shared_ptr) and const char*s (rather than std::string). There is a very important reason for using these old-fashioned constructs: for dynamically loaded ModuleCores (mini-DLLs) this structure will be passed across the thunk layer, an imaginary barrier between the process and the DLL with which it communicates. This absolutely requires using these basic C++ datatypes while avoiding the standard library (in addition to many others) and has to do with the way functions are called and the stack is pushed/popped. The standard library's binaries are different for every version of Visual Studio and Xcode, and are also different for the Debug and Release configurations. This would break the whole idea of compiling the mini-DLLs in whatever version of Visual Studio or Xcode you choose.

The CoreProcData structure contains every piece of information that will be needed for any and all of the five operational phases. The structure is shown below and most of the parameters are self-explanatory based on the name. First are the modulation input and output array pointers:

struct CoreProcData
{
// --- Interfaces to modulation arrays
IModulator* modulationInputs = nullptr; //< input modulation values
IModulator* modulationOutputs = nullptr;//< output modulation values
//

Next are the naked pointers to the audio buffers; the pointers are arrays of pointers as each channel has its own buffer. The fmBuffer contains audio data that is used for phase modulation (FM synthesis).

// --- old fashioned (naked) pointers can survive the thunk-barrier
float** inputBuffers = nullptr; //< set of input bufers, one per channel
float** outputBuffers = nullptr; //< set of output bufers, one per channel
float** fmBuffers = nullptr; //< used for DX synths (phase modulator synths)

To get a pointer to the left channel input buffer during the render() operation you would write:

bool <SynthModule>:render(CoreProcData& processInfo)
{
// --- get pointer to the left buffer
float* leftInBuffer = processInfo.inputBuffers[LEFT_CHANNEL];
//

Next are the database and MIDI input data pointers (the interfaces are likewise "pure" C++ with no std:: library members)

// --- Pointers to shared resources
IWavetableDatabase* wavetableDatabase = nullptr; //< wavetable database, usually owned by engine
IPCMSampleDatabase* sampleDatabase = nullptr; //< PCM sample database, usually owned by engine
IMidiInputData* midiInputData = nullptr; //< MIDI input daa, usually owned by engine
//

The next member is so important, it has its own code listing. This is the custom data structure pointer. This pointer is cloaked as a void* since it will depend on the type of module.

// --- the all-important parameters!
void* moduleParameters = nullptr; // module parameters, cloaked as void* -- varies according to module
//

You cast this pointer to the datatype you need for a particular module. You can use simple casting or more complex versions, checking the cast before usage. Here I will just use a simple cast:

// --- parameters from void*
LFOParameters* parameters = static_cast<LFOParameters*>(processInfo.moduleParameters);
//

The remaining members are used in various operational phases. Note the MIDINoteEvent structure that is used for the note-on and note-off handlers.

const char* dllPath = nullptr; //< path to the plugin, used for finding PCM sample WAV files
double sampleRate = 0.0; //< fs
uint32_t samplesToProcess = 64; //< number of samples in this block
double unisonDetuneCents = 0.0; //< detuning value for this core
double unisonStartPhase = 0.0; //< unison start phase value for this core
double BPM = 120.0; //< current BPM, needed for LFO sync to BPM
MIDINoteEvent noteEvent; //< the MIDI note event for the current audio block
};


synthlab_4.png