Files
SonicForgeStudio/native_bridge/tests/shm_selfcheck.cpp
admin 6a3b430da0 A14: MIDI-out từ plugin (VST2/VST3) → SHM → Rust emit → DAW track
- Vst2Instrument: audioMaster callback thu VstEvents, push SHM midiOutQueue
- Vst3Instrument: IEventList output bus (kAudioMasterWantMidi) — check
- SharedMemoryIPC.h: midiOutQueue[64] + midiOutCount (offset 27556/28324, sizeof 28328)
- shm.rs: MidiEventIPC Serialize, drain_midi_out() emit 'bridge-midi-out'
- lib.rs: pump emit midi-out events
- nativeBridgeService.js: onMidiOut API
- app.jsx: route MIDI-out về selected track qua SonicMidiRouter (feedback guard)
- TASKS.md: A14 ✅
2026-08-19 21:12:07 +07:00

89 lines
3.9 KiB
C++

// native_bridge/tests/shm_selfcheck.cpp
// Self-check for SharedMemoryIPC helpers (assert-based, no framework).
// POSIX path runs on Linux/macOS; Win32 path guarded (runs on Windows build).
#include "../include/SharedMemoryIPC.h"
#include "../src/SharedMemoryIPC.cpp"
#include <cassert>
#include <cstdio>
#include <cstring>
int main() {
// Use a test-only name to avoid clashing with a live DAW bridge.
ShmHandle* h = shm_open("SonicForge_SelfCheck_IPC");
#ifdef _WIN32
// On Windows the DAW (Rust) creates the mapping; standalone self-check
// creates it here when absent.
if (!h) h = shm_create("SonicForge_SelfCheck_IPC");
#endif
assert(h != nullptr);
SharedAudioBufferIPC* ipc = shm_ptr(h);
assert(ipc != nullptr);
// Reset state (POSIX shm may survive an aborted previous run).
std::memset(ipc, 0, sizeof(SharedAudioBufferIPC));
// 1. MIDI event write + read back (A12: data2/data3 for CC/program/pitch bend)
assert(shm_write_midi(h, 0x9, 2, 60, 100, 37));
assert(ipc->midiQueueCount == 1);
assert(ipc->midiQueue[0].command == 0x9);
assert(ipc->midiQueue[0].channel == 2);
assert(ipc->midiQueue[0].pitch == 60);
assert(ipc->midiQueue[0].velocity == 100);
assert(ipc->midiQueue[0].sampleOffset == 37);
assert(ipc->midiQueue[0].data2 == 0 && ipc->midiQueue[0].data3 == 0);
assert(shm_write_midi(h, 0xB, 3, 64, 0, 0, 127)); // CC64 sustain = 127
assert(shm_write_midi(h, 0xE, 3, 0, 0, 0, 0x00, 0x40)); // pitch bend MSB
assert(ipc->midiQueue[1].data2 == 127);
assert(ipc->midiQueue[2].data3 == 0x40);
assert(ipc->midiQueueCount == 3);
// 2. Control event with path + channel (A10)
assert(shm_write_control(h, 2, 1 /*SOUNDFONT_SF2_SF3*/, 0, 4, "C:\\sf\\SGM.sf2"));
assert(ipc->controlQueueCount == 1);
assert(ipc->controlQueue[0].type == 2);
assert(ipc->controlQueue[0].arg0 == 1);
assert(ipc->controlQueue[0].channel == 4);
assert(std::strcmp(ipc->controlQueue[0].arg2, "C:\\sf\\SGM.sf2") == 0);
// TRANSPORT (A13): STOP with playhead arg
assert(shm_write_control(h, 3, 0, 44100, 0, ""));
assert(ipc->controlQueue[1].type == 3 && ipc->controlQueue[1].arg0 == 0
&& ipc->controlQueue[1].arg1 == 44100);
// 3. Queue capacity guard
for (int i = 0; i < 70; ++i) shm_write_midi(h, 0x8, 0, 40, 0, 0);
assert(ipc->midiQueueCount == 64); // capped, not overflowed
// 4. Audio block round-trip
for (uint32_t i = 0; i < AUDIO_BLOCK_SIZE; ++i) ipc->masterLeft[i] = (float)i;
ipc->bridgeWriteIndex++;
ipc->blockTimestamp = 123456;
assert(ipc->masterLeft[255] == 255.0f);
assert(ipc->bridgeWriteIndex == 1);
assert(ipc->blockTimestamp == 123456);
// 5. Layout stability — MUST match Rust src-tauri/src/shm.rs
assert(sizeof(SharedAudioBufferIPC::MidiEventIPC) == 12);
assert(sizeof(SharedAudioBufferIPC::ControlEventIPC) == 1040);
// G4.5 ring: 11168 (G3.1 +heartbeat) + ringMask(4) + ringLeft(8192) + ringRight(8192)
// = 27556, rounded up to 8-byte struct alignment (u64 blockTimestamp) => 27560.
// A14 midi-out: ring_right ends at 27556; midiOutQueue appends there,
// +64*12 + 4 = 28328 (already 8-aligned). Matches Rust size_of (src-tauri/src/shm.rs).
assert(sizeof(SharedAudioBufferIPC) == 28328);
// 6. Audio ring round-trip (multi-slot, VSTi crackle fix)
ipc->ringMask = 7;
for (uint32_t i = 0; i < AUDIO_BLOCK_SIZE; ++i) ipc->ringLeft[3][i] = (float)i * 2.0f;
ipc->ringRight[3][0] = 42.0f;
assert(ipc->ringLeft[3][255] == 510.0f);
assert(ipc->ringRight[3][0] == 42.0f);
assert((ipc->bridgeWriteIndex & ipc->ringMask) < 8);
std::printf("SHM self-check sizeof struct = %zu bytes\n", sizeof(SharedAudioBufferIPC));
std::printf("midiOutQueue offset = %zu, midiOutCount offset = %zu\n",
offsetof(SharedAudioBufferIPC, midiOutQueue),
offsetof(SharedAudioBufferIPC, midiOutCount));
shm_close(h);
return 0;
}