From 656c81b82ebb85b484e5dd67d21419763b95e0c1 Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Sun, 23 Nov 2025 13:50:48 +0800 Subject: [PATCH] [+] Speed control --- README.md | 2 +- src/lib/ui/player/MusicControl.ts | 11 +++++++++++ src/routes/song/[id]/karaoke/+page.svelte | 10 +++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d22bf07..9205458 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Practice Japanese Karaoke lyrics reading and typing at the same time with KaraDa * [x] 跟随音乐滚动歌词 * [ ] 升降调 * [ ] 播放/暂停音乐控制 - * [ ] 变速 + * [x] 变速 * [ ] 音域分析(自动推荐升降调幅度) * [ ] 电视模式 * [ ] 和手机配对、用手机点歌 diff --git a/src/lib/ui/player/MusicControl.ts b/src/lib/ui/player/MusicControl.ts index 46c0c78..87fe94c 100644 --- a/src/lib/ui/player/MusicControl.ts +++ b/src/lib/ui/player/MusicControl.ts @@ -4,6 +4,7 @@ import type { LyricLine } from '$lib/types' export class MusicControl { player: Tone.Player vocalsPlayer?: Tone.Player + speed: number = 1 lyrics: LyricLine[] = [] currentLineIndex: number = 0 checkInterval: any @@ -13,6 +14,8 @@ export class MusicControl { vocalsUrl?: string constructor(audioUrl: string, vocalsUrl?: string) { + this.audioUrl = audioUrl + this.vocalsUrl = vocalsUrl this.audioUrl = audioUrl this.vocalsUrl = vocalsUrl this.player = new Tone.Player(audioUrl).toDestination() @@ -72,6 +75,14 @@ export class MusicControl { this.startCheckLoop() } + setSpeed(speed: number) { + this.speed = speed + this.player.playbackRate = this.speed + if (this.vocalsPlayer) { + this.vocalsPlayer.playbackRate = this.speed + } + } + setVocalsVolume(db: number) { if (this.vocalsPlayer) this.vocalsPlayer.volume.value = db } diff --git a/src/routes/song/[id]/karaoke/+page.svelte b/src/routes/song/[id]/karaoke/+page.svelte index dcee018..24d5f28 100644 --- a/src/routes/song/[id]/karaoke/+page.svelte +++ b/src/routes/song/[id]/karaoke/+page.svelte @@ -22,6 +22,7 @@ $effect(() => { API.saveUserData({ loc }) }) let vocalsVolume = $state(100) // 0-100 + let speed = $state(1) // Process lyrics const isHideRepeated = $derived(settings.hideRepeated) @@ -31,13 +32,14 @@ let musicControl = $state() $effect(() => { li; musicControl?.updateLine(li) }) - // Volume control + // Volume, Speed control $effect(() => { if (musicControl) { // Tone.js volume is in decibels. 0 is full, -Infinity is silent. // Simple mapping: 100 -> 0, 0 -> -60 (or mute) const db = vocalsVolume === 0 ? -Infinity : 20 * Math.log10(vocalsVolume / 100) musicControl.setVocalsVolume(db) + musicControl.setSpeed(speed) } }) @@ -96,6 +98,12 @@ {t.noVocals} {/if} + +
+
+ +
{speed.toFixed(2)}x
+