Git Product home page Git Product logo

ez_usb_midi_host's Introduction

EZ_USB_MIDI_HOST

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.

Adding this library to your project

C/C++ Programs

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.

Arduino

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.

EZ_USB_MIDI_HOST Library Design

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.

Writing Applications

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

EXAMPLE PROGRAMS

Hardware

See the Hardware section of the usb_midi_host REAMDE for the different hardware configurations.

Software

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.

C/C++ Examples

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.

Arduino Examples

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.

LIBRARY CONFIGURATION and IMPLEMENTATION DETAILS

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 ofMidiHostSettingsDefaultand pass this class as an argument to theRPPICOMIDI_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);
};

ez_usb_midi_host's People

Contributors

rppicomidi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

ez_usb_midi_host's Issues

Do some configuration at run-time

Arduino libraries do not include files from the application sketch directory, and touching library configuration files in the libraries directory changes the configuration for all applications. Try instead to do at least some configuration at run-time. See rppicomidi/usb_midi_host#13 for example

No serial output from EZ_USB_MIDI_HOST_PIO_example.ino

Hi there,

Thanks for your hard work. I tried running EZ_USB_MIDI_HOST_PIO_example.ino with a RP2040 Adafruit USB host board and a normal Pico Board but I'm unable to get any serial logs, not even Serial.println("EZ USB MIDI HOST PIO Example for Arduino\r\n");. The LED blinks fine. Weirdly enough I tried running usb_midi_host_pio_example.ino from the usb_midi_host library which does have serial output but does not respond when I plug in a MIDI keyboard. This has been kinda frustrating as I've gotten usb_midi_host_pio_example.ino to work before a couple of weeks ago, but I have update some of the dependencies libraries which seems to have broken it. Hope you can give me some advice!

My settings are as follow

image

I did try various level of debug but didn't see any difference

Multiple libraries were found for "Adafruit_TinyUSB.h"
 Used: C:\Users\han\Documents\Arduino\libraries\Adafruit_TinyUSB_Library
 Not used: C:\Users\han\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.9.0\libraries\Adafruit_TinyUSB_Arduino
Using library Pico_PIO_USB at version 0.6.0 in folder: C:\Users\han\Documents\Arduino\libraries\Pico_PIO_USB 
Using library EZ_USB_MIDI_HOST at version 2.0.0 in folder: C:\Users\han\Documents\Arduino\libraries\EZ_USB_MIDI_HOST 
Using library MIDI_Library at version 5.0.2 in folder: C:\Users\han\Documents\Arduino\libraries\MIDI_Library 
Using library Adafruit_TinyUSB_Library at version 3.3.0 in folder: C:\Users\han\Documents\Arduino\libraries\Adafruit_TinyUSB_Library 
Using library SPI at version 1.0 in folder: C:\Users\han\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.9.0\libraries\SPI 
Using library usb_midi_host at version 1.1.1 in folder: C:\Users\han\Documents\Arduino\libraries\usb_midi_host 

EZ_USB_MIDI_HOST_PIO_example.ino stops handling MidiInCallbacks after ~ 3 Seconds of MIDI Device Inactivity

Hi,

I really appreciate this library, as it provides an excellent starting point for my own project, which is to create a MIDI message filter. I intend to instrument EZ_USB_MIDI_HOST on my Adafruit Feather RP2040 with USB Type A Host, connect a MIDI device to the USB Type A Host connector on the Feather and a PC with a software synthesizer on the USB Type C connector on the Feather.

However, I can not even make the given example, e.g. EZ_USB_MIDI_HOST_PIO_example.ino run as expected.

My development environment

  • Fedora Linux, Version 40
  • Arduino IDE, Version 2.3.2
  • EZ_USB_MIDI_HOST, Version 2.0.0
  • PICO PIO USB, Version 0.5.3, #4
  • Adafruit Feather RP2040 with USB Type A Host

Things that work as expected

 // Sets pin USB_HOST_5V_POWER to HIGH to enable USB power
pinMode(18, OUTPUT); 
digitalWrite(18, HIGH);
  • Serial monitor in Arduino IDE output diagnostic strings, incl. diagnostics regarding incoming MIDI messages, e.g.
