native_bridge: recalibrate after preset restore, reject 3rd live VST2 instance
This commit is contained in:
@@ -158,6 +158,11 @@ public:
|
|||||||
void setBypassed(bool on) { isBypassed_.store(on); }
|
void setBypassed(bool on) { isBypassed_.store(on); }
|
||||||
bool isBypassed() const { return isBypassed_.load(); }
|
bool isBypassed() const { return isBypassed_.load(); }
|
||||||
|
|
||||||
|
// Re-run output calibration after a preset/chunk restore (calibrate() in
|
||||||
|
// assign() runs BEFORE the preset exists - VST2 like Qin probes silent and
|
||||||
|
// stays at makeup=1.0, quieter than VSTi calibrated after restore).
|
||||||
|
float recalibrate(uint32_t channel, double sampleRate, uint32_t blockSize);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::unique_ptr<INativeInstrument> create_instrument(InstrumentType type);
|
static std::unique_ptr<INativeInstrument> create_instrument(InstrumentType type);
|
||||||
// Probe-render 3 notes sau khi load, do peak, tinh makeup de note don ra
|
// Probe-render 3 notes sau khi load, do peak, tinh makeup de note don ra
|
||||||
|
|||||||
@@ -276,6 +276,23 @@ bool InstrumentEngineManager::assign(uint32_t channel, InstrumentType type,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float InstrumentEngineManager::recalibrate(uint32_t channel, double sampleRate, uint32_t blockSize) {
|
||||||
|
INativeInstrument* inst = nullptr;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mu_);
|
||||||
|
auto it = channels_.find(channel);
|
||||||
|
if (it != channels_.end()) inst = it->second.get();
|
||||||
|
}
|
||||||
|
if (!inst) return 1.0f;
|
||||||
|
// calibrate() probe-renders (up to ~0.9s) - never hold mu_ (audio stall).
|
||||||
|
const float mk = calibrate(inst, sampleRate, blockSize);
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mu_);
|
||||||
|
makeup_[channel] = mk;
|
||||||
|
}
|
||||||
|
return mk;
|
||||||
|
}
|
||||||
|
|
||||||
void InstrumentEngineManager::unload(uint32_t channel) {
|
void InstrumentEngineManager::unload(uint32_t channel) {
|
||||||
if (channel >= 16) return;
|
if (channel >= 16) return;
|
||||||
// Destructor runs on the CALLER thread (VST3 terminate must run on the
|
// Destructor runs on the CALLER thread (VST3 terminate must run on the
|
||||||
|
|||||||
@@ -1355,10 +1355,44 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
if (ok) {
|
if (ok) {
|
||||||
if (auto* inst = instruments.get(ch)) {
|
if (auto* inst = instruments.get(ch)) {
|
||||||
if ((t == InstrumentType::VST3 || t == InstrumentType::VST2) && !preset.empty())
|
// QIN LIMIT: same-path VST2 instance #3 would
|
||||||
inst->loadSerializedState(preset);
|
// flood auxhost with ML_RELEASE_ERROR (engine
|
||||||
else
|
// supports 2 live instances max). Restore skips
|
||||||
inst->selectProgram(ch, bank, program);
|
// it; frontend keeps the track but muted/empty.
|
||||||
|
if (t == InstrumentType::VST2 && !path.empty()) {
|
||||||
|
uint32_t samePath = 0;
|
||||||
|
for (uint32_t y = 0; y < 16; ++y) {
|
||||||
|
if (y == ch) continue;
|
||||||
|
if (!instruments.get(y)) continue;
|
||||||
|
if (lower_plugin_path(y) == lower_path_str(path)) ++samePath;
|
||||||
|
}
|
||||||
|
if (samePath >= 2) {
|
||||||
|
std::cerr << "[NativeBridge] restore SKIP ch=" << ch
|
||||||
|
<< " VST2 path already has " << samePath
|
||||||
|
<< " live instances (" << path
|
||||||
|
<< ") - plugin engine supports 2 max" << std::endl;
|
||||||
|
instruments.unload(ch);
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
auto* inst2 = instruments.get(ch);
|
||||||
|
if (!inst2) {
|
||||||
|
std::cerr << "[NativeBridge] restore lost inst ch=" << ch << std::endl;
|
||||||
|
} else {
|
||||||
|
if ((t == InstrumentType::VST3 || t == InstrumentType::VST2) && !preset.empty())
|
||||||
|
inst2->loadSerializedState(preset);
|
||||||
|
else
|
||||||
|
inst2->selectProgram(ch, bank, program);
|
||||||
|
// Re-calibrate AFTER the preset exists: assign()
|
||||||
|
// calibrated the empty instance (VST2 like Qin
|
||||||
|
// probes silent -> makeup=1.0, quieter than the
|
||||||
|
// other VSTi). Probe-render runs off mu_ so the
|
||||||
|
// audio loop never stalls.
|
||||||
|
if (t == InstrumentType::VST3 || t == InstrumentType::VST2)
|
||||||
|
instruments.recalibrate(ch, sampleRate, block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
std::cout << "[NativeBridge] restored ch=" << ch
|
std::cout << "[NativeBridge] restored ch=" << ch
|
||||||
<< " type=" << (int)t << " " << path << std::endl;
|
<< " type=" << (int)t << " " << path << std::endl;
|
||||||
@@ -1421,6 +1455,27 @@ int main(int argc, char* argv[]) {
|
|||||||
<< " (already loaded: " << path << ")" << std::endl;
|
<< " (already loaded: " << path << ")" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// QIN LIMIT: Kong Audio Qin bridges all instances through
|
||||||
|
// ONE auxhost.exe (32-bit engine host); a 3rd live Qin
|
||||||
|
// instance makes the engine spam ML_RELEASE_ERROR forever
|
||||||
|
// (observed: ch0+ch3 fine, +ch2 -> bridge log flooded,
|
||||||
|
// engine unusable). Reject VST2 instance #3 of the same
|
||||||
|
// path so the session stays playable.
|
||||||
|
if (t == InstrumentType::VST2 && !path.empty()) {
|
||||||
|
uint32_t samePath = 0;
|
||||||
|
for (uint32_t y = 0; y < 16; ++y) {
|
||||||
|
if (y == ch) continue;
|
||||||
|
if (!instruments.get(y)) continue;
|
||||||
|
if (lower_plugin_path(y) == lower_path_str(path)) ++samePath;
|
||||||
|
}
|
||||||
|
if (samePath >= 2) {
|
||||||
|
std::cerr << "[NativeBridge] LOAD REJECT ch=" << ch
|
||||||
|
<< " VST2 path already has " << samePath
|
||||||
|
<< " live instances (" << path
|
||||||
|
<< ") - plugin engine supports 2 max" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
// CRASH FIX: this job enters the plugin DLL (createInstance
|
// CRASH FIX: this job enters the plugin DLL (createInstance
|
||||||
// in loadPlugin, old-instance terminate on replace) while
|
// in loadPlugin, old-instance terminate on replace) while
|
||||||
// the audio loop may process() another instance of the SAME
|
// the audio loop may process() another instance of the SAME
|
||||||
|
|||||||
Reference in New Issue
Block a user