From 655473699a1e48a60c7d338c526c5d2c5a6b1800 Mon Sep 17 00:00:00 2001
From: Java-wyx <12211306@mail.sustech.edu.cn>
Date: Mon, 19 May 2025 17:59:20 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=92=AD=E6=94=BE?=
=?UTF-8?q?=E9=80=9F=E5=BA=A6=E6=8E=A7=E5=88=B6=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
现有播放器不支持改变播放速度,用户无法实现 0.5×、1.5×、2.0× 等快进/慢放需求。为了提升可用性和灵活性,决定在播放栏增加速度选择菜单,并支持 Media Session API 同步速率
---
src/i18n/lang/en-US/player.ts | 3 +-
src/i18n/lang/zh-CN/player.ts | 3 +-
src/renderer/components/player/PlayBar.vue | 54 ++++++++++++++++++++++
src/renderer/services/audioService.ts | 34 +++++++++++++-
src/renderer/store/modules/player.ts | 25 +++++++++-
5 files changed, 115 insertions(+), 4 deletions(-)
diff --git a/src/i18n/lang/en-US/player.ts b/src/i18n/lang/en-US/player.ts
index 8ec5932..538a97a 100644
--- a/src/i18n/lang/en-US/player.ts
+++ b/src/i18n/lang/en-US/player.ts
@@ -58,7 +58,8 @@ export default {
next: 'Next',
volume: 'Volume',
favorite: 'Favorite {name}',
- unFavorite: 'Unfavorite {name}'
+ unFavorite: 'Unfavorite {name}',
+ playbackSpeed: 'Playback Speed'
},
eq: {
title: 'Equalizer',
diff --git a/src/i18n/lang/zh-CN/player.ts b/src/i18n/lang/zh-CN/player.ts
index bc5296b..b9986da 100644
--- a/src/i18n/lang/zh-CN/player.ts
+++ b/src/i18n/lang/zh-CN/player.ts
@@ -59,7 +59,8 @@ export default {
volume: '音量',
favorite: '已收藏{name}',
unFavorite: '已取消收藏{name}',
- miniPlayBar: '迷你播放栏'
+ miniPlayBar: '迷你播放栏',
+ playbackSpeed: '播放速度'
},
eq: {
title: '均衡器',
diff --git a/src/renderer/components/player/PlayBar.vue b/src/renderer/components/player/PlayBar.vue
index cc2b5f2..cc4fc91 100644
--- a/src/renderer/components/player/PlayBar.vue
+++ b/src/renderer/components/player/PlayBar.vue
@@ -161,6 +161,23 @@
{{ t('player.playBar.playList') }}
+
+
+
+
+
+ {{ playbackRate }}x
+
+
+ {{ t('player.playBar.playbackSpeed') }}
+
+
@@ -319,6 +336,23 @@ const playModeText = computed(() => {
}
});
+// 播放速度控制
+const playbackRate = ref(1.0);
+const playbackRateOptions = [
+ { label: '0.5x', key: 0.5 },
+ { label: '0.75x', key: 0.75 },
+ { label: '1.0x', key: 1.0 },
+ { label: '1.25x', key: 1.25 },
+ { label: '1.5x', key: 1.5 },
+ { label: '2.0x', key: 2.0 }
+];
+
+
+const handlePlaybackRateChange = (rate: number) => {
+ playbackRate.value = rate;
+ audioService.setPlaybackRate(rate);
+};
+
// 切换播放模式
const togglePlayMode = () => {
playerStore.togglePlayMode();
@@ -702,4 +736,24 @@ const openPlayListDrawer = () => {
color: white;
animation: spin 1s linear infinite;
}
+
+.play-speed {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ padding: 0 8px;
+}
+
+.speed-button {
+ font-size: 14px;
+ color: var(--text-color);
+ padding: 4px 8px;
+ border-radius: 4px;
+ background: var(--hover-color);
+}
+
+.speed-button:hover {
+ background: var(--hover-color-dark);
+}
diff --git a/src/renderer/services/audioService.ts b/src/renderer/services/audioService.ts
index 585f015..5347c51 100644
--- a/src/renderer/services/audioService.ts
+++ b/src/renderer/services/audioService.ts
@@ -18,6 +18,8 @@ class AudioService {
private bypass = false;
+ private playbackRate = 1.0; // 添加播放速度属性
+
// 预设的 EQ 频段
private readonly frequencies = [31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000];
@@ -143,7 +145,7 @@ class AudioService {
if ('setPositionState' in navigator.mediaSession) {
navigator.mediaSession.setPositionState({
duration: this.currentSound.duration(),
- playbackRate: 1.0,
+ playbackRate: this.playbackRate,
position: this.currentSound.seek() as number
});
}
@@ -565,6 +567,7 @@ class AudioService {
volume: localStorage.getItem('volume')
? parseFloat(localStorage.getItem('volume') as string)
: 1,
+ rate: this.playbackRate, // 设置初始播放速度
format: ['mp3', 'aac'],
onloaderror: (_, error) => {
console.error('Audio load error:', error);
@@ -747,6 +750,35 @@ class AudioService {
public setCurrentPreset(preset: string): void {
localStorage.setItem('currentPreset', preset);
}
+
+ public setPlaybackRate(rate: number) {
+ if (!this.currentSound) return;
+ this.playbackRate = rate;
+
+ // Howler 的 rate() 在 html5 模式下不生效
+ this.currentSound.rate(rate);
+
+ // 取出底层 HTMLAudioElement,改原生 playbackRate
+ const sounds = (this.currentSound as any)._sounds as any[];
+ sounds.forEach(({ _node }) => {
+ if (_node instanceof HTMLAudioElement) {
+ _node.playbackRate = rate;
+ }
+ });
+
+ // 同步给 Media Session UI
+ if ('mediaSession' in navigator && 'setPositionState' in navigator.mediaSession) {
+ navigator.mediaSession.setPositionState({
+ duration: this.currentSound.duration(),
+ playbackRate: rate,
+ position: this.currentSound.seek() as number
+ });
+ }
+ }
+
+ public getPlaybackRate(): number {
+ return this.playbackRate;
+ }
}
export const audioService = new AudioService();
diff --git a/src/renderer/store/modules/player.ts b/src/renderer/store/modules/player.ts
index 7994031..852b30d 100644
--- a/src/renderer/store/modules/player.ts
+++ b/src/renderer/store/modules/player.ts
@@ -399,6 +399,9 @@ export const usePlayerStore = defineStore('player', () => {
value: 0
}));
+ // 添加播放速度状态
+ const playbackRate = ref(1.0);
+
// 清空播放列表
const clearPlayAll = async () => {
audioService.pause()
@@ -1042,6 +1045,23 @@ export const usePlayerStore = defineStore('player', () => {
setPlayList(newPlayList);
};
+ // 设置播放速度
+ const setPlaybackRate = (rate: number) => {
+ playbackRate.value = rate;
+ audioService.setPlaybackRate(rate);
+ // 保存到本地存储
+ localStorage.setItem('playbackRate', rate.toString());
+ };
+
+ // 初始化播放速度
+ const initializePlaybackRate = () => {
+ const savedRate = localStorage.getItem('playbackRate');
+ if (savedRate) {
+ playbackRate.value = parseFloat(savedRate);
+ audioService.setPlaybackRate(playbackRate.value);
+ }
+ };
+
// 初始化播放状态
const initializePlayState = async () => {
const settingStore = useSettingsStore();
@@ -1093,6 +1113,7 @@ export const usePlayerStore = defineStore('player', () => {
localStorage.removeItem('playProgress');
}
}
+ initializePlaybackRate();
};
const initializeFavoriteList = async () => {
@@ -1343,6 +1364,8 @@ export const usePlayerStore = defineStore('player', () => {
playAudio,
reparseCurrentSong,
setPlayListDrawerVisible,
- handlePause
+ handlePause,
+ playbackRate,
+ setPlaybackRate
};
});