В прошлом месяце в этой серии мы углубились в систему рандомизации, на которой базируется Sonic Pi. Мы исследовали, как мы можем использовать это для детерминированного добавления новых уровней динамического контроля над нашим кодом. В этом месяце мы продолжим наше техническое погружение и обратим внимание на уникальную систему тиков Sonic Pi. К концу этой статьи вы пройдете через ритмы и риффы на пути к тому, чтобы стать лайвкодером.
При создании музыки мы часто хотим делать разные вещи с учетом текущего такта. Sonic Pi имеет специальную систему подсчета тактов, которая называется tick
, чтобы дать вам контроль над тем, какой в точности сейчас такт, и даже поддерживает работу нескольких тактов одновременно с их собственными темпами.
Давайте поиграем - чтобы сдвинуть такт во времени, нам просто нужно вызвать tick
. Откройте свободный буфер, введите следующий текст и нажмите Выполнить:
puts tick #=> 0
Это вернет нам текущий такт: 0
. Обратите внимание, что даже если вы нажмете кнопку Выполнить несколько раз, вам всегда вернется 0
. Это связано с тем, что каждый цикл начинает новый отсчет тактов с 0. Однако, пока цикл все еще активен, мы можем сдвигать такт во времени столько раз, сколько захотим:
puts tick #=> 0
puts tick #=> 1
puts tick #=> 2
Всякий раз, когда вы видите символ # =>
в конце строки кода, это означает, что эта строка будет записывать в журнал текст, расположенный справа от этого символа. Например, put foo # => 0
означает, что код puts foo
, напечатает 0
в журнале.
Мы видели, что tick
делает две вещи. Он увеличивает (+1) и возвращает нам значение текущего такта. Иногда мы просто хотим посмотреть текущий такт, не увеличивая его. Мы можем сделать это с помощью look
:
puts tick #=> 0
puts tick #=> 1
puts look #=> 1
puts look #=> 1
В этом коде мы делаем два хода такта во времени, а затем дважды вызываем look
. В журнале мы увидим следующие значения: 0
, 1
, 1
, 1
. Первые два tick
вернули 0
и 1
, как и ожидалось, затем два look
просто дважды вернули значение последнего такта, которое было равно 1
.
Теперь мы можем сдвигать такт во времени с помощью tick
и узнавать его текущее значение с помощью look
. Что дальше? Нам пригодится кое-что еще. Sonic Pi использует кольца, чтобы представлять через них рифы, мелодии и ритмы, а система тиков была специально разработана для тесного взаимодействия с ними. На самом деле у колец есть собственная версия такой системы, это tick
с добавленной точкой. Он делает две вещи: во-первых, он действует как обычный tick
и увеличивает значение такта. Во-вторых, он ищет значение кольца, используя такт в качестве индекса. Давайте взглянем:
puts (ring :a, :b, :c).tick #=> :a
.tick
- это специальная версия tick
с точкой, которая будет возвращать первое значение кольца : a
. Мы можем получить каждое значение в кольце, вызвав .tick
несколько раз:
puts (ring :a, :b, :c).tick #=> :a
puts (ring :a, :b, :c).tick #=> :b
puts (ring :a, :b, :c).tick #=> :c
puts (ring :a, :b, :c).tick #=> :a
puts look #=> 3
Взгляните в журнал, и вы увидите : a
,: b
, : c
, а затем снова: a
. Обратите внимание, что look
возвращает 3
. Вызовы .tick
действуют так же, как и вызовы привычного tick
- они увеличивают локальный такт во времени.
Настоящая мощь проявляется, когда вы смешиваете tick
с кольцами и live_loop
. В совокупности у нас есть все инструменты, необходимые для создания простого арпеджиатора. Нам нужно всего четыре вещи:
Все эти вещи можно найти в следующем коде:
notes = (ring 57, 62, 55, 59, 64)
live_loop :arp do
use_synth :dpulse
play notes.tick, release: 0.2
sleep 0.125
end
Давайте посмотрим на каждую из этих строк. Сначала мы определяем кольцо нот, которые мы будем играть. Затем мы создаем live_loop
с именем: arp
, который обеспечит нам цикл. Каждый раз проходя цикл live_loop
, мы устанавливаем наш синтезатор в: dpulse
, а затем проигрываем следующую ноту в нашем кольце, используя .tick
. Помните, что это увеличивает наш счетчик тактов и использует последнее значение тактов в качестве индекса в нашем кольце из нот. Наконец, мы ждем восьмой такт, прежде чем снова начать цикл.
Очень важно знать, что tick
связан с live_loop локальными отношениями. Это означает, что каждый live_loop
имеет свой независимый счетчик тактов. Это намного мощнее, чем глобальный метроном. Давайте посмотрим на это в действии:
notes = (ring 57, 62, 55, 59, 64)
with_fx :reverb do
live_loop :arp do
use_synth :dpulse
play notes.tick + 12, release: 0.1
sleep 0.125
end
end
live_loop :arp2 do
use_synth :dsaw
play notes.tick - 12, release: 0.2
sleep 0.75
end
Основная причина путаницы с системой тиков Sonic Pi заключается в том, что люди используют сразу несколько счетчиков наименованных тактов для несколько колец в одном и том же live_loop
:
use_bpm 300
use_synth :blade
live_loop :foo do
play (ring :e1, :e2, :e3).tick
play (scale :e3, :minor_pentatonic).tick
sleep 1
end
Несмотря на то, что каждый live_loop
имеет свой собственный независимый счетчик ударов, мы вызываем .tick
дважды в одном и том же live_loop
. Это означает, что такт будет увеличиваться дважды на каждый шаг времени. Это может привести к некоторым интересным полиритмам, но часто это не то, что вы хотите. Есть два решения этой проблемы. Один из вариантов - вручную вызвать tick
в начале live_loop
, а затем использовать .look
для поиска текущего такта в каждом live_loop
. Второе решение заключается в передаче уникального имени каждому вызову .tick
, например, .tick (: foo)
. Sonic Pi создаст и отследит отдельный счетчик тактов для каждого поименованного тика. Таким образом, вы можете работать с таким количеством тактов, которое вам нужно! См. Раздел 9.4 о поименованных тиках встроенного руководства для получения дополнительной информации.
Давайте соберем все эти знания оtick
, ring
и live_loop
вместе для одного забавного финального примера. Как обычно, не относитесь к этому как к готовому произведению. Начните менять значения, поиграйте с этим и посмотрите, во что вы можете превратить это. Увидимся в следующий раз…
use_bpm 240
notes = (scale :e3, :minor_pentatonic).shuffle
live_loop :foo do
use_synth :blade
with_fx :reverb, reps: 8, room: 1 do
tick
co = (line 70, 130, steps: 32).tick(:cutoff)
play (octs :e3, 3).look, cutoff: co, amp: 2
play notes.look, amp: 4
sleep 1
end
end
live_loop :bar do
tick
sample :bd_ada if (spread 1, 4).look
use_synth :tb303
co = (line 70, 130, steps: 16).look
r = (line 0.1, 0.5, steps: 64).mirror.look
play notes.look, release: r, cutoff: co
sleep 0.5
end