Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/get-started/first-program.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,10 @@ frames of 16-bit stereo, so 100 of them make 25,600 frames.
Put more effects in the chain with [Chain effects](../guides/chain-effects.md),
or play a part in time with
[Sequence a part](../guides/sequence-a-part.md).

To hear something right away, run one of the two complete examples. Each
writes a WAV file you can open in any audio player:
[audioinstruments_simpletest.py](https://github.com/PyDevices/audiocomponents/blob/main/examples/audioinstruments_simpletest.py)
plays a tune on a Rhodes electric piano, and
[audioeffects_simpletest.py](https://github.com/PyDevices/audiocomponents/blob/main/examples/audioeffects_simpletest.py)
runs plucked notes through a tape echo and a hall.
73 changes: 73 additions & 0 deletions examples/audioeffects_simpletest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# SPDX-FileCopyrightText: 2026 Brad Barnett
#
# SPDX-License-Identifier: MIT
"""Run plucked notes through a tape echo and a hall, and save a WAV file.

A synthio Synthesizer plays short notes, a TapeDelay repeats them and a
Reverb puts them in a hall. Each effect takes the one before it as its
input, and the program writes the end of the chain to a WAV file you can
open in any audio player. Change the names to hear any of the forty-five.

Needs: audioeffects and audiodsp. Both are built into the PyDevices
MicroPython firmware; on CPython, pip install pydevices-audioeffects.

Run it:
micropython audioeffects_simpletest.py

Tested on: the unix port of MicroPython and CPython 3.12 on Linux.
"""

import struct

import audiocore
import audioeffects
import synthio

# Change these to match your setup.
FILENAME = "echoes.wav"
NOTES = (60, 67, 64, 72) # MIDI note numbers
NOTE_SECONDS = 0.6
SAMPLE_RATE = 22050
ECHO_MS = 300 # time between the repeats

# A synthesizer whose notes die away quickly, so the echoes stand out.
envelope = synthio.Envelope(attack_time=0.005, decay_time=0.2, sustain_level=0)
synth = synthio.Synthesizer(sample_rate=SAMPLE_RATE, envelope=envelope)

# A tape echo fed by the synthesizer, then a hall fed by the echo.
echo = audioeffects.create("TapeDelay", synth, SAMPLE_RATE,
time_ms=ECHO_MS, feedback=0.4, mix=0.35)
hall = audioeffects.create("Reverb", echo.output, SAMPLE_RATE,
character="hall", mix=0.3)

# 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("<IHHIIHH", 16, 1, 1, SAMPLE_RATE, SAMPLE_RATE * 2, 2, 16))
out.write(b"data\0\0\0\0")
size = 0


# Pull audio from the end of the chain for the given number of seconds.
def record(seconds):
global size
end = size + int(seconds * SAMPLE_RATE) * 2
while size < end:
status, block = audiocore.get_buffer(hall.output)
size += out.write(block)


# Pluck each note, then leave two seconds for the echoes and the hall.
for note in NOTES:
synth.press(note)
record(NOTE_SECONDS)
synth.release(note)
record(2)

# Go back and fill in the two sizes.
out.seek(4)
out.write(struct.pack("<I", 36 + size))
out.seek(40)
out.write(struct.pack("<I", size))
out.close()
print("Wrote", FILENAME)
69 changes: 69 additions & 0 deletions examples/audioinstruments_simpletest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SPDX-FileCopyrightText: 2026 Brad Barnett
#
# SPDX-License-Identifier: MIT
"""Play a short tune on a classic electric piano and save it as a WAV file.

The program builds one instrument by name, plays four notes and writes
what comes out to a WAV file you can open in any audio player. Then it
lists the instrument's knobs, which inst.set_macro() turns. Change
INSTRUMENT to hear another of the fifty-five.

Needs: audioinstruments and audiodsp. Both are built into the PyDevices
MicroPython firmware; on CPython, pip install pydevices-audioinstruments.

Run it:
micropython audioinstruments_simpletest.py

Tested on: the unix port of MicroPython and CPython 3.12 on Linux.
"""

import struct

import audiocore
import audioinstruments

# Change these to match your setup.
FILENAME = "tune.wav"
INSTRUMENT = "rhodes" # try "juno106", "minimoog", "dx7" or "tr808"
NOTES = (60, 64, 67, 72) # MIDI note numbers: C, E, G and the C above
NOTE_SECONDS = 0.4
SAMPLE_RATE = 22050

# Build the instrument, in mono, on its default patch.
inst = audioinstruments.create(INSTRUMENT, SAMPLE_RATE, channel_count=1)

# 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("<IHHIIHH", 16, 1, 1, SAMPLE_RATE, SAMPLE_RATE * 2, 2, 16))
out.write(b"data\0\0\0\0")
size = 0


# Pull audio out of the instrument for the given number of seconds.
def record(seconds):
global size
end = size + int(seconds * SAMPLE_RATE) * 2
while size < end:
status, block = audiocore.get_buffer(inst.output)
size += out.write(block)


# Play each note, let it go, and leave a second for the last one to fade.
for note in NOTES:
inst.note_on(note)
record(NOTE_SECONDS)
inst.note_off(note)
record(1)

# Go back and fill in the two sizes.
out.seek(4)
out.write(struct.pack("<I", 36 + size))
out.seek(40)
out.write(struct.pack("<I", size))
out.close()
print("Wrote", FILENAME)

# Each knob is a macro, set from 0 to 127 by its place in this list:
# inst.set_macro(inst.macro_labels.index("Tone"), 90), for example.
print("Knobs:", ", ".join(inst.macro_labels))
2 changes: 2 additions & 0 deletions lib/audioeffects/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Nearly everything here needs the audiodsp tier of nodes
`audiomath`, `audioshaper`, `audioladder` and friends), which a stock
CircuitPython board does not have.

For a complete program to start from, see [audioeffects_simpletest.py](https://github.com/PyDevices/audiocomponents/blob/main/examples/audioeffects_simpletest.py).

## What is in it

Each class below is a name you give to `audioeffects.create()`. The module
Expand Down
2 changes: 2 additions & 0 deletions lib/audioinstruments/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ audio_out.play(inst.output)
```


For a complete program to start from, see [audioinstruments_simpletest.py](https://github.com/PyDevices/audiocomponents/blob/main/examples/audioinstruments_simpletest.py).

## Sound stability

The API is our contract with you: class names, signatures, metadata, and
Expand Down
Loading