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

@@ -73,30 +73,15 @@ DRUMLOOP_FILES = {
],
}
# 808 Bass pattern from Ableton project (proven harmonic):
# i - iv - i - V in Am: A1(33) → D2(38) → A1(33) → E2(40)
# 808 Bass pattern — reggaeton i-iv-i-V, one note every 2 bars on beat 1.0.
# Sparse pattern: 808 tail (1.5 beat duration) fills the space between hits.
# Am: A1(33) → D2(38) → A1(33) → E2(40)
# Duration: 1.5 beats, velocity varies by section
BASS_PATTERN_8BARS = [
# Bars 1-2: root (i)
{"pitch": 33, "start_time": 0.0, "duration": 1.5, "velocity": 80},
{"pitch": 33, "start_time": 2.0, "duration": 1.5, "velocity": 80},
{"pitch": 33, "start_time": 4.0, "duration": 1.5, "velocity": 80},
{"pitch": 33, "start_time": 6.0, "duration": 1.5, "velocity": 80},
# Bars 3-4: subdominant (iv)
{"pitch": 38, "start_time": 8.0, "duration": 1.5, "velocity": 80},
{"pitch": 38, "start_time": 10.0, "duration": 1.5, "velocity": 80},
{"pitch": 38, "start_time": 12.0, "duration": 1.5, "velocity": 80},
{"pitch": 38, "start_time": 14.0, "duration": 1.5, "velocity": 80},
# Bars 5-6: root (i)
{"pitch": 33, "start_time": 16.0, "duration": 1.5, "velocity": 80},
{"pitch": 33, "start_time": 18.0, "duration": 1.5, "velocity": 80},
{"pitch": 33, "start_time": 20.0, "duration": 1.5, "velocity": 80},
{"pitch": 33, "start_time": 22.0, "duration": 1.5, "velocity": 80},
# Bars 7-8: dominant (V)
{"pitch": 40, "start_time": 24.0, "duration": 1.5, "velocity": 80},
{"pitch": 40, "start_time": 26.0, "duration": 1.5, "velocity": 80},
{"pitch": 40, "start_time": 28.0, "duration": 1.5, "velocity": 80},
{"pitch": 40, "start_time": 30.0, "duration": 1.5, "velocity": 80},
{"pitch": 33, "start_time": 0.0, "duration": 1.5, "velocity": 80}, # Bar 1-2: i
{"pitch": 38, "start_time": 8.0, "duration": 1.5, "velocity": 80}, # Bar 3-4: iv
{"pitch": 33, "start_time": 16.0, "duration": 1.5, "velocity": 80}, # Bar 5-6: i
{"pitch": 40, "start_time": 24.0, "duration": 1.5, "velocity": 80}, # Bar 7-8: V
]
# Section structure from Ableton project
@@ -650,22 +635,35 @@ def build_fx_track(
def build_pad_track(sections, offsets, key_root: str, key_minor: bool) -> TrackDef:
"""Pad: sustained root chord, only in chorus/build sections."""
"""Pad: arpeggiated chord, cycling through chord tones on eighth notes.
Each section gets an ascending arpeggio cycling through chord notes
at octave 3 (low, avoids clashing with chords at octave 4).
Replaces the old static sustained pad with rhythmic movement.
"""
root_midi = key_to_midi_root(key_root, 3)
quality = "minor" if key_minor else "major"
chord = build_chord(root_midi, quality)
clips = []
for section, sec_off in zip(sections, offsets):
# Pad only where the pad role is active
if not _section_active(section.name, "pad", TRACK_ACTIVITY):
continue
velocity = int(55 * section.velocity_mult)
notes = [
MidiNote(pitch=p, start=0.0, duration=section.bars * 4.0, velocity=velocity)
for p in chord
]
total_beats = section.bars * 4.0
notes = []
beat = 0.0
while beat < total_beats:
pitch = chord[int(beat * 2) % len(chord)] # ascend through chord tones
notes.append(MidiNote(
pitch=pitch,
start=beat,
duration=0.5,
velocity=velocity,
))
beat += 0.5 # eighth note step
clips.append(ClipDef(
position=sec_off * 4.0,
length=section.bars * 4.0,
@@ -677,7 +675,7 @@ def build_pad_track(sections, offsets, key_root: str, key_minor: bool) -> TrackD
plugins = [make_plugin(fx, i, role="pad") for i, fx in enumerate(FX_CHAINS.get("pad", []))]
return TrackDef(
name="Pad",
volume=VOLUME_LEVELS["pad"],
volume=0.55, # lower volume to prevent masking chords
pan=0.0,
clips=clips,
plugins=plugins,