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 ✅
This commit is contained in:
2026-08-19 21:12:07 +07:00
parent 6efef85e47
commit 6a3b430da0
10 changed files with 375 additions and 226 deletions
+22
View File
@@ -120,6 +120,7 @@ static std::mutex g_guiMutex;
static std::map<uint32_t, void*> g_guiWindows; // channel -> HWND (keep window alive)
static std::map<HWND, uint32_t> g_hwndToCh; // HWND -> channel (WM_DESTROY cleanup)
static ChannelWorker* g_uiWorker = nullptr;
static SharedAudioBufferIPC* g_shmIPC = nullptr; // A14: midi-out hook writes SHM
// VST2 (DUNE 3) thread contract: entry + init dispatcher + processReplacing
// must share ONE thread. processReplacing runs on the audio (main) thread,
// so VST2 loads (LoadLibrary + VSTPluginMain + init dispatcher) are deferred
@@ -852,6 +853,7 @@ int main(int argc, char* argv[]) {
}
auto* shmIPC = (SharedAudioBufferIPC*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SharedAudioBufferIPC));
if (!shmIPC) { CloseHandle(hMapFile); return 1; }
g_shmIPC = shmIPC; // A14: midi-out hook (audio thread) writes SHM via this
// VSTi crackle fix: 8-slot audio ring (see SharedMemoryIPC.h). Must be set
// on Windows too — ringMask stays 0 otherwise and the ring collapses to
// 1 slot (slot = writeIndex & 0), dropping whole blocks on any jank.
@@ -903,6 +905,26 @@ int main(int argc, char* argv[]) {
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
});
});
// A14: plugin MIDI output sink. audioMaster(kAudioMasterProcessEvents)
// (VstEvents from Scaler2/Instacomposer/...) -> SHM midiOutQueue,
// drained by the Tauri pump to `bridge-midi-out`, JS routes notes to
// DAW tracks. Append-only, no mutex — same pattern as midiQueue (audio
// thread writes, pump reads+resets). Cap 64; drop when full.
Vst2Instrument::setMidiOutHook([](uint8_t cmd, uint8_t ch, uint8_t d1,
uint8_t d2, uint32_t deltaFrames) {
if (!g_shmIPC) return;
if (g_shmIPC->midiOutCount >= 64) return;
auto& ev = g_shmIPC->midiOutQueue[g_shmIPC->midiOutCount++];
ev.command = cmd;
ev.channel = ch;
ev.pitch = d1;
ev.velocity = d2;
ev.data2 = 0;
ev.data3 = 0;
ev.reserved[0] = 0;
ev.reserved[1] = 0;
ev.sampleOffset = deltaFrames;
});
// B9: native editor windows per channel — keep alive (HWND outlives the job).
// Registry la global (g_guiWindows) — WM_DESTROY cleanup can tu VstWindowProc.
// B8: sample rate from the DAW (Rust spawns us with SF_SAMPLE_RATE).