// native_bridge/include/SharedMemoryIPC.h #pragma once #include #include #ifdef _WIN32 #include #endif #define AUDIO_BLOCK_SIZE 256 // WARNING: volatile is NOT a sync primitive. Real impl should use an // interlocked/index-flag pair or a Win32 event (SetEvent) signalled by the // writer. This struct follows the spec layout so Rust/JS mapping stays in sync. struct SharedAudioBufferIPC { // Synchronization Flags volatile uint32_t clientReadIndex; volatile uint32_t bridgeWriteIndex; // PCM Float32 Audio Buffers float masterLeft[AUDIO_BLOCK_SIZE]; float masterRight[AUDIO_BLOCK_SIZE]; // Latency probe: bridge stamps every rendered block (QueryPerformanceCounter) volatile uint64_t blockTimestamp; // MIDI Event Exchange Queue struct MidiEventIPC { uint8_t command; // 0x9 note on / 0x8 note off / 0xB CC / 0xC program / 0xE pitch bend uint8_t channel; uint8_t pitch; // note number (0x9/0x8) or CC number (0xB) uint8_t velocity; // 0-127 (0x9 + vel=0 == note off) uint8_t data2; // CC value / program number / PB LSB uint8_t data3; // PB MSB (0xE only) uint8_t reserved[2]; // reserved[0]: live flag (1 = live input, bypass transportStopped guard); reserved[1]: 0 — keeps C/Rust layout stable uint32_t sampleOffset; } midiQueue[64]; volatile uint32_t midiQueueCount; // Transport / Control (added vs spec: Stop/Play flush, instrument switch) struct ControlEventIPC { uint32_t type; // 0 = NONE, 1 = PANIC/ALL_NOTES_OFF, 2 = LOAD_INSTRUMENT, // 3 = TRANSPORT, 4 = OPEN_GUI uint32_t arg0; // LOAD: instrument type (InstrumentType); // TRANSPORT: 0=STOP, 1=PLAY, 2=SET_POSITION uint32_t arg1; // LOAD: string length (bytes) of path; // TRANSPORT: playheadSamples (for timeline sync) uint32_t channel; // LOAD: MIDI channel to assign instrument to (A10) char arg2[1024]; // LOAD: UTF-8 path } controlQueue[8]; volatile uint32_t controlQueueCount; // G0.3: set by the bridge while an openGUI attach job is in flight // (reload/attachView on a ChannelWorker). The Tauri watchdog reads this // to extend its stall deadline — a long Nexus attach must not look like // a dead bridge. Append-only field: existing offsets stay stable. volatile uint32_t attachInFlight; // G3.1: dedicated heartbeat, incremented every 100ms by a bridge-side // thread INDEPENDENT of the audio loop. The Tauri watchdog keys bridge // liveness on this, so a slow (but alive) audio loop no longer looks dead. // Append-only field: existing offsets stay stable. volatile uint32_t heartbeat; }; // G4.3: per-plugin sandbox SHM — double-buffered audio handoff. // The child renders into the slot it is NOT publishing (writeSlot flips), // then publishes; the bridge reads the published slot. A stale slot is safe // to read (bridge keeps last block), so a slow child never tears audio. struct SandboxHostIPC { SharedAudioBufferIPC base; // MIDI/control/heartbeat, offsets unchanged volatile uint32_t writeSlot; // 0 or 1: slot the child finished writing float audioLeft[2][AUDIO_BLOCK_SIZE]; // 2 full blocks float audioRight[2][AUDIO_BLOCK_SIZE]; }; // Shared-memory helpers (impl in SharedMemoryIPC.cpp). struct ShmHandle; ShmHandle* shm_open(const char* name, size_t size = sizeof(SharedAudioBufferIPC)); ShmHandle* shm_create(const char* name, size_t size = sizeof(SharedAudioBufferIPC)); SharedAudioBufferIPC* shm_ptr(ShmHandle* h); SandboxHostIPC* shm_sandbox_ptr(ShmHandle* h); void shm_close(ShmHandle* h); bool shm_write_midi(ShmHandle* h, uint8_t cmd, uint8_t channel, uint8_t pitch, uint8_t velocity, uint32_t sampleOffset, uint8_t data2 = 0, uint8_t data3 = 0);