Sync issue among live_loops with proportional sleep times

Hi guys, I have tried this sample code in the tutorial

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

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

and as the log shows, the bar is triggered once for every 3 times foo runs, not as intended twice. I believe it’s a timing issue because when the foo seconds the each 2nd sync, the foo is just on the edge of sleep and not receiving it. So the solution can be fixing the bar loop, making it sleep a little shorter, like 0.9. But this is not a graceful solution, because you have to “hack” with the code, writing non-instinct logics.

Another way to fix it is using sync opt, but as far as I know, the sync opt just makes sure the two loops start at the same time, and won’t check again and again like the sync command. So getting back to the topic, I still expect a “fix” to the sync command which can neatly solve this issue.

@nordicwing - the recommended way to work around the issue is in fact the sync: live_loop opt, perhaps by syncing both loops to a third metronome loop, as has been a common pattern for many Sonic Pi users. (You can read about the history of this behaviour and how Sonic Pi syncing currently works at live_loop syncing issue? · Issue #1730 · sonic-pi-net/sonic-pi · GitHub and the links that are on my last reply there).
As for changing the syncing implementation, I can’t speak for Sam of course but I suspect that’s somewhat unlikely, particularly while Sam is focussing his efforts on Sonic Pi’s eventual successor, Tau5.

I have tried using a common met loop, and it behaves even more weirdly, the foo is triggered every two ticks, and bar is triggered every three ticks

it seems sync opt is the only solution

Yes. See the links in the last comment on that issue - they should explain this in enough detail.
The following adaptation of your original example (using sync: opt to start the two loops after the :met loop) should do what you are after?

live_loop :met do
  sleep 1
end

live_loop :foo, sync: :met do
  play :e4, release: 0.5
  sleep 0.5
end

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

yes with sync opt it always works, the common metronome doesn’t help with this issue, it’s just a design pattern thing. If using sync command, it always fail.

Also sync opt is nothing more than a kickstart, so it’s not functioning exactly as sync command. I’m not sure if there’s case in the future when we need to use sync for each round of iteration, in such a case we can’t use sync opt, and the sync time issue still exists.

Hi Ethan, I have found two solutions of the sync timing issue, I would like them to be shared and spreaded with others.

The intact description of this sync timing issue is as follows:

If you still need sleep in your live_loops, and it’s a Integer multiple of the cuer loop’s sleep duration, then you always have to wait for another cue! e.g. live_loop A sleeps 2, live_loop B sleeps 8, B syncs with A, then the total cycle of B would be 8 + 2 = 10.

live_loop :A do
    play 80
    sleep 2
end

live_loop :B do
    sync :A
    play 50
    sleep 8
end

(run the code and you will hear B sounds once every five times A sounds.)

And I found two solutions for it in another link here: