This README file contains the design notes and limitations of the C++ library code that lets Arduino sketches and C/C++ programs use the Arduino MIDI Library with the usb_midi_host library and the TinyUSB Library (Arduino sketches require the Adafruit TinyUSB Library).
The Arduino MIDI Library provides an API that performs most of the MIDI byte-level parsing and encoding that applications need. Applications such as synthsizers and MIDI controllers are probably easier to implement using that library than by using the usb_midi_host library alone. Interface bridge devices that require no MIDI parsing or MIDI filter applications that require a great deal of parsing anyway probably do not benefit from it.
The code in this project should run on any TinyUSB supported processor that runs the USB MIDI Host Library.
This documentation is for version 2.0.0 of this library or later. The previous versions used a different API.
First, you must install the usb_midi_host library in your file
system at the same directory level as this project. That is,
if this library source is installed in directory EZ_USB_MIDI_HOST
,
then the directory of the usb_midi_host library must be
EZ_USB_MIDI_HOST/../usb_midi_host
. You must also make sure
you have the pico-sdk installed correctly, that the TinyUSB library
is up to date, and if your hardware requires it, the Pico_PIO_USB
library is installed. See the Building C/C++ applications section
of the usb_midi_host README
for more information.
Finally, you must install the Arduino MIDI Library
at the same directory level as this library and the usb_midi_host library.
In the project's CMakeLists.txt
target_link_libraries
section,
add the EZ_USB_MIDI_HOST
library. The library interface
in cmake will pull in other library dependencies it needs.
See the examples in examples/C-Code
for examples. If you are
using the Pico PIO USB Library to implement the USB Host,
see the EZ_USB_MIDI_HOST_PIO_example
for other details.
First, use the Library Manager to install this library and install all of its dependencies (Adafruit TinyUSB Arduino Library, the Arduino MIDI Library, the usb_midi_host Library). Next, if your hardware requires it, install the Pico_PIO_USB library.
Adding #include "EZ_USB_MIDI_HOST.h"
to your sketch should be sufficient
to integrate your Arduino sketch with this library and all of its dependencies.
If you are using the Pico PIO USB Library to implement the host, you must
also add #include "pio_usb.h"
for #include "EZ_USB_MIDI_HOST.h"
.
See the EZ_USB_MIDI_HOST_PIO_example
for other details.
The Arduino MIDI Library has a Transport class interface that expects one Transport object per bidirectional MIDI stream. USB MIDI devices are complex because each one can support up to 16 unique bidirectional MIDI streams on so-called virtual MIDI cables. If you connect a USB hub to the host port, you can have up to the maximum supported number of hub ports devices connected. Finally, USB MIDI devices can be connected and disconnected while the program is running.
To make this complexity more manageable, this library provides the following software components layered as follows:
application | |
EZ_USB_MIDI_HOST | |
EZ_USB_MIDI_HOST_Device | |
MIDI Library | EZ_USB_MIDI_HOST_Transport |
TinyUSB | usb_midi_host_app_driver |
The application interacts with a single EZ_USB_MIDI_HOST
object.
The EZ_USB_MIDI_HOST
object has as many EZ_USB_MIDI_HOST_Device
objects as there are hub ports. Each device has a configurable
number of MIDI Library
bidirectional streams; each stream has
a EZ_USB_MIDI_HOST_Transport
interface. EachEZ_USB_MIDI_HOST_Transport
object interacts with the
TinyUSB
library supplemented by the usb_midi_host_app_driver
.
To create an instance of the EZ_USB_MIDI_HOST class for your
program, you should use the RPPICOMIDI_EZ_USB_MIDI_HOST_INSTANCE()
macro. The first argument is name of the EZ_USB_MIDI_HOST object
the macro defines. The second argument is the settings class to
apply to the object. See the CONFIGURATION and IMPLEMENTATION DETAILS
section for more information.
In practice, assuming the name of the EZ_USB_MIDI_HOST instance
the code creates is usbhMIDI
, then main loop's body looks like this
// Update the USB Host
USBHost.task(); // Arduino, comment out or delete for C++
// tuh_task(); // C++, comment out or delete for Arduino
// Handle any incoming data; triggers MIDI IN callbacks
usbhMIDI.readAll();
// Do other processing that might generate pending MIDI OUT data
// For example, insert code here that sends Note On/Off messages.
// Tell the USB Host to send as much pending MIDI OUT data as possible
usbhMIDI.writeFlushAll();
// Do other non-USB host processing
// For example blink an LED
Note that this loop must call usbhMIDI.writeFlushAll()
after generating
about 16 USB MIDI packets or else the transmitter buffers will overflow.
The application still has to keep track of what devices are connected.
To do this, it should implement the ConnectCallback
function to
record the USB device address of the attached MIDI device and to
register MIDI IN callbacks for the supported messages. It should also
implement the DisconnectCallback
function to unregister the MIDI IN
callbacks associated with the disconnected device address and
to forget the device address of the unplugged MIDI device.
All the Setup()
function of the main application has to do is call the library's begin()
function to specify the USB Host port to use, and
the pointers to the ConnectCallback
and DisconnectCallback
functions
See the Hardware section of the usb_midi_host
REAMDE
for the different hardware configurations.
Each of the 4 example program does the same thing:
- play a 5 note sequence on MIDI cable 0
- print out every MIDI message it receives on cable 0.
The only difference among the example programs is C/C++ vs. Arduino, and native USB host hardware vs. Pico_PIO_USB USB host hardware.
To build the rp2040 C/C++ examples, install the pico-sdk and all required libraries in your build environment.
Then enter these commands
cd [insert example program directory name here]
mkdir build
cd build
cmake ..
make
This will generate the .uf2
file that you can load to
your rp2040 board in the normal way.
To run the Arduino examples, in the Arduino IDE, use the library manager to install this library and all of its dependencies. If your hardware requires it, install the Pico_PIO_USB library. Next select File->Examples->EZ_USB_MIDI_HOST->arduino->[example program name]. A new sketch window will open up. See the Building Arduino Applications section of the usb_midi_host README for for setting up the board parameters. Note that native rp2040 hardware example directs Serial output to the Serial1 port, which is rp2040 UART0.
Because the Arduino IDE's build system does not support configuring libraries using preprocessor macros and constants defined in files in the sketch directory, version 2.0.0 and later of this library redefines the API for using this class to make it possible for applications to configure the library without editing the library configuration files directly.
The EZ_USB_MIDI_HOST class, as well as the classes it uses, are
implemented as template classes that depend on a settings class. The
settings class must be the MidiHostSettingsDefault
struct defined in EZ_USB_MIDI_HOST_Config.h
or a subclass of it. The
MidiHostSettingsDefault
struct is itself a subclass of the Arduino
MIDI Library's DefaultSettings
class. If you need to change one of
the settings in EZ_USB_MIDI_HOSTor in the Arduino MIDI Library, then please define a subclass of
MidiHostSettingsDefaultand pass this class as an argument to the
RPPICOMIDI_EZ_USB_MIDI_HOST_INSTANCE`
macro. You may overload any field the settings structure.
For example, let's say that your application needs to send and
receive System Exclusive messages that are 146 bytes long (excluding
the 0xF0 start of SysEx message and 0xF7 end of SysEx message bytes).
The default settings in MidiHostSettingsDefault
assume that the
longest SysEx message is 128 bytes long. Your application would
have to create a new settings class to make buffers large enough
to for the underlying usb_midi_host library to handle the longer
SysEx messages, and it would have to tell the Arduino MIDI library
that it needs to handle 128 bytes SysEx payloads.
struct MidiHostSettingsDefault : public MIDI_NAMESPACE::DefaultSettings
{
static const unsigned SysExMaxSize = 146; // for MIDI Library
static const unsigned MidiRxBufsize = RPPICOMIDI_EZ_USB_MIDI_HOST_GET_BUFSIZE(SysExMaxSize);
static const unsigned MidiTxBufsize = RPPICOMIDI_EZ_USB_MIDI_HOST_GET_BUFSIZE(SysExMaxSize);
};