Last active
July 13, 2020 19:33
-
-
Save gaspard/6415937 to your computer and use it in GitHub Desktop.
VST plugin: sample accurate midi out.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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