synthio: Add `filter` argument to Synthesizer constructor

as step 1/n of adding FIR filtering
This commit is contained in:
Jeff Epler 2023-05-11 17:56:11 -05:00
parent d3eda0ad52
commit 33fb771b76
No known key found for this signature in database
GPG Key ID: D5BF15AB975AB4DE
9 changed files with 34 additions and 38 deletions

View File

@ -88,9 +88,6 @@ STATIC mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_a
mp_buffer_info_t bufinfo;
mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ);
mp_buffer_info_t bufinfo_waveform;
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
synthio_miditrack_obj_t *self = m_new_obj(synthio_miditrack_obj_t);
self->base.type = &synthio_miditrack_type;
@ -98,8 +95,8 @@ STATIC mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_a
(uint8_t *)bufinfo.buf, bufinfo.len,
args[ARG_tempo].u_int,
args[ARG_sample_rate].u_int,
bufinfo_waveform.buf,
bufinfo_waveform.len / 2,
args[ARG_waveform].u_obj,
mp_const_none,
args[ARG_envelope].u_obj
);

View File

@ -27,12 +27,11 @@
#pragma once
#include "shared-module/synthio/MidiTrack.h"
#include "py/obj.h"
extern const mp_obj_type_t synthio_miditrack_type;
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate, const int16_t *waveform, uint16_t waveform_len,
mp_obj_t envelope);
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self, const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate, mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope_obj);
void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self);
bool common_hal_synthio_miditrack_deinited(synthio_miditrack_obj_t *self);

View File

@ -59,30 +59,29 @@
//| :param int sample_rate: The desired playback sample rate; higher sample rate requires more memory
//| :param int channel_count: The number of output channels (1=mono, 2=stereo)
//| :param ReadableBuffer waveform: A single-cycle waveform. Default is a 50% duty cycle square wave. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
//| :param ReadableBuffer filter: Coefficients of an FIR filter to apply to notes with ``filter=True``. If specified, must be a ReadableBuffer of type 'h' (signed 16 bit)
//| :param Optional[Envelope] envelope: An object that defines the loudness of a note over time. The default envelope, `None` provides no ramping, voices turn instantly on and off.
//| """
STATIC mp_obj_t synthio_synthesizer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_sample_rate, ARG_channel_count, ARG_waveform, ARG_envelope };
enum { ARG_sample_rate, ARG_channel_count, ARG_waveform, ARG_envelope, ARG_filter };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_sample_rate, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 11025} },
{ MP_QSTR_channel_count, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} },
{ MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
{ MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
{ MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none } },
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
mp_buffer_info_t bufinfo_waveform;
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
synthio_synthesizer_obj_t *self = m_new_obj(synthio_synthesizer_obj_t);
self->base.type = &synthio_synthesizer_type;
common_hal_synthio_synthesizer_construct(self,
args[ARG_sample_rate].u_int,
args[ARG_channel_count].u_int,
bufinfo_waveform.buf,
bufinfo_waveform.len / 2,
args[ARG_waveform].u_obj,
args[ARG_filter].u_obj,
args[ARG_envelope].u_obj);
return MP_OBJ_FROM_PTR(self);

View File

@ -32,8 +32,8 @@
extern const mp_obj_type_t synthio_synthesizer_type;
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
uint32_t sample_rate, int channel_count, const int16_t *waveform, uint16_t waveform_length,
mp_obj_t envelope);
uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj,
mp_obj_t envelope_obj);
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self);
bool common_hal_synthio_synthesizer_deinited(synthio_synthesizer_obj_t *self);
uint32_t common_hal_synthio_synthesizer_get_sample_rate(synthio_synthesizer_obj_t *self);

View File

@ -207,10 +207,6 @@ STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma
}
pyb_file_obj_t *file = MP_OBJ_TO_PTR(args[ARG_file].u_obj);
mp_buffer_info_t bufinfo_waveform;
synthio_synth_parse_waveform(&bufinfo_waveform, args[ARG_waveform].u_obj);
uint8_t chunk_header[14];
f_rewind(&file->fp);
UINT bytes_read;
@ -250,7 +246,8 @@ STATIC mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma
result->base.type = &synthio_miditrack_type;
common_hal_synthio_miditrack_construct(result, buffer, track_size,
tempo, args[ARG_sample_rate].u_int, bufinfo_waveform.buf, bufinfo_waveform.len / 2,
tempo, args[ARG_sample_rate].u_int, args[ARG_waveform].u_obj,
mp_const_none,
args[ARG_envelope].u_obj
);

View File

@ -116,14 +116,13 @@ STATIC void start_parse(synthio_miditrack_obj_t *self) {
void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self,
const uint8_t *buffer, uint32_t len, uint32_t tempo, uint32_t sample_rate,
const int16_t *waveform, uint16_t waveform_length,
mp_obj_t envelope) {
mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope_obj) {
self->tempo = tempo;
self->track.buf = (void *)buffer;
self->track.len = len;
synthio_synth_init(&self->synth, sample_rate, 1, waveform, waveform_length, envelope);
synthio_synth_init(&self->synth, sample_rate, 1, waveform_obj, mp_const_none, envelope_obj);
start_parse(self);
}

View File

