Quite new to the party, but not to coding in general. But async programming in SP is new to me
Running SP 3.2.2 on Win and trying to understand the internals of thread communication.
With my latest test, I had created nested live_loops to be able:
to send a cue from the end of :intro (which then stops)
to the loop which waits for the sync :intro => to start the inner live_loop :main
the example is simplified
goal
The goal I try to achieve, is to have a boolean flag stop_main, which I want to change in live-coding.
The issue is, that when I have two nested live_loop the live-coding change of stop_main true is NOT be visible in the inner :main loop.
So I’ve just found out that replacing live_loop :start_main do with in_thread do seems to work!
Question
Can someone pls explain to me what the difference here is?
Is there another pattern how to make live-code changes (i.e. to variables) visible to nested live_loops?
Code:
live_loop :intro do
4.times do
play :a3
sleep 0.125
end
cue :intro
stop
end
stop_main = false
live_loop :start_main do
##| in_thread do
sync :intro
live_loop :main do
4.times do
play :c4
sleep 0.25
end
puts "stop_main: ", stop_main
stop if stop_main
end
end
So I’ve just found out that replacing live_loop :start_main do with in_thread do seems to work!
It only picks up the live-code change stop_main trueWHEN it runs through the live_loop :intro before:
seems the additional trigger ofcue/sync makes a difference
in case I out-comment the live_loop :intro when making the stop_main true change (as I don’t want to play the intro again), then it will NOT pick up the changed variable value
just double-checked but using live_loop :start_main do instead of in_thread do will not work either way (with or w/o the :intro loop)
May i suggest you to search examples on this forum about live_loop. use the search field.
read some code and copy paste in your sonic pi and change it to test.
the sync system in sonic pi is not as easy as you may suppose. so avoid you too much complex code keep simple
good luck
Firstly I should state that I’m still yet to see a good reason for nesting live_loops. Perhaps if you’re generating new live loops to listen to different incoming events, that might be a reason, but in general if you find you’re nesting live loops, you can probably simplify your code.
Secondly, your :intro live loop is explicitly calling cue. This isn’t needed. Live loops automatically call cue with the name of their live loop (you can see this in the cue log). However, you stop your live loop so the implicit cue isn’t ever caught by any other thread, because they are all started after:intro - ordering matters for deterministic behaviour.
If you’re just trying to send a cue to :intro just call cue in the outer scope, there’s no need for a live loop.
With respect to sharing changes across threads, you can use get and set which will work deterministically (i.e. repeatably the same) and give you the ability to share data across threads/live loops. Take a look at section 10 of the tutorial on Time State for more information: https://sonic-pi.net/tutorial.html#section-10
With respect to sharing changes across threads, you can use get and set which will work
Thx for your feedback and the pointer to get/set!
Sounds that they are exactly the cross / thread safe communication channel which I’ve missed.
Secondly, your :intro live loop is explicitly calling cue
Because I wanted to send the cue at the END of my live_loop :intro, just before the stop.
Which seems to work the way I wrote it. Even when I remove the stop the explicit cue :intro sends the signal at the END of the 1st loop and not at the START.
I’ve started with a custom name for the cue/sync but figured out that it works with the loop-name too.
If you’re just trying to send a cue to :intro just call cue in the outer scope, there’s no need for a live loop.
Yes in this reduced example it can be simplified, but I was fiddling to find a more general pattern how many live_loops can start/stop each other by sending messages around, like a jam-session in a pub/sub style calling out the next one for a solo :).
And to me cue/sync and get/set are for sure key elements for this.
Nested live_loop for the moment is my solution
to keep the outer communication-loop with the sync
independent from the inner music-loop
Most likely there is a smarter solution… still ramping up
I’m not 100% sure I’ve fully understood how you want your jam session to function. However, in general:
Put all your repeating code in live_loops at the top level
Start each live_loop with a sync
Then all your code is ready to fire, waiting for the cue to come in. At this point, you can either have a single thread call out all the cue as needed or have different live_loop trigger them.
However, of you want to just trigger something once, I think use in_thread (possibly wrapped in a define if you want to do it multiple times).
For synchronizing other aspects than timing - especially among several threads at once - set/get as Sam suggested. Alternatively, cue can take a second argument that will be returned by the matching sync call - this can be convenient for sharing small pieces of data in a more targeted way.