fix: musical content — 808 timing, chord voicings, melody range, pad arpeggiation, Ozone paths

- 808 bass: fixed note positions to beat 1.0 per bar (i-iv-i-V, 1.5 beat duration)
- Chords: 4-note 7th voicings (Am7, F7, C7, G7) instead of 2-note intervals
- Lead: constrained to 8-semitone range, pentatonic scale
- Pad: arpeggiated eighth-notes instead of static 2-note drones
- Ozone 12: fixed .vst3 filename paths in Calibrator
- Delta-encoding: fixed cumulative timing drift in _build_midi_source() with CC events

298/298 tests pass.
This commit is contained in:
renato97
2026-05-04 01:30:19 -03:00
parent 623af69483
commit 33bb08270d
11 changed files with 4827 additions and 1018 deletions

View File

@@ -0,0 +1,40 @@
# Design: Fix Musical Coherence
## Architecture Decisions
### AD1: Bass pattern — constant over composition
The 808 bass pattern is a static 8-bar loop transposed by key. No section-specific variation needed since energy and velocity_mult handle intensity changes.
### AD2: Lead range constraint — filter in _resolve_chord_tones
Rather than post-filter melody output, constrain chord_tones at the source. Remove oct_shift -12/+12 from _resolve_chord_tones(), keeping only oct_shift=0. This limits all chord tones to one octave around tonic (octave 4), producing melodies within ~12 semitones.
### AD3: Chord 7ths — change progression quality strings
CHORD_TYPES already has m7=[0,3,7,10] and 7=[0,4,7,10]. Switch EMOTION_PROGRESSIONS from "min"/"maj" to "m7"/"7". Voice leading code handles any voicing size transparently.
### AD4: Pad movement — arpeggiate in build_pad_track
Replace 3 sustained notes with ascending arpeggio: for each beat, play one chord note, cycling through chord tones. 0.5 beat duration (eighth note), 0.55 volume. Different octave (3) from chords (4).
### AD5: Ozone path — verify and harden
The `_build_plugin()` lookup in `_build_master_fxchain()` already resolves correctly from PLUGIN_REGISTRY. Fix by verifying the path and adding an assertion/guard so empty path never reaches the VST element.
## Implementation Notes
### File changes
1. `scripts/compose.py`:
- Replace BASS_PATTERN_8BARS with 4-note sparse pattern
- Replace build_pad_track() with arpeggiated version
2. `src/composer/melody_engine.py`:
- `_resolve_chord_tones()`: remove oct_shift in (-12, 12), keep only 0
3. `src/composer/chords.py`:
- EMOTION_PROGRESSIONS: "min"→"m7", "maj"→"7"
4. `src/reaper_builder/__init__.py`:
- `_build_master_fxchain()`: use PLUGIN_REGISTRY to populate PluginDef path
### Test updates
- test_compose.py: verify bass note positions
- test_melody_engine.py: verify range constraint
- test_chords.py: verify 4-note voicings
- test_calibrator.py: verify Ozone master chain

View File

@@ -0,0 +1,76 @@
# Proposal: Fix Musical Coherence
## Intent
Generated RPP MIDI content lacks musical coherence — bass timing wrong for reggaeton, lead melodies span 2+ octaves, chords are triads not 7ths, pads have no movement, and Ozone 12 master chain may fail to load. Fix all five issues.
## Scope
### In Scope
- Fix 808 Bass pattern: sparse i-iv-i-V (1 note/2 bars, on beat 1.0, 1.5 beat duration)
- Fix Lead melody: constrain to ≤1 octave range with chord-tone emphasis
- Fix Chords: use 4-note 7th voicings (m7/7) instead of 3-note triads
- Fix Pad: add arpeggiated movement instead of static sustained notes
- Fix Ozone 12: ensure master chain PluginDef has correct .vst3 path
### Out of Scope
- Drum pattern changes
- Vocal generation
- Mix levels recalibration
## Capabilities
### New Capabilities
None
### Modified Capabilities
None (implementation-only fixes — no spec changes)
## Approach
1. **Ozone 12**: In `_build_master_fxchain()`, construct PluginDef with actual registry path instead of `path=""`. Or verify registry lookup already works and confirm.
2. **808 Bass**: Replace dense 16-note `BASS_PATTERN_8BARS` with sparse 4-note pattern matching Ableton project: bar 1-2 A1, bar 3-4 D2, bar 5-6 A1, bar 7-8 E2, each on beat 1.0 with 1.5 beat duration.
3. **Lead**: Remove ±1 octave expansion in `_resolve_chord_tones()` (melody_engine.py line 80). Constrain chord tones to single octave around tonic (oct_shift=0 only).
4. **Chords**: Change `EMOTION_PROGRESSIONS` in chords.py to use `m7`/`7` qualities instead of `min`/`maj`, producing 4-note seventh chord voicings.
5. **Pad**: Replace single sustained chord with arpeggiated eighth-note pattern cycling through chord notes.
## Affected Areas
| Area | Impact | Description |
|------|--------|-------------|
| `scripts/compose.py` — BASS_PATTERN_8BARS | Modified | Sparse 4-note pattern |
| `scripts/compose.py` — build_pad_track() | Modified | Arpeggiated movement |
| `src/composer/melody_engine.py` — _resolve_chord_tones() | Modified | Single octave constraint |
| `src/composer/chords.py` — EMOTION_PROGRESSIONS | Modified | m7/7 instead of min/maj |
| `src/reaper_builder/__init__.py` — _build_master_fxchain() | Modified | Correct plugin path |
| Tests | Modified | Update expected note counts/positions |
## Risks
| Risk | Likelihood | Mitigation |
|------|------------|------------|
| Sparse bass pattern too empty | Low | 1.5-beat 808 tails fill space |
| 7th chords sound too jazzy | Low | Reggaeton standard is i7-VI7-III7-VII7 |
| Arpeggiated pad clashes with chords | Low | Different octave (3 vs 4) |
## Rollback Plan
Revert git commit. All changes are in existing files.
## Dependencies
None
## Success Criteria
- [ ] 808 bass notes start at beat 1.0 of bars 1,3,5,7 (not 3.5,7.0...)
- [ ] Lead melody stays within 12 semitones per bar
- [ ] Chord voicings have 4 notes (root, 3rd, 5th, 7th)
- [ ] Pad has arpeggiated eighth-note movement
- [ ] Ozone 12 vst3 filename correct in RPP output
- [ ] `python -m pytest tests/ -q` passes
- [ ] Generated RPP loads in REAPER and plays coherently

