Une fois que vous commencez à écrire beaucoup de code, vous pouvez souhaiter trouver le moyen d’organiser et de structurer les choses pour les rendre plus ordonnées et faciles à comprendre. Les fonctions sont un moyen très puissant pour le faire. Elles vous donnent la capacité de donner un nom à un morceau de code. Jetons-y un œil.
define :foo do
play 50
sleep 1
play 55
sleep 2
end
Ici, nous avons défini une nouvelle fonction appelée foo
. Nous faisons cela avec notre vieil ami, le bloc do/end et le mot magique define
suivi par le nom que nous voulons donner à notre fonction. Nous ne sommes pas tenus de l’appeler foo
, nous aurions pu l’appeler avec n’importe quel nom que nous voulions, comme bar
, baz
ou idéalement quelque chose de significatif pour vous comme section_principale
ou lead_riff
.
Rappelez-vous d’ajouter un deux-points :
devant le nom de votre fonction quand vous la définissez.
Une fois que nous avons défini notre fonction, nous pouvons l’appeler simplement en écrivant son nom :
define :foo do
play 50
sleep 1
play 55
sleep 0.5
end
foo
sleep 1
2.times do
foo
end
Nous pouvons même utiliser foo
à l’intérieur de blocs d’itération ou n’importe où nous aurions pu écrire play
ou sample
. Ceci nous donne un super moyen de nous exprimer et de créer de nouveaux mots significatifs à utiliser dans nos compositions.
Pour le moment, chaque fois que nous avons pressé le bouton Run, Sonic Pi a démarré d’un état complètement vierge. Il ne connait rien à part ce qui est dans le buffer. Vous ne pouvez pas référencer du code d’un autre buffer ou d’un autre thread. Toutefois, les fonctions changent cela. Quand vous définissez une fonction, Sonic Pi s’en rappelle. Essayons-le. Effacez tout le code dans votre buffer et remplacez-le par :
foo
Pressez le bouton Run et écoutez votre fonction qui est jouée. Où le code est-il allé ? Comment Sonic Pi sait-il quoi jouer ? Sonic Pi s’est simplement souvenu de votre fonction - ainsi, même après l’avoir effacée de votre buffer, il se rappelle de ce que vous avez tapé. Ce comportement fonctionne uniquement avec les fonctions créées en utilisant define
(et defonce
).
Vous pourriez être intéressé à savoir que juste comme vous pouvez passer les valeurs min et max à rrand
, vous pouvez apprendre à vos fonctions à accepter des arguments. Jetons-y un œil :
define :my_player do |n|
play n
end
my_player 80
sleep 0.5
my_player 90
Ce n’est pas très excitant, mais ça illustre le point. Nous avons créé notre propre version de play
appelée my_player
et qui est paramétrée.
Les paramètres doivent être placés après le do
du bloc do/end de define
, entourés par des barres verticales |
et séparés par des virgules ,
. Vous pouvez utiliser le mot de votre choix pour les noms de paramètres.
La magie se passe à l’intérieur du bloc do/end de define
. Vous pouvez utiliser les noms de paramètres comme si c’étaient des valeurs réelles. Dans cet exemple, je joue la note n
. Vous pouvez considérer les paramètres comme une sorte de promesse qui fait que, lorsque le code s’exécute, ils sont remplacés par des valeurs réelles. Vous faites ceci en passant un argument à la fonction quand vous l’appelez. Je le fais avec my_player 80
pour jouer la note 80. À l’intérieur de la définition de la fonction, n
est alors remplacé par 80, donc play n
est changé en play 80
. Quand je l’appelle une nouvelle fois avec my_player 90
, n
est alors remplacé par 90, donc play n
est changé en play 90
.
Voyons un exemple plus intéressant :
define :chord_player do |root, repeats|
repeats.times do
play chord(root, :minor), release: 0.3
sleep 0.5
end
end
chord_player :e3, 2
sleep 0.5
chord_player :a3, 3
chord_player :g3, 4
sleep 0.5
chord_player :e3, 3
Ici j’ai utilisé repeats
comme si c’était un nombre dans la ligne repeats.times do
. J’ai aussi utilisé root
comme si c’était un nom de note dans mon appel à play
.
Voyez comme nous sommes capables d’écrire quelque chose de très expressif et facile à lire en déplaçant beaucoup de notre logique à l’intérieur d’une fonction !