Files
audio-ui/lib/history/commands/effect-command.ts
Sebastian Krüger ee48f9475f feat: add advanced audio effects and improve UI
Phase 6.5 Advanced Effects:
- Add Pitch Shifter with semitones and cents adjustment
- Add Time Stretch with pitch preservation using overlap-add
- Add Distortion with soft/hard/tube types and tone control
- Add Bitcrusher with bit depth and sample rate reduction
- Add AdvancedParameterDialog with real-time waveform visualization
- Add 4 professional presets per effect type

Improvements:
- Fix undefined parameter errors by adding nullish coalescing operators
- Add global custom scrollbar styling with color-mix transparency
- Add custom-scrollbar utility class for side panel
- Improve theme-aware scrollbar appearance in light/dark modes
- Fix parameter initialization when switching effect types

Integration:
- All advanced effects support undo/redo via EffectCommand
- Effects accessible via command palette and side panel
- Selection-based processing support
- Toast notifications for all effects

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 20:03:40 +01:00

243 lines
5.9 KiB
TypeScript

/**
* Effect commands for undo/redo system
*/
import { BaseCommand } from '../command';
export class EffectCommand extends BaseCommand {
private originalBuffer: AudioBuffer;
private modifiedBuffer: AudioBuffer;
private applyCallback: (buffer: AudioBuffer) => void;
private description: string;
constructor(
originalBuffer: AudioBuffer,
modifiedBuffer: AudioBuffer,
applyCallback: (buffer: AudioBuffer) => void,
description: string
) {
super();
this.originalBuffer = originalBuffer;
this.modifiedBuffer = modifiedBuffer;
this.applyCallback = applyCallback;
this.description = description;
}
getDescription(): string {
return this.description;
}
execute(): void {
this.applyCallback(this.modifiedBuffer);
}
undo(): void {
this.applyCallback(this.originalBuffer);
}
redo(): void {
this.execute();
}
}
/**
* Factory function to create effect commands
*/
export function createEffectCommand(
originalBuffer: AudioBuffer,
effectFunction: (buffer: AudioBuffer) => AudioBuffer | Promise<AudioBuffer>,
applyCallback: (buffer: AudioBuffer) => void,
description: string
): EffectCommand {
const result = effectFunction(originalBuffer);
const modifiedBuffer = result instanceof Promise ? originalBuffer : result;
return new EffectCommand(originalBuffer, modifiedBuffer, applyCallback, description);
}
/**
* Factory function to create async effect commands
*/
export async function createAsyncEffectCommand(
originalBuffer: AudioBuffer,
effectFunction: (buffer: AudioBuffer) => Promise<AudioBuffer>,
applyCallback: (buffer: AudioBuffer) => void,
description: string
): Promise<EffectCommand> {
const modifiedBuffer = await effectFunction(originalBuffer);
return new EffectCommand(originalBuffer, modifiedBuffer, applyCallback, description);
}
/**
* Factory for gain effect command
*/
export function createGainCommand(
buffer: AudioBuffer,
gainValue: number,
applyCallback: (buffer: AudioBuffer) => void
): EffectCommand {
return createEffectCommand(
buffer,
(buf) => {
// Import will happen at runtime
const { applyGain } = require('@/lib/audio/effects/gain');
return applyGain(buf, gainValue);
},
applyCallback,
`Apply Gain (${gainValue.toFixed(2)}x)`
);
}
/**
* Factory for normalize peak command
*/
export function createNormalizePeakCommand(
buffer: AudioBuffer,
targetPeak: number,
applyCallback: (buffer: AudioBuffer) => void
): EffectCommand {
return createEffectCommand(
buffer,
(buf) => {
const { normalizePeak } = require('@/lib/audio/effects/normalize');
return normalizePeak(buf, targetPeak);
},
applyCallback,
`Normalize to Peak (${targetPeak.toFixed(2)})`
);
}
/**
* Factory for normalize RMS command
*/
export function createNormalizeRMSCommand(
buffer: AudioBuffer,
targetRMS: number,
applyCallback: (buffer: AudioBuffer) => void
): EffectCommand {
return createEffectCommand(
buffer,
(buf) => {
const { normalizeRMS } = require('@/lib/audio/effects/normalize');
return normalizeRMS(buf, targetRMS);
},
applyCallback,
`Normalize to RMS (${targetRMS.toFixed(2)})`
);
}
/**
* Factory for fade in command
*/
export function createFadeInCommand(
buffer: AudioBuffer,
duration: number,
applyCallback: (buffer: AudioBuffer) => void
): EffectCommand {
return createEffectCommand(
buffer,
(buf) => {
const { applyFadeIn } = require('@/lib/audio/effects/fade');
return applyFadeIn(buf, duration);
},
applyCallback,
`Fade In (${duration.toFixed(2)}s)`
);
}
/**
* Factory for fade out command
*/
export function createFadeOutCommand(
buffer: AudioBuffer,
duration: number,
applyCallback: (buffer: AudioBuffer) => void
): EffectCommand {
return createEffectCommand(
buffer,
(buf) => {
const { applyFadeOut } = require('@/lib/audio/effects/fade');
return applyFadeOut(buf, duration);
},
applyCallback,
`Fade Out (${duration.toFixed(2)}s)`
);
}
/**
* Factory for reverse command
*/
export function createReverseCommand(
buffer: AudioBuffer,
applyCallback: (buffer: AudioBuffer) => void
): EffectCommand {
return createEffectCommand(
buffer,
(buf) => {
const { reverseAudio } = require('@/lib/audio/effects/reverse');
return reverseAudio(buf);
},
applyCallback,
'Reverse Audio'
);
}
/**
* Factory for low-pass filter command
*/
export async function createLowPassFilterCommand(
buffer: AudioBuffer,
frequency: number,
Q: number,
applyCallback: (buffer: AudioBuffer) => void
): Promise<EffectCommand> {
return createAsyncEffectCommand(
buffer,
async (buf) => {
const { applyLowPassFilter } = require('@/lib/audio/effects/filters');
return await applyLowPassFilter(buf, frequency, Q);
},
applyCallback,
`Low-Pass Filter (${frequency}Hz)`
);
}
/**
* Factory for high-pass filter command
*/
export async function createHighPassFilterCommand(
buffer: AudioBuffer,
frequency: number,
Q: number,
applyCallback: (buffer: AudioBuffer) => void
): Promise<EffectCommand> {
return createAsyncEffectCommand(
buffer,
async (buf) => {
const { applyHighPassFilter } = require('@/lib/audio/effects/filters');
return await applyHighPassFilter(buf, frequency, Q);
},
applyCallback,
`High-Pass Filter (${frequency}Hz)`
);
}
/**
* Factory for band-pass filter command
*/
export async function createBandPassFilterCommand(
buffer: AudioBuffer,
frequency: number,
Q: number,
applyCallback: (buffer: AudioBuffer) => void
): Promise<EffectCommand> {
return createAsyncEffectCommand(
buffer,
async (buf) => {
const { applyBandPassFilter } = require('@/lib/audio/effects/filters');
return await applyBandPassFilter(buf, frequency, Q);
},
applyCallback,
`Band-Pass Filter (${frequency}Hz)`
);
}