View File

@@ -0,0 +1,64 @@
# Spec: Fix Musical Coherence
## Requirements
### R1: Bass Pattern
- 808 Bass MUST use i-iv-i-V pattern over 8 bars
- Each chord gets 2 bars with 1 note on beat 1.0, duration 1.5 beats
- Pitch sequence for Am: A1(33), D2(38), A1(33), E2(40)
- Transposed by key difference from Am
### R2: Lead Melody
- Lead notes MUST NOT exceed 12 semitones between consecutive notes
- Melody MUST use chord tones on strong beats (1 and 3)
- Scale-based passing tones allowed on weak beats
- Octave range constrained to ±6 semitones from root
### R3: Chord Voicings
- ChordEngine MUST produce 4-note voicings (root, 3rd, 5th, 7th)
- Use m7 for minor chords, 7 for major chords
- Voice leading keeps movement ≤4 semitones per voice
- First chord respects requested inversion
### R4: Pad Movement
- Pad MUST have rhythmic movement (arpeggiated eighth-notes)
- Cycle through chord notes in ascending order
- Volume: 0.55 (prevents masking)
- Duration per note: 0.5 beats (eighth note)
### R5: Ozone 12
- Master chain PluginDef MUST have correct .vst3 path
- Filename field MUST match PLUGIN_REGISTRY entry exactly
- No fallback to empty string path
## Scenarios
### S1: Bass timing verification
Given: 8-bar section, key Am, bpm=95
When: build_bass_track runs
Then: First 4 notes at start positions 0.0, 8.0, 16.0, 24.0 (beats)
And: durations all 1.5
And: pitches 33, 38, 33, 40
### S2: Lead range constraint
Given: 4-bar hook motif in Am
When: build_motif(style="hook") runs
Then: all(n.pitch for n in motif) within 12 semitones of tonic
And: max pitch - min pitch ≤ 12
### S3: Chord voicing size
Given: Emotion "romantic", key "Am", 8 bars
When: engine.progression(bars=8) runs
Then: Each voicing has len() == 4
And: Notes include root, 3rd, 5th, 7th intervals
### S4: Pad arpeggiation
Given: 8-bar section, Am key
When: build_pad_track runs
Then: Clip has >24 MIDI notes (arpeggiated, not 3 sustained)
And: Each note duration ≤ 0.5 beats
### S5: Ozone vst3 path
Given: SongDefinition with master_plugins after Calibrator.apply()
When: RPPBuilder writes master FXCHAIN
Then: VST element filename field is "Ozone 12 Equalizer.vst3"

View File

@@ -0,0 +1,14 @@
# Tasks: Fix Musical Coherence
## Task List
- [x] T1: Fix BASS_PATTERN_8BARS in scripts/compose.py — 4-note sparse i-iv-i-V pattern
- [x] T2: Fix _resolve_chord_tones() in src/composer/melody_engine.py — single octave constraint
- [x] T3: Fix EMOTION_PROGRESSIONS in src/composer/chords.py — m7/7 instead of min/maj
- [x] T4: Fix build_pad_track() in scripts/compose.py — arpeggiated eighth-note movement
- [x] T5: Fix _build_master_fxchain() in src/reaper_builder/__init__.py — correct Ozone plugin path
- [x] T6: Fix _build_midi_source() delta-encoding bug — note-off in sorted event stream
- [x] T7: Update tests to match new expected behavior
- [x] T8: Run `python -m pytest tests/ -q` — 298 passed
- [x] T9: Generate RPP with `python scripts/compose.py --bpm 95 --key Am --output output/musical_test.rpp --seed 42`
- [x] T10: Verify generated RPP has correct note positions and content