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 0 : void handleStall() override { MIDI_Interface::handleStall(this); } 71 : 72 : public: 73 : /// Return the received channel voice message. 74 : ChannelMessage getChannelMessage() const; 75 : /// Return the received system common message. 76 : SysCommonMessage getSysCommonMessage() const; 77 : /// Return the received real-time message. 78 : RealTimeMessage getRealTimeMessage() const; 79 : /// Return the received system exclusive message. 80 : SysExMessage getSysExMessage() const; 81 : /// Get the BLE-MIDI timestamp of the latest MIDI message. 82 : /// @note Invalid for SysEx chunks (except the last chunk of a message). 83 : uint16_t getTimestamp() const; 84 : 85 : protected: 86 : // MIDI send implementations 87 : void sendChannelMessageImpl(ChannelMessage) override; 88 : void sendSysCommonImpl(SysCommonMessage) override; 89 : void sendSysExImpl(SysExMessage) override; 90 : void sendRealTimeImpl(RealTimeMessage) override; 91 0 : void sendNowImpl() override { flush(); } 92 : 93 : void sendChannelMessageImpl3Bytes(ChannelMessage); 94 : void sendChannelMessageImpl2Bytes(ChannelMessage); 95 : 96 : public: 97 : void parse(const uint8_t *const data, const size_t len); 98 : 99 : private: 100 : /// The minimum MTU of all connected clients. 101 : std::atomic_uint_fast16_t min_mtu{23}; 102 : /// Override the minimum MTU (0 means don't override, nonzero overrides if 103 : /// it's smaller than the minimum MTU of the clients). 104 : /// @see @ref forceMinMTU() 105 : std::atomic_uint_fast16_t force_min_mtu{0}; 106 : 107 : /// Set the maximum transmission unit of the Bluetooth link. Used to compute 108 : /// the MIDI BLE packet size. 109 : void updateMTU(uint16_t mtu); 110 : 111 : public: 112 : /// Get the minimum MTU of all connected clients. 113 : uint16_t getMinMTU() const { return min_mtu; } 114 : 115 : /// Force the MTU to an artificially small value (used for testing). 116 : void forceMinMTU(uint16_t mtu); 117 : 118 : private: 119 : /// Only one active instance. 120 : static BluetoothMIDI_Interface *instance; 121 : /// MIDI Parser for incoming data. 122 : SerialMIDI_Parser parser{false}; 123 : /// Builds outgoing MIDI BLE packets. 124 : BLEMIDIPacketBuilder packetbuilder; 125 : /// Queue for incoming MIDI messages. 126 : MIDIMessageQueue queue{64}; 127 : /// Incoming message that can be from retrieved using the 128 : /// `getChannelMessage()`, `getSysCommonMessage()`, `getRealTimeMessage()` 129 : /// and `getSysExMessage()` methods. 130 : MIDIMessageQueue::MIDIMessageQueueElement incomingMessage; 131 : 132 : private: 133 : // Synchronization for asynchronous BLE sending 134 : 135 : /// Lock type used to lock the mutex 136 : using lock_t = std::unique_lock<std::mutex>; 137 : /// Mutex to lock the MIDI BLE packet builder and the flush flag. 138 : std::mutex mtx; 139 : /// Condition variable used by the background sender thread to wait for 140 : /// data to send, and for the main thread to wait for the data to be flushed 141 : /// by the sender thread. 142 : std::condition_variable cv; 143 : /// Background thread that sends the actual MIDI BLE packets. 144 : std::thread send_thread; 145 : /// Flag to stop the background thread. 146 : std::atomic_bool stop_sending{false}; 147 : /// Flag to tell the sender thread to send the packet immediately. 148 : bool flushnow = false; 149 : /// Timeout before the sender thread sends a packet. 150 : /// @see @ref setTimeout() 151 28 : std::chrono::milliseconds timeout{10}; 152 : 153 : private: 154 : /// Launch a thread that sends the BLE packets in the background. 155 : void startSendingThread(); 156 : 157 : /// Function that waits for BLE packets and sends them in the background. 158 : /// It either sends them after a timeout (a given number of milliseconds 159 : /// after the first data was added to the packet), or immediately when it 160 : /// receives a flush signal from the main thread. 161 : bool handleSendEvents(); 162 : 163 : /// Tell the background BLE sender thread to send the current packet. 164 : /// Blocks until the packet is sent. 165 : /// 166 : /// @param lock 167 : /// Lock should be locked at entry, will still be locked on exit. 168 : void flushImpl(lock_t &lock); 169 : 170 : #if !defined(ARDUINO) && !defined(DOXYGEN) 171 : public: 172 : #endif 173 : /// Tell the background BLE sender thread to stop gracefully, and join it. 174 : void stopSendingThread(); 175 : 176 : public: 177 0 : static void midi_write_callback(const uint8_t *data, size_t length) { 178 0 : if (instance) 179 0 : instance->parse(data, length); 180 0 : } 181 : 182 0 : static void midi_mtu_callback(uint16_t mtu) { 183 0 : if (instance) 184 0 : instance->updateMTU(mtu); 185 0 : } 186 : 187 : #ifdef ARDUINO 188 : private: 189 : void notifyMIDIBLE(const std::vector<uint8_t> &packet); 190 : #else 191 : public: 192 50 : MOCK_METHOD(void, notifyMIDIBLE, (const std::vector<uint8_t> &), ()); 193 : #endif 194 : }; 195 : 196 : END_CS_NAMESPACE