Files
admin 1390ad545b 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).
2026-08-22 19:55:08 +07:00

99 lines
5.2 KiB
C++

// native_bridge/include/RenderFxJob.h
#ifndef RENDER_FX_JOB_H
#define RENDER_FX_JOB_H
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
// Offline FX render mode (Phase 0 of PLAN_MASTERBUS_FX_RACK_VST.md):
// `daw_vst_bridge --render-fx <job.json> --in <input.wav> --out <output.wav>`
// reads a stereo WAV, runs the input through a chain of VST3 audio effects
// (audio-in/audio-out, NO MIDI) plus builtin DSP (gain/normalize), and writes
// a 16-bit stereo WAV. Same exit-code contract as --render: 0 ok, 1 job/argument
// error, 2 plugin load failure, 3 plugin crashed mid-render (SEH guarded).
//
// job.json: { sample_rate, block_size?,
// fx_chain: [
// { type: "vst3", path, preset_b64?, bypass? },
// { type: "builtin", id: "gain", params: { db } },
// { type: "builtin", id: "normalize", params: { peak } }
// ] }
// vst3: path = .vst3 module; preset_b64 = base64 of a .vstpreset FILE (raw
// SDK chunk format) or of the bridge state blob — both applied via
// setState before processing. bypass=true → slot skipped (passthrough).
// gain: multiply by 10^(db/20), block-wise.
// normalize: post-chain global pass — scale down to peak (0.95) only when
// the peak EXCEEDS it (mirrors render_project's clip guard).
int run_render_fx_job(const std::string& jobPath, const std::string& inPath,
const std::string& outPath);
// Plugin scan mode (Phase 1): `daw_vst_bridge --scan <dir>` walks <dir>
// recursively, loads every .vst3 module and classifies its classes
// (kVstAudioEffectClass without "Instrument" subcategory → effect, else
// instrument), and prints JSON to stdout:
// {"plugins":[{"path":"...","name":"...","is_fx":true,"is_instrument":false,"type":"VST3"}]}
// .dll/.so (VST2) cannot be introspected headlessly → classified as instrument.
// Returns 0 on success, non-zero on unrecoverable error (a single plugin that
// crashes the module loader is skipped, not fatal).
int run_scan_dir(const std::string& dirPath);
// GUI mode (Mastering/FX Rack → "mở VST GUI"): `daw_vst_bridge --open-fx-gui
// <job.json>` loads ONE VST3 effect plugin, opens its native editor window and
// runs a message loop until the user closes the window (WM_DESTROY), then
// detaches the view and exits 0. job.json: { "path": "...", "name": "..." }.
// Windows only. Exit codes: 0 ok, 1 job/argument error, 2 plugin load or
// editor attach failure.
int run_open_fx_gui(const std::string& jobPath);
// Embedded GUI server mode (Phase 0 of PLAN_MASTERBUS_VST_GUI_EMBED.md):
// `daw_vst_bridge --fx-gui <job.json>` — same plugin load + editor attach as
// --open-fx-gui but the host window is parked off-screen and a tiny HTTP
// server (FxGuiServer.cpp) serves JPEG frames (GET /frame) and relays pointer/
// key input (POST /input) so the web UI can embed the native GUI in a panel.
// Prints `SF_FXGUI_PORT=<port>` to stdout. Windows only. Exit codes same as
// --open-fx-gui.
int run_fx_gui_server(const std::string& jobPath);
// Capture the GUI plugin's current state (component + controller) as base64
// of the bridge blob [4B BE compLen][comp][4B BE ctrlLen][ctrl] - the same
// format applyPreset accepts. Runs on the GUI pump thread; blocks up to
// timeoutMs. outB64 stays empty on timeout or when the plugin has no state.
void fxGuiRequestCapture(void* hwnd, std::string& outB64, int timeoutMs);
// Realtime FX chain (T2.4): live VST3 audio effects on the bridge master mix.
// setChain() is async (a worker thread loads the plugins and swaps the chain);
// process() runs ONLY on the audio thread; shutdown() joins the worker and must
// be called before CoUninitialize. v1: master chain only (per-track deferred).
class RealtimeFxChain {
public:
RealtimeFxChain();
~RealtimeFxChain();
// JSON: [{"path":"...","bypass":true|false},...] — chain entries replace
// the previous chain atomically. May be called from any thread.
void setChain(const std::string& json, double sampleRate, int32_t blockSize);
// SET_PARAM (Phase 2.8): áp dụng live vào builtin slot (slot index theo
// chain JSON). Gọi từ loop thread.
void setParam(int slot, const std::string& key, double value);
// Phase 2.8 (SHM control ring): chain generation — tăng mỗi lần worker
// swap chain; RealtimeFxLoop so gen → báo lại latency qua lat ring.
uint64_t chainGen();
// Per-slot latency samples (thứ tự khớp chain JSON): builtin = 0 (IIR
// zero-latency), VST3 = getLatencySamples (đọc lúc load) — PDC Phase 3.
std::vector<uint32_t> entryLatencies();
// Audio thread only: run the chain in-place over one stereo block.
void process(float* inL, float* inR, uint32_t n);
void shutdown();
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
#endif // RENDER_FX_JOB_H
// Realtime SHM FX loop (fx_vst_bridge --realtime-fx): engine (Python) tạo
// mapping FxRealtimeIPC, bridge mở, đọc input ring, xử lý qua RealtimeFxChain,
// ghi output ring. Thoát khi running==0 hoặc parent chết.
int run_realtime_fx_loop(const std::string& jobPath, const std::string& shmName,
uint32_t parentPid);