mirror of
https://github.com/JorySeverijnse/ui-fixer-supreme.git
synced 2026-01-29 18:08:38 +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}
|
isPlaying={isPlaying}
|
||||||
playbackSpeed={playbackSpeed}
|
playbackSpeed={playbackSpeed}
|
||||||
isLooping={isLooping}
|
isLooping={isLooping}
|
||||||
seekPosition={seekPosition}
|
audioElementRef={audioRef}
|
||||||
onPlaybackEnd={() => {
|
onPlaybackEnd={() => {
|
||||||
setIsPlaying(false);
|
setIsPlaying(false);
|
||||||
setCurrentTime(0);
|
setCurrentTime(0);
|
||||||
|
|||||||
@ -11,7 +11,7 @@ interface OscilloscopeDisplayProps {
|
|||||||
isPlaying: boolean;
|
isPlaying: boolean;
|
||||||
playbackSpeed: number;
|
playbackSpeed: number;
|
||||||
isLooping: boolean;
|
isLooping: boolean;
|
||||||
seekPosition: number;
|
audioElementRef?: React.RefObject<HTMLAudioElement | null>;
|
||||||
onPlaybackEnd?: () => void;
|
onPlaybackEnd?: () => void;
|
||||||
onSeek?: (position: number) => void;
|
onSeek?: (position: number) => void;
|
||||||
liveSettings?: LiveDisplaySettings;
|
liveSettings?: LiveDisplaySettings;
|
||||||
@ -39,15 +39,13 @@ export function OscilloscopeDisplay({
|
|||||||
isPlaying,
|
isPlaying,
|
||||||
playbackSpeed,
|
playbackSpeed,
|
||||||
isLooping,
|
isLooping,
|
||||||
seekPosition,
|
audioElementRef,
|
||||||
onPlaybackEnd,
|
onPlaybackEnd,
|
||||||
onSeek,
|
onSeek,
|
||||||
liveSettings
|
liveSettings
|
||||||
}: OscilloscopeDisplayProps) {
|
}: OscilloscopeDisplayProps) {
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const animationRef = useRef<number | null>(null);
|
const animationRef = useRef<number | null>(null);
|
||||||
const currentSampleRef = useRef(0);
|
|
||||||
const lastSeekPositionRef = useRef(0);
|
|
||||||
const { analyzerNode: sharedAnalyzer } = useSharedAudioAnalyzer();
|
const { analyzerNode: sharedAnalyzer } = useSharedAudioAnalyzer();
|
||||||
|
|
||||||
// Use shared analyzer for live audio (music player, sound effects)
|
// Use shared analyzer for live audio (music player, sound effects)
|
||||||
@ -96,10 +94,9 @@ export function OscilloscopeDisplay({
|
|||||||
const primaryColor = getThemeColor('--primary', '#00ff00');
|
const primaryColor = getThemeColor('--primary', '#00ff00');
|
||||||
const backgroundColor = getThemeColor('--background', '#000000');
|
const backgroundColor = getThemeColor('--background', '#000000');
|
||||||
|
|
||||||
let samplesPerFrame: number;
|
let samplesPerFrame: number = 0;
|
||||||
let startSample: number;
|
let endSample: number = 0;
|
||||||
let endSample: number;
|
let samplesToAdvance: number = 0;
|
||||||
let samplesToAdvance: number = samplesPerFrame;
|
|
||||||
|
|
||||||
// Priority: micAnalyzer > liveAnalyzer (shared) > audioData (file)
|
// Priority: micAnalyzer > liveAnalyzer (shared) > audioData (file)
|
||||||
const activeAnalyzer = micAnalyzer || liveAnalyzer;
|
const activeAnalyzer = micAnalyzer || liveAnalyzer;
|
||||||
@ -180,26 +177,22 @@ export function OscilloscopeDisplay({
|
|||||||
animationRef.current = requestAnimationFrame(drawFrame);
|
animationRef.current = requestAnimationFrame(drawFrame);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// File playback mode - need audioData
|
// File playback mode - need audioData
|
||||||
if (!audioData) return;
|
if (!audioData) return;
|
||||||
|
|
||||||
// File playback mode
|
// File playback mode - sync with audio element if available
|
||||||
const baseSamplesPerFrame = Math.floor(audioData.sampleRate / FPS);
|
const baseSamplesPerFrame = Math.floor(audioData.sampleRate / FPS);
|
||||||
samplesPerFrame = Math.floor(baseSamplesPerFrame * playbackSpeed);
|
samplesPerFrame = Math.floor(baseSamplesPerFrame * playbackSpeed);
|
||||||
samplesToAdvance = samplesPerFrame;
|
samplesToAdvance = samplesPerFrame;
|
||||||
|
|
||||||
// Handle seeking
|
// Get current position from audio element (real-time sync at 60fps)
|
||||||
if (seekPosition > 0 && seekPosition !== lastSeekPositionRef.current) {
|
let startSample: number;
|
||||||
startSample = Math.floor(seekPosition * audioData.leftChannel.length);
|
if (audioElementRef?.current && !audioElementRef.current.paused) {
|
||||||
currentSampleRef.current = startSample;
|
const currentTime = audioElementRef.current.currentTime;
|
||||||
lastSeekPositionRef.current = seekPosition;
|
startSample = Math.floor((currentTime / audioData.duration) * audioData.leftChannel.length);
|
||||||
// Reset after one frame
|
|
||||||
setTimeout(() => {
|
|
||||||
lastSeekPositionRef.current = 0;
|
|
||||||
}, 1000 / FPS);
|
|
||||||
} else {
|
} else {
|
||||||
startSample = currentSampleRef.current;
|
// Fallback: just show first frame when paused
|
||||||
|
startSample = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
endSample = Math.min(startSample + samplesPerFrame, audioData.leftChannel.length);
|
endSample = Math.min(startSample + samplesPerFrame, audioData.leftChannel.length);
|
||||||
@ -342,20 +335,18 @@ export function OscilloscopeDisplay({
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSampleRef.current = endSample;
|
|
||||||
ctx.shadowBlur = 0;
|
ctx.shadowBlur = 0;
|
||||||
|
|
||||||
if (endSample >= audioData.leftChannel.length) {
|
// Check if audio ended (when syncing to audio element)
|
||||||
if (isLooping) {
|
if (audioElementRef?.current) {
|
||||||
currentSampleRef.current = 0; // Loop back to start
|
if (audioElementRef.current.ended && !isLooping) {
|
||||||
} else {
|
|
||||||
onPlaybackEnd?.();
|
onPlaybackEnd?.();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
animationRef.current = requestAnimationFrame(drawFrame);
|
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
|
// Initialize canvas
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -375,7 +366,6 @@ export function OscilloscopeDisplay({
|
|||||||
|
|
||||||
if (isPlaying && audioData) {
|
if (isPlaying && audioData) {
|
||||||
// File playback
|
// File playback
|
||||||
currentSampleRef.current = 0;
|
|
||||||
animationRef.current = requestAnimationFrame(drawFrame);
|
animationRef.current = requestAnimationFrame(drawFrame);
|
||||||
} else if (hasLiveSource && !audioData) {
|
} else if (hasLiveSource && !audioData) {
|
||||||
// Live audio visualization (music player, sound effects)
|
// Live audio visualization (music player, sound effects)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user