diff --git a/components/editor/PlaybackControls.tsx b/components/editor/PlaybackControls.tsx index 7981bc8..aff9da8 100644 --- a/components/editor/PlaybackControls.tsx +++ b/components/editor/PlaybackControls.tsx @@ -15,7 +15,7 @@ export interface PlaybackControlsProps { onPlay: () => void; onPause: () => void; onStop: () => void; - onSeek: (time: number) => void; + onSeek: (time: number, autoPlay?: boolean) => void; onVolumeChange: (volume: number) => void; disabled?: boolean; className?: string; @@ -82,7 +82,9 @@ export function PlaybackControls({ max={duration || 100} step={0.01} value={currentTime} - onChange={(e) => onSeek(parseFloat(e.target.value))} + onChange={(e) => onSeek(parseFloat(e.target.value), false)} + onMouseUp={(e) => onSeek(parseFloat((e.target as HTMLInputElement).value), true)} + onTouchEnd={(e) => onSeek(parseFloat((e.target as HTMLInputElement).value), true)} disabled={disabled || duration === 0} className={cn( 'w-full h-2 bg-secondary rounded-lg appearance-none cursor-pointer', diff --git a/components/editor/Waveform.tsx b/components/editor/Waveform.tsx index 20b5c38..04152f4 100644 --- a/components/editor/Waveform.tsx +++ b/components/editor/Waveform.tsx @@ -9,7 +9,7 @@ export interface WaveformProps { audioBuffer: AudioBuffer | null; currentTime: number; duration: number; - onSeek?: (time: number) => void; + onSeek?: (time: number, autoPlay?: boolean) => void; className?: string; height?: number; zoom?: number; @@ -213,9 +213,9 @@ export function Waveform({ setSelectionStart(clickedTime); onSelectionChange({ start: clickedTime, end: clickedTime }); } else if (onSeek) { - // Regular dragging for scrubbing + // Regular dragging for scrubbing (without auto-play) setIsDragging(true); - onSeek(clickedTime); + onSeek(clickedTime, false); } }; @@ -238,13 +238,28 @@ export function Waveform({ const end = Math.max(selectionStart, clampedTime); onSelectionChange({ start, end }); } - // Handle scrubbing + // Handle scrubbing (without auto-play during drag) else if (isDragging && onSeek) { - onSeek(clampedTime); + onSeek(clampedTime, false); } }; - const handleMouseUp = () => { + const handleMouseUp = (e: React.MouseEvent) => { + // If we were dragging (scrubbing), trigger auto-play on mouse up + if (isDragging && onSeek && !isSelecting) { + 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 + onSeek(clampedTime, true); + } + } + setIsDragging(false); setIsSelecting(false); setSelectionStart(null); diff --git a/lib/audio/player.ts b/lib/audio/player.ts index 4c669b6..92202ec 100644 --- a/lib/audio/player.ts +++ b/lib/audio/player.ts @@ -114,18 +114,25 @@ export class AudioPlayer { } /** - * Seek to a specific time and start playback + * Seek to a specific time + * @param time - Time in seconds to seek to + * @param autoPlay - Whether to automatically start playback after seeking (default: false) */ - async seek(time: number): Promise { + async seek(time: number, autoPlay: boolean = false): Promise { if (!this.audioBuffer) return; + const wasPlaying = this.isPlaying; const clampedTime = Math.max(0, Math.min(time, this.audioBuffer.duration)); this.stop(); this.pauseTime = clampedTime; - // Always start playback after seeking - await this.play(clampedTime); + // Auto-play if requested, or continue playing if was already playing + if (autoPlay || wasPlaying) { + await this.play(clampedTime); + } else { + this.isPaused = true; + } } /** diff --git a/lib/hooks/useAudioPlayer.ts b/lib/hooks/useAudioPlayer.ts index 7058a81..65129b5 100644 --- a/lib/hooks/useAudioPlayer.ts +++ b/lib/hooks/useAudioPlayer.ts @@ -14,7 +14,7 @@ export interface UseAudioPlayerReturn { play: () => Promise; pause: () => void; stop: () => void; - seek: (time: number) => Promise; + seek: (time: number, autoPlay?: boolean) => Promise; // Volume control setVolume: (volume: number) => void; @@ -159,14 +159,16 @@ export function useAudioPlayer(): UseAudioPlayerReturn { }, [player]); const seek = React.useCallback( - async (time: number) => { + async (time: number, autoPlay: boolean = false) => { if (!player) return; - await player.seek(time); + await player.seek(time, autoPlay); setCurrentTime(time); - // Seek now auto-starts playback, so update state accordingly - setIsPlaying(true); - setIsPaused(false); + + // Update state based on what actually happened + const state = player.getState(); + setIsPlaying(state.isPlaying); + setIsPaused(state.isPaused); }, [player] );