3.9 KiB
<MeterSpectral> — multi-band spectrum visualiser
(no submodules)
Purpose
<MeterSpectral> renders a real-time bar graph of magnitude bins. Each
bin is drawn as a vertical bar whose height is proportional to the
bin's magnitude in [0, 1]. The component is deliberately tier-pure:
it has no dependency on @suaveplan/audio-analysis-spectrum or any
audio code at all. Bins are pre-computed by the caller — wire it up to
whatever analyser is appropriate (web-audio AnalyserNode, the
spectrum-analysis package, a network stream, or a synthetic generator).
This separation keeps the React rendering layer free of audio processing and makes it trivial to use the same component for any 1-D magnitude signal — FFT magnitudes, wavelet bands, frequency-warped mel bins, or live envelope follower output.
Features
- Static
Float32Arrayor streamingAsyncIterable<Float32Array>source - Configurable
width,height, and inter-bargap - Per-bin
data-valueattributes for E2E and screen-reader inspection role="img"with a singlearia-labeldescribing the visualisation- CSS-variable themed bars (
--cs-meter-spectral-bar-color) - Out-of-range bin values are clamped to
[0, 1]automatically
Usage: Basic
import { MeterSpectral } from "@suaveplan/react-control-surface";
<MeterSpectral
bins={new Float32Array([0.1, 0.5, 0.9, 0.3])}
label="Output spectrum"
/>;
Usage: Advanced
import { MeterSpectral } from "@suaveplan/react-control-surface";
async function* spectrumStream(
analyser: AnalyserNode
): AsyncIterable<Float32Array> {
const buf = new Float32Array(analyser.frequencyBinCount);
while (true) {
analyser.getFloatFrequencyData(buf);
const out = new Float32Array(buf.length);
for (let i = 0; i < buf.length; i++) {
out[i] = Math.max(0, Math.min(1, (buf[i] + 100) / 100));
}
yield out;
await new Promise((r) => requestAnimationFrame(() => r(null)));
}
}
<MeterSpectral
bins={spectrumStream(analyserNode)}
width={480}
height={160}
label="Master spectrum"
/>;
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
bins |
Float32Array | AsyncIterable<Float32Array> |
req | Magnitude bins in [0, 1]. |
width |
number |
240 |
Total width in CSS pixels. |
height |
number |
80 |
Total height in CSS pixels. |
gap |
number |
1 |
Inter-bar gap in CSS pixels. |
label |
string |
— | Visible + accessible label. |
ariaLabel |
string |
derived | Override the accessible label. |
className |
string |
— | Appended to rcs-meter-spectral. |
Implementation Notes
When bins is a Float32Array, the component sets its frame state
directly on mount and whenever the prop identity changes. When bins
is an AsyncIterable, an effect subscribes via for await … of and
calls setFrame for every yielded frame. The subscription is torn down
via a cancelled flag in the cleanup function; errors thrown from the
iterator are swallowed because visualisation is best-effort.
Bars are absolutely-positioned divs aligned to the bottom of the
container; bar width is (width - totalGap) / count. This keeps the
layout pure CSS and avoids <canvas> — every bar is independently
inspectable in DOM and accessible tooling.