1 - Witaj Kolego :-)

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ę…


1.1 - Kodowanie na żywo

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.

Uwolnij swój umysł

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ść…

Żywa Pętla

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:

  1. Upewnij się, że wciąż słyszysz dźwięk uderzającego bębna.
  2. Zmień wartość znajdującą się przy poleceniu sleep z wartości `0.5’ na większą, np. 1.
  3. Ponownie naciśnij przycisk Run
  4. Zauważ jak zmieniła się szybkość uderzeń bębna.
  5. Zapamiętaj ten moment, gdyż jest to twój pierwszy raz (i prawdopobnie nie ostatni), kiedy kodujesz na żywo używając Sonic Pi…

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ć:

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…


1.2 - Interfejs użytkownika w Sonic Pi

Sonic Pi ma bardzo prosty interfejs umożliwiający kodowanie muzyki. Poświęćmy chwilę na zapoznanie się z nim.

Sonic Pi Interface

A. Kontrola Odtwarzania (Play Controls)

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.

B. Kontrola Edytora (Editor Controls)

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).

C. Informacje i Pomoc (Info and Help)

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.

D. Edytor Kodu (Code Editor)

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.

E. Panel preferencji (Prefs Panel)

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.

F. Podgląd Logów (Log Viewer)

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.

G. System Pomocy (Help System)

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.


1.3 - Nauka przez zabawę

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.

Błędów nie ma

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ł.

Zacznij od czegoś prostego

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!


2 - Syntezatory

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…


2.1 - Twoje pierwsze bipy

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…

Bip!

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.

Akordy

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.

Melodia

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.

Tradycyjne Nazwy Nut

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.


2.2 - Parametry Syntezatorów: Amplituda i Balans

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.

Parametry

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

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.

Amplituda w górę

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

Balansowanie

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!


2.3 - Przełączanie Syntezatoró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.

Syntezatory

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.

Brzęczące piły i prophety

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.

Odkrywanie Syntezatorów (Synths)

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:

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.


2.4 - Czas trwania obwiedni dźwięku

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 dźwięku (ang. Duration)

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.

Amplituda (ang. Amplitude)

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.

Release Time - czas zanikania amplitudy

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:

release envelope

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.

Attack Time - czas narastania amplitudy

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:

attack release envelope

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

long attack short release envelope

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

short attack short release envelope

Sustain Time - czas podtrzymania amplitudy

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

ASR envelope

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.

Decay Time - czas opadania amplitudy

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

ADSR envelope

Obwiednie ADSR

Obwiednie ADSR

Podsumowując, obwiednie ADSR w Sonic Pi posiadają następujące fazy:

  1. attack - czas narastania amplitudy od zera do poziomu maksymalnego, którego wysokość jest określana przez parametr attack_level.
  2. decay - czas opadania amplitudy od poziomu maksymalnego (attack_level) do poziomu podtrzymania (sustain_level).
  3. sustain - czas podtrzymania dźwięku na poziomie określonym przez parametr sustain_level.
  4. release - czas zanikania amplitudy od poziomu podtrzymania (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…


3 - Sample

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…


3.1 - Wyzwalanie Sampli

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.

Odkrywanie Sampli

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:

A teraz zacznij wplatać sample w twoje własne kompozycje!


3.2 - Parametry Sampli: Amp i Pan

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:.

Zmiana głośności sampli

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

Przesuwanie sampli na różne kanały

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.


3.3 - Rozciąganie Sampli

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.

Reprezentacja Sampla

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.

Zmiana tempa

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.

Rozciągamy

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ć.

Proste Wyjaśnienie Częstotliwości Próbkowania

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).

Matematyka Stojąca Za Częstotliwością Próbkowania

(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:

sample graph

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.


3.4 - Opakowane Sample

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.

Obwiednie Amen

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

Automatyczne Podtrzymanie (Auto Sustain)

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.

Płynne Zanikanie (Fade Outs)

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.

Wejście i Wyjście (Fade In i Fade Out)

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.

Sprecyzowana faza podtrzymania (sustain)

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.

Talerze perkusyjne

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.


3.5 - Kawałki Sampli

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?

Wybór momentu startu

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

Wybór momentu zakończenia

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

Ustawianie startu i zakończenia

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!

Łączenie z tempem

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

Łączenie z obwiedniami

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…


3.6 - Sample Zewnętrzne

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.

Przenośność

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.

Sample Lokalne

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

3.7 - Sample Packs

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:

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?

Indexing Sample Packs

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.

Filtering Sample Packs

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:

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

Sources

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:

  1. “/path/to/samples” - a string representing a valid path to a directory
  2. “/path/to/samples/foo.wav” - a string representing a valid path to a sample

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.

Filters

Once you have a list of candidates you may use the following filtering types to further reduce the selection:

Composites

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#"]]]

Wrapping Up

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.


4 - Losowość

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.

Powtarzalność

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!

Haunted Bells (Nawiedzone Dzwony)

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

Losowe odcięcie

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

Losowe zarzewie

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.

choose (wybierz)

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

rrand

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):

rrand_i

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):

rand

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

rand_i

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.

dice

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!

one_in

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!


5 - Konstrukcje Programistyczne

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…


5.1 - Bloki kodu

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.


5.2 - Iteracja i Pętle

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.

Powtórzenie

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.

Iteracja

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

Zagnieżdżanie Iteracji

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

Zapętlanie

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!


5.3 - Instrukcje Warunkowe

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).

Rzut Monetą

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.

Proste polecenie if

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.


5.4 - Wątki

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.

Nieskończone Pętle

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).

Wątki na Ratunek

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

Uruchamianie w Wątkach

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.

Zasięg

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.

Dziedziczenie

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.

Nazywanie Wątków

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}

Tylko Jeden Wątek na Jedną Nazwę

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…


5.5 - Funkcje

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.

Definiowane Funkcji

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ę.

Uruchamianie Funkcji

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.

Funkcje są zapamiętywane pomiędzy poszczególnymi uruchomieniami

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).

Funkcje z Parametrami

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!


5.6 - Zmienne

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.

Komunikowanie Znaczenia

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.

Zarządzanie Powtórzeniami

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.

Przechwytywanie Wyników

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.


5.7 - Synchronizacja Wątków

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.

Odziedziczony Czas

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.

Cue (Wskazówka) i Sync (Synchronizacja)

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.

Nazwy dla komunikatów Cue

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 synchronizują 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.


6 - Studio FX

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…

Efekty gitarowe

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)!


6.1 - Dodawanie Efektów

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.

Efekt Reverb

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

Efekt Echo

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

Zagnieżdżanie Efektów

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.

Odkrywanie Efektów

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!


6.2 - Efekty w praktyce

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.


7 - Kontrola działających dźwięków

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ć.


7.1 - Kontrolowanie Uruchomionych Syntezatorów

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:.

Parametry Nie Dające Się Kontrolować

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.


7.2 - Kontrolowanie Efektów

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!


7.3 - Parametry Przesuwne

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ć.

Ślizganie jest lepkie

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.

Płynne Przejścia w Parametrach Efektów

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…


8 - Struktury Danych

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.


8.1 - Listy

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.

Granie Listy

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.

Dostęp do Listy

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…


8.2 - Akordy

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:

Arpeggia

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ł?


8.3 - Skale

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

Losowe nuty

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).

Odkrywanie Akordów i Skal

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.


8.4 - Pierścienie

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.

Tworzenie Pierścieni

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

Indeksowanie Pierścieni

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

Korzystanie z Pierścieni

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.

Skale i Akordy są Pierścieniami

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.

Konstruktory Pierścieni

Oprócz funkcji ring istnieje również kilka innnych funkcji, które pozwalają nam na tworzenie pierścieni.

Przyjrzyj 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).

Łańcuchy poleceń (Chain Commands)

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)

Wiele Łańcuchów

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:

  1. (ring 10, 20, 30, 40, 50) - nasz początkowy pierścień
  2. (ring 10, 20, 30, 40, 50).shuffle - tasujemy - (ring 40, 30, 10, 50, 20)
  3. (ring 10, 20, 30, 40, 50).shuffle.drop(1) - usuwamy 1-wszy element - (ring 30, 10, 50, 20)
  4. (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.

Niezmienność

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.

Dostępne Łańcuchy Metod

Oto lista metod, które możesz łączyć w łańcuchy wywołań:

Oczywiś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.


9 - Kodowanie na Żywo

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…


9.1 - Kodowanie na Żywo (Live Coding)

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…

Spróbujmy coś zmienić

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…


9.2 - Żywe Pętle (Live Loops)

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!


9.3 - Wiele Żywych Pętli

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ć.

Synchronizacja Żywych Pętli

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!


9.4 - Cykanie

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.

Cykające Pierścienie

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

Cyknięcie

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.

Look

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.

Nazwane Cyknięcia

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.

Nie twórz zbyt skomplikowanych rzeczy

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!


10 - Niezbędna Wiedza

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.


10.1 - Korzystanie ze Skrótów Klawiszowych

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.

Spójność pomiędzy Platformami

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.

Klawisze Control i Meta

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.

Skróty

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.

Zatrzymywanie i uruchamianie

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.

Nawigacja

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.

Elegacki Kod

Aby automatycznie wyrównać twój kod wystarczy, że naciśniesz M-m.

System Pomocy

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.


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.

Konwencje

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):

Podstawowe Manipulowanie Aplikacją

Zaznaczanie/Kopiowanie/Wklejanie

Manipulacja Tekstu

Nawigacja

Usuwanie

Zaawansowane Funkcje Edytora


10.3 - Udostępnianie

W 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ść.

Kod -> GitHub

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.

Dźwięki -> SoundCloud

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).

Nadzieja

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ć.


10.4 - Występy Na Scenie

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.

Pokaż Swój Ekran

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.

Stwórz Zespół

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ć.

TOPLAP

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.

Algorave

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.


11 - Minecraft Pi

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).

Brak konieczności importowania bibliotek

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.

Automatyczne Połączenie

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.

Zaprojektowanie do Kodowania Na Żywo

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.

Wymaga Raspberry Pi 2.0

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.

Wspierane API

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.


11.1 - Podstawowe API oferowane przez Minecraft Pi

Sonic Pi aktualnie wspiera następujące podstawowe interakcje z Minecraft Pi:

Spróbujmy przyjrzeć się każemu z nich po kolei.

Wyświetlanie wiadomości w czacie

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?

Ustawianie pozycji gracza

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.

Współrzędne

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:

Jeszcze jedna rzecz - zazwyczaj opisujemy te współrzędne w następującej kolejności x, y, z.

Odczytywanie twojej aktualnej lokalizacji

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.

Zbudujmy Coś!

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…

Patrzenie na Bloki

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 :-)

Dostępne typy bloków

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

12 - Wnioski

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.

Sam Aaron


- MagPi Articles

Appendix A collects all the Sonic Pi articles written for the MagPi magazine.

Dive into Topics

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.

Read the MagPi

You can see them in their glorious professionally typeset form in the free PDF downloads of The MagPi here: https://www.raspberrypi.org/magpi/

Suggest a Topic

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!


- Five Top Tips

1. There are no mistakes

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.

2. Use the FX

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.

3. Parameterise your synths

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.

5. Live Code

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_loops 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.

5. Surf the random streams

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.


- Live Coding

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.

Sam Aaron 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.

Live Loop

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!.

Redefining On-the-fly

Ok, so what’s so special about the live_loop? So far it just seems like a glorified loop! Well, the beauty of live_loops 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!

Sleeping is important

One of the most important lessons about live_loops 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_loops must contain a sleep.

Combining Sounds

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_loops, one looping quickly making beats and another looping slowly making a crazy bass sound.

One of the interesting things about using multiple live_loops 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

Bringing it all together

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

- Coded Beats

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!

The Amen Break

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.

Beat Stretching

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.

Playing with Time

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!

Filtering

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.

Slicing

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).

Bringing it all together

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

- Synth Riffs

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!

Timbral Possibilities

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

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.

Melodic Composition

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.

Auto Melody

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!

Pseudo Randomisation

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.

Finding your Rhythm

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.

Bringing it all together

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

- Acid Bass

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…

- Squelch that Bass

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.

  1. On the first line we set the default synth to be tb303 with the use_synth fn.

  2. On line two we create a live loop called :squelch which will just loop round and round.

  3. 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.

  4. 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.

  5. Line five is our sleep - we’re asking the live loop to loop round every 0.125s or 8 times a second at the default BPM of 60.

  6. 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.

- Deconstructing the TB-303

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.

TB-303 Design

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!

- Bringing it all together

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

- Musical Minecraft

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.

- Hello Minecraft World

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!

Screen 0

- Sonic Teleporter

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!"

Screen 1

- Magic Blocks

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.

- Live Coding Minecraft

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_loops to perform music in nightclubs with Sonic Pi - DJs use discs and I use live_loops :-) 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?

- Bringing it all together

Screen 2

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

- Bizet Beats

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.

- Outrageous and Disruptive

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!

- Decoding the Habanera

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:

Habanera Riff

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.

- Notes

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:

Notes

- Rests

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.

- Rhythm

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.

Habanera Riff 2

- Re-coding the Habanera

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!

- Moody Synths

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.

- Bringing it all together

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

- Become a Minecraft VJ

Screen 0

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…

- Getting 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……

- Sand Storms

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:

If 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!

- Live Loops Unleashed

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:

We 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?

- Epic Block Patterns

Screen 1

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!


- Surfing Random Streams

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!

- There is no random

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.

- A Random Melody

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:

Check out their documentation in the Help system for detailed information and examples.

end breakout box

- Resetting the Stream

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.

- Bringing it all together

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


- Controlling Your Sound

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.

A Basic Sound

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!

Synth Nodes

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 SynthNodes 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:

Multiple Changes

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)

Sliding

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…

Bringing it all together

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