Skip to content

Commit 785ef5a

Browse files
committed
Add loop_start and loop_end properties to synthio.Note for waveshaping and sampling capabilities.
1 parent 87163de commit 785ef5a

File tree

5 files changed

+80
-3
lines changed

5 files changed

+80
-3
lines changed

shared-bindings/synthio/Note.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ static const mp_arg_t note_properties[] = {
4545
{ MP_QSTR_ring_frequency, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
4646
{ MP_QSTR_ring_bend, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
4747
{ MP_QSTR_ring_waveform, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_NONE } },
48+
{ MP_QSTR_loop_start, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
49+
{ MP_QSTR_loop_end, MP_ARG_OBJ, {.u_obj = MP_ROM_INT(0) } },
4850
};
4951
//| class Note:
5052
//| def __init__(
@@ -60,6 +62,8 @@ static const mp_arg_t note_properties[] = {
6062
//| ring_frequency: float = 0.0,
6163
//| ring_bend: float = 0.0,
6264
//| ring_waveform: Optional[ReadableBuffer] = 0.0,
65+
//| loop_start: int = 0,
66+
//| loop_end: int = 0,
6367
//| ) -> None:
6468
//| """Construct a Note object, with a frequency in Hz, and optional panning, waveform, envelope, tremolo (volume change) and bend (frequency change).
6569
//|
@@ -296,6 +300,43 @@ MP_PROPERTY_GETSET(synthio_note_ring_waveform_obj,
296300
(mp_obj_t)&synthio_note_get_ring_waveform_obj,
297301
(mp_obj_t)&synthio_note_set_ring_waveform_obj);
298302

303+
//| loop_start: int
304+
//| """The index of where to begin looping waveform data. Must be greater than 0 and less than the total size of the waveform data."""
305+
STATIC mp_obj_t synthio_note_get_loop_start(mp_obj_t self_in) {
306+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
307+
return mp_obj_new_int(common_hal_synthio_note_get_loop_start(self));
308+
}
309+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_loop_start_obj, synthio_note_get_loop_start);
310+
311+
STATIC mp_obj_t synthio_note_set_loop_start(mp_obj_t self_in, mp_obj_t arg) {
312+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
313+
common_hal_synthio_note_set_loop_start(self, mp_obj_get_int(arg));
314+
return mp_const_none;
315+
}
316+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_loop_start_obj, synthio_note_set_loop_start);
317+
MP_PROPERTY_GETSET(synthio_note_loop_start_obj,
318+
(mp_obj_t)&synthio_note_get_loop_start_obj,
319+
(mp_obj_t)&synthio_note_set_loop_start_obj);
320+
321+
//| loop_end: int
322+
//| """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."""
323+
//|
324+
STATIC mp_obj_t synthio_note_get_loop_end(mp_obj_t self_in) {
325+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
326+
return mp_obj_new_int(common_hal_synthio_note_get_loop_end(self));
327+
}
328+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_note_get_loop_end_obj, synthio_note_get_loop_end);
329+
330+
STATIC mp_obj_t synthio_note_set_loop_end(mp_obj_t self_in, mp_obj_t arg) {
331+
synthio_note_obj_t *self = MP_OBJ_TO_PTR(self_in);
332+
common_hal_synthio_note_set_loop_end(self, mp_obj_get_int(arg));
333+
return mp_const_none;
334+
}
335+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_note_set_loop_end_obj, synthio_note_set_loop_end);
336+
MP_PROPERTY_GETSET(synthio_note_loop_end_obj,
337+
(mp_obj_t)&synthio_note_get_loop_end_obj,
338+
(mp_obj_t)&synthio_note_set_loop_end_obj);
339+
299340

300341

301342
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[] = {
314355
{ MP_ROM_QSTR(MP_QSTR_ring_frequency), MP_ROM_PTR(&synthio_note_ring_frequency_obj) },
315356
{ MP_ROM_QSTR(MP_QSTR_ring_bend), MP_ROM_PTR(&synthio_note_ring_bend_obj) },
316357
{ MP_ROM_QSTR(MP_QSTR_ring_waveform), MP_ROM_PTR(&synthio_note_ring_waveform_obj) },
358+
{ MP_ROM_QSTR(MP_QSTR_loop_start), MP_ROM_PTR(&synthio_note_loop_start_obj) },
359+
{ MP_ROM_QSTR(MP_QSTR_loop_end), MP_ROM_PTR(&synthio_note_loop_end_obj) },
317360
};
318361
STATIC MP_DEFINE_CONST_DICT(synthio_note_locals_dict, synthio_note_locals_dict_table);
319362

shared-bindings/synthio/Note.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,9 @@ void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_
3535

