From dae62102391f543f9173aacb7e867229140c3ee4 Mon Sep 17 00:00:00 2001 From: Astrian Zheng Date: Fri, 22 Aug 2025 11:40:59 +1000 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=85=88=E9=87=8D=E6=96=B0=E6=89=8B?= =?UTF-8?q?=E6=92=B8=E4=BA=86=E4=B8=80=E4=B8=AA=E6=97=A0=E7=BC=9D=E6=92=AD?= =?UTF-8?q?=E6=94=BE=20=E5=A6=82=E6=9E=9C=E7=94=A8=E6=88=B7=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E4=BA=86=E6=92=AD=E6=94=BE=E8=BF=9B=E5=BA=A6=EF=BC=8C?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E9=87=8D=E6=96=B0=E8=B0=83=E5=BA=A6=E9=9F=B3?= =?UTF-8?q?=E4=B9=90=E6=92=AD=E6=94=BE=EF=BC=9B=E5=90=8C=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E4=B8=8A=E4=B8=80=E9=A6=96=E4=B8=8B=E4=B8=80=E9=A6=96=E7=AD=89?= =?UTF-8?q?=E4=B9=9F=E8=A6=81=E7=9C=8B=E6=80=8E=E4=B9=88=E5=8A=9E=20?= =?UTF-8?q?=E5=9C=A8=E7=BD=91=E9=A1=B5=E4=B8=8A=E6=90=9E=E8=BF=99=E7=A7=8D?= =?UTF-8?q?=E4=B8=9C=E8=A5=BF=E7=9C=9F=E7=9A=84=E5=BE=88=E5=A4=A7=E8=84=91?= =?UTF-8?q?=E5=8F=91=E5=85=89=E2=80=A6=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/PlayerWebAudio.vue | 42 +++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/components/PlayerWebAudio.vue b/src/components/PlayerWebAudio.vue index 129264a..a776d24 100644 --- a/src/components/PlayerWebAudio.vue +++ b/src/components/PlayerWebAudio.vue @@ -13,10 +13,16 @@ class WebAudioPlayer { context: AudioContext audioBuffer: { [key: string]: AudioBuffer} dummyAudio: HTMLAudioElement + currentTrackStartTime: number + currentSource: AudioBufferSourceNode | null + nextSource: AudioBufferSourceNode | null constructor() { this.context = new window.AudioContext() this.audioBuffer = {} + this.currentTrackStartTime = 0 + this.currentSource = null + this.nextSource = null // 创建一个隐藏的 HTML Audio 元素来帮助同步媒体会话状态 this.dummyAudio = new Audio() @@ -87,6 +93,7 @@ class WebAudioPlayer { } } + // 缓存歌曲 async loadResourceAndPlay() { try { debugPlayer("从播放器实例内部获取播放项目:") @@ -107,13 +114,17 @@ class WebAudioPlayer { this.audioBuffer[track.song.cid] = audioBuffer } - if (playQueue.currentTrack){ + if (playQueue.currentTrack) { await loadBuffer(playQueue.currentTrack) this.play() } - if (playQueue.nextTrack) + if (playQueue.nextTrack) { await loadBuffer(playQueue.nextTrack) + this.preloadNextTrack() + } else { + this.nextSource = null + } if (playQueue.previousTrack) await loadBuffer(playQueue.previousTrack) @@ -125,12 +136,29 @@ class WebAudioPlayer { } // 从头播放 - async play() { + play() { debugPlayer("开始播放") - const source = this.context.createBufferSource() - source.buffer = this.audioBuffer[playQueue.currentTrack.song.cid] - source.connect(this.context.destination) - source.start() + this.currentSource = this.context.createBufferSource() + this.currentSource.buffer = this.audioBuffer[playQueue.currentTrack.song.cid] + this.currentSource.connect(this.context.destination) + this.currentSource.start() + if (!playQueue.nextTrack) return + // 开始预先准备无缝播放下一首 + // 获取下一首歌接入的时间点 + this.currentTrackStartTime = this.context.currentTime + } + + // 预加载下一首歌 + preloadNextTrack() { + this.nextSource = null + const nextTrackStartTime = this.currentTrackStartTime + this.audioBuffer[playQueue.currentTrack.song.cid].duration + debugPlayer(`下一首歌将在 ${nextTrackStartTime} 时间点接入`) + + this.nextSource = this.context.createBufferSource() + this.nextSource.buffer = this.audioBuffer[playQueue.nextTrack.song.cid] + this.nextSource.connect(this.context.destination) + + this.nextSource.start(nextTrackStartTime) } pause() {}