.claude/skills/max-dsp-agent/SKILL.md
Generate Gen~ GenExpr DSP code, signal processing patches, and audio effect chains
npx skillsauth add taylorbrook/MAX-MSP_CC_Framework max-dsp-agentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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()data-ai
Design and position UI controls for MAX patches in presentation and patching mode
testing
Analyzes user task descriptions and dispatches to the correct specialist agent(s) for MAX/MSP generation
data-ai
RNBO export-aware patch generation, target validation, and param mapping
data-ai
Generate MAX patches with control flow, message routing, subpatcher organization, and MIDI handling