From b22cc18fc098a15833b3db095717770f47e5c8a1 Mon Sep 17 00:00:00 2001 From: lathoub <4082369+lathoub@users.noreply.github.com> Date: Sat, 3 Oct 2020 04:11:56 +0200 Subject: [PATCH] from feat/2.0.0 --- .development | 9 + .gitignore | 2 + README.md | 41 +- .../Esp32_NoteOnOffEverySec.ino | 81 --- examples/MidiBle/MidiBle.ino | 75 +++ keywords.txt | 9 +- library.properties | 9 +- src/BLE-MIDI.cpp | 1 + src/BLE-MIDI.h | 279 ++++++++++ src/BLE-MIDI_Config.h | 15 + src/BLE-MIDI_Defs.h | 17 + ...eMidi_Namespace.h => BLE-MIDI_Namespace.h} | 0 src/BLE-MIDI_Settings.h | 12 + src/BleClient_esp32.h | 524 ------------------ src/BleMidi.cpp | 1 - src/BleMidi.h | 16 - src/Ble_esp32.h | 395 ------------- src/common | 1 - src/hardware/BLE-MIDI_Client_ESP32.h | 95 ++++ src/hardware/BLEMIDI_ArduinoBLE.h | 225 ++++++++ src/hardware/BLEMIDI_ESP32.h | 162 ++++++ src/hardware/BLEMIDI_ESP32_NimBLE.h | 158 ++++++ src/hardware/BLEMIDI_nRF52.h | 121 ++++ src/utility/.DS_Store | Bin 6148 -> 0 bytes src/utility/BleMidi_Defs.h | 30 - src/utility/BleMidi_Settings.h | 29 - 26 files changed, 1216 insertions(+), 1091 deletions(-) create mode 100644 .development delete mode 100644 examples/Esp32_NoteOnOffEverySec/Esp32_NoteOnOffEverySec.ino create mode 100644 examples/MidiBle/MidiBle.ino create mode 100644 src/BLE-MIDI.cpp create mode 100644 src/BLE-MIDI.h create mode 100644 src/BLE-MIDI_Config.h create mode 100644 src/BLE-MIDI_Defs.h rename src/{utility/BleMidi_Namespace.h => BLE-MIDI_Namespace.h} (100%) create mode 100644 src/BLE-MIDI_Settings.h delete mode 100644 src/BleClient_esp32.h delete mode 100644 src/BleMidi.cpp delete mode 100644 src/BleMidi.h delete mode 100644 src/Ble_esp32.h delete mode 160000 src/common create mode 100644 src/hardware/BLE-MIDI_Client_ESP32.h create mode 100644 src/hardware/BLEMIDI_ArduinoBLE.h create mode 100644 src/hardware/BLEMIDI_ESP32.h create mode 100644 src/hardware/BLEMIDI_ESP32_NimBLE.h create mode 100644 src/hardware/BLEMIDI_nRF52.h delete mode 100644 src/utility/.DS_Store delete mode 100644 src/utility/BleMidi_Defs.h delete mode 100644 src/utility/BleMidi_Settings.h diff --git a/.development b/.development new file mode 100644 index 0000000..f7f5e81 --- /dev/null +++ b/.development @@ -0,0 +1,9 @@ +.DS_Store +examples/.DS_Store +src/.DS_Store +test/.vs +test/Debug +examples/ESP32_NoteOnOffEverySec/config.h +src/.vscode +test/x64 +.development diff --git a/.gitignore b/.gitignore index ca33092..89c3ae7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .DS_Store examples/.DS_Store src/.DS_Store +.vscode/settings.json +.vscode/c_cpp_properties.json diff --git a/README.md b/README.md index 4be54e0..77fa357 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,38 @@ -# Arduino-BLE-MIDI (DEPRECATED, use branch feat/2.0.0) -MIDI over Bluetooth Low Energy (BLE-MIDI) 1.0 for Arduino +# Experimental + +# Arduino BLE-MIDI Transport +This library implements the BLE-MIDI transport layer for the [FortySevenEffects Arduino MIDI Library](https://github.com/FortySevenEffects/arduino_midi_library) -Call interface similar to [FortySevenEffects/MIDI](https://github.com/FortySevenEffects/arduino_midi_library) and [lathoub/AppleMIDI](https://github.com/lathoub/Arduino-AppleMIDI-Library) +## Installation +This library depends on the [Arduino MIDI Library](https://github.com/FortySevenEffects/arduino_midi_library). -Inspired by Pedalino https://github.com/alf45tar/Pedalino by alf45tar +When installing this library from the Arduino IDE, the dependency be downloaded and installed in the same directory as this library. (Thanks to the `depends` clause in `library.properties`) -# Supported devices -ESP32 +When manually installing this library, you have to manually download [Arduino MIDI Library](https://github.com/FortySevenEffects/arduino_midi_library) from github and install it in the same directory as this library - without this additional install, this library will not be able to compile. + +## Usage +### Basic / Default +```cpp +#include +#include +... +BLEMIDI_CREATE_DEFAULT_ESP32_INSTANCE(); +... +void setup() +{ + MIDI.begin(1); +... +void loop() +{ + MIDI.read(); +``` +will create a instance named `bleMIDI` and listens to incoming MIDI on channel 1. + +## Tested boards/modules +- ESP32 + +## Other Transport protocols: +The libraries below the same calling mechanism (API), making it easy to interchange the transport layer. +- [Arduino AppleMIDI Transport](https://github.com/lathoub/Arduino-AppleMIDI-Library) +- [Arduino USB-MIDI Transport](https://github.com/lathoub/USB-MIDI) +- [Arduino ipMIDI Transport](https://github.com/lathoub/Arduino-ipMIDI) diff --git a/examples/Esp32_NoteOnOffEverySec/Esp32_NoteOnOffEverySec.ino b/examples/Esp32_NoteOnOffEverySec/Esp32_NoteOnOffEverySec.ino deleted file mode 100644 index cb4724c..0000000 --- a/examples/Esp32_NoteOnOffEverySec/Esp32_NoteOnOffEverySec.ino +++ /dev/null @@ -1,81 +0,0 @@ -#include "BleMidi.h" - -BLEMIDI_CREATE_INSTANCE(bm); - -// ----------------------------------------------------------------------------- -// -// ----------------------------------------------------------------------------- -void setup() -{ - // Serial communications and wait for port to open: - Serial.begin(115200); - while (!Serial) { - ; // wait for serial port to connect. Needed for Leonardo only - } - - bm.begin("hehe"); - - bm.onConnected(OnBleMidiConnected); - bm.onDisconnected(OnBleMidiDisconnected); - - bm.setHandleNoteOn(OnBleMidiNoteOn); - bm.setHandleNoteOff(OnBleMidiNoteOff); - - Serial.println(F("looping")); -} - -// ----------------------------------------------------------------------------- -// -// ----------------------------------------------------------------------------- -void loop() -{ - bm.sendNoteOn(60, 127, 1); // note 60, velocity 127 on channel 1 - bm.sendNoteOff(60, 127, 1); - - delay(1000); -} - -// ==================================================================================== -// Event handlers for incoming MIDI messages -// ==================================================================================== - -// ----------------------------------------------------------------------------- -// rtpMIDI session. Device connected -// ----------------------------------------------------------------------------- -void OnBleMidiConnected() { - Serial.println(F("Connected")); -} - -// ----------------------------------------------------------------------------- -// rtpMIDI session. Device disconnected -// ----------------------------------------------------------------------------- -void OnBleMidiDisconnected() { - Serial.println(F("Disconnected")); -} - -// ----------------------------------------------------------------------------- -// received note on -// ----------------------------------------------------------------------------- -void OnBleMidiNoteOn(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(); -} - - -// ----------------------------------------------------------------------------- -// received note off -// ----------------------------------------------------------------------------- -void OnBleMidiNoteOff(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(); -} diff --git a/examples/MidiBle/MidiBle.ino b/examples/MidiBle/MidiBle.ino new file mode 100644 index 0000000..579c6dd --- /dev/null +++ b/examples/MidiBle/MidiBle.ino @@ -0,0 +1,75 @@ +#include + +#include +//#include +//#include +//#include + +BLEMIDI_CREATE_DEFAULT_INSTANCE() + +unsigned long t0 = millis(); +bool isConnected = false; + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +void setup() +{ + MIDI.begin(); + + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LOW); + + BLEMIDI.setHandleConnected(OnConnected); + BLEMIDI.setHandleDisconnected(OnDisconnected); + + MIDI.setHandleNoteOn(OnNoteOn); + MIDI.setHandleNoteOff(OnNoteOff); +} + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +void loop() +{ + MIDI.read(); + + if (isConnected && (millis() - t0) > 1000) + { + t0 = millis(); + + MIDI.sendNoteOn (60, 100, 1); // note 60, velocity 127 on channel 1 + } +} + +// ==================================================================================== +// Event handlers for incoming MIDI messages +// ==================================================================================== + +// ----------------------------------------------------------------------------- +// Device connected +// ----------------------------------------------------------------------------- +void OnConnected() { + isConnected = true; + digitalWrite(LED_BUILTIN, HIGH); +} + +// ----------------------------------------------------------------------------- +// Device disconnected +// ----------------------------------------------------------------------------- +void OnDisconnected() { + isConnected = false; + digitalWrite(LED_BUILTIN, LOW); +} + +// ----------------------------------------------------------------------------- +// Received note on +// ----------------------------------------------------------------------------- +void OnNoteOn(byte channel, byte note, byte velocity) { +} + +// ----------------------------------------------------------------------------- +// Received note off +// ----------------------------------------------------------------------------- +void OnNoteOff(byte channel, byte note, byte velocity) { +} diff --git a/keywords.txt b/keywords.txt index 3aaab47..ed9ca64 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,12 +1,12 @@ ####################################### -# Syntax Coloring Map for AppleMIDI +# Syntax Coloring Map for BLEMIDI ####################################### ####################################### # Datatypes (KEYWORD1) ####################################### -BLEMIDI KEYWORD1 -BLEMIDI.h KEYWORD1 +BLE-MIDI.h KEYWORD1 +BLEMIDI KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -40,10 +40,11 @@ tick KEYWORD2 ####################################### # Instances (KEYWORD3) ####################################### +BLEMIDI KEYWORD3 ####################################### # Constants (LITERAL1) ####################################### # Namespace, considering it as a literal -blemidi LITERAL1 +BLEMIDI LITERAL1 diff --git a/library.properties b/library.properties index 5b4113c..ba2aa2e 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,11 @@ name=BLE-MIDI -version=0.0.1 +version=2.1.0 author=lathoub maintainer=lathoub sentence=BLE-MIDI I/Os for Arduino -paragraph=MIDI over Bluetooth Low Energy +paragraph=MIDI over Bluetooth Low Energy (BLE-MIDI) 1.0 for Arduino category=Communication url=https://github.com/lathoub/Arduino-BLE-MIDI -architectures=* -includes=BLEMIDI.h +architectures=esp32,samd,megaavr,mbed,nrf52 +includes=BLE-MIDI.h +depends=MIDI Library, NimBLE-Arduino, ArduinoBLE diff --git a/src/BLE-MIDI.cpp b/src/BLE-MIDI.cpp new file mode 100644 index 0000000..258e5f5 --- /dev/null +++ b/src/BLE-MIDI.cpp @@ -0,0 +1 @@ +#include "BLE-MIDI.h" \ No newline at end of file diff --git a/src/BLE-MIDI.h b/src/BLE-MIDI.h new file mode 100644 index 0000000..a9a9581 --- /dev/null +++ b/src/BLE-MIDI.h @@ -0,0 +1,279 @@ +/*! + * @file BLEMIDI.h + */ + +#pragma once + +#include +using namespace MIDI_NAMESPACE; + +#include "BLE-MIDI_Settings.h" +#include "BLE-MIDI_Defs.h" +#include "BLE-MIDI_Namespace.h" + +BEGIN_BLEMIDI_NAMESPACE + +template +class BLEMIDITransport +{ + typedef _Settings Settings; + +private: + byte mRxBuffer[Settings::MaxBufferSize]; + unsigned mRxIndex = 0; + + byte mTxBuffer[Settings::MaxBufferSize]; + unsigned mTxIndex = 0; + + char mDeviceName[24]; + +private: + T mBleClass; + +public: + BLEMIDITransport(const char* deviceName) + { + strncpy(mDeviceName, deviceName, 24); + + mRxIndex = 0; + mTxIndex = 0; + } + +public: + static const bool thruActivated = false; + + void begin() + { + mBleClass.begin(mDeviceName, this); + } + + bool beginTransmission(MidiType) + { + getMidiTimestamp(&mTxBuffer[0], &mTxBuffer[1]); + mTxIndex = 2; + + return true; + } + + void write(byte inData) + { + // check for size! SysEx!!! + if (false) + { + // should only happen from SysEx! + // if we approach the end of the buffer, chop-up in segments until + // we reach F7 (end of SysEx) + } + + mTxBuffer[mTxIndex++] = inData; + } + + void endTransmission() + { + mBleClass.write(mTxBuffer, mTxIndex); + mTxIndex = 0; + } + + byte read() + { + return mRxBuffer[--mRxIndex]; + } + + unsigned available() + { + uint8_t byte; + auto success = mBleClass.available(&byte); + if (!success) return mRxIndex; + + mRxBuffer[mRxIndex++] = byte; + + return mRxIndex; + } + +protected: + /* + The first byte of all BLE packets must be a header byte. This is followed by timestamp bytes and MIDI messages. + + Header Byte + bit 7 Set to 1. + bit 6 Set to 0. (Reserved for future use) + bits 5-0 timestampHigh:Most significant 6 bits of timestamp information. + The header byte contains the topmost 6 bits of timing information for MIDI events in the BLE + packet. The remaining 7 bits of timing information for individual MIDI messages encoded in a + packet is expressed by timestamp bytes. + + Timestamp Byte + bit 7 Set to 1. + bits 6-0 timestampLow: Least Significant 7 bits of timestamp information. + + The 13-bit timestamp for the first MIDI message in a packet is calculated using 6 bits from the + header byte and 7 bits from the timestamp byte. + + Timestamps are 13-bit values in milliseconds, and therefore the maximum value is 8,191 ms. + Timestamps must be issued by the sender in a monotonically increasing fashion. + timestampHigh is initially set using the lower 6 bits from the header byte while the timestampLow is + formed of the lower 7 bits from the timestamp byte. Should the timestamp value of a subsequent + MIDI message in the same packet overflow/wrap (i.e., the timestampLow is smaller than a + preceding timestampLow), the receiver is responsible for tracking this by incrementing the + timestampHigh by one (the incremented value is not transmitted, only understood as a result of the + overflow condition). + + In practice, the time difference between MIDI messages in the same BLE packet should not span + more than twice the connection interval. As a result, a maximum of one overflow/wrap may occur + per BLE packet. + + Timestamps are in the sender’s clock domain and are not allowed to be scheduled in the future. + Correlation between the receiver’s clock and the received timestamps must be performed to + ensure accurate rendering of MIDI messages, and is not addressed in this document. + */ + + /* + Calculating a Timestamp + To calculate the timestamp, the built-in millis() is used. + The BLE standard only specifies 13 bits worth of millisecond data though, + so it’s bitwise anded with 0x1FFF for an ever repeating cycle of 13 bits. + This is done right after a MIDI message is detected. It’s split into a 6 upper bits, 7 lower bits, + and the MSB of both bytes are set to indicate that this is a header byte. + Both bytes are placed into the first two position of an array in preparation for a MIDI message. + */ + static void getMidiTimestamp (uint8_t *header, uint8_t *timestamp) + { + auto currentTimeStamp = millis() & 0x01FFF; + + *header = ((currentTimeStamp >> 7) & 0x3F) | 0x80; // 6 bits plus MSB + *timestamp = (currentTimeStamp & 0x7F) | 0x80; // 7 bits plus MSB + } + + static void setMidiTimestamp (uint8_t header, uint8_t *timestamp) + { + } + +public: + // callbacks + void(*_connectedCallback)() = nullptr; + void(*_disconnectedCallback)() = nullptr; + +public: + void setHandleConnected(void(*fptr)()) { + _connectedCallback = fptr; + } + + void setHandleDisconnected(void(*fptr)()) { + _disconnectedCallback = fptr; + } + + /* + 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(byte* buffer, size_t length) + { + // Pointers used to search through payload. + byte lPtr = 0; + byte rPtr = 0; + // lastStatus used to capture runningStatus + byte 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 + mBleClass.add(lastStatus); + } else if( rPtr - lPtr < 2 ) { + mBleClass.add(lastStatus); + mBleClass.add(buffer[lPtr + 1]); + } else if( rPtr - lPtr < 3 ) { + mBleClass.add(lastStatus); + mBleClass.add(buffer[lPtr + 1]); + mBleClass.add(buffer[lPtr + 2]); + } 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) + { + mBleClass.add(lastStatus); + mBleClass.add(buffer[i + 1]); + mBleClass.add(buffer[i + 2]); + } + break; + case 0xC0: + case 0xD0: + for (auto i = lPtr; i < rPtr; i = i + 1) + { + mBleClass.add(lastStatus); + mBleClass.add(buffer[i + 1]); + } + break; + case 0xF0: + mBleClass.add(buffer[lPtr]); + for (auto i = lPtr; i < rPtr; i++) + mBleClass.add(buffer[i + 1]); + break; + default: + break; + } + } + + // Point to next status + lPtr = rPtr + 2; + if(lPtr >= length) + return; //end of packet + } + } + +}; + +END_BLEMIDI_NAMESPACE + +struct MySettings : public MIDI_NAMESPACE::DefaultSettings +{ + static const bool Use1ByteParsing = false; +}; diff --git a/src/BLE-MIDI_Config.h b/src/BLE-MIDI_Config.h new file mode 100644 index 0000000..5d83206 --- /dev/null +++ b/src/BLE-MIDI_Config.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef ARDUINO_ARCH_ESP32 +#include +//#include +#endif + +#include +#ifdef BLEMIDI_nRF52 +#endif + + +#ifdef ArduinoBLE +#include +#endif diff --git a/src/BLE-MIDI_Defs.h b/src/BLE-MIDI_Defs.h new file mode 100644 index 0000000..abf34e0 --- /dev/null +++ b/src/BLE-MIDI_Defs.h @@ -0,0 +1,17 @@ +#pragma once + +#include "BLE-MIDI_Namespace.h" + +// As specified in +// Specification for MIDI over Bluetooth Low Energy (BLE-MIDI) +// Version 1.0a, NOvember 1, 2015 +// 3. BLE Service and Characteristics Definitions +#define SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700" +#define CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3" + +#if ARDUINO +#include +#else +#include +typedef uint8_t byte; +#endif diff --git a/src/utility/BleMidi_Namespace.h b/src/BLE-MIDI_Namespace.h similarity index 100% rename from src/utility/BleMidi_Namespace.h rename to src/BLE-MIDI_Namespace.h diff --git a/src/BLE-MIDI_Settings.h b/src/BLE-MIDI_Settings.h new file mode 100644 index 0000000..ce60366 --- /dev/null +++ b/src/BLE-MIDI_Settings.h @@ -0,0 +1,12 @@ +#pragma once + +#include "BLE-MIDI_Namespace.h" + +BEGIN_BLEMIDI_NAMESPACE + +struct DefaultSettings +{ + static const size_t MaxBufferSize = 64; +}; + +END_BLEMIDI_NAMESPACE diff --git a/src/BleClient_esp32.h b/src/BleClient_esp32.h deleted file mode 100644 index e1b9cee..0000000 --- a/src/BleClient_esp32.h +++ /dev/null @@ -1,524 +0,0 @@ -#pragma once - -// Headers for ESP32 BLE -#include -#include -#include -#include -//#include "BLEScan.h" -#include "Arduino.h" - -#include "common/midiCommon.h" -using namespace Midi; - -BEGIN_BLEMIDI_NAMESPACE - -static bool isDataReceived = false; -static bool doScan = false; -static BLEScan* pBLEScan; - -static void notifyCallback( - BLERemoteCharacteristic* pBLERemoteCharacteristic, - uint8_t* pData, - size_t length, - bool isNotify) -{ - isDataReceived = true; -} - -static void scanCompleteCB(BLEScanResults scanResults) - { - scanResults.dump(); - if (doScan) - { - pBLEScan->start(10, scanCompleteCB); - } - } - -class MyClientCallbacks; - -class BleMidiInterfaceClient : public MidiCommonInterface -{ -protected: - // ESP32 - BLEClient * pClient; - //BLEAdvertising * _advertising; - //BLECharacteristic *_characteristic; - - uint8_t _midiPacket[5]; // outgoing - -public: - - void * operator new(size_t size) - { - void * p = heap_caps_malloc(size, MALLOC_CAP_SPIRAM); - return p; - } - // callbacks - void(*_connectedCallback)() = NULL; - void(*_disconnectedCallback)() = NULL; - char _deviceName[32]; - bool _doConnect; - BLEAdvertisedDevice * _advertising; - BLERemoteService* pRemoteService; - BLERemoteCharacteristic* pRemoteCharacteristic; - bool _connected; - -protected: - inline static void getMidiTimestamp (uint8_t *header, uint8_t *timestamp) - { - /* - The first byte of all BLE packets must be a header byte. This is followed by timestamp bytes and MIDI messages. - Header Byte - bit 7 Set to 1. - bit 6 Set to 0. (Reserved for future use) - bits 5-0 timestampHigh:Most significant 6 bits of timestamp information. - The header byte contains the topmost 6 bits of timing information for MIDI events in the BLE - packet. The remaining 7 bits of timing information for individual MIDI messages encoded in a - packet is expressed by timestamp bytes. - Timestamp Byte - bit 7 Set to 1. - bits 6-0 timestampLow: Least Significant 7 bits of timestamp information. - The 13-bit timestamp for the first MIDI message in a packet is calculated using 6 bits from the - header byte and 7 bits from the timestamp byte. - Timestamps are 13-bit values in milliseconds, and therefore the maximum value is 8,191 ms. - Timestamps must be issued by the sender in a monotonically increasing fashion. - timestampHigh is initially set using the lower 6 bits from the header byte while the timestampLow is - formed of the lower 7 bits from the timestamp byte. Should the timestamp value of a subsequent - MIDI message in the same packet overflow/wrap (i.e., the timestampLow is smaller than a - preceding timestampLow), the receiver is responsible for tracking this by incrementing the - timestampHigh by one (the incremented value is not transmitted, only understood as a result of the - overflow condition). - In practice, the time difference between MIDI messages in the same BLE packet should not span - more than twice the connection interval. As a result, a maximum of one overflow/wrap may occur - per BLE packet. - Timestamps are in the sender’s clock domain and are not allowed to be scheduled in the future. - Correlation between the receiver’s clock and the received timestamps must be performed to - ensure accurate rendering of MIDI messages, and is not addressed in this document. - */ - /* - Calculating a Timestamp - To calculate the timestamp, the built-in millis() is used. - The BLE standard only specifies 13 bits worth of millisecond data though, - so it’s bitwise anded with 0x1FFF for an ever repeating cycle of 13 bits. - This is done right after a MIDI message is detected. It’s split into a 6 upper bits, 7 lower bits, - and the MSB of both bytes are set to indicate that this is a header byte. - Both bytes are placed into the first two position of an array in preparation for a MIDI message. - */ - auto currentTimeStamp = millis() & 0x01FFF; - - *header = ((currentTimeStamp >> 7) & 0x3F) | 0x80; // 6 bits plus MSB - *timestamp = (currentTimeStamp & 0x7F) | 0x80; // 7 bits plus MSB - } - - // serialize towards hardware - - void write(DataByte b1) - { - getMidiTimestamp(&_midiPacket[0], &_midiPacket[1]); - - _midiPacket[2] = b1; - - // TODO: quid running status - - if(pRemoteCharacteristic && pRemoteCharacteristic->canWrite()) - { - pRemoteCharacteristic->writeValue(_midiPacket, 3); - } - }; - - void write(DataByte b1, DataByte b2) - { - getMidiTimestamp(&_midiPacket[0], &_midiPacket[1]); - - _midiPacket[2] = b1; - _midiPacket[3] = b2; - - // TODO: quid running status - if(pRemoteCharacteristic && pRemoteCharacteristic->canWrite()) - { - pRemoteCharacteristic->writeValue(_midiPacket, 4); - } - }; - - void write(DataByte b1, DataByte b2, DataByte b3) - { - getMidiTimestamp(&_midiPacket[0], &_midiPacket[1]); - - _midiPacket[2] = b1; - _midiPacket[3] = b2; - _midiPacket[4] = b3; - - // TODO: quid running status - if(pRemoteCharacteristic && pRemoteCharacteristic->canWrite()) - { - pRemoteCharacteristic->writeValue(_midiPacket, 5); - } - }; - - bool connectToServer() - { - // Connect to the remote BLE Server. - pClient->connect(_advertising); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private) - - // Obtain a reference to the service we are after in the remote BLE server. - pRemoteService = pClient->getService(BLEUUID(SERVICE_UUID)); - if (pRemoteService == nullptr) - { - pClient->disconnect(); - return false; - } - - // Obtain a reference to the characteristic in the service of the remote BLE server. - pRemoteCharacteristic = pRemoteService->getCharacteristic(BLEUUID(CHARACTERISTIC_UUID)); - if (pRemoteCharacteristic == nullptr) - { - pClient->disconnect(); - return false; - } - - return true; - }; - - -public: - BleMidiInterfaceClient() - { - } - - ~BleMidiInterfaceClient() - { - } - - // TODO why must these functions be inline?? - - inline bool begin(const char* deviceName); - - inline void read() - { - // If the flag "doConnect" is true then we have scanned for and found the desired - // BLE Server with which we wish to connect. Now we connect to it. Once we are - // connected we set the connected flag to be true. - if (_doConnect == true) - { - if (connectToServer()) - { - // ("We are now connected to the BLE Server."); - if (nullptr != pRemoteCharacteristic) - { - pRemoteCharacteristic->registerForNotify(notifyCallback); - } - } - else - { - // ("We have failed to connect to the server; there is nothin more we will do."); - doScan = true; - pBLEScan->start(10, scanCompleteCB); - - } - - _doConnect = false; - } - - - if (false == _connected) - { - return; - } - - - if (nullptr == pRemoteCharacteristic) - { - return; - } - // Read the value of the characteristic. - //if(pRemoteCharacteristic->canRead()) - if (isDataReceived) - { - std::string rxValue = pRemoteCharacteristic->readValue(); - if (rxValue.length() > 0) - { - this->receive((uint8_t *)(rxValue.c_str()), rxValue.length()); - } - - isDataReceived = false; - } - } - - inline void sendMIDI(StatusByte, DataByte data1 = 0, DataByte data2 = 0); - inline void receive(uint8_t *buffer, uint8_t bufferSize); - - void onConnected(void(*fptr)()) - { - _connectedCallback = fptr; - } - void onDisconnected(void(*fptr)()) - { - _disconnectedCallback = fptr; - } -}; - -class MyClientCallbacks: public BLEClientCallbacks { -public: - MyClientCallbacks(BleMidiInterfaceClient* BleMidiInterfaceClient) { - _BleMidiInterfaceClient = BleMidiInterfaceClient; - } -protected: - BleMidiInterfaceClient* _BleMidiInterfaceClient; - - void onConnect(BLEClient* client) - { - if (_BleMidiInterfaceClient->_connectedCallback) - { - _BleMidiInterfaceClient->_connectedCallback(); - } - _BleMidiInterfaceClient->_connected = true; - }; - - void onDisconnect(BLEClient* client) - { - if (_BleMidiInterfaceClient->_disconnectedCallback) - { - _BleMidiInterfaceClient->_disconnectedCallback(); - } - - _BleMidiInterfaceClient->_doConnect = false; - _BleMidiInterfaceClient->pRemoteService = nullptr; - _BleMidiInterfaceClient->pRemoteCharacteristic = nullptr; - doScan = true; - pBLEScan->start(10, scanCompleteCB); - } - -}; - - -class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { - /** - * Called for each advertising BLE server. - */ - public: - - MyAdvertisedDeviceCallbacks(BleMidiInterfaceClient* BleMidiInterfaceClient) : BLEAdvertisedDeviceCallbacks() - { - _BleMidiInterfaceClient = BleMidiInterfaceClient; - } - - void onResult(BLEAdvertisedDevice advertisedDevice) { - - if (strstr(advertisedDevice.toString().c_str(), _BleMidiInterfaceClient->_deviceName) == NULL) - { - // Check that the device matches our host name - return; - } - - // We have found a device, let us now see if it contains the service we are looking for. - if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) - { - BLEDevice::getScan()->stop(); - _BleMidiInterfaceClient->_advertising = new BLEAdvertisedDevice(advertisedDevice); - _BleMidiInterfaceClient->_doConnect = true; - doScan = false; - - } // Found our server - } // onResult - -protected: - BleMidiInterfaceClient* _BleMidiInterfaceClient; - -}; // MyAdvertisedDeviceCallbacks - -bool BleMidiInterfaceClient::begin(const char* deviceName) -{ - BLEDevice::init(deviceName); - - strncpy(_deviceName, deviceName, 32); - - pClient = BLEDevice::createClient(); - pClient->setClientCallbacks(new MyClientCallbacks(this)); - - // Retrieve a Scanner and set the callback we want to use to be informed when we - // have detected a new device. Specify that we want active scanning and start the - // scan to run for 5 seconds. - pBLEScan = BLEDevice::getScan(); - pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(this)); - pBLEScan->setInterval(1349); - pBLEScan->setWindow(449); - pBLEScan->setActiveScan(true); - doScan = true; - pBLEScan->start(10, scanCompleteCB); - - - - return true; -} - - -void BleMidiInterfaceClient::sendMIDI(StatusByte status, DataByte data1, DataByte data2) -{ - MidiType type = getTypeFromStatusByte(status); - Channel channel = getChannelFromStatusByte(status); - - switch (type) { - case NoteOff: - if (_noteOffCallback) _noteOffCallback(channel, data1, data2); - break; - case NoteOn: - if (_noteOnCallback) _noteOnCallback(channel, data1, data2); - break; - case AfterTouchPoly: - if (_afterTouchPolyCallback) _afterTouchPolyCallback(channel, data1, data2); - break; - case ControlChange: - if (_controlChangeCallback) _controlChangeCallback(channel, data1, data2); - break; - case ProgramChange: - if (_programChangeCallback) _programChangeCallback(channel, data1); - break; - case AfterTouchChannel: - if (_afterTouchChannelCallback) _afterTouchChannelCallback(channel, data1); - break; - case PitchBend: - if (_pitchBendCallback) { - int value = (int) ((data1 & 0x7f) | ((data2 & 0x7f) << 7)) + MIDI_PITCHBEND_MIN; - _pitchBendCallback(channel, value); - } - break; - - case SystemExclusive: - break; - - case TimeCodeQuarterFrame: - if (_timeCodeQuarterFrameCallback) _timeCodeQuarterFrameCallback(data1); - break; - case SongPosition: - if (_songPositionCallback) { - unsigned short value = unsigned((data1 & 0x7f) | ((data2 & 0x7f) << 7)); - _songPositionCallback(value); - } - break; - case SongSelect: - if (_songSelectCallback) _songSelectCallback(data1); - break; - case TuneRequest: - if (_tuneRequestCallback) _tuneRequestCallback(); - break; - - case Clock: - if (_clockCallback) _clockCallback(); - break; - case Tick: - break; - case Start: - if (_startCallback) _startCallback(); - break; - case Continue: - if (_continueCallback) _continueCallback(); - break; - case Stop: - if (_stopCallback) _stopCallback(); - break; - case ActiveSensing: - if (_activeSensingCallback) _activeSensingCallback(); - break; - case SystemReset: - if (_resetCallback) _resetCallback(); - break; - } -} - -void BleMidiInterfaceClient::receive(uint8_t *buffer, uint8_t bufferSize) -{ - /* - 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. - */ - - //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(1){ - lastStatus = buffer[lPtr]; - if( (buffer[lPtr] < 0x80) ){ - //Status message not present, bail - return; - } - //Point to next non-data byte - rPtr = lPtr; - while( (buffer[rPtr + 1] < 0x80)&&(rPtr < (bufferSize - 1)) ){ - rPtr++; - } - //look at l and r pointers and decode by size. - if( rPtr - lPtr < 1 ){ - //Time code or system - sendMIDI(lastStatus); - } else if( rPtr - lPtr < 2 ) { - sendMIDI(lastStatus, buffer[lPtr + 1]); - } else if( rPtr - lPtr < 3 ) { - sendMIDI(lastStatus, buffer[lPtr + 1], buffer[lPtr + 2]); - } else { - //Too much data - //If not System Common or System Real-Time, send it as running status - switch( buffer[lPtr] & 0xF0 ) - { - case NoteOff: - case NoteOn: - case AfterTouchPoly: - case ControlChange: - case PitchBend: - for(int i = lPtr; i < rPtr; i = i + 2) - sendMIDI(lastStatus, buffer[i + 1], buffer[i + 2]); - break; - case ProgramChange: - case AfterTouchChannel: - for(int i = lPtr; i < rPtr; i = i + 1) - sendMIDI(lastStatus, buffer[i + 1]); - break; - default: - break; - } - } - //Point to next status - lPtr = rPtr + 2; - if(lPtr >= bufferSize){ - //end of packet - return; - } - } - -} - -END_BLEMIDI_NAMESPACE diff --git a/src/BleMidi.cpp b/src/BleMidi.cpp deleted file mode 100644 index 0026d24..0000000 --- a/src/BleMidi.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "BleMidi.h" diff --git a/src/BleMidi.h b/src/BleMidi.h deleted file mode 100644 index 3d561b3..0000000 --- a/src/BleMidi.h +++ /dev/null @@ -1,16 +0,0 @@ -/*! - * @file BleMidi.h - */ - -#pragma once - -#include "utility/BleMidi_Settings.h" -#include "utility/BleMidi_Defs.h" - -#define SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700" -#define CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3" - -#if defined(ESP32) -#include "Ble_esp32.h" -#include "BleClient_esp32.h" -#endif diff --git a/src/Ble_esp32.h b/src/Ble_esp32.h deleted file mode 100644 index cccdbcd..0000000 --- a/src/Ble_esp32.h +++ /dev/null @@ -1,395 +0,0 @@ -#pragma once - -// Headers for ESP32 BLE -#include -#include -#include -#include - -#include "common/midiCommon.h" -using namespace Midi; - -BEGIN_BLEMIDI_NAMESPACE - -class BleMidiInterface : public MidiCommonInterface -{ -protected: - // ESP32 - BLEServer * _server; - BLEAdvertising * _advertising; - BLECharacteristic *_characteristic; - - bool _connected; - - uint8_t _midiPacket[5]; // outgoing - -public: - // callbacks - void(*_connectedCallback)() = NULL; - void(*_disconnectedCallback)() = NULL; - -protected: - inline static void getMidiTimestamp (uint8_t *header, uint8_t *timestamp) - { - /* - The first byte of all BLE packets must be a header byte. This is followed by timestamp bytes and MIDI messages. - Header Byte - bit 7 Set to 1. - bit 6 Set to 0. (Reserved for future use) - bits 5-0 timestampHigh:Most significant 6 bits of timestamp information. - The header byte contains the topmost 6 bits of timing information for MIDI events in the BLE - packet. The remaining 7 bits of timing information for individual MIDI messages encoded in a - packet is expressed by timestamp bytes. - Timestamp Byte - bit 7 Set to 1. - bits 6-0 timestampLow: Least Significant 7 bits of timestamp information. - The 13-bit timestamp for the first MIDI message in a packet is calculated using 6 bits from the - header byte and 7 bits from the timestamp byte. - Timestamps are 13-bit values in milliseconds, and therefore the maximum value is 8,191 ms. - Timestamps must be issued by the sender in a monotonically increasing fashion. - timestampHigh is initially set using the lower 6 bits from the header byte while the timestampLow is - formed of the lower 7 bits from the timestamp byte. Should the timestamp value of a subsequent - MIDI message in the same packet overflow/wrap (i.e., the timestampLow is smaller than a - preceding timestampLow), the receiver is responsible for tracking this by incrementing the - timestampHigh by one (the incremented value is not transmitted, only understood as a result of the - overflow condition). - In practice, the time difference between MIDI messages in the same BLE packet should not span - more than twice the connection interval. As a result, a maximum of one overflow/wrap may occur - per BLE packet. - Timestamps are in the sender’s clock domain and are not allowed to be scheduled in the future. - Correlation between the receiver’s clock and the received timestamps must be performed to - ensure accurate rendering of MIDI messages, and is not addressed in this document. - */ - /* - Calculating a Timestamp - To calculate the timestamp, the built-in millis() is used. - The BLE standard only specifies 13 bits worth of millisecond data though, - so it’s bitwise anded with 0x1FFF for an ever repeating cycle of 13 bits. - This is done right after a MIDI message is detected. It’s split into a 6 upper bits, 7 lower bits, - and the MSB of both bytes are set to indicate that this is a header byte. - Both bytes are placed into the first two position of an array in preparation for a MIDI message. - */ - auto currentTimeStamp = millis() & 0x01FFF; - - *header = ((currentTimeStamp >> 7) & 0x3F) | 0x80; // 6 bits plus MSB - *timestamp = (currentTimeStamp & 0x7F) | 0x80; // 7 bits plus MSB - } - - - // serialize towards hardware - - void write(DataByte b1) - { - getMidiTimestamp(&_midiPacket[0], &_midiPacket[1]); - - _midiPacket[2] = b1; - - // TODO: quid running status - - _characteristic->setValue(_midiPacket, 3); - _characteristic->notify(); - }; - - void write(DataByte b1, DataByte b2) - { - getMidiTimestamp(&_midiPacket[0], &_midiPacket[1]); - - _midiPacket[2] = b1; - _midiPacket[3] = b2; - - // TODO: quid running status - - _characteristic->setValue(_midiPacket, 4); - _characteristic->notify(); - }; - - void write(DataByte b1, DataByte b2, DataByte b3) - { - getMidiTimestamp(&_midiPacket[0], &_midiPacket[1]); - - _midiPacket[2] = b1; - _midiPacket[3] = b2; - _midiPacket[4] = b3; - - // TODO: quid running status - - _characteristic->setValue(_midiPacket, 5); - _characteristic->notify(); - }; - - -public: - BleMidiInterface() - { - } - - ~BleMidiInterface() - { - } - - // TODO why must these functions be inline?? - - inline bool begin(const char* deviceName); - - inline void read() - { - // n/a no need to call read() in loop, as incoming data comes in async via onWrite - } - - inline void sendMIDI(StatusByte, DataByte data1 = 0, DataByte data2 = 0); - inline void receive(uint8_t *buffer, uint8_t bufferSize); - - void onConnected(void(*fptr)()) { - _connected = true; - _connectedCallback = fptr; - } - void onDisconnected(void(*fptr)()) { - _connected = false; - _disconnectedCallback = fptr; - } - -}; - -class MyServerCallbacks: public BLEServerCallbacks { -public: - MyServerCallbacks(BleMidiInterface* bleMidiInterface) { - _bleMidiInterface = bleMidiInterface; - } -protected: - BleMidiInterface* _bleMidiInterface; - - void onConnect(BLEServer* server) { - if (_bleMidiInterface->_connectedCallback) - _bleMidiInterface->_connectedCallback(); - }; - - void onDisconnect(BLEServer* server) { - if (_bleMidiInterface->_disconnectedCallback) - _bleMidiInterface->_disconnectedCallback(); - } -}; - -class MyCharacteristicCallbacks: public BLECharacteristicCallbacks { -public: - MyCharacteristicCallbacks(BleMidiInterface* bleMidiInterface) { - _bleMidiInterface = bleMidiInterface; - } - -protected: - BleMidiInterface* _bleMidiInterface; - - void onWrite(BLECharacteristic * characteristic) { - std::string rxValue = characteristic->getValue(); - if (rxValue.length() > 0) { - _bleMidiInterface->receive((uint8_t *)(rxValue.c_str()), rxValue.length()); - } - } -}; - -bool BleMidiInterface::begin(const char* deviceName) -{ - BLEDevice::init(deviceName); - - _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), - BLECharacteristic::PROPERTY_READ | - BLECharacteristic::PROPERTY_WRITE | - BLECharacteristic::PROPERTY_NOTIFY | - BLECharacteristic::PROPERTY_WRITE_NR - ); - // Add CCCD 0x2902 to allow notify - _characteristic->addDescriptor(new BLE2902()); - - _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; -} - -void BleMidiInterface::sendMIDI(StatusByte status, DataByte data1, DataByte data2) -{ - MidiType type = getTypeFromStatusByte(status); - Channel channel = getChannelFromStatusByte(status); - - switch (type) { - case NoteOff: - if (_noteOffCallback) _noteOffCallback(channel, data1, data2); - break; - case NoteOn: - if (_noteOnCallback) _noteOnCallback(channel, data1, data2); - break; - case AfterTouchPoly: - if (_afterTouchPolyCallback) _afterTouchPolyCallback(channel, data1, data2); - break; - case ControlChange: - if (_controlChangeCallback) _controlChangeCallback(channel, data1, data2); - break; - case ProgramChange: - if (_programChangeCallback) _programChangeCallback(channel, data1); - break; - case AfterTouchChannel: - if (_afterTouchChannelCallback) _afterTouchChannelCallback(channel, data1); - break; - case PitchBend: - if (_pitchBendCallback) { - int value = (int) ((data1 & 0x7f) | ((data2 & 0x7f) << 7)) + MIDI_PITCHBEND_MIN; - _pitchBendCallback(channel, value); - } - break; - - case SystemExclusive: - break; - - case TimeCodeQuarterFrame: - if (_timeCodeQuarterFrameCallback) _timeCodeQuarterFrameCallback(data1); - break; - case SongPosition: - if (_songPositionCallback) { - unsigned short value = unsigned((data1 & 0x7f) | ((data2 & 0x7f) << 7)); - _songPositionCallback(value); - } - break; - case SongSelect: - if (_songSelectCallback) _songSelectCallback(data1); - break; - case TuneRequest: - if (_tuneRequestCallback) _tuneRequestCallback(); - break; - - case Clock: - if (_clockCallback) _clockCallback(); - break; - case Tick: - break; - case Start: - if (_startCallback) _startCallback(); - break; - case Continue: - if (_continueCallback) _continueCallback(); - break; - case Stop: - if (_stopCallback) _stopCallback(); - break; - case ActiveSensing: - if (_activeSensingCallback) _activeSensingCallback(); - break; - case SystemReset: - if (_resetCallback) _resetCallback(); - break; - } -} - -void BleMidiInterface::receive(uint8_t *buffer, uint8_t bufferSize) -{ - /* - 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. - */ - - //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(1){ - lastStatus = buffer[lPtr]; - if( (buffer[lPtr] < 0x80) ){ - //Status message not present, bail - return; - } - //Point to next non-data byte - rPtr = lPtr; - while( (buffer[rPtr + 1] < 0x80)&&(rPtr < (bufferSize - 1)) ){ - rPtr++; - } - //look at l and r pointers and decode by size. - if( rPtr - lPtr < 1 ){ - //Time code or system - sendMIDI(lastStatus); - } else if( rPtr - lPtr < 2 ) { - sendMIDI(lastStatus, buffer[lPtr + 1]); - } else if( rPtr - lPtr < 3 ) { - sendMIDI(lastStatus, buffer[lPtr + 1], buffer[lPtr + 2]); - } else { - //Too much data - //If not System Common or System Real-Time, send it as running status - switch( buffer[lPtr] & 0xF0 ) - { - case NoteOff: - case NoteOn: - case AfterTouchPoly: - case ControlChange: - case PitchBend: - for(int i = lPtr; i < rPtr; i = i + 2) - sendMIDI(lastStatus, buffer[i + 1], buffer[i + 2]); - break; - case ProgramChange: - case AfterTouchChannel: - for(int i = lPtr; i < rPtr; i = i + 1) - sendMIDI(lastStatus, buffer[i + 1]); - break; - default: - break; - } - } - //Point to next status - lPtr = rPtr + 2; - if(lPtr >= bufferSize){ - //end of packet - return; - } - } - -} - -END_BLEMIDI_NAMESPACE diff --git a/src/common b/src/common deleted file mode 160000 index 8f74508..0000000 --- a/src/common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8f745084c39e6016f0ea7afad95973342c6f7e0e diff --git a/src/hardware/BLE-MIDI_Client_ESP32.h b/src/hardware/BLE-MIDI_Client_ESP32.h new file mode 100644 index 0000000..b2a1029 --- /dev/null +++ b/src/hardware/BLE-MIDI_Client_ESP32.h @@ -0,0 +1,95 @@ +#pragma once + +// Headers for ESP32 BLE +#include +#include +#include +#include + +BEGIN_BLEMIDI_NAMESPACE + +class BLEMIDI_Client_ESP32 +{ +private: + BLEClient* _client = nullptr; + + BLEMIDI* _bleMidiTransport = nullptr; + +public: + BLEMIDI_Client_ESP32() + { + } + + bool begin(const char*, BLEMIDI*); + + void write(uint8_t* data, uint8_t length) + { + _characteristic->setValue(data, length); + _characteristic->notify(); + } + + void receive(uint8_t* buffer, size_t length) + { + // Post the items to the back of the queue + // (drop the first 2 items) + for (size_t i = 2; i < length; i++) + xQueueSend(_bleMidiTransport->mRxQueue, &buffer[i], portMAX_DELAY); + } + + void connected() + { + if (_bleMidiTransport->_connectedCallback) + _bleMidiTransport->_connectedCallback(); + } + + void disconnected() + { + if (_bleMidiTransport->_disconnectedCallback) + _bleMidiTransport->_disconnectedCallback(); + } +}; + +class MyClientCallbacks: public BLEClientCallbacks { +public: + MyClientCallbacks(BLEMIDI_Client_ESP32* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32) { + } + +protected: + BLEMIDI_Client_ESP32* _bluetoothEsp32 = nullptr; + + void onConnect(BLEClient*) { + if (_bluetoothEsp32) + _bluetoothEsp32->connected(); + }; + + void onDisconnect(BLEClient*) { + if (_bluetoothEsp32) + _bluetoothEsp32->disconnected(); + } +}; + +bool BLEMIDI_Client_ESP32::begin(const char* deviceName, BLEMIDI* bleMidiTransport) +{ + _bleMidiTransport = bleMidiTransport; + + BLEDevice::init(deviceName); + + _client = BLEDevice::createClient(); + _client->setCallbacks(new MyClientCallbacks(this)); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 5 seconds. + pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(this)); + pBLEScan->setInterval(1349); + pBLEScan->setWindow(449); + pBLEScan->setActiveScan(true); + doScan = true; + pBLEScan->start(10, scanCompleteCB); + + return true; +} + +END_BLEMIDI_NAMESPACE diff --git a/src/hardware/BLEMIDI_ArduinoBLE.h b/src/hardware/BLEMIDI_ArduinoBLE.h new file mode 100644 index 0000000..ef49e97 --- /dev/null +++ b/src/hardware/BLEMIDI_ArduinoBLE.h @@ -0,0 +1,225 @@ +#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 + +template +class Fifo { +public: + const size_t size; //speculative feature, in case it's needed + + Fifo(): size(rawSize) + { + flush(); + } + + T dequeue() + { + numberOfElements--; + nextOut %= size; + return raw[ nextOut++]; + }; + + bool enqueue( T element ) + { + if ( count() >= rawSize ) + return false; + + numberOfElements++; + nextIn %= size; + raw[nextIn] = element; + nextIn++; //advance to next index + + return true; + }; + + T peek() const + { + return raw[ nextOut % size]; + } + + void flush() + { + nextIn = nextOut = numberOfElements = 0; + } + + // how many elements are currently in the FIFO? + size_t count() { return numberOfElements; } + +private: + size_t numberOfElements; + size_t nextIn; + size_t nextOut; + T raw[rawSize]; +}; + +class BLEMIDI_ArduinoBLE +{ +private: + static BLEMIDITransport* _bleMidiTransport; + static BLEDevice* _central; + + Fifo mRxBuffer; + +public: + BLEMIDI_ArduinoBLE() + { + } + + bool begin(const char*, BLEMIDITransport*); + + void write(uint8_t* buffer, size_t length) + { + // TODO: test length + ((BLECharacteristic)midiChar).writeValue(buffer, length); + } + + bool available(byte* pvBuffer) + { + #ifdef BLE_POLLING + + if (mRxBuffer.count() > 0) { + *pvBuffer = mRxBuffer.dequeue(); + + return true; + } + + poll(); + + if (midiChar.written()) { + // auto buffer = midiChar.value(); + auto length = midiChar.valueLength(); + + if (length > 0) { + auto buffer = midiChar.value().c_str(); + _bleMidiTransport->receive((byte*)buffer, length); + + } + } + return false; +#endif +#ifdef BLE_EVENTS +/ BLE.poll(); + return ; // ?? +#endif + } + + void add(byte value) + { + // called from BLE-MIDI, to add it to a buffer here + mRxBuffer.enqueue(value); + } + +protected: + static void receive(const unsigned char* buffer, size_t length) + { + // forward the buffer so it can be parsed + _bleMidiTransport->receive((uint8_t*)buffer, length); + } + + bool poll() + { + BLEDevice central = BLE.central(); + if (!central) { + if (_central) { + BLEMIDI_ArduinoBLE::blePeripheralDisconnectHandler(*_central); + _central = nullptr; + } + return false; + } + + if (!central.connected()) { + return false; + } + + if (nullptr == _central) { + BLEMIDI_ArduinoBLE::blePeripheralConnectHandler(central); + _central = ¢ral; + } + else { + if (*_central != central) { + BLEMIDI_ArduinoBLE::blePeripheralDisconnectHandler(*_central); + BLEMIDI_ArduinoBLE::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_ArduinoBLE::_bleMidiTransport = nullptr; +BLEDevice* BLEMIDI_ArduinoBLE::_central = nullptr; + +bool BLEMIDI_ArduinoBLE::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_ArduinoBLE::blePeripheralConnectHandler); + BLE.setEventHandler(BLEDisconnected, BLEMIDI_ArduinoBLE::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/BLEMIDI_ESP32.h b/src/hardware/BLEMIDI_ESP32.h new file mode 100644 index 0000000..73a3ea7 --- /dev/null +++ b/src/hardware/BLEMIDI_ESP32.h @@ -0,0 +1,162 @@ +#pragma once + +// Headers for ESP32 BLE +#include +#include +#include +#include + +BEGIN_BLEMIDI_NAMESPACE + +class BLEMIDI_ESP32 +{ +private: + BLEServer* _server = nullptr; + BLEAdvertising* _advertising = nullptr; + BLECharacteristic* _characteristic = nullptr; + + BLEMIDITransport* _bleMidiTransport = nullptr; + + friend class MyServerCallbacks; + friend class MyCharacteristicCallbacks; + +protected: + QueueHandle_t mRxQueue; + +public: + BLEMIDI_ESP32() + { + } + + bool begin(const char*, BLEMIDITransport*); + + void write(uint8_t* buffer, size_t length) + { + _characteristic->setValue(buffer, length); + _characteristic->notify(); + } + + bool available(byte* pvBuffer) + { + return xQueueReceive(mRxQueue, pvBuffer, 0); // return immediately when the queue is empty + } + + void add(byte value) + { + // called from BLE-MIDI, to add it to a buffer here + xQueueSend(mRxQueue, &value, portMAX_DELAY); + } + +protected: + void receive(uint8_t* buffer, size_t length) + { + // parse the incoming buffer + _bleMidiTransport->receive(buffer, length); + } + + void connected() + { + if (_bleMidiTransport->_connectedCallback) + _bleMidiTransport->_connectedCallback(); + } + + void disconnected() + { + if (_bleMidiTransport->_disconnectedCallback) + _bleMidiTransport->_disconnectedCallback(); + } +}; + +class MyServerCallbacks: public BLEServerCallbacks { +public: + MyServerCallbacks(BLEMIDI_ESP32* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32) { + } + +protected: + BLEMIDI_ESP32* _bluetoothEsp32 = nullptr; + + void onConnect(BLEServer*) { + if (_bluetoothEsp32) + _bluetoothEsp32->connected(); + }; + + void onDisconnect(BLEServer*) { + if (_bluetoothEsp32) + _bluetoothEsp32->disconnected(); + } +}; + +class MyCharacteristicCallbacks: public BLECharacteristicCallbacks { +public: + MyCharacteristicCallbacks(BLEMIDI_ESP32* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32 ) { + } + +protected: + BLEMIDI_ESP32* _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_ESP32::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), + BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_NOTIFY | + BLECharacteristic::PROPERTY_WRITE_NR + ); + // Add CCCD 0x2902 to allow notify + _characteristic->addDescriptor(new BLE2902()); + + _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/BLEMIDI_ESP32_NimBLE.h b/src/hardware/BLEMIDI_ESP32_NimBLE.h new file mode 100644 index 0000000..4eb706f --- /dev/null +++ b/src/hardware/BLEMIDI_ESP32_NimBLE.h @@ -0,0 +1,158 @@ +#pragma once + +// Headers for ESP32 NimBLE +#include + +BEGIN_BLEMIDI_NAMESPACE + +class BLEMIDI_ESP32_NimBLE +{ +private: + BLEServer* _server = nullptr; + BLEAdvertising* _advertising = nullptr; + BLECharacteristic* _characteristic = nullptr; + + BLEMIDITransport* _bleMidiTransport = nullptr; + + friend class MyServerCallbacks; + friend class MyCharacteristicCallbacks; + +protected: + QueueHandle_t mRxQueue; + +public: + BLEMIDI_ESP32_NimBLE() + { + } + + bool begin(const char*, BLEMIDITransport*); + + void write(uint8_t* buffer, size_t length) + { + _characteristic->setValue(buffer, length); + _characteristic->notify(); + } + + bool available(byte* pvBuffer) + { + // return 1 byte from the Queue + return xQueueReceive(mRxQueue, (void*)pvBuffer, 0); // return immediately when the queue is empty + } + + void add(byte value) + { + // called from BLE-MIDI, to add it to a buffer here + xQueueSend(mRxQueue, &value, portMAX_DELAY); + } + +protected: + void receive(uint8_t* buffer, size_t length) + { + // forward the buffer so it can be parsed + _bleMidiTransport->receive(buffer, length); + } + + void connected() + { + if (_bleMidiTransport->_connectedCallback) + _bleMidiTransport->_connectedCallback(); + } + + void disconnected() + { + if (_bleMidiTransport->_disconnectedCallback) + _bleMidiTransport->_disconnectedCallback(); + } +}; + +class MyServerCallbacks: public BLEServerCallbacks { +public: + MyServerCallbacks(BLEMIDI_ESP32_NimBLE* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32) { + } + +protected: + BLEMIDI_ESP32_NimBLE* _bluetoothEsp32 = nullptr; + + void onConnect(BLEServer*) { + if (_bluetoothEsp32) + _bluetoothEsp32->connected(); + }; + + void onDisconnect(BLEServer*) { + if (_bluetoothEsp32) + _bluetoothEsp32->disconnected(); + } +}; + +class MyCharacteristicCallbacks: public BLECharacteristicCallbacks { +public: + MyCharacteristicCallbacks(BLEMIDI_ESP32_NimBLE* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32 ) { + } + +protected: + BLEMIDI_ESP32_NimBLE* _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_ESP32_NimBLE::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/BLEMIDI_nRF52.h b/src/hardware/BLEMIDI_nRF52.h new file mode 100644 index 0000000..920550f --- /dev/null +++ b/src/hardware/BLEMIDI_nRF52.h @@ -0,0 +1,121 @@ +#pragma once + +#include + +BEGIN_BLEMIDI_NAMESPACE + +class BLEMIDI_nRF52 +{ +private: + BLEDis bledis; + BLEMidi blemidi; + + BLEMIDITransport* _bleMidiTransport; + + friend class MyServerCallbacks; + friend class MyCharacteristicCallbacks; + +public: + BLEMIDI_nRF52() + { + } + + bool begin(const char*, BLEMIDITransport*); + + void write(uint8_t* buffer, size_t length) + { + } + + bool available(byte* pvBuffer) + { + return false; + } + + void add(byte value) + { + } + + +protected: + void receive(uint8_t* buffer, size_t length) + { + } + + void connected() + { + if (_bleMidiTransport->_connectedCallback) + _bleMidiTransport->_connectedCallback(); + } + + void disconnected() + { + if (_bleMidiTransport->_disconnectedCallback) + _bleMidiTransport->_disconnectedCallback(); + } +}; + +bool BLEMIDI_nRF52::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) +{ + _bleMidiTransport = bleMidiTransport; + + // Config the peripheral connection with maximum bandwidth + // more SRAM required by SoftDevice + // Note: All config***() function must be called before begin() + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + + Bluefruit.begin(); + Bluefruit.setName(deviceName); + Bluefruit.setTxPower(4); // Check bluefruit.h for supported values + + // Setup the on board blue LED to be enabled on CONNECT + Bluefruit.autoConnLed(true); + + // Configure and Start Device Information Service + bledis.setManufacturer("Adafruit Industries"); + bledis.setModel("Bluefruit Feather52"); + bledis.begin(); + + // Start advertising ---------------------------- + + // Set General Discoverable Mode flag + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + + // Advertise TX Power + Bluefruit.Advertising.addTxPower(); + + // Advertise BLE MIDI Service + Bluefruit.Advertising.addService(blemidi); + + // Secondary Scan Response packet (optional) + // Since there is no room for 'Name' in Advertising packet + Bluefruit.ScanResponse.addName(); + + /* Start Advertising + * - Enable auto advertising if disconnected + * - Interval: fast mode = 20 ms, slow mode = 152.5 ms + * - Timeout for fast mode is 30 seconds + * - Start(timeout) with timeout = 0 will advertise forever (until connected) + * + * For recommended advertising interval + * https://developer.apple.com/library/content/qa/qa1931/_index.html + */ + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds + + 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/utility/.DS_Store b/src/utility/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 -#else - #include - typedef uint8_t byte; -#endif - -BEGIN_BLEMIDI_NAMESPACE - -/*! \brief Create an instance of the library - */ -#define BLEMIDI_CREATE_INSTANCE(Name) \ - BLEMIDI_NAMESPACE::BleMidiInterface Name; - -/*! \brief Create an instance of the library - */ -#define BLEMIDI_CREATE_CLIENT_INSTANCE(Name) \ - BLEMIDI_NAMESPACE::BleMidiInterfaceClient Name; - - -/*! \brief -*/ -#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ - BLEMIDI_CREATE_INSTANCE(bm); - -END_BLEMIDI_NAMESPACE diff --git a/src/utility/BleMidi_Settings.h b/src/utility/BleMidi_Settings.h deleted file mode 100644 index 330f9c9..0000000 --- a/src/utility/BleMidi_Settings.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "BleMidi_Namespace.h" - -//#define DEBUG -#define RELEASE - -#if defined(RELEASE) -#define RELEASE_BUILD -#undef DEBUG_BUILD -#endif - -#if defined(DEBUG) -#define DEBUG_BUILD -#undef RELEASE_BUILD -#endif - - -#if defined(RELEASE_BUILD) -#undef BLEMIDI_DEBUG -#undef BLEMIDI_DEBUG_VERBOSE -#endif - -#if defined(DEBUG_BUILD) -#define BLEMIDI_DEBUG 1 -#undef BLEMIDI_DEBUG_VERBOSE -#define BLEMIDI_DEBUG_PARSING -#endif -