Ücretli bir DJ ışık yazılımının yerine kendi motorumu yazıyordum. Referans aldığım davranış şuydu: şarkıyı duraklatırsan şov durur, geri sararsan şov da geri sarar, ortadan bir yere atlarsan şov oraya atlar.
Kullanıcı gözünde bu “senkron” demek. Kod gözünde çok daha katı bir şey demek.
Doğal olan yaklaşım yanlış olan
Bir ışık efektini anlatmanın en doğal yolu adım adımdır:
// chase efekti — "kovalama"
her karede:
aktif_isik = (aktif_isik + 1) % isik_sayisi
hepsini söndür, aktif olanı yak
Çalışır. Güzel de görünür. Ta ki kullanıcı zaman çubuğunu tutup 90. saniyeye atlayana kadar.
O an motora şunu sormuş oluyorsun: “90. saniyede kaçıncı ışık yanıyor olmalı?” Ve motor bilemiyor. Çünkü o sayı, kendisinden önceki bütün karelerin birikmiş sonucu. Hesaplamak için 0’dan 90’a kadar bütün kareleri yeniden çalıştırmak gerekiyor — dört bin adım, üstelik her atlamada.
Tek kural
Verilen t zaman kodu için tüm kanalların değerini döndür. Girdi yalnız t. Motor hiçbir şey hatırlamaz, biriktirmez, t−1’in hesaplanmış olmasına ihtiyaç duymaz.
Aynı chase efekti bu kısıtla şöyle yazılıyor:
aktif_isik = floor(t × hiz) % isik_sayisi
Fark tek satır. Ama artık 90. saniyeyi sormak, 0. saniyeyi sormakla aynı maliyette.
Her salınım fazını zamandan alır
| efekt | durumlu (yanlış) | saf (doğru) |
|---|---|---|
| chase | i = i + 1 | i = ⌊t·h⌋ mod n |
| strobe | if(sayaç%4==0) çevir | açık = (t·f) mod 1 < 0.5 |
| fade | v += adım | v = (t−t₀)/süre |
| hareket | açı += hız | açı = sin(2π·f·t) |
Sağ sütundaki her ifadenin ortak özelliği: t dışında hiçbir şeye bakmıyor.
Yeni bir efekt eklerken tek test şu: rastgele bir t için, öncesini hiç hesaplamadan değerlendirilebiliyor mu? Hayırsa efekt yeniden yazılıyor.
Bedava gelen şeyler
Bu kuralı baştan koymanın maliyeti sıfırdı. Karşılığında gelenler:
- Atlama ve tarama — asıl hedef buydu, zaten çözülmüş oldu.
- Duraklatma —
t’yi ilerletmemek yeterli. Ayrı bir mantık yok. - Yavaş/hızlı oynatma —
t’yi farklı hızda ilerlet; efektler kendiliğinden uyuyor. - Önizleme — şovu çalmadan herhangi bir
t’de ne olacağını gösterebiliyorsun. - Test edilebilirlik — bir efekti test etmek için 44 kare simüle etmek gerekmiyor;
render(37.5)yazıp sonuca bakıyorsun. - Kare atlama — makine yavaşlarsa kare düşürmek zararsız; bir sonraki kare zaten doğru yerden devam ediyor.
Son madde sahnede önemli. Durum biriktiren bir motorda düşen bir kare, şovu kalıcı olarak müzikten geri bırakır ve bir daha hizalanmaz.
Şovun kendisi de ayrı bir katman
Aynı ayrıştırma bir üst seviyede de var. Şov üreticisi DMX kanalı düşünmüyor. Soyut niyet üretiyor:
t=42.0 "sıcak amber wash, %60"
t=45.5 "downbeat'te accent hit"
t=48.0 "drop'tan 4 bar önce sis"
Ayrı bir render katmanı bu niyeti çıkışa çeviriyor. Böylece aynı kurgu hem DMX fixture’larına hem de evdeki ZigBee ampullere gidebiliyor.
Analiz katmanı da ayrı: şarkıyı çözümlemek yavaş (yaklaşık yarım dakika), kurguyu üretmek hızlı (saliseler). Kurguyu onlarca kez denersin; şarkıyı her seferinde yeniden analiz etmenin anlamı yok. Arada duran dosya hem bu ayrımı sağlıyor hem de elle düzeltilebiliyor — algoritma bir drop’u kaçırırsa müdahale edebiliyorsun.
Ne öğrendim
Bazı kısıtlar özgürlük üretiyor. “Durum tutma” kısıtı ilk bakışta efekt yazmayı zorlaştırıyor gibi görünüyor; gerçekte altı ayrı özelliği bedavaya getiriyor.
Ve bu tür kısıtların baştan konması gerekiyor. Sonradan koymak “biraz düzenleme” değil; efekt motorunun tamamını yeniden yazmak demek.