Getting Started

Installation

  1. Download ConjureDSP
  2. Open the .pkg installer and follow the prompts
  3. Launch your DAW and load ConjureDSP as an Audio Unit effect

Your First Effect

ConjureDSP comes with a built-in code editor. When you load the plugin, you’ll see the code editor with a default passthrough script.

Try replacing it with a simple gain effect:

PARAMS = {
    "gain": {"min": 0.0, "max": 1.0, "unit": "", "default": 0.5},
}

def process(inputs, outputs, frame_count, sample_rate, params):
    gain = params["gain"]
    for ch in range(len(inputs)):
        outputs[ch][:frame_count] = inputs[ch][:frame_count] * gain

The process function is called for every audio buffer. You receive:

  • inputs / outputs — lists of numpy float32 arrays, one per channel
  • frame_count — number of valid samples in this buffer
  • sample_rate — current sample rate (e.g., 44100)
  • params — a dict of denormalized parameter values keyed by name (when PARAMS is defined)

Python

The process() Function

Every Python DSP script must define a process function:

def process(inputs, outputs, frame_count, sample_rate, params):
    # Your DSP code here
    pass

Parameters

ArgumentTypeDescription
inputslist[np.ndarray]Input audio buffers (float32), one per channel
outputslist[np.ndarray]Output audio buffers (float32), one per channel
frame_countintNumber of valid samples in this buffer
sample_ratefloatCurrent sample rate
paramsdict[str, float]Denormalized parameter values keyed by name (when PARAMS is defined)

Important Notes

  • Arrays are pre-allocated — do not create new arrays in process()
  • Write directly to outputs[ch][:frame_count] using slice assignment
  • The function is called on the real-time audio thread, so avoid allocations and I/O

Defining Parameters

Define a module-level PARAMS dict to declare automatable parameters:

PARAMS = {
    "rate": {
        "min": 0.5,
        "max": 20.0,
        "default": 5.0,
        "unit": "Hz",
        "curve": "log"
    },
    "depth": {
        "min": 0.0,
        "max": 1.0,
        "default": 0.5,
        "unit": "",
        "curve": "linear"
    }
}

Each key becomes a named parameter in the DAW. The params argument in process() is then a dict — access values with params["rate"], params["depth"], etc.

Supported curve types: "linear" (default) and "log" (logarithmic, useful for frequency and time).

Rust

Writing Rust DSP

Your Rust code is compiled and executed in a sandboxed runtime. Implement the process function:

#[no_mangle]
pub extern "C" fn process(
    input: *const f32,
    output: *mut f32,
    channels: i32,
    frame_count: i32,
    sample_rate: f32,
) {
    // Your DSP code here
}

Input and output buffers are laid out sequentially by channel:

[ch0_frame0, ch0_frame1, ..., ch1_frame0, ch1_frame1, ...]

Memory and Parameters

Your module exports buffer pointers so the host can write input samples and parameters before calling process:

static mut INPUT_BUF: [f32; MAX_CH * MAX_FR] = [0.0; MAX_CH * MAX_FR];
static mut OUTPUT_BUF: [f32; MAX_CH * MAX_FR] = [0.0; MAX_CH * MAX_FR];
static mut PARAMS_BUF: [f32; 16] = [0.0; 16];

#[no_mangle]
pub extern "C" fn get_input_ptr() -> i32 { unsafe { INPUT_BUF.as_ptr() as i32 } }
#[no_mangle]
pub extern "C" fn get_output_ptr() -> i32 { unsafe { OUTPUT_BUF.as_ptr() as i32 } }
#[no_mangle]
pub extern "C" fn get_params_ptr() -> i32 { unsafe { PARAMS_BUF.as_ptr() as i32 } }

Defining Parameters

Export parameter metadata as a static JSON string:

static METADATA: &str = r#"[{"name":"Rate","min":0.5,"max":20.0,"unit":"Hz","default":5.0}]"#;

#[no_mangle]
pub extern "C" fn get_param_metadata_ptr() -> i32 { METADATA.as_ptr() as i32 }
#[no_mangle]
pub extern "C" fn get_param_metadata_len() -> i32 { METADATA.len() as i32 }

The JSON array uses the same fields as Python’s PARAMS: name, min, max, default, unit, and curve.

Compilation

ConjureDSP bundles a standalone Rust compiler. When you switch to Rust mode, the editor compiles your code on save.

When to Use Rust

  • CPU-intensive algorithms (convolution, physical modeling)
  • Effects that need deterministic performance
  • When Python’s overhead is noticeable at low buffer sizes

For most effects (gain, tremolo, filters, delay), Python is fast enough and much easier to iterate with.

API Reference

Python API

process()

Called for every audio buffer. Must write output samples to outputs.

def process(inputs, outputs, frame_count, sample_rate, params):

PARAMS

Optional. A module-level dict that declares automatable parameters. Each key is the parameter name; each value is a dict of metadata.

PARAMS = {
    "cutoff": {"min": 20.0, "max": 20000.0, "default": 1000.0, "unit": "Hz", "curve": "log"},
    "resonance": {"min": 0.0, "max": 1.0, "default": 0.5, "unit": ""},
}

When PARAMS is defined, the params argument in process() is a dict with denormalized values keyed by name (e.g., params["cutoff"] returns a value between 20.0 and 20000.0).

Each parameter entry supports these fields:

FieldTypeRequiredDescription
minfloatYesMinimum value
maxfloatYesMaximum value
defaultfloatYesDefault value
unitstrNoUnit label (e.g., “Hz”, “dB”, “ms”)
curvestrNo"linear" (default) or "log"

Available Libraries

The embedded Python runtime includes:

  • numpy — Array operations, FFT, linear algebra
  • scipy — Signal processing, filters, interpolation

Parameter Curves

Linear

Value maps linearly from min to max. Best for most parameters.

Logarithmic

Value maps on a log scale. Best for frequency (20 Hz – 20 kHz) and time parameters where human perception is logarithmic.