3636
mp_obj_t common_hal_synthio_note_get_envelope_obj(synthio_note_obj_t *self);
3737
void common_hal_synthio_note_set_envelope(synthio_note_obj_t *self, mp_obj_t value);
38+
39+
mp_int_t common_hal_synthio_note_get_loop_start(synthio_note_obj_t *self);
40+
void common_hal_synthio_note_set_loop_start(synthio_note_obj_t *self, mp_int_t value_in);
41+
42+
mp_int_t common_hal_synthio_note_get_loop_end(synthio_note_obj_t *self);
43+
void common_hal_synthio_note_set_loop_end(synthio_note_obj_t *self, mp_int_t value_in);

shared-module/synthio/Note.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,24 @@ void common_hal_synthio_note_set_ring_waveform(synthio_note_obj_t *self, mp_obj_
135135
self->ring_waveform_obj = ring_waveform_in;
136136
}
137137

138+
mp_int_t common_hal_synthio_note_get_loop_start(synthio_note_obj_t *self) {
139+
return self->loop_start;
140+
}
141+
142+
void common_hal_synthio_note_set_loop_start(synthio_note_obj_t *self, mp_int_t value_in) {
143+
mp_int_t val = mp_arg_validate_int_range(value_in, 0, 32767, MP_QSTR_loop_start);
144+
self->loop_start = val;
145+
}
146+
147+
mp_int_t common_hal_synthio_note_get_loop_end(synthio_note_obj_t *self) {
148+
return self->loop_end;
149+
}
150+
151+
void common_hal_synthio_note_set_loop_end(synthio_note_obj_t *self, mp_int_t value_in) {
152+
mp_int_t val = mp_arg_validate_int_range(value_in, 0, 32767, MP_QSTR_loop_end);
153+
self->loop_end = val;
154+
}
155+
138156
void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate) {
139157
if (sample_rate == self->sample_rate) {
140158
return;

shared-module/synthio/Note.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ typedef struct synthio_note_obj {
5050
mp_buffer_info_t waveform_buf;
5151
mp_buffer_info_t ring_waveform_buf;
5252
synthio_envelope_definition_t envelope_def;
53+
54+
uint32_t loop_start, loop_end;
5355
} synthio_note_obj_t;
5456

5557
void synthio_note_recalculate(synthio_note_obj_t *self, int32_t sample_rate);

shared-module/synthio/__init__.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou
180180

181181
uint32_t dds_rate;
182182
const int16_t *waveform = synth->waveform_bufinfo.buf;
183+
uint32_t waveform_start = 0;
183184
uint32_t waveform_length = synth->waveform_bufinfo.len;
184185

185186
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
202203
if (note->waveform_buf.buf) {
203204
waveform = note->waveform_buf.buf;
204205
waveform_length = note->waveform_buf.len;
206+
if (note->loop_start > 0 && note->loop_start < waveform_length) {
207+
waveform_start = note->loop_start;
208+
}
209+
if (note->loop_end > waveform_start && note->loop_end < waveform_length) {
210+
waveform_length = note->loop_end;
211+
}
205212
}
206-
dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)frequency_scaled * waveform_length, sample_rate);
213+
dds_rate = synthio_frequency_convert_scaled_to_dds((uint64_t)frequency_scaled * (waveform_length - waveform_start), sample_rate);
207214
if (note->ring_frequency_scaled != 0 && note->ring_waveform_buf.buf) {
208215
ring_waveform = note->ring_waveform_buf.buf;
209216
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
215222
}
216223
}
217224

225+
uint32_t offset = waveform_start << SYNTHIO_FREQUENCY_SHIFT;
218226
uint32_t lim = waveform_length << SYNTHIO_FREQUENCY_SHIFT;
219227
uint32_t accum = synth->accum[chan];
220228

@@ -225,15 +233,15 @@ static bool synth_note_into_buffer(synthio_synth_t *synth, int chan, int32_t *ou
225233

226234
// can happen if note waveform gets set mid-note, but the expensive modulo is usually avoided
227235
if (accum > lim) {
228-
accum %= lim;
236+
accum = accum % lim + offset;
229237
}
230238

231239
// first, fill with waveform
232240
for (uint16_t i = 0; i < dur; i++) {
233241
accum += dds_rate;
234242
// because dds_rate is low enough, the subtraction is guaranteed to go back into range, no expensive modulo needed
235243
if (accum > lim) {
236-
accum -= lim;
244+
accum = accum - lim + offset;
237245
}
238246
int16_t idx = accum >> SYNTHIO_FREQUENCY_SHIFT;
239247
out_buffer32[i] = waveform[idx];

0 commit comments

Comments
 (0)