Skip to content

Commit 78a7693

Browse files
authored
Merge pull request #11661 from adafruit/synthio-miditrack-long-delays
synthio: keep MidiTrack's timing accurate for long delays
2 parents 81191e6 + 6dfa7e5 commit 78a7693

2 files changed

Lines changed: 25 additions & 8 deletions

File tree

‎shared-module/synthio/MidiTrack.c‎

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ static bool need_bytes(synthio_miditrack_obj_t *self, size_t count) {
2626
return true;
2727
}
2828

29+
// A duration can be longer than span.dur (a uint16_t) can hold, so the
30+
// remainder is kept in `pending` and handed over a chunk at a time.
31+
static void set_remaining(synthio_miditrack_obj_t *self, uint32_t samples) {
32+
uint16_t chunk = samples > UINT16_MAX ? UINT16_MAX : (uint16_t)samples;
33+
self->synth.span.dur = chunk;
34+
self->pending = samples - chunk;
35+
}
36+
2937
// Returns the note in the low 8 bits and the velocity in the next 8, or -1
3038
// if the pair could not be read.
3139
static int parse_note(synthio_miditrack_obj_t *self) {
@@ -61,9 +69,10 @@ static uint32_t decode_varint(synthio_miditrack_obj_t *self) {
6169
return 0;
6270
}
6371

64-
static int decode_duration(synthio_miditrack_obj_t *self) {
72+
static void decode_duration(synthio_miditrack_obj_t *self) {
6573
uint32_t delta = decode_varint(self);
66-
return delta * self->synth.base.sample_rate / self->tempo;
74+
uint64_t samples = (uint64_t)delta * self->synth.base.sample_rate / self->tempo;
75+
set_remaining(self, samples > UINT32_MAX ? UINT32_MAX : (uint32_t)samples);
6776
}
6877

6978
// Meta (0xFF) and system exclusive (0xF0/0xF7) events both carry an explicit
@@ -148,17 +157,18 @@ static void decode_until_pause(synthio_miditrack_obj_t *self) {
148157
break;
149158
}
150159
if (self->pos < len) {
151-
self->synth.span.dur = decode_duration(self);
160+
decode_duration(self);
152161
}
153-
} while (self->pos < len && self->synth.span.dur == 0);
162+
} while (self->pos < len && self->synth.span.dur == 0 && self->pending == 0);
154163
}
155164

156165
static void start_parse(synthio_miditrack_obj_t *self) {
157166
self->pos = 0;
158167
self->error_location = -1;
159168
self->running_status = 0;
160-
self->synth.span.dur = decode_duration(self);
161-
if (self->synth.span.dur == 0) {
169+
self->pending = 0;
170+
decode_duration(self);
171+
if (self->synth.span.dur == 0 && self->pending == 0) {
162172
// the usual case: the file starts with some MIDI event, not a delay
163173
decode_until_pause(self);
164174
}
@@ -191,7 +201,9 @@ mp_int_t common_hal_synthio_miditrack_get_tempo(synthio_miditrack_obj_t *self) {
191201

192202
void common_hal_synthio_miditrack_set_tempo(synthio_miditrack_obj_t *self, mp_int_t value) {
193203
mp_int_t val = mp_arg_validate_int_min(value, 1, MP_QSTR_tempo);
194-
self->synth.span.dur = (uint32_t)self->synth.span.dur * self->tempo / val;
204+
uint64_t remaining = (uint64_t)self->synth.span.dur + self->pending;
205+
remaining = remaining * self->tempo / val;
206+
set_remaining(self, remaining > UINT32_MAX ? UINT32_MAX : (uint32_t)remaining);
195207
self->tempo = val;
196208
}
197209

@@ -210,7 +222,10 @@ audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t
210222

211223
synthio_synth_synthesize(&self->synth, buffer, buffer_length, single_channel_output ? 0 : channel);
212224
if (self->synth.span.dur == 0) {
213-
if (self->pos == self->track.len) {
225+
if (self->pending != 0) {
226+
// still inside a delay too long for a single span
227+
set_remaining(self, self->pending);
228+
} else if (self->pos == self->track.len) {
214229
return GET_BUFFER_DONE;
215230
} else {
216231
decode_until_pause(self);

‎shared-module/synthio/MidiTrack.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ typedef struct {
1717
size_t pos;
1818
mp_int_t error_location;
1919
uint32_t tempo;
20+
// samples of the current delay that did not fit in span.dur
21+
uint32_t pending;
2022
// last channel status byte, for events that omit it (running status)
2123
uint8_t running_status;
2224
} synthio_miditrack_obj_t;

0 commit comments

Comments
 (0)