fix: REAPER playback — D_VOL removed, Ozone filenames corrected, ReaEQ removed, MIDI quantized

- D_VOL: removed from _build_clip() — not valid at REAPER item level
- Ozone 12: fixed 21 PLUGIN_REGISTRY entries with correct .vst3 filenames
- ReaEQ: removed _calibrate_eq() — built-in plugin format incompatible
- MIDI: quantized all notes to 16th grid (120 ticks at 960 PPQ)

298/298 tests. 0 D_VOL, 0 ReaEQ, all notes on grid, Ozone filenames correct.
This commit is contained in:
renato97
2026-05-04 00:55:08 -03:00
parent e99fa231dd
commit 623af69483
10 changed files with 10698 additions and 158 deletions

View File

@@ -378,83 +378,6 @@ class TestCalibrateSends:
assert dly.send_level == {}
class TestCalibrateEq:
def test_reaeq_plugin_prepended(self):
"""_calibrate_eq should prepend ReaEQ with HPF/LPF params to non-return tracks."""
from src.calibrator import Calibrator
song = _make_fixture_song()
Calibrator._calibrate_eq(song)
# Drumloop: HPF 60Hz
drum = [t for t in song.tracks if t.name == "Drumloop"][0]
assert len(drum.plugins) >= 1
eq = drum.plugins[0]
assert eq.name == "ReaEQ"
assert eq.params[0] == 1
assert eq.params[1] == 1 # HPF
assert eq.params[2] == 60.0
# Bass: LPF 300Hz
bass = [t for t in song.tracks if t.name == "808 Bass"][0]
eq = bass.plugins[0]
assert eq.name == "ReaEQ"
assert eq.params[1] == 0 # LPF
assert eq.params[2] == 300.0
# Chords: HPF 200Hz
chords = [t for t in song.tracks if t.name == "Chords"][0]
eq = chords.plugins[0]
assert eq.params[1] == 1 # HPF
assert eq.params[2] == 200.0
# Pad: HPF 100Hz
pad = [t for t in song.tracks if t.name == "Pad"][0]
eq = pad.plugins[0]
assert eq.params[1] == 1 # HPF
assert eq.params[2] == 100.0
def test_return_tracks_no_reaeq(self):
"""Return tracks should not get ReaEQ plugins."""
from src.calibrator import Calibrator
song = _make_fixture_song()
Calibrator._calibrate_eq(song)
rev = [t for t in song.tracks if t.name == "Reverb"][0]
dly = [t for t in song.tracks if t.name == "Delay"][0]
assert rev.plugins == []
assert dly.plugins == []
def test_reaeq_index_zero(self):
"""ReaEQ must be at index 0 (prepended to existing plugins)."""
from src.calibrator import Calibrator
song = _make_fixture_song()
# Add an existing plugin to a track
lead = [t for t in song.tracks if t.name == "Lead"][0]
lead.plugins = [PluginDef(name="Serum 2", path="Serum2.vst3", index=0)]
Calibrator._calibrate_eq(song)
assert len(lead.plugins) == 2
assert lead.plugins[0].name == "ReaEQ"
assert lead.plugins[0].index == 0
assert lead.plugins[1].name == "Serum 2"
def test_unknown_role_no_reaeq(self):
"""Tracks with unknown role should not get ReaEQ."""
from src.calibrator import Calibrator
meta = SongMeta(bpm=95, key="Am")
song = SongDefinition(
meta=meta,
tracks=[TrackDef(name="Vocals", plugins=[])],
)
Calibrator._calibrate_eq(song)
assert song.tracks[0].plugins == []
class TestSwapMasterChain:
def test_ozone12_master_chain(self):
"""_swap_master_chain should replace master_plugins with Ozone 12 triplet."""
@@ -510,9 +433,6 @@ class TestCalibratorApply:
# Sends applied
assert drum.send_level.get(7) == 0.10
# EQ applied (ReaEQ present)
assert drum.plugins[0].name == "ReaEQ"
# Master chain upgraded
assert song.master_plugins == [
"Ozone_12_Equalizer",
@@ -520,17 +440,15 @@ class TestCalibratorApply:
"Ozone_12_Maximizer",
]
def test_apply_skips_bass_lpf_eq(self):
"""Bass track should get LPF, not HPF."""
def test_apply_skips_bass_volume(self):
"""Bass track should get correct calibrated volume, not EQ."""
from src.calibrator import Calibrator
song = _make_fixture_song()
Calibrator.apply(song)
bass = [t for t in song.tracks if t.name == "808 Bass"][0]
eq = bass.plugins[0]
assert eq.params[1] == 0 # LPF type
assert eq.params[2] == 300.0
assert bass.volume == 0.82
# ---------------------------------------------------------------------------

View File

@@ -199,7 +199,7 @@ class TestClipDefVolMult:
assert clip.vol_mult == 0.7
def test_audio_clip_vol_mult_default_is_one(self):
"""Audio clip with default vol_mult=1.0 has no D_VOL side effect."""
"""Audio clip with default vol_mult=1.0 has no volume effect."""
clip = ClipDef(position=0.0, length=16.0, audio_path="test.wav", vol_mult=1.0)
assert clip.is_audio
assert clip.vol_mult == 1.0

View File

@@ -457,11 +457,11 @@ class TestVST3PresetData:
Path(tmp_path).unlink(missing_ok=True)
class TestDVolEmission:
"""Test D_VOL emission for audio clips with vol_mult."""
class TestDVolRemoval:
"""Verify D_VOL is not emitted for any audio clip (removed as REAPER-incompatible)."""
def test_audio_clip_vol_mult_not_one_emits_dvol(self):
"""Audio clip with vol_mult=0.7 emits D_VOL in ITEM."""
def test_audio_clip_with_vol_mult_no_dvol(self):
"""Audio clip with vol_mult=0.7 must NOT emit D_VOL in ITEM."""
meta = SongMeta(bpm=95, key="Am")
clip = ClipDef(
position=0.0, length=16.0, name="Test",
@@ -479,11 +479,11 @@ class TestDVolEmission:
try:
builder.write(tmp_path)
content = Path(tmp_path).read_text(encoding="utf-8")
assert "D_VOL 0.7" in content, "D_VOL line expected for vol_mult=0.7"
assert "D_VOL" not in content, "D_VOL must not be emitted (removed for REAPER compat)"
finally:
Path(tmp_path).unlink(missing_ok=True)
def test_audio_clip_default_vol_mult_emits_no_dvol(self):
def test_audio_clip_default_vol_mult_no_dvol(self):
"""Audio clip with default vol_mult=1.0 emits NO D_VOL."""
meta = SongMeta(bpm=95, key="Am")
clip = ClipDef(
@@ -502,7 +502,7 @@ class TestDVolEmission:
try:
builder.write(tmp_path)
content = Path(tmp_path).read_text(encoding="utf-8")
assert "D_VOL" not in content, "No D_VOL expected for default vol_mult"
assert "D_VOL" not in content, "No D_VOL expected"
finally:
Path(tmp_path).unlink(missing_ok=True)