Files
renato97 014e636889 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.
2026-05-03 23:54:29 -03:00

100 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.