Teensy 3.0 and USB MIDI (problem and fix)

Nantonos

Well-known member
After downloading the recently posted 1Dec snapshot, I noticed there was some Teensy 3.0 MIDI code although it was disabled by default.
I enabled it in hardfware/teensy/boards.txt by uncommenting these three lines:
Code:
teensy3.menu.usb.midi.name=MIDI
teensy3.menu.usb.midi.build.define0=-DUSB_MIDI
teensy3.menu.usb.midi.fake_serial=teensy_gateway

and then tried the following sketch, which works in Teensy 2.0.

Code:
// USB MIDI workout for Teensy family
// set USB type to MIDI

//#include <MIDI-notenames.h>
#define Eflat_1 51
#define C0 60
#define tuningA 69

const uint8_t channel = 1;             // MIDI channel
const uint8_t moderate = 96;       // MIDI note velocity

uint8_t Major[8] = { 
  0, 2, 4, 5, 7, 9, 11, 12 };     // semitone intervals from root note of scale
uint8_t Minor[8] = { 
  0, 2, 3, 5, 7, 9, 11, 12 };

void playScale(uint8_t note,  uint8_t scale[]);

void setup() {
  delay(5000); // time to get organized
  // Tune up
  usbMIDI.sendNoteOn(tuningA, moderate, channel);
  delay(1000);
  usbMIDI.sendNoteOff(tuningA, 0, channel);
}

void loop() {
  delay(1000);
  // Some scales
  playScale(C0, Major);
  delay(500);
  playScale(Eflat_1, Minor);
  delay(500);
  slide(C0, 3000, 4000);
  delay(500);
  slide(C0, -5000, 7000);
}


void playScale(uint8_t note,  uint8_t scale[])  {
  // play a scale with a given root note
  int duration = 1000;
  int between = 500;
  uint8_t i;
  uint8_t tone;

  for (i=0; i <8; i++) {
    tone = note+scale[i];
    usbMIDI.sendNoteOn(tone, moderate, channel);
    delay(duration);
    usbMIDI.sendNoteOff(tone, moderate, channel);
    delay(between);
  }
}

void slide(uint8_t note, int amount, int duration) {
  // play a note, then pitchbend it for the given duration (in ms)
  // amount is negative for bend down, positive for bend up, range +-8k
  int steps;            // number of pitchbend messages we will send
  int bendstep;     // change in pitchbend value per step
  int i;                    // loop counter
  int thisbend;      // signed pitchbend amount for this step 
  uint16_t value;  // unsigned pitchbend amount (zero is 8k)
  // send pitchbend every 50ms so as not to overload the MIDI processor
  steps = duration / 50;
  bendstep = amount / steps;
  usbMIDI.sendNoteOn(note, moderate, channel);
  delay(100);
  for (i=1; i<=steps; i++) {
    thisbend = bendstep * i;
    value = 0x2000 + thisbend;
    usbMIDI.sendPitchBend(value, channel);
    delay(50);
  }
  usbMIDI.sendNoteOff(note, moderate, channel);
}

The note on and off was correct but the pitchbend was not; looking at the output in MIDI-OX, note-on commands were being sent instead, with the high and low bytes of the pitchbend becomig the note and velocity respectively.

Comparing the relevant portions of hardware/teensy/cores/usbmidi/usbapi.cpp:
Code:
void usb_midi_class::sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel)
{
	send_raw(0x08, 0x80 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F);
}
void usb_midi_class::sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel)
{
	send_raw(0x09, 0x90 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F);
}
void usb_midi_class::sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel)
{
	send_raw(0x0A, 0xA0 | ((channel - 1) & 0x0F), note & 0x7F, pressure & 0x7F);
}
void usb_midi_class::sendControlChange(uint8_t control, uint8_t value, uint8_t channel)
{
	send_raw(0x0B, 0xB0 | ((channel - 1) & 0x0F), control & 0x7F, value & 0x7F);
}
void usb_midi_class::sendProgramChange(uint8_t program, uint8_t channel)
{
	send_raw(0x0C, 0xC0 | ((channel - 1) & 0x0F), program & 0x7F, 0);
}
void usb_midi_class::sendAfterTouch(uint8_t pressure, uint8_t channel)
{
	send_raw(0x0D, 0xD0 | ((channel - 1) & 0x0F), pressure & 0x7F, 0);
}
void usb_midi_class::sendPitchBend(uint16_t value, uint8_t channel)
{
	send_raw(0x0E, 0xE0 | ((channel - 1) & 0x0F), value & 0x7F, (value >> 7) & 0x7F);
}

