- 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.
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
ClipDefschema extended withmidi_cc: list[CCEvent]fieldRPPBuilder._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.rppsource — CC11 Expression events interleaved with notes in E-line streamkick-detection-cache:DrumLoopAnalyzertied 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 hitsrpp-clip-encoding:_build_midi_source()emitsE B0 0B xxlines 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 viadrum_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
.rppoutput opens in REAPER without errors; bass audibly ducks when kick hitsvalidate_rpp_output()reports no regressions