Przygotowanie krótkiego utworu (~30s), spełniającego następujące wymagania:
Zadanie z 2 tygodniowym terminem oddania z uwagi na dodatkowe materiały na zajęciach 2024-12-11.
Przykładowe formy utworu spełniające wymagania:
Plik: 10.rb
Nieformalna definicja:
Gramatyka formalna - sposób na opis języka, czyt. sposobu na tworzenie (lub walidację) ciągów znaków w danym języku. Gramatyka bezkontekstowa to gramatyka, w której z pojedynczych symboli możemy tworzyć zdania (= nie potrzeba kontekstu, wystarczy jeden symbol by zastosować jedną z reguł).
Gramatyki najczęściej definiowane są przez reguły. Reguła A → B
oznacza, że posiadając symbol A
można zamienić go na symbol B
.
Prosta gramatyka, która zmienia przykładowy ciąg „AA” w ciąg „BBBB”:
A → BB
Gramatyka, generująca ciągi „ab”, „aabb”, „aaabbb”:
S → aSb
S →
brak treści w drugiej regule dla symbolu S jest celowy - pozwala na zakończenie generowania ciągu znaków przez możliwość „usunięcia” symbolu S. W innym przypadku bez wprowadzenia dodatkowych ograniczeń generowane byłby nieskończenie długie ciągi.
Przykład gramatyki, która generuje nieskończenie długie ciągi:
A → ABA
B → CC
C → BAB
L - systemy, umożliwiające procedularne generowanie np. drzew.
Symbolom wygenerowanym przez gramatykę można przyporządkować znaczenie muzyczne. Ponieważ prostsze jest tworzenie gramatyk nieskończonych, wprowadzone zostaje dodatkowe ograniczenie: liczba rund w których możemy dopasować reguły.
Wykorzystana gramatyka:
S → KCE
K → KSSE
Pierwsze kilka rund zastosowania zasad, zaczynając od symbolu „S”:
Funkcja automatycznie generująca kolejne rundy wygląda następująco:
define :kolejny do |str|
kolejny = ""
for x in str.chars
if x == "S" then kolejny += "KCK" end
if x == "K" then kolejny += "ESSE" end
end
kolejny
end
Wywoływana następująco:
kolejny("S") # zwraca „KCK”
Interpretując „C” jako :c4
, „E” jako :e4
, a pozostałe symbole jako zwiększanie „S” i zmniejszanie „K” tempa możemy interpretować stworzony przez gramatykę utwór następująco:
program = "S"
3.times do program = kolejny(program) end
p = 1.0
for x in program.chars
if x == "C" then play :c4, duration: p end
if x == "E" then play :e4, duration: p end
if x == "S" then p /= 2 end
if x == "K" then p *= 2 end
sleep p
end
Symbole mają pełną kontrolę nad utworem - zmieniają wykorzystywany syntezator, własności wykonywanych dźwięków, tworzą nowe wątki w których wykonywane są programy.
W poniższym przykładzie:
C
, E
, G
odpowiadają dźwiękom c, e, g w wybranej oktawieU
zwiększa oktawę, z ograniczeniem do co najwyżej 6 oktawyD
zmniejsza oktawę z ograniczeniem do co najmniej 2 oktawyY
tworzy wątek, który wykonuje program od kolejnego symbolu ale 4 razy wolniej.
Aktualny wątek zaczyna wykonywać się 2 razy wolniej.define :kolejny do |str|
kolejny = ""
for x in str.chars
if x == "C" then kolejny += "CGE" end
if x == "E" then kolejny += "EEG" end
if x == "G" then kolejny += "CUY" end
if x == "U" then kolejny += "UCGEDD" end
if x == "D" then kolejny += "DEGCU" end
if x == "Y" then
# nie robimy nic
end
end
kolejny
end
use_bpm 240
define :interpretuj do |program, start, start_p, start_oct|
p = start_p
o = start_oct
program.chars[start..].each_with_index do |x, i|
if x == "C" then synth :dsaw, note: note(:c, octave: o), duration: p, amp: 0.5 end
if x == "E" then synth :dsaw, note: note(:e, octave: o), duration: p, amp: 0.5 end
if x == "G" then synth :dsaw, note: note(:g, octave: o), duration: p, amp: 0.5 end
if x == "U" and o <= 6 then o += 1 end
if x == "D" and o >= 2 then o -= 1 end
if x == "Y" then
old_p = p
p = p * 2
in_thread do
interpretuj(program, start+i+1, old_p * 4, o)
end
end
sleep p
end
end
program = "C"
3.times do program = kolejny(program) end
interpretuj(program, 0, 0.5, 4)
Poniższy przykład jest zmienionym utworem z zajęć o minimaliźmie - tym razem zamiast procesu losowego, kontrolowany jest on przez program.
use_bpm 60
use_synth :prophet
program = "AAWAWBCBDE"
define :interpretuj do |program, start|
program.chars[start..].each_with_index do |x, i|
if x == "A"
play :f2, cutoff: :f4
sleep 1
play :e2, cutoff: :e4
sleep 1
end
if x == "B"
with_fx :slicer, phase: 0.5 do
sn = synth :prophet, sustain: 4, note: :e1, cutoff: :e3, cutoff_slide: 4
control sn, cutoff: :e4
sleep 4
end
end
if x == "C"
blade = synth :blade, note: :e3, note_slide: 0.5, duration: 10000
rand_i(6).times do
blade.control note: (ring :f3, :e3).tick
sleep 1
end
blade.control note: :c9, cutoff: 0, cutoff_slide: 2
end
if x == "D"
rand_i(6).times do
with_fx :slicer, phase: 0.25 do
sn = synth :prophet, sustain: 4, note: :e1, cutoff: :e2, cutoff_slide: 4
control sn, cutoff: :e3
sleep 4
end
end
end
if x == "E"
rand_i(6).times do
play :e5
sleep 1
play :f5
sleep 1
end
end
if x == "W"
in_thread do
sleep 2
interpretuj(program, start+i+1)
end
end
end
end
interpretuj(program, 0)