diff --git a/.gitignore b/.gitignore index ca33092..665f29f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store examples/.DS_Store src/.DS_Store +.vscode/settings.json diff --git a/src/BLE-MIDI.h b/src/BLE-MIDI.h index 421d401..4e99ea6 100755 --- a/src/BLE-MIDI.h +++ b/src/BLE-MIDI.h @@ -82,8 +82,8 @@ public: unsigned available() { uint8_t byte; - auto succes = mBleClass.available(&byte); - if (!succes) return mRxIndex; + auto success = mBleClass.available(&byte); + if (!success) return mRxIndex; mRxBuffer[mRxIndex++] = byte; @@ -192,13 +192,13 @@ public: MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved from other messages – except for System Exclusive messages. */ - void receive(uint8_t* buffer, size_t length) + void receive(byte* buffer, size_t length) { // Pointers used to search through payload. - uint8_t lPtr = 0; - uint8_t rPtr = 0; + byte lPtr = 0; + byte rPtr = 0; // lastStatus used to capture runningStatus - uint8_t lastStatus; + byte lastStatus; // Decode first packet -- SHALL be "Full MIDI message" lPtr = 2; //Start at first MIDI status -- SHALL be "MIDI status" @@ -219,14 +219,14 @@ public: // look at l and r pointers and decode by size. if( rPtr - lPtr < 1 ) { // Time code or system - mBleClass.add(&lastStatus); + mBleClass.add(lastStatus); } else if( rPtr - lPtr < 2 ) { - mBleClass.add(&lastStatus); - mBleClass.add(&buffer[lPtr + 1]); + 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]); + 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 @@ -239,23 +239,23 @@ public: case 0xE0: for (auto i = lPtr; i < rPtr; i = i + 2) { - mBleClass.add(&lastStatus); - mBleClass.add(&buffer[i + 1]); - mBleClass.add(&buffer[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]); + mBleClass.add(lastStatus); + mBleClass.add(buffer[i + 1]); } break; case 0xF0: - mBleClass.add(&buffer[lPtr]); + mBleClass.add(buffer[lPtr]); for (auto i = lPtr; i < rPtr; i++) - mBleClass.add(&buffer[i + 1]); + mBleClass.add(buffer[i + 1]); break; default: break; diff --git a/src/BLE-MIDI_Config.h b/src/BLE-MIDI_Config.h index 6efaf53..9d57aaf 100644 --- a/src/BLE-MIDI_Config.h +++ b/src/BLE-MIDI_Config.h @@ -1,14 +1,15 @@ #pragma once #ifdef ARDUINO_ARCH_ESP32 -//#include -#include +#include +//#include #endif #ifdef AAA #include #endif -#ifdef __AVR__ + +//#ifdef BBB #include -#endif +//#endif diff --git a/src/hardware/BLEMIDI_ArduinoBLE.h b/src/hardware/BLEMIDI_ArduinoBLE.h index 7bf0b54..ef49e97 100644 --- a/src/hardware/BLEMIDI_ArduinoBLE.h +++ b/src/hardware/BLEMIDI_ArduinoBLE.h @@ -10,20 +10,70 @@ BLEStringCharacteristic midiChar(CHARACTERISTIC_UUID, // standard 16-bit charac BEGIN_BLEMIDI_NAMESPACE -class BLEMIDI +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 BLEMIDITransport* _bleMidiTransport; static BLEDevice* _central; -// byte mRxBuffer[Settings::MaxBufferSize]; + Fifo mRxBuffer; public: - BLEMIDI() + BLEMIDI_ArduinoBLE() { } - bool begin(const char*, BLEMIDITransport*); + bool begin(const char*, BLEMIDITransport*); void write(uint8_t* buffer, size_t length) { @@ -31,9 +81,16 @@ public: ((BLECharacteristic)midiChar).writeValue(buffer, length); } - size_t available(uint8_t* buffer, const size_t index, const size_t max) + bool available(byte* pvBuffer) { #ifdef BLE_POLLING + + if (mRxBuffer.count() > 0) { + *pvBuffer = mRxBuffer.dequeue(); + + return true; + } + poll(); if (midiChar.written()) { @@ -41,60 +98,30 @@ public: auto length = midiChar.valueLength(); if (length > 0) { - // TODO: test length - memcpy(buffer + index, midiChar.value().c_str(), length); - return length; + auto buffer = midiChar.value().c_str(); + _bleMidiTransport->receive((byte*)buffer, length); + } } + return false; #endif #ifdef BLE_EVENTS / BLE.poll(); - return 0; + return ; // ?? #endif } - void read() + void add(byte value) { + // called from BLE-MIDI, to add it to a buffer here + mRxBuffer.enqueue(value); } - /* - The general form of a MIDI message follows: - n-byte MIDI Message - Byte 0 MIDI message Status byte, Bit 7 is Set to 1. - Bytes 1 to n-1 MIDI message Data bytes, if n > 1. Bit 7 is Set to 0 - There are two types of MIDI messages that can appear in a single packet: full MIDI messages and - Running Status MIDI messages. Each is encoded differently. - A full MIDI message is simply the MIDI message with the Status byte included. - A Running Status MIDI message is a MIDI message with the Status byte omitted. Running Status - MIDI messages may only be placed in the data stream if the following criteria are met: - 1. The original MIDI message is 2 bytes or greater and is not a System Common or System - Real-Time message. - 2. The omitted Status byte matches the most recently preceding full MIDI message’s Status - byte within the same BLE packet. - In addition, the following rules apply with respect to Running Status: - 1. A Running Status MIDI message is allowed within the packet after at least one full MIDI - message. - 2. Every MIDI Status byte must be preceded by a timestamp byte. Running Status MIDI - messages may be preceded by a timestamp byte. If a Running Status MIDI message is not - preceded by a timestamp byte, the timestamp byte of the most recently preceding message - in the same packet is used. - 3. System Common and System Real-Time messages do not cancel Running Status if - interspersed between Running Status MIDI messages. However, a timestamp byte must - precede the Running Status MIDI message that follows. - 4. The end of a BLE packet does cancel Running Status. - In the MIDI 1.0 protocol, System Real-Time messages can be sent at any time and may be - inserted anywhere in a MIDI data stream, including between Status and Data bytes of any other - MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved - from other messages – except for System Exclusive messages. - */ - static void receive(const uint8_t* buffer, size_t length) +protected: + static void receive(const unsigned char* buffer, size_t length) { - Serial.print("receive: "); - for (int i = 0; i < length; i++) { - Serial.print(" 0x"); - Serial.print(buffer[i], HEX); - } - Serial.println(""); + // forward the buffer so it can be parsed + _bleMidiTransport->receive((uint8_t*)buffer, length); } bool poll() @@ -102,7 +129,7 @@ public: BLEDevice central = BLE.central(); if (!central) { if (_central) { - BLEMIDI::blePeripheralDisconnectHandler(*_central); + BLEMIDI_ArduinoBLE::blePeripheralDisconnectHandler(*_central); _central = nullptr; } return false; @@ -113,13 +140,13 @@ public: } if (nullptr == _central) { - BLEMIDI::blePeripheralConnectHandler(central); + BLEMIDI_ArduinoBLE::blePeripheralConnectHandler(central); _central = ¢ral; } else { if (*_central != central) { - BLEMIDI::blePeripheralDisconnectHandler(*_central); - BLEMIDI::blePeripheralConnectHandler(central); + BLEMIDI_ArduinoBLE::blePeripheralDisconnectHandler(*_central); + BLEMIDI_ArduinoBLE::blePeripheralConnectHandler(central); _central = ¢ral; } } @@ -150,10 +177,10 @@ public: } }; -BLEMIDITransport* BLEMIDI::_bleMidiTransport = nullptr; -BLEDevice* BLEMIDI::_central = nullptr; +BLEMIDITransport* BLEMIDI_ArduinoBLE::_bleMidiTransport = nullptr; +BLEDevice* BLEMIDI_ArduinoBLE::_central = nullptr; -bool BLEMIDI::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) +bool BLEMIDI_ArduinoBLE::begin(const char* deviceName, BLEMIDITransport* bleMidiTransport) { _bleMidiTransport = bleMidiTransport; @@ -168,8 +195,8 @@ bool BLEMIDI::begin(const char* deviceName, BLEMIDITransport* ble #ifdef BLE_EVENTS // assign event handlers for connected, disconnected to peripheral - BLE.setEventHandler(BLEConnected, BLEMIDI::blePeripheralConnectHandler); - BLE.setEventHandler(BLEDisconnected, BLEMIDI::blePeripheralDisconnectHandler); + BLE.setEventHandler(BLEConnected, BLEMIDI_ArduinoBLE::blePeripheralConnectHandler); + BLE.setEventHandler(BLEDisconnected, BLEMIDI_ArduinoBLE::blePeripheralDisconnectHandler); midiChar.setEventHandler(BLEWritten, characteristicWritten); #endif @@ -187,8 +214,8 @@ bool BLEMIDI::begin(const char* deviceName, BLEMIDITransport* ble /*! \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); +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 */ diff --git a/src/hardware/BLEMIDI_ESP32.h b/src/hardware/BLEMIDI_ESP32.h index df9201b..73a3ea7 100644 --- a/src/hardware/BLEMIDI_ESP32.h +++ b/src/hardware/BLEMIDI_ESP32.h @@ -36,15 +36,15 @@ public: _characteristic->notify(); } - bool available(void *pvBuffer) + bool available(byte* pvBuffer) { return xQueueReceive(mRxQueue, pvBuffer, 0); // return immediately when the queue is empty } - void add(const void* value) + void add(byte value) { // called from BLE-MIDI, to add it to a buffer here - xQueueSend(mRxQueue, value, portMAX_DELAY); + xQueueSend(mRxQueue, &value, portMAX_DELAY); } protected: diff --git a/src/hardware/BLEMIDI_ESP32_NimBLE.h b/src/hardware/BLEMIDI_ESP32_NimBLE.h index 2c2887b..4eb706f 100644 --- a/src/hardware/BLEMIDI_ESP32_NimBLE.h +++ b/src/hardware/BLEMIDI_ESP32_NimBLE.h @@ -33,21 +33,22 @@ public: _characteristic->notify(); } - size_t available(void *pvBuffer) + bool available(byte* pvBuffer) { - return xQueueReceive(mRxQueue, pvBuffer, 0); // return immediately when the queue is empty + // return 1 byte from the Queue + return xQueueReceive(mRxQueue, (void*)pvBuffer, 0); // return immediately when the queue is empty } - void add(const void* value) + void add(byte value) { // called from BLE-MIDI, to add it to a buffer here - xQueueSend(mRxQueue, value, portMAX_DELAY); + xQueueSend(mRxQueue, &value, portMAX_DELAY); } protected: void receive(uint8_t* buffer, size_t length) { - // parse the incoming buffer + // forward the buffer so it can be parsed _bleMidiTransport->receive(buffer, length); }