@ -32,10 +32,10 @@
void common_hal_synthio_synthesizer_construct(synthio_synthesizer_obj_t *self,
uint32_t sample_rate, int channel_count, const int16_t *waveform, uint16_t waveform_length,
mp_obj_t envelope) {
uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj,
mp_obj_t envelope_obj) {
synthio_synth_init(&self->synth, sample_rate, channel_count, waveform, waveform_length, envelope);
synthio_synth_init(&self->synth, sample_rate, channel_count, waveform_obj, filter_obj, envelope_obj);
}
void common_hal_synthio_synthesizer_deinit(synthio_synthesizer_obj_t *self) {

View File

@ -373,15 +373,16 @@ mp_obj_t synthio_synth_envelope_get(synthio_synth_t *synth) {
return synth->envelope_obj;
}
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, int channel_count, const int16_t *waveform, uint16_t waveform_length, mp_obj_t envelope_obj) {
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope_obj) {
mp_arg_validate_int_range(channel_count, 1, 2, MP_QSTR_channel_count);
synth->buffer_length = SYNTHIO_MAX_DUR * SYNTHIO_BYTES_PER_SAMPLE * channel_count;
synth->buffers[0] = m_malloc(synth->buffer_length, false);
synth->buffers[1] = m_malloc(synth->buffer_length, false);
synth->channel_count = channel_count;
synth->other_channel = -1;
synth->waveform = waveform;
synth->waveform_length = waveform_length;
synth->waveform_obj = waveform_obj;
synthio_synth_parse_waveform(&synth->waveform_bufinfo, waveform_obj);
synthio_synth_parse_filter(&synth->filter_bufinfo, filter_obj);
synth->sample_rate = sample_rate;
synthio_synth_envelope_set(synth, envelope_obj);
@ -402,21 +403,24 @@ void synthio_synth_get_buffer_structure(synthio_synth_t *synth, bool single_chan
}
}
STATIC bool parse_common(mp_buffer_info_t *bufinfo, mp_obj_t o, int16_t what) {
STATIC void parse_common(mp_buffer_info_t *bufinfo, mp_obj_t o, int16_t what, mp_int_t max_len) {
if (o != mp_const_none) {
mp_get_buffer_raise(o, bufinfo, MP_BUFFER_READ);
if (bufinfo->typecode != 'h') {
mp_raise_ValueError_varg(translate("%q must be array of type 'h'"), what);
}
mp_arg_validate_length_range(bufinfo->len / 2, 2, 1024, what);
return true;
mp_arg_validate_length_range(bufinfo->len / 2, 2, max_len, what);
}
return false;
}
void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t waveform_obj) {
*bufinfo_waveform = ((mp_buffer_info_t) { .buf = (void *)square_wave, .len = 4 });
parse_common(bufinfo_waveform, waveform_obj, MP_QSTR_waveform);
parse_common(bufinfo_waveform, waveform_obj, MP_QSTR_waveform, 16384);
}
void synthio_synth_parse_filter(mp_buffer_info_t *bufinfo_filter, mp_obj_t filter_obj) {
*bufinfo_filter = ((mp_buffer_info_t) { .buf = NULL, .len = 0 });
parse_common(bufinfo_filter, filter_obj, MP_QSTR_filter, 128);
}
STATIC int find_channel_with_note(synthio_synth_t *synth, mp_obj_t note) {

View File

@ -70,8 +70,9 @@ typedef struct synthio_synth {
uint16_t last_buffer_length;
uint8_t other_channel, buffer_index, other_buffer_index;
uint16_t waveform_length;
mp_buffer_info_t waveform_bufinfo, filter_bufinfo;
synthio_envelope_definition_t global_envelope_definition;
mp_obj_t envelope_obj;
mp_obj_t waveform_obj, filter_obj, envelope_obj;
synthio_midi_span_t span;
uint32_t accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
uint32_t ring_accum[CIRCUITPY_SYNTHIO_MAX_CHANNELS];
@ -91,12 +92,12 @@ typedef struct {
void synthio_synth_synthesize(synthio_synth_t *synth, uint8_t **buffer, uint32_t *buffer_length, uint8_t channel);
void synthio_synth_deinit(synthio_synth_t *synth);
bool synthio_synth_deinited(synthio_synth_t *synth);
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, int channel_count, const int16_t *waveform, uint16_t waveform_length,
mp_obj_t envelope);
void synthio_synth_init(synthio_synth_t *synth, uint32_t sample_rate, int channel_count, mp_obj_t waveform_obj, mp_obj_t filter_obj, mp_obj_t envelope);
void synthio_synth_get_buffer_structure(synthio_synth_t *synth, bool single_channel_output,
bool *single_buffer, bool *samples_signed, uint32_t *max_buffer_length, uint8_t *spacing);
void synthio_synth_reset_buffer(synthio_synth_t *synth, bool single_channel_output, uint8_t channel);
void synthio_synth_parse_waveform(mp_buffer_info_t *bufinfo_waveform, mp_obj_t waveform_obj);
void synthio_synth_parse_filter(mp_buffer_info_t *bufinfo_filter, mp_obj_t filter_obj);
void synthio_synth_parse_envelope(uint16_t *envelope_sustain_index, mp_buffer_info_t *bufinfo_envelope, mp_obj_t envelope_obj, mp_obj_t envelope_hold_obj);
bool synthio_span_change_note(synthio_synth_t *synth, mp_obj_t old_note, mp_obj_t new_note);