import { useCallback, useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" import { cn } from "@/lib/utils" import { useAutomation } from "../../hooks/use-automation" interface FaderProps { value: number // 0-100 onChange: (value: number) => void label?: string muted?: boolean solo?: boolean onMute?: () => void onSolo?: () => void dbScale?: boolean // Show dB scale instead of percentage className?: string channelId?: string // For automation parameterId?: string // For automation } export function Fader({ value, onChange, label, muted = false, solo = false, onMute, onSolo, dbScale = true, className, channelId, parameterId = "volume", }: FaderProps) { const { t } = useTranslation() const [isDragging, setIsDragging] = useState(false) const faderRef = useRef(null) const { writeParameter, touchParameter, releaseParameter } = useAutomation() // Convert percentage to dB (0% = -∞ dB, 100% = 0 dB) const percentToDb = (percent: number): string => { if (percent === 0) return "-∞" const db = 20 * Math.log10(percent / 100) return db.toFixed(1) } const handleMouseDown = (e: React.MouseEvent) => { setIsDragging(true) updateValue(e) // Touch parameter for automation if (channelId && parameterId) { touchParameter(channelId, parameterId) } } const handleMouseMove = useCallback( (e: MouseEvent) => { if (!isDragging || !faderRef.current) return const rect = faderRef.current.getBoundingClientRect() const y = e.clientY - rect.top const height = rect.height const newValue = Math.max(0, Math.min(100, (1 - y / height) * 100)) onChange(newValue) // Write automation data if (channelId && parameterId) { writeParameter(channelId, parameterId, newValue / 100) } }, [isDragging, onChange, channelId, parameterId, writeParameter], ) const handleMouseUp = useCallback(() => { setIsDragging(false) // Release parameter for automation if (channelId && parameterId) { releaseParameter(channelId, parameterId) } }, [channelId, parameterId, releaseParameter]) const updateValue = (e: React.MouseEvent) => { if (!faderRef.current) return const rect = faderRef.current.getBoundingClientRect() const y = e.clientY - rect.top const height = rect.height const newValue = Math.max(0, Math.min(100, (1 - y / height) * 100)) onChange(newValue) // Write automation data on initial touch if (channelId && parameterId) { writeParameter(channelId, parameterId, newValue / 100) } } // Add global mouse listeners when dragging useEffect(() => { if (isDragging) { window.addEventListener("mousemove", handleMouseMove) window.addEventListener("mouseup", handleMouseUp) return () => { window.removeEventListener("mousemove", handleMouseMove) window.removeEventListener("mouseup", handleMouseUp) } } }, [isDragging, handleMouseMove, handleMouseUp]) return (
{/* Solo/Mute buttons */}
{/* Fader track */}
{/* Background track */}
{/* Value track */}
{/* Fader handle */}
{/* dB scale marks */} {dbScale && (
{t("fairlightAudio.mixer.fader.dbMarkers.zero")}
{t("fairlightAudio.mixer.fader.dbMarkers.minus6")}
{t("fairlightAudio.mixer.fader.dbMarkers.minus12")}
{t("fairlightAudio.mixer.fader.dbMarkers.minus24")}
{t("fairlightAudio.mixer.fader.dbMarkers.infinity")}
)}
{/* Current value */}
{dbScale ? `${percentToDb(value)} dB` : `${value}%`}
{/* Label */} {label &&
{label}
}
) }