fix(engine): defer set_param ra worker thread, VST API khong goi tren audio thread
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user