badcode
badcode

Reputation: 610

how to play multiple audio with js audio api

I'm reading audio files from the server. And I have an array for raw audio files.

const allAudio = [];

Then I play them sequentially.

var volumeSeq=1;

    function playAudio(){
     if(volumeSeq==allAudio.length){ volumeSeq=1;}
      var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
      source = audioCtx.createBufferSource();
      audioCtx.decodeAudioData(allAudio[volumeSeq], function(buffer) {
      source.buffer = buffer;
      console.log(source.buffer);
      source.connect(audioCtx.destination);
      source.loop = false;});

      source.start(0);
      source.onended = function () {
        volumeSeq++;
        playAudio();
}

I catch the problem. But I don't know why. Problem is, that after the decoding i.element of the array, the element in it is being deleted. I mean when I decode the allAudio[1] then allAudio[1] is empty. So I cant create a loop for these audio tracks.

What should i do?

Upvotes: 1

Views: 656

Answers (1)

m51
m51

Reputation: 2010

Why don't you decode all audio files at the begining, and then loop through them? Like this:

var allAudio = [];
var audioCtx = new AudioContext();
var volumeSeq = 0;
var decodedAudio = [];

function decodeAudio() {
    allAudio.forEach(function (file, index) {
        audioCtx.decodeAudioData(file, function(buffer) {
            decodedAudio[index] = buffer;
            if (decodedAudio.length >= allAudio.length) {
                play();
            }
        })
     })
}
function play() {
    if (volumeSeq >= allAudio.length){ volumeSeq=0;}
    var source = audioCtx.createBufferSource();
    source.buffer = decodedAudio[volumeSeq];
    source.connect(audioCtx.destination);
    source.start(0);
    source.onended = function () {
        volumeSeq++;
        play();
    }
}
decodeAudio();

Few more things:

  • You don't need to create AudioContext every time.
  • AudioBufferSourceNode.loop is false by default. You can skip source.loop = false;
  • Every new variable should start with const, let or var. You are missing it with source. BTW nowadays it is beter to use const and let, not var. I will show it in next "modern" example.
  • I don't know why you iterate allAudio starting from index 1. First index in array is 0.

If you don't care about old browsers like IE, you can make code cleaner by using Promises and async/await:

const audioCtx = new AudioContext();
async function start() {
   const decodedAudio = await Promise.all(allAudio.map((file) => audioCtx.decodeAudioData(file)));
   let volumeSeq = 0;
   function play() {
        if(volumeSeq >= allAudio.length) volumeSeq = 0;
        const source = audioCtx.createBufferSource();
        source.buffer = decodedAudio[volumeSeq];
        source.connect(audioCtx.destination);
        source.start(0);
        source.addEventListener('ended', () => {
           volumeSeq++;
           play();
        }, { once: true })
   }
   play();
}
start()

Upvotes: 2

Related Questions