Hm, I can't see much of optimization here to be honest.
Couple of things that come to my mind:
- Code: Select all
// events
pMidiHandler->Process();
pVoiceManager->Process();
int blockSize = samplesLeft;
blockSize = pMidiHandler->GetSamplesTillNextEvent(blockSize);
Does this need to happen on every sample?
I assume pMidiHandler->Process(); pVoiceManager->Process(); pick up new midi notes and trigger the OSC.
Could this happen at control rate?
If yes, you can skip that GetSamplesTillNextEvent as well (block size derives from control rate - if your control rate is 1/16 of sample rate, the block size is 16 frames .. fix len, fixed intervals).
Also, does this need to happen on the signal processing code path?
Don't know what the pMidiHandler->Process() is doing, but if it processes MIDI messages - the signal processing path might not be the right place to do that.
Same for Flush at the end, don't what it does.
So let assume you move midi handling and voice trigger out of that loop, and work with a control rate on triggering voices.
Next would be to reverse the loops.
Process a voice at once, not a sample at once.
To give some rough idea how this could look like instead:
- Code: Select all
void MainIPlug::ProcessDoubleReplacing(double **inputs, double **outputs, int nFrames) {
double *outputLeft = outputs[0];
double *outputRight = outputs[1];
double *inputLeft = inputs[0];
double *inputRight = inputs[1];
//
// Init outputs with 1.0f
// We will use outputLeft to frist accumulate envelopes into it, so it should be 1.0f by default
for (int i = 0; i < nFrames; i++) {
outputLeft[i] = outputRight[i] = 1.0f;
}
//
// Process voices
for (int voiceIndex = 0; voiceIndex < PLUG_VOICES_BUFFER_SIZE; voiceIndex++)
{
Voice &voice = pVoiceManager->mVoices[voiceIndex];
if (!voice.mIsPlaying) {
continue;
}
//
// Collect envelopes
for (int envelopeIndex = 0; envelopeIndex < ENVELOPES_CONTAINER_NUM_ENVELOPE_MANAGER; envelopeIndex++)
{
Envelope &envelope = pEnvelopesContainer->pEnvelopeManager[envelopeIndex]->mEnvelope;
if (voice.mSample == 0.0) {
envelope.Reset(voice);
}
// Envelope.Apply will multiply the Envelope values onto the outputLeft array.
// so after that "Collect envelopes" loop is finished, outputLeft has the product of all Envelopes in it.
envelope.Apply(outputLeft, voice.mSample, nFrames);
}
//
// Apply envelopes
for (int i = 0; i < nFrames; i++)
{
double env_mod = outputLeft[i];
outputLeft[i] = inputLeft[i] * env_mod;
outputRight[i] = inputRight[i] * env_mod;
}
}
}
Lots of little loops with almost no branching in it.
About the void Envelope::Process(Voice &voice) ..


Need some time first to understand what this actually does on the signal