Skip to content

Instantly share code, notes, and snippets.

@rbnpi
Created November 5, 2018 18:53
Show Gist options
  • Save rbnpi/e825b3917e80f6d72953273447855177 to your computer and use it in GitHub Desktop.
Save rbnpi/e825b3917e80f6d72953273447855177 to your computer and use it in GitHub Desktop.
Sonic Pi fx parameter control using TouchOSC. index.xml is the TouchOSC template. Donwload and zip it and rename to paramControl.touchosc
#apcOSC1.rb
#Sonic Pi 3 automated parameter control for fx calls controlled by TouchOSC
#developed from an idea by Martin Butz. Code developed by Robin Newman, November 2018
use_osc "192.168.1.240",9000 #address of TouchOSC and input port
#initialise some parameters
#these will be updated further by signals from TouchOSC
set :start,0
set :finish,0.5
set :dur,1
set :type,:fade
set :lvOSC,0
set :lvOSC2,0
set :lvOSC3,0
#initialise TouchOSC
osc"/param/led",1 #flash led on to show it is working
sleep 0.4
osc "/param/start/1/11",1
osc "/param/finish/1/6",1
osc "/param/dur/1/7",1
osc "/param/type/1/1",1
#adjust state of trigger highlight
define :setTriggerLight do |pointer,state|
puts "pointer,state",pointer,state
case pointer
when :lvOSC
osc "/param/trigger1",state
when :lvOSC2
osc "/param/trigger2",state
when :lvOSC3
osc "/param/trigger3",state
when :lvOSC4
osc "/param/trigger4",state
end
end
setTriggerLight(:lvOSC,0)
setTriggerLight(:lvOSC2,0)
setTriggerLight(:lvOSC3,0)
setTriggerLight(:lvOSC4,0)
#function generates fade levels
define :fadeSteps do |min, max, len, type|
case type
when :fade
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.ramp
when :wave
b = (line min, max, steps: len, inclusive: true).stretch(2).drop(1).butlast.mirror
end
return b #will return without this statement, but more explicit to put it in
end
#This function controls parameter for an fx pointed to by pointer
#start and finish define fade points (range 0->1 each, duration is time in beats for fade
#tyoe is :fade (start->finish) or :wave (start->finish->start)
#opt is parameter name to control eg :mix, :pan, :amp
define :fadeControl do |start,finish,duration,type,pointer,opt|
#puts "first",start,finish,type #for debugging
#adjust values to use according to fade time and direction
#:up works as :down if start>finish
#first check and stop if start==finish
setTriggerLight(pointer,0) if start==finish
return if start==finish
l=fadeSteps start,finish,11,type #11 ensures 20 steps for each up/down, 40 for wave
puts"l=#{l}"
if type==:wave
dt=duration/40.0 #adjust step interval to give correct total time
else
dt=duration/20.0
end
#puts l.length,type #uncomment for debugging to check number of steps
#print current fade
puts "fadeControl #{start} #{finish} #{duration} #{type} #{pointer} #{opt}"
in_thread do
t=vt
tick_reset
l.length.times do
#note that amp: is equivalent to :amp=> This enables use of :amp stored in a variable
# similarly for pan: or phase: or any other similar
# note also how the corresponding _slide is created.
control get(pointer),opt=> l.tick,(opt.to_s+"_slide").to_sym => dt
sleep dt
end
#puts "duration was #{t-vt}" #for debugging
setTriggerLight(pointer,0)
end
end
#helper function extracts wild card matches from osc addresses
define :parse_sync_address do |address|
v= get_event(address).to_s.split(",")[6]
if v != nil
return v[3..-2].split("/")
else
return ["error"]
end
end
#section to update parameters from TouchOSC messages
live_loop :getStart do
use_real_time
b = sync "/osc*/param/start/1/*"
if b[0]>0
num=parse_sync_address("/osc*/param/start/1/*")[4].to_i
start=[1.0,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0][num-1] #lookup fade duration
set :start,start
puts "start set #{start}"
end
end
live_loop :getFinish do
use_real_time
b = sync "/osc*/param/finish/1/*"
if b[0]>0
num=parse_sync_address("/osc*/param/finish/1/*")[4].to_i
finish=[1.0,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0][num-1] #lookup fade duration
set :finish,finish
puts "finish set #{finish}"
end
end
live_loop :getDur do
use_real_time
b=sync "/osc*/param/dur/1/*"
if b[0]>0
num=parse_sync_address("/osc*/param/dur/1/*")[4].to_i
dur=[16,8,6,4,3,2,1,0.5][num-1] #lookup fade duration
puts "fade duration set to #{dur}"
set :dur,dur
end
end
live_loop :getType do
use_real_time
b = sync "/osc*/param/type/*/1"
if b[0]>0
num=parse_sync_address("/osc*/param/type/*/1")[3].to_i
type=[:fade,:wave][num-1] #lookup type
puts "type set to #{type}"
set :type,type
end
end
#end of section to set parameters by TouchOSC
with_fx :reverb,room: 0.8,mix: 0 do |lvOSC2|
set :lvOSC2,lvOSC2 #store pointer to second fx
with_fx :echo,phase: 0.5,mix: 0 do |lvOSC3| # store pointer to third fx
set :lvOSC3,lvOSC3
with_fx :level,amp: 0 do |lvOSC|
set :lvOSC,lvOSC #store pointer to first fx
#This live loop carries out the control of the paramter when triggered by TouchOSC
live_loop :trigger do
use_real_time
trigger = sync "/osc*/param/trigger*"
tnum=parse_sync_address("/osc*/param/trigger*")[2][-1].to_i #get trigger number
if trigger[0]>0 and tnum==1
puts"trigger1"
#apply fade to first fx (level :amp)
offset=0;mult=1 #offset and scaling if required
fadeControl get(:start)*mult+offset,get(:finish)*mult+offset,get(:dur),get(:type),:lvOSC,:amp
end
if trigger[0]>0 and tnum==2
#apply fade to second fx (reverb :mix)
puts"trigger2"
offset=0;mult=1 #offset and scaling if required
fadeControl get(:start)*mult+offset,get(:finish)*mult+offset,get(:dur),get(:type),:lvOSC2,:mix
end
if trigger[0]>0 and tnum==3
#apply fade to third fx (echo :mix)
puts"trigger3"
offset=0;mult=1 ##offset and scaling if required
fadeControl get(:start)*mult+offset,get(:finish)*mult+offset,get(:dur),get(:type),:lvOSC3,:mix
end
if trigger[0]>0 and tnum==4
puts "send trigger4"
cue :trigger4
end
end
#This is the live loop affected by the fx wrappers
live_loop :osc do
#16.times do
sample :loop_breakbeat,beat_stretch: 1,amp: 2
sleep 1
#end
end
end #fx level (1)
with_fx :level, amp: 0 do |lvOSC4|
set :lvOSC4,lvOSC4
live_loop :triggerb do
use_real_time
trigger = sync "/osc*/param/trigger*"
tnum=parse_sync_address("/osc*/param/trigger*")[2][-1].to_i #get trigger number
if trigger[0]>0 and tnum==4
puts"trigger4"
offset=0;mult=1 ##offset and scaling if required
fadeControl get(:start)*mult+offset,get(:finish)*mult+offset,get(:dur),get(:type),:lvOSC4,:amp
end
end
live_loop :playnotes do
use_synth :tb303
play scale(:e3,:minor_pentatonic,num_octaves:2).choose,release: 0.125,amp: 0.5,cutoff: rrand_i(50,120)
sleep 0.125
end
end #fx level(2)
end #fx pan
end #fx reverb

