Confusing sync/cue behavior

I know that there has already been a lot of discussion around the cue/sync behavior and I looked at some other threads but I don’t seem to figure out what’s happening here.

As I was following the tutorial I came around to 9.3 Multiple Live Loops. There is an example that uses the implicit live_loop sync which should line up two different live_loops:

live_loop :foo do
  play :e4, release: 0.5
  sleep 1
end

live_loop :bar do
  sync :foo
  sample :bd_haus
  sleep 1
end

(Note that I changed the first sleep value from 0.5 to 1 to make the issue more apparent).

When I run this snippet the sequence of events is as follows:

{run: 874, time: 0.0, thread: :live_loop_bar}
 └─ sync :foo
 
{run: 874, time: 0.0, thread: :live_loop_foo}
 └─ synth :beep, {note: 64.0, release: 0.5}
 
{run: 874, time: 1.0008, thread: :live_loop_foo}
 └─ synth :beep, {note: 64.0, release: 0.5}
 
{run: 874, time: 1.0008, thread: :live_loop_bar}
 ├─ synced "/{cue,set,live_loop}/foo" 
 └─ sample "~/projects/sonic-pi/etc/samples",
             "bd_haus.flac"
 
{run: 874, time: 2.0008, thread: :live_loop_bar}
 └─ sync :foo
 
{run: 874, time: 2.0008, thread: :live_loop_foo}
 └─ synth :beep, {note: 64.0, release: 0.5}
 
{run: 874, time: 3.0008, thread: :live_loop_bar}
 ├─ synced "/{cue,set,live_loop}/foo" 
 └─ sample "~/projects/sonic-pi/etc/samples",
             "bd_haus.flac"
 
{run: 874, time: 3.0008, thread: :live_loop_foo}
 └─ synth :beep, {note: 64.0, release: 0.5}
 
{run: 874, time: 4.0008, thread: :live_loop_bar}
 └─ sync :foo
 
{run: 874, time: 4.0008, thread: :live_loop_foo}
 └─ synth :beep, {note: 64.0, release: 0.5}
 
{run: 874, time: 5.0005, thread: :live_loop_bar}
 ├─ synced "/{cue,set,live_loop}/foo" 
 └─ sample "~/projects/sonic-pi/etc/samples",
             "bd_haus.flac"
 
{run: 874, time: 5.0005, thread: :live_loop_foo}
 └─ synth :beep, {note: 64.0, release: 0.5}
 
{run: 874, time: 6.0005, thread: :live_loop_bar}
 └─ sync :foo
 
{run: 874, time: 6.0005, thread: :live_loop_foo}
 └─ synth :beep, {note: 64.0, release: 0.5}
 

If you look carefully at the timings you get one bar for every two foo.

If I remove the sleep 1 from bar then they are in sync. The same happens if I just set it to 0.9, so there’s clearly some timing issue: probably bar is still sleeping and skips a cue. Similarly if I use the opt sync: :foo they are also in sync:

live_loop :bar, sync: :foo do
  sample :bd_haus
  sleep 1
end

(You still get one extra foo before bar starts, but I guess that’s expected.)

So my question is: is this behavior expected? Maybe using the opt sync: :foo would be better for the tutorial, otherwise the audible effect is very confusing for a beginner.

Thank you! Looking forward to discover Sonic Pi in depth!

With sync, there’s no need to sleep, so you can just delete the sleep line entirely in this case and there’s never a race condition. I agree that the example in the docs is confusing because using both sync and sleep in that loop is redundant.

The live_loop sync: option only syncs when it starts the first time, and will loop on its own afterwards.

Thank you for your reply! I would say that the example in the docs is actually wrong as it yields an unexpected result which conflicts with the text preceding it.

What piece of code from the doc do you speak about ?
Thanks

The last code snippet in Sonic Pi - Tutorial “9.3 - Multiple Live Loops: Syncing Live Loops”.

@decode but this is only true if both live_loops play patterns that use the same sleep value (resolution), otherwise they still require separate sleep patterns. Even with the suggested workaround there will still be the extra beat. The live_loop must play at least once for the others to sync to it, hence the extra foo.

There are threads and examples that discuss how to overcome this issue, and various proposed fixes, including this one that perhaps gets closest (closer) to a solution: http://in-thread.sonic-pi.net/t/live-loops-sync-questions/1172/18

There’s a bit more here: https://in-thread.sonic-pi.net/t/using-sync-with-live-loops/172/2

Do have a look and see if these help to resolve the issue.

1 Like

hi

live_loop :foo do
  sleep 1
end

live_loop :bar do
  sync :foo
  sample :bd_haus
  
end

live_loop :melody do
  sync :foo
  2.times do
    synth :bass_foundation, note: :c2
    sleep (ring 0.5, 0.499).tick
    # sleep 0.5 no because the melody loop is not finished to catch the foo signal
  end
end

cheers

@nlb yes, I got that the first time. As Sam points out in his post moving to using sync as an opt seems to remove this issue.

live_loop :foo do
  sleep 1
end

live_loop :bar, sync: :foo do
  sample :bd_haus
  sleep 1.0
end

live_loop :melody, sync: :foo do
  2.times do
    synth :bass_foundation, note: :c2
    sleep 0.5 
  end
end

I made some small changes to your code, does this not solve the problem? I’ve run it at different bpm from 30 - 480 and seems to have no issues. Tightening up the release would also help and reduce overlaps which can also create issues.

My little example was only to show what happens and yes as written in previous posts mentioned, the sync : :foo does what most users want to get.
Cheers