Learn Sending and Receiving MIDI in C++

Getting Started

Welcome to RNBO

Quickstart

RNBO Basics

Key Differences

Why We Made RNBO

Coding Resources

Fundamentals

Audio IO

Messages to rnbo~

Using Parameters

MIDI in RNBO

Messages and Ports

Polyphony and Voice Control

Audio Files in RNBO

Using Buffers

Using the FFT

Export Targets

Export Targets Overview

VST/AudioUnit
Max External Target
Raspberry Pi Target
The Web Export Target
The C++ Source Code Target

Code Export

Working with JavaScript
Working with C++

How to Include RNBO in Your C++ Project

RNBO Helper Types

Getting and Setting Parameters in C++

Sending and Receiving Messages in C++

Sending and Receiving MIDI in C++

Multiple RNBO Devices in one C++ Project

Working with Presets in C++

Loading File Dependencies

Special Topics

Sample Accurate Patching
Scala and Custom Tuning

RNBO and Max for Live

RNBO Raspberry Pi OSCQuery Runner

Metadata

Export Description

Raspberry Pi GPIO

Updating the RNBO Package

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;
}