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 cueci 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 cuee 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.