Exactly Martin. Trying to sync without the delay is like the conductor giving his/her initial downbeat without any warning whilst the players (the other live loops are still getting their instruments ready, so they miss the first bar and start at a later bar start from the conductor. With the delay the subsidiary live loops all start and wait the “conductor’s” cue which comes a little (0.01 beats) later.
There is one (more theoretical) question concerning the technical background, which I would like to be ansered for myself. Is this the correct description in plain words:
A synced live_loop can’t just start together meaning at the same time with the cueing loop. The synced live loop notices the ‘first’ cue (which concerning the runtime of the whole script for it happened to be in the past) and then starts with the next cue. So syncing needs kind of to be announced to actually i. e. audibly to happen.
Yes I think this is a fair statement that you make Martin.
Sam stated this when Ethan raised the sync issue on the Sonic Pi github site.
In v3 I made this deterministic which also meant deciding on a repeatable set of semantics. This in turn means that each cue is totally ordered across all runs and all syncs are also ordered. In v3 syncs will only catch cues after them. Which means that for threads syncing and queueing on the same logical time, their thread ID will determine whether they are before or after.
In other words for a sync to work you should make sure that the cue is sent AFTER the sync is waiting for it. For two live_loops starting at the same time, delay the one you wish to sync to slightly and they will then start together. (The second one waits for the first to send a cue as it starts).
Hi,
I hope any conductor is reading your post… The conductor is the metronome and as a metronome he has to be on the beat and not 0.01 seconde after…
The conductor is of course raising its stick before the beat and not after.
And computers are used because they are kown to be very accurate. so a metronome indicates the correct beat and musicians can rely on it.
According to me there is no valable explanation of this bug, it’s not a feature it’s a sad bug and i understand why you met lot of people not understanding this. It’s not logical as a musical point of view.
So this how it works but it’s a bit sad…
So the code below may help people to clear their mind
# a signal is sent every 1 beat
live_loop :metronome do
sleep 1
end
# this loop last 4 beats.
# metronome are accurate and that's why we can rely on them
live_loop :drums, sync: :metronome do
sample :drum_bass_hard
sleep 0.5
sample :drum_bass_soft, compress: 0, pan: [-1,1,-0.8,0.8].ring.tick
sleep 0.5
sample :drum_snare_hard, compress: 1, pan: [-1,1,-0.8,0.8].ring.tick
sleep 1
sample :drum_bass_hard
sleep 0.5
sample :drum_bass_soft, compress: 0, pan: [-1,1,-0.8,0.8].ring.tick
sleep 0.5
sample :drum_snare_hard, compress: 1, pan: [-1,1,-0.8,0.8].ring.tick
sleep 0.5
sample :drum_snare_hard, compress: 1, pan: [-1,1,-0.8,0.8].ring.tick
sleep 0.5
end
# this loop last 15 x 0.5 = 7.5 + 0.49 = 7.99 beats ! ! !
live_loop :hithats do
sync :drums
15.times do
sample :drum_cymbal_pedal, pan: [-0.8,0.8,-0.1,0.1].ring.tick
sleep 0.5
end
sample :drum_cymbal_open
# sleep 0.50 will NOT WORK it's a bug not a feature :(
# musicians are not accurate and it saves us :-)
sleep 0.49
end
best regards.
Hi @nlb,
try this:
live_loop :metronome do
sleep 1
end
live_loop :drums, sync: :metronome do
sample :drum_bass_hard
sleep 0.5
sample :drum_bass_soft, compress: 0, pan: [-1,1,-0.8,0.8].ring.tick
sleep 0.5
sample :drum_snare_hard, compress: 1, pan: [-1,1,-0.8,0.8].ring.tick
sleep 1
sample :drum_bass_hard
sleep 0.5
sample :drum_bass_soft, compress: 0, pan: [-1,1,-0.8,0.8].ring.tick
sleep 0.5
sample :drum_snare_hard, compress: 1, pan: [-1,1,-0.8,0.8].ring.tick
sleep 0.5
sample :drum_snare_hard, compress: 1, pan: [-1,1,-0.8,0.8].ring.tick
sleep 0.5
end
live_loop :hithats, sync: :drums do
15.times do
sample :drum_cymbal_pedal, pan: [-0.8,0.8,-0.1,0.1].ring.tick
sleep 0.5
end
sample :drum_cymbal_open
sleep 0.5
end
Two more thoughts on this:
You are right, it is not the easiest feature to understand in Sonic Pi.
And in defendance of the conductor-metaphor: Have you ever joined a rehearsal of a Jazz band? There will most surely be someone who counts something like that (in the case of a four-four time): “One… two, you know, what to do!” and then everybody is going to start.
Before you can sync to a metronome someone has to make sure that you listen to it at all.
thanks for your help @Martin
indeed your code is working but still don’t understand why mine is not working. i think i’m dumb
Hi @nlb,
no, if I may say so, you are not dumb at all. This thread did also help me to bring some more clarity. I have these questions during the course I give (and these are probable to pop up at any time) and now I am sure to be able to answer it.
See my grafic, which tries to explain exactly this point (as did the last part of Sam’s explanation). It is all about the fact, that your :hihats
will try to sync every time the loop runs. This is a bit like, as if this loop was a musician, who after every bar needs a new start (“one… two… you know, what to do…”) In my example, it does sync one time and then goes on with its own timing. So it is actually listening to sync just once (and remember: the time rule says: a sync can only be recognised if it has happened in the past).
Hope that helps.
I think you misunderstand how this works. It is EXACTLY what a conductor does. When you press RUN all the live loops start and await a sync pulse from the metronome (the conductor). At the same time the “conductor” starts waiting for 0.01 seconds to give the other loops a chance to be ready (equivalent to him/her raising the stick and checking that all the instruments (other live_loops) are ready and waiting for a cue to sync on. THEN the conductor (the metronome) starts giving the first downbeat and all the other loops start exactly in step with the metronome.
There is nothing magical about the 0.01 it could be 1 in which case there would be a slightly longer pause while the “orchestra” gets ready. However, when they start to play they would all be in perfect sync with each other and the conductor, when metronome starts after the 1 beat deal.
If the conductor started without raising his/her stick to warn the other members of the orchestra then chaos could ensue. I have played in several orchestras and this is how it works!
hello everybody,
ok ok maybe sometimes “i’m lost in translation” or miss something : i’m going to read slower your correct post and start again.
thanks for your help and sorry to waste your time !
No time wasted. This is an important topic. I hope that this thread will help users to get syncing right.
A question while revisiting my own grafic: In the last note shouldn’t it be: “BLUE starts from here, and will listen for the next run of RED” instead of “… every new run …”? Because that is exactly the reason for the gap: With sync :red
in the loop’s body BLUE does need a new sync for each run and will only pick up the sync when it is 1. not running and 2. the sync is not in the past. And if this is not correct is there anyone who can put this into plain English which is technically acurat? I’d be very grateful!
Not quite sure I get the distinction between your two statements here (listen for ‘the next run’ vs ‘every new run’). Though to me ‘the next run’ sounds fine.
The next sentence seems fine too, though I think you mean ‘does need a new cue for each run’ and 'will only pick up the cue when it is 1. not running [ie waiting via a sync] and 2. the cue is not in the past.
Hi @ethancrawford,
ok. Thanks a lot. I was wondering if my mental model of the cue/sync mechanism is correct. And you confirmed, it is. The crucial thing for me (referring to my sketch) is exactly that (to repeat what you already wrote):
- the synced
live_loop
- in case the sync statement resides within the loop’s body - does need a new sync for every run AND - it can only listen to a cue if it is not running, which does totally explain the gap (which does confuse people because they seem to expect the sync to be permanent once set up)
To incorrectly humanize this behaviour for my model : The syncing live_loop can 1. only do one of the two things: either play once triggered by a cue signal or be alert to any incoming cues; so quite logically 2. the sync command which triggers it can only be one that came before the syncing loop was in the state of listening.
To be clear: I am not struggling with this feature. I can savely use it but there are occasions, where I do have to explain it to others and therefore it is quite a good idea to have a clear understanding of what’s going on.
I have been messing around a bit with this lately and I think I’ve stumbled upon 2 different solutions.
For one, if you have the sync in the body of the live loop, you should not have a sleep as well. That is one reason for the gap in between - it’s waiting for the sync, AND sleeping. Or at least that’s how it seems from my experiments with it.
The other solution that seems to work for me is to use time_warp in my “metronome” loop and set it to a negative value, then explicitly send a cue rather than using the name of the loop as the cue. This way anything synced to that cue gets the sync message before it starts, and everything syncs up nicely right from “run,” rather than waiting a full loop before starting. Here’s an example:
live_loop :metro4 do
time_warp -0.01 do
cue :m4
end
sleep 4
end
EDIT: I just tried the time warp thing again and it didn’t work. So maybe I was imagining it the first time. Hmph.
Ah, the delay: 0.01 thing on my metronome thread seems to have solved a number of issues that I was having with live timing with loops. It seems to apply every time that loops are updated, even if the metronome live_loop is not modified, which gives any newly added loops the ability to be ready and waiting without missing every second sync.
I had been making sure that all my new live loops were slightly less than 8 beats. This solves the problem much more easily.
EDIT: Except, that no it doesn’t. If I use the following code:
use_bpm 127
live_loop :bdrums do
sync :loop
4.times do
sample :bd_haus
sleep 1
end
end
comment do
live_loop :sdrums do
sync :loop
2.times do
sleep 1
sample :sn_dolf
sleep 1
end
end
end
live_loop :timing, delay: 0.01 do
cue :loop
sleep 4
end
and start it running, and then uncomment the snare drum loop, then it misses every second bar. How do I fix this without using difficult fixes such as making sure that every new live loop added doesn’t last quite as long as the metronome loop?
@PiEaterAndPlayer - Does the sync:
opt of live_loop
meet your needs at all? That is the best way I find of avoiding the undesirable wait.
@ethancrowford
It does for this example. Thanks. I’ll try using it and see if the problem recurs.
The diagram above at Live loops Sync questions :-) explains the difference between the two sync methods well I think
Yes, I see. It does mean that my loops are not synchronised to the metronome after the very first synchronisation. I’d have to think about whether that risks them becoming unsynchronised over time. Particularly in live contexts.
I note that the following works. It means that everything will be slightly out of time. But, all threads sticking to the same slightly longer last beat
use_bpm 127
live_loop :bdrums do
sync :loop
4.times do
sample :bd_haus
sleep 1
end
end
uncomment do
live_loop :snares do
sync :loop
2.times do
sleep 1
sample :sn_dolf
sleep 1
end
end
end
uncomment do
live_loop :hats do
sync :loop
8.times do
sample :drum_cymbal_closed
sleep 0.5
end
end
end
live_loop :timing do
cue :loop
sleep 4.001
end
@samaaron any comments on that first sentence about drift in the above post?