The ruby program runs in Sonic Pi and is used to control 5 with_fx wrappers associated with two live loops. Operation is controlled by a TouchOSC template. This is in the file index.xml, which should be donwloaded and then compressed/zipped his file with the resulting file renamed paramControl.touchosc This file is then loaded into a suotable Apple or Android phone or table running TouchOSC available from app stores. Further details at in-thread.sonic-pi.net

<?xml version="1.0" encoding="UTF-8"?><layout version="17" mode="0" orientation="horizontal"><tabpage name="cGFyYW0=" scalef="0.0" scalet="1.0" li_t="" li_c="gray" li_s="14" li_o="false" li_b="false" la_t="" la_c="gray" la_s="14" la_o="false" la_b="false" ><control name="dHJpZ2dlcjI=" x="103" y="391" w="45" h="45" color="red" scalef="0.0" scalet="1.0" type="toggle" local_off="false" ></control><control name="dHJpZ2dlcjM=" x="164" y="391" w="45" h="45" color="red" scalef="0.0" scalet="1.0" type="toggle" local_off="false" ></control><control name="dHJpZ2dlcjE=" x="42" y="391" w="45" h="45" color="red" scalef="0.0" scalet="1.0" type="toggle" local_off="false" ></control><control name="dHlwZQ==" x="49" y="316" w="222" h="50" color="red" scalef="0.0" scalet="1.0" type="multitoggle" number_x="2" number_y="1" ex_mode="true" local_off="false" ></control><control name="bGFiZWwx" x="31" y="29" w="66" h="23" color="yellow" type="labelh" text="U3RhcnQ=" size="14" background="true" outline="false" ></control><control name="bGFiZWwy" x="212" y="29" w="66" h="23" color="yellow" type="labelh" text="VGltZQ==" size="14" background="true" outline="false" ></control><control name="bGFiZWwz" x="127" y="29" w="66" h="23" color="yellow" type="labelh" text="RmluaXNo" size="14" background="true" outline="false" ></control><control name="bGFiZWw0" x="175" y="292" w="66" h="23" color="yellow" type="labelh" text="V2F2ZQ==" size="14" background="true" outline="false" ></control><control name="bGFiZWw2" x="65" y="292" w="66" h="23" color="yellow" type="labelh" text="RmFkZQ==" size="14" background="true" outline="false" ></control><control name="bGFiZWw3" x="30" y="367" w="66" h="23" color="yellow" type="labelh" text="VHJpZ2dlcjE=" size="14" background="true" outline="false" ></control><control name="bGFiZWw4" x="156" y="367" w="66" h="23" color="yellow" type="labelh" text="VHJpZ2dlcjM=" size="14" background="true" outline="false" ></control><control name="bGFiZWw5" x="92" y="367" w="66" h="23" color="yellow" type="labelh" text="VHJpZ2dlcjI=" size="14" background="true" outline="false" ></control><control name="bGFiZWwxMA==" x="48" y="4" w="224" h="25" color="green" type="labelh" text="RlggUGFyYW1ldGVyIENvbnRyb2xsZXI=" size="14" background="true" outline="false" ></control><control name="ZHVy" x="220" y="54" w="50" h="240" color="red" scalef="0.0" scalet="1.0" type="multitoggle" number_x="1" number_y="8" ex_mode="true" local_off="false" ></control><control name="c3RhcnQ=" x="42" y="54" w="50" h="240" color="red" scalef="0.0" scalet="1.0" type="multitoggle" number_x="1" number_y="11" ex_mode="true" local_off="false" ></control><control name="ZmluaXNo" x="131" y="54" w="50" h="240" color="red" scalef="0.0" scalet="1.0" type="multitoggle" number_x="1" number_y="11" ex_mode="true" local_off="false" ></control><control name="dHJpZ2dlcjQ=" x="225" y="391" w="45" h="45" color="red" scalef="0.0" scalet="1.0" type="toggle" local_off="false" ></control><control name="bGFiZWwxMQ==" x="217" y="367" w="66" h="23" color="yellow" type="labelh" text="VHJpZ2dlcjQ=" size="14" background="true" outline="false" ></control></tabpage></layout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment