Line data Source code
1 : #include "MIDI_Parser.hpp" 2 : #include "SysExBuffer.hpp" 3 : #include <AH/Containers/Array.hpp> 4 : 5 : #ifdef MIDI_NUM_CABLES 6 : #define USB_MIDI_NUMBER_OF_CABLES MIDI_NUM_CABLES 7 : #elif defined(USB_MIDI4_SERIAL) || defined(USB_MIDI4) 8 : #define USB_MIDI_NUMBER_OF_CABLES 4 9 : #elif defined(USB_MIDI16_AUDIO_SERIAL) || defined(USB_MIDI16_SERIAL) || \ 10 : defined(USB_MIDI16) 11 : // TODO: || defined(USB_EVERYTHING) 12 : #define USB_MIDI_NUMBER_OF_CABLES 16 13 : #elif !defined(ARDUINO) || defined(DOXYGEN) 14 : #define USB_MIDI_NUMBER_OF_CABLES 16 15 : #else 16 : #define USB_MIDI_NUMBER_OF_CABLES 1 17 : #endif 18 : 19 : BEGIN_CS_NAMESPACE 20 : 21 : /** 22 : * @brief Parser for MIDI over USB packets. 23 : * 24 : * @ingroup MIDIParsers 25 : */ 26 : class USBMIDI_Parser : public MIDI_Parser { 27 : public: 28 : using MIDIUSBPacket_t = AH::Array<uint8_t, 4>; 29 : 30 : /** 31 : * @brief Parse one incoming MIDI message. 32 : * @param puller 33 : * The source of MIDI USB packets. 34 : * @return The type of MIDI message available, or 35 : * `MIDIReadEvent::NO_MESSAGE` if `puller` ran out of packets 36 : * before a complete message was parsed. 37 : */ 38 : template <class BytePuller> 39 : MIDIReadEvent pull(BytePuller &&puller); 40 : 41 : protected: 42 : /// Feed a new packet to the parser. 43 : MIDIReadEvent feed(MIDIUSBPacket_t packet); 44 : /// Resume the parser with the previously stored and unhandled packet. 45 : MIDIReadEvent resume(); 46 : 47 : public: 48 : #if !IGNORE_SYSEX 49 : /// Get the latest SysEx message. 50 30 : SysExMessage getSysExMessage() const { 51 : return { 52 30 : sysexbuffers[activeCable.getRaw()].getBuffer(), 53 30 : sysexbuffers[activeCable.getRaw()].getLength(), 54 : activeCable, 55 90 : }; 56 : } 57 : #endif 58 : 59 : protected: 60 : MIDIReadEvent handleChannelMessage(MIDIUSBPacket_t packet, Cable cable); 61 : MIDIReadEvent handleSingleByte(MIDIUSBPacket_t packet, Cable cable); 62 : MIDIReadEvent handleSysExStartCont(MIDIUSBPacket_t packet, Cable cable); 63 : template <uint8_t NumBytes> 64 : MIDIReadEvent handleSysExEnd(MIDIUSBPacket_t packet, Cable cable); 65 : MIDIReadEvent handleSysCommon(MIDIUSBPacket_t packet, Cable cable); 66 : 67 : protected: 68 : #if !IGNORE_SYSEX 69 14 : void startSysEx(Cable cable) { sysexbuffers[cable.getRaw()].start(); } 70 12 : void endSysEx(Cable cable) { 71 12 : sysexbuffers[cable.getRaw()].end(); 72 12 : activeCable = cable; 73 12 : } 74 2 : void endSysExChunk(Cable cable) { activeCable = cable; } 75 114 : bool hasSysExSpace(Cable cable, uint8_t amount) const { 76 114 : return sysexbuffers[cable.getRaw()].hasSpaceLeft(amount); 77 : } 78 4 : void addSysExByte(Cable cable, uint8_t data) { 79 4 : sysexbuffers[cable.getRaw()].add(data); 80 4 : } 81 108 : void addSysExBytes(Cable cable, const uint8_t *data, uint8_t len) { 82 108 : sysexbuffers[cable.getRaw()].add(data, len); 83 108 : } 84 108 : bool receivingSysEx(Cable cable) const { 85 108 : return sysexbuffers[cable.getRaw()].isReceiving(); 86 : } 87 : 88 2 : void storePacket(MIDIUSBPacket_t packet) { storedPacket = packet; } 89 39 : bool hasStoredPacket() const { return storedPacket[0] != 0x00; } 90 2 : MIDIUSBPacket_t popStoredPacket() { 91 2 : MIDIUSBPacket_t t = storedPacket; 92 2 : storedPacket[0] = 0x00; 93 2 : return t; 94 : } 95 : 96 : Cable activeCable = Cable_1; 97 : 98 : private: 99 : SysExBuffer sysexbuffers[USB_MIDI_NUMBER_OF_CABLES] = {}; 100 : MIDIUSBPacket_t storedPacket = {{ 0x00 }}; 101 : #endif 102 : }; 103 : 104 : template <class BytePuller> 105 39 : inline MIDIReadEvent USBMIDI_Parser::pull(BytePuller &&puller) { 106 : // First try resuming the parser, we might have a stored packet that has to 107 : // be parsed first. 108 39 : MIDIReadEvent evt = resume(); 109 39 : if (evt != MIDIReadEvent::NO_MESSAGE) 110 1 : return evt; 111 : 112 : // If resumption didn't produce a message, read new packets from the input 113 : // and parse them until either we get a message, or until the input runs out 114 : // of new packets. 115 38 : MIDIUSBPacket_t midiPacket; 116 141 : while (puller.pull(midiPacket)) { 117 131 : evt = feed(midiPacket); 118 131 : if (evt != MIDIReadEvent::NO_MESSAGE) 119 28 : return evt; 120 : } 121 10 : return MIDIReadEvent::NO_MESSAGE; 122 : } 123 : 124 : END_CS_NAMESPACE