Audiovisualizer
Her dykker vi ned i nogle af API’ens audio-egenskaber og viser dig det grundlæggende i at skabe audivisualisatorer. De fleste visualisatorer er baseret på de samme grundlæggende principper, så lidt øvelse kan give meget underholdende resultater.
SignalRGB audio-egenskaber
Sektion kaldt “SignalRGB audio-egenskaber”Audiodataene leveret af SignalRGB kan tilgås via nogle egenskaber i din kode:
- engine.audio.level - returnerer et tal mellem -100 og 0 der angiver den samlede lydstyrke for sporet. 0 er højt, -100 er meget lavt.
- engine.audio.density - returnerer et tal mellem 0 og 1 der angiver lydens grovhed; testtoner returnerer 0, hvid støj returnerer 1.
- engine.audio.freq - returnerer et array med 200 elementer der indeholder frekvensdata for sporet.
Hver egenskab kræver forskellige normaliseringss- eller justeringstrin, inden den kan bruges korrekt, som jeg vil gennemgå om lidt.
Lad os starte med den grundlæggende frekvensanimation.
Frequency
Sektion kaldt “Frequency”Frequency (frekvens) repræsenterer tonen på den lyd der høres og er den vigtigste egenskab for audiovisualisatorer. Her tager vi 200 snit af frekvensbølgen per frame og konverterer dem til en visuel form. Det grundlæggende forløb er:
- Instansiere et array og fylde det med frekvensdata.
- Redigere dette array efter behov (filtrere, mappe, reducere osv.).
- Skrive en “soundbar”-klasse til at repræsentere hvert element.
- Forbinde data med soundbar-klassen per frame.
Det vigtige ved frekvens er at vi skal foretage to justeringer af rådataene. Til tider ankommer et element med en negativ værdi, hvilket er visuelt forstyrrende. Højden på elementerne ankommer også forkert til dette eksempel. Da positive værdier i “height”-indstillingen for et rektangel tegnes nedad fra formens oprindelse, vil vi vende dem om, så det ligner en gennemsnitlig visualisator.
Eksempel - ubehandlede data:

Behandlede data:

Kodeeksempel med behandlede 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" 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>Der er dog stadig nogle problemer med ovenstående visualisator. Selvom sangen lyder afbalanceret i vores høretelefoner, ser vi at dataene massivt favoriserer nogle af vores soundbars og ofte lader andre være helt usynlige. Fra et kunstnerisk synspunkt er det ikke ideelt, så vi vil “normalisere” de data vi modtager fra SignalRGB. Normalisering fordeler data jævnt mellem en maksimal og minimal værdi for bedre at repræsentere deres værdi i forhold til hinanden. Når vi har fundet vores maksimum- og minimumværdier i frekvens-arrayet, er ligningen ganske enkel: (x - min) / (max - min), hvor “x” repræsenterer det aktuelle element.
Normaliserede, behandlede data:

Dernæst tilføjer vi lidt flair ved at arrangere 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++){ //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
Sektion kaldt “Density”Density (tæthed) er nem at bruge. Den returnerede værdi er et tal mellem 0 og 1 der repræsenterer lydens “renhed”. Digitale toner er tættere på 0, analoge toner tættere på 1. I dette eksempel bruger jeg den til at redigere farven på soundbars, hvilket giver visse toner i din sang bestemte farvenuancer.
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() } } }
Denne egenskab returnerer simpelthen lydstyrken for sporet i decibel, med den subtilitet at den producerer tal mellem -100 og 0. -100 er meget lavt og 0 er meget højt. Vi skal redigere disse data lidt for at bruge dem som ønsket, og jeg tegner denne form i vores update-funktion. Her redigerer sporenes lydstyrke lysheden i den indre sorte cirkel.
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); }