Skip to content

Commit 4ff08e0

Browse files
committed
synthio: lfo: bugfixes & improvements
LFO waveforms are now linearly interpolated by default, but a new property (interpolated=False) can disable this. The 'once' logic was improved
1 parent e6c4d12 commit 4ff08e0

File tree

4 files changed

+58
-8
lines changed

4 files changed

+58
-8
lines changed

shared-bindings/synthio/LFO.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@
6464
//| scale: BlockInput = 1.0,
6565
//| offset: BlockInput = 0,
6666
//| phase_offset: BlockInput = 0,
67-
//| once=False
67+
//| once=False,
68+
//| interpolate=True
6869
//| ):
6970
//| pass
7071
static const mp_arg_t lfo_properties[] = {
@@ -74,6 +75,7 @@ static const mp_arg_t lfo_properties[] = {
7475
{ MP_QSTR_offset, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
7576
{ MP_QSTR_phase_offset, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
7677
{ MP_QSTR_once, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(0) } },
78+
{ MP_QSTR_interpolate, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_ROM_INT(1) } },
7779
};
7880

7981
STATIC mp_obj_t synthio_lfo_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
@@ -201,6 +203,26 @@ MP_PROPERTY_GETSET(synthio_lfo_once_obj,
201203
(mp_obj_t)&synthio_lfo_set_once_obj);
202204

203205

206+
//|
207+
//| interpolate: bool
208+
//| """True if the waveform should perform linear interpolation between values"""
209+
STATIC mp_obj_t synthio_lfo_get_interpolate(mp_obj_t self_in) {
210+
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
211+
return mp_obj_new_bool(common_hal_synthio_lfo_get_interpolate(self));
212+
}
213+
MP_DEFINE_CONST_FUN_OBJ_1(synthio_lfo_get_interpolate_obj, synthio_lfo_get_interpolate);
214+
215+
STATIC mp_obj_t synthio_lfo_set_interpolate(mp_obj_t self_in, mp_obj_t arg) {
216+
synthio_lfo_obj_t *self = MP_OBJ_TO_PTR(self_in);
217+
common_hal_synthio_lfo_set_interpolate(self, mp_obj_is_true(arg));
218+
return mp_const_none;
219+
}
220+
MP_DEFINE_CONST_FUN_OBJ_2(synthio_lfo_set_interpolate_obj, synthio_lfo_set_interpolate);
221+
MP_PROPERTY_GETSET(synthio_lfo_interpolate_obj,
222+
(mp_obj_t)&synthio_lfo_get_interpolate_obj,
223+
(mp_obj_t)&synthio_lfo_set_interpolate_obj);
224+
225+
204226
//|
205227
//| phase: float
206228
//| """The phase of the oscillator, in the range 0 to 1 (read-only)"""
@@ -250,6 +272,7 @@ STATIC const mp_rom_map_elem_t synthio_lfo_locals_dict_table[] = {
250272
{ MP_ROM_QSTR(MP_QSTR_offset), MP_ROM_PTR(&synthio_lfo_offset_obj) },
251273
{ MP_ROM_QSTR(MP_QSTR_phase_offset), MP_ROM_PTR(&synthio_lfo_phase_offset_obj) },
252274
{ MP_ROM_QSTR(MP_QSTR_once), MP_ROM_PTR(&synthio_lfo_once_obj) },
275+
{ MP_ROM_QSTR(MP_QSTR_interpolate), MP_ROM_PTR(&synthio_lfo_interpolate_obj) },
253276
{ MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&synthio_lfo_value_obj) },
254277
{ MP_ROM_QSTR(MP_QSTR_phase), MP_ROM_PTR(&synthio_lfo_phase_obj) },
255278
{ MP_ROM_QSTR(MP_QSTR_retrigger), MP_ROM_PTR(&synthio_lfo_retrigger_obj) },

shared-bindings/synthio/LFO.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ void common_hal_synthio_lfo_set_offset_obj(synthio_lfo_obj_t *self, mp_obj_t arg
4949
bool common_hal_synthio_lfo_get_once(synthio_lfo_obj_t *self);
5050
void common_hal_synthio_lfo_set_once(synthio_lfo_obj_t *self, bool arg);
5151

52+
bool common_hal_synthio_lfo_get_interpolate(synthio_lfo_obj_t *self);
53+
void common_hal_synthio_lfo_set_interpolate(synthio_lfo_obj_t *self, bool arg);
54+
5255
mp_float_t common_hal_synthio_lfo_get_value(synthio_lfo_obj_t *self);
5356

5457
mp_float_t common_hal_synthio_lfo_get_phase(synthio_lfo_obj_t *self);

shared-module/synthio/LFO.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#define ONE (MICROPY_FLOAT_CONST(1.))
3232
#define ZERO (MICROPY_FLOAT_CONST(0.))
3333

34+
#define ALMOST_ONE (MICROPY_FLOAT_CONST(32767.) / 32768)
35+
3436
mp_float_t common_hal_synthio_lfo_tick(mp_obj_t self_in) {
3537
synthio_lfo_obj_t *lfo = MP_OBJ_TO_PTR(self_in);
3638

@@ -41,26 +43,41 @@ mp_float_t common_hal_synthio_lfo_tick(mp_obj_t self_in) {
4143

4244
if (lfo->once) {
4345
if (rate > 0) {
44-
if (accum >= ONE) {
45-
accum = ONE;
46+
if (accum > ALMOST_ONE) {
47+
accum = ALMOST_ONE;
4648
}
4749
} else if (rate < 0 && accum < ZERO) {
4850
accum = ZERO;
4951
}
5052
} else {
5153
accum = accum - MICROPY_FLOAT_C_FUN(floor)(accum);
5254
}
55+
lfo->accum = accum - phase_offset;
5356

5457
int len = lfo->waveform_bufinfo.len;
55-
size_t idx = (int)(accum * len); // rounds down towards zero
58+
59+
mp_float_t scaled_accum = accum * (len - lfo->once);
60+
size_t idx = (size_t)MICROPY_FLOAT_C_FUN(floor)(scaled_accum);
5661
assert(idx < lfo->waveform_bufinfo.len);
5762

63+
int16_t *waveform = lfo->waveform_bufinfo.buf;
64+
mp_float_t value = waveform[idx];
65+
66+
if (lfo->interpolate) {
67+
68+
mp_float_t frac = scaled_accum - idx;
69+
70+
size_t idxp1 = idx + 1;
71+
if (idxp1 == lfo->waveform_bufinfo.len) {
72+
idxp1 = lfo->once ? idx : 0;
73+
}
74+
value = value * (1 - frac) + waveform[idxp1] * frac;
75+
}
76+
5877
mp_float_t scale = synthio_block_slot_get(&lfo->scale);
5978
mp_float_t offset = synthio_block_slot_get(&lfo->offset);
60-
int16_t *waveform = lfo->waveform_bufinfo.buf;
61-
mp_float_t value = MICROPY_FLOAT_C_FUN(ldexp)(waveform[idx], -15) * scale + offset;
79+
value = MICROPY_FLOAT_C_FUN(ldexp)(value, -15) * scale + offset;
6280

63-
lfo->accum = accum - phase_offset;
6481
return value;
6582
}
6683

@@ -104,6 +121,13 @@ void common_hal_synthio_lfo_set_once(synthio_lfo_obj_t *self, bool arg) {
104121
self->once = arg;
105122
}
106123

124+
bool common_hal_synthio_lfo_get_interpolate(synthio_lfo_obj_t *self) {
125+
return self->interpolate;
126+
}
127+
void common_hal_synthio_lfo_set_interpolate(synthio_lfo_obj_t *self, bool arg) {
128+
self->interpolate = arg;
129+
}
130+
107131
mp_float_t common_hal_synthio_lfo_get_value(synthio_lfo_obj_t *self) {
108132
return self->base.value;
109133
}

shared-module/synthio/LFO.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
typedef struct synthio_lfo_obj {
3232
synthio_block_base_t base;
33-
bool once;
33+
bool once, interpolate;
3434

3535
synthio_block_slot_t rate, scale, offset, phase_offset;
3636
mp_float_t accum;

0 commit comments

Comments
 (0)