PDA

View Full Version : Teensy 3.0 and USB MIDI (problem and fix)



Nantonos
12-05-2012, 03:25 PM
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:

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.


// 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:


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:


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:


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.

PaulStoffregen
12-05-2012, 06:09 PM
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.

Nantonos
12-06-2012, 03:29 PM
Confirming that Teensy beta9 gives the correct results with the above code, on Teensy 3.0.

kopiller
02-02-2013, 12:39 PM
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));