21:27:11.549 -> EZ USB MIDI HOST PIO Example for Arduino
21:27:11.549 -> Core1 setup to run TinyUSB host with pio-usb
21:27:12.065 -> MIDI device at address 1 has 1 IN cables and 1 OUT cables
21:27:12.808 -> C1: Note on#51 v=16
21:27:13.001 -> C1: Note on#51 v=0
21:27:13.130 -> C1: Note on#51 v=28
21:27:13.259 -> C1: Note on#51 v=0
21:27:13.422 -> C1: Note on#51 v=11
21:27:13.583 -> C1: Note on#51 v=0

Things that do NOT work as expected

  • Once the connected MIDI device does not send MIDI events for more than ~ 3 seconds, the Arduino software EZ_USB_MIDI_HOST_PIO_example.ino stops handling MidiInCallbacks, e.g. onNoteOn, onNoteOff, but also onMIDIdisconnect, etc.

  • The MIDI device cannot be hot-plugged, i.e. it must be connected to the USB Type A port before Feather is powered up (which is less important for now but might be an issue as well).

How to proceed

  • I would not rule out mistakes in compiling the sources or configuring the Feather. So please make suggestions and challenge my approach.
  • I would not exclude that my expectations are wrong. So please challenge them as well.
  • If more information is needed to help me run the example, please give me detailed instructions to follow. My project really depends on this library, and I would like to give you the best feedback I can.

Thanks, Sören

USBHost interferes with SerialPIO RX

Not sure whose issue this is, but I'd rather place it here and work with you on finding the solution than spam it to a bunch of possible locations.

Issue: If USBHost is instantiated before SerialPIO receive, the USBHost will work but PIO RX will not. If the order is reversed, PIO RX will work but USBHost will not.

Regardless of order, creating a SerialPIO object with only TX will work always.

Using Arduino, EZ USB MIDI Host 2.0

Example sketch:

/* Test program for PIO Serial and USB Host interaction

*/

#if defined(USE_TINYUSB_HOST) || !defined(USE_TINYUSB)
#error "Please use the Menu to select Tools->USB Stack: Adafruit TinyUSB"
#endif
#include "pio_usb.h"
#define HOST_PIN_DP   6   // Pin used as D+ for host, D- = D+ + 1
#include "EZ_USB_MIDI_HOST.h"

#include <Adafruit_TinyUSB.h>
#include <MIDI.h>

// USB Host object
Adafruit_USBH_Host USBHost;

USING_NAMESPACE_MIDI
USING_NAMESPACE_EZ_USB_MIDI_HOST

RPPICOMIDI_EZ_USB_MIDI_HOST_INSTANCE(usbhMIDI, MidiHostSettingsDefault)
static uint8_t midiDevAddr = 0;


SerialPIO mySerial(28, 29); // both pins into same serial does not work

static bool core0_booting = true;
static bool core1_booting = true;


/* CONNECTION MANAGEMENT */
static void onMIDIconnect(uint8_t devAddr, uint8_t nInCables, uint8_t nOutCables)
{
  Serial.printf("MIDI device at address %u has %u IN cables and %u OUT cables\r\n", devAddr, nInCables, nOutCables);
  midiDevAddr = devAddr;
  //  registerMidiInCallbacks();
}

static void onMIDIdisconnect(uint8_t devAddr)
{
  Serial.printf("MIDI device at address %u unplugged\r\n", devAddr);
  midiDevAddr = 0;
}


/* MAIN LOOP FUNCTIONS */

static void tick(void)
{

  // repeating timer just to show we're alive
  const uint32_t intervalMs = 1000;
  static uint32_t startMs = 0;

  static bool ledState = false;
  if ( millis() - startMs < intervalMs)
    return;
  startMs += intervalMs;

  ledState = !ledState;

  // send MIDI data out, program change 0 or 3 on ch1
  mySerial.write(0xC0);
  mySerial.write(ledState * 3);
  //
  //  pioTx.write(0xC0);
  //  pioTx.write(ledState * 3);

  Serial.println("tick");

}


// core1's setup
void setup1() {
  delay(2000);   // wait for native usb
  Serial.println("Core1 setup to run TinyUSB host with pio-usb");

  // Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
  uint32_t cpu_hz = clock_get_hz(clk_sys);
  if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
    delay(2000);   // wait for native usb
    Serial.printf("Error: CPU Clock = %u, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
    Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n", cpu_hz);
    while (1) delay(1);
  }

  pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
  pio_cfg.pin_dp = HOST_PIN_DP;

  USBHost.configure_pio_usb(1, &pio_cfg);

  // run host stack on controller (rhport) 1
  // Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
  // host bit-banging processing works done in core1 to free up core0 for other works
  usbhMIDI.begin(&USBHost, 1, onMIDIconnect, onMIDIdisconnect);

  core1_booting = false;
  while (core0_booting) ;
}

