Une fois que vous êtes suffisamment avancés en codage “live” avec un certain nombre de fonctions et de threads en simultanéité, vous avez probablement remarqué que c’est plutôt facile de faire une faute dans l’un des threads et qui lui met fin. Ce n’est pas un gros problème car vous pouvez facilement redémarrer le thread en pressant Run. Toutefois, quand vous redémarrez le thread, il se trouve alors déphasé avec les threads d’origine.
Comme nous en avons discuté auparavant, les nouveaux threads créés avec in_thread
héritent de tous les paramètres du thread parent. Ceci inclut l’heure actuelle. Ce qui signifie que les threads sont toujours en phase entre eux quand ils sont démarrés simultanément.
Toutefois quand vous démarrez un thread tout seul, il démarre avec sa propre heure qui n’est probablement en phase avec aucun des autres threads en cours d’exécution.
Sonic Pi fournit une solution à ce problème avec les fonctions cue
et sync
.
cue
nous permet d’envoyer nos messages de battement de cœur à tous les autres threads. Par défaut, les autres threads ne sont pas intéressés et ignorent ces messages de battement de coeur. Cependant, vous pouvez leur déclarer de l’intérêt avec la fonction sync
.
La chose importante à laquelle il faut être attentif est que sync
est similaire à sleep
dans le sens qu’elle arrête le thread courant et l’empêche de faire quoi que ce soit pendant un moment. Toutefois, avec sleep
, vous spécifiez combien de temps vous voulez attendre tandis qu’avec sync
, vous ne savez pas combien de temps vous allez attendre - étant donné que sync
attend le cue
suivant d’un autre thread, ce qui peut être court ou long.
Explorons ceci en un peu plus de détail :
in_thread do
loop do
cue :tick
sleep 1
end
end
in_thread do
loop do
sync :tick
sample :drum_heavy_kick
end
end
Nous avons ici deux threads - l’un agissant comme un métronome, ne jouant aucun son mais envoyant des messages de battement de cœur :tick
à chaque temps. Le second thread se synchronise sur les messages tick
et quand il en reçoit un, il hérite de l’heure du thread cue
et continue de jouer.
Par conséquent, nous entendons l’échantillon :drum_heavy_kick
exactement quand l’autre thread envoie le message :tick
, même si les deux threads n’ont pas démarré leur exécution en même temps :
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
Ce vilain appel à sleep
mettrait typiquement le second thread en déphasage avec le premier. Cependant, comme nous utilisons cue
et sync
, nous synchronisons automatiquement les threads en évitant un décalage de temps accidentel.
Vous êtes libre d’utiliser n’importe quel nom que vous aimeriez pour vos messages cue
, pas seulement :tick
. Vous devez juste vous assurer que tout autre thread se synchronise sur le bon nom - autrement il attendrait à jamais (au moins jusqu’à ce que vous pressiez le bouton Stop).
Jouons avec quelques noms de 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
Ici, nous avons une boucle principale cue
qui envoie aléatoirement un des battements de cœur nommés :foo
, :bar
ou :baz
. Nous avons aussi ensuite trois boucles en thread se synchronisant sur ces noms indépendamment et jouant un échantillon différent. Il est clair que nous entendons un son à chaque demi-temps puisque chacun des threads sync
est aléatoirement synchronisé avec le thread cue
et joue son échantillon.
Ceci, bien entendu, marche aussi si vous ordonnez les threads en sens inverse puisque les threads sync
restent en attente du cue
suivant.