LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - BluetoothMIDI_Interface.hpp (source / functions) Hit Total Coverage
Test: ffed98f648fe78e7aa7bdd228474317d40dadbec Lines: 20 31 64.5 %
Date: 2022-05-28 15:22:59 Functions: 8 12 66.7 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.15