VST Synth Example

VST 2.4 Synth Example Code
Based on VST SDK 2.4 example 'VSTXSynth':
 * 1) include "audioeffectx.h"

static float freqtab[128]; const double PI = 3.14159265358979323846;

class Phaser {   public: uint16_t phase = 0u; // using an integer type automatically ensures limits // phase is in [0 ; 2^(16-1)] const float PHASE_MAX = (1 << 16); float amp = 1.0f; uint16_t freq__ph_p_smp = 0u;

Phaser(float amp = 1.0f) : amp(amp) {}

inline void update {           phase += freq__ph_p_smp; }       inline void set_freq(float freq__hz, int sample_rate = 48000) {           float freq__ppsmp = freq__hz / sample_rate; // periods per sample freq__ph_p_smp = freq__ppsmp * PHASE_MAX; }       inline float sin01 {           float ph01 = phase / PHASE_MAX; return std::sinf(ph01 * 6.28318530717959f) * amp * 0.5 + amp * 0.5; }       inline float square(float pulse_width) {           float ph01 = phase / PHASE_MAX; return ph01 > pulse_width ? amp : -amp; } };

class VstXSynth : public AudioEffectX {   public: VstXSynth (audioMasterCallback audioMaster); ~VstXSynth ;

virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames); virtual VstInt32 processEvents (VstEvents* events);

virtual void setParameter (VstInt32 index, float value); virtual float getParameter (VstInt32 index);

virtual bool getOutputProperties (VstInt32 index, VstPinProperties* properties);

virtual bool getEffectName (char* name); virtual bool getVendorString (char* text); virtual bool getProductString (char* text); virtual VstInt32 getVendorVersion ; virtual VstInt32 canDo (char* text);

virtual VstInt32 getNumMidiInputChannels ;

private: float param;

// Synth state Phaser phaser, lfo;

unsigned char depressedNotes[128 / 8]; // 128 MIDI notes, 8 per char

VstInt32 currentNote; VstInt32 currentVelocity; VstInt32 currentDelta; bool noteIsOn;

void initProcess ; void noteOn (VstInt32 note, VstInt32 velocity, VstInt32 delta); void noteOff (VstInt32 note); void allNotesOff ; };

AudioEffect* createEffectInstance (audioMasterCallback audioMaster) { return new VstXSynth (audioMaster); }

VstXSynth::VstXSynth (audioMasterCallback audioMaster) : AudioEffectX (audioMaster, 1, 1) {   if (audioMaster) { setNumInputs (0); setNumOutputs (2); canProcessReplacing ; isSynth ; setUniqueID ('SYL0'); }

initProcess ; suspend ; }

VstXSynth::~VstXSynth { }

void VstXSynth::setParameter (VstInt32 index, float value) { param = value; } float VstXSynth::getParameter (VstInt32 index) { return param; }

bool VstXSynth::getOutputProperties (VstInt32 index, VstPinProperties* properties) { if (index < 2) { // # of outputs vst_strncpy (properties->label, "Vstx ", 63); char temp[11] = {0}; int2string (index + 1, temp, 10); vst_strncat (properties->label, temp, 63);

properties->flags = kVstPinIsActive; if (index < 2) properties->flags |= kVstPinIsStereo;  // make channel 1+2 stereo return true; }   return false; }

bool VstXSynth::getEffectName (char* name) {   vst_strncpy (name, "monophonic", kVstMaxEffectNameLen); return true; } bool VstXSynth::getVendorString (char* text) {   vst_strncpy (text, "schmid", kVstMaxVendorStrLen); return true; } bool VstXSynth::getProductString (char* text) { vst_strncpy (text, "monophonic", kVstMaxProductStrLen); return true; } VstInt32 VstXSynth::getVendorVersion { return 1000; }

VstInt32 VstXSynth::canDo (char* text) {   if (!strcmp (text, "receiveVstEvents")) return 1; if (!strcmp (text, "receiveVstMidiEvent")) return 1; return -1;     // explicitly can't do; 0 => don't know }

VstInt32 VstXSynth::getNumMidiInputChannels { return 1; // we are monophonic }

float midi2freq(int note, int octave) {   return 32.70319566257483f * pow(2.0f, note / 12.f + octave); }

void VstXSynth::initProcess { phaser.phase = 0u; lfo.phase = 0u; lfo.set_freq(1.0f); allNotesOff; for (int i = 0; i < 128; i++) { // 128 midi notes freqtab[i] = midi2freq(i % 12, i / 12 - 2); } }

void VstXSynth::processReplacing(       float** inputs, float** outputs, // input / output - buffers        VstInt32 sampleFrames ) {        // buffer size

float* out1 = outputs[0]; // out1 = left channel float* out2 = outputs[1]; // out2 = right channel

if (noteIsOn) { float freq = freqtab[currentNote & 0x7f]; phaser.set_freq(freq, sampleRate);

// loop while (--sampleFrames >= 0) { float lfo_val = lfo.sin01 * 0.9f + 0.05f; (*out1++) = phaser.square(lfo_val); (*out2++) = phaser.square(lfo_val); phaser.update; lfo.update; }   }                           else { memset (out1, 0, sampleFrames * sizeof (float)); memset (out2, 0, sampleFrames * sizeof (float)); } }

VstInt32 VstXSynth::processEvents (VstEvents* ev) {

for (VstInt32 i = 0; i < ev->numEvents; i++) { if ((ev->events[i])->type != kVstMidiType) continue;

VstMidiEvent* event = (VstMidiEvent*)ev->events[i]; char* midiData = event->midiData; VstInt32 status = midiData[0] & 0xf0;  // ignoring channel // note off: 0x80 // note on: 0x90 if (status == 0x90 || status == 0x80) { // we only look at notes VstInt32 note = midiData[1] & 0x7f; VstInt32 velocity = midiData[2] & 0x7f; if (status == 0x80) velocity = 0;  // note off by velocity 0 if (!velocity) noteOff (note); else noteOn (note, velocity, event->deltaFrames); }       else if (status == 0xb0) { if (midiData[1] == 0x7e || midiData[1] == 0x7b) allNotesOff; // all notes off }       event++; }   return 1; }

void VstXSynth::noteOn (VstInt32 note, VstInt32 velocity, VstInt32 delta) { currentNote = note; currentVelocity = velocity; currentDelta = delta; noteIsOn = true; phaser.phase = 0u;

// set the bit corresponding to 'note' depressedNotes[note / 8] |= 1 << (note % 8); }

void VstXSynth::noteOff (VstInt32 note) { // clear the bit corresponding to 'note' depressedNotes[note / 8] &= ~(1 << (note % 8));

// if the released note is the one playing right now, check for another // depressed note if(note == currentNote) {

noteIsOn = false;

// find the byte containing the highest depressed note, if any // - this (strange) behaviour is copied from Roland JP-8080 for(int byte = 128 / 8 - 1; byte >= 0; byte--) { if(depressedNotes[byte] != 0) {

noteIsOn = true;

// find the bit containing the highest depressed note, if any for(int bit = 7; bit >= 0; bit--) { if(depressedNotes[byte] & (1 << (bit % 8))) currentNote = byte * 8 + bit; }           }        }    } }

void VstXSynth::allNotesOff { noteIsOn = false; for(int byte = 0; byte < 128 / 8; byte++) depressedNotes[byte] = 0; }