创建完整的跟踪与网络音频API的波形 [英] Create a waveform of the full track with Web Audio API

查看:546
本文介绍了创建完整的跟踪与网络音频API的波形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

实时移动波形

我目前正在与网络音频API演奏和使用帆布制成的频谱。

功能动画(){
 VAR一个=新Uint8Array(analyser.frequencyBinCount)
     Y =新Uint8Array(analyser.frequencyBinCount),B,C,D;
 analyser.getByteTimeDomainData(Y);
 analyser.getByteFrequencyData(一);
 B = C =则为a.length;
 D = W / C;
 ctx.clearRect(0,0,W,H);
 而(B - ){
  变种BH =一[B] +1;
  ctx.fillStyle ='HSLA('+(B / C * 240)+,+(γ并[b] / 255 * 100 | 0)+'%,50%,1)';
  ctx.fillRect(1 * B,H-BH,1,BH);
  ctx.fillRect(1 * B,Y并[b],1,1);
 }
 动画= webkitRequestAnimationFrame(动画);
}

小的问题:有没有办法不写2次新Uint8Array(analyser.frequencyBinCount)

DEMO

添加MP3 / MP4文件和等待。 (在Chrome测试)

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

但也有很多问题。我无法找到的各种音频过滤器的适当的文件。

另外,如果你看谱,你会发现,经过70%的范围内没有数据。这意味着什么?也许从16K赫兹到2万赫兹没有声音?我会申请文本到画布上展现各种HZ。但其中??

我发现返回的数据是32长的功率为2048最大
和高度为256。

但真正的问题是...我想创建像TRAKTOR移动波形。

我已经做了,前段时间用PHP其上的文件转换成低比特率比提取数据和覆羽,为一个图像。我发现剧本的地方......但我不记得在哪里?
注意:需要 LAME

< PHP
$ a = $ _ GET [F];
如果(file_exists($ A)){
    如果(file_exists($一个。PNG)){
        标题(内容类型:图像/ PNG);
        回声的file_get_contents($ APNG);
    }其他{
        $ B = 3000; $ C = 300;定义(D,3);
        的ini_set(的max_execution_time,30000);
        函数n($克,$ H){
            $ G = hexdec(BIN2HEX($ G));
            $ H = hexdec(BIN2HEX($ H));
            回报($ G +($ H * 256));
        };
        $ K ​​= SUBSTR(MD5(时间()),0,10);
        副本(真实路径($ A),在/ var / WWW /\".$ķ。_ o.mp3);
        EXEC(跛脚/var/www/{$k}_o.mp3 -f -mm -b 16 --resample 8 /var/www/{$k}.mp3&放大器;&安培;跛脚--de code /var/www/{$k}.mp3 /var/www/{$k}.wav);
        //系统(跛脚{$}ķ-f _o.mp3 -mm -b 16 --resample 8 {$}ķ.MP3和放大器;&安培;跛脚--de code $ {}ķ.MP3 {$ ķ} .WAV);
        @unlink(/无功/网络/ {$}ķ_o.mp3);
        @unlink(/无功/网络/ {$}ķ.MP3);
        $ L =/无功/网络/ {$}ķ.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));
        如果($ Q == 2){$ R = 40;}其他{$ R = 80;};
        而(!的feof($ M)){
            $ T =阵列();
            为($ I = 0; $ I< $ P; $ I ++){
                $ T [$ i] =龟etc($ M);
            };
            开关($ P){
                案例1:$ S [] = N($ T [0],$ T [1]);打破;
                案例2:如果(ORD($ T [1])及128){$ U = 0;}其他{$ U = 128;}; $ U = CHR((ORD($ T [1])及127 )+ $ U); $ S [] =地板(N($ T [0],$ U)/ 256);打破;
            };
            FREAD($ M,$ R);
        };
        FCLOSE($ M);
        取消链接(/无功/网络/ {$}ķ.WAV);
        $ X = imagecreatetruecolor(sizeof的($ S)/天,$ C);
        imagealphablending($ X,FALSE);
        imagesavealpha($ X,真实);
        $ Y = imagecolorallocatealpha($ X,255255255127);
        imagefilledrectangle($ X,0,0,的sizeof($ S)/天,$ C,$ Y);
        为($ 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,真正的);
        imagefilledrectangle($ Z,0,0,$ B,$ C,$ Y);
        imagecopyresampled($ Z,$ X,0,0,0,0,$ B,$ C,sizeof的($ S)/天,$ C);
        imagepng($ Z,真实路径($ A)PNG);
        标题(内容类型:图像/ PNG);
        imagepng($ Z);
        imagedestroy($ Z);
    };
}其他{
    回声$一个;
};?>

该脚本...但你只限于4K像素的最大图像尺寸。

