feat: 处理暂停
This commit is contained in:
		
							parent
							
								
									c173f83301
								
							
						
					
					
						commit
						d89dd55a51
					
				| 
						 | 
				
			
			@ -90,7 +90,7 @@ class WebAudioPlayer {
 | 
			
		|||
			})
 | 
			
		||||
			navigator.mediaSession.setActionHandler('stop', () => {
 | 
			
		||||
				console.log('Media session: stop requested')
 | 
			
		||||
				this.stop()
 | 
			
		||||
				
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -111,29 +111,20 @@ class WebAudioPlayer {
 | 
			
		|||
				this.nextSource?.stop()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 将音频 buffer 加载到缓存空间
 | 
			
		||||
			const loadBuffer = async (track: QueueItem) => {
 | 
			
		||||
				if (this.audioBuffer[track.song.cid]) return // 已经缓存了,直接跳
 | 
			
		||||
				const response = await fetch(track.sourceUrl ?? "")
 | 
			
		||||
				const arrayBuffer = await response.arrayBuffer()
 | 
			
		||||
				const audioBuffer = await this.context.decodeAudioData(arrayBuffer)
 | 
			
		||||
				this.audioBuffer[track.song.cid] = audioBuffer
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (playQueue.currentTrack) {
 | 
			
		||||
				await loadBuffer(playQueue.currentTrack)
 | 
			
		||||
				await this.loadBuffer(playQueue.currentTrack)
 | 
			
		||||
				this.play()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (playQueue.nextTrack) {
 | 
			
		||||
				await loadBuffer(playQueue.nextTrack)
 | 
			
		||||
				await this.loadBuffer(playQueue.nextTrack)
 | 
			
		||||
				if (playState.isPlaying) this.scheduleNextTrack()
 | 
			
		||||
			} else {
 | 
			
		||||
				this.nextSource = null
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (playQueue.previousTrack)
 | 
			
		||||
				await loadBuffer(playQueue.previousTrack)
 | 
			
		||||
				await this.loadBuffer(playQueue.previousTrack)
 | 
			
		||||
 | 
			
		||||
			debugPlayer("缓存完成")
 | 
			
		||||
		} catch (error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -141,25 +132,56 @@ class WebAudioPlayer {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 将音频 buffer 加载到缓存空间
 | 
			
		||||
	loadBuffer = async (track: QueueItem) => {
 | 
			
		||||
		if (this.audioBuffer[track.song.cid]) return // 已经缓存了,直接跳
 | 
			
		||||
		const response = await fetch(track.sourceUrl ?? "")
 | 
			
		||||
		const arrayBuffer = await response.arrayBuffer()
 | 
			
		||||
		const audioBuffer = await this.context.decodeAudioData(arrayBuffer)
 | 
			
		||||
		this.audioBuffer[track.song.cid] = audioBuffer
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 播放
 | 
			
		||||
	play() {
 | 
			
		||||
		if (!playQueue.currentTrack) return 
 | 
			
		||||
		debugPlayer("开始播放")
 | 
			
		||||
		if (playState.playProgress !== 0) debugPlayer(`已经有所进度!${playState.playProgress}`)
 | 
			
		||||
		this.currentSource = this.context.createBufferSource()
 | 
			
		||||
		this.currentSource.buffer = this.audioBuffer[playQueue.currentTrack.song.cid]
 | 
			
		||||
		this.currentSource.connect(this.context.destination)
 | 
			
		||||
		this.currentSource.start(this.context.currentTime, playState.playProgress)
 | 
			
		||||
		playState.reportActualPlaying(true)
 | 
			
		||||
		this.reportProgress()
 | 
			
		||||
		
 | 
			
		||||
		if (!playState.actualPlaying) {
 | 
			
		||||
			// 如果实际正在播放,那么跳过音轨初始化阶段
 | 
			
		||||
			debugPlayer("开始播放")
 | 
			
		||||
			if (playState.playProgress !== 0) debugPlayer(`已经有所进度!${playState.playProgress}`)
 | 
			
		||||
			this.currentSource = this.context.createBufferSource()
 | 
			
		||||
			this.currentSource.buffer = this.audioBuffer[playQueue.currentTrack.song.cid]
 | 
			
		||||
			this.currentSource.connect(this.context.destination)
 | 
			
		||||
			this.currentSource.start(this.context.currentTime, playState.playProgress)
 | 
			
		||||
			playState.reportActualPlaying(true)
 | 
			
		||||
			this.reportProgress()
 | 
			
		||||
		}
 | 
			
		||||
		// 开始预先准备无缝播放下一首
 | 
			
		||||
		// 获取下一首歌接入的时间点
 | 
			
		||||
		this.currentTrackStartTime = this.context.currentTime - playState.playProgress
 | 
			
		||||
		if (this.audioBuffer[playQueue.nextTrack.song.cid]) this.scheduleNextTrack()
 | 
			
		||||
		if (playQueue.nextTrack && this.audioBuffer[playQueue.nextTrack.song.cid]) this.scheduleNextTrack()
 | 
			
		||||
 | 
			
		||||
		// 写入当前曲目播放完成后的钩子
 | 
			
		||||
		if (this.currentSource) this.currentSource.onended = () => {
 | 
			
		||||
			debugPlayer("当前歌曲播放结束")
 | 
			
		||||
			if (!!this.reportInterval) {
 | 
			
		||||
				// 页面依然正在回报播放进度,因此为歌曲自然结束
 | 
			
		||||
				debugPlayer("歌曲自然结束")
 | 
			
		||||
				this.onTrackEnded()
 | 
			
		||||
			} else {
 | 
			
		||||
				debugPlayer("用户暂停")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 安排下一首歌
 | 
			
		||||
	scheduleNextTrack() {
 | 
			
		||||
		if (this.nextSource !== null) {
 | 
			
		||||
			debugPlayer("下一首已经调度,跳过重复调度")
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: 处理不同循环逻辑
 | 
			
		||||
		if (!playQueue.nextTrack) return
 | 
			
		||||
		this.nextSource = null
 | 
			
		||||
		const nextTrackStartTime = this.currentTrackStartTime
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +206,9 @@ class WebAudioPlayer {
 | 
			
		|||
 | 
			
		||||
	// 停止回报
 | 
			
		||||
	stopReportProgress() {
 | 
			
		||||
		if (this.reportInterval) clearInterval(this.reportInterval)
 | 
			
		||||
		this.reportInterval = null
 | 
			
		||||
		debugPlayer(this.reportInterval)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pause() {
 | 
			
		||||
| 
						 | 
				
			
			@ -192,33 +216,43 @@ class WebAudioPlayer {
 | 
			
		|||
		debugPlayer(this.currentSource)
 | 
			
		||||
		this.currentSource?.stop()
 | 
			
		||||
		this.nextSource?.stop()
 | 
			
		||||
		this.nextSource = null 
 | 
			
		||||
		playState.reportActualPlaying(false)
 | 
			
		||||
		this.stopReportProgress()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stop() {
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
	async onTrackEnded() {
 | 
			
		||||
		// 1. 清理当前状态
 | 
			
		||||
		this.stopReportProgress()
 | 
			
		||||
		playState.reportPlayProgress(0)
 | 
			
		||||
 | 
			
		||||
	togglePlay() {
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
		// 2. 检查是否还有下一首
 | 
			
		||||
		if (!this.nextSource) {
 | 
			
		||||
			// 播放结束
 | 
			
		||||
			playState.reportActualPlaying(false)
 | 
			
		||||
			playState.togglePlay(false)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	getCurrentTime() {
 | 
			
		||||
		// 3. 切换到下一首
 | 
			
		||||
		playQueue.continueToNext()
 | 
			
		||||
		this.currentSource = this.nextSource
 | 
			
		||||
		this.nextSource = null
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
		// 4. 重新计算时间轴并启动进度报告
 | 
			
		||||
		this.currentTrackStartTime = this.context.currentTime
 | 
			
		||||
		this.reportProgress()
 | 
			
		||||
 | 
			
		||||
	updateMediaSessionState() {
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 定期更新播放位置
 | 
			
		||||
	startPositionUpdates() {
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 清理资源
 | 
			
		||||
	destroy() {
 | 
			
		||||
		
 | 
			
		||||
		// 5. 处理下下首
 | 
			
		||||
		if (playQueue.nextTrack) {
 | 
			
		||||
			debugPlayer("处理下下一首歌")
 | 
			
		||||
			if (this.audioBuffer[playQueue.nextTrack.song.cid]) {
 | 
			
		||||
				this.scheduleNextTrack()
 | 
			
		||||
			} else {
 | 
			
		||||
				await this.loadBuffer(playQueue.nextTrack)
 | 
			
		||||
				if (playState.actualPlaying) this.scheduleNextTrack()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,5 +31,6 @@ async function pauseOrResume() {
 | 
			
		|||
		<button class="bg-white/20 px-2 py-1" @click="playTheList">开始播放</button>
 | 
			
		||||
		<div>当前播放队列里有 {{ playQueue.queue.length }} 首歌</div>
 | 
			
		||||
		<button class="bg-white/20 px-2 py-1" @click="pauseOrResume">播放/暂停</button>
 | 
			
		||||
		<div>播放进度:{{ Math.floor(playState.playProgress) }} / {{ Math.floor(playState.trackDuration) }}</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</template>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,16 +102,10 @@ export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		|||
		currentPlaying.value = turnTo
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 跳至下一首(通常为用户点按下一首按钮)
 | 
			
		||||
	const skipToNext = () => {
 | 
			
		||||
		currentPlaying.value = currentPlaying.value + 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 继续播放接下来的曲目
 | 
			
		||||
	// 通常为当前曲目播放完毕,需要通过循环模式判断应该重置进度或队列指针 +1
 | 
			
		||||
	const continueToNext = () => {
 | 
			
		||||
		debugStore(loopingMode.value)
 | 
			
		||||
		// 注意:单曲循环时的进度重置需要在播放状态管理中处理
 | 
			
		||||
		if (loopingMode.value !== 'single') {
 | 
			
		||||
			currentPlaying.value = currentPlaying.value + 1
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -202,7 +196,6 @@ export const usePlayQueueStore = defineStore('queue', () => {
 | 
			
		|||
		toggleShuffle,
 | 
			
		||||
		toggleLoop,
 | 
			
		||||
		toggleQueuePlay,
 | 
			
		||||
		skipToNext,
 | 
			
		||||
		continueToNext,
 | 
			
		||||
	}
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user