Last active
January 16, 2025 08:48
-
-
Save todbot/68b4ab8d1fa20ed04388bef4619ff1bd to your computer and use it in GitHub Desktop.
a hacky way to do portamento in synthio in CircuitPython
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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