// native_bridge/include/SandboxVst3Host.h // G4.2: VST3 instrument hosted in a CHILD PROCESS (plugin_host.exe). The // plugin's crash kills only the child; this host mutes the channel, respawns // the child, and the bridge + other channels keep running. MIDI flows bridge // -> child SHM (midiQueue), audio flows child -> bridge (masterLeft/Right). #ifndef SANDBOX_VST3_HOST_H #define SANDBOX_VST3_HOST_H #include "INativeInstrument.h" #include "SharedMemoryIPC.h" #include #include #include #ifdef _WIN32 #include #endif class SandboxVst3Host : public INativeInstrument { public: SandboxVst3Host(); ~SandboxVst3Host() override; bool loadPlugin(const std::string& path, double sampleRate, uint32_t channel); bool init(double sampleRate, uint32_t maxBlockSize) override; void selectProgram(uint32_t channel, uint32_t bank, uint32_t program) override {} void noteOn(uint32_t channel, uint32_t pitch, float velocity, uint32_t sampleOffset) override; void noteOff(uint32_t channel, uint32_t pitch, uint32_t sampleOffset) override; void controlChange(uint32_t channel, uint32_t cc, uint32_t value) override; void programChange(uint32_t channel, uint32_t program) override; void pitchBend(uint32_t channel, uint32_t bend14) override; // The child owns its GUI window (plugin_host opens it on its main thread). bool openGUI(void* parentWindowHandle) override { (void)parentWindowHandle; return true; } void closeGUI() override {} // child window dies with the child process void processAudioBlock(float* outputL, float* outputR, uint32_t numSamples) override; private: bool spawnChild(); void watchdogLoop(); std::string path_; std::string shmName_; double sampleRate_ = 44100.0; uint32_t block_ = AUDIO_BLOCK_SIZE; uint32_t channel_ = 0; ShmHandle* shm_ = nullptr; SandboxHostIPC* ipc_ = nullptr; std::atomic alive_{false}; std::atomic stop_{false}; std::thread watchdog_; #ifdef _WIN32 HANDLE childProc_ = nullptr; #endif }; #endif // SANDBOX_VST3_HOST_H