tutorial examples eventos javascript html5 audio canvas web-audio

javascript - eventos - web audio api examples



Cree una forma de onda de la pista completa con Web Audio API (3)

Forma de onda móvil en tiempo real

Actualmente estoy jugando con Web Audio API e hice un espectro usando lienzo.

function animate(){ var a=new Uint8Array(analyser.frequencyBinCount), y=new Uint8Array(analyser.frequencyBinCount),b,c,d; analyser.getByteTimeDomainData(y); analyser.getByteFrequencyData(a); b=c=a.length; d=w/c; ctx.clearRect(0,0,w,h); while(b--){ var bh=a[b]+1; ctx.fillStyle=''hsla(''+(b/c*240)+'',''+(y[b]/255*100|0)+''%,50%,1)''; ctx.fillRect(1*b,h-bh,1,bh); ctx.fillRect(1*b,y[b],1,1); } animation=webkitRequestAnimationFrame(animate); }

Mini pregunta: ¿hay una manera de no escribir 2 veces el new Uint8Array(analyser.frequencyBinCount) ?

MANIFESTACIÓN

agrega un archivo MP3 / MP4 y espera. (probado en Chrome)

http://jsfiddle.net/pc76H/2/

Pero hay muchos problemas. No puedo encontrar una documentación adecuada de los distintos filtros de audio.

Además, si observa el espectro, notará que después del 70% o del rango no hay datos. Qué significa eso? que tal vez de 16k hz a 20k hz no hay sonido? Yo aplicaría un texto al lienzo para mostrar los diferentes HZ. ¿¿pero donde??

Descubrí que los datos devueltos tienen una potencia de 32 en longitud con un máximo de 2048 y la altura siempre es 256.

PERO la pregunta real es ... Quiero crear una forma de onda en movimiento como en traktor.

Ya lo hice hace algún tiempo con PHP que convierte el archivo a una tasa de bits baja que extrae los datos y las cubre en una imagen. Encontré el script en algún lugar ... pero no recuerdo dónde ... nota: necesita LAME

