Skip to content

Commit 6554736

Browse files
committed
feat: 添加播放速度控制功能
现有播放器不支持改变播放速度,用户无法实现 0.5×、1.5×、2.0× 等快进/慢放需求。为了提升可用性和灵活性,决定在播放栏增加速度选择菜单,并支持 Media Session API 同步速率
1 parent 4d371df commit 6554736

File tree

5 files changed

+115
-4
lines changed

5 files changed

+115
-4
lines changed

src/i18n/lang/en-US/player.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export default {
5858
next: 'Next',
5959
volume: 'Volume',
6060
favorite: 'Favorite {name}',
61-
unFavorite: 'Unfavorite {name}'
61+
unFavorite: 'Unfavorite {name}',
62+
playbackSpeed: 'Playback Speed'
6263
},
6364
eq: {
6465
title: 'Equalizer',

src/i18n/lang/zh-CN/player.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ export default {
5959
volume: '音量',
6060
favorite: '已收藏{name}',
6161
unFavorite: '已取消收藏{name}',
62-
miniPlayBar: '迷你播放栏'
62+
miniPlayBar: '迷你播放栏',
63+
playbackSpeed: '播放速度'
6364
},
6465
eq: {
6566
title: '均衡器',

src/renderer/components/player/PlayBar.vue

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,23 @@
161161
</template>
162162
{{ t('player.playBar.playList') }}
163163
</n-tooltip>
164+
<!-- 添加播放速度控制按钮 -->
165+
<n-dropdown
166+
v-if="!isMobile"
167+
:options="playbackRateOptions"
168+
@select="handlePlaybackRateChange"
169+
trigger="click"
170+
:z-index="9999999"
171+
>
172+
<n-tooltip trigger="hover" :z-index="9999999">
173+
<template #trigger>
174+
<div class="play-speed">
175+
<span class="speed-button">{{ playbackRate }}x</span>
176+
</div>
177+
</template>
178+
{{ t('player.playBar.playbackSpeed') }}
179+
</n-tooltip>
180+
</n-dropdown>
164181
</div>
165182
<!-- 播放音乐 -->
166183
<music-full ref="MusicFullRef" v-model="musicFullVisible" :background="background" />
@@ -319,6 +336,23 @@ const playModeText = computed(() => {
319336
}
320337
});
321338
339+
// 播放速度控制
340+
const playbackRate = ref(1.0);
341+
const playbackRateOptions = [
342+
{ label: '0.5x', key: 0.5 },
343+
{ label: '0.75x', key: 0.75 },
344+
{ label: '1.0x', key: 1.0 },
345+
{ label: '1.25x', key: 1.25 },
346+
{ label: '1.5x', key: 1.5 },
347+
{ label: '2.0x', key: 2.0 }
348+
];
349+
350+
351+
const handlePlaybackRateChange = (rate: number) => {
352+
playbackRate.value = rate;
353+
audioService.setPlaybackRate(rate);
354+
};
355+
322356
// 切换播放模式
323357
const togglePlayMode = () => {
324358
playerStore.togglePlayMode();
@@ -702,4 +736,24 @@ const openPlayListDrawer = () => {
702736
color: white;
703737
animation: spin 1s linear infinite;
704738
}
739+
740+
.play-speed {
741+
display: flex;
742+
align-items: center;
743+
justify-content: center;
744+
cursor: pointer;
745+
padding: 0 8px;
746+
}
747+
748+
.speed-button {
749+
font-size: 14px;
750+
color: var(--text-color);
751+
padding: 4px 8px;
752+
border-radius: 4px;
753+
background: var(--hover-color);
754+
}
755+
756+
.speed-button:hover {
757+
background: var(--hover-color-dark);
758+
}
705759
</style>

src/renderer/services/audioService.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class AudioService {
1818

1919
private bypass = false;
2020

21+
private playbackRate = 1.0; // 添加播放速度属性
22+
2123
// 预设的 EQ 频段
2224
private readonly frequencies = [31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000];
2325

@@ -143,7 +145,7 @@ class AudioService {
143145
if ('setPositionState' in navigator.mediaSession) {
144146
navigator.mediaSession.setPositionState({
145147
duration: this.currentSound.duration(),
146-
playbackRate: 1.0,
148+
playbackRate: this.playbackRate,
147149
position: this.currentSound.seek() as number
148150
});
149151
}
@@ -565,6 +567,7 @@ class AudioService {
565567
volume: localStorage.getItem('volume')
566568
? parseFloat(localStorage.getItem('volume') as string)
567569
: 1,
570+
rate: this.playbackRate, // 设置初始播放速度
568571
format: ['mp3', 'aac'],
569572
onloaderror: (_, error) => {
570573
console.error('Audio load error:', error);
@@ -747,6 +750,35 @@ class AudioService {
747750
public setCurrentPreset(preset: string): void {
748751
localStorage.setItem('currentPreset', preset);
749752
}
753+
754+
public setPlaybackRate(rate: number) {
755+
if (!this.currentSound) return;
756+
this.playbackRate = rate;
757+
758+
// Howler 的 rate() 在 html5 模式下不生效
759+
this.currentSound.rate(rate);
760+
761+
// 取出底层 HTMLAudioElement,改原生 playbackRate
762+
const sounds = (this.currentSound as any)._sounds as any[];
763+
sounds.forEach(({ _node }) => {
764+
if (_node instanceof HTMLAudioElement) {
765+
_node.playbackRate = rate;
766+
}
767+
});
768+
769+
// 同步给 Media Session UI
770+
if ('mediaSession' in navigator && 'setPositionState' in navigator.mediaSession) {
771+
navigator.mediaSession.setPositionState({
772+
duration: this.currentSound.duration(),
773+
playbackRate: rate,
774+
position: this.currentSound.seek() as number
775+
});
776+
}
777+
}
778+
779+
public getPlaybackRate(): number {
780+
return this.playbackRate;
781+
}
750782
}
751783

752784
export const audioService = new AudioService();

src/renderer/store/modules/player.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,9 @@ export const usePlayerStore = defineStore('player', () => {
399399
value: 0
400400
}));
401401

402+
// 添加播放速度状态
403+
const playbackRate = ref(1.0);
404+
402405
// 清空播放列表
403406
const clearPlayAll = async () => {
404407
audioService.pause()
@@ -1042,6 +1045,23 @@ export const usePlayerStore = defineStore('player', () => {
10421045
setPlayList(newPlayList);
10431046
};
10441047

1048+
// 设置播放速度
1049+
const setPlaybackRate = (rate: number) => {
1050+
playbackRate.value = rate;
1051+
audioService.setPlaybackRate(rate);
1052+
// 保存到本地存储
1053+
localStorage.setItem('playbackRate', rate.toString());
1054+
};
1055+
1056+
// 初始化播放速度
1057+
const initializePlaybackRate = () => {
1058+
const savedRate = localStorage.getItem('playbackRate');
1059+
if (savedRate) {
1060+
playbackRate.value = parseFloat(savedRate);
1061+
audioService.setPlaybackRate(playbackRate.value);
1062+
}
1063+
};
1064+
10451065
// 初始化播放状态
10461066
const initializePlayState = async () => {
10471067
const settingStore = useSettingsStore();
@@ -1093,6 +1113,7 @@ export const usePlayerStore = defineStore('player', () => {
10931113
localStorage.removeItem('playProgress');
10941114
}
10951115
}
1116+
initializePlaybackRate();
10961117
};
10971118

10981119
const initializeFavoriteList = async () => {
@@ -1343,6 +1364,8 @@ export const usePlayerStore = defineStore('player', () => {
13431364
playAudio,
13441365
reparseCurrentSong,
13451366
setPlayListDrawerVisible,
1346-
handlePause
1367+
handlePause,
1368+
playbackRate,
1369+
setPlaybackRate
13471370
};
13481371
});

0 commit comments

Comments
 (0)