- 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.
65 lines
2.1 KiB
Markdown
65 lines
2.1 KiB
Markdown
# 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"
|