Files
reaper-control/sdd/reggaeton-professional-mix/tasks.md
renato97 8562bfbed1 fix: real preset data for all VST2/VST3 plugins, template system with ground-truth registry
- Extracted preset data from all_plugins_v2.rpp for 14 previously broken plugins
- Fixed PLUGIN_REGISTRY entries: Kontakt 7, Gullfoss, ValhallaDelay, VC 160/76, The Glue
- Template parser falls back to PLUGIN_PRESETS when source RPP has fake data
- Substitute Transient Master (not installed) with FabFilter Pro-C 2
- All 25 plugins now load correctly in REAPER
- Added template generator scripts and ground truth references
- Cleaned up temp/debug files from output/
2026-05-03 18:54:40 -03:00

9.6 KiB
Raw Blame History

Tasks: reggaeton-professional-mix

type: architecture

status: draft


Phase 1 — Note Validity (Foundation)

1.1 Clamp pitch in encode_notes_block()

File: src/flp_builder/events.py Location: encode_notes_block(), lines 201223 Change: Replace key=key & 0x7F with explicit max(0, min(127, key)). Why: key & 0x7F silently wraps values ≥128 rather than clamping, causing the FL Studio "invalid notes" warning. Validation: Verify notes with key=128 produce no warning; key=0 and key=127 work.

1.2 Clamp velocity in encode_notes_block()

File: src/flp_builder/events.py Location: encode_notes_block(), line 219 — velocity=velocity & 0x7F Change: Replace with max(1, min(127, velocity)). Why: Velocity 0 is invalid in MIDI; & 0x7F allows 0 from input. Validation: velocity=0 → clamped to 1; velocity=127 stays 127; velocity=200 → clamped to 127.

1.3 Clamp duration ≥ 1 in encode_notes_block()

File: src/flp_builder/events.py Location: encode_notes_block(), line 212 — length=max(length, 1) Change: Already present. Confirm max(length, 1) is retained (was added in prior fix). Why: Duration of 0 would produce 0-byte notes, which FL Studio rejects. Validation: length=0 → stays 1 after max(); length=1 → stays 1.


Phase 2 — Mix Settings

2.1 Add mix fields to PatternDef

File: src/flp_builder/schema.py Location: PatternDef dataclass, lines 7287 Change: Add four fields to PatternDef:

volume: float = 0.85        # 0.01.0, default -1.5 dB
pan: float = 0.0            # -1.0 to 1.0, center
reverb_send: float = 0.2    # 0.01.0
delay_send: float = 0.1      # 0.01.0

Add volume, pan, reverb_send, delay_send to _PATTERN_KEYS frozenset for validation. Why: Needed so the composer can specify per-pattern mix settings; previously all channels used defaults. Validation: JSON round-trip: PatternDef(..., volume=0.8, pan=-0.3, reverb_send=0.4, delay_send=0.2) survives to_json()from_json().

2.2 Patch mix events in ChannelSkeletonLoader

File: src/flp_builder/skeleton.py Location: ChannelSkeletonLoader.load() — after sample patching, before assembly Change: Accept mix_map: dict[int, dict] parameter (channel_index → {volume, pan, reverb_send, delay_send}). After patching samples, append/replace word events:

  • ChVolWord (72): volume as 0255 word (int(vol * 200), 200 = 0 dB)
  • ChPanWord (73): pan as signed 0255 (int((pan + 1.0) * 127.5))
  • ChReverb (139): reverb_send as 0255 (int(send * 255))
  • ChStereoDelay (85): delay_send as 0255 (int(send * 255)) Why: FL Studio stores these as word events on the channel; skeleton loader must inject them. Validation: Load skeleton with mix_map, verify output bytes contain the four event IDs with expected values.

2.3 Pass mix_map from FLPBuilder to skeleton loader

File: src/flp_builder/builder.py (assumed existing — if not found, locate) Location: FLPBuilder.build() call to ChannelSkeletonLoader.load() Change: Build mix_map from PatternDef fields and pass to loader. Why: Bridges schema (PatternDef) to FLP rendering (skeleton). Note: If builder.py does not yet accept/forward mix_map, add it.


Phase 3 — Groove

3.1 Add groove_strength to rhythm generators

File: src/composer/rhythm.py Location: All generator functions + get_notes() dispatcher Change: Add groove_strength: float = 0.0 parameter (0.0 = no groove, 1.0 = max groove) to all generators:

  • kick_main_notes, kick_sparse_notes, kick_outro_notes
  • snare_verse_notes, snare_fill_notes, snare_outro_notes
  • hihat_16th_notes, hihat_8th_notes
  • clap_24_notes, perc_combo_notes, rim_build_notes

Apply groove via _apply_groove(note_dict, groove_strength) helper:

  • Velocity jitter: vel ±= random.randint(0, int(5 + groove_strength * 10))
  • Positional nudge: pos += random.uniform(-groove_strength * 0.02, groove_strength * 0.02)
  • Swing (every other 16th): shift even-index 16ths forward by swing_amount = groove_strength * 0.1

Why: Groove makes drum patterns feel human and avoids mechanical timing. Validation: get_notes("kick_main_notes", bars=1, groove_strength=0.5) returns notes with positional variation vs. groove_strength=0.0.

3.2 Update get_notes() dispatcher signature

