60 lines
1.4 KiB
TypeScript
60 lines
1.4 KiB
TypeScript
// Copyright 2023 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import classNames from 'classnames';
|
|
import React from 'react';
|
|
import { assertDev } from '../../util/assert';
|
|
|
|
type Props = {
|
|
peaks: ReadonlyArray<number>;
|
|
barMinHeight: number;
|
|
barMaxHeight: number;
|
|
currentTime: number | undefined;
|
|
duration: number | undefined;
|
|
};
|
|
|
|
export function Waveform({
|
|
peaks,
|
|
barMinHeight,
|
|
barMaxHeight,
|
|
currentTime,
|
|
duration,
|
|
}: Props): JSX.Element {
|
|
const currentTimeOrZero = currentTime ?? 0;
|
|
const peakPosition = peaks.length * (currentTimeOrZero / (duration ?? 1e-23));
|
|
|
|
return (
|
|
<div className={classNames(['Waveform'])}>
|
|
{peaks.map((peak, i) => {
|
|
assertDev(
|
|
peak >= 0 && peak <= 1 && !Number.isNaN(peak),
|
|
`Peak outside of range: ${peak}`
|
|
);
|
|
|
|
let height = Math.max(barMinHeight, barMaxHeight * peak);
|
|
|
|
const highlight = i < peakPosition;
|
|
|
|
// Use maximum height for current audio position
|
|
if (highlight && i + 1 >= peakPosition) {
|
|
height = barMaxHeight;
|
|
}
|
|
|
|
assertDev(!Number.isNaN(height), 'Got NaN for peak height');
|
|
|
|
const key = i;
|
|
|
|
return (
|
|
<div
|
|
className={classNames([
|
|
'Waveform__bar',
|
|
highlight ? 'Waveform__bar--active' : null,
|
|
])}
|
|
key={key}
|
|
style={{ height }}
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|