From 2b85bc4825fa035c1b2f897cc2a38a7b1522f408 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 15:00:46 +0000 Subject: [PATCH] Changes --- src/components/Oscilloscope.tsx | 2 +- src/components/OscilloscopeDisplay.tsx | 44 ++++++++++---------------- 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/src/components/Oscilloscope.tsx b/src/components/Oscilloscope.tsx index c343024..d60afde 100644 --- a/src/components/Oscilloscope.tsx +++ b/src/components/Oscilloscope.tsx @@ -338,7 +338,7 @@ export function Oscilloscope() { isPlaying={isPlaying} playbackSpeed={playbackSpeed} isLooping={isLooping} - seekPosition={seekPosition} + audioElementRef={audioRef} onPlaybackEnd={() => { setIsPlaying(false); setCurrentTime(0); diff --git a/src/components/OscilloscopeDisplay.tsx b/src/components/OscilloscopeDisplay.tsx index cf7eb3c..df1a111 100755 --- a/src/components/OscilloscopeDisplay.tsx +++ b/src/components/OscilloscopeDisplay.tsx @@ -11,7 +11,7 @@ interface OscilloscopeDisplayProps { isPlaying: boolean; playbackSpeed: number; isLooping: boolean; - seekPosition: number; + audioElementRef?: React.RefObject; onPlaybackEnd?: () => void; onSeek?: (position: number) => void; liveSettings?: LiveDisplaySettings; @@ -39,15 +39,13 @@ export function OscilloscopeDisplay({ isPlaying, playbackSpeed, isLooping, - seekPosition, + audioElementRef, onPlaybackEnd, onSeek, liveSettings }: OscilloscopeDisplayProps) { const canvasRef = useRef(null); const animationRef = useRef(null); - const currentSampleRef = useRef(0); - const lastSeekPositionRef = useRef(0); const { analyzerNode: sharedAnalyzer } = useSharedAudioAnalyzer(); // Use shared analyzer for live audio (music player, sound effects) @@ -96,10 +94,9 @@ export function OscilloscopeDisplay({ const primaryColor = getThemeColor('--primary', '#00ff00'); const backgroundColor = getThemeColor('--background', '#000000'); - let samplesPerFrame: number; - let startSample: number; - let endSample: number; - let samplesToAdvance: number = samplesPerFrame; + let samplesPerFrame: number = 0; + let endSample: number = 0; + let samplesToAdvance: number = 0; // Priority: micAnalyzer > liveAnalyzer (shared) > audioData (file) const activeAnalyzer = micAnalyzer || liveAnalyzer; @@ -180,26 +177,22 @@ export function OscilloscopeDisplay({ animationRef.current = requestAnimationFrame(drawFrame); return; } - // File playback mode - need audioData if (!audioData) return; - // File playback mode + // File playback mode - sync with audio element if available const baseSamplesPerFrame = Math.floor(audioData.sampleRate / FPS); samplesPerFrame = Math.floor(baseSamplesPerFrame * playbackSpeed); samplesToAdvance = samplesPerFrame; - // Handle seeking - if (seekPosition > 0 && seekPosition !== lastSeekPositionRef.current) { - startSample = Math.floor(seekPosition * audioData.leftChannel.length); - currentSampleRef.current = startSample; - lastSeekPositionRef.current = seekPosition; - // Reset after one frame - setTimeout(() => { - lastSeekPositionRef.current = 0; - }, 1000 / FPS); + // Get current position from audio element (real-time sync at 60fps) + let startSample: number; + if (audioElementRef?.current && !audioElementRef.current.paused) { + const currentTime = audioElementRef.current.currentTime; + startSample = Math.floor((currentTime / audioData.duration) * audioData.leftChannel.length); } else { - startSample = currentSampleRef.current; + // Fallback: just show first frame when paused + startSample = 0; } endSample = Math.min(startSample + samplesPerFrame, audioData.leftChannel.length); @@ -342,20 +335,18 @@ export function OscilloscopeDisplay({ ctx.stroke(); } - currentSampleRef.current = endSample; ctx.shadowBlur = 0; - if (endSample >= audioData.leftChannel.length) { - if (isLooping) { - currentSampleRef.current = 0; // Loop back to start - } else { + // Check if audio ended (when syncing to audio element) + if (audioElementRef?.current) { + if (audioElementRef.current.ended && !isLooping) { onPlaybackEnd?.(); return; } } animationRef.current = requestAnimationFrame(drawFrame); - }, [audioData, micAnalyzer, liveAnalyzer, mode, drawGraticule, onPlaybackEnd, isPlaying, playbackSpeed, isLooping, seekPosition, lineThickness, glowIntensity, liveDisplayMode]); + }, [audioData, micAnalyzer, liveAnalyzer, mode, drawGraticule, onPlaybackEnd, isPlaying, playbackSpeed, isLooping, lineThickness, glowIntensity, liveDisplayMode, audioElementRef]); // Initialize canvas useEffect(() => { @@ -375,7 +366,6 @@ export function OscilloscopeDisplay({ if (isPlaying && audioData) { // File playback - currentSampleRef.current = 0; animationRef.current = requestAnimationFrame(drawFrame); } else if (hasLiveSource && !audioData) { // Live audio visualization (music player, sound effects)