diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..10221f5
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: [lathoub] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..0aa3ff0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,69 @@
+---
+name: Bug report
+about: Report something that does not work as intended
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+
+
+## Context
+
+Using the English language, please answer a few questions to help us understand your problem better and guide you to a solution:
+
+
+
+- What board are you using ?
+ - `example: Arduino Leonardo`
+ - _Please list any shields or other **relevant** hardware you're using_
+- What version of the Arduino IDE are you using ?
+ - `example: 1.8.5`
+- What Operating System are you using for the DAW
+ - [ ] Windows
+ - [ ] MacOS
+ - [ ] Linux
+ - [ ] Other (please specify)
+- Is your problem related to:
+ - [ ] Setup
+ - [ ] Connecting
+ - [ ] AppleMIDI <> MIDI
+ - [ ] Dropped messages
+- How comfortable are you with code ?
+ - [ ] Complete beginner
+ - [ ] I've done basic projects
+ - [ ] I know my way around C/C++
+ - [ ] Advanced / professional
+
+## Describe your project and what you expect to happen:
+
+
+
+## Describe your problem (what does not work):
+
+
+
+## Steps to reproduce
+
+
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..0261b2b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Discussions
+ url: https://github.com/lathoub/Arduino-AppleMIDI-Library/discussions
+ about: Not a bug or a feature request ? Discuss your problem, ask for help or show what you've built in Discussions.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..bbcbbe7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..ffcdb43
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,10 @@
+name: build
+on: [pull_request, push]
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Build on Arduino CLI
+ run: bash ci/build-arduino.sh
diff --git a/.gitignore b/.gitignore
index 2e3a85c..938aa14 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,8 +3,7 @@ examples/.DS_Store
src/.DS_Store
test/.vs
test/Debug
-examples/ESP32_NoteOnOffEverySec/config.h
src/.vscode
test/x64
.development
-examples/ESP32_NoteOnOffEverySec/credentials.h
+examples/ESP32_Callbacks/arduino_secrets.h
diff --git a/.travis.yml b/.travis.yml
index 99f8144..0446ebf 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,7 +10,18 @@ cache:
install:
- pip install -U platformio
- platformio update
- - platformio lib -g install 62@5.0.0 870 872
+ - platformio lib -g install 62@5.0.0 870 872 236
script:
-- platformio ci --board=uno --lib=. examples/EthernetShield_NoteOnOffEverySec/EthernetShield_NoteOnOffEverySec.ino;
+- pio ci --board=uno --lib=. examples/AVR_Callbacks/AVR_Callbacks.ino
+- pio ci --board=uno --lib=. examples/AVR_Directory/AVR_Directory.ino
+- pio ci --board=uno --lib=. examples/AVR_Initiator/AVR_Initiator.ino
+- pio ci --board=uno --lib=. examples/AVR_MinMemUsage/AVR_MinMemUsage.ino
+- pio ci --board=uno --lib=. examples/AVR_MultipleSessions/AVR_MultipleSessions.ino
+- pio ci --board=uno --lib=. examples/AVR_NonDefaultSession/AVR_NonDefaultSession.ino
+- pio ci --board=uno --lib=. examples/AVR_NoteOnOffEverySec/AVR_NoteOnOffEverySec.ino
+- pio ci --board=uno --lib=. examples/AVR_ReceivedRawMidiData/AVR_ReceivedRawMidiData.ino
+- pio ci --board=uno --lib=. examples/AVR_SysEx/AVR_SysEx.ino
+- pio ci --board=mkrzero --lib=. examples/SAMD_Bonjour/SAMD_Bonjour.ino
+- pio ci --board=huzzah --lib=. examples/ESP8266_NoteOnOffEverySec/ESP8266_NoteOnOffEverySec.ino
+- pio ci --board=featheresp32 --lib=. examples/ESP32_NoteOnOffEverySec/ESP32_NoteOnOffEverySec.ino
diff --git a/README.md b/README.md
index 2b6c4ee..5488031 100755
--- a/README.md
+++ b/README.md
@@ -1,35 +1,44 @@
-# AppleMIDI (aka rtpMIDI) for Arduino
-[](https://travis-ci.org/lathoub/Arduino-AppleMIDI-Library) [](http://creativecommons.org/licenses/by-sa/4.0/) [](https://badge.fury.io/gh/lathoub%2FArduino-AppleMidi-Library) [](https://app.codacy.com/app/lathoub/Arduino-AppleMIDI-Library?utm_source=github.com&utm_medium=referral&utm_content=lathoub/Arduino-AppleMIDI-Library&utm_campaign=Badge_Grade_Dashboard)
+# AppleMIDI (aka rtpMIDI) for Arduino [](http://creativecommons.org/licenses/by-sa/4.0/)
-Enables an Arduino with IP/UDP capabilities (Ethernet shield, ESP8266, ESP32, ...) to particpate in an AppleMIDI session.
+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 (Catalina) and using [rtpMIDI](https://www.tobias-erichsen.de/software/rtpmidi.html) from Tobias Erichsen on Windows 10
+* 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
* 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')
+* 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
## Installation
From the Arduino IDE Library Manager, search for AppleMIDI
-
+
This will also install [FortySevenEffects MIDI library](https://github.com/FortySevenEffects/arduino_midi_library)
## Basic Usage
-```
+```cpp
#include
-#include
+#include
+
+byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
void setup()
{
- MIDI.begin(1);
-
- // Optional
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
+ Ethernet.begin(mac);
+
+ MIDI.begin(); // listens on channel 1
}
void loop()
@@ -37,42 +46,48 @@ void loop()
// Listen to incoming notes
MIDI.read();
- // Send MIDI note 40 on, velocity 55 on channel 1
- MIDI.sendNoteOn(40, 55, 1);
-}
-
-void OnAppleMidiConnected(uint32_t ssrc, const char* name) {
+ ....
+ if (something) {
+ // Send MIDI note 40 on, velocity 55 on channel 1
+ MIDI.sendNoteOn(40, 55, 1);
+ }
}
```
-`APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();` creates 2 instance: `MIDI` and `AppleMIDI`. `MIDI` is the instance that manages all MIDI interaction, `AppleMIDI` is the instance this manages the rtp transport layer.
-
-
-More usages in the [examples](https://github.com/lathoub/Arduino-AppleMIDI-Library/tree/master/examples) folder
+More usages in the [examples](https://github.com/lathoub/Arduino-AppleMIDI-Library/tree/master/examples) folder and in the [wiki](https://github.com/lathoub/Arduino-AppleMIDI-Library/wiki)
## Hardware
* Arduino/Genuino (Mega, Uno, Arduino Ethernet, MKRZERO, ...)
* ESP8266 (Adafruit HUZZAH ESP8266, Sparkfun ESP8266 Thing Dev)
-* ESP32 (Adafruit HUZZAH32 – ESP32 Feather Board)
-* Teensy 3.2
+* ESP32 (Adafruit HUZZAH32 – ESP32 Feather Board) Wi-Fi
+* ESP32 with W5500 [Setup](https://github.com/lathoub/Arduino-AppleMIDI-Library/discussions/135)
+* Teensy 3.2 & 4.1
* Adafruit Feather M0 WiFi - ATSAMD21 + ATWINC1500
-
-## Memory usage
-This library is not using any dynamic memory allocation methods - all buffers have a fixed size, set in the `AppleMIDI_Settings.h` file, avoiding potential memory leaks and memory fragmentation.
-
-The minimum buffer size (`MaxBufferSize`) should be set to 64 bytes (also the default). Setting it to a higher value will make sending larger SysEx messages more efficiant (large SysEx messages are chopped in pieces, the larger the buffer, the less pieces needed), at the price of a bigger memory footprint.
-`MaxNumberOfParticipants` is another way to cut memory - each particpants uses approx 300 bytes. Default number of participants is 1 (using 2 sockets).
-Beware: the number of sockets on the Arduino is limited. The W5100 support 4, the W5200 and W5500 based IP chips can use 8 sockets. (Each participant uses 2 sockets: port 5004 and 5004+1). (Base port can be set in `APPLEMIDI_CREATE_DEFAULT_INSTANCE`)
-
## Network Shields
* Arduino Ethernet shield (Wiznet W5100 and W5500)
* Arduino Wifi R3 shield
-* MKR ETH shield
+* MKR ETH shield (W5500 and W6100 based)
* Teensy WIZ820io W5200
-
+* Teensy 4.1 with [Ethernet Kit](https://www.pjrc.com/store/ethernet_kit.html)
+
+## Notes
+
+### 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)
+
+### 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)
+
+### 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)
+
+### 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.10
+* 1.8.16
## 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.
diff --git a/ci/build-arduino.sh b/ci/build-arduino.sh
new file mode 100644
index 0000000..6cf1222
--- /dev/null
+++ b/ci/build-arduino.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+# Exit immediately if a command exits with a non-zero status.
+set -e
+# Enable the globstar shell option
+shopt -s globstar
+# Make sure we are inside the github workspace
+cd $GITHUB_WORKSPACE
+# Create directories
+mkdir $HOME/Arduino
+mkdir $HOME/Arduino/libraries
+# Install Arduino IDE
+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 core update-index
+
+# Install Arduino AVR core
+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
+
+# 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
+
+# Compile all *.ino files for the Arduino Zero
+for f in **/SAMD_*.ino ; do
+ 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
+
+# Compile all *.ino files for the ESP32
+# for f in **/ESP32_*.ino ; do
+# arduino-cli compile -b arduino:esp32:??? $f
+# done
diff --git a/doc/RFC6295_notes.md b/doc/RFC6295_notes.md
new file mode 100644
index 0000000..2e30779
--- /dev/null
+++ b/doc/RFC6295_notes.md
@@ -0,0 +1,174 @@
+# Basic RTPMIDI Cheat Sheet
+
+From https://tools.ietf.org/html/rfc6295
+
+## RTP Header
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | V |P|X| CC |M| PT | Sequence number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Timestamp |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | SSRC |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | MIDI command section ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Journal section ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+| Bit | pos | description |
+| --------------- | :---: | ------------------------------------------------------------------------------------------------------------------------ |
+| V | 0-1 | Version. 2. |
+| P | 2 | Has paddding. RTP needs it for encryption. 0. |
+| X | 3 | Header extension. 0. |
+| CC | 4-7 | CSRC count. 0. |
+| M | 8 | Has MIDI data. |
+| PT | 9-15 | Payload type. Always 0x61. MIDI. |
+| Sequence number | 16-31 | Starts random, increase one on each packet. (%2^16). There is an extended one with 32 bits and rollovers. |
+| Timestamp | 32-63 | Time this packet was generated. On Apple midi the unit is 0.1 ms. (1^-4 seconds). Real RTPMIDI is at session connection. |
+| SSRC | 64-96 | Random unique SSRC for this sender. Same for all the session. |
+
+Timestamp can be buffered to reduce jitter on the receiving end, creating a
+continuous lag of a specific length.
+
+## MIDI Command section
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |B|J|Z|P|LEN... | MIDI list ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+| Bit | pos | description |
+| ------ | :-: | ---------------------------------------------------------------------------------------------------------------- |
+| B | 0 | Length is 12 bits. If true length at 4-7 is MSB, and one more byte. |
+| J | 1 | There is a journal |
+| Z | 2 | First midi command as is in MIDI section. No timestamp for first command. |
+| P | 3 | Phantom MIDI command. The first command is a running command from previous stream. |
+| length | 4-7 | How many bytes. May be extended with the B bit. |
+| MIDI | ... | MIDI data, then timestamp, MIDI data, timestamp and so on.. or timestamp, midi data and so on. Depends on Z bit. |
+
+Timestamps in running Length encoding. https://en.wikipedia.org/wiki/Run-length_encoding
+
+# Journal
+
+## Journal Bits
+
+ 0 1 2
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |S|Y|A|H|TOTCHAN| Checkpoint Packet Seqnum |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+| Bit | pos | description |
+| ------------------------ | :--: | ------------------------------------------------------------------------ |
+| S | 0 | Single packet loss. To indicate only one packet is described in journal. |
+| Y | 1 | Has system journal |
+| A | 2 | Has channel journals. Needs totchan. |
+| H | 3 | Enhanced Chapter C encoding. |
+| TOTCHAN | 4-7 | Nr channels -1 (has totchan + 1 channels) |
+| Checkpoint packet seqnum | 8-23 | Seq nr for this journal Normally the one before the current packet. |
+
+## Channel Journal
+
+One for each (TOTCHAN + 1)
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |S| CHAN |H| LENGTH |P|C|M|W|N|E|T|A| Chapters ... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+| Bit | pos | description |
+| ------ | :----: | ------------------------------------------------------------------------ |
+| S | 0 | Single packet loss. To indicate only one packet is described in journal. |
+| CHAN | 1-4 | Channel number |
+| H | 5 | Whether controllers are Enhanced Chapter C. |
+| LENGTH | 6-15 | Lenght of the journal |
+| P | 16 / 0 | Chapter P. Program Change. |
+| C | 17 / 1 | Chapter C. Control Change. |
+| M | 18 / 2 | Chapter M. Parameter System. |
+| W | 19 / 3 | Chapter W. Pitch Wheel. |
+| N | 20 / 4 | Chapter N. Note On/Off |
+| E | 21 / 5 | Chapter E. Note Command Extras |
+| T | 22 / 6 | Chapter T. After Touch. |
+| A | 23 / 7 | Chapter A. Poly Aftertouch. |
+
+## Chapter P
+
+ 0 1 2
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |S| PROGRAM |B| BANK-MSB |X| BANK-LSB |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+## Chapter C
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 8 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |S| LEN |S| NUMBER |A| VALUE/ALT |S| NUMBER |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |A| VALUE/ALT | .... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+## Chapter M
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 8 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |S| LEN |S| NUMBER |A| VALUE/ALT |S| NUMBER |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |A| VALUE/ALT | .... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+# Chapter W
+
+ 0 1
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |S| FIRST |R| SECOND |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+## Chapter N
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 8 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |B| LEN | LOW | HIGH |S| NOTENUM |Y| VELOCITY |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |S| NOTENUM |Y| VELOCITY | .... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | OFFBITS | OFFBITS | .... | OFFBITS |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+| Bit | pos | description |
+| --- | :-----: | ------------------------------------ |
+| B | 0 | S-Style functionality. By default 1. |
+| S | 16n | If B is 0, all S are 0. |
+| Y | 16n + 8 | Recomendation to play. |
+
+## Chapter T
+
+ 0
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-+-+-+-+
+ |S| PRESSURE |
+ +-+-+-+-+-+-+-+-+
+
+## Chapter A
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 8 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |S| LEN |S| NOTENUM |X| PRESSURE |S| NOTENUM |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ |X| PRESSURE | .... |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+(original by https://github.com/davidmoreno/rtpmidid/blob/master/docs/RFC6295_notes.md)
diff --git a/examples/AVR_Callbacks/AVR_Callbacks.ino b/examples/AVR_Callbacks/AVR_Callbacks.ino
new file mode 100644
index 0000000..241ae79
--- /dev/null
+++ b/examples/AVR_Callbacks/AVR_Callbacks.ino
@@ -0,0 +1,162 @@
+#include
+
+#define SerialMon Serial
+#define ONE_PARTICIPANT
+#define USE_EXT_CALLBACKS
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t&, const APPLEMIDI_NAMESPACE::Exception&, const int32_t);
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Das Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin(MIDI_CHANNEL_OMNI);
+
+ // Normal callbacks - always available
+ // Stay informed on connection status
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+/*
+ // Extended callback, only available when defining USE_EXT_CALLBACKS
+ AppleMIDI.setHandleSentRtp([](const APPLEMIDI_NAMESPACE::Rtp_t & rtp) {
+ // AM_DBG(F("an rtpMessage has been sent with sequenceNr"), rtp.sequenceNr);
+ });
+ AppleMIDI.setHandleSentRtpMidi([](const APPLEMIDI_NAMESPACE::RtpMIDI_t& rtpMidi) {
+ // AM_DBG(F("an rtpMidiMessage has been sent"), rtpMidi.flags);
+ });
+ AppleMIDI.setHandleReceivedRtp([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const APPLEMIDI_NAMESPACE::Rtp_t & rtp, const int32_t& latency) {
+ // AM_DBG(F("setHandleReceivedRtp"), ssrc, rtp.sequenceNr , "with", latency, "ms latency");
+ });
+ AppleMIDI.setHandleStartReceivedMidi([](const APPLEMIDI_NAMESPACE::ssrc_t& ssrc) {
+ // AM_DBG(F("setHandleStartReceivedMidi from SSRC"), ssrc);
+ });
+ AppleMIDI.setHandleReceivedMidi([](const APPLEMIDI_NAMESPACE::ssrc_t& ssrc, byte value) {
+ // AM_DBG(F("setHandleReceivedMidi from SSRC"), ssrc, ", value:", value);
+ });
+ AppleMIDI.setHandleEndReceivedMidi([](const APPLEMIDI_NAMESPACE::ssrc_t& ssrc) {
+ // AM_DBG(F("setHandleEndReceivedMidi from SSRC"), ssrc);
+ });
+ AppleMIDI.setHandleException(OnAppleMidiException);
+
+ MIDI.setHandleControlChange([](Channel channel, byte v1, byte v2) {
+ AM_DBG(F("ControlChange"), channel, v1, v2);
+ });
+ MIDI.setHandleProgramChange([](Channel channel, byte v1) {
+ AM_DBG(F("ProgramChange"), channel, v1);
+ });
+ MIDI.setHandlePitchBend([](Channel channel, int v1) {
+ AM_DBG(F("PitchBend"), channel, v1);
+ });
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), channel, note, velocity);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), channel, note, velocity);
+ });
+*/
+ AM_DBG(F("Sending MIDI messages every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 100)
+ {
+ t1 = millis();
+
+ byte note = random(15, 80);
+ byte velocity = random(55, 100);
+ byte channel = 1;
+
+ // AM_DBG(F("\nsendNoteOn"), note, velocity, channel);
+ MIDI.sendNoteOn(note, velocity, channel);
+ //MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t& ssrc, const APPLEMIDI_NAMESPACE::Exception& e, const int32_t value ) {
+ switch (e)
+ {
+ case APPLEMIDI_NAMESPACE::Exception::BufferFullException:
+ AM_DBG(F("*** BufferFullException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ParseException:
+ AM_DBG(F("*** ParseException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::TooManyParticipantsException:
+ AM_DBG(F("*** TooManyParticipantsException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::UnexpectedInviteException:
+ AM_DBG(F("*** UnexpectedInviteException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ParticipantNotFoundException:
+ AM_DBG(F("*** ParticipantNotFoundException"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ComputerNotInDirectory:
+ AM_DBG(F("*** ComputerNotInDirectory"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::NotAcceptingAnyone:
+ AM_DBG(F("*** NotAcceptingAnyone"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ListenerTimeOutException:
+ AM_DBG(F("*** ListenerTimeOutException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::MaxAttemptsException:
+ AM_DBG(F("*** MaxAttemptsException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::NoResponseFromConnectionRequestException:
+ AM_DBG(F("***:yyy did't respond to the connection request. Check the address and port, and any firewall or router settings. (time)"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::SendPacketsDropped:
+ AM_DBG(F("*** SendPacketsDropped"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ReceivedPacketsDropped:
+ AM_DBG(F("*** ReceivedPacketsDropped"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::UdpBeginPacketFailed:
+ AM_DBG(F("*** UdpBeginPacketFailed"), value);
+ break;
+ }
+}
diff --git a/examples/AVR_Directory/AVR_Directory.ino b/examples/AVR_Directory/AVR_Directory.ino
new file mode 100644
index 0000000..6f8c630
--- /dev/null
+++ b/examples/AVR_Directory/AVR_Directory.ino
@@ -0,0 +1,85 @@
+#include
+
+#define SerialMon Serial
+#define USE_DIRECTORY
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ AppleMIDI.directory.push_back(IPAddress(192, 168, 1, 63));
+ AppleMIDI.directory.push_back(IPAddress(192, 168, 1, 66));
+// AppleMIDI.whoCanConnectToMe = APPLEMIDI_NAMESPACE::None;
+ AppleMIDI.whoCanConnectToMe = APPLEMIDI_NAMESPACE::OnlyComputersInMyDirectory;
+// AppleMIDI.whoCanConnectToMe = APPLEMIDI_NAMESPACE::Anyone;
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin();
+
+ // Stay informed on connection status
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
+
+ AM_DBG(F("Sending MIDI messages every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
+ {
+ t1 = millis();
+
+ byte note = random(1, 127);
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+ // MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
diff --git a/examples/AVR_E3_NoteOnOffEverySec/AVR_E3_NoteOnOffEverySec.ino b/examples/AVR_E3_NoteOnOffEverySec/AVR_E3_NoteOnOffEverySec.ino
new file mode 100644
index 0000000..aee6d15
--- /dev/null
+++ b/examples/AVR_E3_NoteOnOffEverySec/AVR_E3_NoteOnOffEverySec.ino
@@ -0,0 +1,78 @@
+#include // from https://github.com/sstaub/Ethernet3
+
+#define SerialMon Serial
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin();
+
+ // Stay informed on connection status
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
+
+ AM_DBG(F("Sending MIDI messages every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
+ {
+ t1 = millis();
+
+ byte note = random(1, 127);
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+// MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
diff --git a/examples/AVR_Initiator/AVR_Initiator.ino b/examples/AVR_Initiator/AVR_Initiator.ino
new file mode 100644
index 0000000..90e8b2a
--- /dev/null
+++ b/examples/AVR_Initiator/AVR_Initiator.ino
@@ -0,0 +1,83 @@
+#include
+
+#define SerialMon Serial
+#define APPLEMIDI_INITIATOR
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+
+ MIDI.begin();
+
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
+
+ // Initiate the session
+ IPAddress remote(192, 168, 1, 65);
+ AppleMIDI.sendInvite(remote, DEFAULT_CONTROL_PORT); // port is 5004 by default
+
+ AM_DBG(F("Connecting to "), remote, "Port", DEFAULT_CONTROL_PORT, "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Watch as this session is added to the Participants list"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ AM_DBG(F("Sending a random NoteOn/Off every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send note on/off every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
+ {
+ t1 = millis();
+
+ byte note = random(1, 127);
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+ MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
diff --git a/examples/AVR_MinMemUsage/AVR_MinMemUsage.ino b/examples/AVR_MinMemUsage/AVR_MinMemUsage.ino
new file mode 100644
index 0000000..165a8cb
--- /dev/null
+++ b/examples/AVR_MinMemUsage/AVR_MinMemUsage.ino
@@ -0,0 +1,64 @@
+#include
+
+#define ONE_PARTICIPANT
+#define NO_SESSION_NAME
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ pinMode(LED_BUILTIN, OUTPUT);
+ digitalWrite(LED_BUILTIN, LOW);
+
+ if (Ethernet.begin(mac) == 0) for (;;);
+
+ MIDI.begin();
+
+ // Stay informed on connection status
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char*) {
+ isConnected++;
+ digitalWrite(LED_BUILTIN, HIGH);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ digitalWrite(LED_BUILTIN, LOW);
+ });
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ digitalWrite(LED_BUILTIN, LOW);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ digitalWrite(LED_BUILTIN, HIGH);
+ });
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
+ {
+ t1 = millis();
+
+ MIDI.sendNoteOn(54, 100, 1);
+ }
+}
diff --git a/examples/EthernetShield_MultipleSessions/EthernetShield_MultipleSessions.ino b/examples/AVR_MultipleSessions/AVR_MultipleSessions.ino
similarity index 64%
rename from examples/EthernetShield_MultipleSessions/EthernetShield_MultipleSessions.ino
rename to examples/AVR_MultipleSessions/AVR_MultipleSessions.ino
index 75ca6ab..6ee4689 100644
--- a/examples/EthernetShield_MultipleSessions/EthernetShield_MultipleSessions.ino
+++ b/examples/AVR_MultipleSessions/AVR_MultipleSessions.ino
@@ -1,7 +1,7 @@
#include
+#define SerialMon Serial
#include
-USING_NAMESPACE_APPLEMIDI
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
@@ -10,36 +10,33 @@ byte mac[] = {
};
unsigned long t1 = millis();
-bool isConnected = false;
+int8_t isConnected = 0;
APPLEMIDI_CREATE_INSTANCE(EthernetUDP, MIDI1, "Arduino1", DEFAULT_CONTROL_PORT);
APPLEMIDI_CREATE_INSTANCE(EthernetUDP, MIDI2, "Arduino2", DEFAULT_CONTROL_PORT + 2);
+void OnAppleMidiConnected(const APPLEMIDI_NAMESPACE::ssrc_t&, const char*);
+void OnAppleMidiDisconnected(const APPLEMIDI_NAMESPACE::ssrc_t &);
+void OnMidiNoteOn(byte channel, byte note, byte velocity);
+
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void setup()
{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
- Serial.println(F("Getting IP address..."));
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
for (;;);
}
- Serial.print(F("IP address is "));
- Serial.println(Ethernet.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(Ethernet.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI1.getPort(), "(Name", AppleMIDI1.getName(), ")");
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI2.getPort(), "(Name", AppleMIDI2.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
// Listen for MIDI messages on channel 1
MIDI1.begin(1);
@@ -55,7 +52,7 @@ void setup()
MIDI1.setHandleNoteOn(OnMidiNoteOn);
MIDI2.setHandleNoteOn(OnMidiNoteOn);
- Serial.println(F("Every second send a random NoteOn/Off"));
+ AM_DBG(F("Every second, send a random NoteOn/Off, from multiple sessions"));
}
// -----------------------------------------------------------------------------
@@ -69,10 +66,9 @@ void loop()
// send note on/off every second
// (dont cáll delay(1000) as it will stall the pipeline)
- if (isConnected && (millis() - t1) > 1000)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
{
t1 = millis();
- // Serial.print(F(".");
byte note = random(1, 127);
byte velocity = 55;
@@ -89,31 +85,22 @@ void loop()
// -----------------------------------------------------------------------------
// rtpMIDI session. Device connected
// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.print(name);
- Serial.print(" ssrc 0x");
- Serial.println(ssrc, HEX);
+void OnAppleMidiConnected(const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
}
// -----------------------------------------------------------------------------
// rtpMIDI session. Device disconnected
// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.print(F("Disconnected from ssrc 0x"));
- Serial.println(ssrc, HEX);
+void OnAppleMidiDisconnected(const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
static void OnMidiNoteOn(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOn from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
+ AM_DBG(F("in\tNote on"), note, " Velocity", velocity, "\t", channel);
}
diff --git a/examples/AVR_NonDefaultSession/AVR_NonDefaultSession.ino b/examples/AVR_NonDefaultSession/AVR_NonDefaultSession.ino
new file mode 100644
index 0000000..03abf63
--- /dev/null
+++ b/examples/AVR_NonDefaultSession/AVR_NonDefaultSession.ino
@@ -0,0 +1,71 @@
+#include
+
+#define SerialMon Serial
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+// Non default portnr
+APPLEMIDI_CREATE_INSTANCE(EthernetUDP, MIDI, "MyNamedArduino", 5200);
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin();
+
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ AM_DBG(F("Send MIDI messages every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
+ {
+ t1 = millis();
+
+ byte note = random(1, 127);
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+ MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
diff --git a/examples/AVR_NoteOnOffEverySec/AVR_NoteOnOffEverySec.ino b/examples/AVR_NoteOnOffEverySec/AVR_NoteOnOffEverySec.ino
new file mode 100644
index 0000000..60d9012
--- /dev/null
+++ b/examples/AVR_NoteOnOffEverySec/AVR_NoteOnOffEverySec.ino
@@ -0,0 +1,79 @@
+#include
+
+#define SerialMon Serial
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin();
+
+ // Stay informed on connection status
+ AppleMIDI
+ .setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ })
+ .setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
+
+ AM_DBG(F("Sending MIDI messages every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
+ {
+ t1 = millis();
+
+ byte note = random(1, 127);
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+// MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
diff --git a/examples/AVR_ReceivedRawMidiData/AVR_ReceivedRawMidiData.ino b/examples/AVR_ReceivedRawMidiData/AVR_ReceivedRawMidiData.ino
new file mode 100644
index 0000000..2cf3512
--- /dev/null
+++ b/examples/AVR_ReceivedRawMidiData/AVR_ReceivedRawMidiData.ino
@@ -0,0 +1,95 @@
+#include
+
+#define SerialMon Serial
+#define USE_EXT_CALLBACKS
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+void OnAppleMidiStartReceived(const APPLEMIDI_NAMESPACE::ssrc_t&);
+void OnAppleMidiReceivedByte(const APPLEMIDI_NAMESPACE::ssrc_t&, byte);
+void OnAppleMidiEndReceive(const APPLEMIDI_NAMESPACE::ssrc_t&);
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin();
+
+ // check: zien we de connecttion binnenkomen?? Anders terug een ref van maken
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+ AppleMIDI.setHandleStartReceivedMidi(OnAppleMidiStartReceived);
+ AppleMIDI.setHandleReceivedMidi(OnAppleMidiReceivedByte);
+ AppleMIDI.setHandleEndReceivedMidi(OnAppleMidiEndReceive);
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+}
+
+// ====================================================================================
+// Event handlers for incoming MIDI messages
+// ====================================================================================
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void OnAppleMidiStartReceived(const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ AM_DBG(F("Start receiving"), ssrc);
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void OnAppleMidiReceivedByte(const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, byte data) {
+ SerialMon.println(data, HEX);
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void OnAppleMidiEndReceive(const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ AM_DBG(F("End receiving"), ssrc);
+}
diff --git a/examples/AVR_SysEx/AVR_SysEx.ino b/examples/AVR_SysEx/AVR_SysEx.ino
new file mode 100644
index 0000000..da76c5e
--- /dev/null
+++ b/examples/AVR_SysEx/AVR_SysEx.ino
@@ -0,0 +1,110 @@
+#include
+
+#define SerialMon Serial
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+byte sysex14[] = { 0xF0, 0x43, 0x20, 0x7E, 0x4C, 0x4D, 0x20, 0x20, 0x38, 0x39, 0x37, 0x33, 0x50, 0xF7 };
+byte sysex15[] = { 0xF0, 0x43, 0x20, 0x7E, 0x4C, 0x4D, 0x20, 0x20, 0x38, 0x39, 0x37, 0x33, 0x50, 0x4D, 0xF7 };
+byte sysex16[] = { 0xF0, 0x43, 0x20, 0x7E, 0x4C, 0x4D, 0x20, 0x20, 0x38, 0x39, 0x37, 0x33, 0x32, 0x50, 0x4D, 0xF7 };
+byte sysexBig[] = { 0xF0, 0x41,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a,
+ 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0xF7
+ };
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ MIDI.begin();
+ MIDI.setHandleSystemExclusive(OnMidiSysEx);
+
+ AM_DBG(F("Send SysEx every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
+ {
+ // MIDI.sendSysEx(sizeof(sysexBig), sysexBig, true);
+ t1 = millis();
+ }
+}
+
+// ====================================================================================
+// Event handlers for incoming MIDI messages
+// ====================================================================================
+
+void OnMidiSysEx(byte* data, unsigned length) {
+ SerialMon.print(F("SYSEX: ("));
+ SerialMon.print(getSysExStatus(data, length));
+ SerialMon.print(F(", "));
+ SerialMon.print(length);
+ SerialMon.print(F(" bytes) "));
+ for (uint16_t i = 0; i < length; i++)
+ {
+ SerialMon.print(data[i], HEX);
+ SerialMon.print(" ");
+ }
+ SerialMon.println();
+}
+
+char getSysExStatus(const byte* data, uint16_t length)
+{
+ if (data[0] == 0xF0 && data[length - 1] == 0xF7)
+ return 'F'; // Full SysEx Command
+ else if (data[0] == 0xF0 && data[length - 1] != 0xF7)
+ return 'S'; // Start of SysEx-Segment
+ else if (data[0] != 0xF0 && data[length - 1] != 0xF7)
+ return 'M'; // Middle of SysEx-Segment
+ else
+ return 'E'; // End of SysEx-Segment
+}
diff --git a/examples/Basic_IO/Basic_IO.ino b/examples/Basic_IO/Basic_IO.ino
deleted file mode 100644
index 65e8a7c..0000000
--- a/examples/Basic_IO/Basic_IO.ino
+++ /dev/null
@@ -1,37 +0,0 @@
-#include
-#include
-
-// Enter a MAC address for your controller below.
-// Newer Ethernet shields have a MAC address printed on a sticker on the shield
-byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
-};
-
-// Simple tutorial on how to receive and send MIDI messages.
-// Here, when receiving any message on channel 4, the Arduino
-// will blink a led and play back a note for 1 second.
-
-APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-
-void setup()
-{
- if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
- for (;;);
- }
-
- pinMode(LED_BUILTIN, OUTPUT);
- MIDI.begin(4); // Launch MIDI and listen to channel 4
-}
-
-void loop()
-{
- if (MIDI.read()) // If we have received a message
- {
- digitalWrite(LED_BUILTIN, HIGH);
- MIDI.sendNoteOn(42, 127, 1); // Send a Note (pitch 42, velo 127 on channel 1)
- delay(1000); // Wait for a second
- MIDI.sendNoteOff(42, 0, 1); // Stop the note
- digitalWrite(LED_BUILTIN, LOW);
- }
-}
diff --git a/examples/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec.ino b/examples/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec.ino
deleted file mode 100644
index 6f496c4..0000000
--- a/examples/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec.ino
+++ /dev/null
@@ -1,115 +0,0 @@
-#include "ETH_Helper.h"
-
-#include
-USING_NAMESPACE_APPLEMIDI
-
-unsigned long t0 = millis();
-bool isConnected = false;
-
-APPLEMIDI_CREATE_DEFAULTSESSION_ESP32_INSTANCE();
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
- ETH_startup();
-
- MDNS.begin(AppleMIDI.getName());
-
- Serial.print("\nIP address is ");
- Serial.println(ETH.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(ETH.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- // Create a session and wait for a remote host to connect to us
- MIDI.begin(1); // listen on channel 1
-
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
-
- MIDI.setHandleNoteOn(OnAppleMidiNoteOn);
- MIDI.setHandleNoteOff(OnAppleMidiNoteOff);
-
- MDNS.addService("apple-midi", "udp", AppleMIDI.getPort());
-
- Serial.println(F("Every second send a random NoteOn/Off"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void loop()
-{
- // Listen to incoming notes
- MIDI.read();
-
- // send a note every second
- // (dont cáll delay(1000) as it will stall the pipeline)
- if (isConnected && (millis() - t0) > 1000)
- {
- t0 = millis();
- // Serial.print(F(".");
-
- byte note = random(1, 127);
- byte velocity = 55;
- byte channel = 1;
-
- MIDI.sendNoteOn(note, velocity, channel);
- MIDI.sendNoteOff(note, velocity, channel);
- }
-}
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnAppleMidiNoteOn(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOn from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnAppleMidiNoteOff(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOff from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
diff --git a/examples/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec/ETH_Helper.h b/examples/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec/ETH_Helper.h
deleted file mode 100644
index 25b4532..0000000
--- a/examples/ESP32-Ethernet-Kit_A_v1.1_NoteOnOffEverySec/ETH_Helper.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#include
-#include
-
-#define ETH_ADDR 1
-#define ETH_POWER_PIN 5
-#define ETH_MDC_PIN 23
-#define ETH_MDIO_PIN 18
-#define ETH_TYPE ETH_PHY_IP101
-
-static bool eth_connected = false;
-
-void WiFiEvent(WiFiEvent_t event)
-{
- switch (event) {
- case SYSTEM_EVENT_ETH_START:
- Serial.println("ETH Started");
- //set eth hostname here
- ETH.setHostname("esp32-ethernet");
- break;
- case SYSTEM_EVENT_ETH_CONNECTED:
- Serial.println("ETH Connected");
- break;
- case SYSTEM_EVENT_ETH_GOT_IP:
- Serial.print("ETH MAC: ");
- Serial.print(ETH.macAddress());
- Serial.print(", IPv4: ");
- Serial.print(ETH.localIP());
- if (ETH.fullDuplex()) {
- Serial.print(", FULL_DUPLEX");
- }
- Serial.print(", ");
- Serial.print(ETH.linkSpeed());
- Serial.println("Mbps");
- eth_connected = true;
- break;
- case SYSTEM_EVENT_ETH_DISCONNECTED:
- Serial.println("ETH Disconnected");
- eth_connected = false;
- break;
- case SYSTEM_EVENT_ETH_STOP:
- Serial.println("ETH Stopped");
- eth_connected = false;
- break;
- default:
- break;
- }
-}
-
-bool ETH_startup()
-{
- WiFi.onEvent(WiFiEvent);
- ETH.begin(ETH_ADDR, ETH_POWER_PIN, ETH_MDC_PIN, ETH_MDIO_PIN, ETH_TYPE);
-
- Serial.println(F("Getting IP address..."));
-
- while (!eth_connected)
- delay(100);
-
- return true;
-}
diff --git a/examples/ESP32_DynamicInstantiation/ESP32_dynamicClass.ino b/examples/ESP32_DynamicInstantiation/ESP32_dynamicClass.ino
new file mode 100644
index 0000000..e1947e1
--- /dev/null
+++ b/examples/ESP32_DynamicInstantiation/ESP32_dynamicClass.ino
@@ -0,0 +1,57 @@
+#include
+
+#include "ETH_Helper.h"
+
+#define SerialMon Serial
+#include "midiHelpers.h"
+
+
+#define ethernet true; // set to false to demonstrate WiFi usage
+
+char ssid[] = "ssid"; // your network SSID (name)
+char pass[] = "pass"; // your network password (use for WPA, or use as key for WEP)
+
+MidiClient* midiClient; // generic class offered as an alternative of the MACRO
+
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t&, const APPLEMIDI_NAMESPACE::Exception&, const int32_t);
+
+void WIFI_startup(){
+ WiFi.begin(ssid, pass);
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ AM_DBG(F("Establishing connection to WiFi.."));
+ }
+}
+
+void setup(){
+
+ AM_DBG_SETUP(115200);
+
+ bool useEth = false;
+
+ if (useEth){
+ ETH_startup();
+ midiClient = new AppleMidiWithInterfaceWrapper("APPLE_MIDIETHCLIENT",DEFAULT_CONTROL_PORT);
+ }else{
+ WIFI_startup();
+ midiClient = new AppleMidiWithInterfaceWrapper("APPLE_MIDIWIFICLIENT",DEFAULT_CONTROL_PORT);
+ }
+
+ midiClient->begin();
+
+ midiClient->setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ midiClient->setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ AM_DBG(F("OK, now make sure you have an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), useEth?Ethernet.localIP():WiFi.localIP(), "Port", midiClient->getPort(), "(Name", midiClient->getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+}
+
+void loop(){
+ midiClient->read();
+}
\ No newline at end of file
diff --git a/examples/ESP32_DynamicInstantiation/ETH_helper.h b/examples/ESP32_DynamicInstantiation/ETH_helper.h
new file mode 100644
index 0000000..11e5507
--- /dev/null
+++ b/examples/ESP32_DynamicInstantiation/ETH_helper.h
@@ -0,0 +1,110 @@
+#ifdef ETHERNET3
+#include
+#else
+#include
+#include // https://github.com/TrippyLighting/EthernetBonjour
+#endif
+
+// to get the Mac address
+#include
+
+#define RESET_PIN D7
+#define CS_PIN D5
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+/*
+ Wiz W5500 reset function. Change this for the specific reset
+ sequence required for your particular board or module.
+*/
+void hardreset() {
+ pinMode(RESET_PIN, OUTPUT);
+ digitalWrite(RESET_PIN, HIGH);
+ delay(150);
+
+ digitalWrite(RESET_PIN, LOW);
+ delay(500);
+ digitalWrite(RESET_PIN, HIGH);
+ delay(150);
+}
+
+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);
+
+ /*
+ Network configuration - all except the MAC are optional.
+
+ IMPORTANT NOTE - The mass-produced W5500 boards do -not-
+ have a built-in MAC address (depite
+ 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());
+
+ /*
+ Sanity checks for W5500 and cable connection.
+ */
+ 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);
+ } else {
+ rdy_flag = true;
+ break;
+ }
+ }
+ if (rdy_flag == false) {
+ Serial.println("\n\r\tHardware fault, or cable problem... cannot continue.");
+ while (true) {
+ delay(10); // Halt.
+ }
+ } else {
+ 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).
+ // Always call this before any other method!
+ EthernetBonjour.begin("arduino");
+
+ EthernetBonjour.addServiceRecord("apple-midi", //Arduino._apple-midi doesnt work
+ 5004,
+ MDNSServiceUDP);
+#endif
+
+ return true;
+}
\ No newline at end of file
diff --git a/examples/ESP32_DynamicInstantiation/midiHelpers.h b/examples/ESP32_DynamicInstantiation/midiHelpers.h
new file mode 100644
index 0000000..e9ee93e
--- /dev/null
+++ b/examples/ESP32_DynamicInstantiation/midiHelpers.h
@@ -0,0 +1,87 @@
+#ifndef MIDIHELPERS_h
+#define MIDIHELPERS_h
+
+#define USE_EXT_CALLBACKS // as from example => required for MIDI callbacks
+#include //https://github.com/lathoub/Arduino-AppleMIDI-Library
+
+using namespace APPLEMIDI_NAMESPACE;
+
+/*
+class Utilities for creating a generic pointer to AppleMIDISession and MidiInterface
+from a WiFiUDP or EThernetUDP (template) and be able to manipulate it as a generic instance
+and manipulating the pointer generically
+*/
+
+class MidiClient {
+public:
+ virtual void read() = 0;
+ virtual ~MidiClient() {}
+ virtual void setHandleConnected(void (*fptr)(const ssrc_t &, const char *))= 0;
+ virtual void setHandleDisconnected(void (*fptr)(const ssrc_t &)) = 0;
+ virtual void setHandleException(void (*fptr)(const ssrc_t &, const Exception &, const int32_t value))=0;
+ virtual const char *getName() = 0;
+ virtual const uint16_t getPort() = 0;
+ virtual void begin() = 0;
+ // all methods you need to be wrapped below
+ virtual void sendNoteOn(byte note, byte velocity, byte channel) = 0;
+ virtual void sendNoteOff(byte note, byte velocity, byte channel) = 0;
+ // etc...
+
+};
+
+template
+class AppleMidiWithInterfaceWrapper : public MidiClient {
+public:
+ AppleMIDISession* session;
+ MidiInterface, AppleMIDISettings>* midi;
+
+ AppleMidiWithInterfaceWrapper(const char* sessionName, uint16_t port) {
+ session = new AppleMIDISession(sessionName, port);
+ midi = new MidiInterface, AppleMIDISettings>(*session);
+ }
+
+ virtual void begin(){
+ session->begin();
+ }
+
+ virtual const char *getName(){
+ return session->getName();
+ }
+
+ virtual const uint16_t getPort(){
+ return session->getPort();
+ }
+
+ virtual void setHandleConnected(void (*fptr)(const ssrc_t &, const char *)){
+ session->setHandleConnected(fptr);
+ }
+
+ virtual void setHandleDisconnected(void (*fptr)(const ssrc_t &)){
+ session->setHandleDisconnected(fptr);
+ }
+
+ virtual void setHandleException(void (*fptr)(const ssrc_t &, const Exception &, const int32_t value)){
+ session->setHandleException(fptr);
+ }
+
+
+ void read() override {
+ midi->read();
+ }
+
+ void sendNoteOn(byte note, byte velocity, byte channel) override {
+ midi->sendNoteOn(note, velocity, channel);
+ }
+
+ void sendNoteOff(byte note, byte velocity, byte channel) override {
+ midi->sendNoteOff(note, velocity, channel);
+ }
+
+
+ ~AppleMidiWithInterfaceWrapper() {
+ delete midi;
+ delete session;
+ }
+};
+
+#endif
\ No newline at end of file
diff --git a/examples/ESP32_NoteOnOffEverySec/ESP32_NoteOnOffEverySec.ino b/examples/ESP32_NoteOnOffEverySec/ESP32_NoteOnOffEverySec.ino
new file mode 100644
index 0000000..6c506c3
--- /dev/null
+++ b/examples/ESP32_NoteOnOffEverySec/ESP32_NoteOnOffEverySec.ino
@@ -0,0 +1,80 @@
+#include
+#include
+#include
+
+#define SerialMon Serial
+#include
+
+char ssid[] = "ssid"; // your network SSID (name)
+char pass[] = "password"; // your network password (use for WPA, or use as key for WEP)
+
+unsigned long t0 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ WiFi.begin(ssid, pass);
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ AM_DBG(F("Establishing connection to WiFi.."));
+ }
+ AM_DBG(F("Connected to network"));
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), WiFi.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+ AM_DBG(F("Listen to incoming MIDI commands"));
+
+ MIDI.begin();
+
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
+
+ AM_DBG(F("Sending NoteOn/Off of note 45, every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t0) > 1000)
+ {
+ t0 = millis();
+
+ byte note = 45;
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+ MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
diff --git a/examples/ESP32_W5500_Callbacks/ESP32_W5500_Callbacks.ino b/examples/ESP32_W5500_Callbacks/ESP32_W5500_Callbacks.ino
new file mode 100644
index 0000000..cedb154
--- /dev/null
+++ b/examples/ESP32_W5500_Callbacks/ESP32_W5500_Callbacks.ino
@@ -0,0 +1,157 @@
+#include "ETH_Helper.h"
+
+#define SerialMon Serial
+#define ONE_PARTICIPANT
+#define USE_EXT_CALLBACKS
+#include
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_INSTANCE(EthernetUDP, MIDI, "AppleMIDI-Arduino", DEFAULT_CONTROL_PORT);
+
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t&, const APPLEMIDI_NAMESPACE::Exception&, const int32_t);
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Das Booting"));
+
+ ETH_startup();
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin(MIDI_CHANNEL_OMNI);
+
+ // Normal callbacks - always available
+ // Stay informed on connection status
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ // Extended callback, only available when defining USE_EXT_CALLBACKS
+ AppleMIDI.setHandleSentRtp([](const APPLEMIDI_NAMESPACE::Rtp_t & rtp) {
+ // AM_DBG(F("an rtpMessage has been sent with sequenceNr"), rtp.sequenceNr);
+ });
+ AppleMIDI.setHandleSentRtpMidi([](const APPLEMIDI_NAMESPACE::RtpMIDI_t& rtpMidi) {
+ AM_DBG(F("an rtpMidiMessage has been sent"), rtpMidi.flags);
+ });
+ AppleMIDI.setHandleReceivedRtp([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const APPLEMIDI_NAMESPACE::Rtp_t & rtp, const int32_t& latency) {
+ // AM_DBG(F("setHandleReceivedRtp"), ssrc, rtp.sequenceNr , "with", latency, "ms latency");
+ });
+ AppleMIDI.setHandleStartReceivedMidi([](const APPLEMIDI_NAMESPACE::ssrc_t& ssrc) {
+ // AM_DBG(F("setHandleStartReceivedMidi from SSRC"), ssrc);
+ });
+ AppleMIDI.setHandleReceivedMidi([](const APPLEMIDI_NAMESPACE::ssrc_t& ssrc, byte value) {
+ // AM_DBG(F("setHandleReceivedMidi from SSRC"), ssrc, ", value:", value);
+ });
+ AppleMIDI.setHandleEndReceivedMidi([](const APPLEMIDI_NAMESPACE::ssrc_t& ssrc) {
+ // AM_DBG(F("setHandleEndReceivedMidi from SSRC"), ssrc);
+ });
+ AppleMIDI.setHandleException(OnAppleMidiException);
+
+ MIDI.setHandleControlChange([](Channel channel, byte v1, byte v2) {
+ AM_DBG(F("ControlChange"), channel, v1, v2);
+ });
+ MIDI.setHandleProgramChange([](Channel channel, byte v1) {
+ AM_DBG(F("ProgramChange"), channel, v1);
+ });
+ MIDI.setHandlePitchBend([](Channel channel, int v1) {
+ AM_DBG(F("PitchBend"), channel, v1);
+ });
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), channel, note, velocity);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), channel, note, velocity);
+ });
+
+ AM_DBG(F("Sending MIDI messages every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 100)
+ {
+ t1 = millis();
+
+ byte note = random(1, 127);
+ byte velocity = 55;
+ byte channel = 1;
+
+ // AM_DBG(F("\nsendNoteOn"), note, velocity, channel);
+ MIDI.sendNoteOn(note, velocity, channel);
+ //MIDI.sendNoteOff(note, velocity, channel);
+ }
+
+#ifndef ETHERNET3
+ EthernetBonjour.run();
+#endif
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t& ssrc, const APPLEMIDI_NAMESPACE::Exception& e, const int32_t value ) {
+ switch (e)
+ {
+ case APPLEMIDI_NAMESPACE::Exception::BufferFullException:
+ AM_DBG(F("*** BufferFullException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ParseException:
+ AM_DBG(F("*** ParseException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::TooManyParticipantsException:
+ AM_DBG(F("*** TooManyParticipantsException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::UnexpectedInviteException:
+ AM_DBG(F("*** UnexpectedInviteException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ParticipantNotFoundException:
+ AM_DBG(F("*** ParticipantNotFoundException"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ComputerNotInDirectory:
+ AM_DBG(F("*** ComputerNotInDirectory"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::NotAcceptingAnyone:
+ AM_DBG(F("*** NotAcceptingAnyone"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ListenerTimeOutException:
+ AM_DBG(F("*** ListenerTimeOutException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::MaxAttemptsException:
+ AM_DBG(F("*** MaxAttemptsException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::NoResponseFromConnectionRequestException:
+ AM_DBG(F("***:yyy did't respond to the connection request. Check the address and port, and any firewall or router settings. (time)"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::SendPacketsDropped:
+ AM_DBG(F("*** SendPacketsDropped"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ReceivedPacketsDropped:
+ AM_DBG(F("******************************************** ReceivedPacketsDropped"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::UdpBeginPacketFailed:
+ AM_DBG(F("*** UdpBeginPacketFailed"), value);
+ break;
+ }
+}
diff --git a/examples/ESP32_W5500_Callbacks/ETH_Helper.h b/examples/ESP32_W5500_Callbacks/ETH_Helper.h
new file mode 100644
index 0000000..7d0f0b8
--- /dev/null
+++ b/examples/ESP32_W5500_Callbacks/ETH_Helper.h
@@ -0,0 +1,110 @@
+#ifdef ETHERNET3
+#include
+#else
+#include
+#include // https://github.com/TrippyLighting/EthernetBonjour
+#endif
+
+// to get the Mac address
+#include
+
+#define RESET_PIN 26
+#define CS_PIN 5
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+/*
+ Wiz W5500 reset function. Change this for the specific reset
+ sequence required for your particular board or module.
+*/
+void hardreset() {
+ pinMode(RESET_PIN, OUTPUT);
+ digitalWrite(RESET_PIN, HIGH);
+ delay(150);
+
+ digitalWrite(RESET_PIN, LOW);
+ delay(500);
+ digitalWrite(RESET_PIN, HIGH);
+ delay(150);
+}
+
+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);
+
+ /*
+ Network configuration - all except the MAC are optional.
+
+ IMPORTANT NOTE - The mass-produced W5500 boards do -not-
+ have a built-in MAC address (depite
+ 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...");
+#endif
+
+ Ethernet.begin(mac);
+ Serial.print("Ethernet IP is: ");
+ Serial.println(Ethernet.localIP());
+
+ /*
+ Sanity checks for W5500 and cable connection.
+ */
+ 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);
+ } else {
+ rdy_flag = true;
+ break;
+ }
+ }
+ if (rdy_flag == false) {
+ Serial.println("\n\r\tHardware fault, or cable problem... cannot continue.");
+ while (true) {
+ delay(10); // Halt.
+ }
+ } else {
+ 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).
+ // Always call this before any other method!
+ EthernetBonjour.begin("arduino");
+
+ EthernetBonjour.addServiceRecord("Arduino._apple-midi",
+ 5004,
+ MDNSServiceUDP);
+#endif
+
+ return true;
+}
diff --git a/examples/ESP8266_NoteOnOffEverySec/ESP8266_NoteOnOffEverySec.ino b/examples/ESP8266_NoteOnOffEverySec/ESP8266_NoteOnOffEverySec.ino
new file mode 100644
index 0000000..082b62e
--- /dev/null
+++ b/examples/ESP8266_NoteOnOffEverySec/ESP8266_NoteOnOffEverySec.ino
@@ -0,0 +1,80 @@
+#include
+#include
+#include
+
+#define SerialMon Serial
+#include
+
+char ssid[] = "ssid"; // your network SSID (name)
+char pass[] = "password"; // your network password (use for WPA, or use as key for WEP)
+
+unsigned long t0 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ WiFi.begin(ssid, pass);
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ AM_DBG(F("Establishing connection to WiFi.."));
+ }
+ AM_DBG(F("Connected to network"));
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), WiFi.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+ AM_DBG(F("Listen to incoming MIDI commands"));
+
+ MIDI.begin();
+
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
+
+ AM_DBG(F("Sending NoteOn/Off of note 45, every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t0) > 1000)
+ {
+ t0 = millis();
+
+ byte note = 45;
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+ MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
diff --git a/examples/ESP8266_NoteOnOffEverySec_softAP_mDNS/ESP8266_NoteOnOffEverySec_softAP_mDNS.ino b/examples/ESP8266_NoteOnOffEverySec_softAP_mDNS/ESP8266_NoteOnOffEverySec_softAP_mDNS.ino
new file mode 100644
index 0000000..9ce9276
--- /dev/null
+++ b/examples/ESP8266_NoteOnOffEverySec_softAP_mDNS/ESP8266_NoteOnOffEverySec_softAP_mDNS.ino
@@ -0,0 +1,95 @@
+// Example to start ESP8266 in soft access point / hotspot mode
+// and also enable mDNS response on local network. This allows
+// client to discover the AppleMIDI service and connect to it
+// without having to type the IP address and port
+// Tested on iOS 9 (old iPad) and iOS 13 (iPhone 6)
+// On Win10 (rtpMIDI), ESP8266 did not show in directory,
+// but connects fine with default IP(192.168.4.1)/port(5004)
+
+#include
+#include
+#include
+#include
+
+#define SerialMon Serial
+#include
+
+char ssid[] = "ssid"; // your network SSID (name)
+char pass[] = "password"; // your network password (use for WPA, or use as key for WEP)
+
+unsigned long t0 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ WiFi.softAP(ssid, pass);
+
+ AM_DBG(F("Started soft access point:"), WiFi.softAPIP(), "Port", AppleMIDI.getPort());
+ AM_DBG(F("AppleMIDI device name:"), AppleMIDI.getName());
+ // Set up mDNS responder:
+ if (!MDNS.begin(AppleMIDI.getName()))
+ AM_DBG(F("Error setting up MDNS responder!"));
+ char str[128] = "";
+ strcat(str, AppleMIDI.getName());
+ strcat(str,".local");
+ AM_DBG(F("mDNS responder started at:"), str);
+ MDNS.addService("apple-midi", "udp", AppleMIDI.getPort());
+ AM_DBG(F("Open Wifi settings and connect to soft acess point using 'ssid'"));
+ AM_DBG(F("Start MIDI Network app on iPhone/iPad or rtpMIDI on Windows"));
+ AM_DBG(F("AppleMIDI-ESP8266 will show in the 'Directory' list (rtpMIDI) or"));
+ AM_DBG(F("under 'Found on the network' list (iOS). Select and click 'Connect'"));
+
+ MIDI.begin();
+
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
+
+ AM_DBG(F("Sending NoteOn/Off of note 45, every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+
+ MDNS.update();
+
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t0) > 1000)
+ {
+ t0 = millis();
+
+ byte note = 45;
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+ MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
\ No newline at end of file
diff --git a/examples/EthernetShield_Bonjour/EthernetShield_Bonjour.ino b/examples/EthernetShield_Bonjour/EthernetShield_Bonjour.ino
deleted file mode 100644
index edcd6a9..0000000
--- a/examples/EthernetShield_Bonjour/EthernetShield_Bonjour.ino
+++ /dev/null
@@ -1,148 +0,0 @@
-#include
-#include // https://github.com/TrippyLighting/EthernetBonjour
-
-#define OPTIONAL_MDNS
-
-#include
-USING_NAMESPACE_APPLEMIDI
-
-// Enter a MAC address for your controller below.
-// Newer Ethernet shields have a MAC address printed on a sticker on the shield
-byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
-};
-
-unsigned long t1 = millis();
-bool isConnected = false;
-
-APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
- Serial.println(F("Getting IP address..."));
-
- if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
- for (;;);
- }
-
- Serial.print(F("IP address is "));
- Serial.println(Ethernet.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(Ethernet.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- // Listen for MIDI messages on channel 1
- MIDI.begin(1);
-
- // 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).
- // Always call this before any other method!
- EthernetBonjour.begin("arduino");
-
- EthernetBonjour.addServiceRecord("Arduino._apple-midi",
- AppleMIDI.getPort(),
- MDNSServiceUDP);
-
- // Stay informed on connection status
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
- AppleMIDI.setHandleError(OnAppleMidiError);
-
- // and let us know ehen notes come in
- MIDI.setHandleNoteOn(OnMidiNoteOn);
- MIDI.setHandleNoteOff(OnMidiNoteOff);
-
- Serial.println(F("Every second send a random NoteOn/Off"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void loop()
-{
- // Listen to incoming notes
- MIDI.read();
-
- EthernetBonjour.run();
-
- // send note on/off every second
- // (dont cáll delay(1000) as it will stall the pipeline)
- if (isConnected && (millis() - t1) > 1000)
- {
- t1 = millis();
- // Serial.print(F(".");
-
- byte note = random(1, 127);
- byte velocity = 55;
- byte channel = 1;
-
- MIDI.sendNoteOn(note, velocity, channel);
- MIDI.sendNoteOff(note, velocity, channel);
- }
-}
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Error occorded during processing
-// -----------------------------------------------------------------------------
-void OnAppleMidiError(const ssrc_t & ssrc, int32_t errorCode) {
- Serial.println(F("ERROR"));
- exit(1);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiNoteOn(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOn from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiNoteOff(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOff from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
diff --git a/examples/EthernetShield_Initiator/EthernetShield_Initiator.ino b/examples/EthernetShield_Initiator/EthernetShield_Initiator.ino
deleted file mode 100644
index f9c3f6f..0000000
--- a/examples/EthernetShield_Initiator/EthernetShield_Initiator.ino
+++ /dev/null
@@ -1,132 +0,0 @@
-#include
-
-#define APPLEMIDI_INITIATOR
-#include
-USING_NAMESPACE_APPLEMIDI
-
-// Enter a MAC address for your controller below.
-// Newer Ethernet shields have a MAC address printed on a sticker on the shield
-byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
-};
-
-unsigned long t1 = millis();
-bool isConnected = false;
-
-APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
- Serial.println(F("Getting IP address..."));
-
- if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
- for (;;);
- }
-
- Serial.print(F("IP address is "));
- Serial.println(Ethernet.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(Ethernet.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- // Listen for MIDI messages on channel 1
- MIDI.begin(1);
-
- // Stay informed on connection status
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
-
- // and let us know ehen notes come in
- MIDI.setHandleNoteOn(OnMidiNoteOn);
- MIDI.setHandleNoteOff(OnMidiNoteOff);
-
- // Initiate the session
- IPAddress remote(192, 168, 1, 156);
- AppleMIDI.sendInvite(remote); // port is 5004 by default
-
- Serial.println(F("Every second send a random NoteOn/Off"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void loop()
-{
- // Listen to incoming notes
- MIDI.read();
-
- // send note on/off every second
- // (dont cáll delay(1000) as it will stall the pipeline)
- if (isConnected && (millis() - t1) > 1000)
- {
- t1 = millis();
- // Serial.print(F(".");
-
- byte note = random(1, 127);
- byte velocity = 55;
- byte channel = 1;
-
- // MIDI.sendNoteOn(note, velocity, channel);
- // MIDI.sendNoteOff(note, velocity, channel);
- }
-}
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.print(name);
- Serial.print(F(" ssrc: 0x"));
- Serial.println(ssrc, HEX);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.print (F("Disconnected from ssrc 0x"));
- Serial.println(ssrc, HEX);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiNoteOn(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOn from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiNoteOff(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOff from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
diff --git a/examples/EthernetShield_NoteOnOffEverySec/EthernetShield_NoteOnOffEverySec.ino b/examples/EthernetShield_NoteOnOffEverySec/EthernetShield_NoteOnOffEverySec.ino
deleted file mode 100644
index c211404..0000000
--- a/examples/EthernetShield_NoteOnOffEverySec/EthernetShield_NoteOnOffEverySec.ino
+++ /dev/null
@@ -1,95 +0,0 @@
-#include
-
-#include
-USING_NAMESPACE_APPLEMIDI
-
-// Enter a MAC address for your controller below.
-// Newer Ethernet shields have a MAC address printed on a sticker on the shield
-byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
-};
-
-unsigned long t1 = millis();
-bool isConnected = false;
-
-APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
- Serial.println(F("Getting IP address..."));
-
- if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
- for (;;);
- }
-
- Serial.print(F("IP address is "));
- Serial.println(Ethernet.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(Ethernet.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
-
- // Listen for MIDI messages on channel 1
- MIDI.begin(1);
-
- // Stay informed on connection status
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
-
- Serial.println(F("Send MIDI messages to this session and see the latency on the Serial Monitor"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void loop()
-{
- // Listen to incoming notes
- MIDI.read();
-
- // send a note every second
- // (dont cáll delay(1000) as it will stall the pipeline)
- if (isConnected && (millis() - t1) > 1000)
- {
- t1 = millis();
- // Serial.print(F(".");
-
- byte note = random(1, 127);
- byte velocity = 55;
- byte channel = 1;
-
- MIDI.sendNoteOn(note, velocity, channel);
- MIDI.sendNoteOff(note, velocity, channel);
- }
-}
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
diff --git a/examples/EthernetShield_RealTimeMessages/EthernetShield_RealTimeMessages.ino b/examples/EthernetShield_RealTimeMessages/EthernetShield_RealTimeMessages.ino
deleted file mode 100644
index f6052cd..0000000
--- a/examples/EthernetShield_RealTimeMessages/EthernetShield_RealTimeMessages.ino
+++ /dev/null
@@ -1,162 +0,0 @@
-#include
-
-#include
-USING_NAMESPACE_APPLEMIDI
-
-// Enter a MAC address for your controller below.
-// Newer Ethernet shields have a MAC address printed on a sticker on the shield
-byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
-};
-
-bool isConnected;
-
-APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
-
- Serial.println(F("Getting IP address..."));
-
- if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
- for (;;);
- }
-
- Serial.print("IP address is ");
- Serial.println(Ethernet.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(Ethernet.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- // Create a session and wait for a remote host to connect to us
- MIDI.begin(1);
-
- // check: zien we de connecttion binnenkomen?? Anders terug een ref van maken
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
- AppleMIDI.setHandleError(OnAppleMidiError);
-
- MIDI.setHandleClock(OnMidiClock);
- MIDI.setHandleStart(OnMidiStart);
- MIDI.setHandleStop(OnMidiStop);
- MIDI.setHandleContinue(OnMidiContinue);
- MIDI.setHandleActiveSensing(OnMidiActiveSensing);
- MIDI.setHandleSystemReset(OnMidiSystemReset);
- MIDI.setHandleSongPosition(OnMidiSongPosition);
-
- Serial.println(F("Every second send a random NoteOn/Off"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void loop()
-{
- // Listen to incoming notes
- MIDI.read();
-}
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Error occorded during processing
-// -----------------------------------------------------------------------------
-void OnAppleMidiError(const ssrc_t & ssrc, int32_t errorCode) {
- Serial.println(F("ERROR"));
- exit(1);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiClock() {
- Serial.println(F("Clock"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiStart() {
- Serial.println(F("Start"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiStop() {
- Serial.println(F("Stop"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiContinue() {
- Serial.println(F("Continue"));
-}
-
-// -----------------------------------------------------------------------------
-// (https://www.midi.org/specifications/item/table-1-summary-of-midi-message)
-// Active Sensing.
-// This message is intended to be sent repeatedly to tell the receiver that a
-// connection is alive. Use of this message is optional. When initially received,
-// the receiver will expect to receive another Active Sensing message each 300ms (max),
-// and if it does not then it will assume that the connection has been terminated.
-// At termination, the receiver will turn off all voices and return to normal
-// (non- active sensing) operation.
-// -----------------------------------------------------------------------------
-static void OnMidiActiveSensing() {
- Serial.println(F("ActiveSensing"));
-}
-
-// -----------------------------------------------------------------------------
-// (https://www.midi.org/specifications/item/table-1-summary-of-midi-message)
-// Reset.
-// Reset all receivers in the system to power-up status. This should be used
-// sparingly, preferably under manual control. In particular, it should not be
-// sent on power-up.
-// -----------------------------------------------------------------------------
-static void OnMidiSystemReset() {
- Serial.println(F("SystemReset"));
-}
-
-// -----------------------------------------------------------------------------
-// (https://www.midi.org/specifications/item/table-1-summary-of-midi-message)
-// Song Position Pointer.
-// This is an internal 14 bit register that holds the number of MIDI beats
-// (1 beat= six MIDI clocks) since the start of the song. l is the LSB, m the MSB.
-// -----------------------------------------------------------------------------
-static void OnMidiSongPosition(unsigned a) {
- Serial.print (F("SongPosition: "));
- Serial.println(a);
-}
diff --git a/examples/EthernetShield_ReceiveMTC/EthernetShield_ReceiveMTC.ino b/examples/EthernetShield_ReceiveMTC/EthernetShield_ReceiveMTC.ino
deleted file mode 100644
index 7bc351c..0000000
--- a/examples/EthernetShield_ReceiveMTC/EthernetShield_ReceiveMTC.ino
+++ /dev/null
@@ -1,90 +0,0 @@
-#include
-
-#include
-USING_NAMESPACE_APPLEMIDI
-
-// Enter a MAC address for your controller below.
-// Newer Ethernet shields have a MAC address printed on a sticker on the shield
-byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
-};
-
-bool isConnected = false;
-
-APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
- Serial.println(F("Getting IP address..."));
-
- if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
- for (;;);
- }
-
- Serial.print("IP address is ");
- Serial.println(Ethernet.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(Ethernet.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- // Create a session and wait for a remote host to connect to us
- MIDI.begin(1);
-
- // check: zien we de connecttion binnenkomen?? Anders terug een ref van maken
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
-
- MIDI.setHandleTimeCodeQuarterFrame(OnMidiTimeCodeQuarterFrame);
-
- Serial.println(F("Every second send a random NoteOn/Off"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void loop()
-{
- // Listen to incoming notes
- MIDI.read();
-}
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiTimeCodeQuarterFrame(byte data) {
- Serial.print("MTC: ");
- Serial.println(data, HEX);
-}
diff --git a/examples/EthernetShield_ReceivedRawMidiData/EthernetShield_ReceivedRawMidiData.ino b/examples/EthernetShield_ReceivedRawMidiData/EthernetShield_ReceivedRawMidiData.ino
deleted file mode 100644
index c3b026f..0000000
--- a/examples/EthernetShield_ReceivedRawMidiData/EthernetShield_ReceivedRawMidiData.ino
+++ /dev/null
@@ -1,117 +0,0 @@
-#include
-
-#include
-USING_NAMESPACE_APPLEMIDI
-
-// Enter a MAC address for your controller below.
-// Newer Ethernet shields have a MAC address printed on a sticker on the shield
-byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
-};
-
-unsigned long t1 = millis();
-bool isConnected = false;
-
-APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
-
- Serial.println(F("Getting IP address..."));
-
- if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
- for (;;);
- }
-
- Serial.print(F("IP address is "));
- Serial.println(Ethernet.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(Ethernet.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- // Create a session and wait for a remote host to connect to us
- MIDI.begin(1);
-
- // check: zien we de connecttion binnenkomen?? Anders terug een ref van maken
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
- AppleMIDI.setHandleReceivedMidi(OnAppleMidiReceivedByte);
-
- MIDI.setHandleNoteOn(OnMidiNoteOn);
- MIDI.setHandleNoteOff(OnMidiNoteOff);
-
- Serial.println(F("Waiting for incoming MIDI messages"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void loop()
-{
- // Listen to incoming notes
- MIDI.read();
-}
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void OnAppleMidiReceivedByte(const ssrc_t & ssrc, byte data) {
- Serial.println(data, HEX);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiNoteOn(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOn from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnMidiNoteOff(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOff from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
diff --git a/examples/EthernetShield_SysEx/EthernetShield_SysEx.ino b/examples/EthernetShield_SysEx/EthernetShield_SysEx.ino
deleted file mode 100644
index fdcb968..0000000
--- a/examples/EthernetShield_SysEx/EthernetShield_SysEx.ino
+++ /dev/null
@@ -1,135 +0,0 @@
-#include
-
-#include
-USING_NAMESPACE_APPLEMIDI
-
-// Enter a MAC address for your controller below.
-// Newer Ethernet shields have a MAC address printed on a sticker on the shield
-byte mac[] = {
- 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
-};
-
-unsigned long t1 = millis();
-bool isConnected;
-
-byte sysex14[] = { 0xF0, 0x43, 0x20, 0x7E, 0x4C, 0x4D, 0x20, 0x20, 0x38, 0x39, 0x37, 0x33, 0x50, 0xF7 };
-byte sysex15[] = { 0xF0, 0x43, 0x20, 0x7E, 0x4C, 0x4D, 0x20, 0x20, 0x38, 0x39, 0x37, 0x33, 0x50, 0x4D, 0xF7 };
-byte sysex16[] = { 0xF0, 0x43, 0x20, 0x7E, 0x4C, 0x4D, 0x20, 0x20, 0x38, 0x39, 0x37, 0x33, 0x32, 0x50, 0x4D, 0xF7 };
-byte sysexBig[] = { 0xF0, 0x41,
- 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
- 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
- 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
- 0x7a,
-
- 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
- 0xF7 };
-
-APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
- Serial.println(F("Getting IP address..."));
-
- if (Ethernet.begin(mac) == 0) {
- Serial.println(F("Failed DHCP, check network cable & reboot"));
- for (;;);
- }
-
- Serial.print("IP address is ");
- Serial.println(Ethernet.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(Ethernet.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- // Create a session and wait for a remote host to connect to us
- MIDI.begin(1);
-
- // check: zien we de connecttion binnenkomen?? Anders terug een ref van makenDw
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
-
- MIDI.setHandleSystemExclusive(OnMidiSysEx);
-
- Serial.println(F("Every second send a random NoteOn/Off"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void loop()
-{
- // Listen to incoming notes
- MIDI.read();
-
- // send a note every second
- // (dont cáll delay(1000) as it will stall the pipeline)
- if (isConnected && (millis() - t1) > 1000)
- {
- MIDI.sendSysEx(sizeof(sysexBig), sysexBig, true);
- t1 = millis();
- }
-}
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-void OnMidiSysEx(byte* data, unsigned length) {
- Serial.print(F("SYSEX: ("));
- Serial.print(getSysExStatus(data, length));
- Serial.print(F(", "));
- Serial.print(length);
- Serial.print(F(" bytes) "));
- for (uint16_t i = 0; i < length; i++)
- {
- Serial.print(data[i], HEX);
- Serial.print(" ");
- }
- Serial.println();
-}
-
-char getSysExStatus(const byte* data, uint16_t length)
-{
- if (data[0] == 0xF0 && data[length - 1] == 0xF7)
- return 'F'; // Full SysEx Command
- else if (data[0] == 0xF0 && data[length - 1] != 0xF7)
- return 'S'; // Start of SysEx-Segment
- else if (data[0] != 0xF0 && data[length - 1] != 0xF7)
- return 'M'; // Middle of SysEx-Segment
- else
- return 'E'; // End of SysEx-Segment
-}
diff --git a/examples/SAMD_Bonjour/SAMD_Bonjour.ino b/examples/SAMD_Bonjour/SAMD_Bonjour.ino
new file mode 100644
index 0000000..ae73f26
--- /dev/null
+++ b/examples/SAMD_Bonjour/SAMD_Bonjour.ino
@@ -0,0 +1,44 @@
+#include
+#include // https://github.com/TrippyLighting/EthernetBonjour
+
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ if (Ethernet.begin(mac) == 0)
+ for (;;);
+
+ MIDI.begin();
+
+ // 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).
+ // Always call this before any other method!
+ EthernetBonjour.begin("arduino");
+
+ EthernetBonjour.addServiceRecord("Arduino._apple-midi",
+ AppleMIDI.getPort(),
+ MDNSServiceUDP);
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ EthernetBonjour.run();
+}
diff --git a/examples/Teensy41_NoteOnOffEverySec/Teensy41_NoteOnOffEverySec.ino b/examples/Teensy41_NoteOnOffEverySec/Teensy41_NoteOnOffEverySec.ino
new file mode 100644
index 0000000..4a5278c
--- /dev/null
+++ b/examples/Teensy41_NoteOnOffEverySec/Teensy41_NoteOnOffEverySec.ino
@@ -0,0 +1,149 @@
+#include
+
+#define SerialMon Serial
+#define USE_EXT_CALLBACKS
+#include
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = {
+ 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
+};
+
+unsigned long t1 = millis();
+int8_t isConnected = 0;
+
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
+
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t&, const APPLEMIDI_NAMESPACE::Exception&, const int32_t);
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void setup()
+{
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
+
+ if (Ethernet.begin(mac) == 0) {
+ AM_DBG(F("Failed DHCP, check network cable & reboot"));
+ for (;;);
+ }
+
+ // Check for Ethernet hardware present
+ if (Ethernet.hardwareStatus() == EthernetNoHardware) {
+ AM_DBG(F("Ethernet shield was not found. Sorry, can't run without hardware. :("));
+ while (true) {
+ delay(1); // do nothing, no point running without Ethernet hardware
+ }
+ }
+ while (Ethernet.linkStatus() == LinkOFF) {
+ AM_DBG(F("Ethernet cable is not connected."));
+ delay(500);
+ }
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin(MIDI_CHANNEL_OMNI);
+
+ // Stay informed on connection status
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+
+ AppleMIDI.setHandleException(OnAppleMidiException);
+
+ MIDI.setHandleControlChange([](Channel channel, byte v1, byte v2) {
+ AM_DBG(F("ControlChange"), channel, v1, v2);
+ });
+ MIDI.setHandleProgramChange([](Channel channel, byte v1) {
+ AM_DBG(F("ProgramChange"), channel, v1);
+ });
+ MIDI.setHandlePitchBend([](Channel channel, int v1) {
+ AM_DBG(F("PitchBend"), channel, v1);
+ });
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), channel, note, velocity);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), channel, note, velocity);
+ });
+
+ AM_DBG(F("Sending MIDI messages every second"));
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void loop()
+{
+ // Listen to incoming notes
+ MIDI.read();
+
+ // send a note every second
+ // (dont cáll delay(1000) as it will stall the pipeline)
+ if ((isConnected > 0) && (millis() - t1) > 1000)
+ {
+ t1 = millis();
+
+ byte note = random(1, 127);
+ byte velocity = 55;
+ byte channel = 1;
+
+ MIDI.sendNoteOn(note, velocity, channel);
+// MIDI.sendNoteOff(note, velocity, channel);
+ }
+}
+
+// -----------------------------------------------------------------------------
+//
+// -----------------------------------------------------------------------------
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t& ssrc, const APPLEMIDI_NAMESPACE::Exception& e, const int32_t value ) {
+ switch (e)
+ {
+ case APPLEMIDI_NAMESPACE::Exception::BufferFullException:
+ AM_DBG(F("*** BufferFullException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ParseException:
+ AM_DBG(F("*** ParseException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::TooManyParticipantsException:
+ AM_DBG(F("*** TooManyParticipantsException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::UnexpectedInviteException:
+ AM_DBG(F("*** UnexpectedInviteException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ParticipantNotFoundException:
+ AM_DBG(F("*** ParticipantNotFoundException"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ComputerNotInDirectory:
+ AM_DBG(F("*** ComputerNotInDirectory"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::NotAcceptingAnyone:
+ AM_DBG(F("*** NotAcceptingAnyone"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ListenerTimeOutException:
+ AM_DBG(F("*** ListenerTimeOutException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::MaxAttemptsException:
+ AM_DBG(F("*** MaxAttemptsException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::NoResponseFromConnectionRequestException:
+ AM_DBG(F("***:yyy did't respond to the connection request. Check the address and port, and any firewall or router settings. (time)"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::SendPacketsDropped:
+ AM_DBG(F("*** SendPacketsDropped"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ReceivedPacketsDropped:
+ AM_DBG(F("*** ReceivedPacketsDropped"), value);
+ break;
+ }
+}
diff --git a/examples/wESP32_NoteOnOffEverySec/ETH_Helper.h b/examples/wESP32_NoteOnOffEverySec/ETH_Helper.h
index e997fad..cab1ca1 100644
--- a/examples/wESP32_NoteOnOffEverySec/ETH_Helper.h
+++ b/examples/wESP32_NoteOnOffEverySec/ETH_Helper.h
@@ -7,32 +7,25 @@ void WiFiEvent(WiFiEvent_t event)
{
switch (event) {
case SYSTEM_EVENT_ETH_START:
- Serial.println("ETH Started");
+ AM_DBG(F("ETH Started"));
//set eth hostname here
ETH.setHostname("esp32-ethernet");
break;
case SYSTEM_EVENT_ETH_CONNECTED:
- Serial.println("ETH Connected");
+ AM_DBG(F("ETH Connected"));
break;
case SYSTEM_EVENT_ETH_GOT_IP:
- Serial.print("ETH MAC: ");
- Serial.print(ETH.macAddress());
- Serial.print(", IPv4: ");
- Serial.print(ETH.localIP());
- if (ETH.fullDuplex()) {
- Serial.print(", FULL_DUPLEX");
- }
- Serial.print(", ");
- Serial.print(ETH.linkSpeed());
- Serial.println("Mbps");
+ AM_DBG(F("ETH MAC:"), ETH.macAddress(), F("IPv4:"), ETH.localIP(), ETH.linkSpeed(), F("Mbps"));
+ if (ETH.fullDuplex())
+ AM_DBG(F("FULL_DUPLEX"));
eth_connected = true;
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
- Serial.println("ETH Disconnected");
+ AM_DBG(F("ETH Disconnected"));
eth_connected = false;
break;
case SYSTEM_EVENT_ETH_STOP:
- Serial.println("ETH Stopped");
+ AM_DBG(F("ETH Stopped"));
eth_connected = false;
break;
default:
@@ -45,7 +38,7 @@ bool ETH_startup()
WiFi.onEvent(WiFiEvent);
ETH.begin();
- Serial.println(F("Getting IP address..."));
+ AM_DBG(F("Getting IP address..."));
while (!eth_connected)
delay(100);
diff --git a/examples/wESP32_NoteOnOffEverySec/wESP32_NoteOnOffEverySec.ino b/examples/wESP32_NoteOnOffEverySec/wESP32_NoteOnOffEverySec.ino
index a57c7a6..fe58e9d 100644
--- a/examples/wESP32_NoteOnOffEverySec/wESP32_NoteOnOffEverySec.ino
+++ b/examples/wESP32_NoteOnOffEverySec/wESP32_NoteOnOffEverySec.ino
@@ -1,53 +1,55 @@
-#include "ETH_Helper.h"
-
+#define SerialMon Serial
+#define USE_EXT_CALLBACKS
#include
-USING_NAMESPACE_APPLEMIDI
+
+#include "./ETH_Helper.h"
unsigned long t0 = millis();
-bool isConnected = false;
+int8_t isConnected = 0;
-APPLEMIDI_CREATE_DEFAULTSESSION_ESP32_INSTANCE();
+APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
-//WiFiServer server(80);
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t&, const APPLEMIDI_NAMESPACE::Exception&, const int32_t);
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void setup()
{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
+ AM_DBG_SETUP(115200);
+ AM_DBG(F("Booting"));
ETH_startup();
- // Start TCP (HTTP) server
- // server.begin();
-
- MDNS.begin(AppleMIDI.getName());
-
- Serial.print("IP address is ");
- Serial.println(ETH.localIP());
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(ETH.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- MIDI.begin(1); // listen on channel 1
-
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
-
- MIDI.setHandleNoteOn(OnAppleMidiNoteOn);
- MIDI.setHandleNoteOff(OnAppleMidiNoteOff);
+ if (!MDNS.begin(AppleMIDI.getName()))
+ AM_DBG(F("Error setting up MDNS responder"));
+
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), ETH.localIP(), "Port", AppleMIDI.getPort());
+ AM_DBG(F("The device should also be visible in the directory as"), AppleMIDI.getName());
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin();
+
+ AppleMIDI.setHandleConnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
+ isConnected++;
+ AM_DBG(F("Connected to session"), ssrc, name);
+ });
+ AppleMIDI.setHandleDisconnected([](const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
+ isConnected--;
+ AM_DBG(F("Disconnected"), ssrc);
+ });
+ AppleMIDI.setHandleException(OnAppleMidiException);
+
+ MIDI.setHandleNoteOn([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOn"), note);
+ });
+ MIDI.setHandleNoteOff([](byte channel, byte note, byte velocity) {
+ AM_DBG(F("NoteOff"), note);
+ });
MDNS.addService("apple-midi", "udp", AppleMIDI.getPort());
- MDNS.addService("http", "tcp", 80);
-
- Serial.println(F("Every second send a random NoteOn/Off"));
}
// -----------------------------------------------------------------------------
@@ -60,61 +62,61 @@ void loop()
// send a note every second
// (dont cáll delay(1000) as it will stall the pipeline)
- if (isConnected && (millis() - t0) > 1000)
+ if ((isConnected > 0) && (millis() - t0) > 100)
{
t0 = millis();
- // Serial.print(F(".");
- byte note = random(1, 127);
+ byte note = random(15, 100);
byte velocity = 55;
byte channel = 1;
MIDI.sendNoteOn(note, velocity, channel);
- MIDI.sendNoteOff(note, velocity, channel);
+ // MIDI.sendNoteOff(note, velocity, channel);
}
}
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnAppleMidiNoteOn(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOn from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
-static void OnAppleMidiNoteOff(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOff from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
+void OnAppleMidiException(const APPLEMIDI_NAMESPACE::ssrc_t& ssrc, const APPLEMIDI_NAMESPACE::Exception& e, const int32_t value ) {
+ switch (e)
+ {
+ case APPLEMIDI_NAMESPACE::Exception::BufferFullException:
+ AM_DBG(F("*** BufferFullException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ParseException:
+ AM_DBG(F("*** ParseException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::TooManyParticipantsException:
+ AM_DBG(F("*** TooManyParticipantsException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::UnexpectedInviteException:
+ AM_DBG(F("*** UnexpectedInviteException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ParticipantNotFoundException:
+ AM_DBG(F("*** ParticipantNotFoundException"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ComputerNotInDirectory:
+ AM_DBG(F("*** ComputerNotInDirectory"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::NotAcceptingAnyone:
+ AM_DBG(F("*** NotAcceptingAnyone"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ListenerTimeOutException:
+ AM_DBG(F("*** ListenerTimeOutException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::MaxAttemptsException:
+ AM_DBG(F("*** MaxAttemptsException"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::NoResponseFromConnectionRequestException:
+ AM_DBG(F("***:yyy did't respond to the connection request. Check the address and port, and any firewall or router settings. (time)"));
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::SendPacketsDropped:
+ AM_DBG(F("*** SendPacketsDropped"), value);
+ break;
+ case APPLEMIDI_NAMESPACE::Exception::ReceivedPacketsDropped:
+ AM_DBG(F("*** ReceivedPacketsDropped"), value);
+ break;
+ }
}
diff --git a/examples/wESP32_NoteOnOffEverySec_Adv/AppleMIDITask.h b/examples/wESP32_NoteOnOffEverySec_Adv/AppleMIDITask.h
deleted file mode 100644
index db700a8..0000000
--- a/examples/wESP32_NoteOnOffEverySec_Adv/AppleMIDITask.h
+++ /dev/null
@@ -1,102 +0,0 @@
-#include
-USING_NAMESPACE_APPLEMIDI
-
-bool isConnected = false;
-
-// ====================================================================================
-// Event handlers for incoming MIDI messages
-// ====================================================================================
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device connected
-// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
- isConnected = true;
- Serial.print(F("Connected to session "));
- Serial.println(name);
-}
-
-// -----------------------------------------------------------------------------
-// rtpMIDI session. Device disconnected
-// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
- isConnected = false;
- Serial.println(F("Disconnected"));
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnAppleMidiNoteOn(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOn from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
-
-// -----------------------------------------------------------------------------
-//
-// -----------------------------------------------------------------------------
-static void OnAppleMidiNoteOff(byte channel, byte note, byte velocity) {
- Serial.print(F("Incoming NoteOff from channel: "));
- Serial.print(channel);
- Serial.print(F(", note: "));
- Serial.print(note);
- Serial.print(F(", velocity: "));
- Serial.println(velocity);
-}
-
-void TaskAppleMIDIcode(void* pvParameters)
-{
- APPLEMIDI_CREATE_DEFAULTSESSION_ESP32_INSTANCE();
-
- Serial.print("AppleMIDI running on core ");
- Serial.println(xPortGetCoreID());
-
- Serial.println(F("Every second send a random NoteOn/Off"));
-
- Serial.println(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- Serial.print(F("Add device named Arduino with Host/Port "));
- Serial.print(ETH.localIP());
- Serial.println(F(":5004"));
- Serial.println(F("Then press the Connect button"));
- Serial.println(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- AppleMIDI.setHandleConnected(OnAppleMidiConnected);
- AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
-
- MIDI.setHandleNoteOn(OnAppleMidiNoteOn);
- MIDI.setHandleNoteOff(OnAppleMidiNoteOff);
-
- MDNS.addService("apple-midi", "udp", AppleMIDI.getPort());
-
- MIDI.begin(1); // listen on channel 1
-
- auto t0 = millis();
- while (true)
- {
- // Listen to incoming notes
- MIDI.read();
-
- // send a note every second
- // (dont cáll delay(1000) as it will stall the pipeline)
- if ((millis() - t0) > 1000)
- {
- t0 = millis();
-
- if (isConnected)
- {
- byte note = random(1, 127);
- byte velocity = 55;
- byte channel = 1;
-
- Serial.println("Sending notes");
-
- MIDI.sendNoteOn(note, velocity, channel);
- MIDI.sendNoteOff(note, velocity, channel);
- }
- }
- }
-}
diff --git a/examples/wESP32_NoteOnOffEverySec_Adv/ETH_Helper.h b/examples/wESP32_NoteOnOffEverySec_Adv/ETH_Helper.h
deleted file mode 100644
index e997fad..0000000
--- a/examples/wESP32_NoteOnOffEverySec_Adv/ETH_Helper.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#include
-#include
-
-static bool eth_connected = false;
-
-void WiFiEvent(WiFiEvent_t event)
-{
- switch (event) {
- case SYSTEM_EVENT_ETH_START:
- Serial.println("ETH Started");
- //set eth hostname here
- ETH.setHostname("esp32-ethernet");
- break;
- case SYSTEM_EVENT_ETH_CONNECTED:
- Serial.println("ETH Connected");
- break;
- case SYSTEM_EVENT_ETH_GOT_IP:
- Serial.print("ETH MAC: ");
- Serial.print(ETH.macAddress());
- Serial.print(", IPv4: ");
- Serial.print(ETH.localIP());
- if (ETH.fullDuplex()) {
- Serial.print(", FULL_DUPLEX");
- }
- Serial.print(", ");
- Serial.print(ETH.linkSpeed());
- Serial.println("Mbps");
- eth_connected = true;
- break;
- case SYSTEM_EVENT_ETH_DISCONNECTED:
- Serial.println("ETH Disconnected");
- eth_connected = false;
- break;
- case SYSTEM_EVENT_ETH_STOP:
- Serial.println("ETH Stopped");
- eth_connected = false;
- break;
- default:
- break;
- }
-}
-
-bool ETH_startup()
-{
- WiFi.onEvent(WiFiEvent);
- ETH.begin();
-
- Serial.println(F("Getting IP address..."));
-
- while (!eth_connected)
- delay(100);
-
- return true;
-}
diff --git a/examples/wESP32_NoteOnOffEverySec_Adv/WebServerTask.h b/examples/wESP32_NoteOnOffEverySec_Adv/WebServerTask.h
deleted file mode 100644
index 932206e..0000000
--- a/examples/wESP32_NoteOnOffEverySec_Adv/WebServerTask.h
+++ /dev/null
@@ -1,63 +0,0 @@
-void TaskWebServercode(void* pvParameters)
-{
- Serial.print("WebServer running on core ");
- Serial.println(xPortGetCoreID());
-
- MDNS.addService("http", "tcp", 80);
-
- WiFiServer server(80);
- server.begin();
-
- while (true)
- {
- delay(100);
- // Check if a client has connected
- WiFiClient client = server.available();
- if (!client)
- continue;
-
- Serial.println("");
- Serial.println("New client");
-
- // Wait for data from client to become available
- while (client.connected() && !client.available()) {
- delay(1);
- }
-
- // Read the first line of HTTP request
- String req = client.readStringUntil('\r');
-
- // First line of HTTP request looks like "GET /path HTTP/1.1"
- // Retrieve the "/path" part by finding the spaces
- int addr_start = req.indexOf(' ');
- int addr_end = req.indexOf(' ', addr_start + 1);
- if (addr_start == -1 || addr_end == -1) {
- Serial.print("Invalid request: ");
- Serial.println(req);
- return;
- }
- req = req.substring(addr_start + 1, addr_end);
- Serial.print("Request: ");
- Serial.println(req);
-
- String s;
- if (req == "/")
- {
- IPAddress ip = WiFi.localIP();
- String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
- s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n\r\nHello from ESP32 at ";
- s += ipStr;
- s += "\r\n\r\n";
- Serial.println("Sending 200");
- }
- else
- {
- s = "HTTP/1.1 404 Not Found\r\n\r\n";
- Serial.println("Sending 404");
- }
- client.print(s);
-
- client.stop();
- Serial.println("Done with client");
- }
-}
diff --git a/examples/wESP32_NoteOnOffEverySec_Adv/wESP32_NoteOnOffEverySec_Adv.ino b/examples/wESP32_NoteOnOffEverySec_Adv/wESP32_NoteOnOffEverySec_Adv.ino
deleted file mode 100644
index 708e9b4..0000000
--- a/examples/wESP32_NoteOnOffEverySec_Adv/wESP32_NoteOnOffEverySec_Adv.ino
+++ /dev/null
@@ -1,42 +0,0 @@
-#include "ETH_Helper.h"
-
-#include "WebServerTask.h"
-#include "AppleMIDITask.h"
-
-TaskHandle_t TaskWebServer;
-TaskHandle_t TaskAppleMIDI;
-
-void setup()
-{
- Serial.begin(115200);
- while (!Serial);
- Serial.println("Booting");
-
- ETH_startup();
-
- MDNS.begin("ESP32");
-
- //create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
- xTaskCreatePinnedToCore(
- TaskWebServercode, // Task function.
- "WebServer", // name of task.
- 10000, // Stack size of task
- NULL, // parameter of the task
- tskIDLE_PRIORITY, // priority of the task
- &TaskWebServer, // Task handle to keep track of created task
- 0); // pin task to core 0
-
- //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
- xTaskCreatePinnedToCore(
- TaskAppleMIDIcode, // Task function.
- "AppleMIDI", // name of task.
- 10000, // Stack size of task
- NULL, // parameter of the task
- tskIDLE_PRIORITY + 1, // priority of the task
- &TaskAppleMIDI, // Task handle to keep track of created task
- 1); // pin task to core 1
-}
-
-void loop()
-{
-}
diff --git a/keywords.txt b/keywords.txt
index 0a0a68d..e1ff976 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -10,68 +10,25 @@ AppleMidi KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
-
+setName KEYWORD2
+setPort KEYWORD2
setHandleConnected KEYWORD2
setHandleDisconnected KEYWORD2
-setHandleError KEYWORD2
+setHandleException KEYWORD2
+setHandleStartReceivedMidi KEYWORD2
setHandleReceivedMidi KEYWORD2
+setHandleEndReceivedMidi KEYWORD2
setHandleReceivedRtp KEYWORD2
-
-setHandleNoteOff KEYWORD2
-setHandleNoteOn KEYWORD2
-setHandleAfterTouchPoly KEYWORD2
-setHandleControlChange KEYWORD2
-setHandleProgramChange KEYWORD2
-setHandleAfterTouchChannel KEYWORD2
-setHandlePitchBend KEYWORD2
-setHandleSystemExclusive KEYWORD2
-setHandleTimeCodeQuarterFrame KEYWORD2
-setHandleSongPosition KEYWORD2
-setHandleSongSelect KEYWORD2
-setHandleTuneRequest KEYWORD2
-setHandleClock KEYWORD2
-setHandleStart KEYWORD2
-setHandleContinue KEYWORD2
-setHandleStop KEYWORD2
-setHandleActiveSensing KEYWORD2
-setHandleSystemReset KEYWORD2
-
-begin KEYWORD2
-run KEYWORD2
-sendNoteOn KEYWORD2
-sendNoteOff KEYWORD2
-sendAfterTouchPoly KEYWORD2
-sendControlChange KEYWORD2
-sendProgramChange KEYWORD2
-sendAfterTouchChannel KEYWORD2
-sendPitchBend KEYWORD2
-sendSystemEx KEYWORD2
-sendTimeCodeQuarterFrame KEYWORD2
-sendSongPosition KEYWORD2
-sendSongSelect KEYWORD2
-sendTuneRequest KEYWORD2
-sendClock KEYWORD2
-sendStart KEYWORD2
-sendContinue KEYWORD2
-sendStop KEYWORD2
-sendActiveSensing KEYWORD2
-sendSystemReset KEYWORD2
-sendTimeCodeQuarterFrame KEYWORD2
-sendSysEx KEYWORD2
-sendAfterTouch KEYWORD2
-sendPolyPressure KEYWORD2
-sendTick KEYWORD2
+setHandleSendRtp KEYWORD2
#######################################
# Instances (KEYWORD3)
#######################################
AppleMIDI KEYWORD3
-MIDI KEYWORD3
#######################################
# Constants (LITERAL1)
#######################################
APPLEMIDI_CREATE_INSTANCE LITERAL1
-APPLEMIDI_CREATE_DEFAULTSESSION_ESP32_INSTANCE LITERAL1
APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE LITERAL1
diff --git a/library.properties b/library.properties
index 2c9865d..8673196 100644
--- a/library.properties
+++ b/library.properties
@@ -1,9 +1,9 @@
name=AppleMIDI
-version=2.0.4
+version=3.3.0
author=lathoub
maintainer=lathoub
-sentence=AppleMIDI (rtpMIDI) protocol for Arduino
-paragraph=AppleMIDI (also known as rtpMIDI) is a protocol to transport MIDI messages within RTP (Real-time Protocol) packets over Ethernet and WiFi networks. Major rewrite of the library to be faster and use less memory. Read the Wiki page when migrating from v1.* to v2.* (API changes)
+sentence=AppleMIDI (aka rtpMIDI) MIDI I/Os for Arduino
+paragraph=AppleMIDI (aka rtpMIDI) is a protocol to transport MIDI messages within RTP (Real-time Protocol) packets over Ethernet and WiFi networks. This major rewrite is faster, more stable and uses less memory. Read the Wiki page when migrating
category=Communication
url=https://github.com/lathoub/Arduino-AppleMidi-Library
architectures=*
diff --git a/res/Install3-1.PNG b/res/Install3-1.PNG
new file mode 100644
index 0000000..fc52487
Binary files /dev/null and b/res/Install3-1.PNG differ
diff --git a/res/LogErrors.PNG b/res/LogErrors.PNG
new file mode 100644
index 0000000..7d00163
Binary files /dev/null and b/res/LogErrors.PNG differ
diff --git a/res/latency/ESP32W5500.png b/res/latency/ESP32W5500.png
new file mode 100644
index 0000000..7fbb2d0
Binary files /dev/null and b/res/latency/ESP32W5500.png differ
diff --git a/res/latency/ESP32Wi-Fi.png b/res/latency/ESP32Wi-Fi.png
new file mode 100644
index 0000000..6f39b9e
Binary files /dev/null and b/res/latency/ESP32Wi-Fi.png differ
diff --git a/res/latency/atmega2560.png b/res/latency/atmega2560.png
new file mode 100644
index 0000000..343043b
Binary files /dev/null and b/res/latency/atmega2560.png differ
diff --git a/res/latency/samd21.png b/res/latency/samd21.png
new file mode 100644
index 0000000..5f9c42a
Binary files /dev/null and b/res/latency/samd21.png differ
diff --git a/src/AppleMIDI.h b/src/AppleMIDI.h
index d638273..acd1d9a 100644
--- a/src/AppleMIDI.h
+++ b/src/AppleMIDI.h
@@ -1,15 +1,17 @@
#pragma once
+#include "AppleMIDI_Debug.h"
+
// https://developer.apple.com/library/archive/documentation/Audio/Conceptual/MIDINetworkDriverProtocol/MIDI/MIDI.html
#include
using namespace MIDI_NAMESPACE;
-#include "IPAddress.h"
+#include
+#include "AppleMIDI_PlatformBegin.h"
#include "AppleMIDI_Defs.h"
#include "AppleMIDI_Settings.h"
-#include "AppleMIDI_Platform.h"
#include "rtp_Defs.h"
#include "rtpMIDI_Defs.h"
@@ -22,78 +24,171 @@ using namespace MIDI_NAMESPACE;
#include "AppleMIDI_Namespace.h"
-#ifndef UDP_TX_PACKET_MAX_SIZE
-#define UDP_TX_PACKET_MAX_SIZE 24
-#endif
-
BEGIN_APPLEMIDI_NAMESPACE
static unsigned long now;
-template
+struct AppleMIDISettings : public MIDI_NAMESPACE::DefaultSettings
+{
+ // Packet based protocols prefer the entire message to be parsed
+ // as a whole.
+ static const bool Use1ByteParsing = false;
+};
+
+template
class AppleMIDISession
{
typedef _Settings Settings;
typedef _Platform Platform;
- // Allow these internal classes access to our private members
- // to avoid access by the .ino to internal messages
- friend class AppleMIDIParser;
- friend class rtpMIDIParser;
- friend class MIDI_NAMESPACE::MidiInterface>;
+ // Allow these internal classes access to our private members
+ // to avoid access by the .ino to internal messages
+ friend class AppleMIDIParser;
+ friend class rtpMIDIParser;
public:
- AppleMIDISession(const char *name, const uint16_t port = DEFAULT_CONTROL_PORT)
- {
- this->port = port;
- strncpy(this->localName, name, DefaultSettings::MaxSessionNameLen);
- };
-
- void setHandleConnected(void (*fptr)(const ssrc_t&, const char*)) { _connectedCallback = fptr; }
- void setHandleDisconnected(void (*fptr)(const ssrc_t&)) { _disconnectedCallback = fptr; }
- void setHandleError(void (*fptr)(const ssrc_t&, int32_t)) { _errorCallback = fptr; }
- void setHandleReceivedRtp(void (*fptr)(const ssrc_t&, const Rtp_t&, const int32_t&)) { _receivedRtpCallback = fptr; }
- void setHandleStartReceivedMidi(void (*fptr)(const ssrc_t&)) { _startReceivedMidiByteCallback = fptr; }
- void setHandleReceivedMidi(void (*fptr)(const ssrc_t&, byte)) { _receivedMidiByteCallback = fptr; }
- void setHandleEndReceivedMidi(void (*fptr)(const ssrc_t&)) { _endReceivedMidiByteCallback = fptr; }
-
- const char* getName() { return this->localName; };
- const uint16_t getPort() { return this->port; };
-
+ AppleMIDISession(const char *sessionName, const uint16_t port = DEFAULT_CONTROL_PORT)
+ {
+ this->port = port;
+#ifdef KEEP_SESSION_NAME
+ strncpy(this->localName, sessionName, Settings::MaxSessionNameLen);
+#endif
+
+#ifdef ONE_PARTICIPANT
+ participant.ssrc = 0;
+#endif
+ };
+
+ virtual ~AppleMIDISession(){};
+
+ AppleMIDISession &setHandleConnected(void (*fptr)(const ssrc_t &, const char *))
+ {
+ _connectedCallback = fptr;
+ return *this;
+ }
+ AppleMIDISession &setHandleDisconnected(void (*fptr)(const ssrc_t &))
+ {
+ _disconnectedCallback = fptr;
+ return *this;
+ }
+#ifdef USE_EXT_CALLBACKS
+ AppleMIDISession &setHandleException(void (*fptr)(const ssrc_t &, const Exception &, const int32_t value))
+ {
+ _exceptionCallback = fptr;
+ return *this;
+ }
+ AppleMIDISession &setHandleReceivedRtp(void (*fptr)(const ssrc_t &, const Rtp_t &, const int32_t &))
+ {
+ _receivedRtpCallback = fptr;
+ return *this;
+ }
+ AppleMIDISession &setHandleStartReceivedMidi(void (*fptr)(const ssrc_t &))
+ {
+ _startReceivedMidiByteCallback = fptr;
+ return *this;
+ }
+ AppleMIDISession &setHandleReceivedMidi(void (*fptr)(const ssrc_t &, byte))
+ {
+ _receivedMidiByteCallback = fptr;
+ return *this;
+ }
+ AppleMIDISession &setHandleEndReceivedMidi(void (*fptr)(const ssrc_t &))
+ {
+ _endReceivedMidiByteCallback = fptr;
+ return *this;
+ }
+ AppleMIDISession &setHandleSentRtp(void (*fptr)(const Rtp_t &))
+ {
+ _sentRtpCallback = fptr;
+ return *this;
+ }
+ AppleMIDISession &setHandleSentRtpMidi(void (*fptr)(const RtpMIDI_t &))
+ {
+ _sentRtpMidiCallback = fptr;
+ return *this;
+ }
+#endif
+
+#ifdef KEEP_SESSION_NAME
+ const char *getName() const
+ {
+ return this->localName;
+ };
+ AppleMIDISession &setName(const char *sessionName)
+ {
+ strncpy(this->localName, sessionName, Settings::MaxSessionNameLen);
+ return *this;
+ };
+#else
+ const char *getName() const
+ {
+ return nullptr;
+ };
+ AppleMIDISession &setName(const char *sessionName) { return *this; };
+#endif
+
+ const uint16_t getPort() const
+ {
+ return this->port;
+ };
+
+ // call this method *before* calling begin()
+ AppleMIDISession & setPort(const uint16_t port)
+ {
+ this->port = port;
+ return *this;
+ }
+
+ const ssrc_t getSynchronizationSource() const { return this->ssrc; };
+
#ifdef APPLEMIDI_INITIATOR
bool sendInvite(IPAddress ip, uint16_t port = DEFAULT_CONTROL_PORT);
#endif
void sendEndSession();
-
-protected:
- static const bool thruActivated = false;
- void begin()
- {
+public:
+ // Override default thruActivated. Must be false for all packet based messages
+ static const bool thruActivated = false;
+
+#ifdef USE_DIRECTORY
+ Deque directory;
+ WhoCanConnectToMe whoCanConnectToMe = Anyone;
+#endif
+
+ void begin()
+ {
_appleMIDIParser.session = this;
- _rtpMIDIParser.session = this;
+ _rtpMIDIParser.session = this;
// analogRead(0) is not available on all platforms. The use of millis()
// as it preceded by network calls, so timing is variable and usable
// for the random generator.
randomSeed(millis());
-
- // Each stream is distinguished by a unique SSRC value and has a unique sequence
- // number and RTP timestamp space.
- // this is our SSRC
+
+ // Each stream is distinguished by a unique SSRC value and has a unique sequence
+ // number and RTP timestamp space.
+ // this is our SSRC
//
// NOTE: Arduino random only goes to INT32_MAX (not UINT32_MAX)
-
- this->ssrc = random(1, INT32_MAX) * 2;
+ this->ssrc = random(1, INT32_MAX) * 2;
- controlPort.begin(port);
- dataPort.begin(port + 1);
+ controlPort.begin(port);
+ dataPort.begin(port + 1);
- rtpMidiClock.Init(rtpMidiClock.Now(), MIDI_SAMPLING_RATE_DEFAULT);
+ rtpMidiClock.Init(rtpMidiClock.Now(), MIDI_SAMPLING_RATE_DEFAULT);
}
- bool beginTransmission(MIDI_NAMESPACE::MidiType)
- {
+ void end()
+ {
+#ifdef ONE_PARTICIPANT
+ participant.ssrc = 0;
+#endif
+ controlPort.stop();
+ dataPort.stop();
+ }
+
+ bool beginTransmission(MIDI_NAMESPACE::MidiType)
+ {
// All MIDI commands queued up in the same cycle (during 1 loop execution)
// are send in a single MIDI packet
// (The actual sending happen in the available() method, called at the start of the
@@ -116,52 +211,56 @@ class AppleMIDISession
else
outMidiBuffer.push_back(0x00); // zero timestamp
}
-
- // We can't start the writing process here, as we do not know the length
- // of what we are to send (The RtpMidi protocol start with writing the
- // length of the buffer). So we'll copy to a buffer in the 'write' method,
- // and actually serialize for real in the endTransmission method
- return (dataPort.remoteIP() != 0 && participants.size() > 0);
- };
-
- void write(byte byte)
- {
+
+ // We can't start the writing process here, as we do not know the length
+ // of what we are to send (The RtpMidi protocol start with writing the
+ // length of the buffer). So we'll copy to a buffer in the 'write' method,
+ // and actually serialize for real in the endTransmission method
+#ifndef ONE_PARTICIPANT
+ return (dataPort.remoteIP() != (IPAddress)INADDR_NONE && participants.size() > 0);
+#else
+ return (dataPort.remoteIP() != (IPAddress)INADDR_NONE && participant.ssrc != 0);
+#endif
+ };
+
+ void write(byte byte)
+ {
// do we still have place in the buffer for 1 more character?
- if ((outMidiBuffer.size()) + 2 > outMidiBuffer.max_size())
- {
- // buffer is almost full, only 1 more character
- if (MIDI_NAMESPACE::MidiType::SystemExclusive == outMidiBuffer.front())
- {
- // Add Sysex at the end of this partial SysEx (in the last availble slot) ...
- outMidiBuffer.push_back(MIDI_NAMESPACE::MidiType::SystemExclusiveStart);
-
+ if ((outMidiBuffer.size()) + 2 > outMidiBuffer.max_size())
+ {
+ // buffer is almost full, only 1 more character
+ if (MIDI_NAMESPACE::MidiType::SystemExclusive == outMidiBuffer.front())
+ {
+ // Add Sysex at the end of this partial SysEx (in the last availble slot) ...
+ outMidiBuffer.push_back(MIDI_NAMESPACE::MidiType::SystemExclusiveStart);
+
writeRtpMidiToAllParticipants();
- // and start again with a fresh continuation of
- // a next SysEx block.
+ // and start again with a fresh continuation of
+ // a next SysEx block.
outMidiBuffer.clear();
- outMidiBuffer.push_back(MIDI_NAMESPACE::MidiType::SystemExclusiveEnd);
+ outMidiBuffer.push_back(MIDI_NAMESPACE::MidiType::SystemExclusiveEnd);
+ }
+ else
+ {
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, BufferFullException, 0);
+#endif
}
- else
- {
- if (NULL != _errorCallback)
- _errorCallback(ssrc, -1);
- }
- }
+ }
- // store in local buffer, as we do *not* know the length of the message prior to sending
- outMidiBuffer.push_back(byte);
- };
+ // store in local buffer, as we do *not* know the length of the message prior to sending
+ outMidiBuffer.push_back(byte);
+ };
- void endTransmission()
- {
- };
+ void endTransmission(){};
// first things MIDI.read() calls in this method
// MIDI-read() must be called at the start of loop()
- unsigned available()
- {
+ unsigned available()
+ {
now = millis();
-
+
#ifdef APPLEMIDI_INITIATOR
manageSessionInvites();
#endif
@@ -171,126 +270,145 @@ class AppleMIDISession
if (outMidiBuffer.size() > 0)
writeRtpMidiToAllParticipants();
// assert(outMidiBuffer.size() == 0); // must be empty
-
+
if (inMidiBuffer.size() > 0)
- return true;
-
- // read packets from both UDP sockets
- readDataPackets(); // from socket into dataBuffer
- readControlPackets(); // from socket into controlBuffer
+ return inMidiBuffer.size();
+
+ if (readDataPackets()) // from socket into dataBuffer
+ parseDataPackets(); // from dataBuffer into inMidiBuffer
- // parses buffer and places MIDI into inMidiBuffer
- parseDataPackets(); // from dataBuffer into inMidiBuffer
- parseControlPackets(); // from controlBuffer
+ if (readControlPackets()) // from socket into controlBuffer
+ parseControlPackets(); // from controlBuffer to AppleMIDI
- manageReceiverFeedback();
+ manageReceiverFeedback();
manageSynchronization();
- return false;
- };
+ return inMidiBuffer.size();
+ };
byte read()
{
auto byte = inMidiBuffer.front();
inMidiBuffer.pop_front();
-
+
return byte;
};
+protected:
+ UdpClass controlPort;
+ UdpClass dataPort;
+
private:
- UdpClass controlPort;
- UdpClass dataPort;
+ RtpBuffer_t controlBuffer;
+ RtpBuffer_t dataBuffer;
- // reading from the network
- RtpBuffer_t controlBuffer;
- RtpBuffer_t dataBuffer;
+ byte packetBuffer[Settings::UdpTxPacketMaxSize];
- AppleMIDIParser _appleMIDIParser;
- rtpMIDIParser _rtpMIDIParser;
+ AppleMIDIParser _appleMIDIParser;
+ rtpMIDIParser _rtpMIDIParser;
connectedCallback _connectedCallback = nullptr;
+ disconnectedCallback _disconnectedCallback = nullptr;
+#ifdef USE_EXT_CALLBACKS
startReceivedMidiByteCallback _startReceivedMidiByteCallback = nullptr;
receivedMidiByteCallback _receivedMidiByteCallback = nullptr;
endReceivedMidiByteCallback _endReceivedMidiByteCallback = nullptr;
receivedRtpCallback _receivedRtpCallback = nullptr;
- disconnectedCallback _disconnectedCallback = nullptr;
- errorCallback _errorCallback = nullptr;
-
- // buffer for incoming and outgoing MIDI messages
- MidiBuffer_t inMidiBuffer;
- MidiBuffer_t outMidiBuffer;
-
- rtpMidi_Clock rtpMidiClock;
-
- ssrc_t ssrc = 0;
- char localName[DefaultSettings::MaxSessionNameLen + 1];
- uint16_t port = DEFAULT_CONTROL_PORT;
+ sentRtpCallback _sentRtpCallback = nullptr;
+ sentRtpMidiCallback _sentRtpMidiCallback = nullptr;
+ exceptionCallback _exceptionCallback = nullptr;
+#endif
+ // buffer for incoming and outgoing MIDI messages
+ MidiBuffer_t inMidiBuffer;
+ MidiBuffer_t outMidiBuffer;
+
+ rtpMidi_Clock rtpMidiClock;
+
+ ssrc_t ssrc = 0;
+ uint16_t port = DEFAULT_CONTROL_PORT;
+#ifdef ONE_PARTICIPANT
+ Participant participant;
+#else
Deque, Settings::MaxNumberOfParticipants> participants;
- int32_t latencyAdjustment = 0;
-
+#endif
+
+#ifdef KEEP_SESSION_NAME
+ char localName[Settings::MaxSessionNameLen + 1];
+#endif
+
private:
- void readControlPackets();
- void readDataPackets();
-
+ size_t readControlPackets();
+ size_t readDataPackets();
+
void parseControlPackets();
void parseDataPackets();
-
- void ReceivedInvitation (AppleMIDI_Invitation_t &, const amPortType &);
- void ReceivedControlInvitation (AppleMIDI_Invitation_t &);
- void ReceivedDataInvitation (AppleMIDI_Invitation_t &);
- void ReceivedSynchronization (AppleMIDI_Synchronization_t &);
- void ReceivedReceiverFeedback (AppleMIDI_ReceiverFeedback_t &);
- void ReceivedEndSession (AppleMIDI_EndSession_t &);
- void ReceivedBitrateReceiveLimit (AppleMIDI_BitrateReceiveLimit &);
-
- void ReceivedInvitationAccepted (AppleMIDI_InvitationAccepted_t &, const amPortType &);
+
+ void ReceivedInvitation(AppleMIDI_Invitation_t &, const amPortType &);
+ void ReceivedControlInvitation(AppleMIDI_Invitation_t &);
+ void ReceivedDataInvitation(AppleMIDI_Invitation_t &);
+ void ReceivedSynchronization(AppleMIDI_Synchronization_t &);
+ void ReceivedReceiverFeedback(AppleMIDI_ReceiverFeedback_t &);
+ void ReceivedEndSession(AppleMIDI_EndSession_t &);
+ void ReceivedBitrateReceiveLimit(AppleMIDI_BitrateReceiveLimit &);
+
+ void ReceivedInvitationAccepted(AppleMIDI_InvitationAccepted_t &, const amPortType &);
void ReceivedControlInvitationAccepted(AppleMIDI_InvitationAccepted_t &);
- void ReceivedDataInvitationAccepted (AppleMIDI_InvitationAccepted_t &);
- void ReceivedInvitationRejected (AppleMIDI_InvitationRejected_t &);
-
- // rtpMIDI callback from parser
+ void ReceivedDataInvitationAccepted(AppleMIDI_InvitationAccepted_t &);
+ void ReceivedInvitationRejected(AppleMIDI_InvitationRejected_t &);
+
+ // rtpMIDI callback from parser
void ReceivedRtp(const Rtp_t &);
void StartReceivedMidi();
void ReceivedMidi(byte data);
void EndReceivedMidi();
- // Helpers
- void writeInvitation (UdpClass &, IPAddress, uint16_t, AppleMIDI_Invitation_t &, const byte *command);
+ // Helpers
+ void writeInvitation(UdpClass &, const IPAddress &, const uint16_t &, AppleMIDI_Invitation_t &, const byte *command);
void writeReceiverFeedback(const IPAddress &, const uint16_t &, AppleMIDI_ReceiverFeedback_t &);
- void writeSynchronization (const IPAddress &, const uint16_t &, AppleMIDI_Synchronization_t &);
- void writeEndSession (const IPAddress &, const uint16_t &, AppleMIDI_EndSession_t &);
+ void writeSynchronization(const IPAddress &, const uint16_t &, AppleMIDI_Synchronization_t &);
+ void writeEndSession(const IPAddress &, const uint16_t &, AppleMIDI_EndSession_t &);
+
+ void sendEndSession(Participant *);
- void sendEndSession(Participant*);
-
void writeRtpMidiToAllParticipants();
- void writeRtpMidiBuffer(Participant*);
+ void writeRtpMidiBuffer(Participant *);
void manageReceiverFeedback();
-
+
void manageSessionInvites();
void manageSynchronization();
- void manageSynchronizationListener(size_t);
void manageSynchronizationInitiator();
- void manageSynchronizationInitiatorHeartBeat(size_t);
+ void manageSynchronizationInitiatorHeartBeat(Participant *);
void manageSynchronizationInitiatorInvites(size_t);
-
- void sendSynchronization(Participant*);
- Participant* getParticipantBySSRC(const ssrc_t ssrc);
- Participant* getParticipantByInitiatorToken(const uint32_t initiatorToken);
-};
+ void sendSynchronization(Participant *);
-#define APPLEMIDI_CREATE_INSTANCE(Type, Name, SessionName, Port) \
- APPLEMIDI_NAMESPACE::AppleMIDISession Apple##Name(SessionName, Port); \
- MIDI_NAMESPACE::MidiInterface> Name((APPLEMIDI_NAMESPACE::AppleMIDISession&)Apple##Name);
-
-#define APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE() \
-APPLEMIDI_CREATE_INSTANCE(EthernetUDP, MIDI, "Arduino", DEFAULT_CONTROL_PORT);
-
-#define APPLEMIDI_CREATE_DEFAULTSESSION_ESP32_INSTANCE() \
-APPLEMIDI_CREATE_INSTANCE(WiFiUDP, MIDI, "ESP32", DEFAULT_CONTROL_PORT);
+#ifndef ONE_PARTICIPANT
+ Participant *getParticipantBySSRC(const ssrc_t &);
+ Participant *getParticipantByInitiatorToken(const uint32_t &initiatorToken);
+#endif
+#ifdef USE_DIRECTORY
+ bool IsComputerInDirectory(IPAddress) const;
+#endif
+};
END_APPLEMIDI_NAMESPACE
#include "AppleMIDI.hpp"
+#define APPLEMIDI_CREATE_INSTANCE(Type, Name, SessionName, Port) \
+ APPLEMIDI_NAMESPACE::AppleMIDISession Apple##Name(SessionName, Port); \
+ MIDI_NAMESPACE::MidiInterface, APPLEMIDI_NAMESPACE::AppleMIDISettings> Name((APPLEMIDI_NAMESPACE::AppleMIDISession &)Apple##Name);
+
+#if defined(ARDUINO_ARCH_ESP32) || defined(ESP32)
+#define APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE() \
+ APPLEMIDI_CREATE_INSTANCE(WiFiUDP, MIDI, "AppleMIDI-ESP32", DEFAULT_CONTROL_PORT);
+#elif defined(ESP8266)
+#define APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE() \
+ APPLEMIDI_CREATE_INSTANCE(WiFiUDP, MIDI, "AppleMIDI-ESP8266", DEFAULT_CONTROL_PORT);
+#else
+#define APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE() \
+ APPLEMIDI_CREATE_INSTANCE(EthernetUDP, MIDI, "AppleMIDI-Arduino", DEFAULT_CONTROL_PORT);
+#endif
+
+#include "AppleMIDI_PlatformEnd.h"
diff --git a/src/AppleMIDI.hpp b/src/AppleMIDI.hpp
index b90f8ef..54e860d 100644
--- a/src/AppleMIDI.hpp
+++ b/src/AppleMIDI.hpp
@@ -4,10 +4,8 @@
BEGIN_APPLEMIDI_NAMESPACE
-static byte packetBuffer[UDP_TX_PACKET_MAX_SIZE];
-
template
-void AppleMIDISession::readControlPackets()
+size_t AppleMIDISession::readControlPackets()
{
size_t packetSize = controlPort.available();
if (packetSize == 0)
@@ -22,6 +20,8 @@ void AppleMIDISession::readControlPackets()
for (auto i = 0; i < bytesRead; i++)
controlBuffer.push_back(packetBuffer[i]);
}
+
+ return controlBuffer.size();
}
template
@@ -30,18 +30,30 @@ void AppleMIDISession::parseControlPackets()
while (controlBuffer.size() > 0)
{
auto retVal = _appleMIDIParser.parse(controlBuffer, amPortType::Control);
- if (retVal == parserReturn::UnexpectedData)
+ if (retVal == parserReturn::Processed
+ || retVal == parserReturn::NotEnoughData
+ || retVal == parserReturn::NotSureGiveMeMoreData)
{
- if (NULL != _errorCallback)
- _errorCallback(ssrc, -2);
-
+ break;
+ }
+ else if (retVal == parserReturn::UnexpectedData)
+ {
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, ParseException, 0);
+#endif
controlBuffer.pop_front();
}
+ else if (retVal == parserReturn::SessionNameVeryLong)
+ {
+ // purge the rest of the data in controlPort
+ while (controlPort.read() >= 0) {}
+ }
}
}
template
-void AppleMIDISession::readDataPackets()
+size_t AppleMIDISession::readDataPackets()
{
size_t packetSize = dataPort.available();
if (packetSize == 0)
@@ -56,6 +68,8 @@ void AppleMIDISession::readDataPackets()
for (auto i = 0; i < bytesRead; i++)
dataBuffer.push_back(packetBuffer[i]);
}
+
+ return dataBuffer.size();
}
template
@@ -82,12 +96,11 @@ void AppleMIDISession::parseDataPackets()
if (retVal1 == parserReturn::NotSureGiveMeMoreData
|| retVal2 == parserReturn::NotSureGiveMeMoreData)
break; // one or the other buffer does not have enough data
-
- // TODO can we ever get here???
- if (NULL != _errorCallback)
- _errorCallback(ssrc, -3);
-
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, UnexpectedParseException, 0);
+#endif
dataBuffer.pop_front();
}
}
@@ -95,7 +108,7 @@ void AppleMIDISession::parseDataPackets()
template
void AppleMIDISession::ReceivedInvitation(AppleMIDI_Invitation_t &invitation, const amPortType &portType)
{
- if (portType == amPortType::Control)
+ if (portType == amPortType::Control)
ReceivedControlInvitation(invitation);
else
ReceivedDataInvitation(invitation);
@@ -104,34 +117,72 @@ void AppleMIDISession::ReceivedInvitation(AppleMID
template
void AppleMIDISession::ReceivedControlInvitation(AppleMIDI_Invitation_t &invitation)
{
- // advertise our own session name
-#ifdef KEEP_SESSION_NAME
- strncpy(invitation.sessionName, localName, DefaultSettings::MaxSessionNameLen);
- invitation.sessionName[DefaultSettings::MaxSessionNameLen] = '\0';
+ // ignore invitation of a participant already in the participant list
+#ifndef ONE_PARTICIPANT
+ if (nullptr != getParticipantBySSRC(invitation.ssrc))
+#else
+ if (participant.ssrc == invitation.ssrc)
#endif
-
+ return;
+
+#ifndef ONE_PARTICIPANT
if (participants.full())
+#else
+ if (participant.ssrc != 0)
+#endif
{
- writeInvitation(controlPort, controlPort.remoteIP(), controlPort.remotePort(), invitation, amInvitationRejected);
-
- if (NULL != _errorCallback)
- _errorCallback(ssrc, -33);
-
+ writeInvitation(controlPort, controlPort.remoteIP(), controlPort.remotePort(), invitation, amInvitationRejected);
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, TooManyParticipantsException, 0);
+#endif
return;
}
-
+
+#ifndef ONE_PARTICIPANT
Participant participant;
+#endif
participant.kind = Listener;
participant.ssrc = invitation.ssrc;
participant.remoteIP = controlPort.remoteIP();
participant.remotePort = controlPort.remotePort();
participant.lastSyncExchangeTime = now;
- participant.sequenceNr = random(1, UINT16_MAX); // // http://www.rfc-editor.org/rfc/rfc6295.txt , 2.1. RTP Header
#ifdef KEEP_SESSION_NAME
- strncpy(participant.sessionName, invitation.sessionName, DefaultSettings::MaxSessionNameLen);
+ strncpy(participant.sessionName, invitation.sessionName, Settings::MaxSessionNameLen);
#endif
-
+
+#ifdef KEEP_SESSION_NAME
+ // Re-use the invitation for acceptance. Overwrite sessionName with ours
+ strncpy(invitation.sessionName, localName, Settings::MaxSessionNameLen);
+ invitation.sessionName[Settings::MaxSessionNameLen] = '\0';
+#endif
+
+#ifdef USE_DIRECTORY
+ switch (whoCanConnectToMe) {
+ case None:
+ writeInvitation(controlPort, controlPort.remoteIP(), controlPort.remotePort(), invitation, amInvitationRejected);
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, NotAcceptingAnyone, 0);
+#endif
+ return;
+ case OnlyComputersInMyDirectory:
+ if (!IsComputerInDirectory(controlPort.remoteIP())) {
+ writeInvitation(controlPort, controlPort.remoteIP(), controlPort.remotePort(), invitation, amInvitationRejected);
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, ComputerNotInDirectory, 0);
+#endif
+ return;
+ }
+ case Anyone:
+ break;
+ }
+#endif
+
+#ifndef ONE_PARTICIPANT
participants.push_back(participant);
+#endif
writeInvitation(controlPort, participant.remoteIP, participant.remotePort, invitation, amInvitationAccepted);
}
@@ -139,32 +190,43 @@ void AppleMIDISession::ReceivedControlInvitation(A
template
void AppleMIDISession::ReceivedDataInvitation(AppleMIDI_Invitation &invitation)
{
- auto participant = getParticipantBySSRC(invitation.ssrc);
- if (NULL == participant)
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = getParticipantBySSRC(invitation.ssrc);
+#else
+ auto pParticipant = (participant.ssrc == invitation.ssrc) ? &participant : nullptr;
+#endif
+ if (nullptr == pParticipant)
{
writeInvitation(dataPort, dataPort.remoteIP(), dataPort.remotePort(), invitation, amInvitationRejected);
- if (NULL != _errorCallback)
- _errorCallback(ssrc, -4);
-
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, ParticipantNotFoundException, invitation.ssrc);
+#endif
return;
}
+#ifdef KEEP_SESSION_NAME
+ // Re-use the invitation for acceptance. Overwrite sessionName with ours
+ strncpy(invitation.sessionName, localName, Settings::MaxSessionNameLen);
+ invitation.sessionName[Settings::MaxSessionNameLen] = '\0';
+#endif
+
// writeInvitation will alter the values of the invitation,
// in order to safe memory and computing cycles its easier to make a copy
// of the ssrc here.
auto ssrc_ = invitation.ssrc;
- writeInvitation(dataPort, participant->remoteIP, participant->remotePort + 1, invitation, amInvitationAccepted);
+ writeInvitation(dataPort, pParticipant->remoteIP, pParticipant->remotePort + 1, invitation, amInvitationAccepted);
- participant->kind = Listener;
+ pParticipant->kind = Listener;
// Inform that we have an established connection
- if (NULL != _connectedCallback)
+ if (nullptr != _connectedCallback)
#ifdef KEEP_SESSION_NAME
- _connectedCallback(ssrc_, invitation.sessionName);
+ _connectedCallback(ssrc_, pParticipant->sessionName);
#else
- _connectedCallback(ssrc_, NULL);
+ _connectedCallback(ssrc_, nullptr);
#endif
}
@@ -173,6 +235,7 @@ void AppleMIDISession::ReceivedBitrateReceiveLimit
{
}
+#ifdef APPLEMIDI_INITIATOR
template
void AppleMIDISession::ReceivedInvitationAccepted(AppleMIDI_InvitationAccepted_t &invitationAccepted, const amPortType &portType)
{
@@ -185,32 +248,40 @@ void AppleMIDISession::ReceivedInvitationAccepted(
template
void AppleMIDISession::ReceivedControlInvitationAccepted(AppleMIDI_InvitationAccepted_t &invitationAccepted)
{
- auto participant = this->getParticipantByInitiatorToken(invitationAccepted.initiatorToken);
- if (NULL == participant)
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = this->getParticipantByInitiatorToken(invitationAccepted.initiatorToken);
+#else
+ auto pParticipant = (participant.initiatorToken == invitationAccepted.initiatorToken) ? &participant : nullptr;
+#endif
+ if (nullptr == pParticipant)
{
return;
}
- participant->ssrc = invitationAccepted.ssrc;
- participant->lastInviteSentTime = now - 1000; // forces invite to be send
- participant->connectionAttempts = 0; // reset back to 0
- participant->invitationStatus = ControlInvitationAccepted; // step it up
+ pParticipant->ssrc = invitationAccepted.ssrc;
+ pParticipant->lastInviteSentTime = now - 1000; // forces invite to be send
+ pParticipant->connectionAttempts = 0; // reset back to 0
+ pParticipant->invitationStatus = ControlInvitationAccepted; // step it up
#ifdef KEEP_SESSION_NAME
- strncpy(participant->sessionName, invitationAccepted.sessionName, DefaultSettings::MaxSessionNameLen);
- participant->sessionName[DefaultSettings::MaxSessionNameLen] = '\0';
+ strncpy(pParticipant->sessionName, invitationAccepted.sessionName, Settings::MaxSessionNameLen);
+ pParticipant->sessionName[Settings::MaxSessionNameLen] = '\0';
#endif
}
template
void AppleMIDISession::ReceivedDataInvitationAccepted(AppleMIDI_InvitationAccepted_t &invitationAccepted)
{
- auto participant = this->getParticipantByInitiatorToken(invitationAccepted.initiatorToken);
- if (NULL == participant)
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = this->getParticipantByInitiatorToken(invitationAccepted.initiatorToken);
+#else
+ auto pParticipant = (participant.initiatorToken == invitationAccepted.initiatorToken) ? &participant : nullptr;
+#endif
+ if (nullptr == pParticipant)
{
return;
}
- participant->invitationStatus = DataInvitationAccepted;
+ pParticipant->invitationStatus = DataInvitationAccepted;
}
template
@@ -220,11 +291,16 @@ void AppleMIDISession::ReceivedInvitationRejected(
{
if (invitationRejected.ssrc == participants[i].ssrc)
{
+#ifndef ONE_PARTICIPANT
participants.erase(i);
+#else
+ participant.ssrc = 0;
+#endif
return;
}
}
}
+#endif
/*! \brief .
@@ -256,9 +332,18 @@ user to choose between a new connection attempt or closing the session.
template
void AppleMIDISession::ReceivedSynchronization(AppleMIDI_Synchronization_t &synchronization)
{
- auto participant = getParticipantBySSRC(synchronization.ssrc);
- if (NULL == participant)
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = getParticipantBySSRC(synchronization.ssrc);
+#else
+ auto pParticipant = (participant.ssrc == synchronization.ssrc) ? &participant : nullptr;
+#endif
+ if (nullptr == pParticipant)
{
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, ParticipantNotFoundException, synchronization.ssrc);
+#endif
+
return;
}
@@ -298,41 +383,28 @@ void AppleMIDISession::ReceivedSynchronization(App
case SYNC_CK0: /* From session APPLEMIDI_INITIATOR */
synchronization.timestamps[SYNC_CK1] = rtpMidiClock.Now();
synchronization.count = SYNC_CK1;
- writeSynchronization(participant->remoteIP, participant->remotePort + 1, synchronization);
+ writeSynchronization(pParticipant->remoteIP, pParticipant->remotePort + 1, synchronization);
break;
case SYNC_CK1: /* From session LISTENER */
#ifdef APPLEMIDI_INITIATOR
synchronization.timestamps[SYNC_CK2] = rtpMidiClock.Now();
synchronization.count = SYNC_CK2;
- writeSynchronization(participant->remoteIP, participant->remotePort + 1, synchronization);
- participant->synchronizing = false;
+ writeSynchronization(pParticipant->remoteIP, pParticipant->remotePort + 1, synchronization);
+ pParticipant->synchronizing = false;
#endif
break;
case SYNC_CK2: /* From session APPLEMIDI_INITIATOR */
-#ifdef LATENCY_CALCULATION
+#ifdef USE_EXT_CALLBACKS
// each party can estimate the offset between the two clocks using the following formula
- participant->offsetEstimate = (uint32_t)(((synchronization.timestamps[2] + synchronization.timestamps[0]) / 2) - synchronization.timestamps[1]);
-/*
- uint64_t remoteAverage = ((synchronization.timestamps[2] + synchronization.timestamps[0]) / 2);
- uint64_t localAverage = synchronization.timestamps[1];
-
- static uint64_t oldRemoteAverage = 0;
- static uint64_t oldLocalAverage = 0;
-
- uint64_t r = (remoteAverage - oldRemoteAverage);
- uint64_t l = (localAverage - oldLocalAverage);
-
- oldRemoteAverage = remoteAverage;
- oldLocalAverage = localAverage;
-*/
+ pParticipant->offsetEstimate = (uint32_t)(((synchronization.timestamps[2] + synchronization.timestamps[0]) / 2) - synchronization.timestamps[1]);
#endif
break;
}
// All particpants need to check in regularly,
// failing to do so will result in a lost connection.
- participant->lastSyncExchangeTime = now;
+ pParticipant->lastSyncExchangeTime = now;
}
// The recovery journal mechanism requires that the receiver periodically
@@ -344,146 +416,240 @@ void AppleMIDISession::ReceivedSynchronization(App
template
void AppleMIDISession::ReceivedReceiverFeedback(AppleMIDI_ReceiverFeedback_t &receiverFeedback)
{
- // As we do not keep any recovery journals, no command history, nothing!
+ // We do not keep any recovery journals, no command history, nothing!
+ // Here is where you would correct if packets are dropped (send them again)
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = getParticipantBySSRC(receiverFeedback.ssrc);
+#else
+ auto pParticipant = (participant.ssrc == receiverFeedback.ssrc) ? &participant : nullptr;
+#endif
+ if (nullptr == pParticipant) {
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, ParticipantNotFoundException, receiverFeedback.ssrc);
+#endif
+ return;
+ }
+
+ if (pParticipant->sendSequenceNr < receiverFeedback.sequenceNr)
+ {
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(pParticipant->ssrc, SendPacketsDropped, pParticipant->sendSequenceNr - receiverFeedback.sequenceNr);
+#endif
+ }
}
template
void AppleMIDISession::ReceivedEndSession(AppleMIDI_EndSession_t &endSession)
{
+#ifndef ONE_PARTICIPANT
for (size_t i = 0; i < participants.size(); i++)
{
- if (endSession.ssrc == participants[i].ssrc)
+ auto participant = participants[i];
+#else
+ {
+#endif
+ if (endSession.ssrc == participant.ssrc)
{
+ auto ssrc = participant.ssrc;
+
+#ifndef ONE_PARTICIPANT
participants.erase(i);
-
- if (NULL != _disconnectedCallback)
- _disconnectedCallback(endSession.ssrc);
+#else
+ participant.ssrc = 0;
+#endif
+ if (nullptr != _disconnectedCallback)
+ _disconnectedCallback(ssrc);
return;
}
}
}
+#ifdef USE_DIRECTORY
+template
+bool AppleMIDISession::IsComputerInDirectory(IPAddress remoteIP) const
+{
+ for (size_t i = 0; i < directory.size(); i++)
+ if (remoteIP == directory[i])
+ return true;
+ return false;
+}
+#endif
+
+#ifndef ONE_PARTICIPANT
template
-Participant* AppleMIDISession::getParticipantBySSRC(const ssrc_t ssrc)
+Participant* AppleMIDISession::getParticipantBySSRC(const ssrc_t& ssrc)
{
for (size_t i = 0; i < participants.size(); i++)
if (ssrc == participants[i].ssrc)
return &participants[i];
- return NULL;
+ return nullptr;
}
template
-Participant* AppleMIDISession::getParticipantByInitiatorToken(const uint32_t initiatorToken)
+Participant* AppleMIDISession::getParticipantByInitiatorToken(const uint32_t& initiatorToken)
{
for (auto i = 0; i < participants.size(); i++)
if (initiatorToken == participants[i].initiatorToken)
return &participants[i];
- return NULL;
+ return nullptr;
}
+#endif
template
-void AppleMIDISession::writeInvitation(UdpClass &port, IPAddress remoteIP, uint16_t remotePort, AppleMIDI_Invitation_t & invitation, const byte *command)
+void AppleMIDISession::writeInvitation(UdpClass &port, const IPAddress& remoteIP, const uint16_t& remotePort, AppleMIDI_Invitation_t & invitation, const byte *command)
{
- if (port.beginPacket(remoteIP, remotePort))
+ if (!port.beginPacket(remoteIP, remotePort))
{
- port.write((uint8_t *)amSignature, sizeof(amSignature));
-
- port.write((uint8_t *)command, sizeof(amInvitation));
- port.write((uint8_t *)amProtocolVersion, sizeof(amProtocolVersion));
- invitation.initiatorToken = htonl(invitation.initiatorToken);
- invitation.ssrc = htonl(ssrc);
- port.write(reinterpret_cast(&invitation), invitation.getLength());
-
- port.endPacket();
- port.flush();
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, UdpBeginPacketFailed, 1);
+#endif
+ return;
}
+
+ port.write((uint8_t *)amSignature, sizeof(amSignature));
+
+ port.write((uint8_t *)command, sizeof(amInvitation));
+ port.write((uint8_t *)amProtocolVersion, sizeof(amProtocolVersion));
+ invitation.initiatorToken = __htonl(invitation.initiatorToken);
+ invitation.ssrc = ssrc;
+ invitation.ssrc = __htonl(invitation.ssrc);
+ port.write(reinterpret_cast(&invitation), invitation.getLength());
+
+ port.endPacket();
+ port.flush();
}
template
void AppleMIDISession::writeReceiverFeedback(const IPAddress& remoteIP, const uint16_t & remotePort, AppleMIDI_ReceiverFeedback_t & receiverFeedback)
{
- if (controlPort.beginPacket(remoteIP, remotePort))
+ if (!controlPort.beginPacket(remoteIP, remotePort))
{
- controlPort.write((uint8_t *)amSignature, sizeof(amSignature));
-
- controlPort.write((uint8_t *)amReceiverFeedback, sizeof(amReceiverFeedback));
-
- receiverFeedback.ssrc = htonl(receiverFeedback.ssrc);
- receiverFeedback.sequenceNr = htons(receiverFeedback.sequenceNr);
-
- controlPort.write(reinterpret_cast(&receiverFeedback), sizeof(AppleMIDI_ReceiverFeedback));
-
- controlPort.endPacket();
- controlPort.flush();
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, UdpBeginPacketFailed, 2);
+#endif
+ return;
}
+
+ controlPort.write((uint8_t *)amSignature, sizeof(amSignature));
+
+ controlPort.write((uint8_t *)amReceiverFeedback, sizeof(amReceiverFeedback));
+
+ receiverFeedback.ssrc = __htonl(receiverFeedback.ssrc);
+ receiverFeedback.sequenceNr = __htons(receiverFeedback.sequenceNr);
+
+ controlPort.write(reinterpret_cast(&receiverFeedback), sizeof(AppleMIDI_ReceiverFeedback));
+
+ controlPort.endPacket();
+ controlPort.flush();
}
template
void AppleMIDISession::writeSynchronization(const IPAddress& remoteIP, const uint16_t & remotePort, AppleMIDI_Synchronization_t &synchronization)
{
- if (dataPort.beginPacket(remoteIP, remotePort))
+ if (!dataPort.beginPacket(remoteIP, remotePort))
{
- dataPort.write((uint8_t *)amSignature, sizeof(amSignature));
- dataPort.write((uint8_t *)amSynchronization, sizeof(amSynchronization));
- synchronization.ssrc = htonl(this->ssrc);
-
- synchronization.timestamps[0] = htonll(synchronization.timestamps[0]);
- synchronization.timestamps[1] = htonll(synchronization.timestamps[1]);
- synchronization.timestamps[2] = htonll(synchronization.timestamps[2]);
- dataPort.write(reinterpret_cast(&synchronization), sizeof(synchronization));
-
- dataPort.endPacket();
- dataPort.flush();
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, UdpBeginPacketFailed, 3);
+#endif
+ return;
}
+
+ dataPort.write((uint8_t *)amSignature, sizeof(amSignature));
+ dataPort.write((uint8_t *)amSynchronization, sizeof(amSynchronization));
+ synchronization.ssrc = ssrc;
+ synchronization.ssrc = __htonl(synchronization.ssrc);
+
+ synchronization.timestamps[0] = __htonll(synchronization.timestamps[0]);
+ synchronization.timestamps[1] = __htonll(synchronization.timestamps[1]);
+ synchronization.timestamps[2] = __htonll(synchronization.timestamps[2]);
+ dataPort.write(reinterpret_cast(&synchronization), sizeof(synchronization));
+
+ dataPort.endPacket();
+ dataPort.flush();
}
template
void AppleMIDISession::writeEndSession(const IPAddress& remoteIP, const uint16_t & remotePort, AppleMIDI_EndSession_t &endSession)
{
- if (controlPort.beginPacket(remoteIP, remotePort))
+ if (!controlPort.beginPacket(remoteIP, remotePort))
{
- controlPort.write((uint8_t *)amSignature, sizeof(amSignature));
- controlPort.write((uint8_t *)amEndSession, sizeof(amEndSession));
- controlPort.write((uint8_t *)amProtocolVersion, sizeof(amProtocolVersion));
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, UdpBeginPacketFailed, 4);
+#endif
+ return;
+ }
- endSession.initiatorToken = htonl(endSession.initiatorToken);
- endSession.ssrc = htonl(endSession.ssrc);
+ controlPort.write((uint8_t *)amSignature, sizeof(amSignature));
+ controlPort.write((uint8_t *)amEndSession, sizeof(amEndSession));
+ controlPort.write((uint8_t *)amProtocolVersion, sizeof(amProtocolVersion));
- controlPort.write(reinterpret_cast(&endSession), sizeof(endSession));
-
- controlPort.endPacket();
- controlPort.flush();
- }
+ endSession.initiatorToken = __htonl(endSession.initiatorToken);
+ endSession.ssrc = __htonl(endSession.ssrc);
+
+ controlPort.write(reinterpret_cast(&endSession), sizeof(endSession));
+
+ controlPort.endPacket();
+ controlPort.flush();
}
template
void AppleMIDISession::writeRtpMidiToAllParticipants()
{
+#ifndef ONE_PARTICIPANT
for (size_t i = 0; i < participants.size(); i++)
{
- auto participant = &participants[i];
- writeRtpMidiBuffer(participant);
+ auto pParticipant = &participants[i];
+
+ writeRtpMidiBuffer(pParticipant);
}
+#else
+ writeRtpMidiBuffer(&participant);
+#endif
+ outMidiBuffer.clear();
}
template
-void AppleMIDISession::writeRtpMidiBuffer(Participant * participant)
-{
- const IPAddress remoteIP = participant->remoteIP;
- const uint16_t remotePort = participant->remotePort + 1;
-
- if (!dataPort.beginPacket(remoteIP, remotePort))
- {
- return;
- }
+void AppleMIDISession::writeRtpMidiBuffer(Participant* participant)
+{
+ const auto bufferLen = outMidiBuffer.size();
- participant->sequenceNr++; // (modulo 2^16) modulo is automatically done for us ()
-
Rtp rtp;
- rtp.vpxcc = 0b10000000; // TODO: fun with flags
- rtp.mpayload = PAYLOADTYPE_RTPMIDI; // TODO: set or unset marker
- rtp.ssrc = htonl(ssrc);
+
+ // First octet
+ rtp.vpxcc = ((RTP_VERSION_2) << 6); // RTP version 2
+ rtp.vpxcc &= ~RTP_P_FIELD; // no padding
+ rtp.vpxcc &= ~RTP_X_FIELD; // no extension
+ // No CSRC
+
+ // second octet
+ rtp.mpayload = PAYLOADTYPE_RTPMIDI;
+
+/*
+ // The behavior of the 1-bit M field depends on the media type of the
+ // stream. For native streams, the M bit MUST be set to 1 if the MIDI
+ // command section has a non-zero LEN field and MUST be set to 0
+ // otherwise. For mpeg4-generic streams, the M bit MUST be set to 1 for
+ // all packets (to conform to [RFC3640]).
+ if (bufferLen != 0)
+ rtp.mpayload |= RTP_M_FIELD;
+ else
+ rtp.mpayload &= ~RTP_M_FIELD;
+*/
+ // Both https://developer.apple.com/library/archive/documentation/Audio/Conceptual/MIDINetworkDriverProtocol/MIDI/MIDI.html
+ // and https://tools.ietf.org/html/rfc6295#section-2.1 indicate that the M field needs to be set
+ // if the len in the MIDI section is NON-ZERO.
+ // However, doing so on, MacOS does not take the given MIDI commands
+ // Clear the M field
+ rtp.mpayload &= ~RTP_M_FIELD;
+
+ rtp.ssrc = ssrc;
// https://developer.apple.com/library/ios/documentation/CoreMidi/Reference/MIDIServices_Reference/#//apple_ref/doc/uid/TP40010316-CHMIDIServiceshFunctions-SW30
// The time at which the events occurred, if receiving MIDI, or, if sending MIDI,
@@ -499,49 +665,75 @@ void AppleMIDISession::writeRtpMidiBuffer(Particip
// have an option to not transmit messages with future timestamps, to accommodate hardware not
// prepared to defer rendering the messages until the proper time.)
//
- rtp.timestamp = (Settings::TimestampRtpPackets) ? htonl(rtpMidiClock.Now()) : 0;
-
- rtp.sequenceNr = htons(participant->sequenceNr);
- dataPort.write((uint8_t *)&rtp, sizeof(rtp));
+ rtp.timestamp = (Settings::TimestampRtpPackets) ? rtpMidiClock.Now() : 0;
+
+ // increment the sequenceNr
+ participant->sendSequenceNr++;
+
+ rtp.sequenceNr = participant->sendSequenceNr;
+
+#ifdef USE_EXT_CALLBACKS
+ if (_sentRtpCallback)
+ _sentRtpCallback(rtp);
+#endif
+
+ rtp.timestamp = __htonl(rtp.timestamp);
+ rtp.ssrc = __htonl(rtp.ssrc);
+ rtp.sequenceNr = __htons(rtp.sequenceNr);
+
+ if (!dataPort.beginPacket(participant->remoteIP, participant->remotePort + 1))
+ {
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, UdpBeginPacketFailed, 5);
+#endif
+ return;
+ }
- // only now the length is known
- auto bufferLen = outMidiBuffer.size();
+ // write rtp header
+ dataPort.write((uint8_t *)&rtp, sizeof(rtp));
+ // Write rtpMIDI section
RtpMIDI_t rtpMidi;
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |B|J|Z|P|LEN... | MIDI list ... |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ rtpMidi.flags = 0;
+ rtpMidi.flags &= ~RTP_MIDI_CS_FLAG_J; // no journal, clear J-FLAG
+ rtpMidi.flags &= ~RTP_MIDI_CS_FLAG_Z; // no Delta Time 0 field, clear Z flag
+ rtpMidi.flags &= ~RTP_MIDI_CS_FLAG_P; // no phantom flag
+
if (bufferLen <= 0x0F)
{ // Short header
- rtpMidi.flags = (uint8_t)bufferLen;
- // rtpMidi.flags &= RTP_MIDI_CS_FLAG_B; // TODO: set or clear these flags (no journal)
- // rtpMidi.flags &= RTP_MIDI_CS_FLAG_J;
- // rtpMidi.flags &= RTP_MIDI_CS_FLAG_Z;
- // rtpMidi.flags &= RTP_MIDI_CS_FLAG_P;
+ rtpMidi.flags |= (uint8_t)bufferLen;
+ rtpMidi.flags &= ~RTP_MIDI_CS_FLAG_B; // short header, clear B-FLAG
dataPort.write(rtpMidi.flags);
}
else
{ // Long header
- rtpMidi.flags = (uint8_t)(bufferLen >> 8);
- rtpMidi.flags |= RTP_MIDI_CS_FLAG_B; // set B-FLAG for long header
- // rtpMidi.flags &= RTP_MIDI_CS_FLAG_J;
- // rtpMidi.flags &= RTP_MIDI_CS_FLAG_Z;
- // rtpMidi.flags &= RTP_MIDI_CS_FLAG_P;
+ rtpMidi.flags |= (uint8_t)(bufferLen >> 8);
+ rtpMidi.flags |= RTP_MIDI_CS_FLAG_B; // set B-FLAG for long header
dataPort.write(rtpMidi.flags);
dataPort.write((uint8_t)(bufferLen));
}
- // MIDI Section
- while (!outMidiBuffer.empty())
- {
- auto byte = outMidiBuffer.front();
- outMidiBuffer.pop_front();
-
- dataPort.write(byte);
- }
-
+ // write out the MIDI Section
+ for (size_t i = 0; i < bufferLen; i++)
+ dataPort.write(outMidiBuffer[i]);
+
// *No* journal section (Not supported)
-
+
dataPort.endPacket();
dataPort.flush();
+
+#ifdef USE_EXT_CALLBACKS
+ if (_sentRtpMidiCallback)
+ _sentRtpMidiCallback(rtpMidi);
+#endif
}
//
@@ -550,46 +742,52 @@ void AppleMIDISession::writeRtpMidiBuffer(Particip
template
void AppleMIDISession::manageSynchronization()
{
+#ifndef ONE_PARTICIPANT
for (size_t i = 0; i < participants.size(); i++)
+#endif
{
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = &participants[i];
+ if (pParticipant->ssrc == 0) continue;
+#else
+ auto pParticipant = &participant;
+ if (pParticipant->ssrc == 0) return;
+#endif
#ifdef APPLEMIDI_INITIATOR
- auto participant = &participants[i];
-
- if (participant->invitationStatus != Connected)
+ if (pParticipant->invitationStatus != Connected)
continue;
// Only for Initiators that are Connected
- if (participant->kind == Listener)
+ if (pParticipant->kind == Listener)
{
#endif
- manageSynchronizationListener(i);
+ // The initiator must check in with the listener at least once every 60 seconds;
+ // otherwise the responder may assume that the initiator has died and terminate the session.
+ if (now - pParticipant->lastSyncExchangeTime > Settings::CK_MaxTimeOut)
+ {
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, ListenerTimeOutException, 0);
+#endif
+ sendEndSession(pParticipant);
+#ifndef ONE_PARTICIPANT
+ participants.erase(i);
+#else
+ participant.ssrc = 0;
+#endif
+ }
#ifdef APPLEMIDI_INITIATOR
}
else
{
- (participant->synchronizing) ? manageSynchronizationInitiatorInvites(i)
- : manageSynchronizationInitiatorHeartBeat(i);
+ (pParticipant->synchronizing) ? manageSynchronizationInitiatorInvites(i)
+ : manageSynchronizationInitiatorHeartBeat(pParticipant);
}
#endif
}
}
-template
-void AppleMIDISession::manageSynchronizationListener(size_t i)
-{
- auto participant = &participants[i];
-
- // The initiator must initiate a new sync exchange at least once every 60 seconds;
- // otherwise the responder may assume that the initiator has died and terminate the session.
- if (now - participant->lastSyncExchangeTime > Settings::CK_MaxTimeOut)
- {
- sendEndSession(participant);
-
- participants.erase(i);
-
- return;
- }
-}
+#ifdef APPLEMIDI_INITIATOR
//
// The initiator of the session polls if remote station is still alive.
@@ -599,32 +797,30 @@ void AppleMIDISession::manageSynchronizationListen
// otherwise the responder may assume that the initiator has died and terminate the session.
//
template
-void AppleMIDISession::manageSynchronizationInitiatorHeartBeat(size_t i)
+void AppleMIDISession::manageSynchronizationInitiatorHeartBeat(Participant* pParticipant)
{
- auto participant = &participants[i];
-
// Note: During startup, the initiator should send synchronization exchanges more frequently;
// empirical testing has determined that sending a few exchanges improves clock
// synchronization accuracy.
// (Here: twice every 0.5 seconds, then 6 times every 1.5 seconds, then every 10 seconds.)
bool doSyncronize = false;
- if (participant->synchronizationHeartBeats < 2)
+ if (pParticipant->synchronizationHeartBeats < 2)
{
- if (now - participant->lastInviteSentTime > 500) // 2 x every 0.5 seconds
+ if (now - pParticipant->lastInviteSentTime > 500) // 2 x every 0.5 seconds
{
- participant->synchronizationHeartBeats++;
+ pParticipant->synchronizationHeartBeats++;
doSyncronize = true;
}
}
- else if (participant->synchronizationHeartBeats < 7)
+ else if (pParticipant->synchronizationHeartBeats < 7)
{
- if (now - participant->lastInviteSentTime > 1500) // 5 x every 1.5 seconds
+ if (now - pParticipant->lastInviteSentTime > 1500) // 5 x every 1.5 seconds
{
- participant->synchronizationHeartBeats++;
+ pParticipant->synchronizationHeartBeats++;
doSyncronize = true;
}
}
- else if (now - participant->lastInviteSentTime > DefaultSettings::SynchronizationHeartBeat)
+ else if (now - pParticipant->lastInviteSentTime > Settings::SynchronizationHeartBeat)
{
doSyncronize = true;
}
@@ -632,30 +828,40 @@ void AppleMIDISession::manageSynchronizationInitia
if (!doSyncronize)
return;
- participant->synchronizationCount = 0;
- sendSynchronization(participant);
+ pParticipant->synchronizationCount = 0;
+ sendSynchronization(pParticipant);
}
// checks for
template
void AppleMIDISession::manageSynchronizationInitiatorInvites(size_t i)
{
- auto participant = &participants[i];
+ auto pParticipant = &participants[i];
- if (now - participant->lastInviteSentTime > 10000)
+ if (now - pParticipant->lastInviteSentTime > 10000)
{
- if (participant->synchronizationCount > DefaultSettings::MaxSynchronizationCK0Attempts)
+ if (pParticipant->synchronizationCount > Settings::MaxSynchronizationCK0Attempts)
{
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, MaxAttemptsException, 0);
+#endif
// After too many attempts, stop.
- sendEndSession(participant);
+ sendEndSession(pParticipant);
+#ifndef ONE_PARTICIPANT
participants.erase(i);
+#else
+ participant.ssrc = 0;
+#endif
return;
}
- sendSynchronization(participant);
+ sendSynchronization(pParticipant);
}
}
+#endif
+
template
void AppleMIDISession::sendSynchronization(Participant* participant)
{
@@ -675,61 +881,87 @@ void AppleMIDISession::sendSynchronization(Partici
template
void AppleMIDISession::manageSessionInvites()
{
+#ifndef ONE_PARTICIPANT
for (auto i = 0; i < participants.size(); i++)
+#endif
{
- auto participant = &participants[i];
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = &participants[i];
+#else
+ auto pParticipant = &participant;
+#endif
- if (participant->kind == Listener)
+ if (pParticipant->kind == Listener)
+#ifndef ONE_PARTICIPANT
continue;
-
- if (participant->invitationStatus == DataInvitationAccepted)
+#else
+ return;
+#endif
+ if (pParticipant->invitationStatus == DataInvitationAccepted)
{
// Inform that we have an established connection
- if (NULL != _connectedCallback)
+ if (nullptr != _connectedCallback)
#ifdef KEEP_SESSION_NAME
- _connectedCallback(participant->ssrc, participant->sessionName);
+ _connectedCallback(pParticipant->ssrc, pParticipant->sessionName);
#else
- _connectedCallback(participant->ssrc, NULL);
+ _connectedCallback(pParticipant->ssrc, nullptr);
#endif
- participant->invitationStatus = Connected;
+ pParticipant->invitationStatus = Connected;
}
- if (participant->invitationStatus == Connected)
- continue; // We are done here
+ if (pParticipant->invitationStatus == Connected)
+#ifndef ONE_PARTICIPANT
+ continue;
+#else
+ return;
+#endif
// try to connect every 1 second (1000 ms)
- if (now - participant->lastInviteSentTime > 1000)
+ if (now - pParticipant->lastInviteSentTime > 1000)
{
- if (participant->connectionAttempts >= DefaultSettings::MaxSessionInvitesAttempts)
+ if (pParticipant->connectionAttempts >= Settings::MaxSessionInvitesAttempts)
{
- // too many attempts, give up - indicate this participant slot is free
-
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, NoResponseFromConnectionRequestException, 0);
+#endif
+ // After too many attempts, stop.
+ sendEndSession(pParticipant);
+
+#ifndef ONE_PARTICIPANT
participants.erase(i);
+#else
+ participant.ssrc = 0;
+#endif
+#ifndef ONE_PARTICIPANT
continue;
+#else
+ return;
+#endif
}
- participant->lastInviteSentTime = now;
- participant->connectionAttempts++;
+ pParticipant->lastInviteSentTime = now;
+ pParticipant->connectionAttempts++;
AppleMIDI_Invitation invitation;
invitation.ssrc = this->ssrc;
- invitation.initiatorToken = participant->initiatorToken;
+ invitation.initiatorToken = pParticipant->initiatorToken;
#ifdef KEEP_SESSION_NAME
- strncpy(invitation.sessionName, this->localName, DefaultSettings::MaxSessionNameLen);
- invitation.sessionName[DefaultSettings::MaxSessionNameLen] = '\0';
+ strncpy(invitation.sessionName, this->localName, Settings::MaxSessionNameLen);
+ invitation.sessionName[Settings::MaxSessionNameLen] = '\0';
#endif
- if (participant->invitationStatus == Initiating
- || participant->invitationStatus == AwaitingControlInvitationAccepted)
+ if (pParticipant->invitationStatus == Initiating
+ || pParticipant->invitationStatus == AwaitingControlInvitationAccepted)
{
- writeInvitation(controlPort, participant->remoteIP, participant->remotePort, invitation, amInvitation);
- participant->invitationStatus = AwaitingControlInvitationAccepted;
+ writeInvitation(controlPort, pParticipant->remoteIP, pParticipant->remotePort, invitation, amInvitation);
+ pParticipant->invitationStatus = AwaitingControlInvitationAccepted;
}
else
- if (participant->invitationStatus == ControlInvitationAccepted
- || participant->invitationStatus == AwaitingDataInvitationAccepted)
+ if (pParticipant->invitationStatus == ControlInvitationAccepted
+ || pParticipant->invitationStatus == AwaitingDataInvitationAccepted)
{
- writeInvitation(dataPort, participant->remoteIP, participant->remotePort + 1, invitation, amInvitation);
- participant->invitationStatus = AwaitingDataInvitationAccepted;
+ writeInvitation(dataPort, pParticipant->remoteIP, pParticipant->remotePort + 1, invitation, amInvitation);
+ pParticipant->invitationStatus = AwaitingDataInvitationAccepted;
}
}
}
@@ -745,22 +977,34 @@ void AppleMIDISession::manageSessionInvites()
template
void AppleMIDISession::manageReceiverFeedback()
{
- for (size_t i = 0; i < participants.size(); i++)
+#ifndef ONE_PARTICIPANT
+ for (uint8_t i = 0; i < participants.size(); i++)
+#endif
{
- auto participant = &participants[i];
-
- if (participant->doReceiverFeedback == false)
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = &participants[i];
+ if (pParticipant->ssrc == 0) continue;
+#else
+ auto pParticipant = &participant;
+ if (pParticipant->ssrc == 0) return;
+#endif
+
+ if (pParticipant->doReceiverFeedback == false)
+#ifndef ONE_PARTICIPANT
continue;
+#else
+ return;
+#endif
- if ((now - participant->receiverFeedbackStartTime) > Settings::ReceiversFeedbackThreshold)
+ if ((now - pParticipant->receiverFeedbackStartTime) > Settings::ReceiversFeedbackThreshold)
{
AppleMIDI_ReceiverFeedback_t rf;
rf.ssrc = ssrc;
- rf.sequenceNr = participant->sequenceNr;
- writeReceiverFeedback(participant->remoteIP, participant->remotePort, rf);
+ rf.sequenceNr = pParticipant->receiveSequenceNr;
+ writeReceiverFeedback(pParticipant->remoteIP, pParticipant->remotePort, rf);
// reset the clock. It is started when we receive MIDI
- participant->doReceiverFeedback = false;
+ pParticipant->doReceiverFeedback = false;
}
}
}
@@ -770,21 +1014,28 @@ void AppleMIDISession::manageReceiverFeedback()
template
bool AppleMIDISession::sendInvite(IPAddress ip, uint16_t port)
{
+#ifndef ONE_PARTICIPANT
if (participants.full())
+#else
+ if (participant.ssrc != 0)
+#endif
{
return false;
}
-
+
+#ifndef ONE_PARTICIPANT
Participant participant;
+#endif
participant.kind = Initiator;
participant.remoteIP = ip;
participant.remotePort = port;
participant.lastInviteSentTime = now - 1000; // forces invite to be send immediately
participant.lastSyncExchangeTime = now;
- participant.initiatorToken = random(1, INT32_MAX) * 2; // 0xb7062030;
- participant.sequenceNr = random(1, UINT16_MAX); // // http://www.rfc-editor.org/rfc/rfc6295.txt , 2.1. RTP Header
+ participant.initiatorToken = random(1, INT32_MAX) * 2;
+#ifndef ONE_PARTICIPANT
participants.push_back(participant);
+#endif
return true;
}
@@ -794,14 +1045,21 @@ bool AppleMIDISession::sendInvite(IPAddress ip, ui
template
void AppleMIDISession::sendEndSession()
{
+#ifndef ONE_PARTICIPANT
while (participants.size() > 0)
{
- auto participant = &participants[0];
-
+ auto participant = &participants.front();
sendEndSession(participant);
- participants.erase(0);
+ participants.pop_front();
}
+#else
+ if (participant.src != 0)
+ {
+ sendEndSession(&participant);
+ participant.ssrc = 0;
+ }
+#endif
}
template
@@ -812,31 +1070,43 @@ void AppleMIDISession::sendEndSession(Participant<
endSession.ssrc = this->ssrc;
writeEndSession(participant->remoteIP, participant->remotePort, endSession);
- if (NULL != _disconnectedCallback)
+ if (nullptr != _disconnectedCallback)
_disconnectedCallback(participant->ssrc);
}
template
void AppleMIDISession::ReceivedRtp(const Rtp_t& rtp)
{
- auto participant = getParticipantBySSRC(rtp.ssrc);
+#ifndef ONE_PARTICIPANT
+ auto pParticipant = getParticipantBySSRC(rtp.ssrc);
+#else
+ auto pParticipant = (participant.ssrc == rtp.ssrc) ? &participant : nullptr;
+#endif
- if (NULL != participant)
+ if (nullptr != pParticipant)
{
- if (participant->doReceiverFeedback == false)
- participant->receiverFeedbackStartTime = now;
- participant->doReceiverFeedback = true;
+ if (pParticipant->doReceiverFeedback == false)
+ pParticipant->receiverFeedbackStartTime = now;
+ pParticipant->doReceiverFeedback = true;
-#ifdef LATENCY_CALCULATION
- auto offset = (rtp.timestamp - participant->offsetEstimate);
+#ifdef USE_EXT_CALLBACKS
+ auto offset = (rtp.timestamp - pParticipant->offsetEstimate);
auto latency = (int32_t)(rtpMidiClock.Now() - offset);
-#else
- auto latency = 0;
+
+ if (pParticipant->firstMessageReceived == true)
+ // avoids first message to generate sequence exception
+ // as we do not know the last sequenceNr received.
+ pParticipant->firstMessageReceived = false;
+ else if (rtp.sequenceNr - pParticipant->receiveSequenceNr - 1 != 0) {
+ if (nullptr != _exceptionCallback)
+ _exceptionCallback(ssrc, ReceivedPacketsDropped, rtp.sequenceNr - pParticipant->receiveSequenceNr - 1);
+ }
+
+ if (nullptr != _receivedRtpCallback)
+ _receivedRtpCallback(pParticipant->ssrc, rtp, latency);
#endif
- participant->sequenceNr = rtp.sequenceNr;
-
- if (NULL != _receivedRtpCallback)
- _receivedRtpCallback(0, rtp, latency);
+
+ pParticipant->receiveSequenceNr = rtp.sequenceNr;
}
else
{
@@ -847,15 +1117,19 @@ void AppleMIDISession::ReceivedRtp(const Rtp_t& rt
template
void AppleMIDISession::StartReceivedMidi()
{
- if (NULL != _startReceivedMidiByteCallback)
- _startReceivedMidiByteCallback(0);
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _startReceivedMidiByteCallback)
+ _startReceivedMidiByteCallback(ssrc);
+#endif
}
template
void AppleMIDISession::ReceivedMidi(byte value)
{
- if (NULL != _receivedMidiByteCallback)
- _receivedMidiByteCallback(0, value);
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _receivedMidiByteCallback)
+ _receivedMidiByteCallback(ssrc, value);
+#endif
inMidiBuffer.push_back(value);
}
@@ -863,8 +1137,10 @@ void AppleMIDISession::ReceivedMidi(byte value)
template
void AppleMIDISession::EndReceivedMidi()
{
- if (NULL != _endReceivedMidiByteCallback)
- _endReceivedMidiByteCallback(0);
+#ifdef USE_EXT_CALLBACKS
+ if (nullptr != _endReceivedMidiByteCallback)
+ _endReceivedMidiByteCallback(ssrc);
+#endif
}
END_APPLEMIDI_NAMESPACE
diff --git a/src/AppleMIDI_Debug.h b/src/AppleMIDI_Debug.h
new file mode 100644
index 0000000..1b63ca1
--- /dev/null
+++ b/src/AppleMIDI_Debug.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#ifdef SerialMon
+namespace {
+static void AM_DBG_SETUP(unsigned long baud) {
+ SerialMon.begin(baud);
+ while (!SerialMon);
+}
+
+template
+static void AM_DBG_PLAIN(T last) {
+ SerialMon.println(last);
+}
+
+template
+static void AM_DBG_PLAIN(T head, Args... tail) {
+ SerialMon.print(head);
+ SerialMon.print(' ');
+ AM_DBG_PLAIN(tail...);
+}
+
+template
+static void AM_DBG(Args... args) {
+ AM_DBG_PLAIN(args...);
+}
+} // namespace
+#else
+#define AM_DBG_SETUP(...)
+#define AM_DBG_PLAIN(...)
+#define AM_DBG(...)
+#endif
diff --git a/src/AppleMIDI_Defs.h b/src/AppleMIDI_Defs.h
index eb8ee01..d87e7b8 100644
--- a/src/AppleMIDI_Defs.h
+++ b/src/AppleMIDI_Defs.h
@@ -1,13 +1,12 @@
#pragma once
#include "AppleMIDI_Settings.h"
-
#include "AppleMIDI_Namespace.h"
BEGIN_APPLEMIDI_NAMESPACE
-#define APPLEMIDI_LIBRARY_VERSION 0x020000
-#define APPLEMIDI_LIBRARY_VERSION_MAJOR 2
+#define APPLEMIDI_LIBRARY_VERSION 0x030000
+#define APPLEMIDI_LIBRARY_VERSION_MAJOR 3
#define APPLEMIDI_LIBRARY_VERSION_MINOR 0
#define APPLEMIDI_LIBRARY_VERSION_PATCH 0
@@ -17,12 +16,50 @@ typedef uint32_t ssrc_t;
typedef uint32_t initiatorToken_t;
typedef uint64_t timestamp_t;
+union conversionBuffer
+{
+ uint8_t value8;
+ uint16_t value16;
+ uint32_t value32;
+ uint64_t value64;
+ byte buffer[8];
+};
+
+
+enum parserReturn: uint8_t
+{
+ Processed,
+ NotSureGiveMeMoreData,
+ NotEnoughData,
+ UnexpectedData,
+ UnexpectedMidiData,
+ UnexpectedJournalData,
+ SessionNameVeryLong,
+};
+
+#if defined(__AVR__)
+#define APPLEMIDI_PROGMEM PROGMEM
+typedef const __FlashStringHelper* AppleMIDIConstStr;
+#define GFP(x) (reinterpret_cast(x))
+#define GF(x) F(x)
+#else
+#define APPLEMIDI_PROGMEM
+typedef const char* AppleMIDIConstStr;
+#define GFP(x) x
+#define GF(x) x
+#endif
+
#define RtpBuffer_t Deque
#define MidiBuffer_t Deque
-#define APPLEMIDI_LISTENER
+// #define USE_EXT_CALLBACKS
+// #define ONE_PARTICIPANT // memory optimization
+// #define USE_DIRECTORY
+
+// By defining NO_SESSION_NAME in the sketch, you can save 100 bytes
+#ifndef NO_SESSION_NAME
#define KEEP_SESSION_NAME
-#define LATENCY_CALCULATION
+#endif
#define MIDI_SAMPLING_RATE_176K4HZ 176400
#define MIDI_SAMPLING_RATE_192KHZ 192000
@@ -31,14 +68,83 @@ typedef uint64_t timestamp_t;
struct Rtp;
typedef Rtp Rtp_t;
+struct RtpMIDI;
+typedef RtpMIDI RtpMIDI_t;
+
+#ifdef USE_DIRECTORY
+enum WhoCanConnectToMe : uint8_t
+{
+ None,
+ OnlyComputersInMyDirectory,
+ Anyone,
+};
+#endif
+
+// from: https://en.wikipedia.org/wiki/RTP-MIDI
+// Apple decided to create their own protocol, imposing all parameters related to
+// synchronization like the sampling frequency. This session protocol is called "AppleMIDI"
+// in Wireshark software. Session management with AppleMIDI protocol requires two UDP ports,
+// the first one is called "Control Port", the second one is called "Data Port". When used
+// within a multithread implementation, only the Data port requires a "real-time" thread,
+// the other port can be controlled by a normal priority thread. These two ports must be
+// located at two consecutive locations (n / n+1); the first one can be any of the 65536
+// possible ports.
+enum amPortType : uint8_t
+{
+ Control = 0,
+ Data = 1,
+};
+
+// from: https://en.wikipedia.org/wiki/RTP-MIDI
+// AppleMIDI implementation defines two kind of session controllers: session initiators
+// and session listeners. Session initiators are in charge of inviting the session listeners,
+// and are responsible of the clock synchronization sequence. Session initiators can generally
+// be session listeners, but some devices, such as iOS devices, can be session listeners only.
+enum ParticipantKind : uint8_t
+{
+ Listener,
+ Initiator,
+};
+
+enum InviteStatus : uint8_t
+{
+ Initiating,
+ AwaitingControlInvitationAccepted,
+ ControlInvitationAccepted,
+ AwaitingDataInvitationAccepted,
+ DataInvitationAccepted,
+ Connected
+};
+
+enum Exception : uint8_t
+{
+ BufferFullException,
+ ParseException,
+ UnexpectedParseException,
+ TooManyParticipantsException,
+ ComputerNotInDirectory,
+ NotAcceptingAnyone,
+ UnexpectedInviteException,
+ ParticipantNotFoundException,
+ ListenerTimeOutException,
+ MaxAttemptsException,
+ NoResponseFromConnectionRequestException,
+ SendPacketsDropped,
+ ReceivedPacketsDropped,
+ UdpBeginPacketFailed,
+};
+
using connectedCallback = void (*)(const ssrc_t&, const char *);
+using disconnectedCallback = void (*)(const ssrc_t&);
+#ifdef USE_EXT_CALLBACKS
using startReceivedMidiByteCallback = void (*)(const ssrc_t&);
using receivedMidiByteCallback = void (*)(const ssrc_t&, byte);
using endReceivedMidiByteCallback = void (*)(const ssrc_t&);
using receivedRtpCallback = void (*)(const ssrc_t&, const Rtp_t&, const int32_t&);
-using disconnectedCallback = void (*)(const ssrc_t&);
-using errorCallback = void (*)(const ssrc_t&, int32_t);
-
+using exceptionCallback = void (*)(const ssrc_t&, const Exception&, const int32_t value);
+using sentRtpCallback = void (*)(const Rtp_t&);
+using sentRtpMidiCallback = void (*)(const RtpMIDI_t&);
+#endif
/* Signature "Magic Value" for Apple network MIDI session establishment */
const byte amSignature[] = {0xff, 0xff};
@@ -47,91 +153,62 @@ const byte amSignature[] = {0xff, 0xff};
const byte amProtocolVersion[] = {0x00, 0x00, 0x00, 0x02};
/* Apple network MIDI valid commands */
-const byte amInvitation[] = {'I', 'N'};
-const byte amEndSession[] = {'B', 'Y'};
-const byte amSynchronization[] = {'C', 'K'};
-const byte amInvitationAccepted[] = {'O', 'K'};
-const byte amInvitationRejected[] = {'N', 'O'};
-const byte amReceiverFeedback[] = {'R', 'S'};
+const byte amInvitation[] = {'I', 'N'};
+const byte amEndSession[] = {'B', 'Y'};
+const byte amSynchronization[] = {'C', 'K'};
+const byte amInvitationAccepted[] = {'O', 'K'};
+const byte amInvitationRejected[] = {'N', 'O'};
+const byte amReceiverFeedback[] = {'R', 'S'};
const byte amBitrateReceiveLimit[] = {'R', 'L'};
const uint8_t SYNC_CK0 = 0;
const uint8_t SYNC_CK1 = 1;
const uint8_t SYNC_CK2 = 2;
-typedef struct __attribute__((packed)) AppleMIDI_Invitation
+typedef struct PACKED AppleMIDI_Invitation
{
initiatorToken_t initiatorToken;
ssrc_t ssrc;
- char sessionName[DefaultSettings::MaxSessionNameLen + 1];
+#ifdef KEEP_SESSION_NAME
+ char sessionName[DefaultSettings::MaxSessionNameLen + 1];
const size_t getLength() const
{
return sizeof(AppleMIDI_Invitation) - (DefaultSettings::MaxSessionNameLen) + strlen(sessionName);
}
+#else
+ const size_t getLength() const
+ {
+ return sizeof(AppleMIDI_Invitation);
+ }
+#endif
} AppleMIDI_Invitation_t, AppleMIDI_InvitationAccepted_t, AppleMIDI_InvitationRejected_t;
-typedef struct __attribute__((packed)) AppleMIDI_BitrateReceiveLimit
+typedef struct PACKED AppleMIDI_BitrateReceiveLimit
{
ssrc_t ssrc;
uint32_t bitratelimit;
} AppleMIDI_BitrateReceiveLimit_t;
-typedef struct __attribute__((packed)) AppleMIDI_Synchronization
+typedef struct PACKED AppleMIDI_Synchronization
{
ssrc_t ssrc;
uint8_t count;
- uint8_t padding[3];
+ uint8_t padding[3] = {0,0,0};
timestamp_t timestamps[3];
} AppleMIDI_Synchronization_t;
-typedef struct __attribute__((packed)) AppleMIDI_ReceiverFeedback
+typedef struct PACKED AppleMIDI_ReceiverFeedback
{
ssrc_t ssrc;
uint16_t sequenceNr;
uint16_t dummy;
} AppleMIDI_ReceiverFeedback_t;
-typedef struct __attribute__((packed)) AppleMIDI_EndSession
+typedef struct PACKED AppleMIDI_EndSession
{
initiatorToken_t initiatorToken;
ssrc_t ssrc;
} AppleMIDI_EndSession_t;
-// from: https://en.wikipedia.org/wiki/RTP-MIDI
-// Apple decided to create their own protocol, imposing all parameters related to
-// synchronization like the sampling frequency. This session protocol is called "AppleMIDI"
-// in Wireshark software. Session management with AppleMIDI protocol requires two UDP ports,
-// the first one is called "Control Port", the second one is called "Data Port". When used
-// within a multithread implementation, only the Data port requires a "real-time" thread,
-// the other port can be controlled by a normal priority thread. These two ports must be
-// located at two consecutive locations (n / n+1); the first one can be any of the 65536
-// possible ports.
-enum amPortType : uint8_t
-{
- Control = 0,
- Data = 1,
-};
-
-// from: https://en.wikipedia.org/wiki/RTP-MIDI
-// AppleMIDI implementation defines two kind of session controllers: session initiators
-// and session listeners. Session initiators are in charge of inviting the session listeners,
-// and are responsible of the clock synchronization sequence. Session initiators can generally
-// be session listeners, but some devices, such as iOS devices, can be session listeners only.
-enum ParticipantKind : uint8_t
-{
- Listener,
- Initiator,
-};
-
-enum InviteStatus : uint8_t
-{
- Initiating,
- AwaitingControlInvitationAccepted,
- ControlInvitationAccepted,
- AwaitingDataInvitationAccepted,
- DataInvitationAccepted,
- Connected
-};
-
END_APPLEMIDI_NAMESPACE
diff --git a/src/AppleMIDI_Parser.h b/src/AppleMIDI_Parser.h
index 9a6bd54..c956161 100644
--- a/src/AppleMIDI_Parser.h
+++ b/src/AppleMIDI_Parser.h
@@ -1,13 +1,9 @@
#pragma once
#include "utility/Deque.h"
-#include "utility/endian.h"
#include "AppleMIDI_Defs.h"
-
#include "AppleMIDI_Settings.h"
-#include "AppleMIDI_Platform.h"
-
#include "AppleMIDI_Namespace.h"
BEGIN_APPLEMIDI_NAMESPACE
@@ -44,11 +40,7 @@ class AppleMIDIParser
command[0] = buffer[i++];
command[1] = buffer[i++];
- if (false)
- {
- }
-#ifdef APPLEMIDI_LISTENER
- else if (0 == memcmp(command, amInvitation, sizeof(amInvitation)))
+ if (0 == memcmp(command, amInvitation, sizeof(amInvitation)))
{
byte protocolVersion[4];
@@ -75,37 +67,46 @@ class AppleMIDIParser
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- invitation.initiatorToken = ntohl(cb.value32);
+ invitation.initiatorToken = __ntohl(cb.value32);
// The sender's synchronization source identifier.
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- invitation.ssrc = ntohl(cb.value32);
+ invitation.ssrc = __ntohl(cb.value32);
#ifdef KEEP_SESSION_NAME
uint16_t bi = 0;
- while ((i < buffer.size()) && (buffer[i] != 0x00))
+ while (i < buffer.size())
{
- if (bi <= DefaultSettings::MaxSessionNameLen)
- invitation.sessionName[bi++] = buffer[i];
- i++;
+ if (bi < Settings::MaxSessionNameLen)
+ invitation.sessionName[bi++] = buffer[i++];
+ else
+ i++;
}
invitation.sessionName[bi++] = '\0';
#else
- while ((i < buffer.size()) && (buffer[i] != 0x00))
+ while (i < buffer.size())
i++;
#endif
- if (i == buffer.size() || buffer[i++] != 0x00)
- return parserReturn::NotEnoughData;
+ auto retVal = parserReturn::Processed;
+
+ // when given a Session Name and the buffer has been fully processed and the
+ // last character is not 'endl', then we got a very long sessionName. It will
+ // continue in the next memory chunk of the packet. We don't care, so indicated
+ // flush the remainder of the packet.
+ // First part if the session name is kept, processing continues
+ if (i > minimumLen)
+ if (i == buffer.size() && buffer[i] != 0x00)
+ retVal = parserReturn::SessionNameVeryLong;
while (i--)
buffer.pop_front(); // consume all the bytes that made up this message
session->ReceivedInvitation(invitation, portType);
- return parserReturn::Processed;
+ return retVal;
}
else if (0 == memcmp(command, amEndSession, sizeof(amEndSession)))
{
@@ -132,14 +133,14 @@ class AppleMIDIParser
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- endSession.initiatorToken = ntohl(cb.value32);
+ endSession.initiatorToken = __ntohl(cb.value32);
// The sender's synchronization source identifier.
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- endSession.ssrc = ntohl(cb.value32);
+ endSession.ssrc = __ntohl(cb.value32);
while (i--)
buffer.pop_front(); // consume all the bytes that made up this message
@@ -162,7 +163,7 @@ class AppleMIDIParser
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- synchronization.ssrc = ntohl(cb.value32);
+ synchronization.ssrc = __ntohl(cb.value32);
synchronization.count = buffer[i++];
buffer[i++];
@@ -176,7 +177,7 @@ class AppleMIDIParser
cb.buffer[5] = buffer[i++];
cb.buffer[6] = buffer[i++];
cb.buffer[7] = buffer[i++];
- synchronization.timestamps[0] = ntohll(cb.value64);
+ synchronization.timestamps[0] = __ntohll(cb.value64);
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
@@ -186,7 +187,7 @@ class AppleMIDIParser
cb.buffer[5] = buffer[i++];
cb.buffer[6] = buffer[i++];
cb.buffer[7] = buffer[i++];
- synchronization.timestamps[1] = ntohll(cb.value64);
+ synchronization.timestamps[1] = __ntohll(cb.value64);
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
@@ -196,7 +197,7 @@ class AppleMIDIParser
cb.buffer[5] = buffer[i++];
cb.buffer[6] = buffer[i++];
cb.buffer[7] = buffer[i++];
- synchronization.timestamps[2] = ntohll(cb.value64);
+ synchronization.timestamps[2] = __ntohll(cb.value64);
while (i--)
buffer.pop_front(); // consume all the bytes that made up this message
@@ -217,14 +218,14 @@ class AppleMIDIParser
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- receiverFeedback.ssrc = ntohl(cb.value32);
+ receiverFeedback.ssrc = __ntohl(cb.value32);
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
- receiverFeedback.sequenceNr = ntohs(cb.value16);
+ receiverFeedback.sequenceNr = __ntohs(cb.value16);
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
- receiverFeedback.dummy = ntohs(cb.value16);
+ receiverFeedback.dummy = __ntohs(cb.value16);
while (i--)
buffer.pop_front(); // consume all the bytes that made up this message
@@ -233,7 +234,6 @@ class AppleMIDIParser
return parserReturn::Processed;
}
-#endif
#ifdef APPLEMIDI_INITIATOR
else if (0 == memcmp(command, amInvitationAccepted, sizeof(amInvitationAccepted)))
{
@@ -262,37 +262,47 @@ class AppleMIDIParser
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- invitationAccepted.initiatorToken = ntohl(cb.value32);
+ invitationAccepted.initiatorToken = __ntohl(cb.value32);
// The sender's synchronization source identifier.
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- invitationAccepted.ssrc = ntohl(cb.value32);
+ invitationAccepted.ssrc = __ntohl(cb.value32);
- #ifdef KEEP_SESSION_NAME
+#ifdef KEEP_SESSION_NAME
uint16_t bi = 0;
- while ((i < buffer.size()) && (buffer[i] != 0x00))
+ while (i < buffer.size())
{
- if (bi <= DefaultSettings::MaxSessionNameLen)
- invitationAccepted.sessionName[bi++] = buffer[i];
- i++;
+ if (bi < Settings::MaxSessionNameLen)
+ invitationAccepted.sessionName[bi++] = buffer[i++];
+ else
+ i++;
}
invitationAccepted.sessionName[bi++] = '\0';
#else
- while ((i < buffer.size()) && (buffer[i] != 0x00))
+ while (i < buffer.size())
i++;
#endif
- if (i == buffer.size() || buffer[i++] != 0x00)
- return parserReturn::NotEnoughData;
+
+ auto retVal = parserReturn::Processed;
+
+ // when given a Session Name and the buffer has been fully processed and the
+ // last character is not 'endl', then we got a very long sessionName. It will
+ // continue in the next memory chunk of the packet. We don't care, so indicated
+ // flush the remainder of the packet.
+ // First part if the session name is kept, processing continues
+ if (i > minimumLen)
+ if (i == buffer.size() && buffer[i] != 0x00)
+ retVal = parserReturn::SessionNameVeryLong;
while (i--)
buffer.pop_front(); // consume all the bytes that made up this message
session->ReceivedInvitationAccepted(invitationAccepted, portType);
- return parserReturn::Processed;
+ return retVal;
}
else if (0 == memcmp(command, amInvitationRejected, sizeof(amInvitationRejected)))
{
@@ -321,20 +331,20 @@ class AppleMIDIParser
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- invitationRejected.initiatorToken = ntohl(cb.value32);
+ invitationRejected.initiatorToken = __ntohl(cb.value32);
// The sender's synchronization source identifier.
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- invitationRejected.ssrc = ntohl(cb.value32);
+ invitationRejected.ssrc = __ntohl(cb.value32);
#ifdef KEEP_SESSION_NAME
uint16_t bi = 0;
while ((i < buffer.size()) && (buffer[i] != 0x00))
{
- if (bi <= DefaultSettings::MaxSessionNameLen)
+ if (bi < Settings::MaxSessionNameLen)
invitationRejected.sessionName[bi++] = buffer[i];
i++;
}
@@ -343,8 +353,11 @@ class AppleMIDIParser
while ((i < buffer.size()) && (buffer[i] != 0x00))
i++;
#endif
- if (i == buffer.size() || buffer[i++] != 0x00)
- return parserReturn::NotEnoughData;
+ // session name is optional.
+ // If i > minimum size (16), then a sessionName was provided and must include 0x00
+ if (i > minimumLen)
+ if (i == buffer.size() || buffer[i++] != 0x00)
+ return parserReturn::NotEnoughData;
while (i--)
buffer.pop_front(); // consume all the bytes that made up this message
@@ -365,13 +378,13 @@ class AppleMIDIParser
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- bitrateReceiveLimit.ssrc = ntohl(cb.value32);
+ bitrateReceiveLimit.ssrc = __ntohl(cb.value32);
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- bitrateReceiveLimit.bitratelimit = ntohl(cb.value32);
+ bitrateReceiveLimit.bitratelimit = __ntohl(cb.value32);
while (i--)
buffer.pop_front(); // consume all the bytes that made up this message
diff --git a/src/AppleMIDI_Participant.h b/src/AppleMIDI_Participant.h
index 859c807..2a01c40 100644
--- a/src/AppleMIDI_Participant.h
+++ b/src/AppleMIDI_Participant.h
@@ -10,13 +10,16 @@ template
struct Participant
{
ParticipantKind kind;
- ssrc_t ssrc;
- IPAddress remoteIP;
- uint16_t remotePort;
-
+ ssrc_t ssrc = 0;
+ IPAddress remoteIP = INADDR_NONE;
+ uint16_t remotePort = 0;
+
unsigned long receiverFeedbackStartTime;
bool doReceiverFeedback = false;
- uint16_t sequenceNr = random(1, UINT16_MAX); // http://www.rfc-editor.org/rfc/rfc6295.txt , 2.1. RTP Header
+
+ uint16_t sendSequenceNr = random(1, UINT16_MAX); // http://www.rfc-editor.org/rfc/rfc6295.txt , 2.1. RTP Header
+ uint16_t receiveSequenceNr = 0;
+
unsigned long lastSyncExchangeTime;
#ifdef APPLEMIDI_INITIATOR
@@ -30,12 +33,13 @@ struct Participant
bool synchronizing = false;
#endif
-#ifdef LATENCY_CALCULATION
+#ifdef USE_EXT_CALLBACKS
+ bool firstMessageReceived = true;
uint32_t offsetEstimate;
#endif
#ifdef KEEP_SESSION_NAME
- char sessionName[DefaultSettings::MaxSessionNameLen + 1];
+ char sessionName[Settings::MaxSessionNameLen + 1];
#endif
} ;
diff --git a/src/AppleMIDI_PlatformBegin.h b/src/AppleMIDI_PlatformBegin.h
new file mode 100644
index 0000000..9cb35e2
--- /dev/null
+++ b/src/AppleMIDI_PlatformBegin.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "AppleMIDI_Namespace.h"
+
+#include "utility/endian.h"
+
+BEGIN_APPLEMIDI_NAMESPACE
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#define PACKED
+#else
+#define PACKED __attribute__((__packed__))
+#endif
+
+struct DefaultPlatform
+{
+};
+
+END_APPLEMIDI_NAMESPACE
diff --git a/src/AppleMIDI_Platform.h b/src/AppleMIDI_PlatformEnd.h
similarity index 65%
rename from src/AppleMIDI_Platform.h
rename to src/AppleMIDI_PlatformEnd.h
index bda143b..fec7b9f 100644
--- a/src/AppleMIDI_Platform.h
+++ b/src/AppleMIDI_PlatformEnd.h
@@ -4,8 +4,9 @@
BEGIN_APPLEMIDI_NAMESPACE
-struct ArduinoPlatform
-{
-};
+#ifdef WIN32
+#pragma pack(pop)
+#endif
+#undef PACKED
END_APPLEMIDI_NAMESPACE
diff --git a/src/AppleMIDI_Settings.h b/src/AppleMIDI_Settings.h
index 85cd5b2..1d03693 100644
--- a/src/AppleMIDI_Settings.h
+++ b/src/AppleMIDI_Settings.h
@@ -6,11 +6,15 @@ BEGIN_APPLEMIDI_NAMESPACE
struct DefaultSettings
{
+ static const size_t UdpTxPacketMaxSize = 24;
+
static const size_t MaxBufferSize = 64;
static const size_t MaxSessionNameLen = 24;
static const uint8_t MaxNumberOfParticipants = 2;
+
+ static const uint8_t MaxNumberOfComputersInDirectory = 5;
// The recovery journal mechanism requires that the receiver periodically
// inform the sender of the sequence number of the most recently received packet.
@@ -22,10 +26,9 @@ struct DefaultSettings
// two sequence numbers. The sender can then free the memory containing old journalling data if necessary.
static const unsigned long ReceiversFeedbackThreshold = 1000;
- // Should not be lower than 11000 (15s)
- // MacOS sends CK messages every 10 seconds
- // rtpMIDI on Windows sends CK messages every x seconds
- static const unsigned long CK_MaxTimeOut = 45000;
+ // The initiator must initiate a new sync exchange at least once every 60 seconds
+ // as in https://developer.apple.com/library/archive/documentation/Audio/Conceptual/MIDINetworkDriverProtocol/MIDI/MIDI.html
+ static const unsigned long CK_MaxTimeOut = 61000;
// when set to true, the lower 32-bits of the rtpClock ae send
// when set to false, 0 will be set for immediate playout.
@@ -38,12 +41,4 @@ struct DefaultSettings
static const unsigned long SynchronizationHeartBeat = 10000;
};
-enum parserReturn: uint8_t
-{
- Processed,
- NotSureGiveMeMoreData,
- NotEnoughData,
- UnexpectedData,
-};
-
END_APPLEMIDI_NAMESPACE
diff --git a/src/rtpMIDI_Clock.h b/src/rtpMIDI_Clock.h
index 68898d2..649d84a 100644
--- a/src/rtpMIDI_Clock.h
+++ b/src/rtpMIDI_Clock.h
@@ -7,8 +7,6 @@
BEGIN_APPLEMIDI_NAMESPACE
#define MSEC_PER_SEC 1000
-#define USEC_PER_SEC 1000000
-#define NSEC_PER_SEC 1000000000
typedef struct rtpMidi_Clock
{
diff --git a/src/rtpMIDI_Defs.h b/src/rtpMIDI_Defs.h
index d81326b..4d2aa58 100644
--- a/src/rtpMIDI_Defs.h
+++ b/src/rtpMIDI_Defs.h
@@ -133,7 +133,7 @@ BEGIN_APPLEMIDI_NAMESPACE
#define RTP_MIDI_CJ_CHAPTER_E_MASK_LENGTH 0x7f
#define RTP_MIDI_CJ_CHAPTER_A_MASK_LENGTH 0x7f
-typedef struct __attribute__((packed)) RtpMIDI
+typedef struct PACKED RtpMIDI
{
uint8_t flags;
} RtpMIDI_t;
diff --git a/src/rtpMIDI_Parser.h b/src/rtpMIDI_Parser.h
index 9053bf4..914179a 100644
--- a/src/rtpMIDI_Parser.h
+++ b/src/rtpMIDI_Parser.h
@@ -1,7 +1,6 @@
#pragma once
#include "utility/Deque.h"
-#include "utility/endian.h"
#include
@@ -9,8 +8,6 @@
#include "rtp_Defs.h"
#include "AppleMIDI_Settings.h"
-#include "AppleMIDI_Platform.h"
-
#include "AppleMIDI_Namespace.h"
BEGIN_APPLEMIDI_NAMESPACE
@@ -24,9 +21,33 @@ class rtpMIDIParser
private:
bool _rtpHeadersComplete = false;
bool _journalSectionComplete = false;
+ bool _channelJournalSectionComplete = false;
uint16_t midiCommandLength;
uint8_t _journalTotalChannels;
uint8_t rtpMidi_Flags = 0;
+ int cmdCount = 0;
+ uint8_t runningstatus = 0;
+ size_t _bytesToFlush = 0;
+
+protected:
+ void debugPrintBuffer(RtpBuffer_t &buffer)
+ {
+#ifdef DEBUG
+ for (int i = 0; i < buffer.size(); i++)
+ {
+ SerialMon.print(" ");
+ SerialMon.print(i);
+ SerialMon.print(i < 10 ? " " : " ");
+ }
+ for (int i = 0; i < buffer.size(); i++)
+ {
+ SerialMon.print("0x");
+ SerialMon.print(buffer[i] < 16 ? "0" : "");
+ SerialMon.print(buffer[i], HEX);
+ SerialMon.print(" ");
+ }
+#endif
+ }
public:
AppleMIDISession * session;
@@ -41,6 +62,8 @@ class rtpMIDIParser
//
parserReturn parse(RtpBuffer_t &buffer)
{
+ debugPrintBuffer(buffer);
+
conversionBuffer cb;
// [RFC3550] provides a complete description of the RTP header fields.
@@ -78,17 +101,17 @@ class rtpMIDIParser
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
- rtp.sequenceNr = ntohs(cb.value16);
+ rtp.sequenceNr = __ntohs(cb.value16);
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- rtp.timestamp = ntohl(cb.value32);
+ rtp.timestamp = __ntohl(cb.value32);
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
cb.buffer[2] = buffer[i++];
cb.buffer[3] = buffer[i++];
- rtp.ssrc = ntohl(cb.value32);
+ rtp.ssrc = __ntohl(cb.value32);
uint8_t version = RTP_VERSION(rtp.vpxcc);
#ifdef DEBUG
@@ -97,7 +120,7 @@ class rtpMIDIParser
uint8_t csrc_count = RTP_CSRC_COUNT(rtp.vpxcc);
#endif
- if (2 != version)
+ if (RTP_VERSION_2 != version)
{
return parserReturn::UnexpectedData;
}
@@ -119,6 +142,7 @@ class rtpMIDIParser
if (buffer.size() < minimumLen)
return parserReturn::NotSureGiveMeMoreData;
+ // 2.2. MIDI Payload (https://www.ietf.org/rfc/rfc4695.html#section-2.2)
// The payload MUST begin with the MIDI command section. The
// MIDI command section codes a (possibly empty) list of timestamped
// MIDI commands and provides the essential service of the payload
@@ -142,6 +166,9 @@ class rtpMIDIParser
midiCommandLength = (midiCommandLength << 8) | octet;
}
+ cmdCount = 0;
+ runningstatus = 0;
+
while (i--)
buffer.pop_front();
@@ -149,15 +176,15 @@ class rtpMIDIParser
// initialize the Journal Section
_journalSectionComplete = false;
+ _channelJournalSectionComplete = false;
_journalTotalChannels = 0;
}
// Always a MIDI section
if (midiCommandLength > 0)
{
- auto retVal = decodeMidiSection(buffer);
- if (retVal != parserReturn::Processed)
- return retVal;
+ auto retVal = decodeMIDICommandSection(buffer);
+ if (retVal != parserReturn::Processed) return retVal;
}
// The payload MAY also contain a journal section. The journal section
@@ -168,8 +195,16 @@ class rtpMIDIParser
if (rtpMidi_Flags & RTP_MIDI_CS_FLAG_J)
{
auto retVal = decodeJournalSection(buffer);
- if (retVal != parserReturn::Processed)
+ switch (retVal) {
+ case parserReturn::Processed:
+ break;
+ case parserReturn::NotEnoughData:
+ return parserReturn::NotEnoughData;
+ case parserReturn::UnexpectedJournalData:
+ _rtpHeadersComplete = false;
+ default:
return retVal;
+ }
}
_rtpHeadersComplete = false;
@@ -179,7 +214,7 @@ class rtpMIDIParser
#include "rtpMIDI_Parser_JournalSection.hpp"
- #include "rtpMIDI_Parser_MidiCommandSection.hpp"
+ #include "rtpMIDI_Parser_CommandSection.hpp"
};
END_APPLEMIDI_NAMESPACE
diff --git a/src/rtpMIDI_Parser_MidiCommandSection.hpp b/src/rtpMIDI_Parser_CommandSection.hpp
similarity index 54%
rename from src/rtpMIDI_Parser_MidiCommandSection.hpp
rename to src/rtpMIDI_Parser_CommandSection.hpp
index 3cbf44a..143cc63 100644
--- a/src/rtpMIDI_Parser_MidiCommandSection.hpp
+++ b/src/rtpMIDI_Parser_CommandSection.hpp
@@ -1,8 +1,44 @@
-parserReturn decodeMidiSection(RtpBuffer_t &buffer)
+// https://www.ietf.org/rfc/rfc4695.html#section-3
+
+parserReturn decodeMIDICommandSection(RtpBuffer_t &buffer)
{
- int cmdCount = 0;
+ debugPrintBuffer(buffer);
+
+ // https://www.ietf.org/rfc/rfc4695.html#section-3.2
+ //
+ // The first MIDI channel command in the MIDI list MUST include a status
+ // octet.Running status coding, as defined in[MIDI], MAY be used for
+ // all subsequent MIDI channel commands in the list.As in[MIDI],
+ // System Commonand System Exclusive messages(0xF0 ... 0xF7) cancel
+ // the running status state, but System Real - time messages(0xF8 ...
+ // 0xFF) do not affect the running status state. All System commands in
+ // the MIDI list MUST include a status octet.
+
+ // As we note above, the first channel command in the MIDI list MUST
+ // include a status octet.However, the corresponding command in the
+ // original MIDI source data stream might not have a status octet(in
+ // this case, the source would be coding the command using running
+ // status). If the status octet of the first channel command in the
+ // MIDI list does not appear in the source data stream, the P(phantom)
+ // header bit MUST be set to 1. In all other cases, the P bit MUST be
+ // set to 0.
+ //
+ // Note that the P bit describes the MIDI source data stream, not the
+ // MIDI list encoding; regardless of the state of the P bit, the MIDI
+ // list MUST include the status octet.
+ //
+ // As receivers MUST be able to decode running status, sender
+ // implementors should feel free to use running status to improve
+ // bandwidth efficiency. However, senders SHOULD NOT introduce timing
+ // jitter into an existing MIDI command stream through an inappropriate
+ // use or removal of running status coding. This warning primarily
+ // applies to senders whose RTP MIDI streams may be transcoded onto a
+ // MIDI 1.0 DIN cable[MIDI] by the receiver : both the timestamps and
+ // the command coding (running status or not) must comply with the
+ // physical restrictions of implicit time coding over a slow serial
+ // line.
- uint8_t runningstatus = 0;
+ // (lathoub: RTP_MIDI_CS_FLAG_P((phantom) not implemented
/* Multiple MIDI-commands might follow - the exact number can only be discovered by really decoding the commands! */
while (midiCommandLength)
@@ -10,53 +46,47 @@ parserReturn decodeMidiSection(RtpBuffer_t &buffer)
/* for the first command we only have a delta-time if Z-Flag is set */
if ((cmdCount) || (rtpMidi_Flags & RTP_MIDI_CS_FLAG_Z))
{
- auto consumed = decodeTime(buffer);
+ size_t consumed = 0;
+ auto retVal = decodeTime(buffer, consumed);
+ if (retVal != parserReturn::Processed) return retVal;
midiCommandLength -= consumed;
-
while (consumed--)
buffer.pop_front();
-
- if (midiCommandLength > 0 && 0 >= buffer.size())
- return parserReturn::NotEnoughData;
}
if (midiCommandLength > 0)
{
- /* Decode a MIDI-command - if 0 is returned something went wrong */
- size_t consumed = decodeMidi(buffer, runningstatus);
- if (consumed == 0)
- {
- }
- else if (consumed > buffer.size())
- {
- // sysex split in decodeMidi
- return parserReturn::NotEnoughData;
+ cmdCount++;
+
+ size_t consumed = 0;
+ auto retVal = decodeMidi(buffer, runningstatus, consumed);
+ if (retVal == parserReturn::NotEnoughData) {
+ cmdCount = 0; // avoid first command again
+ return retVal;
}
midiCommandLength -= consumed;
-
while (consumed--)
buffer.pop_front();
-
- if (midiCommandLength > 0 && 0 >= buffer.size())
- return parserReturn::NotEnoughData;
-
- cmdCount++;
}
}
return parserReturn::Processed;
}
-size_t decodeTime(RtpBuffer_t &buffer)
+parserReturn decodeTime(RtpBuffer_t &buffer, size_t &consumed)
{
- uint8_t consumed = 0;
+ debugPrintBuffer(buffer);
+
uint32_t deltatime = 0;
/* RTP-MIDI deltatime is "compressed" using only the necessary amount of octets */
for (uint8_t j = 0; j < 4; j++)
{
+ if (buffer.size() < 1)
+ return parserReturn::NotEnoughData;
+
uint8_t octet = buffer[consumed];
deltatime = (deltatime << 7) | (octet & RTP_MIDI_DELTA_TIME_OCTET_MASK);
consumed++;
@@ -64,21 +94,30 @@ size_t decodeTime(RtpBuffer_t &buffer)
if ((octet & RTP_MIDI_DELTA_TIME_EXTENSION) == 0)
break;
}
- return consumed;
+
+ return parserReturn::Processed;
}
-size_t decodeMidi(RtpBuffer_t &buffer, uint8_t &runningstatus)
+parserReturn decodeMidi(RtpBuffer_t &buffer, uint8_t &runningstatus, size_t &consumed)
{
- size_t consumed = 0;
+ debugPrintBuffer(buffer);
- auto octet = buffer[0];
- bool using_rs;
+ if (buffer.size() < 1)
+ return parserReturn::NotEnoughData;
+
+ auto octet = buffer.front();
/* MIDI realtime-data -> one octet -- unlike serial-wired MIDI realtime-commands in RTP-MIDI will
* not be intermingled with other MIDI-commands, so we handle this case right here and return */
if (octet >= 0xf8)
{
- return 1;
+ consumed = 1;
+
+ session->StartReceivedMidi();
+ session->ReceivedMidi(octet);
+ session->EndReceivedMidi();
+
+ return parserReturn::Processed;
}
/* see if this first octet is a status message */
@@ -87,17 +126,14 @@ size_t decodeMidi(RtpBuffer_t &buffer, uint8_t &runningstatus)
/* if we have no running status yet -> error */
if (((runningstatus)&RTP_MIDI_COMMAND_STATUS_FLAG) == 0)
{
- return 0;
+ return parserReturn::Processed;
}
/* our first octet is "virtual" coming from a preceding MIDI-command,
* so actually we have not really consumed anything yet */
octet = runningstatus;
- using_rs = true;
}
else
{
- /* We have a "real" status-byte */
- using_rs = false;
/* Let's see how this octet influences our running-status */
/* if we have a "normal" MIDI-command then the new status replaces the current running-status */
if (octet < 0xf0)
@@ -117,10 +153,8 @@ size_t decodeMidi(RtpBuffer_t &buffer, uint8_t &runningstatus)
/* non-system MIDI-commands encode the command in the high nibble and the channel
* in the low nibble - so we will take care of those cases next */
if (octet < 0xf0)
- {
- uint8_t type = (octet & 0xf0);
-
- switch (type)
+ {
+ switch (octet & 0xf0)
{
case MIDI_NAMESPACE::MidiType::NoteOff:
consumed += 2;
@@ -145,12 +179,16 @@ size_t decodeMidi(RtpBuffer_t &buffer, uint8_t &runningstatus)
break;
}
+ if (buffer.size() < consumed) {
+ return parserReturn::NotEnoughData;
+ }
+
session->StartReceivedMidi();
for (size_t j = 0; j < consumed; j++)
session->ReceivedMidi(buffer[j]);
session->EndReceivedMidi();
- return consumed;
+ return parserReturn::Processed;
}
/* Here we catch the remaining system-common commands */
@@ -158,9 +196,7 @@ size_t decodeMidi(RtpBuffer_t &buffer, uint8_t &runningstatus)
{
case MIDI_NAMESPACE::MidiType::SystemExclusiveStart:
case MIDI_NAMESPACE::MidiType::SystemExclusiveEnd:
- consumed = decodeMidiSysEx(buffer);
- if (consumed > buffer.max_size())
- return consumed;
+ decodeMidiSysEx(buffer, consumed);
break;
case MIDI_NAMESPACE::MidiType::TimeCodeQuarterFrame:
consumed += 1;
@@ -175,38 +211,51 @@ size_t decodeMidi(RtpBuffer_t &buffer, uint8_t &runningstatus)
break;
}
+ if (buffer.size() < consumed)
+ return parserReturn::NotEnoughData;
+
session->StartReceivedMidi();
for (size_t j = 0; j < consumed; j++)
session->ReceivedMidi(buffer[j]);
session->EndReceivedMidi();
- return consumed;
+ return parserReturn::Processed;
}
-size_t decodeMidiSysEx(RtpBuffer_t &buffer)
+parserReturn decodeMidiSysEx(RtpBuffer_t &buffer, size_t &consumed)
{
- size_t consumed = 1; // beginning SysEx Token is not counted (as it could remain)
- size_t i = 0;
- auto octet = buffer[++i];
+ debugPrintBuffer(buffer);
+// consumed = 1; // beginning SysEx Token is not counted (as it could remain)
+ size_t i = 1; // 0 = start of SysEx, so we can start with 1
while (i < buffer.size())
{
consumed++;
- octet = buffer[i++];
+ auto octet = buffer[i++];
+
+ Serial.print("0x");
+ Serial.print(octet < 16 ? "0" : "");
+ Serial.print(octet, HEX);
+ Serial.print(" ");
+
if (octet == MIDI_NAMESPACE::MidiType::SystemExclusiveEnd) // Complete message
- return consumed;
+ {
+ return parserReturn::Processed;
+ }
else if (octet == MIDI_NAMESPACE::MidiType::SystemExclusiveStart) // Start
- return consumed;
+ {
+ return parserReturn::Processed;
+ }
}
-
+
// begin of the SysEx is found, not the end.
// so transmit what we have, add a stop-token at the end,
- // remove the byes, modify the length and indicate
+ // remove the bytes, modify the length and indicate
// not-enough data, so we buffer gets filled with the remaining bytes.
// to compensate for adding the sysex at the end.
consumed--;
-
+
// send MIDI data
session->StartReceivedMidi();
for (size_t j = 0; j < consumed; j++)
@@ -217,11 +266,14 @@ size_t decodeMidiSysEx(RtpBuffer_t &buffer)
// Remove the bytes that were submitted
for (size_t j = 0; j < consumed; j++)
buffer.pop_front();
+ // Start a new SysEx train
buffer.push_front(MIDI_NAMESPACE::MidiType::SystemExclusiveEnd);
midiCommandLength -= consumed;
- midiCommandLength += 1; // adding the manual SysEx SystemExclusiveEnd
-
+ midiCommandLength += 1; // for adding the manual SysEx SystemExclusiveEnd in front
+
// indicates split SysEx
- return buffer.max_size() + 1;
+ consumed = buffer.max_size() + 1;
+
+ return parserReturn::Processed;
}
diff --git a/src/rtpMIDI_Parser_JournalSection.hpp b/src/rtpMIDI_Parser_JournalSection.hpp
index 4669471..c0041b1 100644
--- a/src/rtpMIDI_Parser_JournalSection.hpp
+++ b/src/rtpMIDI_Parser_JournalSection.hpp
@@ -27,23 +27,14 @@ parserReturn decodeJournalSection(RtpBuffer_t &buffer)
{
size_t i = 0;
- minimumLen += 1;
+ // Minimum size for the Journal section is 3
+ minimumLen += 3;
if (buffer.size() < minimumLen)
return parserReturn::NotEnoughData;
/* lets get the main flags from the recovery journal header */
uint8_t flags = buffer[i++];
- // sequenceNr
- minimumLen += 2;
- if (buffer.size() < minimumLen)
- return parserReturn::NotEnoughData;
-
- if ((flags & RTP_MIDI_JS_FLAG_Y) == 0 && (flags & RTP_MIDI_JS_FLAG_A) == 0)
- {
- return parserReturn::Processed;
- }
-
// The 16-bit Checkpoint Packet Seqnum header field codes the sequence
// number of the checkpoint packet for this journal, in network byte
// order (big-endian). The choice of the checkpoint packet sets the
@@ -57,8 +48,21 @@ parserReturn decodeJournalSection(RtpBuffer_t &buffer)
// stream (modulo 2^16).
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
- uint16_t checkPoint = ntohs(cb.value16);
-
+ // uint16_t checkPoint = __ntohs(cb.value16); ; // unused
+
+ // (RFC 4695, 5 Recovery Journal Format)
+ // If A and Y are both zero, the recovery journal only contains its 3-
+ // octet header and is considered to be an "empty" journal.
+ if ((flags & RTP_MIDI_JS_FLAG_Y) == 0 && (flags & RTP_MIDI_JS_FLAG_A) == 0)
+ {
+ // Big fixed by @hugbug
+ while (minimumLen-- > 0)
+ buffer.pop_front();
+
+ _journalSectionComplete = true;
+ return parserReturn::Processed;
+ }
+
// By default, the payload format does not use enhanced Chapter C
// encoding. In this default case, the H bit MUST be set to 0 for all
// packets in the stream.
@@ -83,18 +87,22 @@ parserReturn decodeJournalSection(RtpBuffer_t &buffer)
{
minimumLen += 2;
if (buffer.size() < minimumLen)
+ {
return parserReturn::NotEnoughData;
+ }
cb.buffer[0] = buffer[i++];
cb.buffer[1] = buffer[i++];
- uint16_t systemflags = ntohs(cb.value16);
+ uint16_t systemflags = __ntohs(cb.value16);
uint16_t sysjourlen = systemflags & RTP_MIDI_SJ_MASK_LENGTH;
-
+
uint16_t remainingBytes = sysjourlen - 2;
-
+
minimumLen += remainingBytes;
if (buffer.size() < minimumLen)
+ {
return parserReturn::NotEnoughData;
+ }
i += remainingBytes;
}
@@ -106,45 +114,67 @@ parserReturn decodeJournalSection(RtpBuffer_t &buffer)
{
/* At the same place we find the total channels encoded in the channel journal */
_journalTotalChannels = (flags & RTP_MIDI_JS_MASK_TOTALCHANNELS) + 1;
-
- // do channel reading below, so we can already purge bytes here
}
-
+
while (i-- > 0) // is that the same as while (i--) ??
buffer.pop_front();
-
+
_journalSectionComplete = true;
}
// iterate through all the channels specified in header
while (_journalTotalChannels > 0)
{
- if (buffer.size() < 3)
- return parserReturn::NotEnoughData;
+ if (false == _channelJournalSectionComplete) {
- cb.buffer[0] = 0x00;
- cb.buffer[1] = buffer[0];
- cb.buffer[2] = buffer[1];
- cb.buffer[3] = buffer[2];
- uint32_t chanflags = ntohl(cb.value32);
-
- uint16_t chanjourlen = (chanflags & RTP_MIDI_CJ_MASK_LENGTH) >> 8;
- uint16_t channelNr = (chanflags & RTP_MIDI_CJ_MASK_CHANNEL) >> RTP_MIDI_CJ_CHANNEL_SHIFT;
+ if (buffer.size() < 3)
+ return parserReturn::NotEnoughData;
- // We have the most important bit of information - the length of the channel information
- // no more need to further parse.
+ // 3 bytes for channel journal
+ cb.buffer[0] = 0x00;
+ cb.buffer[1] = buffer[0];
+ cb.buffer[2] = buffer[1];
+ cb.buffer[3] = buffer[2];
+ uint32_t chanflags = __ntohl(cb.value32);
+
+ bool S_flag = (chanflags & RTP_MIDI_CJ_FLAG_S) == 1;
+ uint8_t channelNr = (chanflags & RTP_MIDI_CJ_MASK_CHANNEL) >> RTP_MIDI_CJ_CHANNEL_SHIFT;
+ bool H_flag = (chanflags & RTP_MIDI_CJ_FLAG_H) == 1;
+ uint8_t chanjourlen = (chanflags & RTP_MIDI_CJ_MASK_LENGTH) >> 8;
+
+ if ((chanflags & RTP_MIDI_CJ_FLAG_P)) {
+ }
+ if ((chanflags & RTP_MIDI_CJ_FLAG_C)) {
+ }
+ if ((chanflags & RTP_MIDI_CJ_FLAG_M)) {
+ }
+ if ((chanflags & RTP_MIDI_CJ_FLAG_W)) {
+ }
+ if ((chanflags & RTP_MIDI_CJ_FLAG_N)) {
+ }
+ if ((chanflags & RTP_MIDI_CJ_FLAG_E)) {
+ }
+ if ((chanflags & RTP_MIDI_CJ_FLAG_T)) {
+ }
+ if ((chanflags & RTP_MIDI_CJ_FLAG_A)) {
+ }
+
+ _bytesToFlush = chanjourlen;
+
+ _channelJournalSectionComplete = true;
+ }
- if (buffer.size() < chanjourlen)
- {
+ while (buffer.size() > 0 && _bytesToFlush > 0) {
+ _bytesToFlush--;
+ buffer.pop_front();
+ }
+
+ if (_bytesToFlush > 0) {
return parserReturn::NotEnoughData;
}
-
+
_journalTotalChannels--;
-
- // advance the pointer
- while (chanjourlen--)
- buffer.pop_front();
- }
-
+ }
+
return parserReturn::Processed;
}
diff --git a/src/rtp_Defs.h b/src/rtp_Defs.h
index d9a90d5..85609cd 100644
--- a/src/rtp_Defs.h
+++ b/src/rtp_Defs.h
@@ -4,6 +4,23 @@
BEGIN_APPLEMIDI_NAMESPACE
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | V |P|X| CC |M| PT | Sequence number |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+#define RTP_VERSION_2 2
+
+// first octet
+#define RTP_P_FIELD 0x20
+#define RTP_X_FIELD 0x10
+#define RTP_CC_FIELD 0xF
+
+// second octet
+#define RTP_M_FIELD 0x80
+#define RTP_PT_FIELD 0x7F
+
/* magic number */
#define PAYLOADTYPE_RTPMIDI 97
@@ -12,21 +29,21 @@ BEGIN_APPLEMIDI_NAMESPACE
/* Padding is the third bit; No need to shift, because true is any value
other than 0! */
-#define RTP_PADDING(octet) ((octet)&0x20)
+#define RTP_PADDING(octet) ((octet)&RTP_P_FIELD)
/* Extension bit is the fourth bit */
-#define RTP_EXTENSION(octet) ((octet)&0x10)
+#define RTP_EXTENSION(octet) ((octet)&RTP_X_FIELD)
/* CSRC count is the last four bits */
-#define RTP_CSRC_COUNT(octet) ((octet)&0xF)
+#define RTP_CSRC_COUNT(octet) ((octet)&RTP_CC_FIELD)
/* Marker is the first bit of the second octet */
-#define RTP_MARKER(octet) ((octet)&0x80)
+#define RTP_MARKER(octet) ((octet)&RTP_M_FIELD)
/* Payload type is the last 7 bits */
-#define RTP_PAYLOAD_TYPE(octet) ((octet)&0x7F)
+#define RTP_PAYLOAD_TYPE(octet) ((octet)&RTP_PT_FIELD)
-typedef struct __attribute__((packed)) Rtp
+typedef struct PACKED Rtp
{
uint8_t vpxcc;
uint8_t mpayload;
diff --git a/src/utility/Deque.h b/src/utility/Deque.h
index afc0eac..03848a0 100644
--- a/src/utility/Deque.h
+++ b/src/utility/Deque.h
@@ -1,5 +1,7 @@
#pragma once
+BEGIN_APPLEMIDI_NAMESPACE
+
template
class Deque {
// class iterator;
@@ -32,8 +34,6 @@ class Deque {
const T& at(size_t) const;
void clear();
-
-
// iterator begin();
// iterator end();
@@ -211,3 +211,5 @@ void Deque::clear()
_tail = -1;
_head = 0;
}
+
+END_APPLEMIDI_NAMESPACE
diff --git a/src/utility/endian.h b/src/utility/endian.h
index c853ade..b4de809 100644
--- a/src/utility/endian.h
+++ b/src/utility/endian.h
@@ -1,138 +1,78 @@
#pragma once
-#ifndef BYTE_ORDER
-
-#ifndef BIG_ENDIAN
-#define BIG_ENDIAN 4321
-#endif
-#ifndef LITTLE_ENDIAN
-#define LITTLE_ENDIAN 1234
-#endif
-
-#define TEST_LITTLE_ENDIAN (((union { unsigned x; unsigned char c; }){1}).c)
-
-#ifdef TEST_LITTLE_ENDIAN
-#define BYTE_ORDER LITTLE_ENDIAN
-#else
-#define BYTE_ORDER BIG_ENDIAN
-#endif
-
-#undef TEST_LITTLE_ENDIAN
-#endif
-
-#include
-
-#ifndef __bswap16
-#define __bswap16(x) ((uint16_t)((((uint16_t)(x)&0xff00) >> 8) | (((uint16_t)(x)&0x00ff) << 8)))
-#endif
-
-#ifndef __bswap32
-#define __bswap32(x) \
- ((uint32_t)((((uint32_t)(x)&0xff000000) >> 24) | (((uint32_t)(x)&0x00ff0000) >> 8) | \
- (((uint32_t)(x)&0x0000ff00) << 8) | (((uint32_t)(x)&0x000000ff) << 24)))
-#endif
-
-#ifndef __bswap64
-#define __bswap64(x) \
- ((uint64_t)((((uint64_t)(x)&0xff00000000000000ULL) >> 56) | \
- (((uint64_t)(x)&0x00ff000000000000ULL) >> 40) | \
- (((uint64_t)(x)&0x0000ff0000000000ULL) >> 24) | \
- (((uint64_t)(x)&0x000000ff00000000ULL) >> 8) | \
- (((uint64_t)(x)&0x00000000ff000000ULL) << 8) | \
- (((uint64_t)(x)&0x0000000000ff0000ULL) << 24) | \
- (((uint64_t)(x)&0x000000000000ff00ULL) << 40) | \
- (((uint64_t)(x)&0x00000000000000ffULL) << 56)))
-#endif
-
-union conversionBuffer
-{
- uint8_t value8;
- uint16_t value16;
- uint32_t value32;
- uint64_t value64;
- byte buffer[8];
-};
-
-#if BYTE_ORDER == LITTLE_ENDIAN
-
-// Definitions from musl libc
-#define htobe16(x) __bswap16(x)
-#define be16toh(x) __bswap16(x)
-#define betoh16(x) __bswap16(x)
-#define htobe32(x) __bswap32(x)
-#define be32toh(x) __bswap32(x)
-#define betoh32(x) __bswap32(x)
-#define htobe64(x) __bswap64(x)
-#define be64toh(x) __bswap64(x)
-#define betoh64(x) __bswap64(x)
-#define htole16(x) (uint16_t)(x)
-#define le16toh(x) (uint16_t)(x)
-#define letoh16(x) (uint16_t)(x)
-#define htole32(x) (uint32_t)(x)
-#define le32toh(x) (uint32_t)(x)
-#define letoh32(x) (uint32_t)(x)
-#define htole64(x) (uint64_t)(x)
-#define le64toh(x) (uint64_t)(x)
-#define letoh64(x) (uint64_t)(x)
-
-// From Apple Open Source Libc
-#define ntohs(x) __bswap16(x)
-#define htons(x) __bswap16(x)
-#define ntohl(x) __bswap32(x)
-#define htonl(x) __bswap32(x)
-#define ntohll(x) __bswap64(x)
-#define htonll(x) __bswap64(x)
-
-#define NTOHL(x) (x) = ntohl((uint32_t)x)
-#define NTOHS(x) (x) = ntohs((uint16_t)x)
-#define NTOHLL(x) (x) = ntohll((uint64_t)x)
-#define HTONL(x) (x) = htonl((uint32_t)x)
-#define HTONS(x) (x) = htons((uint16_t)x)
-#define HTONLL(x) (x) = htonll((uint64_t)x)
-
+#if !defined(_BYTE_ORDER)
+
+ #define _BIG_ENDIAN 4321
+ #define _LITTLE_ENDIAN 1234
+
+ #define TEST_LITTLE_ENDIAN (((union { unsigned x; unsigned char c; }){1}).c)
+
+ #ifdef TEST_LITTLE_ENDIAN
+ #define _BYTE_ORDER _LITTLE_ENDIAN
+ #else
+ #define _BYTE_ORDER _BIG_ENDIAN
+ #endif
+
+ #undef TEST_LITTLE_ENDIAN
+
+ #include
+
+ #ifdef __GNUC__
+ #define __bswap16(_x) __builtin_bswap16(_x)
+ #define __bswap32(_x) __builtin_bswap32(_x)
+ #define __bswap64(_x) __builtin_bswap64(_x)
+ #else /* __GNUC__ */
+
+ static __inline __uint16_t
+ __bswap16(__uint16_t _x)
+ {
+ return ((__uint16_t)((_x >> 8) | ((_x << 8) & 0xff00)));
+ }
+
+ static __inline __uint32_t
+ __bswap32(__uint32_t _x)
+ {
+ return ((__uint32_t)((_x >> 24) | ((_x >> 8) & 0xff00) |
+ ((_x << 8) & 0xff0000) | ((_x << 24) & 0xff000000)));
+ }
+
+ static __inline __uint64_t
+ __bswap64(__uint64_t _x)
+ {
+ return ((__uint64_t)((_x >> 56) | ((_x >> 40) & 0xff00) |
+ ((_x >> 24) & 0xff0000) | ((_x >> 8) & 0xff000000) |
+ ((_x << 8) & ((__uint64_t)0xff << 32)) |
+ ((_x << 24) & ((__uint64_t)0xff << 40)) |
+ ((_x << 40) & ((__uint64_t)0xff << 48)) | ((_x << 56))));
+ }
+ #endif /* !__GNUC__ */
+
+ #ifndef __machine_host_to_from_network_defined
+ #if _BYTE_ORDER == _LITTLE_ENDIAN
+ #define __ntohs(x) __bswap16(x)
+ #define __htons(x) __bswap16(x)
+ #define __ntohl(x) __bswap32(x)
+ #define __htonl(x) __bswap32(x)
+ #define __ntohll(x) __bswap64(x)
+ #define __htonll(x) __bswap64(x)
+ #else // BIG_ENDIAN
+ #define __ntohl(x) ((uint32_t)(x))
+ #define __ntohs(x) ((uint16_t)(x))
+ #define __htonl(x) ((uint32_t)(x))
+ #define __htons(x) ((uint16_t)(x))
+ #define __ntohll(x) ((uint64_t)(x))
+ #define __htonll(x) ((uint64_t)(x))
+ #endif
+ #endif /* __machine_host_to_from_network_defined */
+
+#endif /* _BYTE_ORDER */
+
+#ifndef __machine_host_to_from_network_defined
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+#define __ntohll(x) __bswap64(x)
+#define __htonll(x) __bswap64(x)
#else // BIG_ENDIAN
-
-// Definitions from musl libc
-
-#define htobe16(x) (uint16_t)(x)
-#define be16toh(x) (uint16_t)(x)
-#define betoh16(x) (uint16_t)(x)
-#define htobe32(x) (uint32_t)(x)
-#define be32toh(x) (uint32_t)(x)
-#define betoh32(x) (uint32_t)(x)
-#define htobe64(x) (uint64_t)(x)
-#define be64toh(x) (uint64_t)(x)
-#define betoh64(x) (uint64_t)(x)
-#define htole16(x) __bswap16(x)
-#define le16toh(x) __bswap16(x)
-#define letoh16(x) __bswap16(x)
-#define htole32(x) __bswap32(x)
-#define le32toh(x) __bswap32(x)
-#define letoh32(x) __bswap32(x)
-#define htole64(x) __bswap64(x)
-#define le64toh(x) __bswap64(x)
-#define letoh64(x) __bswap64(x)
-
-// From Apple Open Source libc
-#define ntohl(x) ((uint32_t)(x))
-#define ntohs(x) ((uint16_t)(x))
-#define htonl(x) ((uint32_t)(x))
-#define htons(x) ((uint16_t)(x))
-#define ntohll(x) ((uint64_t)(x))
-#define htonll(x) ((uint64_t)(x))
-
-#define NTOHL(x) (x)
-#define NTOHS(x) (x)
-#define NTOHLL(x) (x)
-#define HTONL(x) (x)
-#define HTONS(x) (x)
-#define HTONLL(x) (x)
-
-
-void aa(uint64_t value)
-{
- if ( value >= 10 )
- aa(value / 10);
-}
-
+#define __ntohll(x) ((uint64_t)(x))
+#define __htonll(x) ((uint64_t)(x))
#endif
+#endif /* __machine_host_to_from_network_defined */
diff --git a/test/Arduino.h b/test/Arduino.h
index afb5e0d..ccacad5 100644
--- a/test/Arduino.h
+++ b/test/Arduino.h
@@ -19,7 +19,7 @@ class _serial
void print(long a, int format = DEC) { std::cout << (format == DEC ? std::dec : std::hex) << a; };
void print(unsigned long a, int format = DEC) { std::cout << (format == DEC ? std::dec : std::hex) << a; };
void print(double a, int = 2) { std::cout << a; };
- void print(struct tm * timeinfo, const char * format = NULL) {};
+ void print(struct tm * timeinfo, const char * format = nullptr) {};
void print(IPAddress) {};
void println(const char a[]) { std::cout << a << "\n"; };
@@ -30,7 +30,7 @@ class _serial
void println(long a, int format = DEC) { std::cout << (format == DEC ? std::dec : std::hex) << a << "\n"; };
void println(unsigned long a, int format = DEC) { std::cout << (format == DEC ? std::dec : std::hex) << a << "\n"; };
void println(double a, int format = 2) { std::cout << a << "\n"; };
- void println(struct tm * timeinfo, const char * format = NULL) {};
+ void println(struct tm * timeinfo, const char * format = nullptr) {};
void println(IPAddress) {};
void println(void) { std::cout << "\n"; };
};
diff --git a/test/Ethernet.h b/test/Ethernet.h
index 59d9f3b..086caa2 100644
--- a/test/Ethernet.h
+++ b/test/Ethernet.h
@@ -3,6 +3,8 @@
#include "Arduino.h"
+#define DEFAULT_CONTROL_PORT 5004
+
class EthernetUDP
{
Deque _buffer;
@@ -13,22 +15,71 @@ class EthernetUDP
EthernetUDP()
{
_port = 0;
+
+
+
+
}
void begin(uint16_t port)
{
_port = port;
- if (port == 5004 && true)
+ if (port == DEFAULT_CONTROL_PORT && true)
{
// AppleMIDI messages
+ byte okSessionName[] = {
+ 0xff, 0xff, 0x49, 0x4e, 0x00, 0x00, 0x00, 0x02, 0x4e, 0x27, 0x95, 0x9e, 0x00, 0x00, 0xec, 0xf9,
+ 0x6c, 0x61, 0x70, 0x70, 0x69, 0x65, 0x6d, 0x63, 0x74, 0x6f, 0x70, 0x66, 0x61, 0x63, 0x65, 0x00
+ };
+
+ byte notOKSessionName[] = {
+ 0xff, 0xff, 0x49, 0x4e, 0x00, 0x00, 0x00, 0x02, 0xcc, 0x0f, 0x6c, 0x49, 0x00, 0x00, 0xa4, 0x9b,
+ 0x6c, 0x61, 0x70, 0x70, 0x69, 0x65, 0x6d, 0x63, 0x74, 0x6f, 0x70, 0x66, 0x61, 0x63, 0x65, 0x2f,
+ 0x46, 0x4c, 0x55, 0x49, 0x44, 0x20, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x28, 0x36, 0x34, 0x37,
+ 0x38, 0x29, 0x2d, 0x53, 0x79, 0x6e, 0x74, 0x68, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70,
+ 0x6f, 0x72, 0x74, 0x20, 0x28, 0x36, 0x34, 0x37, 0x38, 0x3a, 0x30, 0x29, 0x00
+ };
+
+ write(okSessionName, sizeof(okSessionName));
+
}
- if (port == 5005 && true)
+ if (port == (DEFAULT_CONTROL_PORT + 1) && true)
{
// rtp-MIDI and AppleMIDI messages
- byte aa[] = {
+ byte rr1[] = {
+ 0x80, 0x61, 0x19, 0x07, 0x00, 0x16,
+ 0xd5, 0x87, 0xf2, 0x2d, 0x56, 0xc3, 0xc0, 0x14, 0x92, 0x71, 0x00, 0x8f, 0xff, 0xff, 0xfe, 0x00,
+ 0x94, 0x64, 0x00, 0x81, 0x5a, 0x90, 0x64, 0x00, 0x02, 0x92, 0x64, 0x00, 0x23, 0x18, 0xf7, 0x00,
+ 0x19, 0x08, 0x89, 0xcd, 0x64, 0x7f, 0xf0, 0x78, 0xf1, 0x78, 0xf2, 0x78, 0xf3, 0x78, 0xf4, 0x78,
+ 0xf5, 0x78, 0xf6, 0x78, 0xf7, 0x78, 0xf0, 0x0f, 0x10, 0x13, 0x08, 0x86, 0xcd, 0x64, 0x7f, 0xf0,
+ 0x78, 0x71, 0x7f, 0xf2, 0x78, 0xf3, 0x78, 0xf4, 0x78, 0xf0, 0x0f, 0x20, 0x15, 0x08, 0x87, 0xcd,
+ 0x64, 0x7f, 0xf0, 0x78, 0xf1, 0x7f, 0xf4, 0x78, 0xf5, 0x7f, 0xf8, 0x78, 0xf9, 0x7f, 0xf0, 0x0f,
+ 0xd0, 0x07, 0x08, 0x81, 0xf1, 0xe0, 0x78
+ };
+
+ byte rr2[] = {
+ 0x80, 0x61, 0x19, 0x14, 0x00, 0x16,
+ 0xf2, 0xd3, 0xf2, 0x2d, 0x56, 0xc3, 0x4b, 0x92, 0x71, 0x00, 0x8f, 0xff, 0xff, 0xfe, 0x00, 0x94,
+ 0x6a, 0x00, 0x23, 0x19, 0x04, 0x00, 0x19, 0x08, 0x89, 0xcd, 0x6a, 0x7f, 0xf0, 0x78, 0xf1, 0x78,
+ 0xf2, 0x78, 0xf3, 0x78, 0xf4, 0x78, 0xf5, 0x78, 0xf6, 0x78, 0xf7, 0x78, 0x1f, 0xc0, 0x10, 0x13,
+ 0x08, 0x86, 0xcd, 0x6a, 0x7f, 0xf0, 0x78, 0x71, 0x7f, 0xf2, 0x78, 0xf3, 0x78, 0xf4, 0x78, 0x1f,
+ 0xc0, 0x20, 0x15, 0x08, 0x87, 0xcd, 0x6a, 0x7f, 0xf0, 0x78, 0xf1, 0x7f, 0xf4, 0x78, 0xf5, 0x7f,
+ 0xf8, 0x78, 0xf9, 0x7f, 0x1f, 0xc0, 0xd0, 0x07, 0x08, 0x81, 0xf1, 0xe0, 0x78
+ };
+
+ byte rr[] = {
+ 0x80, 0x61, 0x19, 0x05, 0x00, 0x16,
+ 0xcf, 0xe0, 0xf2, 0x2d, 0x56, 0xc3, 0x4c, 0x94, 0x63, 0x00, 0x81, 0x6a, 0x90, 0x63, 0x00, 0x02,
+ 0x92, 0x63, 0x00, 0x23, 0x18, 0xf5, 0x00, 0x19, 0x08, 0x89, 0xcd, 0x63, 0x7f, 0xf0, 0x78, 0xf1,
+ 0x78, 0xf2, 0x78, 0xf3, 0x78, 0xf4, 0x78, 0xf5, 0x78, 0xf6, 0x78, 0xf7, 0x78, 0xe0, 0x1f, 0x10,
+ 0x11, 0x08, 0x85, 0xcd, 0x63, 0x7f, 0xf0, 0x78, 0xf2, 0x78, 0xf3, 0x78, 0xf4, 0x78, 0xe0, 0x1f,
+ 0x20, 0x15, 0x08, 0x87, 0xcd, 0x63, 0x7f, 0xf0, 0x78, 0xf1, 0x7f, 0xf4, 0x78, 0xf5, 0x7f, 0xf8,
+ 0x78, 0xf9, 0x7f, 0xe0, 0x1f, 0xd0, 0x07, 0x08, 0x81, 0xf1, 0xe0, 0x78 };
+
+ byte aa[] = {
0x80, 0x61, 0xbf, 0xa2, 0x12, 0xb, 0x5a, 0xf7, 0xaa, 0x34, 0x96, 0x4a,
0xc0, 0x2b,
0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x0, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x0,
@@ -277,10 +328,8 @@ class EthernetUDP
byte slecht[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
- // write(noteOnOff, sizeof(noteOnOff));
+ // write(rr2, sizeof(rr2));
}
-
-
if (port == 5005 && true)
{
@@ -309,12 +358,25 @@ class EthernetUDP
return _buffer.size();
};
+ int read()
+ {
+ if (_buffer.size() == 0)
+ return -1;
+
+ byte value = _buffer.front();
+ _buffer.pop_front();
+
+ return value;
+ }
+
size_t read(byte* buffer, size_t size)
{
size = min(size, _buffer.size());
- for (size_t i = 0; i < size; i++)
- buffer[i] = _buffer.pop_front();
+ for (size_t i = 0; i < size; i++) {
+ buffer[i] = _buffer.front();
+ _buffer.pop_front();
+ }
return size;
};
@@ -331,72 +393,13 @@ class EthernetUDP
};
void endPacket() { };
+
void flush()
{
- if (_port == 5004)
- {
- if (_buffer[0] == 0xff && _buffer[1] == 0xff && _buffer[2] == 'I' &&_buffer[3] == 'N')
- {
- _buffer.clear();
-
-
- byte u[] = {
- 0xff, 0xff,
- 0x4f, 0x4b,
- 0x00, 0x00, 0x00, 0x02,
- 0xb7, 0x06, 0x20, 0x30,
- 0xda, 0x8d, 0xc5, 0x8a,
- 0x4d, 0x61, 0x63, 0x62, 0x6f, 0x6f, 0x6b, 0x20, 0x50, 0x72, 0x6f, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x53, 0x61, 0x6e, 0x64, 0x72, 0x61, 0x20, 0x56, 0x65, 0x72, 0x62, 0x65, 0x6b, 0x65, 0x6e, 0x20, 0x28, 0x32, 0x29, 0x00 };
-
-
-
-
-
- byte r[] = { 0xff, 0xff,
- 0x4f, 0x4b,
- 0x00, 0x0, 0x00, 0x02,
- 0xb7, 0x06, 0x20, 0x30,
- 0xda, 0x8d, 0xc5, 0x8a,
- 0x53, 0x65, 0x73, 0x73, 0x69, 0x6, 0x6e, 0x31, 0x2d, 0x42, 0x00 };
- write(u, sizeof(u));
- }
- }
- if (_port == 5005)
- {
- if (_buffer[0] == 0xff && _buffer[1] == 0xff && _buffer[2] == 'I' &&_buffer[3] == 'N')
- {
- _buffer.clear();
- byte r[] = { 0xff, 0xff,
- 0x4f, 0x4b,
- 0x00, 0x0, 0x00, 0x02,
- 0xb7, 0x06, 0x20, 0x30,
- 0xda, 0x8d, 0xc5, 0x8a,
- 0x53, 0x65, 0x73, 0x73, 0x69, 0x6, 0x6e, 0x31, 0x2d, 0x42, 0x00 };
- write(r, sizeof(r));
- }
- else if (_buffer[0] == 0xff && _buffer[1] == 0xff && _buffer[2] == 'C' &&_buffer[3] == 'K')
- {
- if (_buffer[8] == 0x00)
- {
- _buffer.clear();
- byte r[] = { 0xff, 0xff,
- 0x43, 0x4b,
- 0xda, 0x8d, 0xc5, 0x8a,
- 0x01,
- 0x65, 0x73, 0x73,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x34,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x6c, 0x83,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- write(r, sizeof(r));
- }
- else
- _buffer.clear();
- }
- }
};
void stop() { _buffer.clear(); };
- uint32_t remoteIP() { return 1; }
- uint16_t remotePort() { return _port; }
+ IPAddress remoteIP() { return IPAddress(127,0,0,1); }
+ uint16_t remotePort() { return _port; }
};
diff --git a/test/IPAddress.h b/test/IPAddress.h
index bcb1d0e..1f403a7 100644
--- a/test/IPAddress.h
+++ b/test/IPAddress.h
@@ -9,4 +9,9 @@ class IPAddress
IPAddress(uint32_t address) { }
IPAddress(int address) { }
IPAddress(const uint8_t *address) {};
+
+ bool operator==(const IPAddress&) const { return true; }
+ bool operator!=(const IPAddress&) const { return true; }
};
+
+const IPAddress INADDR_NONE(0, 0, 0, 0);
\ No newline at end of file
diff --git a/test/NoteOn.cpp b/test/NoteOn.cpp
index a4ab27b..57d8191 100644
--- a/test/NoteOn.cpp
+++ b/test/NoteOn.cpp
@@ -4,7 +4,6 @@
#define APPLEMIDI_INITIATOR
#include "AppleMIDI.h"
-USING_NAMESPACE_APPLEMIDI
unsigned long t0 = millis();
bool isConnected = false;
@@ -33,50 +32,39 @@ APPLEMIDI_CREATE_DEFAULTSESSION_INSTANCE();
// -----------------------------------------------------------------------------
// rtpMIDI session. Device connected
// -----------------------------------------------------------------------------
-void OnAppleMidiConnected(const ssrc_t & ssrc, const char* name) {
+void OnAppleMidiConnected(const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, const char* name) {
isConnected = true;
- N_DEBUG_PRINT(F("Connected to session "));
- N_DEBUG_PRINTLN(name);
+ AM_DBG(F("Connected to session"), ssrc, name);
}
// -----------------------------------------------------------------------------
// rtpMIDI session. Device disconnected
// -----------------------------------------------------------------------------
-void OnAppleMidiDisconnected(const ssrc_t & ssrc) {
+void OnAppleMidiDisconnected(const APPLEMIDI_NAMESPACE::ssrc_t & ssrc) {
isConnected = false;
- N_DEBUG_PRINTLN(F("Disconnected"));
+ AM_DBG(F("Disconnected"), ssrc);
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
-void OnAppleMidiByte(const ssrc_t & ssrc, byte data) {
- N_DEBUG_PRINT(F("MIDI: "));
- N_DEBUG_PRINTLN(data);
+void OnAppleMidiByte(const APPLEMIDI_NAMESPACE::ssrc_t & ssrc, byte data) {
+ AM_DBG(F("MIDI: "));
+ AM_DBG(data);
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
static void OnMidiNoteOn(byte channel, byte note, byte velocity) {
- N_DEBUG_PRINT(F("Incoming NoteOn from channel: "));
- N_DEBUG_PRINT(channel);
- N_DEBUG_PRINT(F(", note: "));
- N_DEBUG_PRINT(note);
- N_DEBUG_PRINT(F(", velocity: "));
- N_DEBUG_PRINTLN(velocity);
+ AM_DBG(F("in\tNote on"), note, " Velocity", velocity, "\t", channel);
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
static void OnMidiNoteOff(byte channel, byte note, byte velocity) {
- N_DEBUG_PRINT(F("Incoming NoteOff from channel: "));
- N_DEBUG_PRINT(channel);
- N_DEBUG_PRINT(F(", note: "));
- N_DEBUG_PRINT(note);
- N_DEBUG_PRINT(F(", velocity: "));
- N_DEBUG_PRINTLN(velocity);
+ AM_DBG(F("in\tNote off"), note, " Velocity", velocity, "\t", channel);
}
// -----------------------------------------------------------------------------
@@ -95,33 +83,31 @@ char getSysExStatus(const byte* data, uint16_t length)
}
static void OnMidiSystemExclusive(byte* array, unsigned size) {
- N_DEBUG_PRINT(F("Incoming SysEx: "));
- N_DEBUG_PRINT(getSysExStatus(array, size));
+ AM_DBG(F("Incoming SysEx: "));
+ AM_DBG(getSysExStatus(array, size));
unsigned i = 0;
for (; i < size - 1; i++)
{
- N_DEBUG_PRINT(F(" 0x"));
- N_DEBUG_PRINT(array[i], HEX);
+ AM_DBG(F(" 0x"));
+ AM_DBG(array[i], HEX);
}
- N_DEBUG_PRINT(F(" 0x"));
- N_DEBUG_PRINT(array[i], HEX);
- N_DEBUG_PRINTLN();
+ AM_DBG(F(" 0x"));
+ AM_DBG(array[i], HEX);
+ AM_DBG();
}
void begin()
{
- V_DEBUG_PRINTLN(F("OK, now make sure you an rtpMIDI session that is Enabled"));
- V_DEBUG_PRINT(F("Add device named Arduino with Host/Port "));
- // V_DEBUG_PRINT(Ethernet.localIP());
- V_DEBUG_PRINTLN(F(":5004"));
- V_DEBUG_PRINTLN(F("Then press the Connect button"));
- V_DEBUG_PRINTLN(F("Then open a MIDI listener (eg MIDI-OX) and monitor incoming notes"));
-
- MIDI.begin(1);
+ AM_DBG(F("OK, now make sure you an rtpMIDI session that is Enabled"));
+ AM_DBG(F("Add device named Arduino with Host"), Ethernet.localIP(), "Port", AppleMIDI.getPort(), "(Name", AppleMIDI.getName(), ")");
+ AM_DBG(F("Select and then press the Connect button"));
+ AM_DBG(F("Then open a MIDI listener and monitor incoming notes"));
+
+ MIDI.begin(MIDI_CHANNEL_OMNI);
AppleMIDI.setHandleConnected(OnAppleMidiConnected);
AppleMIDI.setHandleDisconnected(OnAppleMidiDisconnected);
- AppleMIDI.setHandleReceivedMidi(OnAppleMidiByte);
+ // AppleMIDI.setHandleReceivedMidi(OnAppleMidiByte);
MIDI.setHandleNoteOn(OnMidiNoteOn);
MIDI.setHandleNoteOff(OnMidiNoteOff);
@@ -137,10 +123,9 @@ void loop()
// send a note every second
// (dont cáll delay(1000) as it will stall the pipeline)
- if (isConnected && (millis() - t0) > 10000)
+ if ((isConnected) && (millis() - t0) > 10000)
{
t0 = millis();
- // Serial.print(F(".");
byte note = random(1, 127);
byte velocity = 55;
diff --git a/test/TestParser.vcxproj b/test/TestParser.vcxproj
index 05d5db0..6ff98d0 100644
--- a/test/TestParser.vcxproj
+++ b/test/TestParser.vcxproj
@@ -76,6 +76,7 @@
Disabled
true
true
+ C:\Users\bart\Documents\Arduino\libraries\Arduino-AppleMIDI-Library\test;C:\Users\bart\Documents\Arduino\libraries\arduino_midi_library-master\src;C:\Users\bart\Documents\Arduino\libraries\Arduino-AppleMIDI-Library\src;%(AdditionalIncludeDirectories)
Console
@@ -87,8 +88,7 @@
Disabled
true
true
-
-
+ C:\Users\bart\Documents\Arduino\libraries\Arduino-AppleMIDI-Library\src;C:\Users\bart\Documents\Arduino\libraries\arduino_midi_library-master\src;%(AdditionalIncludeDirectories)
Default
@@ -131,6 +131,7 @@
+
diff --git a/test/TestParser.vcxproj.filters b/test/TestParser.vcxproj.filters
index 58698e7..d47dcab 100644
--- a/test/TestParser.vcxproj.filters
+++ b/test/TestParser.vcxproj.filters
@@ -22,5 +22,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/test/rtpMidi.xcodeproj/xcshareddata/xcschemes/rtpMidi.xcscheme b/test/rtpMidi.xcodeproj/xcshareddata/xcschemes/rtpMidi.xcscheme
index 7b96d37..cdfd28e 100644
--- a/test/rtpMidi.xcodeproj/xcshareddata/xcschemes/rtpMidi.xcscheme
+++ b/test/rtpMidi.xcodeproj/xcshareddata/xcschemes/rtpMidi.xcscheme
@@ -1,6 +1,6 @@