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/
This commit is contained in:
200
sdd/reggaeton-professional-mix/tasks.md
Normal file
200
sdd/reggaeton-professional-mix/tasks.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 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`:
|
||||
```python
|
||||
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_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 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_notes` at 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.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 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 |
|
||||
Reference in New Issue
Block a user