Last active
March 14, 2017 16:27
-
-
Save rbnpi/0545a509d96066ae5c6b311ebe98a5b3 to your computer and use it in GitHub Desktop.
Code for remote controlled SP with processing GUI controller
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
These files accompany a video at https://youtu.be/K1KQSFiHg6I which explores the use of a GUI to control a Sonic Pi program | |
running code to drive an external midi player. (I used Logic Pro) It currently works with SP2.12.0-midi-alpha1 on a Mac, | |
but may probably break on future versions. It is intended to show some concepts, particularly the use of a GUI written | |
in processing which can control SP by means of OSC messages. The source code of the processing sketch is included. | |
It also illustrates a technique to allow a Sonic Pi piece to be started at any bar, by calculating the start note for | |
each of the parts concerned. It can handle a tied note already playing at the start of the selected bar. | |
The final technique utilised is to use an external script utilising calls to the Sonic Pi cli interface gem to stop | |
Sonic Pi running and then to restart the program using a run_file command. In this way, Sonic Pi can respond to | |
a stop singal from the GUI and then restart playing when a subsequent Play cue is received via an OSC message. | |
There are some further bits you will ahve to sort out. EG setting up the audio midi program so that Sonic PI can | |
communicate with your midi player. See a picture in the video which shows this. | |
FILES: BWV588.rb is run in a SP buffer. BWV588auto.rb is accessed from a specified location by BWV588.rb | |
Processing 3 should be installed and then the StartBarSelector.txt is pasted into a new sketch, which is then run to create | |
the GUI. You need to speficy the address of the machine running SP in the script, (or leave if the local machine) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Bach Canzona in D minor BWV588 converted from midi file via MuseScore and processing script | |
s=[] #holds cue received parameters | |
bs=1#start bar initial value | |
bpb=4 #beats per bar | |
globalvol=100 | |
until s[0]==1 #loop until play signal arrived | |
s=sync '/transport' | |
puts s | |
end | |
bs=s[1] #extract startbar number from cue info | |
puts "BS selected is "+bs.to_s | |
in_thread do #start thread waiting for stop cue | |
s=[] | |
until s[0]==-1 | |
s=sync '/transport' | |
puts s | |
end | |
puts"stopping" | |
puts "running sonic pi cli script to restart" | |
#run external sonic_cli script to stop and rerun | |
system("/Users/rbn/Documents/SPfromXML/BWV588Auto.rb &") | |
end | |
#define routine to play midi notes/durations lists | |
#s parameter allows list to start in the middle | |
define :mplay do |notes,durs,channel,s=0,vel=globalvol| | |
for x in s..notes.length-1 | |
#puts x #for debugging | |
if notes[x].respond_to?(:each) | |
notes[x].each do |n| | |
midi n+current_transpose,dur: durs[x],channel: channel,vel: vel | |
end | |
else | |
midi notes[x]+current_transpose,dur: durs[x],channel: channel,vel: vel #if notes[x] !=:r | |
end | |
sleep durs[x] | |
end | |
end | |
#efine routine to work out start position from bar number | |
define :getmatchd do|bn,bpb,durations| | |
matchbeat=(bn-1)*bpb | |
l=0.0;x=0 | |
until l>=matchbeat || (l-matchbeat).abs < 0.125 | |
l+=durations[x] | |
x+=1 | |
end | |
return [x ,l-matchbeat] #start pointer plus sleep time | |
end | |
a1=[] | |
b1=[] | |
a1[0]=[:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:A4,:D5,:D5,:F5,:E5,:D5,:C5,:D5,:E5,:D5,:C5,:B4,:Cs5,:D5,:A4,:D5,:C5,:B4,:C5,:B4,:A4,:B4,:A4,:Af4,:A4,:Af4,:A4,:B4,:Af4,:A4,:A4,:B4,:Cs5,:B4,:Cs5,:D5,:C5,:Bf4,:A4,:G4,:A4,:Bf4,:A4,:Bf4,:C5,:F5,:E5,:D5,:G5,:F5,:E5,:C5,:F5,:Ef5,:D5,:C5,:Bf4,:A4,:r,:r,:r,:D5,:Cs5,:C5,:B4,:Bf4,:A4,:G4,:A4,:D5,:G4,:A4,:G4,:Fs4,:E4,:D4,:G4,:Fs4,:E4,:Fs4,:G4,:r,:F5,:E5,:A5,:G5,:F5,:E5,:D5,:Bf5,:A5,:G5,:C6,:C6,:Bf5,:A5,:Bf5,:Bf5,:A5,:G5,:A5,:A4,:B4,:Cs5,:D5,:E5,:F5,:E5,:D5,:G5,:F5,:E5,:F5,:A5,:G5,:F5,:G5,:F5,:E5,:D5,:C5,:D5,:E5,:D5,:C5,:B4,:C5,:D5,:Cs5,:D5,:E5,:A4,:B4,:C5,:B4,:C5,:D5,:Af4,:Fs4,:E4,:A4,:G4,:Fs4,:Af4,:A4,:Af4,:D5,:C5,:B4,:C5,:D5,:E5,:F5,:E5,:D5,:E5,:D5,:Cs5,:D5,:Cs5,:C5,:B4,:Bf4,:A4,:C5,:A4,:D5,:F5,:D5,:G5,:A5,:Bf5,:D5,:D5,:D5,:E5,:F5,:G5,:Cs5,:D5,:D5] | |
b1[0]=[4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,2.0,2.0,3.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,3.0,0.5,0.5,3.0,0.5,0.5,3.0,0.5,0.5,2.5,0.5,0.5,0.5,1.5,0.5,1.0,1.0,1.0,0.5,0.5,2.0,2.0,2.0,2.0,1.0,1.0,1.0,0.5,0.5,1.0,0.5,0.5,1.0,0.5,0.5,0.5,0.5,2.0,1.0,2.0,2.0,4.0,2.0,2.0,4.0,2.0,2.0,2.0,2.0,2.0,1.0,0.5,0.5,3.0,1.0,3.0,0.5,0.5,1.0,0.5,0.5,3.0,0.5,0.5,2.0,2.0,1.0,1.0,2.0,0.5,0.5,0.5,0.5,2.5,0.5,0.5,0.5,2.5,0.5,0.5,0.5,2.0,0.5,0.5,1.0,0.5,0.5,0.5,0.5,0.25,0.25,0.5,0.5,0.5,3.0,0.5,0.5,1.0,0.5,0.5,0.5,0.5,0.5,0.5,1.0,0.5,0.5,1.0,0.5,0.5,1.5,0.5,0.5,0.5,0.5,0.5,1.0,0.5,0.5,1.0,0.5,0.5,1.0,0.5,0.5,1.5,0.5,1.0,0.5,0.5,1.0,2.0,0.5,0.5,1.0,2.0,1.0,3.0,0.5,0.5,3.0,0.5,0.5,2.0,2.0,2.0,2.0,2.0,3.0,0.5,0.5,1.0,0.5,0.5,1.0,0.5,0.5,1.5,0.5,1.0,0.5,0.25,0.25,1.5,0.5,2.0] | |
c1=[120] | |
sv1= getmatchd(bs,bpb,b1[0]) | |
puts "1: "+sv1.to_s | |
in_thread do | |
use_bpm c1[0] | |
sleep sv1[1] #sleep if tied note found over start barline | |
for i in 0..a1.length-1 | |
use_bpm c1[i] | |
mplay(a1[i],b1[i],1,sv1[0]) | |
sv1=[0,0] #reset in case of tempo change | |
end | |
end | |
a2=[] | |
b2=[] | |
a2[0]=[:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:r,:G4,:D5,:D5,:Ef5,:D5,:C5,:Bf4,:C5,:D5,:C5,:Bf4,:A4,:Bf4,:C5,:G4,:C5,:Bf4,:A4,:Bf4,:A4,:G4,:A4,:D4,:G4,:A4,:Bf4,:A4,:Bf4,:C5,:D5,:D5,:Cs5,:C5,:B4,:Bf4,:A4,:G4,:A4,:D5,:Bf4,:A4,:A4,:Af4,:A4,:G4,:Fs4,:G4,:A4,:G4,:A4,:B4,:A4,:B4,:C5,:Bf4,:A4,:Bf4,:C5,:Bf4,:C5,:D5,:C5,:Bf4,:Ef5,:D5,:C5,:F5,:Ef5,:D5,:C5,:Bf4,:A4,:Bf4,:C5,:Fs4,:G4,:A4,:Bf4,:A4,:B4,:C5,:B4,:C5,:C5,:Bf4,:A4,:G4,:r,:r,:r,:r,:r,:r,:G4,:D5,:D5,:Ef5,:D5,:C5,:Bf4,:C5,:D5,:C5,:Bf4,:A4,:Bf4,:C5,:G4,:C5,:Bf4,:A4,:Bf4,:A4,:G4,:A4,:D4,:G4,:Fs4,:E4,:Fs4,:G4,:r,:r,:D5,:Cs5,:C5,:B4,:Bf4,:A4,:D4,:G4,:A4,:B4,:C5,:D5,:D5,:C5,:D5,:Ef5,:A4,:r,:r,:D5,:C5,:Bf4,:A4,:G4,:A4,:Bf4,:A4,:Cs5,:Cs5,:Cs5,:A4,:G4,:A4,:Bf4] | |
b2[0]=[4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,2.0,2.0,3.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,3.0,0.5,0.5,3.0,0.5,0.5,3.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.0,2.0,2.0,2.0,2.0,1.0,0.5,0.5,3.0,1.0,2.0,2.0,3.0,1.0,3.0,1.0,1.0,1.0,2.0,1.0,1.0,1.0,0.5,0.5,2.0,2.0,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,2.0,1.0,1.0,2.0,2.0,2.0,0.5,0.5,1.0,0.5,0.5,2.0,1.0,0.5,0.5,1.0,0.5,0.5,1.5,0.5,1.0,0.5,0.5,1.0,1.0,4.0,4.0,4.0,4.0,2.0,2.0,3.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,3.0,0.5,0.5,3.0,0.5,0.5,3.0,1.0,3.0,0.5,0.5,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,3.0,1.0,4.0,3.0,1.0,2.0,2.0,4.0,3.0,0.5,0.5,1.0,1.0,1.0,1.0,1.0,0.5,0.5,1.0,1.0,6.0,2.0,1.5,0.5,1.0,0.5,0.5,2.0,2.0] | |
c2=[120] | |
sv2= getmatchd(bs,bpb,b2[0]) | |
puts "2: "+sv2.to_s | |
in_thread do | |
use_bpm c2[0] | |
sleep sv2[1] | |
use_transpose -5 #this part was written for a transposed instrument | |
for i in 0..a2.length-1 | |
use_bpm c2[i] | |
mplay(a2[i],b2[i],2,sv2[0]) | |
sv2=[0,0] | |
end | |
end | |
a3=[] | |
b3=[] | |
a3[0]=[:r,:r,:r,:r,:r,:r,:r,:r,:A3,:D4,:D4,:F4,:E4,:D4,:C4,:D4,:E4,:D4,:C4,:B3,:C4,:D4,:A3,:D4,:C4,:B3,:C4,:B3,:A3,:B3,:A3,:B3,:Cs4,:D4,:r,:E4,:F4,:r,:D4,:Cs4,:C4,:B3,:Bf3,:A3,:G3,:A3,:D4,:G3,:A3,:G3,:F3,:G3,:A3,:Bf3,:A3,:G3,:F3,:G3,:A3,:B3,:A3,:G3,:A3,:B3,:Af3,:A3,:F4,:E4,:D4,:C4,:B3,:C4,:B3,:A3,:B3,:A3,:r,:r,:r,:r,:r,:r,:r,:r,:D3,:A3,:A3,:Bf3,:A3,:G3,:F3,:G3,:A3,:G3,:F3,:E3,:Fs3,:G3,:D3,:G3,:F3,:E3,:F3,:Ef3,:D3,:Ef3,:D3,:Ef3,:D3,:C3,:D3,:D4,:Cs4,:C4,:B3,:Bf3,:A3,:D4,:G3,:F3,:Bf3,:E3,:A3,:r,:r,:r,:r,:r,:r,:D3,:A3,:A3,:Bf3,:A3,:G3,:F3,:G3,:A3,:G3,:F3,:E3,:F3,:G3,:D3,:G3,:Fs3,:E3,:Fs3,:G3,:A3,:Bf3,:C4,:D4,:F4,:F4,:F4,:E4,[:A3,:D4]] | |
b3[0]=[4.0,4.0,4.0,4.0,4.0,4.0,4.0,2.0,2.0,3.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,3.0,0.5,0.5,3.0,0.5,0.5,2.0,1.0,0.5,0.5,2.0,1.0,1.0,2.0,2.0,2.0,2.0,2.0,2.0,1.0,0.5,0.5,3.0,1.0,3.0,0.5,0.5,3.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,2.0,3.0,0.5,0.5,2.0,3.0,1.0,1.0,0.5,0.5,3.0,0.5,0.5,3.0,0.5,0.5,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,4.0,2.0,2.0,3.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,3.0,0.5,0.5,3.0,0.5,0.5,2.0,3.0,0.5,0.5,2.0,4.0,2.0,2.0,2.0,2.0,2.0,3.0,1.0,4.0,3.0,1.0,2.0,4.0,2.0,4.0,4.0,4.0,4.0,2.0,2.0,3.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,3.0,0.5,0.5,2.0,1.0,1.0,1.0,1.0,8.0,1.5,0.5,2.0,2.0,2.0] | |
c3=[120] | |
sv3= getmatchd(bs,bpb,b3[0]) | |
puts "3: "+sv3.to_s | |
in_thread do | |
use_bpm c3[0] | |
sleep sv3[1] | |
for i in 0..a3.length-1 | |
use_bpm c3[i] | |
mplay(a3[i],b3[i],3,sv3[0]) | |
sv3=[0,0] | |
end | |
end | |
a=[] | |
b=[] | |
a[0]=[:r,:D3,:A3,:A3,:Bf3,:A3,:G3,:F3,:G3,:A3,:G3,:F3,:E3,:F3,:G3,:D3,:G3,:F3,:E3,:F3,:E3,:D3,:E3,:D3,:E3,:F3,:E3,:F3,:G3,:A3,:A3,:Af3,:G3,:Fs3,:F3,:E3,:D3,:E3,:A3,:D3,:C3,:D3,:E3,:F3,:E3,:D3,:Cs3,:D3,:G2,:A2,:Bf2,:A2,:A3,:G3,:E3,:Cs3,:D3,:B2,:Cs3,:D3,:E3,:F3,:E3,:D3,:C3,:B2,:A3,:Af3,:E3,:A3,:A3,:Af3,:A3,:Fs3,:E3,:A2,:A3,:Bf3,:A3,:G3,:A3,:Bf3,:A3,:Bf3,:C4,:Bf3,:A3,:Bf3,:C4,:r,:C3,:D3,:E3,:Fs3,:E3,:Fs3,:G3,:A3,:G3,:F3,:E3,:D3,:Cs3,:D3,:G2,:A2,:Bf2,:A2,:G2,:E3,:Cs3,:A2,:D3,:C3,:B2,:C3,:Bf2,:A2,:D3,:G2,:r,:r,:r,:r,:r,:r,:r,:r,:A2,:D3,:D3,:F3,:E3,:D3,:C3,:D3,:E3,:D3,:C3,:B2,:Cs3,:D3,:A2,:D3,:C3,:B2,:C3,:B2,:A2,:B2,:A2,:D3,:G2,:D3,:Cs3,:C3,:B2,:Bf2,:A2,:D3,:G2,:G3,:F3,:A3,:F3,:Bf3,:B3,:B3,:B3,:A3,:D3] | |
b[0]=[2.0,2.0,3.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,3.0,0.5,0.5,3.0,0.5,0.5,2.0,3.0,1.0,1.0,1.0,1.0,1.0,2.0,2.0,2.0,2.0,2.0,1.0,0.5,0.5,3.0,1.0,1.0,1.0,1.0,1.0,3.0,0.5,0.5,2.0,2.0,1.0,1.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,9.0,1.0,2.0,4.0,1.0,1.0,1.0,1.0,1.0,1.0,2.0,2.0,2.0,2.0,2.0,4.0,2.0,2.0,2.0,2.0,1.0,1.0,1.0,0.5,0.5,2.0,2.0,2.0,2.0,3.0,0.5,0.5,1.5,0.5,1.0,0.5,0.5,5.0,0.5,0.5,1.0,0.5,0.5,2.0,2.0,1.0,1.0,2.0,4.0,3.0,1.0,1.0,1.0,1.5,0.5,2.0,4.0,2.0,2.0,2.0,2.0,2.0,4.0,4.0,4.0,4.0,4.0,2.0,1.0,1.0,3.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,0.5,0.5,1.0,1.0,1.0,1.0,3.0,0.5,0.5,3.0,0.5,0.5,2.0,3.0,1.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,2.0,1.0,0.5,0.5,6.0,1.5,0.5,2.0,2.0,2.0] | |
c=[120] | |
sv0= getmatchd(bs,4,b[0]) | |
puts "0: "+sv0.to_s | |
in_thread do | |
use_bpm c[0] | |
sleep sv0[1] | |
for i in 0..a.length-1 | |
use_bpm c[i] | |
mplay(a[i],b[i],4,sv0[0]) | |
sv0=[0,0] | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
`/usr/local/bin/sonic_pi stop` | |
`/usr/local/bin/sonic_pi "run_file '/Users/rbn/Documents/SPfromXML/BWV588Midi.rb'"` | |
#This file stops SP and then runs the BWV588Midi.rb file again using a run_file command | |
#it utilises the sonic-pi-cli gem | |
#adjust path for BWV588Midi.rb to suit your installtion | |
#install sonic-pi-cli for system ruby in /usr/bin/ruby | |
#use sudo gem install sonic-pi-cli | |
#This should give binary /usr/local/bin/sonic_pi | |
#If you use rvm, you can install the gem for your current ruby, but you need to use a wrapper to run it | |
#using the system ruby supplied by apple is easier here |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import oscP5.*; | |
import netP5.*; | |
OscP5 oscP5; | |
NetAddress sonicPi; | |
float mx,my; | |
int bs = 1; | |
int tr = 0; | |
PFont f; | |
void setup(){ | |
background(204); | |
frameRate(60); | |
size(100,120); | |
oscP5 = new OscP5(this, 8000); | |
sonicPi = new NetAddress("127.0.0.1",4559);//////INSERT ADDRESS REQUIRED////////////// | |
// add click rectangles | |
rect(45,15,10,10);//bs+ | |
rect(45,55,10,10);//bs=1 | |
rect(45,90,10,10);//bs- | |
rect(10,55,10,10);//stop | |
rect(80,55,10,10);//play | |
// Create the font | |
f = createFont("ArialNarrow-16.vlw", 12); | |
textFont(f); | |
textAlign(CENTER, CENTER); | |
fill(0); | |
text("stop",15,40);//add screen captions | |
text("play",85,40); | |
text("bs+",50,4); | |
text("bs-",50,105); | |
text("bs1",50,72); | |
text("1",48,40); //print initialbs value | |
} | |
void sendOscData(int tr,int bs) { | |
OscMessage toSend = new OscMessage("/transport"); | |
toSend.add(tr); | |
toSend.add(bs); | |
oscP5.send(toSend,sonicPi); | |
//println(toSend);//for debugging | |
} | |
void draw() { | |
mx=mouseX; | |
my=mouseY; | |
if (mousePressed) { | |
if ((mx > 44) && (mx <56 )&&(my > 89) && (my < 101)){//bs- clicked | |
bs-=1; | |
if (bs<1){ | |
bs=1; | |
} | |
//println(bs);//for debugging | |
} | |
if ((mx > 44) && (mx < 56)&&(my >14) && (my<26)) {//bs+ clicked | |
bs+=1; | |
//println(bs);//for debugging | |
} | |
if ((mx >44)&&(mx<56)&&(my>54) && (my<66)){//bs=1 clicked | |
bs=1; | |
//println(bs);//for debugging | |
} | |
if ((mx >79)&&(mx<91)&&(my>54) && (my<66)){//play clicked | |
tr=1; | |
sendOscData(tr,bs); | |
//println(tr); //for debugging | |
//println(bs); | |
} | |
if ((mx >9)&&(mx<21)&&(my>54) && (my<66)) {//stop clicked | |
tr=-1; | |
sendOscData(tr,bs); | |
//println(tr); //for debugging | |
} | |
fill(204); //update bs number on the screen | |
noStroke(); //clear last entry with a background rectangle | |
rect(30,32,35,16); | |
stroke(255); //change stroke colour to black | |
fill(0); //set fill to black | |
text(bs,48,40); //print bs value | |
} | |
delay(125); //loop delay | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment