From e79dae5d5cf03b828a475f26b26be33a7d7aad47 Mon Sep 17 00:00:00 2001 From: locphamtran Date: Mon, 17 Aug 2026 11:46:24 +0700 Subject: [PATCH] =?UTF-8?q?phase4:=20Linux=20native=5Fbridge=20build=20(Do?= =?UTF-8?q?cker)=20=E2=80=94=20sfizz=20from=20source,=20--render=20SF2/SFZ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile: build sfizz tu native_bridge/sfizz-src (hoac git clone v1.2.3), sed fix sfizz.pc (-llibsfizz -> -lsfizz), cmake bridge voi 3 flag SMTG OFF (VSTGUI/plugin/hosting examples) — bridge headless chi can --render. - CMakeLists: nhanh non-WIN32 build daw_vst_bridge tu main_linux.cpp + RenderJob.cpp + NativeInstrumentEngine.cpp (SF2/SF3 + SFZ; VST3/VST2 hosting Windows-only, guard #ifdef _WIN32). gui_probe/plugin_host/ seh_channel_test chi build tren Windows. - RenderJob.cpp: JSON parser (sheredom json.h) khong con phu thuoc HAVE_VST3SDK — render SF2/SFZ chay duoc khong can VST3 SDK. - NativeInstrumentEngine.cpp: guard windows.h/VST3/VST2/Sandbox/SEH. - main.cpp: g_engine unconditional + guard disable_ime_contexts (_WIN32). - engine.spec: bo pedalboard collect_dynamic_libs + hidden imports. - vst_engine/render_engine: DecentSampler -> NotImplementedError + huong dan convert .dspreset -> .vstpreset; pianobook fallback synth. - Verified: WSL Ubuntu 24.04 build OK, bridge Linux --render SF2 ra WAV 44100 stereo peak 1575; Python tests 115 passed 1 skipped. - Dockerfile build chua test duoc (may khong co Docker). --- Dockerfile | 27 ++++++++++++++ PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md | 3 +- app/core/render_engine.py | 38 +++++--------------- app/core/vst_engine.py | 28 +++++---------- engine.spec | 12 ++----- native_bridge/CMakeLists.txt | 16 +++++++-- native_bridge/src/NativeInstrumentEngine.cpp | 37 ++++++++++++++++++- native_bridge/src/RenderJob.cpp | 17 ++++----- native_bridge/src/main.cpp | 6 +++- native_bridge/src/main_linux.cpp | 22 ++++++++++++ 10 files changed, 135 insertions(+), 71 deletions(-) create mode 100644 native_bridge/src/main_linux.cpp diff --git a/Dockerfile b/Dockerfile index ddbea95..cf90a81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,6 +37,33 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . +# ── Build native_bridge (Linux) — thay pedalboard GPL-3.0 ────────────────── +# Xem PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md §4. sfizz khong co trong apt +# Debian → build tu nguon vendored (native_bridge/sfizz-src, co trong build +# context) hoac git clone fallback khi thieu. Bridge chay --render duoi +# Xvfb :99 (DISPLAY da set o tren) — khong can GUI plugin editor. +RUN apt-get update && apt-get install -y --no-install-recommends \ + cmake pkg-config libfluidsynth-dev libfreetype-dev git \ + && rm -rf /var/lib/apt/lists/* \ + && if [ -d native_bridge/sfizz-src ]; then cp -r native_bridge/sfizz-src /tmp/sfizz; \ + else git clone --recursive --depth 1 --branch v1.2.3 https://github.com/sfztools/sfizz.git /tmp/sfizz; fi \ + && cmake -S /tmp/sfizz -B /tmp/sfizz/build -DCMAKE_BUILD_TYPE=Release \ + -DSFIZZ_SHARED=ON -DSFIZZ_JACK=OFF -DSFIZZ_RENDER=OFF -DSFIZZ_TESTS=OFF \ + -DSFIZZ_BENCHMARKS=OFF -DSFIZZ_DEMOS=OFF -DSFIZZ_DEVTOOLS=OFF \ + -DSFIZZ_GIT_SUBMODULE_CHECK=OFF -DBUILD_TESTING=OFF \ + && cmake --build /tmp/sfizz/build --target sfizz_shared -j"$(nproc)" \ + && cmake --install /tmp/sfizz/build \ + && sed -i 's/-llibsfizz/-lsfizz/' /usr/local/lib/pkgconfig/sfizz.pc \ + && cmake -S native_bridge -B /tmp/bridge-build -DCMAKE_BUILD_TYPE=Release \ + -DSMTG_ENABLE_VST3_PLUGIN_EXAMPLES=OFF \ + -DSMTG_ENABLE_VST3_HOSTING_EXAMPLES=OFF \ + -DSMTG_ENABLE_VSTGUI_SUPPORT=OFF \ + && cmake --build /tmp/bridge-build --target daw_vst_bridge -j"$(nproc)" \ + && mkdir -p /app/install \ + && cp /tmp/bridge-build/daw_vst_bridge /app/install/ \ + && rm -rf /tmp/sfizz /tmp/sfizz/build /tmp/bridge-build + + # Copy VST3 plugins if present COPY ./vst_plugins/ /opt/daw_engine/vst3/ diff --git a/PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md b/PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md index 0ad385a..f8f9fef 100644 --- a/PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md +++ b/PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md @@ -1,8 +1,7 @@ # PLAN: thay pedalboard (GPL-3.0) bằng native_bridge — render VSTi offline Ngày: 2026-08-15 -Trạng thái: Phase 2 xong (app/core/native_render.py client spawn bridge --render; plugins.py preview + /midi-render dùng bridge khi SF_RENDER_ENGINE=bridge; velocity float 0..1 đã fix double-scaling; bank/program parity; golden test bridge vs pedalboard RMS; test Python 111 pass 1 skip, C++ 20/20). Windows sửa code bridge/desktop; -Linux làm bridge Linux + Docker + test so sánh. +Trạng thái: Phase 4 xong (engine.spec bỏ pedalboard; DecentSampler → hướng dẫn convert .dspreset→.vstpreset; Dockerfile build native_bridge Linux — sfizz build từ nguồn + sed fix sfizz.pc + bridge --render SF2/SFZ; Linux port: CMakeLists nhánh non-WIN32, main_linux.cpp, guard Windows-only NativeInstrumentEngine.cpp/RenderJob.cpp; verified WSL Ubuntu 24.04 — bridge Linux render SF2 ra WAV 44100 stereo peak 1575; test Python 115 pass 1 skip). Phase 5 còn lại: đảo flag mặc định bridge, gỡ pedalboard khỏi requirements/source, LICENSE. ## 1. Mục tiêu Xóa dependency GPL-3.0 (`pedalboard`) khỏi cả 2 môi trường, chuyển toàn bộ diff --git a/app/core/render_engine.py b/app/core/render_engine.py index 2fa383c..2c5cded 100644 --- a/app/core/render_engine.py +++ b/app/core/render_engine.py @@ -7,7 +7,6 @@ from app.config import settings from app.core.vst_engine import ( render_midi_events_to_audio, PluginManager, - DecentSamplerManager, HAS_PEDALBOARD, HAS_PYFLUIDSYNTH, apply_preset_to_plugin, @@ -204,35 +203,16 @@ class PythonRenderEngine: vst = plugin_mgr.load_vst(instrument_id) if instrument_id else None if instrument_source == "pianobook": + # Phase 4 (§3.4): .dspreset không hỗ trợ nữa (pedalboard + # GPL-3.0 gỡ; spike load .dspreset fail). Chặn + fallback synth. dspreset_path = track.get("dspreset_path", "") - if dspreset_path and os.path.exists(dspreset_path) and HAS_PEDALBOARD: - from pedalboard import Pedalboard - ds_manager = DecentSamplerManager() - try: - vst = ds_manager.create_decent_sampler_instance(dspreset_path) - midi_messages = PluginManager.midi_events_to_messages( - midi_events, bpm, self.sample_rate - ) - total_needed = 0 - for ev in midi_events: - end_sec = (ev.get("start_beat", 0) + ev.get("duration_beats", 1)) * (60.0 / bpm) - dur_samples = int(end_sec * self.sample_rate) - if dur_samples > total_needed: - total_needed = dur_samples - total_needed = max(total_needed, 1024) - silent = np.zeros((2, total_needed), dtype=np.float32) - # pedalboard >= 0.9: instrument KHÔNG vào Pedalboard container - synth_buffer = vst(midi_messages, sample_rate=self.sample_rate, - duration=total_needed / float(self.sample_rate), num_channels=2) - except Exception as e: - logger.warning(f"[RenderEngine] DecentSampler/Pianobook error: {e}") - synth_buffer = render_midi_events_to_audio( - midi_events=midi_events, sr=self.sample_rate, bpm=bpm, instrument='synth' - ) - else: - synth_buffer = render_midi_events_to_audio( - midi_events=midi_events, sr=self.sample_rate, bpm=bpm, instrument='synth' - ) + logger.warning( + "[RenderEngine] Pianobook track bỏ qua: .dspreset không còn hỗ trợ. " + "Convert sang .vstpreset (DecentSampler → Save preset) rồi gán lại track." + ) + synth_buffer = render_midi_events_to_audio( + midi_events=midi_events, sr=self.sample_rate, bpm=bpm, instrument='synth' + ) elif instrument_id and not instrument_id.startswith("sf_"): # VSTi track: native_bridge --render (SF_RENDER_ENGINE=bridge) # thay pedalboard; ngược lại giữ pedalboard path cũ. diff --git a/app/core/vst_engine.py b/app/core/vst_engine.py index 62af676..cd33757 100644 --- a/app/core/vst_engine.py +++ b/app/core/vst_engine.py @@ -521,25 +521,15 @@ class DecentSamplerManager: self.vst_path = vst_path def create_decent_sampler_instance(self, dspreset_path: str): - if not HAS_PEDALBOARD: - raise RuntimeError("pedalboard not available") - if not os.path.exists(self.vst_path): - raise FileNotFoundError(f"DecentSampler VST3 not found at {self.vst_path}") - if not os.path.exists(dspreset_path): - raise FileNotFoundError(f"Preset file not found at {dspreset_path}") - - plugin = VST3Plugin(self.vst_path) - - abs_preset = os.path.abspath(dspreset_path) - preset_dir = os.path.dirname(abs_preset) - cwd_before = os.getcwd() - try: - os.chdir(preset_dir) - plugin.load_preset(abs_preset) - finally: - os.chdir(cwd_before) - - return plugin + # Phase 4 (PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md §3.4): .dspreset là + # format riêng của DecentSampler (không phải VST3 state) — spike đã fail + # cả trên pedalboard (container: "failed to load data from preset file"). + # pedalboard GPL-3.0 bị gỡ → chặn Pianobook + hướng dẫn convert. + raise NotImplementedError( + "Pianobook .dspreset không còn được hỗ trợ (pedalboard GPL-3.0 đã gỡ). " + "Chuyển đổi: mở preset trong DecentSampler → save .vstpreset (VST3 state) " + "rồi upload .vstpreset qua web UI — native_bridge đọc được." + ) # ── Preset bridge (Carla ↔ pedalboard) ───────────────────────────────────── diff --git a/engine.spec b/engine.spec index 40bd61b..8fe7b7e 100644 --- a/engine.spec +++ b/engine.spec @@ -5,9 +5,8 @@ import os from PyInstaller.utils.hooks import collect_dynamic_libs, collect_data_files -# Native DLL cho pedalboard va soundfile (Windows .pyd/.dll, Linux .so) -binaries = collect_dynamic_libs('pedalboard') -binaries += collect_dynamic_libs('soundfile') +# Native DLL cho soundfile (Windows .pyd/.dll, Linux .so) +binaries = collect_dynamic_libs('soundfile') # Root tuyet doi cua thu muc chua spec — dung cho cac datas KHONG nam trong # package 'app' (vd thu muc md/). Cac assets cua app (static/templates/models) @@ -89,12 +88,7 @@ _hidden = [ 'uvicorn.protocols.http.auto', 'uvicorn.protocols.websockets', 'uvicorn.protocols.websockets.auto', - 'pedalboard', - # C++ ExternalPlugin.h import pedalboard.midi_utils khi tao VST3Plugin - # (py::module_::import) - PyInstaller khong thay duoc import tu C++, phai - # khai bao hiddenimport thu cong (thieu -> "No module named - # 'pedalboard.midi_utils'" khi /midi-render). - 'pedalboard.midi_utils', + # pedalboard (GPL-3.0) DA XOA — native_bridge thay the (xem PLAN_REPLACE_PEDALBOARD_NATIVE_BRIDGE.md) 'soundfile', 'sf2utils', # pyfluidsynth (fluidsynth.py cffi) - render soundfont native cho diff --git a/native_bridge/CMakeLists.txt b/native_bridge/CMakeLists.txt index 02abd27..9bf136e 100644 --- a/native_bridge/CMakeLists.txt +++ b/native_bridge/CMakeLists.txt @@ -8,11 +8,11 @@ if(MSVC) endif() # ── 1. VST3 SDK (Steinberg, vendored as git submodule — NOT in vcpkg) ── -if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/vst3sdk/CMakeLists.txt") +if(WIN32 AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/vst3sdk/CMakeLists.txt") add_subdirectory(vst3sdk EXCLUDE_FROM_ALL) set(VST3_SDK_TARGET sdk) else() - message(WARNING "vst3sdk submodule missing — VST3 host disabled; SF2/SF3/SFZ still build") + message(STATUS "vst3sdk build skipped (Windows-only) — VST3 host disabled; SF2/SF3/SFZ still build") set(VST3_SDK_TARGET "") endif() @@ -54,6 +54,7 @@ include_directories( ${SFIZZ_INCLUDE_DIR} ) +if(WIN32) add_executable(daw_vst_bridge src/main.cpp src/RenderJob.cpp @@ -64,6 +65,15 @@ add_executable(daw_vst_bridge src/StateStore.cpp src/SandboxVst3Host.cpp ) +else() +# Linux: offline --render only (SF2/SF3 + SFZ; VST3/VST2 hosting is +# Windows-only). main_linux.cpp is the tiny --render entry. +add_executable(daw_vst_bridge + src/main_linux.cpp + src/RenderJob.cpp + src/NativeInstrumentEngine.cpp +) +endif() # VST3 SDK hosting: sdk_hosting lacks the platform module loader — module_win32.cpp # is added to the bridge itself (same approach as the audiohost sample). @@ -86,6 +96,7 @@ else() endif() # ── gui_probe: standalone VST3 GUI attach probe (debug tool, not shipped) ── +if(WIN32) add_executable(gui_probe src/gui_probe.cpp src/NativeInstrumentEngine.cpp @@ -147,3 +158,4 @@ endif() if(WIN32) target_link_libraries(seh_channel_test PRIVATE ${FLUIDSYNTH_LIBRARY} ${SFIZZ_LIBRARY} winmm) endif() +endif() # WIN32-only debug tools (gui_probe, plugin_host, seh_channel_test) diff --git a/native_bridge/src/NativeInstrumentEngine.cpp b/native_bridge/src/NativeInstrumentEngine.cpp index 3116d0b..21fa1ac 100644 --- a/native_bridge/src/NativeInstrumentEngine.cpp +++ b/native_bridge/src/NativeInstrumentEngine.cpp @@ -3,15 +3,19 @@ #include #include +#ifdef _WIN32 #include "Vst3Instrument.h" #include "Vst2Instrument.h" #include "SandboxVst3Host.h" +#endif // void* members keep fluid types out of the public header; cast here. #define FS_SYNTH (static_cast(synth)) #define FS_SETTINGS (static_cast(settings)) +#ifdef _WIN32 #include +#endif #include #include @@ -168,6 +172,7 @@ std::unique_ptr InstrumentEngineManager::create_instrument(In switch (type) { case InstrumentType::SOUNDFONT_SF2_SF3: return std::make_unique(); case InstrumentType::SFZ: return std::make_unique(); +#ifdef _WIN32 case InstrumentType::VST3: // G4.2: SF_SANDBOX_VST3=1 -> host the VST3 in a child // process (plugin_host.exe). Crash kills only the child. @@ -176,6 +181,13 @@ std::unique_ptr InstrumentEngineManager::create_instrument(In return std::make_unique(); case InstrumentType::VST2: return std::make_unique(); +#else + case InstrumentType::VST3: + case InstrumentType::VST2: + // Linux bridge: SF2/SF3 + SFZ only (Docker render). VST3/VST2 + // hosting is Windows-only (COM/HWND/SHM realtime loop). + return nullptr; +#endif default: return nullptr; } } @@ -191,6 +203,7 @@ bool InstrumentEngineManager::assign(uint32_t channel, InstrumentType type, loaded = static_cast(inst.get())->loadSoundFontFile(path, sampleRate); else if (type == InstrumentType::SFZ) loaded = static_cast(inst.get())->loadSfzFile(path, sampleRate); +#ifdef _WIN32 else if (type == InstrumentType::VST3) { if (auto* v = dynamic_cast(inst.get())) loaded = v->loadPlugin(path, sampleRate); @@ -207,6 +220,7 @@ bool InstrumentEngineManager::assign(uint32_t channel, InstrumentType type, if (auto* v = dynamic_cast(inst.get())) v->setChannel(channel); } +#endif if (!loaded) return false; // init() AFTER load: FluidSynth creates its synth inside loadSoundFontFile. if (!inst->init(sampleRate, blockSize)) return false; @@ -415,8 +429,11 @@ static float* alignF(std::vector& v, size_t n) { // (0xC0000005) is caught here — the channel is muted instead of killing the // whole bridge process. /EHa (set in CMakeLists) allows mixing with the // outer C++ try/catch net in main.cpp. +#ifdef _WIN32 static DWORD g_sehCode = 0; static void* g_sehAddr = nullptr; +#endif +#ifdef _WIN32 static bool SafeProcessChannel(INativeInstrument* inst, float* outL, float* outR, uint32_t n, DWORD* outCode) { *outCode = 0; __try { @@ -429,6 +446,18 @@ static bool SafeProcessChannel(INativeInstrument* inst, float* outL, float* outR return false; } } +#else +static bool SafeProcessChannel(INativeInstrument* inst, float* outL, float* outR, uint32_t n, uint32_t* outCode) { + *outCode = 0; + try { + inst->processAudioBlock(outL, outR, n); + return true; + } catch (...) { + *outCode = 1; + return false; + } +} +#endif void InstrumentEngineManager::renderAll(float* outputL, float* outputR, uint32_t numSamples) { if (isBypassed_.load()) { @@ -446,11 +475,12 @@ void InstrumentEngineManager::renderAll(float* outputL, float* outputR, uint32_t float* spR = alignF(scratchR_, numSamples); std::memset(spL, 0, numSamples * sizeof(float)); std::memset(spR, 0, numSamples * sizeof(float)); - DWORD sehCode = 0; + uint32_t sehCode = 0; if (!SafeProcessChannel(inst.get(), spL, spR, numSamples, &sehCode)) { // G3.3: plugin fault — mute this channel only, keep the bridge up. if (!crashedCh_[ch]) { crashedCh_[ch] = true; +#ifdef _WIN32 { HMODULE m = nullptr; char mod[MAX_PATH] = "?"; @@ -477,6 +507,11 @@ void InstrumentEngineManager::renderAll(float* outputL, float* outputR, uint32_t } std::cerr << std::endl; } +#else + std::cerr << "[NativeBridge] G3.3: channel " << ch + << " plugin crashed in processAudioBlock — muted until reload" + << std::endl; +#endif } continue; } diff --git a/native_bridge/src/RenderJob.cpp b/native_bridge/src/RenderJob.cpp index c148a76..a9e5a8a 100644 --- a/native_bridge/src/RenderJob.cpp +++ b/native_bridge/src/RenderJob.cpp @@ -9,8 +9,10 @@ #include "RenderJob.h" #include "NativeInstrumentEngine.h" -#ifdef HAVE_VST3SDK +// sheredom/json.h (public domain, vendored with the VST3 SDK) is header-only +// and needed for SF2/SFZ render even without the VST3 SDK — include it directly. #include "vst3sdk/public.sdk/source/vst/moduleinfo/json.h" +#ifdef HAVE_VST3SDK #include "public.sdk/source/vst/vstpresetfile.h" #include "public.sdk/source/common/memorystream.h" using Steinberg::int32; @@ -37,7 +39,6 @@ using Steinberg::uint32; namespace { -#ifdef HAVE_VST3SDK // --- sheredom/json.h helpers ------------------------------------------------- const json_object_element_s* member(const json_object_s* o, const char* key) { for (const json_object_element_s* e = o ? o->start : nullptr; e; e = e->next) @@ -73,7 +74,6 @@ int64_t memberInt(const json_object_s* o, const char* key, int64_t def) { double d; return memberNumber(o, key, d) ? (int64_t)d : def; } -#endif // HAVE_VST3SDK // --- WAV writer (stdlib only, 16-bit PCM stereo, little-endian) -------------- void writeU16(std::ofstream& f, uint16_t v) { @@ -247,10 +247,6 @@ int run_render_job(const std::string& jobPath, const std::string& outPath) { std::remove(outPath.c_str()); // never leave a partial clip behind }; -#ifndef HAVE_VST3SDK - fail("built without the VST3 SDK submodule — JSON parser unavailable, --render disabled"); - return rc; -#else struct RootHolder { json_value_s* p = nullptr; ~RootHolder() { if (p) std::free(p); } } rootH; // 1. Read + parse the job. @@ -347,12 +343,18 @@ int run_render_job(const std::string& jobPath, const std::string& outPath) { if (type != InstrumentType::VST3) { std::cerr << "[RenderJob] preset ignored for non-VST3 type " << (int)type << " (SF2/SFZ use soundfont_bank/soundfont_program)" << std::endl; +#ifdef HAVE_VST3SDK } else if (!applyVst3Preset(mgr.get(0), preset)) { fail("preset import FAILED for " + pluginPath); return rc; // rc stays 1: job/argument error } else { std::cout << "[RenderJob] preset imported (VST3 .vstpreset) type=" << (int)type << std::endl; } +#else + } else { + std::cerr << "[RenderJob] preset ignored: built without VST3 SDK" << std::endl; + } +#endif } // 6. Offline render loop — same dispatch/render split as the realtime @@ -404,5 +406,4 @@ int run_render_job(const std::string& jobPath, const std::string& outPath) { << " samples, " << (dataBytes / 4) << " frames)" << std::endl; rc = 0; return rc; -#endif // HAVE_VST3SDK } diff --git a/native_bridge/src/main.cpp b/native_bridge/src/main.cpp index 87477e0..da5ac6e 100644 --- a/native_bridge/src/main.cpp +++ b/native_bridge/src/main.cpp @@ -110,7 +110,7 @@ class ChannelWorker; // fwd — WM_DESTROY posts closeGUI() to the channel work static void post_close_gui(uint32_t ch, HWND hwnd); // defined after ChannelWorker static void post_resize_view(uint32_t ch, int w, int h); // defined after ChannelWorker -static InstrumentEngineManager* g_engine = nullptr; +static InstrumentEngineManager* g_engine = nullptr; // declared unconditionally (Linux --render build needs the symbol) static std::mutex g_guiMutex; static std::map g_guiWindows; // channel -> HWND (keep window alive) static std::map g_hwndToCh; // HWND -> channel (WM_DESTROY cleanup) @@ -1010,7 +1010,9 @@ int main(int argc, char* argv[]) { // a JUCE_ window + plugin wndproc (ImmIsUIMessageW -> // SendMessageW WM_IME_SETCONTEXT same hwnd) = infinite // recursion (0xC00000FD, observed inside attachView). +#ifdef _WIN32 if (ok) disable_ime_contexts((HWND)hwnd); +#endif if (ok) ok = inst->attachView(hwnd); } catch (...) { std::cerr << "[NativeBridge] GUI attach EXCEPTION ch=" << guiCh @@ -1033,7 +1035,9 @@ int main(int argc, char* argv[]) { unmute_if_not_closing(y, "openGUI"); if (ok) { +#ifdef _WIN32 disable_ime_contexts((HWND)hwnd); +#endif std::cout << "[NativeBridge] GUI attached hwnd=" << hwnd << " plugin=" << arg2 << " ch=" << guiCh diff --git a/native_bridge/src/main_linux.cpp b/native_bridge/src/main_linux.cpp new file mode 100644 index 0000000..56becd0 --- /dev/null +++ b/native_bridge/src/main_linux.cpp @@ -0,0 +1,22 @@ +// native_bridge/src/main_linux.cpp +// Linux entry point of daw_vst_bridge: offline --render only (Docker). +// The Windows realtime SHM loop / VST3 GUI hosting is Windows-only and is +// not built here. See main.cpp for the full Windows entry. +#include "RenderJob.h" + +#include +#include +#include + +int main(int argc, char* argv[]) { + std::string renderJob, renderOut; + for (int i = 1; i < argc; ++i) { + if (std::strcmp(argv[i], "--render") == 0 && i + 1 < argc) renderJob = argv[i + 1]; + else if (std::strcmp(argv[i], "--out") == 0 && i + 1 < argc) renderOut = argv[i + 1]; + } + if (renderJob.empty() || renderOut.empty()) { + std::cerr << "usage: daw_vst_bridge --render --out " << std::endl; + return 1; + } + return run_render_job(renderJob, renderOut); +}