hvianna/audioMotion-analyzer

Gain keeps getting higher every time a AudioMotionAnalyzer is initialized

lucienimmink opened this issue ยท 6 comments

Hi,

First of all: great software! I'm implementing this with Lit with a web component that is only added to the DOM if needed.
I have some hacky work-around to bypass having to disconnect on destroy of the component

let audioCtx;
let source;
if (!window._audioCtx) {
          audioCtx = new AudioContext();
          source = audioCtx.createMediaElementSource(window._player);

          // store in memory for reuse
          window._audioCtx = audioCtx;
          window._source = source;
        } else {
          audioCtx = window._audioCtx;
          source = window._source;
        }

Every time I call new AudioMotionAnalyzer() the gain of this chain is increased and it starts clipping. Setting the volume has no effect.

const visualizer = new AudioMotionAnalyzer(canvas, {
          audioCtx,
          source,
});

I guess it has to do with the constructor itself

const analyzer = this._analyzer = [ audioCtx.createAnalyser(), audioCtx.createAnalyser() ];
const splitter = this._splitter = audioCtx.createChannelSplitter(2);
const merger   = this._merger   = audioCtx.createChannelMerger(2);
this._input    = audioCtx.createGain();
this._output   = audioCtx.createGain();

Would be nice to either have a destroy() method that cleans up all webaudio related nodes so that can be called when the component is disconnected or a way to bypass the creation of these webaudio nodes when they are already present, these should be re-used if possible.

The analyzer is connected to the speakers by default, so multiple calls to the constructor will multiply the output as well. Also, depending on the player you're using, it may already be doing the connection to the AudioContext destination node (speakers).

You can use connectSpeakers: false in the constructor options to skip connecting the analyzer to the speakers, like so:

const visualizer = new AudioMotionAnalyzer(canvas, {
          audioCtx,
          source,
          connectSpeakers: false
});

If you get no sound after that, connect your source node to the destination, like so:

source.connect( audioCtx.destination );

Do you really need to call the constructor multiple times, though? Like, you need multiple visualizations running at the same time? Please note these instances will keep consuming resources, unless you use toggleAnalyzer() to stop the audio processing.

Anyway, a destroy() method is a good suggestion! I'll look into adding this in a future release! ๐Ÿ‘

I'll look into your thought, for now just want to come back to the remark if I need more visualisations at the same time: no :)
I have a music player with an optional visualiser. If shown it's added to the Dom as a web component and if removed, the component is removed from the dom, so I can call destroy() when the web component is removed, all the while the music keeps on playing.

Just FYI:
Using the connectSpeaker: false option is (for me) fixing the issue, although I believe it would be a better/safer option to have the proper destroy() method.
Thank you for the tip!

Cool, thanks for the follow-up!

destroy() is now available in v4.2.0 ๐ŸŽ‰