'use client'; import * as React from 'react'; import { cn } from '@/lib/utils/cn'; export interface FrequencyAnalyzerProps { analyserNode: AnalyserNode | null; className?: string; } export function FrequencyAnalyzer({ analyserNode, className }: FrequencyAnalyzerProps) { const canvasRef = React.useRef(null); const animationFrameRef = React.useRef(undefined); React.useEffect(() => { if (!analyserNode || !canvasRef.current) return; const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); if (!ctx) return; // Set canvas size const dpr = window.devicePixelRatio || 1; const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; ctx.scale(dpr, dpr); const bufferLength = analyserNode.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); // Get background color from computed styles const bgColor = getComputedStyle(canvas.parentElement!).backgroundColor; const draw = () => { animationFrameRef.current = requestAnimationFrame(draw); analyserNode.getByteFrequencyData(dataArray); // Clear canvas with parent background color ctx.fillStyle = bgColor; ctx.fillRect(0, 0, rect.width, rect.height); const barWidth = rect.width / bufferLength; let x = 0; for (let i = 0; i < bufferLength; i++) { const barHeight = (dataArray[i] / 255) * rect.height; // Color gradient based on frequency const hue = (i / bufferLength) * 120; // 0 (red) to 120 (green) ctx.fillStyle = `hsl(${180 + hue}, 70%, 50%)`; ctx.fillRect(x, rect.height - barHeight, barWidth, barHeight); x += barWidth; } // Draw frequency labels ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; ctx.font = '10px monospace'; ctx.textAlign = 'left'; ctx.fillText('20Hz', 5, rect.height - 5); ctx.textAlign = 'center'; ctx.fillText('1kHz', rect.width / 2, rect.height - 5); ctx.textAlign = 'right'; ctx.fillText('20kHz', rect.width - 5, rect.height - 5); }; draw(); return () => { if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); } }; }, [analyserNode]); return (
Frequency Analyzer
); }