// core1's loop
void loop1()
{
  USBHost.task();
}

void setup()
{
  Serial.begin(115200); // init USB serial

  // if SerialPIO MIDI is initialized before USB HOST,
  // USB HOST does not work

//#define USBFIRST // comment this to boot serial first

#ifndef USBFIRST
  Serial.println("SERIAL FIRST");
  mySerial.begin(31250);
  //  pioTx.begin(31250);
  //  pioRx.begin(31250);

#endif
  delay(4000);   // wait for serial port

  Serial.println("TinyUSB MIDI Host Example");

  core0_booting = false;
  while (core1_booting) ;

  // if SerialPIO MIDI is initialized after USB HOST,
  // SerialPIO does not work

#ifdef USBFIRST
  Serial.println("USB FIRST");
  mySerial.begin(31250);
  //  pioTx.begin(31250);
  //  pioRx.begin(31250);
#endif

  Serial.println("DONE BOOTING");

}

void loop() {
  // Handle any incoming data; triggers MIDI IN callbacks
  usbhMIDI.readAll();

  // Tell the USB Host to send as much pending MIDI OUT data as possible
  usbhMIDI.writeFlushAll();

// test routine for combined RX and TX
  if (mySerial.available() ) {
    // read the incoming byte:
    char incomingByte = mySerial.read();

    // say what you got:
    Serial.print("I received: ");
    Serial.println(incomingByte, DEC);
  }

// test routine for separate RX
//  if (pioRx.available() ) {
//    // read the incoming byte:
//    char incomingByte = pioRx.read();
//
//    // say what you got:
//    Serial.print("I received: ");
//    Serial.println(incomingByte, DEC);
//  }


  tick();
}

I'm able to detect connect / disconnect if I start the USBHost first, but I don't get any data over the SerialPIO RX. If I swap the order, I don't get USB Host connect / disconnect. My guess is that there is some conflict between the PIO assignments, but I'm lost as to how to resolve this.

EZ_USB_MIDI_HOST_PIO_example.ino does not Recognize MIDI Devices Attached to Host-Connector

Hi,

I really appreciate this library, as it provides an excellent starting point for my own project, which is to create a MIDI message filter. I intend to instrument EZ_USB_MIDI_HOST on my Adafruit Feather RP2040 with USB Type A Host, connect a MIDI device to the USB Type A Host connector on the Feather and a PC with a software synthesizer on the USB Type C connector on the Feather.

However, I can not even make the given example, e.g. EZ_USB_MIDI_HOST_PIO_example.ino run as expected.

My development environment

  • Fedora Linux, Version 40
  • Arduino IDE, Version 2.3.2
  • EZ_USB_MIDI_HOST, Version 2.0.0
  • Adafruit Feather RP2040 with USB Type A Host

Things that work as expected

  • Compilation and upload of EZ_USB_MIDI_HOST_PIO_example.ino without any modifications
  • Blinking of red LED (LED_BUILTIN) on Feather, when Feather is connected to PC via USB Type C connector
  • Serial monitor in Arduino IDE output diagnostic strings, e.g.
    19:34:36.589 -> EZ USB MIDI HOST PIO Example for Arduino
    19:34:36.589 -> Core1 setup to run TinyUSB host with pio-usb
  • Green LED indicating 5V power supply on USB Type A Host connector

Things that do NOT work as expected

  • Recognition of MIDI controller by EZ_USB_MIDI_HOST and provisioning of subsequent functionality, e.g. receiving and processing MIDI events.

I would expect the serial monitor to output diagnostic strings on onMIDIconnect and onMIDIdisconnect events whenever I connect or disconnect a MIDI device using the Feathers USB Type A connector. However, this is not the case even though my MIDI devices are powered and working. I have tested with two different MIDI devices, e.g. Arturia MicroLab and ESI Xjam without success.

How to proceed

  • I would not rule out mistakes in compiling the sources or configuring the Feather. So please make suggestions and challenge my approach.
  • I would not exclude that my expectations are wrong. So please challenge them as well.
  • If more information is needed to help me run the example, please give me detailed instructions to follow. My project really depends on this library, and I would like to give you the best feedback I can.

Thanks, Sören

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.