"""Tests cho tools/autosample_vsti.py — SF2 writer tự-sinh phải là RIFF/sfbk hợp lệ mà sf2utils parse được (không cần VSTi/pedalboard).""" import os import sys import numpy as np import pytest from tools.autosample_vsti import write_sf2 ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) FAKE_VST2 = os.path.join(ROOT, "native_host", "tests", "fake_vst2.dll") sf2utils = pytest.importorskip("sf2utils.sf2parse") def _sine(note, sr=44100, dur=0.2): t = np.arange(int(sr * dur)) / sr f = 440.0 * 2 ** ((note - 69) / 12) return (np.sin(2 * np.pi * f * t) * 20000).astype(np.int16) def test_write_sf2_valid_structure(tmp_path): out = str(tmp_path / "test.sf2") samples = [{"note": n, "frames": _sine(n)} for n in (60, 62, 64)] write_sf2(out, samples, sample_rate=44100, name="Test") assert os.path.getsize(out) > 100 with open(out, "rb") as f: head = f.read(12) assert head[:4] == b"RIFF" assert head[8:12] == b"sfbk" with open(out, "rb") as f: sf2 = sf2utils.Sf2File(f) real_samples = [s for s in sf2.samples if s.end > s.start] assert len(real_samples) == 3 assert len(sf2.presets) == 2 # preset + terminator assert sf2.presets[0].bank == 0 assert sf2.presets[0].preset == 0 assert sf2.presets[0].name == "Test" for s in real_samples: assert s.end - s.start == 8820 assert s.sample_rate == 44100 def test_write_sf2_rejects_empty(tmp_path): with pytest.raises(ValueError): write_sf2(str(tmp_path / "empty.sf2"), []) def test_write_sf2_odd_name_no_corruption(tmp_path, caplog): """INFO text chunk chan — ten le (vd "Nexus") khong duoc lam mat can (sf2utils khong skip pad byte cua odd-size chunk).""" import logging out = str(tmp_path / "odd.sf2") write_sf2(out, [{"note": 60, "frames": _sine(60)}], 44100, name="Nexus") with caplog.at_level(logging.WARNING): with open(out, "rb") as f: sf2 = sf2utils.Sf2File(f) assert len([s for s in sf2.samples if s.end > s.start]) == 1 assert not any("corrupted" in r.message for r in caplog.records) def test_autosample_vst2_native_bridge(tmp_path): """Nhanh VST2: render qua native bridge (fake_vst2.dll) → SF2 hop le, khong can pedalboard. Skip tren nen khong phai Windows / thieu DLL.""" if sys.platform.startswith("win") and os.path.isfile(FAKE_VST2): from tools.autosample_vsti import autosample_sf2 out = str(tmp_path / "vst2.sf2") res = autosample_sf2("fake_vst2", out, low=60, high=64, step=2, duration=0.3, release=0.2, extra_vst_dirs=[os.path.dirname(FAKE_VST2)]) assert res["note_count"] == 3 assert res["size_bytes"] == os.path.getsize(out) with open(out, "rb") as f: sf2 = sf2utils.Sf2File(f) assert len([s for s in sf2.samples if s.end > s.start]) == 3 else: pytest.skip("fake_vst2.dll chi chay Windows (bridge ctypes WinDLL)")