From c16e23b90918b4310af227bf3021de5a93e7891f Mon Sep 17 00:00:00 2001 From: Brad Barnett <127794626+bdbarnett@users.noreply.github.com> Date: Tue, 27 Oct 2026 21:43:55 -0500 Subject: [PATCH] Add a simpletest: a synthio chord through a low-pass, saved as a WAV --- docs/get-started/first-program.md | 5 +++ examples/audiodsp_simpletest.py | 63 +++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 examples/audiodsp_simpletest.py diff --git a/docs/get-started/first-program.md b/docs/get-started/first-program.md index 8eb8f6d..66b6aee 100644 --- a/docs/get-started/first-program.md +++ b/docs/get-started/first-program.md @@ -72,6 +72,11 @@ That renders a block from a source; it doesn't select a speaker. A host can pull the graph for offline rendering, give it to a mixer or a pump, or attach it to a platform-specific audio output. +[`examples/audiodsp_simpletest.py`](../../examples/audiodsp_simpletest.py) +takes it to something you can hear: a chord through a low-pass, pulled +block by block into a WAV file. It runs unchanged on MicroPython, +CircuitPython and CPython. + ## Next - [Play computed audio](../guides/play-computed-audio.md) with `audiopump.Ring`. diff --git a/examples/audiodsp_simpletest.py b/examples/audiodsp_simpletest.py new file mode 100644 index 0000000..bab2a35 --- /dev/null +++ b/examples/audiodsp_simpletest.py @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: 2026 Brad Barnett +# +# SPDX-License-Identifier: MIT +"""Play a chord on synthio, filter it, and save it as a WAV file. + +A synthio Synthesizer plays three notes, an audiofilters low-pass softens +them, and the program pulls the result one block at a time and writes it +to a WAV file you can open in any audio player. No speaker is needed. + +Needs: audiodsp, built into MicroPython or installed on CPython with +pip install pydevices-audiodsp. CircuitPython has these modules built in. + +Run it: + micropython audiodsp_simpletest.py + +Tested on: the unix port of MicroPython, the unix port of CircuitPython, +and CPython 3.12 on Linux. +""" + +import struct + +import audiocore +import audiofilters +import synthio + +# Change these to match your setup. +FILENAME = "chord.wav" +NOTES = (60, 64, 67) # MIDI note numbers: C, E and G +SAMPLE_RATE = 22050 +SECONDS = 2 +CUTOFF = 1200 # Hz; lower it for a darker sound + +# A synthesizer with a gentle attack and release. +envelope = synthio.Envelope(attack_time=0.05, release_time=0.5) +synth = synthio.Synthesizer(sample_rate=SAMPLE_RATE, envelope=envelope) + +# A low-pass filter that takes the synthesizer as its input. +lowpass = synthio.Biquad(synthio.FilterMode.LOW_PASS, CUTOFF) +effect = audiofilters.Filter(filter=lowpass, sample_rate=SAMPLE_RATE) +effect.play(synth) + +# Write the WAV header now; the data size is filled in at the end. +out = open(FILENAME, "wb") +out.write(b"RIFF\0\0\0\0WAVEfmt ") +out.write(struct.pack("= (SECONDS - 0.5) * SAMPLE_RATE * 2: + synth.release_all() + status, block = audiocore.get_buffer(effect) + size += out.write(block) + +# Go back and fill in the two sizes. +out.seek(4) +out.write(struct.pack("