Witaj w Sonic Pi. Mam nadzieję, że jesteś podekscytowany możliwością rozpoczęcia tworzenia zwariowanych dźwięków tak samo, jak ja jestem podekcytowany tym, że mogę Ci to pokazać. To będzie naprawdę świetna zabawa, podczas której nauczysz się wszystkiego o muzyce, syntezie, programowaniu, kompozycji, wydajności i wielu innych rzeczach.
Ale zaraz, zaraz, gdzie moje maniery! Pozwól, że się przedstawię - jestem Sam Aaron - facet, który stworzył Sonic Pi. Możesz znaleźć mnie pod nikiem @samaaron na Twitterze i będzie mi bardzo miło jeśli będę mógł powiedzieć Ci cześć. Być może zainteresuje Cię również moja strona Live Coding Performances, gdzie koduję przed publicznością na żywo używając Sonic Pi.
Jeśli masz jakiekolwiek spostrzeżenia i pomysły dotyczące tego jak można ulepszyć Sonic Pi - będę wdzięczny za ich przekazanie - każda informacja zwrotna jest bardzo pomocna. Nigdy nie wiadomo, być może twój pomysł stanie się kolejną epicką funkcją, która wzbogaci Sonic Pi!
Ten samouczek został podzielony na sekcje, które są pogrupowane wg kategorii. Pomimo, że napisałem go w taki sposób, aby umożliwiał łatwą naukę od początku do końca, to nic nie szkodzi na przeszkodzie abyś abyś po prostu zaczął czytać dowolną z sekcji - taką która wydaje Ci się odpowiednia dla Ciebie. Jeśli uważasz, że czegoś tutaj brakuje daj mi o tym znać, rozważę dołożenie tego do kolejnej wersji.
Jeszcze jedna rzecz na koniec. Oglądanie innych osób kodujących na żywo jest naprawdę bardzo dobrym sposobem nauki. Regularnie nadaję na żywo na moim kanale livecoding.tv/samaaron, możesz więc wpaść, powiedzieć mi cześć i zadać mi dowolne pytanie :-)
Dobra, czas zacząć przygodę…
Jednym z najbardziej ekscytujących aspektów Sonic Pi jest to, że pozwala Ci na pisanie i modyfikację kodu na żywo aby tworzyć muzykę w czasie rzeczywistym, tak samo jak w momencie gdy grasz na żywo na gitarze. Oznacza to, że dzięki odpowiednim ćwiczeniom możesz wykorzytać Sonic Pi do koncertów przed publicznością na żywo.
Zanim przejdziemy do dalszej części samouczka i zaczniemy zgłębiać szczegóły tego w jaki sposób działa Sonic Pi, chciałbym abyś mógł przez chwilę poczuć czym jest Live Coding (kodowanie na żywo). Nie przejmuj się, jeśli nic nie zrozumiesz z tego co zobaczysz i zrobisz za chwilę. Po prostu usiądź wygodnie, zapnij pasy i poczuj radość…
Zacznijmy od skopiowania następującego kawałka kodu do pustego buforu powyżej:
live_loop :flibble do
sample :bd_haus, rate: 1
sleep 0.5
end
Teraz nacisnij przycisk Run
a usłyszysz fajne i szybkie
uderzenia bębna. W każdym momencie możesz zatrzymać dźwięk naciskając
przycisk Stop
. Aczkolwiek nie rób tego jeszcze… Zamiast tego wykonaj
następujące kroki:
sleep
z wartości `0.5’ na
większą, np. 1.Run
OK, to było dosyć łatwe. Dodajmy do naszego miksu coś innego. Powyżej
linii sample :bd_haus
dodaj linijkę sample :ambi_choir, rate: 0.3
. Twój kod
powinien teraz wyglądać następująco:
live_loop :flibble do
sample :ambi_choir, rate: 0.3
sample :bd_haus, rate: 1
sleep 1
end
A teraz czas na zabawę. Zacznij zmieniać liczby - co się stanie gdy użyjesz
dużych wartości, co się stanie gdy użyjesz małych lub ujemnych liczb?
Spróbuj zobaczyć co się stanie gdy zmienisz wartość parametru rate:
dla
sampla :ambi_choir
tylko odrobinę (np. na 0.29). Co się stanie jeśli
wybierzesz naprawdę małą wartość dla parametru sleep
? Zobacz, czy uda Ci się
uruchomić powyższą pętlę tak szybko, że twój komputer zatrzyma się z powodu błedu,
gdyż nie będzie w stanie nadążyć (jeśli to się zdarzy, po prostu wybierz
większa wartość dla parametru sleep
i ponownie naciśnij przycisk Run
).
Spróbuj zrobić komentarz w jednej z linii zawierających sample
dodając na
początku linii znak #
, np.:
live_loop :flibble do
sample :ambi_choir, rate: 0.3
# sample :bd_haus, rate: 1
sleep 1
end
Zauważ, że dodanie znaku #
na początku linii mówi komputerowi aby zignorował
daną linię, dzięki czemu jej nie słyszymy. Taką linijkę nazywamy komentarzem.
W Sonic Pi możemy używać komentarzy do usuwania i dodawania różnych rzeczy do
naszej muzyki.
Na koniec pozwól, że pozostawię Ci do zabawy coś fajnego. Spójrz na kod poniżej
i skopiuj go do wolnego i pustego bufora. Teraz, nie próbuj
zrozumieć nic więcej niż to, że w tym kodzie są dwie pętle (live_loop
). Oznacza to,
że w tym samym czasie dzieją się dwie rzeczy. Teraz, rób to co umiesz najlepiej
- eksperymentuj i baw się próbując zmieniać ten kod. Oto kilka sugestii
co możesz zrobić:
rate:
aby usłyszeć zmianę
w dźwiękach poszczególnych sampli.sleep
dzięki czemu usłyszysz, że obie
pętle mogą kręcić się z różną szybkością.#
) a usłyszysz dźwięk
gitary granej od tyłu.mix:
na wartości pomiędzy 0
(brak dźwięku w naszym utworze) a 1
(brzmienie
o normalnej głośności).Pamiętaj aby wcisnąć przycisk Run
. Dzięki temu usłyszysz zmianę przy kolejnym
przebiegu pętli. Jeśli coś pójdzie nie tak i zacznie się kakofonia nie przejmuj
się tym. Wystarczy, że naciśniesz przycisk Stop
, usuniesz cały kod w buforze,
wkleisz świeżą kopię poniższego kodu i jesteś znowu gotowy do improwizacji. Pamiętaj,
człowiek najszybciej uczy się na błędach…
live_loop :guit do
with_fx :echo, mix: 0.3, phase: 0.25 do
sample :guit_em9, rate: 0.5
end
# sample :guit_em9, rate: -0.5
sleep 8
end
live_loop :boom do
with_fx :reverb, room: 1 do
sample :bd_boom, amp: 10, rate: 1
end
sleep 8
end
Baw się i eksperymentuj tym kawałkiem kodu do momentu, w którym twoja ciekawość sprawi, że zaczniesz zastanawiać się jak to wszystko właściwie działa oraz co innego można jeszcze wyczarować za pomocą Sonic Pi. Jeśli ten moment już nastąpił to jesteś gotów aby przejść do dalszej części samouczka.
Więc na co czekasz…
Sonic Pi ma bardzo prosty interfejs umożliwiający kodowanie muzyki. Poświęćmy chwilę na zapoznanie się z nim.
Różowe przyciski są głównymi kontrolerami uruchamiania i zatrzymywania dźwięków. Jest przycisk Run (Start) umożliwiający uruchomienie kodu znajdującego się w edytorze. Przycisk Stop umożliwia zatrzymanie całego uruchomionego kodu. Przycisk Save (Zapisz) służy do zapisywania kodu wpisanego w edytorze do zewnętrznego pliku tekstowego. Przycisk Record (Nagrywaj) umożliwia nagrywanie (w formacie WAV) aktualnie odtwarzanego dźwięku.
Pomarańczowe przyciski pozwalają Ci manipulować edytorem kodu. Przyciski Size + (Zwiększ rozmiar) i Size - (Zmniejsz rozmiar) umożliwiają powiększanie i zmniejszanie rozmiaru tekstu (czcionki). Przycisk Align (Wyrównaj) pozwala na uporządkowanie wyglądu twojego kodu tak aby wyglądał bardziej profesjonalnie (poprawia wcięcia).
Niebieskie przyciski dają Ci dostęp do informacji, pomocy i ustawień. Naciśnięcie przycisku Info spowoduje otworzenie dodatkowego okna, które zawiera informacje dotyczące Sonic Pi - zespół podstawowy, historia, współtwórcy oraz społeczność. Przycisk Help (Pomoc) otwiera i zamyka system pomocy (F), który właśnie czytasz. Przycisk Prefs (Ustawienia) otwiera i zamyka panel ustawień, który pozwala Ci na kontrolę kilku podstawowych ustawień aplikacji.
Obszar, w którym będziesz pisał swój kod oraz komponował/wykonywał muzykę. Jest to prosty edytor tekstowy, w którym możesz pisać kod, kasować go, wycinać, wklejać, itd. Myśl o nim jako o bardzo prostej wersji edytora Word czy Google Docs. Edytor automatycznie koloruje słowa bazując na ich znaczeniu w kodzie. Może się to wydawać dziwne na początku, ale bardzo szybko zauważysz, że jest to bardzo przydatne. Na przykład - wiesz, że coś dany tekst jest liczbą ponieważ ma kolor niebieski.
Sonic Pi wspiera wiele ustawień, które są dostępne za pomocą przycisku Preferencje (prefs), który znajduje się tuż za przyciskami Informacje i Pomoc. Naciśnięcie go spowoduje pokazanie panelu ustawień, który zawiera wiele opcji, które można zmianiać. Przykłady takich ustawień to: wymuszenie trybu mono, odwrócone stereo, włączanie i wyłączanie panelu logowania a także suwak głośności selektor dźwięku, które są dostępne tylko na platformie Raspberry Pi.
Kiedy uruchamiasz swój kod, informacja o tym co program aktualnie robi będzie wyświetlana w panelu podglądu logów. Domyślnie, zobaczysz wiadomość pojawiającą się dla każdego dźwięku, który stworzysz wraz z dokładnym czasem kiedy ten dźwięk został uruchomiony. Jest to bardzo przydatne do debugowania twojego kodu i zrozumienia co twój kod robi.
I na samym końcu została jedna z najważniejszych cześci interfejsu Sonic Pi - system pomocy, który pojawia się w dolnym oknie. Może on być włączony i wyłączony za pomocą naciśnięcia niebieskiego przycisku Help (Pomoc). System pomocy zawiera pomoc oraz informacje dotyczące wszystkich aspektów związanych z Sonic Pi włączając w to ten samouczek, listę dostępnych syntezatorów, sample (próbki dźwięków), przykłady, efekty (FX) oraz listę wszystkich funkcji umożliwiających tworzenie muzyki jakie Sonic Pi udostępnia.
Sonic Pi zachęca abyś poprzez zabawę i eksperymentowanie uczył się
jednocześnie programowania i tworzenia muzyki. Najważniejsze jest
abyś się dobrze bawił. W międzyczasie, nawet się nie obejrzysz,
a zauważysz, że nauczyłeś się programować, komponowac i “grać” na żywo.
Skoro jesteśmy przy tym temacie, pozwól że dam Ci jedną radę, coś czego nauczyłem się przez lata kodowania muzyki na żywo - błędów nie ma, są tylko nowe możliwości. To jest coś, co często słyszy się w kontekście muzyki z gatunku jazz, ale sprawdza się równie dobrze w odniesieniu do kodowania na żywo. Nie ma znaczenia jak dużo masz doświadczenia - niezależnie czy jesteś całkowitym żółtodziobem, czy zaprawionym w bojach Algorejwerem, na pewno zdarzy Ci się napisać kod, którego wynik po uruchomieniu będzie całkowicie nieprzewidziany. Może to brzmieć niesamowicie fascynująco - i w tym przypadku faktycznie tak jest. Można też, patrząc na to z innej strony, stwierdzić że brzmi to kiepsko i jest całkowicie nie na miejscu. Pamiętaj, to że to się wydarzyło jest całkowicie nieistotne - to co jest naprawdę ważne to co z tym zrobisz dalej. Weź dźwięk, manipuluj nim i przekształć go w coś niesamowitego. Tłum będzie szalał.
Zawsze kiedy się uczysz, najbardziej kuszące jest to, że chcesz robić niesamowite rzeczy tu i teraz. Postaraj się jednak powstrzymać te myśli i spójrz na nie jako odległy cel, który uda się się osiągnąć później. Zamiast tego, pomyśl o najprostszej rzeczy jaką potrafisz napisać, która będzie fajna, a jednocześnie sprawi, że będziesz czuł satysfakcję z tego, że jest to mały krok w kierunku wizji, która wypełnia twoją głowę. Gdy już będziesz widział w swojej głowie ten mały krok, spróbuj go wykonać - napisz kawałek kodu, uruchom go, baw się nim i zobacz jakie nowe pomysły przyjdą Ci do głowy. Zobaczysz, niedługo będziesz tak pochłonięty zabawą, że zaczniesz robić prawdziwe postępy.
Nie zapomnij tylko podzielić się swoją pracą z innymi!
OK, tyle tytułem wstępu - pora zająć się muzyką.
W tym rozdziale poznasz podstawy uruchamiania i manipulowania
syntezatorami. Syntezator to taka fajna nazwa dla czegoś co wytwarza
dźwięk. Normalnie syntezatory są dość skomplikowane w użyciu. Zwłaszcza
syntezatory analogowe zawierają wiele różnych połączeń, kabelków
i modułów. Natomiast Sonic Pi oddaje w twoje ręce wiele z tych możliwości
w bardzo prostej i przystępnej formie.
Nie daj się zwieść prostocie interfejsu, który prezentuje Sonic Pi. Masz możliwość zanurzyć się bardzo głęboko. Jeśli to twoja dziłka, to do twojej dyspozycji są bardzo wyszukane możliwości manipulacji dźwiękiem. Zapnij pasy…
Spójrz na poniższy kod:
play 70
To jest miejsce, w którym wszystko się zaczyna. Śmiało, skopiuj powyższy kod i wklej go do edytora kodu na górze aplikacji (duża biała przestrzeń tuż pod przyciskiem Run). Kiedy już to zrobisz, naciśnij przycisk Run…
A teraz naciśnij przycisk jescze raz. I jeszcze raz. I jeszcze raz…
Łał, szaleństwo. Jestem pewien, że możesz tak przez cały dzień. Ale poczekaj. Zanim zatracisz się w pętli nieskończonych bipów, spróbuj zmienić liczbę:
play 75
Słyszysz różnicę? OK, teraz spróbuj mniejszej liczby:
play 60
Zauważ, że mniejsze liczby powodują bipy o niższym tonie a większe liczby
powodują bipy o wyższym tonie. Tak samo jak na pianinie - klawisze
znajdujące się po lewej stronie instrumentu grają nuty o niższym brzmieniu
a klawisze znajdujące się po prawej stronie grają nuty o wyższym brzmieniu.
I faktycznie tak jest, powyższe liczby odpowiadają nutom na pianinie.
Kod play 47
znaczy nic innego jak - zagraj nutę znajdującą się pod
47 klawiszem na pianinie. Oznacza to, że kod play 48
to dźwięk o jedną
nutę wyżej (następny klawisz po prawej). Tak się składa, że nuta C w 4-tej
oktawie odpowiada liczbie 60. Smiało, spróbuj zagrać ten dźwięk:
play 60
.
Nie przejmuj się jeśli nic z tego nie rozumiesz - ja również nie rozumiałem gdy zaczynałem na poczatku tak jak Ty teraz. Wszystko co się teraz liczy to to, że wiesz, że małe liczby powodują bipy o niskim brzmieniu a duże liczby powodują bipy o wyższym brzmieniu.
Zagranie nuty jest całkiem fajne, ale zagranie kilku jednocześnie może być jeszcze fajniejsze. Spróbuj:
play 72
play 75
play 79
Super! Zauważ, że kiedy napiszesz kilka razy komendę play
, wszytkie dźwięki
zagrają w tym samym momencie. Teraz spróbuj sam - które liczby brzmią razem
dobrze? Które brzmią okropnie? Eksperymentuj, odkrywaj i przekonaj się
na własnej skórze.
Granie nut i akordów jest fajne - a co powiesz na zagranie melodii?
Co jeśli chciałbyś zagrać jedną nutę po drugiej ale nie w tym samym
czasie? Nic prostszego, wystarczy że odczekasz kawałek czasu pomiędzy
poszczególnymi nutami używając polecenia sleep
:
play 72
sleep 1
play 75
sleep 1
play 79
Cudownie, właśnie stworzyłeś małe arpeggio. No dobrze, ale co oznacza liczba
1
w poleceniu sleep 1
? Oznacza to długość trwania odstępu pomiędzy
nutami. Zasadniczo oznacza to odstęp o długości jednego uderzenia,
ale póki co możesz myśleć o tym jako o przeczekaniu 1-ną sekundę.
Co powinniśmy w takim razie zrobić jeśli chcielibyśmy trochę przyśpieszyć
nasze arpeggio? Jedyne co musimy zrobić to użyć “krótszych” wartości
dla polecenia sleep. Weźmy na przykład połowę, czyli wartość 0.5
:
play 72
sleep 0.5
play 75
sleep 0.5
play 79
Zauważ, że arpeggio gra teraz szybciej. Teraz twoja kolej, pobaw się tym kawałkiem kodu zmieniając czasy na takie jak uważasz, tak jak wcześniej spróbuj użyć przy tym różnych nut.
Jest jedna rzecz, którą szczególnie warto wypróbować. Spróbuj użyć nut, które
są “pomiędzy” całymi nutami, np. play 52.3
, play 52.63
. Nie ma absolutnie
żadnej konieczności, abyś kurczowo trzymał się standardowych pełnych nut.
Kombinuj z różnymi wartościami i baw się dobrze.
Osoby, które aktualnie znają już trochę notację muzyczną (nie przejmuj
się jeśli Ty nie znasz - nie musisz, żeby móc się dobrze bawić) być może
będą preferować pisanie melodii przy wykorzystaniu standardowych nazw nut,
np. C lub F# (Fis) zamiast używania liczb. Sonic Pi pozwala na to. Nic
nie stoi na przeszkodzie abyś napisał i uruchomił taki kod:
play :C
sleep 0.5
play :D
sleep 0.5
play :E
Pamiętaj tylko, że by umieścić dwukropek :
tuż przed nazwą twojej nuty.
Spowoduje to, że zmieni ona kolor na różowy. Możesz również zdefiniować
oktawę umieszczająć odpowiednią liczbę tuż po nazwie nuty:
play :C3
sleep 0.5
play :D3
sleep 0.5
play :E4
Jeśli chcesz sprawić aby nuta brzmiała o pół tonu wyżej (uzyskanie dźwięku
fis), dodaj s
tuż za twoją nutą, np. play :Fs3
. Analogicznie - jeśli
chcesz obniżyć dźwięk nuty o połowę (uzyskanie dźwięku mol), dodaj na końcu
twojej nuty b
, np. play :Eb3
A teraz czas poszaleć. Możesz zacząć bawić się w tworzenie twoich własnych melodii.
Tak samo jak masz kontrolę nad tym, którą nutę zagrać lub który sampel uruchomić, Sonic Pi udostępnia cały asortyment parametrów umożliwiających kształtowanie i kontrolowanie dźwięków. Wiele z tych parametrów zostanie omówionych w tej części samouczka. Ponadto w systemie pomocy jest obszerna dokumentacja, która szczegółowo opisuje każdy z nich. Tymczasem, przedstawimy dwa najbardziej przydatne: aplituda (amp) i balans (pan). Na początek, zobaczmy czym są te parametry w rzeczywistości.
Syntezatory obecne w Sonic Pi wspierają pojęcie parametrów. Parametry to
sterowniki, które po przekazaniu do syntezatora play
modyfikują
i kontrolują różne aspekty odtwarzanego dźwięku. Każdy z syntezatorów
posiada własny zestaw parametrów pozwalających na subtelny tuning
produkowanego dźwięku. Jednakże, istnieje zestaw parametrów, które
są wspólne dla wielu dźwięków. Przykładami takich parametrów są np. amp:
czy parametry kopertowe (zostaną omówione w dalszej części samouczka).
Parametry składają się z dwóch podstawowych części: nazwy (kontrolowanego
parametru) oraz wartości (na jaką chcemy ustawić dany parametr).
Na przykład możesz mieć parametr, który nazywa się ser:
i chciałbyś
ustawić jego wartość na 1
.
Parametry są przekazywane do polecenia play
w następujący sposób:
najpierw dodajemy tuż za poleceniem przecinek ,
po nim wpisujemy nazwę
parametru, np. amp:
(nie zapomnij o dwukropku :
) i na końcu po spacji
podajemy wartość parametru. Oto przykład:
play 50, ser: 1
(Zauważ, że ser:
nie jest poprawnym parametrem, używamy go tutaj tylko
jako przykład).
Możesz przekazać wiele parametrów oddzielając je przecinkami:
play 50, ser: 1, fasolki: 0.5
Kolejnośc parametrów nie ma znaczenia, poniższy kod też zadziała:
play 50, fasolki: 0.5, ser: 1
Parametry, które nie są rozpoznawane przez dany syntezator są po prostu
ignorowane (tak jak ser
i fasolki
jak w powyższym przypadku, które są
po prostu śmiesznymi nazwami parametrów!).
Jeśli niechcący zdarzy Ci się dwa razy użyć parametru o tej samej nazwie
to wygrywa ostani. Na przykład - ostateczna wartość dla parametru fasolki:
wyniesie 2 (a nie 0.5):
play 50, fasolki: 0.5, ser: 3, jajka: 0.1, fasolki: 2
Wiele rzeczy w Sonic Pi akceptuje parametry, warto więc poświęcić parę
chwil aby nauczyć się jak ich używać, dzięki temu będziesz ustawiony!
Spróbujmy pobawić się trochę z naszym pierwszym parametrem: amp:
.
Amplituda jest w komputerową miarą reprezentującą głośność dźwięku. Wysoka aplituda powoduję głośny dźwięk a niska aplituda powoduje cichyy dźwięk. Tak samo jak Sonic Pi używa liczb do reprezentacji czasu i nut, tak samo używa ich do reprezentacji wielkości amplitudy. Amplituda o wartości 0 to cisza (nie słyszysz nic). Natomiast amplituda o wartości 1 ustawienie głośności na normalny poziom. Możesz nawet podkręcić na 2, 10, 100. Musisz jednak mieć na uwadze to, że gdy całkowita amplituda wszystkich dźwięków stanie się zbyt wysoka, Sonic Pi wykorzysta narzędzie zwane kompresorem, żeby zdusić je tak, żeby mieć pewność, że dźwięk nie jest zbyt głośny dla twoich uszu. Może to spowodować, że dźwięk stanie się nieczysty i dziwny. Staraj się więc używać niskch aplitud, np. w zakresie od 0 do 0.5, żeby uniknąć kompresji.
Aby zmienić amplitudę dźwięku użyj parametru amp:
. Na przykład,
aby zagrać z głośnością na poziomie 0.5 użyj takiego kawałka kodu:
play 60, amp: 0.5
Aby zagrać z podwójną głośnością (amplitudą) przekaż parametr 2:
play 60, amp: 2
Parametr amp:
modyfikuje tylko wywołanie tego polecenia play
, z którym
jest powiązany. Tak więc w poniższym przykładzie pierwsze wywołanie polecenia
play zostanie zagrane z głośnością na poziomie równym połowie normalnej
głośności, natomiast drugie polecenie zostanie zagrane z głośnością
domyślną (1):
play 60, amp: 0.5
sleep 0.5
play 65
Nic nie stoi na przeszkodzie, abyś użył różnych wartości parametru amp:
dla różnych wywołań polecenia play:
play 50, amp: 0.1
sleep 0.25
play 55, amp: 0.2
sleep 0.25
play 57, amp: 0.4
sleep 0.25
play 62, amp: 1
Kolejnym fajnym parametrem z którego można korzystać jest pan:
. Kontroluje
on balans dźwięku w stereo. Balansowanie dźwiękiem w lewą stronę oznacza,
że będziesz go słyszał tylko w lewym głośniku (słuchawce). Z kolei
balansowanie dźwiękiem na lewą strone oznacza, że będziesz go słyszał tylko
w prawym głośniku. W naszym przypadku używamy wartości -1 do reprezentacji
dźwięku, którego balans został w pełni przesunięty na lewą stronę, 0 aby
dla balansu po środku oraz 1 dla dźwięku po prawej stronie w polu stereo.
Naturalnie nic nie stoi na przeszkodzie abyśmy używali dowolnej wartości
znajdującej się pomiędzy -1 i 1 w celu dokładnej kontroli “pozycji” naszego
dźwięku.
Zagrajmy bip z lewego głośnika:
play 60, pan: -1
A teraz zagrajmy go z prawego głośnika:
play 60, pan: 1
I na końcu zagrajmy z powrotem z obu głośników (domyślna pozycja):
play 60, pan: 0
Teraz kolej na Ciebie. Spróbuj samodzielnie pobawić się zmieniając amplitudę (amp:) i balans (pan:) twoich dźwięków!
Do tej pory bawiliśmy się całkiem nieźle robiąc wiele fajnych bipów. Jest jednak bardzo prawdopodobne, że podstawowy dźwięk bip zaczyna Cię już powoli nudzić. Czy to jest wszystko co Sonic Pi ma do zaoferowania? Na pewno jest dużo więcej możliwości w kodowaniu na żywo niż tylko granie bipów? Owszem, są inne możliwości i to jest rozdział, w którym poznasz fascynującą paletę dźwięków, jakie Sonic Pi ma do zaoferowania.
Sonic Pi posiada szeroki wachlarz instrumentów, które nazywa syntezatorami. Zważywszy na to, że sample (próbki) reprezentują nagrane dźwięki, syntezatory mają możliwość generowania nowych dźwięków, które są zależne od tego jak je kontrolujesz (w tym samouczku dowiesz się o tym później). Syntezatory Sonic Pi są bardzo potężne i ekspresyjne. Będziesz miał dużo przyjemności podczas poznawania i zabawy nimi. Na początku jednak nauczmy się jak wybierać aktualnie grający syntezator.
Fajnym dźwiękiem jest sawdźwięk piły - spróbujmy:
use_synth :saw
play 38
sleep 0.25
play 50
sleep 0.25
play 62
sleep 0.25
A teraz spróbujmy innego dźwięku - syntezatora prophet:
use_synth :prophet
play 38
sleep 0.25
play 50
sleep 0.25
play 62
sleep 0.25
Co powiesz na połączenie dwóch dźwięków. Jeden po drugim:
use_synth :saw
play 38
sleep 0.25
play 50
sleep 0.25
use_synth :prophet
play 57
sleep 0.25
A teraz w tym samym czasie:
use_synth :tb303
play 38
sleep 0.25
use_synth :dsaw
play 50
sleep 0.25
use_synth :prophet
play 57
sleep 0.25
Zauważ, że polecenie use_synth
wywiera wpływ tylko na kolejne wywołania
poleceń play
. Pomyśl o tym poleceniu jako o dużym przełączniku - kolejne
wywołania polecenia play
będą odtwarzać dowolny syntezator, który jest
w danym momencie wskazany. Możesz zmienić aktualny syntezator na inny
używając przełącznika use_synth
.
Aby zobaczyć jakie syntezatory Sonic Pi ma dla Ciebie do zabawy zerknij na opcję Synth w lewym dolnym rogu (obok Fx). Znajdziesz tam ponad 20 do wyboru. Oto kilka moich ulubionych:
:prophet
:dsaw
:fm
:tb303
:pulse
Tera pobaw się zmieniając syntezatory w trakcie odtwarzania twojej muzyki. Próbuj bawić się łącząc ze sobą różne dźwięki aby tworzyć nowe brzmienia jak również używająć różnych syntezatorów w różnych sekcjach twojego utworu.
W poprzedniej sekcji, zobaczyliśmy w jaki sposób możemy używać komendy
sleep
aby kontrolować, kiedy dźwięki zaczynają grać. Nie wiemy jednak
jeszcze w jaki sposób kontrolować długość trwania naszych dźwięków.
Aby móc korzystać z prostej ale jakże potężnej możliwości kontrolowania długości trwania naszych dźwięków, Sonic Pi udostępnia pojęcie obwiedni dla amplitudy ADSR (czym jest ADSR dowiesz się za chwilę w kolejnych sekcjach). Obwiednia amplitudy udostępnia dwa przydatne aspekty kontroli:
Długość trwania mówi o tym jak długo słychać dany dźwięk. Im długość trwania jest
większa tym dłużej słyszysz dźwięk. Wszystkie dźwięki w Sonic Pi posiadają
kontrolowalną obwiednię amplitudy a całkowita długości trwania tej obwiedni
to długość trwania dźwięku. Dlatego też kontrolując obwiednię kontrolujesz
Długość trwania.
Obwiednia ADSR kontroluje nie tylko długość trwania, lecz pozwala Ci również na precyzyjną kontrolę amplitudy dźwięku. Wszystkie dźwięki zaczynają się i kończą ciszą. Pomiędzy tymi ciszami jest moment podczas, którego słychać dźwięk. Obwiednie pozwalają Ci przesuwać i ustawiać amplitudę tej części, w której słychać dźwięk. Jest to analogiczne do sytuacji, w której powiedziałbyś komuś kiedy powinien zwiększać i zmieniejszać głośność dla jakiegoś utworu muzycznego. Na przykład - możesz poprosić kogoś “zacznij od ciszy, potem powoli zwiększaj poziom głośności, utrzymaj go na tym poziomie przez chwilę po czym szybko wycisz”. Sonic Pi pozwala Ci na zaprogramowanie tego za pomocą obwiedni.
Jak widzieliśmy w poprzedniej sekcji samouczka, amplituda o wartości 0 to cisza, natomiast amplituda o wartości 1 to głośność normalna.
Domyślnie wszystkie syntezatory posiadają czas zanikania amplitudy równy 1. Oznacza to,
że trwa to 1 uderzenie przed zakończeniem (domyślnie będzie to 1 sekunda). Możemy
zmieniać czas trwania tego zanikania za pomocą przekazania do naszego polecenia play
parametru release:
. Na przykład, aby sprawić by syntezator grał przez 2 uderzenia, dla parametru
release:
ustawiamy wartość 2
:
play 60, release: 2
Możemy też sprawić aby syntezator brzmiał przez bardzo krótką chwilę. Wystarczy użyć bardzo małej wartości dla czasu zanikania amplitudy:
play 60, release: 0.2
W takim razie czym jest długość zanikania amplitudy? Jesto to czas, który jest potrzebny aby dźwięk z maksymalnej amplitudy (zazwyczaj jest to wartość 1) zmniejszył się do amplitudy równej zero. Jest to tak zwana faza zanikania (ang. release phase) i jest to przejście liniowe (tzn. obniżające się w linii prostej). Poniższy diagram ilustruje to przejście:
Pionowa linia po lewej stronie diagramu pokazuje, że dźwięk zaczyna się
z amplitudą równą 0, ale osiąga pełną głośność momentalnie (jest to faza
ataku, omówimy ją za chwilę). Gdy pełna głośność zostanie już osiągnięta
następuje zmniejszanie głośności po linii prostej aż do wartości zero
i zajmuje to tyle czasu ile zostało ustawione za pomocą parametru zanikania
release:
. Im czas zanikania jest dłuższy, tym dłuższy będzie czas znikania
(wyciszania) dźwięku syntezatora.
Możesz zatem zmieniać długość trwania twoich dźwięków zmieniając czas zanikania. Spróbuj teraz pobawić się dodając do twojej muzyki różne czasy zanikania.
Domyślnie, faza ataku jest równa 0 dla wszystkich sytezatorów. Oznacza to, że przejście od amplitudy 0 do aplitudy 1 jest natychmiastowe. Nadaje to syntezatorowi początkowy perkusyjny dźwięk. Mimo to, może się zdarzyć, że będziesz chciał aby twój dźwięk zaczynał brzmieć stopniowo. Aby to zrobić wystarczy wykorzystać parametr ‘attack:’. Spróbuj użyć takiego przejścia w kilku różnych dźwiękach:
play 60, attack: 2
sleep 3
play 65, attack: 0.5
Zauważ, że możesz używać wielu parametrów w tym samym czasie. Na przykład dla krótkiej fazy ataku (attack) i długiej fazy zanikania (release) spróbuj coś takiego:
play 60, attack: 0.7, release: 4
Taka obwiednia z krótką fazą ataku i długą zanikania została zilustrowana na poniższym rysunku:
Oczywiście, możesz zmieniać parametry według swoich upodobań. Spróbuj teraz długiej fazy ataku i krótkiej fazy zanikania:
play 60, attack: 4, release: 0.7
Na koniec możesz też spróbować ustawić obie wartości dla czasu narastania i zanikania na małe wartości aby uzyskać krótsze dźwięki.
play 60, attack: 0.5, release: 0.5
Oprócz możliwości ustawiania czasu narastania i czasu zanikania dźwięku, możesz również określić czas podtrzymania. Jest to moment, w którym dźwięk jest utrzymany na pełnej aplitudzie pomiędzy fazami narastania i zanikania.
play 60, attack: 0.3, sustain: 1, release: 1
Czas podtrzymania jest bardzo przydatny dla istotnych dźwięków, którym
chciałbyś dać pełną obecność w miksie tuż przed wejściem opcjonalnej fazy
zanikania. Oczywiście, całkowicie dopuszczalne jest ustawienie obu
parametrów, zarówno fazy ataku jak i fazy zanikania na wartość 0
a także użycie tylko fazy podtrzymania żeby nie mieć absolutnie żadnej
fazy wejścia lub wyjścia dla danego dźwięku. Bądź jednak ostrożny i wiedz,
że ustawienie fazy zanikania na 0 może spowodować dziwne kliki w dźwiękach
i bardzo często zamiast tego dużo lepiej jest użyć bardzo małej wartości,
np. 0.2.
Na sam koniec, dla momentów gdzie potrzebujesz dodatkowego poziomu kontroli,
masz możliwość skorzystania z fazy opadania. Jest to taki moment w obwiedni dźwięku,
który znajduje się pomiędzy fazą ataku a fazą podtrzymania (wybrzmiewania)
i określa moment, w którym amplituda spada z poziomu ataku attack_level
do poziomu
podtrzymania sustain_level
. Domyślnym argumentem dla fazy opadania jest 0, natomiast
poziomy ataku i podtrzymania posiadają domyślną wartość 1. W związku z tym musisz
określić dla nich czas opadania aby uzyskać jakikolwiek efekt:
play 60, attack: 0.1, attack_level: 1, decay: 0.2, sustain_level: 0.4, sustain: 1, release: 0.5
Podsumowując, obwiednie ADSR w Sonic Pi posiadają następujące fazy:
attack_level
.attack_level
)
do poziomu podtrzymania (sustain_level
).sustain_level
.sustain_level
) do zera.Należy tu zwrócić uwagę na fakt, że całkowita długość trwania dźwięku jest sumą czasu trwania każdej z faz występujących w danym dźwięku. Dlatego też poniższy dźwięk będzie miał długość 0.5 + 1 + 2 + 0.5 = 4 uderzeń:
play 60, attack: 0.5, decay: 1, sustain_level: 0.4, sustain: 2, release: 0.5
Tyle teorii, teraz spróbuj sam pobawić się dodając obwiednie do twoich dźwięków…
Innym świetnym sposobem tworzenia twojej muzyki jest wykorzystanie nagranych wcześniej dźwięków. W wielkiej tradycji hip hopu, takie nagrane wcześniej dźwięki nazywamy samplami. Innymi słowy jeśli weźmiesz mikrofon i nagrasz subtelny dźwięk kropel deszczu uderzających o szybę, to właśnie nagrałeś swój pierwszy sampel.
Sonic Pi pozwala Ci robić wiele fajnych rzeczy z samplami. Poza tym, że na pokładzie jest ponad 90 publicznie dostępnych sampli, które są gotowe abyś zaczął się nimi bawić, to masz też możliwość manipulowania nimi oraz korzystania z twoich własnych. Bierzmy się do zabawy…
Granie prostych dźwięków to dopiero początek. Coś co jest dużo fajniejsze to wyzwalanie nagranych sampli. Spróbuj:
sample :ambi_lunar_land
Sonic Pi posiada wiele sampli, których możesz używać do tworzenia swojej muzyki. Możesz używać ich tak samo jak używasz polecenia `play’. Aby zagrać kilka sampli i nut po prostu napisz je jeden po drugim:
play 36
play 48
sample :ambi_lunar_land
sample :ambi_drone
Jeśli chcesz rozdzielić je w czasie wykorzystaj polecenie sleep
:
sample :ambi_lunar_land
sleep 1
play 48
sleep 0.5
play 36
sample :ambi_drone
sleep 1
play 36
Zauważ, że Sonic Pi nie czeka aż dźwięk przestanie grać zanim zacznie
grać kolejny dźwięk. Polecenie sleep
tylko opisuje rozdzielenie
wyzwalania poszczególnych dźwięków. Takie podejście pozwala na łatwe
nawarstwianie kolejnych dźwięków, które razem mogą tworzyć ciekawe
efekty nakładania się na siebie. W dalszej części tego samouczka
pokażemy w jaki sposób można kontrolować długość trwania dźwięków
przy użyciu obwiedni.
Są dwa sposoby na odkrywanie wachlarza sampli dostępnych w Sonic Pi. Po pierwsze możesz korzystać z systemu pomocy. Kliknij na sekcję Sample w lewym dolnym menu, wybierz kategorię a zobaczysz listę dostępnych dźwięków.
Alternatywnie możesz skorzystać z systemu autopodpowiadania. Wystarczy,
że wpiszesz początek wybranej grupy sampli, np. sample :ambi_
a twoim
oczom ukaże się rozwijana lista sampli do wyboru. Wypróbuj następujące
prefiksy kategorii:
:ambi_
:bass_
:elec_
:perc_
:guit_
:drum_
:misc_
:bd_
A teraz zacznij wplatać sample w twoje własne kompozycje!
Tak samo jak w przypadku Syntezatorów, możemy łatwo kontrolować
nasze dźwięki za pomocą parametrów. Sample wspierają takie sam
mechanizm parametrów. Przyjrzyjmy się ponownie naszym kolegom
amp:
i pan:
.
Możesz zmieniać głośność sampli dokładnie w taki sam sposób jakiego używałeś dla syntezatorów:
sample :ambi_lunar_land, amp: 0.5
Możemy również używać parametru pan:
dla sampli. Na przykład,
oto jak możemy zagrać breakbeat amen aby brzmiał tylko w lewym
głośniku, a potem w połowie czasu, żeby zaczął grać też w prawym
glośniku:
sample :loop_amen, pan: -1
sleep 0.877
sample :loop_amen, pan: 1
Zauważ, że liczba 0.877 jest połową czasu trwania pętli loop_amen
wyrażoną w sekundach.
Na koniec należy zauważyć, że jeśli ustawisz pewne wartości domyślne
syntezatorów za pomocą parametru use_synth_defaults
(zostanie on
omówiony później), to będą one ignorowane przez polecenie sample
.
Teraz, gdy już umiemy grać za pomocą różnorodnych syntezatorów i sampli, przyszedł czas aby nauczyć się w jaki sposób można modyfikować syntezatory i sample aby sprawić by muzyka była jeszcze bardziej unikalna i interesująca. Na początek poznajmy możliwość rozciągania (stretch) i ściskania (squash) sampli.
Sample to nagrane dźwięki, które są przechowywane niczym liczby, które mówią o tym jak poruszyć stożek głośnika by zreprodukować dany dźwięk. Stożek głośnika może poruszać się do środka i na zewnątrz, tak samo liczby muszą jedynie przedstawiać jak daleko do środka lub na zewnątrz powinien znajdować się stożek głośnika w danym momencie. Aby móć wiernie odwzorować nagrany dźwięk sampla zazwyczaj potrzeba do tego wiele tysięcy liczb na sekundę! Sonic Pi bierze tę listę liczb i zasila nimi głośniki z odpowiednią prędkością tak aby głośniki twojego komputera poruszały się do przodu i do tyłu w taki właśnie sposób aby zreprodukować dany dźwięk. Mimo to, całkiem fajnie jest zmieniać prędkość z jaką te liczby są przekazywane do głośnika aby w ten sposób zmieniać brzmienie.
Pobawmy się jednym z dźwięków z
gatunku ambient:
:ambi_choir
. Aby zagrać go w domyślnym tempie, możesz przekazać
parametr rate:
do polecenia sample:
sample :ambi_choir, rate: 1
Takie poleceni sprawia, że sampel zostanie zagrany w normalnym tempie
(1), więc póki co nic specjalnego się nie dzieje. Nic jednak nie stoi
nam na przeszkodzie abyśmy zmienili tę liczbę na coś innego. Co
powiesz na wartośc 0.5
:
sample :ambi_choir, rate: 0.5
Łał! Co się tutaj dzieje? Otóż, dwie rzeczy. Po pierwsze, odtworzenie naszego naszego sampla zajmuje drugie tyle czasu. Po drugie, dźwięk jest niższy o oktawę. Przyjrzyjmy się tym dwóm tematom nieco bardziej szczegółowo.
Sampel, który jest bardzo fajny do rozciągania i kompresji to Amen beakbit. Przy normalnym tempie, możemy wyobrazić sobie wrzucenie go do utworu drum ‘n’ bass:
sample :loop_amen
Jednak gdy zmienimy tempo możemy bardzo szybko zmienić gatunek. Spróbuj połowy prędkości aby stworzyć oldschool’owy hip hop:
sample :loop_amen, rate: 0.5
Jeśli przyśpieszymy, wejdziemy na terytorium jungle:
sample :loop_amen, rate: 1.5
A teraz nasz ulubiony finalny trik - zobaczmy co się stanie jeśli użyjemy ujemnego tempa:
sample :loop_amen, rate: -1
Łoł! To jest odtwarzane od tyłu! A teraz spróbuj sam pokombinować z różnymi samplami ustawiając je na różne tempa. Spróbuj bardzo szybkich temp. Spróbuj niewiarygodnie niskich temp. Sprawdź jak różne i interesujące dźwięki możesz wyprodukować.
Przydatnym sposobem myślenia o samplach jest myślenie o nich jak o sprężynkach. Z tempem (szybkością) odtwarzania jest tak jak ze ściskaniem i rozciąganiem sprężyny. Zagranie sampla w tempie równym 2 spowoduje, że ściśniesz sprężynę do połowy jej normalnej długości. Dlatego też zagranie takiego sampla zajmie o połowę mniej czasu. Jeśli zagrasz sampel w tempie równym połowe normalnego, to wtedy *rozciągasz sprężynę tak, że podwaja swoją długość. W takim przypadku zagranie całego sampla zajmie dwa razy więcej czasu. Im bardziej ściśniesz sprężynę (wyższe tempo), tym stanie się krótsza. Analogicznie, im bardziej rozciągniesz (niższe tempo), tym będzie dłuższa.
Ściskanie sprężyny zwiększa jej gęstość (liczba zwojów na cm) - jest to podobne do tego gdy sampel brzmi na wyższym poziomie (pitch). Rozciąganie zmniejsza gęstość i jest podobne to dźwięku posiadającego niższy poziom (pitch).
(Ta sekcja jest udostępniona dla tych osób, które są zainteresowane szczegółami. Jeśli nie jesteś, możesz ją po prostu pominąć…)
Jak zauważyliśmy już powyżej, sampel jest reprezentowany przez wielką, długą listę liczb, które defuniują to w jakiej pozycji powinien znajdować się głośnik w danym momencie czasu. Możemy wziąć te liczby i wykorzystać je do narysowania wykresu graficznego, który mógłby wyglądać bardzo podobnie do tego:
Być może widziałeś już podobne obrazki. Nazywa się ją przebiegiem fali sampla. Jest to nic innego jak tylko wykres prezentujący liczby. Zazwyczaj przebieg fali takiej jak ta będzie miał 44100 punktów z danymi na sekundę(jest związane z twierdzeniem Kotelnikowa-Shanona). Więc jeśli sampel trwa przez 2 sekundy, to przebieg fali będzie reprezentowany przez 88200 liczb które przekażemy do głośnika z prędkością 44100 punktów na sekundę. Odtworzenie tego powinno zatem zajęć tylko 1 sekundę. Możemy spróbować również odtworzyć go w tempie o połowę mniejszym, co dałoby wartość 22050 punktów na sekundę i odtworzenie zajęłoby 4 sekundy.
Na czas trwania sampli ma wpływ szybkość odtwarzania:
Możemy przedstawić to za pomocą następującego wzoru:
nowy_czas_trwania_sampla = (1 / tempo) * czas_trwania_sampla
Zmiana szybkości odtwarzania wpływa również na wysokość tonu (pitch) sampla. Częstotliwość lub wysokość tonu widoczna na fali dźwięku jest determinowana przez to jak szybko się ona zmienia w górę i w dół. Nasze mózgi w jakiś sposób zmieniają szybkie ruchy głośnika na wysokie nuty oraz wolne ruchy głośników na niskie nuty. To jest właśnie przyczyną tego, że czasami możesz nawet zobaczyć ruch dużego głośnika basowego gdy wydaje on z siebie super niski bas - w zasadzie to porusza się on wtedy znacznie wolniej w tę i z powrotem niż wtedy gdy głośnik produkuje wyższe dźwięki.
Jeśli weźmie się falę dźwięku i ściśnie się to wtedy będzie się ona poruszać w górę i w dół więcej razy na sekundę. Spowoduje to, że dany dźwięk będzie miał wyższy ton. Oznacza to, że podwojenie ruchów w górę i w dół (oscylacji) zwiększa częstotliwość dwukrotnie. Podsumowując, zagranie twojego sampla z podwójną prędkością spowoduje, że częstotliwość, którą usłyszysz będzie dwa razy wyższa. Analogicznie, obniżenie tempa o połowę spowoduje, że częstotliwość będzie też niższa o połowę. Inne wartości tempa będą będą oddziaływać na częstotliwość odpowiednio.
Korzystając z obwiedni ADSR możliwa jest również modyfikacja czasu trwania
oraz amplitudy sampli. Jednakże działanie jest w tym przypadku trochę
inne niż w przypadku syntezatorów. Obwiednie sampli pozwalają Ci tylko
na zmniejszanie amplitudy oraz czasu trwania sampla - natomiast nigdy
nie jest możliwe zwiększanie wartości tych parametrów. Sampel przestanie
grać gdy się skończy lub gdy zakończy się obwiednia - obojętne co skończy
się szybciej. Jeśli więc użyjesz bardzo długiego parametru release:
,
to nie spowoduje to, że wydłuży się czas odtwarzania sampla.
Wróćmy do naszego sprawdzonego kolegi Amen Break:
sample :loop_amen
Bez podawania żadnych parametrów słyszymy całego sampla na pełnej
głośności. Jeśli chcemy aby dźwięk pojawiał się stopniowo przez
1 sekundę możemy użyć parametru attack:
:
sample :loop_amen, attack: 1
Jeśli chcemy aby wejście trwało krócej wystarczy, że użyjemy krótszej wartości dla fazy ataku:
sample :loop_amen, attack: 0.3
Miejsce, w kórym zachowanie obwiedni ADSR dla sampli różni się od obwiedni dla syntezatorów jest wartość parametru sustain. Domyśnie w obwiedni syntezatorów, parametr fazy podtrzymania domyślnie otrzymuje wartość 0, o ile nie zmienimy jej ręcznie. W przypadku sampli, faza podtrzymania domyślnie otrzymuje wartość automagiczną - czas potrzebny na odtworzenie pozostałej części sampla. Jest to właśnie przyczyną dla której słyszymy całego sampla kiedy nie ustawimy żadnej wartości dla fazy podtrzymania. Jeśli domyślne wartości dla parametrów ataku, opadania, podtrzymania i zanikania były ustawione na 0 nigdy nie usłyszelibyśmy nawet jednego piknięcia. Sonic Pi sam oblicza jak długi jest czas trwania sampla, obcina fazy ataku, opadania oraz zanikania i używa wyniku jako czasu dla fazy podtrzymania. Jeśli wartości podane dla ataku, opadania lub zanikania po zsumowaniu dają wartość większą od czasu trwania sampla, to faza podtrzymania otrzymuje po prostu wartość 0.
Aby to zbadać, przyjrzyjmy się bardziej szczegółowo naszej pętli Amen Loop. Jeśli zapytamy Sonic Pi o to jak długo trwa ten sampel:
print sample_duration :loop_amen
To wyświetli liczbę 1.753310657596372
i jest ona długością sampla wyrażoną
w sekundach. Dla wygody zaogrąglijmy ją tutaj do wartości 1.75
. Teraz,
jeśli ustawimy fazę zanikania (release) na wartość 0.75
to wydarzy się
coś niespodziewanego:
sample :loop_amen, release: 0.75
Pierwsza sekunda sampla zostanie zagrana z pełną amplitudą po czym zacznie stopniowo zanikać przez okres 0.75 sekundy. To jest właśnie auto sustain w akcji (automatyczne ustawianie czasu trwania dla fazy podtrzymania). Domyślnie, faza zanikania (release) zawsze działa (jest ustawiana) od końca sampla. Jeśli nasz sample trwałby 10.75 sekundy, to najpierw pierwszye 10 sekund zostałoby zagrane z pełną amplitudą, po czym zaczęłoby się stopniowe zanikanie (fade out), trwające przez 0.75s.
Zapamiętaj: domyślnie, faza zanikania (release:
) wchodzi pod koniec
sampla.
Możemy używać jednocześnie parametrów dla ustawiania fazy ataku attack:
oraz fazy zanikania release:
z wykorzystaniem automatycznego ustawiania
fazy podtrzymania (sustain) aby użyć stopniowego pojawienia się i stopniowego
zanikania przez cały czas trwania sampla:
sample :loop_amen, attack: 0.75, release: 0.75
Jako, że pełny czas trwania naszego sampla to 1.75s, a nasze fazy ataku i zanikania dają w sumie 1.5 s, to czas trwania fazy podtrzymania (sustain) automagicznie ustawia się na wartość 0.25s. Pozwala nam to na łatwe tworzenie łagodnych wejść i wyjść w samplach.
Możemy bardzo łatwo przywrócić normalne zachowanie ADSR znane z syntezatorów
poprze manualne ustawienie wartości parametru sustain:
na wartość 0:
We can easily get back to our normal synth ADSR behaviour by manually
setting sustain:
to a value such as 0:
sample :loop_amen, sustain: 0, release: 0.75
Teraz nasz sampel zostanie zagrany tylko przez 0.75 sekundy w sumie.
Używająć domyślnych wartości dla parametrów ataku attack:
i opadania
decay:
na poziomie 0, sampel przeskakuje bezpośrednio do pełnej amplitudy,
przechodzi w fazę podtrzymania na 0s po czym zanika z powrotem do
amplitudy równej zero przy czym czas zanikania to 0.75s.
Możemy wykorzystać to zachowania aby uzyskać dobry efekt sprawiając, że
sample trwające dłużej będą krótsze, bardziej perkusyjne. Przyjrzyjmy
się samplowi :drum_cymbal_open
:
sample :drum_cymbal_open
Słyszysz dźwięk talerza, który brzmi przez chwilę. Możemy jednak skorzystać z naszej obwiedni aby sprawić by ten dźwięk stał się bardziej perkusyjny:
sample :drum_cymbal_open, attack: 0.01, sustain: 0, release: 0.1
Następnie możesz spróbować zasymulować uderzenie w talerz i stłumienie go poprzez zwiększenie wartości parametru podtrzymania:
sample :drum_cymbal_open, attack: 0.01, sustain: 0.3, release: 0.1
A teraz spróbuj się pobawić nakładająć obwiednie na sample. Próbuj zmieniać wartości różnych parametrów obwiedni aby otrzymać ciekawe rezultaty.
Ta sekcja sfinalizuje nasze odkrywanie odtwarzacza sampli dostępnego w Sonic Pi. Zróbym szybkie podsumowanie. Do tej pory wiemy już w jaki sposób uruchamiać sample:
sample :loop_amen
Następnie dowiedzieliśmy się w jaki sposób można zmieniać parametry sampli, np. w jaki sposób możemy zagrac wybraną próbke w tempie równym połowie normalnego:
sample :loop_amen, rate: 0.5
Kolejną rzeczą jakiej się nauczyliśmy była umiejętność stopniowego wchodzenia sampla (spróbujmy zrobić to dla sampla zagranego w połowie jego normalnego tempa):
sample :loop_amen, rate: 0.5, attack: 1
Dowiedzieliśmy się również, że za pomocą podania konkretnych krótkich
wartości dla parametrów podtrzymania sustain:
oraz ataku możemy
uzyskać perkusyjne brzmienie:
sample :loop_amen, rate: 2, attack: 0.01, sustain: 0, release: 0.35
Jednakże, czy nie było by fajnie gdybyśmy nie musieli zawsze zaczynać odtwarzania sampla od jego początku? Czy nie było by fajnie gdybyśmy nie musieli też zawsze kończyć odtwarzania sampla dopiero w momencie jego końca?
Możliwe jest wybranie bezwzględnego momentu startu, od którego uruchomimy sampel za pomocą podania liczby o wielkości od 0 do 1, gdzie 0 to początek sampla, 1 oznacza koniec sampla, a 0.5 to połowa sampla. Spróbujmy zagrać tylko drugą połowę sampla amen break:
sample :loop_amen, start: 0.5
A teraz spróbujmy zagrać ostatnią ćwiartke sampla:
sample :loop_amen, start: 0.75
Analogicznie, jest również możliwy wybór bezwzględnego momentu końca odtwarzania sampla za pomocą wartości pomiędzy 0 a 1. Spróbujmy skończyć sampel amen break w połowie czasu:
sample :loop_amen, finish: 0.5
Nic nam nie stoi na przeszkodzie, abyśmy połączyli oba powyższe parametry aby zagrać wybrane kawałki z danego sampla. Co powiesz na wycięcie tylko małego kawałka ze środka:
sample :loop_amen, start: 0.4, finish: 0.6
Co się stanie jeśli ustawimy moment startu tak aby znajdował się po momencie końca?
sample :loop_amen, start: 0.6, finish: 0.4
Świetnie! Wybrany kawałek jest odtwarzany od końca!
Możemy teraz połączyć tę nową możliwość odtwarzania wybrancyh części dźwięku
z naszym startym dobrym znajomym parametrem tempa rate:
. Na przykład, możemy
zagrać bardzo mały kawałek ze środka sampla amen break w bardzo wolnym tempie:
sample :loop_amen, start: 0.5, finish: 0.7, rate: 0.2
Na sam koniec, możemy połączyć wszytkie powyższe możliwości z naszą obwiednią ADSR aby wyprodukować bardzo ciekawe rezultaty:
sample :loop_amen, start: 0.5, finish: 0.8, rate: -0.2, attack: 0.3, release: 1
A teraz idź i spróbuj pobawić się zmieniając sample wykorzystując cały ten kram…
Podczas gdy wbudowane sample pomogą Ci zacząć dość szybko, być może chciałbyś poeksperymentować z innymi nagraniami, które posiadasz w swojej bibliotece muzycznej. Sonic Pi całkowicie to wspiera. Zanim jednak pokażemy jak można tego dokonać, przeprowadźmy krótką dyskusję dotyczącą przenośności twoich utworów.
Kiedy komponujesz twoje utwory opierając się tylko i wyłącznie na wbudowanych syntezatorach i samplach, kod jest jedyną rzeczą, która jest niezbędna aby wiernie odtworzyć twoją muzykę. Pomyśl o tym przez chwilę - to jest niesamowite! Prosty kawałek tekstu, który możesz wysłać pocztą lub trzymać jako Gist przedstawia wszystko czego potrzebujesz aby odtworzyć twoje kawałki. Takie podejście sprawia, że dzielenie się tą muzyką z twoimi znajomymi jest naprawdę proste, gdyż jedyne co muszą zrobić to zdobyć kod.
Jednakże, gdy zaczniesz używać swoich własnych sampli, stracisz tę przenośność. Stanie się tak dlatego, że do odtworzenia twojej muzyki inni ludzie będą potrzebować nie tylko twojego kodu, będą potrzebować również twoich sampli. Takie podejście ogranicza innymi możliwość manipulowania, zmieniania i eksperymentowania z twoją pracą. Oczywiście nie powinno to powstrzymać Cię od używania własnych sampli, to tylko coś co musisz wziąć pod uwagę, gdy się na to zdecydujesz.
W jaki sposób możesz więc zagrać dowolny plik WAV lub AIFF, który
znajduje się na twoim komputerze? Jedyne co musisz zrobic to przekazać
ścieżkę do tego pliku do polecenia sample
:
sample "/Users/sam/Desktop/my-sound.wav"
Sonic Pi automatycznie załaduję i zagra wybrany sampel. Możesz również
przekazać wszystkie standardowe parametry, które do tej pory przekazywałeś
do polecenia sample
:
sample "/Users/sam/Desktop/my-sound.wav", rate: 0.5, amp: 0.3
Note: this section of the tutorial covers the advanced topic of working with large directories of your own samples. This will be the case if you’ve downloaded or bought your own sample packs and wish to use them within Sonic Pi.
Feel free to skip this if you’re happy working with the built-in samples.
When working with large folders of external samples it can be cumbersome to have to type the whole path every time to trigger an individual sample.
For example, say you have the following folder on your machine:
/path/to/my/samples/
When we look inside that folder we find the following samples:
100_A#_melody1.wav
100_A#_melody2.wav
100_A#_melody3.wav
120_A#_melody4.wav
120_Bb_guit1.wav
120_Bb_piano1.wav
Typically in order to play the piano sample we can use the full path:
sample "/path/to/my/samples/120_Bb_piano1.wav"
If we want to then play the guitar sample we can use its full path too:
sample "/path/to/my/samples/120_Bb_guit.wav"
However, both of these calls to sample requires us to know the names of the samples within our directory. What if we just want to listen to each sample in turn quickly?
If we want to play the first sample in a directory we just need to pass
the directory’s name to sample
and the index 0
as follows:
sample "/path/to/my/samples/", 0
We can even make a shortcut to our directory path using a variable:
samps = "/path/to/my/samples/"
sample samps, 0
Now, if we want to play the second sample in our directory, we just need to add 1 to our index:
samps = "/path/to/my/samples/"
sample samps, 1
Notice that we no longer need to know the names of the samples in the directory - we just need to know the directory itself (or have a shortcut to it). If we ask for an index which is larger than the number of samples, it simply wraps round just like Rings. Therefore, whatever number we use we’re guaranteed to get one of the samples in that directory.
Ususally indexing is enough, but sometimes we need more power to sort and organise our samples. Luckily many sample packs add useful information in the filenames. Let’s take another look at the sample file names in our directory:
100_A#_melody1.wav
100_A#_melody2.wav
100_A#_melody3.wav
120_A#_melody4.wav
120_Bb_guit1.wav
120_Bb_piano1.wav
Notice that in these filenames we have quite a bit of information. Firstly, we have the BPM of the sample (beats per minute) at the start. So, the piano sample is at 120 BPM and our first three melodies are at 100 BPM. Also, our sample names containtain the key. So the guitar sample is in Bb and the melodies are in A#. This information is very useful for mixing in these samples with our other code. For example, we know we can only play the piano sample with code that’s in 120 BPM and in the key of Bb.
It turns out that we can use this particular naming convention of our
sample sets in the code to help us filter out the ones we want. For
example, if we’re working at 120 BPM, we can filter down to all the
samples that contain the string "120"
with the following:
samps = "/path/to/my/samples/"
sample samps, "120"
This will play us the first match. If we want the second match we just need to use the index:
samps = "/path/to/my/samples/"
sample samps, "120", 1
We can even use multiple filters at the same time. For example, if we want a sample that’s filename contains both the substring “120” and “A#” we can find it easily with the following code:
samps = "/path/to/my/samples/"
sample samps, "120", "A#"
Finally, we’re still free to add our usual opts to the call to sample
:
samps = "/path/to/my/samples/"
sample samps, "120", "Bb", 1, lpf: 70, amp: 2
The sample filter pre-arg system understands two types of information: sources and filters. Sources are information used to create the list of potential candidates. A source can take two forms:
The sample
fn will first gather all sources and use them to create a
large list of candidates. This list is constructed by first adding all
valid paths and then by adding all the valid .flac
, .aif
, .aiff
,
.wav
, .wave
files contained within the directories.
For example, take a look at the following code:
samps = "/path/to/my/samples/"
samps2 = "/path/to/my/samples2/"
path = "/path/to/my/samples3/foo.wav"
sample samps, samps2, path, 0
Here, we’re combining the contents of the samples within two directories
and adding a specific sample. If "/path/to/my/samples/"
contained 3
samples and "/path/to/my/samples2/"
contained 12, we’d have 16
potential samples to index and filter (3 + 12 + 1).
By default, only the sample files within a directory are gathered into
the candidate list. Sometimes you might have number of nested folders of
samples you wish to search and filter within. You can therefore do a
recursive search for all samples within all subfolders of a particular
folder by adding **
to the end of the path:
samps = "/path/to/nested/samples/**"
sample samps, 0
Take care though as searching through a very large set of folders may take a long time. However, the contents of all folder sources are cached, so the delay will only happen the first time.
Once you have a list of candidates you may use the following filtering types to further reduce the selection:
"foo"
Strings will filter on substring occurrance within file name (minus directory path and extension)./fo[oO]/
Regular Expressions will filter on pattern matching of file name (minus directory path and extension).:foo
- Keywords will filter candidates on whether the keyword is a direct match of the filename (minus directory path and extension).lambda{ "foo" }
- Procs with no arguments will be automatically called and their result be treated as a source or filter.lambda{|a| ... }
- Procs with one argument will be treated as a candidate filter or genrator function. It will be passed the list of current candidates and must return a new list of candidates (a list of valid paths to sample files).1
- Numbers will select the candidate with that index (wrapping round like a ring if necessary).Finally, you may use lists wherever you may place a source or
filter. The list will be automatically flattened and the contents be
treated as regular sources and filters. Therefore the following calls to
sample
are semantically equivalent:
sample "/path/to/dir", "100", "C#"
sample ["/path/to/dir", "100", "C#"]
sample "/path/to/dir", ["100", "C#"]
sample ["/path/to/dir", ["100", ["C#"]]]
This was an advanced section for people that need real power to manipulate and use sample packs. If most of this section didn’t make too much sense, don’t worry. It’s likely you don’t need any of this functionality just yet. However, you’ll know when you do need it and you can come back and re-read this when you start working with large directories of samples.
Wspaniałym sposobem aby dodać odrobine intrygi do twojej muzyki, jest wykorzystanie paru losowych liczb. Sonic Pi posiada parę fajnych funkcjonalności, które umożliwiają dodanie przypadkowości do twojej muzyki. Zanim jednak zaczniemy musimy nauczyć się jednej szokującej prawdy: w Soni Pi losowość nie jest tak naprawdę losowa. Co to do diaska znaczy? No cóż, zobaczmy.
Bardzo przydatną funkcją do generowania liczb losowych jest rrand
, która
zwróca losową wartość pomiędzy dwoma liczbami - minimum min i maksimum
max. (rrand
to skrót od angielskiego ranged random - liczba losowa
z określonego przedziału). Spróbujmy zagrać losową nutę:
play rrand(50, 100)
Oh, została zagrana losowa nuta. Zagrana nuta to 77.4407
. Fajna liczba
losowa pomiędzy 50 a 100. Zaraz zaraz, czy przypadkiem nie zgadłem dokładnej
wartości losowej nuty, którą przed chwilą otrzymałeś? Dzieje się tu coś
podejrzanego. Spróbuj uruchomić kod ponownie. I co? Znowu została wybrana
liczba 77.4407
? Toć to nie jest liczba losowa!
Odpowiedzią jest fakt, że tak naprawde wynik nie jest naprawdę losowy, jest pseudo losowy. Sonic Pi wygeneruje dla Ciebie liczby, które wyglądają jak losowe w sposób powtarzalny. Takie podejście jest bardzo przydatne jeśli chcesz być pewny, że muzyka, którą tworzysz na swojej maszynie będzie brzmieć identycznie na każdym innym komputerze - nawet jeśli używasz w swojej kompozycji pewnej losowości.
Oczywiście, jeśli w danym utworze, ta ‘losowo wybrana liczba’ będzie
za każdym razem miała wartość 77.4407
, to nie będzie to zbyt interesujące.
Jednakże jest inaczej. Spróbój poniższego kawałka kodu:
loop do
play rrand(50, 100)
sleep 0.5
end
Tak! Wreszcie uzyskaliśmy losowe dźwieki. W ramach danego uruchomienia kolejnych wywołań funkcji losującej zostaną zwrócone wartości losowe. Jendakże, kolejne uruchomienie spowoduje wyprodukowanie dokładnie takiej samej sekwencji losowych wartości i brzmienie będzie takie same jak przy pierwszym uruchomieniu. To tak jakby cały kod Sonic Pi cofnął się w czasie dokładnie do tego samego punktu za każdym razem gdy zostaje wciśnięty przycisk Run (Uruchom). Toć to Dzien Świstaka dla syntezy muzycznej!
Piękną ilustracją randomizacji w akcji jest przykład nawiedzonych dzwonów
(Haunted Bells), który zapętla sampel :perc_bell
nadając mu przy tym
losowe tempo (parametr rate) oraz czas przerwy (polecenie sleep) pomiędzy
poszczególnymi dźwiękami dzwonów:
loop do
sample :perc_bell, rate: (rrand 0.125, 1.5)
sleep rrand(0.2, 2)
end
Innym ciekawym przykładem losowości jest modyfikacja odcięcia
syntezatora losowo. Świetnym syntezatorem, na którym możemy tego
spróbować jest syntezator emulujący :tb303
:
use_synth :tb303
loop do
play 50, release: 0.1, cutoff: rrand(60, 120)
sleep 0.125
end
A co, jeśli nie podoba Ci się ta konkretna sekwencja liczb losowych,
które generuje Sonic Pi? Nic nie stoi na przeszkodzie abyś wybrał
inny punkt rozpoczęcia za pomocą polecenia use_random_seed
. Domyślną
wartością dla funkcji odsiewu (seed?) jest 0, wystarczy więc wybrać
inną wartość aby uzyskać inne wartości losowe.
Weźmy pod uwagę poniższy przykład:
5.times do
play rrand(50, 100)
sleep 0.5
end
Za każdym razem gdy uruchomisz powyższy kod, usłyszysz sekwencję tych samych 5 nut. Aby uzyskać inną sekwencję wystarczy zmienić wartość odsiewu (?seed):
use_random_seed 40
5.times do
play rrand(50, 100)
sleep 0.5
end
Spowoduje to wygenerowanie sekwencji 5 innych losowych nut. Zmieniając parametr odsiewu (?seed) i słuchając wyników jakie to spowodowało, możesz w końcu znaleźć taka sekwencję, która Ci się spodoba - a gdy wtedy podzielisz się nią z innymi, usłyszą oni dokładnie to samo co ty.
A teraz przyjrzyjmy się kilku innym, przydatnym funkcją generującym wartości losowe.
Bardzo popularną rzeczą jest wybór losowego obiektu z listy znanych nam
obiektów. Na przykład, mogę chcieć zagrać jedną z następujących nut:
60, 65 lub 72. Żeby to osiągnąć mogę użyć funkcji choose
, która pozwala
mi na wybór jednego obiektu z istniejącej listy obiektów. Po pierwsze,
muszę wsadzić moje liczby (nuty) do listy. Aby tego dokonać wystarczy,
że opakujemy nasze liczby w nawiasy kwadratowe i oddzielimy każdą z nich
przecinkiem: [60, 65, 72]
. Następnie wystarczy przekazać je jako
parametr do polecenia choose
:
choose([60, 65, 72])
Posłuchajmy zatem jak to brzmi:
loop do
play choose([60, 65, 72])
sleep 1
end
Widzieliśmy już w akcji funkcję rrand
, ale spróbujmy uruchomić ją raz jeszcze.
Zwraca ona losową liczbę z podanego przedziału, który jest w tym wypadku przedziałem
otwartym. Oznacza to, że żadna z podanych liczb jako wartości minimalna i maksymalna
nigdy nie zostanie zwrócona - zawsze coś pomiędzy tymi dwoma liczbami. Zwrócona
liczba będzie liczbą zmiennoprzecinkową - oznacza to, że nie jest to liczba całkowita
tylko ułamek. Oto kilka przykładów liczb zmiennoprzecinkowych zwracanych przez
polecenie rrand(20,110)
:
Sporadycznie może się zdarzyć, że będziesz potrzebował liczbę losową, która jest
liczbą całkowitą, a nie zmiennoprzecinkową. W tym przypadku z pomocą przychodzi
polecenie rrand_i
. Polecenie to działa bardzo podobnie do rrand
, z tą jednak różnicą,
że może zwracać teź liczbę minimalną i maksymalną z podanego przedziału jako potencjalna
wartość losowa (co oznacza, że podawany przedział jest domknięty, a nie otwarty). Poniżej
przykłady liczb losowych, które mogą zostać zwrócone przez polecenie rrand_i(20,110)
:
Te polecenie zwraca losową liczbę zmiennoprzecinkową pomiędzy 0 (przedział domknięty)
a maksymalną liczbą, którą podajesz jako parametr (przedział otwarty). Domyślnie
(jeśli nie podamy parametru) zostanie zwrócona liczba z przedziału od 0 do 1. Warto
zauważyć, że polecenie to może być bardzo użyteczne do ustawiania losowego poziomu
poziomu amplitudy amp:
loop do
play 60, amp: rand
sleep 0.25
end
Przy tym poleceniu mamy relację podobną jak w przypadku poleceń rrand_i
i rrand
. Polecenie rand_i
zwraca losową liczbę całkowitą z zakresu
od 0 do maksymalnej wartości, którą podasz.
Czasami chciałbyś zasymulować rzut kostką - jest to specyficzny przypadek
dla polecenia rrand_i
, dla którego najniższa liczba to zawsze 1. Uruchomienie
polecenia rzut kostką dice
wymaga abyś podał liczbę oczek na kostce. Standardowa
kostka ma 6 oczek, więc polecenie dice(6)
zachowa się bardzo podobnie -
każde uruchomienie będzie zwracać jedną z wartości 1, 2, 3, 4, 5 lub 6. Jednakże,
tak damo jak w grach RPG (role-playing games), może się zdarzyć, że będziesz
potrzebować kostki o 4 oczkach, albo o 12, albo o 20 - być może będziesz
potrzebować nawet kostki która będzie miała 120 oczek!
Na koniec może się zdarzyć, że będziesz chciał zasymulować wyrzucenie
najlepszego wyniku dla danej kostki, np. 6 oczek dla standardowej kostki.
Polecenie one_in
zwraca prawdę (true) z prawdopodobieństwem, że zostało
wyrzucone jedno oczko. Idąc dalej polecenie one_in(6)
zwróci prawdę (true)
z prawdopodobieństwem 1 do 6-sciu lub fałsz. Wartości prawda (true)
i fałsz (false) są bardzo przydatne przy korzystaniu z polecenie warunkowego
if
, które omówimy w jednej z kolejnych sekcji tego tutoriala.
A teraz, spróbuj trochę zagmatwać twój kod i muzykę odrobiną losowości!
Teraz, kiedy nauczyłeś się podstaw tworzenia dźwięków z wykorzystaniem
poleceń play
i sample
oraz utworzyłeś swoje pierwsze proste melodie
i rytmy korzystając z polecenia sleep
do tworzenia odstępów między
poszczególnymi dźwiękami, zastanawiasz się zapewne co takiego może Ci
jeszcze zaoferować świat kodowania…
Dobrze, jesteś przygotowany na ekscytującą ucztę. Okazuje się, że proste konstrukcje programistyczne, takie jak pętle (looping), instrukcje warunkowe, funkcje mogą stać się dla Ciebie niesamowicie potężnymi narzędziami do wyrażenia twoich muzycznych pomysłów.
Zakasaj rękawy i zabieramy się do podstaw…
Struktura, którą będzie Ci dane zobaczyć w Sonic Pi niezwykle często to blok kodu. Bloki pozwalają nam na robienie wielu przydatnych rzeczy z dużymi kawałkami kodu. Na przykład, w przypadku parametrów syntezatorów i sampli byliśmy w stanie zmieniać coś co działo się w obrębie jednej linii. Jednakże, czasami chcielibyśmy zrobić coś znaczącego dla kilku linii kodu. Na przykład, chcielibyśmy zapętlić coś, potem nałożyć na to efekt reverb, tak, żeby uruchomił się tylko przy 1-wszym przebiegu pętli z wszystkich 5-ciu, itd. Przyjrzyj się poniższemu kawałkowi kodu:
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
Abyśmy mogli zrobić coś z kawałkiem kodu, musimy powiedzieć Sonic Pi gdzie
zaczyna się i kończy dany blok kodu. Aby określić początek takiego bloku
używamy polecenia do, natomiast polecenie end służy do określenia gdzie
dany blok się kończy. Na przykład:
do
do
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
end
Jednakże, nie jest to jeszcze całość i nie zadziała (spróbuj uruchomić
powyższy kod a zobaczysz błąd) poneważ nie powiedzieliśmy jeszcze Sonic Pi
co chcielibyśmy zrobić z naszym blokiem zawartym pomiędzy poleceniami
do/end. Aby powiedzieć Sonic Pi co chcemy zrobić z tym kawałkiem musimy
napisać kawałek specjalnego kodu przed poleceniem do
. Zobaczysz jeszcze
wiele takich różnych specjalnych kawałków kodu w kolejnych sekcjach tego
samouczka. Na razie, najważniejsze jest abyś wiedział, że umieszczenie twojego
kodu pomiędzy polecenia do
i end
mówi Sonic Pi, że chciałbyś zrobić
z tym kawałkiem kodu coś specjalnego.
Do tej pory spędziliśmy sporo czasu patrząc na różne dźwięki, które
możesz zrobić użyciem bloków play
i sample
. Nauczyliśmy się również
w jaki sposób uruchamiać te dźwięki na przestrzeni czasu używając
polecenia sleep
.
Jak zapewne już zauważyłeś, przy korzystaniu z tych kilku podstawowych bloków możesz mieć całą masę frajdy. Jednakże, kiedy zaczniesz wykorzystywać możliwości kodu do porządkowania twojej muzyki i kompozycji, twoim oczom ukaże się zupełnie nowy wymiar możliwości i zabawy. W najbliższych kilku sekcjach poznasz kilka z tych potężnych narzędzi. Na początek zaczniemy od iteracji i pętli.
Czy zdarzyło ci się napisać kawalek kodu, który chciałbyś powtórzyć kilka razy? Przypuścmy, że napisałeś coś w tym stylu:
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
Co jeśli chciałeś aby powyższy kawałek powtórzył się 3 razy? Najprostsze co mógłbyś zrobić to po prostu skopiować i wkleić powyższy kawałek kodu trzy razy:
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
Zobacz ile kodu! Pomyśl, co trzeba by było zrobić, gdybys chciał zmienić
sampel na :elec_plip
? Musiałbyś wtedy znaleźć w kodzie wszystkie miejca
gdzie użyłeś oryginalnego sampla :elec_blup
a zmienić je ręcznie.
Ale to i tak nic, pomyśl co by było gdybyś chciał powtórzyć oryginalny
blok kodu 50 albo 1000 razy? Dopiero teraz byłoby to dużo kodu, bardzo
dużo kodu, który musiałbyś zmienić, gdybyć chciał dokonać tak prostej
zmiany jak przełączenie się na inny sampel.
W rzeczywistości, powtórzenie kodu powinno być tak proste jak powiedzenie
zrób to trzy razy. Dobrze, w dużym stopniu tak jest. Pamiętasz naszego
starego dobrego znajomego - blok kodu? Możemy użyć go aby zaznaczyć
miejsce początku i końca kodu, który chcielibyśmy powtórzyć 3 razy. Następnie
wystarczy, że użyjemy specjalnego kawałka kodu 3.times
(3.razy).
Zamiast więc napisać zrób to trzy razy, napiszemy 3.times do
- to nie
jest zbyt trudne. Pamiętaj tylko aby napisać polecenie end
na końcu
bloku kodu, który zamierzać powtórzyć:
3.times do
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
sleep 0.25
end
Przyznasz, że takie podejście jest dużo bardziej eleganckie niż kopiowanie i wklejanie! Możemy użyć tego specjalnego polecenia to tworzenia wielu fajnych powtarzających się struktur:
4.times do
play 50
sleep 0.5
end
8.times do
play 55, release: 0.2
sleep 0.25
end
4.times do
play 50
sleep 0.5
end
Możemy umieszczać jedne iteracje wewnątrz innych iteracji to utworzenia ciekawych wzorców. Na przykład:
4.times do
sample :drum_heavy_kick
2.times do
sample :elec_blip2, rate: 2
sleep 0.25
end
sample :elec_snare
4.times do
sample :drum_tom_mid_soft
sleep 0.125
end
end
Jeśli chcesz powtórzyć coś wiele razy, możesz złapać się na tym, że będziesz
podawał naprawdę bardzo duże liczby jako parametr iteracji, na przykład
1000.times do
. W takim przypadku zapewne dużo lepiej byłoby poprosić
Sonic Pi aby powtarzać dany wzorzec w nieskończoność (a przynajmniej do
momentu w którym naciśniesz przycisk Stop aby zatrzymać odtwarzanie!).
Spróbujmy zapętlić w nieskończoność sampel Amen Break:
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
Bardzo ważną rzeczą do zapamiętania o pętlach jest to, że dla kodu mają one takie same zachowanie jak czarne dziury. Jak tylko kod raz już wejdzie w daną pętlę może już jej nigdy nie opuścić, chyba że naciśniesz przycisk Stop - nasza pętla będzie się kręcić w kółko w nieskończoność. Oznacza to, że jeśli masz jakiś kod, który jest umieszczony po pętli, to nigdy go nie usłyszysz. Na przykład, talerz, który znajduje się poniżej poniższej pętli nie zostanie zagrany nigdy:
loop do
play 50
sleep 1
end
sample :drum_cymbal_open
A teraz, spróbuj poprawić strukturę twojego kodu za pomocą iteracji i pętli!
Bardzo prawdopodobne jest, że często będziesz chciał zagrać nie tylko
losową nutę (patrz poprzednia sekcja dot. losowości) lecz również
będziesz chciał podejmować losowe decyzje i na podstawie ich wyniku będziesz
chciał uruchomić taki lub inny kod. Na przykład, mógłbyś chcieć aby
losowo zagrać bęben lub talerz perkusji. Możemy tego dokonać używając
polecenia warunkowego if
(jeśli).
Spróbujmy rzucić monetą: jeśli (if) wypadnie orzeł, zagraj bęben, jeśli
wypadnie reszka reszka (else), zagraj talerz. Proste. Możemy zasymulować
rzut monetą wykorzystując naszą funkcję one_in
(poznaliśmy ją w sekcji
dotyczącej losowości) określając prawdopodobieństwo wyboru 1 z 2: one_in(2)
.
Następnie możemy użyć tego wyniku aby wybrać do zagrania jeden z dwóch
kawałków kodu - jeden, który zagra bęben i drugi, który zagra talerz:
loop do
if one_in(2)
sample :drum_heavy_kick
else
sample :drum_cymbal_closed
end
sleep 0.5
end
Zauważ, że polecenie warunkowe if
posiada 3 części:
Zazwyczaj w językach programowania, pojęcie odpowiedzi tak jest
reprezentowane przez termin true
(prawda), natomiast pojęcie
odpowiedzi nie jest reprezentowane przez termin false' (nieprawda,
fałsz). Do nas więc należy znalezienie odpowiedniego pytania, którego
zadanie zwróci nam odpowiedź
true (prawda) lub
false (fałsz).
W tym wypadku naszym pytaniem jest uruchomienie funkcji
one_in`.
Zauważ, że pierwszy wybór jest wpasowany pomiędzy polecenia if
(jeśli)
a else
(w przeciwnym razie), natomiast drugi wybór mieści się pomiędzy
poleceniami else
i end
(koniec). Tak samo jak w przypadku bloków
kodu do/end, pomiędzy obiema tymi przestrzeniami możesz napisać wiele
linii kodu. Oto przykład:
loop do
if one_in(2)
sample :drum_heavy_kick
sleep 0.5
else
sample :drum_cymbal_closed
sleep 0.25
end
end
Tym razem, w zależności od tego jaka decyzja została wylosowana
śpimy (sleep
) przez różnie długości czasu.
Czasami chciałbyś wykonać opcjonalnie tylko jedną określoną linię kodu.
Aby to osiągnąć wystarczy umieścić polecenie if
wraz z naszym pytaniem
(warunkiem) na końcu tej linii. Oto przykład:
use_synth :dsaw
loop do
play 50, amp: 0.3, release: 2
play 53, amp: 0.3, release: 2 if one_in(2)
play 57, amp: 0.3, release: 2 if one_in(3)
play 60, amp: 0.3, release: 2 if one_in(4)
sleep 1.5
end
Powyższy kod zagra akordy złożone z różnych liczb. Szansa na to, że każda z podanych nut zostanie zagrana jest różna, gdyż każda z linijek (oprócz pierwszej) jest opatrzona warunkiem o różnym prawdopodobieństwie wystąpienia.
A więc udało Ci się przygotować zabójczy bassline oraz tłusty beat. W jaki sposób udaje Ci się je zagrać w tym samym czasie? Jednym z możliwych rozwiązań jest przeplecenie ich razem ręcznie - najpierw zagrać jakis bas, potem trochę bębnów, potem znowu bas… Jednakże, bardzo szybko chronometraż stanie się czymś, o czym będzie ciężko myśleć, zwłaszcza kiedy zaczniemy wplatać do naszej kompozycji kolejne elementy.
A co jeśli Sonic Pi mógłby przeplatać różne elementy za Ciebie automagicznie? Takie coś jest możliwe i możesz to robić używając specjalnego polecenia nazywanego wątkiem - thread.
Aby sprawić by kolejny przykład był prosty, musisz sobie wyobrazić jak
wygląda ten tłusty beat i zabójczy bassline:
loop do
sample :drum_heavy_kick
sleep 1
end
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
Jak już wcześniej mówiliśmy, pętle są jak czarne dziury dla naszych programów. Gdy już raz wejdziesz do pętli nie wyjdziesz z niej nigdy, chyba że naciśniesz Stop. W jaki zatem sposób możemy zagrać obie pętle jednocześnie? Musimy powiedzieć Sonic Pi, że chcemy rozpocząć coś w tym samym czasie gdy uruchamiamy pozostały kod. To jest właśnie moment, w którym z pomocą przychodzą nam Wątki (ang. threads).
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
Jeśli otoczymy pierwszą pętlę blokiem kodu do/end in_thread
, to w ten
sposób powiemy Sonic Pi żeby uruchomił zawartość tego bloku do/end
dokładnie w tym samym czasie, gdy zostają uruchomione kolejne polecenia,
które znajdują się tuż za blokiem kodu (w tym przypadku jest to druga pętla).
Spróbuj uruchomić ten kod a usłysz jednocześnie bębny oraz bassline, które
są przeplecione i grają jednocześnie!
A co teraz, jeśli chcielibyśmy dodać jeszcze jakiś syntezator (synth). Coś w tym stylu:
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
Znowu mamy taki sam problem jak przed chwilą. Pierwsza pętla jest uruchomiona
w tym samym momencie co druga pętla ze względu na to, że użyliśmy polecenia
in_thread
. Jednakże, trzecia pętla nigdy nie zostaje uruchomiona. Żeby to
naprawić potrzebujemy kolejnego wątku (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
Coś co może być dla Ciebie zaskakujące, to fakt, że gdy naciskasz przycisk Run, to w rzeczywistości tworzysz nowy wątek do uruchomienia twojego kodu. Dlatego też, gdy naciśniesz przycisk uruchom wiele razy to dźwięki zaczną nakładać się na siebie. Jako, że kolejne uruchomienia same są po prostu wątkami, to dźwięki zostaną dla Ciebie automatycznie poprzeplatane.
Gdy już nauczysz się w jaki sposób opanować Sonic Pi, nauczysz się również
tego, że wątki są jednym z najważniejszych materiałów używanych do
tworzenia twojej muzyki. Jednym z ważnych zadań, które do nich należą
to izolacja pojęcia aktualnych ustawień od innych wątków. Co to oznacza?
Otóż, kiedy zmieniasz syntezatory poleceniem use_synth
w rzeczywistości
po prostu zmieniasz syntezator dla aktualnego wątku (current thread)
- w żadnym innym wątku syntezator się nie zmieni. Zobaczmy te zachowanie
w akcji:
play 50
sleep 1
in_thread do
use_synth :tb303
play 50
end
sleep 1
play 50
Zauważyłeś, że środkowy dźwięk był inny od pozostałych? Polecenie
use_synth
wpłynęło tylko na to co zostało uruchomione w wątku, natomiast
pozostała, zewnętrzna część kodu została nietknięta.
Kiedy utworzysz nowy wątek z wykorzystaniem in_thread
, nowy wątek
automatycznie odziedziczy wszsystkie aktualne ustawienia z bieżącego
wątku. Spójrzmy na taki kod:
use_synth :tb303
play 50
sleep 1
in_thread do
play 55
end
Zauważyłeś, że druga nuta zostaje zagrana z użyciem syntezatora :tb303
mimo to, że została zagrana z oddzielnego wątku? Każde ustawienie
zmienione przy użyciu różnych funkcji use_*
będą zachowywać się tak samo.
Kiedy są tworzone kolejne wątki, dziedziczą one wszystkie ustawienia ze swoich rodziców, natomiast jakiekolwiek zmiany nie są udostępniane z powrotem.
Na koniec, możemy nadawać naszym Wątkom nazwy:
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
Spójrz na panel z logiem kiedy uruchomisz ten kod. Czy widzisz jak w logach przy kolejnych wiadomościach pojawiają się nazwy wątków?
[Run 36, Time 4.0, Thread :bass]
|- synth :prophet, {release: 0.6, note: 47}
Jedną ostatnią już rzecz, którą powinieneś wiedzieć o wątkach posiadających swoją nazwę to to, że w tym samym czasie może być uruchomiony tylko jeden wątek o tej samej nazwie. Spróbujmy to zbadać. Weźmy pod uwagę następujący kod:
in_thread do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Sróbuj śmiało wkleić powyższy kod do obszaru roboczego i naciśnij przycisk Run. Następnie naciśnij go jeszcze kilka razy. Usłyszysz kakofonię wielu zapętlonych w czasie sampli Amen Break. Ok, możesz teraz nacisnać przycisk Stop.
To jest zachowanie, które widzieliśmy już nie raz - jeśli naciśniesz przycisk Run, dźwięk zostanie nałożony na istniejące już dźwięki. Dlatego jeśli masz pętle i naciśniesz przycisk Run trzy raz, to będziesz miał trzy warstwy pętli, które będą grane jednocześnie.
Jednakże, w przypadku nazwanych wątków jest inaczej:
in_thread(name: :amen) do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Spróbuj teraz dla tego kodu nacisnąć przycisk Run kilkukrotnie. Nie usłyszysz teraz więcej niż jedną pętlę amen na raz. W logach zauważysz również taką wiadomość:
==> Skipping thread creation: thread with name :amen already exists.
Sonic Pi mówi Ci, że wątek o nazwie :amen
już istnieje, nie zostanie
więc utworzony kolejny.
Takie zachowanie może się teraz nie wydawac do niczego przydatne - ale będzie bardzo przydatne kiedy zaczniemy kodować na żywo…
Gdy już zaczniesz pisać znaczne ilości kodu, to w pewnym momencie będziesz chciał znaleźć sposób aby uporządkować i poukładać go tak aby był one elegancki i łatwiejszy do zrozumienia. Funkcje są bardzo skutecznym sposobem, który to umożliwia. Pozwalają nam nadać kawałkowi kodu nazwę. Rzućmy na to okiem.
define :foo do
play 50
sleep 1
play 55
sleep 2
end
W powyższym przykładzie zdefiniowaliśmy nową funkcję zwaną foo
. Zrobiliśmy
to używając naszego dobrego znajomego bloku do/end oraz magicznego słowa
define
, po którym podaliśmy nazwę, którą chcemy nadać naszej funkcji.
Nie musieliśmy nazywać jej foo
, mogliśmy nazwać ją jak tylko mamy ochotę,
np. bar
, baz
albo idealnie pasowałoby, gdybyśmy nadali jakąs nazwę,
która coś znaczy, np. czesc_glowna
albo glowna_gitara
.
Pamiętaj tylko aby poprzedzić nazwę dwukropkiem :
gdy definiujesz nową
funkcję.
Skoro już udało się nam zdefiniować naszą funkcję, możemy uruchomić ją wpisując jej nazwę:
define :foo do
play 50
sleep 1
play 55
sleep 0.5
end
foo
sleep 1
2.times do
foo
end
Możemy użyć naszej funkcji foo
wewnątrz bloku iteracji albo gdziekolwiek
indziej, gdzie mogliśmy do tej pory używać polecenia play
i sample
.
Pozwala nam to w bardzo fajny sposób na wyrażanie siebie i na tworzenie
nowych słów, które znaczą coś nowego i używanie ich w naszych kompozycjach.
Jak do tej pory, za każdym razem gdy nacisnąłeś przycisk Run, Sonic Pi zaczynał od czystej tablicy. Nie wiedział nic poza tym co znajduje się aktualnej przestrzeni roboczej. Nie możesz odnosić się do kodu w innych przestrzeniach roboczych lub innym wątku. Możliwość korzystania z funkcji zmienia to. Kiedy zdefiniujesz funkcję, Sonic Pi zapamiętuje ją. Spróbujmy to wykorzystać. Usuń cały kod znajdujący się w twojej aktualnej przestrzeni roboczej i zastąp go następującą linią:
foo
Naciśnij przycisk Run - usłyszysz jak gra twoja funkcja. Skąd się wziął
ten kod? Skąd Sonic Pi wiedział co powinien zagrać? Sonic Pi po prostu
zapamiętał wcześniej twoją funkcję - więc nawet po tym jak już ją skasujesz
z twojej przestrzeni roboczej, to będzie on pamiętał to co wcześniej
napisałeś. Ten mechanizm działa tylko z funkcjami stworzonymi z wykorzystaniem
polecenia define
(oraz defonce
).
Być może zainteresuje Cie fakt, że tak samo jak możesz przekazywać wartości
minimalną (min) i maksymalną (max) do funkcji rrand
, tak samo możesz nauczyć
twoją funkcję aby potrafiła przyjmować różne argumenty. Spójrzmy na następujący
kod:
define :my_player do |n|
play n
end
my_player 80
sleep 0.5
my_player 90
Nie jest on za bardzo ekscytujący, ale świetnie przedstawia o co chodzi.
Stworzyliśmy naszą własną wersję polecenia play
, która przyjmuje
parametr i nazwaliśmy ją my_player
.
Parametry należy umieścić tuż za poleceniem do
bloku kodu define
,
otoczyć pionowymi kreskami |
oraz oddzielić przecinkami ,
. Możesz
użyć dowolnego słowa jakiego chcesz dla nazw parametrów.
Magia dzieje się w środku bloku do/end define
. Możesz używać nazw
parametrów tak jak byłyby prawdziwymi wartościami. W powyższym przykładzie
gram nutę n
. Możesz patrzeć na parametry jak na swego rodzaju obietnice,
która mówi o tym, że gdy kod zostanie uruchomiony, to zostaną one zastąpione
aktualnymi wartościami. Możesz to zrobić poprzez przekazanie parametru
do funkcji gdy ją uruchamiasz. Uruchamiam polecenie my_player 80
aby
zagrać nutę 80. Wewnątrz definicji funkcji, n
zostanie zamienione na 80,
więc polecenie play n
przemieni się w play 80
. Kiedy uruchomię ją
ponownie w taki sposób my_player 90
, to teraz n
zostanie zastąpione
przez 90, więc polecenie play n
będzie zmieni się teraz w polecenie
play 90
.
Przyjrzyjmy się teraz bardziej interesującemu przykładowi:
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
Użyłem tutaj parametru repeats
w linii repeats.times do
tak jakby
był liczbą. Użyłem również parametru root
dla polecenia play
tak,
jakby był normalną nazwą nuty.
Zauważ, że poprzez przeniesienia dużej ilości logiki do funkcji, jesteśmy teraz w stanie napisać coś bardzo ekspresyjnego a zarazem łatwego do przeczytania!
Przydatną rzeczą, którą możesz robić w swoim kodzie jest tworzenie
nazw dla różnych rzeczy. Sonic Pi sprawia, że jest to bardzo łatwe,
wpisujesz nazwę, której chciałbyś użyć, znak równości (=
), następnie
rzecz, którą chcialbyś zapamiętać:
sample_name = :loop_amen
W powyższym kawałku kodu ‘zapisaliśmy’ wartość symbolu :loop_amen
w zmiennej sample_name
. Od teraz możemy używać nazwy sample_name
wszędzie, gdzie do tej pory użylibyśmy sampla :loop_amen
. Na przykład:
sample_name = :loop_amen
sample sample_name
Są trzy podstawowe powody na korzystanie ze zmiennych w Sonic Pi: komunikowanie znaczenia, zarządzanie powtórzeniami oraz przechwytywanie wyników różnych rzeczy.
Kiedy piszesz kod łatwo jest myśleć, że jedyne co robisz to mówisz komputerowi jak ma wykonać jakieś rzeczy - tak długo jak komputer rozumie co do niego mówisz to jest w porządku. Jednakże, ważne jest aby pamiętać że nie tylko komputer czyta kod. Inni ludzie również mogą chcieć przeczytać go i spróbować zrozumieć co się w nim dzieje. Ponadto, bardzo prawdopodobne, że Ty również będziesz czytał swój własny kod w przyszłości i próbował zrozumieć o co w nim chodzi. Chociaż w tej chwili jego znaczenie może być dla Ciebie oczywiste - może nie być takie oczywiste dla innych lub nawet dla Ciebie samego w przyszłości!
Jedną z metod, która pomoże innym zrozumieć co twój kod robi jest pisanie komentarzy (co widzieliśmy już we wcześniejszej sekcji tego samouczka). Inną jest używanie takich nazw dla zmiennych, które coś znaczą. Spójrz na poniższy kod:
sleep 1.7533
Dlaczego używa on liczby 1.7533
? Skąd wzięła się ta liczba? Co ona
oznacza? A teraz, spójrz na poniższy kod:
loop_amen_duration = 1.7533
sleep loop_amen_duration
Teraz, zrozumienie tego co oznacza liczba 1.7533
jest znacznie prostsze:
oznacza ona dłuość trwania sampla :loop_amen
! Oczywiście, możesz zapytać,
dlaczego po prostu nie napisaliśmy:
sleep sample_duration(:loop_amen)
Co, jak najbardziej, jest bardzo fajnym sposobem zakomunikowania intencji zawartych w kodzie.
Często widzisz dużo powtórzeń w twoim kodzie i kiedy chcesz coś zmienić, musisz zmienić to w wielu miejscach. Spójrz na poniższy kawałek kodu:
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)
Robiliśmy tutaj sporo rzeczy z samplem :loop_amen
! Co jeśli
chcielibyśmy usłyszeć, jak ten kawałek kodu brzmi z innym samplem,
na przykład :loop_garzul
? Musielibyśmy wtedy znaleźć i zamienić
wszystkie wystąpienia sampla :loop_amen
na :loop_garzul
. To może
być całkiem w porządku jeśli masz dużo czasu - ale co jeśli właśnie
występujesz na scenie? Czasami nie masz tego luksusu, że masz czasu
tyle ile chcesz - zwłaszcza gdy chcesz utrzymać ludzi na parkiecie.
A co jeśli powyższy kawałek kodu przepiszemy na coś takiego:
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)
Teraz, ten kod robi dokładnie to samo co wcześniejszy (spróbuj). Oprócz
tego daje na możliwość zmiany tylko jednej linijki z obecnej
sample_name = :loop_amen
na sample_name = :loop_garzul
aby
jednocześnie zmienić brzmienie w wielu miejscach dzięki magii zmiennych.
I na koniec, dobrą powodem do używania zmiennych jest przechwytywanie wyniku wykonania różnych rzeczy. Na przykład, możesz chcieć robić różne rzeczy z długością trwania sampla:
sd = sample_duration(:loop_amen)
Możemy teraz używać zmiennej sd
wszędzie gdzie potrzebujemy użyć
długości trwania sampla :loop_amen
.
Możliwe, że nawet bardziej ważne jest to, że zmienne pozwalają nam
na przechwycenie i zapisanie wyniku uruchomienia polecenia play
lub sample
:
s = play 50, release: 8
Teraz złapaliśmy i zapamiętaliśmy s
jako zmienną, co pozwala nam
na kontrolę syntezatora w trakcie jego działania:
s = play 50, release: 8
sleep 2
control s, note: 62
Przyjrzymy się bardziej kontrolowaniu syntezatorów w kolejnej sekcji.
Skoro osiągnąłeś już wystarczająco zaawansowane umiejętności kodowania na żywo (live coding) wraz z wykorzystaniem jednocześnie funkcji i wątków, na pewno zauważyłeś już, że bardzo łatwo jest popełnić błąd w jednym z wątków co powoduje, że zostaje on zabity. To nie jest wielka rzecz, ponieważ możesz bardzo łatwo zrestartować wątek poprzez ponowne naciśnięcie przycisku Run. Jednakże, kiedy zrestartujesz wątek to wypadnie on teraz z rytmu oryginalnych wątków.
Jak już mówiliśmy wcześniej, nowe wątki tworzone z wykorzystaniem polecenia
in_thread
dziedziczą wszystkie ustawienia z wątku rodzica. W skład
tych ustawień wchodzi też aktualny czas. Oznacza to, że wątki pozostają
zawsze w korelacji czasowej kiedy zostaną uruchomione jednocześnie.
Jendakże, kiedy uruchomisz samodzielny wątek, rozpoczyna się on ze swoim własnym czasem i jest mało prawdopodobne, że będzie on w synchronizacji z jakimkolwiek innym uruchomionym aktualnie wątkiem.
Sonic Pi udostępnia rozwiązanie dla tego problemu za pomocą funkcji
cue
(wskazówka) i sync
(synchronizacja).
cue
pozwala nam na wysłanie wiadomości o pulsie to wszystkich innych
aktualnie uruchomionych wątków. Domyślnie inne wątki nie są tym zainteresowane
i ignorują tę wiadomość o pulsie. Jendakże, możesz bardzo łatwo wywołać
takie zainteresowanie za pomocą funkcji sync
.
Ważną rzeczą, której należy być świadomym jest to, że funkcja sync
jest podobna do funkcji sleep
w taki sposób, że powstrzymuje ona
aktualny wątek od robienia czegokolwiek przez pewien okres czasu.
Jednakże, w przypadku funkcji sleep
musisz zdefiniować jak długo
chcesz poczekać, natomiast w przypadku funkcji sync
nie wiesz jak
długo będziesz chicał poczekać, ponieważ funkcja sync
czeka na
następny punkt cue
z innego wątku, co może nastąpić szybciej lub
później.
Spróbujmy przyjrzeć się temu trochę bardziej dokładnie:
in_thread do
loop do
cue :tick
sleep 1
end
end
in_thread do
loop do
sync :tick
sample :drum_heavy_kick
end
end
Mamy tutaj dwa wątki - jeden zachowuje się jak metronom, nie gra żadnych
dźwięków tylko wysyła komunikat :tik
(cyk) przy każdym uderzeniu. Drugi
wątek synchronizuje się natomiast przy otrzymaniu każdej kolejnej wiadomości
:tick
i kiedy otrzymuje kolejną odziedzicza czas uruchomienia cue
i rozpoczyna swoje działanie.
Jako wynik słyszymy sampel :drum_heavy_kick
dokładnie w tym samym momencie
gdy inny wątek wysyła komunikat :tick
, nawet jeśli obydwa wątki nie
zostały uruchomione w tym samym czasie.
in_thread do
loop do
cue :tick
sleep 1
end
end
sleep(0.3)
in_thread do
loop do
sync :tick
sample :drum_heavy_kick
end
end
Te niegrzeczne polecenie sleep
normalnie stworzyłoby drugi wątek,
który byłby zupełnie niezsynchronizowany z pierwszym. Jednakże, dzięki
użyciu poleceń cue
i sync
automatycznie synchronizujemy wątki blokując
jednocześnie jakiekolwiek nieprzewidziane rozjazdy w czasie.
Dla nazw twoich komunikatów cue
możesz używać dowolnych nazw - może to
być nie tylko :tick
. Musisz się tylko upewnić, że inne wątki
sync
hronizują się z właściwą nazwą - w przeciwnym wypadku będą czekać
w nieskończoność (albo do momentu, w którym naciśniesz przycisk Stop).
Spróbujmy pobawić się z kilkoma komunikatami cue
o różnych nazwach:
in_thread do
loop do
cue [:foo, :bar, :baz].choose
sleep 0.5
end
end
in_thread do
loop do
sync :foo
sample :elec_beep
end
end
in_thread do
loop do
sync :bar
sample :elec_flip
end
end
in_thread do
loop do
sync :baz
sample :elec_blup
end
end
Mamy tutaj główną pętlę cue
, która co iterację wysyła komunikat z
losową wybraną nazwą punktu cue do synchronizacji - :foo
, :bar
lub
:baz
. Następnie mamy trzy wątki z pętlami, które synchronizują się
na każdym z tych komunikatów niezależnie i odtwarzają różne sample.
Wynikiem tego jest to, że słyszymy dźwięk co każde 0.5 uderzenia
jako, że każdy z synchronizowanych (sync
) wątków jest wybierany
losowo z wątku cue
po czym odtwarza swojego sampla.
Oczywiście to wszystko zadziała analogicznie nawet jeśli poukładasz
wątki w odwrotnej kolejności jako, że wątek sync
będzie po prostu
czekał na kolejną wartość wysłaną przez wątek cue
.
Jednym z najbardziej satysfakcjonujących aspektów Sonic Pi jest możliwość łatwego dodawania efektów studyjnych do twoich brzmień. Na przykład, być może zdarzy się, że będziesz chciał dodać trochę efektu reverb (pogłos) do pewnych części twojego kawałka, albo trochę echa albo może nawet efektu distort czy wobble do twojej linii basowej.
Sonic Pi udostępnia bardzo prosty a jednocześnie potężny sposób na dodawanie takich efektów (FX). Pozwala on nawet na łączenie ich (możesz więc przepuścić twoje brzmienie kolejno przez efekt distortion/overdrive?, potem przez echo i na końcu przez reverb) i jednocześnie daje Ci możliwość kontrolowania każdego z nich indywidualnie za pomocą parametrów (w podobny sposób do tego w jaki przekazujemy parametry do syntezatorów czy sampli). Możesz nawet modyfikować parametry efektu (FX) gdy wciąż jest uruchomiony. Możesz więc, na przykład, zwiększyć wartość efektu reverb twojej sekcji basowej na przestrzeni całego utworu…
Jeśli wszystko to brzmi dla ciebie trochę zbyt skomplikowanie, nie przejmuj się. Gdy już trochę się tym pobawisz, wszystko stanie się całkiem jasne. Zanim jednak to nastąpi, wprowadzę prostą analogię do efektów (FX) gitarowych. Istnieje wiele rodzajów takich efektów (FX), które możesz kkupić. Pewne z nich dodają efekt reverb, inne distort, itd. Gitarzyści podłączają swoją gitarę do jednego z efektów (FX), tzw. pedałów gitarowych - np. distortion. Następnie biorą kabel i łączą ten efekt z kolejnym, np. efektem reverb. Na koniec wyjście z efektu reverb może być podłączone do wzmacniacza:
Gitara -> Distortion -> Reverb -> Wzmacniacz
Nazywa się to łączeniem efektów w łańcuch. Sonic Pi wspiera taki właśnie model dla swoich efektów. Dodatkowo, każdy z efektów posiada pokrętła i suwaki pozwalające Ci na kontrolę tego jak mocny ma być efekt distortion, reverb, echo, itd. Sonic Pi wspiera taką kontrolę. Na konic, możesz wyobrazić sobie grającego gitarzystę podczas gdy ktoś inny w tym samym czasie bawi się i kontroluje efekty (FX). Sonic Pi również wspiera taką możliwość - tylko, że zamiast innej osoby do kontrolowania tych efektów, na scenę wkroczy w tym wypadku komputer.
Poznajmy zatem efekty (FX)!
W tej sekcji przyjrzymy się kilku efektom (FX): reverb i echo. Zobaczymy w jaki sposób ich używać, w jaki sposób kontrolować ich parametry oraz jak łączyć je ze sobą w jeden łańcuch.
System efektów w Sonic Pi korzysta z bloków kodu. Jeśli więc do tej pory nie przeczytałeś jeszcze sekcji 5.1, to teraz jest dobry moment by nadrobić zaległości.
Jeśli chcemy użyć efektu reverb wystarczy, że napiszemy with_fx :reverb
jako specjalny kawałek kodu dla naszego bloku do/end:
with_fx :reverb do
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
end
A teraz uruchom ten kod a usłyszysz jak jest zagrany z nałożonym na niego efektem reverb. Brzmi nieźle, prawda?! Wszystko brzmi całkiem fajnie z efektem reverb.
A teraz zobaczmy co się wydarzy, gdy umieścimy jakiś dodatkowy kod poza naszym blokiem do/end:
with_fx :reverb do
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
end
sleep 1
play 55
Zauważ, że ostania linia play 55
nie jest zagrana z efektem reverb.
Dzieje się tak ponieważ znajduje się poza blokiem kodu do/end, więc
dzięki temu nie jest objęta efektem reverb.
Analogicznie, jeśli zrobisz jakieś dźwięki przed blokiem do/end, to one również nie będą pod wpływem efektu reverb:
play 55
sleep 1
with_fx :reverb do
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
end
sleep 1
play 55
Jest wiele efektów do wyboru. Co powiesz na echo?
with_fx :echo do
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
end
Jednym z potężnych aspektów bloków efektów w Sonic Pi jest to, że można
do nich przekazywać parametry podobne do parametrów, które już widzieliśmy
gdy korzystaliśmy z poleceń play
i sample
. Na przykład bardzo fajnym
parametrem, który nieźle brzmi z efektem echo jest phase:
. Reprezentuje
on czas trwania danego efektu przez określoną liczbę uderzeń (ile uderzeń trwa efekt).
Spróbujmy sprawić aby efekt echo był trochę wolniejszy:
with_fx :echo, phase: 0.5 do
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
end
Spróbujmy też sprawić aby echo brzmiało szybciej:
with_fx :echo, phase: 0.125 do
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
end
Sprawmy teraz aby zanikanie echa trwało dłużej ustawiając parametr
decay
na 8 sekund:
with_fx :echo, phase: 0.5, decay: 8 do
play 50
sleep 0.5
sample :elec_plip
sleep 0.5
play 62
end
Jednym z najbardziej potężnych aspektów bloków zawierających efekty jest to, że możesz je zagnieżdzać. Pozwala to na bardzo łatwe łączenie ich razem. Na przykład, co jeśli chciałeś zagrać kawałek jakiegoś kodu z efektami echo i reverb? Łatwizna, po prostu umieść jeden w drugim:
with_fx :reverb do
with_fx :echo, phase: 0.5, decay: 8 do
play 50
sleep 0.5
sample :elec_blup
sleep 0.5
play 62
end
end
Pomyśl o przepływie dźwięku ze środka na zewnątrz. Dźwięk z najbardziej
wewnętrznego bloku kodu, np. play 50
jest najpierw wysyłany do
efektu echo, następnie dźwięk z nałożonym echo jest z kolei wysyłany
do efektu reverb.
Możemy używać wielu takich zagnieżdżeń dlo osiągania zwariowanych wyników. Jednakże uważaj, efekty mogą zużywać sporo zasobów. Zagnieżdżając jeden w drugim uruchamiasz wiele efektów na raz. Bądź więc oszczędny z ich używaniem, zwłaszcza na platformach o słabych parametrach (procesor, pamięć, itd.) na przykład takich jak Raspberry Pi.
Sonic Pi posiada wiele wbudowanych efektów, z których możesz korzystać. Aby zobaczyć jakie z nich są dostępne, kliknij na ikonie Fx (efekty) w sekcji pomocy a zobaczysz listę wszystkich dostępnych opcji. Oto kilka moich ulubionych:
A teraz powariuj trochę i dodaj efektów wszędzie gdzie tylko możesz aby stworzyć zdumiewające nowe dźwięki!
Pomimo, że na pierwszy rzut oka wyglądają one na zwodniczo łatwe, to od środka efekty są tak naprawde bardzo złożonymi bestiami. Ich prostota bardzo często kusi ludzi by naduzywać ich w swoich kawałkach. Może to być w porządku jeśli masz dobry i wydajny komputer, ale jeśli - tak jak ja - korzystasz z Raspberry Pi do jam’owania, musisz być wtedy bardzo ostrożny z tym ile pracy mu zlecisz do wykonania, jeśli chcesz być pewny, że beat zachowa swą płynność.
Rozważ taki kawałek kodu:
loop do
with_fx :reverb do
play 60, release: 0.1
sleep 0.125
end
end
W powyższym kawałku kodu zagraliśmy nutę 60 z bardzo krótkim czasem zanikania (release), więc jest to bardzo krótka nuta. Chemy również nałożyć na nią efekt reverb, więc opakowaliśmy ją w blok reverb. Jak na razie wszystko w porządku. Z wyjątkiem…
Przyjrzyjmy się co ten kod tak naprawdę robi. Na początku mamy
pętlę loop
co oznacza, że wszystko wewnątrz niej będzie powtarzane
w nieskończoność. Następnie mamy blok with_fx
. Oznacza to, że utworzymy
nowych efekt FX dla każdej iteracji pętli loop
. To tak jakbyśmy
chcieli mieć odzielny efekt gitarowowy reverb przy każdym jednym uderzeniu
w strunę gitary. Fajnie, że możemy tak zrobić, ale nie zawsze jest to
coś co chciałbyś osiągnąć. Na przykład, ten kawałek kodu czeka niezła
walka aby został on ładnie uruchomiony na Raspberry Pi. Cała praca
potrzebna do stworzenia efektu reverb a potem czekanie do momentu,
w którym będzie wymagał zatrzymania i usunięcia jest obsługiwana
za Ciebie przez funkcję with_fx
, zajmie to jednak sporo mocy
procesora (CPU), która może okazać się bardzo cenna.
A co jeśli sprawimy, by ten kod był bardziej podobny do tradycyjnego zestawu, gdzie nasz gitarzysta posiada tylko jeden efekt reverb i to przez niego przechodzą wszystkie dźwięki? Łatwizna:
with_fx :reverb do
loop do
play 60, release: 0.1
sleep 0.125
end
end
Umieściliśmy naszą pętęlę wewnątrz bloku with_fx
. W ten sposób
tworzymy tylko jeden efekt reverb dla wszystkich nut, które zostaną
zagrane w naszej pętli. Taki kod jest dużo bardziej wydajny i na pewno
będzie działał w porządku na Raspberry Pi.
Kompromisem może też być użycie efektu with_fx
wewnątrz pętli loop,
ale powyżej iteracji wewnętrznej:
loop do
with_fx :reverb do
16.times do
play 60, release: 0.1
sleep 0.125
end
end
end
W ten sposób przenieśliśmy efekt with_fx
na zewnątrz wewnętrznej części
pętli loop
i teraz tworzymy nowy efekt reverb tylko co 16 zagranych
nut.
Pamiętaj jedno - błędów nie ma, sa tylko nowe możliwości. Jednakże, każdy z powyższych podejść będzie miał inne brzmienie oraz inną charakterystykę wydajności. Staraj się więc używać takiego podejścia, które będzie brzmiało najlepiej mając jednocześnie na uwadze fakt pracy z ograniczeniami wydajnościowymi twojego komputera/platformy na której pracujesz.
Do tej pory dowiedzieliśmy się jak możemy wyzwalać syntezatory i sample oraz w jaki sposób zmieniać ich domyślne parametry takie jak amplituda, kanał, ustawienia obwiedni i inne. Każdy uruchomiony dźwięk jest w zasadzie osobnym dźwiękiem z jego własnym zestawem parametrów ustalonych na czas trwania dźwięku.
Czy nie było by super gdybyś mógł zmieniać parametry dźwięku w trakcie odtwarzania danego dźwięku, tak samo jak możesz naciągnać struny gitary gdy jej struny wciąż wibruja?
Masz szczęście - ten rozdział pokaże Ci dokładnie jak możesz tego dokonać.
Do tej pory interesowaliśmy się tylko uruchamianiem nowych dźwięków i efektów. Jednakże, Sonic Pi daje nam możliwość manipulacji i kontroli aktualnie uruchomionych dźwięków. Dokonujemy tego używając zmiennych do przechwycenie referencji do syntezatora:
s = play 60, release: 5
Mamy tutaj zmienną lokalną s
, która reprezentuje syntezator
grający nutę 60. Nuta, która jest zmienną działającą lokalnnie
- nie masz do niej dostępu z innych uruchomień takich jak funkcje (?).
Gdy już mamy zmienną s
, możemy zacząć kontrolować ją za pomoca
funkcji control
:
s = play 60, release: 5
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72
Rzeczą godną zauważenia jest tutaj to, że nie uruchamiamy 4 oddzielnych
syntezatorów - uruchamiamy tylko jeden po czym zmieniamy wysokość tonu
3 razy w trakcie gdy wciąż gra.
Możemy przekazywać dowolne ze standardowych parametrów do polecenia
control
, możesz więc kontrolować takie rzeczy jak amp:
, cutoff:
czy pan:
.
Są pewne parametry, które nie mogą być kontrolowane gdy syntezator został już uruchomiony. Dotyczy to m.in. wszytkich parametrów obwiedni ADSR. Możesz samodzielnie sprawdzić, które z parametrów danego syntezatora mogą być kontrolowane zaglądając do systemu pomocy. Jeśli w dokumentacji jest napisane can not be changed once set (nie może być zmieniony gdy już raz został ustawiony), to już wiesz, że nie możesz kontrolować i zmienać danego parametru gdy syntezator już wystartuje.
Możliwe jest również kontrolowanie efektów, jendakże jest ono realizowane w trochę inny sposób:
with_fx :reverb do |r|
play 50
sleep 0.5
control r, mix: 0.7
play 55
sleep 1
control r, mix: 0.9
sleep 1
play 62
end
Zamiast używać zmiennej, korzystamy z parametru pomiędzy słupkami
znajdujacymi się w bloku kodu do/end. Pomiędzy słupki |
wstawiamy
unikalną nazwę dla naszego efektu, dzięki czemu możemy się później
do niej odwoływać w danym bloku kodu do/end. Postępowanie w tym
przypadku jest identyczne do używania parametrów w funkcjach.
A teraz spróbuj wykorzystać kontrolę nad syntezatorami i efektami!
Podczas poznawania parametrów syntezatorów i efektów, być może udało
Ci się zauważyć, że kilka nazw parametrów ma kończy się na _slide
.
Być może nawet próbowałeś korzystać z nich i nie zauważyłeś żadnego
efektu. Dzieje się tak ponieważ nie są one normalnymi parametrami,
są to specjalne parametry, które działają tylko gdy kontrolujesz
syntezatory w sposób jaki pokazaliśmy w poprzedniej sekcji.
Przyjrzyjmy się następującemu przykładowi:
s = play 60, release: 5
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72
W powyższym przykładzie słyszymy jak wysokość tonu (pitch) zmienia się
przy każdym uruchomieniu polecenia control
. Może się jednak zdarzyć,
że będziemy chcieli aby zmiana tonu nastąpiła w sposób płynny (tak jakbyśmy
użyli do tego suwaka). Tak samo jak kontrolujemy zmianę tonu parametrem
note:
, aby dodać płynne przejście, musimy ustawić na syntezatorze parametr
note_slide
:
s = play 60, release: 5, note_slide: 1
sleep 0.5
control s, note: 65
sleep 0.5
control s, note: 67
sleep 3
control s, note: 72
Teraz słyszymy, że pomiędzy poszczególnymi wywołaniami poleceń
control
nuty są “naciągane”. Brzmi to całkiem fajnie, prawda?
Możesz przyśpieszyć płynne przejście pomiędzy nutami używająć
krótszego czasu, np. `note_slide: 0.2’, albo zwolnić je, używająć
dłuższego czasu przejścia (ślizgania).
Każdy z parametrów, który może być kontrolowany posiada odpowiadający
my parametr typu _slide
, z którego możesz korzystać.
Gdy już raz ustawimy parametr typu _slide
na uruchomionym
syntezatorze, to zostanie on zapamiętany i ta wartość będzie wykorzystywana
za każdym razem gdy będziesz zmieniał płynnie wartość odpowiedającego
mu parametru. Aby zatrzymać płynne przejścia musisz ustawić wartość
parametru _slide
na 0 przed kolejnym wywołaniem polecenia control
.
Są również możliwe płynne przejścia dla parametrów efektów:
with_fx :wobble, phase: 1, phase_slide: 5 do |e|
use_synth :dsaw
play 50, release: 5
control e, phase: 0.025
end
Spróbuj teraz pobawić się płynnymi przejściami aby spróbować dokonać płynnych przejść i płynnej kontroli…
Struktury danych to bardzo przydatne narzędzie dostępne w standardowm zestawie narzędzi każdego programisty.
Czasami będziesz chciał reprezentować i używać czegoś więcej niż tylko jednej rzeczy. Na przykład, przydatnym może okazać się możliwość posiadania serii nut do zagrania jedna po drugiej. Języki programowania posiadają coś takiego jak struktury danych, które pozwalają Ci robić takie rzeczy.
Istnieje wiele ekscytujących i egzotycznych struktur danych dostępnych dla programistów - a ludzie wciąż wymyślają nowe. Na razie potrzebujemy w zasadzie wziąć pod uwagę tylko jedną prostą strukturę danych - listę.
Przyjrzyjmy się jej bardziej szczegółowo. Skupimy się jej podstawową formą, następnie pokażemy w jaki sposób możemy wykorzystać listy do reprezentacji skal oraz akordów.
W tej sekcji przyjrzymy się bardzo przydatnej strukturze danych - liście. Spotkaliśmy ją na krótko już wcześniej w sekcji poświęconej randomizacji kiedy to wybieraliśmy z listy losowe nuty do zagrania:
play choose([50, 55, 62])
W obecnym rozdziale zobaczymy jak możemy wykorzystać listy również
do reprezentacji akordów i skal. Na początku jednak spróbujmy sobie
przypomnieć w jaki sposób możemy zagrać akord. Pamiętasz, że jeśli
nie użyliśmy polecenia sleep
, to wszystkie dźwięki zagrają
jednocześnie:
play 52
play 55
play 59
Spróbujmy przyjrzeć się innym sposobom, za pomocą których możemy wyrazić powyższy kod.
Jedną z opcji jest umieszczenie wszystkich nut w liście [52, 55 , 59]
.
Nasza poczciwa funkcja play
jest wystarczająco bystra by wiedzieć
w jaki sposób ma zagrać listę nut. Spróbuj tego:
play [52, 55, 59]
Ooh, taki kod jest dużo przyjemniejszy do czytania. Zagranie listy nut, nie blokuje Ci korzystania z żadnego z parametrów:
play [52, 55, 59], amp: 0.3
Oczywiście, możesz również używać tradycyjnych nazw nut zamiast korzystania z liczb MIDI:
play [:E3, :G3, :B3]
Teraz Ci z Was, który mieli farta i studiowali trochę teorii muzycznej mogą zauważyć, że to jest akord E Minor zagrany na 3-ciej oktawie.
Innym bardzo przydatną cechą list jest możliwość wyciągnięcia z nich informacji. Może to brzmieć odrobinę dziwnie, ale nie jest to nic bardziej trudnego niż to gdy ktoś prosi Cię o to abyś przeszedł książce na stronę 23. Korzystając z listy, mógłbyś zapytać, jaki element znajduje się na 23 pozycji? Jedyną dziwną rzeczą w programowaniu jest to, że numery takich pozycji (indeksy) zazwyczaj zaczynają się od 0 a nie od 1.
Indeksy w listach nie są odliczane przez 1, 2, 3… Zamiast tego odliczamy 0, 1, 2…
Przyjrzyjmy się temu trochę bardziej. Spójrz na taką listę:
[52, 55, 59]
Nie ma w niej nic specjalnie strasznego. Teraz pytania, jaki jest
drugi element w tej liście. Tak, zgadłeś, jest to 55
. To było proste.
Sprawdźmy, czy możemy poprosić komputer aby odpowiedział nam na
to pytanie:
puts [52, 55, 59][1]
Dobrze, to wygląda trochę dziwnie, jeśli jeszcze nigdy nie widziałeś
niczego takiego. Zaufaj mi jednak, to nie jest trudne. W powyższej linii
są 3 części: słowo puts
, nasza lista 52, 55, 59
oraz nasz indeks
[1]
. Najpierw mówimy puts
ponieważ chcemy aby Sonic Pi wyświetlił
nam odpowiedź w panelu logowania (po prawej stronie). Następnie, przekazujemy
do tego polecenia naszą listę i na końcu nasz indeks, który pyta nas
o drugi element. Musimy otoczyć nasz indeks w nawiasy kwadratowe,
a ponieważ liczenie zaczynamy od 0
, to indeks dla drugiego elementu
to 1
. Spójrz:
# indeksy: 0 1 2
[52, 55, 59]
Spróbuj uruchomić kod puts [52, 55, 59][1]
a zobaczysz, że liczba
55
pojawi się w panelu logowania. Zmień indeks 1
na inne wartości,
spróbuj użyć dłuższych list i pomyśl w jaki sposob mógłbyś użyć listy
w twojej kolejnej zabawie kodem. Na przykład, jakie muzyczne struktury
mogą być reprezentowane jako seria liczb…
Sonic Pi posiada wbudowane wsparcie dla nazw akordów, które zwracają listy. Spróbuj sam:
play chord(:E3, :minor)
Teraz, naprawdę zaczynamy dokądś zmierzać. To wygląda dużo ładniej niż czyste listy (i jest dużo łatwiejsze do czytania dla innych ludzi). Jakie więc inne akordy wspiera Sonic Pi? Całkiem sporo. Spróbuj tych:
chord(:E3, :m7)
chord(:E3, :minor)
chord(:E3, :dim7)
chord(:E3, :dom7)
Możemy bardzo łatwo zmienić akordy w arpeggia za pomocą funkcji
play_pattern
:
play_pattern chord(:E3, :m7)
Ok, to nie jest jakoś mocno fajne - zostało zagrane dość wolno. Funkcja
play_pattern
zagra każdą listę z przekazanej listy odzdzielając każdą
nutę uruchomieniem polecenia sleep 1
pomiędzy każdym uruchomieniem
funkcji play
. Możemy jednak użyć innej funkcji play_pattern_timed
aby móc samodzielnie określić timing i przyśpieszyć bieg rzeczy:
play_pattern_timed chord(:E3, :m7), 0.25
Możemy nawet przekazać listę czasów, która zostanie potraktowania jako krąg czasów:
play_pattern_timed chord(:E3, :m13), [0.25, 0.5]
Jest to odpowiednik takiego kwała kodu:
play 52
sleep 0.25
play 55
sleep 0.5
play 59
sleep 0.25
play 62
sleep 0.5
play 66
sleep 0.25
play 69
sleep 0.5
play 73
Który kawałek wydaje Ci się wygodnieszy do napisania, którego byś użył?
Sonic Pi posiada wsparcie dla szerokiego zakresu skal. Co powiesz na to aby zagrać C3 ze skali durowej?
play_pattern_timed scale(:c3, :major), 0.125, release: 0.1
Możemy nawet poprosić o więcej oktaw:
play_pattern_timed scale(:c3, :major, num_octaves: 3), 0.125, release: 0.1
Co powiesz na wszystkie nuty w skali pentatonicznej (pentatonika)?
play_pattern_timed scale(:c3, :major_pentatonic, num_octaves: 3), 0.125, release: 0.1
Akordy i skale są świetnym sposobem na ograniczenie wyboru losowego do czegoś sensownego. Spróbuj pobawić się z tym przykładem, który wybiera losowe nuty z akordu E3 moll:
use_synth :tb303
loop do
play choose(chord(:E3, :minor)), release: 0.3, cutoff: rrand(60, 120)
sleep 0.25
end
Spróbuj poustawiać inne nazwy akordów oraz inne zakresy odcięcia (cutoff).
Aby zobaczyć jakie skale i akordy są wspierane przez Sonic Pi kliknij na przycisk Lang znajdujący się po lewej stronie tego samouczka, następnie z listy dostępnych API wybierz pozycję chord (akord) lub scale (skala). W informacjach znajdujących się w głównym panelu, przewiń w dół aż ujrzysz długą listę akordów lub skal (w zależności od tego na którą pozycję patrzysz).
Baw się i pamiętaj: błędów nie ma, sa tylko możliwości.
Ciekawym przypadkiem stanardowych list są pierścienie. Jeśli znasz się trochę na programowaniu, to mogło się zdarzyć, że miałeś do czynienia z buforami cyklicznymi lub tablicami cyklicznymi. Tutaj przyjrzymy się tylko pierścieniowi - jest krótki i prosty.
W poprzedniej sekcji dotyczącej list widzieliśmy w jaki sposób możemy wyciągać z nich elementy korzystając z mechanizmu indeksowania:
puts [52, 55, 59][1]
A co się stanie jeśli chciałbyś pozycję spod indeksu 100
? Dobrze, więc
na pozycji 100 nie ma żadnego elementu jako, że lista posiada w sobie
tylko trzy elementy. Sonic Pi zwróci więc wartość nil
co oznacza nic.
Jednakże, biorąc pod uwagę, że masz licznik, taki jak aktualne uderzenie, które nieustannie rośnie. Spróbujmy utworzyć sobie licznik oraz listę:
counter = 0
notes = [52, 55, 59]
Możemy teraz użyć naszego licznika aby dostać się do nuty znajdującej się w naszej liście:
puts notes[counter]
Super, otrzymaliśmy 52
. Teraz, spróbujmy zwiększyć nasz licznik
i dostać się do kolejnej nuty:
counter = (inc counter)
puts notes[counter]
Super, teraz otrzymaliśmy 55
i jeśli zrobimy to jeszcze raz, to otrzymamy
liczbę 59
. Jednakże, jeśli zrobimy to kolejny raz, to wyskoczymy poza
liczby znajdujące się w naszej liście i otrzymamy nil
. A co jeśli
chcieliśmy tylko zrobić pętlę, wrócić do początku i zacząć na samym początku
listy od nowa? Do tego właśnie służą pierścienie.
Możemy tworzyć pierścienie na dwa sposoby. Albo użyjemy funkcji ring
podając
elementy dla danego pierścienia jako parametry:
(ring 52, 55, 59)
Albo możemy wziąć normalną listę i przekształcić ją w pierścień poprzez
wysłanie do niej wiadomości .ring
:
[52, 55, 59].ring
Gdy już masz pierścień, możesz go używać dokładnie w ten sam sposób w jaki używasz normalnej listy z tym jednym wyjątkiem, że możesz używać indeksów ujemnych oraz takich, które są większe niż ilość elementów w danym pierścieniu a będą one powtarzać się cyklicznie i zawsze będą wskazywać na jeden z elementów pierścienia:
(ring 52, 55, 59)[0] #=> 52
(ring 52, 55, 59)[1] #=> 55
(ring 52, 55, 59)[2] #=> 59
(ring 52, 55, 59)[3] #=> 52
(ring 52, 55, 59)[-1] #=> 59
Przyjmijmy, że używamy zmiennej do reprezentacji aktualnego numeru uderzenia. Możemy użyć jej jako indeksu w naszym pierścieniu aby wydobywać kolejne nuty do zagrania, czasy zanikania albo cokolwiek innego przydatnego co załadowaliśmy do naszego pierścienia niezależnie od numeru uderzenia (beat) przy którym aktualnie się znajdujemy.
Przydatną rzeczą jest wiedza o tym, że listy zwracane przez skale scale
oraz akordy chord
są również pierścieniami i pozwalają Ci na dostęp
do nich z wykorzystaniem bezwzględnych indeksów.
Oprócz funkcji ring
istnieje również kilka innnych funkcji, które
pozwalają nam na tworzenie pierścieni.
range
wymaga abyś określił punkt startu, końca oraz rozmiar krokubools
pozwala Ci na użycie wartości 1
i 0
aby treściwie reprezentować
wartości logiczne (boolean)knit
pozwala Ci na spajanie sekwencji powtarzalnych wartościspread
tworzy pierścień wartości logicznych o rozkładzie EuklidesowymPrzyjrzyj się poszczególnym dokumentacjom dla uzyskania większej ilości informacji.
Oprócz konstruktorów takich jak range
i spread
innym sposobem
tworzenia nowych pierścieni jest manipulowanie i modyfikacja istniejących
pierścieni (rings).
Aby zbadać ten temat stwórzmy prosty pierścień:
(ring 10, 20, 30, 40, 50)
Co jeśli chcielibyśmy otrzymać wszystkie elementy tego pierścienia
w odwrotnej kolejności? Wystarczy, że użyjemy w łańuchu poleceń
.reverse
aby wziąć nasz pierścień i odwrócić go:
(ring 10, 20, 30, 40, 50).reverse #=> (ring 50, 40, 30, 20, 10)
A co, jeśli teraz potrzebujemy tylko pierwsze trzy elementy z naszego pierścienia?
(ring 10, 20, 30, 40, 50).take(3) #=> (ring 10, 20, 30)
I na koniec jeszcze jedno, co jeśli chcielibyśmy przetasować elementy naszego pierścienia tak jak tasujemy karty przed grą?
(ring 10, 20, 30, 40, 50).shuffle #=> (ring 40, 30, 10, 50, 20)
Powyższe polecenia dają bardzo duże możliwości tworzenia nowych pierścieni (rings). Jednakże, prawdziwa moc pojawia się dopiero w momencie, gdy połączysz kilka poleceni w jeden łańcuch.
Co powiesz na to aby najpierw przetasować pierścień, potem usunać z niego 1 element i z elementów, które pozostały wybrać tylko pierwsze 3?
Zróbmy to krok po kroku:
(ring 10, 20, 30, 40, 50)
- nasz początkowy pierścień(ring 10, 20, 30, 40, 50).shuffle
- tasujemy - (ring 40, 30, 10, 50, 20)
(ring 10, 20, 30, 40, 50).shuffle.drop(1)
- usuwamy 1-wszy element - (ring 30, 10, 50, 20)
(ring 10, 20, 30, 40, 50).shuffle.drop(1).take(3)
- pozostawiamy tylko pierwsze 3 - (ring 30, 10, 50)
Widzisz jak łatwo możemy stworzyć długi łańcuch tych metod sklejając je po prostu razem? Możemy użyć tych metod w dowolnej kolejności na jaką nas tylko najdzie ochota. Dzięki temu mamy bardzo bogatą i potężną paletę sposobów, dzięki którym możemy tworzyć nowe pierścienie z już istniejących.
Pierścienie posiadają pewną potężną i ważną cechę. Są niezmienne co oznacza, że gdy raz zostaną już utworzone to nie mogą zostać zmienione. Z tego wynika, że łańcuchy wywołań metod opisane w tym rozdziale nie zmieniają pierścieni lecz tworzą nowe pierścienie. Dzięki takiemu podejśćiu nic nie stoi na przeszkodzie abyś mógł współdzielić dany pierścień pomiędzy różnymi wątkami i wywyływać na nim łańcuchy metod w ramach jednego wątku, gdyż wiesz że nie wpłynie to w żaden sposób na jakikolwiek inny wątek, który korzysta z tego samego pierścienia.
Oto lista metod, które możesz łączyć w łańcuchy wywołań:
.reverse
- zwraca odwróconą wersję pierścienia.sort
- tworzy posortowaną wersję pierścienia.shuffle
- tworzy pierścień z wartościami, które są przetasowane.pick(3)
- zwraca pierścień, który zawiera 3 losowo wybrane elementy z oryginalnego pierścienia (działanie analogiczne do funkcji choose
).pick
- działanie analogiczne do funkcji .pick(3)
, z tym że w tym przypadku liczba wylosowanych elementów jest równa ilości elementów znajdujących się w oryginalnym pierścieniu.take(5)
- zwraca nowy pierścień, który zawiera tylko 5 pierwszych elementów.drop(3)
- zwraca nowy pierścień, który zawiera wszystkie elementy oprócz pierwszych 3.butlast
- zwraca nowy pierścień, który nie zawiera ostatniego elementu.drop_last(3)
- zwraca nowy pierścień, kóry nie zawiera 3 ostatnich elementów.take_last(6)
- zwraca nowy pierścień, który posiada tylko 6 ostatnich elementów.stretch(2)
- powtarza każdy z elementów w pierścieniu dwukrotnie.repeat(3)
- powtarza cały pierścień trzykrotnie.mirror
- dodaje pierścień do odwróconej wersji samego siebie.reflect
- działa tak samo jak mirror, ale nie powtarza środkowej wartości z pierścieniaOczywiście, metody które przyjmują jako parametr liczbę, mogą przyjmować
też inne liczby! Nie krępuj się więc i spróbuj wywołać metodę .drop(5)
zamiast widocznej parę linijek wyżej metody .drop(3)
. Pozwoli Ci to stworzyć
nową listę, która nie będzie zawierała 5 pierwszych elementów.
Jednym z najbardziej ekscytujących aspektów Sonic Pi jest to, że pozwala Ci tworzyć muzykę porzez pisanie i modyfikację kodu na żywo, tak samo jakbys mógł występować na żywo z gitarą. Jedną z zalet takiego podejścia jest to, że powala Ci na otrzymywanie informacji zwrotnej znacznie szybciej niż w sytuacji gdybyś komponował (stwórz prostą pętlę, która będzie się kręcić w kółko i zacznij dopieszczać ją aż zacznie brzmieć idealnie). Główną i podstawową zaletą jest jednak to, że możesz wziąć Sonic Pi ze sobą na scenę i wykorzystać go do występów na żywo.
W tym rozdziale przedstawimy Ci podstawy tego w jaki sposób możesz zmienić twoje statyczne kompozycje w pełne życia występy.
Zapnijcie pasy…
Teraz nauczyliśmy się już wystarczająco dużo aby zacząć prawdziwą zabaę. W tym rozdziale będziemy czerpać ze wszystkich poprzednich i pokażemy Ci w jaki sposób możesz zacząć tworzyć swoje własne kompozycje muzyczne na żywo i zmienić je w występy na żywo. Będziemy do tego potrzebować 3 podstawowych sładników:
W porząsiu, zaczynamy. Spróbujmy zakodować na żywo nasze pierwsze dźwięki. Najpierw potrzebujemy funkcję, która będzie zawierać kod, który chcemy zagrać. Zacznijmy od czegoś prostego. Potrzebujemy również aby każda pętla wywołująca tę funkcję była uruchamiana w nowym wątku:
define :my_loop do
play 50
sleep 1
end
in_thread(name: :looper) do
loop do
my_loop
end
end
Jeśli powyższy kawałek kodu wydaje Ci się zbyt skomplikowany, wróć z powrotem i ponownie przeczytaj rozdziały dotyczące funkcji i wątków. Gdy to zrobisz i uda Ci się dobrze zrozumieć te 2 tematy to powyższy kawałek nie powinien sprawić Ci żadnych kłopotów.
To co mamy powyżej to nic innego jak funkcja, która po prostu gra nutę
o wartości 50 i następnie czeka 1 uderzenie. W kolejnym kroku mamy
definicję wątku nazwanego :looper
, który po prostu kręci się w kółko
i uruchamia fukcję my_loop
.
Gdy uruchomisz ten kod, usłyszysz nutę 50 która powtarza się i gra w kółko…
Teraz, czas na moment w którym zaczyna się cała zabawa. W momencie gdy kod jest wciąż uruchomiony, spróbuj zmienić liczbę 50 na inną, powiedzmy 55, następnie naciśnij przycisk Run jeszcze raz. Łał! To się zmieniło! Ten kod żyje!
Nowa warstwa dźwięku nie została dodana ponieważ korzystamy z nazwanych
wątków co pozwala tylko na jeden wątek dla jednej nazwy. Ponadto,
dźwięk się zmienił ponieważ przedefiniowaliśmy funkcję. Nadaliśmy
funkcji :my_loop
nową definicję - znaczenie. Kiedy wątek :looper
przekręci się po raz kolejny, to w następnej iteracji zostanie zawołana
funkcja o nowej definicji.
Spróbuj zmienić ją ponownie, zmień nutę, zmień wartość parametru jaki
przekazujemy do funkcji sleep. Co powiesz na dodanie polecenia use_synth
?
Na przykład, zmieńmy kod w taki sposób:
define :my_loop do
use_synth :tb303
play 50, release: 0.3
sleep 0.25
end
Brzmi to całkiem ciekawie, ale możemy urozmaicić to jeszcze bardziej. Zamiast grać w kółko tę samę nutę, spróbujmy zagrać akord:
define :my_loop do
use_synth :tb303
play chord(:e3, :minor), release: 0.3
sleep 0.5
end
A co powiesz na granie losowych nut z akordu:
define :my_loop do
use_synth :tb303
play choose(chord(:e3, :minor)), release: 0.3
sleep 0.25
end
Albo użycie losowej wartości dla parametru odcięcia (cutoff):
define :my_loop do
use_synth :tb303
play choose(chord(:e3, :minor)), release: 0.2, cutoff: rrand(60, 130)
sleep 0.25
end
I na sam koniec, dodajmy trochę bębnów:
define :my_loop do
use_synth :tb303
sample :drum_bass_hard, rate: rrand(0.5, 2)
play choose(chord(:e3, :minor)), release: 0.2, cutoff: rrand(60, 130)
sleep 0.25
end
Teraz zabawa zaczyna być naprawdę ekscytująca!
Jednakże, zanim zamkniesz tutorial i zaczniesz kodować na żywo
z wykorzystaniem funkcji i wątków, zatrzymaj się na chwile i przeczytaj
kolejny rozdział dotyczący funkcji live_loop
, która zmieni twój sposób
kodowania w Sonic Pi na zawsze…
Dobrze, ten rozdział to prawdziwa perełka w tym samouczku. Jeśli
masz przeczytać tylko jeden rozdział w tym tutorialu, powinien to
być właśnie ten rozdział. Jeśli przeczytałeś poprzedni rozdział
o Podstawach Kodowania Na Żywo, to live_loop
jest prostszą metodą
dokonania dokładnie tego samego, ale bez konieczności pisania
tak dużej ilości kodu.
Jeśli nie przeczytałeś poprzedniego rozdziału, funkcja live_loop
jest najlepszym sposobem na jam’owanie z Sonic Pi.
Zabawmy się. Wpiszmy następujący kod do nowego bufora:
live_loop :foo do
play 60
sleep 1
end
Naciśnij przycisk Run. Słyszysz podstawowy bip przy każdym uderzeniu.
Nic szczególnego. Powstrzymaj się jednak i nie naciskaj jeszcze przycisku
Stop. Zmień wartość 60
na 65
i naciśnij przycisk Run jeszcze raz.
Łał! Brzmienie zmieniło się automatycznie bez utraty żadnego uderzenia.
Czemu więc nie spróbować zmienić ten kawałek aby brzmiał trochę bardziej jak linia basowa? Wystarczy, że zmienisz kod gdy ten wciąż jest uruchomiony:
live_loop :foo do
use_synth :prophet
play :e1, release: 8
sleep 8
end
I naciśniesz przycisk Run.
Spróbujmy sprawić, aby pojawiła się odrobina odcięcia (cutoff):
live_loop :foo do
use_synth :prophet
play :e1, release: 8, cutoff: rrand(70, 130)
sleep 8
end
Ponownie naciśnij przycisk Run.
Dodajmy trochę bębnów:
live_loop :foo do
sample :loop_garzul
use_synth :prophet
play :e1, release: 8, cutoff: rrand(70, 130)
sleep 8
end
Zmień nutę e1
na c1
:
live_loop :foo do
sample :loop_garzul
use_synth :prophet
play :c1, release: 8, cutoff: rrand(70, 130)
sleep 8
end
A teraz przestań słuchać moich propozycji i zacznij bawić się samodzielnie! Miłej zabawy!
Przyjrzyjmy się natępującej żywej pętli:
live_loop :foo do
play 50
sleep 1
end
Być może zastanawiałeś się dlaczego konieczna jest nazwa :foo
. Nazwa
ta jest ważna ponieważ oznacza, że ta konkrenta żywa pętla jest inna od wszystkich
innych żywych pętli.
W tym samym czasie nie mogą być uruchomione dwie żywe pętle o tej samej nazwie.
Oznacza to, że jeśli potrzebujemy kilku jednocześnie kręcących się żywych pętli, to po prostu musimy nadać im inne nazwy:
live_loop :foo do
use_synth :prophet
play :c1, release: 8, cutoff: rrand(70, 130)
sleep 8
end
live_loop :bar do
sample :bd_haus
sleep 0.5
end
Możesz teraz aktualizować i zmieniać każdą z żywych pętli niezależnie i wszystio będzie po prostu działać.
Jedną z rzeczy, które być może już zauważyłeś to to, że żywe pętle
działają automatycznie z mechanizmem punktów cue dla wątków, które
poznaliśmy już wcześniej. Za każdym razem, gdy żywa pętla wykona pętlę,
generuje nowe zdarzenie cue
nadając mu nazwę taką samą jaką nadaliśmy
żywej pętli. Możemy więc sychroznizować się z wykorzystaniem funkcji
sync
na każdym zdarzeniu cue
aby upewnić się, że nasze pętle
są zsynchronizowane bez konieczności zatrzymywania czegokolwiek.
Spójrzmy na poniższy kod, który został źle zsynchronizowany:
live_loop :foo do
play :e4, release: 0.5
sleep 0.4
end
live_loop :bar do
sample :bd_haus
sleep 1
end
Zobaczmy czy możemy naprawić chronometraż (timing) bez zatrzymywania. Najpierw,
naprawmy pętlę :foo
tak, aby sprawić, że wartość parametru sleep będzie
współczynnikiem liczby 1 - coś jak 0.5
powinno być ok:
live_loop :foo do
play :e4, release: 0.5
sleep 0.5
end
live_loop :bar do
sample :bd_haus
sleep 1
end
To jeszcze nie koniec - powinieneś zauważyć, że uderzenia nie do końca ustawiają się odpowiednio. Dzieje się tak, ponieważ pętle nie są synchronizowane. Spróbujmy to naprawić przez ich synchronizację:
live_loop :foo do
play :e4, release: 0.5
sleep 0.5
end
live_loop :bar do
sync :foo
sample :bd_haus
sleep 1
end
Łał, teraz wszystko perfekcyjnie w czas - i to wszystko bez zatrzymywania.
A teraz, nie zatrzymuj się i koduj na żywo z wykorzystaniem żywych pętli!
Coś na co na pewno będziesz robił dość często podczas kodowania na żywo to iterowanie przez pierścienie. Będziesz wrzucał nuty do pierścieni dla tworzenia melodii, czasów uśpienia dla rytmów, progresji akordów, różne wariacje barw dźwięku, itd.
Sonic Pi udostępnia bardzo przydatne narzędzie do pracy z pierścieniami
w żywych pętlach live_loop
. Nazywa się to systemem cykania. Udostępnia
Ci możliwość cykania przez pierścienie. Spójrzmy na następujący przykład:
live_loop :arp do
play (scale :e3, :minor_pentatonic).tick, release: 0.1
sleep 0.125
end
Wzięliśmy tu pentatoniczną skalę E3 moll i cykamy przez każdy jej element.
Dokonaliśmy tego przez dodanie polecenia .tick
na końcu deklaracji skali.
Każde cyknięcie jest lokalne dla danej żywej pętli, więc każda żywa pętla
może posiadać swojego własnego niezależnego cykacza:
live_loop :arp do
play (scale :e3, :minor_pentatonic).tick, release: 0.1
sleep 0.125
end
live_loop :arp2 do
use_synth :dsaw
play (scale :e2, :minor_pentatonic, num_octaves: 3).tick, release: 0.25
sleep 0.25
end
Możesz również uruchomić polecenie cyknięcia tick
jako standardową funkcje
i używać zwracanej wartości jako indeksu:
live_loop :arp do
idx = tick
play (scale :e3, :minor_pentatonic)[idx], release: 0.1
sleep 0.125
end
Jednakże, dużo ładniej jest korzystać z polecenia .tick
dodanego na końcu.
Funkcja tick
jest stworzona dla sytuacji gdy chesz zrobić coś fantazyjnego
z wartością zwracaną przy cyknięciu oraz gdy chcesz używać cyknięć
do innych rzeczy niż indeksowania elementów pierścieni.
Magiczną rzeczą w cykaniu jest to, że możliwe jest nie tylko zwrócenie
nowego indeksu (albo wartości znajdującej się w pierścieniu pod tym
indeksem). Mechanizm ten pilnuje też aby przy każdym twoim kolejnym
cyknięciu była to kolejna wartość. Zerknij na przykłady znajdujące się
w dokumentacji dla polecenia tick
aby zobaczyć wiele różnych sposobów
na pracę z nim. Jednakże, na tą chwilę, ważne jest aby zaznaczyć, że czasami
będziesz chciał po prostu podejrzeć aktualną wartość cyknięcia bez zwiększania
jego wartości. Możesz tego dokonać za pomocą funkcji look
. Możesz uruchamiać
polecenie look
jako standardową funkcję lub poprzez dodanie .look
na końcu pierścienia.
Na koniec, czasami będziesz potrzebował więcej niż jednego cykacza dla danej żywej pętli. Można tego dokonać poprzez nadanie twojemu cyknięciu nazwę:
live_loop :arp do
play (scale :e3, :minor_pentatonic).tick(:foo), release: 0.1
sleep (ring 0.125, 0.25).tick(:bar)
end
Powyżej używamy dwóch cykaczy, jeden dla zagrania nuty i drugi dla
odliczania czasu uśpienia. Jako, że oba znajdują się w tej samej żywej pętli,
potrzebowaliśmy nadać im unikalne nazwy aby sprawić by były oddzielne.
To jest dokładnie taki sam mechanizm jak w przypadku nadawania nazw
żywym pętlom (live_loop
) - po prostu przekazujemy symbol poprzedzony
dwukropkiem :
. W powyższym przykładzie nazwaliśmy pierwszego
cykacza :foo
a drugiego :bar
. Jeśli chcemy podejrzeć aktualną
wartość, któregoś z nich za pomocą polecenia look
, to musimy
do polecenia look
również przekazać nazwę konkretnego cykacza.
Większość mocy zawartej w systemie cykania nie jest przydatna gdy dopiero zaczynasz. Nie próbój i nie ucz się wszystkiego z tego rozdziału. Postaraj skupić się na cykaniu przez pojedynczy pierścień. To powinno dać Ci największą satysfakcję oraz prostotę związaną z cykaniem przez pierścienie w twoich żywych pętlach.
Zerknij do dokumentacji na polecenie tick
gdzie znajdziesz wiele
przydatnych przykładów. Wesołego cykania!
Ten rozdział obejmie trochę bardzo przydatnej - w zasadzie niezbędnej - wiedzy, która pozwoli Ci na wyciągnięcie maksimum możliwości z Sonic Pi.
Pokażemy tutaj jak wykorzystać wiele skrótów klawiszowych, które są dostępne, w jaki sposób możesz dzielić się swoją pracą a także parę przydatnych sztuczek dotyczących występów z Sonic Pi.
Sonic Pi zarówno instrumentem muzycznym jak i środowiskiem programistycznym. Skróty klawiszowe mogą więc sprawić, że granie z Sonic Pi będzie dużo bardziej wydajne i naturalne - zwłaszcza gdy gdy będziesz grać na żywo przed publicznością.
Wiele funkcji Sonic Pi może być kontrolowanych za pomocą klawiatury. Gdy posiądziesz już więcej wprawy w pracy i występach z Sonic Pi, na pewno zaczniesz używać skrótów coraz częściej i częściej. Potrafie pisać na klawiaturze bezwzrokowo (I tobie też polecam sie tego nauczyć) i czuję frustrację za każdym razem kiedy tylko muszę sięgnąć po myszkę ponieważ to mnie spowalnia. W związku z powyższym używam wszystkich tych skrótów w kółko!
Ponadto, jeśli nauczysz się skrótów, nauczysz się korzystać z twojej klawiatury dużo bardziej efektywnie i zaczniesz kodować na żywo niczym profesjonalista w bardzo krótkim czasie.
Jednakże, nie próbuj i nie ucz się ich wszystkich na raz, postaraj się sprawdzić i zapamiętać tylko te, których używasz najczęśniej. Dopiero potem postaraj się regularnie dodawać i ćwiczyć kolejne.
Wyobraź sobie, że uczysz się gry na klarnecie. Zapewne oczekiwałbyś, że wszystkie klarnety będą zapewniały podobną kontrole i palcowanie. Jeśli nie, to potrzebowałbyś sporo czasu za każdym razem gdy przesiadałbyś się pomiędzy różnymi klarnetami i tak naprawdę byłbyś wtedy ograniczony do korzystania tylko z jednego wybranego modelu.
Na nieszczęście 3 podstawowe systemy operacyjne (Linux, Mac OS X i Windows) posiadają swoje własne standardy dla domyślnych akcji takich jak np. kopiuj wklej. Sonic Pi postara się honorować wszystkie te standardy. Jednakże, priorytet został położony na spójność pomiędzy różnymi platformami, na których działa Sonic Pi i jest on ważniejszy niż próby zapewnienia zgodności z poszczególnymi platformami. Oznacza to, że jeśli nauczysz się skrótów klawiszowych gdy będziesz grał na Sonic Pi na Raspberry Pi, to będziesz mógł przenieść się na komputer marki Apple z systemem Mac OS lub peceta z zainstalowanym Windows’em i dalej czuć się jak w domu.
Częścią dotyczącą pojęcia spójności jest nazewnictwo skrótów. W Sonic Pi używamy nazw Control i Meta dla odniesienia się do dwóch podstawowych klawiszy używanych w kombinacjach skrótów. Na wszystkich platformach klawisz Control odnosi się do tego samego przycisku na klawiaturze. Jednakże, w systemach Linux i Window, klawisz Meta oznacza klawisz Alt, podczas gdy w systemie Mac OS klawisz Meta oznacza klawisz Command. Dla zachowania spójności będziemy używali terminu Meta - jedyne co musisz zapamiętać to to, w jaki sposób zmapować ten termin na odpowiedni klawisz dla twojego systemu operacyjnego.
Aby dalsze instrukcje były proste i czytelne, będziemy używać skrótu
C- dla klawisza Control plus innego klawisza oraz M- dla Meta
plus inny klawisz. Na przykład, jeśli skrót klawiszowy wymaga abyś
nacisnął razem klawisze Meta i r zapiszemy to jako M-r
. Symbol
- oznacza po prostu “w tym samym czasie co”.
Przedstawię Ci kilka skrótów klawiszowych, które są dla mnie najbardziej przydatne.
Zamiast za każdym razem sięgać po myszkę, aby uruchomić twój kod, możesz
po prostu nacisnąć skrót M-r
. Podobnie, aby zatrzymać uruchomiony
kod możesz nacisnąć M-s
.
Bez skrótów umożliwiających nawigację czuję się naprawdę zagubiony. W związku z tym bardzo zalecam ci spędzić tyle czasu ile potrzeba aby się ich nauczyć. Skróty te działają również bardzo dobrze kiedy już nauczysz się pisać na klawiaturze bezwzrokowo jako, że używają one standardowych liter i nie wymagają od ciebie abyś sięgał ręka do myszki lub klawiszy ze strzałkami na twojej klawiaturze.
Możesz przenieść się do początku linii naciśniskając skrót C-a
,
aby przenieść się na koniec linii naciśnij C-e
, aby przenieść się
o 1 linię w górę naciśnij C-p
, jedną linię w dół C-n
, aby przesunąć
sie o jeden znak do przodu użyj C-f
, jeden znak do tyłu C-b
. Możesz
nawet usunąć wszystkie znaki od miejsca, w którym aktualinie znajduje się
kursor, aż do końca linii używając skrótu C-k
.
Aby automatycznie wyrównać twój kod wystarczy, że naciśniesz M-m
.
Aby pokazać i ukryć system pomocy możesz nacisnąć przycisk M-i
. Jednakże,
dużo bardziej przydatnym skrótem, który warto znać jest C-i
- pozwala on
na wyszukanie w systemie pomocy słowa, na którym aktualnie znajduje się
kursor i wyświetlenie tego jeśli uda mu się znaleźć cokolwiek. Pomoc
błyskawiczna!
Aby zobaczyć pełną listę dostępnych skrótów klawiszowych zajrzyj do rozdziału 10.2 Ściągawka Skrótów Klawiszowych.
Poniżej dostępne jest podsumowanie najważniejszych skrótów dostępnych w Sonic Pi. Przejrzyj proszę rozdzial 10.1 dla uzyskania motywacji i tła.
W poniższej liście używamy następujących konwencji (gdzie Meta oznacza klawisz Alt w systemach Windows/Linux oraz Cmd w systemie Mac OS):
C-a
oznacza przytrzymaj klawisz Control następnie naciśnij klawisz a tak, żeby przez chwilę oba były jednocześnie
naciśnięte po czym je puść.M-r
oznacza przytrzymaj klawisz Meta i następnie naciśnij klawisz r tak, żeby przez chwilę oba były naciśnięte
jednocześnie po czym je puść.S-M-z
oznacza naciśnij i przytrzymaj klawisz Shift, następnie naciśnij i przytrzymaj klawisz Meta i na końcu
dołóż do tej kombinacji w tym samym czasie klawisz z, następnie puść wszystkie klawisze.C-M-f
oznacza naciśnij i przytrzymaj klawisz Control, potem naciśnij i przytrzymaj klawisz Meta, po czym
na końcu naciśnij do tego jeszcze klawisz f i puść wszystkie klawisze.M-r
- Uruchom kod (przycisk Run)M-s
- Zatrzymaj kod (przycisk Stop)M-i
- Otwórz/Zamknij System PomocyM-p
- Otwórz/Zamknij UstawieniaM-{
- Przejdź do buforu po lewej stronieM-}
- Przejdź do buforu po prawej stronieM-+
- Zwiększ rozmiar czcionki w aktualnym buforzeM--
- Zmniejsz rozmiar czczionki w aktualnym buforzeM-a
- Zaznacz wszystkoM-c
- Skopiuj zaznaczony obszar aby móc go wkleić do bufora w innym miejscuM-]
- Skopiuj zaznaczony obszar aby móc go wkleić do bufora w innym miejscuM-x
- Wytnij zaznaczony obszar aby móc wkleić go do bufora w innym miejscuC-]
- Wytnij zaznaczony obszar aby móc wkleić go do bufora w innym miejscuC-k
- Wytnij tekst od aktualnej pozycji kursora do końca liniiM-v
- Wklej zawartość schowka do aktualnego buforaC-y
- Wklej zawartość schowka do aktualnego buforaC-SPACE
- Ustaw punkt zaznaczenia. Następnie nawiguj po tekście aby podświetlić tekst do zaznaczenia.
Użyj C-g
aby wyłączyć/usunąć zaznaczenie.M-m
- Wyrównaj cały tekst w buforzeTab
- Popraw wyrównianie/zaznaczenie aktualnej liczby lub pokaż podpowiedziC-l
- Wyśrodkuj edytorC-t
- Transponuj/zamień znakiM-u
- Konwertuj następne słowo (lub zaznaczenie) do wielkich literM-l
- Konwertuj następne słowo (lub zaznaczenie) do małych literC-a
- Przenieś kursor do początku liniiC-e
- Przenieś kursor na koniec liniiC-p
- Przenieś kursor do poprzedniej liniiC-n
- Przenieś kursor do następnej liniiC-f
- Przenieś kursor o jeden znak do przodu (w prawo)C-b
- Przenieś kursor o jeden znak do tyłu (w lewo)M-f
- Przenieś kursor do przodu (w prawo) o jedno słowoM-b
- Przenieś kursor do tyłu (w lewo) o jedno słowoC-M-n
- Przenieś aktualną linie lub zaznaczenie w dółC-M-p
- Przenieś aktualną linię lub zaznaczenie do góryS-M-u
- Przenieś się do góry o 10 liniiS-M-d
- Przenieś się w dół o 10 liniiM-<
- Przenieś się na początek buforaM->
- Przenieś się na koniec buforaC-h
- Usuń poprzedni znakC-d
- Usuń kolejny znakC-i
- Pokaż dokumentację dla słowa, na którym znajduje się aktualnie kursorM-z
- Wstecz (cofnij zmianę)S-M-z
- Redo (powtórz zmianę)C-g
- Ucieczka (anulowanie)S-M-f
- Otwórz/Zamknij tryb pełnoekranowyS-M-b
- Pokaż/Showaj przyciskiS-M-l
- Pokaż/Schowaj panel logowaniaS-M-m
- Przełączanie pomiędzy trybem jasnym i ciemnymW Sonic Pi chodzi przede wszystkim o udostępnianie i uczenie się od siebie nawzajem.
Gdy już nauczyłeś się w jaki sposób kodować muzykę, udostępnienie twojej kompozycji jest tak łatwe jak wysłanie wiadomości email, która będzie zawierać twój kod. Proszę abyś dzielił się swoim kodem z innymi, dzięki temu będą oni mogli uczyć się z twojej pracy a nawet używac kawałków twojej muzyki do tworzenia nowych mash-up’ów.
Jeśli nie jesteś pewien tego w jaki sposób możesz najlepiej dzielić się twoimi dokonaniami z innymi, to polecam Ci wrzucanie twojego kodu na stronę GitHub natomiast gotową muzykę na stronę SoundCloud. W ten sposób bardzo łatwo uda Ci się zyskać znaczną publiczność.
GitHub to strona umożliwiająca dzielenie się i pracę nad kodem. Jest ona używana zarówno przez profesjonalnych programistów jak ich artystów w celu udostępniania i współpracy z kodem. Najprostszą metodą aby podzielić się nowym kawałkiem kodu (nawet tym niedokończonym) jest stworzenie Gist’a Gist to bardzo prosty sposób na przesłanie twojego kodu w bardzo prosty sposób, tak żeby inni mogli go zobaczyć, skopiować i dzielić się nim.
Innym bardzo ważnym sposobem na dzielenie się twoją pracą jest nagranie pliku audio i przesłanie go na stronę SoundCloud. Gdy już wrzucisz tam swój kawałek, inni użytkownicy tej strony będą mogli komentować i rozmawiać o twoim dziele. Zalecam również zamieszczenie w opisie linku do Gist’a zawierającego kod źródłowy tego utworu.
Aby nagrać twoją pracę wystarczy, że naciśniesz przycisk Rec
znajdujący
się w panelu z przyciskami a nagrywanie rozpocznie się natychmiast.
Naciśnij więc przycisk Run
aby uruchomić twój kod jeśli przypadkiem
jeszcze tego nie zrobiłeś. Kiedy skończysz nagranie, naciśnij ponownie
migający przycisk Rec
a zostaniesz poproszony o podanie nazwy pliku
pod jaką ma zostać zapisane twoje nagranie. Zostanie ono zapisane
jako plik WAV, który może być bardzo łatwo przeedytowany i skonwertowany
do formatu MP3 przez jeden z wielu darmowych programów (spróbuj na przykład
programu Audacity).
Zachęcam Cię abyś dzielił się swoją pracą i naprawdę wierzę w to, że wszyscy razem będziemy się uczyć od siebie nowych sztuczek i trików w Sonic Pi. Jestem bardzo podekscytowany tym co będziesz chciał mi pokazać.
Jednym z najbardziej ekscytujących aspektów Sonic Pi jest fakt, że pozwala Ci na używanie kodu jako instrumentu muzycznego. Oznacza to, że pisanie kodu na żywo może być teraz uznawane jako nowa metoda prezentowania muzyki na scenie.
Nazywamy to Kodowaniem Na Żywo.
Kiedy kodujesz na żywo polecam Ci abyś pokazał swojej publiczności twój ekran. W przeciwnym przypadku będzie to tak jak granie na gitarze ale chowając swoje palce i struny. Kiedy ćwiczę w domu używam Raspberry Pi i małego mini projektora na ścianie w moim salonie. Możesz użyć twojego TV albo jednego z projektorów, które są dostępne w twojej szkole lub pracy aby dać występ. Spróbuj, to świetna zabawa.
Nie graj tylko sam - spróbuj stworzyć zespół kodujący na żywo! Granie z innymi daje wiele radości i satysfakcji. Jedna osoba może robić bity, inna podkład w stylu ambient, itd. Spróbuj zobaczyć jakie ciekawe kombinacje dźwięków możecie razem stworzyć.
Kodowanie na żywo nie jest całkowicie nowe - mała grupa ludzi robi to już od kilku lat, zazwyczaj korzystając z systemów na zamówienie, które zbudowali dla siebie sami. Świetnym miejscem do którego możesz się udać i dowiedzieć się więcej na temat innych osób kodujących na żywo oraz ich systemów jest TOPLAP.
Innym świetnym źrodłem dla odkrywania świata kodowania na żywo jest Algorave. Możesz tutaj znaleźć wszystko co dotyczy wszystkich aspektów kodowania na żywo, które są związane z tworzeniem muzyki w klubach.
Sonic Pi wspiera proste API umożliwia interakcję z Minecraft Pi - specjalną edycją gry Minecraft, która jest domyślnie preinstalowana w systemie Raspbian na Raspberry Pi (system operacyjny bazujący na Linuksie).
Integracja z Minecraft Pi została zaprojektowana aby być szalenie
łatwa w użyciu. Wszystko co potrzebujesz zrobić to włączyć Minecraft Pi
i stworzyć nowy świat. Gdy już to zrobić możesz dowolnie używać funkcji
mc_*
tak samo jak używałeś do tej pory poleceń play
i synth
. Nie
ma konieczności importowania czegokolwiek lub instalacji dodatkowych
bibliotek - wszystko jest gotowe i działa po wyjęciu z pudełka.
API serwowane przez Minecraft Pi zajmuje się zarządzaniem twoim połączeniem
z aplikacją Minecraft Pi. Oznacza to, że nie musisz się przejmować o nic.
Jeśli spróbujesz skorzystać z API oferowanego przez Minecraft Pi kiedy
gra nie jest włączona, Sonic Pi uprzejmie Cię o tym poinformuje. Podobnie,
jeśli zamkniesz Minecraft Pi w momencie kiedy wciąż masz uruchomioną
żywą pętlę live_loop
, która korzysta z API, żywa pętla zatrzyma sie
i uprzejmie poinformuje Cię, że nie może się połączyć. Aby wznowić
połączenie wystarczy, że ponownie włączysz Minecraft Pi a Sonic Pi
automatycznie wykryje i ponownie utworzy połączenie za Ciebie.
API oferowane przez Minecraft Pi zostało zaprojektowane w taki sposób,
aby pracować bez żadnych problemów o obrębie żywych pętli tworzonych
z wykorzystaniem polecenia live_loop
. Oznacza to, że jest możliw a
synchronizacja zmian w twoim świecie Minecraft Pi ze zmianami dokonywanymi
w dźwiękach brzmiących w twoim Sonic Pi. Błyskawiczne nagrania wideo
z muzyką bazujące na grze Minecraft! Jeśli jednak zdarzy się, że natrafisz
na jakieś problemy, po prostu uruchom ponownie Minecraft Pi i kontynuuj
wcześniejszą zabawę. Funkcjonalność automatycznego połączenia Sonic Pi
zajmie się wszystkim za Ciebie.
Zalecane jest abyś używał Raspberry Pi 2 jeśli chcesz jednocześnie uruchomić Sonic Pi i Minecraft - zwłaszcza jeśli chcesz wykorzystać możliwości dźwiękowe Sonic Pi.
Na ten moment, Sonic Pi wspiera podstawowe bloki oraz manipulacje graczy, które sa opisane w rozdziale 11.1. Wsparcie dla zdarzeń zwrotnych wywoływanych przez interakcje gracza w świecie jest planowane na przyszłe wydanie.
Sonic Pi aktualnie wspiera następujące podstawowe interakcje z Minecraft Pi:
Spróbujmy przyjrzeć się każemu z nich po kolei.
Zobaczmy jak łatwo jest kontrolować Minecraft Pi z Sonic Pi. Najpierw, upewnij się, że zarówno Minecraft Pi jak i Sonic Pi są jednocześnie otwarte a także, upewnij się, że udało Ci się wejść do świata Minecraft i możesz się poruszać.
W czystym buforze Sonic Pi po prostu wprowadź poniższy kod:
mc_message "Hello from Sonic Pi"
Kiedy naciśniesz przycisk Run, zobaczysz migającą wiadomość w ekranie Minecraft. Gratulacje, właśnie napisałeś swój pierwszy kod w Minecraft! To było proste, prawda?
Teraz, spróbujmy odrobine magii. Spróbujmy przeteleportować nas w inne miejsce. Spróbuj następującego kodu:
mc_teleport 50, 50, 50
Kiedy naciśniesz Run - bum! Zostajesz błyskawicznie przetransportowany
w nowe miejsce. Bardzo prawdopodobne jest to, że to gdzieś w powietrzu
i spadniesz w dół na suchy ląd lub do wody. Teraz, co to za liczby:
50, 50, 50
? Są to współrzędne miejsca do którego starasz się
przetleportować. Poświęćmy krótką chwilę żeby dowiedzieć się czym są
współrzędne i jak działają, ponieważ są one naprawdę ważne w programowaniu
Minecraft.
Wyobraź sobie mapę piratów z wielkim X
oznacającym miejsce jakiegoś
skarbu. Dokładna lokalizacja X
może być opisana przez podanie
dwóch liczb - jak daleko na mapie od lewej do prawej oraz jak daleko
od dołu mapy w górę. Na przykład 10cm
wszerz i 8cm
w górę. Te dwie
liczby 10
i 8
to współrzędne. Możesz bardzo łatwo wyobrazić sobie
opisywanie sekretnych miejsc ze skarbami za pomocą innej pary liczb.
Być może bardzo duża skrzynia złota znajduje się na 2
w poprzek
9
w górę.
Teraz, w Minecraft dwie liczby to za mało. Potrzebujemy także wiedzieć o tym, jak wysoko się znajdujemy. Dlatego też potrzebujemy 3 liczb:
x
z
y
Jeszcze jedna rzecz - zazwyczaj opisujemy te współrzędne w następującej
kolejności x
, y
, z
.
Spróbujmy pobawić się współrzędnymi. Przenieść się do jakiegoś ładnego miejsca na mapie Minecraft następnie przełącz się do Sonic Pi. Teraz wpisz następującą linijkę:
puts mc_location
Kiedy uruchomisz przycis Run zobaczysz współrzędne twojego aktualnego położenia wyświetlone w panelu z logami. Zapisz je, następnie spróbuj przemieścić się do przodu w świecie i spróbuj jeszcze raz. Zauważ, że współrzędne się zmieniły! Teraz, zalecam Ci poświęcić Ci trochę czasu na powtórzenie tego kilka razy - przenieś się gdzieś kawałek w twoim świecie, zerknij na współrzędne i powtórz. Próbuj tego do momentu, w którym poczujesz jak zmieniają się współrzędne gdy się poruszasz. Gdy już zrozumiesz jak działają współrzędne, programowanie z API oferowanym przez Minecraft będzie dla Ciebie bułką z masłem.
Teraz gdy już wiesz w jaki sposob znaleźć aktualną pozycje oraz w jaki
sposob możesz się teleportować korzystając z współrzędnych, posiadasz
już wszystkie narzędzia, których potrzebujesz aby zacząć budować rzeczy
w Minecraft za pomocą kodu. Powiedzmy, że chciałbyś stworzyć blok ze szkła
o współrzędnych 40
, 50
, 60
. Proste:
mc_set_block :glass, 40, 50, 60
Tak, tak, to naprawde jest takie proste. Aby obejrzeć swoje dzieło po prostu przeteleportuj się niedaleko i zerknij na nie:
mc_teleport 35, 50, 60
Teraz obróć się i powinieneś zobaczyć twój blok ze szkła. Spróbuj zmienić go w diament:
mc_set_block :diamond, 40, 50, 60
Jeśli patrzyłeś w odpowiednim kierunku to być może nawet zauważyłeś jak zmienił się na twoich oczach! To początek czegoś ekscytującego…
Spróbujmy spojrzeć na ostatnią rzecz zanim zaczniemy coś bardziej angażującego. Podająć zestaw współrzędnych możemy zapytać Minecraft o to jakiego typu jest specyficzny blok. Spróbujmy tego z naszym blokiem diamentu, który stworzyłeś przed chwilą:
puts mc_get_block 40, 50, 60
Łał! To diament (:diamond
)!. Spróbuj ponownie zmienić go w szkło
i jeszcze raz zapytać o typ - czy teraz pokazał szkło (:glass
).
Jestem pewien, że tak :-)
Zanim udasz oddasz się szaleństwu programowania z Minecraft Pi, być może zainteresuje Cie poniższa lista dostępnych typów bloków:
:air
:stone
:grass
:dirt
:cobblestone
:wood_plank
:sapling
:bedrock
:water_flowing
:water
:water_stationary
:lava_flowing
:lava
:lava_stationary
:sand
:gravel
:gold_ore
:iron_ore
:coal_ore
:wood
:leaves
:glass
:lapis
:lapis_lazuli_block
:sandstone
:bed
:cobweb
:grass_tall
:flower_yellow
:flower_cyan
:mushroom_brown
:mushroom_red
:gold_block
:gold
:iron_block
:iron
:stone_slab_double
:stone_slab
:brick
:brick_block
:tnt
:bookshelf
:moss_stone
:obsidian
:torch
:fire
:stairs_wood
:chest
:diamond_ore
:diamond_block
:diamond
:crafting_table
:farmland
:furnace_inactive
:furnace_active
:door_wood
:ladder
:stairs_cobblestone
:door_iron
:redstone_ore
:snow
:ice
:snow_block
:cactus
:clay
:sugar_cane
:fence
:glowstone_block
:bedrock_invisible
:stone_brick
:glass_pane
:melon
:fence_gate
:glowing_obsidian
:nether_reactor_core
Czas na podsumowanie samouczka wprowadzającego do Sonic Pi. Mam nadziję, że nauczyłeś się czegoś w trakcie jego czytania. Nie przejmuj się jeśli czujesz, że nie wszystko zrozumiałeś - po prostu graj i baw się a na pewno załapiesz kolejne rzeczy w odpowiednim dla siebie czasie. Nie krępuj się i zajrzyj tutaj ponownie jeśli będziesz miał jakieś pytanie, na które odpowiedź znajduje się w jednym z rozdziałów.
Jeśli masz jakiekolwiek pytania, na które nie znalazłeś odpowiedzi w tym samouczku, polecam Ci zajrzeć na forum Sonic Pi i zadać swoje pytanie właśnie tam. Na pewno znajdziesz tam kogoś kto bez problemu i z chęcią poda Ci pomocną dłoń.
I na koniec, zachęcam Cię również abyś przyjrzał się głebiej pozostałej części dokumentacji dostępnej w systemie pomocy. Znajdziesz tam wiele funkcjonalności, które nie zostały omówione w tym samouczku i czekają tam abyś je odkrył.
Tak więc, graj, baw się, dziel się swoim kodem, występuj na żywo przed twoimi znajomymi, pokazuj swój ekran i pamiętaj:
Błędów nie ma, są tylko nowe możliwości.
Appendix A collects all the Sonic Pi articles written for the MagPi magazine.
These articles aren’t meant to be read in any strict order and contain a lot of cross-over material from the tutorial itself. Rather than try and teach you all of Sonic Pi, they instead each focus on a specific aspect of Sonic Pi and cover it in a fun and accessible way.
You can see them in their glorious professionally typeset form in the free PDF downloads of The MagPi here: https://www.raspberrypi.org/magpi/
If you don’t see a topic that interests you covered in these articles - why not suggest one? The easiest way to do that is to tweet your suggestion to @Sonic_Pi. You never know - your suggestion might be the subject of the next article!
The most important lesson to learn with Sonic Pi is that there really are no mistakes. The best way to learn is to just try and try and try. Try lots of different things out, stop worrying whether your code sounds good or not and start experimenting with as many different synths, notes, FX and opts as possible. You’ll discover a lot of things that make you laugh because they sound just awful and some real gems that sound truly amazing. Simply drop the things you don’t like and keep the things you do. The more ‘mistakes’ you allow yourself to make the quicker you’ll learn and discover your personal coding sound.
Say you’ve already mastered the Sonic Pi basics of making sounds with
sample
, play
? What’s next? Did you know that Sonic Pi supports over
27 studio FX to change the sound of your code? FX are like fancy image
filters in drawing programs except that instead of blurring or making
something black and white, you can add things like reverb, distortion
and echo to your sound. Think of it like sticking the cable from your
guitar to an effects pedal of your choice and then into the
amplifier. Luckily, Sonic Pi makes using FX really easy and requires no
cables! All you need to do is to choose which section of your code you’d
like the FX added to and wrap it with the FX code. Let’s look at an
example. Say you had the following code:
sample :loop_garzul
16.times do
sample :bd_haus
sleep 0.5
end
If you wanted to add FX to the :loop_garzul
sample, you’d just tuck it
inside a with_fx
block like this:
with_fx :flanger do
sample :loop_garzul
end
16.times do
sample :bd_haus
sleep 0.5
end
Now, if you wanted to add FX to the bass drum, go and wrap that with
with_fx
too:
with_fx :flanger do
sample :loop_garzul
end
with_fx :echo do
16.times do
sample :bd_haus
sleep 0.5
end
end
Remember, you can wrap any code within with_fx
and any sounds
created will pass through that FX.
In order to really discover your coding sound you’ll soon want to know how to modify and control synths and FX. For example, you might want to change the duration of a note, add more reverb, or change the time between echoes. Luckily, Sonic Pi gives you an amazing level of control to do exactly this with special things called optional parameters or opts for short. Let’s take a quick look. Copy this code into a workspace and hit run:
sample :guit_em9
Ooh, a lovely guitar sound! Now, let’s start playing with it. How about changing its rate?
sample :guit_em9, rate: 0.5
Hey, what’s that rate: 0.5
bit I just added at the end? That’s called
an opt. All of Sonic Pi’s synths and FX support them and there’s loads
to play around with. They’re also available for FX too. Try this:
with_fx :flanger, feedback: 0.6 do
sample :guit_em9
end
Now, try increasing that feedback to 1 to hear some crazy sounds! Read the docs for full details on all the many opts available to you.
The best way to quickly experiment and explore Sonic Pi is to live code. This allows you to start off some code and continually change and tweak it whilst it’s still playing. For example, if you don’t know what the cutoff parameter does to a sample, just play around. Let’s have a try! Copy this code into one of your Sonic Pi workspaces:
live_loop :experiment do
sample :loop_amen, cutoff: 70
sleep 1.75
end
Now, hit run and you’ll hear a slightly muffled drum break. Now, change
the cutoff:
value to 80
and hit run again. Can you hear the
difference? Try 90
, 100
, 110
…
Once you get the hang of using live_loop
s you’ll not turn
back. Whenever I do a live coding gig I rely on live_loop
as much as a
drummer relies on their sticks. For more information about live coding
check out Section 9 of the built-in tutorial.
Finally, one thing I love doing is cheating by getting Sonic Pi to compose things for me. A really great way to do this is using randomisation. It might sound complicated but it really isn’t. Let’s take a look. Copy this into a spare workspace:
live_loop :rand_surfer do
use_synth :dsaw
notes = (scale :e2, :minor_pentatonic, num_octaves: 2)
16.times do
play notes.choose, release: 0.1, cutoff: rrand(70, 120)
sleep 0.125
end
end
Now, when you play this, you’ll hear a constant stream of random notes
from the scale :e2 :minor_pentatonic
played with the :dsaw
synth. “Wait, wait! That’s not a melody”, I hear you shout! Well, here’s
the first part of the magic trick. Every time we go round the
live_loop
we can tell Sonic Pi to reset the random stream to a known
point. This is a bit like going back in time in the TARDIS with the
Doctor to a particular point in time and space. Let’s try it - add the
line use_random_seed 1
to the live_loop
:
live_loop :rand_surfer do
use_random_seed 1
use_synth :dsaw
notes = (scale :e2, :minor_pentatonic, num_octaves: 2)
16.times do
play notes.choose, release: 0.1, cutoff: rrand(70, 120)
sleep 0.125
end
end
Now, every time the live_loop
loops around, the random stream is
reset. This means it chooses the same 16 notes every time. Hey presto!
An instant melody. Now, here’s the really exciting bit. Change the seed
value from 1
to another number. Say 4923
. Wow! Another melody! So,
just by changing one number (the random seed), you can explore as many
melodic combinations as you can imagine! Now, that’s the magic of code.
The laser beams sliced through the wafts of smoke as the subwoofer pumped bass deep into the bodies of the crowd. The atmosphere was ripe with a heady mix of synths and dancing. However something wasn’t quite right in this nightclub. Projected in bright colours above the DJ booth was futuristic text, moving, dancing, flashing. This wasn’t fancy visuals, it was merely a projection of Sonic Pi running on a Raspberry Pi. The occupant of the DJ booth wasn’t spinning disks, he was writing, editing and evaluating code. Live. This is Live Coding.
This may sound like a far fetched story from a futuristic night club but coding music like this is a growing trend and is often described as Live Coding (http://toplap.org). One of the recent directions this approach to music making has taken is the Algorave (http://algorave.com) - events where artists like myself code music for people to dance to. However, you don’t need to be in a nightclub to Live Code - with Sonic Pi v2.6+ you can do it anywhere you can take your Raspberry Pi and a pair of headphones or some speakers. Once you reach the end of this article, you’ll be programming your own beats and modifying them live. Where you go afterwards will only be constrained by your imagination.
The key to live coding with Sonic Pi is mastering the live_loop
. Let’s
look at one:
live_loop :beats do
sample :bd_haus
sleep 0.5
end
There are 4 core ingredients to a live_loop
. The first is its
name. Our live_loop
above is called :beats
. You’re free to call your
live_loop
anything you want. Go crazy. Be creative. I often use names
that communicate something about the music they’re making to the
audience. The second ingredient is the do
word which marks where the
live_loop
starts. The third is the end
word which marks where the
live_loop
finishes, and finally there is the body of the live_loop
which describes what the loop is going to repeat - that’s the bit
between the do
and end
. In this case we’re repeatedly playing a bass
drum sample and waiting for half a beat. This produces a nice regular
bass beat. Go ahead, copy it into an empty Sonic Pi buffer and hit
run. Boom, Boom, Boom!.
Ok, so what’s so special about the live_loop
? So far it just seems
like a glorified loop
! Well, the beauty of live_loop
s is that you
can redefine them on-the-fly. This means that whilst they’re still
running, you can change what they do. This is the secret to live coding
with Sonic Pi. Let’s have a play:
live_loop :choral_drone do
sample :ambi_choir, rate: 0.4
sleep 1
end
Now press the Run button or hit alt-r
. You’re now listening to
some gorgeous choir sounds. Now, whilst it’s still playing, change the
rate from 0.4
to 0.38
. Hit run again. Woah! Did you hear the choir
change note? Change it back up to 0.4
to return back to how it
was. Now, drop it to 0.2
, down to 0.19
and then back up to
0.4
. See how changing just one parameter on the fly can give you real
control of the music? Now play around with the rate yourself - choose
your own values. Try negative numbers, really small numbers and large
numbers. Have fun!
One of the most important lessons about live_loop
s is that they need
rest. Consider the following live_loop
:
live_loop :infinite_impossibilities do
sample :ambi_choir
end
If you try running this code, you’ll immediately see Sonic Pi
complaining that the live_loop
did not sleep. This is a safety system
kicking in! Take a moment to think about what this code is asking the
computer to do. That’s right, it’s asking the computer to play an
infinite amount of choir samples in zero time. Without the safety system
the poor computer will try and do this and crash and burn in the
process. So remember, your live_loop
s must contain a sleep
.
Music is full of things happening at the same time. Drums at the same
time as bass at the same time as vocals at the same time as
guitars… In computing we call this concurrency and Sonic Pi provides
us with an amazingly simple way of playing things at the same
time. Simply use more than one live_loop
!
live_loop :beats do
sample :bd_tek
with_fx :echo, phase: 0.125, mix: 0.4 do
sample :drum_cymbal_soft, sustain: 0, release: 0.1
sleep 0.5
end
end
live_loop :bass do
use_synth :tb303
synth :tb303, note: :e1, release: 4, cutoff: 120, cutoff_attack: 1
sleep 4
end
Here, we have two live_loop
s, one looping quickly making beats and
another looping slowly making a crazy bass sound.
One of the interesting things about using multiple live_loop
s is that
they each manage their own time. This means it’s really easy to create
interesting polyrhythmical structures and even play with phasing Steve
Reich style. Check this out:
# Steve Reich's Piano Phase
notes = (ring :E4, :Fs4, :B4, :Cs5, :D5, :Fs4, :E4, :Cs5, :B4, :Fs4, :D5, :Cs5)
live_loop :slow do
play notes.tick, release: 0.1
sleep 0.3
end
live_loop :faster do
play notes.tick, release: 0.1
sleep 0.295
end
In each of these tutorials, we’ll end with a final example in the form of a new piece of music which draws from all of the ideas introduced. Read this code and see if you can imagine what it’s doing. Then, copy it into a fresh Sonic Pi buffer and hit Run and actually hear what it sounds like. Finally, change one of the numbers or comment and uncomment things out. See if you can use this as a starting point for a new performance, and most of all have fun! See you next time…
with_fx :reverb, room: 1 do
live_loop :time do
synth :prophet, release: 8, note: :e1, cutoff: 90, amp: 3
sleep 8
end
end
live_loop :machine do
sample :loop_garzul, rate: 0.5, finish: 0.25
sample :loop_industrial, beat_stretch: 4, amp: 1
sleep 4
end
live_loop :kik do
sample :bd_haus, amp: 2
sleep 0.5
end
with_fx :echo do
live_loop :vortex do
# use_random_seed 800
notes = (scale :e3, :minor_pentatonic, num_octaves: 3)
16.times do
play notes.choose, release: 0.1, amp: 1.5
sleep 0.125
end
end
end
One of the most exciting and disrupting technical developments in modern music was the invention of samplers. These were boxes that allowed you to record any sound into them and then manipulate and play back those sounds in many interesting ways. For example, you could take an old record, find a drum solo (or break), record it into your sampler and then play it back on repeat at half-speed to provide the foundation for your latest beats. This is how early hip-hop music was born and today it’s almost impossible to find electronic music that doesn’t incorporate samples of some kind. Using samples is a really great way of easily introducing new and interesting elements into your live coded performances.
So where can you get a sampler? Well you already have one - it’s your Raspberry Pi! The built-in live coding app Sonic Pi has an extremely powerful sampler built into its core. Let’s play with it!
One of the most classic and recognisable drum break samples is called the Amen Break. It was first performed in 1969 in the song “Amen Brother” by the Winstons as part of a drum break. However, it was when it was discovered by early hip-hop musicians in the 80s and used in samplers that it started being heavily used in a wide variety of other styles such as drum and bass, breakbeat, hardcore techno and breakcore.
I’m sure you’re excited to hear that it’s also built right into Sonic Pi. Clear up a buffer and throw in the following code:
sample :loop_amen
Hit Run and boom! You’re listening to one of the most influential drum breaks in the history of dance music. However, this sample wasn’t famous for being played as a one-shot, it was built for being looped.
Let’s loop the Amen Break by using our old friend the live_loop
introduced in this tutorial last month:
live_loop :amen_break do
sample :loop_amen
sleep 2
end
OK, so it is looping, but there’s an annoying pause every time
round. That is because we asked it to sleep for 2
beats and with
the default BPM of 60 the :loop_amen
sample only lasts for 1.753
beats. We therefore have a silence of 2 - 1.753 = 0.247
beats. Even
though it’s short, it’s still noticeable.
To fix this issue we can use the beat_stretch:
opt to ask Sonic Pi to
stretch (or shrink) the sample to match the specified number of beats.
[Breakout box start] Sonic Pi’s sample
and synth
fns give you a lot
of control via optional parameters such as amp:
, cutoff:
and
release:
. However, the term optional parameter is a real mouthful so
we just call them opts to keep things nice and simple.
[Breakout box end]
live_loop :amen_break do
sample :loop_amen, beat_stretch: 2
sleep 2
end
Now we’re dancing! Although, perhaps we want to speed it up or slow it down to suit the mood.
OK, so what if we want to change styles to old school hip hop or
breakcore? One simple way of doing this is to play with time - or in
other words mess with the tempo. This is super easy in Sonic Pi - just
throw in a use_bpm
into your live loop:
live_loop :amen_break do
use_bpm 30
sample :loop_amen, beat_stretch: 2
sleep 2
end
Whilst you’re rapping over those slow beats, notice that we’re still
sleeping for 2 and our BPM is 30, yet everything is in time. The
beat_stretch
opt works with the current BPM to make sure everything just works.
Now, here’s the fun part. Whilst the loop is still live, change the 30
in the use_bpm 30
line to 50
. Woah, everything just got faster yet kept
in time! Try going faster - up to 80, to 120, now go crazy and punch in
200!
Now we can live loop samples, let’s look at some of the most fun opts
provided by the sample
synth. First up is cutoff:
which controls the
cutoff filter of the sampler. By default this is disabled but you can
easily turn it on:
live_loop :amen_break do
use_bpm 50
sample :loop_amen, beat_stretch: 2, cutoff: 70
sleep 2
end
Go ahead and change the cutoff:
opt. For example, increase it to 100,
hit Run and wait for the loop to cycle round to hear the change in the
sound. Notice that low values like 50 sound mellow and bassy and high
values like 100 and 120 are more full-sounding and raspy. This is
because the cutoff:
opt will chop out the high frequency parts of the
sound just like a lawn-mower chops off the top of the grass. The
cutoff:
opt is like the length setting - determining how much grass is
left over.
Another great tool to play with is the slicer FX. This will chop (slice)
the sound up. Wrap the sample
line with the FX code like this:
live_loop :amen_break do
use_bpm 50
with_fx :slicer, phase: 0.25, wave: 0, mix: 1 do
sample :loop_amen, beat_stretch: 2, cutoff: 100
end
sleep 2
end
Notice how the sound bounces up and down a little more. (You can hear
the original sound without the FX by changing the mix:
opt to 0
.)
Now, try playing around with the phase:
opt. This is the rate (in
beats) of the slicing effect. A smaller value like 0.125
will slice
faster and larger values like 0.5
will slice more slowly. Notice that
successively halving or doubling the phase:
opts val tends to always
sound good. Finally, change the wave:
opt to one of 0, 1, or 2 and
hear how it changes the sound. These are the various wave shapes. 0 is a
saw wave, (hard in, fade out) 1 is a square wave (hard in, hard out) and
2 is a triangle wave (fade in, fade out).
Finally, let’s go back in time and revisit the early Bristol drum and bass scene with this month’s example. Don’t worry too much about what all this means, just type it in, hit Run, then start live coding it by changing opt numbers and see where you can take it. Please do share what you create! See you next time…
use_bpm 100
live_loop :amen_break do
p = [0.125, 0.25, 0.5].choose
with_fx :slicer, phase: p, wave: 0, mix: rrand(0.7, 1) do
r = [1, 1, 1, -1].choose
sample :loop_amen, beat_stretch: 2, rate: r, amp: 2
end
sleep 2
end
live_loop :bass_drum do
sample :bd_haus, cutoff: 70, amp: 1.5
sleep 0.5
end
live_loop :landing do
bass_line = (knit :e1, 3, [:c1, :c2].choose, 1)
with_fx :slicer, phase: [0.25, 0.5].choose, invert_wave: 1, wave: 0 do
s = synth :square, note: bass_line.tick, sustain: 4, cutoff: 60
control s, cutoff_slide: 4, cutoff: 120
end
sleep 4
end
Whether it’s the haunting drift of rumbling oscillators or the detuned punch of saw waves piercing through the mix, the lead synth plays an essential role on any electronic track. In last month’s edition of this tutorial series we covered how to code our beats. In this tutorial we’ll cover how to code up the three core components of a synth riff - the timbre, melody and rhythm.
OK, so power up you Raspberry Pi, crack open Sonic Pi v2.6+ and let’s make some noise!
An essential part of any synth riff is changing and playing with the timbre of the sounds. We can control the timbre in Sonic Pi in two ways - choosing different synths for a dramatic change and setting the various synth opts for more subtle modifications. We can also use FX, but that’s for another tutorial…
Let’s create a simple live loop where we continually change the current synth:
live_loop :timbre do
use_synth (ring :tb303, :blade, :prophet, :saw, :beep, :tri).tick
play :e2, attack: 0, release: 0.5, cutoff: 100
sleep 0.5
end
Take a look at the code. We’re simply ticking through a ring of synth
names (this will cycle through each of these in turn repeating the list
over and over). We pass this synth name to the use_synth
fn (function)
which will change the live_loop
’s current synth. We also play note
:e2
(E at the second octave), with a release time of 0.5 beats (half a
second at the default BPM of 60) and with the cutoff:
opt set to 100.
Hear how the different synths have very different sounds even though
they’re all playing the same note. Now experiment and have a
play. Change the release time to bigger and smaller values. For example,
change the attack:
and release:
opts to see how different fade
in/out times have a huge impact on the sound. Finally change the
cutoff:
opt to see how different cutoff values also massively
influence the timbre (values between 60 and 130 are good). See how many
different sounds you can create by just changing a few values. Once
you’ve mastered that, just head to the Synths tab in the Help system for
a full list of all the synths and all the available opts each individual
synth supports to see just how much power you have under your coding
fingertips.
Timbre is just a fancy word describing the sound of a sound. If you play the same note with different instruments such as a violin, guitar, or piano, the pitch (how high or low it sounds) would be the same, but the sound quality would be different. That sound quality - the thing which allows you to tell the difference between a piano and a guitar is the timbre.
Another important aspect to our lead synth is the choice of notes we want to play. If you already have a good idea, then you can simply create a ring with your notes in and tick through them:
live_loop :riff do
use_synth :prophet
riff = (ring :e3, :e3, :r, :g3, :r, :r, :r, :a3)
play riff.tick, release: 0.5, cutoff: 80
sleep 0.25
end
Here, we’ve defined our melody with a ring which includes both notes
such as :e3
and rests represented by :r
. We’re then using .tick
to
cycle through each note to give us a repeating riff.
It’s not always easy to come up with a nice riff from scratch. Instead it’s often easier to ask Sonic Pi for a selection of random riffs and to choose the one you like the best. To do that we need to combine three things: rings, randomisation and random seeds. Let’s look at an example:
live_loop :random_riff do
use_synth :dsaw
use_random_seed 3
notes = (scale :e3, :minor_pentatonic).shuffle
play notes.tick, release: 0.25, cutoff: 80
sleep 0.25
end
There’s a few things going on - let’s look at them in turn. First, we specify that we’re using random seed 3. What does this mean? Well, The useful thing is that when we set the seed, we can predict what the next random value is going to be - it’s the same as it was last time we set the seed to 3! Another useful thing to know is that shuffling a ring of notes works in the same way. In the example above we’re essentially asking for the ‘third shuffle’ in the standard list of shuffles - which is also the same every time as we’re always setting the random seed to the same value right before the shuffle. Finally we’re just ticking through our shuffled notes to play the riff.
Now, here’s where the fun starts. If we change the random seed value to another number, say 3000, we get an entirely different shuffling of the notes. So now it’s extremely easy to explore new melodies. Simply choose the list of notes we want to shuffle (scales are a great starting point) and then choose the seed we want to shuffle with. If we don’t like the melody, just change one of those two things and try again. Repeat until you like what you hear!
Sonic Pi’s randomisation is not actually random it’s what’s called pseudo random. Imagine if you were to roll a dice 100 times and write down the result of each roll onto a piece of paper. Sonic Pi has the equivalent of this list of results which it uses when you ask for a random value. Instead of rolling an actual dice, it just picks the next value from the list. Setting the random seed is just jumping to a specific point in that list.
Another important aspect to our riff is the rhythm - when to play a note
and when not to. As we saw above we can use :r
in our rings to insert
rests. Another very powerful way is to use spreads which we’ll cover in
a future tutorial. Today we’ll use randomisation to help us find our
rhythm. Instead of playing every note we can use a conditional to play a
note with a given probability. Let’s take a look:
live_loop :random_riff do
use_synth :dsaw
use_random_seed 30
notes = (scale :e3, :minor_pentatonic).shuffle
16.times do
play notes.tick, release: 0.2, cutoff: 90 if one_in(2)
sleep 0.125
end
end
A really useful fn to know is one_in
which will give us a
true
or false
value with the specified probability. Here, we’re
using a value of 2 so on average one time every two calls to one_in
it
will return true
. In other words, 50% of the time it will return
true
. Using higher values will make it return false
more often
introducing more space into the riff.
Notices that we’ve added some iteration in here with 16.times
. This is
because we only want to reset our random seed value every 16 notes so
our rhythm repeats every 16 times. This doesn’t affect the shuffling as
that is still done immediately after the seed is set. We can use the
iteration size to alter the length of the riff. Try changing the 16 to 8
or even 4 or 3 and see how it affects the rhythm of the riff.
OK, so let’s combine everything we’ve learned together into one final example. See you next time!
live_loop :random_riff do
# uncomment to bring in:
# synth :blade, note: :e4, release: 4, cutoff: 100, amp: 1.5
use_synth :dsaw
use_random_seed 43
notes = (scale :e3, :minor_pentatonic, num_octaves: 2).shuffle.take(8)
8.times do
play notes.tick, release: rand(0.5), cutoff: rrand(60, 130) if one_in(2)
sleep 0.125
end
end
live_loop :drums do
use_random_seed 500
16.times do
sample :bd_haus, rate: 2, cutoff: 110 if rand < 0.35
sleep 0.125
end
end
live_loop :bd do
sample :bd_haus, cutoff: 100, amp: 3
sleep 0.5
end
It’s impossible to look through the history of electronic dance music without seeing the enormous impact of the tiny Roland TB-303 synthesiser. It’s the secret sauce behind the original acid bass sound. Those classic squealing and squelching TB-303 bass riffs can be heard from the early Chicago House scene through to more recent electronic artists such as Plastikman, Squarepusher and Aphex Twin.
Interestingly, Roland never intended for the TB-303 to be used in dance music. It was originally created as a practice aid for guitarists. They imagined that people would program them to play bass lines to jam along to. Unfortunately there were a number of problems: they were a little fiddly to program, didn’t sound particularly good as a bass-guitar replacement and were pretty expensive to buy. Deciding to cut their losses, Roland stopped making them after 10,000 units were sold and after a number of years sitting on guitarist’s shelves, they soon could be found in the windows of second hand shops. These lonely discarded TB-303s were waiting to be discovered by a new generation of experimenters who started using them in ways that Roland didn’t imagine to create new crazy sounds. Acid House was born.
Although getting your hands on an original TB-303 is not so easy you will be pleased to know that you can turn your Raspberry Pi into one using the power of Sonic Pi. Behold, fire up Sonic Pi and throw this code into an empty buffer and hit Run:
use_synth :tb303
play :e1
Instant acid bass! Let’s play around…
First, let’s build a live arpeggiator to make things fun. In the last tutorial we looked at how riffs can just be a ring of notes that we tick through one after another, repeating when we get to the end. Let’s create a live loop that does exactly that:
use_synth :tb303
live_loop :squelch do
n = (ring :e1, :e2, :e3).tick
play n, release: 0.125, cutoff: 100, res: 0.8, wave: 0
sleep 0.125
end
Take a look at each line.
On the first line we set the default synth to be tb303
with the
use_synth
fn.
On line two we create a live loop called :squelch
which will just
loop round and round.
Line three is where we create our riff - a ring of notes (E in
octaves 1, 2, and 3) which we simply tick through with .tick
. We
define n
to represent the current note in the riff. The equals sign
just means to assign the value on the right to the name on the
left. This will be different every time round the loop. The first
time round, n
will be set to :e1
. The second time round it will
be :e2
, followed by :e3
, and then back to :e1
, cycling round
forever.
Line four is where we actually trigger our :tb303
synth. We’re
passing a few interesting opts here: release:
, cutoff:
, res:
and wave:
which we’ll discuss below.
Line five is our sleep
- we’re asking the live loop to loop round
every 0.125
s or 8 times a second at the default BPM of 60.
Line six is the end
to the live loop. This just tells Sonic Pi
where the end of the live loop is.
Whilst you’re still figuring out what’s going on, type in the code above
and hit the Run button. You should hear the :tb303
kick into
action. Now, this is where the action is: let’s start live coding.
Whilst the loop is still live, change the cutoff:
opt to 110
. Now
hit the Run button again. You should hear the sound become a little
harsher and more squelchy. Dial in 120
and hit run. Now 130
. Listen
how higher cutoff values make it sound more piercing and
intense. Finally, drop it down to 80
when you feel like a rest. Then
repeat as many times as you want. Don’t worry, I’ll still be here…
Another opt worth playing with is res:
. This controls the level of
resonance of the filter. A high resonance is characteristic of acid bass
sounds. We currently have our res:
set to 0.8
. Try cranking it up to
0.85
, then 0.9
, and finally 0.95
. You might find that a cutoff
such as 110
or higher will make the differences easier to
hear. Finally go crazy and dial in 0.999
for some insane sounds. At a
res
this high, you’re hearing the cutoff filter resonate so much it
starts to make sounds of its own!
Finally, for a big impact on the timbre try changing the wave:
opt to
1
. This is the choice of source oscillator. The default is 0
which
is a sawtooth wave. 1
is a pulse wave and 2
is a triangle wave.
Of course, try different riffs by changing the notes in the ring or even picking notes from scales or chords. Have fun with your first acid bass synth.
The design of the original TB-303 is actually pretty simple. As you can see from the following diagram there’s only 4 core parts.
First is the oscillator wave - the raw ingredients of the sound. In this
case we have a square wave. Next there’s the oscillator’s amplitude
envelope which controls the amp of the square wave through time. These
are accessed in Sonic Pi by the attack:
, decay:
, sustain:
and
release:
opts along with their level counterparts. For more
information read Section 2.4 ‘Duration with Envelopes’ in the built-in
tutorial. We then pass our enveloped square wave through a resonant low
pass filter. This chops off the higher frequencies as well as having
that nice resonance effect. Now this is where the fun starts. The cutoff
value of this filter is also controlled by its own envelope! This means
we have amazing control over the timbre of the sound by playing with
both of these envelopes. Let’s take a look:
use_synth :tb303
with_fx :reverb, room: 1 do
live_loop :space_scanner do
play :e1, cutoff: 100, release: 7, attack: 1, cutoff_attack: 4, cutoff_release: 4
sleep 8
end
end
For each standard envelope opt, there’s a cutoff_
equivalent opt in
the :tb303
synth. So, to change the cutoff attack time we can use the
cutoff_attack:
opt. Copy the code above into an empty buffer and hit
Run. You’ll hear a crazy sound warble in and out. Now start to play. Try
changing the cutoff_attack:
time to 1
and then 0.5
. Now try 8
.
Notice that I’ve passed everything through a :reverb
FX for extra
atmosphere - try other FX to see what works!
Finally, here’s a piece I composed using the ideas in this tutorial. Copy it into an empty buffer, listen for a while and then start live coding your own changes. See what crazy sounds you can make with it! See you next time…
use_synth :tb303
use_debug false
with_fx :reverb, room: 0.8 do
live_loop :space_scanner do
with_fx :slicer, phase: 0.25, amp: 1.5 do
co = (line 70, 130, steps: 8).tick
play :e1, cutoff: co, release: 7, attack: 1, cutoff_attack: 4, cutoff_release: 4
sleep 8
end
end
live_loop :squelch do
use_random_seed 3000
16.times do
n = (ring :e1, :e2, :e3).tick
play n, release: 0.125, cutoff: rrand(70, 130), res: 0.9, wave: 1, amp: 0.8
sleep 0.125
end
end
end
Hello and welcome back! In the previous tutorials we’ve focussed purely on the music possibilities of Sonic Pi - (turning your Raspberry Pi into a performance ready musical instrument). So far we’ve learned how to:
There’s so much more to show you (which we will explore in future editions). However, this month, let’s look at something Sonic Pi can do that you probably didn’t realise: control Minecraft.
OK, let’s get started. Boot up your Raspberry Pi, fire up Minecraft Pi and create a new world. Now start up Sonic Pi and re-size and move your windows so you can see both Sonic Pi and Minecraft Pi at the same time.
In a fresh buffer type the following:
mc_message "Hello Minecraft from Sonic Pi!"
Now, hit Run. Boom! Your message appeared in Minecraft! How easy was that? Now, stop reading this for a moment and play about with your own messages. Have fun!
Now let’s do some exploring. The standard option is to reach for the mouse and keyboard and start walking around. That works, but it’s pretty slow and boring. It would be far better if we had some sort of teleport machine. Well, thanks to Sonic Pi, we have one. Try this:
mc_teleport 80, 40, 100
Crikey! That was a long way up. If you weren’t in flying-mode then you would have fallen back down all the way to the ground. If you double-tap space to enter flying-mode and teleport again, you’ll stay hovering at the location you zap to.
Now, what do those numbers mean? We have three numbers which describe the coordinates of where in the world we want to go. We give each number a name - x, y and z:
By choosing different values for x, y and z we can teleport anywhere in our world. Try it! Choose different numbers and see where you can end up. If the screen goes black it’s because you’ve teleported yourself under the ground or into a mountain. Just choose a higher y value to get back out above land. Keep on exploring until you find somewhere you like…
Using the ideas so far, let’s build a Sonic Teleporter which makes a fun teleport sound whilst it whizzes us across the Minecraft world:
mc_message "Preparing to teleport...."
sample :ambi_lunar_land, rate: -1
sleep 1
mc_message "3"
sleep 1
mc_message "2"
sleep 1
mc_message "1"
sleep 1
mc_teleport 90, 20, 10
mc_message "Whoooosh!"
Now you’ve found a nice spot, let’s start building. You could do what you’re used to and start clicking the mouse furiously to place blocks under the cursor. Or you could use the magic of Sonic Pi. Try this:
x, y, z = mc_location
mc_set_block :melon, x, y + 5, z
Now look up! There’s a melon in the sky! Take a moment to look at the
code. What did we do? On line one we grabbed the current location of
Steve as the variables x, y and z. These correspond to our coordinates
described above. We use these coordinates in the fn mc_set_block
which
will place the block of your choosing at the specified coordinates. In
order to make something higher up in the sky we just need to increase
the y value which is why we add 5 to it. Let’s make a long trail of them:
live_loop :melon_trail do
x, y, z = mc_location
mc_set_block :melon, x, y-1, z
sleep 0.125
end
Now, jump over to Minecraft, make sure you’re in flying-mode (double tap space if not) and fly all around the world. Look behind you to see a pretty trail of melon blocks! See what kind of twisty patterns you can make in the sky.
Those of you that have been following this tutorial over the last few
months will probably have your minds blown at this point. The trail of
melons is pretty cool, but the most exciting part of the previous
example is that you can use live_loop
with Minecraft! For those that
don’t know, live_loop
is Sonic Pi’s special magic ability that no
other programming language has. It lets you run multiple loops at the
same time and allows you to change them whilst they run. They are
incredibly powerful and amazing fun. I use live_loop
s to perform music
in nightclubs with Sonic Pi - DJs use discs and I use live_loop
s :-)
However, today we’re going to live code both music and Minecraft.
Let’s get started. Run the code above and start making your melon
trail again. Now, without stopping the code, just simply change :melon
to
:brick
and hit run. Hey presto, you’re now making a brick trail. How
simple was that! Fancy some music to go with it? Easy. Try this:
live_loop :bass_trail do
tick
x, y, z = mc_location
b = (ring :melon, :brick, :glass).look
mc_set_block b, x, y -1, z
note = (ring :e1, :e2, :e3).look
use_synth :tb303
play note, release: 0.1, cutoff: 70
sleep 0.125
end
Now, whilst that’s playing start changing the code. Change the block
types - try :water
, :grass
or your favourite block type. Also, try
changing the cutoff value from 70
to 80
and then up to 100
. Isn’t
this fun?
Let’s combine everything we’ve seen so far with a little extra magic. Let’s combine our teleportation ability with block placing and music to make a Minecraft Music Video. Don’t worry if you don’t understand it all, just type it in and have a play by changing some of the values whilst it’s running live. Have fun and see you next time…
live_loop :note_blocks do
mc_message "This is Sonic Minecraft"
with_fx :reverb do
with_fx :echo, phase: 0.125, reps: 32 do
tick
x = (range 30, 90, step: 0.1).look
y = 20
z = -10
mc_teleport x, y, z
ns = (scale :e3, :minor_pentatonic)
n = ns.shuffle.choose
bs = (knit :glass, 3, :sand, 1)
b = bs.look
synth :beep, note: n, release: 0.1
mc_set_block b, x+20, n-60+y, z+10
mc_set_block b, x+20, n-60+y, z-10
sleep 0.25
end
end
end
live_loop :beats do
sample :bd_haus, cutoff: 100
sleep 0.5
end
After our brief excursion to the fantastic world of coding Minecraft with Sonic Pi last month, let’s get musical again. Today we’re going to bring a classical operatic dance piece straight into the 21st century using the awesome power of code.
Let’s jump into a time machine back to the year 1875. A composer called Bizet had just finished his latest opera Carmen. Unfortunately like many exciting and disruptive new pieces of music people initially didn’t like it at all because it was too outrageous and different. Sadly Bizet died ten years before the opera gained huge international success and became one of the most famous and frequently performed operas of all time. In sympathy with this tragedy let’s take one of the main themes from Carmen and convert it to a modern format of music that is also too outrageous and different for most people in our time - live coded music!
Trying to live code the whole opera would be a bit of a challenge for this tutorial, so let’s focus on one of the most famous parts - the bass line to the Habanera:
This may look extremely unreadable to you if you haven’t yet studied music notation. However, as programmers we see music notation as just another form of code - only it represents instructions to a musician instead of a computer. We therefore need to figure out a way of decoding it.
The notes are arranged from left to right like the words in this magazine but also have different heights. The height on the score represents the pitch of the note. The higher the note on the score, the higher the pitch of the note.
In Sonic Pi we already know how to change the pitch of a note - we
either use high or low numbers such as play 75
and play 80
or we use
the note names: play :E
and play :F
. Luckily each of the vertical
positions of the musical score represents a specific note name. Take a
look at this handy look up table:
Music scores are an extremely rich and expressive kind of code capable
of communicating many things. It therefore shouldn’t come as much of a
surprise that musical scores can not only tell you what notes to play but
also when not to play notes. In programming this is pretty much
equivalent to the idea of nil
or null
- the absence of a value. In
other words not playing a note is like the absence of a note.
If you look closely at the score you’ll see that it’s actually a
combination of black dots with lines which represent notes to play and
squiggly things which represent the rests. Luckily Sonic Pi has a very
handy representation for a rest: :r
, so if we run: play :r
it
actually plays silence! We could also write play :rest
, play nil
or
play false
which are all equivalent ways of representing rests.
Finally, there’s one last thing to learn how to decode in the notation - the timings of the notes. In the original notation you’ll see that the notes are connected with thick lines called beams. The second note has two of these beams which means it lasts for a 16th of a beat. The other notes have a single beam which means they last for an 8th of a beat. The rest has two squiggly beams which means it also represents a 16th of the beat.
When we attempt to decode and explore new things a very handy trick is to make everything as similar as possible to try and see any relationships or patterns. For example, when we re-write our notation purely in 16ths you can see that our notation just turns into a nice sequence of notes and rests.
We’re now in a position to start translating this bass line to Sonic Pi. Let’s encode these notes and rests in a ring:
(ring :d, :r, :r, :a, :f5, :r, :a, :r)
Let’s see what this sounds like. Throw it in a live loop and tick through it:
live_loop :habanera do
play (ring :d, :r, :r, :a, :f5, :r, :a, :r).tick
sleep 0.25
end
Fabulous, that instantly recognisable riff springs to life through your speakers. It took a lot of effort to get here, but it was worth it - high five!
Now we have the bass line, let’s re-create some of the ambience of the
operatic scene. One synth to try out is :blade
which is a moody 80s
style synth lead. Let’s try it with the starting note :d
passed
through a slicer and reverb:
live_loop :habanera do
use_synth :fm
use_transpose -12
play (ring :d, :r, :r, :a, :f5, :r, :a, :r).tick
sleep 0.25
end
with_fx :reverb do
live_loop :space_light do
with_fx :slicer, phase: 0.25 do
synth :blade, note: :d, release: 8, cutoff: 100, amp: 2
end
sleep 8
end
end
Now, try the other notes in the bass line: :a
and :f5
. Remember, you
don’t need to hit stop, just modify the code whilst the music is playing
and hit run again. Also, try different values for the slicer’s phase:
opt such as 0.5
, 0.75
and 1
.
Finally, let’s combine all the ideas so far into a new remix of the
Habanera. You might notice that I’ve included another part of the bass
line as a comment. Once you’ve typed it all into a fresh buffer hit Run
to hear the composition. Now, without hitting stop, uncomment the
second line by removing the #
and hit run again - how marvellous is
that! Now, start mashing it around yourself and have fun.
use_debug false
bizet_bass = (ring :d, :r, :r, :a, :f5, :r, :a, :r)
#bizet_bass = (ring :d, :r, :r, :Bb, :g5, :r, :Bb, :r)
with_fx :reverb, room: 1, mix: 0.3 do
live_loop :bizet do
with_fx :slicer, phase: 0.125 do
synth :blade, note: :d4, release: 8,
cutoff: 100, amp: 1.5
end
16.times do
tick
play bizet_bass.look, release: 0.1
play bizet_bass.look - 12, release: 0.3
sleep 0.125
end
end
end
live_loop :ind do
sample :loop_industrial, beat_stretch: 1,
cutoff: 100, rate: 1
sleep 1
end
live_loop :drums do
sample :bd_haus, cutoff: 110
synth :beep, note: 49, attack: 0,
release: 0.1
sleep 0.5
end
Everyone has played Minecraft. You will all have built amazing structures, designed cunning traps and even created elaborate cart lines controlled by redstone switches. How many of you have performed with Minecraft? We bet you didn’t know that you could use Minecraft to create amazing visuals just like a professional VJ.
If your only way of modifying Minecraft was with the mouse, you’d have a tough time changing things fast enough. Luckily for you your Raspberry Pi comes with a version of Minecraft that can be controlled with code. It also comes with an app called Sonic Pi which makes coding Minecraft not only easy but also incredibly fun.
In today’s article we’ll be showing you some of the tips and tricks that we’ve used to create performances in night clubs and music venues around the world.
Let’s get started…
Let’s start with a simple warm up exercise to refresh ourselves with the basics. First up, crack open your Raspberry Pi and then fire up both Minecraft and Sonic Pi. In Minecraft, create a new world, and in Sonic Pi choose a fresh buffer and write in this code:
mc_message "Let's get started..."
Hit the Run button and you’ll see the message over in the Minecraft window. OK, we’re ready to start, let’s have some fun……
When we’re using Minecraft to create visuals we try and think about what will both look interesting and also be easy to generate from code. One nice trick is to create a sand storm by dropping sand blocks from the sky. For that all we need are a few basic fns:
sleep
- for inserting a delay between actionsmc_location
- to find our current locationmc_set_block
- to place sand blocks at a specific locationrrand
- to allow us to generate random values within a rangelive_loop
- to allow us to continually make it rain sandIf you’re unfamiliar with any of the built-in fns such as rrand
, just
type the word into your buffer, click on it and then hit the keyboard
combo Control-i
to bring up the built-in documentation. Alternatively
you can navigate to the lang tab in the Help system and then look up
the fns directly along with all the other exciting things you can do.
Let’s make it rain a little first before unleashing the full power of the storm. Grab your current location and use it to create a few sand blocks up in the sky nearby:
x, y, z = mc_location
mc_set_block :sand, x, y + 20, z + 5
sleep 2
mc_set_block :sand, x, y + 20, z + 6
sleep 2
mc_set_block :sand, x, y + 20, z + 7
sleep 2
mc_set_block :sand, x, y + 20, z + 8
When you hit Run, you might have to look around a little as the blocks may start falling down behind you depending on which direction you’re currently facing. Don’t worry, if you missed them just hit Run again for another batch of sand rain - just make sure you’re looking the right way!
Let’s quickly review what’s going on here. On the first line we grabbed
Steve’s location as coordinates with the fn mc_location
and placed
them into the vars x
, y
, and z
. Then on the next lines we used the
mc_set_block
fn to place some sand at the same coordinates as Steve
but with some modifications. We chose the same x coordinate, a y
coordinate 20 blocks higher and then successively larger z coordinates
so the sand dropped in a line away from Steve.
Why don’t you take that code and start playing around with it yourself?
Try adding more lines, changing the sleep times, try mixing :sand
with
:gravel
and choose different coordinates. Just experiment and have fun!
OK, it’s time to get the storm raging by unleashing the full power of
the live_loop
- Sonic Pi’s magical ability which unleashes the full
power of live coding - changing code on-the-fly whilst it’s running!
live_loop :sand_storm do
x, y, z = mc_location
xd = rrand(-10, 10)
zd = rrand(-10, 10)
co = rrand(70, 130)
synth :cnoise, attack: 0, release: 0.125, cutoff: co
mc_set_block :sand, x + xd, y+20, z+zd
sleep 0.125
end
What fun! We’re looping round pretty quickly (8 times a second) and during each loop we’re finding Steve’s location like before but then generating 3 random values:
xd
- the difference for x which will be between -10 and 10zd
- the difference for z also between -10 and 10co
- a cutoff value for the low pass filter between 70 and 130We then use those random values in the fns synth
and mc_set_block
giving us sand falling in random locations around Steve along with a
percussive rain-like sound from the :cnoise
synth.
For those of you new to live loops - this is where the fun really starts
with Sonic Pi. Whilst the code is running and the sand is pouring down,
try changing one of the values, perhaps the sleep time to 0.25
or the
:sand
block type to :gravel
. Now hit run again. Hey Presto! Things
changed without the code stopping. This is your gateway to performing
like a real VJ. Keep practising and changing things around. How
different can you make the visuals without stopping the code?
Finally, another great way of generating interesting visuals is to
generate huge patterned walls to fly towards and close by. For this
effect we’ll need to move from placing the blocks randomly to placing
them in an ordered manner. We can do this by nesting two sets of
iteration (hit the Help button and navigate to section 5.2 of the
tutorial “Iteration and Loops” for more background on iteration). The
funny |xd|
after the do means that xd
will be set for each value of
the iteration. So the first time it will be 0, then 1, then 2… etc. By
nesting two lots of iteration together like this we can generate all the
coordinates for a square. We can then randomly choose block types from a
ring of blocks for an interesting effect:
x, y, z = mc_location
bs = (ring :gold, :diamond, :glass)
10.times do |xd|
10.times do |yd|
mc_set_block bs.choose, x + xd, y + yd, z
end
end
Pretty neat. Whilst we’re having fun here, try changing bs.choose
to
bs.tick
to move from a random pattern to a more regular one. Try
changing the block types and the more adventurous of you might want to
try sticking this within a live_loop
so that the patterns keep changing
automatically.
Now, for the VJ finale - change the two 10.times
to 100.times
and
hit Run. Kaboom! A Huge gigantic wall of randomly placed bricks. Imagine
how long it would take you to build that manually with your mouse!
Double-tap space to enter fly-mode and start swooping by for some great
visual effects. Don’t stop here though - use your imagination to conjure
up some cool ideas and then use the coding power of Sonic Pi to make it
real. When you’ve practised enough dim the lights and put on a VJ show
for your friends!
Back in episode 4 of this tutorial series we took a brief look at randomisation whilst coding up some sizzling synth riffs. Given that randomisation is such an important part of my live coding DJ sets I thought it would be useful to cover the fundamentals in much greater detail. So, get your lucky hat on and let’s surf some random streams!
The first thing to learn which might really surprise you when playing
with Sonic Pi’s randomisation functions is that they’re not actually
really random. What does this actually mean? Well, let’s try a couple of
tests. First, imagine a number in your head between 0 and 1. Keep it
there and don’t tell me. Now let me guess… was it 0.321567
? No? Bah,
I’m clearly no good at this. Let me have another go, but let’s ask Sonic
Pi to choose a number this time. Fire up Sonic Pi v2.7+ and ask it for a
random number but again don’t tell me:
print rand
Now for the reveal… was it 0.75006103515625
? Yes! Ha, I can see
you’re a little sceptical. Perhaps it was just a lucky guess. Let’s try
again. Press the Run button again and see what we get… What?
0.75006103515625
again? This clearly can’t be random! You’re right,
it’s not.
What’s going on here? The fancy computer science word here is
determinism. This just means that nothing is by chance and everything is
destined to be. Your version of Sonic Pi is destined to always return
0.75006103515625
in the program above. This might sound pretty
useless, but let me assure you that it’s one of the most powerful parts
of Sonic Pi. If you stick at it you’ll learn how to rely on the
deterministic nature of Sonic Pi’s randomisation as a fundamental
building block to your compositions and live coded DJ sets.
When Sonic Pi boots it actually loads into memory a sequence of 441,000
pre-generated random values. When you call a random function such as
rand
or rrand
, this random stream is used to generate your
result. Each call to a random function consumes a value from this
stream. Therefore the 10th call to a random function will use the 10th
value from the stream. Also, every time you press the Run button, the
stream is reset for that run. This is why I could predict the result to
rand
and why the ‘random’ melody was the same every time. Everybody’s
version of Sonic Pi uses the exact same random stream which is very
important when we start sharing our pieces with each other.
Let’s use this knowledge to generate a repeatable random melody:
8.times do
play rrand_i(50, 95)
sleep 0.125
end
Type this into a spare buffer and hit Run. You’ll hear a melody consisting of ‘random’ notes between 50 and 95. When it’s finished, hit Run again to hear exactly the same melody again.
start breakout box ## Handy Randomisation Functions Sonic Pi comes with a number of useful functions for working with the random stream. Here’s a list of some of the most useful:
rand
- Simply returns the next value in the random streamrrand
- Returns a random value within a rangerrand_i
- Returns a random whole number within a rangeone_in
- Returns true or false with the given probabilitydice
- Imitates rolling a dice and returns a value between 1 and 6choose
- Chooses a random value from a listCheck out their documentation in the Help system for detailed information and examples.
end breakout box
Whilst the ability to repeat a sequence of chosen notes is essential to allow you to replay a riff on the dance floor, it might not be the exactly riff you want. Wouldn’t it be great if we could try a number of different riffs and choose the one we liked best? This is where the real magic starts.
We can manually set the stream with the fn use_random_seed
. In
Computer Science, a random seed is the starting point from which a new
stream of random values can sprout out and blossom. Let’s try it:
use_random_seed 0
3.times do
play rrand_i(50, 95)
sleep 0.125
end
Great, we get the first three notes of our random melody above: 84
,
83
and 71
. However, we can now change the seed to something
else. How about this:
use_random_seed 1
3.times do
play rrand_i(50, 95)
sleep 0.125
end
Interesting, we get 83
, 71
and 61
. You might notice that the
first two numbers here are the same as the last two numbers before -
this isn’t a coincidence.
Remember that the random stream is just a giant list of ‘pre-rolled’ values. Using a random seed simply jumps us to a point in that list. Another way of thinking about it is to imagine a huge deck of pre-shuffled cards. Using a random seed is cutting the deck at a particular point. The fabulous part of this is that it’s precisely this ability to jump around the random which gives us huge power when making music.
Let’s revisit our random melody of 8 notes with this new stream resetting power, but let’s also throw in a live loop so we can experiment live whilst it’s playing:
live_loop :random_riff do
use_random_seed 0
8.times do
play rrand_i(50, 95), release: 0.1
sleep 0.125
end
end
Now, whilst it’s still playing, change the seed value from 0
to
something else. Try 100
, what about 999
. Try your own values,
experiment and play around - see which seed generates the riff you like
best.
This month’s tutorial has been quite a technical dive into the workings of Sonic Pi’s randomisation functionality. Hopefully it has given you some insight into how it works and how you can start using randomisation in a reliable way to create repeatable patterns within your music. It’s important to stress that you can use repeatable randomisation anywhere you want. For example, you can randomise the amplitude of notes, the timing of the rhythm, the amount of reverb, the current synth, the mix of an FX, etc. etc. In the future we’ll take a close look at some of these applications, but for now let me leave you with a short example.
Type the following into a spare buffer, hit Run, and then start changing the seeds around, hit Run again (whilst it’s still playing) and explore the different sounds, rhythms and melodies you can make. When you find a nice one, remember the seed number so you can get back to it. Finally, when you’ve found a few seeds you like, put on a live coded performance for your friends by simply switching between your favourite seeds to create a full piece.
live_loop :random_riff do use_random_seed 10300 use_synth :prophet s = [0.125, 0.25, 0.5].choose 8.times do r = [0.125, 0.25, 1, 2].choose n = (scale :e3, :minor).choose co = rrand(30, 100) play n, release: r, cutoff: co sleep s end end
live_loop :drums do use_random_seed 2001 16.times do r = rrand(0.5, 10) sample :drum_bass_hard, rate: r, amp: rand sleep 0.125 end end
So far during this series we’ve focussed on triggering sounds. We’ve
discovered that we can trigger the many synths built into Sonic Pi with
play
or synth
and how to trigger pre-recorded samples with
sample
. We’ve also looked at how we can wrap these triggered sounds
within studio FX such as reverb and distortion using the with_fx
command. Combine this with Sonic Pi’s incredibly accurate timing system
and you can produce a vast array of sounds, beats and riffs. However,
once you’ve carefully selected a particular sound’s options and
triggered it, there’s no ability to mess with it whilst it’s playing
right? Wrong! Today you’re going to learn something very powerful - how
to control running synths.
Let’s create a nice simple sound. Fire up Sonic Pi and in a fresh buffer type the following:
synth :prophet, note: :e1, release: 8, cutoff: 100
Now press the Run button at the top left to hear a lovely rumbling synth sound. Go ahead, press it again a few times to get a feel for it. OK, done? Let’s start controlling it!
A little known feature in Sonic Pi is that the fns play
, synth
and
sample
, return something called a SynthNode
which represents a
running sound. You can capture one of these SynthNode
s using a
standard variable and then control it at a later point in time. For
example, let’s change the value of the cutoff:
opt after 1 beat:
sn = synth :prophet, note: :e1, release: 8, cutoff: 100
sleep 1
control sn, cutoff: 130
Let’s look at each line in turn:
Firstly we trigger the :prophet
synth using the synth
fn as
norml. However we also capture the result in a variable called sn
. We
could have called this variable something completely different such as
synth_node
or jane
- the name doesn’t matter. However, it’s
important to choose a name that’s meaningful to you for your
performances and for people reading your code. I chose sn
as it’s a nice
short mnemonic for synth node.
On line 2 we have a standard sleep
command. This does nothing special
- it just asks the computer to wait for 1 beat before moving onto the
next line.
Line 3 is where the control fun starts. Here, we use the control
fn to
tell our running SynthNode
to change the cutoff value to 130
. If you
hit the Run button, you’ll hear the :prophet
synth start playing
as before, but after 1 beat it will shift to sound a lot brighter.
** Breakout Box Start ** Modulatable Options
Most of Sonic Pi’s synths and FX opts may be changed after being
triggered. However, this isn’t the case for all of them. For example,
the envelope opts attack:
, decay:
, sustain:
and release:
can
only be set when triggering the synth. Figuring out which opts can and
can’t be changed is simple - just head to the documentation for a given
synth or FX and then scroll down to the individual option documentation
and look for the phrases “May be changed whilst playing “ or “Can not be
changed once set”. For example, the documentation for the :beep
synth’s attack:
opt makes it clear that it’s not possible to change
it:
Whilst a synth is running you’re not limited to changing it only once -
you’re free to change it as many times as you like. For example, we can
turn our :prophet
into a mini arpegiator with the following:
notes = (scale :e3, :minor_pentatonic)
sn = synth :prophet, note: :e1, release: 8, cutoff: 100
sleep 1
16.times do
control sn, note: notes.tick
sleep 0.125
end
In this snippet of code we just added a couple of extra things. First we
defined a new variable called notes
which contains the notes we’d like
to cycle through (an arpeggiator is just a fancy name for something that
cycles through a list of notes in order). Secondly we replaced our
single call to control
with an iteration calling it 16 times. In each
call to control
we .tick
through our ring of notes
which will
automaticall repeat once we get to the end (thanks to the fabulous power
of Sonic Pi’s rings). For a bit of variety try replacing .tick
with
.choose
and see if you can hear the difference.
Note that we can change multiple opts simultaneously. Try changing the control line to the following and listen for the difference:
control sn, note: notes.tick, cutoff: rrand(70, 130)
When we control a SynthNode
, it responds exactly on time and instantly
changes the value of the opt to the new one as if you’d pressed a button
or flicked a switch requesting the change. This can sound rhythmical and percussive -
especially if the opt controls an aspect of the timbre such as
cutoff:
. However, sometimes you don’t want the change to happen
instantaneously. Instead, you might want to smoothly move from the
current value to the new one as if you’d moved a slider or dial. Of
course, Sonic Pi can also do this too using the _slide:
opts.
Each opt that can be modified also has a special corresonding _slide:
opt that allows you to specify a slide time. For example, amp:
has
amp_slide:
and cutoff:
has cutoff_slide:
. These slide opts work
slightly differently than all the other opts in that they tell the synth
note how to behave next time they are controlled. Let’s take a look:
sn = synth :prophet, note: :e1, release: 8, cutoff: 70, cutoff_slide: 2
sleep 1
control sn, cutoff: 130
Notice how this example is exactly the same as before except with the
addition of cutoff_slide:
. This is saying that next time this synth
has its cutoff:
opt controlled, it will take 2 beats to slide from the
current value to the new one. Therefore, when we use control
you can
hear the cutoff slide from 70 to 130. It creates an interesting dynamic
feel to the sound. Now, try changing the cutoff_slide:
time to a
shorter value such as 0.5 or a longer value such as 4 to see how it
changes the sound. Remember, you can slide any of the modifiable opts in
exactly this way and each _slide:
value can be totally different so
you can have the cutoff sliding slowly, the amp sliding fast and the pan
sliding somewhere in between if that’s what you’re looking to create…
Let’s look at a short example which demonstrates the power of controlling synths after they’ve been triggered. Notice that you can also slide FX just like synths although with a slightly different syntax. Check out section 7.2 of the built-in tutorial for more information on controlling FX.
Copy the code into a spare buffer and take a listen. Don’t stop there though - play around with the code. Change the slide times, change the notes, the synth, the FX and the sleep times and see if you can turn it into something completely different!
live_loop :moon_rise do
with_fx :echo, mix: 0, mix_slide: 8 do |fx|
control fx, mix: 1
notes = (scale :e3, :minor_pentatonic, num_octaves: 2).shuffle
sn = synth :prophet , sustain: 8, note: :e1, cutoff: 70, cutoff_slide: 8
control sn, cutoff: 130
sleep 2
32.times do
control sn, note: notes.tick, pan: rrand(-1, 1)
sleep 0.125
end
end
end