Closed relic-se closed 2 weeks ago
In the example I shared originally, the duty cycle does not change linearly and produces an output that is not the best demonstration of controlling a square wave duty cycle through synthio.
I've modified the example to demonstrate controlling duty cycle with LFO in a linear fashion. This uses a "window" technique by controlling both waveform_loop_start
and waveform_loop_end
in tandem. The loop "window" should always have a size of SIZE // 2
(this would be affected by any drift between the two synthio.LFO
objects).
import audiobusio
import board
import synthio
import ulab.numpy as np
SIZE = 256
VOLUME = 32000
SAMPLE_RATE = 48000
RATE = 0.1
audio = audiobusio.I2SOut(bit_clock=board.GP0, word_select=board.GP1, data=board.GP2)
synth = synthio.Synthesizer(sample_rate=SAMPLE_RATE)
audio.play(synth)
waveform = np.concatenate((
np.full(SIZE // 2, VOLUME, dtype=np.int16),
np.full(SIZE // 2, -VOLUME, dtype=np.int16)
))
note = synthio.Note(
frequency=110,
waveform=waveform,
waveform_loop_start=synthio.LFO(
scale=SIZE // 4,
offset=SIZE // 4,
rate=RATE,
),
waveform_loop_end=synthio.LFO(
scale=SIZE // 4,
offset=SIZE * 3 // 4,
rate=RATE,
),
)
synth.press(note)
The test failure occurs because the repr of a note has changed slightly:
-(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, waveform_loop_start=0, waveform_loop_end=16384, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None, ring_waveform_loop_start=0, ring_waveform_loop_end=16384),)
+(Note(frequency=830.6076004423605, panning=0.0, amplitude=1.0, bend=0.0, waveform=None, waveform_loop_start=0.0, waveform_loop_end=16384.0, envelope=None, filter=None, ring_frequency=0.0, ring_bend=0.0, ring_waveform=None, ring_waveform_loop_start=0.0, ring_waveform_loop_end=16384.0),)
notice how e.g., the ring_waveform_loop_start
now prints as the float 0.0 instead of the int 0.
Do you know how or want to learn how to run the tests locally and update the expected output files? Otherwise, it looks like I could push that change to this branch, let me know if you'd like
I haven't figured out how to build unix build-standard with synthio support just yet, but I'll dive into that soon. I'm assuming your solution is to update the tests to use floats instead rather than attempt to get integers working with BlockInput
?
And I'd rather fix the tests first before merging.
yes, failed tests stop everything else from building, so it'll need to be fixed.
In ports/unix make VARIANT=coverage test
is the basic way to build & run tests that include synthio.
You can make VARIANT=coverage print-failures
. Then in the tests
directory there's a script endorse.py
which can be used to copy the new output file into an expected (".exp") file, or you can do it manually in the shell or however you like to manipulate/rename files.
The third target to know about is make VARIANT=coverage clean-failures
which removes anything in the results directory.
Thank you very much for the instruction, @jepler ! I'll tackle that this afternoon and get the PR ready for merging.
The following
int
properties ofsynthio.Note
have been updated to supportsynthio.BlockInput
:synthio.Note.waveform_loop_start
synthio.Note.waveform_loop_end
synthio.Note.ring_waveform_loop_start
synthio.Note.ring_waveform_loop_end
The most common use-case for this feature is controlling PWM/duty-cycle on a square waveform with a
synthio.LFO
object (see example below), but it could be utilized for other wave-shaping scenarios.This PR addresses the following issue: https://github.com/adafruit/circuitpython/issues/9780.
In this PR, I've also included two small bug fixes that I discovered along the way:
ring_waveform_loop_start
during note frequency calculation.synthio.Note.amplitude
changed from0.0
to1.0
in documentation to better reflect actual implementation.