7c22ae1f02
- 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
79 lines
2.8 KiB
Python
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')
|