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

问题描述:

我从一首歌曲中将原始44,1 kHz音频数据作为Javascript数组,并且我想创建一个可缩放的时间轴。在Javascript中绘制可缩放的音频波形时间轴

实例从时间轴无畏:

Sample waveform from Audacity

由于有几百万的时间点的普通的JavaScript图形库可能不会削减它:我想,不知道,正常的图形库将在此多人死亡时间点。但是,是否存在用于JS的这种可视化的库?画布,webGL,SVG都是可以接受的解决方案。

解决方案最好带有缩放和平移。

请注意,这发生严格的客户端和服务器端解决方案不可接受。

+0

您不能扔你的振幅值成图形库,并希望简单地解决这个问题它会处理它。您需要创建缩小数据的“概览”或“预览”。 – 2012-07-26 20:35:19

+0

因此,我在问这样的解决方案是否已经存在?我很清楚目前的图形库无法处理它。 – 2012-07-27 07:46:27

+0

创建概述并不是一件困难的事情。这样的图书馆可能存在,但大部分麻烦在于将数据存入图书馆或从图书馆出库,而不是实际创建概览。 – 2012-07-27 14:42:28

我已经相当广泛地研究了这个相同的问题。据我所知,唯一现有的项目接近你想要的是wavesurfer.js。我没有使用它,但屏幕截图和描述听起来很有希望。请参阅this question

祝你好运。

我已经在浏览器中使用了RaphaelJS进行SVG渲染,它的表现非常好。这是我会去的。希望SVG能够胜任这项任务。

您不能简单地取出波形数据并渲染所有数据点,这是非常低效的。

变量解释:

  • 宽度:以像素为单位绘制区域宽度,max是屏幕宽度
  • 高度:同的宽度,但然后绘图区的高度
  • :每个像素的采样数,这是您的缩放级别
  • 分辨率:数字o f采样取像素采样范围,调整性能与准确性。
  • 滚动:您将需要表现的虚拟滚动,这是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; 
} 

有了这些数据,你可以画出像这样,你可以使用线条,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); 
    } 
}