#!/usr/bin/env python3 # G2 end-to-end smoke: DAW side creates SHM (mirrors Rust shm.rs), spawns the # real bridge, pushes LOAD_INSTRUMENT + MIDI note, asserts non-zero audio. # Usage: python tests/g2_bridge_smoke.py import ctypes, os, struct, subprocess, sys, time BRIDGE = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "build", "Release", "daw_vst_bridge.exe")) SF2 = r"C:\Users\locpham\SonicForgeStudio\app\storage\soundfonts\518e850f-a5d3-4790-b1f9-0c90c203c524.sf2" SFZ = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "sfizz-src", "tests", "TestFiles", "g2_test.sfz")) SHM_NAME = "SonicForge_DAW_IPC" SIZE = 11160 kern = ctypes.windll.kernel32 PAGE_READWRITE, FILE_MAP_ALL_ACCESS = 0x04, 0xF001F kern.CreateFileMappingW.restype = ctypes.c_void_p kern.CreateFileMappingW.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_wchar_p] kern.MapViewOfFile.restype = ctypes.c_void_p kern.MapViewOfFile.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_size_t] kern.UnmapViewOfFile.restype = ctypes.c_int kern.UnmapViewOfFile.argtypes = [ctypes.c_void_p] kern.CloseHandle.restype = ctypes.c_int kern.CloseHandle.argtypes = [ctypes.c_void_p] h = kern.CreateFileMappingW(None, None, PAGE_READWRITE, 0, SIZE, SHM_NAME) if not h: sys.exit("CreateFileMappingW failed") v = kern.MapViewOfFile(h, FILE_MAP_ALL_ACCESS, 0, 0, 0) if not v: sys.exit("MapViewOfFile failed") buf = (ctypes.c_ubyte * SIZE).from_address(v) def w32(o, x): struct.pack_into(" mx: mx = p time.sleep(0.01) return mx def load_instrument(itype, path, ch=0): w32(CTRL_Q_COUNT, 0) set_str(CTRL_Q + 16, path, 1024) w32(CTRL_Q + 0, 2); w32(CTRL_Q + 4, itype); w32(CTRL_Q + 8, 0); w32(CTRL_Q + 12, ch) w32(CTRL_Q_COUNT, 1) env = dict(os.environ, SF_PARENT_PID=str(os.getpid()), SF_SAMPLE_RATE="44100") logpath = os.path.join(os.path.dirname(BRIDGE), "g2_bridge_stdout.txt") logf = open(logpath, "w", encoding="utf-8") proc = subprocess.Popen([BRIDGE], env=env, cwd=os.path.dirname(BRIDGE), stdout=logf, stderr=subprocess.STDOUT, text=True) results = [] try: # SF2: load, note, expect non-silent audio load_instrument(2, SF2) time.sleep(0.3) note_on(0, 60, 100) p = poll_peak() ok = p > 0.001 results.append(("SF2 note audio", ok, "peak=%.5f" % p)) print("SF2 PEAK=%.5f" % p) if not ok: logf.flush() print("bridge out:", open(logpath, encoding="utf-8").read()[-1500:]) # SFZ on channel 1 load_instrument(3, SFZ, ch=1) time.sleep(0.5) note_on(1, 60, 100) p2 = poll_peak() ok2 = p2 > 0.001 results.append(("SFZ note audio", ok2, "peak=%.5f" % p2)) print("SFZ PEAK=%.5f" % p2) if not ok2: logf.flush() print("bridge out:", open(logpath, encoding="utf-8").read()[-1500:]) # VST3 (native hosting, no Carla): mda-vst3 bundle from SDK build VST3 = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "build", "VST3", "Release", "mda-vst3.vst3")) load_instrument(0, VST3, ch=2) # wait until bridge reports the load (poll log), up to 10s deadline = time.time() + 10 while time.time() < deadline: logf.flush() if "instrument loaded ch=2" in open(logpath, encoding="utf-8").read(): break if proc.poll() is not None: print("BRIDGE DIED rc=", proc.poll()) break time.sleep(0.2) note_on(2, 60, 100) p3 = poll_peak(2.0) ok3 = p3 > 0.001 results.append(("VST3 note audio", ok3, "peak=%.5f" % p3)) print("VST3 PEAK=%.5f" % p3) logf.flush() print("bridge out tail:", open(logpath, encoding="utf-8").read()[-2500:]) if not ok3: print("bridge out:", open(logpath, encoding="utf-8").read()[-2500:]) finally: proc.terminate() try: proc.wait(timeout=3) except subprocess.TimeoutExpired: proc.kill() logf.close() kern.UnmapViewOfFile(v) kern.CloseHandle(h) failed = [r for r in results if not r[1]] print("G2 RESULT:", "FAIL" if failed else "PASS") for r in results: print(" %-20s %s %s" % (r[0], "PASS" if r[1] else "FAIL", r[2])) sys.exit(1 if failed else 0)