- 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.
2.1 KiB
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"