Візуалізація звуку

Публікації

З появою 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');
Адмін 2018-09-14 17:45:09
Коментарів 1
Alsk Alsk # 2021-07-31 14:00:05
ух, класно. Згадав молодість (це я про Ірину Білик)

Тільки зареєстровані користувачі можуть писати коментарі.