З появою Web Audio зявилась можливість створювати візуалізацію звуку безпосередньо у браузері за допомогою JavaScript.
Для отримання даних про звук використовується об'єкт AnalyserNode, отримані дані обробляються і візуалізація малюється на полотні canvas за допомогою об'єкта CanvasRenderingContext2D.
Гістаграма
Візуалізація звуку у вигляді гістограми.
<canvas id="canvas"></canvas>
if(window.AudioContext){
audioCtx = new AudioContext();
audio = new Audio('/dani/test.ogg');
source = audioCtx.createMediaElementSource(audio);
analis=audioCtx.createAnalyser();
canvas=document.getElementById('canvas');
ctx=canvas.getContext('2d');
source.connect(analis);
analis.connect(audioCtx.destination);
analis.fftSize=1024;
canvas.width=512;
canvas.height=320;
dani = new Uint8Array(analis.frequencyBinCount);
function draw(){ //функція яка відображає отримані дані частот на полотні canvas
analis.getByteFrequencyData(dani);
ctx.fillStyle='black'; //колір фону
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle='blue'; //колір стопчика
var w=Math.ceil(canvas.width/dani.length);
var h=(canvas.height-5)/255;
for(var i=0,x=0;i<dani.length;i++,x+=w)
ctx.fillRect(x,canvas.height-(h*dani[i]),w,canvas.height);
setTimeout(draw,50); //викликаємо функція через 50 мс
}
draw();
audio.play(); //починаємо відтворення
}else alert('Ваш браузер не підтримує Web Audio');
if(window.AudioContext){
audioCtx1 = new AudioContext();
audio1 = new Audio('/dani/test.ogg');
source1 = audioCtx1.createMediaElementSource(audio1);
analis1=audioCtx1.createAnalyser();
canvas1=document.getElementById('canva');
ctx1=canvas1.getContext('2d');
source1.connect(analis1);
analis1.connect(audioCtx1.destination);
analis1.fftSize=1024;
canvas1.width=600;
canvas1.height=380;
dani1 = new Uint8Array(analis1.frequencyBinCount);
function draw1(){ //функція яка відображає отримані дані частот на полотні canvas
analis1.getByteFrequencyData(dani1);
ctx1.fillStyle='black'; //колір фону
ctx1.fillRect(0, 0, canvas1.width, canvas1.height);
ctx1.fillStyle='blue'; //колір стопчика
ctx1.shadowBlur=26;
ctx1.shadowColor='red';
var w1=Math.ceil(canvas1.width/dani1.length);
var h1=(canvas1.height-5)/255;
ctx1.beginPath();
ctx1.moveTo(0,canvas1.height);
for(var i=0,x=0;i<dani1.length;i++,x+=w1)
ctx1.lineTo(x,canvas1.height-(h1*dani1[i]));
ctx1.lineTo(canvas1.width, canvas1.height);
ctx1.fill();
setTimeout(draw1,50); //викликаємо функція через 50 мс
}
draw1();
audio1.play(); //починаємо відтворення
}else alert('Ваш браузер не підтримує Web Audio');
Створимо гістограму з малою кількістю стопчиків, з відстаню 1 піксель. Вкажемо fftSize мінімальне значення 32.
if(window.AudioContext){
audioCtx2 = new AudioContext();
audio2 = new Audio('/dani/test.ogg');
source2 = audioCtx2.createMediaElementSource(audio2);
analis2=audioCtx2.createAnalyser();
canvas2=document.getElementById('canvas2');
ctx2=canvas2.getContext('2d');
source2.connect(analis2);
analis2.connect(audioCtx2.destination);
analis2.fftSize=32;
canvas2.width=320;
canvas2.height=150;
dani2 = new Uint8Array(analis2.frequencyBinCount);
function draw2(){ //функція яка відображає отримані дані частот на полотні canvas
analis2.getByteFrequencyData(dani2);
ctx2.fillStyle='black'; //колір фону
ctx2.fillRect(0, 0, canvas2.width, canvas2.height);
ctx2.fillStyle='red'; //колір стопчика
var w2=Math.ceil((canvas2.width-dani2.length)/dani2.length);
var h2=(canvas2.height-5)/255;
for(var i2=0,x2=0;i2<dani2.length;i2++){
ctx2.fillRect(x2,canvas2.height-(h2*dani2[i2]),w2,canvas2.height);
x2+=w2+1; //вираховуємо кординати наступного стопчика + 1 піксель
}
setTimeout(draw2,50);
}
draw2();
audio2.play();
}else alert('Ваш браузер не підтримує Web Audio');
Додаємо градієнт до стопчика:
if(window.AudioContext){
audioCtxG = new AudioContext();
audioG = new Audio('/dani/test.ogg');
sourceG = audioCtxG.createMediaElementSource(audioG);
analisG=audioCtxG.createAnalyser();
canvasG=document.getElementById('canvasG');
ctxG=canvasG.getContext('2d');
sourceG.connect(analisG);
analisG.connect(audioCtxG.destination);
analisG.fftSize=256;
canvasG.width=600;
canvasG.height=320;
daniG = new Uint8Array(analisG.frequencyBinCount);
var gradient=ctxG.createLinearGradient(0, 0, 0, canvasG.height);
gradient.addColorStop(0,'#FF0000');
gradient.addColorStop(0.4,'#FF6A00');
gradient.addColorStop(0.5,'#FFD800');
gradient.addColorStop(0.8,'#B6FF00');
gradient.addColorStop(1,'#4CFF00');
function drawG(){ //функція яка відображає отримані дані частот на полотні canvas
analisG.getByteFrequencyData(daniG);
ctxG.fillStyle='black'; //колір фону
ctxG.fillRect(0, 0, canvasG.width, canvasG.height);
ctxG.fillStyle=gradient; //колір стопчика
var wG=Math.ceil((canvasG.width-daniG.length)/daniG.length);
var hG=(canvasG.height-5)/255;
for(var iG=0,xG=0;iG<daniG.length;iG++){
ctxG.fillRect(xG,canvasG.height-(hG*daniG[iG]),wG,canvasG.height);
xG+=wG+1; //вираховуємо кординати наступного стопчика + 1 піксель
}
setTimeout(drawG,50);
}
drawG();
audioG.play();
}else alert('Ваш браузер не підтримує Web Audio');
Також можна показувати лише вершину стопчиків гістаграми.
if(window.AudioContext){
audioCtx3 = new AudioContext();
audio3 = new Audio('/dani/test.ogg');
source3 = audioCtx3.createMediaElementSource(audio3);
analis3=audioCtx3.createAnalyser();
canvas3=document.getElementById('canvas3');
ctx3=canvas3.getContext('2d');
source3.connect(analis3);
analis3.connect(audioCtx3.destination);
analis3.fftSize=32;
canvas3.width=320;
canvas3.height=150;
dani3 = new Uint8Array(analis3.frequencyBinCount);
function draw3(){ //функція яка відображає отримані дані частот на полотні canvas
analis3.getByteFrequencyData(dani3);
ctx3.fillStyle='black'; //колір фону
ctx3.fillRect(0, 0, canvas3.width, canvas3.height);
ctx3.fillStyle='red'; //колір стопчика
var w3=Math.ceil((canvas3.width-dani3.length)/dani3.length);
var h3=(canvas3.height)/255;
for(var i3=0,x3=0;i3<dani3.length;i3++){
ctx3.fillRect(x3, canvas3.height-(h3*dani3[i3]), w3, 10);
x3+=w3+1; //вираховуємо кординати наступного стопчика + 1 піксель
}
setTimeout(draw3,50);
}
draw3();
audio3.play();
}else alert('Ваш браузер не підтримує Web Audio');
Літаючі шари
Візуалізація звуку у вигляді літаючих шарів. Розмір кожного шару змінюється відносно частоти звуку.
canvas4=document.getElementById('canvas4');
ctx4=canvas4.getContext('2d');
audioCtx4 = new AudioContext();
audio4 = new Audio('/dani/test.ogg');
source4 = audioCtx4.createMediaElementSource(audio4);
analis4=audioCtx4.createAnalyser();
canvas4=document.getElementById('canvas4');
ctx4=canvas4.getContext('2d');
source4.connect(analis4);
analis4.connect(audioCtx4.destination);
analis4.fftSize=32;
canvas4.width=800;
canvas4.height=600;
dani4 = new Uint8Array(analis4.frequencyBinCount);
var colorCircles=['red', 'blue', 'yellow', '#3f3f3f', '#00FF21', '#A17FFF', '#FF00DC', '#FF6A00'];
var circles=[];
for(var i=0;i<dani4.length;i++){
circles[i]={x:parseInt(Math.random()*canvas4.width),
y:parseInt(Math.random()*canvas4.height),
left: Math.random()>0.5,
top: Math.random()<0.5,
color: colorCircles[parseInt(Math.random()*colorCircles.length)]
};
}
draw4=function(){
ctx4.fillStyle='black';
ctx4.globalAlpha=1;
ctx4.shadowBlur=15;
ctx4.fillRect(0, 0, canvas4.width, canvas4.height);
analis4.getByteFrequencyData(dani4);
for(var i=0, radius;i<dani4.length;i++){
radius=50*(dani4[i]/255);
ctx4.beginPath();
circles[i].x+=(circles[i].left?2:-2);
circles[i].y+=(circles[i].top?2:-2);
if(circles[i].x<0)circles[i].left=true;
if(circles[i].x>canvas4.width)circles[i].left=false;
if(circles[i].y<0)circles[i].top=true;
if(circles[i].y>canvas4.height)circles[i].top=false;
ctx4.globalAlpha=dani4[i]/255;
ctx4.arc(circles[i].x, circles[i].y, radius, 0, 6.28);
ctx4.fillStyle=ctx4.strokeStyle=ctx4.shadowColor=circles[i].color;
ctx4.fill();
//ctx4.globalAlpha=0.12;
//ctx4.lineWidth=5;
//ctx4.stroke();
}
setTimeout(draw4,50);
}
draw4();
audio4.play();
Осцилографія
Візуалізація у вигляді осцилографії звуку.
if(window.AudioContext){
audio5 = new AudioContext();
player5 = new Audio();
player5.src='/dani/test.ogg';
source5 = audio5.createMediaElementSource(player5);
analis5=audio5.createAnalyser(); //створюємо об'єкт AnalyserNode
canvas5=document.getElementById('canvas5');
ctx5=canvas5.getContext('2d');
canvas5.width=600;
canvas5.height=200;
analis5.fftSize=512;
source5.connect(analis5);
analis5.connect(audio5.destination);
dani5 = new Uint8Array(analis5.frequencyBinCount);
draw5=function(){ //функція яка відображає отримані дані частот на полотні canvas
analis5.getByteTimeDomainData(dani5);
ctx5.fillStyle='black';
ctx5.fillRect(0, 0, canvas5.width, canvas5.height);
ctx5.lineWidth=2;
ctx5.strokeStyle='#757272';
ctx5.beginPath();
var w5=canvas5.width/dani5.length;
var h5=canvas5.height/2/128;
ctx5.moveTo(0, dani5[0]);
for(var i5=1,x5=0;i5<dani5.length;i5++,x5+=w5){
ctx5.lineTo(x5, h5*dani5[i5]);
}
ctx5.lineTo(x5+10, h5*dani5[i5-2]);
ctx5.stroke();
setTimeout(draw5,50);
}
draw5();
player5.play();
}else alert('Ваш браузер не підтримує Web Audio');
Розділюємо звук на лівий і правий за допомогою ChannelSplitterNode, і відображаємо лівий синім а правий червоним кольором.
if(window.AudioContext){
audioMC = new AudioContext();
playerMC = new Audio();
playerMC.src='/dani/test.ogg';
sourceMC = audioMC.createMediaElementSource(playerMC);
splitter=audioMC.createChannelSplitter();
analisMCl=audioMC.createAnalyser(); //створюємо об'єкт AnalyserNode
analisMCr=audioMC.createAnalyser();
analisMCc=audioMC.createAnalyser();
canvasMC=document.getElementById('canvasMC');
ctxMC=canvasMC.getContext('2d');
canvasMC.width=600;
canvasMC.height=320;
analisMCl.fftSize=analisMCr.fftSize=128;
sourceMC.connect(splitter);
splitter.connect(analisMCl,0);
splitter.connect(analisMCr,1);
analisMCl.connect(audioMC.destination);
analisMCr.connect(audioMC.destination);
analisMCc.connect(audioMC.destination);
daniMCl = new Uint8Array(analisMCl.frequencyBinCount);
daniMCr = new Uint8Array(analisMCr.frequencyBinCount);
drawMC=function(){ //функція яка відображає отримані дані частот на полотні canvas
analisMCl.getByteTimeDomainData(daniMCl);
analisMCr.getByteTimeDomainData(daniMCr);
ctxMC.fillStyle='black';
ctxMC.fillRect(0, 0, canvasMC.width, canvasMC.height);
ctxMC.lineWidth=2;
var wMC=canvasMC.width/daniMCl.length;
var hMC=canvasMC.height/2/128;
//left
ctxMC.strokeStyle='blue';
ctxMC.beginPath();
ctxMC.moveTo(0, daniMCl[0]);
for(var iMC=1,xMC=0;iMC<daniMCl.length;iMC++,xMC+=wMC){
ctxMC.lineTo(xMC, hMC*daniMCl[iMC]);
}
ctxMC.lineTo(canvasMC.width, hMC*daniMCl[iMC-2]);
ctxMC.stroke();
//right
ctxMC.strokeStyle='red';
ctxMC.beginPath();
ctxMC.moveTo(0, daniMCr[0]);
for(var iMC=1,xMC=0;iMC<daniMCr.length;iMC++,xMC+=wMC){
ctxMC.lineTo(xMC, hMC*daniMCr[iMC]);
}
ctxMC.lineTo(canvasMC.width, hMC*daniMCr[iMC-2]);
ctxMC.stroke();
setTimeout(drawMC, 50);
}
drawMC();
playerMC.play();
}else alert('Ваш браузер не підтримує Web Audio');
Коло
Візуалізація звуку у вигляді кола.
if(window.AudioContext){
audio6 = new AudioContext();
player6 = new Audio();
player6.src='/dani/test.ogg';
source6 = audio6.createMediaElementSource(player6);
analis6=audio6.createAnalyser(); //створюємо об'єкт AnalyserNode
canvas6=document.getElementById('canvas6');
ctx6=canvas6.getContext('2d');
canvas6.width=800;
canvas6.height=600;
analis6.fftSize=1024;
source6.connect(analis6);
analis6.connect(audio6.destination);
dani6 = new Uint8Array(analis6.frequencyBinCount);
centerX=canvas6.width/2;
centerY=canvas6.height/2;
radius=parseInt(Math.min(canvas6.width,canvas6.height)/2);
function drawCircle(){
analis6.getByteTimeDomainData(dani6);
ctx6.fillStyle='black';
ctx6.fillRect(0,0,canvas6.width,canvas6.height);
ctx6.beginPath();
var x, y, dx, dy;
for(var i = 0, suma=0; i < 361; i ++){
suma+=dani6[i];
x=Math.cos(i/180 * Math.PI) * radius + centerX;
y=Math.sin(i/180 * Math.PI) * radius + centerY;
dx=(x<centerX? ((centerX-x)*(dani6[i]/255))+x: ((x-centerX)*(dani6[i]/255))+centerX);
dy=(y<centerY? ((centerY-y)*(dani6[i]/255))+y: ((y-centerY)*(dani6[i]/255))+centerY);
if(i==0)
ctx6.moveTo(dx, dy);
else
ctx6.lineTo(dx, dy);
}
ctx6.lineWidth=6;
ctx6.strokeStyle='red';
ctx6.shadowBlur=10;
ctx6.shadowColor='red';
ctx6.closePath();
ctx6.stroke();
setTimeout(drawCircle,50);
}
drawCircle();
player6.play();
}else alert('Ваш браузер не підтримує WebAudio');
Пляма
Візуалізація звуку у вигляді плями.
if(window.AudioContext){
audio7 = new AudioContext();
player7 = new Audio();
player7.src='/dani/test.ogg';
source7 = audio7.createMediaElementSource(player7);
analis7=audio7.createAnalyser(); //створюємо об'єкт AnalyserNode
canvas7=document.getElementById('canvas7');
ctx7=canvas7.getContext('2d');
canvas7.width=800;
canvas7.height=600;
analis7.fftSize=1024;
source7.connect(analis7);
analis7.connect(audio7.destination);
dani7 = new Uint8Array(analis7.frequencyBinCount);
centerX=canvas7.width/2;
centerY=canvas7.height/2;
radius=Math.min(canvas7.width,canvas7.height)/2;
function drawCircle(){
analis7.getByteTimeDomainData(dani7);
ctx7.fillStyle='black';
ctx7.fillRect(0,0,canvas7.width,canvas7.height);
ctx7.beginPath();
var x, y, dx, dy;
for(var i = 0, suma=0; i < 361; i ++){
suma+=dani7[i];
x=Math.cos(i/180 * Math.PI) * radius + centerX;
y=Math.sin(i/180 * Math.PI) * radius + centerY;
dx=(x<centerX? ((centerX-x)*(dani7[i]/255))+x: ((x-centerX)*(dani7[i]/255))+centerX);
dy=(y<centerY? ((centerY-y)*(dani7[i]/255))+y: ((y-centerY)*(dani7[i]/255))+centerY);
if(i==0)
ctx7.moveTo(dx, dy);
else
ctx7.lineTo(dx, dy);
}
ctx7.fillStyle='blue';
ctx7.closePath();
ctx7.fill();
setTimeout(drawCircle,50);
}
drawCircle();
player7.play();
}else alert('Ваш браузер не підтримує WebAudio');
Дуги
Візуалізація звуку з допогомою 4 дуг.
if(window.AudioContext){
canvas8=document.getElementById('canvas8');
ctx8=canvas8.getContext('2d');
audio8 = new AudioContext();
player8 = new Audio();
player8.src='/dani/test.ogg';
source8 = audio8.createMediaElementSource(player8);
analis8=audio8.createAnalyser(); //створюємо об'єкт AnalyserNode
source8.connect(analis8);
analis8.connect(audio8.destination);
canvas8.width=800;
canvas8.height=600;
analis8.fftSize=64;
var w2=canvas8.width/2, h2=canvas8.height/2, kw=canvas8.width/128, kh=canvas8.height/128;
var dani8 = new Uint8Array(analis8.frequencyBinCount);
var color=['red','blue','yellow','white'];
function drawDuha(){
analis8.getByteTimeDomainData(dani8);
ctx8.fillStyle='black';
ctx8.fillRect(0,0,canvas8.width, canvas8.height);
ctx8.strokeStyle='red';
ctx8.lineWidth=2;
for(var j=0, len=dani8.length/4; j<4; j++){
for(var i=j*len, li=i+len; i<li;i++){
ctx8.beginPath();
switch(j){
case 0:
ctx8.bezierCurveTo(0, 0, w2, (dani8[i])*kh, canvas8.width, 0);
break;
case 1:
ctx8.bezierCurveTo(canvas8.width, 0, (128-dani8[i])*kw, h2, canvas8.width, canvas8.height);
break;
case 2:
ctx8.bezierCurveTo(0, canvas8.height, w2, (128-dani8[i])*kh, canvas8.width, canvas8.height);
break;
case 3:
ctx8.bezierCurveTo(0, 0, (dani8[i])*kw, h2, 0, canvas8.height);
break;
}
ctx8.stroke();
}
}
setTimeout(drawDuha, 50);
}
drawDuha();
player8.play();
}else alert('Ваш браузер не підтримує Web Audio');