Ljudvisualiserare
Här dyker vi in i några av API:ets ljudegenskaper och visar grunderna för att skapa ljudvisualiserare. De flesta visualiserare är byggda utifrån samma uppsättning grundläggande principer, så lite övning kan ge mycket roliga resultat.
SignalRGB-ljudegenskaper
Section titled “SignalRGB-ljudegenskaper”Ljuddata som tillhandahålls av SignalRGB kan nås via ett par egenskaper i din kod:
- engine.audio.level – returnerar ett tal mellan -100 och 0 som representerar den övergripande loudness för spåret. 0 är högt, -100 är mycket lågt.
- engine.audio.density – returnerar ett tal mellan 0 och 1 som representerar tonens råhet, där testtoner returnerar 0 och vitt brus 1.
- engine.audio.freq – returnerar en matris med 200 element som innehåller spårets frekvensdata.
Varje egenskap kräver olika nivåer av normalisering eller justering innan den kan användas korrekt, vilket jag återkommer till lite senare.
Låt oss börja med grundläggande frekvensanimation.
Frekvens
Section titled “Frekvens”Frekvens representerar tonhöjden på det ljud vi hör och är den viktigaste egenskapen för ljudvisualiserare. Vad vi gör här är att ta 200 skivor av frekvensvågen varje bildruta och konvertera det till visuell form. Den grundläggande processen är:
- Instantiera en matris och fyll den med frekvensdata.
- Redigera den här matrisen efter dina behov (filter, map, reduce, osv.).
- Skriv en “soundbar”-klass för att representera varje element.
- Koppla data till soundbar-klassen varje bildruta.
Det viktiga med frekvens är att vi behöver göra två justeringar av rådata. Ibland kommer ett element in med ett negativt värde, vilket är visuellt störande. Höjden på elementen kommer också in felaktigt för det här exemplet. Eftersom positiva värden i en rektangels “height”-alternativ ritar nedåt från formens origo vill vi vända på dem för att likna din genomsnittliga visualiserare.
Exempel – obearbetad data:

Bearbetad data:

Kodexempel med bearbetad data:
<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" representerar de fullständiga 200 elementen från vår frekvensdata var frequency = new Int8Array(engine.audio.freq) // "reducedFreq" filtrerar data ned till var fjärde resultat för att spara lite CPU-belastning reducedFreq = frequency.filter((element, index) => { return index % 4 === 0; })
// Skapa effekten om den inte finns ännu if(effects.length < 1){ effects.push(new soundBars(20, 100)) }
// Bakgrundsfärg DrawRect(0, 0, 320, 200, "black")
// Spela effekt 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 // Databearbetning sker för "height". Hitta absolutvärdet av varje element, vänd sedan negativt 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>Det finns fortfarande några problem med visualiseraren ovan. Även om sången låter välrundad genom våra hörlurar kan vi se att data massivt gynnar några av våra soundbars och ofta lämnar andra helt osynliga. Ur ett konstnärligt perspektiv är det inte idealiskt, så vi ska “normalisera” data som tas emot från SignalRGB. Normalisering innebär att data distribueras jämnt mellan en maximal och minimal punkt för att bättre illustrera deras värde i förhållande till varandra. Efter att ha räknat ut våra maximala och minimala värden i frekvensmatrisen är ekvationen ganska enkel: (x - min) / (max - min), där “x” representerar det aktuella elementet.
Normaliserad, bearbetad data:

Härnäst lägger vi till lite pizzazz genom att arrangera soundbars i en cirkel:
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++){ // Spara det aktuella tillståndet för canvas ctx.save() // Lägg till den cirkulära komponenten till dina staplar. "50" här är cirkelns radie 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 // Bestäm stapelns vinkel, för att göra den vinkelrät mot cirkeln var rotate = Math.atan2(y - this.y, x - this.x) + Math.PI / 2; // Översätt till cirkelns centrum ctx.translate(x, y) // Rotera canvas ctx.rotate(rotate) // Översätt tillbaka för ritning ctx.translate(-x, -y) DrawRect(x, y, 5, height, "white") // Återställ det gamla tillståndet för canvas efter ritning för att förhindra positiva feedbackloopar ctx.restore() } } }
Densitet
Section titled “Densitet”Densitet är enkel att använda. Det returnerade värdet är ett tal mellan 0 och 1 som representerar tonens “renhet”. Digitala toner är närmre 0, och analoga toner är närmre 1. I det här exemplet använder jag det för att redigera färgen på soundbars, vilket ger distinkta toner i din sång distinkta färgsättningar.
function soundBars (x, y){
this.x = x; this.y = y; this.rotate = 0;
this.draw = function(){ // Hue beräknas varje bildruta, det resulterande beloppet är ett korrekt hue-alternativ för 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 // Höjd något redigerad för synlighet 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) // Infoga det redigerade hue-värdet varje bildruta DrawRect(x, y, 5, height, `hsl(${hue}, 100%, 50%)`) this.rotate = 0; ctx.restore() } } }
Den här egenskapen returnerar helt enkelt loudness för spåret i decibel, med tricket att den producerar tal mellan -100 och 0. -100 är mycket tyst och 0 är mycket högt. Vi behöver göra lite redigering av dessa data för att använda dem som jag vill, och jag ritar den här formen i vår update-funktion. Här redigerar spårsnivån ljusheten på den inre svarta cirkeln.
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-beräkning för att matcha resten av visualiseraren var hue = engine.audio.density * 360 // Nivån har multiplicerats med 10 och sedan adderats med 100. Det är godtyckligt, men någon kombination av åtgärder här ger dig det resultat du vill ha DrawCircle(160, 100, 50, `hsl(${hue}, 100%, ${100 + engine.audio.level * 10}%)`)
window.requestAnimationFrame(update); }