Prometheus
Prometheus

Reputation: 1015

Web audio - Analyser frequency data not 0 during silence

I create sounds using oscillator nodes and want to draw a frequency visualization on a canvas. When the oscillator is playing, the visualization looks like this (standard oscillator settings, see code below). http://i58.tinypic.com/wtvwgz.png

After the oscillator stopped playing (complete silence!), this is what I get. The exact result changes from run to run, sometimes the values even keep slightly changing after the stop. http://i62.tinypic.com/2duji81.png

I don't understand why the frequency data is not zero for all bins, when the sound is not playing.

Tested on Firefox 30.0 and Iron 34.0.1850.0 (Chrome)

Here's my sample code:

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type="text/javascript">
            window.onload = function () {
                var ctx = document.getElementById("canvas").getContext("2d");
                var audioContext = new (window.AudioContext
                    || window.webkitAudioContext || window.mozAudioContext)();

                var analyser = audioContext.createAnalyser();
                analyser.fftSize = 512;
                analyser.connect(audioContext.destination);
                var frequencyBins = new Uint8Array(analyser.frequencyBinCount);

                var osc = audioContext.createOscillator();
                osc.connect(analyser);
                osc.start(audioContext.currentTime + 2);
                osc.stop(audioContext.currentTime + 4);

                var WIDTH = 512;
                var HEIGHT = 100;
                var value, h, w;

                function draw() {
                    ctx.clearRect(0, 0, WIDTH, HEIGHT);

                    for (var i = 0; i < frequencyBins.length; i++) {
                        value = frequencyBins[i];
                        h = HEIGHT * (value / 255);
                        w = WIDTH / frequencyBins.length;
                        ctx.fillRect(i * w, HEIGHT - 1, w, -h);
                    }
                };

                function animate() {
                    analyser.getByteFrequencyData(frequencyBins);
                    draw();
                    requestAnimationFrame(animate);
                }

                requestAnimationFrame(animate);
            };
        </script>
    </head>
    <body>
        <canvas id="canvas" width="512" height="100"></canvas>
    </body>
</html>

Upvotes: 5

Views: 2800

Answers (4)

Birkensox
Birkensox

Reputation: 3721

After experiencing the same issue I tried:

  • Various biquad filters
  • Various window functions

While I got the best results with a 2K-highpass/blackman combo, the freeze still persisted.

In the end I also had to adjust the min/max dB settings of the analyser. I had originally set min/max dB to -100/0.

  • min dB of -101 yielded slow melting during silence.
  • min dB of -150 yielded fast melting.

Intuitively it makes sense that increasing the response range of the analyser would resolve this issue. I assume it can be traced to quantization in the FFT algo.

Notes:

The highpass filter doubles overall processing overhead :(

While I also found that changing the smoothingTimeConstant to values approaching 1.0 resolved the freezing, it also induced lag in the responsiveness of the analyser (too smooth).

Upvotes: 0

Ural
Ural

Reputation: 385

I had the same problem. It was analyserNode.smoothingTimeConstant, it has very high value by default. Try to analyserNode.smoothingTimeConstant = 0; //or 0.2 It will fix it

Upvotes: 0

Prometheus
Prometheus

Reputation: 1015

I found a solution that works for me.

Instead of connecting the oscillator directly to the analyser, I pass the frequencies through a high-pass filter first. It seems that the value for the cutoff frequency can be set arbitrarily low, as long as it is not 0. Even with a cutoff of 0.00000001 the visualisation will be blank during silence.

http://jsfiddle.net/a2ZL9/3/

var analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
analyser.connect(audioContext.destination);
var frequencyBins = new Uint8Array(analyser.frequencyBinCount);

var filter = audioContext.createBiquadFilter();
filter.type = "highpass";
filter.frequency.value = 0.0001;
filter.connect(analyser);

var osc = audioContext.createOscillator();
osc.connect(filter);
osc.start(audioContext.currentTime + 2);
osc.stop(audioContext.currentTime + 4);

Upvotes: 3

cwilso
cwilso

Reputation: 13908

It's not a noise floor. It happens with audio buffers too. It's a bug.

Upvotes: 2

Related Questions