From a4e60ec69818d750775a1c4cbc28a51318165125 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 22 Apr 2024 20:35:46 -0500 Subject: [PATCH 1/4] LFO: document some things that trip up users Closes: #9073 Closes: #9063 --- shared-bindings/synthio/LFO.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/shared-bindings/synthio/LFO.c b/shared-bindings/synthio/LFO.c index a2c3af2d0dd4..6518d1420f7f 100644 --- a/shared-bindings/synthio/LFO.c +++ b/shared-bindings/synthio/LFO.c @@ -57,7 +57,30 @@ STATIC const uint16_t triangle[] = {0, 32767, 0, -32767}; //| //| 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``). +//| behave for instance when used to implement an integrator (``l.offset = l``).A +//| +//| An LFO's output is not updated in any other way than when its associated +//| synthesizer updates it. For instance, if an LFO is created and its "first" +//| output is non-zero, its output is 0 until it is updated by its associated synthesizer. +//| Similarly, it is not updated when its inputs like ``scale`` and ``offset`` are changed. +//| +//| The interpolation of the waveform is necessarily different depending on the ``once`` +//| property. Consider a LFO with +//| ``waveform=np.array([0, 32767], dtype=np.int16), interpolate=True, once=True, rate=1``. +//| Over 1 second this LFO's output will change +//| from ``0`` to ``32767``, and will remain at ``32767`` thereafter, creating +//| a "bend out" over a duration of 1 second. +//| +//| However, when ``once=False``, this creates a sawtooth waveform with a period of 1 second. +//| Over about the first half second the input will increase from ``0`` to ``32767``, +//| then during the second half of the second it will decrease back to ``0``. +//| +//| The time of the peak output is different depending on the value of ``once``: +//| At 1.0s for ``once=True`` and at 0.5s for ``once=False``. +//| +//| Because of this difference in interpolation, dynamically updating the +//| ``once`` flag except when the LFO is at a phase of 0 will cause a step in +//| the LFO's output. //| """ //| //| def __init__( From bf10008c94a6c85b4e72762333a816e14b4de489 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Mon, 22 Apr 2024 21:04:04 -0500 Subject: [PATCH 2/4] Update shared-bindings/synthio/LFO.c Co-authored-by: Dan Halbert --- shared-bindings/synthio/LFO.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/synthio/LFO.c b/shared-bindings/synthio/LFO.c index 6518d1420f7f..a17b8377145d 100644 --- a/shared-bindings/synthio/LFO.c +++ b/shared-bindings/synthio/LFO.c @@ -57,7 +57,7 @@ STATIC const uint16_t triangle[] = {0, 32767, 0, -32767}; //| //| 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``).A +//| behave for instance when used to implement an integrator (``l.offset = l``). //| //| An LFO's output is not updated in any other way than when its associated //| synthesizer updates it. For instance, if an LFO is created and its "first" From 9979e1ac1d797bb3cb1769cf9e25afe6b25cbdaa Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 23 Apr 2024 07:56:13 -0500 Subject: [PATCH 3/4] Update LFO implementation detail notes based on Dan's feedback (& re-wrap some paragraphs, sorry) --- shared-bindings/synthio/LFO.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/shared-bindings/synthio/LFO.c b/shared-bindings/synthio/LFO.c index a17b8377145d..f6c07c66620e 100644 --- a/shared-bindings/synthio/LFO.c +++ b/shared-bindings/synthio/LFO.c @@ -43,7 +43,8 @@ STATIC const uint16_t triangle[] = {0, 32767, 0, -32767}; //| //| If `waveform` is None, a triangle waveform is used. //| -//| `rate`, `phase_offset`, `offset`, `scale`, and `once` can be changed at run-time. `waveform` may be mutated. +//| `rate`, `phase_offset`, `offset`, `scale`, and `once` can be changed at +//| run-time. `waveform` may be mutated. //| //| `waveform` must be a ``ReadableBuffer`` with elements of type ``'h'`` //| (16-bit signed integer). Internally, the elements of `waveform` are scaled @@ -59,21 +60,22 @@ STATIC const uint16_t triangle[] = {0, 32767, 0, -32767}; //| should be considered an implementation detail, though it affects how LFOs //| behave for instance when used to implement an integrator (``l.offset = l``). //| -//| An LFO's output is not updated in any other way than when its associated -//| synthesizer updates it. For instance, if an LFO is created and its "first" -//| output is non-zero, its output is 0 until it is updated by its associated synthesizer. -//| Similarly, it is not updated when its inputs like ``scale`` and ``offset`` are changed. +//| An LFO's output, which is reflected in its `value` property, is not +//| updated in any other way than when its associated synthesizer updates it. +//| For instance, if an LFO is created with ``offset=1``, its `value` will still +//| be ``0`` until it is updated by its associated synthesizer. Similarly, merely +//| updating its properties does not update its value property. //| -//| The interpolation of the waveform is necessarily different depending on the ``once`` -//| property. Consider a LFO with -//| ``waveform=np.array([0, 32767], dtype=np.int16), interpolate=True, once=True, rate=1``. -//| Over 1 second this LFO's output will change -//| from ``0`` to ``32767``, and will remain at ``32767`` thereafter, creating -//| a "bend out" over a duration of 1 second. +//| The interpolation of the waveform is necessarily different depending on the +//| ``once`` property. Consider a LFO with ``waveform=np.array([0, 100], +//| dtype=np.int16), interpolate=True, once=True, rate=1``. Over 1 second this +//| LFO's output will change from ``0`` to ``100``, and will remain at +//| ``100`` thereafter, creating a "bend out" over a duration of 1 second. //| -//| However, when ``once=False``, this creates a sawtooth waveform with a period of 1 second. -//| Over about the first half second the input will increase from ``0`` to ``32767``, -//| then during the second half of the second it will decrease back to ``0``. +//| However, when ``once=False``, this creates a sawtooth waveform with a +//| period of 1 second. Over about the first half second the input will +//| increase from ``0`` to ``100``, then during the second half of the second +//| it will decrease back to ``0``. //| //| The time of the peak output is different depending on the value of ``once``: //| At 1.0s for ``once=True`` and at 0.5s for ``once=False``. From 8fcb1bfa0d93c190369459784792b9bbdd1d94b0 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 23 Apr 2024 16:07:03 -0500 Subject: [PATCH 4/4] Update LFO.c --- shared-bindings/synthio/LFO.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/synthio/LFO.c b/shared-bindings/synthio/LFO.c index f6c07c66620e..7b44bb883beb 100644 --- a/shared-bindings/synthio/LFO.c +++ b/shared-bindings/synthio/LFO.c @@ -72,7 +72,7 @@ STATIC const uint16_t triangle[] = {0, 32767, 0, -32767}; //| LFO's output will change from ``0`` to ``100``, and will remain at //| ``100`` thereafter, creating a "bend out" over a duration of 1 second. //| -//| However, when ``once=False``, this creates a sawtooth waveform with a +//| However, when ``once=False``, this creates a triangle waveform with a //| period of 1 second. Over about the first half second the input will //| increase from ``0`` to ``100``, then during the second half of the second //| it will decrease back to ``0``.