veza/apps/web/src/features/player/components/AudioSettingsPanel.tsx

91 lines
3.4 KiB
TypeScript
Raw Normal View History

/**
* AudioSettingsPanel v0.13.1 TASK-AUDIO-002 + TASK-AUDIO-003
*
* Configurable crossfade (0-12s) and audio normalization toggle.
* Appears in the expanded player and settings.
*/
import { usePlayerStore } from '../store/playerStore';
import { cn } from '@/lib/utils';
import { Slider } from '@/components/ui/slider';
import { Tooltip } from '@/components/ui/tooltip';
import { Volume2, AudioWaveform } from 'lucide-react';
interface AudioSettingsPanelProps {
className?: string;
compact?: boolean;
}
export function AudioSettingsPanel({ className, compact = false }: AudioSettingsPanelProps) {
const crossfadeSeconds = usePlayerStore((s) => s.crossfadeSeconds);
const setCrossfadeSeconds = usePlayerStore((s) => s.setCrossfadeSeconds);
const normalizationEnabled = usePlayerStore((s) => s.normalizationEnabled);
const setNormalizationEnabled = usePlayerStore((s) => s.setNormalizationEnabled);
return (
<div className={cn('flex flex-col gap-4', className)}>
{/* Crossfade control */}
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<label className="flex items-center gap-2 text-sm font-medium text-foreground">
<AudioWaveform className="w-4 h-4 text-muted-foreground" aria-hidden="true" />
Crossfade
</label>
<span className="text-xs text-muted-foreground font-mono">
{crossfadeSeconds === 0 ? 'Off' : `${crossfadeSeconds}s`}
</span>
</div>
<Slider
value={[crossfadeSeconds]}
onValueChange={(val) => setCrossfadeSeconds(val[0] ?? 0)}
min={0}
max={12}
step={1}
aria-label="Crossfade duration"
className={compact ? 'w-32' : undefined}
/>
{!compact && (
<p className="text-xs text-muted-foreground">
Smooth transition between tracks. Set to 0 for gapless playback.
</p>
)}
</div>
{/* Normalization toggle */}
<div className="flex items-center justify-between">
<label
htmlFor="normalization-toggle"
className="flex items-center gap-2 text-sm font-medium text-foreground cursor-pointer"
>
<Volume2 className="w-4 h-4 text-muted-foreground" aria-hidden="true" />
Volume normalization
</label>
<Tooltip content="Equalizes volume levels between tracks to prevent sudden jumps">
<button
id="normalization-toggle"
role="switch"
aria-checked={normalizationEnabled}
onClick={() => setNormalizationEnabled(!normalizationEnabled)}
className={cn(
'relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-background',
normalizationEnabled ? 'bg-primary' : 'bg-muted',
)}
>
<span
className={cn(
'inline-block h-4 w-4 transform rounded-full bg-white transition-transform',
normalizationEnabled ? 'translate-x-6' : 'translate-x-1',
)}
/>
</button>
</Tooltip>
</div>
{!compact && (
<p className="text-xs text-muted-foreground -mt-2">
Adjusts volume to consistent levels across all tracks (EBU R128).
</p>
)}
</div>
);
}