Arduino MIDI Library

repository·master·Indexed 23 days ago

https://github.com/fortyseveneffects/arduino_midi_library

A comprehensive library for implementing MIDI I/O communications on Arduino boards. It supports various transport layers including Serial, USB, Bluetooth, and IP. The library provides a core engine for processing MIDI messages, featuring a callback system for handling incoming data, methods for sending NoteOn/Off and ControlChange messages, and utilities for encoding and decoding 8-bit data to 7-bit SysEx format, including support for Korg-specific encoding.

Tokens
2.9K
Snippets
5
Records
16
Agent score
82%

What's inside arduino_midi_library

  1. How MIDI Transports work

    master

    The library acts as a core engine for MIDI processing. While the original specification uses hardware Serial, version 5 allows you to swap the transport layer to use different protocols. This makes it easy to build bridges between different transport types (e.g., Serial to BLE).

    Supported transport layers include:

    • USB-MIDI (via Arduino-USB-MIDI)
    • AppleMIDI / rtpMIDI (via Arduino-AppleMIDI-Library)
    • ipMIDI (via ipMIDI)
    • BLE-MIDI (via Arduino-BLE-MIDI)

    Note on Software Thru: Software Thru is enabled by default when using the Serial transport, but it is not enabled by default on other transport layers.

  2. Quickstart: Send and receive MIDI messages

    master

    To use the library with the default hardware Serial port, include <MIDI.h>, use the MIDI_CREATE_DEFAULT_INSTANCE() macro, and call MIDI.begin() in setup(). Use MIDI.sendNoteOn() to transmit messages and MIDI.read() in the loop() to process incoming data.

    #include <MIDI.h>
    
    // Create and bind the MIDI interface to the default hardware Serial port
    MIDI_CREATE_DEFAULT_INSTANCE();
    
    void setup()
    {
        MIDI.begin(MIDI_CHANNEL_OMNI);  // Listen to all incoming messages
    }
    
    void loop()
    {
        // Send note 42 with velocity 127 on channel 1
        MIDI.sendNoteOn(42, 127, 1);
    
        // Read incoming messages
        MIDI.read();
    }
  3. Migrate from MIDI Library 4.x to 5.x for USB MIDI

    master

    In version 5.x, USB MIDI support has been moved to a separate repository (Arduino-USB-MIDI) which depends on this library and the MIDIUSB library.

    To migrate, you only need to change how the MIDI object is declared. Replace the manual UsbTransport typedef and MIDI_CREATE_INSTANCE call with the new USBMIDI_CREATE_DEFAULT_INSTANCE() macro.

    // 4.3.1 code:
    #include <MIDI.h>
    #include <midi_UsbTransport.h>
    
    static const unsigned sUsbTransportBufferSize = 16;
    typedef midi::UsbTransport<sUsbTransportBufferSize> UsbTransport;
    
    UsbTransport sUsbTransport;
    
    MIDI_CREATE_INSTANCE(UsbTransport, sUsbTransport, MIDI);
    
    // ...
    
    // 5.x code:
    #include <USB-MIDI.h>
    USBMIDI_CREATE_DEFAULT_INSTANCE();
    
    // ...
  4. Handle Korg-specific SysEx encoding/decoding

    master

    Korg devices use a variation of the SysEx encoding convention where the bit order in the header byte is reversed.

    To support Korg devices, use the inFlipHeaderBits argument in the decodeSysEx function and set it to true. This ensures the header byte is interpreted correctly according to Korg's implementation.

    void handleSysEx(byte* inData, unsigned inSize)
    {
        // SysEx body data starts at 3rd byte: F0 42 aa bb cc dd F7
        // 42 being the hex value of the Korg SysEx ID.
        const unsigned dataStartOffset   = 2;
        const unsigned encodedDataLength = inSize - 3; // Remove F0 42 & F7
    
        // Create a large enough buffer where to decode the message
        byte decodedData[64];
    
        const unsigned decodedSize = decodeSysEx(inData + dataStartOffset,
                                                 decodedData,
                                                 encodedDataLength,
                                                 true); // flip header bits
        // Do stuff with your message
    }
  5. Encode and decode 8-bit data to 7-bit SysEx

    master

    The MIDI library provides functions to convert arbitrary 8-bit wide data into 7-bit wide SysEx format (and vice versa). This follows the official FileDump data exchange specification where every 7 bytes of 8-bit data are converted into 8 bytes of MIDI stream data by using the top bits of each byte to construct a header byte.

    Use midi::encodeSysEx to convert 8-bit data to SysEx, and midi::decodeSysEx to revert SysEx back to 8-bit data.

    #include <MIDI.h>
    
    static const byte myData[12] = {
        // Hex dump: CAFEBABE BAADF00D FACADE42
        0xca, 0xfe, 0xba, 0xbe, 0xba, 0xad, 0xf0, 0x0d,
        0xfa, 0xca, 0xde, 0x42
    };
    
    byte encoded[16];
    const unsigned encodedSize = midi::encodeSysEx(myData, encoded, 12);
    // Encoded hex dump: 07 4a 7e 3a 3e 3a 2d 70 07 0d 7a 4a 5e 42
    
    byte decoded[12];
    const unsigned decoded = midi::decodeSysEx(encoded, decoded, encodedSize);
  6. Configure MIDI BaudRate via DefaultSerialSettings

    master

    The DefaultSerialSettings struct defines the default communication speed. By default, the BaudRate is set to 31250, which is the standard MIDI baud rate.

    To use the library with software like Hairless MIDI (which requires a higher baud rate to bridge Serial to USB MIDI), you can define your own settings struct with a different BaudRate (e.g., 115200) and pass it to MIDI_CREATE_CUSTOM_INSTANCE.

    struct DefaultSerialSettings
    {
        static const long BaudRate = 31250;
    };
  7. Create a default MIDI instance using MIDI_CREATE_DEFAULT_INSTANCE

    master

    The MIDI_CREATE_DEFAULT_INSTANCE macro provides a quick way to set up a MIDI instance using standard defaults. The behavior depends on your hardware:

    • USB-capable boards (e.g., Leonardo, Due): Uses HardwareSerial on Serial1 and names the instance MIDI.
    • Standard AVR boards: Uses HardwareSerial on Serial and names the instance MIDI.

    This is useful for maintaining compatibility with older sketches or when you do not require custom naming or ports.

  8. Create a custom MIDI instance with specific settings using MIDI_CREATE_CUSTOM_INSTANCE

    master

    If you need to override default settings (such as the BaudRate), use the MIDI_CREATE_CUSTOM_INSTANCE macro. This allows you to pass a custom settings struct to the MIDI interface.

    Syntax: MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings)

    • Type: The class of the serial port.
    • SerialPort: The specific instance of the serial port.
    • Name: The name for your MIDI instance.
    • Settings: A struct defining custom settings (e.g., a custom BaudRate).
  9. Encode and Decode System Exclusive (SysEx) messages

    master

    The library provides utility functions to wrap and unwrap SysEx messages with appropriate headers and boundaries.

    • encodeSysEx(inData, outSysEx, inLength, [inFlipHeaderBits]): Encodes raw data into a SysEx formatted buffer.
    • decodeSysEx(inSysEx, outData, inLength, [inFlipHeaderBits]): Decodes a SysEx formatted buffer into raw data.

    Both functions return the number of bytes processed.

  10. Read and handle incoming MIDI messages

    master

    To process incoming MIDI data, you must call read() in your main loop. You can then retrieve the message details using getter methods or by using the callback system.

    Polling Method:

    1. Call MIDI.read() (returns true if a message was successfully read).
    2. Use getters to extract data:
      • getType(): Returns the MidiType.
      • getChannel(): Returns the Channel.
      • getData1(): Returns the first data byte.
      • getData2(): Returns the second data byte.
      • getSysExArray(): Returns the pointer to the SysEx buffer.
      • getSysExArrayLength(): Returns the length of the SysEx buffer.

    Callback Method:

    Instead of polling, you can register specific callback functions for different MIDI message types using setHandle... methods. This allows the library to automatically trigger your functions when a message is parsed.

    void myNoteOnHandler(const MidiMessage& message) {
      // Handle note on
    }
    
    void setup() {
      MIDI.begin();
      MIDI.setHandleNoteOn(myNoteOnHandler);
    }
    
    void loop() {
      MIDI.read(); // Triggers the callback
    }
  11. Initialize the MidiInterface class

    master

    The MidiInterface class is the primary entry point for the library. It is a template class that abstracts the hardware interface by requiring a Transport type. The Transport class must implement begin(), read(), write(), and available(). Common transports include HardwareSerial or SoftwareSerial.

    To use it, instantiate the class with your chosen transport and call .begin() to initialize the MIDI interface.