Skip to content

Instantly share code, notes, and snippets.

@cowboy
Last active December 11, 2023 08:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cowboy/9c9ea9151a330e1e4552e76292a12bf4 to your computer and use it in GitHub Desktop.
Save cowboy/9c9ea9151a330e1e4552e76292a12bf4 to your computer and use it in GitHub Desktop.
USB MIDI device -> filter -> DIN MIDI out - for Teensy LC
// To give your project a unique name, this code must be
// placed into a .c file (its own tab). It can not be in
// a .cpp file or your main sketch (the .ino file).
#include "usb_names.h"
// Edit these lines to create your own name. The length must
// match the number of characters in your custom name.
#define MIDI_NAME {'R','e','n','a','m','e',' ','M','e',' ','D','u','d','e'}
#define MIDI_NAME_LEN 14
// Do not change this part. This exact format is required by USB.
struct usb_string_descriptor_struct usb_string_product_name = {
2 + MIDI_NAME_LEN * 2,
3,
MIDI_NAME
};
// ===============================================================
// USB MIDI device -> filter -> DIN MIDI out - for Teensy LC
// ===============================================================
// Settings:
// Tools > USB Type to "MIDI"
#include <MIDI.h> // access to serial (5 pin DIN) MIDI
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, downstreamDinMIDI);
// =====================
// LED BASICS
// =====================
int LED_OTHER = 17;
int LED_HOST_ACTIVITY = 20;
void blink(int count) {
int delay_ms = 100;
for (int i = 0; i < count; i++) {
digitalWrite(LED_OTHER, HIGH);
delay(delay_ms);
digitalWrite(LED_OTHER, LOW);
delay(delay_ms);
}
}
// =====================
// SETUP
// =====================
void setup() {
downstreamDinMIDI.begin(MIDI_CHANNEL_OMNI);
pinMode(LED_OTHER, OUTPUT);
pinMode(LED_HOST_ACTIVITY, OUTPUT);
blink(3);
}
// =====================
// CLOCK LOGIC
// =====================
// Assume 4/4 time
int BEATS_PER_MEASURE = 4;
int PPQN = 24;
int MAX_PULSES = BEATS_PER_MEASURE * PPQN;
bool clockRunning = false;
int clockCounter = 0;
void updateClock() {
if (clockCounter % PPQN == 0) {
digitalWriteFast(LED_OTHER, HIGH);
} else if (clockCounter % (PPQN / 2) == 0) {
digitalWriteFast(LED_OTHER, LOW);
}
int currentBeat = clockCounter / PPQN;
if (++clockCounter >= MAX_PULSES) {
clockCounter = 0;
}
}
void startClock() {
clockCounter = 0;
clockRunning = true;
}
void stopClock() {
clockCounter = 0;
clockRunning = false;
digitalWriteFast(LED_OTHER, LOW);
}
// =====================
// LED MIDI ACTIVITY
// =====================
// Different types of MIDI messages should display for different lengths of time
const int MIDI_MESSAGE_NONE = 0;
const int MIDI_MESSAGE_DEFAULT = 1;
const int MIDI_MESSAGE_CLOCK = 2;
const uint8_t ledTimeMap[3] = {
0, // MIDI_MESSAGE_NONE
15, // MIDI_MESSAGE_DEFAULT
1 // MIDI_MESSAGE_CLOCK
};
// Keep track of the current MIDI message type
int hostCurrentMessageType = MIDI_MESSAGE_NONE;
// A variable to know how long the LED has been turned on
elapsedMillis hostActivityLedTime;
// =====================
// RUN LOOP
// =====================
void loop() {
bool hostActivity = false;
// Read messages the PC (upstream host) sends and forward them to devices
if (usbMIDI.read()) {
byte type = usbMIDI.getType();
byte data1 = usbMIDI.getData1();
byte data2 = usbMIDI.getData2();
byte channel = usbMIDI.getChannel();
const uint8_t *sys = usbMIDI.getSysExArray();
byte cable = usbMIDI.getCable();
hostActivity = sendToDownstreamDevice(type, data1, data2, channel, sys, cable);
}
// Flash LED on activity
if (hostActivity) {
digitalWriteFast(LED_HOST_ACTIVITY, HIGH);
hostActivityLedTime = 0;
}
if (hostCurrentMessageType != MIDI_MESSAGE_NONE && hostActivityLedTime > ledTimeMap[hostCurrentMessageType]) {
hostCurrentMessageType = MIDI_MESSAGE_NONE;
digitalWriteFast(LED_HOST_ACTIVITY, LOW);
}
}
// =====================
// HANDLE MIDI MESSAGES
// =====================
// Send data from the upstream host (eg. computer) to the downstream MIDI device (eg. controller)
bool sendToDownstreamDevice(byte type, byte data1, byte data2, byte channel, const uint8_t *sysexarray, byte cable) {
int prevMessageType = hostCurrentMessageType;
hostCurrentMessageType = MIDI_MESSAGE_DEFAULT;
if (type != usbMIDI.SystemExclusive) {
// Handle clock and MMC messages
if (type == usbMIDI.Clock) {
hostCurrentMessageType = prevMessageType == MIDI_MESSAGE_NONE ? MIDI_MESSAGE_CLOCK : prevMessageType;
updateClock();
} else if (type == usbMIDI.Start || type == usbMIDI.Continue) {
startClock();
} else if (type == usbMIDI.Stop) {
stopClock();
}
downstreamDinMIDI.send(type, data1, data2, channel);
// if (type == usbMIDI.Clock) {
// return false;
// }
}
else {
unsigned int SysExLength = data1 + data2 * 256;
downstreamDinMIDI.sendSysEx(SysExLength, sysexarray, true);
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment