Phase 2: BuiltinFxChain C++ (8 DSP) + control ring SET_PARAM/REPORT_LATENCY

- native_bridge/src/BuiltinFxChain.{h,cpp}: 8 builtin DSP port 1:1 từ
  Python _apply_builtin_fx_chain (eq/eqpro/compressor/limiter/exciter/rebalance)
  và JS MASTER_MODULE_IO (imager/maximizer). Biquad RBJ DF2T, block-wise
  stateful. createBuiltinFx trả nullptr cho gain/normalize (legacy).
- RenderFxJob: FxSlot thêm bfx, parse builtin 8 loại, runBuiltinSlotSafe SEH,
  realtimeRunChain nhánh builtin, chainGen()/entryLatencies(), setParam
  áp live lên bfx; Vst3Fx latencySamples từ getLatencySamples().
- RealtimeFxLoop: drain ctrl ring (SET_PARAM -> chain.setParam, drop cũ khi
  đầy) + REPORT_LATENCY khi gen đổi (ipc->lat[] ring, lastGen).
- FxRealtimeIPC.h: header 18 u32 (72B), ctrl/lat slots 8, FxCtrlCmd 24B,
  FxLatReport 8B, static_asserts.
- app/core/fx_realtime.py: mirror header 72B (ctrl_write/read, lat_write/read,
  slots), set_param ghi ctrl ring qua _ctrl_enqueue_locked (guard shm),
  get_latencies drain lat ring qua _lat_drain.
- tests/test_builtin_fx_golden.py: golden SNR C++ vs Python (8 cases, ngưỡng
  30dB) + order test eq/compressor xen kẽ.
- TASKS_DAW_A.md: tick 2.1-2.9.

Test: 136 passed (4 fx_realtime_chain + 8 builtin golden + regression).
This commit is contained in:
2026-08-22 19:55:08 +07:00
parent fd790c1dc6
commit 1390ad545b
10 changed files with 980 additions and 29 deletions
+30
View File
@@ -166,11 +166,41 @@ int run_realtime_fx_loop(const std::string& jobPath, const std::string& shmName,
uint32_t inMask = ipc->h.inSlots - 1;
uint32_t outMask = ipc->h.outSlots - 1;
uint64_t processed = 0;
uint64_t lastGen = 0; // chain generation đã báo latency
while (ipc->h.running) {
if (parentPid && !parentAlive(parentPid)) {
std::cerr << "[RealtimeFxLoop] parent gone — exiting" << std::endl;
break;
}
// SET_PARAM ring (engine -> bridge, Phase 2.8): drain mỗi iteration —
// áp dụng vào chain TRƯỚC block kế tiếp. Ring đầy → drop lệnh cũ.
while (ipc->h.ctrlRead < ipc->h.ctrlWrite) {
const uint32_t cs = ipc->h.ctrlRead & (FXRT_CTRL_SLOTS - 1);
const FxCtrlCmd& cmd = ipc->ctrl[cs];
const char* kend = std::find(cmd.key, cmd.key + sizeof(cmd.key), '\0');
chain.setParam((int)cmd.slot,
std::string(cmd.key, (size_t)(kend - cmd.key)),
(double)cmd.value);
MemoryBarrier();
ipc->h.ctrlRead++;
}
// REPORT_LATENCY (bridge -> engine): chain swap (gen đổi) → báo lại
// latency từng slot (builtin=0; VST3=getLatencySamples). Ring 8 —
// engine drain định kỳ, đầy thì overwrite (drop cũ).
const uint64_t gen = chain.chainGen();
if (gen != lastGen) {
const std::vector<uint32_t> lats = chain.entryLatencies();
for (size_t i = 0; i < lats.size(); ++i) {
const uint32_t ls = ipc->h.latWrite & (FXRT_LAT_SLOTS - 1);
ipc->lat[ls].slot = (uint32_t)i;
ipc->lat[ls].samples = lats[i];
MemoryBarrier();
ipc->h.latWrite++;
}
lastGen = gen;
std::cerr << "[RealtimeFxLoop] latency reported: " << lats.size()
<< " slot(s)" << std::endl;
}
const uint32_t avail = ipc->h.inWrite - ipc->h.inRead;
if (avail == 0) { sleepMs(1); continue; }
const uint32_t slot = ipc->h.inRead & inMask;