5.4 Wątki

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