Live loops inside of FX, with control capabilities

Hi all! I’m having some trouble with using effects and live loops. I want to be able to put live loops inside of effects and control them externally, but am running into some problems. I’ll try to articulate each of my specific issues as clearly as possible.

In my first example, when I run the code it starts with the default values for the reverb, as I have not specified any opts. When I make a change in the second thread using the control command, it will make the change the first time I re-run. However, if I try to change it again, it doesn’t work.

use_bpm 100

in_thread do
  with_fx :reverb do |f|
    set :f, f
    live_loop :sample do
      sample :loop_amen, beat_stretch: 4
      sleep 4
    end
  end
end

in_thread do
  fx = get[:f]
  control fx, room: 0.4, mix: 0
end

I solved this problem by naming the thread with the effects:

use_bpm 100

in_thread(name: :effect) do
  with_fx :reverb do |f|
    set :f, f
    live_loop :sample do
      sample :loop_amen, beat_stretch: 4
      sleep 4
    end
  end
end

in_thread do
  fx = get[:f]
  control fx, room: 0.4, mix: 0
end

I can now change the control values to my heart’s content. However, now that I’ve done this, I am unable to update the live loop inside the effect. Any change I make, for example, to the rate of the sample does not take effect when I re-run.

I want the live loop to be inside the effects for two reasons. First, I want to conserve as much processor power as I can. Second, and more importantly, if the effect is inside the live loop I can no longer use the external thread to control its parameters, because the effect resets to its initial parameters every loop.

Can someone offer some insight into why I lose the ability to update the live loop inside of a named thread? Possible solutions?

Thanks!

-Luke

2 Likes

This is a namespace problem. (Edit: it’s not, I didn’t look at the problem properly.)

Put the block that controls the effect inside the effect itself: (Edit: this works anyway.)

use_bpm 100

#cue 'up'

with_fx :reverb do |f|
  live_loop :sample do
    sample :loop_amen, beat_stretch: 4
    sleep 2
  end
  
  live_loop :control do
    sync 'up'
    control f, room: (line 0.1,0.8).choose
  end
end

You can run the control loop as a sort of LFO or on cues.

Shouldn’t using set and get circumvent the namespace problem?

You’re right, it should.

My first answer is just what I would do to fix it, I didn’t look at the problem.
I did that now:

Having the first loop in an in_thread block means every time you run the code, a new effect is made, and it’s pointer set to the timestate.
When you look at the cues, you see that the set/f uses a different id every time.
The sample loop however is not recreated, it still is inside the original effect block.

I usually avoid using in_thread together with live_loops.

Ok, that makes sense. This actually does solve my problem, as it allows me to put several dynamic things inside of a single effect (essentially using it as an effects rack) and still change the effects using control messages. Thanks!

In regards to the in_thread block creating a new effect every time, I’m trying to wrap my head around the difference in behavior between a named in_thread block and a non-named one. Why is it that when the block is not named, I am able to update the live loop inside of it, but when it is named I can’t?

Also, I’m not sure why, but I just tried out your example and it didn’t work for me.

It would make sense if it was the other way round, with a named thread not being updated.

Did you uncomment the cue in my example?

Sorry to bump an old topic, but I am running into this same problem today, both on a mac, v3.3.1 and linux v4.0 beta. Using a fairly obvious effect like slicer:

#cue 'up'

with_fx :slicer do |sc|
  live_loop :foo do
    play 50
    sleep 2
  end
  
  live_loop :control do
    sync 'up'
    control sc, phase: 0.1
  end
end

Uncommenting the cue the first time updates the slicer phase as expected – you’ll hear the slicing effect increase in speed. But then update the phase to something like 0.5 and run the code again, e.g.,

cue 'up'

with_fx :slicer do |sc|
  live_loop :foo do
    play 50
    sleep 2
  end

  live_loop :control do
    sync 'up'
    control sc, phase: 0.5
  end
end

and nothing seems to happen to the slicer effect – it continues at the faster speed. I’ve tried both with cue commented first, and then uncommenting as well as just leaving cue uncommented. Any idea what’s going on, or is there a different way we need to be controlling FX?

Hi Mobius :slight_smile:

In fact, this is by design (as far as Sam originally way back when being unable to find a simple, quick and easy way to enable live updating fx that wrap around live_loops). However, we are interested in changing this some time in the future (it’s on our wishlist) if our priorities and resources fit :slight_smile:

See the following for a discussion around this behaviour:

As for how you can otherwise automate fx from outside, the usual solution involves moving the fx inside the live_loop, and then using variables to give the fx their values. One way to do this is to use set and get to transfer your desired fx values via the Time-State memory store. @robin.newman shows an example here:

(See the Tutorial or the Lang section of the Help panel in the app for more details of set and get if you need :slight_smile:)

2 Likes