Line data Source code
1 : #pragma once 2 : 3 : #include "BLEMIDI.hpp" 4 : #include "SerialMIDI_Interface.hpp" 5 : 6 : #include <Helpers/Error.hpp> 7 : 8 : BEGIN_CS_NAMESPACE 9 : 10 : /** 11 : * @brief Bluetooth Low Energy MIDI Interface for the ESP32. 12 : * 13 : * @ingroup MIDIInterfaces 14 : */ 15 10 : class BluetoothMIDI_Interface : public Parsing_MIDI_Interface, 16 : public BLEServerCallbacks, 17 : public BLECharacteristicCallbacks { 18 : 19 : // BLE Callbacks 20 : 21 0 : void onConnect(UNUSED_PARAM BLEServer *pServer) override { 22 0 : DEBUGFN("Connected"); 23 0 : connected++; 24 0 : }; 25 0 : void onDisconnect(UNUSED_PARAM BLEServer *pServer) override { 26 0 : DEBUGFN("Disonnected"); 27 0 : if (!connected) { 28 0 : ERROR(F("Error: disconnect event, but was not connected"), 0x7788); 29 0 : return; 30 : } 31 0 : connected--; 32 0 : } 33 : 34 0 : void onRead(BLECharacteristic *pCharacteristic) override { 35 0 : DEBUGFN("Read"); 36 0 : pCharacteristic->setValue(nullptr, 0); 37 0 : } 38 0 : void onWrite(BLECharacteristic *pCharacteristic) override { 39 0 : DEBUGFN("Write: "); 40 0 : std::string value = pCharacteristic->getValue(); 41 0 : const uint8_t *const data = 42 0 : reinterpret_cast<const uint8_t *>(value.data()); 43 0 : size_t len = value.size(); 44 0 : for (size_t i = 0; i < len; i++) { 45 0 : Serial.print(data[i], HEX); 46 0 : Serial.print(' '); 47 0 : } 48 0 : Serial.println(); 49 0 : parse(data, len); 50 0 : } 51 : 52 : constexpr static unsigned long MAX_MESSAGE_TIME = 10000; // microseconds 53 : 54 10 : unsigned long startTime = 0; 55 : 56 : constexpr static size_t BUFFER_LENGTH = 1024; 57 : 58 10240 : uint8_t buffer[BUFFER_LENGTH] = {}; 59 10 : size_t index = 0; 60 : 61 : SerialMIDI_Parser parser; 62 : 63 : BLEMIDI bleMidi; 64 : 65 10 : uint8_t connected = 0; 66 : 67 0 : bool hasSpaceFor(size_t bytes) { return index + bytes < BUFFER_LENGTH; } 68 : 69 : public: 70 20 : BluetoothMIDI_Interface() : Parsing_MIDI_Interface(parser) {} 71 : 72 10 : void begin() override { bleMidi.begin(this, this); } 73 : 74 0 : void publish() { 75 0 : if (index == 0) 76 0 : return; 77 0 : if (!connected) { 78 0 : DEBUGFN("No connected BLE clients"); 79 0 : return; 80 : } 81 0 : bleMidi.notifyValue(buffer, index); 82 0 : index = 0; 83 0 : } 84 : 85 0 : MIDI_read_t read() override { 86 0 : update(); // TODO 87 0 : return NO_MESSAGE; // TODO 88 : } 89 : 90 : template <size_t N> 91 0 : void addToBuffer(const uint8_t (&data)[N]) { 92 0 : addToBuffer(&data[0], N); 93 0 : } 94 : 95 0 : void addToBuffer(const uint8_t *data, size_t len) { 96 0 : bool first = index == 0; 97 0 : if (!hasSpaceFor(len + 1 + first)) { // TODO 98 0 : DEBUGFN("Buffer full"); 99 0 : publish(); 100 0 : if (!hasSpaceFor(len + 1 + first)) { // TODO 101 0 : DEBUGFN("Message is larger than buffer"); 102 0 : return; 103 : } 104 0 : } 105 : 106 0 : if (first) 107 0 : startTime = micros(); 108 : 109 0 : if (first) 110 0 : buffer[index++] = 0x80; // header / timestamp msb 111 0 : buffer[index++] = 0x80; // timestamp lsb 112 0 : memcpy(&buffer[index], data, len); 113 0 : index += len; 114 : 115 0 : update(); 116 0 : } 117 : 118 0 : void update() override { 119 0 : if (micros() - startTime >= MAX_MESSAGE_TIME) 120 0 : publish(); 121 0 : } 122 : 123 0 : void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t d2, 124 : uint8_t cn) override { 125 : (void)cn; 126 0 : uint8_t msg[3] = {uint8_t(m | c), d1, d2}; 127 0 : addToBuffer(msg); 128 0 : } 129 0 : void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t cn) override { 130 : (void)cn; 131 0 : uint8_t msg[2] = {uint8_t(m | c), d1}; 132 0 : addToBuffer(msg); 133 0 : } 134 : 135 0 : void sendImpl(const uint8_t *data, size_t length, uint8_t cn) override { 136 : (void)data; 137 : (void)length; 138 : (void)cn; // TODO 139 0 : } 140 : 141 0 : void sendImpl(uint8_t rt, uint8_t cn) override { 142 : (void)rt; 143 : (void)cn; // TODO 144 0 : } 145 : 146 10 : void parse(const uint8_t *const data, const size_t len) { 147 10 : if (len <= 1) 148 0 : return; 149 10 : if (MIDI_Parser::isData(data[0])) 150 0 : return; 151 10 : if (MIDI_Parser::isData(data[1])) 152 1 : parse(data[1]); 153 10 : bool prevWasTimestamp = true; 154 84 : for (const uint8_t *d = data + 2; d < data + len; d++) { 155 74 : if (MIDI_Parser::isData(*d)) { 156 39 : parse(*d); 157 39 : prevWasTimestamp = false; 158 39 : } else { 159 35 : if (prevWasTimestamp) 160 21 : parse(*d); 161 35 : prevWasTimestamp = !prevWasTimestamp; 162 : } 163 74 : } 164 10 : } 165 : 166 61 : void parse(uint8_t data) { 167 61 : MIDI_read_t event = parser.parse(data); 168 61 : dispatchMIDIEvent(event); 169 61 : } 170 : 171 10 : BLEMIDI &getBLEMIDI() { return bleMidi; } 172 : }; 173 : 174 : END_CS_NAMESPACE