Cuando comiences a escribir mucho código, desearás encontrar una manera de organizar y estructurar todo para hacerlo más entendible. Las funciones permiten hacer exactamente eso, permitiéndonos darle nombre a muchas partes de nuestro código. Veamos:
define :foo do
play 50
sleep 1
play 55
sleep 2
end
Aquí definimos una nueva función llamada foo
. Lo hacemos con nuestro viejo amigo el bloque de do/end y la palabra mágica define
seguidos del nombre que queremos darle a nuestra función. No necesitábamos llamarla foo
, podríamos haberla llamado cualquier cosa que quisiésemos, tales como bar
, baz
o idealmente algo significativo para tí como main_section
o lead_riff
.
Recuerda anteponer dos puntos :
al nombre de la función que defines.
Una vez hemos definido nuestra función, podemos llamarla simplemente escribiendo su nombre:
define :foo do
play 50
sleep 1
play 55
sleep 0.5
end
foo
sleep 1
2.times do
foo
end
Incluso podemos usar foo
dentro de bloques de iteración o cualquier lugar donde hayamos escrito play
o sample
. Esto nos da una fantástica manera de expresar y crear nuevas palabras significativas a usar en nuestras composiciones.
Hasta ahora, cada vez que presionamos el botón de Ejecutar, Sonic Pi ha comenzado de cero. Sólo conoce lo que está en el buffer actual. No puedes referenciar código en otro buffer o hilo. sin embargo, las funciones cambian eso. Cuando defines una función, Sonic Pi recuerda. Probemos borrando todo el código en tu buffer y reemplazandolo por:
foo
Presiona el botón de Ejecutar y escucha. ¿Dónde se fue el código?¿cómo supo Sonic Pi qué tocar? Sonic Pi recordó tu función, inclusive cuando la borraste del buffer. Esta conducta sólo funciona con las funciones creadas con define
(y defonce
).
Quizás te interese saber que al igual que podías pasar valores mínimos y máximos con rrand
, también puedes enseñar a tus funciones a aceptar argumentos. Miremos:
define :my_player do |n|
play n
end
my_player 80
sleep 0.5
my_player 90
Esto no es tan excitante, pero ilustra el punto. Creamos nuestra propia versión de play
llamada my_player
y la cual está parametrizada.
Los parámetros deben ir después del do
y define
en el bloque do/end
, rodeado de postes verticales |
y separados por comas ,
. Puedes usar cualquier palabra para los nombres de los parámetros.
Lo mágico sucede dentro del bloque do/end del define
. Puedes usar nombres de parámetros como si fueran valores reales. En este ejemplo estoy tocando la nota n
. Puedes considerar los parámetros como una especie de promesa de que cuando el código se ejecute, ellos serán reemplazados por los valores. Esto lo haces al pasar un parámetro a la función, cuando la llamas. Yo lo hago con my_player 80
para tocar la nota 80. Dentro de la definición de la función n
se reemplaza con 80, así play n
se convierte en play 80
. Cuando la llamo otra vez con my_player 90
, n
es reemplazada por 90, así que play n
se convierte en play 90
.
Veamos un ejemplo más interesante:
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
Aquí usé repeats
como si fuera un número para la línea de repeats.times do
. También usé root
como si fuera un nombre de nota en mi llamada a play
.
¡Nota cómo podemos utilizar algo muy expresivo y fácil de leer sólo con mover mucho de nuestra lógica en el código!