fix(engine): defer set_param ra worker thread, VST API khong goi tren audio thread

This commit is contained in:
2026-08-24 12:03:05 +07:00
parent b907db3251
commit 4240f15e30
6 changed files with 58 additions and 33 deletions
+1 -1
View File
@@ -51,7 +51,7 @@
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script> <script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script> <script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
<script src="/static/js/services/chordTheory.js?v=202608211300"></script> <script src="/static/js/services/chordTheory.js?v=202608211300"></script>
<script src="/static/js/app.precompiled.js?v=202608241041" defer></script> <script src="/static/js/app.precompiled.js?v=202608241142" defer></script>
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016"> <link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
<style> <style>
:root { :root {
+57 -32
View File
@@ -1228,6 +1228,7 @@ struct RealtimeFxChain::Impl {
struct PendingParam { int slot; std::string key; double value; }; struct PendingParam { int slot; std::string key; double value; };
std::mutex pmutex_; // guards pending_ std::mutex pmutex_; // guards pending_
std::vector<PendingParam> pending_; std::vector<PendingParam> pending_;
std::atomic<int> paramQueued_{0}; // wake worker: param chờ apply
std::mutex mutex_; // guards chain_ / retired_ std::mutex mutex_; // guards chain_ / retired_
std::shared_ptr<Chain> chain_; std::shared_ptr<Chain> chain_;
@@ -1304,20 +1305,19 @@ void RealtimeFxChain::process(float* inL, float* inR, uint32_t n) {
} }
void RealtimeFxChain::setParam(int slot, const std::string& key, double value) { void RealtimeFxChain::setParam(int slot, const std::string& key, double value) {
// SET_PARAM (SHM control ring, Phase 2.8): áp dụng live vào builtin slot. // SET_PARAM (SHM control ring, Phase 2.8): KHÔNG gọi VST API trên audio
// VST3 param automation qua VST3 API — ponytail: them khi co nhu cau. // thread (chain.setParam chạy từ RealtimeFxLoop drain ctrl ring) —
// controller->setParamNormalized là COM call cross-thread với GUI pump,
// block ms→chục ms → out ring cạn → worklet underrun → crackle. Mọi
// param defer vào pending_, worker thread áp (builtin nhanh, VST3 an
// toàn) — audio thread chỉ push queue + notify.
if (!impl_) return; if (!impl_) return;
std::shared_ptr<Impl::Chain> c; {
{ std::lock_guard<std::mutex> lk(impl_->mutex_); c = impl_->chain_; } std::lock_guard<std::mutex> lk(impl_->pmutex_);
if (c && slot >= 0 && (size_t)slot < c->entries.size()) { impl_->pending_.push_back({slot, key, value});
auto& e = c->entries[(size_t)slot];
if (e.bfx) e.bfx->setParam(key, value);
else if (e.fx) e.fx->setParam(key, value);
return;
} }
// Chain chưa swap (SET_PARAM tới trước setChain) — stash, worker áp sau swap. impl_->paramQueued_.fetch_add(1, std::memory_order_release);
std::lock_guard<std::mutex> lk(impl_->pmutex_); impl_->qcv_.notify_all();
impl_->pending_.push_back({slot, key, value});
} }
uint64_t RealtimeFxChain::chainGen() { uint64_t RealtimeFxChain::chainGen() {
@@ -1405,29 +1405,54 @@ void RealtimeFxChain::Impl::workerLoop() {
std::string job; std::string job;
{ {
std::unique_lock<std::mutex> lk(qmutex_); std::unique_lock<std::mutex> lk(qmutex_);
qcv_.wait(lk, [&]() { return quit_ || !q_.empty(); }); qcv_.wait(lk, [&]() {
return quit_ || !q_.empty() || paramQueued_.load(std::memory_order_acquire) > 0;
});
if (quit_) break; if (quit_) break;
job = std::move(q_.front()); if (!q_.empty()) {
q_.erase(q_.begin()); job = std::move(q_.front());
} q_.erase(q_.begin());
std::shared_ptr<Chain> next = buildChain(job); // slow: plugin load }
{ }
std::lock_guard<std::mutex> lk(mutex_); if (!job.empty()) {
retired_ = std::move(chain_); // old chain destroyed on THIS thread std::shared_ptr<Chain> next = buildChain(job); // slow: plugin load
chain_ = std::move(next); {
} std::lock_guard<std::mutex> lk(mutex_);
{ retired_ = std::move(chain_); // old chain destroyed on THIS thread
// Áp param chờ (đăng ký trước khi chain tồn tại). chain_ = std::move(next);
std::lock_guard<std::mutex> lk(pmutex_); }
for (const auto& p : pending_) { gen_.fetch_add(1, std::memory_order_release); // loop báo lại latency
if (p.slot < 0 || (size_t)p.slot >= chain_->entries.size()) continue; }
auto& e = chain_->entries[(size_t)p.slot]; {
if (e.bfx) e.bfx->setParam(p.key, p.value); // Áp param chờ trên WORKER thread (audio thread không bao giờ gọi
else if (e.fx) e.fx->setParam(p.key, p.value); // VST API): queue trước swap áp vào chain mới; queue trong lúc
// chain tồn tại áp ngay, không cần đợi setChain.
std::vector<Impl::PendingParam> toApply;
{
std::lock_guard<std::mutex> lk(pmutex_);
if (!pending_.empty()) {
toApply.swap(pending_);
paramQueued_.store(0, std::memory_order_release);
}
}
if (!toApply.empty()) {
std::shared_ptr<Chain> c;
{ std::lock_guard<std::mutex> lk(mutex_); c = chain_; }
if (c) {
for (const auto& p : toApply) {
if (p.slot < 0 || (size_t)p.slot >= c->entries.size()) continue;
auto& e = c->entries[(size_t)p.slot];
if (e.bfx) e.bfx->setParam(p.key, p.value);
else if (e.fx) e.fx->setParam(p.key, p.value);
}
} else {
// Chain chưa tồn tại — stash lại, áp sau swap.
std::lock_guard<std::mutex> lk(pmutex_);
pending_.insert(pending_.begin(), toApply.begin(), toApply.end());
paramQueued_.fetch_add((int)toApply.size(), std::memory_order_release);
}
} }
pending_.clear();
} }
gen_.fetch_add(1, std::memory_order_release); // loop báo lại latency
} }
#ifdef _WIN32 #ifdef _WIN32
CoUninitialize(); CoUninitialize();
Binary file not shown.
Binary file not shown.