Audio Visualizer
Di sini kita akan mendalami beberapa properti audio API dan menunjukkan dasar-dasar pembuatan audio visualizer. Sebagian besar visualizer dibangun dari kumpulan prinsip dasar yang sama, sehingga sedikit latihan dapat menghasilkan hasil yang sangat menyenangkan.
Properti Audio SignalRGB
Section titled “Properti Audio SignalRGB”Data audio yang disediakan oleh SignalRGB dapat diakses melalui beberapa properti dalam kode Anda:
- engine.audio.level - mengembalikan angka antara -100 dan 0 yang mewakili tingkat kekerasan keseluruhan dari track. 0 berarti keras, -100 sangat pelan.
- engine.audio.density - mengembalikan angka antara 0 dan 1 yang mewakili kekasaran nada, dengan nada uji mengembalikan 0 dan white noise 1.
- engine.audio.freq - mengembalikan array dari 200 elemen yang berisi data frekuensi track.
Setiap properti akan memerlukan tingkat normalisasi atau penyesuaian yang berbeda sebelum dapat digunakan dengan baik, yang akan saya bahas sebentar lagi.
Mari kita mulai dengan animasi frekuensi dasar.
Frekuensi
Section titled “Frekuensi”Frekuensi mewakili pitch suara yang kita dengar dan merupakan properti terpenting untuk audio visualizer. Yang kita lakukan di sini adalah mengambil 200 irisan gelombang frekuensi setiap frame dan mengubahnya menjadi bentuk visual. Proses dasarnya adalah:
- Membuat sebuah array dan mengisinya dengan data frekuensi.
- Mengedit array ini sesuai kebutuhan Anda (filter, map, reduce, dll.).
- Menulis kelas “sound bar” untuk mewakili setiap elemen.
- Menghubungkan data ke kelas sound bar setiap frame.
Hal penting dengan frekuensi adalah kita harus membuat dua penyesuaian pada data mentah. Terkadang sebuah elemen akan masuk dengan nilai negatif, yang secara visual terasa janggal. Tinggi elemen juga masuk dengan tidak tepat untuk contoh ini. Karena nilai positif dalam opsi “height” pada persegi panjang menggambar ke bawah dari asal bentuk, kita ingin membaliknya agar menyerupai visualizer rata-rata.
Contoh - data yang belum diproses:

Data yang telah diproses:

Contoh kode dengan data yang telah diproses:
<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" represents the full 200 elements from our frequency data var frequency = new Int8Array(engine.audio.freq) // "reducedFreq" filters the data down to every fourth result to save some CPU load reducedFreq = frequency.filter((element, index) => { return index % 4 === 0; })
// Create the effect if it does not yet exist if(effects.length < 1){ effects.push(new soundBars(20, 100)) }
// Background color DrawRect(0, 0, 320, 200, "black")
// Play effect 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 // Data processing occurs for "height". Find the absolute value of each element, then flip negative 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>Namun masih ada beberapa masalah dengan visualizer di atas. Meskipun lagu terdengar seimbang melalui headphone kita, kita dapat melihat bahwa data sangat mendukung beberapa sound bar dan sering membuat yang lain sama sekali tidak terlihat. Dari perspektif artistik, ini tidak ideal, jadi kita akan “menormalisasi” data yang diterima dari SignalRGB. Normalisasi melibatkan distribusi data secara merata antara titik maksimum dan minimum untuk lebih menggambarkan nilainya dalam kaitannya satu sama lain. Setelah mengetahui nilai maksimum dan minimum dalam array frekuensi, persamaannya cukup sederhana secara keseluruhan: (x - min) / (max - min), di mana “x” mewakili elemen saat ini.
Data yang telah dinormalisasi dan diproses:

Selanjutnya, kita akan menambahkan sedikit keindahan dengan mengatur sound bar dalam bentuk lingkaran:
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++){ //Save the current state of the canvas ctx.save() //Add the circular component to your bars. "50" here is the radius of the circle 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 // Determine the angle of the bar, to make it perpindicular to the circle var rotate = Math.atan2(y - this.y, x - this.x) + Math.PI / 2; //Translate to circle center ctx.translate(x, y) //Rotate the canvas ctx.rotate(rotate) //Translate back for drawing ctx.translate(-x, -y) DrawRect(x, y, 5, height, "white") //Restore the old state of the canvas after drawing to prevent positive feedback loops ctx.restore() } } }
Density
Section titled “Density”Density mudah digunakan. Nilai yang dikembalikan akan berupa angka antara 0 dan 1 yang mewakili “kebersihan” nada. Nada digital akan lebih mendekati 0, dan nada analog akan lebih mendekati 1. Untuk contoh ini, saya akan menggunakannya untuk mengedit warna sound bar, yang akan memberikan warna berbeda pada nada yang berbeda dalam lagu Anda.
function soundBars (x, y){
this.x = x; this.y = y; this.rotate = 0;
this.draw = function(){ //Hue is calculated each frame, the resulting amount will be a proper hue option for hsl 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 //Height slightly edited for visability 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) //Insert the edited hue value each frame DrawRect(x, y, 5, height, `hsl(${hue}, 100%, 50%)`) this.rotate = 0; ctx.restore() } } }
Properti ini hanya mengembalikan tingkat kekerasan track dalam desibel, dengan trik bahwa ia menghasilkan angka antara -100 dan 0. -100 sangat pelan, dan 0 sangat keras. Kita harus sedikit mengedit data ini untuk menggunakannya sesuai keinginan, dan saya akan menggambar bentuk ini dalam fungsi update kita. Di sini, level track akan mengedit lightness lingkaran hitam bagian dalam.
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); } });
// Hue calcualtion to match the rest of the visualizer var hue = engine.audio.density * 360 //The level has been multiplied by 10, then added to 100. This is arbitrary, but some combination of actions here will give you the result you want DrawCircle(160, 100, 50, `hsl(${hue}, 100%, ${100 + engine.audio.level * 10}%)`)
window.requestAnimationFrame(update); }