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:
134
.sdd/changes/section-energy/spec.md
Normal file
134
.sdd/changes/section-energy/spec.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# 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`
|
||||
Reference in New Issue
Block a user