Fundamentals
Export Targets
Code Export
Patcher UI
Special Topics
RNBO Raspberry Pi OSCQuery Runner
Sending and Receiving MIDI C++
Recall from Getting and Setting Parameters C++ that we use the setParameterValue
method of a RNBO core object to update parameters. To send a message, we use the sendMessage
method. Both of these functions are convenience wrappers around a more generic function scheduleEvent
, which can send any kind of RNBO event to be executed at a given time. This is the function used to send MIDI events to a RNBO core object.
#include "RNBO.h"
using namespace RNBO;
const uint8_t noteOn = 0x90;
const uint8_t midiChannel = 0;
const uint8_t noteOnLeadByte = noteOn | midiChannel;
const uint8_t pitch = 60;
const uint8_t velocity = 100;
const uint8_t midiBytes[3] = { noteOnLeadByte, pitch, velocity };
rnboObject.scheduleEvent(MidiEvent(RNBOTimeNow, 0, midiBytes, 3));
Using the same approach, it's possible to schedule MIDI Control Change, Pitch Bend, and other events. See https://ccrma.stanford.edu/~craig/articles/linuxmidi/misc/essenmidi.html for a more thorough overview of MIDI essentials.
Receiving MIDI Events
We can receive MIDI events by creating a new class that is a subclass of RNBO::EventHandler
and implementing the handleMidiEvent
method. A simple example might look something like this:
#include <iostream>
#include "RNBO.h"
using namespace RNBO;
class LoggingMidiHandler : EventHandler {
void eventsAvailable() override {
drainEvents();
}
void handleMidiEvent(const MidiEvent& event) override {
const uint8_t *data = event.getData();
int length = event.getLength();
if ((data[0] >> 4) == 0x09) { // note on
std::cout << "Received note on event with pitch: " << ((int) data[1]) << " velocity: " << ((int) data[2]) << "\n";
} else if ((data[0] >> 4) == 0x08) { // note off
std::cout << "Received note off event with pitch: " << ((int) data[1]) << "\n";
} else {
std::cout << "Received midi event with data: ";
for (int i = 0; i < length; i++) {
if (i != 0) std::cout << ", ";
std::cout << ((int) data[i]);
}
std::cout << "\n";
}
}
};
int main(int argc, const char * argv[]) {
CoreObject rnboObject;
LoggingMidiHandler handler;
auto interface =
rnboObject.createParameterInterface(ParameterEventInterface::MultiProducer, (EventHandler *) &handler);
rnboObject.prepareToProcess(44100, 64);
SampleValue** inputs = nullptr;
SampleValue** outputs = new SampleValue*[1];
outputs[0] = new double[64];
while (true) {
const uint8_t noteOn = 0x90;
const uint8_t noteOff = 0x80;
const uint8_t midiChannel = 0;
const uint8_t noteOnLeadByte = noteOn | midiChannel;
const uint8_t pitch = 60;
const uint8_t velocity = 100;
uint8_t midiBytes[3] = { noteOnLeadByte, pitch, velocity };
// send note on
rnboObject.scheduleEvent(MidiEvent(RNBOTimeNow, 0, midiBytes, 3));
midiBytes[0] = noteOff | midiChannel;
midiBytes[2] = 0;
// send note off
rnboObject.scheduleEvent(MidiEvent(RNBOTimeNow, 0, midiBytes, 3));
rnboObject.process(inputs, 0, outputs, 1, 64);
}
delete [] outputs[0];
delete [] outputs;
return 0;
}