Spent so long recently working on building packages of Sonic Pi I thought it was about time I used it!. Had a great afternoon playing with OSC commo ands controlling some live loops playing percussion instruments and notes. Found it was a great way to control things, syncing loops together, passing parameters to them, and even using a backdoor OSC call to stop the program at the end.
Code is here (FOR VERSION I which has a potential problem:
(version 2 follows after. Preferable to use)
#an experimental piece entirely controlled by OSC messages that Sonic Pi
#sends to itself. This gives a great way of controlling running live_loops
#letting you synchronise then and pass paramters to control their operation
#written by Robin Newman April 2020
use_osc "localhost",4560 #send OSC commands to Sonic Pi port 4560
use_osc_logging false
use_cue_logging false
define :pvalue do #this functions reads from server-output.log
#the dynamically allocated port used for incoming commands to sonic-pi server.
value='' #initialise variable
#read server-output.log
File.open(ENV['HOME']+'/.sonic-pi/log/server-output.log','r') do |f1|
while l = f1.gets
if l.include?"Listen port:" #find line containing Listen port:
value = l.split(" ").last.to_i #extract port
break
end
end
f1.close #close file
end
return value #return found port value
end
define :x do #functions gives x 1->4
dice(4)
end
live_loop :tempo do #live loop gives variable bpm
bpm= range(120,160,inclusive: true,step: 5).mirror.tick
osc "/bpm",bpm
use_bpm bpm
sleep 4
end
#this live loop controls operations of the other loops below
live_loop :control,delay: 0.04 do #delay to make sure bpm value is set
use_bpm get("/osc*/bpm")[0]
#list of pattern timings
plist=[[1,1,1,1],[1,0.5,1.5,1],[0.5,1.5,1.5,0.5],\
[1,1.5,0.5,1],[2.5,0.5,0.5,0.5],[0.5,0.5,0.5,2.5]]
n=rrand_i(0,5) #choose pattern to use
pat=plist[n]
#trigger notes and four sample live_loops
osc "/one",x,2
#notes parameters: synth and case value t 1 or 2
osc "/notes",(["beep","saw","fm","tb303"].tick(:syn)),dice(2)
sleep pat.tick #slepp times from current pattern
osc "/two",x,1
sleep pat.tick
osc "/three",x,2
sleep pat.tick
osc "/four",x,0.3
sleep pat.tick
end
#playing section below, wrapped in fx reverb and fx level
with_fx :reverb,room: 0.8,amp: 1.2,mix: 0.7 do
with_fx :level,amp: 0.1 do |vol| #adjust the level as the tempo changes
set :vol,vol #save control reference
live_loop :controlVol,delay: 0.04 do #wait to make sure bpm value is set
use_bpm get("/osc*/bpm")[0]
#puts current_bpm
v=range(0.1,1.0,inclusive: true,step: 0.1).mirror.tick #get next vol level
tick(:end) if v==0.1 #bump every time vol is at lowest
control get(:vol),amp: v,amp_slide: 4 #change vol with control slide
sleep 4 #wait for end of current period
puts "end tick #{look(:end)}"
#choose tick(:end) value to finish below (I chose 7)
#next osc message stops all jobs using dynamic port variable
#note special port, and also needs a "guid" (can be anything)
osc_send "localhost",pvalue,"/stop-all-jobs","rbnguid" if look(:end)==7
end
live_loop :notes do
use_real_time
s,t=sync "/osc*/notes" #trigger and get synth and t value
use_synth s.to_sym #convert s from string to symbol
use_bpm get("/osc*/bpm")[0] #get current bpm
case t #select order for playing notes
when 1
8.times do
play scale(:c4,:minor_pentatonic,num_octaves:2).choose,release: 0.1,pan: -0.7
sleep 0.25
end
play :c5,release: 0.1
#note dont need sleep here as synced by nect osc message
when 2
play :c5,release: 0.1
sleep 2
7.times do #only 7 notes so finished before next osc message
play scale(:c4,:minor_pentatonic,num_octaves:2).reverse.tick,release: 0.1,pan: 0.7
sleep 0.25
end
end
end
#You can try adding a variety of the modifiers for each sample
#of the form: if c <condition> some examples shown
live_loop :one do
use_real_time
c,v = sync "/osc*/one" #parameters are c (1-4) and volume v
sample :bd_haus,amp: v if c >1 #optional variety using c
end
live_loop :two do
use_real_time
c,v = sync "/osc*/two"
sample :drum_cymbal_closed,amp: v #if c <2 #optional
end
live_loop :three do
use_real_time
c,v = sync "/osc*/three"
sample :drum_cymbal_closed,amp: v #if c != 3 #optional
end
live_loop :four do
use_real_time
c,v = sync "/osc*/four"
sample :perc_bell,amp: v if c !=4 #optional
end
end #level
end#reverb
VERSION 2 CODE (eliminates get "/osc*… statements which could fail in their timing:
#an experimental piece entirely controlled by OSC messages that Sonic Pi
#sends to itself. This gives a great way of controlling running live_loops
#letting you synchronise then and pass paramters to control their operation
#written by Robin Newman April 2020
#VERSION 2 avoiding get("/osc*... statements
use_osc "localhost",4560 #send OSC commands to Sonic Pi port 4560
use_osc_logging false
use_cue_logging false
define :pvalue do #this functions reads from server-output.log
#the dynamically allocated port used for incoming commands to sonic-pi server.
value='' #initialise variable
#read server-output.log
File.open(ENV['HOME']+'/.sonic-pi/log/server-output.log','r') do |f1|
while l = f1.gets
if l.include?"Listen port:" #find line containing Listen port:
value = l.split(" ").last.to_i #extract port
break
end
end
f1.close #close file
end
return value #return found port value
end
define :x do #functions gives x 1->4
dice(4)
end
live_loop :tempo do #live loop gives variable bpm
bpm= range(120,160,inclusive: true,step: 5).mirror.tick
osc "/bpm",bpm
use_bpm bpm
sleep 4
end
#this live loop controls operations of the other loops below
live_loop :control do
bpm = sync "/osc*/bpm"
use_bpm bpm[0]
#list of pattern timings
plist=[[1,1,1,1],[1,0.5,1.5,1],[0.5,1.5,1.5,0.5],\
[1,1.5,0.5,1],[2.5,0.5,0.5,0.5],[0.5,0.5,0.5,2.5]]
n=rrand_i(0,5) #choose pattern to use
pat=plist[n]
#trigger notes and four sample live_loops
osc "/one",x,2
#notes parameters: synth and case value t 1 or 2
osc "/notes",(["beep","saw","fm","tb303"].tick(:syn)),dice(2)
sleep pat.tick #slepp times from current pattern
osc "/two",x,1
sleep pat.tick
osc "/three",x,2
sleep pat.tick
osc "/four",x,0.3
tick #to keep tick counter at correct value since ignoring 4th entry
#sleep pat.tick #this sleep value provided by sync at start of next pass
end
#playing section below, wrapped in fx reverb and fx level
with_fx :reverb,room: 0.8,amp: 1.2,mix: 0.7 do
with_fx :level,amp: 0.1 do |vol| #adjust the level as the tempo changes
set :vol,vol #save control reference
live_loop :cVol do #loop controls leve amp setting
bpm = sync ("/osc*/bpm")
use_bpm bpm[0]
puts current_bpm
v=range(0.1,1.0,inclusive: true,step: 0.1).mirror.tick #get next vol level
tick(:end) if v==0.1 #bump every time vol is at lowest
puts "end tick #{look(:end)}"
control get(:vol),amp: v,amp_slide: 4 #change vol with control slide
#choose tick(:end) value to finish below (I chose 7)
if look(:end)==7 #check for finish
sleep 4
#next osc message stops all jobs using dynamic port variable
#note special port, and also needs a "guid" (can be anything)
osc_send "localhost",pvalue,"/stop-all-jobs","rbnguid"
end
end
live_loop :notes do
use_real_time
s,t=sync "/osc*/notes" #trigger and get synth and t value
use_synth s.to_sym #convert s from string to symbol
bpm= get("/osc*/bpm") #get current bpm
use_bpm bpm[0]
case t #select order for playing notes
when 1
8.times do
play scale(:c4,:minor_pentatonic,num_octaves:2).choose,release: 0.1,pan: -0.7
sleep 0.25
end
play :c5,release: 0.1
#note dont need sleep here as synced by nect osc message
when 2
play :c5,release: 0.1
sleep 2
7.times do #only 7 notes so finished before next osc message
play scale(:c4,:minor_pentatonic,num_octaves:2).reverse.tick,release: 0.1,pan: 0.7
sleep 0.25
end
end
end
#You can try adding a variety of the modifiers for each sample
#of the form: if c <condition> some examples shown
live_loop :one do
use_real_time
c,v = sync "/osc*/one" #parameters are c (1-4) and volume v
sample :bd_haus,amp: v if c >1 #optional variety using c
end
live_loop :two do
use_real_time
c,v = sync "/osc*/two"
sample :drum_cymbal_closed,amp: v #if c <2 #optional
end
live_loop :three do
use_real_time
c,v = sync "/osc*/three"
sample :drum_cymbal_closed,amp: v #if c != 3 #optional
end
live_loop :four do
use_real_time
c,v = sync "/osc*/four"
sample :perc_bell,amp: v if c !=4 #optional
end
end #level
end#reverb