Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 3 1 2 3 LastLast
Results 1 to 25 of 58

Thread: Virtual Midi Cables - 8 ports OSX and PC

  1. #1
    Junior Member
    Join Date
    Sep 2015
    Location
    London
    Posts
    8

    Virtual Midi Cables - 8 ports OSX and PC

    I've managed to get 8 virtual midi cables working over USB for Teensy 3.2/3.6 on OSX and Windows 10, don't have Linux but it should work (famous last words)...

    This thread helped start the ball rolling : thread

    Hopefully the way I've done it is fairly clean (the midi send/receive commands haven't changed ) and useful to some people.

    The files that need to be changed (OSX locations) are:

    /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/usb_desc.c
    /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/usb_midi.c
    /Applications/Arduino.app/Contents/Java/hardware/teensy/avr/cores/teensy3/usb_midi.h

    Installation:

    1/ Make a copy of these files: usb_desc.c , usb_midi.c , usb_midi.h

    On MacOs in finder goto Applications/Arduino right click show Package contents then

    /Contents/Java/hardware/teensy/avr/cores/teensy3/

    2/ replace them with the three files attached.

    Usage:

    Before sending set the virtual midi cable using usbMIDI.setMidiCable(cable). Note there is no bounds checking and cable needs to be between 0 and 7.

    int cable = 7; // cable can be from 0 to 7
    int note = 45;

    usbMIDI.setMidiCable(cable);
    usbMIDI.sendNoteOn(note,0x7F,1); // note , vel, channel

    Receiving:

    It up to you to check what virtual midi cable received the midi data and decide what to do with it...

    Serial.print("Cable, cb=");
    int cable = usbMIDI.getCable();
    Serial.print(cable, DEC);

    Example code

    The example below sends 50 note on's and 50 note off's as fast as possible when a button is pressed.
    It also writes to the serial terminal when a midi message (note on, off, pitch bend, control change etc) is received.


    Code:
    #include <Bounce.h>
    
    Bounce button2 = Bounce(2, 55); 
    
    
    void setup() {
      Serial.begin(115200);
      delay(300);
      usbMIDI.setHandleNoteOff (OnNoteOff);
      usbMIDI.setHandleNoteOn (OnNoteOn);
      usbMIDI.setHandleVelocityChange(OnVelocityChange);
      usbMIDI.setHandleControlChange(OnControlChange);
      usbMIDI.setHandleProgramChange(OnProgramChange);
      usbMIDI.setHandleAfterTouch(OnAfterTouch);
      usbMIDI.setHandlePitchChange(OnPitchChange);
       pinMode(2, INPUT_PULLUP);
    }
    
    void loop() { 
      usbMIDI.read(); // USB MIDI receive
    
      button2.update();
      int value = button2.read();
    
      if(value == LOW){
        delay(50);
        send_notes();
        delay(250);
      }
        
    }
    
    
    
    void send_notes(){
      
      int cable = 0;
      int i = 0;
     
      
         for (i=0;i<50;i++){
            for ( cable = 0; cable < 8; cable++){ 
                usbMIDI.setMidiCable(cable);
                usbMIDI.sendNoteOn(i,0x7F,1);  // note , vel, channel 
            }
           }
    
      
            for (i=0;i<50;i++){ 
              for ( cable = 0; cable < 8; cable++){ 
                  usbMIDI.setMidiCable(cable);
                  usbMIDI.sendNoteOff(i,0x00,1);  // note , vel, channel
            }
       }
      usbMIDI.send_now();
    }
    
    
    
    void OnNoteOn( byte channel, byte note, byte velocity) {
      
      Serial.print("Cable, cb=");
      int cable = usbMIDI.getCable();
      Serial.print(cable, DEC);
      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 OnNoteOff(byte channel, byte note, byte velocity) {
      Serial.print("Cable, cb=");
      Serial.print(usbMIDI.getCable(), DEC);
      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 OnVelocityChange(byte channel, byte note, byte velocity) {
    
    
      Serial.print("Cable, cb=");
      Serial.print(usbMIDI.getCable(), DEC);
      Serial.print("Velocity Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", note=");
      Serial.print(note, DEC);
      Serial.print(", velocity=");
      Serial.print(velocity, DEC);
      Serial.println();
    }
    
    void OnControlChange(byte channel, byte control, byte value) {
      Serial.print("Cable, cb=");
      Serial.print(usbMIDI.getCable(), DEC);
      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 OnProgramChange(byte channel, byte program) {
      Serial.print("Cable, cb=");
      Serial.print(usbMIDI.getCable(), DEC);
      Serial.print("Program Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", program=");
      Serial.print(program, DEC);
      Serial.println();
    }
    
    void OnAfterTouch(byte channel, byte pressure) {
      
      Serial.print("Cable, cb=");
      Serial.print(usbMIDI.getCable(), DEC);
      Serial.print("After Touch, ch=");
      Serial.print(channel, DEC);
      Serial.print(", pressure=");
      Serial.print(pressure, DEC);
      Serial.println();
    }
    
    void OnPitchChange( byte channel, int pitch) {
      uint8_t data1;
      uint8_t data2;
      uint8_t type;
      uint8_t ch;
      data1 = usbMIDI.getData1();
      data2 = usbMIDI.getData1();
      type = usbMIDI.getType();
      ch = usbMIDI.getChannel();
      Serial.print("Cable, cb=");
      Serial.print(usbMIDI.getCable(), DEC);
      
      Serial.print("Data1 = ");
      Serial.print(data1, DEC);
      Serial.print("Data2 = ");
      Serial.print(data2, DEC);
      Serial.print("Type = ");
      Serial.print(type, DEC);
      Serial.print("Channel = ");
      Serial.print(ch, DEC);
      
     
      Serial.print("Pitch Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", pitch=");
      Serial.print(pitch, DEC);
      Serial.println();
    }
    The 50 notes sent across eight ports/virtual midi cables (400 in total) were received by the software (Reaper64 in this case) in 3ms - not too shabby.

    Preliminary Testing

    - tested Arduino 1.6.12 and Teensyduino 1.32(?) and
    Arduino 1.8.0 and Teensyduino 1.34

    - left running for a couple of hours pumping midi Note on/off's across 8 ports to Ableton Live no problems.

    - works using USB profiles: Midi / Midi + Serial and Midi + Serial + Audio (audio not tested but shows up in device manager and in OSX and PC)

    - works on T3.2, T3.6 (fairly cursory - mainly 3.2)

    The need for more than one midi port/cable:

    If you are trying to implement some kinds to midi automation e.g. HUI/Mackie etc then you need more than one port. One port limits you to 8 channels. Frequently you need to be able to control up to 32 channels.

    ---

    Hope I haven't missed something...
    Attached Files Attached Files
    Last edited by BuzzBang; 01-05-2017 at 07:08 PM.

  2. #2
    Junior Member
    Join Date
    Sep 2015
    Location
    London
    Posts
    8
    Just tested and it works on iOS too... powered over USB from iPad using T3.2

  3. #3
    I've been looking for hours to find this and could only find the original thread that started this one, thank you for making this. I was wondering if it would be possible to make the number of virtual midi cables editable from inside the project code instead of having to edit the usb_desc.c everytime I switch projects?

  4. #4
    Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    98
    Thank you very much for this!
    It's exactly what I need for my DAW (Logic Pro X) plugin controller project. Following the MIDI_name example I set the device name to "ZeusDPC" (DAW Plugin Controller) and it shows up in the OS X Audio MIDI setup with 8 virtual ports:

    Name:  TeensyUsbMidi.png
Views: 258
Size:  25.2 KB

    The system exclusive message communication with Logic Pro X is also working, the Teensy 3.6 emulates a Logic Control Master unit and a Logic control XT unit:

    Click image for larger version. 

Name:	TeensyDualControl.png 
Views:	34 
Size:	71.6 KB 
ID:	12367

    Now that the basic communication is operational I can continue working on the hardware.

    Kind regards,

    Gerrit

  5. #5
    that's cool, I will maybe need more than 16 channels for a project in the future, thanks

  6. #6
    Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    98
    It seems my enthusiasm was a bit premature as I'm running into problems with the handling of system exclusive messages (sysEx) on multiple ports. Using getChannel and a switch statement like this:

    Code:
    void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete){
      uint8_t midiPort;
      boolean isMackieControlMessage = true;
      boolean isMackieControlXTMessage = true;
    
      midiPort = usbMIDI.getChannel();
      switch (midiPort) {
        // Logic Control Main on Port 0
        case 0:
          // compare the first five bytes of the message to the sysExHeader to check if it is a Mackie control message
          for (int i=0;i<5;i++) {
            if (sysExData[i]!=sysExHeader[i]) {
              isMackieControlMessage = false;
            }
          }
          // handle mackie control message
          if (isMackieControlMessage) {
            boolean processMessage = true;
            // handle different message types
            switch (sysExData[5]) {
             ............
    
        // Logic Control XT on Port 1
        case 1:
          // compare the first five bytes of the message to the sysExHeader to check if it is a Mackie control XT message
          for (int i=0;i<5;i++) {
            if (sysExData[i]!=sysExHeaderXT[i]) {
              isMackieControlXTMessage = false;
            }
           }
          // handle mackie control message
          if (isMackieControlXTMessage) {
            boolean processMessage = true;
            // handle different message types
          ...............
        break;
        // Synth control on Port 2
        case 2:
          // nothing yet
        break;
        default: 
          // ignore sysExData
        break;
      }
    }
    Results in Logic Pro not recognising one of the units. Also, only one unit will be automatically installed. Quitting and restarting Logic Pro results in the other unit being installed but the sysEx communication is only working on one unit.
    If the incoming channel is ignored everything works fine:

    Code:
    void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete){
      //uint8_t midiPort;
      boolean isMackieControlMessage = true;
      boolean isMackieControlXTMessage = true;
      for (int i=0;i<5;i++) {
        if (sysExData[i]!=sysExHeader[i]) isMackieControlMessage = false;
        if (sysExData[i]!=sysExHeaderXT[i]) isMackieControlXTMessage = false;
      }
      // handle mackie control message
      if (isMackieControlMessage) {
        boolean processMessage = true;
        // handle different message types
        switch (sysExData[5]) {
          // respond to device query
          case 0:
            usbMIDI.setMidiCable(0);
            usbMIDI.sendSysEx(18, sysExHostConnectionQuery);
            break;
        ...............
      }
      
      // handle mackie control XT message
      if (isMackieControlXTMessage) {
        boolean processMessage = true;
        // handle different message types
        switch (sysExData[5]) {
          // respond to device query
          case 0:
            usbMIDI.setMidiCable(1);
            usbMIDI.sendSysEx(18, sysExHostConnectionQueryXT);
            break;
           ..............
      }       
      
    }
    On startup Logic will recognise both units and install them both. Because I only emulate one Logic control XT unit this solves my problem but if one would like to use more extension units then this will be a serious issue. The DAW host software does not distinguish extension units (Logic Control XT or Mackie Control XT) based on a device id, each unit is supposed to be connected to a separate midi port.

    It might also be a good idea to increase the maximum sysEx length to 255 bytes. Projects using this library will probably use a Teensy with enough RAM to handle this.

    Kind regards,

    Gerrit

  7. #7
    I seem to remember also having a problem with SysEx messages when the max length isnít set to 255 bytes or when the SysEx messages arenít handled correctly with the complete boolean. But once I had the SysEx messages being handled correctly I could easily emulate all 16 ports as a Mackie device.

    If you want more or less than the 8 ports that BuzzBang has made available itís easy enough to edit his work and make if variable from 1-16 depending on a define. I just havenít figured out a way to set it from the project, but best regards with your project.

  8. #8
    Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    98
    Quote Originally Posted by vjmuzik View Post
    I seem to remember also having a problem with SysEx messages when the max length isnít set to 255 bytes or when the SysEx messages arenít handled correctly with the complete boolean. But once I had the SysEx messages being handled correctly I could easily emulate all 16 ports as a Mackie device.

    If you want more or less than the 8 ports that BuzzBang has made available itís easy enough to edit his work and make if variable from 1-16 depending on a define. I just havenít figured out a way to set it from the project, but best regards with your project.
    How did you distinguish the virtual input ports in your code?

    I've set the max length to 255, this is more than enough for all sysEx communication regarding Mackie control units so it is not necessary to handle chuncks of sysEx data. In another project I successfully handled large (16k-32k) sysEx program dumps in chuncks of 255 bytes, this will come in handy at a later stage.
    The next stage is connecting the two displays and the 16 rotary encoders, when this is completed I can see if de sysEx communication is completely functional.

    Kind regards,

    Gerrit

  9. #9
    Quote Originally Posted by Gerrit View Post
    How did you distinguish the virtual input ports in your code?

    I've set the max length to 255, this is more than enough for all sysEx communication regarding Mackie control units so it is not necessary to handle chuncks of sysEx data. In another project I successfully handled large (16k-32k) sysEx program dumps in chuncks of 255 bytes, this will come in handy at a later stage.
    The next stage is connecting the two displays and the 16 rotary encoders, when this is completed I can see if de sysEx communication is completely functional.

    Kind regards,

    Gerrit
    The way I distinguished them is a basically had one Teensy that would decode the midi message and then send it over i2c to another Teensy for each bank of channels and I would just have the port number point to an array with each i2c address and vise versa when it read data from the i2c address it would point to its corresponding port. But itís easy enough to make 3D arrays of all your buttons/leds and knobs and when ever you go to write to it just set the x of the array equal to the port number and y is the button/led or knob ID. This way you donít have to make a case test for every port.

  10. #10
    Junior Member
    Join Date
    Sep 2015
    Location
    London
    Posts
    8
    Sorry for the delay in replying. I constantly seem to be snowed under at my day job...

    Glad to see that it of use to people :-)

    vjmusik could you post your code for variable number of ports (up to 16) using a define? That would be very useful, both for me and others. I did mean to update for 16 ports but got pulled off onto other things

    SysEx was something I didn't really test. Gerrit, did the changing the max length of the buffer surtout your problems? Vjmusik can you confirm that you're tested sysEx with multiple ports successfully?

    Cheers

    BuzzBang

  11. #11
    Quote Originally Posted by BuzzBang View Post
    Sorry for the delay in replying. I constantly seem to be snowed under at my day job...

    Glad to see that it of use to people :-)

    vjmusik could you post your code for variable number of ports (up to 16) using a define? That would be very useful, both for me and others. I did mean to update for 16 ports but got pulled off onto other things

    SysEx was something I didn't really test. Gerrit, did the changing the max length of the buffer surtout your problems? Vjmusik can you confirm that you're tested sysEx with multiple ports successfully?

    Cheers

    BuzzBang
    I will have to post the code once I get back, Iím about to leave on a trip for a couple days.

    I can confirm that SysEx with multiple ports works from the initial testing Iíve done and no data was lost as far as I could tell with 16 ports and 8 channels of MCU and HUI. Though I never got around to fully emulating the Logic Control with the auto detection but I had all the LCD messages the encoder knob LEDs and the VU meter programmed for the test I did.

  12. #12
    Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    98
    Quote Originally Posted by BuzzBang View Post
    Sorry for the delay in replying. I constantly seem to be snowed under at my day job...

    Glad to see that it of use to people :-)

    vjmusik could you post your code for variable number of ports (up to 16) using a define? That would be very useful, both for me and others. I did mean to update for 16 ports but got pulled off onto other things

    SysEx was something I didn't really test. Gerrit, did the changing the max length of the buffer surtout your problems? Vjmusik can you confirm that you're tested sysEx with multiple ports successfully?

    Cheers

    BuzzBang
    No need to apologise, I'm already glad you're willing to provide support for something you made available for free :-)

    No, changing the max length does not solve the issue with the ports. It could very well be that I just need to change the code handle the incoming data differently. As far as programming in C is concerned I'm a newbie and still learning.
    The other midi events all seem to work fine.

    My setup does fully emulate a Logic control base unit and XT unit including the auto detection by Logic Pro and initial sysEx handshake. On startup Logic Pro will send device queries over all available ports to detect new devices and to wake up the connected controllers. Somehow the second query is not handled correctly when I use a case statement to distinguish the ports.

    Quote Originally Posted by vjmuzik View Post
    I will have to post the code once I get back, Iím about to leave on a trip for a couple days.

    I can confirm that SysEx with multiple ports works from the initial testing Iíve done and no data was lost as far as I could tell with 16 ports and 8 channels of MCU and HUI. Though I never got around to fully emulating the Logic Control with the auto detection but I had all the LCD messages the encoder knob LEDs and the VU meter programmed for the test I did.
    128 channels, that's a really large controller setup! May I ask what the intended use is? Any pictures you might want to share?


    Kind regards,

    Gerrit

  13. #13
    Junior Member
    Join Date
    Sep 2015
    Location
    London
    Posts
    8
    Vjmusik, that's really encouraging :-)
    No rush for me on posting the code, look forward to seeing it.

    Gerrit, Vjmusik's experience sounds very positive, I'm sure that there's a way to make it work for you.
    I don't have Logic, recently purchased Digital Performer and also have Ableton Live and Reason. Happy to look at any code you want to share, and see if it plays ball with the above.

    Cheers

    Rob

  14. #14
    Hereís a screenshot of a short video I took before I had the VU meter code in place, but I donít have video of that currently.

    Click image for larger version. 

Name:	6CE682F3-932B-421D-8053-6850E0D79E41.jpeg 
Views:	25 
Size:	179.7 KB 
ID:	12505

    This is a part of a project of mine of replacing an Uptown 990 Automation System on a Trident Vector 432. As a side note I didnít physically have 128 channels built but I saved all channels on the usb Teensy and then I had a potentiometer setup to scroll through all the channels saved on the Teensy but I had next to latency as far as I could tell with all 128 channels. I wrote the code as a proof of concept to see how much I could do on one Teensy. The results were promising since I only need about 56 channels of automation that I need to make.

    The Uptown 990 system was so obsolete because the company went under and they never released an upgrade from the old DOS pc it had to use. So I hooked up a logic analyzer to the cable running to the pc and was able to reverse engineer the i2c data they were using to communicate with the controller board in the console. I used a Teensy LC to replace the old computer and make it work with MCU and HUI.

  15. #15
    Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    98
    Quote Originally Posted by BuzzBang View Post
    Vjmusik, that's really encouraging :-)
    No rush for me on posting the code, look forward to seeing it.

    Gerrit, Vjmusik's experience sounds very positive, I'm sure that there's a way to make it work for you.
    I don't have Logic, recently purchased Digital Performer and also have Ableton Live and Reason. Happy to look at any code you want to share, and see if it plays ball with the above.

    Cheers

    Rob
    Here's a little test containing only the sysEx part:
    Code:
    // Logic Control system exclusive messages
    // changed serialnumber of Logic control from 48 to 49
    const byte sysExHeader[5]                       = {0xF0,0x00,0x00,0x66,0x10};
    const byte sysExDeviceQuery[7]                  = {0xF0,0x00,0x00,0x66,0x10,0x00,0xF7};
    const byte sysExHostConnectionQuery[18]         = {0xF0,0x00,0x00,0x66,0x10,0x01,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0x64,0x7A,0x61,0x41,0xF7};
    const byte sysExHostConnectionConfirmation[14]  = {0xF0,0x00,0x00,0x66,0x10,0x03,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0xF7};
    const byte sysExVersionReply[12]                = {0xF0,0x00,0x00,0x66,0x10,0x14,0x56,0x31,0x2E,0x30,0x32,0xF7};
    // Logic Control XT system exclusive messages
    const byte sysExHeaderXT[5]                       = {0xF0,0x00,0x00,0x66,0x11};
    const byte sysExDeviceQueryXT[7]                  = {0xF0,0x00,0x00,0x66,0x11,0x00,0xF7};
    const byte sysExHostConnectionQueryXT[18]         = {0xF0,0x00,0x00,0x66,0x11,0x01,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0x64,0x7A,0x61,0x41,0xF7};
    const byte sysExHostConnectionConfirmationXT[14]  = {0xF0,0x00,0x00,0x66,0x11,0x03,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0xF7};
    const byte sysExVersionReplyXT[12]                = {0xF0,0x00,0x00,0x66,0x11,0x14,0x56,0x31,0x2E,0x30,0x32,0xF7};
    
    void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete){
      uint8_t midiPort;
      boolean isMackieControlMessage = true;
      boolean isMackieControlXTMessage = true;
    
      midiPort = usbMIDI.getChannel();
      switch (midiPort) {
        // Logic Control Main on Port 0
        case 0:
          // compare the first five bytes of the message to the sysExHeader to check if it is a Mackie control message
          for (int i=0;i<5;i++) {
            if (sysExData[i]!=sysExHeader[i]) {
              isMackieControlMessage = false;
            }
          }
          // handle mackie control message
          if (isMackieControlMessage) {
            // handle different message types
            switch (sysExData[5]) {
              // respond to device query
              case 0:
                usbMIDI.setMidiCable(0);
                usbMIDI.sendSysEx(18, sysExHostConnectionQuery);
                break;
              // respond to host connection reply with connection confirmation
              case 2:
                usbMIDI.setMidiCable(0);
                usbMIDI.sendSysEx(14, sysExHostConnectionConfirmation);
                break;
              // respond to firmware version request
              case 19:
                usbMIDI.setMidiCable(0);
                usbMIDI.sendSysEx(12, sysExVersionReply);
                break;
              default: 
                // statements
              break;
            }
          }
          break;
        // Logic Control XT on Port 1
        case 1:
          // compare the first five bytes of the message to the sysExHeader to check if it is a Mackie control XT message
          for (int i=0;i<5;i++) {
            if (sysExData[i]!=sysExHeaderXT[i]) {
              isMackieControlXTMessage = false;
            }
           }
          // handle mackie control message
          if (isMackieControlXTMessage) {
            // handle different message types
            switch (sysExData[5]) {
              // respond to device query
              case 0:
                usbMIDI.setMidiCable(1);
                usbMIDI.sendSysEx(18, sysExHostConnectionQueryXT);
                break;
              // respond to host connection reply with connection confirmation
              case 2:
                usbMIDI.setMidiCable(1);
                usbMIDI.sendSysEx(14, sysExHostConnectionConfirmationXT);
                break;
              // respond to firmware version request
              case 19:
                usbMIDI.setMidiCable(1);
                usbMIDI.sendSysEx(12, sysExVersionReplyXT);
                break;
              default: 
                // statements
              break;
            }
          }       
        break;
        // Synth control on Port 2
        case 2:
          // nothing yet
        break;
        default: 
          // ignore sysExData
        break;
      }
    }
    
    
    
    void setup() {
      // register sysEx event handler function
      usbMIDI.setHandleSysEx(handleSysEx);
    
    
    }
    
    void loop() {
      // read usbMIDI, let the handlers do their job
      usbMIDI.read(); 
    
    }
    The code emulates a Logic Control Basic unit on port 0 and a XT unit on port 1 and will respond to device queries from a DAW. I don't know if your DAWs can work with a Logic control unit, it could be that it has to be a Mackie control. I have a Logic Control hardware unit at hand which I can switch to Mackie control mode. I'll have to take a look at the sysEx communication between the unit and the DAW when in this mode.

    Here's the complete code for the project I'm working on:Zeus-DPC.ino
    Just to be clear on this, identifying the units based on the sysEx header in stead of port number solves the problem and allows me to continue but the question remains why the identification by port (as in the example above) does not work.

    Quote Originally Posted by vjmuzik View Post
    ...This is a part of a project of mine of replacing an Uptown 990 Automation System on a Trident Vector 432.....
    Wow, that's a serious console. Very interesting project!


    Kind regards,

    Gerrit

  16. #16
    Quote Originally Posted by Gerrit View Post
    Here's a little test containing only the sysEx part:
    Code:
    // Logic Control system exclusive messages
    // changed serialnumber of Logic control from 48 to 49
    const byte sysExHeader[5]                       = {0xF0,0x00,0x00,0x66,0x10};
    const byte sysExDeviceQuery[7]                  = {0xF0,0x00,0x00,0x66,0x10,0x00,0xF7};
    const byte sysExHostConnectionQuery[18]         = {0xF0,0x00,0x00,0x66,0x10,0x01,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0x64,0x7A,0x61,0x41,0xF7};
    const byte sysExHostConnectionConfirmation[14]  = {0xF0,0x00,0x00,0x66,0x10,0x03,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0xF7};
    const byte sysExVersionReply[12]                = {0xF0,0x00,0x00,0x66,0x10,0x14,0x56,0x31,0x2E,0x30,0x32,0xF7};
    // Logic Control XT system exclusive messages
    const byte sysExHeaderXT[5]                       = {0xF0,0x00,0x00,0x66,0x11};
    const byte sysExDeviceQueryXT[7]                  = {0xF0,0x00,0x00,0x66,0x11,0x00,0xF7};
    const byte sysExHostConnectionQueryXT[18]         = {0xF0,0x00,0x00,0x66,0x11,0x01,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0x64,0x7A,0x61,0x41,0xF7};
    const byte sysExHostConnectionConfirmationXT[14]  = {0xF0,0x00,0x00,0x66,0x11,0x03,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0xF7};
    const byte sysExVersionReplyXT[12]                = {0xF0,0x00,0x00,0x66,0x11,0x14,0x56,0x31,0x2E,0x30,0x32,0xF7};
    
    void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete){
      uint8_t midiPort;
      boolean isMackieControlMessage = true;
      boolean isMackieControlXTMessage = true;
    
      midiPort = usbMIDI.getChannel();
      switch (midiPort) {
        // Logic Control Main on Port 0
        case 0:
          // compare the first five bytes of the message to the sysExHeader to check if it is a Mackie control message
          for (int i=0;i<5;i++) {
            if (sysExData[i]!=sysExHeader[i]) {
              isMackieControlMessage = false;
            }
          }
          // handle mackie control message
          if (isMackieControlMessage) {
            // handle different message types
            switch (sysExData[5]) {
              // respond to device query
              case 0:
                usbMIDI.setMidiCable(0);
                usbMIDI.sendSysEx(18, sysExHostConnectionQuery);
                break;
              // respond to host connection reply with connection confirmation
              case 2:
                usbMIDI.setMidiCable(0);
                usbMIDI.sendSysEx(14, sysExHostConnectionConfirmation);
                break;
              // respond to firmware version request
              case 19:
                usbMIDI.setMidiCable(0);
                usbMIDI.sendSysEx(12, sysExVersionReply);
                break;
              default: 
                // statements
              break;
            }
          }
          break;
        // Logic Control XT on Port 1
        case 1:
          // compare the first five bytes of the message to the sysExHeader to check if it is a Mackie control XT message
          for (int i=0;i<5;i++) {
            if (sysExData[i]!=sysExHeaderXT[i]) {
              isMackieControlXTMessage = false;
            }
           }
          // handle mackie control message
          if (isMackieControlXTMessage) {
            // handle different message types
            switch (sysExData[5]) {
              // respond to device query
              case 0:
                usbMIDI.setMidiCable(1);
                usbMIDI.sendSysEx(18, sysExHostConnectionQueryXT);
                break;
              // respond to host connection reply with connection confirmation
              case 2:
                usbMIDI.setMidiCable(1);
                usbMIDI.sendSysEx(14, sysExHostConnectionConfirmationXT);
                break;
              // respond to firmware version request
              case 19:
                usbMIDI.setMidiCable(1);
                usbMIDI.sendSysEx(12, sysExVersionReplyXT);
                break;
              default: 
                // statements
              break;
            }
          }       
        break;
        // Synth control on Port 2
        case 2:
          // nothing yet
        break;
        default: 
          // ignore sysExData
        break;
      }
    }
    
    
    
    void setup() {
      // register sysEx event handler function
      usbMIDI.setHandleSysEx(handleSysEx);
    
    
    }
    
    void loop() {
      // read usbMIDI, let the handlers do their job
      usbMIDI.read(); 
    
    }
    The code emulates a Logic Control Basic unit on port 0 and a XT unit on port 1 and will respond to device queries from a DAW. I don't know if your DAWs can work with a Logic control unit, it could be that it has to be a Mackie control. I have a Logic Control hardware unit at hand which I can switch to Mackie control mode. I'll have to take a look at the sysEx communication between the unit and the DAW when in this mode.

    Here's the complete code for the project I'm working on:Zeus-DPC.ino
    Just to be clear on this, identifying the units based on the sysEx header in stead of port number solves the problem and allows me to continue but the question remains why the identification by port (as in the example above) does not work.



    Wow, that's a serious console. Very interesting project!


    Kind regards,

    Gerrit


    The problem with the code posted above is you used usbMIDI.getChannel() by accident instead of usbMIDI.getCable() to get the port number, if you fix that then it should function correctly.

  17. #17
    Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    98
    Quote Originally Posted by vjmuzik View Post
    The problem with the code posted above is you used usbMIDI.getChannel() by accident instead of usbMIDI.getCable() to get the port number, if you fix that then it should function correctly.
    Oops, thank you for noticing this rookie mistake! This is the kind of thing you tend to overlook by yourself.

    Kind regards,

    Gerrit

  18. #18
    I forgot I had the files on my iCloud so here they are.

    USB_MIDI_Variable_Cable.zip

    To change the number of midi cables you change ď#define MIDI_CABLE 8Ē anywhere from 1-16 in the usb_names.h.
    If youíre on a Mac you wonít see the change in the Audio Midi Setup until you unplug the device and delete it from there then reconnect it. Windows is a little more complicated and probably the easiest way is to just change the serial number the same way you can change the device name just so it registers as a new device.

    As far as emulating the Logic Control I did do it in Logic but I was using the MCU in Logic Control mode protocol which is the same exact commands just the SysEx device ID is for the MCU and not the Logic Control, however on that same note the MCU protocol and the Logic Control protocol are for the most part functionally identical thereís mostly just less SysEx commands and no handshake based on the documents Iíve read. So from the limited testing I did with Logic I programmed it for the MCU in Logic Control mode and the same code worked for what I tested in Ableton.

    When Mackie designed the Logic Control protocol they used separate commands from HUI so you can have your project detect both simultaneously and not have to switch modes like the real MCU and Logic Control has to.

  19. #19
    Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    98
    I corrected my silly mistake in the code and now it's all working perfectly!

    The corrected file: Zeus-DPC.ino

    Emulating a Logic Control uit was the obvious choice as I have a hardware unit and can analyse the communication and the manual contains the complete sysEx documentation. I have used the device in MCU (Mackie Control Unit) mode together with Cubase and noticed that the behaviour with respect to plugin editing is quite different. As my goal is to build a controller with the focus on plugin editing in a DAW I'll stick to Logic Control mode for now.

    Kind regards,

    Gerrit

  20. #20
    Quote Originally Posted by Gerrit View Post
    I corrected my silly mistake in the code and now it's all working perfectly!

    The corrected file: Zeus-DPC.ino

    Emulating a Logic Control uit was the obvious choice as I have a hardware unit and can analyse the communication and the manual contains the complete sysEx documentation. I have used the device in MCU (Mackie Control Unit) mode together with Cubase and noticed that the behaviour with respect to plugin editing is quite different. As my goal is to build a controller with the focus on plugin editing in a DAW I'll stick to Logic Control mode for now.

    Kind regards,

    Gerrit
    Yeah that’s true the way the DAW will respond to MCU will be different as opposed to a Logic Control, but based on looking at the command set they are the same, the only other difference I found is that the encoder knob only has one mode in MCU whereas Logic Control has 4 modes. But yeah I understand what you mean about being about to see the exact commands your physical controller sends, I also worked on emulating a Pro Control that I have and it was pretty simple once you get the handshake part of the Ethernet message down because it mainly uses midi messages to communicate, including SysEx messages. I was also able to figure out how to reverse engineer most of the Diginet devices that Digidesign/Avid made by just changing the device ID in the Ethernet message. From what I found only the Pro Control uses standard midi messages but the rest of them are in a similar format that it’s easy enough to distinguish it.

  21. #21
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    17,624
    I'm planning to merge this 8X MIDI cable feature, and a number of other updates to bring Teensy's USB MIDI support up to date and in sync with the MIDI 4.3 library.

  22. #22
    Quote Originally Posted by PaulStoffregen View Post
    I'm planning to merge this 8X MIDI cable feature, and a number of other updates to bring Teensy's USB MIDI support up to date and in sync with the MIDI 4.3 library.
    If I’m not mistaken, doesn’t MIDI 4.3 have a usb section?

  23. #23
    Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    98
    Quote Originally Posted by PaulStoffregen View Post
    I'm planning to merge this 8X MIDI cable feature, and a number of other updates to bring Teensy's USB MIDI support up to date and in sync with the MIDI 4.3 library.
    That's great news, thank you

    King regards,

    Gerrit

  24. #24
    Junior Member
    Join Date
    Sep 2015
    Location
    London
    Posts
    8
    Quote Originally Posted by vjmuzik View Post
    I forgot I had the files on my iCloud so here they are.

    USB_MIDI_Variable_Cable.zip

    To change the number of midi cables you change ď#define MIDI_CABLE 8Ē anywhere from 1-16 in the usb_names.h.
    If youíre on a Mac you wonít see the change in the Audio Midi Setup until you unplug the device and delete it from there then reconnect it. Windows is a little more complicated and probably the easiest way is to just change the serial number the same way you can change the device name just so it registers as a new device.

    As far as emulating the Logic Control I did do it in Logic but I was using the MCU in Logic Control mode protocol which is the same exact commands just the SysEx device ID is for the MCU and not the Logic Control, however on that same note the MCU protocol and the Logic Control protocol are for the most part functionally identical thereís mostly just less SysEx commands and no handshake based on the documents Iíve read. So from the limited testing I did with Logic I programmed it for the MCU in Logic Control mode and the same code worked for what I tested in Ableton.

    When Mackie designed the Logic Control protocol they used separate commands from HUI so you can have your project detect both simultaneously and not have to switch modes like the real MCU and Logic Control has to.
    Thanks for posting your update to implement all 16 midi ports vjmuzik, great job (getting those numbers right drove me slightly mad hence I stopped when I had the eight ports I needed; straight forward(ish), but very easy to make a mistake), and apologies for not getting back to you re your earlier post.

    @Gerrit, thanks for uploading your code. If work pans out as expected I should be back in 'midi mode' in about 3-4 weeks...

    @Paul, If you can roll this into the next release that would be fantastic, vjmusik's update for 16 ports is the one to go for. I was being slightly lazy by only doing the 8 ports, but to be fair work pulled me off onto something else with higher priority.

  25. #25
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    17,624
    Quote Originally Posted by BuzzBang View Post
    @Paul, If you can roll this into the next release that would be fantastic
    I've already committed the new code on github.

    https://github.com/PaulStoffregen/co...72da6cc9bcfee7

    It's slightly different, but the same general idea. I added an extra optional parameter to the send functions for the cable number, rather than setMidiCable(). I made this decision in anticipation of a future event-driven (via EventResponder) version of usbMIDI and USBHost_t36's MIDI.

    I'm also updating the usbMIDI functions to closely match the Arduino MIDI library version 4.3.1 (latest version).

    My plan is to publish 1.41-beta3 within a few days, with MIDIx8 in the Tools > USB Type menu, so everyone can easily use it. Could really use some help testing...

Posting Permissions

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