fix(bridge): 3 bug âm thanh Qin/Nexus standalone
1) wavesynth tự phát khi mở app: probe calibrate() render vào live mix (renderAll process cùng instance) -> thêm calibratingCh_[16], renderAll bỏ qua channel đang calibrate; plugin_host panic (sustain off + all notes off + 128 noteOff) ngay sau preset apply để startup im lặng. 2) bấm GUI mở 2 cửa sổ Qin: sandbox VST2 child tự sở hữu top-level window -> ownsGuiWindow() + main.cpp forward OPEN_GUI control, không tạo native parent / không registry double-open. 3) play phát nhạc cụ sai: restore clobber — seed preset đè patch thật trong state file (ch3 63ba3667 -> 7f07473c) do saveStateJob chạy lúc child còn seed. Fix: queuePreset(waitApply=true) chờ child apply xong (presetOut memcmp), consume-before-apply (zero presetInLen trước khi apply), deferred double-load re-check trong main.cpp skip assign trùng path, watchdog respawn re-queue lastPreset_.
This commit is contained in:
@@ -162,6 +162,14 @@ void SandboxVst2Host::watchdogLoop() {
|
||||
if (stop_.load()) break;
|
||||
if (spawnChild()) {
|
||||
fails = 0;
|
||||
// G4.6: respawned child starts from FACTORY state — re-queue the
|
||||
// last preset (real patch or seed) so it does not publish/sound
|
||||
// silent factory and clobber the state file.
|
||||
if (!lastPreset_.empty()) {
|
||||
queuePreset(lastPreset_);
|
||||
std::cerr << "[SandboxVst2Host] preset re-queued after respawn ch="
|
||||
<< channel_ << " (" << lastPreset_.size() << "B)" << std::endl;
|
||||
}
|
||||
std::cerr << "[SandboxVst2Host] ch=" << channel_ << " respawned" << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -253,24 +261,51 @@ void SandboxVst2Host::seedDefaultPreset() {
|
||||
std::string b64;
|
||||
b64.resize((size_t)sz);
|
||||
if (fread(&b64[0], 1, (size_t)sz, f) == (size_t)sz && !b64.empty()) {
|
||||
memcpy((void*)ipc_->presetIn, b64.data(), b64.size());
|
||||
ipc_->presetIn[b64.size()] = 0;
|
||||
ipc_->presetInLen = (uint32_t)b64.size();
|
||||
lastPreset_ = b64;
|
||||
queuePreset(b64);
|
||||
std::cerr << "[SandboxVst2Host] default preset seeded ch=" << channel_
|
||||
<< " (" << b64.size() << "B)" << std::endl;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
void SandboxVst2Host::loadSerializedState(const std::string& base64) {
|
||||
if (!alive_.load() || !ipc_ || base64.empty()) return;
|
||||
void SandboxVst2Host::queuePreset(const std::string& base64, bool waitApply) {
|
||||
if (!ipc_ || base64.empty()) return;
|
||||
size_t n = base64.size();
|
||||
if (n >= PRESET_BLOB_SIZE) n = PRESET_BLOB_SIZE - 1;
|
||||
memcpy((void*)ipc_->presetIn, base64.data(), n);
|
||||
ipc_->presetIn[n] = 0;
|
||||
ipc_->presetInLen = (uint32_t)n;
|
||||
// waitApply: block until the child has APPLIED this preset (presetOut
|
||||
// echoes the chunk) before returning. The restore path queues the real
|
||||
// saved preset right after spawn; without this wait a saveStateJob can
|
||||
// fire while the child is still applying the seed default and persist the
|
||||
// seed over the real chunk (restore clobber). Qin round-trips
|
||||
// byte-identical; 20s timeout guards a stuck child.
|
||||
if (!waitApply) return;
|
||||
if (ipc_->presetOutLen == (uint32_t)n &&
|
||||
memcmp(ipc_->presetOut, base64.data(), n) == 0) return;
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
while (std::chrono::steady_clock::now() - t0 < std::chrono::seconds(20)) {
|
||||
if (ipc_->presetOutLen == (uint32_t)n &&
|
||||
memcmp(ipc_->presetOut, base64.data(), n) == 0) break;
|
||||
Sleep(50);
|
||||
}
|
||||
bool confirmed = ipc_->presetOutLen == (uint32_t)n &&
|
||||
memcmp(ipc_->presetOut, base64.data(), n) == 0;
|
||||
std::cerr << "[SandboxVst2Host] preset apply waited ch=" << channel_
|
||||
<< " (" << base64.size() << "B)"
|
||||
<< (confirmed ? " confirmed" : " TIMEOUT")
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void SandboxVst2Host::loadSerializedState(const std::string& base64) {
|
||||
if (base64.empty()) return;
|
||||
lastPreset_ = base64; // keep for watchdog respawn re-queue
|
||||
if (!alive_.load() || !ipc_) return;
|
||||
queuePreset(base64, true); // wait for apply so early saves see the real chunk
|
||||
std::cerr << "[SandboxVst2Host] presetIn queued ch=" << channel_
|
||||
<< " (" << n << "B)" << std::endl;
|
||||
<< " (" << base64.size() << "B)" << std::endl;
|
||||
}
|
||||
|
||||
std::string SandboxVst2Host::serializeState() const {
|
||||
|
||||
Reference in New Issue
Block a user