Behaviour of code outside of live loops when reloaded


#1

Hi folks,

I’m trying to make the knobs on my midi keyboard control FXs or something else that can be changed over time (such as note releases or other things), without any delay between turning the knob (triggering the midi value change) and the actual effect happening in the sound.

I have a rudimentary version running, which is:

with_fx :reverb, room: 0 do |reverb|
  live_loop :bb do
    sample :loop_breakbeat, beat_stretch: 2
    sleep 2
  end
  
  live_loop :control_reverb do
    use_real_time
    knob_num, value = sync "/midi/device/0/1/control_change"
    control reverb, room: (value / 127.to_f).round(2)
  end
end

(the calculation on the control line is only a way of translating the midi value range of 0 to 127 to 0 to 1 value that the reverb room parameter understands).

When I start that code for the first time, it works perfectly and does exactly what I want. The room parameter changes exactly to the value I’m setting via the knob, without any delay.

BUT as soon as I reload the running code, the call to control is no longer having any effect.
The log kinda gives a hint, as it says before the reload something like control node 360, {room: 0.2} and after the reload something like control node 370, {room: 0.2}.
So it seems the fx block also gets reloaded and the reverb variable that’s being controlled in the :control_reverb loop gets replaced somehow.

Question now is: Can this reload behaviour be changed somehow? Is the approach maybe totally awkward and something else might be way easier?

Thanks for any pointers!


Sequencer based on short fragments - controlling parameters via midi?
#2

This modified program works

with_fx :reverb, room: 0 do |reverb|
  defonce :setup,override: false do
    set :rv,reverb
  end
  setup
  live_loop :bb do
    sample :loop_breakbeat, beat_stretch: 2
    sleep 2
  end
end

live_loop :control_reverb do
  use_real_time
  knob_num, value = sync "/midi/device/0/1/control_change"
  control get(:rv), room: (value / 127.to_f).round(2)
end

The FIRST time the program is run the defonce setup is created and will
store the reverb pointer in :rv. This is then used in the control_reverb loop.
On subsequent runs it is NOT created and :rv does not have its value changed and continues to work.
If you stop the program, you need to change override to true for the next run (unless you restart Sonic PI), and then change it to false again before doing any subsequent run commands.

Alternatively you could just have the set :rv,reverb line, and comment this out after the program has run first time. Uncomment it after stopping the program before the next run then re-comment and so on.
Hope this makes sense.


#3

Oh, I love it. Thanks so much! :blush:
This works perfectly!
I didn’t know about defonce (and obviously didn’t even try looking if something like this exists).


#4

After playing around a bit, I realize, that defonce really is only defined and executed once per application run. That is, if I run the code the first time after Sonic Pi started up, it behaves as expected. Once I stop all runs (with Cmd/Ctrl-s or the ‘Stop’ Button) and later run the code again, defonce is not defined and executed again. So the value inside :rv which the :control_reverb loop tries to control references an FX from a previous run.

The way around this, is what you wrote, to simply comment out the code that sets :rv.

So the first time my code looks like this

with_fx :reverb, room: 1 do |reverb|
  set :rv, reverb
  
  live_loop :bb do
    sample :loop_breakbeat, beat_stretch: 2
    sleep 2
  end
end

live_loop :control_reverb do
  use_real_time
  knob_num, value = sync "/midi/device/0/1/control_change"
  control get(:rv), room: (value / 127.to_f).round(2)
end

and immediately after starting I change it to

with_fx :reverb, room: 1 do |reverb|
  # set :rv, reverb
  
  live_loop :bb do
    sample :loop_breakbeat, beat_stretch: 2
    sleep 2
  end
end

live_loop :control_reverb do
  use_real_time
  knob_num, value = sync "/midi/device/0/1/control_change"
  control get(:rv), room: (value / 127.to_f).round(2)
end

Thanks again @robin.newman for the idea and code.
And while I’m at it: Thanks also for the awesome ideas you bring to the community and this forum. Whenever I try to show people what Sonic Pi can do, I end up also mentioning your blog and your videos :muscle: :musical_keyboard: