LCOV - code coverage report
Current view: top level - src/MIDI_Interfaces - USBMIDI_Interface.hpp (source / functions) Hit Total Coverage
Test: 90a1b9beff85a60dc6ebcea034a947a845e56960 Lines: 40 43 93.0 %
Date: 2019-11-30 15:53:32 Functions: 12 13 92.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include "MIDI_Interface.hpp"
       4             : #include <AH/Error/Error.hpp>
       5             : #include <AH/Teensy/TeensyUSBTypes.hpp>
       6             : #include <MIDI_Parsers/USBMIDI_Parser.hpp>
       7             : 
       8             : #ifdef TEENSY_MIDIUSB_ENABLED
       9             : #ifndef __AVR_AT90USB1286__ // Probably Teensy 3.x
      10             : #include <usb_dev.h>
      11             : #else // Teensy++ 2.0
      12             : #include <usb_private.h>
      13             : #endif
      14             : #elif defined TEENSYDUINO
      15             : #warning                                                                       \
      16             :     "Teensy: USB MIDI not enabled. Enable it from the Tools > USB Type menu."
      17             : #endif
      18             : 
      19             : // If the main MCU has a USB connection but is not a Teensy
      20             : #if defined(USBCON) && !defined(TEENSYDUINO)
      21             : #include "MIDIUSB.h"
      22             : #endif
      23             : 
      24             : #ifndef ARDUINO
      25             : #include <gmock-wrapper.h>
      26             : #endif
      27             : 
      28             : // If the main MCU has a USB connection or is a Teensy with MIDI USB type
      29             : #if defined(USBCON) || defined(TEENSY_MIDIUSB_ENABLED) || !defined(ARDUINO)
      30             : 
      31             : BEGIN_CS_NAMESPACE
      32             : 
      33             : /**
      34             :  * @brief   A class for MIDI interfaces sending MIDI messages over a USB MIDI
      35             :  *          connection.
      36             :  * 
      37             :  * @note    See the [MIDI over USB Wiki]
      38             :  *          (https://github.com/tttapa/MIDI_controller/wiki/MIDI-over-USB)
      39             :  *          for more information.
      40             :  * 
      41             :  * @ingroup MIDIInterfaces
      42             :  */
      43          16 : class USBMIDI_Interface : public Parsing_MIDI_Interface {
      44             :   public:
      45             :     /**
      46             :      * @brief   Construct a new USBMIDI_Interface.
      47             :      */
      48          16 :     USBMIDI_Interface() : Parsing_MIDI_Interface(parser) {}
      49             : 
      50             :   private:
      51             :     USBMIDI_Parser parser;
      52             : 
      53             : // If this is a test on PC
      54             : #ifndef ARDUINO
      55             : 
      56             :   public:
      57             :     W_SUGGEST_OVERRIDE_OFF
      58             : 
      59          40 :     MOCK_METHOD5(writeUSBPacket,
      60             :                  void(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t));
      61             :     using MIDIUSBPacket_t = Array<uint8_t, 4>;
      62           8 :     MOCK_METHOD0(readUSBPacket, MIDIUSBPacket_t(void));
      63          11 :     void flush() {}
      64             : 
      65             :     W_SUGGEST_OVERRIDE_ON
      66             : 
      67           3 :     MIDI_read_t read() override {
      68           5 :         for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
      69           5 :             MIDIUSBPacket_t rx_packet = readUSBPacket();
      70           5 :             if (!rx_packet[0])
      71           0 :                 return NO_MESSAGE;
      72           5 :             MIDI_read_t parseResult = parser.parse(rx_packet.data);
      73           5 :             if (parseResult != NO_MESSAGE)
      74           3 :                 return parseResult;
      75           2 :         }
      76           0 :         return NO_MESSAGE;
      77           3 :     }
      78             : 
      79             : // If it's a Teensy board
      80             : #elif defined(TEENSYDUINO)
      81             : #ifndef __AVR_AT90USB1286__ // Probably Teensy 3.x
      82             :     void writeUSBPacket(uint8_t cn, uint8_t cin, uint8_t d0, uint8_t d1,
      83             :                         uint8_t d2) {
      84             :         usb_midi_write_packed((cn << 4) | cin | // CN|CIN
      85             :                               (d0 << 8) |       // status
      86             :                               (d1 << 16) |      // data 1
      87             :                               (d2 << 24));      // data 2
      88             :     }
      89             : #else                       // Teensy++ 2.0
      90             :     void writeUSBPacket(uint8_t cn, uint8_t cin, uint8_t d0, uint8_t d1,
      91             :                         uint8_t d2) {
      92             :         uint8_t intr_state, timeout;
      93             : 
      94             :         if (!usb_configuration)
      95             :             return;
      96             :         intr_state = SREG;
      97             :         cli();
      98             :         UENUM = MIDI_TX_ENDPOINT;
      99             :         timeout = UDFNUML + 2;
     100             :         while (1) {
     101             :             // are we ready to transmit?
     102             :             if (UEINTX & (1 << RWAL))
     103             :                 break;
     104             :             SREG = intr_state;
     105             :             if (UDFNUML == timeout)
     106             :                 return;
     107             :             if (!usb_configuration)
     108             :                 return;
     109             :             intr_state = SREG;
     110             :             cli();
     111             :             UENUM = MIDI_TX_ENDPOINT;
     112             :         }
     113             :         UEDATX = (cn << 4) | cin;
     114             :         UEDATX = d0;
     115             :         UEDATX = d1;
     116             :         UEDATX = d2;
     117             :         if (!(UEINTX & (1 << RWAL)))
     118             :             UEINTX = 0x3A;
     119             :         SREG = intr_state;
     120             :     }
     121             : #endif                      // Teensy++ 2.0
     122             : 
     123             :     void flush() {}
     124             : 
     125             : // If the main MCU has a USB connection but is not a Teensy
     126             : #elif defined(USBCON)
     127             : 
     128             :     void writeUSBPacket(uint8_t cn, uint8_t cin, uint8_t d0, uint8_t d1,
     129             :                         uint8_t d2) {
     130             :         midiEventPacket_t msg = {
     131             :             uint8_t((cn << 4) | cin), // CN|CIN
     132             :             d0,                       // status
     133             :             d1,                       // data 1
     134             :             d2,                       // data 2
     135             :         };
     136             :         MidiUSB.sendMIDI(msg);
     137             :     }
     138             : 
     139             :     void flush() { MidiUSB.flush(); }
     140             : 
     141             : #endif
     142             : 
     143           2 :     void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t d2,
     144             :                   uint8_t cn) override {
     145           4 :         writeUSBPacket(cn, m >> 4, // CN|CIN
     146           2 :                        (m | c),    // status
     147           2 :                        d1,         // data 1
     148           2 :                        d2);        // data 2
     149           2 :         flush();
     150           2 :     }
     151             : 
     152           1 :     void sendImpl(uint8_t m, uint8_t c, uint8_t d1, uint8_t cn) override {
     153           1 :         sendImpl(m, c, d1, 0, cn);
     154           1 :     }
     155             : 
     156           8 :     void sendImpl(const uint8_t *data, size_t length, uint8_t cn) override {
     157          17 :         while (length > 3) {
     158           9 :             writeUSBPacket(cn, 0x4, data[0], data[1], data[2]);
     159           9 :             data += 3;
     160           9 :             length -= 3;
     161             :         }
     162           8 :         switch (length) {
     163           3 :             case 3: writeUSBPacket(cn, 0x7, data[0], data[1], data[2]); break;
     164           3 :             case 2: writeUSBPacket(cn, 0x6, data[0], data[1], 0); break;
     165           2 :             case 1: writeUSBPacket(cn, 0x5, data[0], 0, 0); break;
     166           0 :             default: break;
     167             :         }
     168           8 :         flush();
     169           8 :     }
     170             : 
     171           1 :     void sendImpl(uint8_t rt, uint8_t cn) override {
     172           2 :         writeUSBPacket(cn, 0xF, // CN|CIN
     173           1 :                        rt,      // single byte
     174             :                        0,       // no data
     175             :                        0);      // no data
     176           1 :         flush();
     177           1 :     }
     178             : 
     179             :   public:
     180             : // If it's a Teensy board
     181             : #if defined(TEENSYDUINO)
     182             : #ifndef __AVR_AT90USB1286__ // Probably Teensy 3.x
     183             :     MIDI_read_t read() override {
     184             :         for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
     185             :             if (rx_packet == nullptr) { // If there's no previous packet
     186             :                 if (!usb_configuration) // Check USB configuration
     187             :                     return NO_MESSAGE;
     188             :                 rx_packet = usb_rx(
     189             :                     MIDI_RX_ENDPOINT); // Read a new packet from the USB buffer
     190             :                 if (rx_packet == nullptr) { // If there's no new packet, return
     191             :                     return NO_MESSAGE;
     192             :                 }
     193             :                 if (rx_packet->len < 4) {
     194             :                     // If the lenght is less than 4, it's not a valid MIDI USB
     195             :                     // packet
     196             :                     usb_free(rx_packet); // Free the packet
     197             :                     rx_packet = nullptr; // Read new packet next time around
     198             :                     return NO_MESSAGE;
     199             :                 }
     200             :             }
     201             : 
     202             :             size_t index = rx_packet->index;
     203             :             uint8_t *data = rx_packet->buf + index; // A pointer to this packet
     204             : 
     205             :             MIDI_read_t parseResult = parser.parse(data);
     206             : 
     207             :             index += 4;
     208             :             if (index < rx_packet->len) {
     209             :                 // If the packet is longer than 4 bytes
     210             :                 rx_packet->index = index; // Change the index and read the next
     211             :                                           // four bytes on next read
     212             :             } else {
     213             :                 // If the packet was only 4 bytes long
     214             :                 usb_free(rx_packet);                  // Free the packet
     215             :                 rx_packet = usb_rx(MIDI_RX_ENDPOINT); // Read the next packet
     216             :             }
     217             :             if (parseResult != NO_MESSAGE)
     218             :                 return parseResult;
     219             :         }
     220             :         return NO_MESSAGE;
     221             :     }
     222             : #else  // Teensy++ 2.0
     223             :     MIDI_read_t read() override {
     224             :         for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
     225             :             uint8_t data[4];
     226             : 
     227             :             // https://github.com/PaulStoffregen/cores/blob/73ea157600a7082686d9cc48786a73caa7567da9/usb_midi/usb_api.cpp#L195
     228             :             uint8_t c, intr_state;
     229             : 
     230             :             intr_state = SREG;
     231             :             cli();
     232             :             if (!usb_configuration) {
     233             :                 SREG = intr_state;
     234             :                 return NO_MESSAGE;
     235             :             }
     236             :             UENUM = MIDI_RX_ENDPOINT;
     237             :         retry:
     238             :             c = UEINTX;
     239             :             if (!(c & (1 << RWAL))) {
     240             :                 if (c & (1 << RXOUTI)) {
     241             :                     UEINTX = 0x6B;
     242             :                     goto retry;
     243             :                 }
     244             :                 SREG = intr_state;
     245             :                 return NO_MESSAGE;
     246             :             }
     247             :             data[0] = UEDATX;
     248             :             data[1] = UEDATX;
     249             :             data[2] = UEDATX;
     250             :             data[3] = UEDATX;
     251             :             if (!(UEINTX & (1 << RWAL)))
     252             :                 UEINTX = 0x6B;
     253             :             SREG = intr_state;
     254             : 
     255             :             MIDI_read_t parseResult = parser.parse(data);
     256             : 
     257             :             if (parseResult != NO_MESSAGE)
     258             :                 return parseResult;
     259             :         }
     260             :         return NO_MESSAGE;
     261             :     }
     262             : #endif // Teensy++ 2.0
     263             : 
     264             : // If the main MCU has a USB connection but is not a Teensy → MIDIUSB library
     265             : #elif defined(USBCON)
     266             :     MIDI_read_t read() override {
     267             :         for (uint8_t i = 0; i < (SYSEX_BUFFER_SIZE + 2) / 3; ++i) {
     268             :             midiEventPacket_t midipacket = MidiUSB.read();
     269             :             uint8_t rx_packet[4] = {midipacket.header, midipacket.byte1,
     270             :                                     midipacket.byte2, midipacket.byte3};
     271             :             if (!midipacket.header)
     272             :                 return NO_MESSAGE;
     273             :             MIDI_read_t parseResult = parser.parse(rx_packet);
     274             :             if (parseResult != NO_MESSAGE)
     275             :                 return parseResult;
     276             :         }
     277             :         return NO_MESSAGE;
     278             :     }
     279             : #endif
     280             : 
     281             :   private:
     282             : // If it's a Teensy board
     283             : #if defined(TEENSYDUINO) && !defined(__AVR_AT90USB1286__)
     284             :     usb_packet_t *rx_packet = nullptr;
     285             : #endif
     286             : };
     287             : 
     288             : END_CS_NAMESPACE
     289             : 
     290             : // If the main MCU doesn't have a USB connection:
     291             : // Fall back on Serial connection at the hardware MIDI baud rate.
     292             : // (Can be used with HIDUINO or USBMidiKliK.)
     293             : #elif !defined(USBCON) && !defined(TEENSYDUINO)
     294             : 
     295             : #include "SerialMIDI_Interface.hpp"
     296             : 
     297             : BEGIN_CS_NAMESPACE
     298             : 
     299             : /**
     300             :  * @brief   A class for MIDI interfaces sending MIDI messages over a USB MIDI
     301             :  *          connection.
     302             :  * 
     303             :  * @note    See the [MIDI over USB Wiki]
     304             :  *          (https://github.com/tttapa/MIDI_controller/wiki/MIDI-over-USB)
     305             :  *          for more information.
     306             :  * 
     307             :  * @ingroup MIDIInterfaces
     308             :  */
     309             : class USBMIDI_Interface : public USBSerialMIDI_Interface {
     310             :   public:
     311             :     /**
     312             :      * @brief   Construct a new USBMIDI_Interface.
     313             :      */
     314             :     USBMIDI_Interface() : USBSerialMIDI_Interface(MIDI_BAUD){};
     315             : };
     316             : 
     317             : END_CS_NAMESPACE
     318             : 
     319             : #endif

Generated by: LCOV version 1.14-5-g4ff2ed6