实时处理网络音频API

时间:2013-03-28 09:26:27

标签: javascript html5-canvas html5-audio web-audio

我正在使用web audio api和requestAnimationFrame来显示麦克风的音频输入。我可以成功地可视化时域频率数据,但问题是因为网络音频api以秒为单位计算时间,所以我的界面每秒都会根据输入的变化而变化。

所以我的问题是,我如何可视化声音并使图形保持在屏幕上,因此我可以在一定的时间限制内看到我的所有频率数据(假设我说话,同时在画布上可视化仅5秒)。

我使用以下代码(来自示例here):

MicrophoneSample.prototype.visualize = function() {
  this.canvas.width = this.WIDTH;
  this.canvas.height = this.HEIGHT;
  var drawContext = this.canvas.getContext('2d');

  var times = new Uint8Array(this.analyser.frequencyBinCount);
  this.analyser.getByteTimeDomainData(times);
  for (var i = 0; i < times.length; i++) {
    var value = times[i];
    var percent = value / 256;
    var height = this.HEIGHT * percent;
    var offset = this.HEIGHT - height - 1;
    var barWidth = this.WIDTH/times.length;
    drawContext.fillStyle = 'purple';
    drawContext.fillRect(i * barWidth, offset, 1, 1);

  }
  requestAnimFrame(this.visualize.bind(this));

}

2 个答案:

答案 0 :(得分:2)

getByteTimeDomainData不会为您提供频率信息。这些是实时的时域波形值,也称为幅度值。如果要随着时间的推移将它们可视化,请将值附加到数组中并绘制它。如果您想要实际频率值,请使用getByteFrequencyData。

答案 1 :(得分:2)

OP,这是一些伪代码。仅供参考,这不是一个网络音频问题,而是一个动画问题。

在可视化器原型函数中存储一个变量/字段,用于跟踪您想要延迟重绘画布的秒数,保留一个单独的计数器,每当requestAnimFrame(...)被绘制时它将递增。计数器达到延迟量后,重新绘制画布。

修改 现在我想到了......解决方案应该非常简单。如果我错了,请纠正我,但是这个粗略的解决方案是假设您在动画循环中调用MicrophoneSample.visualize()...因此,其中的代码每秒执行一次。如果您发布MicrophoneSample对象代码,或者至少是您的动画循环,我可以提供更多帮助。

/* NOTE!
*
*/
// Find a way to put these into your PARENT MicrophoneSample object
var delay = 5;
// Note that I am setting delayCount initially to zero - during the loop
// the delayCount will actually get reset to 1 from thereafter (not 0)...
// this gives us a way to initially draw your visualization on the first frame.
var delayCount = 0;

// Pull var times out so it doesn't get calculated each time.
var times = new Uint8Array(MicrophoneSample.analyser.frequencyBinCount);

// Same goes for the canvas...
// I would set these values inside of the PARENT MicrophoneSample object
MicrophoneSample.canvas.width = this.WIDTH;
MicrophoneSample.canvas.height = this.HEIGHT;

// you only need to establish the drawing context once. Do it in the PARENT
// MicrophoneSample object
var drawContext = this.canvas.getContext('2d');

MicrophoneSample.prototype.visualize = function() {

      /*
      *    NOTE!
      */
      // Here's the juicy meat & potatoes:
      // only if the delayCount reaches the delay amount, should you UPDATE THE
      // TIME DOMAIN DATA ARRAY (times)
      // if your loop runs every second, then delayCount increments each second
      // and after 5 seconds will reach your designated delay amount and update your
      // times array.

      if(delayCount == 0 || delayCount == delay) {
          this.analyser.getByteTimeDomainData(times);

          // Now, it would be redundant (and totally noob-programmer of you) to 
          // redraw the same visualization onto the canvas 5 times in a row, so
          // only draw the visualization after the first pass through the loop and then
          // every 5th pass after that :]
          for (var i = 0; i < times.length; i++) {
              var value = times[i];
              var percent = value / 256;
              var height = this.HEIGHT * percent;
              var offset = this.HEIGHT - height - 1;
              var barWidth = this.WIDTH/times.length;
              drawContext.fillStyle = 'purple';
              drawContext.fillRect(i * barWidth, offset, 1, 1);
          }

          // Note: 1, not 0!
          delayCount = 1;
      } 
      else {
          delayCount++;
      } 


      requestAnimFrame(this.visualize.bind(this));
}

请记住,我实际上没有测试过这些。但它至少应该指向正确的方向。