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

5.5 KiB

Proposal: 808 Bass Sidechain Ducking

Intent

808 bass and kick drum overlap in low frequencies with zero separation. Professional reggaeton uses sidechain-style ducking — bass dips when kick hits — creating the "pumping" feel and preventing low-frequency mud. Currently build_bass_track() generates static-velocity MIDI notes with no awareness of the drumloop's kick pattern.

Scope

In Scope

  • Pre-analyze drumloop WAV files to extract kick transient positions via DrumLoopAnalyzer
  • Cache kick beat-positions per drumloop path (same file reused across sections)
  • Generate MIDI CC11 (Expression) events on bass clips at kick hit positions
  • Duck shape: instantaneous drop to CC11≈50, 80ms release ramp to CC11=127
  • ClipDef schema extended with midi_cc: list[CCEvent] field
  • RPPBuilder._build_midi_source() emits CC E-lines interleaved with Note events

Out of Scope

  • Track-level volume automation envelopes (VOLENV2) — complex binary encoding, deferred
  • ReaComp-sidechain routing via ReaScript — Phase 2 enhancement only
  • DrumLoopAnalyzer integration at composition time (not pre-cached) — deferred to Phase 2
  • Ducking for non-bass tracks (chords, lead, pad)
  • User-configurable duck depth/shape — constants only

Capabilities

New Capabilities

  • midi-cc-events: MIDI CC event emission in .rpp source — CC11 Expression events interleaved with notes in E-line stream
  • kick-detection-cache: DrumLoopAnalyzer tied into composition pipeline; kick positions cached per drumloop WAV path

Modified Capabilities

  • bass-generation: build_bass_track() accepts kick position data and generates per-note velocity ducking OR CC11 events synchronized to kick hits
  • rpp-clip-encoding: _build_midi_source() emits E B0 0B xx lines alongside Note On/Off

Approach

Principle: MIDI CC11 (Expression) is the simplest .rpp-native sidechain. No REAPER-specific features, no binary envelope encoding, no ReaScript bridge. Pure MIDI standard — works with any synth (Serum 2 confirmed).

Data flow:

Drumloop WAV
  → DrumLoopAnalyzer.analyze() → transient_positions("kick")
  → beat-positions cache (dict[str, list[float]])
  → build_bass_track(sections, offsets, key_root, key_minor, kick_cache={})
  → generates CCEvent objects {controller=11, time, value}
  → ClipDef.midi_cc = [...]
  → RPPBuilder._build_midi_source() emits E-lines

CC11 ducking shape per kick hit (all times in beats relative to clip start):

Offset from kick CC11 Value Description
kick_time 50 Instant dip (~-9dB)
kick_time + 0.02 50 Hold through transient
kick_time + 0.18 127 Release complete (80ms ≈ 0.16 beats)

Key decision — MIDI CC11 vs alternatives:

Option Verdict Why
A: MIDI CC11 (Expression) Chosen .rpp MIDI source format supports E B0 0B xx lines. Serum 2, most synths respond. Trivial builder change.
B: Track volume envelope (VOLENV2) Rejected Binary/chunk encoding in .rpp — fragile, hard to debug, no benefit over CC11 for this use case.
C: ReaScript ReaComp sidechain ⏸️ Deferred Works only in Phase 2 with REAPER running. Use as future enhancement for non-MIDI audio basses.

Affected Areas

Area Impact Description
src/core/schema.py Modified Add CCEvent dataclass (controller, time, value); add midi_cc: list[CCEvent] to ClipDef
scripts/compose.py Modified Add _get_kick_cache(), pass to build_bass_track(), generate CC11 events in bass clips
src/reaper_builder/__init__.py Modified _build_midi_source() interleaves CC events into E-line stream
src/composer/drum_analyzer.py Unchanged Already exports transient_positions("kick") — zero changes needed
tests/test_compose_integration.py Modified Verify CC events present in bass clips, correct CC11 values at kick positions
tests/test_reaper_builder.py Modified Verify _build_midi_source() emits B0 0B E-lines

Risks

Risk Likelihood Mitigation
Synth doesn't respond to CC11 Low Serum 2, Omnisphere, Diva all support it. Add _CC11_VOLUME_MIN constant for easy disable (set to 127 = no ducking).
DrumloopAnalyzer misclassifies kick transients Med Only use transients with confidence > 0.6; add KICK_CONFIDENCE_THRESHOLD = 0.6 constant.
CC events overlap MIDI notes in E-line stream Low Sort all events (notes + CC) by absolute time; REAPER E-lines are monotonic delta-encoded.

Rollback Plan

Delete midi_cc from ClipDef and revert builder to skip CC events. Remove _get_kick_cache() from compose.py. No schema migrations needed — midi_cc defaults to empty list (zero behavioral change). One-commit revert.

Dependencies

  • librosa (already a project dependency via drum_analyzer.py)
  • DrumLoopAnalyzer (already implemented and tested)
  • No new packages, no external APIs.

Success Criteria

  • Bass MIDI clips contain CC11 (Expression) E-lines at kick hit positions
  • CC11 value drops to ~50 at kick onset, recovers to 127 within 0.18 beats
  • DrumLoopAnalyzer correctly identifies kick transients in all 5 drumloop variants
  • Kick position cache avoids re-analyzing same WAV across sections
  • 110 existing tests pass unchanged
  • .rpp output opens in REAPER without errors; bass audibly ducks when kick hits
  • validate_rpp_output() reports no regressions