- 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.
4.9 KiB
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
drumloopreturnsTrue
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=
xyznot 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_VOLattribute on ITEM - MIDI clips: scale all
MidiNote.velocitybyvol_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_VOLline 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.6andvol_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.0andvol_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", ...)returnsFalse - 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-chorusnotbuild