From c33a9b49e84c4c3e5b9f6b96d464bdcb9dbd4c48 Mon Sep 17 00:00:00 2001 From: HorizonCode Date: Wed, 14 May 2025 13:33:58 +0200 Subject: [PATCH] fix: bpm timing --- bun.lockb | Bin 107187 -> 107615 bytes package.json | 1 + .../ui/background/background.svelte | 5 +- src/lib/components/ui/logo/logo.svelte | 49 ++++++++++++------ src/lib/utils.ts | 9 ++++ src/routes/+page.svelte | 7 ++- 6 files changed, 51 insertions(+), 20 deletions(-) diff --git a/bun.lockb b/bun.lockb index 8be036112e213246edd95a163f6f7cf8d6a3e9eb..a09b4b3fa68eb0fbc3d812320cf7b56e29b6937e 100644 GIT binary patch delta 16075 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 delta 15986 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 diff --git a/package.json b/package.json index be7ec56..6bddcf0 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "license": "MIT", "dependencies": { + "@elron/svelte-audio-store": "^1.0.0", "@tailwindcss/typography": "^0.5.15", "@tauri-apps/api": "^2", "@tauri-apps/plugin-dialog": "~2", diff --git a/src/lib/components/ui/background/background.svelte b/src/lib/components/ui/background/background.svelte index 660cad9..0679dac 100644 --- a/src/lib/components/ui/background/background.svelte +++ b/src/lib/components/ui/background/background.svelte @@ -1,12 +1,15 @@
diff --git a/src/lib/components/ui/logo/logo.svelte b/src/lib/components/ui/logo/logo.svelte index d53e3db..6c8a8ec 100644 --- a/src/lib/components/ui/logo/logo.svelte +++ b/src/lib/components/ui/logo/logo.svelte @@ -2,22 +2,22 @@ import { onMount } from "svelte"; import ezppLogo from "../../../../assets/logo.png"; import { osudirect } from "@/api/osudirect"; - import { playAudio } from "@/utils"; + import { gameSounds, playAudio } from "@/utils"; import { BeatmapDecoder } from "osu-parsers"; type logoProps = { + beatmapId: number; extended: boolean; onclick: () => void; }; - let { extended, onclick }: logoProps = $props(); + let { beatmapId, extended, onclick }: logoProps = $props(); let hovered = $state(false); let bpm = $state(150); // 1000 * 60 / bpm let lastTimeout: number | undefined = undefined; onMount(async () => { - const beatmapId = 2226722; const beatmapData = await osudirect.osu(beatmapId); if (beatmapData) { const decoder = new BeatmapDecoder(); @@ -31,39 +31,54 @@ // Function to play the heartbeat sound const playHeartbeat = () => { - playAudio("/audio/menuHeartbeat.mp3", hovered ? 1 : 0.3); + gameSounds.play("menuHeartbeat", { + volume: hovered ? 1 : 0.3, + }); }; // Function to synchronize the heartbeat with the song const syncHeartbeat = () => { - const currentTime = audio.currentTime * 1000; // Convert to milliseconds + const currentTime = audio.currentTime * 1000 - 25; // Convert to milliseconds const timingPoint = beatmap.controlPoints.timingPointAt(currentTime); + const timingPointTime = timingPoint.startTime; + console.log(currentTime, timingPointTime); if (timingPoint && bpm !== timingPoint.bpm) { bpm = timingPoint.bpm; - if (lastTimeout) { - clearTimeout(lastTimeout); - } + if (lastTimeout) window.clearTimeout(lastTimeout); const interval = (1000 * 60) / bpm; // Interval in milliseconds - const nextBeat = interval - (currentTime % interval); // Time to the next beat - setTimeout(() => { + const nextBeat = currentTime % interval; // Invert: time since last beat + + // Schedule the next heartbeat at the correct time + lastTimeout = window.setTimeout(() => { playHeartbeat(); - lastTimeout = setInterval(playHeartbeat, interval); + // Clear any previous interval + if (lastTimeout) { + window.clearInterval(lastTimeout); + lastTimeout = undefined; + } + if (!lastTimeout) + lastTimeout = window.setInterval(playHeartbeat, interval); }, nextBeat); } // Continue syncing requestAnimationFrame(syncHeartbeat); }; - // Start playback and syncing - audio.addEventListener("play", () => { + // Wait for audio to be ready before starting playback and syncing + audio.addEventListener("canplay", async () => { + audio.addEventListener("play", syncHeartbeat); + audio.addEventListener("ended", async() => await audio.play()); + await audio.play(); syncHeartbeat(); }); - - await audio.play(); } }); + + onMount(() => { + gameSounds.preload(); + });
(hovered = false)} onclick={() => { if (extended) { - playAudio("/audio/menuBack.wav", 1); + gameSounds.play("menuBack"); } else { - playAudio("/audio/menuHit.wav", 1); + gameSounds.play("menuHit"); } onclick(); }} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 4f962f8..eb6a47f 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,15 @@ +import { createAudioStore } from "@elron/svelte-audio-store"; import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; +const sounds = { + menuHeartbeat: "/audio/menuHeartbeat.mp3", + menuBack: "/audio/menuBack.wav", + menuHit: "/audio/menuHit.wav", +}; + +export const gameSounds = createAudioStore(sounds); + export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index d0ae83c..4ffa276 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -6,10 +6,13 @@ import Progressbar from "@/components/ui/progressbar/progressbar.svelte"; let progress = $state(0); let extended = $state(false); + + let beatmapId = $state(3820896); +
- +
U @@ -22,7 +25,7 @@ > - (extended = !extended)} /> + (extended = !extended)} />