İçeriğe geç

Ses Görselleştirici

Burada bazı API ses özelliklerine dalacak ve ses görselleştirici oluşturmanın temellerini göstereceğiz. Çoğu görselleştirici aynı temel ilkeler kümesinden oluşturulur, bu nedenle biraz pratik oldukça eğlenceli sonuçlar üretebilir.

SignalRGB tarafından sağlanan ses verileri kodunuzdaki birkaç özellik aracılığıyla erişilebilir:

  • engine.audio.level - Parçanın genel ses yüksekliğini temsil eden -100 ile 0 arasında bir sayı döndürür. 0 yüksek ses, -100 çok düşük sestir.
  • engine.audio.density - Tonun pürüzlülüğünü temsil eden 0 ile 1 arasında bir sayı döndürür; test tonları 0, beyaz gürültü 1 döndürür.
  • engine.audio.freq - Parçanın frekans verilerini içeren 200 öğeli bir dizi döndürür.

Her özellik, düzgün kullanılabilmesi için farklı düzeylerde normalleştirme veya ayarlama gerektirecektir; buna birazdan değineceğim.

Temel frekans animasyonuyla başlayalım.

Frekans, duyduğumuz sesin perdesini temsil eder ve ses görselleştiriciler için en önemli özelliktir. Burada yaptığımız şey, her karede frekans dalgasının 200 dilimini alarak görsel forma dönüştürmektir. Temel süreç şöyledir:

  • Bir dizi oluştur ve frekans verileriyle doldur.
  • Bu diziyi ihtiyaçlarınıza göre düzenle (filtrele, eşle, azalt vb.).
  • Her öğeyi temsil etmek için bir “ses çubuğu” sınıfı yaz.
  • Verileri her karede ses çubuğu sınıfına bağla.

Frekans konusundaki önemli nokta, ham veriye iki ayarlama yapmamız gerektiğidir. Bazen bir öğe negatif değerle gelir ve bu görsel açıdan rahatsız edicidir. Öğelerin yüksekliği de bu örnekte hatalı gelir. Dikdörtgenin “height” seçeneğindeki pozitif değerler şeklin başlangıç noktasından aşağı doğru çizdiğinden, bunları ortalama bir görselleştiriciye benzetmek için tersine çevirmek isteriz.

Örnek - işlenmemiş veri:

İşlenmiş veri:

İşlenmiş veriyle kod örneği:

<head>
<title>Visualizer Tutorial</title>
<meta description="Basic Effects" />
<meta publisher="SignalRgb" />
</head>
<body style="margin: 0; padding: 0; background: #000;">
<canvas id="exCanvas" width="320" height="200"></canvas>
</body>
<script>
canvas = document.getElementById('exCanvas');
ctx = canvas.getContext('2d');
var effects = [];
var reducedFreq = [];
function update() {
// "frequency" frekans verilerimizden tam 200 öğeyi temsil eder
var frequency = new Int8Array(engine.audio.freq)
// "reducedFreq" CPU yükünden tasarruf etmek için verileri her dördüncü sonuca filtreler
reducedFreq = frequency.filter((element, index) => {
return index % 4 === 0;
})
// Efekt henüz yoksa oluştur
if(effects.length < 1){
effects.push(new soundBars(20, 100))
}
// Arka plan rengi
DrawRect(0, 0, 320, 200, "black")
// Efekti oynat
effects.forEach((ele, i) => {
ele.draw();
if (ele.lifetime <= 0) {
effects.splice(i, 1);
}
});
window.requestAnimationFrame(update);
}
function soundBars (x, y){
this.x = x;
this.y = y;
this.draw = function(){
for(let i = 0; i < reducedFreq.length; i++){
var x = this.x + 5 * i
var y = this.y
// "height" için veri işleme gerçekleşir. Her öğenin mutlak değerini bul, sonra negatife çevir
var height = -Math.abs(reducedFreq[i])
DrawRect(x, y, 5, height, "white")
}
}
}
function DrawRect(x, y, width, height, color) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.fillRect(x, y, width, height);
};
window.requestAnimationFrame(update);
</script>

Yukarıdaki görselleştiricide hâlâ bazı sorunlar var. Kulaklıklarımızda şarkı dengeli duyulsa da verilerin ses çubuklarımızın bir kısmını büyük ölçüde tercih ettiğini ve diğerlerini sıklıkla tamamen görünmez bıraktığını görüyoruz. Sanatsal açıdan bu ideal değildir, bu nedenle SignalRGB’den alınan verileri “normalleştireceğiz”. Normalleştirme, değerlerin birbirlerine göre daha iyi gösterilmesi için verileri maksimum ve minimum noktalar arasında eşit dağıtmayı içerir. Frekans dizisindeki maksimum ve minimum değerleri bulduktan sonra, denklem oldukça basittir: (x - min) / (max - min), burada “x” mevcut öğeyi temsil eder.

