from math import pi, sin SAMPLE_RATE = 44_100 INT16_MAX = 2**15 - 1 def synth(note): buf = [0] * 512 for i, _ in enumerate(buf): note.phase = (note.phase + note.freq / SAMPLE_RATE) % 1.0 # oscillator match note.waveform: case "saw": sample = 2 * note.phase - 1.0 case "square": sample = 1.0 if note.phase < 0.5 else -1.0 case "sine": sample = sin(2 * pi * note.phase) case "triangle": sample = 4 * abs(note.phase - 0.5) - 1.0 case _: sample = 0.0 # envelope target = 1 if note.playing else 0 if target == 1: note.gain = min(note.gain + 1 / (0.010 * SAMPLE_RATE), 0.2) # attack else: note.gain = max(note.gain - 1 / (0.050 * SAMPLE_RATE), 0) # release # signal processing if note.waveshaping: drive = 2 sample *= abs(sample) * drive #sample = tanh(drive * sample) if note.lowpass: note.lp += 0.05 * (sample - note.lp) sample = note.lp * note.gain else: sample = sample * note.gain # convert to 16-bit signed integer and feed to buffer buf[i] = int(sample * INT16_MAX) return buf