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