skip navigational linksPJRC
Shopping Cart Checkout Shipping Cost Download Website
Home MP3 Player 8051 Tools All Projects PJRC Store Site Map
You are here: Teensy Teensyduino USB MIDI

PJRC Store
Teensy 3.1, $19.80
Teensy 2.0, $16.00
Teensy++ 2.0, $24.00
USB Cable, $4.00
Teensy
Main Page
Teensy 3.1
Getting Started
How-To Tips
Code Library
Projects
Teensyduino
Reference

Using USB MIDI

When you select "MIDI" from the Tools->USB Type menu, the Teensy becomes a USB MIDI (Musical Instrument Digital Interface) device, capable of sending and receiving MIDI messages.

MIDI can be used for non-music applications, such a controlling a large number of lights from a PC. Environments like Puredata and Max/MSP can give you access to MIDI for almost any purpose. MIDI is optimized to simultaneously transmit and receive large numbers of short messages with minimal delay.

Teensyduino implements a "class compliant" MIDI device, which can work with the built-in drivers on all major operating systems.

Transmitting Messages

These functions allow you to transmit all of the standard MIDI messages.

  usbMIDI.sendNoteOn(note, velocity, channel)
  usbMIDI.sendNoteOff(note, velocity, channel)
  usbMIDI.sendPolyPressure(note, pressure, channel)
  usbMIDI.sendControlChange(control, value, channel)
  usbMIDI.sendProgramChange(program, channel)
  usbMIDI.sendAfterTouch(pressure, channel)
  usbMIDI.sendPitchBend(value, channel)
  usbMIDI.sendSysEx(length, array)

MIDI messages are grouped together into USB packets. Up to 16 messages can transmit at once! They are held for a brief time, not more than 1 ms, to facilitate grouping. You can use the send_now() function to immediately allow any buffered messagse to transmit

  usbMIDI.send_now()

USB bandwidth is shared among devices. Usually there is very little delay, unless other USB devices are using all the available USB bandwidth.

Transmit Examples

Philip Cunningham's MIDI Controller reads 6 knobs and sends Control Change messages.

Arcade Button MIDI Controller (instructables)

This simple sketch shows how to use the Bounce library together with MIDI. A longer example can be found in File > Examples > Teensy > USB_MIDI > Buttons.

#include <Bounce.h>  // Bounce library makes button change detection easy
const int channel = 1;

Bounce button1 = Bounce(1, 5);  // 5 = 5 ms debounce time
Bounce button2 = Bounce(2, 5);  // which is appropriate for good
Bounce button3 = Bounce(3, 5);  // quality mechanical pushbuttons
void setup() {
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
}

void loop() {
  button1.update();
  button2.update();
  button3.update();
  // Note On messages when each button is pressed
  if (button1.fallingEdge()) {
    usbMIDI.sendNoteOn(60, 99, channel);  // 60 = C4
  }
  if (button2.fallingEdge()) {
    usbMIDI.sendNoteOn(61, 99, channel);  // 61 = C#4
  }
  if (button3.fallingEdge()) {
    usbMIDI.sendNoteOn(62, 99, channel);  // 62 = D4
  }
  // Note Off messages when each button is released
  if (button1.risingEdge()) {
    usbMIDI.sendNoteOff(60, 0, channel);  // 60 = C4
  }
  if (button2.risingEdge()) {
    usbMIDI.sendNoteOff(61, 0, channel);  // 61 = C#4
  }
  if (button3.risingEdge()) {
    usbMIDI.sendNoteOff(62, 0, channel);  // 62 = D4
  }
}

  // MIDI Controllers should discard incoming MIDI messages.
  while (usbMIDI.read()) {
  }
}

Transmit Issues

When using MIDI-OX on Windows, together with a sketch that transmits but never receives, open only the input device. From the menu Options > MIDI Devices, select "USB Audio Device" in the MIDI Inputs box. Do NOT select "USB Audio Device" in the MIDI Outputs box. Uncheck the "Automatically attach Inputs to Outputs during selection" box, then click OK.

Receiving Messages with Read & Callback Functions

