Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f175b58
Reject unknown-SSRC MIDI and stop the RTP parser hanging on bad comma…
lathoub Aug 17, 2026
2fac894
Parse Bitrate Receive Limit on listeners so initiator RL packets do n…
lathoub Aug 17, 2026
75e3200
Update rtpMIDI_Parser_JournalSection.hpp
lathoub Aug 17, 2026
870ac98
fix: 16-bit sequence wrap looks like packet loss
lathoub Aug 17, 2026
385fef0
fix Session timeout uses 32-bit millis (must be 64bit)
lathoub Aug 17, 2026
c3f53d6
fix for Outgoing MIDI is gated on the last UDP source
lathoub Aug 17, 2026
6cb30f2
UDP datagrams are treated as a byte stream
lathoub Aug 17, 2026
ff23a5b
fix Invitation parser consumes the rest of the buffer
lathoub Aug 17, 2026
79a28c0
ONE_PARTICIPANT + APPLEMIDI_INITIATOR did not compile
lathoub Aug 17, 2026
3d6cec2
Next is outgoing SysEx continuation (wrong front()) and silent inMidi…
lathoub Aug 17, 2026
0e54957
system-journal length underflow, real data UDP port, and wrap-safe se…
lathoub Aug 17, 2026
4729239
Endian test is always little-endian
lathoub Aug 17, 2026
53c1665
bump to 3.5.0
lathoub Aug 17, 2026
3badb0f
CI does not build ESP examples
lathoub Aug 17, 2026
13d212a
Gate unused journal parse and outbound RS so small sketches can drop …
lathoub Aug 17, 2026
c6baf52
drop MIDI 2.0
lathoub Aug 17, 2026
3fcf7e0
Match invitation reject by initiator token so a NO before control OK …
lathoub Aug 17, 2026
4cb0536
Do not queue MIDI or send RTP until a participant has a real SSRC.
lathoub Aug 17, 2026
2ae387f
Hold RTP until the data-port handshake sets remoteDataPort instead of…
lathoub Aug 17, 2026
f26b74b
Parse invitation reject names like IN/OK so a long Mac name does not …
lathoub Aug 17, 2026
6ba845e
Document that extra F0/F7 on large SysEx is RFC 6295 segmentation, no…
lathoub Aug 17, 2026
931b33e
Document 3.5.0, APPLEMIDI_SMALL, and align the license badge and keyw…
lathoub Aug 17, 2026
fd584da
State the license as Creative Commons Attribution-ShareAlike 4.0 thro…
lathoub Aug 17, 2026
17b0a17
Update AppleMIDI_Defs.h
lathoub Aug 18, 2026
e1fc6cd
remove Ethernet3
lathoub Aug 18, 2026
ab010f8
Update build-arduino.sh
lathoub Aug 18, 2026
9ac1927
Update ESP32_DynamicInstantiation.ino
lathoub Aug 18, 2026
dc8ff5d
remove references to Ethernet3
lathoub Aug 18, 2026
469fd35
skip test using bespoke PIN nrs
lathoub Aug 18, 2026
e7c9c22
Revise README for OS compatibility and installation
lathoub Aug 19, 2026
131148e
Update README with GitHub release badge
lathoub Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Build on Arduino CLI
run: bash ci/build-arduino.sh
4 changes: 2 additions & 2 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
The hardware is released under Creative Commons Share-alike 4.0.
This work is licensed under Creative Commons Attribution-ShareAlike 4.0 International.

All other code is open source so please feel free to do anything you want with it; you buy me a beer if you use this and we meet someday (Beerware license).
https://creativecommons.org/licenses/by-sa/4.0/
41 changes: 28 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
# AppleMIDI (aka rtpMIDI) for Arduino [![License: CC BY-SA 4.0](https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg)](http://creativecommons.org/licenses/by-sa/4.0/)
# AppleMIDI (aka rtpMIDI) for Arduino

