- 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/
9.6 KiB
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 201–223
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 72–87
Change: Add four fields to PatternDef:
volume: float = 0.85 # 0.0–1.0, default -1.5 dB
pan: float = 0.0 # -1.0 to 1.0, center
reverb_send: float = 0.2 # 0.0–1.0
delay_send: float = 0.1 # 0.0–1.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 0–255 word (int(vol * 200), 200 = 0 dB)ChPanWord(73): pan as signed 0–255 (int((pan + 1.0) * 127.5))ChReverb(139): reverb_send as 0–255 (int(send * 255))ChStereoDelay(85): delay_send as 0–255 (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_notessnare_verse_notes,snare_fill_notes,snare_outro_noteshihat_16th_notes,hihat_8th_notesclap_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 303–311
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 18–19 (pre-chorus 1) and 42–43 (pre-chorus 2)
Change: Insert 2-bar rim_build pattern before each chorus:
- Pattern id 9:
rim_build_notesat channel 13 - Place at bars 18–19 for chorus 1 (bars 20–31)
- Place at bars 42–43 for chorus 2 (bars 44–55) 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 128–137
Change: Set explicit mix values per pattern:
kick_main: volume=0.88, pan=0.0, reverb_send=0.1, delay_send=0.0kick_sparse: volume=0.82, pan=0.0, reverb_send=0.1, delay_send=0.0snare_main: volume=0.80, pan=0.05, reverb_send=0.35, delay_send=0.15hihat_main: volume=0.65, pan=0.0, reverb_send=0.25, delay_send=0.0clap_main: volume=0.82, pan=0.0, reverb_send=0.45, delay_send=0.1perc_main: volume=0.72, pan=-0.15, reverb_send=0.3, delay_send=0.1perc2_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 18–19 and 42–43:
- Rim_build pattern at channel 13 (bars 18–19 and 42–43)
- 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 |