# Delta Specs: Section Energy Curve

## ADDED Requirements — section-activity

### Requirement: Centralized Activity Matrix

The system MUST provide a `TRACK_ACTIVITY` dict mapping `section_type → {role: bool}` as the single source of truth for which track roles play in each section. Section types: `intro`, `verse`, `pre-chorus`, `chorus`, `bridge`, `final`, `outro`. Roles: `drumloop`, `perc`, `bass`, `chords`, `lead`, `clap`, `pad`.

| Section | drumloop | perc | bass | chords | lead | clap | pad |
|---------|----------|------|------|--------|------|------|-----|
| intro | true | - | - | - | - | - | - |
| verse | true | true | true | true | - | - | - |
| pre-chorus | true | true | true | true | - | - | true |
| chorus | true | true | true | true | true | true | true |
| bridge | true | - | - | true | - | - | true |
| final | true | - | true | true | true | - | true |
| outro | - | - | - | - | - | - | - |

#### Scenario: Intro is sparse

- GIVEN section_type=`intro`
- WHEN `_section_active("intro", "bass", activity)` is called
- THEN it returns `False`
- AND only `drumloop` returns `True`

#### Scenario: Chorus is full band

- GIVEN section_type=`chorus`
- WHEN `_section_active("chorus", "lead", activity)` is called
- THEN it returns `True`
- AND all 7 roles return `True`

### Requirement: Section Activity Helper

The system MUST provide `_section_active(section: SectionDef, role: str, activity: dict) -> bool` that returns whether a role is active, defaulting to `False` for unknown section/role.

#### Scenario: Unknown section returns False

- GIVEN section_type=`xyz` not in TRACK_ACTIVITY
- WHEN `_section_active(section, "bass", matrix)` is called
- THEN it returns `False`

---

## ADDED Requirements — clip-volume

### Requirement: ClipDef Volume Multiplier

`ClipDef` MUST have an optional `vol_mult` field (float, default 1.0). When `vol_mult != 1.0`, the RPP builder SHALL apply it:
- Audio clips: emit `D_VOL` attribute on ITEM
- MIDI clips: scale all `MidiNote.velocity` by `vol_mult`

#### Scenario: Audio clip with vol_mult emits D_VOL

- GIVEN ClipDef(audio_path="kick.wav", vol_mult=0.7)
- WHEN RPPBuilder writes the ITEM
- THEN the ITEM includes `D_VOL 0.7`

#### Scenario: MIDI clip with vol_mult scales velocity

- GIVEN ClipDef(midi_notes=[MidiNote(velocity=80)], vol_mult=0.5)
- WHEN clip is processed by RPPBuilder
- THEN emitted velocity is 40

### Requirement: RPPBuilder D_VOL Emission

`_build_clip()` MUST append `["D_VOL", str(clip.vol_mult)]` to the ITEM element when `clip.vol_mult != 1.0` and the clip is audio.

#### Scenario: Default vol_mult=1.0 emits no D_VOL

- GIVEN ClipDef(audio_path="loop.wav") with default vol_mult=1.0
- WHEN RPPBuilder writes the ITEM
- THEN no `D_VOL` line is emitted

---

## MODIFIED Requirements — section-structure

### Requirement: SectionDef Multipliers Per Section Type

`build_section_structure()` MUST populate `SectionDef.velocity_mult` and `vol_mult` based on section type, not default to 1.0. Multipliers SHALL follow this table:

| Section | velocity_mult | vol_mult |
|---------|--------------|----------|
| 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     |

(Previously: velocity_mult and vol_mult always defaulted to 1.0)

#### Scenario: Intro has low velocity and volume

- GIVEN `build_section_structure()` is called
- WHEN the intro section is created
- THEN `velocity_mult=0.6` and `vol_mult=0.70`

#### Scenario: Chorus has full velocity and volume

- GIVEN `build_section_structure()` is called
- WHEN the chorus section is created
- THEN `velocity_mult=1.0` and `vol_mult=1.0`

---

## MODIFIED Requirements — track-generation

### Requirement: Builders Use Centralized Activity + Section Multipliers

All 7 track builders MUST replace ad-hoc section name checks with calls to `_section_active()`. All builders MUST multiply MIDI velocities by `section.velocity_mult`. The `build` section SHALL be renamed to `pre-chorus` in `SECTIONS` and all references.

(Previously: builders used inline `if section.name in (...)` checks and `section.energy` for velocity; section was named `build`)

#### Scenario: Chords not generated in intro

- GIVEN `build_chords_track()` with sections including intro
- WHEN processing the intro section
- THEN `_section_active("intro", "chords", ...)` returns `False`
- AND no clip is created for that section

#### Scenario: Bass velocity scaled by section multiplier

- GIVEN `build_bass_track()` with a verse section (velocity_mult=0.7)
- WHEN MIDI notes are created
- THEN each note velocity is multiplied by 0.7

#### Scenario: Section rename reflects in output

- GIVEN SECTIONS tuple has `("pre-chorus", 4, 0.7, False)`
- WHEN `build_section_structure()` is called
- THEN the section is named `pre-chorus` not `build`
