Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,11 @@ permissions:
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: [self-hosted, Linux, X64, codeql-mini]
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
# One CodeQL language at a time: this host has 4 cores and a single
# codeql-mini runner, so a parallel matrix starves ordinary CI jobs.
# Analyze one language at a time to limit concurrent analysis jobs.
max-parallel: 1
matrix:
language:
Expand Down
37 changes: 37 additions & 0 deletions AAX_PARAMETER_DELIVERY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Private AAX parameter-only delivery

`io.intrect.aax-parameter-delivery/1` is separate from standard CLAP
`params.flush`. It requires an explicit
`ClapPlugin::CLAP_AAX_CONCURRENT_PARAMETER_DELIVERY` opt-in, no MIDI input/output,
no polyphonic modulation, and disabled sample-accurate automation.

The plugin must tolerate atomic parameter values changing during DSP and use
thread-safe parameter callbacks. The extension never borrows the mutable plugin
instance or its audio/note event buffers.

The host serializes incoming parameter batches with a nonblocking gate. Audio
tries once: when background owns the gate, audio/transport processing continues
without incoming parameter/modulation events. A later owning callback consumes
the queued batch. Lifecycle and state callbacks must not overlap delivery.

Background calls `try_begin` before consuming any host input. Zero means busy;
a nonzero token identifies that acquisition. The same thread supplies that token
to `flush` and calls `end` exactly once, including on failure. Failed acquisition,
stale tokens and wrong-thread calls cannot release another owner's gate.
Exhausted token generations refuse acquisition instead of reusing a token.

`flush` validates the entire stable input batch before applying any value. It
accepts only finite monophonic values in the advertised CLAP range for known parameter IDs, and emits only GUI
parameter values/gestures. A rejected input batch must be retained in order by the host. Rejected output is
retained ahead of later gestures/values and acknowledged events are not replayed.
Audio and background parameter-output drains share an atomic gate, preserving
gesture/value order. Audio defers this drain when busy and continues DSP.

The ABI is `repr(C)` with three CLAP C function pointers: `try_begin(plugin) ->
u64`, `flush(plugin, token, input, output) -> u32`, and `end(plugin, token) ->
bool`. Standard CLAP flush and lifecycle contracts remain unchanged. This is a
private Intrect host/plugin agreement, not permission to invoke standard flush
concurrently with processing.

Flush results are 0 (invalid/unowned input, retain batch), 1 (input consumed, output
still pending, discard batch and retry output), or 2 (complete delivery).
18 changes: 17 additions & 1 deletion src/context/process.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! A context passed during the process function.

use super::PluginApi;
use crate::prelude::{Plugin, PluginNoteEvent};
use crate::prelude::{Plugin, PluginNoteEvent, ProcessMode};

/// Contains both context data and callbacks the plugin can use during processing. Most notably this
/// is how a plugin sends and receives note events, gets transport information, and accesses
Expand Down Expand Up @@ -92,6 +92,22 @@ pub trait ProcessContext<P: Plugin> {
/// monophonic modulation when dropping the capacity down to 1.
fn set_current_voice_capacity(&self, capacity: u32);

/// The render mode the host has requested **after** [`Plugin::initialize()`], for plugin APIs
/// that let a host switch it mid-session. `None` means the API has no such mechanism, in which
/// case [`BufferConfig::process_mode`][crate::prelude::BufferConfig::process_mode] from the
/// last `initialize()` call is still authoritative.
///
/// CLAP hosts may call `clap_plugin_render::set()` at any time on the main thread, for
/// instance to render an offline bounce faster than real time without re-activating the
/// plugin. That change only reaches `BufferConfig` on the next `initialize()`, so a plugin
/// whose real-time path cannot keep up with faster-than-real-time processing (e.g. one that
/// hands audio to a worker thread and polls for results without blocking) can read this to
/// switch to a blocking strategy for the duration of the bounce. The value is a lock-free
/// atomic load and is safe to call from the audio thread.
fn host_render_mode(&self) -> Option<ProcessMode> {
None
}

// TODO: Add this, this works similar to [GuiContext::set_parameter] but it adds the parameter
// change to a queue (or directly to the VST3 plugin's parameter output queues) instead of
// using main thread host automation (and all the locks involved there).
Expand Down
9 changes: 9 additions & 0 deletions src/plugin/clap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ pub trait ClapPlugin: Plugin {
/// browser.
const CLAP_FEATURES: &'static [ClapFeature];

/// Opt into Intrect's private AAX parameter-only delivery extension.
///
/// This permits atomic parameter values and GUI parameter gestures to be updated while
/// `Plugin::process()` runs. The plugin must read parameters through their atomic getters,
/// have thread-safe parameter callbacks, and tolerate a value changing within a block.
/// Sample-accurate automation, MIDI and polyphonic modulation are not supported by this
/// extension. Standard CLAP `params.flush` retains its normal thread/exclusion contract.
const CLAP_AAX_CONCURRENT_PARAMETER_DELIVERY: bool = false;

/// If set, this informs the host about the plugin's capabilities for polyphonic modulation.
const CLAP_POLY_MODULATION_CONFIG: Option<PolyModulationConfig> = None;

Expand Down
1 change: 1 addition & 0 deletions src/wrapper/clap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#[macro_use]
mod util;

pub mod aax_params;
mod context;
mod descriptor;
pub mod features;
Expand Down
Loading
Loading