W poprzednim numerze tego poradnika mieliśmy okazję zgłębiać bardzo techniczne tematy związane z systemem randomizacji, który jest fundamentalną częścią Sonic Pi. Dowiedzieliśmy się, w jaki sposób możemy użyć go, aby wynieść możliwość dynamicznej kontroli nad naszym kodem na zupełnie nowy poziom. W tym miesiącu zamierzamy kontynuować naszą techniczną wycieczkę i zwrócimy naszą uwagę ku unikalnemu systemowi tykania dostępnego w Sonic Pi. Pod koniec tego artykułu będziesz tykał po swojemu poprzez rytmy i riffy, aby stać się DJ’em kodującym na żywo.
Podczas tworzenia muzyki często mamy ochotę na zrobienie czegoś innego, w zależności od którego uderzenia to jest. Sonic Pi posiada specjalny system liczenia uderzeń zwany tick
. Daje on precyzyjną kontrolę podczas występowania uderzenia, a także i obsługuje wiele uderzeń mających własne tempa.
Zabawmy się - by przyspieszyć uderzanie, musimy tylko wezwać tick
. Otwórz czysty bufor, by następnie wpisać poniższy fragment kodu i nacisnąć Run:
puts tick #=> 0
Spowoduje to zwrócenie aktualnego bitu: 0
. Zauważ, że nawet gdy naciśniesz kilkukrotnie przycisk Run, to i tak zawsze zostanie zwrócona wartość 0
. Dzieje się tak, ponieważ każde uruchomienie spowoduje, że bit zacznie być liczony od 0. Jednakże tak długo, jak nasza muzyka jest uruchomiona, możemy przechodzić do kolejnych uderzeń tak wiele razy, jak tylko chcemy:
puts tick #=> 0
puts tick #=> 1
puts tick #=> 2
W każdym miejscu, w którym zobaczysz symbol #=>
na końcu danej linii kodu, oznacza to, że linia ta spowoduje zalogowanie tekstu po prawej stronie okna. Na przykład puts foo #=> 0
oznacza, iż kod puts foo
wyświetli 0
w logach w danym punkcie programu.
Widzieliśmy, że tick
anie robi dwie rzeczy. Powiększa ono (dodaje jedno) i powraca do obecnego uderzenia. Czasami chcemy tylko spojrzeć na obecne uderzenia bez zwiększania ich ilości. Do tego zadania wykorzystamy polecenie look
:
puts tick #=> 0
puts tick #=> 1
puts look #=> 1
puts look #=> 1
W tym kawałku kodu cykniemy nasz bit dwa razy, a następnie dwa razy uruchomimy polecenie look
. W logu zobaczymy następujące wartości: 0
, 1
, 1
, 1
. Pierwsze dwa tyknięcia (wywołania polecenia tick
) zwracają 0
, potem 1
tak jak tego oczekujemy, a kolejne dwa polecenia look
zwracają po prostu ostatnią wartość bitu dwa razy, co w tym przypadku oznacza 1
.
Teraz możemy więc zwiększać bit z wykorzystaniem polecenia tick
i sprawdzać go z wykorzystaniem polecenia look
? Co teraz? Potrzebujemy czegoś, przez co moglibyśmy tykać. Sonic Pi wykorzystuje pierścienie do reprezentacji riffów, melodii i rytmów, natomiast system tykania został zaprojektowany z myślą o ścisłej współpracy z nimi. W rzeczywistości pierścienie posiadają swoją własną kropkową wersję polecenia tick
, która robi dwie rzeczy. Po pierwsze, zachowuje się tak jak normalne tykanie i powoduje podniesienie wartości aktualnego bitu. Po drugie, przeszukuje wartości pierścienia, wykorzystując dane tyknięcie bitu jako indeks. Spójrzmy na to:
puts (ring :a, :b, :c).tick #=> :a
Polecenie .tick
to specjalna kropkowa wersja polecenia tick
, która zwraca w tym przypadku pierwszą wartość z pierścienia - :a
. Możemy dostać się do każdej z wartości w pierścieniu przez kilkukrotne wywołanie polecenia .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
Zwróć uwagę na log, a zauważysz, że zostały wypisane kolejno :a
, :b
, :c
, a potem znowu :a
. Zauważ, iż polecenie look
zwraca wartość 3
. Kolejne uruchomienia polecenia .tick
zachowują się dokładnie tak samo jak normalne standardowe wywołania polecenia tick
- zwiększają aktualną wartość dla lokalnego wskaźnika bitu.
Prawdziwą moc zobaczysz, gdy połączysz razem polecenie tick
z pierścieniami (ring
) i żywymi pętlami (live_loop
). Gdy je połączymy, to mamy wszystkie narzędzia, które są potrzebne, aby zbudować i zrozumieć prosty arpeggiator. Potrzebujemy tylko czterech rzeczy:
Te pojęcia można znaleźć w poniższym kodzie:
notes = (ring 57, 62, 55, 59, 64)
live_loop :arp do
use_synth :dpulse
play notes.tick, release: 0.2
sleep 0.125
end
Przyjrzyjmy się uważnie każdej z tych linii. Najpierw definiujemy pierścień z naszymi nutami, które będziemy grać w kółko. Następnie tworzymy żywą pętlę live_loop
o nazwie :arp
kręcącą się dla nas w kółko. Przy każdym kolejnym przebiegu naszej żywej pętli jako aktualny syntezator ustawiamy :dpulse
, a następnie gramy kolejną nutę z naszego pierścienia, korzystając z polecenia .tick
. Pamiętaj, że spowoduje to, iż nasz licznik bitów będzie się zwiększał i każde jego kolejne użycie będzie korzystało z ostatniego bitu jako indeksu do wyciągania nut z naszego pierścienia. Na koniec chcemy poczekać przez jedną ósmą uderzenia zanim rozpoczniemy kolejny przebieg żywej pętli.
Bardzo ważną rzeczą, którą warto wiedzieć, jest to, iż polecenia tick
i są lokalne dla danej pętli live_loop
. Oznacza to, iż każda żywa pętla live_loop
posiada własny niezależny licznik uderzeń. Takie podejście daje o wiele więcej możliwości niż posiadanie jednego globalnego metronomu i bitu. Rzućmy okiem na to, jak to działa w praktyce:
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
Duża przyczyna zamieszania z systemem cykania Sonic Pi jest wtedy, gdy ludzie chcą użyć go na wielu pierścieniach w tym samym 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
Nawet jeśli każda żywa pętla live_loop
posiada swój własny niezależny licznik bitów, to wywołujemy polecenie .tick
dwa razy w ramach tej samej żywej pętli. Ta informacja oznacza, że bit zostanie zwiększony dwa razy przy każdej rundzie. Może to spowodować pewne interesujące polirytmie, ale zazwyczaj nie jest to coś, czego byś oczekiwał. Istnieją dwa rozwiązania tego problemu. Pierwsza opcja to manualne uruchomianie polecenia tick
na początku każdej żywej pętli live_loop
, a następnie korzystanie z polecenia .look
, aby odwołać się do aktualnego uderzenia w każdym przebiegu żywej pętli. Drugie rozwiązanie to przekazanie unikalnej nazwy do każdego wywołania polecenia .tick
, na przykład .tick(:foo)
. Sonic Pi stworzy wtedy i będzie śledził oddzielny licznik bitów dla każdej użytej przez Ciebie z nazw. Tym sposobem masz możliwość pracy z tyloma licznikami uderzeń, z iloma będziesz tylko chciał. Aby dowiedzieć się więcej, zwróć uwagę na sekcję poświęconą nazwanym cyknięciom, która jest dostępna w puncie 9.4 samouczka wbudowanego w Sonic Pi.
Doprowadźmy tę całą wiedzę na temat tick
ania, ring
ów i live_loop
ów razem do ostatecznego przykładu zabawy. Jak przywykliśmy już do tego, nie traktuj tego jako gotowy kawałek - rozpocznij zmianę wielu rzeczy i baw się z nimi. Za pomocą swojej wyobraźni wyczaruj coś, co Cię zachwyci. Do zobaczenia następnym razem…
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