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,99 @@
# Design: Section Energy Curve
## Technical Approach
Add three layers of dynamics: (1) which tracks play per section, (2) MIDI velocity scaling per section, (3) clip-level volume multipliers. Wiring already exists — `SectionDef` has `velocity_mult`/`vol_mult` fields that are never populated. Add the wiring and a centralized activity matrix.
## Architecture Decisions
| Decision | Choice | Tradeoff | Reason |
|----------|--------|----------|--------|
| Activity source of truth | Module-level `TRACK_ACTIVITY` dict | Not configurable per-song (yet) | Proposal explicitly defines it as constant; CLI flag is deferred |
| Section rename | `build``pre-chorus` in all references | Requires test fixture updates | Professional reggaeton convention; no external consumers of "build" |
| Clip volume | `D_VOL` on ITEM (not track fader) | Per-clip, not per-section | Track fader already used for static mix; D_VOL is REAPER-native item gain |
| MIDI velocity | Scale at note creation (builders), not in RPPBuilder | No post-processing needed | Velocity is a MIDI property best set when notes are created |
## Data Flow
```
build_section_structure()
└─ reads SECTIONS → creates SectionDef(name, bars, velocity_mult, vol_mult)
├─→ TRACK_ACTIVITY (module-level dict)
│ └─ _section_active(section, role) → bool
└─→ 7 track builders
├─ check _section_active() → skip/mute inactive roles
├─ multiply MIDI note velocity × section.velocity_mult
└─ set clip.vol_mult ← section.vol_mult
└─→ RPPBuilder._build_clip()
├─ audio: emit D_VOL if vol_mult ≠ 1.0
└─ MIDI: notes already velocity-scaled
```
## File Changes
| File | Action | Description |
|------|--------|-------------|
| `src/core/schema.py` | Modify | Add `vol_mult: float = 1.0` to ClipDef |
| `scripts/compose.py` | Modify | Add TRACK_ACTIVITY dict, `_section_active()` helper, set multipliers in `build_section_structure()`, rename build→pre-chorus, refactor 7 builders |
| `src/reaper_builder/__init__.py` | Modify | `_build_clip()` emits D_VOL for audio clips with vol_mult≠1.0 |
| `tests/test_section_builder.py` | Modify | Add tests for multiplier population per section type |
| `tests/test_compose_integration.py` | Modify | Update section-aware tests |
| `tests/test_reaper_builder.py` | Modify | Add D_VOL emission tests |
## Interfaces / Contracts
```python
# New: TRACK_ACTIVITY dict in compose.py
TRACK_ACTIVITY: dict[str, dict[str, bool]] = {
"intro": {"drumloop": True, "perc": False, "bass": False, ...},
"verse": {"drumloop": True, "perc": True, "bass": True, ...},
"pre-chorus": {...},
"chorus": {...}, # all True
"bridge": {"drumloop": True, "chords": True, "pad": True, ...},
"final": {"drumloop": True, "bass": True, "chords": True, "lead": True, "pad": True},
"outro": {}, # all False
}
# New helper
def _section_active(section: SectionDef, role: str, activity: dict) -> bool:
return activity.get(section.name, {}).get(role, False)
# Modified: build_section_structure() sets multipliers
SECTION_MULTIPLIERS = {
"intro": (0.6, 0.70),
"verse": (0.7, 0.85),
"pre-chorus": (0.85, 0.95),
"chorus": (1.0, 1.00),
"bridge": (0.6, 0.75),
"final": (1.0, 1.00),
"outro": (0.4, 0.60),
}
# Modified: ClipDef gains vol_mult
@dataclass
class ClipDef:
...
vol_mult: float = 1.0
```
## Testing Strategy
| Layer | What to Test | Approach |
|-------|-------------|----------|
| Unit | SectionDef multiplier population | `test_section_builder.py` — verify velocity_mult/vol_mult by section type |
| Unit | `_section_active()` helper | Edge cases: unknown section, unknown role, all known sections |
| Unit | ClipDef.vol_mult default | `test_core_schema.py` — default is 1.0 |
| Integration | D_VOL in RPP output | `test_reaper_builder.py` — audio clip with vol_mult≠1.0 emits D_VOL, default vol_mult=1.0 emits none |
| Integration | Builders respect activity | `test_compose_integration.py` — intro has no bass/chords/lead, chorus has all |
| Integration | Section rename | Grep all `.py` for "build" section name, CI runs full suite (110 tests) |
## Migration / Rollout
No migration required. `vol_mult` defaults to 1.0 (no behavioral change). Section rename is cosmetic. Revert commit to undo.
## Open Questions
None.