Muzyka algorytmiczna

Zajęcia 5 (2025-11-21)

Zadanie domowe (na 2025-11-28)

Stwórz krótki (około 30 sekund) utwór w Sonic Pi spełniający następujące wymagania:

Sample

Sonic Pi posiada wbudowany bank sampli - wcześniej przygotowanych fragmentów dźwiękowych. Odpowiednikiem funkcji play jest funkcja sample :nazwa_sampla np:

sample :ambi_drone
sleep 1
sample :ambi_drone

Zauważ, że funkcja sample podobnie jak play nie oczekuje na zakończenie odtwarzania sampla! Ponadto każdy odtwarzany sampel może mieć różną długość - aby zdobyć dlugość sampla używamy funkcji sample_duration:

sample :ambi_drone
sleep sample_duration(:ambi_drone)
sample :ambi_drone

Jeśli zmienimy parametry odtwarzania musimy te same parametry przekazać do funkcji sleep_duration!

Modyfikacja parametrów odtwarzania

Funkcja sample posiada (podobnie jak play) ADSR kontrolujący poziom głośności. Ponadto dostępne jest:

Więcej parametrów opisanych jest w dokumentacji funkcji sample

Sample odtworzony od końca, dwa razy szybciej, z pominiętym 30% utworu od końca. Następinie ten sam sample, pierwsze 30%, dwa razy wolniej do przodu:

sample :loop_amen, finish: 0.7, rate: -2
sleep sample_duration(:loop_amen, finish: 0.7, rate: -2)
sample :loop_amen, finish: 0.3, rate: 0.5

Sample spoza Sonic Pi

Funkcja sample pozwala na odtworzenie plików dźwiękowych spoza banku zapisanych w Sonic Pi. Wykonujemy to przez podanie łańcucha z ścieżką do pliku:

sample "/ścieżka/do/pliku.wav"

Sonic Pi nie wspiera niektórych powszechnie używanych formatów plików! Rekomendowanym jest używanie plików wav lub ogg.

Dzielenie sample'a na fragmenty o równej długości

Przykład z dokumentacji Sonic Pi

Wykorzystujemy fakt, że :loop_amen dzieli się na 8 równych fragmentów:

Równomierny podział :loop_amen na 8 fragmentów

Każda iteracja pętli :beat_slicer losuje indeks od 0 do 8, wedle którego odtworzymy jeden z 8 fragmentow :loop_amen. Każdy fragment ma dlugość 1/8 utworu (slice_size). Wyliczamy start (zmienna s) - jest to pominięcie poprzednich do wylosowanego fragmentów oraz koniec (zmiemma f) - jest to start przesunięty o dlugośc fragmentu. Następnie możemy odtworzyć fragment z użyciem funkcji sample.

live_loop :beat_slicer do
  slice_idx = rand_i(8)
  slice_size = 1 / 8.0
  
  s = slice_idx * slice_size
  f = s + slice_size
  sample :loop_amen, start: s, finish: f
  sleep sample_duration :loop_amen, start: s, finish: f
end

Dzielenie sample'a na fragmenty o różnej długości przy pomocy tablicy

Powyższy przykład można rozszerzyć poprzez nierównomierny podział sampla - każda sekcja może mieć inną długość. Aby osiągnąć ten efekt będziemy losować z tablicy, w której każdy element oznacza długość danego fragmentu. Pętla rozpoczyna się podobnie - od losowania indeksu, tym razem indeksu tablicy.

Następnie wyciągamy długość pojedyńczego fragmentu (slice_size = durations[slice_idx]) oraz liczymy sumę długości fragmentów pominiętych (s = durations[...slice_idx].sum) - zapis durations[...slice_idx]wyciąga elementy tablicy od 0 do slice_idx (bez slice_idx), natomiast .sum sumuje elementy tablicy.

Pozostała część kodu jest identyczna.

durations = [0.4, 0.1, 0.2, 0.1, 0.2]

live_loop :beat_slicer do
  slice_idx = rand_i(durations.length)
  slice_size = durations[slice_idx]
  s = durations[...slice_idx].sum
  f = s + slice_size
  sample :loop_amen, start: s, finish: f
  sleep sample_duration :loop_amen, start: s, finish: f
end