Skip to content

Instantly share code, notes, and snippets.

@todbot
Last active December 27, 2024 00:08
Show Gist options
  • Save todbot/532e069845c2cc4c1bc39c9162a34bfe to your computer and use it in GitHub Desktop.
Save todbot/532e069845c2cc4c1bc39c9162a34bfe to your computer and use it in GitHub Desktop.
Drone synthing using two knobs to control three voices in synthio with optional echo effect in CircuitPython
# two_pot_drone_synth.py -- Use two knobs to control three voices in synthio
# with optional echo effect
# 25 Nov 2024 - @todbot / Tod Kurt
# video demo: https://www.youtube.com/watch?v=xEmhk-dVXqQ
# original simpler version:
# https://gist.github.com/todbot/53bfa7333ac7c1fcff8eaf0f547556ff
import time, random
import board, keypad, analogio
import audiopwmio, audiomixer, synthio
import ulab.numpy as np
sample_rate=36000 # higher sampling rate means faster knob response
# set up input devices
keys = keypad.Keys( (board.GP4, board.GP8), value_when_pressed=False, pull=True)
knob1 = analogio.AnalogIn(board.GP26)
knob2 = analogio.AnalogIn(board.GP27)
# set up audio out. Using mixer reduces glitches
audio = audiopwmio.PWMAudioOut(board.GP15)
mixer = audiomixer.Mixer(voice_count=1, sample_rate=sample_rate, channel_count=1,
bits_per_sample=16, samples_signed=True, buffer_size=2048)
synth = synthio.Synthesizer(sample_rate=sample_rate)
audio.play(mixer)
# try to load up echo effect, if not available no worries
try:
import audiodelays
effect = audiodelays.Echo( max_delay_ms=600, delay_ms=600,
decay=0.7, mix=0.4, freq_shift=False,
buffer_size=2048, sample_rate=sample_rate )
mixer.voice[0].play(effect)
effect.play(synth)
except:
mixer.voice[0].play(synth) # if we don't have delay
# configure our synthesizer and set it going
lfo = synthio.LFO(rate=5)
amp_env = synthio.Envelope(attack_time=0.5, release_time=0.5)
wave_saw = np.linspace(30000, -30000, num=512, dtype=np.int16) # max is +/-32k but gives us headroom
# need to lower amplitude or get clipping when delays are active
note1 = synthio.Note(frequency=100, waveform=wave_saw, amplitude=0.5, envelope=amp_env)
note2 = synthio.Note(frequency=100, waveform=wave_saw, amplitude=0.5, envelope=amp_env)
note3 = synthio.Note(frequency=100, waveform=wave_saw, amplitude=0.5, envelope=amp_env)
synth.press((note1,note2, note3))
f = 0.7 # for simple knob filtering
k1, k2 = 0, 0
last_print_time = 0
while True:
# handle button presses
if key := keys.events.get():
print("key:",key)
if key.key_number == 0:
if key.pressed:
note1.bend = lfo
else:
note1.bend = 0
if key.key_number == 1:
if key.pressed:
synth.release_all()
else:
synth.press((note1,note2))
# simple knob filtering because noisy ADCs
k1 += int((knob1.value - k1) * f)
k2 += int((knob2.value - k2) * f)
# update the drone voices' pitches
note1.frequency = synthio.midi_to_hz( k1 / 512 )
note2.frequency = synthio.midi_to_hz( k2 / 512 )
note3.frequency = note2.frequency*1.005 # detune for phatness
time.sleep(0.001)
if time.monotonic() - last_print_time > 0.2:
last_print_time = time.monotonic()
print("notes %4.1f %4.1f" % (k1/512, k2/512)) # a little debug info
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment