forked from averyfelts/Lofi_Generator
work in progress implementation of: - mixer with channel strips, faders, pan knobs, level meters - timeline with ruler, playhead, sections, keyframe tracks - pattern and progression pickers for drums/chords - automation lanes and mute tracks - loop bracket for loop region selection - export modal placeholder known issues: - drum pattern changes don't update audio engine - timeline keyframes not connected to scheduler - some UI bugs remain this is a checkpoint commit for further iteration
103 lines
2.4 KiB
TypeScript
103 lines
2.4 KiB
TypeScript
'use client';
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
import { VerticalFader } from './VerticalFader';
|
|
import { LevelMeter } from './LevelMeter';
|
|
import { PanKnob } from './PanKnob';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface ChannelStripProps {
|
|
name: string;
|
|
icon?: React.ReactNode;
|
|
volume: number;
|
|
pan: number;
|
|
muted: boolean;
|
|
soloed: boolean;
|
|
level: number;
|
|
onVolumeChange: (volume: number) => void;
|
|
onPanChange: (pan: number) => void;
|
|
onMuteToggle: () => void;
|
|
onSoloToggle: () => void;
|
|
showPan?: boolean;
|
|
showSolo?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
export function ChannelStrip({
|
|
name,
|
|
icon,
|
|
volume,
|
|
pan,
|
|
muted,
|
|
soloed,
|
|
level,
|
|
onVolumeChange,
|
|
onPanChange,
|
|
onMuteToggle,
|
|
onSoloToggle,
|
|
showPan = true,
|
|
showSolo = true,
|
|
className,
|
|
}: ChannelStripProps) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'flex flex-col items-center gap-1.5 p-2 rounded-lg bg-card/50 border border-border/50',
|
|
'min-w-[64px]',
|
|
className
|
|
)}
|
|
>
|
|
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
|
{icon}
|
|
<span className="font-medium">{name}</span>
|
|
</div>
|
|
|
|
{showPan && <PanKnob value={pan} onChange={onPanChange} size={24} />}
|
|
|
|
<div className="flex gap-1.5 items-end">
|
|
<div className="flex flex-col items-center gap-1">
|
|
<VerticalFader
|
|
value={volume}
|
|
onChange={onVolumeChange}
|
|
min={0}
|
|
max={1}
|
|
height={64}
|
|
/>
|
|
<span className="text-[9px] font-mono text-muted-foreground">
|
|
{Math.round(volume * 100)}
|
|
</span>
|
|
</div>
|
|
|
|
<LevelMeter level={muted ? 0 : level} className="h-16" />
|
|
</div>
|
|
|
|
<div className="flex gap-1">
|
|
{showSolo && (
|
|
<Button
|
|
variant={soloed ? 'default' : 'outline'}
|
|
size="sm"
|
|
className={cn(
|
|
'h-6 w-6 p-0 text-[10px] font-bold',
|
|
soloed && 'bg-yellow-500 hover:bg-yellow-600 text-black'
|
|
)}
|
|
onClick={onSoloToggle}
|
|
>
|
|
S
|
|
</Button>
|
|
)}
|
|
<Button
|
|
variant={muted ? 'default' : 'outline'}
|
|
size="sm"
|
|
className={cn(
|
|
'h-6 w-6 p-0 text-[10px] font-bold',
|
|
muted && 'bg-red-500 hover:bg-red-600'
|
|
)}
|
|
onClick={onMuteToggle}
|
|
>
|
|
M
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|