[![GitHub release](https://img.shields.io/github/release/lathoub/Arduino-AppleMIDI-Library.svg?maxAge=3600)](https://github.com/lathoub/Arduino-AppleMIDI-Library/releases/latest)
[![License: CC BY-SA 4.0](https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg)](http://creativecommons.org/licenses/by-sa/4.0/)

Enables an Arduino with IP/UDP capabilities (Ethernet shield, ESP8266, ESP32, ...) to participate in an AppleMIDI session.

**Important:** Please read the [note below](https://github.com/lathoub/Arduino-AppleMIDI-Library#ethernet-buffer-size) on enlarging the standard Ethernet library buffersize to avoid dropping MIDI messages!

## Features
* Build on top of the popular [FortySevenEffects MIDI library](https://github.com/FortySevenEffects/arduino_midi_library)
* Tested with AppleMIDI on Mac OS (Big Sur) and using [rtpMIDI](https://www.tobias-erichsen.de/software/rtpmidi.html) from Tobias Erichsen on Windows 10
* Tested with AppleMIDI on Mac OS (Tahoe) and using [rtpMIDI](https://www.tobias-erichsen.de/software/rtpmidi.html) from Tobias Erichsen on Windows 10 & 11.
* Send and receive all MIDI messages
* Uses callbacks to receive MIDI commands (no need for polling)
* Automatic instantiation of AppleMIDI object (see at the end of 'AppleMidi.h')
* Automatic instantiation of AppleMIDI object (see at the end of 'AppleMIDI.h')
* Compiles on Arduino, MacOS (XCode) and Windows (MSVS)

### New in 3.2.0
* Event chaining

### New in 3.3.0
* Better parsing of large incoming MIDI messages with a small internal Arduino buffer
### New in 3.5.0
* Safer session and RTP parsing (known-SSRC MIDI only, UDP datagram isolation, wrap-safe sequence numbers)
* RTP is sent only after the data-port handshake; invitation reject matches by initiator token
* Size gates: `APPLEMIDI_SMALL`, `ONE_PARTICIPANT`, `NO_SESSION_NAME` (see [Memory footprint](#memory-footprint))
* Large SysEx is split per RFC 6295 (`F0`/`F7`); see [SysEx and extra F0/F7 bytes](#sysex-and-extra-f0f7-bytes)

## Installation
From the Arduino IDE Library Manager, search for AppleMIDI
Clone locally or download from the Arduino IDE Library Manager, search for AppleMIDI

<img width="786" alt="Installation" src="https://github.com/lathoub/Arduino-AppleMIDI-Library/blob/master/res/Install3-1.PNG">

Expand Down Expand Up @@ -62,6 +65,7 @@ More usages in the [examples](https://github.com/lathoub/Arduino-AppleMIDI-Libra
* ESP32 (Adafruit HUZZAH32 – ESP32 Feather Board) Wi-Fi
* ESP32 with W5500 [Setup](https://github.com/lathoub/Arduino-AppleMIDI-Library/discussions/135)
* ESP32 ETH W5500 (Native ESP32 W5500 support)
* Seeed Studio XIAO ESP32 C3
* Teensy 3.2 & 4.1
* Adafruit Feather M0 WiFi - ATSAMD21 + ATWINC1500

Expand All @@ -75,22 +79,33 @@ More usages in the [examples](https://github.com/lathoub/Arduino-AppleMIDI-Libra

## Notes

### SysEx and extra F0/F7 bytes

Outgoing SysEx that does not fit in [`MaxMidiOutSize`](https://github.com/lathoub/Arduino-AppleMIDI-Library/blob/v3.5.0/src/AppleMIDI_Settings.h) (default 64) is split across RTP packets per [RFC 6295](https://www.rfc-editor.org/rfc/rfc6295.html#section-3): the current packet ends with `F0` and the next starts with `F7`. USB, serial, and BLE do not add those markers, so a capture can look malformed ([#169](https://github.com/lathoub/Arduino-AppleMIDI-Library/issues/169)). AppleMIDI/rtpMIDI receivers reassemble them. Raise `MaxMidiOutSize` (and usually `MaxBufferSize`) in a custom Settings struct if you need a larger SysEx in one command section.

### Session names

Session names can get really long on Macs (eg 'Macbook Pro of Johann Gambolputty .. von Hautkopft of Ulm') and will be truncated to the [`MaxSessionNameLen`](https://github.com/lathoub/Arduino-AppleMIDI-Library/blob/af4c7bd9a960a90e09e211f0ea00db2d9832d1f7/src/AppleMIDI_Settings.h#L13)
Session names can get really long on Macs (eg 'Macbook Pro of Johann Gambolputty .. von Hautkopft of Ulm') and will be truncated to [`MaxSessionNameLen`](src/AppleMIDI_Settings.h).

### Memory footprint
The memory footprint of the library can be lowered significantly, read the [wiki](https://github.com/lathoub/Arduino-AppleMIDI-Library/wiki/Memory-footprint)
Define these in the sketch **before** `#include <AppleMIDI.h>`:

* `ONE_PARTICIPANT` — one remote peer (saves a participant slot)
* `NO_SESSION_NAME` — do not store session names (~100 bytes)
* `APPLEMIDI_SMALL` — skip unused journal parsing and outbound RS
(`APPLEMIDI_PARSE_JOURNALS` / `APPLEMIDI_KEEP_RECEIVER_FEEDBACK` keep those while still using `APPLEMIDI_SMALL`)

`APPLEMIDI_SMALL` is used in `examples/AVR_MinMemUsage`. More on the [wiki](https://github.com/lathoub/Arduino-AppleMIDI-Library/wiki/Memory-footprint).

### Ethernet buffer size
It's highly recommended to modify the [Ethernet library](https://github.com/arduino-libraries/Ethernet) or use the [Ethernet3 library](https://github.com/sstaub/Ethernet3) to avoid buffer overruns - [learn more](https://github.com/lathoub/Arduino-AppleMIDI-Library/wiki/Enlarge-Ethernet-buffer-size-to-avoid-dropping-UDP-packages)
It's highly recommended to modify the [Ethernet library](https://github.com/arduino-libraries/Ethernet) - [learn more](https://github.com/lathoub/Arduino-AppleMIDI-Library/wiki/Enlarge-Ethernet-buffer-size-to-avoid-dropping-UDP-packages)

### Latency
Use wired Ethernet to reduce latency, Wi-Fi increases latency and latency varies. More of the [wiki](https://github.com/lathoub/Arduino-AppleMIDI-Library/wiki/Keeping-Latency-under-control)

## Arduino IDE (arduino.cc)
* 1.8.16
* 2.3.7
* 2.3.10

## Contributing
I would love to include your enhancements or bug fixes! In lieu of a formal styleguide, please take care to maintain the existing coding style. Please test your code before sending a pull request. It would be very helpful if you include a detailed explanation of your changes in the pull request.
39 changes: 22 additions & 17 deletions ci/build-arduino.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,45 @@ export PATH=$PATH:$GITHUB_WORKSPACE/bin
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
arduino-cli config init
arduino-cli config set library.enable_unsafe_install true
arduino-cli core update-index --additional-urls https://arduino.esp8266.com/stable/package_esp8266com_index.json
arduino-cli core update-index --additional-urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
arduino-cli config add board_manager.additional_urls https://arduino.esp8266.com/stable/package_esp8266com_index.json
arduino-cli config add board_manager.additional_urls https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
arduino-cli core update-index

# Install Arduino AVR core
# Install cores
arduino-cli core install arduino:avr
arduino-cli core install arduino:samd
# arduino-cli core install arduino:esp8266
# arduino-cli core install esp32:esp32:esp32doit-devkit-v1
arduino-cli core install esp8266:esp8266
arduino-cli core install esp32:esp32

# Link Arduino library
ln -s $GITHUB_WORKSPACE $HOME/Arduino/libraries/CI_Test_Library

arduino-cli lib install Ethernet
arduino-cli lib install "MIDI library"
arduino-cli lib install --git-url https://github.com/sstaub/Ethernet3.git
arduino-cli lib install EthernetBonjour

# Compile all *.ino files for the Arduino Uno
for f in **/AVR_*.ino ; do
arduino-cli compile -b arduino:avr:uno $f
done
# Uno has 2 KiB SRAM; skip the full AVR_* set (e.g. AVR_MultipleSessions).
echo "Compiling examples/AVR_NoteOnOffEverySec/AVR_NoteOnOffEverySec.ino for arduino:avr:uno"
arduino-cli compile -b arduino:avr:uno examples/AVR_NoteOnOffEverySec/AVR_NoteOnOffEverySec.ino

# Compile all *.ino files for the Arduino Zero
for f in **/SAMD_*.ino ; do
echo "Compiling $f for arduino:samd:mkrzero"
arduino-cli compile -b arduino:samd:mkrzero $f
done

# Compile all *.ino files for the ESP8266
# for f in **/ESP8266_*.ino ; do
# arduino-cli compile -b arduino:esp8266:??? $f
# done
for f in **/ESP8266_*.ino ; do
echo "Compiling $f for esp8266:esp8266:generic"
arduino-cli compile -b esp8266:esp8266:generic $f
done

# Compile all *.ino files for the ESP32
# for f in **/ESP32_*.ino ; do
# arduino-cli compile -b arduino:esp32:??? $f
# done
# Compile ESP32 examples. Skip DynamicInstantiation: it needs a W5500 helper
# (D5/D7 pins) and is not a generic esp32:esp32:esp32 target.
for f in **/ESP32_*.ino ; do
case "$f" in
*ESP32_DynamicInstantiation*) continue ;;
esac
echo "Compiling $f for esp32:esp32:esp32"
arduino-cli compile -b esp32:esp32:esp32 $f
done
50 changes: 0 additions & 50 deletions doc/NetworkMIDI2_UMP.md

This file was deleted.

78 changes: 0 additions & 78 deletions examples/AVR_E3_NoteOnOffEverySec/AVR_E3_NoteOnOffEverySec.ino

This file was deleted.

1 change: 1 addition & 0 deletions examples/AVR_MinMemUsage/AVR_MinMemUsage.ino
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#define ONE_PARTICIPANT
#define NO_SESSION_NAME
#define APPLEMIDI_SMALL
#include <AppleMIDI.h>

// Enter a MAC address for your controller below.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <WiFi.h>

#include "ETH_Helper.h"
#include "ETH_helper.h"

#define SerialMon Serial
#include "midiHelpers.h"
Expand Down
23 changes: 1 addition & 22 deletions examples/ESP32_DynamicInstantiation/ETH_helper.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
#ifdef ETHERNET3
#include <Ethernet3.h>
#else
#include <Ethernet.h>
#include <EthernetBonjour.h> // https://github.com/TrippyLighting/EthernetBonjour
#endif

// to get the Mac address
#include "esp_mac.h"
Expand Down Expand Up @@ -34,17 +30,9 @@ void hardreset() {

bool ETH_startup()
{
#ifdef ETHERNET3
Ethernet.setRstPin(RESET_PIN);
Ethernet.setCsPin(CS_PIN);
Ethernet.init(4); // maxSockNum = 4 Socket 0...3 -> RX/TX Buffer 4k
Serial.println("Resetting Wiz W5500 Ethernet Board... ");
Ethernet.hardreset();
#else
Ethernet.init(CS_PIN);
Serial.println("Resetting Wiz Ethernet Board... ");
hardreset();
#endif

esp_read_mac(mac, ESP_MAC_WIFI_STA);

Expand All @@ -56,12 +44,9 @@ bool ETH_startup()
comments to the contrary elsewhere). You
-must- supply a MAC address here.
*/
#ifdef ETHERNET3
Serial.println("Starting Ethernet3 connection...");
#else
Serial.println("Starting Ethernet connection...");
//if (Ethernet.hardwareStatus() == EthernetNoHardware) Serial.println("No EthernetHW");
#endif

Ethernet.begin(mac);
Serial.print("Ethernet IP is: ");
Serial.println(Ethernet.localIP());
Expand All @@ -72,11 +57,7 @@ bool ETH_startup()
Serial.println("Checking connection.");
bool rdy_flag = false;
for (uint8_t i = 0; i <= 20; i++) {
#ifdef ETHERNET3
if ((Ethernet.link() == 0)) {
#else
if ((Ethernet.linkStatus() == Unknown)) {
#endif
Serial.print(".");
rdy_flag = false;
delay(80);
Expand All @@ -94,7 +75,6 @@ bool ETH_startup()
Serial.println("OK");
}

#ifndef ETHERNET3
// Initialize the Bonjour/MDNS library. You can now reach or ping this
// Arduino via the host name "arduino.local", provided that your operating
// system is Bonjour-enabled (such as MacOS X).
Expand All @@ -104,7 +84,6 @@ bool ETH_startup()
EthernetBonjour.addServiceRecord("apple-midi", //Arduino._apple-midi doesnt work
5004,
MDNSServiceUDP);
#endif

return true;
}
Loading
Loading