Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 4 of 4

Thread: Teensy 3.0 and USB MIDI (problem and fix)

  1. #1
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,107

    Teensy 3.0 and USB MIDI (problem and fix)

    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 by Nantonos; 12-05-2012 at 02:36 PM.

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,301
    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.

  3. #3
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,107
    Confirming that Teensy beta9 gives the correct results with the above code, on Teensy 3.0.

  4. #4
    Junior Member
    Join Date
    Feb 2013
    Posts
    1
    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));

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •