Line data Source code
1 : #pragma once 2 : 3 : #include <Settings/NamespaceSettings.hpp> 4 : 5 : #include <chrono> 6 : #include <condition_variable> 7 : #include <mutex> 8 : #include <thread> 9 : 10 : #include "BLEAPI.hpp" 11 : #include <MIDI_Interfaces/BLEMIDI/BLEMIDIPacketBuilder.hpp> 12 : 13 : BEGIN_CS_NAMESPACE 14 : 15 : /// Class that manages a background thread that sends BLE packets asynchronously. 16 : template <class Derived> 17 : class ThreadedBLEMIDISender { 18 : public: 19 58 : ThreadedBLEMIDISender() = default; 20 : ThreadedBLEMIDISender(const ThreadedBLEMIDISender &) = delete; 21 : ThreadedBLEMIDISender &operator=(const ThreadedBLEMIDISender &) = delete; 22 : ~ThreadedBLEMIDISender(); 23 : 24 : /// Start the background thread. 25 : void begin(); 26 : 27 : struct ProtectedBuilder; 28 : 29 : /// Acquire exclusive access to the buffer to be sent by the timer. 30 : /// @return A RAII wrapper that automatically releases the buffer upon 31 : /// destruction. Just make sure you don't keep any pointers to the 32 : /// `packet` member. 33 : ProtectedBuilder acquirePacket(); 34 : /// Release exclusive access to the buffer and notify the sender thread that 35 : /// data is available. 36 : void releasePacketAndNotify(ProtectedBuilder &lck); 37 : 38 : /// Sends the data immediately without waiting for the timeout. 39 : void sendNow(ProtectedBuilder &lck); 40 : 41 : /// Set the maximum transmission unit of the Bluetooth link. Used to compute 42 : /// the MIDI BLE packet size. 43 : void updateMTU(uint16_t mtu); 44 : /// Get the minimum MTU of all connected clients. 45 : uint16_t getMinMTU() const { return min_mtu; } 46 : /// Force the MTU to an artificially small value (used for testing). 47 : void forceMinMTU(uint16_t mtu); 48 : 49 : /// Set the timeout, the number of milliseconds to buffer the outgoing MIDI 50 : /// messages. 51 : void setTimeout(std::chrono::milliseconds timeout); 52 : 53 : private: 54 : /// Actually perform the BLE notification with the given data. 55 : void sendData(BLEDataView) = delete; // should be implemented by subclass 56 : 57 : /// Function that waits for BLE packets and sends them in the background. 58 : /// It either sends them after a timeout (a given number of milliseconds 59 : /// after the first data was added to the packet), or immediately when it 60 : /// receives a flush signal from the main thread. 61 : bool handleSendEvents(); 62 : 63 : private: 64 : struct { 65 : /// View of the data to send 66 : BLEMIDIPacketBuilder packet; 67 : /// Flag to stop the background thread. 68 : bool stop = false; 69 : /// Flag to tell the sender thread to send the packet immediately. 70 : bool flush = false; 71 : /// Timeout before the sender thread sends a packet. 72 : /// @see @ref setTimeout() 73 29 : std::chrono::milliseconds timeout {10}; 74 : /// Lock to protect all shared data in this struct. 75 : std::mutex mtx; 76 : } shared {}; 77 : /// Condition variable used by the background sender thread to wait for 78 : /// data to send, and for the main thread to wait for the data to be flushed 79 : /// by the sender thread. 80 : std::condition_variable cv; 81 : /// Lock type used to lock the mutex 82 : using lock_t = std::unique_lock<std::mutex>; 83 : /// The background thread responsible for sending the data. 84 : std::thread send_thread; 85 : 86 : private: 87 : /// The minimum MTU of all connected clients. 88 : std::atomic_uint_fast16_t min_mtu {23}; 89 : /// Override the minimum MTU (0 means don't override, nonzero overrides if 90 : /// it's smaller than the minimum MTU of the clients). 91 : /// @see @ref forceMinMTU() 92 : std::atomic_uint_fast16_t force_min_mtu {515}; 93 : 94 : public: 95 : struct ProtectedBuilder { 96 : BLEMIDIPacketBuilder *packet; 97 : lock_t lck; 98 : }; 99 : }; 100 : 101 : END_CS_NAMESPACE 102 : 103 : #include "ThreadedBLEMIDISender.ipp"