diff --git a/shared-bindings/synthio/LFO.c b/shared-bindings/synthio/LFO.c index ce64881f7d..61c66c7fa9 100644 --- a/shared-bindings/synthio/LFO.c +++ b/shared-bindings/synthio/LFO.c @@ -37,16 +37,23 @@ //| //| Every `rate` seconds, the output of the LFO cycles through its `waveform`. //| The output at any particular moment is ``waveform[idx] * scale + offset``. -//| Internally, the calculation takes place in fixed point for speed. //| -//| `rate`, `offset`, `scale`, and `once` can be changed at run-time. +//| `rate`, `offset`, `scale`, and `once` can be changed at run-time. `waveform` may be mutated. //| -//| An LFO only updates if it is actually associated with a playing Note, -//| including if it is indirectly associated with the Note via an intermediate -//| LFO. +//| `waveform` must be a ``ReadableBuffer`` with elements of type `h` (16-bit signed integer). +//| Internally, the elements of `waveform` are scaled so that the input +//| range ``[-32768,32767]`` maps to ``[-1.0, 0.99996]``. +//| +//| An LFO only updates if it is actually associated with a playing `Synthesizer`, +//| including indirectly via a `Note` or another intermediate LFO. //| //| Using the same LFO as an input to multiple other LFOs or Notes is OK, but -//| the result if an LFO is tied to multiple Synthtesizer objects is undefined.""" +//| the result if an LFO is tied to multiple Synthtesizer objects is undefined. +//| +//| In the current implementation, LFOs are updated every 256 samples. This +//| should be considered an implementation detail, though it affects how LFOs +//| behave for instance when used to implement an integrator (``l.offset = l``). +//| """ //| //| def __init__( //| self, diff --git a/shared-module/synthio/LFO.c b/shared-module/synthio/LFO.c index b3f31e48f2..d0a69fb3d5 100644 --- a/shared-module/synthio/LFO.c +++ b/shared-module/synthio/LFO.c @@ -90,9 +90,9 @@ mp_float_t synthio_lfo_obj_tick_limited(synthio_lfo_slot_t *lfo_slot, mp_float_t return value; } -int32_t synthio_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi, int shift) { +int32_t synthio_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi) { mp_float_t value = synthio_lfo_obj_tick_limited(lfo_slot, lo, hi); - return (int32_t)MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(ldexp)(value, shift)); + return (int32_t)MICROPY_FLOAT_C_FUN(round)(MICROPY_FLOAT_C_FUN(ldexp)(value, 15)); } void synthio_lfo_assign_input(mp_obj_t obj, synthio_lfo_slot_t *slot, qstr arg_name) { diff --git a/shared-module/synthio/LFO.h b/shared-module/synthio/LFO.h index 8012d4c433..9b8aaf4e3d 100644 --- a/shared-module/synthio/LFO.h +++ b/shared-module/synthio/LFO.h @@ -55,8 +55,8 @@ typedef struct synthio_lfo_obj { mp_float_t synthio_lfo_obj_tick(synthio_lfo_slot_t *lfo_slot); // the same, but the output is constrained to be between lo and hi mp_float_t synthio_lfo_obj_tick_limited(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi); -// the same, but the output is constrained to be between lo and hi and converted to an integer with `shift` fractional bits -int32_t synthio_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi, int shift); +// the same, but the output is constrained to be between lo and hi and converted to an integer with 15 fractional bits +int32_t synthio_lfo_obj_tick_scaled(synthio_lfo_slot_t *lfo_slot, mp_float_t lo, mp_float_t hi); // Assign an object (which may be a float or a synthio_lfo_obj_t) to an lfo slot void synthio_lfo_assign_input(mp_obj_t obj, synthio_lfo_slot_t *lfo_slot, qstr arg_name); diff --git a/shared-module/synthio/Note.c b/shared-module/synthio/Note.c index 55a57f0ed3..f69f149bb4 100644 --- a/shared-module/synthio/Note.c +++ b/shared-module/synthio/Note.c @@ -177,7 +177,7 @@ STATIC uint32_t pitch_bend(uint32_t frequency_scaled, int32_t bend_value) { #define ALMOST_ONE (MICROPY_FLOAT_CONST(32767.) / 32768) uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_t dur, uint16_t loudness[2]) { - int panning = synthio_lfo_obj_tick_scaled(&self->panning, -ALMOST_ONE, ALMOST_ONE, 15); + int panning = synthio_lfo_obj_tick_scaled(&self->panning, -ALMOST_ONE, ALMOST_ONE); int left_panning_scaled, right_panning_scaled; if (panning >= 0) { left_panning_scaled = 32768; @@ -187,18 +187,18 @@ uint32_t synthio_note_step(synthio_note_obj_t *self, int32_t sample_rate, int16_ left_panning_scaled = 32767 + panning; } - int amplitude = synthio_lfo_obj_tick_scaled(&self->amplitude, ZERO, ALMOST_ONE, 15); + int amplitude = synthio_lfo_obj_tick_scaled(&self->amplitude, ZERO, ALMOST_ONE); left_panning_scaled = (left_panning_scaled * amplitude) >> 15; right_panning_scaled = (right_panning_scaled * amplitude) >> 15; loudness[0] = (loudness[0] * left_panning_scaled) >> 15; loudness[1] = (loudness[1] * right_panning_scaled) >> 15; if (self->ring_frequency_scaled != 0) { - int ring_bend_value = synthio_lfo_obj_tick_scaled(&self->ring_bend, -12, 12, 15); + int ring_bend_value = synthio_lfo_obj_tick_scaled(&self->ring_bend, -12, 12); self->ring_frequency_bent = pitch_bend(self->ring_frequency_scaled, ring_bend_value); } - int bend_value = synthio_lfo_obj_tick_scaled(&self->bend, -12, 12, 15); + int bend_value = synthio_lfo_obj_tick_scaled(&self->bend, -12, 12); uint32_t frequency_scaled = pitch_bend(self->frequency_scaled, bend_value); return frequency_scaled;