How to avoid "HTMLMediaElement already connected previously to a different MediaElementSourceNode" in the AudioContext Interface?
Asked Answered
R

1

8

I've made an audio visualizer in React with the AudioContext interface and I want the user able to enable and disable it.
The visualizer works fine and I can disable it as well (I just remove the vis component)

However, when I want to enable it again, it tells me : "InvalidStateError: Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode."
I suppose that I can't have 2 ElementSources on a audio element at the same time. But I can't manage to work around this error.
I've tried to return audiocontext.close() in my useEffect hook so I can create a new MediaElementSource then (not sure if it works this way) but it doesn't change anything.
Maybe there is a property on the audio element that can tell me if there is already a MediaElementSource ? (I didn't find anything)

Or maybe the AudioContext interface is a bit too hard for me since I'm only a beginner in React and I've just copy paste an existing visualizer...

Thanks for the help!

Here is some code from my visualizer component :

useEffect(() => {
  var context = new AudioContext(); //Some visualiser stuff
  var src = context.createMediaElementSource(audio);// The error is here
  src.crossOrigin = "anonymous";
  var analyser = context.createAnalyser();
  src.connect(analyser);
  analyser.connect(context.destination);
  analyser.fftSize = 1024;

  // Some canvas stuff here
  //

  return () => {
    context.close() // doesn't work ?
  };
}, [somedeps]);

return  <canvas>...</canvas>
Recognizee answered 16/4, 2021 at 8:32 Comment(1)
For me it was due to React Strict mode -> #61254872Daybook
D
2

I had exactly the same problem .

according to error message you don't need dependency in useEffect (Because src only needs to be defined once otherwise you will get an error).

instead save src into state and use two useEffect to access src changes when audio changes :

import React, { useEffect, useState } from "react";

  const [source, setSource] = useState(null);

  useEffect(() => {
    if (window !== undefined) {
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      const ctx = new AudioContext();
      
      //declare source just once
      const src = ctx.createMediaElementSource(audio);
      setSource(src);
      
      //connect analayser to source
      const analayser = ctx.createAnalyser();
      src.connect(analayser);
      analayser.connect(ctx.destination);
    }
  }, []);
  
    useEffect(() => {
    console.log(source);
    //result : MediaElementAudioSourceNode {mediaElement: audio, context: AudioContext, numberOfInputs: 0, numberOfOutputs: 1, channelCount: 2, …}
    
  }, [//use dependency here]);
Dekow answered 5/2, 2022 at 21:20 Comment(2)
React.StrictMode makes the component render twice which creates the same error as the HTMLMediaElement gets connected in the first render. Am wondering, is the a way to disconnect the HTMLMediaElement from the previous MediaElementSourceNode so that it can get a fresh reconnection?Hedgerow
You can remove the window !== undefined check inside useEffect. If useEffect on [] triggers, window is guaranteed to be defined.Pendragon

© 2022 - 2024 — McMap. All rights reserved.