Files
audio-playground/audio_python/sound_tkinter.py
T
2026-10-27 22:10:43 +01:00

100 lines
2.3 KiB
Python

import importlib
from tkinter import Frame, Tk
import synth
from pyalsa import Alsa
SAMPLE_RATE = 44_100
LATENCY = 50_000 # 50ms
INT16_MAX = 2**15 - 1
alsa = Alsa(SAMPLE_RATE, LATENCY)
keys = "qzsedftgyhujk"
base_freq = 261.6256 # C4, piano middle C
keys_map = [0] * len(keys)
def keydown(e):
if e.keysym in keys:
i = keys.index(e.keysym)
keys_map[i] = 1
def keyup(e):
if e.keysym in keys:
i = keys.index(e.keysym)
keys_map[i] = 0
def key_to_freq(i):
if 0 <= i < len(keys):
return base_freq * (2 ** (i / 12))
return 0
root = Tk()
# note parameters: freq, volume
# note state: phase, volume
class Note:
def __init__(self):
self.freq = 0
self.vol = 0
self.phase = 0
self.gain = 0
self.playing = False
self.waveform = "sine"
self.attack = 0.01
self.release = 0.05
self.waveshaping = False
self.lowpass = False
self.lp = 0
def reload_synth(event):
importlib.reload(synth)
print("reloaded synth module")
def update(root, note):
pressed_key = keys_map.index(1) if 1 in keys_map else -1
note_freq = key_to_freq(pressed_key)
if note_freq != note.freq:
note.freq = note_freq
if not note.playing and note_freq != 0:
note.playing = True
if note.playing and note_freq == 0:
note.playing = False
buf = synth.synth(note)
alsa.write(buf) # send sound to alsa
root.after(0, update, root, note) # schedule next update
def toggle_note_waveform(e):
waves = ["sine", "saw", "square", "triangle"]
current = waves.index(note.waveform)
note.waveform = waves[(current + 1) % len(waves)]
print("waveform:", note.waveform)
def toggle_note_waveshaping(e):
note.waveshaping = not note.waveshaping
print("waveshaping:", note.waveshaping)
def toggle_note_lowpass(e):
note.lowpass = not note.lowpass
print("lowpass:", note.lowpass)
note = Note()
frame = Frame(root, width=100, height=100)
frame.bind("<KeyPress>", keydown)
frame.bind("<KeyRelease>", keyup)
root.bind_all("<Control-r>", reload_synth)
root.bind_all("w", toggle_note_waveform)
root.bind_all("x", toggle_note_waveshaping)
root.bind_all("c", toggle_note_lowpass)
frame.pack()
frame.focus_set()
root.after(100, update, root, note)
root.mainloop()