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:
renato97
2026-05-03 23:54:29 -03:00
parent 48bc271afc
commit 014e636889
51 changed files with 11394 additions and 113 deletions

View 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