Divarrek
Divarrek

Reputation: 322

AnalyserNode.getFloatFrequencyData always returns -Infinity

Alright, so I'm trying to determine the intensity (in dB) on samples of an audio file which is recorded by the user's browser.

I have been able to record it and play it through an HTML element. But when I try to use this element as a source and connect it to an AnalyserNode, AnalyserNode.getFloatFrequencyData always returns an array full of -Infinity, getByteFrequencyData always returns zeroes, getByteTimeDomainData is full of 128.

Here's my code:

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var source;

var analyser = audioCtx.createAnalyser();

var bufferLength = analyser.frequencyBinCount;
var data = new Float32Array(bufferLength);

mediaRecorder.onstop = function(e) {
  var blob = new Blob(chunks, { 'type' : 'audio/ogg; codecs=opus' });

  chunks = [];
  var audioURL = window.URL.createObjectURL(blob);
  // audio is an HTML audio element
  audio.src = audioURL;

  audio.addEventListener("canplaythrough", function() {
      source = audioCtx.createMediaElementSource(audio);

      source.connect(analyser);
      analyser.connect(audioCtx.destination);

      analyser.getFloatFrequencyData(data);
      console.log(data);
  });
}

Any idea why the AnalyserNode behaves like the source is empty/mute? I also tried to put the stream as source while recording, with the same result.

Upvotes: 2

Views: 1422

Answers (3)

Artur
Artur

Reputation: 342

I ran into the same issue, thanks to some of your code snippets, I made it work on my end (the code bellow is typescript and will not work in the browser at the moment of writing):

audioCtx.decodeAudioData(this.result as ArrayBuffer).then(function (buffer: AudioBuffer) { 
      soundSource = audioCtx.createBufferSource();
      soundSource.buffer = buffer;
      //soundSource.connect(audioCtx.destination); //I do not need to play the sound
      soundSource.connect(analyser);
      soundSource.start(0);

      setInterval(() => {
         calc(); //In here, I will get the analyzed data with analyser.getFloatFrequencyData 
      }, 300); //This can be changed to 0.
      // The interval helps with making sure the buffer has the data

Some explanation (I'm still a beginner when it comes to the Web Audio API, so my explanation might be wrong or incomplete): An analyzer needs to be able to analyze a specific part of your sound file. In this case I create a AudioBufferSoundNode that contains the buffer that I got from decoding the audio data. I feed the buffer to the source, which eventually will be able to be copied inside the Analyzer. However, without the interval callback, the buffer never seems to be ready and the analysed data contains -Inifinity (which I assume is the absence of any sound, as it has nothing to read) at every index of the array. Which is why the interval is there. It analyses the data every 300ms.

Hope this helps someone!

Upvotes: 2

Richard
Richard

Reputation: 2625

You need to fetch the audio file and decode the audio buffer. The url to the audio source must also be on the same domain or have have the correct CORS headers as well (as mentioned by Anthony).

Note: Replace <FILE-URI> with the path to your file in the example below.

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var source;
var analyser = audioCtx.createAnalyser();
var button = document.querySelector('button');
var freqs;
var times;


button.addEventListener('click', (e) => {
  fetch("<FILE-URI>", {
    headers: new Headers({
      "Content-Type" : "audio/mpeg"
    })
  }).then(function(response){
    return response.arrayBuffer()
  }).then((ab) => {
    audioCtx.decodeAudioData(ab, (buffer) => {
      source = audioCtx.createBufferSource();
      source.connect(audioCtx.destination)
      source.connect(analyser);
      source.buffer = buffer;
      source.start(0);
      viewBufferData();
    });
  });
});

// Watch the changes in the audio buffer
function viewBufferData() {
  setInterval(function(){
    freqs = new Uint8Array(analyser.frequencyBinCount);
    times = new Uint8Array(analyser.frequencyBinCount);
    analyser.smoothingTimeConstant = 0.8;
    analyser.fftSize = 2048;
    analyser.getByteFrequencyData(freqs);
    analyser.getByteTimeDomainData(times);
    console.log(freqs)
    console.log(times)
  }, 1000)
}

Upvotes: 1

cwilso
cwilso

Reputation: 13908

If the source file from a different domain? That would fail in createMediaElementSource.

Upvotes: 0

Related Questions