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,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"