- 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.0 KiB
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
-
scripts/compose.py:- Replace BASS_PATTERN_8BARS with 4-note sparse pattern
- Replace build_pad_track() with arpeggiated version
-
src/composer/melody_engine.py:_resolve_chord_tones(): remove oct_shift in (-12, 12), keep only 0
-
src/composer/chords.py:- EMOTION_PROGRESSIONS: "min"→"m7", "maj"→"7"
-
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