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"
|