Skip to content

Nonlinear Space and Zero-Latency Convolution

Pulp has two advanced space processors with different jobs. NonlinAmbience generates a designed, gated/reverse/nonlinear tap envelope. ZeroLatencyConvolver runs an imported impulse response with a direct head and scheduled partitioned tail while reporting zero algorithmic latency.

Nonlinear ambience

#include <pulp/signal/nonlin_ambience.hpp>

pulp::signal::NonlinAmbience ambience;
ambience.prepare(sample_rate);
ambience.set_program(pulp::signal::NonlinProgram::gated);
ambience.set_length_ms(900.0);
ambience.set_density_pct(70.0);
ambience.set_width_pct(100.0);
ambience.set_mix_pct(100.0);
ambience.process(left, right, num_frames);

Use set_topology() on a non-audio thread when applying a coherent snapshot. Use request_topology() for hosted automation; it bounds the audio-thread work and swaps the prepared tap bank. envelope(tau), tap(), tap_norm(), and worst_case_gain() expose the actual design for editors and tests.

Zero-latency convolution

#include <pulp/signal/zero_latency_convolver.hpp>

pulp::signal::ZeroLatencyConvolver convolver;
convolver.prepare(sample_rate, max_block, 2);
convolver.set_normalize_mode(pulp::signal::IrNormalizeMode::peak);
convolver.load_impulse_response(ir_channels, ir_channel_count,
                                ir_length, ir_sample_rate);

convolver.process(input_channels, output_channels, num_frames);

prepare() and load_impulse_response() allocate and perform design work; call both off the audio thread. process() accepts any n <= max_block and remains allocation-free. IR resampling, trim/fade, normalization, filters, and true stereo routing are resolved at load time or through the documented controls.

latency_samples() is zero by construction. Predelay is an artistic delay and is exposed separately through predelay_samples(). The per-level scheduling accessors and last_block_cost() are intended for tests, diagnostics, and budget-aware hosts; normal plugins do not need to schedule partitions themselves.

The complete advanced DSP API lists every method on both processors.

Shared GPU convolution trace

The experimental GpuConvolver and GpuConvolutionReverb routes accept an optional trace configuration before prepare():

pulp::gpu_audio::GpuConvolverTraceConfig trace;
trace.enabled = true;
trace.capture_admissions = true;
trace.success_stride = 1;

pulp::gpu_audio::GpuConvolutionReverbConfig config;
config.gpu_enabled = true;
config.trace = trace;
pulp::gpu_audio::GpuConvolutionReverb reverb(config);
// prepare() runs with audio stopped.
reverb.prepare();

The callback only publishes fixed-size records to bounded SPSC queues. It does not allocate, lock, wait, or call Perfetto. The transport worker emits the gpu.audio.session, admission, terminal, delivery, and counter events through the host's active Pulp trace session. It is the sole live queue consumer and the node's final close drains any records that remain; release the route before stopping the host's trace owner and verify the flushed trace. Each lane receives its own engine ID, while (generation, sequence) identifies a block within that lane and terminal GPU and delivered-audio dispositions remain separate.