1
+ #pragma once
2
+
3
+ #include < AH/Error/Error.hpp>
4
+
5
+ #include " ArduinoBLE/midi.hpp"
6
+ #include " BLEAPI.hpp"
7
+ #include " MIDI_Interfaces/BLEMIDI/BLERingBuf.hpp"
8
+ #include " PollingMIDISender.hpp"
9
+ #include < MIDI_Parsers/BLEMIDIParser.hpp>
10
+ #include < MIDI_Parsers/SerialMIDI_Parser.hpp>
11
+
12
+ BEGIN_CS_NAMESPACE
13
+
14
+ // / ArduinoBLE backend intended to be plugged into
15
+ // / @ref GenericBLEMIDI_Interface.
16
+ class ArduinoBLEBackend : private PollingMIDISender <ArduinoBLEBackend>,
17
+ private MIDIBLEInstance {
18
+ private:
19
+ // Callbacks from the ArduinoBLE stack.
20
+ void handleConnect (BLEConnectionHandle) override { connected = true ; }
21
+ void handleDisconnect (BLEConnectionHandle) override {
22
+ connected = subscribed = false ;
23
+ }
24
+ void handleMTU (BLEConnectionHandle, uint16_t mtu) override {
25
+ Sender::updateMTU (mtu);
26
+ }
27
+ void handleSubscribe (BLEConnectionHandle, BLECharacteristicHandle,
28
+ bool notify) override {
29
+ subscribed = notify;
30
+ }
31
+ void handleData (BLEConnectionHandle, BLEDataGenerator &&data,
32
+ BLEDataLifetime) override {
33
+ while (true ) {
34
+ BLEDataView packet = data ();
35
+ if (packet.length == 0 ) {
36
+ break ;
37
+ } else if (!ble_buffer.push (packet)) {
38
+ DEBUGREF (F (" BLE packet dropped, size: " ) << packet.length );
39
+ break ;
40
+ }
41
+ }
42
+ }
43
+
44
+ private:
45
+ // / Are we connected to a BLE Central?
46
+ bool connected = false ;
47
+ // / Did the BLE Central subscribe to be notified for the MIDI characteristic?
48
+ bool subscribed = false ;
49
+ // / Contains incoming data to be parsed.
50
+ BLERingBuf<1024 > ble_buffer {};
51
+ // / Parses the (chunked) BLE packet obtained from @ref ble_buffer.
52
+ BLEMIDIParser ble_parser {nullptr , 0 };
53
+ // / Parser for MIDI data extracted from the BLE packet by @ref ble_parser.
54
+ SerialMIDI_Parser parser {false };
55
+
56
+ public:
57
+ // / MIDI message variant type.
58
+ struct IncomingMIDIMessage {
59
+ MIDIReadEvent eventType = MIDIReadEvent::NO_MESSAGE;
60
+ union Message {
61
+ ChannelMessage channelmessage;
62
+ SysCommonMessage syscommonmessage;
63
+ RealTimeMessage realtimemessage;
64
+ SysExMessage sysexmessage;
65
+
66
+ Message () : realtimemessage (0x00 ) {}
67
+ Message (ChannelMessage msg) : channelmessage (msg) {}
68
+ Message (SysCommonMessage msg) : syscommonmessage (msg) {}
69
+ Message (RealTimeMessage msg) : realtimemessage (msg) {}
70
+ Message (SysExMessage msg) : sysexmessage (msg) {}
71
+ } message;
72
+ uint16_t timestamp = 0xFFFF ;
73
+
74
+ IncomingMIDIMessage () = default ;
75
+ IncomingMIDIMessage (ChannelMessage message, uint16_t timestamp)
76
+ : eventType(MIDIReadEvent::CHANNEL_MESSAGE), message(message),
77
+ timestamp (timestamp) {}
78
+ IncomingMIDIMessage (SysCommonMessage message, uint16_t timestamp)
79
+ : eventType(MIDIReadEvent::SYSCOMMON_MESSAGE), message(message),
80
+ timestamp(timestamp) {}
81
+ IncomingMIDIMessage (RealTimeMessage message, uint16_t timestamp)
82
+ : eventType(MIDIReadEvent::REALTIME_MESSAGE), message(message),
83
+ timestamp(timestamp) {}
84
+ IncomingMIDIMessage (SysExMessage message, uint16_t timestamp)
85
+ : eventType(message.isLastChunk() ? MIDIReadEvent::SYSEX_MESSAGE
86
+ : MIDIReadEvent::SYSEX_CHUNK),
87
+ message(message), timestamp(timestamp) {}
88
+ };
89
+
90
+ // / Retrieve and remove a single incoming MIDI message from the buffer.
91
+ bool popMessage (IncomingMIDIMessage &incomingMessage) {
92
+ // This function is assumed to be polled regularly by the higher-level
93
+ // MIDI_Interface, so we check the sender's timer here, and we poll
94
+ // the ArduinoBLE library.
95
+ auto lck = Sender::acquirePacket ();
96
+ Sender::releasePacketAndNotify (lck);
97
+ arduino_ble_midi::poll ();
98
+ // Try reading a MIDI message from the parser
99
+ auto try_read = [&] {
100
+ MIDIReadEvent event = parser.pull (ble_parser);
101
+ switch (event) {
102
+ case MIDIReadEvent::CHANNEL_MESSAGE:
103
+ incomingMessage = {parser.getChannelMessage (),
104
+ ble_parser.getTimestamp ()};
105
+ return true ;
106
+ case MIDIReadEvent::SYSEX_CHUNK: // fallthrough
107
+ case MIDIReadEvent::SYSEX_MESSAGE:
108
+ incomingMessage = {parser.getSysExMessage (),
109
+ ble_parser.getTimestamp ()};
110
+ return true ;
111
+ case MIDIReadEvent::REALTIME_MESSAGE:
112
+ incomingMessage = {parser.getRealTimeMessage (),
113
+ ble_parser.getTimestamp ()};
114
+ return true ;
115
+ case MIDIReadEvent::SYSCOMMON_MESSAGE:
116
+ incomingMessage = {parser.getSysCommonMessage (),
117
+ ble_parser.getTimestamp ()};
118
+ return true ;
119
+ case MIDIReadEvent::NO_MESSAGE: return false ;
120
+ default : break ; // LCOV_EXCL_LINE
121
+ }
122
+ return false ;
123
+ };
124
+ while (true ) {
125
+ // Try reading a MIDI message from the current buffer
126
+ if (try_read ())
127
+ return true ; // success, incomingMessage updated
128
+ // Get the next chunk of the BLE packet (if available)
129
+ BLEDataView chunk;
130
+ auto popped = ble_buffer.pop (chunk);
131
+ if (popped == BLEDataType::None)
132
+ return false ; // no more BLE data available
133
+ else if (popped == BLEDataType::Continuation)
134
+ ble_parser.extend (chunk.data , chunk.length ); // same BLE packet
135
+ else if (popped == BLEDataType::Packet)
136
+ ble_parser = {chunk.data , chunk.length }; // new BLE packet
137
+ }
138
+ }
139
+
140
+ public:
141
+ // / Initialize the BLE stack etc.
142
+ void begin (BLESettings ble_settings) {
143
+ arduino_ble_midi::init (*this , ble_settings);
144
+ Sender::begin ();
145
+ }
146
+ // / Deinitialize the BLE stack.
147
+ // / @todo Not yet implemented.
148
+ void end () {}
149
+ // / Returns true if we are connected to a BLE Central device.
150
+ bool isConnected () const { return connected; }
151
+
152
+ private:
153
+ // Implement the interface for the BLE sender.
154
+ using Sender = PollingMIDISender<ArduinoBLEBackend>;
155
+ friend Sender;
156
+ // / Send the given MIDI BLE packet.
157
+ void sendData (BLEDataView data) {
158
+ if (connected && subscribed)
159
+ arduino_ble_midi::notify (data);
160
+ }
161
+
162
+ public:
163
+ // Expose the necessary BLE sender functions.
164
+ using Sender::acquirePacket;
165
+ using Sender::forceMinMTU;
166
+ using Sender::getMinMTU;
167
+ using Sender::releasePacketAndNotify;
168
+ using Sender::sendNow;
169
+ using Sender::setTimeout;
170
+ };
171
+
172
+ END_CS_NAMESPACE
0 commit comments