T14: SF host bridge native (FluidSynth runtime DLL) + golden test
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test smoke SFHost bridge (T14): FluidSynth native qua ctypes.
|
||||
|
||||
Chạy: python tests/test_sf_host_bridge.py [path_sf2]
|
||||
Yêu cầu: native_host/build/Release/sf_host_bridge.dll + fluidsynth_runtime/.
|
||||
Verify: Create → LoadSF2 → SelectInstrument → AudioStart → NoteOn → chờ
|
||||
(blocks tăng, underruns 0) → NoteOff → AudioStop → Close; mixer set OK.
|
||||
"""
|
||||
import ctypes
|
||||
import ctypes.wintypes as wt
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
DLL = os.path.join(ROOT, "build", "Release", "sf_host_bridge.dll")
|
||||
SF2 = sys.argv[1] if len(sys.argv) > 1 else os.path.join(
|
||||
ROOT, "..", "app", "storage", "soundfonts",
|
||||
"518e850f-a5d3-4790-b1f9-0c90c203c524.sf2")
|
||||
|
||||
assert os.path.exists(DLL), f"thiếu {DLL}"
|
||||
assert os.path.exists(SF2), f"thiếu {SF2}"
|
||||
|
||||
dll = ctypes.WinDLL(DLL)
|
||||
err = ctypes.create_string_buffer(256)
|
||||
|
||||
dll.SF_FS_Create.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_char_p, ctypes.c_int32]
|
||||
dll.SF_FS_Create.restype = ctypes.c_int32
|
||||
dll.SF_FS_LoadSF2.argtypes = [ctypes.c_int32, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int32]
|
||||
dll.SF_FS_LoadSF2.restype = ctypes.c_int32
|
||||
dll.SF_FS_SelectInstrument.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32]
|
||||
dll.SF_FS_SelectInstrument.restype = ctypes.c_int32
|
||||
dll.SF_FS_NoteOn.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_int32]
|
||||
dll.SF_FS_NoteOn.restype = ctypes.c_int32
|
||||
dll.SF_FS_NoteOff.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_int32]
|
||||
dll.SF_FS_NoteOff.restype = ctypes.c_int32
|
||||
dll.SF_FS_AudioStart.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_int32, ctypes.c_char_p, ctypes.c_int32]
|
||||
dll.SF_FS_AudioStart.restype = ctypes.c_int32
|
||||
dll.SF_FS_AudioStop.argtypes = [ctypes.c_int32]
|
||||
dll.SF_FS_AudioStop.restype = ctypes.c_int32
|
||||
dll.SF_FS_AudioUnderruns.argtypes = [ctypes.c_int32]
|
||||
dll.SF_FS_AudioUnderruns.restype = ctypes.c_int32
|
||||
dll.SF_FS_AudioBlocks.argtypes = [ctypes.c_int32]
|
||||
dll.SF_FS_AudioBlocks.restype = ctypes.c_int32
|
||||
dll.SF_FS_SetTrackGainPan.argtypes = [ctypes.c_int32, ctypes.c_float, ctypes.c_float]
|
||||
dll.SF_FS_SetTrackGainPan.restype = ctypes.c_int32
|
||||
dll.SF_FS_SetMasterLimiter.argtypes = [ctypes.c_int32, ctypes.c_int32, ctypes.c_float]
|
||||
dll.SF_FS_SetMasterLimiter.restype = ctypes.c_int32
|
||||
dll.SF_FS_Close.argtypes = [ctypes.c_int32, ctypes.c_char_p, ctypes.c_int32]
|
||||
dll.SF_FS_Close.restype = ctypes.c_int32
|
||||
|
||||
|
||||
def check(name, rc, expect=0):
|
||||
assert rc == expect, f"{name}: rc={rc} err={err.value.decode() if err.value else ''}"
|
||||
print(f"OK {name}")
|
||||
|
||||
|
||||
h = dll.SF_FS_Create(44100, 512, err, 256)
|
||||
assert h > 0, f"Create trả {h}, err={err.value.decode() if err.value else ''}"
|
||||
print(f"OK SF_FS_Create -> handle {h}")
|
||||
|
||||
check("SF_FS_LoadSF2", dll.SF_FS_LoadSF2(h, SF2.encode(), err, 256))
|
||||
|
||||
# Tìm preset đầu tiên tồn tại trong SF2 (SF2 tuỳ biến có thể không có 0/0).
|
||||
sel = None
|
||||
for bank in (0, 1, 128):
|
||||
for prog in (0, 1, 12, 40, 80):
|
||||
rc = dll.SF_FS_SelectInstrument(h, 0, -1, bank, prog)
|
||||
if rc == 0:
|
||||
sel = (bank, prog)
|
||||
break
|
||||
if sel:
|
||||
break
|
||||
assert sel, "không tìm được preset nào trong SF2"
|
||||
print(f"OK SF_FS_SelectInstrument bank={sel[0]} program={sel[1]}")
|
||||
check("SF_FS_SetTrackGainPan", dll.SF_FS_SetTrackGainPan(h, -6.0, 0.0))
|
||||
check("SF_FS_SetMasterLimiter", dll.SF_FS_SetMasterLimiter(h, 1, -1.0))
|
||||
|
||||
check("SF_FS_AudioStart", dll.SF_FS_AudioStart(h, 44100, 512, err, 256))
|
||||
b0 = dll.SF_FS_AudioBlocks(h)
|
||||
check("SF_FS_NoteOn", dll.SF_FS_NoteOn(h, 0, 60, 100))
|
||||
time.sleep(0.4)
|
||||
b1 = dll.SF_FS_AudioBlocks(h)
|
||||
u = dll.SF_FS_AudioUnderruns(h)
|
||||
assert b1 > b0, f"blocks không tăng: {b0} -> {b1}"
|
||||
assert u == 0, f"underruns={u}"
|
||||
print(f"OK render blocks {b0} -> {b1}, underruns {u}")
|
||||
|
||||
check("SF_FS_NoteOff", dll.SF_FS_NoteOff(h, 0, 60))
|
||||
time.sleep(0.2)
|
||||
check("SF_FS_AudioStop", dll.SF_FS_AudioStop(h))
|
||||
check("SF_FS_Close", dll.SF_FS_Close(h, err, 256))
|
||||
print("PASS")
|
||||
Reference in New Issue
Block a user