From 253f3e4b519367072805eb12d505e5ea57422b9d Mon Sep 17 00:00:00 2001 From: lathoub Date: Thu, 15 Aug 2019 14:44:11 +0200 Subject: [PATCH] initial upload --- .../MidiBle.ino} | 27 +- keywords.txt | 7 +- library.properties | 15 +- src/BleMidi.cpp | 1 - src/BleMidi.h | 15 - src/Ble_esp32.h | 532 +++++------------- src/common | 1 - src/midi_bleTransport.cpp | 1 + src/midi_bleTransport.h | 129 +++++ src/utility/.DS_Store | Bin 6148 -> 0 bytes .../{BleMidi_Defs.h => midi_bleDefs.h} | 50 +- ...leMidi_Namespace.h => midi_bleNamespace.h} | 22 +- ...{BleMidi_Settings.h => midi_bleSettings.h} | 58 +- 13 files changed, 360 insertions(+), 498 deletions(-) rename examples/{Esp32_NoteOnOffEverySec/Esp32_NoteOnOffEverySec.ino => MidiBle/MidiBle.ino} (79%) delete mode 100644 src/BleMidi.cpp delete mode 100644 src/BleMidi.h mode change 100644 => 100755 src/Ble_esp32.h delete mode 160000 src/common create mode 100755 src/midi_bleTransport.cpp create mode 100755 src/midi_bleTransport.h delete mode 100644 src/utility/.DS_Store rename src/utility/{BleMidi_Defs.h => midi_bleDefs.h} (52%) mode change 100644 => 100755 rename src/utility/{BleMidi_Namespace.h => midi_bleNamespace.h} (96%) mode change 100644 => 100755 rename src/utility/{BleMidi_Settings.h => midi_bleSettings.h} (86%) mode change 100644 => 100755 diff --git a/examples/Esp32_NoteOnOffEverySec/Esp32_NoteOnOffEverySec.ino b/examples/MidiBle/MidiBle.ino similarity index 79% rename from examples/Esp32_NoteOnOffEverySec/Esp32_NoteOnOffEverySec.ino rename to examples/MidiBle/MidiBle.ino index cb4724c..6d80999 100644 --- a/examples/Esp32_NoteOnOffEverySec/Esp32_NoteOnOffEverySec.ino +++ b/examples/MidiBle/MidiBle.ino @@ -1,6 +1,11 @@ -#include "BleMidi.h" +#include -BLEMIDI_CREATE_INSTANCE(bm); +#include +#include + +bleMidi::BluetoothEsp32 sBluetoothEsp32; +bleMidi::BleMidiTransport bm((bleMidi::BluetoothEsp32&) sBluetoothEsp32); +midi::MidiInterface> MIDI((bleMidi::BleMidiTransport&)bm); // ----------------------------------------------------------------------------- // @@ -9,17 +14,15 @@ 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 - } + while (!Serial); - bm.begin("hehe"); + MIDI.begin("hihi", 1); bm.onConnected(OnBleMidiConnected); bm.onDisconnected(OnBleMidiDisconnected); - bm.setHandleNoteOn(OnBleMidiNoteOn); - bm.setHandleNoteOff(OnBleMidiNoteOff); + MIDI.setHandleNoteOn(OnBleMidiNoteOn); + MIDI.setHandleNoteOff(OnBleMidiNoteOff); Serial.println(F("looping")); } @@ -29,8 +32,10 @@ void setup() // ----------------------------------------------------------------------------- void loop() { - bm.sendNoteOn(60, 127, 1); // note 60, velocity 127 on channel 1 - bm.sendNoteOff(60, 127, 1); + MIDI.read(); + + MIDI.sendNoteOn(60, 127, 1); // note 60, velocity 127 on channel 1 + MIDI.sendNoteOff(60, 127, 1); delay(1000); } @@ -78,4 +83,4 @@ void OnBleMidiNoteOff(byte channel, byte note, byte velocity) { Serial.print(F(" velocity:")); Serial.print(velocity); Serial.println(); -} +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index 3aaab47..b63d67f 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,12 +1,13 @@ ####################################### -# Syntax Coloring Map for AppleMIDI +# Syntax Coloring Map for BLEMIDI ####################################### ####################################### # Datatypes (KEYWORD1) ####################################### -BLEMIDI KEYWORD1 -BLEMIDI.h KEYWORD1 +midi_bleTransport KEYWORD1 +midi_bleTransport.h KEYWORD1 +Ble_esp32.h KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) diff --git a/library.properties b/library.properties index 914a661..24f026d 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,11 @@ -name=BLE-MIDI -version=0.0.1 +name=MIDI BLE Library +version=2.0.0 author=lathoub -maintainer=lathoub -sentence=MIDI over Bluetooth Low Energy (BLE-MIDI) 1.0 for Arduino -paragraph=MIDI over Bluetooth Low Energy +maintainer=lathoub +sentence=BLE MIDI I/Os for Arduino +paragraph=Read & send MIDI messages over BLE category=Communication -url=https://github.com/lathoub/Arduino-BLE-MIDI +url= architectures=* -includes=BLEMIDI.h +includes=midi_bleTransport.h +dependency=https://github.com/FortySevenEffects/arduino_midi_library \ No newline at end of file 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 b54c267..0000000 --- a/src/BleMidi.h +++ /dev/null @@ -1,15 +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" -#endif diff --git a/src/Ble_esp32.h b/src/Ble_esp32.h old mode 100644 new mode 100755 index cccdbcd..f5aae65 --- a/src/Ble_esp32.h +++ b/src/Ble_esp32.h @@ -1,395 +1,137 @@ -#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 +#pragma once + +// Headers for ESP32 BLE +#include +#include +#include +#include + +#define SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700" +#define CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3" + +BEGIN_BLEMIDI_NAMESPACE + +class BluetoothEsp32 +{ +private: + BLEServer* _server; + BLEAdvertising* _advertising; + BLECharacteristic* _characteristic; + + BleMidiTransport* _bleMidiTransport; + +public: + BluetoothEsp32() + : _server(NULL), + _advertising(NULL), + _characteristic(NULL), + _bleMidiTransport(NULL) + { + } + + ~BluetoothEsp32() + { + } + + bool begin(const char*, BleMidiTransport*); + + inline void write(uint8_t* data, uint8_t length) + { + _characteristic->setValue(data, length); + _characteristic->notify(); + } + + inline void receive(uint8_t* buffer, uint8_t length) + { + _bleMidiTransport->receive(buffer, length); + } + + inline void connected() + { + if (_bleMidiTransport->_connectedCallback) + _bleMidiTransport->_connectedCallback(); + } + + inline void disconnected() + { + if (_bleMidiTransport->_disconnectedCallback) + _bleMidiTransport->_disconnectedCallback(); + } +}; + +class MyServerCallbacks: public BLEServerCallbacks { +public: + MyServerCallbacks(BluetoothEsp32* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32) { + } + +protected: + BluetoothEsp32* _bluetoothEsp32; + + void onConnect(BLEServer* server) { + _bluetoothEsp32->connected(); + }; + + void onDisconnect(BLEServer* server) { + _bluetoothEsp32->disconnected(); + } +}; + +class MyCharacteristicCallbacks: public BLECharacteristicCallbacks { +public: + MyCharacteristicCallbacks(BluetoothEsp32* bluetoothEsp32) + : _bluetoothEsp32(bluetoothEsp32 ) { + } + +protected: + BluetoothEsp32* _bluetoothEsp32; + + void onWrite(BLECharacteristic * characteristic) { + std::string rxValue = characteristic->getValue(); + if (rxValue.length() > 0) { + _bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length()); + } + } +}; + +bool BluetoothEsp32::begin(const char* deviceName, BleMidiTransport* bleMidiTransport) +{ + _bleMidiTransport = bleMidiTransport; + + 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; +} + +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/midi_bleTransport.cpp b/src/midi_bleTransport.cpp new file mode 100755 index 0000000..fef6562 --- /dev/null +++ b/src/midi_bleTransport.cpp @@ -0,0 +1 @@ +#include "midi_bleTransport.h" diff --git a/src/midi_bleTransport.h b/src/midi_bleTransport.h new file mode 100755 index 0000000..96f1468 --- /dev/null +++ b/src/midi_bleTransport.h @@ -0,0 +1,129 @@ +/*! + * @file midi_bleTransport.h + */ + +#pragma once + +#include "utility/midi_bleSettings.h" +#include "utility/midi_bleDefs.h" + +#include + +BEGIN_BLEMIDI_NAMESPACE + +template +class BleMidiTransport +{ +private: + uint8_t _midiPacket[5]; // outgoing + + typedef midi::RingBuffer TxBuffer; + typedef midi::RingBuffer RxBuffer; + TxBuffer mTxBuffer; + RxBuffer mRxBuffer; + + bool _connected; + +private: + BleClass& mBleClass; + +public: + inline BleMidiTransport(BleClass& inBleClass) + : mBleClass(inBleClass) + { + } + + inline ~BleMidiTransport() {} + + inline bool begin(int baudrate) {} // n/a + + inline bool begin(const char* deviceName) + { + return mBleClass.begin(deviceName, this); + } + + inline unsigned available() { return mRxBuffer.getLength(); } + inline byte read() { return mRxBuffer.read(); } + inline void beginWrite() { mTxBuffer.clear(); } + inline void write(byte inData) { mTxBuffer.write(inData); } + inline void endWrite() + { + getMidiTimestamp(&_midiPacket[0], &_midiPacket[1]); + + mTxBuffer.read(_midiPacket + 2, mTxBuffer.getLength()); + + mBleClass.write((uint8_t*)_midiPacket, mTxBuffer.getLength() + 2); + } + +public: + void receive(uint8_t* buffer, uint8_t length) + { + mRxBuffer.read(buffer, length); + } + +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. + */ + inline 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 + } + +public: + // callbacks + void(*_connectedCallback)() = NULL; + void(*_disconnectedCallback)() = NULL; + +public: + void onConnected(void(*fptr)()) { + _connected = true; + _connectedCallback = fptr; + } + + void onDisconnected(void(*fptr)()) { + _connected = false; + _disconnectedCallback = fptr; + } + +}; + +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 -*/ -#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ - BLEMIDI_CREATE_INSTANCE(bm); - -END_BLEMIDI_NAMESPACE +#pragma once + +#include "midi_bleNamespace.h" + +#if ARDUINO + #include +#else + #include + typedef uint8_t byte; +#endif + +BEGIN_BLEMIDI_NAMESPACE + +/*! \brief Create an instance of the library + */ +#define BLEMIDI_CREATE_INSTANCE(Type, Name) \ + BLEMIDI_NAMESPACE::BleMidiTransport Name((Type&)SerialPort); + + +/*! \brief +*/ +#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \ + BLEMIDI_CREATE_INSTANCE(bm); + +END_BLEMIDI_NAMESPACE diff --git a/src/utility/BleMidi_Namespace.h b/src/utility/midi_bleNamespace.h old mode 100644 new mode 100755 similarity index 96% rename from src/utility/BleMidi_Namespace.h rename to src/utility/midi_bleNamespace.h index c632a32..dc179c9 --- a/src/utility/BleMidi_Namespace.h +++ b/src/utility/midi_bleNamespace.h @@ -1,11 +1,11 @@ -#pragma once - -#define BLEMIDI_NAMESPACE bleMidi -#define BEGIN_BLEMIDI_NAMESPACE namespace BLEMIDI_NAMESPACE { -#define END_BLEMIDI_NAMESPACE } - -#define USING_NAMESPACE_BLEMIDI using namespace BLEMIDI_NAMESPACE; - -BEGIN_BLEMIDI_NAMESPACE - -END_BLEMIDI_NAMESPACE +#pragma once + +#define BLEMIDI_NAMESPACE bleMidi +#define BEGIN_BLEMIDI_NAMESPACE namespace BLEMIDI_NAMESPACE { +#define END_BLEMIDI_NAMESPACE } + +#define USING_NAMESPACE_BLEMIDI using namespace BLEMIDI_NAMESPACE; + +BEGIN_BLEMIDI_NAMESPACE + +END_BLEMIDI_NAMESPACE diff --git a/src/utility/BleMidi_Settings.h b/src/utility/midi_bleSettings.h old mode 100644 new mode 100755 similarity index 86% rename from src/utility/BleMidi_Settings.h rename to src/utility/midi_bleSettings.h index 330f9c9..d9f20a8 --- a/src/utility/BleMidi_Settings.h +++ b/src/utility/midi_bleSettings.h @@ -1,29 +1,29 @@ -#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 - +#pragma once + +#include "midi_bleNamespace.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 +