//------------------------------------------------------------------------------------------------------- // VST Plug-Ins SDK // Version 2.4 $Date: 2006/11/13 09:08:27 $ // // Filename : vstxsynthproc.cpp // Created by : Alex Medearis, extended from Steinberg Media Technologies // Description : Alex's VST Synth // // A basic FM softsynth. Leveraged from boilerplate code from Steinberg's 2 voice // example. // // Also defines the Note class, used to encapsulate parameters of a single note. // // //------------------------------------------------------------------------------------------------------- #include "vstxsynth.h" enum { kNumFrequencies = 128, // 128 midi notes kWaveSize = 4096 // samples (must be power of 2 here) }; const double midiScaler = (1. / 127.); static float sawtooth[kWaveSize]; static float pulse[kWaveSize]; static float sinwave[kWaveSize]; static float freqtab[kNumFrequencies]; static float currentWaveform1[kWaveSize]; static float currentWaveform2[kWaveSize]; #define _USE_MATH_DEFINES 1 #include "math.h" //----------------------------------------------------------------------------------------- // VstXSynth //----------------------------------------------------------------------------------------- void VstXSynth::setSampleRate (float sampleRate) { AudioEffectX::setSampleRate (sampleRate); fScaler = (float)((double)kWaveSize / (double)sampleRate); } //----------------------------------------------------------------------------------------- void VstXSynth::setBlockSize (VstInt32 blockSize) { AudioEffectX::setBlockSize (blockSize); // you may need to have to do something here... } //----------------------------------------------------------------------------------------- void VstXSynth::initProcess () { this->notes = new std::vector(); this->notesSwap = new std::vector(); fScaler = (float)((double)kWaveSize / 44100.); // we don't know the sample rate yet VstInt32 i; // make waveforms VstInt32 wh = kWaveSize / 4; // 1:3 pulse for (i = 0; i < kWaveSize; i++) { sawtooth[i] = (float)(-1. + (2. * ((double)i / (double)kWaveSize))); pulse[i] = (i < wh) ? -1.f : 1.f; sinwave[i] = (float)sin(M_PI * 2.0 * ((float)i / kWaveSize)); } // make frequency (Hz) table // begins at c (6.875 * k * k * k) Hz and goes up 128 notes double k = 1.059463094359; // 12th root of 2 double a = 6.875; // a a *= k; // b a *= k; // bb a *= k; // c, frequency of midi note 0 for (i = 0; i < kNumFrequencies; i++) // 128 midi notes { freqtab[i] = (float)a; a *= k; } updateWaveforms(); } //----------------------------------------------------------------------------------------- void VstXSynth::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) { //this is a hack -- not quite sure why this is necessary updateWaveforms(); float* out1 = outputs[0]; float* out2 = outputs[1]; memset (out1, 0, sampleFrames * sizeof (float)); memset (out2, 0, sampleFrames * sizeof (float)); //play each of the current notes for(int i = 0; i < notes->size(); i++) { Note * noteI = (*notes)[i]; noteI->Play(inputs, outputs, sampleFrames); } //clean up notes which are dead while(!notes->empty()) { Note * noteI = notes->back(); notes->pop_back(); if(noteI->isDead) delete noteI; else notesSwap->push_back(noteI); } std::vector * temp = notes; notes = notesSwap; notesSwap = temp; } void VstXSynth::updateWaveforms() { //interpolate the waveform so that it is between our presets (sin-sawtooth) or (sawtooth-pulse) if(fWaveform1 < .5) { double coeff = 2 * fWaveform1; for (int i = 0; i < kWaveSize; i++) { currentWaveform1[i] = (float)((1 - coeff) * sinwave[i]) + (coeff * sawtooth[i]); } } else { double coeff = 2 * (fWaveform1 - .5); for (int i = 0; i < kWaveSize; i++) { currentWaveform1[i] = (float)((1 - coeff) * sawtooth[i]) + (coeff * pulse[i]); } } //interpolate the waveform so that it is between our presets if(fWaveform2 < .5) { double coeff = 2 * fWaveform2; for (int i = 0; i < kWaveSize; i++) { currentWaveform2[i] = (float)((1 - coeff) * sinwave[i]) + (coeff * sawtooth[i]); } } else { double coeff = 2 * (fWaveform2 - .5); for (int i = 0; i < kWaveSize; i++) { currentWaveform2[i] = (float)((1 - coeff) * sawtooth[i]) + (coeff * pulse[i]); } } } //----------------------------------------------------------------------------------------- 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 if (status == 0x90) { VstInt32 note = midiData[1] & 0x7f; VstInt32 velocity = midiData[2] & 0x7f; noteOn (note, velocity, event->deltaFrames); } else if(status == 0x80) { VstInt32 note = midiData[1] & 0x7f; noteOff(note); } event++; } return 1; } //----------------------------------------------------------------------------------------- void VstXSynth::noteOn (VstInt32 note, VstInt32 velocity, VstInt32 delta) { Note * nNote = new Note(this, note, velocity, delta); notes->push_back(nNote); } //----------------------------------------------------------------------------------------- void VstXSynth::noteOff (VstInt32 note) { for(int i = 0; i < notes->size(); i++) { Note * noteI = (*notes)[i]; noteI->Stop(note); } } #define SLOPE .01 Note::Note(VstXSynth * parent, VstInt32 currentNote, VstInt32 currentVelocity, VstInt32 currentDelta) { this->parent = parent; envelope = 0; fPhase1 = fPhase2 = 0.f; noteIsOn = true; this->currentNote = currentNote; this->currentVelocity = currentVelocity; this->currentDelta = currentDelta; isDead = false; } void Note::updateEnvelope(float slope) { envelope += slope; if(envelope > 1) envelope = 1; if(envelope < 0) envelope = 0; } void Note::Play(float** inputs, float** outputs, VstInt32 sampleFrames) { float* out1 = outputs[0]; float* out2 = outputs[1]; //knob is actually exponential... float currAttack = ((parent->fAttack * parent->fAttack) / 1000.0) + .000001; float currDecay = ((parent->fDecay * parent->fDecay) / 1000.0) + .000001; float envelopeSlope; if (noteIsOn) envelopeSlope = currAttack; else envelopeSlope = -currDecay; //fScaler is the length of our waveform lookup table / sampling rate //i.e. it is the amount of time to go through 1 cycle of the waveform //base Freq units are PERIODS -- i.e. how many oscillations we want to occur //in the number of samples we have //for example, if baseFreq = 1, we have 1 oscillation every 4096 samples. If //baseFreq = 2, we have 2 etc. float baseFreq = freqtab[currentNote & 0x7f] * parent->fScaler; float* wave1; float* wave2; wave1 = currentWaveform1; wave2 = currentWaveform2; float wsf = (float)kWaveSize; float vol = (float)(parent->fVolume * (double)currentVelocity * midiScaler); // this will be something like 00011111... since kWaveSize is a power of 2 // by anding it, we truncate to within 0-(kWaveSize - 1) VstInt32 mask = kWaveSize - 1; // now, count down until we finish with this buffer // loop while (--sampleFrames >= 0) { //the currentDelta is how long until this note is supposed to play in samples //we have to wait this long until we want to output audio if(currentDelta > 0) updateEnvelope(-currDecay); else updateEnvelope(envelopeSlope); //we generate samples by iterating through wave1 at the masked index //of fPhase1 *out1 += wave1[(VstInt32)fPhase1 & mask] * parent->fVolume1 * vol * envelope; *out2 += wave1[(VstInt32)fPhase1 & mask] * parent->fVolume1 * vol * envelope; out1++; out2++; //we generate the modulation of the frequency by iterating through wave2 //at the masked index of fPhase2 float modulation = wave2[(VstInt32)fPhase2 & mask] * parent->fVolume2; //the speed at which we iterate -- i.e. the frequency is a result of the //base frequency plus the modulation geneated from wave2 fPhase1 += baseFreq + (parent->fModIndex * MAX_MOD_INDEX * modulation * parent->fScaler); //we iterate through wave2 at a constant frequency -- 2 times our base frequency fPhase2 += baseFreq * 2; } if(!noteIsOn && envelope <=0) isDead = true; } bool Note::Stop(VstInt32 note) { if(this->currentNote == note) { noteIsOn = false; return true; } return false; }