# SHM injection test for live-flag fix: verify transportStopped guard drops # non-live note-ons but passes live ones (reserved[0]=1). # Layout from SharedMemoryIPC.h (verified): base=2064 bytes, midiQueue 64x12 @2064, # midiQueueCount @2832, controlQueue 8x1040 @2836, controlQueueCount @11156. import ctypes, sys, time, os sys.stdout.reconfigure(encoding='utf-8', errors='replace') APP_PID = sys.argv[1] if len(sys.argv) > 1 else '9712' SHM_NAME = f'SonicForge_DAW_IPC_{APP_PID}' OFF_MQ = 2064 OFF_MQ_COUNT = 2832 OFF_CQ = 2836 OFF_CQ_COUNT = 11156 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] def get_shm(): h = k32.OpenFileMappingA(0x0002, 0, SHM_NAME.encode()) # FILE_MAP_WRITE first if not h: h = k32.OpenFileMappingA(0x0004, 0, SHM_NAME.encode()) # fallback FILE_MAP_READ if not h: err = ctypes.get_last_error() print(f'FATAL: cannot open SHM {SHM_NAME} (err={err})') sys.exit(1) ptr = k32.MapViewOfFile(h, 0x0002, 0, 0, 0) # FILE_MAP_WRITE if not ptr: print(f'FATAL: MapViewOfFile err={ctypes.get_last_error()}') sys.exit(1) return h, ptr def w32(ptr, off, val): ctypes.c_uint32.from_address(ptr + off).value = val def send_ctrl(ptr, type_, arg0, arg1=0, channel=0): idx = ctypes.c_uint32.from_address(ptr + OFF_CQ_COUNT).value if idx >= 8: print('FATAL: control queue full') sys.exit(1) 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(base + 8).value = arg1 ctypes.c_uint32.from_address(base + 12).value = channel ctypes.c_uint32.from_address(ptr + OFF_CQ_COUNT).value = idx + 1 print(f'sent ctrl type={type_} arg0={arg0}') def send_midi(ptr, cmd, channel, pitch, velocity, live, data2=0, data3=0): idx = ctypes.c_uint32.from_address(ptr + OFF_MQ_COUNT).value if idx >= 64: print('FATAL: midi queue full') sys.exit(1) base = ptr + OFF_MQ + idx * MIDI_ENTRY ctypes.c_uint8.from_address(base + 0).value = cmd ctypes.c_uint8.from_address(base + 1).value = channel ctypes.c_uint8.from_address(base + 2).value = pitch ctypes.c_uint8.from_address(base + 3).value = velocity 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 # reserved[0] = live flag ctypes.c_uint8.from_address(base + 7).value = 0 ctypes.c_uint32.from_address(base + 8).value = 0 # sampleOffset ctypes.c_uint32.from_address(ptr + OFF_MQ_COUNT).value = idx + 1 print(f'sent midi cmd=0x{cmd:x} ch={channel} pitch={pitch} vel={velocity} live={live}') h, ptr = get_shm() print(f'SHM {SHM_NAME} mapped at {ptr:#x}') # 1) transport STOP -> guard arms send_ctrl(ptr, 3, 0) # TRANSPORT STOP time.sleep(1.0) # 2) non-live NOTE_ON -> must be dropped (no [midi] ON log) send_midi(ptr, 0x9, 0, 60, 100, live=0) time.sleep(1.0) # 3) live NOTE_ON -> must pass send_midi(ptr, 0x9, 0, 60, 100, live=1) time.sleep(1.0) # 4) non-live NOTE_ON again -> dropped (sanity) send_midi(ptr, 0x9, 0, 62, 90, live=0) time.sleep(1.0) # 5) restore PLAY so app continues working send_ctrl(ptr, 3, 1) time.sleep(0.5) k32.UnmapViewOfFile(ptr) k32.CloseHandle(h) print('DONE')