1
1
/* *************************************************************************/
2
2
/* !
3
3
@file BLEMidi.cpp
4
- @author hathach
4
+ @author hathach & toddtreece
5
5
6
6
@section LICENSE
7
7
@@ -93,10 +93,18 @@ typedef struct ATTR_PACKED
93
93
{
94
94
midi_header_t header;
95
95
midi_timestamp_t timestamp;
96
- uint8_t data[3 ];
96
+ uint8_t data[20 ];
97
97
} midi_event_packet_t ;
98
98
99
- VERIFY_STATIC ( sizeof (midi_event_packet_t ) == 5 );
99
+ VERIFY_STATIC ( sizeof (midi_event_packet_t ) == 22 );
100
+
101
+ typedef struct ATTR_PACKED
102
+ {
103
+ midi_header_t header;
104
+ uint8_t data[20 ];
105
+ } midi_split_packet_t ;
106
+
107
+ VERIFY_STATIC ( sizeof (midi_split_packet_t ) == 21 );
100
108
101
109
void blemidi_write_cb (BLECharacteristic& chr, uint8_t * data, uint16_t len, uint16_t offset);
102
110
@@ -208,36 +216,97 @@ int BLEMidi::read ( void )
208
216
return _rxd_fifo.read (&ch) ? (int ) ch : EOF;
209
217
}
210
218
219
+ bool BLEMidi::oneByteMessage ( uint8_t status )
220
+ {
221
+ // system messages
222
+ if (status >= 0xF4 && status <= 0xFF ) return true ;
223
+
224
+ // system common
225
+ if (status == 0xF1 ) return true ;
226
+
227
+ return false ;
228
+ }
229
+
230
+ bool BLEMidi::twoByteMessage ( uint8_t status )
231
+ {
232
+ // program change, aftertouch
233
+ if (status >= 0xC0 && status <= 0xDF ) return true ;
234
+
235
+ // song select
236
+ if (status == 0xF3 ) return true ;
237
+
238
+ return false ;
239
+ }
240
+
241
+ bool BLEMidi::threeByteMessage ( uint8_t status )
242
+ {
243
+ // note off, note on, aftertouch, control change
244
+ if (status >= 0x80 && status <= 0xBF ) return true ;
245
+
246
+ // pitch wheel change
247
+ if (status >= 0xE0 && status <= 0xEF ) return true ;
248
+
249
+ // song position pointer
250
+ if (status == 0xF2 ) return true ;
251
+
252
+ return false ;
253
+ }
254
+
211
255
size_t BLEMidi::write ( uint8_t b )
212
256
{
213
- // MIDI Library will write event byte by byte. Locally buffered
214
- // Until we gather all 3 bytes
257
+ // MIDI Library will write event byte by byte.
258
+ // We need to buffer the data until we have a full event,
259
+ // or until we reach the BLE payload limit.
215
260
static uint8_t count = 0 ;
216
- static uint8_t buf[3 ] = { 0 };
261
+ static uint8_t buf[16 ] = { 0 };
217
262
263
+ buf[count++] = b;
218
264
219
- // Not SysEx message, keep accumulating data
220
- if ( buf[0 ] != 0xf0 )
265
+ // if we are at the end of a sysex message
266
+ // or at the end of the buffer
267
+ if ((b == 0xF7 ) || (count == 16 ))
221
268
{
222
- buf[count++] = b;
223
-
224
- if ( count == 3 )
225
- {
226
- count = 0 ;
227
269
228
- send (buf);
229
- }
230
- }else
231
- {
232
- // skip until we reach 0xF7
233
- if (b == 0xF7 )
234
- {
235
- buf[0 ] = 0 ;
236
- count = 0 ;
270
+ // send full event if the first byte is a status byte
271
+ if (bitRead (buf[0 ], 7 )) {
272
+ send (buf, count);
273
+ } else {
274
+ sendSplit (buf, count);
237
275
}
276
+
277
+ // reset buffer
278
+ buf[0 ] = 0 ;
279
+ count = 0 ;
280
+
281
+ return 1 ;
282
+
238
283
}
239
284
285
+ // don't send if this is a full or split sysex
286
+ if (buf[0 ] == 0xF0 || ! bitRead (buf[0 ], 7 ))
287
+ return 1 ;
288
+
289
+ // don't send if we don't have 1 byte
290
+ if (oneByteMessage (buf[0 ]) && count != 1 )
291
+ return 1 ;
292
+
293
+ // don't send if we don't have 2 bytes
294
+ if (twoByteMessage (buf[0 ]) && count != 2 )
295
+ return 1 ;
296
+
297
+ // don't send if we don't have 3 bytes
298
+ if (threeByteMessage (buf[0 ]) && count != 3 )
299
+ return 1 ;
300
+
301
+ // send full event
302
+ send (buf, count);
303
+
304
+ // reset buffer
305
+ buf[0 ] = 0 ;
306
+ count = 0 ;
307
+
240
308
return 1 ;
309
+
241
310
}
242
311
243
312
int BLEMidi::available ( void )
@@ -259,7 +328,7 @@ void BLEMidi::flush ( void )
259
328
/* ------------------------------------------------------------------*/
260
329
/* Send Event (notify)
261
330
*------------------------------------------------------------------*/
262
- err_t BLEMidi::send (uint8_t data[])
331
+ err_t BLEMidi::send (uint8_t data[], uint8_t len )
263
332
{
264
333
uint32_t tstamp = millis ();
265
334
@@ -276,16 +345,31 @@ void BLEMidi::flush ( void )
276
345
}}
277
346
};
278
347
279
- memcpy (event.data , data, 3 );
348
+ memcpy (event.data , data, len );
280
349
281
- VERIFY_STATUS ( _io.notify (&event, sizeof (event)) );
350
+ // send data length + 1 byte for header + 1 byte for timestamp
351
+ VERIFY_STATUS ( _io.notify (&event, len + 2 ) );
282
352
283
353
return ERROR_NONE;
284
354
}
285
355
286
- err_t BLEMidi::send (uint8_t status , uint8_t byte1, uint8_t byte2 )
356
+ err_t BLEMidi::sendSplit (uint8_t data[] , uint8_t len )
287
357
{
288
- uint8_t data[] = { status, byte1, byte2 };
289
- return send (data);
290
- }
358
+ uint32_t tstamp = millis ();
359
+
360
+ midi_split_packet_t event =
361
+ {
362
+ .header = {{
363
+ .timestamp_hi = (uint8_t ) ((tstamp & 0x1F80UL ) >> 7 ),
364
+ .start_bit = 1
365
+ }}
366
+ };
367
+
368
+ memcpy (event.data , data, len);
291
369
370
+ // send data length + 1 byte for header
371
+ // don't include the second timestamp byte
372
+ VERIFY_STATUS ( _io.notify (&event, len + 1 ) );
373
+
374
+ return ERROR_NONE;
375
+ }
0 commit comments