25

如何编写 JavaScript 程序来显示音频文件中的波形?我想使用网络音频和画布。

我试过这段代码:

(new window.AudioContext).decodeAudioData(audioFile, function (data) {
   var channel = data.getChannelData(0);
   for (var i = 0; i < channel; i++) {
       canvas.getContext('2d').fillRect(i, 1, 40 - channel[i], 40);
   }
});

但结果与我想要的相差甚远(即图像不平滑,因为我用矩形绘制)。我希望它看起来像这张图片一样平滑:

波形示例

关于如何实现波形的任何提示?

4

4 回答 4

45

毕竟推出了我自己的库:wavesurfer.js

它从 PCM 数据中绘制波形,并通过单击它来寻找音频区域。

伊姆古尔

于 2012-03-15T09:03:11.400 回答
9

您可能对AudioJedit感兴趣。这是一个托管在 GitHub 上的开源项目。它有用于加载音频文件的小型服务器端 node.js 脚本,但所有与音频的交互都是在客户端 JavaScript 中实现的。我认为这与您正在寻找的相似。

于 2012-03-06T15:19:53.550 回答
2

对于(希望)简单地使用波形并将波形与您的应用程序集成,您可能需要检查我们在 IRCAM 所做的事情,特别是在这种特殊情况下的波形可见性。

它都是开源的,旨在实现模块化(并且正在进行中)

你可以在这里找到一个演示
和相应的githug 存储库

于 2014-06-23T12:12:17.047 回答
2

您的渲染代码效率极低,因为它将为每秒的音频渲染 44100 像素。您希望最好使用减少的数据集渲染最多视口宽度。

可以使用 audioDurationSeconds * samplerate / viewPortWidthPx 计算适合视口中波形所需的每像素采样范围。因此,对于 1000 像素的视口和 2 秒的音频文件,采样率为 44100,每个像素的样本数 = (2 * 44100) / 1000 = ~88。对于屏幕上的每个像素,您从该样本范围中获取最小值和最大值,然后使用此数据绘制波形。

这是一个执行此操作的示例算法,但允许您将每个像素的样本作为参数以及滚动位置以允许虚拟滚动和缩放。它包括一个可以调整性能的分辨率参数,这表明每个像素样本范围应该采用多少样本: 在 Javascript 中绘制可缩放的音频波形时间线

那里的绘制方法与您的相似,为了使其平滑,您需要使用lineTo而不是fillRect。这个差异实际上应该没有那么大,我想您可能忘记在画布上设置宽度和高度属性。在 css 中设置此项会导致绘图模糊,您需要设置属性。

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

   ctx.beginPath();
   ctx.moveTo(0, drawHeight);
   for(let i = 0; i < width; i++) {
      // transform data points to pixel height and move to centre
      let minPixel = drawData[i][0] * drawHeigth + drawHeight;
      ctx.lineTo(i, minPixel);
   }
   ctx.lineTo(width, drawHeight);
   ctx.moveTo(0, drawHeight);
   for(let i = 0; i < width; i++) {
      // transform data points to pixel height and move to centre
      let maxPixel = drawData[i][1] * drawHeigth + drawHeight;
      ctx.lineTo(i, maxPixel);
   }
   ctx.lineTo(width, drawHeight);
   ctx.closePath();
   ctx.fill(); // can do ctx.stroke() for an outline of the waveform
} 
于 2017-11-17T17:39:45.493 回答