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

Classes

struct  HostTransport
 Format-neutral transport snapshot, already decoded from a host playhead into canonical units (quarter-note beats, samples, real BPM). More...
class  LatencyCompensatedBypassT
 Per-channel dry-signal delay used by an adapter's bypass path. More...
struct  ParamSnapshotState
 Generation-gated pre-process snapshot: which generation it was taken at and whether it can be trusted. More...
struct  MpeSidecar
 Owns the MPE tracker + expression buffer + inbound-offset cursor an adapter wraps around its MIDI decode, plus the descriptor opt-in flag. More...

Typedefs

using LatencyCompensatedBypass = LatencyCompensatedBypassT<kBoundaryMaxChannels>

Functions

void copy_f32_to_f64 (const float *src, double *dst, std::uint32_t count) noexcept
 Widen count floats src → dst as doubles. No-op on a null pointer.
void copy_f64_to_f32 (const double *src, float *dst, std::uint32_t count) noexcept
 Narrow count doubles src → dst as floats. No-op on a null pointer.
void zero_f64 (double *dst, std::uint32_t count) noexcept
 Zero count doubles. No-op on a null pointer.
void apply_host_transport (ProcessContext &ctx, const HostTransport &transport, detail::PlayheadSnapshot &snapshot) noexcept
 Write transport into ctx's transport fields, derive the bar when the host did not supply one, then diff ctx against snapshot to populate tempo_changed / time_sig_changed / transport_changed / transport_started / transport_jump and advance the snapshot in place.
template<std::size_t MaxChannels>
void render_bypass_passthrough (LatencyCompensatedBypassT< MaxChannels > &bypass, float *const *out_ptrs, int out_channels, const float *const *in_ptrs, int in_channels, std::uint32_t count)
 Render one bypassed block: copy main input → output per channel, latency- compensated through bypass so the dry signal stays sample-aligned with the host's plugin-delay-compensation, and zero any output channel without a matching input.
bool apply_param_value (state::ParameterEventQueue &queue, state::StateStore &store, state::ParamID id, std::int32_t sample_offset, float value)
 Apply one incoming host parameter-value event, the way every adapter must.
std::size_t find_param_index (std::span< const state::ParamInfo > all_params, state::ParamID id) noexcept
 Position of id within all_params, or kParamIndexNotFound.
void snapshot_param_values (const state::StateStore &store, std::span< const state::ParamInfo > all_params, std::vector< float > &snapshot)
 Capture every parameter's current value into snapshot, positionally aligned with all_params, so a post-process() diff can spot the values the processor changed.
template<typename AfterRead = void (*)()>
void refresh_param_snapshot (const state::StateStore &store, std::span< const state::ParamInfo > all_params, std::vector< float > &snapshot, ParamSnapshotState &state, std::uint64_t generation, AfterRead &&after_read=[] {})
 Brings snapshot up to date for a block whose store generation is generation, re-reading every value only when a write landed since the last trusted snapshot.
bool changed_since_snapshot (float current, float snapshotted) noexcept
 True when current differs from snapshotted, compared bitwise.

Variables

constexpr std::size_t kBoundaryMaxChannels = 8
 Channel ceiling — the single source of truth for the per-bus channel count every adapter caps at.
constexpr std::size_t kParamIndexNotFound = static_cast<std::size_t>(-1)
 Index returned by find_param_index when the id is not registered.

Typedef Documentation

◆ LatencyCompensatedBypass

Function Documentation

◆ apply_host_transport()

void pulp::format::boundary::apply_host_transport ( ProcessContext & ctx,
const HostTransport & transport,
detail::PlayheadSnapshot & snapshot )
inlinenoexcept

Write transport into ctx's transport fields, derive the bar when the host did not supply one, then diff ctx against snapshot to populate tempo_changed / time_sig_changed / transport_changed / transport_started / transport_jump and advance the snapshot in place.

Pure aside from the in-place snapshot update; no allocation. The snapshot is the adapter's per-instance previous-block state (a PlayheadSnapshot member), so a first block after construction raises no spurious change flags. ctx.sample_rate / ctx.num_samples must already be set — the diff uses them to tell a continuous beat advance from a seek.

◆ apply_param_value()

bool pulp::format::boundary::apply_param_value ( state::ParameterEventQueue & queue,
state::StateStore & store,
state::ParamID id,
std::int32_t sample_offset,
float value )
inline

Apply one incoming host parameter-value event, the way every adapter must.

A host param change has to reach two consumers with different contracts:

  • the sample-accurate DSP cursor (for_each_subblock / ParamCursor), which reads a sorted ParameterEventQueue and applies the value at its exact sample_offset within the block; and
  • the listener path (editor / analysis), fed by the RT-safe set_value_rt (atomic store + non-allocating SPSC push) rather than the heap-allocating set_value.

Doing only the first leaves listeners stale; doing only the second collapses sample-accurate automation to block-rate. Adapters that read parameters once per block (AU v2, LV2 control ports) pass sample_offset = 0.

Returns
false if the DSP queue was full and the event was dropped (the listener write still happened); true otherwise. RT-safe.

◆ changed_since_snapshot()

