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 : for (size_t i = 0; i < len; i++) { 47 0 : Serial.print(data[i], HEX); 48 0 : Serial.print(' '); 49 0 : } 50 0 : Serial.println(); 51 0 : parse(data, len); 52 0 : } 53 : 54 : constexpr static unsigned long MAX_MESSAGE_TIME = 10000; // microseconds 55 : 56 10 : unsigned long startTime = 0; 57 : 58 : constexpr static size_t BUFFER_LENGTH = 1024; 59 : 60 10240 : uint8_t buffer[BUFFER_LENGTH] = {}; 61 10 : size_t index = 0; 62 : 63 : SerialMIDI_Parser parser; 64 : 65 : BLEMIDI bleMidi; 66 : 67 10 : uint8_t connected = 0; 68 : 69 0 : bool hasSpaceFor(size_t bytes) { return index + bytes < BUFFER_LENGTH; } 70 : 71 : public: 72 20 : BluetoothMIDI_Interface() : Parsing_MIDI_Interface(parser) {} 73 : 74 10 : void begin() override { bleMidi.begin(this, this); } 75 : 76 0 : void publish() { 77 0 : if (index == 0) 78 0 : return; 79 0 : if (!connected) { 80 0 : DEBUGFN("No connected BLE clients"); 81 0 : return; 82 : } 83 0 : bleMidi.notifyValue(buffer, index); 84 0 : index = 0; 85 0 : } 86 : 87 0 : MIDI_read_t read() override { 88 0 : update(); // TODO 89 0 : return NO_MESSAGE; // TODO 90 : } 91 : 92 : template <size_t N> 93 0 : void addToBuffer(const uint8_t (&data)[N]) { 94 0 : addToBuffer(&data[0], N); 95 0 : } 96 : 97 0 : void addToBuffer(const uint8_t *data, size_t len) { 98 0 : bool first = index == 0; 99 0 : if (!hasSpaceFor(len + 1 + first)) { // TODO 100 0 : DEBUGFN("Buffer full"); 101 0 : publish(); 102 0 : if (!hasSpaceFor(len + 1 + first)) { // TODO 103 0 : DEBUGFN("Message is larger than buffer"); 104 0 : return; 105 : } 106 0 : } 107 : 108 0 : if (first) 109 0 : startTime = micros(); 110 : 111 0 : if (first) 112 0 : buffer[index++] = 0x80; // header / timestamp msb 113 0 : buffer[index++] = 0x80; // timestamp lsb 114 0 : memcpy(&buffer[index], data, len); 115 0 : index += len; 116 : 117 0 : update(); 118 0 : } 119 : 120 0 : void update() override { 121 0 : if (micros() - startTime >= MAX_MESSAGE_TIME) 122 0 : publish(); 123 0 : } 124 : 125 0 : void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t d2, 126 : uint8_t cn) override { 127 : (void)cn; 128 0 : uint8_t msg[3] = {uint8_t(m | c), d1, d2}; 129 0 : addToBuffer(msg); 130 0 : } 131 0 : void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t cn) override { 132 : (void)cn; 133 0 : uint8_t msg[2] = {uint8_t(m | c), d1}; 134 0 : addToBuffer(msg); 135 0 : } 136 : 137 0 : void sendImpl(const uint8_t *data, size_t length, uint8_t cn) override { 138 : (void)data; 139 : (void)length; 140 : (void)cn; // TODO 141 0 : } 142 : 143 0 : void sendImpl(uint8_t rt, uint8_t cn) override { 144 : (void)rt; 145 : (void)cn; // TODO 146 0 : } 147 : 148 10 : void parse(const uint8_t *const data, const size_t len) { 149 10 : if (len <= 1) 150 0 : return; 151 10 : if (MIDI_Parser::isData(data[0])) 152 0 : return; 153 10 : if (MIDI_Parser::isData(data[1])) 154 1 : parse(data[1]); 155 10 : bool prevWasTimestamp = true; 156 84 : for (const uint8_t *d = data + 2; d < data + len; d++) { 157 74 : if (MIDI_Parser::isData(*d)) { 158 39 : parse(*d); 159 39 : prevWasTimestamp = false; 160 39 : } else { 161 35 : if (prevWasTimestamp) 162 21 : parse(*d); 163 35 : prevWasTimestamp = !prevWasTimestamp; 164 : } 165 74 : } 166 10 : } 167 : 168 61 : void parse(uint8_t data) { 169 61 : MIDI_read_t event = parser.parse(data); 170 61 : dispatchMIDIEvent(event); 171 61 : } 172 : 173 10 : BLEMIDI &getBLEMIDI() { return bleMidi; } 174 : }; 175 : 176 : END_CS_NAMESPACE