import React, { useCallback, useState } from "react" import { useTranslation } from "react-i18next" import { cn } from "@/lib/utils" // Surround sound formats export type SurroundFormat = "stereo" | "5.1" | "7.1" // Speaker positions for different formats const SPEAKER_POSITIONS = { stereo: [ { id: "L", labelKey: "left", x: 30, y: 50, angle: -30 }, { id: "R", labelKey: "right", x: 70, y: 50, angle: 30 }, ], "5.1": [ { id: "L", labelKey: "left", x: 20, y: 60, angle: -30 }, { id: "R", labelKey: "right", x: 80, y: 60, angle: 30 }, { id: "C", labelKey: "center", x: 50, y: 20, angle: 0 }, { id: "LFE", labelKey: "lfe", x: 50, y: 85, angle: 0 }, { id: "LS", labelKey: "leftSurround", x: 15, y: 80, angle: -110 }, { id: "RS", labelKey: "rightSurround", x: 85, y: 80, angle: 110 }, ], "7.1": [ { id: "L", labelKey: "left", x: 20, y: 60, angle: -30 }, { id: "R", labelKey: "right", x: 80, y: 60, angle: 30 }, { id: "C", labelKey: "center", x: 50, y: 20, angle: 0 }, { id: "LFE", labelKey: "lfe", x: 50, y: 85, angle: 0 }, { id: "LS", labelKey: "leftSurround", x: 10, y: 75, angle: -110 }, { id: "RS", labelKey: "rightSurround", x: 90, y: 75, angle: 110 }, { id: "LR", labelKey: "leftRear", x: 25, y: 90, angle: -135 }, { id: "RR", labelKey: "rightRear", x: 75, y: 90, angle: 135 }, ], } interface SurroundPannerProps { format: SurroundFormat position: { x: number; y: number } // Position in the surround field (0-100) onPositionChange: (position: { x: number; y: number }) => void className?: string } export function SurroundPanner({ format, position, onPositionChange, className }: SurroundPannerProps) { const { t } = useTranslation() const [isDragging, setIsDragging] = useState(false) const speakers = SPEAKER_POSITIONS[format] const handleMouseDown = useCallback((e: React.MouseEvent) => { setIsDragging(true) e.preventDefault() }, []) const handleMouseMove = useCallback( (e: React.MouseEvent) => { if (!isDragging) return const rect = e.currentTarget.getBoundingClientRect() const x = ((e.clientX - rect.left) / rect.width) * 100 const y = ((e.clientY - rect.top) / rect.height) * 100 // Clamp values to valid range const clampedX = Math.max(0, Math.min(100, x)) const clampedY = Math.max(0, Math.min(100, y)) onPositionChange({ x: clampedX, y: clampedY }) }, [isDragging, onPositionChange], ) const handleMouseUp = useCallback(() => { setIsDragging(false) }, []) // Calculate distances from position to each speaker for visual feedback const calculateDistance = (speakerX: number, speakerY: number) => { const dx = position.x - speakerX const dy = position.y - speakerY return Math.sqrt(dx * dx + dy * dy) } return (
{/* Format selector */}
{t(`fairlightAudio.surround.panner.${format}`)}
{t("fairlightAudio.surround.panner.position", { x: position.x.toFixed(0), y: position.y.toFixed(0) })}
{/* Panning field */}
{/* Speaker positions */} {speakers.map((speaker) => { const distance = calculateDistance(speaker.x, speaker.y) const intensity = Math.max(0, 1 - distance / 100) // Closer = more intense return (
{/* Speaker indicator */}
0.5 ? "bg-primary border-primary" : intensity > 0.2 ? "bg-primary/50 border-primary" : "bg-muted border-muted-foreground", )} /> {/* Speaker label */}
{t(`fairlightAudio.surround.speakers.${speaker.labelKey}`)}
) })} {/* Audio source position */}
{/* Position indicator */}
{t("fairlightAudio.surround.panner.source")}
{/* Center reference */}
{/* Distance rings for reference */}
{/* Channel levels indicator */}
{speakers.map((speaker) => { const distance = calculateDistance(speaker.x, speaker.y) const level = Math.max(0, Math.min(1, 1 - distance / 100)) const dbLevel = level > 0 ? 20 * Math.log10(level) : -60 return (
{t(`fairlightAudio.surround.channels.${speaker.id}`)}:
{dbLevel > -60 ? `${dbLevel.toFixed(0)}dB` : "-∞"}
) })}
) }