Line data Source code
1 : #pragma once 2 : 3 : #include <AH/Error/Error.hpp> 4 : 5 : #include "BLEMIDI/BLEMIDIPacketBuilder.hpp" 6 : #include "BLEMIDI/MIDIMessageQueue.hpp" 7 : #include "MIDI_Interface.hpp" 8 : #include "Util/ESP32Threads.hpp" 9 : #include <MIDI_Parsers/BLEMIDIParser.hpp> 10 : #include <MIDI_Parsers/SerialMIDI_Parser.hpp> 11 : 12 : #include <atomic> 13 : #include <chrono> 14 : #include <condition_variable> 15 : #include <mutex> 16 : #include <thread> 17 : 18 : #ifndef ARDUINO 19 : #include <gmock/gmock.h> 20 : #endif 21 : 22 : BEGIN_CS_NAMESPACE 23 : 24 : /** 25 : * @brief Bluetooth Low Energy MIDI Interface for the ESP32. 26 : * 27 : * @ingroup MIDIInterfaces 28 : */ 29 : class BluetoothMIDI_Interface : public MIDI_Interface { 30 : 31 : public: 32 56 : BluetoothMIDI_Interface() { 33 28 : if (instance) 34 0 : FATAL_ERROR(F("Only one instance is supported"), 0x1345); 35 28 : instance = this; 36 28 : }; 37 57 : ~BluetoothMIDI_Interface() { 38 28 : instance = nullptr; 39 28 : stopSendingThread(); 40 28 : end(); 41 29 : } 42 : 43 : public: 44 : /// Send the buffered MIDI BLE packet immediately. 45 12 : void flush() { 46 24 : lock_t lock(mtx); 47 12 : flushImpl(lock); 48 12 : } 49 : 50 : /// Set the timeout, the number of milliseconds to buffer the outgoing MIDI 51 : /// messages. A shorter timeout usually results in lower latency, but also 52 : /// causes more overhead, because more packets might be required. 53 4 : void setTimeout(std::chrono::milliseconds timeout) { 54 4 : lock_t lock(mtx); 55 4 : this->timeout = timeout; 56 4 : } 57 : 58 : public: 59 : /// Set the BLE device name. Must be called before @ref begin(). 60 : void setName(const char *name); 61 : 62 : void begin() override; 63 : void end(); 64 : 65 : MIDIReadEvent read(); 66 : 67 11 : void update() override { MIDI_Interface::updateIncoming(this); } 68 : 69 : private: 70 : #if !DISABLE_PIPES 71 0 : void handleStall() override { MIDI_Interface::handleStall(this); } 72 : #endif 73 : 74 : public: 75 : /// Return the received channel voice message. 76 : ChannelMessage getChannelMessage() const; 77 : /// Return the received system common message. 78 : SysCommonMessage getSysCommonMessage() const; 79 : /// Return the received real-time message. 80 : RealTimeMessage getRealTimeMessage() const; 81 : /// Return the received system exclusive message. 82 : SysExMessage getSysExMessage() const; 83 : /// Get the BLE-MIDI timestamp of the latest MIDI message. 84 : /// @note Invalid for SysEx chunks (except the last chunk of a message). 85 : uint16_t getTimestamp() const; 86 : 87 : protected: 88 : // MIDI send implementations 89 : void sendChannelMessageImpl(ChannelMessage) override; 90 : void sendSysCommonImpl(SysCommonMessage) override; 91 : void sendSysExImpl(SysExMessage) override; 92 : void sendRealTimeImpl(RealTimeMessage) override; 93 0 : void sendNowImpl() override { flush(); } 94 : 95 : void sendChannelMessageImpl3Bytes(ChannelMessage); 96 : void sendChannelMessageImpl2Bytes(ChannelMessage); 97 : 98 : public: 99 : void parse(const uint8_t *const data, const size_t len); 100 : 101 : private: 102 : /// The minimum MTU of all connected clients. 103 : std::atomic_uint_fast16_t min_mtu{23}; 104 : /// Override the minimum MTU (0 means don't override, nonzero overrides if 105 : /// it's smaller than the minimum MTU of the clients). 106 : /// @see @ref forceMinMTU() 107 : std::atomic_uint_fast16_t force_min_mtu{0}; 108 : 109 : /// Set the maximum transmission unit of the Bluetooth link. Used to compute 110 : /// the MIDI BLE packet size. 111 : void updateMTU(uint16_t mtu); 112 : 113 : public: 114 : /// Get the minimum MTU of all connected clients. 115 : uint16_t getMinMTU() const { return min_mtu; } 116 : 117 : /// Force the MTU to an artificially small value (used for testing). 118 : void forceMinMTU(uint16_t mtu); 119 : 120 : private: 121 : /// Only one active instance. 122 : static BluetoothMIDI_Interface *instance; 123 : /// MIDI Parser for incoming data. 124 : SerialMIDI_Parser parser{false}; 125 : /// Builds outgoing MIDI BLE packets. 126 : BLEMIDIPacketBuilder packetbuilder; 127 : /// Queue for incoming MIDI messages. 128 : MIDIMessageQueue queue{64}; 129 : /// Incoming message that can be from retrieved using the 130 : /// `getChannelMessage()`, `getSysCommonMessage()`, `getRealTimeMessage()` 131 : /// and `getSysExMessage()` methods. 132 : MIDIMessageQueue::MIDIMessageQueueElement incomingMessage; 133 : 134 : private: 135 : // Synchronization for asynchronous BLE sending 136 : 137 : /// Lock type used to lock the mutex 138 : using lock_t = std::unique_lock<std::mutex>; 139 : /// Mutex to lock the MIDI BLE packet builder and the flush flag. 140 : std::mutex mtx; 141 : /// Condition variable used by the background sender thread to wait for 142 : /// data to send, and for the main thread to wait for the data to be flushed 143 : /// by the sender thread. 144 : std::condition_variable cv; 145 : /// Background thread that sends the actual MIDI BLE packets. 146 : std::thread send_thread; 147 : /// Flag to stop the background thread. 148 : std::atomic_bool stop_sending{false}; 149 : /// Flag to tell the sender thread to send the packet immediately. 150 : bool flushnow = false; 151 : /// Timeout before the sender thread sends a packet. 152 : /// @see @ref setTimeout() 153 28 : std::chrono::milliseconds timeout{10}; 154 : 155 : private: 156 : /// Launch a thread that sends the BLE packets in the background. 157 : void startSendingThread(); 158 : 159 : /// Function that waits for BLE packets and sends them in the background. 160 : /// It either sends them after a timeout (a given number of milliseconds 161 : /// after the first data was added to the packet), or immediately when it 162 : /// receives a flush signal from the main thread. 163 : bool handleSendEvents(); 164 : 165 : /// Tell the background BLE sender thread to send the current packet. 166 : /// Blocks until the packet is sent. 167 : /// 168 : /// @param lock 169 : /// Lock should be locked at entry, will still be locked on exit. 170 : void flushImpl(lock_t &lock); 171 : 172 : #if !defined(ARDUINO) && !defined(DOXYGEN) 173 : public: 174 : #endif 175 : /// Tell the background BLE sender thread to stop gracefully, and join it. 176 : void stopSendingThread(); 177 : 178 : public: 179 0 : static void midi_write_callback(const uint8_t *data, size_t length) { 180 0 : if (instance) 181 0 : instance->parse(data, length); 182 0 : } 183 : 184 0 : static void midi_mtu_callback(uint16_t mtu) { 185 0 : if (instance) 186 0 : instance->updateMTU(mtu); 187 0 : } 188 : 189 : #ifdef ARDUINO 190 : private: 191 : void notifyMIDIBLE(const std::vector<uint8_t> &packet); 192 : #else 193 : public: 194 50 : MOCK_METHOD(void, notifyMIDIBLE, (const std::vector<uint8_t> &), ()); 195 : #endif 196 : }; 197 : 198 : END_CS_NAMESPACE