Variables

A useful thing to do in your code is to create names for things. Sonic Pi makes this very easy: you write the name you wish to use, an equal sign (=), then the thing you want to remember:

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.

Warning: Variables and Threads

Whilst variables are great for giving things names and capturing the results of things, it is important to know that they should typically only be used locally within a thread. For example, don’t do this:

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

In the above example we assign a ring of numbers to a variable a and then used it within two separate live_loops. In the first live loop every 0.5s we sort the ring (to (ring 1, 2, 3, 4, 5, 6)) and then print it out to the log. If you run the code, you’ll find that the printed list is not always sorted!. This may surprise you - especially that sometimes the list is printed as sorted, and sometimes it is not. This is called non-deterministic behaviour and is the result of a rather nasty problem called a race-condition. The problem is due to the fact that the second live loop is also manipulating the list (in this case shuffling it) and by the time the list is printed, sometimes it has just been sorted and sometimes it has just been shuffled. Both live loops are racing to do something different to the same variable and every time round a different loop ‘wins’.

There are two solutions to this. Firstly, don’t use the same variable in multiple live loops or threads. For example, the following code will always print a sorted list as each live loop has its own separate variable:

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

However, sometimes we do want to share things across threads. For example, the current key, BPM, synth etc. In these cases, the solution is to use Sonic Pi’s special thread-safe state system via the fns get and set. This is discussed later on in section 10.