Getting Started
Installation
- Download ConjureDSP
- Open the
.pkginstaller and follow the prompts - 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 channelframe_count— number of valid samples in this buffersample_rate— current sample rate (e.g., 44100)params— a dict of denormalized parameter values keyed by name (whenPARAMSis 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
| Argument | Type | Description |
|---|---|---|
inputs | list[np.ndarray] | Input audio buffers (float32), one per channel |
outputs | list[np.ndarray] | Output audio buffers (float32), one per channel |
frame_count | int | Number of valid samples in this buffer |
sample_rate | float | Current sample rate |
params | dict[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:
| Field | Type | Required | Description |
|---|---|---|---|
min | float | Yes | Minimum value |
max | float | Yes | Maximum value |
default | float | Yes | Default value |
unit | str | No | Unit label (e.g., “Hz”, “dB”, “ms”) |
curve | str | No | "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.