diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 1148b54..21e4d38 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -14,7 +14,12 @@ "Bash(git pull *)", "Bash(git stash *)", "Bash(git fetch *)", - "Bash(rtk npx *)" + "Bash(rtk npx *)", + "Bash(echo \"EXIT:$?\")", + "Bash(git -c core.editor=\"echo\" rebase --continue)", + "Bash(cat .git/rebase-merge/interactive)", + "Bash(ls .git/rebase-merge/)", + "Bash(git *)" ] } } diff --git a/frontend/src/components/AudioVisualizer.tsx b/frontend/src/components/AudioVisualizer.tsx index b575cc2..0d9345a 100644 --- a/frontend/src/components/AudioVisualizer.tsx +++ b/frontend/src/components/AudioVisualizer.tsx @@ -17,6 +17,7 @@ export default function AudioVisualizer({ height = 80 }: AudioVisualizerProps) { const playingRef = useRef(isPlaying); const getDataRef = useRef(getFrequencyData); const timeRef = useRef({ currentTime, duration }); + const hasRealDataRef = useRef(false); useEffect(() => { playingRef.current = isPlaying; }, [isPlaying]); useEffect(() => { getDataRef.current = getFrequencyData; }, [getFrequencyData]); @@ -54,21 +55,38 @@ export default function AudioVisualizer({ height = 80 }: AudioVisualizerProps) { const progress = dur > 0 ? ct / dur : 0; const playing = playingRef.current; - // Update heights from frequency data (every frame for fluidity) + // Detect if real frequency data is available (CORS allows it) if (raw && playing) { - for (let i = 0; i < NUM_POINTS; i++) { - const bucketSize = raw.length / NUM_POINTS; - const startIdx = Math.floor(i * bucketSize); - const endIdx = Math.floor((i + 1) * bucketSize); - let sum = 0; - let count = 0; - for (let j = startIdx; j < endIdx && j < raw.length; j++) { - sum += raw[j]; - count++; + const sum = raw.reduce((a, b) => a + b, 0); + hasRealDataRef.current = sum > 0; + } + + // Update heights + if (playing) { + if (raw && hasRealDataRef.current) { + // Real frequency data from Web Audio API + for (let i = 0; i < NUM_POINTS; i++) { + const bucketSize = raw.length / NUM_POINTS; + const startIdx = Math.floor(i * bucketSize); + const endIdx = Math.floor((i + 1) * bucketSize); + let sum = 0; + let count = 0; + for (let j = startIdx; j < endIdx && j < raw.length; j++) { + sum += raw[j]; + count++; + } + const avg = count > 0 ? sum / count : 0; + const target = (avg / 255) * MAX_AMPLITUDE; + hts[i] += (target - hts[i]) * 0.25; + } + } else { + // Simulated visualization for cross-origin audio (no CORS) + for (let i = 0; i < NUM_POINTS; i++) { + const wave = Math.sin(frame * 0.04 + i * 0.35) * 0.5 + 0.5; + const noise = Math.sin(frame * 0.07 + i * 1.7) * 0.3 + 0.7; + const target = MAX_AMPLITUDE * 0.6 * (0.5 + wave * noise); + hts[i] += (target - hts[i]) * 0.18; } - const avg = count > 0 ? sum / count : 0; - const target = (avg / 255) * MAX_AMPLITUDE; - hts[i] += (target - hts[i]) * 0.25; } } else if (!playing) { for (let i = 0; i < NUM_POINTS; i++) { @@ -177,4 +195,4 @@ function drawSmoothPath( ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.stroke(); -} +} \ No newline at end of file diff --git a/frontend/src/context/AudioContext.tsx b/frontend/src/context/AudioContext.tsx index a20c443..dc2d84f 100644 --- a/frontend/src/context/AudioContext.tsx +++ b/frontend/src/context/AudioContext.tsx @@ -142,7 +142,7 @@ export function AudioProvider({ children }: { children: ReactNode }) { const getFrequencyData = () => { if (!analyserRef.current || !dataArrayRef.current) return null; - analyserRef.current.getByteFrequencyData(dataArrayRef.current); + analyserRef.current.getByteFrequencyData(dataArrayRef.current as Uint8Array); return dataArrayRef.current; }; @@ -208,7 +208,6 @@ export function AudioProvider({ children }: { children: ReactNode }) { key={currentEpisode._id} ref={audioRef} src={currentEpisode.audioUrl} - crossOrigin="anonymous" onTimeUpdate={() => audioRef.current && setCurrentTime(audioRef.current.currentTime)} onLoadedMetadata={() => audioRef.current && setDuration(audioRef.current.duration)} onPlay={() => setIsPlaying(true)}