clipper/frontend/src/Waveform.tsx

131 lines
3.0 KiB
TypeScript
Raw Normal View History

2021-10-08 14:38:35 +00:00
import { useEffect, useState, useRef } from 'react';
import { Frames, newRPC } from './App';
import { MediaSetServiceClientImpl, MediaSet } from './generated/media_set';
2021-10-08 14:38:35 +00:00
import { WaveformCanvas } from './WaveformCanvas';
import { secsToCanvasX } from './Helpers';
2021-11-06 20:52:47 +00:00
import { from, Observable } from 'rxjs';
import { bufferCount } from 'rxjs/operators';
2021-10-08 14:38:35 +00:00
interface Props {
mediaSet: MediaSet;
position: number;
viewport: Frames;
offsetPixels: number;
}
export const CanvasLogicalWidth = 2000;
export const CanvasLogicalHeight = 500;
export const Waveform: React.FC<Props> = ({
mediaSet,
position,
viewport,
offsetPixels,
}: Props) => {
2021-11-06 20:52:47 +00:00
const [peaks, setPeaks] = useState<Observable<number[]>>(from([]));
2021-10-08 14:38:35 +00:00
const hudCanvasRef = useRef<HTMLCanvasElement>(null);
// effects
// load peaks on MediaSet change
useEffect(() => {
(async function () {
if (mediaSet == null) {
return;
}
if (viewport.start >= viewport.end) {
return;
2021-10-08 14:38:35 +00:00
}
console.log('fetch audio segment...');
const service = new MediaSetServiceClientImpl(newRPC());
const segment = await service.GetAudioSegment({
id: mediaSet.id,
numBins: CanvasLogicalWidth,
startFrame: viewport.start,
endFrame: viewport.end,
});
console.log('got segment', segment);
const peaks = from(segment.peaks).pipe(
bufferCount(mediaSet.audioChannels)
);
setPeaks(peaks);
2021-10-08 14:38:35 +00:00
})();
}, [viewport]);
2021-10-08 14:38:35 +00:00
// render HUD
useEffect(() => {
const canvas = hudCanvasRef.current;
if (canvas == null) {
return;
}
const ctx = canvas.getContext('2d');
if (ctx == null) {
console.error('no hud 2d context available');
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (mediaSet == null) {
return;
}
2021-11-02 16:20:47 +00:00
const x = secsToCanvasX(position, mediaSet.audioSampleRate, viewport);
2021-10-08 14:38:35 +00:00
if (x == null) {
return;
}
ctx.strokeStyle = 'red';
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineWidth = 4;
ctx.lineTo(x, canvas.height);
ctx.stroke();
}, [viewport, position]);
// render component
const containerStyles = {
background: 'black',
margin: '0 ' + offsetPixels + 'px',
flexGrow: 1,
position: 'relative',
} as React.CSSProperties;
const canvasStyles = {
position: 'absolute',
width: '100%',
height: '100%',
display: 'block',
} as React.CSSProperties;
return (
<>
<div style={containerStyles}>
<WaveformCanvas
peaks={peaks}
2021-11-06 20:52:47 +00:00
channels={mediaSet.audioChannels}
2021-10-08 14:38:35 +00:00
width={CanvasLogicalWidth}
height={CanvasLogicalHeight}
strokeStyle="green"
fillStyle="black"
zIndex={0}
alpha={1}
></WaveformCanvas>
<canvas
width={CanvasLogicalWidth}
height={CanvasLogicalHeight}
ref={hudCanvasRef}
style={canvasStyles}
></canvas>
</div>
</>
);
};