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

3.4 KiB
Raw Permalink Blame History

Delta Spec: 808 Bass Sidechain Ducking

ADDED Requirements

Requirement: MIDI CC11 Event Data Model

The schema MUST support an CCEvent dataclass with controller, time, and value fields, and ClipDef MUST accept an optional midi_cc: list[CCEvent] field defaulting to empty list.

Scenario: CCEvent round-trips correctly

  • GIVEN CCEvent(controller=11, time=0.5, value=50)
  • WHEN serialized/deserialized via dataclass
  • THEN all fields preserved exactly

Scenario: ClipDef with midi_cc

  • GIVEN a ClipDef with midi_cc=[CCEvent(11, 0.0, 50), CCEvent(11, 0.18, 127)]
  • WHEN clip is processed by builder
  • THEN builder sees midi_cc field and can iterate it

Requirement: Kick Position Cache

A kick-cache dict {drumloop_wav_path: list[beat_positions]} SHALL be computed once per session, keyed by WAV path. DrumLoopAnalyzer.transient_positions("kick") MUST be the source, filtered by confidence >= KICK_CONFIDENCE_THRESHOLD (default 0.6).

Scenario: Cache hit

  • GIVEN drumloop WAV already analyzed in same session
  • WHEN build_bass_track() requests kick positions for that path
  • THEN cached positions returned without re-analyzing WAV

Scenario: Cache miss

  • GIVEN drumloop WAV not yet cached
  • WHEN kick positions requested
  • THEN DrumLoopAnalyzer.analyze() runs, positions cached by path key

Requirement: CC11 Ducking on Kick Hits

For each kick transient position in the bass clip's time span, the system MUST emit CC11 events forming a ducking envelope: instantaneous drop to value 50 at kick time, hold at 50 for 0.02 beats, ramp to 127 by 0.18 beats after kick.

Scenario: Single kick duck

  • GIVEN kick at beat 1.0 within a 4-beat bass clip
  • WHEN CC events generated
  • THEN emits CCEvent(11, 1.0, 50), CCEvent(11, 1.02, 50), CCEvent(11, 1.18, 127)

Scenario: No kicks in clip

  • GIVEN drumloop with no kick transients in clip time range
  • THEN midi_cc is empty list — no CC events emitted

MODIFIED Requirements

Requirement: RPPBuilder MIDI Source Encoding

_build_midi_source() MUST emit MIDI CC events as E B0 0B xx lines interleaved with note events, all sorted by absolute start time. Delta-encoding MUST continue for all E-lines.

Scenario: CC events interleaved with notes

  • GIVEN clip with midi_notes=[Note(60, 0.5, 1.0)] and midi_cc=[CCEvent(11, 0.0, 50)]
  • WHEN _build_midi_source() called
  • THEN E-lines emitted in time order: CC at 0.0, Note at 0.5
  • AND CC line reads E 0 B0 0B 32 (delta=0, CC11, value=50=0x32)

Scenario: Delta sequencing across note+CC

  • GIVEN CC at 0.0, note at 0.5 beats
  • WHEN building E-lines
  • THEN CC delta = 0×960 = 0; note delta = 0.5×960 - 0 = 480
  • AND cursor reset correctly after CC event ticks

Requirement: Bass Track Generation

build_bass_track() SHALL accept an optional kick_cache: dict[str, list[float]] parameter. When kick data is present for the drumloop used in each section, midi_cc events SHALL be generated and added to the bass ClipDef.

Scenario: Bass clip with ducking CC

  • GIVEN kick cache has [1.0, 2.5] for drumloop, section covers beats 0-16
  • WHEN bass track built
  • THEN bass clip at that section has midi_cc with 2×3 CC events (one envelope per kick in range)
  • AND note generation unchanged from existing behavior

Scenario: No kick cache provided

  • GIVEN kick_cache is {} or omitted
  • THEN midi_cc is empty — zero behavioral change from current output