在Javascript中绘制可缩放的音频波形时间轴

时间:2012-07-26 20:08:30

标签: javascript graph html5-canvas

我将来自歌曲的原始44,1 kHz音频数据作为Javascript数组,我想创建一个可缩放的时间轴。

Audacity的示例时间表:

Sample waveform from Audacity

由于有数百万个时间点正常Javascript图形库可能不会削减它:我认为,不确定,正常的图形库将在这么多时间点死亡。但是,JS的这种可视化是否已存在库? Canvas,webGL,SVG都是可以接受的解决方案。

优选使用变焦和平移的解决方案。

请注意,这种情况严格地发生在客户端,服务器端解决方案不可接受。

3 个答案:

答案 0 :(得分:3)

我已经非常广泛地研究了同样的问题。据我所知,唯一能够接近您想要的项目是wavesurfer.js。我没有使用它,但屏幕截图和描述听起来很有希望。

另见this question

祝你好运。

答案 1 :(得分:0)

我在浏览器中使用RaphaelJS进行SVG渲染,但它表现得非常好。这就是我想要的。希望SVG能胜任这项任务。

答案 2 :(得分:0)

你不能简单地获取波形数据并渲染所有数据点,这非常低效。

变量说明:

  • 宽度:以像素为单位绘制区域宽度,最大值是屏幕宽度
  • 高度:与宽度相同,但绘制区域的高度
  • spp :每个像素的样本数,这是缩放级别
  • 分辨率:每个像素采样范围采样的数量,调整性能与精度。
  • 滚动:您需要虚拟滚动才能获得性能,这是px中的滚动位置
  • 数据:原始音频数据阵列,可能长达数百万个样本
  • drawData :用于绘制的缩减音频数据

您将只需从音频数据中获取视口中的样本并减少这些样本。通常这会导致数据集为2 *宽度,您可以使用此数据集来渲染图像。 要缩小增加spp,要放大减小它。更改滚动值可以平移它。

以下代码具有O(RN)复杂度,其中N是宽度,R是分辨率。最大精度为spp <=分辨率。

代码看起来像这样,它获得峰值,你也可以做rms或平均值。

let reduceAudioPeak = function(data, spp, scroll, width, resolution) {
    let drawData = new Array(width);
    let startSample = scroll * spp; 
    let skip = Math.ceil(spp / resolution);

    // For each pixel in draw area
    for (let i = 0; i < width; i++) {
        let min = 0; // minimum value in sample range
        let max = 0; // maximum value in sample range
        let pixelStartSample = startSample + (i * spp);

        // Iterate over the sample range for this pixel (spp) 
        // and find the min and max values. 
        for(let j = 0; j < spp; j += skip) {
           const index = pixelStartSample + j;
           if(index < data.length) {
               let val = data[index];
               if (val > max) {
                  max = val;
               } else if (val < min) {
                  min = val;
               }
           }
        }

        drawData[i] = [min, max];
    }
    return drawData;
}

使用这些数据你可以像这样绘制它,你可以使用line,svg等:

let drawWaveform = function(canvas, drawData, width, height) {
   let ctx = canvas.getContext('2d');
   let drawHeight = height / 2;

   // clear canvas incase there is already something drawn
   ctx.clearRect(0, 0, width, height);
   for(let i = 0; i < width; i++) {
      // transform data points to pixel height and move to centre
      let minPixel = drawData[i][0] * drawHeigth + drawHeight;
      let maxPixel = drawData[i][1] * drawHeight + drawHeight;
      let pixelHeight = maxPixel - minPixel;

      ctx.fillRect(i, minPixel, 1, pixelHeight);
   }
}