import { useEffect, useMemo, useRef, useState } from "react"; import { prepareAudio, toAudioBuffer } from "@plate-lab/core"; export interface AudioPlayerProps { /** Pressure signal [Pa] */ p: Float64Array; fs: number; label?: string; } /** * Plays the radiated-pressure estimate once, exactly as long as it is. Playback * level is normalized; the displayed peak is not. A clip of a few milliseconds * sounds like a click, and is labelled as such rather than repeated. */ export function AudioPlayer({ p, fs, label }: AudioPlayerProps) { const prep = useMemo(() => prepareAudio(p, fs), [p, fs]); 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 short = prep.durationS < 0.1; return (
{(prep.durationS * 1000).toFixed(1)} ms at {fs} Hz, peak {prep.peakPa.toExponential(2)} Pa; normalized for playback, played once {short ? ". This excerpt is too short to hear as a note; lengthen the simulated window for a longer clip" : ""}
); }