所以你没有一个很好的波形是否应该说唱present只有一些毫秒。

我需要存储/创建一个实时波形像traktors应用程式或PHP脚本是什么? BTW的TRAKTOR还具有彩色波形(php脚本不是)。

修改

我改写了你的脚本,它适合我的想法...它比较快的。

你可以在功能createArray看到里面我的各种线推入与作为x坐标的关键对象。

我只是取的最高数字。

这里是我们可以与色彩玩。

VAR ajaxB,AC,B,LC,OP,X,Y,ARRAY = {},W = 1024,H = 256;
VAR AMAX = Math.max.apply.bind(Math.max,数学);
功能错误(){
 的console.log(一);
};
功能createDrawing(){
 的console.log('drawingArray');
 变种C =使用document.createElement('画布');
 C.width = W;
 C.height = H;
 document.body.appendChild(℃);
 VAR上下文= C.getContext('2D');
 context.save();
 context.strokeStyle ='#121';
 context.globalCompositeOperation ='轻';
 L2 = W * 1;
 而(L2 - ){
  context.beginPath();
  context.moveTo(L2,0);
  context.lineTo(L2 + 1,ARRAY [L2]);
  context.stroke();
 }
 context.restore();
};
功能createArray(一){
 的console.log('creatingArray');
 B = A;
 LC = B.getChannelData(0); // Float32Array描述左声道
 L = LC.length;
 运算= W / L;
 对于(VAR I = 0; I< L,我++){
  X = W *我/ L | 0;
  Y = LC [I] * H / 2;
  如果(ARRAY [X]){
   ARRAY [X] .push(Y)
  }其他{
   !ARRAY [X-1] ||(ARRAY [X-1] = AMAX(ARRAY [X-1]));
   //上面一行包含值的数组
   //这可以转换为一个颜色
   //或者只是简单地创建渐变
   //根据平均最高分(频率?)什么
   ARRAY [X] = [Y]
  }
 };
 createDrawing();
};
功能去code(){
 的console.log('decodingMusic');
 AC =新webkitAudioContext
 AC.de codeAudioData(this.response,createArray,错误);
};
功能loadMusic(URL){
 的console.log('loadingMusic');
 ajaxB =新XMLHtt prequest;
 ajaxB.open('GET',URL);
 ajaxB.responseType ='arraybuffer';
 ajaxB.onload =去code;
 ajaxB.send();
}
loadMusic('AudioOrVideo.mp4');


解决方案

好了,我会做的是与XMLHtt prequest加载声音,然后去code。使用webaudio它,然后显示它仔细有你正在寻找的颜色。

我只是做了一个快速的版本,复制粘贴各种我的项目,那是相当的工作,你可能与此图片中看到:

问题是,它是慢如蜗牛。有(更多)体面的速度,你就必须做一些计算,以减少线路在画布上绘制的数量,因为在441000赫兹,你很快得到太多的线条吸引。

  // AUDIO CONTEXT
