mirror of
https://github.com/JorySeverijnse/ui-fixer-supreme.git
synced 2026-01-29 21:58:37 +00:00
Improve oscilloscope init and sync
- Ensure oscilloscope starts immediately and animates at full FPS - Sync visualization with audio element without relying on slow timeupdate - Update OscilloscopeDisplay to read from audio element directly and handle seek properly - Adjust playback integration in Oscilloscope.tsx to pass proper refs and props X-Lovable-Edit-ID: edt-073ab13c-7eb6-4c9d-b2fd-ac84088b119a
This commit is contained in:
commit
6693f94b65
@ -338,7 +338,7 @@ export function Oscilloscope() {
|
||||
isPlaying={isPlaying}
|
||||
playbackSpeed={playbackSpeed}
|
||||
isLooping={isLooping}
|
||||
seekPosition={seekPosition}
|
||||
audioElementRef={audioRef}
|
||||
onPlaybackEnd={() => {
|
||||
setIsPlaying(false);
|
||||
setCurrentTime(0);
|
||||
|
||||
@ -11,7 +11,7 @@ interface OscilloscopeDisplayProps {
|
||||
isPlaying: boolean;
|
||||
playbackSpeed: number;
|
||||
isLooping: boolean;
|
||||
seekPosition: number;
|
||||
audioElementRef?: React.RefObject<HTMLAudioElement | null>;
|
||||
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<HTMLCanvasElement>(null);
|
||||
const animationRef = useRef<number | null>(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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user