Line data Source code
1 : #pragma once
2 :
3 : #include <Settings/NamespaceSettings.hpp>
4 :
5 : #include "BLERingBuf.hpp"
6 : #include <MIDI_Parsers/AnyMIDI_Message.hpp>
7 : #include <MIDI_Parsers/BLEMIDIParser.hpp>
8 : #include <MIDI_Parsers/SerialMIDI_Parser.hpp>
9 :
10 : BEGIN_CS_NAMESPACE
11 :
12 : /// FIFO buffer that you can push BLE packets into, and pop MIDI messages out of.
13 : /// If @p SizeT is chosen to be atomic, one thread can push packets, and another
14 : /// thread can pop MIDI messages, without additional synchronization.
15 : template <uint16_t Capacity, class SizeT = NonatomicBLERingBufSize<uint16_t>>
16 : class BufferedBLEMIDIParser {
17 : private:
18 : /// Contains incoming data to be parsed.
19 : BLERingBuf<Capacity, SizeT> ble_buffer {};
20 : /// Parses the (chunked) BLE packet obtained from @ref ble_buffer.
21 : BLEMIDIParser ble_parser {nullptr, 0};
22 : /// Parser for MIDI data extracted from the BLE packet by @ref ble_parser.
23 : SerialMIDI_Parser parser {false};
24 :
25 : public:
26 : using IncomingMIDIMessage = AnyMIDIMessage;
27 :
28 : /// Add a new BLE packet or chunk to the buffer.
29 15 : bool pushPacket(BLEDataView packet,
30 : BLEDataType type = BLEDataType::Packet) {
31 15 : return ble_buffer.push(packet, type);
32 : }
33 :
34 : /// Retrieve and remove a single incoming MIDI message from the buffer.
35 52 : bool popMessage(IncomingMIDIMessage &incomingMessage) {
36 : // Try reading a MIDI message from the parser
37 52 : auto try_read = [&] {
38 67 : MIDIReadEvent event = parser.pull(ble_parser);
39 67 : switch (event) {
40 33 : case MIDIReadEvent::CHANNEL_MESSAGE:
41 66 : incomingMessage = {parser.getChannelMessage(),
42 33 : ble_parser.getTimestamp()};
43 33 : return true;
44 4 : case MIDIReadEvent::SYSEX_CHUNK: // fallthrough
45 : case MIDIReadEvent::SYSEX_MESSAGE:
46 8 : incomingMessage = {parser.getSysExMessage(),
47 4 : ble_parser.getTimestamp()};
48 4 : return true;
49 2 : case MIDIReadEvent::REALTIME_MESSAGE:
50 4 : incomingMessage = {parser.getRealTimeMessage(),
51 2 : ble_parser.getTimestamp()};
52 2 : return true;
53 1 : case MIDIReadEvent::SYSCOMMON_MESSAGE:
54 2 : incomingMessage = {parser.getSysCommonMessage(),
55 1 : ble_parser.getTimestamp()};
56 1 : return true;
57 27 : case MIDIReadEvent::NO_MESSAGE: return false;
58 : default: break; // LCOV_EXCL_LINE
59 : }
60 0 : return false;
61 : };
62 15 : while (true) {
63 : // Try reading a MIDI message from the current buffer
64 67 : if (try_read())
65 40 : return true; // success, incomingMessage updated
66 : // Get the next chunk of the BLE packet (if available)
67 27 : BLEDataView chunk;
68 27 : auto popped = ble_buffer.pop(chunk);
69 27 : if (popped == BLEDataType::None)
70 12 : return false; // no more BLE data available
71 15 : else if (popped == BLEDataType::Continuation)
72 0 : ble_parser.extend(chunk.data, chunk.length); // same BLE packet
73 15 : else if (popped == BLEDataType::Packet)
74 15 : ble_parser = {chunk.data, chunk.length}; // new BLE packet
75 : }
76 : }
77 : };
78 :
79 : END_CS_NAMESPACE
|