From 2a0852611fbeec1b0001d0cae522fbe2a7a65e86 Mon Sep 17 00:00:00 2001 From: SrGooglo <srgooglo@ragestudio.net> Date: Wed, 12 Apr 2023 18:36:21 +0000 Subject: [PATCH] handle audio preloads --- packages/app/src/cores/player/index.jsx | 83 ++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/packages/app/src/cores/player/index.jsx b/packages/app/src/cores/player/index.jsx index c297797b..d6ac68e3 100755 --- a/packages/app/src/cores/player/index.jsx +++ b/packages/app/src/cores/player/index.jsx @@ -45,6 +45,9 @@ export default class Player extends Core { audioContext = new AudioContext() + bufferLoadQueue = [] + bufferLoadQueueLoading = false + audioQueueHistory = [] audioQueue = [] audioProcessors = [] @@ -315,6 +318,63 @@ export default class Player extends Core { this.state.livestream = false } + enqueueLoadBuffer(audioElement) { + if (!audioElement) { + console.error("Audio element is required") + return false + } + + if (audioElement instanceof Audio) { + this.bufferLoadQueue.push(audioElement) + } + + if (!this.bufferLoadQueueLoading) { + this.bufferLoadQueueLoading = true + + this.loadNextQueueBuffer() + } + } + + async loadNextQueueBuffer() { + if (!this.bufferLoadQueue.length) { + this.bufferLoadQueueLoading = false + + return false + } + + const audioElement = this.bufferLoadQueue.shift() + + if (audioElement.signal.aborted) { + console.warn("Aborted audio element") + + this.bufferLoadQueueLoading = false + + this.loadNextQueueBuffer() + + return false + } + + this.bufferLoadQueueLoading = true + + const preloadPromise = () => new Promise((resolve, reject) => { + audioElement.addEventListener("canplaythrough", () => { + resolve() + }, { once: true }) + + console.log("Preloading audio buffer", audioElement.src) + + audioElement.load() + }) + + await preloadPromise() + + this.bufferLoadQueueLoading = false + + this.loadNextQueueBuffer() + + return true + } + async createInstance(manifest) { if (!manifest) { console.error("Manifest is required") @@ -340,6 +400,7 @@ export default class Player extends Core { } let instanceObj = { + abortController: new AbortController(), audioElement: new Audio(audioSource), audioSource: audioSource, manifest: manifest, @@ -349,9 +410,10 @@ export default class Player extends Core { crossfading: false } + instanceObj.audioElement.signal = instanceObj.abortController.signal instanceObj.audioElement.loop = this.state.playbackMode === "repeat" instanceObj.audioElement.crossOrigin = "anonymous" - instanceObj.audioElement.preload = "metadata" + instanceObj.audioElement.preload = "none" const createCrossfadeInterval = () => { console.warn("Crossfader is not supported yet") @@ -470,6 +532,8 @@ export default class Player extends Core { } }) + this.enqueueLoadBuffer(instanceObj.audioElement) + //await this.instanciateRealtimeAnalyzerNode() instanceObj.track = this.audioContext.createMediaElementSource(instanceObj.audioElement) @@ -563,6 +627,9 @@ export default class Player extends Core { throw new Error("Playlist is required") } + // !IMPORTANT: abort preloads before destroying current instance + await this.abortPreloads() + this.destroyCurrentInstance() // clear current queue @@ -587,6 +654,9 @@ export default class Player extends Core { } async start(manifest) { + // !IMPORTANT: abort preloads before destroying current instance + await this.abortPreloads() + this.destroyCurrentInstance() const instance = await this.createInstance(manifest) @@ -598,6 +668,7 @@ export default class Player extends Core { this.state.loading = true this.play(this.audioQueue[0]) + } next(params = {}) { @@ -685,9 +756,19 @@ export default class Player extends Core { } } + async abortPreloads() { + for await (const instance of this.audioQueue) { + if (instance.abortController?.abort) { + instance.abortController.abort() + } + } + } + stop() { this.destroyCurrentInstance() + this.abortPreloads() + this.state.playbackStatus = "stopped" this.state.currentAudioManifest = null