The easiest way to receive MIDI messages is with functions that are automatically called when each type of message is received. Usually you would create one or more of these:

  void OnNoteOn(byte channel, byte note, byte velocity)
  void OnNoteOff(byte channel, byte note, byte velocity)
  void OnVelocityChange(byte channel, byte note, byte velocity)
  void OnControlChange(byte channel, byte control, byte value)
  void OnProgramChange(byte channel, byte program)
  void OnAfterTouch(byte channel, byte pressure)
  void OnPitchChange(byte channel, int pitch)

For each function you create, you must use the corresponding "setHandle" functions to tell usbMIDI to call it when that message type is read.

  usbMIDI.setHandleNoteOff(OnNoteOff)
  usbMIDI.setHandleNoteOn(OnNoteOn)
  usbMIDI.setHandleVelocityChange(OnVelocityChange)
  usbMIDI.setHandleControlChange(OnControlChange)
  usbMIDI.setHandleProgramChange(OnProgramChange)
  usbMIDI.setHandleAfterTouch(OnAfterTouch)
  usbMIDI.setHandlePitchChange(OnPitchChange)

Once everything is set up, you then need the "read" function to actually read data. The callback functions are only called when you use read. There are 2 choices for read:

  usbMIDI.read();         // All Channels

If you give a specific channel number to read, messages to other channels are ignored, which is easier than having to check the channel number in your callback function.

  usbMIDI.read(channel);  // One Specific Channel (1 to 16)

Here is a simple example which controls a LED by MIDI note messages:

// USB MIDI receive example, Note on/off -> LED on/off
// contributed by Alessandro Fasan

int ledPin = 13;

void OnNoteOn(byte channel, byte note, byte velocity)
{
  digitalWrite(ledPin, HIGH);
}

void OnNoteOff(byte channel, byte note, byte velocity)
{
  digitalWrite(ledPin, LOW);
}

void setup()
{
  pinMode(ledPin, OUTPUT);
  usbMIDI.setHandleNoteOff(OnNoteOff);
  usbMIDI.setHandleNoteOn(OnNoteOn) ;
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
}

void loop()
{
  usbMIDI.read();
}

Receiving Messages with Read & Query Functions

Instead of callback functions, you can also check the return value from read(). When it returns true, a new message has arrived. The "get" functions allow you to access information about the most recently received MIDI message.
  usbMIDI.getType()

The types are: 0 = Note Off, 1 = Note On, 2 = Velocity Change, 3 = Control Change, 4 = Program Change, 5 = After Touch, 6 = Pitch Bend, 7 = System Exclusive

  usbMIDI.getChannel()
The getChannel function gives you the channel number, from 1 to 16. If you requested only a specific channel when calling read, there is no need to use this.
  usbMIDI.getData1()
  usbMIDI.getData2()
The actual message data can be accessed with getData1 and getData2.
  usbMIDI.getSysExArray()
For system exclusive messages, the message data is available as an array. Use getData1 to find the number of bytes stored in the array.

Connecting Many Buttons & Knobs/Sliders

Often MIDI controllers need more pushbuttons and knobs or sliders than can be connected directly. The inexpensive 74HC4051 chip can be used to connect 8 inputs to one pin. It works for analog or digital signals. Three digital pins are needed to control the 74HC4051, but those same 3 pins can control many chips.


8X Input Expansion with 74HC4051 Chips

To use these 74HC4051 chips, first select the desired channel using the 3 control pins. Then wait a brief time for the signals to propagate through the chips. 50 µs is plenty. Then you can read the pins. This process must be repeated 8 times to read all the signals.

  // select 74HC4051 channel 5 (of 0 to 7)
  digitalWrite(1, HIGH);
  digitalWrite(2, LOW);
  digitalWrite(3, HIGH);
  
  // allow 50 us for signals to stablize
  delayMicroseconds(50);
  
  // read the signals routed to pins 10, 19, 20
  // through channel 5 of each 74HC4051 chip
  buttonPin10channel5.update();
  knobPin19channel5 = analogRead(19);
  knobPin20channel5 = analogRead(20);

Drum Machine Example

TODO: this section....