bool pulp::format::boundary::changed_since_snapshot ( float current,
float snapshotted )
inlinenoexcept

True when current differs from snapshotted, compared bitwise.

Deliberately not current != snapshotted. The question this answers is "did the stored bits move", not "are these numerically equal", and the two differ at the edges every parameter store hits eventually: a NaN parameter would compare unequal to itself under != and be republished on every single block forever, while +0.0/-0.0 compare equal under != and would silently drop a real store write. RT-safe.

◆ copy_f32_to_f64()

void pulp::format::boundary::copy_f32_to_f64 ( const float * src,
double * dst,
std::uint32_t count )
inlinenoexcept

Widen count floats src → dst as doubles. No-op on a null pointer.

◆ copy_f64_to_f32()

void pulp::format::boundary::copy_f64_to_f32 ( const double * src,
float * dst,
std::uint32_t count )
inlinenoexcept

Narrow count doubles src → dst as floats. No-op on a null pointer.

◆ find_param_index()

std::size_t pulp::format::boundary::find_param_index ( std::span< const state::ParamInfo > all_params,
state::ParamID id )
inlinenoexcept

Position of id within all_params, or kParamIndexNotFound.

The position — not the id — is the key for every per-block scratch vector an adapter sizes off all_params() (the value snapshot, the skip-set, the per-parameter host queue cache), so a publication path holding an id has to resolve it back to an index.

First match wins. StateStore permits an id to be registered more than once and resolves reads/writes to the latest such slot (see the duplicate- ParamID contract in StateStore::reset_triggers_rt), so under a duplicate id this returns a slot the store itself no longer reads. Preserved as-is because it is what every adapter's publication path has always done. RT-safe.

◆ refresh_param_snapshot()

template<typename AfterRead = void (*)()>
void pulp::format::boundary::refresh_param_snapshot ( const state::StateStore & store,
std::span< const state::ParamInfo > all_params,
std::vector< float > & snapshot,
ParamSnapshotState & state,
std::uint64_t generation,
AfterRead && after_read = [] {} )
inline

Brings snapshot up to date for a block whose store generation is generation, re-reading every value only when a write landed since the last trusted snapshot.

A writer advances the generation before it stores its value, so a snapshot is trusted only when no writer was in flight when it started and none had landed when it ended; a check at the end alone would accept a value read just before an in-flight writer stored its new one, and that parameter would later be reported to the host as a plugin-side change it never made. after_read runs between the read and the final check (tests use it to finish a writer there).

◆ render_bypass_passthrough()

template<std::size_t MaxChannels>
void pulp::format::boundary::render_bypass_passthrough ( LatencyCompensatedBypassT< MaxChannels > & bypass,
float *const * out_ptrs,
int out_channels,
const float *const * in_ptrs,
int in_channels,
std::uint32_t count )
inline

Render one bypassed block: copy main input → output per channel, latency- compensated through bypass so the dry signal stays sample-aligned with the host's plugin-delay-compensation, and zero any output channel without a matching input.

This is the single implementation of the float-buffer bypass pass-through the AU v2/v3 and AAX adapters share; VST3 and CLAP keep their own inline loops because they additionally marshal f64 host buffers (see the per-format usage of LatencyCompensatedBypass above).

All-or-nothing across the block. Compensation is applied only when the boundary owns a dry-delay line for every output channel — a channel count above bypass.channel_capacity() falls back to a zero-delay copy for the whole block, degrading uniformly rather than mixing delayed and undelayed channels (matches the VST3/CLAP delayed gate). A zero reported latency collapses to a straight passthrough at no cost.

out_ptrs / in_ptrs are the per-channel float pointers (either may hold null entries — a null output channel is skipped, a null/absent input channel yields silence). count is the block frame count. RT-safe: no allocation, no lock (the delay lines were sized in bypass.prepare() off the audio thread).

◆ snapshot_param_values()

void pulp::format::boundary::snapshot_param_values ( const state::StateStore & store,
std::span< const state::ParamInfo > all_params,
std::vector< float > & snapshot )
inline

Capture every parameter's current value into snapshot, positionally aligned with all_params, so a post-process() diff can spot the values the processor changed.

snapshot is resized to match; the adapter must have reserved it to all_params.size() off the audio thread (at activate/setup) or the first block grow-and-allocates. RT-safe given that reservation.

◆ zero_f64()

void pulp::format::boundary::zero_f64 ( double * dst,
std::uint32_t count )
inlinenoexcept

Zero count doubles. No-op on a null pointer.

Variable Documentation

◆ kBoundaryMaxChannels

std::size_t pulp::format::boundary::kBoundaryMaxChannels = 8
inlineconstexpr

Channel ceiling — the single source of truth for the per-bus channel count every adapter caps at.

clap_adapter::kMaxChannels and au::kMaxChannels are static_cast<int> aliases of this value; the LV2 adapter keeps an independent kMaxChannels = 8 literal (its header deliberately does not pull in this one) cross-referenced back here. Change the ceiling here.

◆ kParamIndexNotFound

std::size_t pulp::format::boundary::kParamIndexNotFound = static_cast<std::size_t>(-1)
inlineconstexpr

Index returned by find_param_index when the id is not registered.