mirror of
https://github.com/JorySeverijnse/ui-fixer-supreme.git
synced 2026-01-29 19:48:38 +00:00
Fix oscilloscope init and mode
- Ensure oscilloscope renders immediately by initializing audio analyzer context on load - Simplify live display mode to support only Combined and All/XY, and wire display mode to live visualization - Adjust panels to reflect new mode options and ensure live data switches render correctly - Update OscilloscopeDisplay to honor live display mode changes and use shared audio analyzer - Minor tweaks to MiniOscilloscope for consistent visualization and navigation behavior X-Lovable-Edit-ID: edt-33738654-f505-4a6a-9854-8683f1b4efdb
This commit is contained in:
commit
53f2dc6e96
@ -82,19 +82,13 @@ export function ControlPanel({
|
|||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<RadioGroupItem value="combined" id="live-combined" className="border-primary" />
|
<RadioGroupItem value="combined" id="live-combined" className="border-primary" />
|
||||||
<Label htmlFor="live-combined" className="font-mono-crt text-sm cursor-pointer">
|
<Label htmlFor="live-combined" className="font-mono-crt text-sm cursor-pointer">
|
||||||
Combined (L+R merged)
|
Combined (L+R waveform)
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<RadioGroupItem value="separate" id="live-separate" className="border-primary" />
|
<RadioGroupItem value="all" id="live-xy" className="border-primary" />
|
||||||
<Label htmlFor="live-separate" className="font-mono-crt text-sm cursor-pointer">
|
<Label htmlFor="live-xy" className="font-mono-crt text-sm cursor-pointer">
|
||||||
Separate (L/R stacked)
|
XY Mode (Lissajous)
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-3">
|
|
||||||
<RadioGroupItem value="all" id="live-all" className="border-primary" />
|
|
||||||
<Label htmlFor="live-all" className="font-mono-crt text-sm cursor-pointer">
|
|
||||||
All (L/R + XY below)
|
|
||||||
</Label>
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|||||||
@ -57,6 +57,7 @@ export function OscilloscopeDisplay({
|
|||||||
const lineThickness = liveSettings?.lineThickness ?? 2;
|
const lineThickness = liveSettings?.lineThickness ?? 2;
|
||||||
const showGrid = liveSettings?.showGrid ?? true;
|
const showGrid = liveSettings?.showGrid ?? true;
|
||||||
const glowIntensity = liveSettings?.glowIntensity ?? 1;
|
const glowIntensity = liveSettings?.glowIntensity ?? 1;
|
||||||
|
const liveDisplayMode = liveSettings?.displayMode ?? 'combined';
|
||||||
|
|
||||||
const drawGraticule = useCallback((ctx: CanvasRenderingContext2D) => {
|
const drawGraticule = useCallback((ctx: CanvasRenderingContext2D) => {
|
||||||
if (!showGrid) return;
|
if (!showGrid) return;
|
||||||
@ -122,10 +123,6 @@ export function OscilloscopeDisplay({
|
|||||||
liveData[i] = (dataArray[i] - 128) / 128; // Normalize to -1 to 1
|
liveData[i] = (dataArray[i] - 128) / 128; // Normalize to -1 to 1
|
||||||
}
|
}
|
||||||
|
|
||||||
samplesPerFrame = liveData.length;
|
|
||||||
startSample = 0;
|
|
||||||
endSample = liveData.length;
|
|
||||||
|
|
||||||
// Apply glow effect
|
// Apply glow effect
|
||||||
if (glowIntensity > 0) {
|
if (glowIntensity > 0) {
|
||||||
ctx.shadowColor = primaryColor;
|
ctx.shadowColor = primaryColor;
|
||||||
@ -134,28 +131,49 @@ export function OscilloscopeDisplay({
|
|||||||
ctx.shadowBlur = 0;
|
ctx.shadowBlur = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw live data directly
|
|
||||||
ctx.strokeStyle = primaryColor;
|
ctx.strokeStyle = primaryColor;
|
||||||
ctx.lineWidth = lineThickness;
|
ctx.lineWidth = lineThickness;
|
||||||
ctx.beginPath();
|
|
||||||
|
|
||||||
const sliceWidth = WIDTH / samplesPerFrame;
|
// Draw based on live display mode
|
||||||
let x = 0;
|
if (liveDisplayMode === 'all') {
|
||||||
|
// XY / Lissajous mode - treat odd/even samples as L/R
|
||||||
|
ctx.beginPath();
|
||||||
|
const centerX = WIDTH / 2;
|
||||||
|
const centerY = HEIGHT / 2;
|
||||||
|
const scale = Math.min(WIDTH, HEIGHT) * 0.4;
|
||||||
|
|
||||||
for (let i = 0; i < samplesPerFrame; i++) {
|
for (let i = 0; i < liveData.length - 1; i += 2) {
|
||||||
const v = liveData[i];
|
const x = centerX + liveData[i] * scale;
|
||||||
const y = (v * HEIGHT) / 2 + HEIGHT / 2;
|
const y = centerY - liveData[i + 1] * scale;
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
ctx.moveTo(x, y);
|
ctx.moveTo(x, y);
|
||||||
} else {
|
} else {
|
||||||
ctx.lineTo(x, y);
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
} else {
|
||||||
|
// Combined waveform mode (default)
|
||||||
|
ctx.beginPath();
|
||||||
|
const sliceWidth = WIDTH / liveData.length;
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
x += sliceWidth;
|
for (let i = 0; i < liveData.length; i++) {
|
||||||
|
const v = liveData[i];
|
||||||
|
const y = (v * HEIGHT) / 2 + HEIGHT / 2;
|
||||||
|
|
||||||
|
if (i === 0) {
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
} else {
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
x += sliceWidth;
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.stroke();
|
|
||||||
ctx.shadowBlur = 0;
|
ctx.shadowBlur = 0;
|
||||||
|
|
||||||
// Request next frame for real-time
|
// Request next frame for real-time
|
||||||
@ -337,7 +355,7 @@ export function OscilloscopeDisplay({
|
|||||||
}
|
}
|
||||||
|
|
||||||
animationRef.current = requestAnimationFrame(drawFrame);
|
animationRef.current = requestAnimationFrame(drawFrame);
|
||||||
}, [audioData, micAnalyzer, liveAnalyzer, mode, drawGraticule, onPlaybackEnd, isPlaying, playbackSpeed, isLooping, seekPosition, lineThickness, glowIntensity]);
|
}, [audioData, micAnalyzer, liveAnalyzer, mode, drawGraticule, onPlaybackEnd, isPlaying, playbackSpeed, isLooping, seekPosition, lineThickness, glowIntensity, liveDisplayMode]);
|
||||||
|
|
||||||
// Initialize canvas
|
// Initialize canvas
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -16,8 +16,9 @@ export const AudioAnalyzerProvider = ({ children }: { children: ReactNode }) =>
|
|||||||
const analyzerRef = useRef<AnalyserNode | null>(null);
|
const analyzerRef = useRef<AnalyserNode | null>(null);
|
||||||
const sourceMapRef = useRef<Map<HTMLAudioElement, MediaElementAudioSourceNode>>(new Map());
|
const sourceMapRef = useRef<Map<HTMLAudioElement, MediaElementAudioSourceNode>>(new Map());
|
||||||
const [isReady, setIsReady] = useState(false);
|
const [isReady, setIsReady] = useState(false);
|
||||||
|
const [, forceUpdate] = useState(0);
|
||||||
|
|
||||||
// Initialize audio context lazily on first user interaction
|
// Initialize audio context - call immediately but handle suspended state
|
||||||
const initAudioContext = useCallback(() => {
|
const initAudioContext = useCallback(() => {
|
||||||
if (audioContextRef.current) return audioContextRef.current;
|
if (audioContextRef.current) return audioContextRef.current;
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ export const AudioAnalyzerProvider = ({ children }: { children: ReactNode }) =>
|
|||||||
analyzerRef.current = analyzer;
|
analyzerRef.current = analyzer;
|
||||||
|
|
||||||
setIsReady(true);
|
setIsReady(true);
|
||||||
|
forceUpdate(n => n + 1); // Force re-render to update context value
|
||||||
return ctx;
|
return ctx;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to create AudioContext:', e);
|
console.error('Failed to create AudioContext:', e);
|
||||||
@ -40,6 +42,11 @@ export const AudioAnalyzerProvider = ({ children }: { children: ReactNode }) =>
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Initialize immediately on mount
|
||||||
|
useEffect(() => {
|
||||||
|
initAudioContext();
|
||||||
|
}, [initAudioContext]);
|
||||||
|
|
||||||
// Connect an audio element to the analyzer
|
// Connect an audio element to the analyzer
|
||||||
const connectAudioElement = useCallback((element: HTMLAudioElement) => {
|
const connectAudioElement = useCallback((element: HTMLAudioElement) => {
|
||||||
const ctx = initAudioContext();
|
const ctx = initAudioContext();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user