TouchOSC button to replace command-r?

Hi, everyone. I’ve been reading mountains of TouchOSC ideas, but have not come across anyone setting up a button in a TouchOSC layout to run the script (instead of reaching for my keyboard to replace command-r). I set a button in TouchOSC editor to key R using command, but it doesn’t seem to be doing anything. Do I need some other software running to intercept that key command and send it to OS X?

Thanks!
Jack

I suggest doing something like this:

code = "play :c7  #your code here
sleep 1
play :c5
"

use_real_time
loop do
  sync "/osc:127.0.0.1:4560/run" #whatever the message from your
  run_code(code)                 #touchosc looks like
end

This enables you to run the code stored in the variable code on a button press, however if you want to change that code you will need to press M-r.
However if you want to change the code, you will need the keyboard anyway, so this should not be a problem :slight_smile:.

EDIT: The explanation for this approach is that, unfortunately, a function like store_buffer does not exist, otherwise one could do this:
Your code in e.g. buffer 1,
in another buffer this:

use_real_time
loop do
  sync "/osc:127.0.0.1:4560/run" #whatever the message from your
  store_buffer 1, "filename"     #touchosc looks like
  run_file("filename")
end

and you would only have to press M-r once for the second buffer.

Thanks for the reply! I’m trying to update as many variables as I can with TouchOSC instead of a keyboard. I JUST found out I can update BPM without a new command-r, so now I’m trying to make that list of possibilities longer. If I can’t get TouchOSC to run it, I can always just hook up a foot switch or a dedicated macro button off to the side. It would be super cool to have it all from one screen, though. I’ll mess with TouchOSC more. It gives inputs the ability to do keystrokes, so there must be a way that I am missing.

Thanks again!

To maybe make some of this make a little more sense: a reading disability makes the prospect of manipulating text live horrifying. But manipulating that text with big buttons and sliders has so far been one of the coolest musical things I have encountered.

There is a trick you can use to do this, because Sonic Pi uses OSC to communicate between the GUI and the server. However, the port it uses is randomised, so you need a little helper function to read the port number from the log file, like this one by @robin.newman:

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

With that you can create a live_loop that waits for your signal and then sends a command to run the current buffer:

live_loop :runner do
  sync "/osc:127.0.0.1:4560/run"
  osc_send "localhost", pvalue, "/save-and-run-buffer", "anything"
end

(that "anything" string can literally be any string, it’s there because Sonic Pi expects a string there that identifies where the message comes from, but doesn’t do anything with it).

You’ll need to start this running once from the GUI, but then the :runner live_loop should stay running and you’ll be able to use TouchOSC to start subsequent runs.

However, after writing this I’m thinking that maybe it’s not what you need after all. Once you start a buffer running, any live_loops will continue running, and can pick up new values from external sources (like TouchOSC) and use them to control the code, so you don’t really need to run the buffer again to pick up different values.

Hmm. Not sure that the example you give would work. Looking at the code for save-and-run-buffer (in sonic-pi-server.rb) below:

  server.add_method("/save-and-run-buffer") do |args|
    gui_id = args[0]
    buffer_id = args[1]
    code = args[2].force_encoding("utf-8")
    workspace = args[3]
    sp.__save_buffer(buffer_id, code)
    sp.__spider_eval code, {workspace: workspace}
  end

it requires several parameters. You can supply a gui_id and a buffer_id (eg workspace_eight) but you can’t get at code (which is the text from the editor buffer in the gui, which is sent from there and saved in the workspace_eight.spi file and then evaluated and run.

Instead I have produced code which can play the last-saved code placed in that file. It will not pick up any changes you make on the editor screen until you press run (which then invokes save-and-run OSC command). However it is still useful.
I have also used it from TouchOSC with buttons to play any saved buffer. The only drawback is that if you stop an instance playing, which you can also do using the stopAll command I also implement, you can only do that once as it also stops the live loop running to poll input from TouchOSC, I also have another version which uses a python script and (for convenience) the sonic-pi-cli gem file, and this will let you run and stop buffer runs repeatedly, although at present it has hardcoded pathnames which need to be rewritten with environmental variables instead. Not sure its worth the effort. It is based on my previously published jukebox script written back in 2015.

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
#function to run contents of buffer 0-9
define :runBuff do |n|
    buf=["_zero","_one","_two","_three","_four","_five","_six","_seven","_eight","_nine"]
    f=ENV['HOME']+"/.sonic-pi/store/default/workspace"+buf[n]+".spi"
    k="run_file '#{f}'"
    osc_send "localhost",pvalue,"/run-code","rbnguid",k
end

#function to stop all running jobs
define :stopAll do
    osc_send "localhost",pvalue, "/stop-all-jobs"
end

I had fun with the runBuff command using it to overlay instances of Frere Jaques. I put this code in buffer 3:

#frere jaques
use_bpm 180
f=[:c4,:d4,:e4,:c4]*2+[:e4,:f4,:g4]*2+[:g4,:a4,:g4,:f4,:e4,:c4]*2+[:c4,:g3,:c4]*2
d=[1,1,1,1,1,1,1,1,1,1,2,1,1,2,0.5,0.5,0.5,0.5,1,1,0.5,0.5,0.5,0.5,1,1,1,1,2,1,1,2]
use_synth :tri
f.zip(d) do |n,d|
  play n,release: d
  sleep d
end

I put the code for :pvalue, :runBuff and :stopAll in the init.rb file in my .sonic-pi folder, so that they can be called at any time once Sonic Pi has started.
Then in a separate buffer I tried

#note instances all played at 180bpm in buffer 3
#this use_bpm just varies separation
use_bpm 180 #try 190,220,240,300 etc
6.times do #vary number of instances 2-6
  runBuff 3
  sleep 8
end
1 Like

Yes you’re right, I didn’t pay enough attention to how the command is called… thanks for the working code.

I’m going to try to just rig up one of my usb foot switches to send a command-r to Sonic Pi. On account of this being a few million dollars above my pay grade.

I have found, surprising nobody, that I am reaching for the keyboard a lot anyway to make small adjustments to my code live just for fun. So until I rig up some potentiometers to adjust those things I’m having fun adjusting, I’ll need the keyboard around anyway.

By ohhhhhhhh man am I falling down a long dark hole of input device building. Already laser cutting some stuff today just to get me some physical knobs and buttons instead of TouchOSC’s not-as-kinetically-satisfying touch interface.

Thanks again for all the help. I really appreciate it.

2 Likes