From db1e01c462876846ae52ae98ca4da17d0b7f3208 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Fri, 31 Mar 2023 16:41:05 -0500 Subject: [PATCH] Don't require huge buffers for long-held notes --- shared-module/synthio/MidiTrack.c | 29 +++++++++++++++++++---------- shared-module/synthio/MidiTrack.h | 3 ++- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/shared-module/synthio/MidiTrack.c b/shared-module/synthio/MidiTrack.c index dd36e7c454..c745479c4a 100644 --- a/shared-module/synthio/MidiTrack.c +++ b/shared-module/synthio/MidiTrack.c @@ -27,9 +27,10 @@ #include "py/runtime.h" #include "shared-bindings/synthio/MidiTrack.h" -#define BITS_PER_SAMPLE 16 +#define BITS_PER_SAMPLE (16) #define BYTES_PER_SAMPLE (BITS_PER_SAMPLE / 8) -#define SILENCE 0x80 +#define MAX_DUR (512) +#define SILENCE (0x80) STATIC NORETURN void raise_midi_stream_error(uint32_t pos) { mp_raise_ValueError_varg(translate("Error in MIDI stream at position %d"), pos); @@ -77,6 +78,7 @@ STATIC void change_span_note(synthio_miditrack_obj_t *self, uint8_t old_note, ui terminate_span(self, dur); span.note[channel] = new_note; add_span(self, &span); + *dur = 0; } } @@ -144,7 +146,7 @@ void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self, for (int i = 0; i < self->total_spans; i++) { max_dur = MAX(self->track[i].dur, max_dur); } - self->buffer_length = max_dur * BYTES_PER_SAMPLE; + self->buffer_length = MIN(MAX_DUR, max_dur) * BYTES_PER_SAMPLE; self->buffer = m_malloc(self->buffer_length, false); } @@ -171,6 +173,7 @@ uint8_t common_hal_synthio_miditrack_get_channel_count(synthio_miditrack_obj_t * void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self, bool single_channel_output, uint8_t channel) { + self->remaining_dur = 0; self->next_span = 0; } @@ -189,14 +192,20 @@ static int count_active_channels(synthio_midi_span_t *span) { audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *self, bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) { + synthio_midi_span_t span = self->track[self->next_span - !!self->remaining_dur]; - if (self->next_span >= self->total_spans) { - *buffer_length = 0; - return GET_BUFFER_DONE; + if (self->remaining_dur == 0) { + if (self->next_span >= self->total_spans) { + *buffer_length = 0; + return GET_BUFFER_DONE; + } + self->next_span++; + self->remaining_dur = span.dur; } - synthio_midi_span_t span = self->track[self->next_span++]; - *buffer_length = span.dur * BYTES_PER_SAMPLE; + uint16_t dur = MIN(MAX_DUR, self->remaining_dur); + self->remaining_dur -= dur; + *buffer_length = dur * BYTES_PER_SAMPLE; memset(self->buffer, 0, *buffer_length); int32_t sample_rate = self->sample_rate; @@ -212,7 +221,7 @@ audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t uint16_t base_freq = notes[span.note[chan] % 12]; uint32_t accum = self->accum[chan]; uint32_t rate = base_freq * 2; - for (uint16_t i = 0; i < span.dur; i++) { + for (uint16_t i = 0; i < dur; i++) { accum += rate; int16_t semiperiod = (accum / sample_rate) >> (10 - octave); self->buffer[i] += semiperiod % 2 ? loudness : -loudness; @@ -223,7 +232,7 @@ audioio_get_buffer_result_t synthio_miditrack_get_buffer(synthio_miditrack_obj_t *buffer = (uint8_t *)self->buffer; - return self->next_span >= self->total_spans ? + return (self->remaining_dur == 0 && self->next_span >= self->total_spans) ? GET_BUFFER_DONE : GET_BUFFER_MORE_DATA; } diff --git a/shared-module/synthio/MidiTrack.h b/shared-module/synthio/MidiTrack.h index 083392e608..42ef9c93ae 100644 --- a/shared-module/synthio/MidiTrack.h +++ b/shared-module/synthio/MidiTrack.h @@ -41,10 +41,11 @@ typedef struct { uint32_t sample_rate; uint16_t *buffer; uint16_t buffer_length; - synthio_midi_span_t *track; + uint16_t remaining_dur; uint16_t next_span; uint16_t total_spans; uint32_t accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS]; + synthio_midi_span_t *track; } synthio_miditrack_obj_t;