fix music player

This commit is contained in:
Youwen Wu 2024-02-22 17:16:25 -08:00
parent f5f7371595
commit 1ed960d8e8
8 changed files with 123 additions and 139 deletions

View file

@ -7,8 +7,7 @@
let { title, artist, coverImg } = song
const handlePlay = () => {
musicStore.setCurrent(slug)
!$musicStore.playing && musicStore.toggle()
musicStore.play(slug)
}
const handleQueueNext = () => {
@ -33,7 +32,6 @@
<button
class="mt-2 hover:brightness-75"
on:click={!nowPlaying ? handlePlay : () => {}}
class:invisible={nowPlaying}
>
<span class="material-symbols-outlined icon fill">play_arrow</span>
</button>

View file

@ -3,10 +3,10 @@
import Speedometer from './Speedometer.svelte'
import SpeedLimit from './SpeedLimit.svelte'
import MediaDisplay from './MediaPlayer/MediaDisplay.svelte'
import Player from './MediaPlayer/Player.svelte'
// import Player from './MediaPlayer/Player.svelte'
</script>
<Player />
<!-- <Player /> -->
<div class="mt-2">
<div class="px-5">

View file

@ -1,9 +1,16 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { musicStore } from '../../stores/musicStore'
const dispatch = createEventDispatcher()
export let playing = false
let startTime = Date.now()
$: if (playing) {
startTime = Date.now()
}
</script>
<div class="my-auto flex gap-4 mr-4">

View file

@ -2,6 +2,9 @@
import Controls from './Controls.svelte'
import { musicStore } from '../../stores/musicStore'
import { songList } from './songList'
import { fly } from 'svelte/transition'
import { quintInOut } from 'svelte/easing'
import { onMount } from 'svelte'
$: currentSong = $musicStore.queue[$musicStore.currentIndex]
$: songData = songList[currentSong]
@ -17,10 +20,19 @@
const toggle = () => {
musicStore.toggle()
}
onMount(() => {
document.addEventListener('ended', () => {
musicStore.skip()
})
})
</script>
{#if songData}
<div class="rounded-t-lg bg-neutral-800 px-4 py-2 h-24 flex justify-between">
<div
class="rounded-t-lg bg-neutral-800 px-4 py-2 h-24 flex justify-between"
transition:fly={{ y: 100, duration: 300, easing: quintInOut }}
>
<div class="flex gap-6">
<div class="aspect-square">
<img

View file

@ -1,26 +0,0 @@
<script lang="ts">
import { AudioManager } from './audioManager'
import { musicStore } from '../../stores/musicStore'
import { songList } from './songList'
const audioManager = new AudioManager()
$: currentSong = songList[$musicStore.queue[$musicStore.currentIndex]]
let src: string = ''
$: {
if (currentSong) src = currentSong.src
console.log(currentSong)
}
$: {
if (src !== '' && $musicStore.playing) {
audioManager.playAudio(src)
console.log(src)
} else if (!$musicStore.playing) {
console.log('stopping')
audioManager.stopAudio()
}
console.log($musicStore.queue)
console.log($musicStore.currentIndex)
}
</script>

View file

@ -1,88 +0,0 @@
export class AudioManager {
private audioContext: AudioContext | null = null
private currentSource: AudioBufferSourceNode | null = null
private currentToken: number = 0 // Unique token for each play request
private isPlaying: boolean = false // Track whether audio is playing
constructor() {
this.initAudioContext()
}
private async initAudioContext() {
if (!this.audioContext) {
this.audioContext = new AudioContext()
}
}
public async playAudio(url: string): Promise<void> {
const playToken = ++this.currentToken // Update the token for this request
await this.initAudioContext()
if (this.audioContext) {
this.stopAudio() // Stop any currently playing audio
try {
const response = await fetch(url)
const arrayBuffer = await response.arrayBuffer()
// Before decoding, check if the token has changed
if (this.currentToken !== playToken) {
return // Abort this operation if a new play request has been made
}
const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer)
const source = this.audioContext.createBufferSource()
source.buffer = audioBuffer
source.connect(this.audioContext.destination)
// Again check the token before starting playback
if (this.currentToken !== playToken) {
return // Abort if a newer request has been made
}
source.start(0)
this.currentSource = source
this.isPlaying = true // Update the playing status
// Set the playing status to false when the audio ends
source.onended = () => {
if (this.currentToken === playToken) {
// Check to avoid race conditions
this.isPlaying = false
}
}
} catch (error) {
console.error('Error playing audio:', error)
this.isPlaying = false // Ensure status is accurate in case of error
}
}
}
public stopAudio() {
if (this.currentSource) {
this.currentSource.stop()
this.currentSource = null
this.isPlaying = false // Update the playing status
}
}
// Method to check if audio is currently playing
public isAudioPlaying(): boolean {
return this.isPlaying
}
}
// Usage example
const audioManager = new AudioManager()
console.log(audioManager.isAudioPlaying()) // False, initially
// To play audio
audioManager
.playAudio('https://example.com/path/to/your/audio/file.mp3')
.then(() => {
console.log(audioManager.isAudioPlaying()) // Should log true when audio starts playing
})
// Later, you can check if audio is still playing
setTimeout(() => {
console.log(audioManager.isAudioPlaying()) // The result depends on the audio length and timing
}, 1000)

View file

@ -0,0 +1,51 @@
export class AudioPlayer {
private audio: HTMLAudioElement
private currentUrl: string | null = null
private isPlaying: boolean = false
public length: number = 0
constructor() {
this.audio = new Audio()
this.audio.addEventListener('ended', () => {
this.isPlaying = false
const ended = new CustomEvent('ended', {
detail: this.currentUrl,
bubbles: true,
})
document.dispatchEvent(ended)
})
this.audio.onloadedmetadata = () => {
this.length = this.audio.duration
}
}
// Getter for playing state
get playing(): boolean {
return this.isPlaying
}
// Method to play audio from a URL
play(url: string): void {
this.audio.src = url
this.currentUrl = url
this.audio.play().catch(e => console.error('Error playing audio:', e))
this.isPlaying = true
}
// Method to pause audio playback
pause(): void {
if (this.isPlaying) {
this.audio.pause()
this.isPlaying = false
}
}
// Method to unpause (resume) audio playback
unpause(): void {
if (!this.isPlaying && this.currentUrl) {
this.audio.play().catch(e => console.error('Error playing audio:', e))
this.isPlaying = true
}
}
}

View file

@ -1,8 +1,11 @@
import { writable } from 'svelte/store'
import { songList } from '../Dashboard/MediaPlayer/songList'
import { AudioPlayer } from './audioManager'
interface MusicQueue {
queue: string[]
currentIndex: number
player: AudioPlayer
playing: boolean
}
@ -10,11 +13,27 @@ function createMusicStore() {
const { subscribe, set, update } = writable<MusicQueue>({
queue: [],
currentIndex: 0,
player: new AudioPlayer(),
playing: false,
})
return {
subscribe,
play: (songSlug: string) =>
update(store => {
if (store.queue.length >= 1) {
store.queue.splice(store.currentIndex + 1, 0, songSlug)
store.currentIndex++
store.player.play(songList[songSlug].src)
} else {
store.queue[0] = songSlug
store.currentIndex = 0
store.player.play(songList[songSlug].src)
}
store.playing = true
return store
}),
push: (songSlug: string) =>
update(store => {
store.queue.push(songSlug)
@ -22,43 +41,54 @@ function createMusicStore() {
}),
skip: () =>
update(store => {
if (store.currentIndex < store.queue.length - 1) {
let next = store.queue[store.currentIndex + 1]
if (next !== undefined) {
store.currentIndex++
store.playing = true
store.player.play(songList[next].src)
}
return store
}),
setCurrent: (songSlug: string) =>
update(store => {
store.currentIndex = store.queue.length
store.queue.push(songSlug)
return store
}),
queueNext: (songSlug: string) =>
update(store => {
store.queue.splice(store.currentIndex + 1, 0, songSlug)
return store
}),
pause: update(store => {
store.playing = false
return store
}),
play: update(store => {
store.playing = true
return store
}),
toggle: () =>
update(store => {
store.playing = !store.playing
if (store.player.playing) {
store.playing = false
store.player.pause()
} else {
store.playing = true
store.player.unpause()
}
return store
}),
pause: update(store => {
store.player.pause()
return store
}),
unpause: update(store => {
store.player.unpause()
return store
}),
rewind: () =>
update(store => {
store.currentIndex--
store.player.play(songList[store.queue[store.currentIndex]].src)
store.playing = true
return store
}),
reset: () => set({ queue: [], currentIndex: 0, playing: false }),
reset: () =>
set({
queue: [],
currentIndex: 0,
player: new AudioPlayer(),
playing: false,
}),
}
}