class AudioManager { constructor(audioCtx) { this.list = []; this.audioCtx = audioCtx || new (window.AudioContext || window.webkitAudioContext)(); this.pool = {}; } async load(url) { let data = this.pool[url]; if(!data) { /* global fetch */ const buffer = await fetch(url).then(res => res.arrayBuffer()); data = await this.audioCtx.decodeAudioData(buffer); this.pool[url] = data; } const source = this.audioCtx.createBufferSource(); source.buffer = data; return source; } /* list[]: { url: when: offset: duration: next[]: index } */ async play(list) { this.stop(); this.list = list; for(let index = 0; index < list.length; index += 1) { const item = list[index]; // preload await this.load(item.url); item.start = async () => { const current = item; const source = await this.load(current.url); source.connect(this.audioCtx.destination); current.source = source; if(!current.stoped) { current.source.addEventListener('ended', (e) => { current.next.forEach((i) => { list[i].start(); }); current.source.disconnect(this.audioCtx.destination); }); source.start(current.when, current.offset, current.duration); } }; } list[0].start(); } async stop() { this.list.forEach((item) => { item.stoped = true; }); this.list.forEach((item) => { if(!item.ended && item.source) { item.ended = true; item.source.stop(); } }); } }