Variables

Une chose utile à faire dans votre code est d’attribuer des noms aux choses. Sonic Pi rend cela très facile : vous écrivez le nom que vous voulez utiliser, un signe égal (=), puis la chose dont vous voulez vous rappeler :

sample_name = :loop_amen

Ici, nous nous sommes ‘rappelés’ du symbole :loop_amen dans la variable sample_name. Nous pouvons maintenant utiliser sample_name partout où nous aurions utilisé :loop_amen. Par exemple :

sample_name = :loop_amen
sample sample_name

Il y a trois raisons principales pour utiliser des variables dans Sonic Pi : communication de la signification, gestion de la répétition et capture des résultats des choses.

Communication de la signification

Quand vous écrivez du code, c’est facile de ne penser qu’à dire à l’ordinateur comment faire le boulot - tant que l’ordinateur comprend, c’est OK. Toutefois, il est important de se rappeler qu’il n’y a pas que l’ordinateur qui lit le code. D’autres personnes peuvent aussi le lire et essayer de comprendre ce qu’il fait. En outre, il est probable que vous ayez à lire votre propre code dans le futur et à essayer de comprendre ce qui s’y passe. Bien que cela puisse vous sembler évident maintenant, ce pourrait ne pas être si évident pour les autres et pour vous-même dans le futur !

Un moyen d’aider les autres à comprendre ce que fait votre code est d’écrire des commentaires (comme nous l’avons vu dans une section précédente). Une autre façon est d’utiliser des noms de variables significatifs. Regardez ce code :

sleep 1.7533

Pourquoi utilise-t-il le nombre 1.7533 ? D’où est-il venu ? Qu’est-ce que cela signifie ? En revanche, regardez ce code :

loop_amen_duration = 1.7533
sleep loop_amen_duration

Maintenant, il est beaucoup plus clair que 1.7533 signifie la durée de l’échantillon :loop_amen ! Bien sûr, vous pourriez dire pourquoi ne pas simplement écrire :

sleep sample_duration(:loop_amen)

Qui, bien entendu, est un moyen très sympathique de communiquer l’intention du code.

Gestion de la répétition

Vous voyez souvent beaucoup de répétitions dans votre code et quand vous voulez changer les choses, vous devez les changer à beaucoup d’endroits. Jetez un œil à ce code :

sample :loop_amen
sleep sample_duration(:loop_amen)
sample :loop_amen, rate: 0.5
sleep sample_duration(:loop_amen, rate: 0.5)
sample :loop_amen
sleep sample_duration(:loop_amen)

Nous sommes en train de faire beaucoup de choses avec :loop_amen ! Que faire si nous voulons écouter comment ça sonne avec un autre échantillon tel que :loop_garzul ? Nous aurons à trouver et remplacer tous les :loop_amen par :loop_garzul. Ce ne serait pas grave si vous disposiez de beaucoup de temps - mais que faire si vous œuvrez sur scène ? Quelquefois, vous n’aurez pas le luxe de temps disponible - en particulier si vous voulez que les gens continuent à danser.

Et si vous aviez écrit votre code comme ceci :

sample_name = :loop_amen
sample sample_name
sleep sample_duration(sample_name)
sample sample_name, rate: 0.5
sleep sample_duration(sample_name, rate: 0.5)
sample sample_name
sleep sample_duration(sample_name)

Maintenant, ça fait exactement la même chose qu’au dessus (essayez-le). Ça nous procure aussi la capacité de changer juste une ligne : sample_name = :loop_amen en sample_name = :loop_garzul et la magie des variables nous le change dans beaucoup d’endroits.

Capture des résultats

Finalement, une bonne motivation pour utiliser des variables est de capturer les résultats des choses. Par exemple, vous pouvez vouloir faire des choses avec la durée d’un échantillon :

sd = sample_duration(:loop_amen)

Nous pouvons maintenant utiliser sd partout où nous avons besoin de la durée de l’échantillon :loop_amen.

Plus important peut-être, une variable nous permet de capturer le résultat d’un appel à play ou sample :

s = play 50, release: 8

Maintenant, nous avons capturé et mémorisé s comme une variable, ce qui nous permet de contrôler le synthé pendant qu’il est actif :

s = play 50, release: 8
sleep 2
control s, note: 62

Nous verrons le contrôle des synthés plus en détail dans une prochaine section.

Avertissement : variables et fils

Tandis que les variables sont géniales pour donner des noms et capturer les résultats des choses, il est important de savoir qu’elles ne doivent généralement être utilisées que localement au sein d’un fil d’exécution. Par exemple, ne faites pas ceci :

a = (ring 6, 5, 4, 3, 2, 1)
live_loop :sorted do
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end
live_loop :shuffled do
  a = a.shuffle
  sleep 0.5
end

Dans l’exemple ci-dessus, nous avons assigné un anneau de nombres à la variable a et nous l’avons utilisé dans deux live_loop séparées. Dans la première boucle en direct, toutes les 0.5s, nous trions l’anneau (à (ring 1, 2, 3, 4, 5, 6)) et nous l’inscrivons dans le journal. Si vous exécutez le code, vous remarquerez que la liste imprimée n’est pas toujours triée !. Cela peut vous surprendre - surtout que des fois la liste est imprimée de façon triée, et d’autres fois non. Ceci s’appelle un comportement non-déterministe et est le résultat d’un méchant problème qui s’appelle la concurrence (NdT : race-condition). Le problème est dû au fait que la deuxième boucle en direct manipule également la liste (pour la mélanger dans ce cas) et, au moment où la liste est imprimée, parfois elle vient d’être triée, et d’autres fois, elle vient d’être mélangée. Les deux boucles en direct se font concurrence pour faire une manipulation différente à la même variable et, à chaque itération, une boucle différente “gagne”.

Il y a deux solutions à cela. Premièrement, n’utilisez pas la même variable dans de multiples boucles en direct ou fil d’exécution. Par exemple, le code suivant imprimera toujours une liste triée puisque chaque boucle en direct possède ses propres variables séparées :

live_loop :shuffled do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.shuffle
  sleep 0.5
end
live_loop :sorted do
  a = (ring 6, 5, 4, 3, 2, 1)
  a = a.sort
  sleep 0.5
  puts "sorted: ", a
end

Par contre, parfois nous voulons partager des éléments entre plusieurs fils d’exécution. Par exemple, la clé courante, BPM, synthé etc. Dans ces cas, la solution est d’utiliser le système d’état de fil d’exécution sécurisé spécial de Sonic Pi par les fonctions get et set. Nous en reparlons dans la section 10.