Implement basic zoom in/out

This commit is contained in:
Rob Watson 2021-09-11 12:58:43 +02:00
parent 06e4b7f550
commit faf818e4ae
1 changed files with 54 additions and 23 deletions

View File

@ -11,12 +11,20 @@ type AudioFile = {
sampleRate: number;
};
type ZoomSettings = {
startFrame: number;
endFrame: number;
};
const defaultZoomSettings: ZoomSettings = { startFrame: 0, endFrame: 0 };
export const Waveform: React.FC<WaveformProps> = ({
audioContext,
}: WaveformProps) => {
const [audioFile, setAudioFile] = useState<AudioFile | null>(null);
const [currentTime, setCurrentTime] = useState(0);
const [audio, setAudio] = useState(new Audio());
const [zoomSettings, setZoomSettings] = useState(defaultZoomSettings);
const waveformCanvasRef = useRef<HTMLCanvasElement>(null);
const hudCanvasRef = useRef<HTMLCanvasElement>(null);
@ -41,13 +49,13 @@ export const Waveform: React.FC<WaveformProps> = ({
return Math.floor((x / canvasLogicalWidth) * audioFile.frames);
};
const canvasXToSecs = (x: number): number => {
if (audioFile == null) {
return 0;
}
const duration = audioFile.frames / audioFile.sampleRate;
return (canvasXToFrame(x) / audioFile.frames) * duration;
};
// const canvasXToSecs = (x: number): number => {
// if (audioFile == null) {
// return 0;
// }
// const duration = audioFile.frames / audioFile.sampleRate;
// return (canvasXToFrame(x) / audioFile.frames) * duration;
// };
const secsToCanvasX = (canvasWidth: number, secs: number): number => {
if (audioFile == null) {
@ -92,10 +100,11 @@ export const Waveform: React.FC<WaveformProps> = ({
};
setAudioFile(audioFile);
setZoomSettings({ startFrame: 0, endFrame: audioFile.frames });
})();
}, [audioContext]);
// render waveform to canvas when audioData is updated:
// render waveform to canvas when zoom settings are updated:
useEffect(() => {
(async function () {
if (audioFile == null) {
@ -116,18 +125,21 @@ export const Waveform: React.FC<WaveformProps> = ({
return;
}
ctx.strokeStyle = '#00aa00';
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let endFrame = zoomSettings.endFrame;
if (endFrame <= zoomSettings.startFrame) {
endFrame = audioFile.frames;
}
const resp = await fetch(
`http://localhost:8888/api/peaks?video_id=${videoID}&start=0&end=${Math.round(
audioFile.frames
)}&bins=${canvas.width}`
`http://localhost:8888/api/peaks?video_id=${videoID}&start=${zoomSettings.startFrame}&end=${endFrame}&bins=${canvas.width}`
);
const peaks = await resp.json();
console.log('respBody from peaks =', peaks);
ctx.strokeStyle = '#00aa00';
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const numChannels = peaks.length;
const chanHeight = canvas.height / numChannels;
for (let c = 0; c < numChannels; c++) {
@ -144,7 +156,7 @@ export const Waveform: React.FC<WaveformProps> = ({
}
}
})();
}, [audioFile]);
}, [zoomSettings]);
// redraw HUD
useEffect(() => {
@ -163,6 +175,10 @@ export const Waveform: React.FC<WaveformProps> = ({
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (audioFile == null) {
return;
}
const x = secsToCanvasX(canvas.width, currentTime);
ctx.strokeStyle = 'red';
@ -180,14 +196,8 @@ export const Waveform: React.FC<WaveformProps> = ({
console.log('mousemove, x =', canvasX, 'frame =', canvasXToFrame(canvasX));
};
const handleMouseDown = (evt: MouseEvent<HTMLCanvasElement>) => {
if (audioFile == null) {
return;
}
const canvasX = mouseEventToCanvasX(evt);
audio.currentTime = canvasXToSecs(canvasX);
console.log('currentTime now', canvasXToSecs(canvasX));
const handleMouseDown = () => {
return null;
};
const handleMouseUp = () => {
@ -207,11 +217,29 @@ export const Waveform: React.FC<WaveformProps> = ({
};
const handleZoomIn = () => {
if (audioFile == null) {
return;
}
console.log('zoom in');
const diff = zoomSettings.endFrame - zoomSettings.startFrame;
const endFrame = zoomSettings.startFrame + Math.floor(diff / 2);
const settings = { ...zoomSettings, endFrame: endFrame };
setZoomSettings(settings);
};
const handleZoomOut = () => {
if (audioFile == null) {
return;
}
console.log('zoom out');
const diff = zoomSettings.endFrame - zoomSettings.startFrame;
const newDiff = diff * 2;
const endFrame = Math.min(
zoomSettings.endFrame + newDiff,
audioFile.frames
);
const settings = { ...zoomSettings, endFrame: endFrame };
setZoomSettings(settings);
};
// render component:
@ -222,6 +250,7 @@ export const Waveform: React.FC<WaveformProps> = ({
position: 'relative',
margin: '0 auto',
} as React.CSSProperties;
const waveformCanvasProps = {
width: '100%',
position: 'absolute',
@ -231,6 +260,7 @@ export const Waveform: React.FC<WaveformProps> = ({
bottom: 0,
zIndex: 0,
} as React.CSSProperties;
const hudCanvasProps = {
width: '100%',
position: 'absolute',
@ -240,6 +270,7 @@ export const Waveform: React.FC<WaveformProps> = ({
bottom: 0,
zIndex: 1,
} as React.CSSProperties;
const clockTextAreaProps = { color: '#999', width: '400px' };
return (