Retrieve from OSC Address Pattern


#1

Hello,

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:

address: “modulate/”
argument: [1,0,0,0,0]

I will appreciate your thoughts and suggestions.

Thank you very much!


#2

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.

This gives output.

 ├─ "values" 1 2 3 4
 └─ "channel" 1

 ├─ "values" 5 6 7 8
 └─ "channel" 2

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

 ├─ "values" 1 2 3 4
 ├─ #<SonicPi::CueEvent:[[1550307484.7874072, 0, #<SonicPi::ThreadId [-1]>, 0, 0.0, 60.0], "/osc/modulate/1", [1, 2, 3, 4]]
 ├─ ["#<SonicPi::CueEvent:[[1550307484.7874072", " 0", " #<SonicPi::ThreadId [-1]>", " 0", " 0.0", " 60.0]", " \"/osc/modulate/1\"", " [1", " 2", " 3", " 4]]"]
 ├─ " \"/osc/modulate/1\""
 ├─ ["osc", "modulate", "1"]
 └─ "channel" 1

and

 ├─ "values" 5 6 7 8
 ├─ #<SonicPi::CueEvent:[[1550307485.7777338, 0, #<SonicPi::ThreadId [-1]>, 0, 0.0, 60.0], "/osc/modulate/2", [5, 6, 7, 8]]
 ├─ ["#<SonicPi::CueEvent:[[1550307485.7777338", " 0", " #<SonicPi::ThreadId [-1]>", " 0", " 0.0", " 60.0]", " \"/osc/modulate/2\"", " [5", " 6", " 7", " 8]]"]
 ├─ " \"/osc/modulate/2\""
 ├─ ["osc", "modulate", "2"]
 └─ "channel" 2

for the two osc messages


#3

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?

Thank you!


#4

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.


#5

Excellent! I will use it and update this post when it is finished. Thank you very much!