Line data Source code
1 : #pragma once 2 : 3 : #include "BLEMIDI.hpp" 4 : #include "SerialMIDI_Interface.hpp" 5 : 6 : #include <AH/Error/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(BLEServer *pServer) override { 22 : (void)pServer; 23 0 : DEBUGFN("Connected"); 24 0 : connected++; 25 0 : }; 26 0 : void onDisconnect(BLEServer *pServer) override { 27 : (void)pServer; 28 0 : DEBUGFN("Disonnected"); 29 0 : if (!connected) { 30 0 : ERROR(F("Error: disconnect event, but was not connected"), 0x7788); 31 0 : return; 32 : } 33 0 : connected--; 34 0 : } 35 : 36 0 : void onRead(BLECharacteristic *pCharacteristic) override { 37 0 : DEBUGFN("Read"); 38 0 : pCharacteristic->setValue(nullptr, 0); 39 0 : } 40 0 : void onWrite(BLECharacteristic *pCharacteristic) override { 41 0 : DEBUGFN("Write: "); 42 0 : std::string value = pCharacteristic->getValue(); 43 0 : const uint8_t *const data = 44 0 : reinterpret_cast<const uint8_t *>(value.data()); 45 0 : size_t len = value.size(); 46 0 : parse(data, len); 47 0 : } 48 : 49 : constexpr static unsigned long MAX_MESSAGE_TIME = 10000; // microseconds 50 : 51 10 : unsigned long startTime = 0; 52 : 53 : constexpr static size_t BUFFER_LENGTH = 1024; 54 : 55 10240 : uint8_t buffer[BUFFER_LENGTH] = {}; 56 10 : size_t index = 0; 57 : 58 : SerialMIDI_Parser parser; 59 : 60 : BLEMIDI bleMidi; 61 : 62 10 : uint8_t connected = 0; 63 : 64 0 : bool hasSpaceFor(size_t bytes) { return bytes <= BUFFER_LENGTH - index; } 65 : 66 : public: 67 20 : BluetoothMIDI_Interface() : Parsing_MIDI_Interface(parser) {} 68 : 69 10 : void begin() override { bleMidi.begin(this, this); } 70 : 71 0 : void publish() { 72 0 : if (index == 0) 73 0 : return; 74 0 : if (!connected) { 75 0 : DEBUGFN("No connected BLE clients"); 76 0 : return; 77 : } 78 0 : bleMidi.notifyValue(buffer, index); 79 0 : index = 0; 80 0 : } 81 : 82 0 : MIDIReadEvent read() override { 83 0 : update(); // TODO 84 0 : return MIDIReadEvent::NO_MESSAGE; // TODO 85 : } 86 : 87 : template <size_t N> 88 0 : void addToBuffer(const uint8_t (&data)[N]) { 89 0 : addToBuffer(&data[0], N); 90 0 : } 91 : 92 0 : void addToBuffer(const uint8_t *data, size_t len) { 93 0 : bool first = index == 0; 94 0 : if (!hasSpaceFor(len + 1 + first)) { // TODO 95 0 : DEBUGFN("Buffer full"); 96 0 : publish(); 97 0 : if (!hasSpaceFor(len + 1 + first)) { // TODO 98 0 : DEBUGFN("Message is larger than buffer"); 99 0 : return; 100 : } 101 0 : } 102 : 103 0 : if (first) 104 0 : startTime = micros(); 105 : 106 0 : if (first) 107 0 : buffer[index++] = 0x80; // header / timestamp msb 108 0 : buffer[index++] = 0x80; // timestamp lsb 109 0 : memcpy(&buffer[index], data, len); 110 0 : index += len; 111 : 112 0 : update(); 113 0 : } 114 : 115 0 : void update() override { 116 0 : if (micros() - startTime >= MAX_MESSAGE_TIME) 117 0 : publish(); 118 0 : } 119 : 120 0 : void sendImpl(uint8_t header, uint8_t d1, uint8_t d2, uint8_t cn) override { 121 : (void)cn; 122 0 : uint8_t msg[3] = {header, d1, d2}; 123 0 : addToBuffer(msg); 124 0 : } 125 0 : void sendImpl(uint8_t header, uint8_t d1, uint8_t cn) override { 126 : (void)cn; 127 0 : uint8_t msg[2] = {header, d1}; 128 0 : addToBuffer(msg); 129 0 : } 130 : 131 0 : void sendImpl(const uint8_t *data, size_t length, uint8_t cn) override { 132 : (void)data; 133 : (void)length; 134 : (void)cn; // TODO 135 0 : } 136 : 137 0 : void sendImpl(uint8_t rt, uint8_t cn) override { 138 : (void)rt; 139 : (void)cn; // TODO 140 0 : } 141 : 142 10 : void parse(const uint8_t *const data, const size_t len) { 143 : // TODO: documentation and link to BLE MIDI spec 144 10 : if (len <= 1) 145 0 : return; 146 10 : if (MIDI_Parser::isData(data[0])) 147 0 : return; 148 10 : if (MIDI_Parser::isData(data[1])) 149 1 : parse(data[1]); 150 10 : bool prevWasTimestamp = true; 151 84 : for (const uint8_t *d = data + 2; d < data + len; d++) { 152 74 : if (MIDI_Parser::isData(*d)) { 153 39 : parse(*d); 154 39 : prevWasTimestamp = false; 155 39 : } else { 156 35 : if (prevWasTimestamp) 157 21 : parse(*d); 158 35 : prevWasTimestamp = !prevWasTimestamp; 159 : } 160 74 : } 161 10 : } 162 : 163 61 : void parse(uint8_t data) { 164 61 : event = parser.parse(data); 165 : // Best we can do is just retry until the pipe is no longer in exclusive 166 : // mode. 167 : // Keep in mind that this is executed in the callback of the BLE stack, 168 : // I don't know what happens to the Bluetooth connection if we let it 169 : // wait for longer than the communication interval. 170 : // 171 : // TODO: If this causes problems, we could buffer the data until the 172 : // pipe is available for writing again. 173 61 : while (!dispatchMIDIEvent(event)) 174 : #ifdef ARDUINO 175 : delay(1); 176 : #else 177 0 : usleep(1e3); 178 : #endif 179 61 : } 180 : 181 10 : BLEMIDI &getBLEMIDI() { return bleMidi; } 182 : }; 183 : 184 : END_CS_NAMESPACE