diff --git a/components/editor/AudioEditor.tsx b/components/editor/AudioEditor.tsx
index c14c3dd..bf5c273 100644
--- a/components/editor/AudioEditor.tsx
+++ b/components/editor/AudioEditor.tsx
@@ -44,17 +44,17 @@ export function AudioEditor() {
togglePlayPause,
} = useMultiTrackPlayer(tracks, masterVolume);
- // Effect chain (for selected track)
+ // Master effect chain
const {
- chain: effectChain,
- presets: effectPresets,
- toggleEffectEnabled,
- removeEffect,
- reorder: reorderEffects,
- clearChain,
- savePreset,
- loadPresetToChain,
- deletePreset,
+ chain: masterEffectChain,
+ presets: masterEffectPresets,
+ toggleEffectEnabled: toggleMasterEffect,
+ removeEffect: removeMasterEffect,
+ reorder: reorderMasterEffects,
+ clearChain: clearMasterChain,
+ savePreset: saveMasterPreset,
+ loadPresetToChain: loadMasterPreset,
+ deletePreset: deleteMasterPreset,
} = useEffectChain();
// Multi-track handlers
@@ -84,6 +84,48 @@ export function AudioEditor() {
}
};
+ // Per-track effect chain handlers
+ const handleToggleTrackEffect = (effectId: string) => {
+ if (!selectedTrack) return;
+ const updatedChain = {
+ ...selectedTrack.effectChain,
+ effects: selectedTrack.effectChain.effects.map((e) =>
+ e.id === effectId ? { ...e, enabled: !e.enabled } : e
+ ),
+ };
+ updateTrack(selectedTrack.id, { effectChain: updatedChain });
+ };
+
+ const handleRemoveTrackEffect = (effectId: string) => {
+ if (!selectedTrack) return;
+ const updatedChain = {
+ ...selectedTrack.effectChain,
+ effects: selectedTrack.effectChain.effects.filter((e) => e.id !== effectId),
+ };
+ updateTrack(selectedTrack.id, { effectChain: updatedChain });
+ };
+
+ const handleReorderTrackEffects = (fromIndex: number, toIndex: number) => {
+ if (!selectedTrack) return;
+ const effects = [...selectedTrack.effectChain.effects];
+ const [removed] = effects.splice(fromIndex, 1);
+ effects.splice(toIndex, 0, removed);
+ const updatedChain = {
+ ...selectedTrack.effectChain,
+ effects,
+ };
+ updateTrack(selectedTrack.id, { effectChain: updatedChain });
+ };
+
+ const handleClearTrackChain = () => {
+ if (!selectedTrack) return;
+ const updatedChain = {
+ ...selectedTrack.effectChain,
+ effects: [],
+ };
+ updateTrack(selectedTrack.id, { effectChain: updatedChain });
+ };
+
// Zoom controls
const handleZoomIn = () => {
setZoom((prev) => Math.min(20, prev + 1));
@@ -254,15 +296,20 @@ export function AudioEditor() {
onUpdateTrack={updateTrack}
onRemoveTrack={handleRemoveTrack}
onClearTracks={handleClearTracks}
- effectChain={effectChain}
- effectPresets={effectPresets}
- onToggleEffect={toggleEffectEnabled}
- onRemoveEffect={removeEffect}
- onReorderEffects={reorderEffects}
- onSavePreset={savePreset}
- onLoadPreset={loadPresetToChain}
- onDeletePreset={deletePreset}
- onClearChain={clearChain}
+ trackEffectChain={selectedTrack?.effectChain ?? null}
+ onToggleTrackEffect={handleToggleTrackEffect}
+ onRemoveTrackEffect={handleRemoveTrackEffect}
+ onReorderTrackEffects={handleReorderTrackEffects}
+ onClearTrackChain={handleClearTrackChain}
+ masterEffectChain={masterEffectChain}
+ masterEffectPresets={masterEffectPresets}
+ onToggleMasterEffect={toggleMasterEffect}
+ onRemoveMasterEffect={removeMasterEffect}
+ onReorderMasterEffects={reorderMasterEffects}
+ onSaveMasterPreset={saveMasterPreset}
+ onLoadMasterPreset={loadMasterPreset}
+ onDeleteMasterPreset={deleteMasterPreset}
+ onClearMasterChain={clearMasterChain}
/>
{/* Main canvas area */}
diff --git a/components/layout/SidePanel.tsx b/components/layout/SidePanel.tsx
index 9d3abd5..ec33161 100644
--- a/components/layout/SidePanel.tsx
+++ b/components/layout/SidePanel.tsx
@@ -28,16 +28,23 @@ export interface SidePanelProps {
onRemoveTrack: (trackId: string) => void;
onClearTracks: () => void;
- // Effect chain
- effectChain: EffectChain;
- effectPresets: EffectPreset[];
- onToggleEffect: (effectId: string) => void;
- onRemoveEffect: (effectId: string) => void;
- onReorderEffects: (fromIndex: number, toIndex: number) => void;
- onSavePreset: (preset: EffectPreset) => void;
- onLoadPreset: (preset: EffectPreset) => void;
- onDeletePreset: (presetId: string) => void;
- onClearChain: () => void;
+ // Track effect chain (for selected track)
+ trackEffectChain: EffectChain | null;
+ onToggleTrackEffect: (effectId: string) => void;
+ onRemoveTrackEffect: (effectId: string) => void;
+ onReorderTrackEffects: (fromIndex: number, toIndex: number) => void;
+ onClearTrackChain: () => void;
+
+ // Master effect chain
+ masterEffectChain: EffectChain;
+ masterEffectPresets: EffectPreset[];
+ onToggleMasterEffect: (effectId: string) => void;
+ onRemoveMasterEffect: (effectId: string) => void;
+ onReorderMasterEffects: (fromIndex: number, toIndex: number) => void;
+ onSaveMasterPreset: (preset: EffectPreset) => void;
+ onLoadMasterPreset: (preset: EffectPreset) => void;
+ onDeleteMasterPreset: (presetId: string) => void;
+ onClearMasterChain: () => void;
className?: string;
}
@@ -51,19 +58,24 @@ export function SidePanel({
onUpdateTrack,
onRemoveTrack,
onClearTracks,
- effectChain,
- effectPresets,
- onToggleEffect,
- onRemoveEffect,
- onReorderEffects,
- onSavePreset,
- onLoadPreset,
- onDeletePreset,
- onClearChain,
+ trackEffectChain,
+ onToggleTrackEffect,
+ onRemoveTrackEffect,
+ onReorderTrackEffects,
+ onClearTrackChain,
+ masterEffectChain,
+ masterEffectPresets,
+ onToggleMasterEffect,
+ onRemoveMasterEffect,
+ onReorderMasterEffects,
+ onSaveMasterPreset,
+ onLoadMasterPreset,
+ onDeleteMasterPreset,
+ onClearMasterChain,
className,
}: SidePanelProps) {
const [isCollapsed, setIsCollapsed] = React.useState(false);
- const [activeTab, setActiveTab] = React.useState<'tracks' | 'chain'>('tracks');
+ const [activeTab, setActiveTab] = React.useState<'tracks' | 'trackFx' | 'masterFx'>('tracks');
const [presetDialogOpen, setPresetDialogOpen] = React.useState(false);
const selectedTrack = tracks.find((t) => t.id === selectedTrackId);
@@ -102,13 +114,21 @@ export function SidePanel({