Repository navigation
Description
Problem / motivation
feedpak's only structured tone representation today (§6.9 tones.definitions) is a list of raw, opaque, source-specific objects that a Reader cannot portably interpret — the schema is literally {"type": "object"}, and readers "that do not interpret gear MUST preserve it verbatim." So there is no way for a feedpak to carry a tone that another Reader/engine can actually render, and no structured place to describe amps/cabs/pedals at all.
We want feedpak to carry tone information natively and portably, and we want that model to be future-proof — not tied to a single serial chain, a fixed gear catalog, a fixed knob set, or one plugin format.
Who needs this: any Reader that renders a rig (a player/practice app with an audio engine), and any Writer that has tone data (hand-authored, exported from a DAW, or captured as NAM/IR).
Proposed change
Add an engine-agnostic rig model as a new OPTIONAL side-file, plus a manifest opt-in and per-arrangement references. Purely additive; the existing opaque tones.definitions stays.
Design principle — intent vs. realization (the future-proofing). Every effect block carries an engine-independent intent (what it is — e.g. a hi-gain amp) AND an ordered list of concrete realizations (how to render it — a NAM capture, an IR, a plugin, or a built-in DSP). A Reader picks the first realization whose engine it supports; if it supports none, it still knows the tone from intent (display it, approximate it, or map it to its own gear). New engines become new engine values — older Readers skip what they don't know and fall through. Nothing is tied to a plugin catalog or a fixed gear list.
- New / changed manifest key:
rigs(string) — relative path to the rig-library side-file (opt-in, §9.1). - New side-file
rigs.json+schemas/rigs.schema.json— a pack-level rig library (rigs shared across arrangements). - §6.9
tonesgainsbase_rigandchanges[].rig(rigidreferences into the library), alongside the unchangeddefinitions.
Capabilities: open role/kind/engine vocabularies (unknown values allowed + preserved), an open normalized parameter map (not a fixed knob count; continuous/discrete + optional time automation), and an OPTIONAL graph (nodes/edges) for non-serial topology — parallel amps, wet/dry, stereo split/merge. Asset refs (NAM/IR) are pack-relative (or URI) with optional sha256; an opaque plugin state blob may be carried for exact recall but is always paired with intent+params so it is never the only representation.
# manifest fragment
feedpak_version: "1.13.0"
rigs: rigs.json// rigs.json
{
"version": 1,
"rigs": [
{
"id": "lead-drive",
"name": "Lead Drive",
"instrument": "guitar",
"blocks": [
{
"id": "amp", "role": "amp", "name": "Hi-Gain",
"intent": { "kind": "amp", "family": "british-hi-gain", "tags": ["crunch","lead"] },
"realizations": [
{ "engine": "nam", "ref": "captures/higain.nam", "sha256": "…" },
{ "engine": "plugin", "format": "clap", "id": "com.vendor.amp", "name": "AmpX", "vendor": "Vendor", "state": "<base64>", "params": { "gain": 0.7 } },
{ "engine": "builtin", "id": "feedback.amp.tubestage.v1", "params": { "gain": 0.7 } }
],
"params": { "gain": 0.7, "bass": 0.5, "mid": 0.6, "treble": 0.55, "master": 0.5 }
}
],
"graph": { "nodes": ["input","amp","output"], "edges": [["input","amp"],["amp","output"]] }
}
]
}// arrangements/lead.json — bind tones over time by rig id
"tones": {
"base": "Clean Rhythm",
"base_rig": "clean-rhythm",
"changes": [ { "t": 48.0, "name": "Lead Drive", "rig": "lead-drive" } ]
}Backward compatibility
- Older Reader: ignores the unknown
rigsmanifest key +rigs.json, and the unknownbase_rig/changes[].rigfields; still loads, still seestones.definitionsif present. No breakage. - Removes/renames/repurposes nothing.
tones.definitionsis untouched; a pack MAY carry both, and a rig-aware Reader prefers the structuredrigs.json. - Unknown
role/kind/engine/params and anyextkeys MUST be preserved verbatim, so future engines/effects aren't dropped by today's tools.
Version impact
- PATCH
- MINOR — new optional side-file + optional manifest key + optional arrangement fields; older readers unaffected. 1.12.0 → 1.13.0.
- MAJOR
Alternatives considered
- Keep only
tones.definitions(opaque). Rejected — it's not portable and can't express modern rigs (NAM/IR/plugin, parallel/stereo, automation). This is the limitation to escape. - Per-arrangement inline rigs (no shared library). Rejected as primary store — songs reuse a rig across arrangements/sections; a pack-level library dedupes. (Inline is still expressible as a one-rig library.)
- A single fixed schema per engine (e.g. NAM-only). Rejected — binds the format to one engine. The intent+realizations split keeps it engine-agnostic and additive as engines evolve.
Activity
topkoa commentedon Jul 21, 2026
Closing as completed — this FEP was implemented, essentially verbatim, by #45 (spec 1.13.0, merged 2026-07-01):
rigs.json+schemas/rigs.schema.json(spec §7.9), the manifestrigskey, the intent-vs-realizations split, the openrole/kind/enginevocabularies, the normalizedparamsmap withautomation, and the optionalgraphtopology. It stayed open only because #45 didn't use a closing keyword.The model has since been extended in place, which is a good sign the design held up:
sourceblock role, thesoundfontengine,intent.gm— as new values in the open vocabularies this issue proposed, exactly the "new engines become newenginevalues" extension path it designed for.Remaining rig-model follow-ups (the
intent.gmmelodic+percussion overlap and non-headsourceblocks) are recorded in the #62 thread for a future clarification FEP, not here.