Muzyka algorytmiczna

Zajęcia 4 (2024-10-23)

Cel zajęć

  1. Kontrola współbieżnych programów w Sonic Pi

Zadanie (na 2024-10-30)

Przygotowanie krótkiego utworu (~30s) w środowisku Sonic Pi, spełniającego następujące wymagania:

  1. Utwór o nietrywialnej formie
  2. Wykorzystujący powtórzenia, sekcje wykonywane równolegle
  3. Wykorzystujący co najmniej jeden sposób synchronizacji w Sonic Pi: równomierny postęp czasu, cue, sync

Zrzut kodu z zajęć

Plik: 04.rb

Generowanie liczb pseudolosowych

Sonic Pi posiada wbudowany generator liczb pseudolosowych, którego stan początkowy jest taki sam przy każdym uruchomieniu.

Kolejne wywołania wszystkich funkcji losujących w ramach tego samego wątku korzystają z tego samego generatora liczb. Poniższy przykład zwraca ten sam wynik, niezależnie od kolejności wykonania wątków:

print("x=", rand_i(100))

in_thread do
  sleep 2
  print("y=", rand_i(100))
end

in_thread do
  sleep 1
  print("z=", rand_i(100))
end

Współdzielenie stanu pomiędzy wątkami

set(:nazwa, wartość) ustawia wartość, a get[:nazwa] pobiera wartość

# Pętla kontroluje czas przez zmniejszanie czasu sleep'ów
# halves(1, 4) = (ring 1.0, 0.5, 0.25, 0.125)
live_loop :czas do
  set :shared, halves(1, 4).tick
  sleep 4
end

live_loop :wysokie do
  synth :piano, note: :e4
  sleep get[:shared]
  synth :piano, note: :f4
  sleep get[:shared]
end

live_loop :niskie do
  with_fx :lpf, cutoff: :c4 do |f|
    f.control cutoff: :g2, cutoff_slide: get[:shared]
    synth :piano, note: :c2, duration: 4 * get[:shared]
    synth :piano, note: :e2, duration: 4 * get[:shared]
    synth :piano, note: :g2, duration: 4 * get[:shared]
  end
  sleep 2 * get[:shared]
end

Organizacja utworu wykorzystująca czas

beat zwraca aktualny czas w utworze.

Zakładając, że wykonanie pojedyńczej iteracji każdej pętli zajmuje zawsze tyle samo czasu (dla danej pętli) oraz znając czas wykonania każdej z nich można wprowadzić wspólne przerwania wykonania.

Wykonanie pętli :a zajmuje 2 uderzenia, :b 3, :c 5. Oznacza to, że pętla :a wydaje dźwięk w momencie 0, 2, 4, 6...; pętla :b w momencie 0, 3, 6, 9...; pętla :c w momencie 0, 5, 10, 15... Pierwszym momentem w którym wydadzą wspólny dźwięk jest 30 (= NWW(2, 3, 5)). Można wykorzystać ten punkt wspólny jako przerwanie i wykonanie innego zbioru dźwięków, jak w przykładzie poniżej:

Na zajęciach zidentyfikowano problem z poniższym kodem w Sonic Pi od wersji 4.0.0. W pliku z zajęć dostępne jest rozwiązanie dla wymienionej wersji. Poniższe rozwiązanie działa dla wersji starszych niż 4.0.0.

use_bpm 240
use_synth :tri

live_loop :a do
  if beat > 0 and beat % 30 == 0 then sync :drop end
  play :c4
  sleep 2
end

live_loop :b do
  if beat > 0 and beat % 30 == 0 then sync :drop end
  play :g4
  sleep 3
end

live_loop :c do
  if beat > 0 and beat % 30 == 0 then sync :drop end
  play :e4
  sleep 5
end

sleep 30
loop do
  sleep(30 * (beat / 30r).ceil - beat)
  s = synth :prophet, note: :c2, cutoff: :c9, duration: 8.1
  control s, cutoff: :c2, cutoff_slide: 8
  sleep 8
  cue :drop
end

Proces ten można zautomatyzować, co pokazuje ten przykład.