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
|