Search code examples
javascriptweb-audio-api

Failed to execute 'createMediaElementSource' on 'AudioContext'


I am getting Uncaught DOMException: Failed to execute 'createMediaElementSource' on 'AudioContext': HTMLMediaElement already connected previously to a different MediaElementSourceNode when I select track2 after track1 or track1 after track2.

This is what my code is and I have no idea how can i get rid of it. I want to be able to select elements without this error.

const audioDB = [{
        'index': 0,
        'src': 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/128337/ongoing-thing-crop.ogg'
    },
    {
        'index': 1,
        'src': 'https://s.cdpn.io/1202/Star_Wars_original_opening_crawl_1977.mp3'
    }
];

const container = document.querySelector('#container');
const audio = document.querySelector('audio');

container.addEventListener('change', (event) => {
    event.preventDefault();
    const dropVal = document.querySelector('#valueSelection').value;
    const adjVal = dropVal - 1;
    const url = audioDB[adjVal].src;
    const urlIndex = audioDB[adjVal].index

    audio.src = url;    
    audio.play();
     
    let audioContext;

    if (!audioContext) {
        audioContext = new(window.AudioContext || window.webkitAudioContext)();
    };

    const analyser = audioContext.createAnalyser();
    const audioSrc = audioContext.createMediaElementSource(audio);
    audioSrc.connect(analyser);
    analyser.connect(audioContext.destination);  


})
<audio id="audio" crossorigin="anonymous"></audio>
 <div id="container">
   <label for="valueSelection">Choose a value <select id="valueSelection">
       <option value="0">Please select a Value</option>
       <option value="1">track1</option>
       <option value="2">track2</option>
     </select>
   </label>
 </div>


Solution

  • I think you only need to create one AudioContext with a single AnalyserNode and a single MediaElementAudioSourceNode to achieve the desired result.

    // Define the AudioContext variable outside of the event listener.
    let audioContext;
    
    container.addEventListener('change', (event) => {
        event.preventDefault();
    
        const dropVal = document.querySelector('#valueSelection').value;
        const adjVal = dropVal - 1;
        const url = audioDB[adjVal].src;
    
        audio.src = url;    
        audio.play();
    
        if (!audioContext) {
            audioContext = new AudioContext();
    
            // Only create the AnalyserNode and MediaElementAudioSourceNode once.
            const analyser = audioContext.createAnalyser();
            const audioSrc = audioContext.createMediaElementSource(audio);
        
            audioSrc
                .connect(analyser)
                .connect(audioContext.destination);  
        }
    });