window.AudioContext = window.AudioContext || window.webkitAudioContext;如果警报(该网站无法在浏览器中运行尝试最近Chrome或Firefox。')(AudioContext!);VAR audioContext =新AudioContext();
VAR currentBuffer = NULL;// CANVAS
VAR canvasWidth = 512,canvasHeight = 120;
VAR newCanvas = createCanvas(canvasWidth,canvasHeight);
VAR语境= NULL;在window.onload = appendCanvas;
功能appendCanvas(){document.body.appendChild(newCanvas);
                          上下文= newCanvas.getContext('2D'); }// MUSIC LOADER + DE code
功能loadMusic(URL){
    VAR REQ =新XMLHtt prequest();
    req.open(GET,网址,真实);
    req.responseType =arraybuffer;
    req.onreadystatechange =功能(E){
          如果(req.readyState == 4){
             如果(req.status == 200)
                  audioContext.de codeAudioData(req.response,
                    功能(缓冲液){
                             currentBuffer =缓冲;
                             displayBuffer(缓冲液);
                    },ONDE codeError);
             其他
                  警报('期间load.Wrong URL或跨起源问题的错误');
          }
    };
    req.send();
}功能ONDE codeError(){警报('错误而解码文件。'); }// MUSIC DISPLAY
功能displayBuffer(BUFF / *是* AudioBuffer /){
   变种leftChannel = buff.getChannelData(0); // Float32Array描述左声道
   VAR lineOpacity = canvasWidth / leftChannel.length;
   context.save();
   context.fillStyle ='#222';
   context.fillRect(0,0,canvasWidth,canvasHeight);
   context.strokeStyle ='#121';
   context.globalCompositeOperation ='轻';
   context.translate(0,canvasHeight / 2);
   context.globalAlpha = 0.06; // lineOpacity;
   对于(VAR I = 0; I< leftChannel.length;我++){
       //上线我们得到什么?
       VAR X = Math.floor(canvasWidth * I / leftChannel.length);
       变种Y = leftChannel [I] * canvasHeight / 2;
       context.beginPath();
       context.moveTo(X,0);
       context.lineTo(X + 1,y)基
       context.stroke();
   }
   context.restore();
   的console.log('完成');
}功能createCanvas(W,H){
    VAR newCanvas =使用document.createElement('画布');
    newCanvas.width = W; newCanvas.height = H;
    返回newCanvas;
};
loadMusic('could_be_better.mp3');

编辑:这里的问题是,我们有太多的数据来绘制。以一记3分的MP3,你就会有3 * 60 * 44100 =约8.000.000行画。在有,比方说,1024像素分辨率的显示屏,使得每个像素的8.000线......结果
在上面的code,画布是做'采样',通过绘制与低透明度和'ligther组成模式(例如像素的R,G,B将增加时)。结果行
为了加速东西,你必须自己重样,但要获得一些颜色,它不只是一个降采样,你必须处理一组桶的(性能数组最有可能内),为每个水平像素(所以,说1024),并在每斗你计算累积声pressure,方差,最小值,最大值,然后在显示的时候,你决定你将如何呈现颜色。< BR>
例如:结果
0 positiveMin之间的值是很清楚的。 (任何样本是低于点)。结果
positiveMin和positiveAverage之间的值 - 方差暗,结果
positiveAverage之间的值 - 方差和positiveAverage +方差暗,结果
和positiveAverage +方差和positiveMax更轻之间的值。结果
(同为负值)
这使得5种颜色对于每个桶,而它仍然是相当一些工作,为您code以及浏览器来计算。结果
我不知道,如果性能能得到体面的这个问题,但我担心的统计数据的准确性和软件的颜色编码你提到不能在浏览器中达(显然不是实时的),而且你'将不得不作出一些妥协。

编辑2:结果
我试图让一些颜色统计出来,但它很失败。我的猜测,现在,是在tracktor的家伙取决于频率也改变颜色......这里颇有些工作......

总之,只要备案时,code,平均/平均变化如下。结果
(方差太低,我不得不用平均变化)。

  // MUSIC DISPLAY
功能displayBuffer2(BUFF / *是* AudioBuffer /){
   变种leftChannel = buff.getChannelData(0); // Float32Array描述左声道
   //我们'重新取样与cumul,计数,方差
   //偏移0:1 PositiveCumul:PositiveCount 2:PositiveVariance
   // 3:NegativeCumul 4:NegativeCount 5:NegativeVariance
   //使每桶6数据
   变种再采样=新Float64Array(canvasWidth * 6);
   变种I = 0,J = 0,buckIndex = 0;
   变种分钟= 1E3,最大= -​​1e3;
   VAR thisValue = 0,RES = 0;
   VAR sampleCount = leftChannel.length;
   //第一遍均值
   对于(i = 0; I&LT; sampleCount;我++){
        //在水桶我们跌倒?
        buckIndex = 0 | (canvasWidth * I / sampleCount);
        buckIndex * = 6;
        //积极的还是消极的?
        thisValue = leftChannel [I]
        如果(thisValue大于0){
            重新取样[buckIndex] + = thisValue;
            再采样[buckIndex + 1] + = 1;
        }否则如果(thisValue℃,){
            再采样[buckIndex + 3] + = thisValue;
            再采样[buckIndex + 4] + = 1;
        }
        如果(thisValue&LT;分)分= thisValue;
        如果(thisValue&GT;最大)最大= thisValue;
   }
   //计算现在的意思
   对于(i = 0,J = 0; I&LT; canvasWidth;我++,J + = 6){
       如果(重新采样[J + 1]!= 0){
             重采样[J] / =重采样[J + 1]; ;
       }
       如果(重新采样[J + 4]!= 0){
             重采样[J + 3] / =重采样[J + 4];
       }
   }
   //第二次的平均偏差(方差过低)
   对于(i = 0; I&LT; leftChannel.length;我++){
        //在水桶我们跌倒?
        buckIndex = 0 | (canvasWidth * I / leftChannel.length);
        buckIndex * = 6;
        //积极的还是消极的?
        thisValue = leftChannel [I]
        如果(thisValue大于0){
            再采样[buckIndex + 2] + = Math.abs(重采样[buckIndex] - thisValue);
        }否则如果(thisValue℃,){
            再采样[buckIndex + 5] + = Math.abs(重采样[buckIndex + 3] - thisValue);
        }
   }
   //现在计算平均偏差/方差
   对于(i = 0,J = 0; I&LT; canvasWidth;我++,J + = 6){
        如果(重新采样[J + 1])重新采样[J + 2] / =重采样[J + 1];
        如果(重新采样[J + 4])重新采样[J + 5] / =重采样[J + 4];
   }
   context.save();
   context.fillStyle ='#000';
   context.fillRect(0,0,canvasWidth,canvasHeight);
   context.translate(0.5,canvasHeight / 2);
  context.scale(1,200);   对于(VAR I = 0; I&LT; canvasWidth;我++){
        J = * 6;
       方差negativeAvg - - //来自positiveAvg画方差
       context.strokeStyle ='#F00;
       context.beginPath();
       context.moveTo(I,(重采样[J] - 重采样[J + 2]));
       context.lineTo(I,(重采样[J +3] +重新采样[J + 5));
       context.stroke();
       //借鉴positiveAvg - 方差positiveAvg +方差
       context.strokeStyle ='#FFF';
       context.beginPath();
       context.moveTo(I,(重采样[J] - 重采样[J + 2]));
       context.lineTo(I,(重采样[J] +重新采样[J + 2]));
       context.stroke();
       //借鉴negativeAvg +方差negativeAvg - 方差
       // context.strokeStyle ='#FFF';
       context.beginPath();
       context.moveTo(I,(重采样[J + 3] +重新采样[J + 5));
       context.lineTo(I,(重采样[J + 3] - 重采样[J + 5));
       context.stroke();
   }
   context.restore();
   的console.log('做231甲苯基');
}

Realtime moving Waveform

I'm currently playing with Web Audio API and made a spectrum using canvas.

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 question: is there a way to not write 2 times new Uint8Array(analyser.frequencyBinCount)?

DEMO

add a MP3/MP4 file and wait. (tested in Chrome)

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

But there are many problems. I can't find a proper documentation of the various audio filters.

Also, if you look at the spectrum you will notice that after 70% or the range there is no data. What does that mean? that maybe from 16k hz to 20k hz is no sound? I would apply a text to the canvas to show the various HZ. but where??

I found out that the returned data is a power of 32 in length with a max of 2048 and the height is always 256.

BUT the real question is ... I want to create a moving waveform like in traktor.

I already did that some time ago with PHP it converts the file to low bitrate than extracts the data and coverts that to a image. i found the script somewhere...but I don't remember where... note: needs 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;
};

?>

The script works... but you are limited to a max image size of 4k pixels.

so you have not a nice waveform if it should rappresent only some milliseconds.

What do i need to store/create a realtime waveform like the traktors app or this php script? btw the traktor has also a colored waveform(the php script not).

EDIT

I rewrote your script that it fits my idea... it's relatively fast.

As you can see inside the function createArray i push the various lines into an object with the key as x coordinate.

I'm simply taking the the highest number.

here is where we could play with the colors.

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');

解决方案

Ok, so what i would do is to load the sound with an XMLHttpRequest, then decode it using webaudio, then display it 'carefully' to have the colors you are searching for.

I just made a quick version, copy-pasting from various of my projects, it is quite working, as you might see with this picture :

The issue is that it is slow as hell. To have (more) decent speed, you'll have to do some computation to reduce the number of lines to draw on the canvas, because at 441000 Hz, you very quickly get too many lines to draw.

// 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 : The issue here is that we have too much data to draw. Take a 3 minutes mp3, you'll have 3*60*44100 = about 8.000.000 line to draw. On a display that has, say, 1024 px resolution, that makes 8.000 lines per pixel...
In the code above, the canvas is doing the 'resampling', by drawing lines with low-opacity and in 'ligther' composition mode (e.g. pixel's r,g,b will add-up).
To speed-up things, you have to re-sample by yourself, but to get some colors, it's not just a down-sampling, you'll have to handle a set (within a performance array most probably) of 'buckets', one for each horizontal pixel (so, say 1024), and in every bucket you compute the cumulated sound pressure, the variance, min, max and then, at display time, you decide how you will render that with colors.
For instance :
values between 0 positiveMin are very clear. (any sample is below that point).
values between positiveMin and positiveAverage - variance are darker,
values between positiveAverage - variance and positiveAverage + variance are darker,
and values between positiveAverage+variance and positiveMax lighter .
(same for negative values) That makes 5 colors for each bucket, and it's still quite some work, for you to code and for the browser to compute.
I don't know if the performance could get decent with this, but i fear the statistics accuracy and the color coding of the software you mention can't be reached on a browser (obviously not in real-time), and that you'll have to make some compromises.

Edit 2 :
I tried to get some colors out of stats but it quite failed. My guess, now, is that the guys at tracktor also change color depending on frequency.... quite some work here....

Anyway, just for the record, the code for an average / mean variation follows.
(variance was too low, i had to use mean variation).

// 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');
}

这篇关于创建完整的跟踪与网络音频API的波形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