with hardware/teensy/cores/teensy3/usb_midi.h:

Code:
        void sendNoteOff(uint32_t note, uint32_t velocity, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x8008 | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((velocity & 0x7F) << 24));
	}
        void sendNoteOn(uint32_t note, uint32_t velocity, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x9009 | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((velocity & 0x7F) << 24));
	}
        void sendPolyPressure(uint32_t note, uint32_t pressure, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x9009 | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((pressure & 0x7F) << 24));
	}
        void sendControlChange(uint32_t control, uint32_t value, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x9009 | (((channel - 1) & 0x0F) << 8)
		  | ((control & 0x7F) << 16) | ((value & 0x7F) << 24));
	}
        void sendProgramChange(uint32_t program, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x9009 | (((channel - 1) & 0x0F) << 8)
		  | ((channel & 0x7F) << 16));
	}
        void sendAfterTouch(uint32_t pressure, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x9009 | (((channel - 1) & 0x0F) << 8)
		  | ((pressure & 0x7F) << 16));
	}
        void sendPitchBend(uint32_t value, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x9009 | (((channel - 1) & 0x0F) << 8)
		  | ((value & 0x7F) << 16) | ((value & 0x3F80) << 17));
	}

shows what looks like a copy-and-paste error; the value 0x9009 (which corresponds to the two values 0x09, 0x90 for the Teensy 2 code) has been propogated from NoteOn to the other functions. The fix is therefore trivial:

Code:
        void sendNoteOff(uint32_t note, uint32_t velocity, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x8008 | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((velocity & 0x7F) << 24));
	}
        void sendNoteOn(uint32_t note, uint32_t velocity, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x9009 | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((velocity & 0x7F) << 24));
	}
        void sendPolyPressure(uint32_t note, uint32_t pressure, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xA00A | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((pressure & 0x7F) << 24));
	}
        void sendControlChange(uint32_t control, uint32_t value, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xB00B | (((channel - 1) & 0x0F) << 8)
		  | ((control & 0x7F) << 16) | ((value & 0x7F) << 24));
	}
        void sendProgramChange(uint32_t program, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xC00C | (((channel - 1) & 0x0F) << 8)
		  | ((channel & 0x7F) << 16));
	}
        void sendAfterTouch(uint32_t pressure, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xD00D | (((channel - 1) & 0x0F) << 8)
		  | ((pressure & 0x7F) << 16));
	}
        void sendPitchBend(uint32_t value, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xE00E | (((channel - 1) & 0x0F) << 8)
		  | ((value & 0x7F) << 16) | ((value & 0x3F80) << 17));
	}

I have made that change locally and verified that it works.
 
Last edited:
Oppps, yes, a copy-n-paste error indeed.

The last couple days I've been testing the PC-to-Teensy3 MIDI stuff. I'm planning to publish beta9 later today, with MIDI enabled. It'll have this fixed.
 
there is another bug in usbMidi.sendProgramChange.

where it says

void sendProgramChange(uint32_t program, uint32_t channel) __attribute__((always_inline)) {
usb_midi_write_packed(0xC00C | (((channel - 1) & 0x0F) << 8)
| ((channel & 0x7F) << 16));
it must say:

void sendProgramChange(uint32_t program, uint32_t channel) __attribute__((always_inline)) {
usb_midi_write_packed(0xC00C | (((channel - 1) & 0x0F) << 8)
| ((program & 0x7F) << 16));
 
Back
Top