I'm going to walk through sending MIDI messages in this post. In the next post, I'm going to introduce Captain MIDI, a full-scale modular MIDI interface in the classic Ornament and Crime style.
MIDI support is enabled in Arduino IDE as one of the compile options. From the Tools menu, go down to "USB Type" and choose "MIDI" instead of the usual "No USB."
Note #1: MIDI support adds about 3% to the size of the compiled binary, so if you're working with a normal O_C codebase, MIDI support might take you over the limit.
Note #2: I haven't tested the default O_C apps with MIDI support enabled. I think they'll continue to work as normal, but I haven't tried it.
Note #3: Arduino IDE remembers your last settings. This is usually a good thing, but it means that you'll need to keep track of whether you want MIDI support enabled or not if you have multiple codebases.
Note #4: When I compile with MIDI, I don't have to push the Program button on the Teensy; programming starts automatically as soon as the build is done. I don't know why it works this way, but I suspect that it's normal USB behavior that is suppressed when compilation is done with "No USB."
The Teensy USB MIDI Library
See the Teensyduino USB MIDI documentation here:
The information on that page can be characterized as somewhat accurate. Hopefully, I can save you from some of the frustration that I experienced when I counted on it being right. Still, it's a decent starting point, and it's worth knowing.
If you go through the library code that's part of Teensyduino, there's more than one MIDI library, so I suspect that the documentation just wasn't updated. At very least, it's not specific to the Teensy version that we care about, 3.2.
But the documentation's gaps won't vex us too much for Midi out. It'll become more relevant when we look at MIDI in.
Basic MIDI Out
I'm starting with MIDI out because it's a bit easier than MIDI in. Don't worry, MIDI in is still pretty easy. But with MIDI out, you're just using the documented methods to send messages.
Every MIDI operation (out and in) uses the global object usbMIDI. For example, to send a Note On message, you do
usbMIDI.sendNoteOn(midi_note, velocity, channel);
midi_note is an integer from 0-127, velocity is an integer from 0-127, and channel is an integer from 1 to 16. Take note that MIDI channels in this library are 1-indexed.
The MIDI library puts each message in a buffer, and holds onto it until 16 messages have been buffered, or until 1ms (or so) has elapsed, whichever comes first. If you want to send buffered messages earlier than that, you can call
Note Off is similar to Note On, including velocity:
usbMIDI.sendNoteOff(midi_note, 0, channel);
MIDI control change looks like this:
usbMIDI.sendControlChange(controller_number, value, channel);
Note that value has a seven-bit range from 0-127, per the MIDI specification.
This is channel aftertouch, and the value here is also from 0-127.
And pitch bend:
The bend range here is from 0-16383. Values from 0 to 8191 are negative bend, 8192 is 0 bend, and values from 8193 to 16383 are positive. For logging purposes, I'd just display (bend - 8192).
Other MIDI Out
I've been working with system exclusive messages, and I'll probably deal with that separately if anyone is interested. I haven't really figured out clock yet. The documentation says this:
but there is no usbMIDI.Clock, at least for Teensy 3.2, and there doesn't seem to be specific support for clock in the library code. So this is sort of on my "pending" list. The library lets us construct MIDI messages a byte at a time, and that might be necessary if we want clock.