15 #include "MidiFileParserState.h"
33 inline uint64_t millis() {
34 using namespace std::chrono;
35 return duration_cast<milliseconds>(system_clock::now().time_since_epoch())
40 #define MIDI_BUFFER_SIZE 1024 * 2
41 #define MIDI_MIN_REFILL_SIZE 512
47 virtual size_t write(uint8_t) = 0;
64 bool begin(
bool log =
true,
int bufferSize = MIDI_BUFFER_SIZE) {
67 parser_state.in.resize(bufferSize);
73 size_t write(uint8_t c)
override {
return write(&c, 1); }
76 virtual size_t write(
const uint8_t *data,
size_t len) {
82 if (len <= parser_state.in.availableForWrite()) {
83 return parser_state.in.write(data, len);
94 parser_state.status = MIDI_PARSER_EOB;
95 if (!parser_state.in.isEmpty()) {
96 midi_parser_status status = midi_parse();
97 parser_state.status = status;
103 if (parser_state.status == MIDI_PARSER_EOB ||
104 parser_state.status == MIDI_PARSER_ERROR) {
113 static uint64_t timeout = 0l;
117 if (millis() < timeout) {
126 if (!parser_state.in.isEmpty()) {
127 midi_parser_status status = midi_parse();
129 if (parser_state.timeInMs() > 0) {
130 timeout = millis() + parser_state.timeInMs();
133 parser_state.status = status;
138 if (parser_state.status == MIDI_PARSER_EOB ||
139 parser_state.status == MIDI_PARSER_ERROR) {
147 operator bool() {
return is_ok; }
155 case MIDI_STATUS_NOTE_OFF:
157 case MIDI_STATUS_NOTE_ON:
159 case MIDI_STATUS_NOTE_AT:
160 return "Note Aftertouch";
163 case MIDI_STATUS_PGM_CHANGE:
164 return "Program Change";
165 case MIDI_STATUS_CHANNEL_AT:
166 return "Channel Aftertouch";
167 case MIDI_STATUS_PITCH_BEND:
178 case MIDI_FILE_FORMAT_SINGLE_TRACK:
179 return "single track";
180 case MIDI_FILE_FORMAT_MULTIPLE_TRACKS:
181 return "multiple tracks";
182 case MIDI_FILE_FORMAT_MULTIPLE_SONGS:
183 return "multiple songs";
193 case MIDI_META_SEQ_NUM:
194 return "Sequence Number";
197 case MIDI_META_COPYRIGHT:
199 case MIDI_META_TRACK_NAME:
201 case MIDI_META_INSTRUMENT_NAME:
202 return "Instrument Name";
203 case MIDI_META_LYRICS:
205 case MIDI_META_MAKER:
207 case MIDI_META_CUE_POINT:
209 case MIDI_META_CHANNEL_PREFIX:
210 return "Channel Prefix";
211 case MIDI_META_END_OF_TRACK:
212 return "End of Track";
213 case MIDI_META_SET_TEMPO:
215 case MIDI_META_SMPTE_OFFSET:
216 return "SMPTE Offset";
217 case MIDI_META_TIME_SIGNATURE:
218 return "Time Signature";
219 case MIDI_META_KEY_SIGNATURE:
220 return "Key Signature";
221 case MIDI_META_SEQ_SPECIFIC:
222 return "Sequencer Specific";
232 bool log_active =
false;
238 midi_parser_state eob{MIDI_PARSER_EOB};
240 void setState(midi_parser_status state){
241 parser_state.status = state;
242 if (parser_state.status == MIDI_PARSER_EOB ||
243 parser_state.status == MIDI_PARSER_ERROR) {
248 void logStatus(midi_parser_status status) {
250 case MIDI_PARSER_EOB:
251 printf(
"MIDI_PARSER_EOB\n");
254 case MIDI_PARSER_ERROR:
255 printf(
"MIDI_PARSER_ERROR\n");
258 case MIDI_PARSER_INIT:
259 printf(
"MIDI_PARSER_INIT\n");
262 case MIDI_PARSER_HEADER:
263 printf(
"\nheader\n");
264 printf(
" size: %d\n", parser_state.header.size);
265 printf(
" format: %d [%s]\n", parser_state.header.format,
267 printf(
" tracks count: %d\n", parser_state.header.tracks_count);
268 printf(
" time division: %d\n", parser_state.header.time_division);
271 case MIDI_PARSER_TRACK:
273 printf(
" length: %d\n", parser_state.track.size);
276 case MIDI_PARSER_TRACK_MIDI:
277 printf(
"\ntrack-midi\n");
278 printf(
" time: %ld\n", (
long)parser_state.vtime);
279 printf(
" status: %d [%s]\n", parser_state.midi.status,
281 printf(
" channel: %d\n", parser_state.midi.channel);
282 printf(
" param1: %d\n", parser_state.midi.param1);
283 printf(
" param2: %d\n", parser_state.midi.param2);
286 case MIDI_PARSER_TRACK_META:
287 printf(
"\ntrack-meta\n");
288 printf(
" time: %ld\n", (
long)parser_state.vtime);
289 printf(
" type: %d [%s]\n", parser_state.meta.type,
291 printf(
" length: %d\n", parser_state.meta.length);
294 case MIDI_PARSER_TRACK_SYSEX:
295 printf(
"\ntrack-sysex\n");
296 printf(
" time: %ld\n", (
long)parser_state.vtime);
299 case MIDI_PARSER_DELAY:
303 printf(
"\nunhandled state: %d\n", status);
309 parser_state.in.reset();
310 parser_state.status = MIDI_PARSER_INIT;
311 parser_state.status_internal = MIDI_PARSER_INIT;
314 int midi_event_datalen(
int status) {
316 case MIDI_STATUS_PGM_CHANGE:
318 case MIDI_STATUS_CHANNEL_AT:
325 uint16_t midi_parse_be16(
const uint8_t *in) {
326 return (
static_cast<uint16_t
>(in[0]) << 8) | in[1];
329 uint32_t midi_parse_be32(
const uint8_t *in) {
330 return (
static_cast<uint32_t
>(in[0]) << 24) |
331 (
static_cast<uint32_t
>(in[1]) << 16) |
332 static_cast<uint32_t
>((in[2]) << 8) | in[3];
335 uint64_t midi_parse_N(
const uint8_t *from_bytes,
int len) {
337 for (
int j = 0; j < len; j++) {
338 uint64_t
byte = from_bytes[j];
339 int shift = 8 * (len - j - 1);
340 tmp |=
byte << shift;
345 uint64_t midi_parse_variable_length(int32_t *offset) {
349 for (; i < parser_state.in.available(); ++i) {
350 value = (value << 7) | (parser_state.in[i] & 0x7f);
351 if (!(parser_state.in[i] & 0x80))
358 enum midi_parser_status midi_parse_header() {
359 if (parser_state.in.available() < 14)
360 return MIDI_PARSER_EOB;
362 if (!parser_state.in.
equals(
"MThd"))
363 return MIDI_PARSER_ERROR;
365 parser_state.header.size = midi_parse_be32(parser_state.in.
peekStr(4, 4));
366 parser_state.header.format = midi_parse_be16(parser_state.in.
peekStr(8, 2));
367 parser_state.header.tracks_count =
368 midi_parse_be16(parser_state.in.
peekStr(10, 2));
369 int time_div = midi_parse_be16(parser_state.in.
peekStr(12, 2));
371 parser_state.header.time_division = time_div;
375 parser_state.status_internal = MIDI_PARSER_HEADER;
376 return MIDI_PARSER_HEADER;
379 enum midi_parser_status midi_parse_track() {
380 if (parser_state.in.available() < 8)
381 return MIDI_PARSER_EOB;
383 parser_state.track.size = midi_parse_be32(parser_state.in.
peekStr(0, 4));
384 parser_state.track.number = ++track_no;
385 parser_state.status_internal = MIDI_PARSER_TRACK;
388 parser_state.buffered_status = MIDI_STATUS_NA;
389 return MIDI_PARSER_TRACK;
392 bool midi_parse_vtime() {
396 parser_state.vtime = 0;
400 if (parser_state.in.available() < nbytes ||
401 parser_state.track.size < nbytes)
404 uint8_t b = parser_state.in[nbytes - 1];
405 parser_state.vtime = (parser_state.vtime << 7) | (b & 0x7f);
413 if (parser_state.vtime > 0x0fffffff || nbytes > 5)
419 if (parser_state.in.available() < nbytes) {
423 parser_state.in.
consume(nbytes);
425 parser_state.track.size -= nbytes;
430 enum midi_parser_status midi_parse_channel_event() {
431 if (parser_state.in.available() < 2)
432 return MIDI_PARSER_EOB;
434 if ((parser_state.in[0] & 0x80) == 0) {
436 if (parser_state.buffered_status == 0)
437 return MIDI_PARSER_EOB;
438 parser_state.midi.status = parser_state.buffered_status;
439 int datalen = midi_event_datalen(parser_state.midi.status);
440 if (parser_state.in.available() < datalen)
441 return MIDI_PARSER_EOB;
442 parser_state.midi.channel = parser_state.buffered_channel;
443 parser_state.midi.param1 = (datalen > 0 ? parser_state.in[0] : 0);
444 parser_state.midi.param2 = (datalen > 1 ? parser_state.in[1] : 0);
446 parser_state.in.
consume(datalen);
448 parser_state.track.size -= datalen;
451 if (parser_state.in.available() < 3)
452 return MIDI_PARSER_EOB;
453 parser_state.midi.status = (parser_state.in[0] >> 4) & 0xf;
454 int datalen = midi_event_datalen(parser_state.midi.status);
455 if (parser_state.in.available() < 1 + datalen)
456 return MIDI_PARSER_EOB;
457 parser_state.midi.channel = parser_state.in[0] & 0xf;
458 parser_state.midi.param1 = (datalen > 0 ? parser_state.in[1] : 0);
459 parser_state.midi.param2 = (datalen > 1 ? parser_state.in[2] : 0);
460 parser_state.buffered_status =
461 (midi_status)parser_state.midi.status;
462 parser_state.buffered_channel = parser_state.midi.channel;
464 parser_state.in.
consume(1 + datalen);
466 parser_state.track.size -= 1 + datalen;
469 return MIDI_PARSER_TRACK_MIDI;
472 int midi_parse_sysex_event() {
473 assert(parser_state.in.available() == 0 || parser_state.in[0] == 0xf0);
475 if (parser_state.in.available() < 2)
476 return MIDI_PARSER_ERROR;
479 parser_state.sysex.length = midi_parse_variable_length(&offset);
480 if (offset < 1 || offset > parser_state.in.available())
481 return MIDI_PARSER_ERROR;
482 parser_state.in.
consume(offset);
484 parser_state.track.size -= offset;
487 if (parser_state.sysex.length <= 0 ||
488 parser_state.sysex.length > parser_state.in.available())
489 return MIDI_PARSER_ERROR;
491 parser_state.sysex.bytes =
492 parser_state.in.
peekStr(0, parser_state.sysex.length);
493 parser_state.in.
consume(parser_state.sysex.length);
495 parser_state.track.size -= parser_state.sysex.length;
497 if (parser_state.sysex.bytes[parser_state.sysex.length - 1] == 0xF7)
498 parser_state.sysex.length--;
500 return MIDI_PARSER_TRACK_SYSEX;
503 enum midi_parser_status midi_parse_meta_event() {
504 assert(parser_state.in.available() == 0 || parser_state.in[0] == 0xff);
505 if (parser_state.in.available() < 2)
506 return MIDI_PARSER_ERROR;
508 parser_state.meta.type = parser_state.in[1];
510 parser_state.meta.length = midi_parse_variable_length(&offset);
513 if (parser_state.meta.length < 0 ||
514 parser_state.meta.length > parser_state.in.available())
515 return MIDI_PARSER_ERROR;
518 if (parser_state.in.available() < offset ||
519 parser_state.in.available() - offset < parser_state.meta.length)
520 return MIDI_PARSER_ERROR;
522 parser_state.meta.bytes =
523 parser_state.in.
peekStr(offset, parser_state.meta.length);
524 offset += parser_state.meta.length;
527 if (parser_state.meta.type == 0x51) {
529 midi_parse_N(parser_state.meta.bytes, parser_state.meta.length);
532 parser_state.in.
consume(offset);
534 parser_state.track.size -= offset;
535 return MIDI_PARSER_TRACK_META;
538 enum midi_parser_status midi_parse_event() {
539 parser_state.meta.bytes = NULL;
540 if (!midi_parse_vtime())
541 return MIDI_PARSER_EOB;
545 if (parser_state.in.available() <= 0 || parser_state.track.size <= 0)
546 return MIDI_PARSER_ERROR;
548 if (parser_state.in[0] < 0xf0) {
549 return midi_parse_channel_event();
551 parser_state.buffered_status = MIDI_STATUS_NA;
553 if (parser_state.in[0] == 0xf0)
554 return (midi_parser_status)midi_parse_sysex_event();
556 if (parser_state.in[0] == 0xff)
557 return midi_parse_meta_event();
559 return MIDI_PARSER_ERROR;
562 enum midi_parser_status midi_parse() {
563 if (parser_state.in.isEmpty())
564 return MIDI_PARSER_EOB;
566 switch (parser_state.status_internal) {
567 case MIDI_PARSER_INIT:
568 return midi_parse_header();
570 case MIDI_PARSER_HEADER:
571 return midi_parse_track();
573 case MIDI_PARSER_TRACK:
574 if (parser_state.track.size == 0) {
576 parser_state.status_internal = MIDI_PARSER_HEADER;
579 return midi_parse_event();
582 return MIDI_PARSER_ERROR;
Midi file parser which stores the data in RAM before making them available for parsing....
Definition: MidiFileParserMultiTrack.h:130
Midi File parser. Provide the data via write: You should try to keep the buffer as full as possible w...
Definition: MidiFileParser.h:61
midi_parser_state & parse()
Parse data in order to provide the next midi element.
Definition: MidiFileParser.h:92
int availableForWrite()
Max number of bytes that we can write.
Definition: MidiFileParser.h:89
const char * midi_meta_name(int type)
Provides the string description for the midi_meta value.
Definition: MidiFileParser.h:191
const char * midi_status_name(int status)
Provides the string description for the midi_status value.
Definition: MidiFileParser.h:153
const char * midi_file_format_name(int fmt)
Provides the string description for the file format.
Definition: MidiFileParser.h:176
bool begin(bool log=true, int bufferSize=MIDI_BUFFER_SIZE)
Initializes & starts the processing.
Definition: MidiFileParser.h:64
virtual size_t write(const uint8_t *data, size_t len)
Feed/Provide the midi data to the parser.
Definition: MidiFileParser.h:76
midi_parser_state & parseTimed()
Definition: MidiFileParser.h:112
void end()
Ends the processing: currently does nothing.
Definition: MidiFileParser.h:150
Definition: MidiFileParser.h:46
void consume(int offset)
Removes the next n characters from the ringbuffer.
Definition: RingBuffer.h:145
bool equals(const char *str)
Compares the string with the current peek values.
Definition: RingBuffer.h:139
uint8_t * peekStr(int idx, int len)
returns a temporary copy of the requested bytes
Definition: RingBuffer.h:119
MIDI Parser State Information.
Definition: MidiFileParserState.h:119