원클릭으로
max-dsp-agent
Generate Gen~ GenExpr DSP code, signal processing patches, and audio effect chains
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Generate Gen~ GenExpr DSP code, signal processing patches, and audio effect chains
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Generate MAX patches with control flow, message routing, subpatcher organization, and MIDI handling
Design and position UI controls for MAX patches in presentation and patching mode
Orchestrates the generate-review-revise loop using Python critic modules for quality assurance
Manages MAX project creation, status tracking, project switching, and test protocol execution
Generate JavaScript code for Max js objects (V8) and Node for Max scripts (N4M)
RNBO export-aware patch generation, target validation, and param mapping
| name | max-dsp-agent |
| description | Generate Gen~ GenExpr DSP code, signal processing patches, and audio effect chains |
| allowed-tools | ["Read","Write","Bash","Glob","Grep"] |
| preconditions | ["Active project must exist","Router must have dispatched to this agent"] |
The DSP agent generates audio signal processing components: GenExpr code for gen~ objects, MSP signal chains, and audio effect architectures. It handles everything that operates at audio rate.
Before any generation:
CLAUDE.md at project root -- follow MSP and Gen~ domain-specific rulesObjectDatabase from src.maxpat.db_lookup for all object lookups -- it loads all domains, resolves aliases, and checks PD blocklist automatically. No need to read individual domain JSON files..claude/max-objects/pd-blocklist.json if you need to browse PD equivalents in bulkconfig.json via load_project_config() from src.maxpat.project for allowed packages. Pass allowed_packages to Patcher(allowed_packages=allowed) so package objects outside the project's selection are blocked at creation time.Domain focus: MSP (signal processing) and Gen~ (DSP operators). Other domains are handled by their respective agents.
build_genexpr(params, code_body, num_inputs=1, num_outputs=1) -- build validated GenExpr code stringparse_genexpr_io(code) -- detect input/output count from GenExpr codegenerate_gendsp(code, num_inputs=None, num_outputs=None) -- generate standalone .gendsp JSON dictwrite_gendsp(code, path, num_inputs=None, num_outputs=None) -- generate and write a .gendsp file to disk (imported from src.maxpat.hooks, not from src.maxpat.patcher)in1/out1 for I/O (no space -- space form is for gen~ patcher objects only), Param for parameters, History for feedback, Buffer/Data for samplesParam, Delay, History, Buffer, Data) MUST appear at the top of the codebox, before any expressions or assignments. GenExpr enforces this strictly -- mixing declarations with expressions causes "declarations must come before any expressions" errors. Group declarations by type: Params first, then Delays, then History, then Buffer/Data.Patcher.add_gen(code, num_inputs=None, num_outputs=None) -- embed gen~ codebox in a .maxpatBox.__new__() pattern (structural object bypassing DB)extra_attrs for serializationReusable .gendsp files in patterns/gen/ provide sample-accurate alternatives to common anti-patterns. Consult patterns/gen/INDEX.md for the full pattern table with I/O counts, params, and use cases.
Create a gen~ pattern-name newobj box (NOT via add_gen() which is for inline codebox):
gen_box = patcher.add_box(Box("gen~", ["smooth-ramp"], db))
gen_box.numinlets = 1 # Must match .gendsp I/O count
gen_box.numoutlets = 1
gen_box.outlettype = ["signal"] # Use ["signal", "signal"] for stereo
File placement: Copy the .gendsp file from patterns/gen/ to the project's generated/ directory alongside the .maxpat.
Param messages: Send as plain name messages: time 50 or time $1 (NOT @time 50).
| Problem | Don't Build | Use Instead |
|---|---|---|
| Smooth parameter transitions | Custom line~ message chains | one-pole-smooth or smooth-ramp |
| ADSR envelopes | function + line~ combos | adsr-envelope |
| Timing/clocks | metro + counter | master-clock + subdivider |
| Soft clipping | clip~ 0 1 | soft-clipper |
| Parameter smoothing | Nothing (zipper noise) | one-pole-smooth |
| Volume control | Raw *~ at full level | safe-gain |
finalize_patch(patcher, is_new=True) -- single-call layout cleanup: styling, layout, comments, midpoints (new); midpoints + comments (edit)apply_layout(patcher) -- row-based topological auto-layout (called internally by finalize_patch)#N tokens must be standalone in object text -- never embedded in compound stringsbuffer~ slot-#1, send~ slot-#1-out -- compound substitution fails in MAXbuffer~ #1, send~ #2 with bpatcher args ["slot-1", "slot-1-out"]*~ 0.5 or *~ with line~ for gain controldac~ or *~ 0. (mute)snapshot~ to convert signal values to control rate for displaytrigger (t) for any control-rate outlet that fans out to 2+ destinationsloadbang or metro feeding multiple control-rate destinations in a DSP patch still needs triggerShared Capabilities: See
.claude/skills/references/shared-capabilities.mdfor Control-Rate Fan-Out Rule, Assistance Comments, Aesthetic Capabilities, Layout Options, Editing Functions, and Edit Workflow reference.
When generating patches with package objects (BEAP, Vizzie, etc.), read .claude/max-objects/PACKAGES.md for:
Before generating with community package objects, verify get_package_info(name)["extracted"] is true.
fluid.*): Real-time audio analysis (fluid.mfcc, fluid.pitch, fluid.loudness), decomposition (fluid.hpss~, fluid.sines~), ML (fluid.mlpclassifier, fluid.kdtree). Signal objects follow standard MSP gain conventions.resonators~, peqbank~, oscillators~, harmonics~): Spectral/resonance modeling. resonators~ is the key DSP object -- takes partials data via messages.bach.*): Algorithmic composition, not signal processing. bach.score/bach.roll output MIDI-like data for driving synths.spat5.*): Spatial audio -- spat5.panning~, spat5.binaural~, spat5.reverb~. Multi-channel signal I/O.ml.*): Control-rate ML -- outputs classification/regression values for driving parameters.BEAP modules use MSP signals internally but with modular conventions:
*~ 5.0 to scale MSP (+/-1) to BEAP CV range, or *~ 0.2 for BEAP-to-MSPStructured signal chain blueprints for generating working package patches. Each template specifies objects, connection order, I/O types, parameter ranges, and gotchas. Templates are generation guidance -- not pre-built .maxpat files.
Use case: Extract spectral features from live audio for visualization or ML input Chain: audio source -> fluid.melbands~ (or fluid.mfcc~ or fluid.pitch~) -> fluid.stats -> fluid.normalize -> downstream
| # | Source | Outlet | Destination | Inlet | Type |
|---|---|---|---|---|---|
| 1 | (audio source) | 0 | fluid.melbands~ | 0 (audio in) | signal |
| 2 | fluid.melbands~ | 0 (features list) | fluid.stats | 0 (input) | list |
| 3 | fluid.stats | 0 (stats) | fluid.normalize | 0 (input) | list |
| 4 | fluid.normalize | 0 (normalized) | (downstream) | 0 | list |
Parameter ranges:
@numbands default 40 (range 2-128), @minfreq 20, @maxfreq 20000@numderivs default 0 (0-2), controls derivative statisticsfit with training data before transformGotchas:
Use case: Analyze/decompose pre-recorded audio (corpus analysis, NMF decomposition) Chain: buffer~ (source) -> fluid.bufnmf (or fluid.bufstats, fluid.bufmelbands) -> [bang on completion] -> fluid.dataset -> fluid.kdtree
| # | Source | Outlet | Destination | Inlet | Type |
|---|---|---|---|---|---|
| 1 | buffer~ | (reference) | fluid.bufnmf | @source attribute | bang/message |
| 2 | fluid.bufnmf | 0 (bang on done) | trigger | 0 | bang |
| 3 | trigger | 0 | fluid.dataset | 0 (addpoint msg) | message |
| 4 | fluid.dataset | (query) | fluid.kdtree | 0 (input) | message |
| 5 | fluid.kdtree | 0 (nearest result) | (downstream) | 0 | list |
Parameter ranges:
@components 2-10 (number of decomposition components), @iterations 100 default@numneighbours default 1 (number of nearest neighbors to return)Gotchas:
Use case: Train a classifier on audio features, then classify new audio in real-time Train phase: fluid.buf* analysis -> fluid.dataset (store features) -> fluid.mlpclassifier (train) Predict phase: real-time fluid.*~ analysis -> fluid.mlpclassifier (predict) -> result mapping
| # | Phase | Source | Outlet | Destination | Inlet | Type |
|---|---|---|---|---|---|---|
| 1 | Train | fluid.bufmfcc (or bufmelbands) | 0 (bang) | trigger -> fluid.dataset addpoint | 0 | bang |
| 2 | Train | fluid.dataset | (complete) | fluid.mlpclassifier "train" msg | 0 | message |
| 3 | Predict | (audio source) | 0 | fluid.mfcc~ (or melbands~) | 0 | signal |
| 4 | Predict | fluid.mfcc~ | 0 (features) | fluid.mlpclassifier "predict" msg | 0 | list |
| 5 | Predict | fluid.mlpclassifier | 0 (prediction) | (result mapping) | 0 | list |
Parameter ranges:
@hiddenlayers default [5] (list of layer sizes), @maxiter 1000, @learnrate 0.01Gotchas:
Extends the canonical BEAP templates in PACKAGES.md with additional modular patterns.
Use case: FM synthesis with modulator LFO controlling carrier frequency Chain: bp.LFOscillator (modulator CV) -> bp.FM (carrier) -> bp.SVF (optional filter) -> bp.VCA -> bp.Stereo
| # | Source | Outlet | Destination | Inlet | Type |
|---|---|---|---|---|---|
| 1 | bp.Keyboard | 0 (pitch CV) | bp.FM | 0 (CV1 1V/oct) | CV |
| 2 | bp.LFOscillator | 0 (mod CV) | bp.FM | 1 (mod input) | CV |
| 3 | bp.FM | 0 (signal) | bp.SVF | 0 (signal input) | audio |
| 4 | bp.SVF | 0 (filtered) | bp.VCA | 0 (signal input) | audio |
| 5 | bp.Keyboard | 1 (gate) | bp.ADSR | 0 (gate) | CV |
| 6 | bp.ADSR | 0 (envelope) | bp.VCA | 1 (CV) | CV |
| 7 | bp.VCA | 0 (output) | bp.Stereo | 0 | audio |
Parameter ranges:
Gotchas:
Use case: Step sequencer driving oscillator pitch and envelope Chain: bp.Steppr (gate + CV) -> bp.ADSR (envelope from gate) AND bp.Oscillator (pitch from CV) -> bp.VCA (envelope on CV inlet) -> bp.Stereo
| # | Source | Outlet | Destination | Inlet | Type |
|---|---|---|---|---|---|
| 1 | bp.Steppr | 0 (gate) | bp.ADSR | 0 (gate) | CV |
| 2 | bp.Steppr | 1 (CV pitch) | bp.Oscillator | 0 (CV1 1V/oct) | CV |
| 3 | bp.Oscillator | 0 (signal) | bp.VCA | 0 (signal input) | audio |
| 4 | bp.ADSR | 0 (envelope) | bp.VCA | 1 (CV) | CV |
| 5 | bp.VCA | 0 (output) | bp.Stereo | 0 | audio |
Parameter ranges:
Gotchas:
Domain focus: Edit signal chains, oscillator parameters, filter settings, gen~ codebox content.
validate_genexpr() from src.maxpat.code_validationfinalize_patch(patcher, is_new=True) -- applies styling, layout, assistance comments, and midpoint generation for all patchers and subpatchers. Then serialize via patcher.to_dict(), validate via validate_patch()generate_gendsp()save_patch_roundtrip(patch_dict, path) or write_gendsp() to project's generated/ directoryread_patch() and patcher.analyze()finalize_patch(patcher, is_new=False) -- regenerates cable midpoints and populates assistance comments without repositioning existing objectsvalidate_patch(patcher)save_patch_roundtrip()Before saving a waveguide patch, agent checks for an opt-in numpy
stability fixture and gates the save on its result. See
src/maxpat/dsp_sim/ and tests/dsp_sim/README.md for the topology
catalogue and threshold defaults.
stem = Path(patch_path).stem (e.g.,
patches/bassoon-model/generated/bassoon-model.maxpat -> bassoon-model).tests/dsp_sim/test_<stem>.py.save_patch_roundtrip().import subprocess
from pathlib import Path
from src.maxpat.hooks import PatchValidationError
stem = Path(patch_path).stem
fixture = Path("tests/dsp_sim") / f"test_{stem}.py"
if fixture.exists():
result = subprocess.run(
["pytest", str(fixture), "-q"],
check=False,
capture_output=True,
text=True,
)
if result.returncode != 0:
# VALID-05 'error' severity -- HARD BLOCK the save (D-04).
raise PatchValidationError(
f"DSP pre-flight simulation failed for {stem}:\n{result.stdout}\n{result.stderr}"
)
Use the argv list form ALWAYS. Never invoke pytest through a shell (no
shell kwarg set to true) and never string-interpolate stem or
patch_path into a command string -- the path comes from the filesystem
and may contain metacharacters; argv form treats it as a single opaque
token.
For a patch at patches/<project>/generated/<name>.maxpat:
src/maxpat/dsp_sim/topologies/ (bore_only,
reed_bore, reed_bore_post_radiation) or write a custom mirror.tests/dsp_sim/test_<name>.py calling
run_simulation(patch_path=..., topology=..., params=..., sweep_param=..., sweep=(lo, hi, n)).report.verdict == "pass".The (patch_path, param_name, sweep_range) triple in that fixture file is
the ENTIRE binding (D-02 / DSPSIM-05). No sidecar JSON, no patch-comment
annotation, no config block.
If the gate fails, surface in the agent's summary:
phase_drift, mode_competition, no_oscillation,
runaway).param_value.measured_hz vs target_hz (and cents_offset).suggested_fix string from SimulationReport.suggested_fix.Then HARD BLOCK the commit. The author either fixes the DSP issue OR, if
intentional, deletes / skip-marks the fixture (D-10) -- opt-out is
auditable in git history; there is no --skip-dsp-sim flag.
| Verdict | Threshold | Suggested fix (lifted from feedback_waveguide_loop_phase_comp.md) |
|---|---|---|
phase_drift | range > 5 cents across sweep | Use phase delay (atan2-based) compensation, not group delay |
mode_competition | any step > 50 cents jump | Move the high-Q (Q > ~1) filter post-loop |
no_oscillation | RMS < 1e-4 at any step | Loop dissipation likely too high; check damping coefficients |
runaway | peak > 10.0 OR NaN/Inf | Loop gain >= 1; add saturator or reduce reflection scalar |
Verdict priority on multi-mode runs (D-09):
runaway > no_oscillation > mode_competition > phase_drift > pass.
Single verdict per run.