Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
# `<MeterVU>` — VU meter with IEC 60268-10 ballistics
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`<MeterVU>` renders a single-channel level meter with the classic analogue
VU response defined by IEC 60268-10: a 300 ms attack and 300 ms release
single-pole low-pass smoothing the displayed needle relative to the
instantaneous input level. The component is a pure value-in /
DOM-out leaf — consumers feed a normalised level in `[0, 1]` and the
meter handles ballistic smoothing on a `requestAnimationFrame` loop.
Use it for output-bus meters, mixer-strip meters, and any
human-readable loudness indication where peak-precise behaviour is
less important than the perception of "musical loudness".
## Features
- IEC 60268-10 default ballistics (300 ms attack, 300 ms release)
- Caller-tunable `attackMs` and `releaseMs` for non-standard ballistics
- Pure `BoundControlProps`-compatible value contract (value only)
- `role="meter"` with `aria-valuemin` / `aria-valuemax` / `aria-valuenow`
- CSS-variable themed fill (`--cs-meter-vu-fill-color`)
- RAF-driven smoothing, automatic cleanup on unmount
## Usage: Basic
```tsx
import { MeterVU } from "@suaveplan/react-control-surface";
<MeterVU value={0.6} label="Master Out" />;
```
## Usage: Advanced
```tsx
import { MeterVU } from "@suaveplan/react-control-surface";
// Live-driven, fast attack / slow release for a punchy display
function LiveMeter({ source }: { source: () => number }) {
const [v, setV] = useState(0);
useEffect(() => {
const id = setInterval(() => setV(source()), 16);
return () => clearInterval(id);
}, [source]);
return <MeterVU value={v} attackMs={40} releaseMs={600} width={260} />;
}
```
## API Reference
| Prop | Type | Default | Description |
| ----------- | -------- | ------------- | --------------------------------------------- |
| `value` | `number` | required | Source level in `[0, 1]`, clamped on input. |
| `attackMs` | `number` | `300` | Single-pole attack time-constant. |
| `releaseMs` | `number` | `300` | Single-pole release time-constant. |
| `width` | `number` | `160` | Root width in CSS pixels. |
| `height` | `number` | `48` | Root height in CSS pixels. |
| `label` | `string` | — | Visible + accessible label. |
| `ariaLabel` | `string` | derived | Overrides the accessible label. |
| `className` | `string` | — | Appended to `rcs-meter-vu`. |
The exported `stepBallistic(previous, target, dtMs, attackMs, releaseMs)`
helper is the single-pole smoothing primitive used internally; it is
exported for re-use in custom meters and is exhaustively unit-tested.
## Implementation Notes
The meter holds a `useRef` containing `{ level, last }` so that the
ballistic state persists across React renders without forcing a
re-render on every smoothing tick. The animation loop is created once
on mount and torn down via `cancelAnimationFrame` on unmount. Time
deltas are measured with `Date.now()` rather than the RAF callback's
high-resolution timestamp to keep the math identical to the
`stepBallistic` unit tests, which run without `requestAnimationFrame`.
The fill bar uses a single absolutely-positioned div whose width is
recomputed from the smoothed level on every frame. The `data-level`
attribute is provided as a deterministic hook for E2E assertions.