Search code examples
javascriptfirefoxhtml5-audio

Audio not playing properly on Firefox


The following code plays the audio correctly on Chrome 88, but on Firefox 85 the audio cuts off and sometimes doesn't even play.

window.onload = function() {
    Audio.prototype.stop = function() {
        audio.pause();
        audio.currentTime = 0;
    }

    let audio = new Audio("./audio.mp3");
    let count = 0;

    function replay() {
        audio.stop();
        audio.play();
    }

    let button = document.getElementsByTagName("button")[0];
    button.addEventListener("click", function() {
        let interval = setInterval(function() {
            if (count < 10) {
                replay();
                count += 1;
            } else {
                clearInterval(interval);
                count = 0;
            }
        }, 100);
    });
}

How can I fix this so that audio is played correctly in Firefox? All the files needed to reproduce the problem can be found here.


Solution

  • It's because the HTMLMediaElement API is asynchronous by nature and that you can't expect it to be able to sustain navigating every 100ms like that.
    Even the setting of currentTime is actually async, so with your very short interval you may be requiring the audio to stop even before it had time to start again.

    For what you want to do, use the Web Audio API and its AudioBuffer interface, along with AudioBufferSourceNodes which is a really lite representation of a player, that you can control with extreme precision with very low latency:

    (async () => {
      const context = new (window.AudioContext || window.webkitAudioContext)();
      const buffer = await fetch( "https://cdn.jsdelivr.net/gh/joaobzrr/firefox-audio-problem/audio.mp3" )
        .then( (resp) => resp.ok && resp.arrayBuffer() );
      const audiobuffer = await context.decodeAudioData( buffer );
    
      document.getElementById( "btn" ).onclick = async (evt) => {
        for ( let i = 0; i < 10; i++ ) {
          const buffersource = context.createBufferSource();
          buffersource.buffer = audiobuffer;
          buffersource.connect( context.destination );
          const starttime = context.currentTime + (i * 0.1);
          buffersource.start( starttime, 0, 0.1 );
        }
      };
    })().catch( console.error );
    <script src="https://cdn.jsdelivr.net/gh/mohayonao/promise-decode-audio-data@eb4b1322113b08614634559bc12e6a8163b9cf0c/build/promise-decode-audio-data.min.js"></script>
    <button id="btn">click to start</button>