3
votes

I am working with web audio api and requestAnimationFrame to visualize the audio input from microphone. I can successfully visualize the time-domain frequency data, but the problem is that since web audio api calculates the time in seconds, every second my interface changes depending on what the input is.

So my question is, how can I visualize the sound and make the graph stay on screen, thus I can see all my frequency data for a certain limit of time (let's say I speak and meanwhile visualize on canvas for only 5 seconds).

I am using the following code (took from examples 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
Rq : resizing the canvas / getting the context on each call is an overkill : do it once and store the ctx. Isn't there a need to connect your sound source to an analyser to get the frequencies ?GameAlchemist
Input is already connected to the analyzer and I also can get waveform values can be fetched or other sound properties. The main problem is how to make the visualization of them resident?user2039789

2 Answers

2
votes

getByteTimeDomainData does not give you frequency information. These are time domain waveform values in real time also known as amplitude values. If you want to visualize them over time append the values it into an array and draw that. If you want real frequency values use getByteFrequencyData.

2
votes

OP, here's some pseudo code. FYI, this really isn't a web audio question, more of an animation question.

Store a variable / field in your visualizer prototype function that keeps track of how many seconds you want to delay the redrawing of your canvas, keep a separate counter that will increment everytime requestAnimFrame(...) gets drawn. Once your counter reaches your delay amount, then redraw the canvas.

Edit Now that I think of it...the solution should be very simple. Correct me if I'm wrong, but this rough solution is assuming that you are calling MicrophoneSample.visualize() from within your animation loop...and therefore, the code therein executes every second. I could be of more help if you post your MicrophoneSample object code as well, or at least your animation loop.

/* 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));
}

And just keep in mind that I haven't actually tested any of this. But it should at least point you in the right direction.