diff --git a/components/WaveformEditor.tsx b/components/WaveformEditor.tsx index b85297b..51a83e6 100644 --- a/components/WaveformEditor.tsx +++ b/components/WaveformEditor.tsx @@ -16,6 +16,7 @@ export default function WaveformEditor({ audioUrl, startTime, duration, unlockSt const [audioDuration, setAudioDuration] = useState(0); const [isPlaying, setIsPlaying] = useState(false); const [playingSegment, setPlayingSegment] = useState(null); + const [isPlayingFullTitle, setIsPlayingFullTitle] = useState(false); const [zoom, setZoom] = useState(1); // 1 = full view, higher = zoomed in const [viewOffset, setViewOffset] = useState(0); // Offset in seconds for panning const [playbackPosition, setPlaybackPosition] = useState(null); // Current playback position in seconds @@ -237,6 +238,7 @@ export default function WaveformEditor({ audioUrl, startTime, duration, unlockSt sourceRef.current?.stop(); setIsPlaying(false); setPlayingSegment(null); + setIsPlayingFullTitle(false); setPlaybackPosition(null); if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); @@ -305,16 +307,16 @@ export default function WaveformEditor({ audioUrl, startTime, duration, unlockSt const handlePlayFull = () => { if (!audioBuffer || !audioContextRef.current) return; - // If full playback is already playing, stop it - if (isPlaying && playingSegment === null) { + // If full selection playback is already playing, stop it + if (isPlaying && playingSegment === null && !isPlayingFullTitle) { stopPlayback(); return; } - // Stop any current playback (segment or full) + // Stop any current playback (segment, full selection, or full title) stopPlayback(); - // Start full playback + // Start full selection playback const source = audioContextRef.current.createBufferSource(); source.buffer = audioBuffer; source.connect(audioContextRef.current.destination); @@ -325,12 +327,53 @@ export default function WaveformEditor({ audioUrl, startTime, duration, unlockSt source.start(0, startTime, duration); sourceRef.current = source; setIsPlaying(true); - setPlayingSegment(null); // Ensure playingSegment is null for full playback + setPlayingSegment(null); + setIsPlayingFullTitle(false); setPlaybackPosition(startTime); source.onended = () => { setIsPlaying(false); setPlayingSegment(null); + setIsPlayingFullTitle(false); + setPlaybackPosition(null); + if (animationFrameRef.current) { + cancelAnimationFrame(animationFrameRef.current); + animationFrameRef.current = null; + } + }; + }; + + const handlePlayFullTitle = () => { + if (!audioBuffer || !audioContextRef.current) return; + + // If full title playback is already playing, stop it + if (isPlaying && isPlayingFullTitle) { + stopPlayback(); + return; + } + + // Stop any current playback (segment, full selection, or full title) + stopPlayback(); + + // Start full title playback (from 0 to audioDuration) + const source = audioContextRef.current.createBufferSource(); + source.buffer = audioBuffer; + source.connect(audioContextRef.current.destination); + + playbackStartTimeRef.current = audioContextRef.current.currentTime; + playbackOffsetRef.current = 0; + + source.start(0, 0, audioDuration); + sourceRef.current = source; + setIsPlaying(true); + setPlayingSegment(null); + setIsPlayingFullTitle(true); + setPlaybackPosition(0); + + source.onended = () => { + setIsPlaying(false); + setPlayingSegment(null); + setIsPlayingFullTitle(false); setPlaybackPosition(null); if (animationFrameRef.current) { cancelAnimationFrame(animationFrameRef.current); @@ -427,7 +470,21 @@ export default function WaveformEditor({ audioUrl, startTime, duration, unlockSt fontWeight: 'bold' }} > - {isPlaying && playingSegment === null ? '⏸ Pause' : '▶ Play Full Selection'} + {isPlaying && playingSegment === null && !isPlayingFullTitle ? '⏸ Pause' : '▶ Play Full Selection'} + +