I have done some work on this tonight and come up with a simpler program to give polyphonic sustained notes from a midi controller. I tested it with my midi keyboard, but by changing the sync strings to what your controller produces ti should work with that as well. eg note, on = "/midi/lightpad_block_gxcx_bluetooth/0/5/note_on"
pitchbend is a compromise. You can adjust the pitch of a note, but only before it starts playing, and not while it is playing. That can be done in the polyphonic synth I described in my previous post, but the program is much more involved. I’m not sure what info your pitchbend control produces. My keyboard gives a number in the range 0 to 163843. You will have to adjust the values in the live_loop :pb to suit your set up.
#polyphonic midi input program with sustained notes
#experimental program by Robin Newman, November 2017
#pirchbend can be applied to note AS IT STARTS
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/steinberg_ur22mkii__port1/3/1/pitch_bend" #change to match your controller
set :pb,(b[0]-8192).to_f/8192*12
end
live_loop :midi_piano_on do #this loop starts 5 second notes for spcified pitches and stores reference
use_real_time
note, on = sync "/midi/steinberg_ur22mkii__port1/3/1/note_on" #change to match your controller
if on >0
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 :tb303
#max duration of note set to 5 on next line. Can increase if you wish.
x = play note+get(:pb), sustain: 5 #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
The first section of the program sets up three lists plist, ns and nv.
plist contains the references to notes which are currently active notes but need to be killed as the relevant midi input has changed to note_off (or in my case note_on with velocity 0)
ns containts an array containing the symbolic references to the notes. These are arranged as:n0 ,:n1… :n127 and are used with set and get commands to store and retriev the reference to playing notes
nv contains a list of the state of each note pitch , set to 1 if it is playing and 0 if it is not.
sv is a fucntion which retreives the numberic value of the note references, thus sv(:ny64) returns 64
live_loop :pb returns an number in teh range -12 to +12 depending upon the current pitchbend value (used to shift the note up or down an octave range)
live_loop :midi_piano_on detects inputs from the midi controller keyboard. If the velocity setting (the second paramter) is >0 then it checks the value of nv[note} and if it is 0 (ie the note is not already playing) it sets nv[note] to 1 (note playing) and starts 5 Second note with pitch note+pb value playing and stores a refereece to the note in ns[note] using a set command.
otherwise (on=0 and the key is released)
it checks to see if the note is playing (nv[note]==1) and if so resets nv[note]=0 and adss the reference to the note in ns[note} to the end of the plist.
The note is NOT stopped playing in this loop as that takes time, and wheh I tried it here, the loop missed processing some note releases if more than one note was released at a time. Doing this allows the references to the notes to be killed to be stored in plist without any being missed.
live_loop :notekill is used to actually stop notes playing.
It does so by checking if there are any notes to be killed (plist.length>0) and if so it pops a value from plist and uses it to first fade out the note
(control k,amp: 0,amp_slide: 0.02 )
and then to kill the note (kill k)
The very short sleep time at the end just ensures that there is a delay in teh loop when there are no notes waiting to be killed.
This loop is delayed from starting for a short period until the other loops are running, to eliminate some can’t keep up errors that occurred otherwise.
I enjoyed working at this, and will look at it further to see if it is possible to allow control of the pitchbend while the note is playing as in the much more involved polyphonic synth I developed last August, although I’m not sure if it can be done at present.