简体   繁体   中英

How to make audio files play in the correct order using Web Audio API?

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:

Why is AJAX in for loop executing in wrong order

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;
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM