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

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