Skip to content

Instantly share code, notes, and snippets.

@gaspard
Last active July 13, 2020 19:33
Show Gist options
  • Save gaspard/6415937 to your computer and use it in GitHub Desktop.
Save gaspard/6415937 to your computer and use it in GitHub Desktop.
VST plugin: sample accurate midi out.
// This is part of a midi generating plugin made with Juce library.
//
// The goal of this thing is simply to generate random midi notes that are on-time
// with sample accuracy.
void MidiRandomAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
// Clear all
for (int i = 0; i < getNumOutputChannels(); ++i)
{
buffer.clear (i, 0, buffer.getNumSamples());
}
// ============= Generate random midi notes.
midiMessages.clear();
AudioPlayHead *head = getPlayHead();
if ( head == nullptr ) return;
AudioPlayHead::CurrentPositionInfo positionInfo;
head->getCurrentPosition (positionInfo);
// positionInfo as a double from song start (in midi tick)
// = number of beats (1/4 note) from start.
// ppqPosition = position in 1 pulse per 1/4 note
double currentPosition = positionInfo.ppqPosition;
if (!positionInfo.isPlaying)
{
if (wasPlaying)
{
debugMessage = "Stop.";
wasPlaying = false;
// all notes off
auto it = notesPlaying.begin();
while (it != notesPlaying.end())
{
midiMessages.addEvent (it->message, 0);
// remove
it = notesPlaying.erase (it);
}
}
return;
} else if (!wasPlaying) {
// Wait for next 1/4 note before playing.
nextPlayPosition = floor(currentPosition) + 1;
debugMessage = "Start.";
wasPlaying = true;
}
int sampleFrames = buffer.getNumSamples();
double sampleRate = getSampleRate();
// compute stepLen from sampleFrames and sampleRate
// sampleRate = sample / second
// tickPerS = tick / second
// tempo/60 = beat / second
// Since ppq here is 1 for every beat, we have:
double tickPerS = positionInfo.bpm / 60.0;
// sampleRate = sample / second
// tickPerSample = tickPerS / sampleRate
double tickPerSample = tickPerS / sampleRate;
double stepLen = sampleFrames * tickPerSample;
// "delta" is a (manually set) latency compensation
// value that SHOULD NOT EXIST but is currently needed in DP8
currentPosition = currentPosition + delta;
String msg = "currentPosition = ";
msg += String (currentPosition);
msg += "\nstepLength = ";
msg += String (stepLen);
logger.logMessage(msg);
sendNoteOff (midiMessages, currentPosition, stepLen, tickPerSample);
while (currentPosition + stepLen >= nextPlayPosition)
{
double playPosition = nextPlayPosition;
// Play a random note every 1/4
nextPlayPosition += 1.0;
// Output to channels 1 or 2
int channel (1); //1 + std::rand() % 2);
// The higher the channel, the higher the note.
int note (48 + 12 * ((double)std::rand() / RAND_MAX));
int velocity (80);
// Length in ppq
double length (0.5);
// Exact position from now in audio frames
// Adding 0.5 to round to nearest int
int e_delta = 0.5 + (playPosition - currentPosition) / tickPerSample;
debugMessage = "\n\nPLAY\ncurrentPosition = ";
debugMessage += String (currentPosition);
debugMessage += "\nnextPlayPosition = ";
debugMessage += String (playPosition);
debugMessage += "\nsampleFrames = ";
debugMessage += String (sampleFrames);
debugMessage += "\nbpm = ";
debugMessage += String (positionInfo.bpm);
debugMessage += "\nsampleRate = ";
debugMessage += String (sampleRate);
debugMessage += "\ntickPerS = ";
debugMessage += String (tickPerS);
debugMessage += "\ntickPerSample = ";
debugMessage += String (tickPerSample);
debugMessage += "\nstepLen = ";
debugMessage += String (stepLen);
debugMessage += "\nStep length = ";
debugMessage += String (stepLen);
debugMessage += "\nDelta = ";
debugMessage += String(e_delta);
logger.logMessage(debugMessage);
// Prepare NoteOff
notesPlaying.push_back (
PositionedMidiMessage (
MidiMessage (
// first byte is channel + event type
// NoteOff
channel - 1 + 0x80,
// second byte is note value
note,
// third byte is velocity
0 // velocity
),
playPosition + length
)
);
// Send NoteOn to host
midiMessages.addEvent (
MidiMessage (
// first byte is channel + event type
// NoteOn
channel - 1 + 0x90,
// second byte is note value
note,
// third byte is velocity
velocity
),
// delta in samples
e_delta
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment