import { useState } from "react" import { useTranslation } from "react-i18next" import { Slider } from "@/components/ui/slider" import { cn } from "@/lib/utils" interface CompressorProps { onParameterChange?: (param: keyof CompressorSettings, value: number) => void gainReduction?: number // Current gain reduction in dB className?: string } export interface CompressorSettings { threshold: number // -60 to 0 dB ratio: number // 1:1 to 20:1 attack: number // 0 to 100 ms release: number // 0 to 1000 ms knee: number // 0 to 40 dB makeup: number // 0 to 30 dB } const DEFAULT_SETTINGS: CompressorSettings = { threshold: -24, ratio: 4, attack: 10, release: 100, knee: 2.5, makeup: 0, } export function Compressor({ onParameterChange, gainReduction = 0, className }: CompressorProps) { const { t } = useTranslation() const [settings, setSettings] = useState(DEFAULT_SETTINGS) const handleParameterChange = (param: keyof CompressorSettings, value: number) => { setSettings((prev) => ({ ...prev, [param]: value })) onParameterChange?.(param, value) } const reset = () => { setSettings(DEFAULT_SETTINGS) Object.entries(DEFAULT_SETTINGS).forEach(([param, value]) => { onParameterChange?.(param as keyof CompressorSettings, value) }) } const formatRatio = (ratio: number) => { if (ratio >= 20) return "∞:1" return `${ratio}:1` } return (

{t("fairlightAudio.effects.compressor.title")}

{/* Visual Display */}
{/* Grid */} {/* Horizontal lines (output) */} {[0, -10, -20, -30, -40, -50, -60].map((db) => { const y = ((60 + db) / 60) * 100 return ( ) })} {/* Vertical lines (input) */} {[0, -10, -20, -30, -40, -50, -60].map((db) => { const x = ((60 + db) / 60) * 100 return ( ) })} {/* Unity gain line */} {/* Compression curve */} {/* Threshold line */} {/* Gain reduction meter */}
{/* GR label */}
{t("fairlightAudio.effects.compressor.gainReduction")}
{/* Controls */}
{/* Threshold */}
{t("fairlightAudio.effects.compressor.threshold")} {settings.threshold.toFixed(1)} dB
handleParameterChange("threshold", value)} min={-60} max={0} step={0.1} className="w-full" />
{/* Ratio */}
{t("fairlightAudio.effects.compressor.ratio")} {formatRatio(settings.ratio)}
handleParameterChange("ratio", value)} min={1} max={20} step={0.5} className="w-full" />
{/* Attack */}
{t("fairlightAudio.effects.compressor.attack")} {settings.attack.toFixed(1)} ms
handleParameterChange("attack", value)} min={0} max={100} step={0.1} className="w-full" />
{/* Release */}
{t("fairlightAudio.effects.compressor.release")} {settings.release.toFixed(0)} ms
handleParameterChange("release", value)} min={0} max={1000} step={1} className="w-full" />
{/* Knee */}
{t("fairlightAudio.effects.compressor.knee")} {settings.knee.toFixed(1)} dB
handleParameterChange("knee", value)} min={0} max={40} step={0.5} className="w-full" />
{/* Makeup Gain */}
{t("fairlightAudio.effects.compressor.makeup")} {settings.makeup.toFixed(1)} dB
handleParameterChange("makeup", value)} min={0} max={30} step={0.1} className="w-full" />
{/* Presets */}
) function applyPreset(preset: string) { let newSettings: CompressorSettings switch (preset) { case "gentle": newSettings = { threshold: -20, ratio: 2, attack: 10, release: 100, knee: 10, makeup: 2, } break case "vocal": newSettings = { threshold: -15, ratio: 3, attack: 5, release: 50, knee: 5, makeup: 3, } break case "drums": newSettings = { threshold: -10, ratio: 6, attack: 1, release: 100, knee: 0, makeup: 5, } break case "master": newSettings = { threshold: -12, ratio: 2.5, attack: 30, release: 300, knee: 20, makeup: 1, } break default: newSettings = DEFAULT_SETTINGS } setSettings(newSettings) Object.entries(newSettings).forEach(([param, value]) => { onParameterChange?.(param as keyof CompressorSettings, value) }) } } function generateCompressionCurve(settings: CompressorSettings): string { const points: string[] = [] const { threshold, ratio, knee, makeup } = settings for (let inputDb = -60; inputDb <= 0; inputDb += 1) { let outputDb: number if (inputDb < threshold - knee / 2) { // Below knee - no compression outputDb = inputDb } else if (inputDb > threshold + knee / 2) { // Above knee - full compression const excess = inputDb - threshold outputDb = threshold + excess / ratio } else { // In knee - smooth transition const kneeProgress = (inputDb - (threshold - knee / 2)) / knee const compressionAmount = kneeProgress * kneeProgress const excess = inputDb - threshold outputDb = inputDb - excess * compressionAmount * (1 - 1 / ratio) } // Apply makeup gain outputDb += makeup // Convert to coordinates const x = ((60 + inputDb) / 60) * 100 const y = ((60 - outputDb) / 60) * 100 points.push(`${x},${Math.max(0, Math.min(100, y))}`) } return `M ${points.join(" L ")}` }