feat: native host bridge integration — C++ bridge, Rust SHM, JS routing, build scripts, docs
- native_bridge/: InstrumentEngineManager multi-channel, sample-accurate, CC/program/pitchbend, transport, Vst3Instrument stub (HAVE_VST3SDK) - src-tauri: shm.rs, bridge spawn + audio pump + health monitor, open_vst_gui, externalBin, commands - app: UnifiedMidiRouter, NativeBridgeService, bridgeAudioNode, audioRoutingEngine, Plugin Manager UI, Bridge/WASM indicator, set_position sync - build: 3 ps1 (force-added, build/ ignored), verify_bundle --check-bridge, CI workflow - docs: TASKS.md, TEST_NOTES.md (Windows verify checklist), install/report updates
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
// native_bridge/src/SharedMemoryIPC.cpp
|
||||
#include "SharedMemoryIPC.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
// ── Platform shared-memory helpers (used by main.cpp on Windows and by the
|
||||
// self-check test on POSIX). The DAW (Rust) creates the mapping; the bridge
|
||||
// opens it. Layout must match the Rust struct (see src-tauri/src/shm.rs).
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
struct ShmHandle {
|
||||
HANDLE map = nullptr;
|
||||
void* view = nullptr;
|
||||
};
|
||||
|
||||
ShmHandle* shm_open(const char* name) {
|
||||
ShmHandle* h = new ShmHandle();
|
||||
h->map = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, name);
|
||||
if (!h->map) { delete h; return nullptr; }
|
||||
h->view = MapViewOfFile(h->map, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SharedAudioBufferIPC));
|
||||
if (!h->view) { CloseHandle(h->map); delete h; return nullptr; }
|
||||
return h;
|
||||
}
|
||||
void shm_close(ShmHandle* h) {
|
||||
if (!h) return;
|
||||
if (h->view) UnmapViewOfFile(h->view);
|
||||
if (h->map) CloseHandle(h->map);
|
||||
delete h;
|
||||
}
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct ShmHandle {
|
||||
int fd = -1;
|
||||
void* view = nullptr;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
ShmHandle* shm_open(const char* name) {
|
||||
size_t sz = sizeof(SharedAudioBufferIPC);
|
||||
int fd = ::shm_open(name, O_CREAT | O_RDWR, 0666);
|
||||
if (fd < 0) return nullptr;
|
||||
if (ftruncate(fd, (off_t)sz) != 0) { ::close(fd); return nullptr; }
|
||||
void* view = mmap(nullptr, sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (view == MAP_FAILED) { ::close(fd); return nullptr; }
|
||||
ShmHandle* h = new ShmHandle();
|
||||
h->fd = fd;
|
||||
h->view = view;
|
||||
h->name = name;
|
||||
return h;
|
||||
}
|
||||
void shm_close(ShmHandle* h) {
|
||||
if (!h) return;
|
||||
if (h->view) munmap(h->view, sizeof(SharedAudioBufferIPC));
|
||||
if (h->fd >= 0) ::close(h->fd);
|
||||
shm_unlink(h->name.c_str());
|
||||
delete h;
|
||||
}
|
||||
#endif
|
||||
|
||||
ShmHandle* shm_open_default() { return shm_open("SonicForge_DAW_IPC"); }
|
||||
|
||||
SharedAudioBufferIPC* shm_ptr(ShmHandle* h) {
|
||||
return h ? static_cast<SharedAudioBufferIPC*>(h->view) : nullptr;
|
||||
}
|
||||
|
||||
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) {
|
||||
SharedAudioBufferIPC* ipc = shm_ptr(h);
|
||||
if (!ipc) return false;
|
||||
// Ring overwrite guard: keep at most queue capacity pending events.
|
||||
if (ipc->midiQueueCount >= 64) return false;
|
||||
uint32_t i = ipc->midiQueueCount++;
|
||||
SharedAudioBufferIPC::MidiEventIPC& e = ipc->midiQueue[i];
|
||||
e.command = cmd;
|
||||
e.channel = channel;
|
||||
e.pitch = pitch;
|
||||
e.velocity = velocity;
|
||||
e.data2 = data2;
|
||||
e.data3 = data3;
|
||||
e.sampleOffset = sampleOffset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shm_write_control(ShmHandle* h, uint32_t type, uint32_t arg0, uint32_t arg1,
|
||||
uint32_t channel, const char* path) {
|
||||
SharedAudioBufferIPC* ipc = shm_ptr(h);
|
||||
if (!ipc) return false;
|
||||
if (ipc->controlQueueCount >= 8) return false;
|
||||
uint32_t i = ipc->controlQueueCount++;
|
||||
SharedAudioBufferIPC::ControlEventIPC& c = ipc->controlQueue[i];
|
||||
c.type = type;
|
||||
c.arg0 = arg0;
|
||||
c.channel = channel;
|
||||
std::memset(c.arg2, 0, sizeof(c.arg2));
|
||||
if (path && type == 2 /*LOAD*/) {
|
||||
c.arg1 = (uint32_t)std::strlen(path); // path length in arg1
|
||||
if (c.arg1 < sizeof(c.arg2)) std::memcpy(c.arg2, path, c.arg1);
|
||||
} else {
|
||||
c.arg1 = arg1; // TRANSPORT playhead / OPEN_GUI hwnd etc.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user