LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - BluetoothMIDI_Interface.hpp (source / functions) Hit Total Coverage
Test: e224b347cd670555e44f06608ac41bd1ace9d9d8 Lines: 28 104 26.9 %
Date: 2020-09-08 17:44:46 Functions: 6 22 27.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include "BLEMIDI.hpp"
       4             : #include "SerialMIDI_Interface.hpp"
       5             : 
       6             : #include <AH/Error/Error.hpp>
       7             : 
       8             : BEGIN_CS_NAMESPACE
       9             : 
      10             : /**
      11             :  * @brief   Bluetooth Low Energy MIDI Interface for the ESP32.
      12             :  * 
      13             :  * @ingroup MIDIInterfaces
      14             :  */
      15          10 : class BluetoothMIDI_Interface : public Parsing_MIDI_Interface,
      16             :                                 public BLEServerCallbacks,
      17             :                                 public BLECharacteristicCallbacks {
      18             : 
      19             :     // BLE Callbacks
      20             : 
      21           0 :     void onConnect(BLEServer *pServer) override {
      22             :         (void)pServer;
      23           0 :         DEBUGFN("Connected");
      24           0 :         connected++;
      25           0 :     };
      26           0 :     void onDisconnect(BLEServer *pServer) override {
      27             :         (void)pServer;
      28           0 :         DEBUGFN("Disonnected");
      29           0 :         if (!connected) {
      30           0 :             ERROR(F("Error: disconnect event, but was not connected"), 0x7788);
      31           0 :             return;
      32             :         }
      33           0 :         connected--;
      34           0 :     }
      35             : 
      36           0 :     void onRead(BLECharacteristic *pCharacteristic) override {
      37           0 :         DEBUGFN("Read");
      38           0 :         pCharacteristic->setValue(nullptr, 0);
      39           0 :     }
      40           0 :     void onWrite(BLECharacteristic *pCharacteristic) override {
      41           0 :         DEBUGFN("Write: ");
      42           0 :         std::string value = pCharacteristic->getValue();
      43           0 :         const uint8_t *const data =
      44           0 :             reinterpret_cast<const uint8_t *>(value.data());
      45           0 :         size_t len = value.size();
      46           0 :         parse(data, len);
      47           0 :     }
      48             : 
      49             :     constexpr static unsigned long MAX_MESSAGE_TIME = 10000; // microseconds
      50             : 
      51          10 :     unsigned long startTime = 0;
      52             : 
      53             :     constexpr static size_t BUFFER_LENGTH = 1024;
      54             : 
      55       10240 :     uint8_t buffer[BUFFER_LENGTH] = {};
      56          10 :     size_t index = 0;
      57             : 
      58             :     SerialMIDI_Parser parser;
      59             : 
      60             :     BLEMIDI bleMidi;
      61             : 
      62          10 :     uint8_t connected = 0;
      63             : 
      64           0 :     bool hasSpaceFor(size_t bytes) { return bytes <= BUFFER_LENGTH - index; }
      65             : 
      66             :   public:
      67          20 :     BluetoothMIDI_Interface() : Parsing_MIDI_Interface(parser) {}
      68             : 
      69          10 :     void begin() override { bleMidi.begin(this, this); }
      70             : 
      71           0 :     void publish() {
      72           0 :         if (index == 0)
      73           0 :             return;
      74           0 :         if (!connected) {
      75           0 :             DEBUGFN("No connected BLE clients");
      76           0 :             return;
      77             :         }
      78           0 :         bleMidi.notifyValue(buffer, index);
      79           0 :         index = 0;
      80           0 :     }
      81             : 
      82           0 :     MIDIReadEvent read() override {
      83           0 :         update();                         // TODO
      84           0 :         return MIDIReadEvent::NO_MESSAGE; // TODO
      85             :     }
      86             : 
      87             :     template <size_t N>
      88           0 :     void addToBuffer(const uint8_t (&data)[N]) {
      89           0 :         addToBuffer(&data[0], N);
      90           0 :     }
      91             : 
      92           0 :     void addToBuffer(const uint8_t *data, size_t len) {
      93           0 :         bool first = index == 0;
      94           0 :         if (!hasSpaceFor(len + 1 + first)) { // TODO
      95           0 :             DEBUGFN("Buffer full");
      96           0 :             publish();
      97           0 :             if (!hasSpaceFor(len + 1 + first)) { // TODO
      98           0 :                 DEBUGFN("Message is larger than buffer");
      99           0 :                 return;
     100             :             }
     101           0 :         }
     102             : 
     103           0 :         if (first)
     104           0 :             startTime = micros();
     105             : 
     106           0 :         if (first)
     107           0 :             buffer[index++] = 0x80; // header / timestamp msb
     108           0 :         buffer[index++] = 0x80;     // timestamp lsb
     109           0 :         memcpy(&buffer[index], data, len);
     110           0 :         index += len;
     111             : 
     112           0 :         update();
     113           0 :     }
     114             : 
     115           0 :     void update() override {
     116           0 :         if (micros() - startTime >= MAX_MESSAGE_TIME)
     117           0 :             publish();
     118           0 :     }
     119             : 
     120           0 :     void sendImpl(uint8_t header, uint8_t d1, uint8_t d2, uint8_t cn) override {
     121             :         (void)cn;
     122           0 :         uint8_t msg[3] = {header, d1, d2};
     123           0 :         addToBuffer(msg);
     124           0 :     }
     125           0 :     void sendImpl(uint8_t header, uint8_t d1, uint8_t cn) override {
     126             :         (void)cn;
     127           0 :         uint8_t msg[2] = {header, d1};
     128           0 :         addToBuffer(msg);
     129           0 :     }
     130             : 
     131           0 :     void sendImpl(const uint8_t *data, size_t length, uint8_t cn) override {
     132             :         (void)data;
     133             :         (void)length;
     134             :         (void)cn; // TODO
     135           0 :     }
     136             : 
     137           0 :     void sendImpl(uint8_t rt, uint8_t cn) override {
     138             :         (void)rt;
     139             :         (void)cn; // TODO
     140           0 :     }
     141             : 
     142          10 :     void parse(const uint8_t *const data, const size_t len) {
     143             :         // TODO: documentation and link to BLE MIDI spec
     144          10 :         if (len <= 1)
     145           0 :             return;
     146          10 :         if (MIDI_Parser::isData(data[0]))
     147           0 :             return;
     148          10 :         if (MIDI_Parser::isData(data[1]))
     149           1 :             parse(data[1]);
     150          10 :         bool prevWasTimestamp = true;
     151          84 :         for (const uint8_t *d = data + 2; d < data + len; d++) {
     152          74 :             if (MIDI_Parser::isData(*d)) {
     153          39 :                 parse(*d);
     154          39 :                 prevWasTimestamp = false;
     155          39 :             } else {
     156          35 :                 if (prevWasTimestamp)
     157          21 :                     parse(*d);
     158          35 :                 prevWasTimestamp = !prevWasTimestamp;
     159             :             }
     160          74 :         }
     161          10 :     }
     162             : 
     163          61 :     void parse(uint8_t data) {
     164          61 :         event = parser.parse(data);
     165             :         // Best we can do is just retry until the pipe is no longer in exclusive
     166             :         // mode.
     167             :         // Keep in mind that this is executed in the callback of the BLE stack,
     168             :         // I don't know what happens to the Bluetooth connection if we let it
     169             :         // wait for longer than the communication interval.
     170             :         //
     171             :         // TODO: If this causes problems, we could buffer the data until the
     172             :         //       pipe is available for writing again.
     173          61 :         while (!dispatchMIDIEvent(event))
     174             : #ifdef ARDUINO
     175             :             delay(1);
     176             : #else
     177           0 :             usleep(1e3);
     178             : #endif
     179          61 :     }
     180             : 
     181          10 :     BLEMIDI &getBLEMIDI() { return bleMidi; }
     182             : };
     183             : 
     184             : END_CS_NAMESPACE

Generated by: LCOV version 1.14-6-g40580cd