import { useCanvas, drawFrame, PLOT_COLORS } from './canvasUtils'; interface Props { y: Float32Array; sampleRate: number; } export default function WaveformPlot({ y, sampleRate }: Props) { const ref = useCanvas( (ctx, w, h) => { drawFrame(ctx, w, h); const padL = 34; const padB = 16; const pw = w - padL - 6; const ph = h - padB - 6; const midY = 6 + ph / 2; ctx.strokeStyle = PLOT_COLORS.grid; ctx.beginPath(); ctx.moveTo(padL, midY); ctx.lineTo(padL + pw, midY); ctx.stroke(); // adaptive time gridlines const durS = y.length / sampleRate; const tStep = durS > 8 ? 2.0 : durS > 4 ? 1.0 : durS > 1.5 ? 0.5 : 0.1; ctx.fillStyle = PLOT_COLORS.axis; ctx.font = '10px system-ui'; ctx.textAlign = 'center'; for (let t = 0; t <= durS + 1e-9; t += tStep) { const x = padL + (t / durS) * pw; ctx.strokeStyle = PLOT_COLORS.grid; ctx.beginPath(); ctx.moveTo(x, 6); ctx.lineTo(x, 6 + ph); ctx.stroke(); ctx.fillText(`${t.toFixed(1)}s`, x, h - 4); } ctx.textAlign = 'right'; ctx.fillText('+1', padL - 4, 12); ctx.fillText('-1', padL - 4, 6 + ph); // min/max envelope per pixel column ctx.strokeStyle = PLOT_COLORS.trace; ctx.beginPath(); const perPx = y.length / pw; for (let px = 0; px < pw; px++) { const i0 = Math.floor(px * perPx); const i1 = Math.min(y.length, Math.max(i0 + 1, Math.floor((px + 1) * perPx))); let mn = Infinity, mx = -Infinity; for (let i = i0; i < i1; i++) { if (y[i] < mn) mn = y[i]; if (y[i] > mx) mx = y[i]; } if (!Number.isFinite(mn)) continue; ctx.moveTo(padL + px + 0.5, midY - mx * (ph / 2)); ctx.lineTo(padL + px + 0.5, midY - mn * (ph / 2) + 0.5); } ctx.stroke(); }, [y, sampleRate], 170, ); return ; }