Присвоение имён полезно для вашего кода. С Sonic Pi это очень просто. Вы пишете желаемое имя, знак равенства, а затем то, что нужно запомнить:
sample_name = :loop_amen
В этом примере мы “запомнили” обозначение :loop_amen
в переменной sample_name
. Теперь мы можем пользоваться sample_name
везде, где мы могли бы указать :loop_amen
. Например:
sample_name = :loop_amen
sample sample_name
Есть три главных причины для использования переменных в Sonic Pi: присвоение значения, управление повторами и захват результатов операций.
Когда вы пишете код, вы говорите компьютеру сделать что-то. Пока компьютер это понимает - всё в порядке. Но всё же важно помнить, что код читает не только компьютер. Другие люди, возможно, захотят тоже прочесть его и попробовать разобраться, что в нём происходит. Скорее всего и вы сами будете читать и пытаться понять свой собственный код в будущем. Сейчас всё кажется очевидным, но для остальных, или же для вас самих в будущем, это может быть не так!
Одним из способов помочь другим понять, что делает ваш код, - это оставлять комментарии (как мы это видели в предыдущей главе). Ещё один способ - давать осмысленные имена переменным. Посмотрите на этот код:
sleep 1.7533
Зачем указано число 1.7533
? Откуда оно взялось? Что оно означает? Теперь взгляните на этот пример:
loop_amen_duration = 1.7533
sleep loop_amen_duration
Вот теперь стало ясно, что значит 1.7533
. Это же продолжительность сэмпла :loop_amen
! Конечно, вы могли бы заметить, почему бы просто не написать так:
sleep sample_duration(:loop_amen)
Это, безусловно, очень хороший способ выразить намерения кода.
Часто вы видите много повторений в своем коде. Когда необходимо что-то поменять, то делать это приходится во многих местах. Посмотрите на следующее:
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)
Много чего мы делаем с :loop_amen
! Но что если мы захотим послушать как это будет звучать с другим замкнутым сэмплом, таким как :loop_garzul
? Пришлось бы найти и заменить все :loop_amen
на :loop_garzul
. Этим можно заняться, если больше нечего делать. А как насчет живого выступления? Иногда время - это непозволительная роскошь. Особенно, если вы хотите, чтобы люди продолжали танцевать.
Ну, а если бы вы записали свой код так:
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)
Он делает совершенно то же самое (попробуйте сами). Зато тут есть возможность заменить строку sample_name = :loop_amen
на sample_name = :loop_garzul
, что приведёт к реальным изменениям во многих местах. Вот что даёт нам магия переменных.
Наконец, хороший повод использовать переменные - захватывать результат чего-либо. К примеру, вам может понадобится что-то сделать с продолжительностью сэмпла:
sd = sample_duration(:loop_amen)
Теперь можно вставлять sd
везде, где нам нужна длительность сэмпла :loop_amen
.
Вероятно, ещё более важно то, что переменная разрешает нам сохранить результат вызова play
или sample
:
s = play 50, release: 8
Мы захватили и запомнили s
как переменную, которая позволяет управлять синтом, пока он играет:
s = play 50, release: 8
sleep 2
control s, note: 62
В следующей главе мы рассмотрим подробнее управление синтами.
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_loop
s. In the first live loop every 0.5
s 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.