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.9 KiB
Raw Permalink Blame History

Design: Transitions FX

Technical Approach

Add build_fx_track() to scripts/compose.py that places audio FX clips from the sample library at 7 section boundaries. Uses SampleSelector.select_one(role="fx") with per-type character hints. Reuses ClipDef.fade_in/out. New track inserted after Clap, before Pad — after main tracks, before sends are wired.

Architecture Decisions

Decision Choice Rejected Rationale
One FX track vs per-section Single dedicated track Per-section tracks Simpler; one import per sample in REAPER; manageable clip count (79)
Sample selection Weighted random top-5 Pinned specific files Variety across runs; selector scoring already works
Boundary timing Hardcoded beat-offset map Audio analysis Section structure is deterministic; bar counts are fixed
Riser+impact at chorus Two clips, same boundary Single combined clip Requires different timing; riser before boundary, impact on it

Data Flow

SECTIONS → offsets (bar → beat)
         │
         ▼
FX_TRANSITIONS map: {boundary_idx: (type, start_offset, length, fade_in, fade_out)}
         │
         ▼
build_fx_track(sections, offsets, selector, seed)
  ├── for each entry in FX_TRANSITIONS:
  │     ├── boundary_beat = offsets[boundary_idx] * 4
  │     ├── position = boundary_beat + start_offset
  │     ├── sample = selector.select_one(role="fx", seed=seed+idx)
  │     └── ClipDef(position, length, audio_path, fade_in, fade_out)
         │
         ▼
TrackDef("Transition FX", volume=0.72, clips=[...], send_level={...})

Boundary → FX Map

# Boundary Beat FX Type Position Length Fade In Fade Out
2 verse→build 48 sweep 46 2 0.3 0.0
3 build→chorus 64 riser 60 4 1.5 0.0
3 build→chorus 64 impact 64 2 0.0 0.3
4 chorus→verse2 96 transition 94 2 0.2 0.2
5 verse2→chorus2 128 riser 124 4 1.0 0.0
6 chorus2→bridge 160 sweep 158 2 0.2 0.2
7 bridge→final 176 riser 172 4 1.0 0.0
8 final→outro 208 sweep 206 2 0.3 0.5

File Changes

File Action Description
scripts/compose.py Modify Add FX_TRANSITIONS dict + build_fx_track() (~50 lines); call in main() after clap track, before return tracks

Key Implementation Detail

SampleSelector.select_one() has a seed kwarg — new in the selector API. If not yet supported, use select(role="fx", limit=5) with manual random.choice(). Since FX is in ATONAL_ROLES, key compatibility scoring is skipped (neutral 0.5).

Track Ordering

tracks = [
    build_drumloop_track(...),    # 0
    build_perc_track(...),        # 1
    build_bass_track(...),        # 2
    build_chords_track(...),      # 3
    build_lead_track(...),        # 4
    build_clap_track(...),        # 5
    build_fx_track(...),          # 6 ← NEW
    build_pad_track(...),         # 7
]
return_tracks = create_return_tracks()  # 8 (Reverb), 9 (Delay)

Send wiring applies to all non-return tracks automatically via existing loop. FX track sends: Reverb=0.08, Delay=0.05.

Testing Strategy

Layer What How
Unit build_fx_track returns TrackDef with 8 clips Mock selector via SampleSelector.__init__ patching
Unit Clip positions match boundary map Assert clip.position values equal expected beats
Integration End-to-end .rpp output compose.py --bpm 99 --key Am --output test.rpp; grep for "Transition FX" <TRACK block
Existing 110 tests pass pytest before/after regression

Open Questions

None — all dependencies exist today (SampleSelector, ClipDef.fade_in/out, SECTIONS structure).