Quantise "run" / Update live loops without clicking "run"?

I’m still getting my head around sonic pi, have just run through the tutorials. So perhaps there’s a different approach to this I haven’t thought of/come across yet. Or perhaps there is a solution…
Here are 3 questions stemming from the same issue:

  • Is it possible to have new values “applied” to a live loop (eg, say I want to change the playback rate of a loop sample), without having to press the ‘run’ button? (some kind of live variable memory space even?)
  • Alternatively is it possible to ‘quantise’ the run button (say, so that the run button will only trigger on the beginning of every 4th bar from the beginning of my session)
  • Alternatively is it possible to control the “run” button with OSC or call it from within the code itself?

An example is as follows::
I have a 1 bar sample playing once every loop.
I alter the rate of the sample (and the corresponding sleep sample_duration rate) to be say 2. meaning it’s now a 1/2 bar loop.
Then I want to change back to a rate of 1, but I only want it to happen if the time from beginning of ‘song’ is a multiple of whole bars (to maintain the timing of the entire ‘song’).

eg:
1 bar + 1 bar + 1 bar + 1 bar = 4 bars, a nice musical number for 4/4 music.
1 bar + 1 bar + 1/2 bar + 1/2 bar + 1 bar = 4 bars. nice again.
1 bar + 1 bar + 1/2 bar + 1 bar = 3.5 bars. Not so nice.

Also ideally I want to operate the entire system without having to refer back to the mouse UI (using OSC messages preferably) and so having to go and ‘click’ run with my mouse is kind of blocking what I’d like to achieve.

1 Like

I don’t think it’s possible to update the code without pressing the Run button. But there are definitely ways to achieve what you want (i.e. making sure you always add up to 4 bars). What I would do is move your bars to functions and then call them from a main loop, which reads the variables set by your OSC messages (this is a separate topic, which I have written a post on here)

use_bpm 120

define :fullbar do
  beat = (ring true,false,false,false)
  64.times do
    tick
    sample :bd_tek if beat.look
    sleep 0.25
  end
end

define :halfbar do
  beat = (ring true,false,false,true,false,false,true,true)
  32.times do
    tick
    sample :bd_tek if beat.look
    sleep 0.25
  end
end

variable1 = true

live_loop :main do
  if variable1 == true then
    fullbar()
  else
    2.times do
      halfbar()
      sleep 2
      halfbar()
    end
  end
end

I use rings made up of true/false values to determine when to play the samples, but that’s a personal preference of mine. You could expand on this method by using a single function which takes arguments to determine which rate to play the sequence at.

1 Like

Regarding automatically triggering a run - it is absolutely possible to do such a thing, although perhaps not in the way you might first expect. I have been using this exact approach myself just recently, to test an idea for an upcoming festival installation where I needed to rerun certain code in a certain context without a human directly triggering it.

The key here is using either of the functions run_code or run_file, which take a string of Sonic Pi code, or the path to a file containing Sonic Pi code respectively. Those functions then execute said code as a new ‘run’.
You would then also need to consider whether you wanted a way to influence the behaviour of this separately run code without directly editing the code string or external file between runs - perhaps by using set and get to communicate state between it and your main buffer code.

Having said all that, while there may not be a ‘right’ way to achieve what you’re after in this particular instance, I’d personally say that maybe externally ‘running’ a code string or file might be a little unnecessary at first glance? Perhaps wrapping some things up into functions as Chris suggests might be enough… :man_shrugging:

1 Like

Great idea, but n00bs like me would love to see an example of the code you’re suggesting! :thinking:

Thanks all… I will have another play and report back.
I think I need to just adjust my brain to a different way of approaching problems.

I had some examples birv2 but now think I need to go back to the drawing board… I will report back with code once I have played further

I’m not aware of any OSC endpoints for controlling the transport (run/stop), but that would make a nice addition in a future version IMO. (paging @samaaron :wink:

This might be an option for you: Tool to run Sonic Pi from command line. With that tool you should be able to execute new runs via eval. But it’s not clear who or what is changing (which) values from your description.

It sounds like you might be after a solution that relies more on set/get like @ethancrawford mentioned. The basic idea is:

  1. Something “external” is changing the state of your Sonic Pi program - which might a combo of any or all of: code running in another buffer, another program or a piece of hardware sending MIDI or OSC messages
  2. You have a piece of code (live_loop / in_thread) in Sonic Pi whose job is to receive these messages and then record them (using set) or pass them on to a function. We can call this piece the “Dispatcher”. This may be a logical place to handle quantization of the changes if you want that…
  3. You set up your main live_loop(s) and audio generation code to use get variables when setting options, notes to play, etc. You refer to the variables you’ve set in the dispatcher, which might be named things like next_note, loop_drums_amp, drone_reverb_mix, bpm, etc. (names are up to you)

One nice side effect of this setup is that every time the loop repeats any changes in your variables will become the new state - without needing an explicit “run”. In this way you can continually change the output within the same run.

Here’s an example of what I’m describing: Link sonic pi to Python - #5 by perpetual_monday