From 8cade215af8971ed6509ba1a69e2826dc27a0e80 Mon Sep 17 00:00:00 2001 From: Rob Watson Date: Mon, 27 Sep 2021 19:47:42 +0200 Subject: [PATCH] Improve overview selection behaviour --- frontend/src/Waveform.tsx | 27 ++++++++++++++-- frontend/src/Waveform/Helpers.tsx | 6 ---- frontend/src/Waveform/Overview.tsx | 50 +++++++++++++++++++++--------- 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/frontend/src/Waveform.tsx b/frontend/src/Waveform.tsx index bec2805..a3d327a 100644 --- a/frontend/src/Waveform.tsx +++ b/frontend/src/Waveform.tsx @@ -238,7 +238,6 @@ export const Waveform: React.FC = ({ audioContext }: Props) => { } video.pause(); - console.log('paused video'); }; const handleZoomIn = () => { @@ -267,15 +266,36 @@ export const Waveform: React.FC = ({ audioContext }: Props) => { setZoomSettings(settings); }; + const handleSelectionStart = (x: number) => { + const video = videoRef.current; + if (video == null) { + return; + } + + const frame = canvasXToFrame(x, mediaSet.audio.frames); + if (video.paused) { + video.currentTime = frame / mediaSet.audio.sampleRate; + } + }; + const handleSelectionChange = (selection: Selection) => { + console.log('in handleSelectionChange handler'); if (mediaSet == null) { return; } + const startFrame = canvasXToFrame(selection.x1, mediaSet.audio.frames); + const endFrame = canvasXToFrame(selection.x2, mediaSet.audio.frames); const settings: ZoomSettings = { - startFrame: canvasXToFrame(selection.x1, mediaSet.audio.frames), - endFrame: canvasXToFrame(selection.x2, mediaSet.audio.frames), + startFrame: startFrame, + endFrame: endFrame, }; setZoomSettings(settings); + + const video = videoRef.current; + if (video == null) { + return; + } + video.currentTime = startFrame / mediaSet.audio.sampleRate; }; // render component: @@ -335,6 +355,7 @@ export const Waveform: React.FC = ({ audioContext }: Props) => { peaks={overviewPeaks} numFrames={mediaSet.audio.frames} style={overviewStyles} + onSelectionStart={handleSelectionStart} onSelectionChange={handleSelectionChange} >
diff --git a/frontend/src/Waveform/Helpers.tsx b/frontend/src/Waveform/Helpers.tsx index 1f7c325..3512e3b 100644 --- a/frontend/src/Waveform/Helpers.tsx +++ b/frontend/src/Waveform/Helpers.tsx @@ -16,12 +16,6 @@ export const canvasXToFrame = (x: number, numFrames: number): number => { return Math.floor((x / CanvasLogicalWidth) * numFrames); }; -// // TODO: add tests -// export const canvasXToSecs = (x: number, numFrames: number): number => { -// const frame := canvasXToFrame(x, numFrames) - -// } - // TODO: add tests export const secsToCanvasX = ( secs: number, diff --git a/frontend/src/Waveform/Overview.tsx b/frontend/src/Waveform/Overview.tsx index 8c5d49f..412a7a2 100644 --- a/frontend/src/Waveform/Overview.tsx +++ b/frontend/src/Waveform/Overview.tsx @@ -11,6 +11,7 @@ type Props = { peaks: number[][] | null; numFrames: number; style: React.CSSProperties; + onSelectionStart: (x1: number) => void; onSelectionChange: (selection: Selection) => void; }; @@ -19,17 +20,21 @@ enum Mode { Selecting, } +// TODO: render position marker during playback export const Waveform: React.FC = (props: Props) => { const hudCanvasRef = useRef(null); const [mode, setMode] = useState(Mode.Normal); const defaultSelection: Selection = { x1: 0, x2: 0 }; + // selection is the current selection in canvas coordinates: const [selection, setSelection] = useState(defaultSelection); - // TODO: is it needed? - const [newStartSelection, setNewStartSelection] = useState(0); + // newSelection is a new selection in the process of being drawn by the user. + // It is only useful if Mode.Selecting is active. + const [newSelection, setNewSelection] = useState(defaultSelection); // effects + // draw the overview waveform useEffect(() => { (async function () { const canvas = hudCanvasRef.current; @@ -46,7 +51,13 @@ export const Waveform: React.FC = (props: Props) => { ctx.clearRect(0, 0, canvas.width, canvas.height); - if (selection.x1 >= selection.x2) { + let currentSelection: Selection; + if (mode == Mode.Selecting) { + currentSelection = newSelection; + } else { + currentSelection = selection; + } + if (currentSelection.x1 >= currentSelection.x2) { return; } @@ -54,7 +65,12 @@ export const Waveform: React.FC = (props: Props) => { ctx.strokeStyle = 'red'; ctx.lineWidth = 2; ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; - ctx.rect(selection.x1, 2, selection.x2 - selection.x1, canvas.height - 8); + ctx.rect( + currentSelection.x1, + 2, + currentSelection.x2 - currentSelection.x1, + canvas.height - 8 + ); ctx.fill(); ctx.stroke(); })(); @@ -63,35 +79,41 @@ export const Waveform: React.FC = (props: Props) => { // handlers const handleMouseDown = (evt: MouseEvent) => { - console.log('mousedown'); if (mode != Mode.Normal) { return; } setMode(Mode.Selecting); - setNewStartSelection(mouseEventToCanvasX(evt)); + + const x = mouseEventToCanvasX(evt); + setNewSelection({ x1: x, x2: x }); + props.onSelectionStart(x); }; const handleMouseMove = (evt: MouseEvent) => { - console.log('mousemove'); if (mode != Mode.Selecting) { return; } - const selection: Selection = { - x1: newStartSelection, - x2: mouseEventToCanvasX(evt), - }; - setSelection(selection); + const x = mouseEventToCanvasX(evt); + if (x == newSelection.x2) { + return; + } + + setNewSelection({ ...newSelection, x2: x }); }; const handleMouseUp = () => { - console.log('mouseup'); if (mode != Mode.Selecting) { return; } setMode(Mode.Normal); - props.onSelectionChange(selection); + + // TODO: better shallow equality check? + if (selection.x1 !== newSelection.x1 || selection.x2 !== newSelection.x2) { + setSelection(newSelection); + props.onSelectionChange(newSelection); + } }; // render component