import { useEffect, useMemo, useRef, useState } from "react"; import { encodeWav, prepareAudio, toAudioBuffer } from "@plate-dyn/core"; export interface AudioPlayerProps { /** Pressure signal [Pa] */ p: Float64Array; fs: number; /** Peak level of the export and the playback [dBFS]; default -1 */ normalizeDbfs?: number; label?: string; /** Base name for the exported .wav file (no extension) */ filename?: string; } /** * Plays the radiated-pressure estimate once, exactly as long as it is, and * offers the same clip as a 24-bit mono WAV download, peak-normalized as the * reference simulators write theirs. The displayed peak in pascals is not * normalized. */ export function AudioPlayer({ p, fs, normalizeDbfs = -1, label, filename = "plate-sound", }: AudioPlayerProps) { const prep = useMemo( () => prepareAudio(p, fs, 10 ** (normalizeDbfs / 20)), [p, fs, normalizeDbfs], ); const ctxRef = useRef(null); const srcRef = useRef(null); const [playing, setPlaying] = useState(false); useEffect(() => () => stop(), []); const stop = () => { srcRef.current?.stop(); srcRef.current = null; setPlaying(false); }; const play = () => { if (!ctxRef.current) ctxRef.current = new AudioContext(); const ctx = ctxRef.current; void ctx.resume(); stop(); const src = ctx.createBufferSource(); src.buffer = toAudioBuffer(ctx, prep); src.connect(ctx.destination); src.start(); src.onended = () => setPlaying(false); srcRef.current = src; setPlaying(true); }; const download = () => { const blob = new Blob([encodeWav(prep.samples, Math.round(prep.fs), 24) as BlobPart], { type: "audio/wav", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `${filename}.wav`; a.click(); setTimeout(() => URL.revokeObjectURL(url), 0); }; return (
{prep.durationS.toFixed(2)} s at {fs} Hz, peak {prep.peakPa.toExponential(2)} Pa; normalized to {normalizeDbfs} dBFS for playback and export
); }