【问题标题】:How to scale and draw and Audio Buffer to a Canvas element如何缩放和绘制音频缓冲区到 Canvas 元素
【发布时间】:2013-07-01 00:45:59
【问题描述】:

我试图将音频缓冲区的波形数据可视化为 html5 画布。

我使用buffer.getChannelData(0) 来获取每个样本的值。 然后我使用 for 循环将每个样本值绘制到缓冲区。

它现在可以工作了,但问题是它使用每个像素作为样本,这意味着它需要 44100 像素大小的画布来绘制一秒钟的音频。

这是我用来绘制的代码:

for (var i = 0; i < data.length; i++) {

ctx.fillStyle = 'black';
ctx.fillRect(i, height / 2, 0.5, (data[i] * height));
        }

你可以在这里看到整个代码 http://jsfiddle.net/ehsanziya/KKNFL/1/

如何根据每次加载缓冲区时加载的缓冲区大小对其进行缩放以使其适合canvas.width

谢谢

【问题讨论】:

  • 来自 mdn 的以下示例代码似乎可以满足您的需求:developer.mozilla.org/en-US/docs/Visualizing_Audio_Spectrum
  • @markE 这是关于频谱分析和可视化的。我的问题是关于波形数据可视化。我找到了一个代码github.com/cwilso/Audio-Buffer-Draw,但不明白他是如何进行缩放的。
  • 我不是发烧友,很抱歉,你的区别就在我的脑海中浮现 :( 但是..从他们的代码看来,他们将大量传入数据分类到垃圾箱中并只显示聚合的bin 信息,而不是每个数据点。这就是他们处理扩展问题的方式。

标签: javascript canvas scaling web-audio-api


【解决方案1】:

假设您的画布宽度为 500,音频长度为 60 秒,采样率为 44100 kHz...

您需要做的是弄清楚有多少样本将代表画布上的单个像素。要做到这一点,你基本上说var binSize = ( audioBuffer.duration * 44100 ) / 500;

现在您遍历所有样本并将它们分组为binSize 长度的部分。对于每个 bin,您可以找到最大值并将其推送到数组中。完成后,您将拥有一个包含 500 个值的数组,可用于绘制到画布中。

【讨论】:

    【解决方案2】:

    我已经修改了你的 JSFiddle 并在这里发布了一个分支:

    http://jsfiddle.net/jjDpm/4/

    这里是关键行:

    ctx.lineTo(i, height/2 + data[step*i] * amp);
    

    X 轴上的缩放问题通过提出一个“步长”因子然后使用它以适当的速率在数据中移动来解决。这个想法是,您将无法绘制每个数据点的图表,但您实际上将绘制一组均匀(步长)间隔的数据样本。

    【讨论】:

      【解决方案3】:

      我为此编写了一个示例库:https://github.com/cwilso/Audio-Buffer-Draw

      【讨论】:

      • 我实际上最终使用了您的库。但鉴于您没有绘制所有样本(据我所知),如果您想在音频编辑场景中使用它,并且您希望拥有样本准确的界面,这可能是一个问题。我尝试在这里link 以像素的分数绘制所有样本,但我还没有弄清楚如何放大。但是选择工具现在可以工作了。
      • 关于如何制作音频编辑界面的任何其他建议?这里是链接顺便说一句:jsfiddle.net/ehsanziya/yxCAU/1
      【解决方案4】:

      解决方案作者:enhzflep 是正确的方法,但是对于具有大型音频缓冲区数组的音频文件,您需要在迭代音频数组的缓冲区长度时对处理进行分块,以防止浏览器崩溃。与其使用 setTimeout,不如将变量设置为可以在块处理完成后检查的值。我的做法是这样的:

       function processChunk(ctx, cwidth, cheight){
      
          var chsize = 6000;
          var index=0;
          var chComplete;
      
          function doChunk(){
      
              var chunk = chsize;
      
              while(chunk-- && index<data.length){
      
                  var x = (index*cwidth)/data.length;
                  var y = ((data[index]*cheight/2)+cheight/2)
                  ctx.lineTo(x,y);
                  index++;
                  chComplete=1;
      
              }
      
              chComplete=0;
      
              if(index < data.length){
      
                  function checkChunk(){
                      if(chComplete==0){
                          doChunk()
                          console.log('chunking');
                      }else{
      
                          setTimeout(checkChunk(), 500);  
                      }
                  }
      
                  checkChunk();
              }
      
      
          }
      
          doChunk();
      
      }
      

      【讨论】:

        【解决方案5】:

        我编写了一个简单的 ADSR 合成器,允许用户输入乐器的参数,然后进行合成,使其数据可用于可视化或回放。我也用了 44100 赫兹。

        获得好看图像的关键是要意识到您不想为每个水平像素只绘制一次,而是为每个样本绘制一次。假设一个 0.2 秒的样本和一个 256 像素宽的画布,您需要将 8,820 个样本放入 256 个像素中 - 每像素约 34 个样本。这似乎有点矫枉过正,但这是获得不会丢失数据的可视化的唯一方法。这也是获得类似于声音编辑程序中的图像的方法,例如 Audacity、Milkytracker 等。

        这是一个踩镲鼓,Fmax > 10,000 hz

        编辑: 添加图像以显示调用 closePath 不会添加从终点回到起点的线。

        这是我使用的可视化代码:

        function drawFloatArray(samples, canvas)
        {
            var i, n = samples.length;
            var dur = (n / 44100 * 1000)>>0;
            canvas.title = 'Duration: ' +  dur / 1000.0 + 's';
        
            var width=canvas.width,height=canvas.height;
            var ctx = canvas.getContext('2d');
            ctx.strokeStyle = 'yellow';
            ctx.fillStyle = '#303030';
            ctx.fillRect(0,0,width,height);
            ctx.beginPath();
            ctx.moveTo(0,height/2);
            for (i=0; i<n; i++)
            {
                x = ( (i*width) / n);
                y = ((samples[i]*height/2)+height/2);
                ctx.lineTo(x, y);
            }
            ctx.stroke();
            ctx.closePath();
            canvas.mBuffer = samples;
            canvas.onclick = function() { playSound(this.mBuffer, 44100, 50); };
        }
        

        【讨论】:

        • 我不确定你是否应该使用ctx.closepath() - 该命令从终点画一条线到初始起点,对于关闭循环和类似的东西很有用。在大多数情况下,您可能不会看到那条额外的线,因为第一个和最后一个点通常为零,但在某些情况下,您会看到一条奇怪的线将波形的开头连接到结尾
        • @GershomMaes - 我是,不,它根本不一定像这样。由于首先调用了 stroke ,因此您建议的这条线不会修改路径。调用 closePath 失败意味着画布有一个开放的路径,其中包含大量的线段。您可以看到我添加到原始帖子中的额外图像来演示这一点。 :) - 您可以在此处查看代码的实时演示:jsfiddle.net/enhzflep/rsNZB - 如果您将第 222 行的 800 更改为 700,您将获得我展示的图像。遗憾的是,弃用 webkitAudioContext 意味着代码不再播放音频。
        猜你喜欢
        • 1970-01-01
        • 2011-10-31
        • 1970-01-01
        • 2018-10-07
        • 1970-01-01
        • 2017-12-15
        • 1970-01-01
        • 2014-11-14
        • 1970-01-01
        相关资源
        最近更新 更多