Forum Rule: Always post complete source code & details to reproduce any issue!
Page 2 of 2 FirstFirst 1 2
Results 26 to 41 of 41

Thread: More uniform APIs between MIDI, USB MIDI and USBHost_t36

  1. #26
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    I'm working right now to add the many callbacks the MIDI 4.3.1 has which Teensyduino's usbMIDI lacks.

    Teensy has 2 callbacks which MIDI 4.3.1 doesn't.

    We do a different sysex callback that provides partial results when the buffer fills, with a 3rd parameter telling you whether this is the last part of the message. MIDI 4.3.1 uses a 2 param callback and just truncates the message if larger than the buffer.

    We have a single setHandleRealTimeSystem() handler for all 6 realtime messages, contributed some time ago by Sebastian Tomczak. While it consumes 24 bytes of RAM and a little more code space, I'm going to add MIDI's 6 individual handlers too. Seems a bit redundant, but at least it should allow programs written for MIDI 4.3.1 to "just work" with Teensy's usbMIDI.

    My plan is to call the more specific realtime handlers, if they've been set, and otherwise fall back to the catch-all one.

    Likewise for sysex, if the partial handler is set, it will be used. The MIDI 4.3.1 compatible one that truncates will be used only if the partial handler isn't defined.

  2. #27
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    Ok, I've added the callbacks. I believe this brings Teensy USB MIDI up to all the callbacks of MIDI 4.3.1.

    https://github.com/PaulStoffregen/co...bfdee6ddf3d3e2

  3. #28
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    Here's the painful change. usbMIDI.getType() now returns the same codes the Arduino MIDI library 4.3.1, and which match the codes from the MIDI 1.0 protocol.

    https://github.com/PaulStoffregen/co...1fe857d3ca6613

    This will break existing programs that look for codes 0 to 7 (defined by the very old Arduino MIDI library version 2.5) which Teensy has used for so long.

  4. #29
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,103
    Quote Originally Posted by PaulStoffregen View Post
    My plan is to call the more specific realtime handlers, if they've been set, and otherwise fall back to the catch-all one.

    Likewise for sysex, if the partial handler is set, it will be used. The MIDI 4.3.1 compatible one that truncates will be used only if the partial handler isn't defined.
    I saw that in the commit, and think these were a good choices.

  5. #30
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    Can anyone recommend a simple program, ideally Linux-based (alsa, not jack), for sending all/any possible MIDI messages for testing purpose?

  6. #31
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,103
    On Windows I would use MIDIOX.

    For Linux, perhaps this list of Linux MIDI monitors is helpful? There is also KDIMon

  7. #32
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,103
    Are the new sysex partial and complete functions expected to be called directly? Or do you still call setHandleSysEx and it does the appropriate thing?

    I put together a test program and those two sysex handlers don't have syntax hilighting; plus I'm getting an error

    Code:
    TestUSBDeviceMIDI: In function 'void setup()':
    TestUSBDeviceMIDI:26: error: 'class usb_midi_class' has no member named 'setHandleSysExPartial'
       usbMIDI.setHandleSysExPartial(OnSysExPartial);
    This is the test program. Test environment was a fresh Arduino 1.8.5, Teensyduino 1.4.1beta 2, and the GitHub cores checked out today copied on top. If the two SysEx lines are commented out it compiles.

    Code:
    // TestUSBDeviceMIDI.ino
    // Test of USB Device receiving MIDI, prints to terminal
    // Set USB type to Serial + MIDI
    //
    // This example is in the public domain
    
    
    void setup()
    {
    	while (!Serial) ; // wait for Arduino Serial Monitor
    	Serial.println("USB Device MIDI Testing");
      
      // Channel Voice Messages, complete
    	usbMIDI.setHandleNoteOff(OnNoteOff);
    	usbMIDI.setHandleNoteOn(OnNoteOn);
      usbMIDI.setHandleVelocityChange(OnPolyPressure); // Polyphonic Aftertouch
      usbMIDI.setHandleControlChange(OnControlChange);
      usbMIDI.setHandleProgramChange(OnProgramChange);
      usbMIDI.setHandleAfterTouch(OnAfterTouch);      // Channel Aftertouch
      usbMIDI.setHandlePitchChange(OnPitchChange);
      
      // Channel Mode Messages, not specially handled
      // use Control Change handler
      
      // System Common Messages, complete 
      usbMIDI.setHandleSysExPartial(OnSysExPartial);
    //  usbMIDI.setHandleSysExComplete(OnSysExComplete);
      usbMIDI.setHandleTimeCodeQuarterFrame(OnTimeCodeQuarterFrame);
      usbMIDI.setHandleSongPosition(OnSongPosition);
      usbMIDI.setHandleSongSelect(OnSongSelect);
      usbMIDI.setHandleTuneRequest(OnTuneRequest);
    
      // System Real-Time Messages, complete 
      usbMIDI.setHandleClock(OnClock);
      usbMIDI.setHandleStart(OnStart);
      usbMIDI.setHandleContinue(OnContinue);
      usbMIDI.setHandleStop(OnStop);
      usbMIDI.setHandleActiveSensing(OnActiveSense);
      usbMIDI.setHandleSystemReset(OnSystemReset);
      // This one won't be called as all the more specific handlers are used
      usbMIDI.setHandleRealTimeSystem(OnRealTimeSystem);
    }
    
    void loop()
    {
    	usbMIDI.read();
    }
    
    void OnNoteOn(uint8_t channel, uint8_t note, uint8_t velocity)
    {
      if (velocity==0) {
        // Looks like this will never get called, as library
        // already checks for zero velocity Note On and dispatches Note Off
        Serial.print("Running Status Note Off, ch=");
        Serial.print(channel);
        Serial.print(", note=");
        Serial.print(note);
        Serial.println();
        }
      else {
    	  Serial.print("Note On, ch=");
    	  Serial.print(channel);
    	  Serial.print(", note=");
    	  Serial.print(note);
    	  Serial.print(", velocity=");
    	  Serial.print(velocity);
    	  Serial.println();
      }
    }
    
    void OnNoteOff(uint8_t channel, uint8_t note, uint8_t velocity)
    {
    	Serial.print("Note Off, ch=");
    	Serial.print(channel);
    	Serial.print(", note=");
    	Serial.print(note);
    	Serial.print(", release velocity=");
    	Serial.print(velocity);
    	Serial.println();
    }
    
    void OnPolyPressure(uint8_t channel, uint8_t note, uint8_t pressure)
    {
      Serial.print("Poly Aftertouch, ch=");
      Serial.print(channel);
      Serial.print(", note=");
      Serial.print(note);
      Serial.print(", pressure=");
      Serial.print(pressure);
      Serial.println();
    }
    
    void OnControlChange(uint8_t channel, uint8_t control, uint8_t value)
    {
    	Serial.print("Control Change, ch=");
    	Serial.print(channel);
    	Serial.print(", control=");
    	Serial.print(control);
    	Serial.print(", value=");
    	Serial.print(value);
    	Serial.println();
    }
    
    void OnProgramChange(uint8_t channel, uint8_t patchno)
    {
      Serial.print("Program Change, ch=");
      Serial.print(channel);
      Serial.print(", patch number=");
      Serial.print(patchno);
      Serial.println();
    }
    
    void OnAfterTouch(uint8_t channel, uint8_t pressure)
    {
      Serial.print("Channel Aftertouch, ch=");
      Serial.print(channel);
      Serial.print(", pressure=");
      Serial.print(pressure);
      Serial.println();
    }
    
    void OnPitchChange(uint8_t channel, int pitchbend) // int, sigh
    {
      Serial.print("Pitchbend, ch=");
      Serial.print(channel);
      Serial.print(", pitchbend=");
      Serial.print(pitchbend - 0x2000); // positive and negative bends
      Serial.println();
    }
    
    void OnSysExPartial(const uint8_t *data, uint16_t length, bool complete)
    {
      Serial.print("Sysex, of length=");
      Serial.print(length);
      (complete)? (Serial.print(" now complete")) : (Serial.print(" still going"));
      Serial.println();
    }
    
    void OnSysExComplete(uint8_t *data, unsigned int size)
    {
      Serial.print("Sysex, of size=");
      Serial.print(size);
      Serial.println();
    }
    
    void OnTimeCodeQuarterFrame(uint8_t data)
    {
      // Should split into first three bits (type) and last 4 (values)
      Serial.print("Time Code, data=");
      Serial.print(data);
      Serial.println();
    }
    
    void OnSongPosition(uint16_t beats)
    {
      Serial.print("Song Position, beat=");
      Serial.print(beats);
      Serial.println();
    }
    
    void OnSongSelect(uint8_t songnumber)
    {
      Serial.print("Song Select, song number=");
      Serial.print(songnumber);
      Serial.println();
    }
    
    void OnTuneRequest()
    {
      Serial.print("Get your oscillators in tune, please!");
      Serial.println();
    }
    
    void OnClock()
    {
      Serial.print("MIDI clock pulse. ");
      Serial.print("(Expecting 24 of these per quarter note)");
      Serial.println();
    }
    
    void OnStart()
    {
      Serial.print("Start syncing to clock pulses.");
      Serial.println();
    }
    
    void OnContinue()
    {
      Serial.print("Carry on syncing, pause is over");
      Serial.println();
    }
    
    void OnStop()
    {
      Serial.print("Stop syncing to clock pulses (for now).");
      Serial.println();
    }
    
    void OnActiveSense()
    {
      Serial.print("Active Sensing detected. ");
      Serial.print("(Expecting more of these, max 300ms apart)");
      Serial.println();
    }
    
    void OnSystemReset()
    {
      Serial.print("Have you tried turning it off, and back on again?");
      Serial.println();
    }
    
    void OnRealTimeSystem(uint8_t rtb)
    // only called if one of the more specific handlers is not defined
    {
      Serial.print("Realtime, data=");
      Serial.print(rtb);
      Serial.println();
    }
    
    // MIDI Specification at
    // https://www.midi.org/specifications/item/table-1-summary-of-midi-message
    // https://www.midi.org/specifications/item/table-2-expanded-messages-list-status-bytes
    // https://www.midi.org/specifications/item/table-4-universal-system-exclusive-messages

  8. #33
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    996
    About the 'pain' from getType....

    What about retaining the current values in getType but adding a separate function that returns the status offset (the status values minus the channel).

    Maybe getTypeOffset or whatever...

    The very slight divergence would only matter to those processing midi between the two libraries.

    If not... how long until the change would migrate to the current download?

  9. #34
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    I thought quite a lot about adding another getType function. I guess the question is whether we want the drag out the pain slowly with Teensy's API not matching MIDI 4.3.1 (and presumably future versions), or just take it all at once.

  10. #35
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    Quote Originally Posted by Nantonos View Post
    Code:
    TestUSBDeviceMIDI: In function 'void setup()':
    TestUSBDeviceMIDI:26: error: 'class usb_midi_class' has no member named 'setHandleSysExPartial'
       usbMIDI.setHandleSysExPartial(OnSysExPartial);
    It's setHandleSystemExclusive(function), where the partial vs complete/truncated version is selected by function overloading (whether you give it a 2 or 3 param function).

    I'm working today on updated examples and documentation. Here's a work-in-progress example to demonstrate all the new callbacks. Still filling in code, but it does compile as at least it should be useful for resolving the name & syntax.

    Code:
    void setup() {
      Serial.begin(115200);
      usbMIDI.setHandleNoteOff(myNoteOff);
      usbMIDI.setHandleNoteOn(myNoteOn);
      usbMIDI.setHandleAfterTouchPoly(myAfterTouchPoly);
      usbMIDI.setHandleControlChange(myControlChange);
      usbMIDI.setHandleProgramChange(myProgramChange);
      usbMIDI.setHandleAfterTouchChannel(myAfterTouchChannel);
      usbMIDI.setHandlePitchChange(myPitchChange);
      usbMIDI.setHandleSystemExclusive(mySystemExclusiveChunk);
      usbMIDI.setHandleSystemExclusive(mySystemExclusive);
      usbMIDI.setHandleTimeCodeQuarterFrame(myTimeCodeQuarterFrame);
      usbMIDI.setHandleSongPosition(mySongPosition);
      usbMIDI.setHandleSongSelect(mySongSelect);
      usbMIDI.setHandleTuneRequest(myTuneRequest);
      usbMIDI.setHandleClock(myClock);
      usbMIDI.setHandleStart(myStart);
      usbMIDI.setHandleContinue(myContinue);
      usbMIDI.setHandleStop(myStop);
      usbMIDI.setHandleActiveSensing(myActiveSensing);
      usbMIDI.setHandleSystemReset(mySystemReset);
      usbMIDI.setHandleRealTimeSystem(myRealTimeSystem);
    }
    
    void loop() {
      usbMIDI.read(); // USB MIDI receive
    }
    
    
    void myNoteOn(byte channel, byte note, byte velocity) {
      Serial.print("Note On, ch=");
      Serial.print(channel, DEC);
      Serial.print(", note=");
      Serial.print(note, DEC);
      Serial.print(", velocity=");
      Serial.print(velocity, DEC);
      Serial.println();
    }
    
    void myNoteOff(byte channel, byte note, byte velocity) {
      Serial.print("Note Off, ch=");
      Serial.print(channel, DEC);
      Serial.print(", note=");
      Serial.print(note, DEC);
      Serial.print(", velocity=");
      Serial.print(velocity, DEC);
      Serial.println();
    }
    
    void myAfterTouchPoly(byte channel, byte note, byte velocity) {
      Serial.print("AfterTouch Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", note=");
      Serial.print(note, DEC);
      Serial.print(", velocity=");
      Serial.print(velocity, DEC);
      Serial.println();
    }
    
    void myControlChange(byte channel, byte control, byte value) {
      Serial.print("Control Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", control=");
      Serial.print(control, DEC);
      Serial.print(", value=");
      Serial.print(value, DEC);
      Serial.println();
    }
    
    void myProgramChange(byte channel, byte program) {
      Serial.print("Program Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", program=");
      Serial.print(program, DEC);
      Serial.println();
    }
    
    void myAfterTouchChannel(byte channel, byte pressure) {
      Serial.print("After Touch, ch=");
      Serial.print(channel, DEC);
      Serial.print(", pressure=");
      Serial.print(pressure, DEC);
      Serial.println();
    }
    
    void myPitchChange(byte channel, int pitch) {
      Serial.print("Pitch Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", pitch=");
      Serial.print(pitch, DEC);
      Serial.println();
    }
    
    void mySystemExclusiveChunk(const byte *data, uint16_t length, bool last) {
      Serial.print("SysEx Message: ");
      printBytes(data, length);
      if (last) {
        Serial.println(" (end)");
      } else {
        Serial.println(" (to be continued)");
      }
    }
    
    void mySystemExclusive(byte *data, unsigned int length) {
      Serial.print("SysEx Message: ");
      printBytes(data, length);
      Serial.println();
    }
    
    void myTimeCodeQuarterFrame(byte data) {
    }
    void mySongPosition(uint16_t beats) {
    }
    void mySongSelect(byte songNumber) {
    }
    void myTuneRequest() {
    }
    void myClock() {
    }
    void myStart() {
    }
    void myContinue() {
    }
    void myStop() {
    }
    void myActiveSensing() {
    }
    void mySystemReset() {
    }
    void myRealTimeSystem(uint8_t realtimebyte) {
    }
    
    
    
    void printBytes(const byte *data, unsigned int size) {
      while (size > 0) {
        byte b = *data++;
        if (b < 16) Serial.print('0');
        Serial.print(b, HEX);
        if (size > 1) Serial.print(' ');
        size = size - 1;
      }
    }

  11. #36
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,103
    Quote Originally Posted by PaulStoffregen View Post
    It's setHandleSystemExclusive(function), where the partial vs complete/truncated version is selected by function overloading (whether you give it a 2 or 3 param function).

    I'm working today on updated examples and documentation. Here's a work-in-progress example to demonstrate all the new callbacks.
    Ahah, thanks for that.

    The example I posted was also intended as an example, and deals with all of the callbacks.

  12. #37
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    I've committed the new USB MIDI examples to github.

    https://github.com/PaulStoffregen/Te...209de80a782cc2

    How do these look? Anything missing? Any subtle points of MIDI missing? Should the control change look for the RPN & NPRN stuff?

  13. #38
    Senior Member
    Join Date
    Nov 2012
    Location
    Boston, MA, USA
    Posts
    1,103
    The inline comments about how to choose between the two sysex calls will be helpful, and the splitting out of clock data into SMPTE codes will also be helpful. In my example I pointed out the consequence of seeing an active sensing (you should see more, maximum interval 300ms, and if they stop you do an all notes off per spec). In my example I tried to illustrate that Stop is actually Pause (because Continue un-pauses it).

    The comparison examples show the callback style vs case statement on MIDI type well.

    I don't see anything missing.

    Once you get into working out what the different received control changes mean, including interactions like MSB LSB and higher level messages like RPN and NRPN, that is a higher level that can be built on top in my opinion.

    An example which looks for a particular RPN could be useful as self-contained example. I was thinking of Pitch Bend Sensitivity (0, 0) which lets you turn the arbitrary 0-16k pitch bend number into an actual number of semitones and cents shift. I'm back at work now but could put together such an example if you would like.

    However, for sending, a couple of utility functions to construct an RPN or NRPN (like the Francois Best MIDI library) would be nice to have and increase compatibility. But lack of that shouldn't delay release of these great improvements in the next Teensyduino beta.

  14. #39
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    Quote Originally Posted by Nantonos View Post
    a couple of utility functions to construct an RPN or NRPN (like the Francois Best MIDI library) would be nice to have and increase compatibility.
    I put all those send RPN & NRPN functions in.

  15. #40
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    I found & fixed a few bugs last night. Now I'm working to bring all this new stuff over to Teensy 2.0. Well, except 8 bit Teensy is only going to support single cable, with getCable() always returning zero.

  16. #41
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,370
    I've updated Teensy 2.0 for all the new send & receive features and new type codes.

    https://github.com/PaulStoffregen/co...1a8147dfecff54

Posting Permissions

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