Skip to content

Instantly share code, notes, and snippets.

@todbot
Last active January 16, 2025 08:48
Show Gist options
  • Save todbot/68b4ab8d1fa20ed04388bef4619ff1bd to your computer and use it in GitHub Desktop.
Save todbot/68b4ab8d1fa20ed04388bef4619ff1bd to your computer and use it in GitHub Desktop.
a hacky way to do portamento in synthio in CircuitPython
# synthio_portamento_hack.py -- a hacky way to do portamento in synthio
#
import board, time, random
import audiobusio, audiomixer, synthio
import ulab.numpy as np
lck_pin, bck_pin, dat_pin = board.MISO, board.MOSI, board.SCK
audio = audiobusio.I2SOut(bit_clock=bck_pin, word_select=lck_pin, data=dat_pin)
mixer = audiomixer.Mixer(voice_count=1, sample_rate=28000, channel_count=1,
bits_per_sample=16, samples_signed=True, buffer_size=2048 )
audio.play(mixer)
synth = synthio.Synthesizer(sample_rate=28000)
mixer.voice[0].level = 0.5 # turn down the volume a bit since this can get loud
mixer.voice[0].play(synth)
wave_saw = np.linspace(30000,-30000, num=512, dtype=np.int16) # default squ is too clippy
amp_env = synthio.Envelope(sustain_level=1.0, release_time=0.2)
synth.envelope = amp_env
# standard linear interpolate
def lerp(a, b, t): return (1-t)*a + t*b
# hacky portamento class to slide pitches around
# it owns the synthio.Note object but you still need to synth.press() it
class PortamentoNote:
def __init__(self, end_f, start_f=0, duration=0):
self.set_freq(end_f, start_f, duration)
self.note = synthio.Note(frequency=start_f, waveform=wave_saw)
def set_freq(self, end_f, start_f=0, duration=0):
self.start_f = start_f if start_f > 0 else end_f
self.end_f = end_f
self.duration = duration
self.start_time = time.monotonic()
def update(self):
dt = time.monotonic() - self.start_time
if dt < self.duration: # we're still sliding
self.note.frequency = lerp(self.start_f, self.end_f, dt / self.duration)
midi_notes = (44, 48, 60)
n = 0
last_note_time = time.monotonic()
porta_time = 0.5 # how long our portamentos should be
porta_note = PortamentoNote( synthio.midi_to_hz(midi_notes[n]) )
synth.press(porta_note.note)
while True:
porta_note.update()
if time.monotonic() - last_note_time > 2: # play notes every 2 secs:
last_note_time = time.monotonic()
n = (n+1) % len(midi_notes) # go to next note, with looping
new_f = synthio.midi_to_hz(midi_notes[n]) # get new note's frequency
# make old end frequency new start frequency
porta_note.set_freq( start_f=porta_note.end_f, end_f=new_f, duration=porta_time )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment