When your host software receives a MIDI message, your plug is probably busy generating audio in ProcessDoubleReplacing. So the host temporarily stores the MIDI message, recording it's sample offset. When your plug returns from ProcessDoubleReplacing (i.e. when it has finished processing a sample block), the host will send the temporarilly stored MIDI messages to your plug using ProcessMidiMsg. Along with the MIDI message it gives you the sample offset at which the MIDI message should occur. This offset is relatoive to the
next ProcessDoubleReplacing call, so an offset of 0 means before the 1st sample, an offset of 1 means after the 1st sample etc.
When you send MIDI back out and you want it to be sample accurate, you will need to specify the offset again. I don't think it's safe to specify an offset larger than the current sample block frame count, so your plug will have to know when exactly to send the MIDI messages. Here is a fragment of code that sends a Note On and a Note Off every 0.5 seconds (assuming a sample rate of 44100 Hz):
Code:
void ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
{
for (int offset = 0; offset < nFrames; ++offset)
{
// Note On
if (mTimer == 0)
SendMidiMsg(&IMidiMsg(offset, 0x90, mNoteNumber, 127));
// Note Off
else if (mTimer == 22050)
SendMidiMsg(&IMidiMsg(offset, 0x80, mNoteNumber, 0));
if (++mTimer >= 44100)
mTimer = 0;
}
}
I hope this clarifies things a bit for you.