From acd1d95001dbe857fcf7d9c2aee655021009c23d Mon Sep 17 00:00:00 2001 From: lathoub <4082369+lathoub@users.noreply.github.com> Date: Sun, 27 Sep 2020 13:28:45 +0200 Subject: [PATCH] renamed ESP32 -> MIDI_ESP32 --- examples/MidiBle/MidiBle.ino | 45 ++--- src/hardware/MIDI_ArduinoBLE.h | 198 ++++++++++++++++++ src/hardware/{ESP32.h => MIDI_ESP32.h} | 24 +-- src/hardware/MIDI_ESP32_NimBLE.h | 267 +++++++++++++++++++++++++ src/hardware/{nRF52.h => MIDI_nRF52.h} | 14 +- 5 files changed, 503 insertions(+), 45 deletions(-) create mode 100644 src/hardware/MIDI_ArduinoBLE.h rename src/hardware/{ESP32.h => MIDI_ESP32.h} (91%) create mode 100644 src/hardware/MIDI_ESP32_NimBLE.h rename src/hardware/{nRF52.h => MIDI_nRF52.h} (90%) diff --git a/examples/MidiBle/MidiBle.ino b/examples/MidiBle/MidiBle.ino index b1bc86d..d15f588 100644 --- a/examples/MidiBle/MidiBle.ino +++ b/examples/MidiBle/MidiBle.ino @@ -1,7 +1,8 @@ #include -#include -//#include -//#include +#include +//#include +//#include +//#include BLEMIDI_CREATE_DEFAULT_INSTANCE() @@ -13,19 +14,21 @@ bool isConnected = false; // ----------------------------------------------------------------------------- void setup() { - Serial.begin(115200); + Serial.begin(9600); // initialize serial communication while (!Serial); - Serial.println("Booting"); - + MIDI.begin(); + Serial.println("booting!"); + + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LOW); + BLEMIDI.setHandleConnected(OnConnected); BLEMIDI.setHandleDisconnected(OnDisconnected); MIDI.setHandleNoteOn(OnNoteOn); MIDI.setHandleNoteOff(OnNoteOff); - - Serial.println(F("Ready")); } // ----------------------------------------------------------------------------- @@ -39,8 +42,8 @@ void loop() { t0 = millis(); - MIDI.sendNoteOn(60, 127, 1); // note 60, velocity 127 on channel 1 - MIDI.sendNoteOff(60, 0, 1); + // MIDI.sendNoteOn (60, 100, 1); // note 60, velocity 127 on channel 1 + // MIDI.sendNoteOff(60, 0, 1); } } @@ -52,40 +55,30 @@ void loop() // Device connected // ----------------------------------------------------------------------------- void OnConnected() { - Serial.println(F("Connected")); isConnected = true; + digitalWrite(LED_BUILTIN, HIGH); + Serial.println("connected!"); } // ----------------------------------------------------------------------------- // Device disconnected // ----------------------------------------------------------------------------- void OnDisconnected() { - Serial.println(F("Disconnected")); isConnected = false; + digitalWrite(LED_BUILTIN, LOW); + Serial.println("disconnected!"); } // ----------------------------------------------------------------------------- // Received note on // ----------------------------------------------------------------------------- void OnNoteOn(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.print(velocity); - Serial.println(); + Serial.println("note on"); } // ----------------------------------------------------------------------------- // Received note off // ----------------------------------------------------------------------------- void OnNoteOff(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.print(velocity); - Serial.println(); + Serial.println("note off"); } diff --git a/src/hardware/MIDI_ArduinoBLE.h b/src/hardware/MIDI_ArduinoBLE.h new file mode 100644 index 0000000..7bf0b54 --- /dev/null +++ b/src/hardware/MIDI_ArduinoBLE.h @@ -0,0 +1,198 @@ +#pragma once + +#include + +BLEService midiService(SERVICE_UUID); +BLEStringCharacteristic midiChar(CHARACTERISTIC_UUID, // standard 16-bit characteristic UUID + BLERead | BLEWrite | BLENotify | BLEWriteWithoutResponse, 16); // remote clients will be able to get notifications if this characteristic changes + +#define BLE_POLLING + +BEGIN_BLEMIDI_NAMESPACE + +class BLEMIDI +{ +private: + static BLEMIDITransport* _bleMidiTransport; + static BLEDevice* _central; + +// byte mRxBuffer[Settings::MaxBufferSize]; + +public: + BLEMIDI() + { + } + + bool begin(const char*, BLEMIDITransport*); + + void write(uint8_t* buffer, size_t length) + { + // TODO: test length + ((BLECharacteristic)midiChar).writeValue(buffer, length); + } + + size_t available(uint8_t* buffer, const size_t index, const size_t max) + { + #ifdef BLE_POLLING + poll(); + + if (midiChar.written()) { + // auto buffer = midiChar.value(); + auto length = midiChar.valueLength(); + + if (length > 0) { + // TODO: test length + memcpy(buffer + index, midiChar.value().c_str(), length); + return length; + } + } +#endif +#ifdef BLE_EVENTS +/ BLE.poll(); + return 0; +#endif + } + + void read() + { + } + + /* + The general form of a MIDI message follows: + n-byte MIDI Message + Byte 0 MIDI message Status byte, Bit 7 is Set to 1. + Bytes 1 to n-1 MIDI message Data bytes, if n > 1. Bit 7 is Set to 0 + There are two types of MIDI messages that can appear in a single packet: full MIDI messages and + Running Status MIDI messages. Each is encoded differently. + A full MIDI message is simply the MIDI message with the Status byte included. + A Running Status MIDI message is a MIDI message with the Status byte omitted. Running Status + MIDI messages may only be placed in the data stream if the following criteria are met: + 1. The original MIDI message is 2 bytes or greater and is not a System Common or System + Real-Time message. + 2. The omitted Status byte matches the most recently preceding full MIDI message’s Status + byte within the same BLE packet. + In addition, the following rules apply with respect to Running Status: + 1. A Running Status MIDI message is allowed within the packet after at least one full MIDI + message. + 2. Every MIDI Status byte must be preceded by a timestamp byte. Running Status MIDI + messages may be preceded by a timestamp byte. If a Running Status MIDI message is not + preceded by a timestamp byte, the timestamp byte of the most recently preceding message + in the same packet is used. + 3. System Common and System Real-Time messages do not cancel Running Status if + interspersed between Running Status MIDI messages. However, a timestamp byte must + precede the Running Status MIDI message that follows. + 4. The end of a BLE packet does cancel Running Status. + In the MIDI 1.0 protocol, System Real-Time messages can be sent at any time and may be + inserted anywhere in a MIDI data stream, including between Status and Data bytes of any other + MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved + from other messages – except for System Exclusive messages. + */ + static void receive(const uint8_t* buffer, size_t length) + { + Serial.print("receive: "); + for (int i = 0; i < length; i++) { + Serial.print(" 0x"); + Serial.print(buffer[i], HEX); + } + Serial.println(""); + } + + bool poll() + { + BLEDevice central = BLE.central(); + if (!central) { + if (_central) { + BLEMIDI::blePeripheralDisconnectHandler(*_central); + _central = nullptr; + } + return false; + } + + if (!central.connected()) { + return false; + } + + if (nullptr == _central) { + BLEMIDI::blePeripheralConnectHandler(central); + _central = ¢ral; + } + else { + if (*_central != central) { + BLEMIDI::blePeripheralDisconnectHandler(*_central); + BLEMIDI::blePeripheralConnectHandler(central); + _central = ¢ral; + } + } + + return true; + } + + static void blePeripheralConnectHandler(BLEDevice central) + { + _central = ¢ral; + if (_bleMidiTransport->_connectedCallback) + _bleMidiTransport->_connectedCallback(); + } + + static void blePeripheralDisconnectHandler(BLEDevice central) + { + if (_bleMidiTransport->_disconnectedCallback) + _bleMidiTransport->_disconnectedCallback(); + _central = nullptr; + } + + static void characteristicWritten(BLEDevice central, BLECharacteristic characteristic) { + auto buffer = characteristic.value(); + auto length = characteristic.valueLength(); + + if (length > 0) + receive(buffer, length); + } +}; + +BLEMIDITransport* BLEMIDI::_bleMidiTransport = nullptr; +BLEDevice* BLEMIDI::_central = nullptr; + +bool BLEMIDI::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) +{ + _bleMidiTransport = bleMidiTransport; + + if (!BLE.begin()) + return false; + + BLE.setLocalName(deviceName); + + BLE.setAdvertisedService(midiService); + midiService.addCharacteristic(midiChar); + BLE.addService(midiService); + +#ifdef BLE_EVENTS + // assign event handlers for connected, disconnected to peripheral + BLE.setEventHandler(BLEConnected, BLEMIDI::blePeripheralConnectHandler); + BLE.setEventHandler(BLEDisconnected, BLEMIDI::blePeripheralDisconnectHandler); + + midiChar.setEventHandler(BLEWritten, characteristicWritten); +#endif + + /* Start advertising BLE. It will start continuously transmitting BLE + advertising packets and will be visible to remote BLE central devices + until it receives a new connection */ + + // start advertising + BLE.advertise(); + + return true; +} + + /*! \brief Create an instance for nRF52 named + */ +#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ +BLEMIDI_NAMESPACE::BLEMIDITransport BLE##Name(DeviceName); \ +MIDI_NAMESPACE::MidiInterface, MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDITransport &)BLE##Name); + + /*! \brief Create a default instance for nRF52 named BLE-MIDI + */ +#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ +BLEMIDI_CREATE_INSTANCE("BLE-MIDI", MIDI) + +END_BLEMIDI_NAMESPACE diff --git a/src/hardware/ESP32.h b/src/hardware/MIDI_ESP32.h similarity index 91% rename from src/hardware/ESP32.h rename to src/hardware/MIDI_ESP32.h index 0895194..591eff2 100644 --- a/src/hardware/ESP32.h +++ b/src/hardware/MIDI_ESP32.h @@ -8,24 +8,24 @@ BEGIN_BLEMIDI_NAMESPACE -class BLEMIDI_ESP32 +class BLEMIDI { private: BLEServer* _server = nullptr; BLEAdvertising* _advertising = nullptr; BLECharacteristic* _characteristic = nullptr; - BLEMIDITransport* _bleMidiTransport = nullptr; + BLEMIDITransport* _bleMidiTransport = nullptr; protected: QueueHandle_t mRxQueue; public: - BLEMIDI_ESP32() + BLEMIDI() { } - bool begin(const char*, BLEMIDITransport*); + bool begin(const char*, BLEMIDITransport*); void write(uint8_t* buffer, size_t length) { @@ -78,7 +78,7 @@ public: // Decode first packet -- SHALL be "Full MIDI message" lPtr = 2; //Start at first MIDI status -- SHALL be "MIDI status" - //While statement contains incrementing pointers and breaks when buffer size exceeded. + // While statement contains incrementing pointers and breaks when buffer size exceeded. while (true) { lastStatus = buffer[lPtr]; @@ -160,12 +160,12 @@ public: class MyServerCallbacks: public BLEServerCallbacks { public: - MyServerCallbacks(BLEMIDI_ESP32* bluetoothEsp32) + MyServerCallbacks(BLEMIDI* bluetoothEsp32) : _bluetoothEsp32(bluetoothEsp32) { } protected: - BLEMIDI_ESP32* _bluetoothEsp32 = nullptr; + BLEMIDI* _bluetoothEsp32 = nullptr; void onConnect(BLEServer*) { if (_bluetoothEsp32) @@ -180,12 +180,12 @@ protected: class MyCharacteristicCallbacks: public BLECharacteristicCallbacks { public: - MyCharacteristicCallbacks(BLEMIDI_ESP32* bluetoothEsp32) + MyCharacteristicCallbacks(BLEMIDI* bluetoothEsp32) : _bluetoothEsp32(bluetoothEsp32 ) { } protected: - BLEMIDI_ESP32* _bluetoothEsp32 = nullptr; + BLEMIDI* _bluetoothEsp32 = nullptr; void onWrite(BLECharacteristic * characteristic) { std::string rxValue = characteristic->getValue(); @@ -195,7 +195,7 @@ protected: } }; -bool BLEMIDI_ESP32::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) +bool BLEMIDI::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) { _bleMidiTransport = bleMidiTransport; @@ -242,8 +242,8 @@ bool BLEMIDI_ESP32::begin(const char* deviceName, BLEMIDITransport */ #define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ -BLEMIDI_NAMESPACE::BLEMIDITransport BLE##Name(DeviceName); \ -MIDI_NAMESPACE::MidiInterface, MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDITransport &)BLE##Name); +BLEMIDI_NAMESPACE::BLEMIDITransport BLE##Name(DeviceName); \ +MIDI_NAMESPACE::MidiInterface, MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDITransport &)BLE##Name); /*! \brief Create a default instance for ESP32 named BLE-MIDI */ diff --git a/src/hardware/MIDI_ESP32_NimBLE.h b/src/hardware/MIDI_ESP32_NimBLE.h new file mode 100644 index 0000000..ad474b3 --- /dev/null +++ b/src/hardware/MIDI_ESP32_NimBLE.h @@ -0,0 +1,267 @@ +#pragma once + +// Headers for ESP32 NimBLE +#include + +BEGIN_BLEMIDI_NAMESPACE + +class BLEMIDI +{ +private: + BLEServer* _server = nullptr; + BLEAdvertising* _advertising = nullptr; + BLECharacteristic* _characteristic = nullptr; + + BLEMIDITransport* _bleMidiTransport = nullptr; + +protected: + QueueHandle_t mRxQueue; + +public: + BLEMIDI() + { + } + + bool begin(const char*, BLEMIDITransport*); + + void write(uint8_t* buffer, size_t length) + { + _characteristic->setValue(buffer, length); + _characteristic->notify(); + } + + size_t available(uint8_t* buffer, size_t index, const size_t max) + { + size_t length = uxQueueMessagesWaiting(mRxQueue); + if (0 == length) return 0; + + Serial.print("nimBLE available: "); + + while (xQueueReceive(mRxQueue, buffer + index++, 0)) + { + Serial.print(" 0x"); + Serial.print(buffer[index], HEX); + index++; + } + Serial.println(""); + + if (length > 0) + { + Serial.print("index: "); + Serial.println(length); + } + + return length; + } + + /* + The general form of a MIDI message follows: + n-byte MIDI Message + Byte 0 MIDI message Status byte, Bit 7 is Set to 1. + Bytes 1 to n-1 MIDI message Data bytes, if n > 1. Bit 7 is Set to 0 + There are two types of MIDI messages that can appear in a single packet: full MIDI messages and + Running Status MIDI messages. Each is encoded differently. + A full MIDI message is simply the MIDI message with the Status byte included. + A Running Status MIDI message is a MIDI message with the Status byte omitted. Running Status + MIDI messages may only be placed in the data stream if the following criteria are met: + 1. The original MIDI message is 2 bytes or greater and is not a System Common or System + Real-Time message. + 2. The omitted Status byte matches the most recently preceding full MIDI message’s Status + byte within the same BLE packet. + In addition, the following rules apply with respect to Running Status: + 1. A Running Status MIDI message is allowed within the packet after at least one full MIDI + message. + 2. Every MIDI Status byte must be preceded by a timestamp byte. Running Status MIDI + messages may be preceded by a timestamp byte. If a Running Status MIDI message is not + preceded by a timestamp byte, the timestamp byte of the most recently preceding message + in the same packet is used. + 3. System Common and System Real-Time messages do not cancel Running Status if + interspersed between Running Status MIDI messages. However, a timestamp byte must + precede the Running Status MIDI message that follows. + 4. The end of a BLE packet does cancel Running Status. + In the MIDI 1.0 protocol, System Real-Time messages can be sent at any time and may be + inserted anywhere in a MIDI data stream, including between Status and Data bytes of any other + MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved + from other messages – except for System Exclusive messages. + */ + void receive(uint8_t* buffer, size_t length) + { + // Pointers used to search through payload. + uint8_t lPtr = 0; + uint8_t rPtr = 0; + // lastStatus used to capture runningStatus + uint8_t lastStatus; + // Decode first packet -- SHALL be "Full MIDI message" + lPtr = 2; //Start at first MIDI status -- SHALL be "MIDI status" + + //While statement contains incrementing pointers and breaks when buffer size exceeded. + while (true) + { + lastStatus = buffer[lPtr]; + + if( (buffer[lPtr] < 0x80)) + return; // Status message not present, bail + + // Point to next non-data byte + rPtr = lPtr; + while( (buffer[rPtr + 1] < 0x80) && (rPtr < (length - 1)) ) + rPtr++; + if (buffer[rPtr + 1] == 0xF7) rPtr++; + + // look at l and r pointers and decode by size. + if( rPtr - lPtr < 1 ) { + // Time code or system + xQueueSend(mRxQueue, &lastStatus, portMAX_DELAY); + } else if( rPtr - lPtr < 2 ) { + xQueueSend(mRxQueue, &lastStatus, portMAX_DELAY); + xQueueSend(mRxQueue, &buffer[lPtr + 1], portMAX_DELAY); + } else if( rPtr - lPtr < 3 ) { + xQueueSend(mRxQueue, &lastStatus, portMAX_DELAY); + xQueueSend(mRxQueue, &buffer[lPtr + 1], portMAX_DELAY); + xQueueSend(mRxQueue, &buffer[lPtr + 2], portMAX_DELAY); + } else { + // Too much data + // If not System Common or System Real-Time, send it as running status + switch(buffer[lPtr] & 0xF0) + { + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: + for (auto i = lPtr; i < rPtr; i = i + 2) + { + xQueueSend(mRxQueue, &lastStatus, portMAX_DELAY); + xQueueSend(mRxQueue, &buffer[i + 1], portMAX_DELAY); + xQueueSend(mRxQueue, &buffer[i + 2], portMAX_DELAY); + } + break; + case 0xC0: + case 0xD0: + for (auto i = lPtr; i < rPtr; i = i + 1) + { + xQueueSend(mRxQueue, &lastStatus, portMAX_DELAY); + xQueueSend(mRxQueue, &buffer[i + 1], portMAX_DELAY); + } + break; + case 0xF0: + xQueueSend(mRxQueue, &buffer[lPtr], portMAX_DELAY); + for (auto i = lPtr; i < rPtr; i++) + xQueueSend(mRxQueue, &buffer[i + 1], portMAX_DELAY); + break; + default: + break; + } + } + + // Point to next status + lPtr = rPtr + 2; + if(lPtr >= length) + return; //end of packet + } + } + + void connected() + { + if (_bleMidiTransport->_connectedCallback) + _bleMidiTransport->_connectedCallback(); + } + + void disconnected() + { + if (_bleMidiTransport->_disconnectedCallback) + _bleMidiTransport->_disconnectedCallback(); + } +}; + +class MyServerCallbacks: public BLEServerCallbacks { +public: + MyServerCallbacks(BLEMIDI* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32) { + } + +protected: + BLEMIDI* _bluetoothEsp32 = nullptr; + + void onConnect(BLEServer*) { + if (_bluetoothEsp32) + _bluetoothEsp32->connected(); + }; + + void onDisconnect(BLEServer*) { + if (_bluetoothEsp32) + _bluetoothEsp32->disconnected(); + } +}; + +class MyCharacteristicCallbacks: public BLECharacteristicCallbacks { +public: + MyCharacteristicCallbacks(BLEMIDI* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32 ) { + } + +protected: + BLEMIDI* _bluetoothEsp32 = nullptr; + + void onWrite(BLECharacteristic * characteristic) { + std::string rxValue = characteristic->getValue(); + if (rxValue.length() > 0) { + _bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length()); + } + } +}; + +bool BLEMIDI::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) +{ + _bleMidiTransport = bleMidiTransport; + + BLEDevice::init(deviceName); + + // To communicate between the 2 cores. + // Core_0 runs here, core_1 runs the BLE stack + mRxQueue = xQueueCreate(64, sizeof(uint8_t)); // TODO Settings::MaxBufferSize + + _server = BLEDevice::createServer(); + _server->setCallbacks(new MyServerCallbacks(this)); + + // Create the BLE Service + auto service = _server->createService(BLEUUID(SERVICE_UUID)); + + // Create a BLE Characteristic + _characteristic = service->createCharacteristic( + BLEUUID(CHARACTERISTIC_UUID), + NIMBLE_PROPERTY::READ | + NIMBLE_PROPERTY::WRITE | + NIMBLE_PROPERTY::NOTIFY | + NIMBLE_PROPERTY::WRITE_NR + ); + + _characteristic->setCallbacks(new MyCharacteristicCallbacks(this)); + // Start the service + service->start(); + + auto advertisementData = BLEAdvertisementData(); + advertisementData.setFlags(0x04); + advertisementData.setCompleteServices(BLEUUID(SERVICE_UUID)); + advertisementData.setName(deviceName); + + // Start advertising + _advertising = _server->getAdvertising(); + _advertising->setAdvertisementData(advertisementData); + _advertising->start(); + + return true; +} + + /*! \brief Create an instance for ESP32 named + */ +#define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ +BLEMIDI_NAMESPACE::BLEMIDITransport BLE##Name(DeviceName); \ +MIDI_NAMESPACE::MidiInterface, MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDITransport &)BLE##Name); + + /*! \brief Create a default instance for ESP32 named BLE-MIDI + */ +#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ +BLEMIDI_CREATE_INSTANCE("BLE-MIDI", MIDI) + +END_BLEMIDI_NAMESPACE diff --git a/src/hardware/nRF52.h b/src/hardware/MIDI_nRF52.h similarity index 90% rename from src/hardware/nRF52.h rename to src/hardware/MIDI_nRF52.h index 68116f3..42dbc14 100644 --- a/src/hardware/nRF52.h +++ b/src/hardware/MIDI_nRF52.h @@ -6,18 +6,18 @@ BEGIN_BLEMIDI_NAMESPACE -class BLEMIDI_nRF52 +class BLEMIDI { private: - BLEMIDITransport* _bleMidiTransport = nullptr; + BLEMIDITransport* _bleMidiTransport = nullptr; public: - BLEMIDI_nRF52() + BLEMIDI() { } - bool begin(const char*, BLEMIDITransport*); + bool begin(const char*, BLEMIDITransport*); void write(uint8_t* buffer, size_t length) { @@ -70,7 +70,7 @@ public: } }; -bool BLEMIDI_nRF52::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) +bool BLEMIDI::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) { _bleMidiTransport = bleMidiTransport; @@ -125,8 +125,8 @@ bool BLEMIDI_nRF52::begin(const char* deviceName, BLEMIDITransport */ #define BLEMIDI_CREATE_INSTANCE(DeviceName, Name) \ -BLEMIDI_NAMESPACE::BLEMIDITransport BLE##Name(DeviceName); \ -MIDI_NAMESPACE::MidiInterface, MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDITransport &)BLE##Name); +BLEMIDI_NAMESPACE::BLEMIDITransport BLE##Name(DeviceName); \ +MIDI_NAMESPACE::MidiInterface, MySettings> Name((BLEMIDI_NAMESPACE::BLEMIDITransport &)BLE##Name); /*! \brief Create a default instance for nRF52 named BLE-MIDI */