Files
admin 7c22ae1f02 refactor(bridge): split fx bridge, stabilize VST3 host chain, drop old install engines
- remove diagnostic canaries from audio hot path (crackle fix) and
  stuck C5 note raw-MIDI test (Vst2Instrument::init noteOff)
- split native bridge into daw_vst_bridge (VSTi) + fx_vst_bridge (VST FX)
- VST3: processData prepare before setActive, zero input buses, accept
  both .vstpreset and bridge-state blob in RenderJob presets
- native_render/vst_engine: bridge-exe discovery + render_fx_chain + /fx-gui
- delete old install/ engine binaries (install via sidecar+resources now)
- add BUILD_LAYOUT.md, build_selfcheck, tools/ diagnostics
2026-08-20 09:42:42 +07:00

79 lines
2.8 KiB
Python

# Inject note-on (live=1) and measure master buffer energy while held.
import ctypes, sys, time
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
APP_PID = sys.argv[1]
SHM_NAME = f'SonicForge_DAW_IPC_{APP_PID}'
OFF_MQ = 2064
OFF_MQ_COUNT = 2832
OFF_CQ = 2836
OFF_CQ_COUNT = 11156
OFF_MASTER_L = 8
OFF_MASTER_R = 1032
MIDI_ENTRY = 12
CTRL_ENTRY = 1040
k32 = ctypes.WinDLL('kernel32', use_last_error=True)
k32.OpenFileMappingA.restype = ctypes.c_void_p
k32.OpenFileMappingA.argtypes = [ctypes.c_uint32, ctypes.c_int, ctypes.c_char_p]
k32.MapViewOfFile.restype = ctypes.c_void_p
k32.MapViewOfFile.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_size_t]
k32.UnmapViewOfFile.argtypes = [ctypes.c_void_p]
k32.CloseHandle.argtypes = [ctypes.c_void_p]
h = k32.OpenFileMappingA(0x0002, 0, SHM_NAME.encode())
if not h: print('FATAL open', ctypes.get_last_error()); sys.exit(1)
ptr = k32.MapViewOfFile(h, 0x0002, 0, 0, 0)
if not ptr: print('FATAL map', ctypes.get_last_error()); sys.exit(1)
def send_ctrl(type_, arg0):
idx = ctypes.c_uint32.from_address(ptr + OFF_CQ_COUNT).value
base = ptr + OFF_CQ + idx * CTRL_ENTRY
ctypes.c_uint32.from_address(base + 0).value = type_
ctypes.c_uint32.from_address(base + 4).value = arg0
ctypes.c_uint32.from_address(ptr + OFF_CQ_COUNT).value = idx + 1
def send_midi(cmd, ch, pitch, vel, live, data2=0, data3=0):
idx = ctypes.c_uint32.from_address(ptr + OFF_MQ_COUNT).value
base = ptr + OFF_MQ + idx * MIDI_ENTRY
ctypes.c_uint8.from_address(base + 0).value = cmd
ctypes.c_uint8.from_address(base + 1).value = ch
ctypes.c_uint8.from_address(base + 2).value = pitch
ctypes.c_uint8.from_address(base + 3).value = vel
ctypes.c_uint8.from_address(base + 4).value = data2
ctypes.c_uint8.from_address(base + 5).value = data3
ctypes.c_uint8.from_address(base + 6).value = 1 if live else 0
ctypes.c_uint8.from_address(base + 7).value = 0
ctypes.c_uint32.from_address(base + 8).value = 0
ctypes.c_uint32.from_address(ptr + OFF_MQ_COUNT).value = idx + 1
def energy(off):
arr = (ctypes.c_float * 256).from_address(ptr + off)
return sum(abs(v) for v in arr)
def sample(label, n=8):
tot = 0.0
for _ in range(n):
tot += energy(OFF_MASTER_L) + energy(OFF_MASTER_R)
time.sleep(0.05)
print(f'{label}: avg energy = {tot/(2*n):.8f}')
print('baseline (no note)')
sample('before', 5)
print('--- NOTE_ON ch=0 p=60 vel=100 live=1 ---')
send_midi(0x9, 0, 60, 100, live=1)
time.sleep(0.8)
sample('held', 8)
print('--- NOTE_ON ch=0 p=72 vel=120 live=1 ---')
send_midi(0x9, 0, 72, 120, live=1)
time.sleep(0.8)
sample('held2', 8)
print('--- NOTE_OFF ---')
send_midi(0x9, 0, 60, 0, live=1)
send_midi(0x9, 0, 72, 0, live=1)
time.sleep(0.5)
sample('after off', 5)
k32.UnmapViewOfFile(ptr); k32.CloseHandle(h)
print('DONE')