Raggiungendo un livello avanzato di live coding e usando molte funzioni e thread simultaneamente, ti sarai accordo che è semplice fare un errore che ferma un thread. Non è un problema perché puoi sempre farlo ripartire premendo Run ma, quando si riavvierà, sarà fuori tempo rispetto al thread originale.
Come abbiamo discusso in precedenza, i nuovi thread creati con in_thread
ereditano le impostazioni dal thread principale. Questo include, ovviamente, anche il tempo. Questo significa che i thread sono sempre a tempo quando partono in simultanea.
Tuttavia, quando fai partire un thread da solo, questo partirà con il suo tempo ed è davvero improbabile che sia sincronizzato con gli altri thread.
Sonic Pi offre una soluzione a questo problema con le funzioni cue
e sync
.
La funzione cue
ci permette di inviare il messaggio del cuore pulsante a tutti gli altri thread. Di default, gli altri thread non sono interessati a ricevere questo messaggio e lo ignorano. È possibile, però, attivare l’interesse al segnale utilizzando la funzione sync
.
La cosa importante da sapere è che sync
funziona in modo simile a sleep
nel senso che ferma il thread corrente per un certo periodo di tempo. La differenza è che con sleep
specifichiamo quanto tempo aspettare mentre con sync
non è possibile sapere quando aspetterà dal momento che sync
aspetta il segnale cue
da un altro thread che potrebbe essere vicino oppure lontano nel tempo.
Proviamo ad andare nel dettaglio:
in_thread do
loop do
cue :tick
sleep 1
end
end
in_thread do
loop do
sync :tick
sample :drum_heavy_kick
end
end
Qui abbiamo due thread, uno che si comporta come un metronomo e non riproduce alcun suono ma invia il segnale :tick
a ogni battito. Il secondo thread è sincronizzato al messaggio tick
e quando ne riceve uno, eredita il tempo del thread cue
e continua a funzionare.
Di conseguenza, sentiremo il campione :drum_heavy_kick
esattamente nel momento in cui l’altro thread invia il messaggio :tick
, anche se i due thread non vengono avviati in contemporanea:
in_thread do
loop do
cue :tick
sleep 1
end
end
sleep(0.3)
in_thread do
loop do
sync :tick
sample :drum_heavy_kick
end
end
Quella chiamata a sleep
normalmente manderebbe il secondo thread fuori fase rispetto al primo, ma, dal momento che usiamo cue
e sync
, sincronizziamo automaticamente i thread bypassando ogni possibile deviazione di tempo.
Sei libero di usare tutti i nomi che vuoi per i messaggi cue
, non solo :tick
. Devi solo assicurarti che tutti i thread siano sincronizzati sullo stesso nome, altrimenti aspetteranno all’infinito (o, fino alla pressione del pulsante di Stop).
Facciamo qualche prova con diversi nomi per cue
:
in_thread do
loop do
cue [:foo, :bar, :baz].choose
sleep 0.5
end
end
in_thread do
loop do
sync :foo
sample :elec_beep
end
end
in_thread do
loop do
sync :bar
sample :elec_flip
end
end
in_thread do
loop do
sync :baz
sample :elec_blup
end
end
Qui abbiamo un loop principale cue
che in modo casuale invia segnali chiamati :foo
, :bar
o :baz
. Abbiamo poi tre thread di loop con suoni differenti, ciascuno dei quali si sincronizza con quei segnali in modo indipendente. L’effetto rete fa si che sentiamo un suono ogni 0.5 battiti dal momento che ciascuno dei thread sync
è sincronizzato in modo casuale con i thread cue
che riproducono il campione.
Questo ovviamente funziona se ordini i thread al contrario dal momento che i thread sync
aspetteranno di ricevere il cue
successivo.