Module examples
Expand source code
from tinkersynth.midi import midi_note_source, NO_VELOCITY, SQUARE_ROOT, create_controllers_from_mapping, cc, \
pitch_bend_controller
from tinkersynth.synthesizer import *
# MIDI controller mappings
arturia_keylab_essential_61 = {
# using Analog Lab map on keyboard
22: "part_1_btn", # 0 or 127
23: "part_2_btn", # 0 or 127
24: "part_3_btn", # 0 or 127
74: "cutoff", # linear between 0 and 127 from here on
71: "resonance",
76: "lfo_rate",
77: "lfo_amt",
93: "param_1",
18: "param_2",
19: "param_3",
16: "param_4",
17: "panning", # master_pan (turny-twisty thing above master volume slider)
73: "attack_1",
75: "decay_1",
79: "sustain_1",
72: "release_1",
80: "attack_2",
81: "decay_2",
82: "sustain_2",
83: "release_2",
85: "volume", # master_vol
# control cluster at center of keyboard:
116: "cat_btn",
117: "preset_btn",
28: "left_btn",
29: "right_btn",
114: "rotary_knob",
}
# Main functions
gate_state = HIGH
def gate_gen():
"""This is more of a test helper than anything else.
Maybe usable for playing notes without interaction.
midi_note_source() provides live gate generation."""
global gate_state
while True:
new_state = yield gate_state
if new_state is not None:
gate_state = new_state
def pad_test():
# print(f"main: {threading.active_count(), threading.current_thread()}")
freqs, gates = midi_note_source(0, velocity_curve=NO_VELOCITY) # 0 = Arturia KeyLab Essential 61 MIDI In
create_controllers_from_mapping(arturia_keylab_essential_61, overwrite=True)
pitch_bend = transpose(freqs, scale(pitch_bend_controller(), -3.0, 3.0))
pitch1, pitch2, pitch3, pitch4 = split(pitch_bend, 4)
saw = sawtooth(pitch1)
squ_frq1 = transpose(pitch2, -11.9)
squ_frq2 = transpose(pitch3, -12.0)
squ_frq3 = transpose(pitch4, -12.1)
squ1 = square(squ_frq1)
squ2 = square(squ_frq2)
squ3 = square(squ_frq3)
mixed = mix([saw, squ1, squ2, squ3], [1.0, 0.3, 0.3, 0.3])
envelope = eg(mixed, gate=gates, adsr=[1.0, 0.0, 1.0, 2.0])
lfo = scale(triangle(10), 0.8, 1.0)
with_vibrato = gain(envelope, lfo)
flt_cutoff = scale(cc["cutoff"], 0.0, 0.7)
filt = low_pass_filter(with_vibrato, alpha=flt_cutoff, poles=8)
vol = gain(filt, cc["volume"])
player = play_live(vol)
def lead_test():
freqs, gates = midi_note_source(0, velocity_curve=SQUARE_ROOT) # 0 = Arturia KeyLab Essential 61 MIDI In
create_controllers_from_mapping(arturia_keylab_essential_61, overwrite=True)
squ = square(mult(freqs, 0.5))
sinus = sine(freqs)
tri = triangle(freqs)
m = mix([squ, tri], [0.4, 1.0]) # 2 * sqrt(2) = ca. 1.4 RMS
envelope = eg(m, gate=gates, adsr=[cc["attack_1"], cc["decay_1"], cc["sustain_1"], cc["release_1"]])
flt = low_pass_filter(envelope, alpha=cc["cutoff"])
drv = drive(flt, gain=cc["param_1"], mixin=0.5)
rev = reverb_hall(drv)
dely = delay(rev, delay=cc["param_2"], mixin=cc["param_3"], feedback=cc["param_4"])
vol = cc["volume"]
scaled = gain(dely, gain=vol)
player = play_live(scaled)
def porta_test():
freqs, gates = midi_note_source(0, velocity_curve=SQUARE_ROOT) # 0 = Arturia KeyLab Essential 61 MIDI In
create_controllers_from_mapping(arturia_keylab_essential_61, overwrite=True)
gate1, gate2 = split(gates, 2)
porta = portamento(freqs, gate1, cc["panning"])
osc = scale(square(5), -0.2, 0.2)
tremolo = transpose(porta, osc)
tri = sawtooth(tremolo)
envelope = eg(tri, gate=gate2, adsr=[0.005, 0.1, 0.2, 0.1])
filt = low_pass_filter(envelope, alpha=cc["cutoff"], resonance=0.0)
scaled = gain(filt, gain=cc["volume"])
player = play_live(scaled)
def plot_test():
# todo: http://navjodh.com/test-and-measurement/continuously-updating-audio-waveform-display-using-python/
squ = square(440)
s = sine(440)
# filt = high_pass_filter(s, alpha=0.99, poles=32)
filt = drive(s, gain=5.0, mixin=0.0)
vals = []
for i in range(int(300)):
if i == sample_rate:
gate_state = LOW
vals.append(next(filt))
plt.plot(vals)
plt.show()
def play_notes():
flat_adsr = [0.0, 0.0, 1.0, 0.0]
pluck_adsr = [0.005, 0.0, 1.0, 0.3]
crash_adsr = [0.005, 0.5, 0.0, 0.0]
# TODO: make example for playing static notes work again
samples = [0.0] * 50 # Add some padding
envelope = []
note_freqs = [220, 220 * 5 / 4, 220 * 3 / 2, 440, 440 * 3 / 2, 440, 220 * 3 / 2, 220]
durations = [0.5] * len(note_freqs)
durations[-1] = 0.4
adsr_values = flat_adsr
a = 0.2
for freq, duration in zip(note_freqs, durations):
# todo
anzahl_benoetigte_samples = round(sample_rate * (duration + adsr_values[RELEASE]))
for sample_schritt in range(anzahl_benoetigte_samples):
aktuelle_laufzeit = sample_schritt / sample_rate
adsr_val = adsr.send(aktuelle_laufzeit < duration)
envelope.append(adsr_val)
sample = (
mixer.send(None) *
adsr_val
)
samples.append(sample)
play(samples)
# plt.plot(samples[:round(anzahl_benoetigte_samples/20)])
# plt.plot(samples[:round(sample_rate / f) * 3])
# plt.plot(all_envelopes)
plt.plot(samples[:round(anzahl_benoetigte_samples)])
plt.show()
def main():
print("Press Ctrl-C a couple of times to exit (for your own programs "
"you can use synthesizer.stop_stream() to stop playing)")
# plot_test()
porta_test()
# porta_test()
# lead_test()
if __name__ == '__main__':
# Create a thread to run the script
run_and_reload_on_change(main)
Functions
def gate_gen()-
This is more of a test helper than anything else. Maybe usable for playing notes without interaction. midi_note_source() provides live gate generation.
Expand source code
def gate_gen(): """This is more of a test helper than anything else. Maybe usable for playing notes without interaction. midi_note_source() provides live gate generation.""" global gate_state while True: new_state = yield gate_state if new_state is not None: gate_state = new_state def lead_test()-
Expand source code
def lead_test(): freqs, gates = midi_note_source(0, velocity_curve=SQUARE_ROOT) # 0 = Arturia KeyLab Essential 61 MIDI In create_controllers_from_mapping(arturia_keylab_essential_61, overwrite=True) squ = square(mult(freqs, 0.5)) sinus = sine(freqs) tri = triangle(freqs) m = mix([squ, tri], [0.4, 1.0]) # 2 * sqrt(2) = ca. 1.4 RMS envelope = eg(m, gate=gates, adsr=[cc["attack_1"], cc["decay_1"], cc["sustain_1"], cc["release_1"]]) flt = low_pass_filter(envelope, alpha=cc["cutoff"]) drv = drive(flt, gain=cc["param_1"], mixin=0.5) rev = reverb_hall(drv) dely = delay(rev, delay=cc["param_2"], mixin=cc["param_3"], feedback=cc["param_4"]) vol = cc["volume"] scaled = gain(dely, gain=vol) player = play_live(scaled) def main()-
Expand source code
def main(): print("Press Ctrl-C a couple of times to exit (for your own programs " "you can use synthesizer.stop_stream() to stop playing)") # plot_test() porta_test() # porta_test() # lead_test() def pad_test()-
Expand source code
def pad_test(): # print(f"main: {threading.active_count(), threading.current_thread()}") freqs, gates = midi_note_source(0, velocity_curve=NO_VELOCITY) # 0 = Arturia KeyLab Essential 61 MIDI In create_controllers_from_mapping(arturia_keylab_essential_61, overwrite=True) pitch_bend = transpose(freqs, scale(pitch_bend_controller(), -3.0, 3.0)) pitch1, pitch2, pitch3, pitch4 = split(pitch_bend, 4) saw = sawtooth(pitch1) squ_frq1 = transpose(pitch2, -11.9) squ_frq2 = transpose(pitch3, -12.0) squ_frq3 = transpose(pitch4, -12.1) squ1 = square(squ_frq1) squ2 = square(squ_frq2) squ3 = square(squ_frq3) mixed = mix([saw, squ1, squ2, squ3], [1.0, 0.3, 0.3, 0.3]) envelope = eg(mixed, gate=gates, adsr=[1.0, 0.0, 1.0, 2.0]) lfo = scale(triangle(10), 0.8, 1.0) with_vibrato = gain(envelope, lfo) flt_cutoff = scale(cc["cutoff"], 0.0, 0.7) filt = low_pass_filter(with_vibrato, alpha=flt_cutoff, poles=8) vol = gain(filt, cc["volume"]) player = play_live(vol) def play_notes()-
Expand source code
def play_notes(): flat_adsr = [0.0, 0.0, 1.0, 0.0] pluck_adsr = [0.005, 0.0, 1.0, 0.3] crash_adsr = [0.005, 0.5, 0.0, 0.0] # TODO: make example for playing static notes work again samples = [0.0] * 50 # Add some padding envelope = [] note_freqs = [220, 220 * 5 / 4, 220 * 3 / 2, 440, 440 * 3 / 2, 440, 220 * 3 / 2, 220] durations = [0.5] * len(note_freqs) durations[-1] = 0.4 adsr_values = flat_adsr a = 0.2 for freq, duration in zip(note_freqs, durations): # todo anzahl_benoetigte_samples = round(sample_rate * (duration + adsr_values[RELEASE])) for sample_schritt in range(anzahl_benoetigte_samples): aktuelle_laufzeit = sample_schritt / sample_rate adsr_val = adsr.send(aktuelle_laufzeit < duration) envelope.append(adsr_val) sample = ( mixer.send(None) * adsr_val ) samples.append(sample) play(samples) # plt.plot(samples[:round(anzahl_benoetigte_samples/20)]) # plt.plot(samples[:round(sample_rate / f) * 3]) # plt.plot(all_envelopes) plt.plot(samples[:round(anzahl_benoetigte_samples)]) plt.show() def plot_test()-
Expand source code
def plot_test(): # todo: http://navjodh.com/test-and-measurement/continuously-updating-audio-waveform-display-using-python/ squ = square(440) s = sine(440) # filt = high_pass_filter(s, alpha=0.99, poles=32) filt = drive(s, gain=5.0, mixin=0.0) vals = [] for i in range(int(300)): if i == sample_rate: gate_state = LOW vals.append(next(filt)) plt.plot(vals) plt.show() def porta_test()-
Expand source code
def porta_test(): freqs, gates = midi_note_source(0, velocity_curve=SQUARE_ROOT) # 0 = Arturia KeyLab Essential 61 MIDI In create_controllers_from_mapping(arturia_keylab_essential_61, overwrite=True) gate1, gate2 = split(gates, 2) porta = portamento(freqs, gate1, cc["panning"]) osc = scale(square(5), -0.2, 0.2) tremolo = transpose(porta, osc) tri = sawtooth(tremolo) envelope = eg(tri, gate=gate2, adsr=[0.005, 0.1, 0.2, 0.1]) filt = low_pass_filter(envelope, alpha=cc["cutoff"], resonance=0.0) scaled = gain(filt, gain=cc["volume"]) player = play_live(scaled) def random()-
random() -> x in the interval [0, 1).