Merge pull request #241 from Java-wyx/speed-up

feat: 添加播放速度控制功能
This commit is contained in:
Alger
2025-05-19 19:17:26 +08:00
committed by GitHub
5 changed files with 115 additions and 4 deletions

View File

@@ -58,7 +58,8 @@ export default {
next: 'Next', next: 'Next',
volume: 'Volume', volume: 'Volume',
favorite: 'Favorite {name}', favorite: 'Favorite {name}',
unFavorite: 'Unfavorite {name}' unFavorite: 'Unfavorite {name}',
playbackSpeed: 'Playback Speed'
}, },
eq: { eq: {
title: 'Equalizer', title: 'Equalizer',

View File

@@ -59,7 +59,8 @@ export default {
volume: '音量', volume: '音量',
favorite: '已收藏{name}', favorite: '已收藏{name}',
unFavorite: '已取消收藏{name}', unFavorite: '已取消收藏{name}',
miniPlayBar: '迷你播放栏' miniPlayBar: '迷你播放栏',
playbackSpeed: '播放速度'
}, },
eq: { eq: {
title: '均衡器', title: '均衡器',

View File

@@ -161,6 +161,23 @@
</template> </template>
{{ t('player.playBar.playList') }} {{ t('player.playBar.playList') }}
</n-tooltip> </n-tooltip>
<!-- 添加播放速度控制按钮 -->
<n-dropdown
v-if="!isMobile"
:options="playbackRateOptions"
@select="handlePlaybackRateChange"
trigger="click"
:z-index="9999999"
>
<n-tooltip trigger="hover" :z-index="9999999">
<template #trigger>
<div class="play-speed">
<span class="speed-button">{{ playbackRate }}x</span>
</div>
</template>
{{ t('player.playBar.playbackSpeed') }}
</n-tooltip>
</n-dropdown>
</div> </div>
<!-- 播放音乐 --> <!-- 播放音乐 -->
<music-full ref="MusicFullRef" v-model="musicFullVisible" :background="background" /> <music-full ref="MusicFullRef" v-model="musicFullVisible" :background="background" />
@@ -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 = () => { const togglePlayMode = () => {
playerStore.togglePlayMode(); playerStore.togglePlayMode();
@@ -702,4 +736,24 @@ const openPlayListDrawer = () => {
color: white; color: white;
animation: spin 1s linear infinite; 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);
}
</style> </style>

View File

@@ -18,6 +18,8 @@ class AudioService {
private bypass = false; private bypass = false;
private playbackRate = 1.0; // 添加播放速度属性
// 预设的 EQ 频段 // 预设的 EQ 频段
private readonly frequencies = [31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000]; private readonly frequencies = [31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000];
@@ -143,7 +145,7 @@ class AudioService {
if ('setPositionState' in navigator.mediaSession) { if ('setPositionState' in navigator.mediaSession) {
navigator.mediaSession.setPositionState({ navigator.mediaSession.setPositionState({
duration: this.currentSound.duration(), duration: this.currentSound.duration(),
playbackRate: 1.0, playbackRate: this.playbackRate,
position: this.currentSound.seek() as number position: this.currentSound.seek() as number
}); });
} }
@@ -565,6 +567,7 @@ class AudioService {
volume: localStorage.getItem('volume') volume: localStorage.getItem('volume')
? parseFloat(localStorage.getItem('volume') as string) ? parseFloat(localStorage.getItem('volume') as string)
: 1, : 1,
rate: this.playbackRate, // 设置初始播放速度
format: ['mp3', 'aac'], format: ['mp3', 'aac'],
onloaderror: (_, error) => { onloaderror: (_, error) => {
console.error('Audio load error:', error); console.error('Audio load error:', error);
@@ -747,6 +750,35 @@ class AudioService {
public setCurrentPreset(preset: string): void { public setCurrentPreset(preset: string): void {
localStorage.setItem('currentPreset', preset); 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(); export const audioService = new AudioService();

View File

@@ -399,6 +399,9 @@ export const usePlayerStore = defineStore('player', () => {
value: 0 value: 0
})); }));
// 添加播放速度状态
const playbackRate = ref(1.0);
// 清空播放列表 // 清空播放列表
const clearPlayAll = async () => { const clearPlayAll = async () => {
audioService.pause() audioService.pause()
@@ -1042,6 +1045,23 @@ export const usePlayerStore = defineStore('player', () => {
setPlayList(newPlayList); 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 initializePlayState = async () => {
const settingStore = useSettingsStore(); const settingStore = useSettingsStore();
@@ -1093,6 +1113,7 @@ export const usePlayerStore = defineStore('player', () => {
localStorage.removeItem('playProgress'); localStorage.removeItem('playProgress');
} }
} }
initializePlaybackRate();
}; };
const initializeFavoriteList = async () => { const initializeFavoriteList = async () => {
@@ -1343,6 +1364,8 @@ export const usePlayerStore = defineStore('player', () => {
playAudio, playAudio,
reparseCurrentSong, reparseCurrentSong,
setPlayListDrawerVisible, setPlayListDrawerVisible,
handlePause handlePause,
playbackRate,
setPlaybackRate
}; };
}); });