Normalleştirilmiş, işlenmiş veri:

Sonraki adımda ses çubuklarını bir daire şeklinde düzenleyerek biraz renk katacağız:

function soundBars (x, y){
this.x = x;
this.y = y;
this.draw = function(){
var max = Math.max(...reducedFreq)
var min = Math.min(...reducedFreq)
for(let i = 0; i < reducedFreq.length; i++){
// Canvas'ın mevcut durumunu kaydet
ctx.save()
// Çubuklarınıza dairesel bileşen ekle. Buradaki "50" dairenin yarıçapıdır
var x = this.x + Math.cos(i) * 50
var y = this.y + Math.sin(i) * 50
var height = ((Math.abs(reducedFreq[i])) - min) / (max - min) * -40
// Çubuğun daireye dik olmasını sağlamak için açıyı belirle
var rotate = Math.atan2(y - this.y, x - this.x) + Math.PI / 2;
// Daire merkezine çevir
ctx.translate(x, y)
// Canvas'ı döndür
ctx.rotate(rotate)
// Çizim için geri çevir
ctx.translate(-x, -y)
DrawRect(x, y, 5, height, "white")
// Pozitif geri besleme döngülerini önlemek için çizdikten sonra canvas'ın eski durumunu geri yükle
ctx.restore()
}
}
}

Yoğunluk kullanımı basittir. Döndürülen değer, tonun “temizliğini” temsil eden 0 ile 1 arasında bir sayıdır. Dijital tonlar 0’a yakın, analog tonlar 1’e yakın olacaktır. Bu örnek için ses çubuklarının rengini düzenlemek amacıyla kullanacağım; bu sayede şarkınızdaki belirgin tonlar belirgin renklere sahip olacak.

function soundBars (x, y){
this.x = x;
this.y = y;
this.rotate = 0;
this.draw = function(){
// Hue her karede hesaplanır, sonuçtaki miktar hsl için uygun bir hue seçeneği olacak
var hue = engine.audio.density * 360
var max = Math.max(...reducedFreq)
var min = Math.min(...reducedFreq)
for(let i = 0; i < reducedFreq.length; i++){
ctx.save()
var x = this.x + Math.cos(i) * 50
var y = this.y + Math.sin(i) * 50
// Görünürlük için yükseklik biraz düzenlendi
var height = ((Math.abs(reducedFreq[i])) - min) / (max - min) * -50 - 5
this.rotate = Math.atan2(y - this.y, x - this.x) + Math.PI / 2;
ctx.translate(x, y)
ctx.rotate(this.rotate)
ctx.translate(-x, -y)
// Düzenlenen hue değerini her kareye ekle
DrawRect(x, y, 5, height, `hsl(${hue}, 100%, 50%)`)
this.rotate = 0;
ctx.restore()
}
}
}

Bu özellik yalnızca parçanın desibel cinsinden ses yüksekliğini döndürür; püf noktası -100 ile 0 arasında sayılar üretmesidir. -100 çok sessiz, 0 çok yüksek sestir. Verileri istediğim şekilde kullanmak için biraz düzenleme yapmam gerekecek ve bu şekli güncelleme fonksiyonumuzda çizeceğim. Burada parça düzeyi, iç siyah dairenin parlaklığını düzenleyecek.

function update() {
var frequency = new Int8Array(engine.audio.freq)
reducedFreq = frequency.filter((element, index) => {
return index % 4 === 0;
})
if(effects.length < 1){
effects.push(new soundBars(160, 100))
}
DrawRect(0, 0, 320, 200, "black")
effects.forEach((ele, i) => {
ele.draw();
if (ele.lifetime <= 0) {
effects.splice(i, 1);
}
});
// Görselleştiricinin geri kalanıyla eşleşmesi için hue hesaplaması
var hue = engine.audio.density * 360
// Düzey 10 ile çarpılıp 100 eklendi. Bu keyfi bir değerdir ancak buradaki eylemlerden oluşan bir kombinasyon istediğiniz sonucu verecektir
DrawCircle(160, 100, 50, `hsl(${hue}, 100%, ${100 + engine.audio.level * 10}%)`)
window.requestAnimationFrame(update);
}