File: src/composer/rhythm.py Location: get_notes(), lines 303311 Change: Add groove_strength: float = 0.0 to dispatcher and pass to generator. Why: All generators now accept groove_strength; dispatcher must forward it.


Phase 4 — Melodic Humanization

4.1 Add humanize to melodic generators

File: src/composer/melodic.py Location: bass_tresillo(), lead_hook(), chords_block(), pad_sustain() Change: Add humanize: float = 0.0 parameter (0.0 = no humanization, 1.0 = max humanization).

Apply humanization via _apply_humanize(note_dict, humanize) helper:

  • Velocity jitter: vel ±= random.randint(0, int(humanize * 5)) — gentler than drums
  • Micro-timing: pos += random.uniform(-humanize * 0.03, humanize * 0.03) — tighter than drums

Why: Melodic instruments (bass, lead, pad) need subtler humanization than drums to avoid sounding out of tune. Validation: bass_tresillo(key="Am", bars=1, humanize=0.5) vs. humanize=0.0 shows velocity/position variance.

4.2 Add humanization to melodic dispatcher (if exists)

File: src/composer/melodic.py Location: Any get_melodic_notes() dispatcher function Change: Forward humanize parameter to all generators. Why: Single entry point for melodic generation.


Phase 5 — Transitions

5.1 Create FX channel (ch21) with riser sample

File: scripts/compose_full_track.py Location: After melodic track setup, before SongDefinition Change: Add FX channel (channel_index=21) with role "fx" and riser sample from SampleSelector. Use rim_build_notes pattern at channel 21 for riser fills. Place riser clips before chorus sections at bars 19 and 43 (1-bar riser items). Why: Transitions between verse→chorus need riser FX to build energy. Note: Verify ch21 is available (not used by drums/melodic); if conflicts, use next available.

5.2 Add rim_build pre-chorus fills

File: scripts/compose_full_track.py Location: Arrangement items for bars 1819 (pre-chorus 1) and 4243 (pre-chorus 2) Change: Insert 2-bar rim_build pattern before each chorus:

  • Pattern id 9: rim_build_notes at channel 13
  • Place at bars 1819 for chorus 1 (bars 2031)
  • Place at bars 4243 for chorus 2 (bars 4455) Why: Rim roll builds tension going into the chorus ("rim_build" generator is designed for this).

5.3 Place riser FX before choruses

File: scripts/compose_full_track.py Location: Arrangement items, pre-chorus sections Change: Add 1-bar riser clip at bars 19 and 43 (before chorus 1 and 2). Riser should be on a dedicated FX track (arrangement track 7). Why: Riser sample creates sonic "lift" into the chorus section.


Phase 6 — Compose Script Update

6.1 Add reggaeton-standard mix values to PatternDefs

File: scripts/compose_full_track.py Location: patterns list, lines 128137 Change: Set explicit mix values per pattern:

  • kick_main: volume=0.88, pan=0.0, reverb_send=0.1, delay_send=0.0
  • kick_sparse: volume=0.82, pan=0.0, reverb_send=0.1, delay_send=0.0
  • snare_main: volume=0.80, pan=0.05, reverb_send=0.35, delay_send=0.15
  • hihat_main: volume=0.65, pan=0.0, reverb_send=0.25, delay_send=0.0
  • clap_main: volume=0.82, pan=0.0, reverb_send=0.45, delay_send=0.1
  • perc_main: volume=0.72, pan=-0.15, reverb_send=0.3, delay_send=0.1
  • perc2_main: volume=0.72, pan=0.15, reverb_send=0.3, delay_send=0.1

Why: These values are the reggaeton-standard mix targets documented in the design.

6.2 Enable groove_strength on drum patterns

File: scripts/compose_full_track.py Location: patterns list — update call sites that generate drum notes Change: Pass groove_strength=0.3 to drum rhythm generators in compose script. Why: Drums benefit from subtle groove humanization to avoid mechanical feel. Note: This requires get_notes() to accept groove_strength (Phase 3 must be complete first).

6.3 Enable humanize on melodic tracks

File: scripts/compose_full_track.py Location: section_notes() calls for bass, lead, pad, pluck Change: Pass humanize=0.2 to melodic generators. Why: Melodic humanization makes bass/lead/pad feel organic and less quantized.

6.4 Add transition sections to arrangement

File: scripts/compose_full_track.py Location: items list — insert pre-chorus transition blocks Change: Insert pre-chorus sections at bars 1819 and 4243:

  • Rim_build pattern at channel 13 (bars 1819 and 4243)
  • Riser FX at channel 21 (bars 19 and 43, 1 bar each) Why: Proper transitions create professional song flow rather than abrupt section changes.

Dependencies

  • Phase 2 depends on Phase 1 (schema fields needed before builder integration)
  • Phase 3 can run in parallel with Phase 4 (separate modules)
  • Phase 5 depends on Phase 2 (mix infrastructure for FX sends)
  • Phase 6 depends on Phases 2, 3, 4, 5 (all capabilities wired up)

Files Modified (summary)

File Phases
src/flp_builder/events.py 1
src/flp_builder/schema.py 2
src/flp_builder/skeleton.py 2
src/flp_builder/builder.py 2
src/composer/rhythm.py 3
src/composer/melodic.py 4
scripts/compose_full_track.py 5, 6