Programmatically build Max for Live (.amxd) devices
Project description
m4l-builder
Build Max for Live devices in Python. Write scripts, emit .amxd files straight to your Ableton User Library. No Max GUI required. Everything is version-controllable, scriptable, and reproducible. Zero runtime dependencies -- pure stdlib.
Install
pip install m4l-builder
Quick start
from m4l_builder import AudioEffect, WARM, device_output_path
device = AudioEffect("My Gain", width=150, height=110, theme=WARM)
device.add_panel("bg", [0, 0, 150, 110])
device.add_dial("gain", "Gain", [10, 6, 50, 90],
min_val=-70.0, max_val=6.0, initial=0.0,
unitstyle=4, annotation_name="Gain")
device.add_newobj("mul_l", "*~ 1.", numinlets=2, numoutlets=1, outlettype=["signal"])
device.add_newobj("mul_r", "*~ 1.", numinlets=2, numoutlets=1, outlettype=["signal"])
device.add_newobj("db2a", "dbtoa", numinlets=1, numoutlets=1, outlettype=[""])
device.add_newobj("pk", "pack f 20", numinlets=2, numoutlets=1, outlettype=[""])
device.add_newobj("ln", "line~", numinlets=2, numoutlets=2, outlettype=["signal", "bang"])
device.add_line("obj-plugin", 0, "mul_l", 0) # stereo in
device.add_line("obj-plugin", 1, "mul_r", 0)
device.add_line("mul_l", 0, "obj-plugout", 0) # stereo out
device.add_line("mul_r", 0, "obj-plugout", 1)
device.add_line("gain", 0, "db2a", 0) # dial -> dbtoa -> smooth -> gain
device.add_line("db2a", 0, "pk", 0)
device.add_line("pk", 0, "ln", 0)
device.add_line("ln", 0, "mul_l", 1)
device.add_line("ln", 0, "mul_r", 1)
device.build(device_output_path("My Gain"))
Restart Ableton (or refresh the browser) and your device shows up in the User Library.
Key concepts
Device types
Three device types, matching what Ableton expects:
from m4l_builder import AudioEffect, Instrument, MidiEffect
fx = AudioEffect("FX", 300, 170) # auto-adds plugin~/plugout~
synth = Instrument("Synth", 400, 200) # no auto I/O
midi = MidiEffect("MIDI FX", 200, 100) # MIDI only
AudioEffect auto-wires stereo plugin~ / plugout~ objects (IDs obj-plugin, obj-plugout). Instruments and MIDI effects leave I/O to you.
DSP blocks
All DSP functions return (boxes, lines) tuples. Compose them with add_dsp:
from m4l_builder import gain_stage, highpass_filter
boxes, lines = gain_stage("gain")
device.add_dsp(boxes, lines)
Filters: highpass, lowpass, bandpass, notch, onepole, shelves, tilt EQ, 3-band crossover, peaking EQ, allpass Dynamics: compressor, limiter, envelope follower, gate/expander, sidechain detect, multiband compressor Delay/Reverb: delay line, feedback delay, reverb network, FDN reverb, convolver Modulation: LFO (4 waveforms), tremolo, transport LFO, morphing LFO MIDI: notein/out, ctlin/out, velocity curve, transpose, arpeggiator, chord, pitch quantize, midi learn Synthesis: wavetable osc, noise, oscillator bank, ADSR, poly voices, grain cloud Spectral: spectral gate, crossover, vocoder, phase vocoder Routing: selector, send/receive (signal + message), matrix mixer, sidechain routing Utility: param smooth, tempo sync, sample and hold, bitcrusher, coll/dict/pattr storage
90+ blocks total. See docs/api.md for the full list with signatures.
Engines (jsui visualizations)
JavaScript generators for Max's jsui object. Each returns an ES5 string for mgraphics/Cairo rendering:
from m4l_builder.engines import filter_curve_js
device.add_jsui("display", [10, 30, 200, 80],
js_code=filter_curve_js(), numinlets=3)
18 generators available: filter curves, EQ, envelopes, spectrum, waveforms, XY pad, piano roll, step grids, grain clouds, vocoder bands, and more.
Themes
Seven built-in color themes. Pass to the device constructor and all UI elements inherit colors automatically:
from m4l_builder import AudioEffect, MIDNIGHT, WARM, COOL, LIGHT, FOREST, VIOLET, SOLAR
device = AudioEffect("FX", 300, 170, theme=MIDNIGHT)
Build a theme from just an accent color:
from m4l_builder import Theme
my_theme = Theme.from_accent([0.8, 0.3, 0.1, 1.0])
Recipes
Pre-wired DSP combos for common patterns. They add objects to a device and return IDs for further wiring:
from m4l_builder import gain_controlled_stage, dry_wet_stage
ids = gain_controlled_stage(device, "out", [10, 10, 50, 70])
# ids["gain"] is the *~ you wire into your signal chain
Available: gain_controlled_stage, dry_wet_stage, tempo_synced_delay, midi_note_gate.
Layout helpers
Automatic positioning with Row, Column, and Grid context managers:
with device.row(10, 10, spacing=8, height=70) as r:
r.add_dial("d1", "Param1", width=50)
r.add_dial("d2", "Param2", width=50)
r.add_dial("d3", "Param3", width=50)
Subpatchers
Nested patchers for organizing signal chains:
from m4l_builder import Subpatcher
sub = Subpatcher("processor")
sub.add_newobj("in", "inlet~", numinlets=1, numoutlets=1)
sub.add_newobj("out", "outlet~", numinlets=1, numoutlets=1)
sub.add_line("in", 0, "out", 0)
device.add_subpatcher(sub, "proc_box", [30, 100, 80, 20])
Round-trip editing with from_amxd
Read an existing .amxd back into a Device, modify it, write it out:
device = AudioEffect.from_amxd("path/to/effect.amxd")
device.add_dial("new_knob", "NewParam", [10, 10, 50, 70])
device.build("path/to/modified.amxd")
Examples
40+ examples in examples/. A few highlights:
| File | What it builds |
|---|---|
simple_gain.py |
Minimal starter, single gain dial |
stereo_delay.py |
L/R delay with feedback saturation |
simple_compressor.py |
Threshold, ratio, attack/release |
parametric_eq.py |
JSUI custom EQ curve display |
poly_synth.py |
Polyphonic synth with wavetable |
midi_arpeggiator.py |
MIDI arpeggiator |
from_amxd_demo.py |
Round-trip: build, read back, modify |
uv run python examples/simple_gain.py # run one
for f in examples/*.py; do uv run python "$f"; done # build all
Development
git clone https://github.com/alaarab/m4l-builder.git
cd m4l-builder
uv pip install -e .
# Tests
uv run pytest tests/ -v
# With coverage
uv run pytest tests/ --cov=m4l_builder --cov-report=term-missing
API reference
Full API docs at docs/api.md.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file m4l_builder-0.7.0.tar.gz.
File metadata
- Download URL: m4l_builder-0.7.0.tar.gz
- Upload date:
- Size: 91.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8a48a52564b1ad72f824492c263d5cff948273eb9d78bab2ba92196b005cd43e
|
|
| MD5 |
b050fb0c6b34ed5fc616e5e93b003935
|
|
| BLAKE2b-256 |
157233248271f84fe1d4bba92102ee3f4734c2672febbcc3ab38b558ffa174c5
|
File details
Details for the file m4l_builder-0.7.0-py3-none-any.whl.
File metadata
- Download URL: m4l_builder-0.7.0-py3-none-any.whl
- Upload date:
- Size: 105.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17b12258295ffbb60c2709dc7083653c9ef55dbeb1cfb51ee0547b084d03cc26
|
|
| MD5 |
627afce5b90af92737b596d9af48b1ae
|
|
| BLAKE2b-256 |
48a79f2ebf8009916b45c8574e4022d87f0374af68853c9eb6624651d43651f9
|