Create live_loops dynamically with a for loop


#1

Hello,

I need to create 4 live_loops to play 4 different samples and sleep parameters and I have tried to build them dynamally using a for loop:

# Loop
for x in 0..(2)
  puts "Before: #{x}"
  loop_name=eval '"channel#{x.to_s}_loop"'
  live_loop loop_name do
    puts "Inside live_loop: #{x}"
    sleep 5
  end
end

However I am getting unexpected results:

{run: 56, time: 0.0, thread: :live_loop_channel0_loop}
└─ "Inside live_loop: 1"
{run: 56, time: 0.0, thread: :live_loop_channel2_loop}
└─ "Inside live_loop: 2"
{run: 56, time: 0.0, thread: :live_loop_channel1_loop}
└─ "Inside live_loop: 1"
{run: 56, time: 0.0}
├─ "Before: 0"
├─ "Before: 1"
└─ "Before: 2"

Does anybody know how to build live_loops dynamically, or should I create 4 single live_loops?

Thank you very much!


#2

I have done this before using a definition to create the loops
Try this:

define :doLoop do |ln,n|
  loop_name=eval'"channel#{ln.to_s}_loop"'
  live_loop loop_name do
    puts "inside #{loop_name}"
    sample [:bd_haus,:elec_blup,:elec_flip][n]
    sleep 1.5
  end
end

for x in 0..(2)
  doLoop x.to_s,x
  sleep 0.5
end

generates three live loops, each of which plays a different samplesonic
The log output looks like this

=> Starting run 1

=> Defining fn :doLoop

=> Defining fn :live_loop_channel0_loop

=> Loaded sample "/Applications/Sonic Pi.app/etc/samples/bd_haus.flac"

{run: 1, time: 0.0, thread: :live_loop_channel0_loop}
 └─ "inside channel0_loop"
 
{run: 1, time: 0.0}
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "bd_haus.flac"
 
=> Defining fn :live_loop_channel1_loop

=> Loaded sample "/Applications/Sonic Pi.app/etc/samples/elec_blup.flac"

{run: 1, time: 0.5, thread: :live_loop_channel1_loop}
 └─ "inside channel1_loop"
 
=> Defining fn :live_loop_channel2_loop

{run: 1, time: 0.5}
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "elec_blup.flac"
 
=> Loaded sample "/Applications/Sonic Pi.app/etc/samples/elec_flip.flac"

{run: 1, time: 1.0, thread: :live_loop_channel2_loop}
 └─ "inside channel2_loop"
 
{run: 1, time: 1.0}
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "elec_flip.flac"
 
{run: 1, time: 1.5}
 └─ Stopped internal thread
 
{run: 1, time: 1.5, thread: :live_loop_channel0_loop}
 ├─ "inside channel0_loop"
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "bd_haus.flac"
 
{run: 1, time: 2.0, thread: :live_loop_channel1_loop}
 ├─ "inside channel1_loop"
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "elec_blup.flac"
 
{run: 1, time: 2.5, thread: :live_loop_channel2_loop}
 ├─ "inside channel2_loop"
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "elec_flip.flac"
 
{run: 1, time: 3.0, thread: :live_loop_channel0_loop}
 ├─ "inside channel0_loop"
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "bd_haus.flac"
 
{run: 1, time: 3.5, thread: :live_loop_channel1_loop}
 ├─ "inside channel1_loop"
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "elec_blup.flac"
 
{run: 1, time: 4.0, thread: :live_loop_channel2_loop}
 ├─ "inside channel2_loop"
 └─ sample "/Applications/Sonic Pi.app/etc/samples",
             "elec_flip.flac"
 
=> Stopping all runs...

=> Stopping run 1

=> Completed run 1

=> All runs completed

=> Pausing SuperCollider Audio Server

The order in which the log entries are printed may seem a bit confusing, but if you look at the times of the entries you can see that the loops are operating as you would expect, each generated and playing for the first iteration 0.5 beats after the previous one, and repeating every 1.5 beats.

I used an expanded version of this definition method in a project I did which also let me remove the live loops as well.
see https://gist.github.com/rbnpi/a86ced02b74817f3070887df30fb0701


#3

Fantastic, that’s exactly what I need.

I am very grateful, again, Robin. Thank you!


#4

Other than the use of eval, which you could replace with standard string interpolation, I don’t think there’s anything wrong with your code.

It seems to me that the strange print out behaviour you’re observing is due to x being a variable that’s shared across threads. This is usually a bad thing that can produce unusual results as all threads are reading/writing to the same variable.

In general, in Sonic Pi, variables are to be avoided unless you are either storing an immutable thing in there (such as a scale or number) and you keep them local to a live loop or thread.

In this case, a simple way of not using variables is to use basic iteration and grab the current iteration value:

3.times do |x|
  live_loop "loop-#{x}" do
    puts "Inside live_loop: #{x}"
    sleep 5
  end
end

Hope that this helps :slight_smile:


#5

Moveed - Sorry, wrong post.


#6

Thank you for your clarification. :slight_smile: