Visualiseur Audio
Ici, nous allons explorer certaines propriétés audio de l’API et vous montrer les bases de la création d’un visualiseur audio. La plupart des visualiseurs sont construits à partir du même ensemble de principes de base, donc un peu de pratique peut produire des résultats très amusants.
Propriétés Audio de SignalRGB
Section intitulée « Propriétés Audio de SignalRGB »Les données audio fournies par SignalRGB sont accessibles via quelques propriétés dans votre code :
- engine.audio.level - retourne un nombre entre -100 et 0 représentant le volume global de la piste. 0 est fort, -100 est très faible.
- engine.audio.density - retourne un nombre entre 0 et 1 représentant la rugosité du ton, les sons de test retournant 0 et le bruit blanc 1.
- engine.audio.freq - retourne un tableau de 200 éléments contenant les données de fréquence de la piste.
Chaque propriété nécessitera différents niveaux de normalisation ou d’ajustement avant de pouvoir être correctement utilisée, ce que j’aborderai dans un moment.
Commençons par une animation de fréquence de base.
Fréquence
Section intitulée « Fréquence »La fréquence représente la hauteur du son que nous entendons et est la propriété la plus importante pour les visualiseurs audio. Ce que nous faisons ici, c’est prendre 200 tranches de l’onde de fréquence à chaque image et les convertir en forme visuelle. Le processus de base est :
- Instancier un tableau et le remplir avec les données de fréquence.
- Modifier ce tableau selon vos besoins (filtrer, mapper, réduire, etc.).
- Écrire une classe “barre sonore” pour représenter chaque élément.
- Connecter les données à la classe de barre sonore à chaque image.
L’aspect important avec la fréquence est que nous devrons apporter deux ajustements aux données brutes. Parfois, un élément arrivera avec une valeur négative, ce qui est visuellement désagréable. La hauteur des éléments arrive également incorrectement pour cet exemple. Puisque les valeurs positives dans l’option “height” d’un rectangle se dessinent vers le bas depuis l’origine de la forme, nous voudrons les inverser pour ressembler à un visualiseur classique.
Exemple - données non traitées :

Données traitées :

Exemple de code avec données traitées :
<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>Il reste encore quelques problèmes avec le visualiseur ci-dessus. Bien que la chanson semble bien équilibrée dans nos écouteurs, nous pouvons voir que les données favorisent massivement certaines de nos barres sonores et en laissent souvent d’autres complètement invisibles. D’un point de vue artistique, ce n’est pas idéal, alors nous allons “normaliser” les données reçues de SignalRGB. La normalisation consiste à distribuer uniformément les données entre un point maximum et minimum afin de mieux illustrer leur valeur les unes par rapport aux autres. Après avoir déterminé nos valeurs maximale et minimale dans le tableau de fréquences, l’équation est assez simple : (x - min) / (max - min), où “x” représente l’élément actuel.
Données normalisées et traitées :

Ensuite, nous allons ajouter un peu de fantaisie en disposant les barres sonores en cercle :
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() } } }
La densité est simple à utiliser. La valeur retournée sera un nombre entre 0 et 1 représentant la “propreté” du ton. Les tons numériques seront plus proches de 0, et les tons analogiques seront plus proches de 1. Pour cet exemple, je vais l’utiliser pour modifier la couleur des barres sonores, ce qui donnera des colorations distinctes aux tons distincts dans votre chanson.
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() } } }
Cette propriété retourne simplement le volume de la piste en décibels, avec la particularité qu’elle produit des nombres entre -100 et 0. -100 est très silencieux, et 0 est très fort. Nous devrons effectuer quelques modifications de ces données pour les utiliser comme je le souhaite, et je vais dessiner cette forme dans notre fonction de mise à jour. Ici, le niveau de la piste modifiera la luminosité du cercle noir intérieur.
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); }