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>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -207,15 +207,11 @@ export function Waveform({
|
||||
const actualX = x + scrollOffset;
|
||||
const clickedTime = (actualX / visibleWidth) * duration;
|
||||
|
||||
// Shift key for selection
|
||||
if (e.shiftKey && onSelectionChange) {
|
||||
setIsSelecting(true);
|
||||
setSelectionStart(clickedTime);
|
||||
// Start selection on drag
|
||||
setIsSelecting(true);
|
||||
setSelectionStart(clickedTime);
|
||||
if (onSelectionChange) {
|
||||
onSelectionChange({ start: clickedTime, end: clickedTime });
|
||||
} else if (onSeek) {
|
||||
// Regular dragging for scrubbing (without auto-play)
|
||||
setIsDragging(true);
|
||||
onSeek(clickedTime, false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -234,31 +230,33 @@ export function Waveform({
|
||||
|
||||
// Handle selection dragging
|
||||
if (isSelecting && onSelectionChange && selectionStart !== null) {
|
||||
setIsDragging(true); // Mark that we're dragging
|
||||
const start = Math.min(selectionStart, clampedTime);
|
||||
const end = Math.max(selectionStart, clampedTime);
|
||||
onSelectionChange({ start, end });
|
||||
}
|
||||
// Handle scrubbing (without auto-play during drag)
|
||||
else if (isDragging && onSeek) {
|
||||
onSeek(clampedTime, false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = (e: React.MouseEvent<HTMLCanvasElement>) => {
|
||||
// If we were dragging (scrubbing), trigger auto-play on mouse up
|
||||
if (isDragging && onSeek && !isSelecting) {
|
||||
// If we didn't drag (just clicked), seek to that position and clear selection
|
||||
if (!isDragging && onSeek) {
|
||||
const canvas = canvasRef.current;
|
||||
if (canvas) {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const visibleWidth = width * zoom;
|
||||
const actualX = x + scrollOffset;
|
||||
const releaseTime = (actualX / visibleWidth) * duration;
|
||||
const clampedTime = Math.max(0, Math.min(duration, releaseTime));
|
||||
// Auto-play on mouse up after dragging
|
||||
const clickTime = (actualX / visibleWidth) * duration;
|
||||
const clampedTime = Math.max(0, Math.min(duration, clickTime));
|
||||
// Seek and auto-play
|
||||
onSeek(clampedTime, true);
|
||||
// Clear selection on click
|
||||
if (onSelectionChange) {
|
||||
onSelectionChange(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we dragged, the selection is already set via handleMouseMove
|
||||
|
||||
setIsDragging(false);
|
||||
setIsSelecting(false);
|
||||
|
||||
Reference in New Issue
Block a user