Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A polyphonic gated synth for Sonic Pi3 with midi keyboard input. Runs on Raspberry Pi3 or more powerful computer with Sonic Pi 3. Accompanying article and video SECOND VERSION ADDED: see comments
#polyphonic midi input program with sustained notes
#experimental program by Robin Newman, November 2017
#pitchbend can be applied to notes at any time while they are sounding
use_debug false
set :synth,:tb303 #initial value
set :pb,0 #pitchbend initial value
kill_list=[] #list to contain notes to be killed
on_notes=[] #list of notes currently playing
ns=[] #array to store note playing references
nv=[0]*128 #array to store state of note for a particlar pitch 1=on, 0=off
128.times do |i|
ns[i]=("n"+i.to_s).to_sym #set up array of symbols :n0 ...:n127
end
#puts ns #for testing
define :sv do |sym| #extract numeric value associated with symbol eg :n64 => 64
return sym.to_s[1..-1].to_i
end
#puts sv(ns[64]) #for testing
live_loop :choose_synth do
b= sync "/midi/*/*/*/control_change" #use wild cards to works with any controller
if b[0]==10 #adjust control number to suit your controller
sc=(b[1].to_f/127*3 ).to_i
set :synth,[:tri,:saw,:tb303,:fm][sc] #can change synth list if you wish
puts "Synth #{get(:synth)} selected"
end
end
live_loop :pb do #get current pitchbend value adjusted in range -12 to +12 (octave)
b = sync "/midi/*/*/*/pitch_bend" #change to match your controller
set :pb,(b[0]-8192).to_f/8192*12
end
with_fx :reverb,room: 0.8,mix: 0.6 do #add some reverb
live_loop :midi_note_on do #this loop starts 100 second notes for specified pitches and stores reference
use_real_time
note, on = sync "/midi/*/*/*/note_on"
if on >0
if nv[note]==0 #check if new start for the note
puts "setting note #{note} on"
vn=on.to_f/127
nv[note]=1 #mark note as started for this pitch
use_synth get(:synth)
x = play note+get(:pb),attack: 0.01, sustain: 100,amp: vn #start playing note
set ns[note],x #store reference to note in ns array
on_notes.push [note,vn] #add note to list of notes playing
end
else
if nv[note]==1 #check if this pitch is on
nv[note]=0 #set this pitch off
kill_list.push note #add note to list of notes to kill
end
end
end
live_loop :processnote,auto_cue: false,delay: 0.4 do # this applies pitchbend if any to note as it plays
#delayed start helps reduce timing errors
use_real_time
if on_notes.length > 0 #check if any notes on
k=on_notes.pop #get next note from "on" list
puts "processing note #{k[0]}"
in_thread do #start a thread to apply pitchbend to the note every 0.05 seconds
v=get(ns[k[0]]) #retrieve control value for the note
while nv[k[0]]==1 #while the note is still merked as on
control v,note: k[0]+get(:pb),note_slide: 0.05,amp: k[1]
sleep 0.05
end
#belt and braces kill here as well as in notekill liveloop: catches any that miss
control v,amp: 0,amp_slide: 0.02 #fade note out in 0.02 seconds
sleep 0.02
puts "backup kill note #{k[0]}"
kill v #kill the note referred to in ns array
end
end
sleep 0.08 #so that the loop sleeps if no notes on
end
live_loop :notekill,auto_cue: false,delay: 0.3 do # this loop kills released notes
#delayed start helps reduce timing errors
use_real_time
while kill_list.length > 0 #check if there are notes to be killed
k=kill_list.pop #get next note to kill
puts "killing note #{k}"
v=get(ns[k]) #retrieve reference to the note
control v,amp: 0,amp_slide: 0.02 #fade note out in 0.02 seconds
sleep 0.02
kill v #kill the note referred to in ns array
end
sleep 0.08 #so that the loop sleeps if no notes to be killed
end
end #reverb
#polyphonic midi input program with sustained notes
#experimental program by Robin Newman, November 2017
#pitchbend can be applied to note AS IT STARTS
#This version for controllers with separate note_on and note_off midi signals
#rather than using note_on with velocity 0 for midi_off signal
set :pb,0 #pitchbend value
plist=[] #list to contains references to notes to be killed
ns=[] #array to store note playing references
nv=[0]*128 #array to store state of note for a particlar pitch 1=on, 0 = 0ff
128.times do |i|
ns[i]=("n"+i.to_s).to_sym #set up array of symbols :n0 ...:n127
end
#puts ns #for testing
define :sv do |sym| #extract numeric value associated with symbol eg :n64 => 64
return sym.to_s[1..-1].to_i
end
#puts sv(ns[64]) #for testing
live_loop :pb do #get current pitchbend value adjusted in range -12 to +12 (octave)
b = sync "/midi/*/*/*/pitch_bend"
set :pb,(b[0]-8192).to_f/8192*12
puts get(:pb)
end
define :geton do |address|
v= get_event(address).to_s.split(",")[6]#[address.length+1..-2].to_i
return v.include?"note_on"
end
define :parse_sync_address do |address|
v= get_event(address).to_s.split(",")[6]#[address.length+1..-2].to_i
if v != nil
return v[3..-2].split("/")
else
return ["error"]
end
end
live_loop :midi_piano_on do #this loop starts 5 second notes for spcified pitches and stores reference
use_real_time
note, vol = sync "/midi/*/*/*/note_*"
res= parse_sync_address "/midi/*/*/*/*"
puts res[4]
if res[4]=="note_on"
puts note,nv[note]
if nv[note]==0 #check if new start for the note
nv[note]=1 #mark note as started for this pitch
use_synth :tri
#max duration of note set to 5 on next line. Can increase if you wish.
x = play note+get(:pb), amp: vol/127.0,sustain: 50 #play note
set ns[note],x #store reference in ns array
end
else
if nv[note]==1 #check if this pitch is on
nv[note]=0 #set this pitch off
plist << get(ns[note])
end
end
end
live_loop :notekill,auto_cue: false,delay: 0.25 do
use_real_time
if plist.length > 0 #check if notes to be killed
k=plist.pop
control k,amp: 0,amp_slide: 0.02 #fade note out in 0.02 seconds
sleep 0.02
kill k #kill the note referred to in ns array
end
sleep 0.01
end
@mrclasse

This comment has been minimized.

Show comment
Hide comment
@mrclasse

mrclasse Apr 25, 2018

Hi ! I tested it and the kill process doesn't seem to work. When I press a key & release it the note stays sustained. My version is 3.1.0.

Hi ! I tested it and the kill process doesn't seem to work. When I press a key & release it the note stays sustained. My version is 3.1.0.

@rbnpi

This comment has been minimized.

Show comment
Hide comment
@rbnpi

rbnpi Apr 29, 2018

I Have added a second version simplepolysynth2.rb which works with a controller which uses separate note_on and note_off midi signals, rather than using midi_on with velocity 0 which many controllers use for the midi_off signal.
It may be that this is what you need for your keyboard controller.

Owner

rbnpi commented Apr 29, 2018

I Have added a second version simplepolysynth2.rb which works with a controller which uses separate note_on and note_off midi signals, rather than using midi_on with velocity 0 which many controllers use for the midi_off signal.
It may be that this is what you need for your keyboard controller.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment