I have an array of imported audio files, and I'm trying to play them sequentially with the Web Audio API. I have some code which almost works, but it plays the sounds in the wrong order, seemingly randomly.
Can someone tell me what I'm doing wrong, or if there's a better way to achieve my goal?
import soundA from './assets/soundA.mp3'
import soundB from './assets/soundB.mp3'
import soundC from './assets/soundC.mp3'
function playSoundsSequentially() {
let mySounds = [soundA, soundB, soundC]
const ctx = new window.AudioContext();
let time = 0;
mySounds.forEach(sound => fetch(sound).then(response => response.arrayBuffer())
.then(buffer => ctx.decodeAudioData(buffer))
.then(buffer => {
let track = ctx.createBufferSource();
track.buffer = buffer;
track.connect(ctx.destination);
track.start(ctx.currentTime + time);
time += track.buffer.duration;
}));
}
Update: My solution based on JMP's insight on using promises. I also ditched the forEach for a simple for loop because I am more comfortable with that.
import soundA from './assets/soundA.mp3'
import soundB from './assets/soundB.mp3'
import soundC from './assets/soundC.mp3'
function playSoundsSequentially() {
let mySounds = [soundA, soundB, soundC]
let promises = []
for(let i = 0; i < mySounds.length; ++i) {
promises.push(fetch(mySounds[i]).then(response => response.arrayBuffer())
.then(buffer => ctx.decodeAudioData(buffer)));
}
Promise.all(promises).then(buffer => {
for(let i = 0; i < buffer.length; ++i) {
let track = ctx.createBufferSource();
track.buffer = buffer[i];
track.connect(ctx.destination);
track.start(ctx.currentTime + time);
time += track.buffer.duration;
}
});
}
Your question is very similar to this question:
Your code assembles the tracks in the order they are returned, not in the order they were sent,
Promise.all()
deals with an array of promises (MDN), and is also a Promise
itself, which is only fulfilled when all of the array elements are, returning an array in the original order.
Try
let mySounds = [soundA, soundB, soundC]
const ctx = new window.AudioContext();
let time = 0;
mySounds.forEach(sound => fetch(sound).then(response => response.arrayBuffer())
.then(buffer => ctx.decodeAudioData(buffer)));
Promise.all(mySounds).then(buffer => {
let track = ctx.createBufferSource();
track.buffer = buffer;
track.connect(ctx.destination);
track.start(ctx.currentTime + time);
time += track.buffer.duration;
});