1010
1111
1212static void record_midi_stream_error (synthio_miditrack_obj_t * self ) {
13- self -> error_location = self -> pos ;
13+ // keep the first error; later ones are usually fallout from this one
14+ if (self -> error_location < 0 ) {
15+ self -> error_location = self -> pos ;
16+ }
1417 self -> pos = self -> track .len ;
1518}
1619
20+ // Check that `count` more bytes are available, ending the track if not.
21+ static bool need_bytes (synthio_miditrack_obj_t * self , size_t count ) {
22+ if (count > self -> track .len - self -> pos ) {
23+ record_midi_stream_error (self );
24+ return false;
25+ }
26+ return true;
27+ }
28+
1729static mp_obj_t parse_note (synthio_miditrack_obj_t * self ) {
1830 uint8_t * buffer = self -> track .buf ;
19- size_t len = self -> track .len ;
20- if (self -> pos + 1 >= len ) {
21- record_midi_stream_error (self );
31+ if (!need_bytes (self , 2 )) {
2232 return MP_OBJ_NEW_SMALL_INT (0 );
2333 }
2434 uint8_t note = buffer [(self -> pos )++ ];
@@ -28,23 +38,58 @@ static mp_obj_t parse_note(synthio_miditrack_obj_t *self) {
2838 return MP_OBJ_NEW_SMALL_INT (note );
2939}
3040
41+ // MIDI variable-length quantity: up to 4 bytes of 7 bits each.
42+ static uint32_t decode_varint (synthio_miditrack_obj_t * self ) {
43+ uint8_t * buffer = self -> track .buf ;
44+ uint32_t value = 0 ;
45+ for (size_t i = 0 ; i < 4 ; i ++ ) {
46+ if (!need_bytes (self , 1 )) {
47+ return 0 ;
48+ }
49+ uint8_t c = buffer [self -> pos ++ ];
50+ value = (value << 7 ) | (c & 0x7f );
51+ if (!(c & 0x80 )) {
52+ return value ;
53+ }
54+ }
55+ // a fifth continuation byte cannot be part of a valid quantity
56+ record_midi_stream_error (self );
57+ return 0 ;
58+ }
59+
3160static int decode_duration (synthio_miditrack_obj_t * self ) {
61+ uint32_t delta = decode_varint (self );
62+ return delta * self -> synth .base .sample_rate / self -> tempo ;
63+ }
64+
65+ // Meta (0xFF) and system exclusive (0xF0/0xF7) events both carry an explicit
66+ // length, so they can be skipped exactly rather than guessed at.
67+ static void decode_system_event (synthio_miditrack_obj_t * self , uint8_t status ) {
3268 uint8_t * buffer = self -> track .buf ;
33- size_t len = self -> track .len ;
34- uint8_t c ;
35- uint32_t delta = 0 ;
36- do {
37- c = buffer [self -> pos ++ ];
38- delta <<= 7 ;
39- delta |= c & 0x7f ;
40- } while ((c & 0x80 ) && (self -> pos < len ));
41-
42- // errors cannot be raised from the background task, so simply end the track.
43- if (c & 0x80 ) {
44- self -> pos = self -> track .len ;
69+ if (status == 0xFF ) {
70+ if (!need_bytes (self , 1 )) {
71+ return ;
72+ }
73+ uint8_t type = buffer [self -> pos ++ ];
74+ uint32_t length = decode_varint (self );
75+ if (!need_bytes (self , length )) {
76+ return ;
77+ }
78+ if (type == 0x2F ) { // End of Track
79+ self -> pos = self -> track .len ;
80+ return ;
81+ }
82+ self -> pos += length ;
83+ } else if (status == 0xF0 || status == 0xF7 ) {
84+ uint32_t length = decode_varint (self );
85+ if (!need_bytes (self , length )) {
86+ return ;
87+ }
88+ self -> pos += length ;
89+ } else {
90+ // system common and real-time bytes have no meaning in a file
4591 record_midi_stream_error (self );
4692 }
47- return delta * self -> synth .base .sample_rate / self -> tempo ;
4893}
4994
5095// invariant: pointing at a MIDI message
@@ -55,7 +100,8 @@ static void decode_until_pause(synthio_miditrack_obj_t *self) {
55100 if (self -> pos >= len ) {
56101 break ;
57102 }
58- switch (buffer [self -> pos ++ ] >> 4 ) {
103+ uint8_t status = buffer [self -> pos ++ ];
104+ switch (status >> 4 ) {
59105 case 8 : { // Note Off
60106 mp_obj_t note = parse_note (self );
61107 synthio_span_change_note (& self -> synth , note , SYNTHIO_SILENCE );
@@ -73,15 +119,13 @@ static void decode_until_pause(synthio_miditrack_obj_t *self) {
73119 break ;
74120 case 12 :
75121 case 13 : // one data byte to ignore
76- if (self -> pos >= len || buffer [self -> pos ++ ] > 127 ) {
122+ if (need_bytes ( self , 1 ) && buffer [self -> pos ++ ] > 127 ) {
77123 record_midi_stream_error (self );
78124 }
79125 break ;
80- case 15 : // the full syntax is too complicated, just assume it's "End of Track" event
81- self -> pos = len ;
126+ default : // 15: meta or system exclusive
127+ decode_system_event ( self , status ) ;
82128 break ;
83- default : // invalid event
84- record_midi_stream_error (self );
85129 }
86130 if (self -> pos < len ) {
87131 self -> synth .span .dur = decode_duration (self );
0 commit comments