From 81735655218443091786851211e72303b14de5a2 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 14:24:15 +0000 Subject: [PATCH] Changes --- src/components/ControlPanel.tsx | 107 ++++++++++++++++++++++--- src/components/Oscilloscope.tsx | 13 ++- src/components/OscilloscopeDisplay.tsx | 76 ++++++++++++++---- 3 files changed, 168 insertions(+), 28 deletions(-) diff --git a/src/components/ControlPanel.tsx b/src/components/ControlPanel.tsx index 866ea07..ddecc4c 100755 --- a/src/components/ControlPanel.tsx +++ b/src/components/ControlPanel.tsx @@ -5,6 +5,13 @@ import { Progress } from '@/components/ui/progress'; import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import type { OscilloscopeMode } from '@/hooks/useOscilloscopeRenderer'; +export interface LiveDisplaySettings { + lineThickness: number; + showGrid: boolean; + glowIntensity: number; + displayMode: OscilloscopeMode; +} + interface ControlPanelProps { mode: OscilloscopeMode; onModeChange: (mode: OscilloscopeMode) => void; @@ -29,6 +36,8 @@ interface ControlPanelProps { onExportFormatChange: (format: string) => void; exportQuality: string; onExportQualityChange: (quality: string) => void; + liveSettings: LiveDisplaySettings; + onLiveSettingsChange: (settings: LiveDisplaySettings) => void; } export function ControlPanel({ @@ -55,36 +64,97 @@ export function ControlPanel({ onExportFormatChange, exportQuality, onExportQualityChange, + liveSettings, + onLiveSettingsChange, }: ControlPanelProps) { return (
- {/* Mode Selection */} + {/* Live Display Options */}
- + + + {/* Display Mode */} onModeChange(value as OscilloscopeMode)} + value={liveSettings.displayMode} + onValueChange={(value) => onLiveSettingsChange({ ...liveSettings, displayMode: value as OscilloscopeMode })} className="space-y-2" >
- -
- -
- -
+ + {/* Line Thickness */} +
+ Line Thickness +
+ {[1, 2, 3, 4].map((thickness) => ( + + ))} +
+
+ + {/* Show Grid */} +
+ Show Grid + +
+ + {/* Glow Intensity */} +
+ Glow +
+ {[0, 1, 2, 3].map((glow) => ( + + ))} +
+
{/* Playback Controls */} @@ -141,8 +211,23 @@ export function ControlPanel({ {/* Export Options */} -
+
+

Settings for video export only

+ + {/* Export Display Mode */} +
+ Mode + +
{/* Resolution */}
diff --git a/src/components/Oscilloscope.tsx b/src/components/Oscilloscope.tsx index 212da4f..0bf0904 100644 --- a/src/components/Oscilloscope.tsx +++ b/src/components/Oscilloscope.tsx @@ -1,6 +1,6 @@ import { useState, useCallback, useEffect, useRef } from 'react'; import { AudioUploader } from './AudioUploader'; -import { ControlPanel } from './ControlPanel'; +import { ControlPanel, LiveDisplaySettings } from './ControlPanel'; import { OscilloscopeDisplay } from './OscilloscopeDisplay'; import { useAudioAnalyzer } from '@/hooks/useAudioAnalyzer'; import { useOscilloscopeRenderer } from '@/hooks/useOscilloscopeRenderer'; @@ -11,6 +11,12 @@ import { Button } from '@/components/ui/button'; export function Oscilloscope() { const [mode, setMode] = useState<'combined' | 'separate' | 'all'>('combined'); + const [liveSettings, setLiveSettings] = useState({ + lineThickness: 2, + showGrid: true, + glowIntensity: 1, + displayMode: 'combined', + }); const [isMicActive, setIsMicActive] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [currentSample, setCurrentSample] = useState(0); @@ -249,6 +255,8 @@ export function Oscilloscope() { onExportFormatChange={handleExportFormatChange} exportQuality={exportQuality} onExportQualityChange={handleExportQualityChange} + liveSettings={liveSettings} + onLiveSettingsChange={setLiveSettings} />
@@ -257,13 +265,14 @@ export function Oscilloscope() { setIsPlaying(false)} onSeek={handleSeek} + liveSettings={liveSettings} />
diff --git a/src/components/OscilloscopeDisplay.tsx b/src/components/OscilloscopeDisplay.tsx index 92323ea..97e3506 100755 --- a/src/components/OscilloscopeDisplay.tsx +++ b/src/components/OscilloscopeDisplay.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef, useCallback } from 'react'; import type { AudioData } from '@/hooks/useAudioAnalyzer'; import type { OscilloscopeMode } from '@/hooks/useOscilloscopeRenderer'; import { useAudioAnalyzer as useSharedAudioAnalyzer } from '@/contexts/AudioAnalyzerContext'; +import type { LiveDisplaySettings } from './ControlPanel'; interface OscilloscopeDisplayProps { audioData: AudioData | null; @@ -13,12 +14,24 @@ interface OscilloscopeDisplayProps { seekPosition: number; onPlaybackEnd?: () => void; onSeek?: (position: number) => void; + liveSettings?: LiveDisplaySettings; } const WIDTH = 800; const HEIGHT = 600; const FPS = 60; +// Get computed CSS color from theme +const getThemeColor = (cssVar: string, fallback: string): string => { + if (typeof window === 'undefined') return fallback; + const root = document.documentElement; + const value = getComputedStyle(root).getPropertyValue(cssVar).trim(); + if (value) { + return `hsl(${value})`; + } + return fallback; +}; + export function OscilloscopeDisplay({ audioData, micAnalyzer, @@ -28,7 +41,8 @@ export function OscilloscopeDisplay({ isLooping, seekPosition, onPlaybackEnd, - onSeek + onSeek, + liveSettings }: OscilloscopeDisplayProps) { const canvasRef = useRef(null); const animationRef = useRef(null); @@ -39,8 +53,17 @@ export function OscilloscopeDisplay({ // Use shared analyzer for live audio (music player, sound effects) const liveAnalyzer = sharedAnalyzer || micAnalyzer; + // Get settings with defaults + const lineThickness = liveSettings?.lineThickness ?? 2; + const showGrid = liveSettings?.showGrid ?? true; + const glowIntensity = liveSettings?.glowIntensity ?? 1; + const drawGraticule = useCallback((ctx: CanvasRenderingContext2D) => { - ctx.strokeStyle = '#00ff00'; + if (!showGrid) return; + + const primaryColor = getThemeColor('--primary', '#00ff00'); + ctx.strokeStyle = primaryColor; + ctx.globalAlpha = 0.3; ctx.lineWidth = 1; // Horizontal center line (X axis) @@ -54,7 +77,9 @@ export function OscilloscopeDisplay({ ctx.moveTo(WIDTH / 2, 0); ctx.lineTo(WIDTH / 2, HEIGHT); ctx.stroke(); - }, []); + + ctx.globalAlpha = 1; + }, [showGrid]); const drawFrame = useCallback(() => { if (!canvasRef.current) return; @@ -67,6 +92,9 @@ export function OscilloscopeDisplay({ const ctx = canvas.getContext('2d'); if (!ctx) return; + const primaryColor = getThemeColor('--primary', '#00ff00'); + const backgroundColor = getThemeColor('--background', '#000000'); + let samplesPerFrame: number; let startSample: number; let endSample: number; @@ -81,8 +109,8 @@ export function OscilloscopeDisplay({ const dataArray = new Uint8Array(bufferLength); activeAnalyzer.getByteTimeDomainData(dataArray); - // Clear to pure black - ctx.fillStyle = '#000000'; + // Clear to background color + ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, WIDTH, HEIGHT); // Draw graticule first @@ -98,9 +126,17 @@ export function OscilloscopeDisplay({ startSample = 0; endSample = liveData.length; + // Apply glow effect + if (glowIntensity > 0) { + ctx.shadowColor = primaryColor; + ctx.shadowBlur = glowIntensity * 8; + } else { + ctx.shadowBlur = 0; + } + // Draw live data directly - ctx.strokeStyle = '#00ff00'; - ctx.lineWidth = 2; + ctx.strokeStyle = primaryColor; + ctx.lineWidth = lineThickness; ctx.beginPath(); const sliceWidth = WIDTH / samplesPerFrame; @@ -120,6 +156,7 @@ export function OscilloscopeDisplay({ } ctx.stroke(); + ctx.shadowBlur = 0; // Request next frame for real-time animationRef.current = requestAnimationFrame(drawFrame); @@ -149,20 +186,28 @@ export function OscilloscopeDisplay({ endSample = Math.min(startSample + samplesPerFrame, audioData.leftChannel.length); - // Clear to pure black - ctx.fillStyle = '#000000'; + // Clear to background color + ctx.fillStyle = backgroundColor; ctx.fillRect(0, 0, WIDTH, HEIGHT); // Draw graticule first drawGraticule(ctx); - ctx.lineWidth = 2; + // Apply glow effect + if (glowIntensity > 0) { + ctx.shadowColor = primaryColor; + ctx.shadowBlur = glowIntensity * 8; + } else { + ctx.shadowBlur = 0; + } + + ctx.lineWidth = lineThickness; ctx.lineCap = 'round'; - const leftColor = '#00ff00'; - const rightColor = '#00ccff'; - const xyColor = '#ff8800'; - const dividerColor = '#333333'; + const leftColor = primaryColor; + const rightColor = getThemeColor('--accent', '#00ccff'); + const xyColor = getThemeColor('--secondary', '#ff8800'); + const dividerColor = 'rgba(255,255,255,0.1)'; if (mode === 'combined') { // Combined: both channels merged @@ -280,6 +325,7 @@ export function OscilloscopeDisplay({ } currentSampleRef.current = endSample; + ctx.shadowBlur = 0; if (endSample >= audioData.leftChannel.length) { if (isLooping) { @@ -291,7 +337,7 @@ export function OscilloscopeDisplay({ } animationRef.current = requestAnimationFrame(drawFrame); - }, [audioData, micAnalyzer, liveAnalyzer, mode, drawGraticule, onPlaybackEnd, isPlaying, playbackSpeed, isLooping, seekPosition]); + }, [audioData, micAnalyzer, liveAnalyzer, mode, drawGraticule, onPlaybackEnd, isPlaying, playbackSpeed, isLooping, seekPosition, lineThickness, glowIntensity]); // Initialize canvas useEffect(() => {