# 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.
