Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::format::validation Namespace Reference

Classes

struct  CheckResult
 Outcome of a single validation check. More...

Functions

CheckResult check_finite (std::span< const float > samples)
 True when no sample is NaN or infinite.
template<typename Sample>
CheckResult check_finite (const audio::BufferView< Sample > &buffer)
 True when no sample is NaN or infinite.
CheckResult check_any_nonzero (std::span< const float > samples, float eps=1.0e-7f)
 True when at least one sample's magnitude exceeds eps — i.e.
CheckResult check_silent (std::span< const float > samples, float eps=1.0e-7f)
 True when every sample's magnitude is at or below eps — the inverse of check_any_nonzero, for "bypass with zero input must be silent" style tests.
CheckResult check_peak_below (std::span< const float > samples, float peak_limit)
 True when every sample's magnitude is at or below peak_limit (linear, not dB).
CheckResult check_param_round_trip (const state::ParamRange &range, float raw, float tol=1.0e-4f)
 Verify a parameter's normalize↔denormalize map is correct for raw.
CheckResult check_state_round_trip (HeadlessHost &host)
 Verify plugin state actually round-trips: save a snapshot, perturb every parameter to a different value, restore the snapshot, and require the re-saved bytes to match the original.
CheckResult check_sysex_payload_equal (std::span< const uint8_t > expected, std::span< const uint8_t > actual)
 True when two SysEx payloads carry identical bytes.
CheckResult check_midi_events_equal (const midi::MidiBuffer &expected, const midi::MidiBuffer &actual)
 True when two MIDI buffers carry the same short messages (bytes + sample offset, in order), the same SysEx payloads (bytes + sample offset), and the same UMP (MIDI 2.0) sidecar packets.

Function Documentation

◆ check_any_nonzero()

CheckResult pulp::format::validation::check_any_nonzero ( std::span< const float > samples,
float eps = 1.0e-7f )
inline

True when at least one sample's magnitude exceeds eps — i.e.

the processor actually produced signal. Catches the silent-output regression where a gain stage or oscillator is wired but never writes.

◆ check_finite() [1/2]

template<typename Sample>
CheckResult pulp::format::validation::check_finite ( const audio::BufferView< Sample > & buffer)
inline

True when no sample is NaN or infinite.

The single most common DSP smoke check — a filter that blows up or a denormal path that returns inf is the classic "it compiled but the audio is broken" bug. Templated on sample constness so it accepts both a mutable process() output view (BufferView<float>) and a const input view.

◆ check_finite() [2/2]

CheckResult pulp::format::validation::check_finite ( std::span< const float > samples)
inline

True when no sample is NaN or infinite.

The single most common DSP smoke check — a filter that blows up or a denormal path that returns inf is the classic "it compiled but the audio is broken" bug.

◆ check_midi_events_equal()

CheckResult pulp::format::validation::check_midi_events_equal ( const midi::MidiBuffer & expected,
const midi::MidiBuffer & actual )
inline

True when two MIDI buffers carry the same short messages (bytes + sample offset, in order), the same SysEx payloads (bytes + sample offset), and the same UMP (MIDI 2.0) sidecar packets.

Use to assert a MIDI-effect's output, or a thru/echo path, matches expectation.

Compares the buffers as-laid-out; call sort() on both first if you only care about sample-offset ordering rather than insertion order. This is block-event equality: it compares each event's block-relative sample_offset (the audio-block timing contract) and intentionally ignores the absolute device-I/O timestamp field. For device/I/O paths where the wall-clock timestamp matters, compare that separately.

◆ check_param_round_trip()

CheckResult pulp::format::validation::check_param_round_trip ( const state::ParamRange & range,
float raw,
float tol = 1.0e-4f )
inline

Verify a parameter's normalize↔denormalize map is correct for raw.

Host automation lives in the normalized [0,1] domain; the round trip real → normalize → denormalize must project raw onto a representable value (honoring step quantization and skew). A naive "denormalize(normalize(raw)) == raw" check spuriously fails on any quantized or non-linear parameter, but an idempotency-only check is too weak — a broken map that always returns 0.5 / a constant is idempotent and would pass. So we check three independent things, fail-closed on any non-finite result:

  1. Finiteness + domain: normalize(raw) is finite and in [0,1]; denormalize is finite and in range.
  2. An independent expected value for the common linear/stepped case (clamp, then snap to step) — this catches a constant/garbage map.
  3. For skewed ranges, the endpoint anchors (min↔0, max↔1) hold — a constant map cannot satisfy both anchors unless min == max. tol is an absolute tolerance in the parameter's own units; it is widened internally by a small fraction of the range span so legitimate wide/skewed ranges (e.g. 20..20000 Hz) don't false-fail on float precision.

◆ check_peak_below()

CheckResult pulp::format::validation::check_peak_below ( std::span< const float > samples,
float peak_limit )
inline

True when every sample's magnitude is at or below peak_limit (linear, not dB).

Use for "this effect must not exceed unity / must not clip" bounds. Fails closed on a non-finite sample (a blown-up filter must not pass a peak-bound check).

◆ check_silent()

CheckResult pulp::format::validation::check_silent ( std::span< const float > samples,
float eps = 1.0e-7f )
inline

True when every sample's magnitude is at or below eps — the inverse of check_any_nonzero, for "bypass with zero input must be silent" style tests.

Fails closed on a non-finite sample (NaN/Inf is never "silent").

◆ check_state_round_trip()

CheckResult pulp::format::validation::check_state_round_trip ( HeadlessHost & host)
inline

Verify plugin state actually round-trips: save a snapshot, perturb every parameter to a different value, restore the snapshot, and require the re-saved bytes to match the original.

The perturbation step is what makes this catch the most common state bug — a load_state() that returns true but silently ignores the blob. Without it, a no-op loader would "pass" because the host's state never changed.

Covers the StateStore parameter portion generically. Custom (non-parameter) plugin state from Processor::serialize_plugin_state() is exercised by the save/load/save path but is not perturbed here, since its shape is unknown to this helper; cover that with a processor-specific test if the plugin owns custom state.

Note
Mutates host. On success the host's parameter values are restored to the original snapshot; on a detected bug they may be left perturbed.

◆ check_sysex_payload_equal()

CheckResult pulp::format::validation::check_sysex_payload_equal ( std::span< const uint8_t > expected,
std::span< const uint8_t > actual )
inline

True when two SysEx payloads carry identical bytes.