Skip to content

Repository files navigation

midi2duino

USB MIDI 2.0 for classic Arduino boards. On the stock core, with nothing else to install.

logo_midi2duino

C99 transport over PluggableUSB: no LUFA, no TinyUSB, no extra USB stack. Zero UMP dependency, MIT. AVR and classic SAMD.

License: MIT MIDI 2.0 Arduino Platform Sponsor


The library

midi2duino turns a classic Arduino board into a USB MIDI 2.0 device using only the core it already ships with. Through PluggableUSB (the same extension the Arduino ecosystem already uses to add USB functions), it plugs a dual alternate-setting MIDIStreaming interface (alt 0 = USB-MIDI 1.0, alt 1 = USB-MIDI 2.0 / UMP) and its Group Terminal Block into the core's own USB, then pumps whole Universal MIDI Packets between the bulk endpoints and two word rings.

No LUFA, no TinyUSB, nothing to install beyond the board core. Your sketch sees only 32-bit UMP words in and out.

Where it fits

midi2duino is a transport. It carries UMP; it does not interpret it. It has no opinion about what the messages mean, answers no MIDI-CI, and owns no device identity. Pair it with a UMP library or your own code:

   your sketch                notes, controllers, device logic
   UMP library                parse / build UMP, MIDI-CI, Stream discovery
   midi2duino   <- you are here: UMP words <-> USB wire
   Arduino core (PluggableUSB) USB device stack
   AVR / SAMD                  hardware

The reference pairing is the midi2 C99 core, which builds and parses UMP and runs MIDI-CI on top of this transport.

Quick start

The node self-registers at startup, so a sketch needs no USB setup:

#include <midi2duino.h>

void setup() {}

void loop() {
    midi2duino_task();                    // pump the endpoints
    uint32_t w;
    while (midi2duino_read(&w)) {          // handle inbound UMP
        midi2duino_write_word(w);          // (this echoes it back)
    }
}

Open the device in a MIDI 2.0 host (Linux with a 6.5+ kernel exposes a /dev/snd/ump* node; Windows via MIDI Services; the MIDI 2.0 Workbench on any platform). See examples/Echo.

API

Function Purpose
midi2duino_task() pump both endpoints once per loop()
midi2duino_read(&word) pop one received UMP word, false if empty
midi2duino_write(words, n) queue a whole UMP message atomically
midi2duino_write_word(word) queue one word (one-word message types only)
midi2duino_configured() true once the host has configured the device

Multi-word messages are never split across a USB packet: the pump reads the message type's word count and only flushes a message when it fits whole. When the RX ring fills, the OUT bank is left unread and the hardware NAKs, which is the correct USB flow control, so the host simply retries.

Always-UMP

midi2duino always speaks UMP. The descriptors advertise both alternate settings so the device enumerates correctly everywhere, and a MIDI 2.0 host selects alt 1 and receives the full protocol. The stock Arduino cores do not surface the host's alternate-setting choice to PluggableUSB, so alt 0 (legacy MIDI 1.0) is not a separate runtime path here. Target a MIDI 2.0 host.

Supported hardware

  • AVR (ATmega32U4): Arduino Leonardo, Pro Micro, Micro. Uses arduino:avr.
  • Classic SAMD (SAMD21): Seeed XIAO SAMD21, MKR, Zero. Uses Seeeduino:samd or arduino:samd.

Boards whose core routes USB through TinyUSB (RP2040, ESP32, Adafruit SAMD/nRF52) already have a MIDI 2.0 path through TinyUSB; use midi2cpp there instead.

Install

Add the library to the Arduino IDE with Sketch > Include Library > Add .ZIP Library (download this repository as a ZIP), or clone it into your libraries/ folder. The Echo sketch then appears under File > Examples > midi2duino.

Build and upload as usual, or from the command line:

arduino-cli compile --upload -p /dev/ttyACM0 --fqbn arduino:avr:leonardo examples/Echo

Configuration

Defaults build a working device. Override before building with a -D flag if needed.

Macro Default Meaning
MIDI2DUINO_RING_WORDS 64 depth of each UMP word ring
MIDI2DUINO_EP_SIZE 64 bulk packet size (full speed)

Identity

Under PluggableUSB the core owns the device descriptor: the USB VID, PID, and manufacturer / product strings come from the board's build properties, not from this library. To brand the device, set them with a boards.local.txt next to the core's boards.txt, or inline with arduino-cli:

--build-property build.vid=0x1209 --build-property build.pid=0x0001 \
--build-property 'build.usb_product="My MIDI 2.0 Device"'

Without an override the device enumerates with the board's stock Arduino VID/PID. The UMP Endpoint Name and the MIDI-CI identity live in the UMP layer above this transport, not here.

What this is not

  • Not a USB stack. The Arduino core is, and stays untouched.
  • Not a UMP parser or builder. That is the layer above; midi2duino moves opaque words.
  • Not a MIDI-CI or Property Exchange implementation. Those are application concerns.
  • Not a host. Device side only.

Related projects

  • midi2 — the portable C99 UMP + MIDI-CI core. The natural layer above this transport.
  • midi2cpp — a C++17 wrapper over midi2 for TinyUSB platforms.

Sponsor

You can sponsor midi2duino at GitHub Sponsors. Sponsorship funds boards for cross-platform validation, spec access, and continued maintenance.

About

midi2duino is created and maintained by Saulo Veríssimo. It is the classic-Arduino-core member of the midi2 family, validated on hardware (ATmega32U4 and SAMD21).

Specifications and trademarks

The MIDI 2.0 specifications referenced here are copyright of the MIDI Association and available at https://midi.org/midi-2-0.

"MIDI" is a registered trademark of the MIDI Manufacturers Association (now MIDI Association). "MIDI 2.0", "MIDI-CI", and "UMP" are terms defined by the MIDI Association in the public specifications.

License

MIT. Free for commercial and open-source use.

About

USB MIDI 2.0 device transport for classic Arduino cores.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages