Teensy 3.6 as USBMIDI-to-SerialMIDI adapter

Status
Not open for further replies.

XFer

Well-known member
Hello everyone,

a few weeks ago I enquired about using Teensy 3.6 to interface new USB MIDI controller keyboards to old (DIN) MIDI synths, and received great support (thanks!).

Now I have my shiny new Teensy 3.6, a proper USB cable connected to the USB Host header on the Teensy, and a USB MIDI keyboard attached to it (cheap JamminPro PK25). :)

To get things started, I tested the nice "InputMethods" sample sketch from "USBHost_t36" library (from brand new Teensyduino 1.41), and I can see the various MIDI messages being properly received and decoded by the Teensy on the Serial Monitor.
So everything seems to work OK.

Now the (two) questions.

I am building a simpler version of this board:
https://www.pjrc.com/teensy/td_libs_MIDI.html
with only MIDI OUT port (that's all I need) and the needed corrections to work at 3.3v

I need to write a sketch which:

- detects if a MIDI USB Keyboard is attached to USB Host port (needed to give user feedback via status LED)
- "blindly/dummily" forwards *all* incoming USB Host MIDI messages (without handling them) to the (serial) UART MIDI output port

I'm not sure how to procede.

1) To start, I can't seem to find a method equivalent to, say "isMidiDeviceAttached(USBHostPort)".

2) Then, the "InputMessages" sketch works by settings a lot of handlers and "doing stuff" inside the handlers (writing debug to serial monitor); I don't need to decode/handle MIDI messages, I just want to forward them as is; but I don't seem to find a way to get a MIDI message from

Code:
MIDIDevice midiUSBInput(USBHostPort)

and write it as is to the output MIDI serial port defined by

Code:
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDISerialOutPort)

Do I really need to explicitely handle every possible MIDI incoming message and then, inside each handler, use for example

Code:
MIDISerialOutPort.sendNoteOn(...)

etc. etc.?

I mean, there should be a way to do say

Code:
midiMessage = midiUSBInput.readNextMessage();
MIDISerialOutPort.sendMessage(midiMessage);

...right? :confused:

Thanks a lot!!

Fernando
 
Last edited:
- detects if a MIDI USB Keyboard is attached to USB Host port (needed to give user feedback via status LED)

Good news, this part is really easy. Just use the name you defined as a boolean. When a device is connected, it is true, and when no device is connected it is false. For example, if you used "midi1" (as the examples do), then:

Code:
  if (midi1) {
    // a device is connected
    digitalWrite(13, HIGH);
  } else {
    // nothing connected
    digitalWrite(13, LOW);
  }


- "blindly/dummily" forwards *all* incoming USB Host MIDI messages (without handling them) to the (serial) UART MIDI output port

The Interface_16x16 examples does this, forwarding both serial and host-connected device to your PC. But the process is basically the same to forward message from any of them to any of the others, just read() from the device and send() to the serial MIDI. Starting with 1.41, all three use the same read() and send(), so you can just adapt the example with only minimal edits.
 
If you are forwarding from the Jammin Pro, it doesn't appear to generate a wide variety of MIDI messages. It looks like handling Note on/off, Control Change and Program Change are all that's required which would only require four of the callbacks.

What does the midi output port send to?

Pete
 
I've tested the program below using an M-Audio 61es keyboard on the USB Host port and an SY77 synthesizer on Serial1. This allows the synthesizer to be played from the 61-es.

Pete

Code:
/* Receive Incoming USB Host MIDI using functions.  As usbMIDI
   reads incoming messages, handler functions are run.
   See the InputRead example for the non-function alterative.

   This very long example demonstrates all possible handler
   functions.  Most applications need only some of these.
   This example is meant to allow easy copy-and-paste of the
   desired functions.

   Use the Arduino Serial Monitor to view the messages
   as Teensy receives them by USB MIDI

   You must select MIDI from the "Tools > USB Type" menu

   This example code is in the public domain.

   180120 Modified by Pete (El Supremo) and still Public Domain
   Forward all Note on/off, Control Change and Program Change
   messages from the USB Host port to a MIDI device on
   hardware Serial1 (Pin 1)
*/

#include <USBHost_t36.h>

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
MIDIDevice midi1(myusb);

#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);

void setup(void)
{
  Serial.begin(115200);

  // Wait 1.5 seconds before turning on USB Host.  If connected USB devices
  // use too much power, Teensy at least completes USB enumeration, which
  // makes isolating the power issue easier.
  delay(1500);
  Serial.println("USB Host InputFunctions example");
  delay(10);
  myusb.begin();

  pinMode(LED_BUILTIN,OUTPUT);
  digitalWrite(LED_BUILTIN,0);
  // Wait for the USB Host to be connected.
  while(!midi1);
  // indicate that the port is connected
  digitalWrite(LED_BUILTIN,1);

  // I think these are all that are required to handle
  // a JAMMIN Pro PK25 keyboard but it's easy to add more
  // if required.
  midi1.setHandleNoteOn(myNoteOn);
  midi1.setHandleNoteOff(myNoteOff);
  midi1.setHandleControlChange(myControlChange);
  midi1.setHandleProgramChange(myProgramChange);

  // Start the Serial1 device
  MIDI.begin(MIDI_CHANNEL_OMNI);
}

void loop(void)
{
  // The handler functions are called when midi1 reads data.  They
  // will not be called automatically.  You must call midi1.read()
  // regularly from loop() for midi1 to actually read incoming
  // data and run the handler functions as messages arrive.
  myusb.Task();
  midi1.read();
}


void myNoteOn(byte channel, byte note, byte velocity)
{
  MIDI.sendNoteOn(note, velocity, channel);
}

void myNoteOff(byte channel, byte note, byte velocity)
{
  MIDI.sendNoteOff(note, velocity, channel);
}

void myControlChange(byte channel, byte control, byte value)
{
  MIDI.sendControlChange(control, value, channel);
}

void myProgramChange(byte channel, byte program)
{
  MIDI.sendProgramChange(program,channel);
}
 
Hello Pete,
thanks for chiming in.
Of course I don't want to support just the Jammin Pro PK25 (which anyway sends at least 5 message types); ideally I want my adapter to work with every (MIDI-class compliant) USB keyboard.
Your approach is the one I referred to in my post ("Do I really need to explicitely handle every possible MIDI incoming message and then, inside each handler...").
I don't see why we should decode and handle every type of MIDI message just to forward them to the serial Midi out.
The approach suggested by Paul and adopted in the "interface_16x16" example seems more general and straightforward. :)
 
OK, I see what you mean.
In my situation, I am modifying some of the MIDI messages, primarily changing the channel of the 61-es from whatever it is sending to 2. Then sending everything from the SY77, 61es and a couple of buttons to the PC.

Pete
 
Either approach is fine. There are only 18 message types, and really only 7 if you don't care about the system messages. The generic forwarding approach still does involve checking for sysex and a little typecast trick to convert the message type byte to the enum MIDI.h uses. Especially if you only want the 7 regular messages (and 2 are rarely used aftertouch), I can see how handlers are conceptually simpler and actually about the same number of lines of code.
 
Status
Not open for further replies.
Back
Top