Resolve race condition

This commit is contained in:
wvffle 2022-10-28 14:31:50 +00:00 committed by Georg Krause
parent 0118fe0c63
commit 8c62d516b0
No known key found for this signature in database
GPG Key ID: 2970D504B2183D22
3 changed files with 33 additions and 27 deletions

View File

@ -1,5 +1,5 @@
import { createGlobalState, tryOnMounted, useIntervalFn, useRafFn, useStorage, useTimeoutFn, whenever } from '@vueuse/core' import { createGlobalState, tryOnMounted, useIntervalFn, useRafFn, useStorage, useTimeoutFn, whenever } from '@vueuse/core'
import { computed, nextTick, ref, watch, watchEffect, type Ref } from 'vue' import { computed, ref, watch, watchEffect, type Ref } from 'vue'
import { useTracks } from '~/composables/audio/tracks' import { useTracks } from '~/composables/audio/tracks'
import { setGain } from './audio-api' import { setGain } from './audio-api'
@ -28,7 +28,7 @@ export const isPlaying = ref(false)
// Use Player // Use Player
export const usePlayer = createGlobalState(() => { export const usePlayer = createGlobalState(() => {
const { currentSound, createTrack } = useTracks() const { currentSound } = useTracks()
const { playNext } = useQueue() const { playNext } = useQueue()
watchEffect(() => { watchEffect(() => {
@ -43,12 +43,6 @@ export const usePlayer = createGlobalState(() => {
sound.pause() sound.pause()
}) })
const stop = async () => {
isPlaying.value = false
seekTo(0)
return nextTick()
}
// Create first track when we initalize the page // Create first track when we initalize the page
// NOTE: We want to have it called only once, hence we're using createGlobalState // NOTE: We want to have it called only once, hence we're using createGlobalState
const initializeFirstTrack = createGlobalState(() => tryOnMounted(() => { const initializeFirstTrack = createGlobalState(() => tryOnMounted(() => {
@ -59,8 +53,6 @@ export const usePlayer = createGlobalState(() => {
trackRadioPopulating() trackRadioPopulating()
trackListenSubmissions() trackListenSubmissions()
createTrack(currentIndex.value)
})) }))
// Volume // Volume
@ -208,7 +200,6 @@ export const usePlayer = createGlobalState(() => {
return { return {
initializeFirstTrack, initializeFirstTrack,
isPlaying, isPlaying,
stop,
volume, volume,
mute, mute,
looping, looping,

View File

@ -6,7 +6,7 @@ import { computed, ref, shallowReactive, watchEffect } from 'vue'
import { getMany, setMany } from 'idb-keyval' import { getMany, setMany } from 'idb-keyval'
import { useClamp } from '@vueuse/math' import { useClamp } from '@vueuse/math'
import { looping, LoopingMode, isPlaying, usePlayer } from '~/composables/audio/player' import { looping, LoopingMode, isPlaying } from '~/composables/audio/player'
import { useStore } from '~/store' import { useStore } from '~/store'
import { useTracks } from '~/composables/audio/tracks' import { useTracks } from '~/composables/audio/tracks'
@ -166,13 +166,17 @@ export const useQueue = createGlobalState(() => {
} }
// Play track // Play track
const playTrack = async (trackIndex: number, force = false) => { const playTrack = async (trackIndex: number, forceRestartIfCurrent = false) => {
if (isPlaying.value) currentSound.value?.pause() if (isPlaying.value) currentSound.value?.pause()
if (currentIndex.value !== trackIndex) currentSound.value?.seekTo(0)
if (force && currentIndex.value === trackIndex) { const shouldRestart = forceRestartIfCurrent && currentIndex.value === trackIndex
const nextTrackIsTheSame = queue.value[trackIndex].id === currentTrack.value.id
if (shouldRestart || nextTrackIsTheSame) {
currentSound.value?.seekTo(0) currentSound.value?.seekTo(0)
if (isPlaying.value) currentSound.value?.play() if (isPlaying.value) currentSound.value?.play()
return if (shouldRestart) return
} }
currentIndex.value = trackIndex currentIndex.value = trackIndex
@ -270,8 +274,9 @@ export const useQueue = createGlobalState(() => {
// Clear // Clear
const clearRadio = ref(false) const clearRadio = ref(false)
const clear = async () => { const clear = async () => {
const { stop } = usePlayer() currentSound.value?.pause()
await stop() currentSound.value?.seekTo(0)
currentSound.value?.dispose()
clearRadio.value = true clearRadio.value = true
tracks.value.length = 0 tracks.value.length = 0
} }

View File

@ -1,7 +1,7 @@
import type { QueueTrack, QueueTrackSource } from '~/composables/audio/queue' import type { QueueTrack, QueueTrackSource } from '~/composables/audio/queue'
import type { Sound } from '~/api/player' import type { Sound } from '~/api/player'
import { createGlobalState, syncRef, whenever } from '@vueuse/core' import { createGlobalState, syncRef, useTimeoutFn, whenever } from '@vueuse/core'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { connectAudioSource } from '~/composables/audio/audio-api' import { connectAudioSource } from '~/composables/audio/audio-api'
@ -55,30 +55,40 @@ export const useTracks = createGlobalState(() => {
const createSoundPromise = async () => { const createSoundPromise = async () => {
const sources = getTrackSources(track) const sources = getTrackSources(track)
const { playNext, currentIndex } = useQueue() const { playTrack, currentIndex } = useQueue()
const SoundImplementation = soundImplementation.value const SoundImplementation = soundImplementation.value
const sound = new SoundImplementation(sources) const sound = new SoundImplementation(sources)
sound.onSoundEnd(() => { sound.onSoundEnd(() => {
console.log('TRACK ENDED, PLAYING NEXT') console.log('TRACK ENDED, PLAYING NEXT')
createTrack(currentIndex.value + 1)
// NOTE: We push it to the end of the job queue // NOTE: We push it to the end of the job queue
setTimeout(playNext, 0) setTimeout(() => {
playTrack(currentIndex.value + 1)
}, 0)
}) })
soundCache.set(track.id, sound) soundCache.set(track.id, sound)
soundPromises.delete(track.id) soundPromises.delete(track.id)
return sound return sound
} }
console.log('NO TRACK IN CACHE, CREATING') console.log('NO TRACK IN CACHE, CREATING', track)
const soundPromise = createSoundPromise() const soundPromise = createSoundPromise()
soundPromises.set(track.id, soundPromise) soundPromises.set(track.id, soundPromise)
return soundPromise return soundPromise
} }
// Preload next track
const { start: startPreloadTimeout, stop: stopPreloadTimeout } = useTimeoutFn(async (index) => {
const { queue } = useQueue()
const sound = await createSound(queue.value[index as number])
await sound.preload()
}, 100, { immediate: false })
// Create track from queue // Create track from queue
const createTrack = async (index: number) => { const createTrack = async (index: number) => {
stopPreloadTimeout()
const { queue, currentIndex } = useQueue() const { queue, currentIndex } = useQueue()
if (queue.value.length <= index || index === -1) return if (queue.value.length <= index || index === -1) return
console.log('LOADING TRACK', index) console.log('LOADING TRACK', index)
@ -97,10 +107,8 @@ export const useTracks = createGlobalState(() => {
// NOTE: Preload next track // NOTE: Preload next track
if (index === currentIndex.value && index + 1 < queue.value.length) { if (index === currentIndex.value && index + 1 < queue.value.length) {
setTimeout(async () => { // @ts-expect-error vueuse is wrongly typed?
const sound = await createSound(queue.value[index + 1]) startPreloadTimeout(index + 1)
await sound.preload()
}, 100)
} }
} }
@ -110,7 +118,9 @@ export const useTracks = createGlobalState(() => {
const initialize = createGlobalState(() => { const initialize = createGlobalState(() => {
const { currentIndex, currentTrack: track } = useQueue() const { currentIndex, currentTrack: track } = useQueue()
whenever(track, () => createTrack(currentIndex.value)) whenever(track, () => {
createTrack(currentIndex.value)
}, { immediate: true })
syncRef(track, currentTrack, { syncRef(track, currentTrack, {
direction: 'ltr' direction: 'ltr'
}) })