<?php $a=$_GET["f"]; if(file_exists($a)){ if(file_exists($a.".png")){ header("Content-Type: image/png"); echo file_get_contents($a.".png"); }else{ $b=3000;$c=300;define("d",3); ini_set("max_execution_time","30000"); function n($g,$h){ $g=hexdec(bin2hex($g)); $h=hexdec(bin2hex($h)); return($g+($h*256)); }; $k=substr(md5(time()),0,10); copy(realpath($a),"/var/www/".$k."_o.mp3"); exec("lame /var/www/{$k}_o.mp3 -f -m m -b 16 --resample 8 /var/www/{$k}.mp3 && lame --decode /var/www/{$k}.mp3 /var/www/{$k}.wav"); //system("lame {$k}_o.mp3 -f -m m -b 16 --resample 8 {$k}.mp3 && lame --decode {$k}.mp3 {$k}.wav"); @unlink("/var/www/{$k}_o.mp3"); @unlink("/var/www/{$k}.mp3"); $l="/var/www/{$k}.wav"; $m=fopen($l,"r"); $n[]=fread($m,4); $n[]=bin2hex(fread($m,4)); $n[]=fread($m,4); $n[]=fread($m,4); $n[]=bin2hex(fread($m,4)); $n[]=bin2hex(fread($m,2)); $n[]=bin2hex(fread($m,2)); $n[]=bin2hex(fread($m,4)); $n[]=bin2hex(fread($m,4)); $n[]=bin2hex(fread($m,2)); $n[]=bin2hex(fread($m,2)); $n[]=fread($m,4); $n[]=bin2hex(fread($m,4)); $o=hexdec(substr($n[10],0,2)); $p=$o/8; $q=hexdec(substr($n[6],0,2)); if($q==2){$r=40;}else{$r=80;}; while(!feof($m)){ $t=array(); for($i=0;$i<$p;$i++){ $t[$i]=fgetc($m); }; switch($p){ case 1:$s[]=n($t[0],$t[1]);break; case 2:if(ord($t[1])&128){$u=0;}else{$u=128;};$u=chr((ord($t[1])&127)+$u);$s[]= floor(n($t[0],$u)/256);break; }; fread($m,$r); }; fclose($m); unlink("/var/www/{$k}.wav"); $x=imagecreatetruecolor(sizeof($s)/d,$c); imagealphablending($x,false); imagesavealpha($x,true); $y=imagecolorallocatealpha($x,255,255,255,127); imagefilledrectangle($x,0,0,sizeof($s)/d,$c,$y); for($d=0;$d<sizeof($s);$d+=d){ $v=(int)($s[$d]/255*$c); imageline($x,$d/d,0+($c-$v),$d/d,$c-($c-$v),imagecolorallocate($x,255,0,255)); }; $z=imagecreatetruecolor($b,$c); imagealphablending($z,false); imagesavealpha($z,true); imagefilledrectangle($z,0,0,$b,$c,$y); imagecopyresampled($z,$x,0,0,0,0,$b,$c,sizeof($s)/d,$c); imagepng($z,realpath($a).".png"); header("Content-Type: image/png"); imagepng($z); imagedestroy($z); }; }else{ echo $a; }; ?>

El script funciona ... pero está limitado a un tamaño máximo de imagen de 4k píxeles.

por lo que no tiene una forma de onda agradable si debería representar tan solo algunos milisegundos.

¿Qué necesito para almacenar / crear una forma de onda en tiempo real como la aplicación traktors o este script php? Por cierto, el traktor también tiene una forma de onda de color (el script php no).

EDITAR

Reescribí tu guión para que se ajuste a mi idea ... es relativamente rápido.

Como puede ver dentro de la función createArray, empujo las diversas líneas en un objeto con la tecla como la coordenada x.

Simplemente estoy tomando el número más alto.

Aquí es donde podríamos jugar con los colores.

var ajaxB,AC,B,LC,op,x,y,ARRAY={},W=1024,H=256; var aMax=Math.max.apply.bind(Math.max, Math); function error(a){ console.log(a); }; function createDrawing(){ console.log(''drawingArray''); var C=document.createElement(''canvas''); C.width=W; C.height=H; document.body.appendChild(C); var context=C.getContext(''2d''); context.save(); context.strokeStyle=''#121''; context.globalCompositeOperation=''lighter''; L2=W*1; while(L2--){ context.beginPath(); context.moveTo(L2,0); context.lineTo(L2+1,ARRAY[L2]); context.stroke(); } context.restore(); }; function createArray(a){ console.log(''creatingArray''); B=a; LC=B.getChannelData(0);// Float32Array describing left channel L=LC.length; op=W/L; for(var i=0;i<L;i++){ x=W*i/L|0; y=LC[i]*H/2; if(ARRAY[x]){ ARRAY[x].push(y) }else{ !ARRAY[x-1]||(ARRAY[x-1]=aMax(ARRAY[x-1])); // the above line contains an array of values // which could be converted to a color // or just simply create a gradient // based on avg max min (frequency???) whatever ARRAY[x]=[y] } }; createDrawing(); }; function decode(){ console.log(''decodingMusic''); AC=new webkitAudioContext AC.decodeAudioData(this.response,createArray,error); }; function loadMusic(url){ console.log(''loadingMusic''); ajaxB=new XMLHttpRequest; ajaxB.open(''GET'',url); ajaxB.responseType=''arraybuffer''; ajaxB.onload=decode; ajaxB.send(); } loadMusic(''AudioOrVideo.mp4'');


Hola también estaba enfrentando problema de tiempo de carga. Solo he controlado que al reducir el número de líneas que se quieren dibujar y la pequeña función de lienzo, la ubicación de las llamadas. Consulte el siguiente código para su referencia.

// AUDIO CONTEXT window.AudioContext = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext); if (!AudioContext) alert(''This site cannot be run in your Browser. Try a recent Chrome or Firefox. ''); var audioContext = new AudioContext(); var currentBuffer = null; // CANVAS var canvasWidth = window.innerWidth, canvasHeight = 120 ; var newCanvas = createCanvas (canvasWidth, canvasHeight); var context = null; window.onload = appendCanvas; function appendCanvas() { document.body.appendChild(newCanvas); context = newCanvas.getContext(''2d''); } // MUSIC LOADER + DECODE function loadMusic(url) { var req = new XMLHttpRequest(); req.open( "GET", url, true ); req.responseType = "arraybuffer"; req.onreadystatechange = function (e) { if (req.readyState == 4) { if(req.status == 200) audioContext.decodeAudioData(req.response, function(buffer) { currentBuffer = buffer; displayBuffer(buffer); }, onDecodeError); else alert(''error during the load.Wrong url or cross origin issue''); } } ; req.send(); } function onDecodeError() { alert(''error while decoding your file.''); } // MUSIC DISPLAY function displayBuffer(buff /* is an AudioBuffer */) { var drawLines = 500; var leftChannel = buff.getChannelData(0); // Float32Array describing left channel var lineOpacity = canvasWidth / leftChannel.length ; context.save(); context.fillStyle = ''#080808'' ; context.fillRect(0,0,canvasWidth,canvasHeight ); context.strokeStyle = ''#46a0ba''; context.globalCompositeOperation = ''lighter''; context.translate(0,canvasHeight / 2); //context.globalAlpha = 0.6 ; // lineOpacity ; context.lineWidth=1; var totallength = leftChannel.length; var eachBlock = Math.floor(totallength / drawLines); var lineGap = (canvasWidth/drawLines); context.beginPath(); for(var i=0;i<=drawLines;i++){ var audioBuffKey = Math.floor(eachBlock * i); var x = i*lineGap; var y = leftChannel[audioBuffKey] * canvasHeight / 2; context.moveTo( x, y ); context.lineTo( x, (y*-1) ); } context.stroke(); context.restore(); } function createCanvas ( w, h ) { var newCanvas = document.createElement(''canvas''); newCanvas.width = w; newCanvas.height = h; return newCanvas; }; loadMusic(''could_be_better.mp3'');


Ok, entonces lo que haría es cargar el sonido con un XMLHttpRequest, luego decodificarlo usando webaudio, luego mostrarlo ''cuidadosamente'' para que tenga los colores que está buscando.

Acabo de hacer una versión rápida, copiar y pegar de varios de mis proyectos, está funcionando bastante bien, como puede ver con esta imagen:

El problema es que es lento como el infierno. Para tener (más) una velocidad decente, tendrá que hacer algunos cálculos para reducir el número de líneas a dibujar en el lienzo, porque a 441000 Hz, obtiene muy rápidamente muchas líneas para dibujar.

// AUDIO CONTEXT window.AudioContext = window.AudioContext || window.webkitAudioContext ; if (!AudioContext) alert(''This site cannot be run in your Browser. Try a recent Chrome or Firefox. ''); var audioContext = new AudioContext(); var currentBuffer = null; // CANVAS var canvasWidth = 512, canvasHeight = 120 ; var newCanvas = createCanvas (canvasWidth, canvasHeight); var context = null; window.onload = appendCanvas; function appendCanvas() { document.body.appendChild(newCanvas); context = newCanvas.getContext(''2d''); } // MUSIC LOADER + DECODE function loadMusic(url) { var req = new XMLHttpRequest(); req.open( "GET", url, true ); req.responseType = "arraybuffer"; req.onreadystatechange = function (e) { if (req.readyState == 4) { if(req.status == 200) audioContext.decodeAudioData(req.response, function(buffer) { currentBuffer = buffer; displayBuffer(buffer); }, onDecodeError); else alert(''error during the load.Wrong url or cross origin issue''); } } ; req.send(); } function onDecodeError() { alert(''error while decoding your file.''); } // MUSIC DISPLAY function displayBuffer(buff /* is an AudioBuffer */) { var leftChannel = buff.getChannelData(0); // Float32Array describing left channel var lineOpacity = canvasWidth / leftChannel.length ; context.save(); context.fillStyle = ''#222'' ; context.fillRect(0,0,canvasWidth,canvasHeight ); context.strokeStyle = ''#121''; context.globalCompositeOperation = ''lighter''; context.translate(0,canvasHeight / 2); context.globalAlpha = 0.06 ; // lineOpacity ; for (var i=0; i< leftChannel.length; i++) { // on which line do we get ? var x = Math.floor ( canvasWidth * i / leftChannel.length ) ; var y = leftChannel[i] * canvasHeight / 2 ; context.beginPath(); context.moveTo( x , 0 ); context.lineTo( x+1, y ); context.stroke(); } context.restore(); console.log(''done''); } function createCanvas ( w, h ) { var newCanvas = document.createElement(''canvas''); newCanvas.width = w; newCanvas.height = h; return newCanvas; }; loadMusic(''could_be_better.mp3'');

Edit: El problema aquí es que tenemos demasiados datos para dibujar. Toma un mp3 de 3 minutos, tendrás 3 * 60 * 44100 = aproximadamente 8.000.000 líneas para dibujar. En una pantalla que tiene, digamos, una resolución de 1024 px, que hace 8.000 líneas por píxel ...
En el código anterior, el lienzo está haciendo el ''remuestreo'', dibujando líneas con baja opacidad y en el modo de composición ''más ligero'' (por ejemplo, el píxel r, g, b se sumará).
Para acelerar las cosas, debe volver a muestrear por sí mismo, pero para obtener algunos colores, no es solo un muestreo descendente, sino que tendrá que manejar un conjunto (probablemente dentro de una matriz de rendimiento) de ''cubos'', una para cada píxel horizontal (por lo tanto, digamos 1024), y en cada cubeta calcula la presión de sonido acumulada, la varianza, mín, máx y luego, en el momento de la visualización, decide cómo procesará eso con los colores.
Por ejemplo :
Los valores entre 0 positivoMin son muy claros. (cualquier muestra está por debajo de ese punto).
valores entre positiveMin y positiveAverage - la varianza es más oscura,
los valores entre positiveAverage - varianza y positiveAverage + varianza son más oscuros,
y los valores entre positiveAverage + variance y positiveMax más ligero.
(lo mismo para los valores negativos) Eso hace 5 colores para cada grupo, y todavía es bastante trabajo, para que usted codifique y para que el navegador lo calcule.
No sé si el rendimiento podría ser decente con esto, pero me temo que la precisión de las estadísticas y el código de colores del software que mencionas no se puede alcanzar en un navegador (obviamente no en tiempo real), y que '' Tendré que hacer algunos compromisos.

Edición 2:
Intenté sacar algunos colores de las estadísticas pero falló bastante. Mi conjetura, ahora, es que los chicos de tracktor también cambian de color según la frecuencia ... bastante trabajo aquí ...

De todos modos, solo para el registro, sigue el código para una variación promedio / promedio.
(la varianza fue muy baja, tuve que usar la variación media).

// MUSIC DISPLAY function displayBuffer2(buff /* is an AudioBuffer */) { var leftChannel = buff.getChannelData(0); // Float32Array describing left channel // we ''resample'' with cumul, count, variance // Offset 0 : PositiveCumul 1: PositiveCount 2: PositiveVariance // 3 : NegativeCumul 4: NegativeCount 5: NegativeVariance // that makes 6 data per bucket var resampled = new Float64Array(canvasWidth * 6 ); var i=0, j=0, buckIndex = 0; var min=1e3, max=-1e3; var thisValue=0, res=0; var sampleCount = leftChannel.length; // first pass for mean for (i=0; i<sampleCount; i++) { // in which bucket do we fall ? buckIndex = 0 | ( canvasWidth * i / sampleCount ); buckIndex *= 6; // positive or negative ? thisValue = leftChannel[i]; if (thisValue>0) { resampled[buckIndex ] += thisValue; resampled[buckIndex + 1] +=1; } else if (thisValue<0) { resampled[buckIndex + 3] += thisValue; resampled[buckIndex + 4] +=1; } if (thisValue<min) min=thisValue; if (thisValue>max) max = thisValue; } // compute mean now for (i=0, j=0; i<canvasWidth; i++, j+=6) { if (resampled[j+1] != 0) { resampled[j] /= resampled[j+1]; ; } if (resampled[j+4]!= 0) { resampled[j+3] /= resampled[j+4]; } } // second pass for mean variation ( variance is too low) for (i=0; i<leftChannel.length; i++) { // in which bucket do we fall ? buckIndex = 0 | (canvasWidth * i / leftChannel.length ); buckIndex *= 6; // positive or negative ? thisValue = leftChannel[i]; if (thisValue>0) { resampled[buckIndex + 2] += Math.abs( resampled[buckIndex] - thisValue ); } else if (thisValue<0) { resampled[buckIndex + 5] += Math.abs( resampled[buckIndex + 3] - thisValue ); } } // compute mean variation/variance now for (i=0, j=0; i<canvasWidth; i++, j+=6) { if (resampled[j+1]) resampled[j+2] /= resampled[j+1]; if (resampled[j+4]) resampled[j+5] /= resampled[j+4]; } context.save(); context.fillStyle = ''#000'' ; context.fillRect(0,0,canvasWidth,canvasHeight ); context.translate(0.5,canvasHeight / 2); context.scale(1, 200); for (var i=0; i< canvasWidth; i++) { j=i*6; // draw from positiveAvg - variance to negativeAvg - variance context.strokeStyle = ''#F00''; context.beginPath(); context.moveTo( i , (resampled[j] - resampled[j+2] )); context.lineTo( i , (resampled[j +3] + resampled[j+5] ) ); context.stroke(); // draw from positiveAvg - variance to positiveAvg + variance context.strokeStyle = ''#FFF''; context.beginPath(); context.moveTo( i , (resampled[j] - resampled[j+2] )); context.lineTo( i , (resampled[j] + resampled[j+2] ) ); context.stroke(); // draw from negativeAvg + variance to negativeAvg - variance // context.strokeStyle = ''#FFF''; context.beginPath(); context.moveTo( i , (resampled[j+3] + resampled[j+5] )); context.lineTo( i , (resampled[j+3] - resampled[j+5] ) ); context.stroke(); } context.restore(); console.log(''done 231 iyi''); }


// AUDIO CONTEXT window.AudioContext = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext); if (!AudioContext) alert(''This site cannot be run in your Browser. Try a recent Chrome or Firefox. ''); var audioContext = new AudioContext(); var currentBuffer = null; // CANVAS var canvasWidth = window.innerWidth, canvasHeight = 120 ; var newCanvas = createCanvas (canvasWidth, canvasHeight); var context = null; window.onload = appendCanvas; function appendCanvas() { document.body.appendChild(newCanvas); context = newCanvas.getContext(''2d''); } // MUSIC LOADER + DECODE function loadMusic(url) { var req = new XMLHttpRequest(); req.open( "GET", url, true ); req.responseType = "arraybuffer"; req.onreadystatechange = function (e) { if (req.readyState == 4) { if(req.status == 200) audioContext.decodeAudioData(req.response, function(buffer) { currentBuffer = buffer; displayBuffer(buffer); }, onDecodeError); else alert(''error during the load.Wrong url or cross origin issue''); } } ; req.send(); } function onDecodeError() { alert(''error while decoding your file.''); } // MUSIC DISPLAY function displayBuffer(buff /* is an AudioBuffer */) { var drawLines = 500; var leftChannel = buff.getChannelData(0); // Float32Array describing left channel var lineOpacity = canvasWidth / leftChannel.length ; context.save(); context.fillStyle = ''#080808'' ; context.fillRect(0,0,canvasWidth,canvasHeight ); context.strokeStyle = ''#46a0ba''; context.globalCompositeOperation = ''lighter''; context.translate(0,canvasHeight / 2); //context.globalAlpha = 0.6 ; // lineOpacity ; context.lineWidth=1; var totallength = leftChannel.length; var eachBlock = Math.floor(totallength / drawLines); var lineGap = (canvasWidth/drawLines); context.beginPath(); for(var i=0;i<=drawLines;i++){ var audioBuffKey = Math.floor(eachBlock * i); var x = i*lineGap; var y = leftChannel[audioBuffKey] * canvasHeight / 2; context.moveTo( x, y ); context.lineTo( x, (y*-1) ); } context.stroke(); context.restore(); } function createCanvas ( w, h ) { var newCanvas = document.createElement(''canvas''); newCanvas.width = w; newCanvas.height = h; return newCanvas; }; loadMusic(''https://raw.githubusercontent.com/katspaugh/wavesurfer.js/master/example/media/demo.wav'');