I am building a sample loop controller for Raspberry Pi that sends OSC messages to Sonic Pi. I am playing 3 loops at once and I have 4 knobs per loop that control the values of each track. I was wandering which is the best practice to send and retrieve these messages. I was thinking a pattern such as:
address:“modulate/1/”
argument: [0,0,0,0]
Where “/1/” is the track number and [0,0,0,0] the values that the loop has to change (volume, amp, start, length). My question is how to retrieve the track number from the OSC address pattern. I guess that I could achieve it using a regular expression but I would like to know if there is a better way to do it in Sonic Pi. Alternatively I could send the track number within the array:
You can use a function like this , which extracts wild cards from an OSC address
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
EDITED TO FIT YOUR EXAMPLE. I posted this late last night after a “night out” and thought it needed a bit more detail and explanation this morning!
So the program below works with the example requirement you gave in your post.
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
live_loop :getModulate do
use_real_time
#the * after osc in the next line allows this to work on 3.2dev as well as 3.2
#you can miss it out on 3.2 if you wish,althouhg it will work with it
p,q,r,s= sync "/osc*/modulate/*"
puts "values",p,q,r,s
#the FIRST * after osc in the next line allows this to work on 3.2dev as well as 3.2
#you can miss it out on 3.2 if you wish,although it will work with it
res = parse_sync_address "/osc*/modulate/*"
puts "channel",res[2].to_i #track activated extracts the 3rd element of the osc address, ie the value of the *
end
#sample osc messages now sent to test this
use_osc "localhost",4559
osc "/modulate/1",1,2,3,4
sleep 1
osc "/modulate/2",5,6,7,8
See my project
for a practical demonstration of this.
The function uses an undocumented function get_event from Sonic PI. It may change in the futre, but works at present with all version 3 including latest dev version.
The * following /osc in the address lines ie /osc*/ allows this to work in version 3.2dev as well as 3.1 If you will only use 3.1 then you can miss out this *, although it will work if you leave it there. Currently it IS necessary to be there to work with version 3.2dev.
To help understand how the parseAddress function works add some puts statements as below:
define :parse_sync_address do |address|
puts get_event(address)
puts get_event(address).to_s.split(",")
v= get_event(address).to_s.split(",")[6]
if v != nil
puts v
puts v[3..-2].split("/")
return v[3..-2].split("/")
else
return ["error"]
end
end
With the example based on your requirements the program will then give output
It works like a charm! Thank you very much, it is very simple and easy to understand. I really apreciate your help and the custom code to match my needs. I have been following your projects in Youtube and they are a big inspiration, but I missed your Record Player. It looks great and I will check it out!
About the code, do you think this method is less optimal than the array, in terms of performance? I mean, when we send many messages in a short time, Sonic Pi must execute the function each time it receives a message to extract the element. Because of this, I was wandering if I should add the track number into the argument array instead of the address pattern. I think that it is more semantic the example you have showed me but I am concerned about the performance. What do you think?
I have run it like this in a variety of demanding project and have not noticed a performance degradation as a result. I developed it mainly to deal with multiple push button inputs which used identical code. Using a wild card in the OSC address meant that I could use just one live loop to process all these inputs.