From 785ef5abd2a7ddff0024f8e6f04f357a6068f09c Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Fri, 17 Nov 2023 17:41:49 -0600 Subject: [PATCH 1/3] Add loop_start and loop_end properties to synthio.Note for waveshaping and sampling capabilities. --- shared-bindings/synthio/Note.c | 43 ++++++++++++++++++++++++++++++++ shared-bindings/synthio/Note.h | 6 +++++ shared-module/synthio/Note.c | 18 +++++++++++++ shared-module/synthio/Note.h | 2 ++ shared-module/synthio/__init__.c | 14 ++++++++--- 5 files changed, 80 insertions(+), 3 deletions(-) diff --git a/shared-bindings/synthio/Note.c b/shared-bindings/synthio/Note.c index acb12ae0db..60e7c67e1c 100644 --- a/shared-bindings/synthio/Note.c +++ b/shared-bindings/synthio/Note.c @@ -45,6 +45,8 @@ static const mp_arg_t note_properties[] = { { MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, { MP_QSTR_ring_bend, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, { MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, + { MP_QSTR_loop_start, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, + { MP_QSTR_loop_end, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, }; //| class Note: //| def __init__( @@ -60,6 +62,8 @@ static const mp_arg_t note_properties[] = { //| ring_frequency: float = 0.0, //| ring_bend: float = 0.0, //| ring_waveform: Optional[ReadableBuffer] = 0.0, +//| loop_start: int = 0, +//| loop_end: int = 0, //| ) -> None: //| """Construct a Note object, with a frequency in Hz, and optional panning, waveform, envelope, tremolo (volume change) and bend (frequency change). //| @@ -296,6 +300,43 @@ MP_PROPERTY_GETSET(synthio_note_ring_waveform_obj, (mp_obj_t)&synthio_note_get_ring_waveform_obj, (mp_obj_t)&synthio_note_set_ring_waveform_obj); +//| loop_start: int +//| """The index of where to begin looping waveform data. Must be greater than 0 and less than the total size of the waveform data.""" +STATIC mp_obj_t synthio_note_get_loop_start(mp_obj_t self_in) { + synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_synthio_note_get_loop_start(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_loop_start_obj, synthio_note_get_loop_start); + +STATIC mp_obj_t synthio_note_set_loop_start(mp_obj_t self_in, mp_obj_t arg) { + synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_synthio_note_set_loop_start(self, mp_obj_get_int(arg)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_loop_start_obj, synthio_note_set_loop_start); +MP_PROPERTY_GETSET(synthio_note_loop_start_obj, + (mp_obj_t)&synthio_note_get_loop_start_obj, + (mp_obj_t)&synthio_note_set_loop_start_obj); + +//| loop_end: int +//| """The index of where to end looping waveform data. Must be greater than 0 or ``loop_start`` and less than the total size of the waveform data.""" +//| +STATIC mp_obj_t synthio_note_get_loop_end(mp_obj_t self_in) { + synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_synthio_note_get_loop_end(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_loop_end_obj, synthio_note_get_loop_end); + +STATIC mp_obj_t synthio_note_set_loop_end(mp_obj_t self_in, mp_obj_t arg) { + synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_synthio_note_set_loop_end(self, mp_obj_get_int(arg)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_loop_end_obj, synthio_note_set_loop_end); +MP_PROPERTY_GETSET(synthio_note_loop_end_obj, + (mp_obj_t)&synthio_note_get_loop_end_obj, + (mp_obj_t)&synthio_note_set_loop_end_obj); + static void note_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -314,6 +355,8 @@ STATIC const mp_rom_map_elem_t synthio_note_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_ring_frequency), MP_ROM_PTR(&synthio_note_ring_frequency_obj) }, { MP_ROM_QSTR(MP_QSTR_ring_bend), MP_ROM_PTR(&synthio_note_ring_bend_obj) }, { MP_ROM_QSTR(MP_QSTR_ring_waveform), MP_ROM_PTR(&synthio_note_ring_waveform_obj) }, + { MP_ROM_QSTR(MP_QSTR_loop_start), MP_ROM_PTR(&synthio_note_loop_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_loop_end), MP_ROM_PTR(&synthio_note_loop_end_obj) }, }; STATIC MP_DEFINE_CONST_DICT(synthio_note_locals_dict, synthio_note_locals_dict_table); diff --git a/shared-bindings/synthio/Note.h b/shared-bindings/synthio/Note.h index 263ca701e3..89529c7c57 100644 --- a/shared-bindings/synthio/Note.h +++ b/shared-bindings/synthio/Note.h @@ -35,3 +35,9 @@ void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_ mp_obj_t common_hal_synthio_note_get_envelope_obj(synthio_note_obj_t *self); void common_hal_synthio_note_set_envelope(synthio_note_obj_t *self, mp_obj_t value); + +mp_int_t common_hal_synthio_note_get_loop_start(synthio_note_obj_t *self); +void common_hal_synthio_note_set_loop_start(synthio_note_obj_t *self, mp_int_t value_in); + +mp_int_t common_hal_synthio_note_get_loop_end(synthio_note_obj_t *self); +void common_hal_synthio_note_set_loop_end(synthio_note_obj_t *self, mp_int_t value_in); diff --git a/shared-module/synthio/Note.c b/shared-module/synthio/Note.c index f384269214..22bd74cd3c 100644 --- a/shared-module/synthio/Note.c +++ b/shared-module/synthio/Note.c @@ -135,6 +135,24 @@ void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_ self->ring_waveform_obj = ring_waveform_in; } +mp_int_t common_hal_synthio_note_get_loop_start(synthio_note_obj_t *self) { + return self->loop_start; +} + +void common_hal_synthio_note_set_loop_start(synthio_note_obj_t *self, mp_int_t value_in) { + mp_int_t val = mp_arg_validate_int_range(value_in, 0, 32767, MP_QSTR_loop_start); + self->loop_start = val; +} + +mp_int_t common_hal_synthio_note_get_loop_end(synthio_note_obj_t *self) { + return self->loop_end; +} + +void common_hal_synthio_note_set_loop_end(synthio_note_obj_t *self, mp_int_t value_in) { + mp_int_t val = mp_arg_validate_int_range(value_in, 0, 32767, MP_QSTR_loop_end); + self->loop_end = val; +} + void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate) { if (sample_rate == self->sample_rate) { return; diff --git a/shared-module/synthio/Note.h b/shared-module/synthio/Note.h index 8ae4fd910e..106b4a5bd2 100644 --- a/shared-module/synthio/Note.h +++ b/shared-module/synthio/Note.h @@ -50,6 +50,8 @@ typedef struct synthio_note_obj { mp_buffer_info_t waveform_buf; mp_buffer_info_t ring_waveform_buf; synthio_envelope_definition_t envelope_def; + + uint32_t loop_start, loop_end; } synthio_note_obj_t; void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate); diff --git a/shared-module/synthio/__init__.c b/shared-module/synthio/__init__.c index b0c1437887..1158fc0840 100644 --- a/shared-module/synthio/__init__.c +++ b/shared-module/synthio/__init__.c @@ -180,6 +180,7 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou uint32_t dds_rate; const int16_t *waveform = synth->waveform_bufinfo.buf; + uint32_t waveform_start = 0; uint32_t waveform_length = synth->waveform_bufinfo.len; uint32_t ring_dds_rate = 0; @@ -202,8 +203,14 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou if (note->waveform_buf.buf) { waveform = note->waveform_buf.buf; waveform_length = note->waveform_buf.len; + if (note->loop_start > 0 && note->loop_start < waveform_length) { + waveform_start = note->loop_start; + } + if (note->loop_end > waveform_start && note->loop_end < waveform_length) { + waveform_length = note->loop_end; + } } - dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)frequency_scaled * waveform_length, sample_rate); + dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)frequency_scaled * (waveform_length - waveform_start), sample_rate); if (note->ring_frequency_scaled != 0 && note->ring_waveform_buf.buf) { ring_waveform = note->ring_waveform_buf.buf; ring_waveform_length = note->ring_waveform_buf.len; @@ -215,6 +222,7 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou } } + uint32_t offset = waveform_start << SYNTHIO_FREQUENCY_SHIFT; uint32_t lim = waveform_length << SYNTHIO_FREQUENCY_SHIFT; uint32_t accum = synth->accum[chan]; @@ -225,7 +233,7 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou // can happen if note waveform gets set mid-note, but the expensive modulo is usually avoided if (accum > lim) { - accum %= lim; + accum = accum % lim + offset; } // first, fill with waveform @@ -233,7 +241,7 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou accum += dds_rate; // because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed if (accum > lim) { - accum -= lim; + accum = accum - lim + offset; } int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT; out_buffer32[i] = waveform[idx]; From 55db6b79475b85eac094cd003e9ddb11bcd07372 Mon Sep 17 00:00:00 2001 From: dcooperdalrymple Date: Mon, 20 Nov 2023 09:50:03 -0600 Subject: [PATCH 2/3] Added `SYNTHIO_WAVEFORM_SIZE` definition, renamed 'loop_...' to 'waveform_loop_...', added 'ring_waveform_loop_...` parameters, and updated docstrings. --- shared-bindings/synthio/Note.c | 99 +++++++++++++++++++++--------- shared-bindings/synthio/Note.h | 18 ++++-- shared-bindings/synthio/__init__.h | 2 + shared-module/synthio/Note.c | 38 +++++++++--- shared-module/synthio/Note.h | 4 +- shared-module/synthio/__init__.c | 24 +++++--- 6 files changed, 131 insertions(+), 54 deletions(-) diff --git a/shared-bindings/synthio/Note.c b/shared-bindings/synthio/Note.c index 60e7c67e1c..f0a18b13a6 100644 --- a/shared-bindings/synthio/Note.c +++ b/shared-bindings/synthio/Note.c @@ -40,13 +40,15 @@ static const mp_arg_t note_properties[] = { { MP_QSTR_amplitude, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } }, { MP_QSTR_bend, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } }, { MP_QSTR_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, + { MP_QSTR_waveform_loop_start, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, + { MP_QSTR_waveform_loop_end, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(SYNTHIO_WAVEFORM_SIZE) } }, { MP_QSTR_envelope, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, { MP_QSTR_filter, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, { MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, { MP_QSTR_ring_bend, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, { MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } }, - { MP_QSTR_loop_start, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, - { MP_QSTR_loop_end, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, + { MP_QSTR_ring_waveform_loop_start, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } }, + { MP_QSTR_ring_waveform_loop_end, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(SYNTHIO_WAVEFORM_SIZE) } }, }; //| class Note: //| def __init__( @@ -55,6 +57,8 @@ static const mp_arg_t note_properties[] = { //| frequency: float, //| panning: BlockInput = 0.0, //| waveform: Optional[ReadableBuffer] = None, +//| waveform_loop_start: int = 0, +//| waveform_loop_end: int = 0, //| envelope: Optional[Envelope] = None, //| amplitude: BlockInput = 0.0, //| bend: BlockInput = 0.0, @@ -62,8 +66,8 @@ static const mp_arg_t note_properties[] = { //| ring_frequency: float = 0.0, //| ring_bend: float = 0.0, //| ring_waveform: Optional[ReadableBuffer] = 0.0, -//| loop_start: int = 0, -//| loop_end: int = 0, +//| ring_waveform_loop_start: int = 0, +//| ring_waveform_loop_end: int = 0, //| ) -> None: //| """Construct a Note object, with a frequency in Hz, and optional panning, waveform, envelope, tremolo (volume change) and bend (frequency change). //| @@ -214,6 +218,43 @@ MP_PROPERTY_GETSET(synthio_note_waveform_obj, (mp_obj_t)&synthio_note_get_waveform_obj, (mp_obj_t)&synthio_note_set_waveform_obj); +//| waveform_loop_start: int +//| """The index of where to begin looping waveform data. Must be greater than or equal to 0 and less than the total size of the waveform data.""" +STATIC mp_obj_t synthio_note_get_waveform_loop_start(mp_obj_t self_in) { + synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_synthio_note_get_waveform_loop_start(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_waveform_loop_start_obj, synthio_note_get_waveform_loop_start); + +STATIC mp_obj_t synthio_note_set_waveform_loop_start(mp_obj_t self_in, mp_obj_t arg) { + synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_synthio_note_set_waveform_loop_start(self, mp_obj_get_int(arg)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_waveform_loop_start_obj, synthio_note_set_waveform_loop_start); +MP_PROPERTY_GETSET(synthio_note_waveform_loop_start_obj, + (mp_obj_t)&synthio_note_get_waveform_loop_start_obj, + (mp_obj_t)&synthio_note_set_waveform_loop_start_obj); + +//| waveform_loop_end: int +//| """The index of where to end looping waveform data. Must be greater than 0 or ``waveform_loop_start`` and less than or equal to the total size of the waveform data. If the value of the index does not match these parameters, the loop will occur at the end of the waveform.""" +//| +STATIC mp_obj_t synthio_note_get_waveform_loop_end(mp_obj_t self_in) { + synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(common_hal_synthio_note_get_waveform_loop_end(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_waveform_loop_end_obj, synthio_note_get_waveform_loop_end); + +STATIC mp_obj_t synthio_note_set_waveform_loop_end(mp_obj_t self_in, mp_obj_t arg) { + synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_synthio_note_set_waveform_loop_end(self, mp_obj_get_int(arg)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_waveform_loop_end_obj, synthio_note_set_waveform_loop_end); +MP_PROPERTY_GETSET(synthio_note_waveform_loop_end_obj, + (mp_obj_t)&synthio_note_get_waveform_loop_end_obj, + (mp_obj_t)&synthio_note_set_waveform_loop_end_obj); + //| envelope: Envelope //| """The envelope of this note""" @@ -300,42 +341,42 @@ MP_PROPERTY_GETSET(synthio_note_ring_waveform_obj, (mp_obj_t)&synthio_note_get_ring_waveform_obj, (mp_obj_t)&synthio_note_set_ring_waveform_obj); -//| loop_start: int -//| """The index of where to begin looping waveform data. Must be greater than 0 and less than the total size of the waveform data.""" -STATIC mp_obj_t synthio_note_get_loop_start(mp_obj_t self_in) { +//| ring_waveform_loop_start: int +//| """The index of where to begin looping waveform data. Must be greater than or equal to 0 and less than the total size of the waveform data.""" +STATIC mp_obj_t synthio_note_get_ring_waveform_loop_start(mp_obj_t self_in) { synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(common_hal_synthio_note_get_loop_start(self)); + return mp_obj_new_int(common_hal_synthio_note_get_ring_waveform_loop_start(self)); } -MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_loop_start_obj, synthio_note_get_loop_start); +MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_ring_waveform_loop_start_obj, synthio_note_get_ring_waveform_loop_start); -STATIC mp_obj_t synthio_note_set_loop_start(mp_obj_t self_in, mp_obj_t arg) { +STATIC mp_obj_t synthio_note_set_ring_waveform_loop_start(mp_obj_t self_in, mp_obj_t arg) { synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_synthio_note_set_loop_start(self, mp_obj_get_int(arg)); + common_hal_synthio_note_set_ring_waveform_loop_start(self, mp_obj_get_int(arg)); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_loop_start_obj, synthio_note_set_loop_start); -MP_PROPERTY_GETSET(synthio_note_loop_start_obj, - (mp_obj_t)&synthio_note_get_loop_start_obj, - (mp_obj_t)&synthio_note_set_loop_start_obj); +MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_ring_waveform_loop_start_obj, synthio_note_set_ring_waveform_loop_start); +MP_PROPERTY_GETSET(synthio_note_ring_waveform_loop_start_obj, + (mp_obj_t)&synthio_note_get_ring_waveform_loop_start_obj, + (mp_obj_t)&synthio_note_set_ring_waveform_loop_start_obj); -//| loop_end: int -//| """The index of where to end looping waveform data. Must be greater than 0 or ``loop_start`` and less than the total size of the waveform data.""" +//| ring_waveform_loop_end: int +//| """The index of where to end looping waveform data. Must be greater than 0 or ``waveform_loop_start`` and less than or equal to the total size of the waveform data. If the value of the index does not match these parameters, the loop will occur at the end of the waveform.""" //| -STATIC mp_obj_t synthio_note_get_loop_end(mp_obj_t self_in) { +STATIC mp_obj_t synthio_note_get_ring_waveform_loop_end(mp_obj_t self_in) { synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(common_hal_synthio_note_get_loop_end(self)); + return mp_obj_new_int(common_hal_synthio_note_get_ring_waveform_loop_end(self)); } -MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_loop_end_obj, synthio_note_get_loop_end); +MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_ring_waveform_loop_end_obj, synthio_note_get_ring_waveform_loop_end); -STATIC mp_obj_t synthio_note_set_loop_end(mp_obj_t self_in, mp_obj_t arg) { +STATIC mp_obj_t synthio_note_set_ring_waveform_loop_end(mp_obj_t self_in, mp_obj_t arg) { synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_synthio_note_set_loop_end(self, mp_obj_get_int(arg)); + common_hal_synthio_note_set_ring_waveform_loop_end(self, mp_obj_get_int(arg)); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_loop_end_obj, synthio_note_set_loop_end); -MP_PROPERTY_GETSET(synthio_note_loop_end_obj, - (mp_obj_t)&synthio_note_get_loop_end_obj, - (mp_obj_t)&synthio_note_set_loop_end_obj); +MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_ring_waveform_loop_end_obj, synthio_note_set_ring_waveform_loop_end); +MP_PROPERTY_GETSET(synthio_note_ring_waveform_loop_end_obj, + (mp_obj_t)&synthio_note_get_ring_waveform_loop_end_obj, + (mp_obj_t)&synthio_note_set_ring_waveform_loop_end_obj); @@ -349,14 +390,16 @@ STATIC const mp_rom_map_elem_t synthio_note_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_filter), MP_ROM_PTR(&synthio_note_filter_obj) }, { MP_ROM_QSTR(MP_QSTR_panning), MP_ROM_PTR(&synthio_note_panning_obj) }, { MP_ROM_QSTR(MP_QSTR_waveform), MP_ROM_PTR(&synthio_note_waveform_obj) }, + { MP_ROM_QSTR(MP_QSTR_waveform_loop_start), MP_ROM_PTR(&synthio_note_waveform_loop_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_waveform_loop_end), MP_ROM_PTR(&synthio_note_waveform_loop_end_obj) }, { MP_ROM_QSTR(MP_QSTR_envelope), MP_ROM_PTR(&synthio_note_envelope_obj) }, { MP_ROM_QSTR(MP_QSTR_amplitude), MP_ROM_PTR(&synthio_note_amplitude_obj) }, { MP_ROM_QSTR(MP_QSTR_bend), MP_ROM_PTR(&synthio_note_bend_obj) }, { MP_ROM_QSTR(MP_QSTR_ring_frequency), MP_ROM_PTR(&synthio_note_ring_frequency_obj) }, { MP_ROM_QSTR(MP_QSTR_ring_bend), MP_ROM_PTR(&synthio_note_ring_bend_obj) }, { MP_ROM_QSTR(MP_QSTR_ring_waveform), MP_ROM_PTR(&synthio_note_ring_waveform_obj) }, - { MP_ROM_QSTR(MP_QSTR_loop_start), MP_ROM_PTR(&synthio_note_loop_start_obj) }, - { MP_ROM_QSTR(MP_QSTR_loop_end), MP_ROM_PTR(&synthio_note_loop_end_obj) }, + { MP_ROM_QSTR(MP_QSTR_ring_waveform_loop_start), MP_ROM_PTR(&synthio_note_ring_waveform_loop_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_ring_waveform_loop_end), MP_ROM_PTR(&synthio_note_ring_waveform_loop_end_obj) }, }; STATIC MP_DEFINE_CONST_DICT(synthio_note_locals_dict, synthio_note_locals_dict_table); diff --git a/shared-bindings/synthio/Note.h b/shared-bindings/synthio/Note.h index 89529c7c57..150f8ee549 100644 --- a/shared-bindings/synthio/Note.h +++ b/shared-bindings/synthio/Note.h @@ -24,6 +24,12 @@ void common_hal_synthio_note_set_bend(synthio_note_obj_t *self, mp_obj_t value); mp_obj_t common_hal_synthio_note_get_waveform_obj(synthio_note_obj_t *self); void common_hal_synthio_note_set_waveform(synthio_note_obj_t *self, mp_obj_t value); +mp_int_t common_hal_synthio_note_get_waveform_loop_start(synthio_note_obj_t *self); +void common_hal_synthio_note_set_waveform_loop_start(synthio_note_obj_t *self, mp_int_t value_in); + +mp_int_t common_hal_synthio_note_get_waveform_loop_end(synthio_note_obj_t *self); +void common_hal_synthio_note_set_waveform_loop_end(synthio_note_obj_t *self, mp_int_t value_in); + mp_float_t common_hal_synthio_note_get_ring_frequency(synthio_note_obj_t *self); void common_hal_synthio_note_set_ring_frequency(synthio_note_obj_t *self, mp_float_t value); @@ -33,11 +39,11 @@ void common_hal_synthio_note_set_ring_bend(synthio_note_obj_t *self, mp_obj_t va mp_obj_t common_hal_synthio_note_get_ring_waveform_obj(synthio_note_obj_t *self); void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_t value); +mp_int_t common_hal_synthio_note_get_ring_waveform_loop_start(synthio_note_obj_t *self); +void common_hal_synthio_note_set_ring_waveform_loop_start(synthio_note_obj_t *self, mp_int_t value_in); + +mp_int_t common_hal_synthio_note_get_ring_waveform_loop_end(synthio_note_obj_t *self); +void common_hal_synthio_note_set_ring_waveform_loop_end(synthio_note_obj_t *self, mp_int_t value_in); + mp_obj_t common_hal_synthio_note_get_envelope_obj(synthio_note_obj_t *self); void common_hal_synthio_note_set_envelope(synthio_note_obj_t *self, mp_obj_t value); - -mp_int_t common_hal_synthio_note_get_loop_start(synthio_note_obj_t *self); -void common_hal_synthio_note_set_loop_start(synthio_note_obj_t *self, mp_int_t value_in); - -mp_int_t common_hal_synthio_note_get_loop_end(synthio_note_obj_t *self); -void common_hal_synthio_note_set_loop_end(synthio_note_obj_t *self, mp_int_t value_in); diff --git a/shared-bindings/synthio/__init__.h b/shared-bindings/synthio/__init__.h index 4a44277e85..e77e77ecc6 100644 --- a/shared-bindings/synthio/__init__.h +++ b/shared-bindings/synthio/__init__.h @@ -29,6 +29,8 @@ #include "py/objnamedtuple.h" #include "py/enum.h" +#define SYNTHIO_WAVEFORM_SIZE 16384 + typedef enum { SYNTHIO_ENVELOPE_STATE_ATTACK, SYNTHIO_ENVELOPE_STATE_DECAY, SYNTHIO_ENVELOPE_STATE_SUSTAIN, SYNTHIO_ENVELOPE_STATE_RELEASE diff --git a/shared-module/synthio/Note.c b/shared-module/synthio/Note.c index 22bd74cd3c..065ca388ac 100644 --- a/shared-module/synthio/Note.c +++ b/shared-module/synthio/Note.c @@ -120,6 +120,24 @@ void common_hal_synthio_note_set_waveform(synthio_note_obj_t *self, mp_obj_t wav self->waveform_obj = waveform_in; } +mp_int_t common_hal_synthio_note_get_waveform_loop_start(synthio_note_obj_t *self) { + return self->waveform_loop_start; +} + +void common_hal_synthio_note_set_waveform_loop_start(synthio_note_obj_t *self, mp_int_t value_in) { + mp_int_t val = mp_arg_validate_int_range(value_in, 0, SYNTHIO_WAVEFORM_SIZE - 1, MP_QSTR_waveform_loop_start); + self->waveform_loop_start = val; +} + +mp_int_t common_hal_synthio_note_get_waveform_loop_end(synthio_note_obj_t *self) { + return self->waveform_loop_end; +} + +void common_hal_synthio_note_set_waveform_loop_end(synthio_note_obj_t *self, mp_int_t value_in) { + mp_int_t val = mp_arg_validate_int_range(value_in, 1, SYNTHIO_WAVEFORM_SIZE, MP_QSTR_waveform_loop_end); + self->waveform_loop_end = val; +} + mp_obj_t common_hal_synthio_note_get_ring_waveform_obj(synthio_note_obj_t *self) { return self->ring_waveform_obj; } @@ -135,22 +153,22 @@ void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_ self->ring_waveform_obj = ring_waveform_in; } -mp_int_t common_hal_synthio_note_get_loop_start(synthio_note_obj_t *self) { - return self->loop_start; +mp_int_t common_hal_synthio_note_get_ring_waveform_loop_start(synthio_note_obj_t *self) { + return self->ring_waveform_loop_start; } -void common_hal_synthio_note_set_loop_start(synthio_note_obj_t *self, mp_int_t value_in) { - mp_int_t val = mp_arg_validate_int_range(value_in, 0, 32767, MP_QSTR_loop_start); - self->loop_start = val; +void common_hal_synthio_note_set_ring_waveform_loop_start(synthio_note_obj_t *self, mp_int_t value_in) { + mp_int_t val = mp_arg_validate_int_range(value_in, 0, SYNTHIO_WAVEFORM_SIZE - 1, MP_QSTR_ring_waveform_loop_start); + self->ring_waveform_loop_start = val; } -mp_int_t common_hal_synthio_note_get_loop_end(synthio_note_obj_t *self) { - return self->loop_end; +mp_int_t common_hal_synthio_note_get_ring_waveform_loop_end(synthio_note_obj_t *self) { + return self->ring_waveform_loop_end; } -void common_hal_synthio_note_set_loop_end(synthio_note_obj_t *self, mp_int_t value_in) { - mp_int_t val = mp_arg_validate_int_range(value_in, 0, 32767, MP_QSTR_loop_end); - self->loop_end = val; +void common_hal_synthio_note_set_ring_waveform_loop_end(synthio_note_obj_t *self, mp_int_t value_in) { + mp_int_t val = mp_arg_validate_int_range(value_in, 1, SYNTHIO_WAVEFORM_SIZE, MP_QSTR_ring_waveform_loop_end); + self->ring_waveform_loop_end = val; } void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate) { diff --git a/shared-module/synthio/Note.h b/shared-module/synthio/Note.h index 106b4a5bd2..c9faa60cac 100644 --- a/shared-module/synthio/Note.h +++ b/shared-module/synthio/Note.h @@ -48,10 +48,10 @@ typedef struct synthio_note_obj { int32_t ring_frequency_scaled, ring_frequency_bent; mp_buffer_info_t waveform_buf; + uint32_t waveform_loop_start, waveform_loop_end; mp_buffer_info_t ring_waveform_buf; + uint32_t ring_waveform_loop_start, ring_waveform_loop_end; synthio_envelope_definition_t envelope_def; - - uint32_t loop_start, loop_end; } synthio_note_obj_t; void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate); diff --git a/shared-module/synthio/__init__.c b/shared-module/synthio/__init__.c index 1158fc0840..06477e26ac 100644 --- a/shared-module/synthio/__init__.c +++ b/shared-module/synthio/__init__.c @@ -185,6 +185,7 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou uint32_t ring_dds_rate = 0; const int16_t *ring_waveform = NULL; + uint32_t ring_waveform_start = 0; uint32_t ring_waveform_length = 0; if (mp_obj_is_small_int(note_obj)) { @@ -203,18 +204,24 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou if (note->waveform_buf.buf) { waveform = note->waveform_buf.buf; waveform_length = note->waveform_buf.len; - if (note->loop_start > 0 && note->loop_start < waveform_length) { - waveform_start = note->loop_start; + if (note->waveform_loop_start > 0 && note->waveform_loop_start < waveform_length) { + waveform_start = note->waveform_loop_start; } - if (note->loop_end > waveform_start && note->loop_end < waveform_length) { - waveform_length = note->loop_end; + if (note->waveform_loop_end > waveform_start && note->waveform_loop_end < waveform_length) { + waveform_length = note->waveform_loop_end; } } dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)frequency_scaled * (waveform_length - waveform_start), sample_rate); if (note->ring_frequency_scaled != 0 && note->ring_waveform_buf.buf) { ring_waveform = note->ring_waveform_buf.buf; ring_waveform_length = note->ring_waveform_buf.len; - ring_dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)note->ring_frequency_bent * ring_waveform_length, sample_rate); + if (note->ring_waveform_loop_start > 0 && note->ring_waveform_loop_start < ring_waveform_length) { + ring_waveform_start = note->waveform_loop_start; + } + if (note->ring_waveform_loop_end > ring_waveform_start && note->ring_waveform_loop_end < ring_waveform_length) { + ring_waveform_length = note->ring_waveform_loop_end; + } + ring_dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)note->ring_frequency_bent * (ring_waveform_length - ring_waveform_start), sample_rate); uint32_t lim = ring_waveform_length << SYNTHIO_FREQUENCY_SHIFT; if (ring_dds_rate > lim / sizeof(int16_t)) { ring_dds_rate = 0; // can't ring at that frequency @@ -257,18 +264,19 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou // now modulate by ring and accumulate accum = synth->ring_accum[chan]; + offset = ring_waveform_start << SYNTHIO_FREQUENCY_SHIFT; lim = ring_waveform_length << SYNTHIO_FREQUENCY_SHIFT; // can happen if note waveform gets set mid-note, but the expensive modulo is usually avoided if (accum > lim) { - accum %= lim; + accum = accum % lim + offset; } for (uint16_t i = 0; i < dur; i++) { accum += ring_dds_rate; // because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed if (accum > lim) { - accum -= lim; + accum = accum - lim + offset; } int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT; int16_t wi = (ring_waveform[idx] * out_buffer32[i]) / 32768; @@ -442,7 +450,7 @@ STATIC void parse_common(mp_buffer_info_t *bufinfo, mp_obj_t o, int16_t what, mp 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 = 2 }); - parse_common(bufinfo_waveform, waveform_obj, MP_QSTR_waveform, 16384); + parse_common(bufinfo_waveform, waveform_obj, MP_QSTR_waveform, SYNTHIO_WAVEFORM_SIZE); } STATIC int find_channel_with_note(synthio_synth_t *synth, mp_obj_t note) { From 0fe750269fda9160ed440098da917fc5a2d35b6a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 20 Nov 2023 10:36:50 -0600 Subject: [PATCH 3/3] Documentation improvements --- shared-bindings/synthio/Note.c | 32 ++++++++++++++++++++++++------ shared-bindings/synthio/__init__.c | 6 ++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/shared-bindings/synthio/Note.c b/shared-bindings/synthio/Note.c index f0a18b13a6..17169e4665 100644 --- a/shared-bindings/synthio/Note.c +++ b/shared-bindings/synthio/Note.c @@ -58,7 +58,7 @@ static const mp_arg_t note_properties[] = { //| panning: BlockInput = 0.0, //| waveform: Optional[ReadableBuffer] = None, //| waveform_loop_start: int = 0, -//| waveform_loop_end: int = 0, +//| waveform_loop_end: int = waveform_max_length, //| envelope: Optional[Envelope] = None, //| amplitude: BlockInput = 0.0, //| bend: BlockInput = 0.0, @@ -67,7 +67,7 @@ static const mp_arg_t note_properties[] = { //| ring_bend: float = 0.0, //| ring_waveform: Optional[ReadableBuffer] = 0.0, //| ring_waveform_loop_start: int = 0, -//| ring_waveform_loop_end: int = 0, +//| ring_waveform_loop_end: int = waveform_max_length, //| ) -> None: //| """Construct a Note object, with a frequency in Hz, and optional panning, waveform, envelope, tremolo (volume change) and bend (frequency change). //| @@ -219,7 +219,11 @@ MP_PROPERTY_GETSET(synthio_note_waveform_obj, (mp_obj_t)&synthio_note_set_waveform_obj); //| waveform_loop_start: int -//| """The index of where to begin looping waveform data. Must be greater than or equal to 0 and less than the total size of the waveform data.""" +//| """The sample index of where to begin looping waveform data. +//| +//| Values outside the range ``0`` to ``waveform_max_length-1`` (inclusive) are rejected with a `ValueError`. +//| +//| Values greater than or equal to the actual waveform length are treated as 0.""" STATIC mp_obj_t synthio_note_get_waveform_loop_start(mp_obj_t self_in) { synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); return mp_obj_new_int(common_hal_synthio_note_get_waveform_loop_start(self)); @@ -237,7 +241,13 @@ MP_PROPERTY_GETSET(synthio_note_waveform_loop_start_obj, (mp_obj_t)&synthio_note_set_waveform_loop_start_obj); //| waveform_loop_end: int -//| """The index of where to end looping waveform data. Must be greater than 0 or ``waveform_loop_start`` and less than or equal to the total size of the waveform data. If the value of the index does not match these parameters, the loop will occur at the end of the waveform.""" +//| """The sample index of where to end looping waveform data. +//| +//| Values outside the range ``1`` to ``waveform_max_length`` (inclusive) are rejected with a `ValueError`. +//| +//| If the value is greater than the actual waveform length, or less than or equal to the loop start, the loop will occur at the end of the waveform. +//| +//| Use the `synthio.waveform_max_length` constant to set the loop point at the end of the wave form, no matter its length.""" //| STATIC mp_obj_t synthio_note_get_waveform_loop_end(mp_obj_t self_in) { synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -342,7 +352,11 @@ MP_PROPERTY_GETSET(synthio_note_ring_waveform_obj, (mp_obj_t)&synthio_note_set_ring_waveform_obj); //| ring_waveform_loop_start: int -//| """The index of where to begin looping waveform data. Must be greater than or equal to 0 and less than the total size of the waveform data.""" +//| """The sample index of where to begin looping waveform data. +//| +//| Values outside the range ``0`` to ``waveform_max_length-1`` (inclusive) are rejected with a `ValueError`. +//| +//| Values greater than or equal to the actual waveform length are treated as 0.""" STATIC mp_obj_t synthio_note_get_ring_waveform_loop_start(mp_obj_t self_in) { synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); return mp_obj_new_int(common_hal_synthio_note_get_ring_waveform_loop_start(self)); @@ -360,7 +374,13 @@ MP_PROPERTY_GETSET(synthio_note_ring_waveform_loop_start_obj, (mp_obj_t)&synthio_note_set_ring_waveform_loop_start_obj); //| ring_waveform_loop_end: int -//| """The index of where to end looping waveform data. Must be greater than 0 or ``waveform_loop_start`` and less than or equal to the total size of the waveform data. If the value of the index does not match these parameters, the loop will occur at the end of the waveform.""" +//| """The sample index of where to end looping waveform data. +//| +//| Values outside the range ``1`` to ``waveform_max_length`` (inclusive) are rejected with a `ValueError`. +//| +//| If the value is greater than the actual waveform length, or less than or equal to the loop start, the loop will occur at the end of the waveform. +//| +//| Use the `synthio.waveform_max_length` constant to set the loop point at the end of the wave form, no matter its length.""" //| STATIC mp_obj_t synthio_note_get_ring_waveform_loop_end(mp_obj_t self_in) { synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in); diff --git a/shared-bindings/synthio/__init__.c b/shared-bindings/synthio/__init__.c index 34c1675c60..f671ee8001 100644 --- a/shared-bindings/synthio/__init__.c +++ b/shared-bindings/synthio/__init__.c @@ -304,6 +304,11 @@ STATIC mp_obj_t voct_to_hz(mp_obj_t arg) { } MP_DEFINE_CONST_FUN_OBJ_1(synthio_voct_to_hz_obj, voct_to_hz); +//| +//| waveform_max_length: int +//| """The maximum number of samples permitted in a waveform""" +//| + #if CIRCUITPY_AUDIOCORE_DEBUG STATIC mp_obj_t synthio_lfo_tick(size_t n, const mp_obj_t *args) { shared_bindings_synthio_lfo_tick(48000); @@ -333,6 +338,7 @@ STATIC const mp_rom_map_elem_t synthio_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_Envelope), MP_ROM_PTR(&synthio_envelope_type_obj) }, { MP_ROM_QSTR(MP_QSTR_midi_to_hz), MP_ROM_PTR(&synthio_midi_to_hz_obj) }, { MP_ROM_QSTR(MP_QSTR_voct_to_hz), MP_ROM_PTR(&synthio_voct_to_hz_obj) }, + { MP_ROM_QSTR(MP_QSTR_waveform_max_length), MP_ROM_INT(SYNTHIO_WAVEFORM_SIZE) }, #if CIRCUITPY_AUDIOCORE_DEBUG { MP_ROM_QSTR(MP_QSTR_lfo_tick), MP_ROM_PTR(&synthio_lfo_tick_obj) }, #endif