Итак, вы написали чумовую бас-партию и заводной ритм. Как сыграть обе эти части одновременно? Одно из решений - переплести их вместе вручную - играть небольшую часть баса, затем немного от ударных, потом еще бас… Однако, вскоре становится очень сложно даже думать о том, как разместить это все во времени, особенно если вы начнёте добавлять новые элементы.
Что если бы Sonic Pi мог соединять партии инструментов за вас автоматически? Вообще-то это возможно, для этого вам надо использовать специальную вещь, которая называется поток выполнения.
Дабы не усложнять наш пример, представим, что это и есть крутой ритм и потрясающая бас-партия:
loop do
sample :drum_heavy_kick
sleep 1
end
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
Как мы уже обсуждали, циклы - это черные дыры в программе. Как только начинает выполняться цикл, выйти из него уже нельзя, не нажав кнопку “Остановить”. Как сыграть сразу оба цикла в одно и то же время? Надо сообщить Sonic Pi, что мы хотим выполнять что-то параллельно с остальной частью кода. Вот тут-то нам и придут на помощь потоки выполнения.
in_thread do
loop do
sample :drum_heavy_kick
sleep 1
end
end
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
Обернув первый цикл в do/end блок типа in_thread
, мы заставляем Sonic Pi выполнять содержимое блока do/end точно в то же самое время, когда начинается следующее выражение после блока do/end. В нашем примере таковым является второй цикл. Попробуйте, и вы услышите, как ударные и бас переплетаются вместе!
Теперь, что если мы хотим добавить синт поверх. Что-то типа:
in_thread do
loop do
sample :drum_heavy_kick
sleep 1
end
end
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
loop do
use_synth :zawa
play 52, release: 2.5, phase: 2, amp: 0.5
sleep 2
end
Опять та же самая проблема. Первый цикл воспроизводится вместе со вторым из-за in_thread
. Но третий цикл не достигается. То есть нам нужен ещё один поток:
in_thread do
loop do
sample :drum_heavy_kick
sleep 1
end
end
in_thread do
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
end
loop do
use_synth :zawa
play 52, release: 2.5, phase: 2, amp: 0.5
sleep 2
end
Вас может это удивить, но при нажатии кнопки “Выполнить” также создаётся поток выполнения кода. Вот почему её многократное нажатие наслаивает звуки друг на друга. Запуски автоматически наслаивают звуки, потому что они сами являются потоками.
По мере овладевания секретами мастерства Sonic Pi вы узнаете, что потоки - это самые важные строительные блоки вашей музыки. Не последним их качеством является способность изолировать от других потоков то, что называется текущей установкой. Что это значит? Ну, когда вы переключаете синт при помощи use_synth
, на самом деле вы изменяете синт в текущем потоке - ни в одном другом потоке он не изменится. Давайте посмотрим на это в действии:
play 50
sleep 1
in_thread do
use_synth :tb303
play 50
end
sleep 1
play 50
Обратите внимание, как звук в середине отличается от остальных. Команда use_synth
применилась только к тому потоку, где она была выполнена, а не к окружающему главному потоку выполнения программы.
Когда вы создаёте новый поток, вызывая in_thread
, то он автоматически наследует все текущие настройки из родительского потока. Проверим это:
use_synth :tb303
play 50
sleep 1
in_thread do
play 55
end
Заметили, что вторая нота воспроизводится на :tb303
, несмотря на то, что это происходит в отдельном потоке? Любые регулировки, устанавливаемые различными use_*
функциями, будут вести себя аналогично.
При создании, потоки наследуют все установки от своего родителя, но любые последующие изменения не имеют обратного эффекта.
Наконец, мы можем называть наши потоки:
in_thread(name: :bass) do
loop do
use_synth :prophet
play chord(:e2, :m7).choose, release: 0.6
sleep 0.5
end
end
in_thread(name: :drums) do
loop do
sample :elec_snare
sleep 1
end
end
Посмотрите на панель сообщений, когда этот код выполняется. Видите, как появляются сообщения, в которые включены имена потоков?
[Run 36, Time 4.0, Thread :bass]
|- synth :prophet, {release: 0.6, note: 47}
Последнее, что вам нужно узнать об именованных потоках, это то, что только один поток с определённым именем может выполняться одновременно. Рассмотрим следующий код:
in_thread do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Скопируйте этот фрагмент в пустой буфер редактора и нажмите кнопку “Выполнить”. Нажмите её еще пару раз. Получилась какафония от того, что много повторений амен-брейка смешались вместе. Ладно, нажимайте “Остановить”.
Это то самое поведение, которое нам не раз уже доводилось наблюдать. Если нажимать кнопку “Выполнить”, то новый звук накладывается поверх уже играющего. Поэтому, если у вас есть цикл, и вы нажмёте кнопку “Выполнить” три раза, то получится три слоя циклов, звучащих вместе.
Но ситуация меняется с именованными потоками:
in_thread(name: :amen) do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Теперь попробуте понажимать кнопку “Выполнить” с этим кодом. Вы услышите только один цикл амен-брейк. В сообщениях вы также увидите:
==> Пропуск создания потока: thread, поток с именем :amen уже существует.
Sonic Pi сообщает вам, что поток с именем :amen
уже существует, так что он не создаст ещё одного такого же.
Сейчас такое поведение может не показаться вам таким уж полезным. Но в дальнейшем вы ещё вспомните о нём, когда мы начнем лайвкодинг…