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

201 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`:
```python
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 |