feat: professional reggaeton production engine — 7 SDD changes, 302 tests
- section-energy: track activity matrix + volume/velocity multipliers per section - smart-chords: ChordEngine with voice leading, inversions, 4 emotion modes - hook-melody: melody engine with hook/stabs/smooth styles, call-and-response - mix-calibration: Calibrator module (LUFS volumes, HPF/LPF, stereo, sends, master) - transitions-fx: FX track with risers/impacts/sweeps at section boundaries - sidechain: MIDI CC11 bass ducking on kick hits via DrumLoopAnalyzer - presets-pack: role-aware plugin presets (Serum/Decapitator/Omnisphere per role) Full SDD pipeline (propose→spec→design→tasks→apply→verify) for all 7 changes. 302/302 tests passing.
This commit is contained in:
73
.sdd/changes/presets-pack/proposal.md
Normal file
73
.sdd/changes/presets-pack/proposal.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Proposal: presets-pack
|
||||
|
||||
## Intent
|
||||
|
||||
All plugins use the SAME flat preset regardless of track role (bass/lead/chords/pad) or genre context. A Serum_2 on a bass track gets the same sound as Serum_2 on a lead track. Professional reggaeton needs role-specific timbres: deep sine 808 for bass, detuned saw for lead, warm pad for chords, evolving texture for pad. Same for FX: Decapitator on drums needs aggressive drive, on bass needs subtle warmth.
|
||||
|
||||
## Scope
|
||||
|
||||
### In Scope
|
||||
- Restructure `PLUGIN_PRESETS` from flat `{plugin: [chunks]}` to role-aware `{plugin: {role: [chunks]}}`
|
||||
- Create role-specific presets for plugins used in multiple roles: **Serum_2** (bass/lead), **Omnisphere** (chords/pad), **Decapitator** (drums/bass)
|
||||
- Programmatically derive new presets by base64-decoding existing presets (Serum=JSON, SoundToys=key=value), modifying genre-specific parameters, re-encoding
|
||||
- Update `make_plugin()` in `compose.py` and `_build_plugin()` in `__init__.py` to resolve role-aware presets
|
||||
- Add fallback: if no role-specific preset exists, use existing default preset
|
||||
|
||||
### Out of Scope
|
||||
- Creating presets from scratch in REAPER (requires GUI — can't programmatically)
|
||||
- ReaScript-based preset capture (Phase 2)
|
||||
- Presets for all 113 plugins — only multi-role targets initially
|
||||
- Pro-Q 3 reggaeton EQ curve (no decodable format available)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `presets-pack`: Role-specific plugin preset resolution and preset data management
|
||||
|
||||
### Modified Capabilities
|
||||
None — existing plugin resolution unchanged; backward-compatible fallback to default preset.
|
||||
|
||||
## Approach
|
||||
|
||||
**Option B — Programmatic modification of decodable presets:**
|
||||
|
||||
1. **Serum_2**: Decode base64 → JSON. Serum preset JSON has `component: "processor"` block with oscillator/wavetable/filter data. Create variants by modifying oscillator type (sine for bass, saw for lead), filter cutoff, envelope settings. Re-encode.
|
||||
|
||||
2. **Decapitator (SoundToys)**: Decode base64 → key=value text (`WIDGET = Decapitator;...`). Create "aggressive" (high Drive, Tone bright) for drums, "warm" (low Drive, Tone dark) for bass. Re-encode.
|
||||
|
||||
3. **Omnisphere**: Decode base64 → `SynthMaster` text block. Create "warm pad" variant with slow attack, filter modulation; "evolving texture" with movement/LFO. Re-encode.
|
||||
|
||||
No GUI or REAPER needed — pure Python string processing over decoded preset text.
|
||||
|
||||
## Affected Areas
|
||||
|
||||
| Area | Impact | Description |
|
||||
|------|--------|-------------|
|
||||
| `src/reaper_builder/__init__.py` | Modified | `PLUGIN_PRESETS` restructured; `_build_plugin()` accepts role param |
|
||||
| `src/composer/templates.py` | Modified | `_parse_vst_block()`, `make_plugin()` resolution updated |
|
||||
| `scripts/compose.py` | Modified | `make_plugin()` passes role; `FX_CHAINS` keys used for role |
|
||||
| `src/core/schema.py` | Unchanged | `PluginDef` already has `preset_data` field |
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Likelihood | Mitigation |
|
||||
|------|------------|------------|
|
||||
| Modified preset crashes plugin on load | Low | Each variant derived from working ground-truth preset; only tweak known-safe params |
|
||||
| Base64 decode/re-encode breaks binary integrity | Low | Round-trip test per plugin: decode → encode → bytes equal |
|
||||
| Omnisphere text format undocumented | Med | Preserve structure, only modify known `ATTRIBUTE` values visible in decoded text |
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
Revert `PLUGIN_PRESETS` to flat dict. Remove role param from `_build_plugin()` and `make_plugin()`. Existing tests verify preset injection still works.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `data/sample_index.json` (independent — not affected)
|
||||
- Existing ground-truth presets in `PLUGIN_PRESETS` (source material for variants)
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] `python scripts/compose.py --bpm 99 --key Am` produces .rpp where Serum_2 on bass track has different preset data than Serum_2 on lead track
|
||||
- [ ] 110 existing tests continue to pass (backward-compatible fallback)
|
||||
- [ ] Round-trip test: decode → modify → encode produces valid base64 matching original length structure
|
||||
- [ ] At least 3 (plugin, role) combinations have distinct preset variants
|
||||
Reference in New Issue
Block a user