#include "MIDI_Parser.hpp" #include "SysExBuffer.hpp" #include #ifdef MIDI_NUM_CABLES #define USB_MIDI_NUMBER_OF_CABLES MIDI_NUM_CABLES #elif defined(USB_MIDI4_SERIAL) || defined(USB_MIDI4) #define USB_MIDI_NUMBER_OF_CABLES 4 #elif defined(USB_MIDI16_AUDIO_SERIAL) || defined(USB_MIDI16_SERIAL) || \ defined(USB_MIDI16) // TODO: || defined(USB_EVERYTHING) #define USB_MIDI_NUMBER_OF_CABLES 16 #elif defined(ARDUINO_ARCH_RP2040) #define USB_MIDI_NUMBER_OF_CABLES 16 #elif !defined(ARDUINO) || defined(DOXYGEN) #define USB_MIDI_NUMBER_OF_CABLES 16 #else #define USB_MIDI_NUMBER_OF_CABLES 1 #endif BEGIN_CS_NAMESPACE /** * @brief Parser for MIDI over USB packets. * * @ingroup MIDIParsers */ class USBMIDI_Parser : public MIDI_Parser { public: using MIDIUSBPacket_t = AH::Array; /** * @brief Parse one incoming MIDI message. * @param puller * The source of MIDI USB packets. * @return The type of MIDI message available, or * `MIDIReadEvent::NO_MESSAGE` if `puller` ran out of packets * before a complete message was parsed. */ template MIDIReadEvent pull(BytePuller &&puller); protected: /// Feed a new packet to the parser. MIDIReadEvent feed(MIDIUSBPacket_t packet); /// Resume the parser with the previously stored and unhandled packet. MIDIReadEvent resume(); public: #if !IGNORE_SYSEX /// Get the latest SysEx message. SysExMessage getSysExMessage() const { return { sysexbuffers[activeCable.getRaw()].getBuffer(), sysexbuffers[activeCable.getRaw()].getLength(), activeCable, }; } #endif protected: MIDIReadEvent handleChannelMessage(MIDIUSBPacket_t packet, Cable cable); MIDIReadEvent handleSingleByte(MIDIUSBPacket_t packet, Cable cable); MIDIReadEvent handleSysExStartCont(MIDIUSBPacket_t packet, Cable cable); template MIDIReadEvent handleSysExEnd(MIDIUSBPacket_t packet, Cable cable); MIDIReadEvent handleSysCommon(MIDIUSBPacket_t packet, Cable cable); protected: #if !IGNORE_SYSEX void startSysEx(Cable cable) { sysexbuffers[cable.getRaw()].start(); } void endSysEx(Cable cable) { sysexbuffers[cable.getRaw()].end(); activeCable = cable; } void endSysExChunk(Cable cable) { activeCable = cable; } bool hasSysExSpace(Cable cable, uint8_t amount) const { return sysexbuffers[cable.getRaw()].hasSpaceLeft(amount); } void addSysExByte(Cable cable, uint8_t data) { sysexbuffers[cable.getRaw()].add(data); } void addSysExBytes(Cable cable, const uint8_t *data, uint8_t len) { sysexbuffers[cable.getRaw()].add(data, len); } bool receivingSysEx(Cable cable) const { return sysexbuffers[cable.getRaw()].isReceiving(); } void storePacket(MIDIUSBPacket_t packet) { storedPacket = packet; } bool hasStoredPacket() const { return storedPacket[0] != 0x00; } MIDIUSBPacket_t popStoredPacket() { MIDIUSBPacket_t t = storedPacket; storedPacket[0] = 0x00; return t; } Cable activeCable = Cable_1; private: SysExBuffer sysexbuffers[USB_MIDI_NUMBER_OF_CABLES] = {}; MIDIUSBPacket_t storedPacket = {{ 0x00 }}; #endif }; template inline MIDIReadEvent USBMIDI_Parser::pull(BytePuller &&puller) { // First try resuming the parser, we might have a stored packet that has to // be parsed first. MIDIReadEvent evt = resume(); if (evt != MIDIReadEvent::NO_MESSAGE) return evt; // If resumption didn't produce a message, read new packets from the input // and parse them until either we get a message, or until the input runs out // of new packets. MIDIUSBPacket_t midiPacket; while (puller.pull(midiPacket)) { evt = feed(midiPacket); if (evt != MIDIReadEvent::NO_MESSAGE) return evt; } return MIDIReadEvent::NO_MESSAGE; } END_CS_NAMESPACE