Zodra u veel code begint te schrijven, kan je als je wil, een manier vinden om dingen te gaan organiseren en structureren, om deze netter te maken en gemakkelijker te begrijpen. Functies zijn een zeer krachtige manier om dit te doen. Ze brengen ons de mogelijkheid om een naam te geven aan een bundel code. Laten we een kijkje nemen.
define :foo do
play 50
sleep 1
play 55
sleep 2
end
Hier hebben we een nieuwe functie genaamd foo
gedefinieerd. We doen dit met onze oude vriend het do/end blok en het magische woord define
gevolgd door de naam die wij willen geven aan onze functie. We hoefde deze niet foo
te noemen, we konden deze om het even wat noemen zoals fiets
, bal
of een ideale beschrijving zoals refrein_stuk
of lead_riff
.
Vergeet niet van een dubbele punt :
bij de naam van je functie te voegen wanneer je deze definieert.
Eens we een functie hebben gedefinieerd kunnen we deze oproepen door gewoon zijn naam te schrijven:
define :foo do
play 50
sleep 1
play 55
sleep 0.5
end
foo
sleep 1
2.times do
foo
end
We kunnen zelfs gebruik maken van ‘foo’ binnen iteratie blokken of overal waar we spelen
of samplen
hebben geschreven. Dit schenkt ons een geweldige manier om onszelf te uiten en nieuwe zinvolle woorden te gebruiken in onze composities.
Tot nu toe was het zo, dat telkens als u de Afspeel-knop ingedrukte, Sonic Pi met een schone lei startte. Het weet weet van niets tenzij van wat er in de buffer zit. Je kan niet verwijzen naar code die in een andere buffer of een andere thread zit. Met functies veranderd dat. Wanneer je een functie definieert zal Sonic Pi zich deze herinneren. Laten we dit eens proberen. Verwijder alle code uit uw buffer en vervang het met:
foo
Druk op de Afspeel-knop - en hoor je functie nu spelen. Zo zonder code? Hoe wist Sonic Pi wat het moest spelen? Sonic Pi herinnerde zich jouw functie - dus zelfs nadat je deze hebt gewist uit de buffer, het herinnerde zich wat jij had getypt. Dit gedrag werkt alleen met functies die zijn gemaakt met define
(en defonce
).
Interessant om weten ook is dat je net zoals je min als max waarden kan geven aan rrand
, kan je ook jouw functies argumenten laten accepteren. Laat ons dat even bekijken:
define :my_player do |n|
play n
end
my_player 80
sleep 0.5
my_player 90
Dit is niet erg spannend, maar het illustreert wel de zaak. We hebben onze eigen versie van play
genaamd ‘my_player’ die is geparametriseerd.
De parameters moeten na de do
van het define
do/end blok komen te staan, omringd door een sluisteken |
en gescheiden door komma’s ,
. Om deze parameters te benoemen mag je elk woord gebruiken dat je maar wil.
Het zit ‘h allemaal in de define
do/end blok. Je kan de parameternamen gebruiken zoals echte waarden. In dit voorbeeld speel ik de noot n. Je kan deze beschouwen als een soort toezegging, dat wanneer de code loopt, deze door de werkelijke waarden vervangen wordt wanneer je deze benoemd. Ik doe dit met
my_player 80. om noot 80 te spelen. Binnen de definitie van deze functie, is
nnu vervangen door 80, dus
play n verandert in
play 80. Wanneer ik deze vernoem met
my_player 90´, is n
nu vervangen met 90, play n
is veranderd in play 90
.
Laten we eens kijken naar een interessanter voorbeeld:
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
Hier gebruikte ik repeats
als een cijfer in de regel repeats.times do
.Ik gebruikte root
ook als naam van een noot in mijn play
oproep.
Hierbij zien we dat we in staat zijn om iets zeer expressief, maar makkelijk leesbaar te schrijven door onze logica in een functie te steken!