clipper/frontend/src/Overview.tsx

115 lines
2.9 KiB
TypeScript
Raw Normal View History

2021-12-06 22:52:24 +00:00
import { useState, useEffect, useCallback } from 'react';
2021-11-29 17:44:31 +00:00
import { MediaSet } from './generated/media_set';
import { Frames, VideoPosition } from './App';
2021-10-08 14:38:35 +00:00
import { WaveformCanvas } from './WaveformCanvas';
2021-12-06 22:52:24 +00:00
import { HudCanvas } from './HudCanvas';
2021-11-29 17:44:31 +00:00
import { Observable } from 'rxjs';
2021-10-08 14:38:35 +00:00
2021-11-25 18:02:37 +00:00
export interface Selection {
start: number;
end: number;
}
2021-10-08 14:38:35 +00:00
interface Props {
2021-11-29 17:44:31 +00:00
peaks: Observable<number[]>;
2021-10-08 14:38:35 +00:00
mediaSet: MediaSet;
height: number;
offsetPixels: number;
2021-11-25 18:02:37 +00:00
position: VideoPosition;
2021-11-30 19:41:34 +00:00
viewport: Frames;
2021-12-06 22:52:24 +00:00
onSelectionChange: (selection: Selection) => void;
2021-10-08 14:38:35 +00:00
}
2021-11-29 17:44:31 +00:00
export const CanvasLogicalWidth = 2_000;
export const CanvasLogicalHeight = 500;
2021-10-08 14:38:35 +00:00
export const Overview: React.FC<Props> = ({
2021-11-29 17:44:31 +00:00
peaks,
2021-10-08 14:38:35 +00:00
mediaSet,
height,
offsetPixels,
position,
2021-11-30 19:41:34 +00:00
viewport,
2021-10-08 14:38:35 +00:00
onSelectionChange,
}: Props) => {
2021-12-06 22:52:24 +00:00
const [selectedPixels, setSelectedPixels] = useState({ start: 0, end: 0 });
const [positionPixels, setPositionPixels] = useState(0);
2021-10-08 14:38:35 +00:00
2021-11-30 19:41:34 +00:00
// side effects
2021-10-08 14:38:35 +00:00
2021-12-06 22:52:24 +00:00
// convert viewport from frames to canvas pixels.
2021-11-25 18:02:37 +00:00
useEffect(() => {
2021-12-06 22:52:24 +00:00
setSelectedPixels({
2021-11-30 19:41:34 +00:00
start: Math.round(
(viewport.start / mediaSet.audioFrames) * CanvasLogicalWidth
),
end: Math.round(
(viewport.end / mediaSet.audioFrames) * CanvasLogicalWidth
),
});
2021-12-06 22:52:24 +00:00
}, [viewport, mediaSet]);
2021-10-08 14:38:35 +00:00
// convert position from frames to canvas pixels:
useEffect(() => {
const ratio =
position.currentTime / (mediaSet.audioFrames / mediaSet.audioSampleRate);
setPositionPixels(Math.round(ratio * CanvasLogicalWidth));
frames;
}, [mediaSet, position]);
2021-10-08 14:38:35 +00:00
// handlers
2021-12-06 22:52:24 +00:00
// convert selection change from canvas pixels to frames, and trigger callback.
const handleSelectionChange = useCallback(
({ start, end }: Selection) => {
onSelectionChange({
start: Math.round((start / CanvasLogicalWidth) * mediaSet.audioFrames),
end: Math.round((end / CanvasLogicalWidth) * mediaSet.audioFrames),
});
},
[mediaSet]
);
2021-10-08 14:38:35 +00:00
// render component
const containerStyles = {
flexGrow: 0,
position: 'relative',
margin: `0 ${offsetPixels}px`,
height: `${height}px`,
} as React.CSSProperties;
const hudStyles = {
borderLineWidth: 4,
borderStrokeStyle: 'red',
positionLineWidth: 4,
positionStrokeStyle: 'red',
};
2021-10-08 14:38:35 +00:00
return (
<>
<div style={containerStyles}>
<WaveformCanvas
peaks={peaks}
channels={mediaSet.audioChannels}
width={CanvasLogicalWidth}
height={CanvasLogicalHeight}
strokeStyle="black"
fillStyle="#003300"
zIndex={1}
alpha={1}
></WaveformCanvas>
2021-12-06 22:52:24 +00:00
<HudCanvas
2021-10-08 14:38:35 +00:00
width={CanvasLogicalWidth}
height={CanvasLogicalHeight}
2021-12-06 22:52:24 +00:00
zIndex={1}
styles={hudStyles}
position={positionPixels}
2021-12-06 22:52:24 +00:00
selection={selectedPixels}
onSelectionChange={handleSelectionChange}
/>
2021-10-08 14:38:35 +00:00
</div>
</>
);
};