feat: native host bridge integration — C++ bridge, Rust SHM, JS routing, build scripts, docs
- native_bridge/: InstrumentEngineManager multi-channel, sample-accurate, CC/program/pitchbend, transport, Vst3Instrument stub (HAVE_VST3SDK) - src-tauri: shm.rs, bridge spawn + audio pump + health monitor, open_vst_gui, externalBin, commands - app: UnifiedMidiRouter, NativeBridgeService, bridgeAudioNode, audioRoutingEngine, Plugin Manager UI, Bridge/WASM indicator, set_position sync - build: 3 ps1 (force-added, build/ ignored), verify_bundle --check-bridge, CI workflow - docs: TASKS.md, TEST_NOTES.md (Windows verify checklist), install/report updates
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# build_native_bridge.ps1 - Build daw_vst_bridge.exe (C++ Native Host Bridge)
|
||||
# Run on Windows x64 with VS Build Tools 2022 (C++ workload) installed.
|
||||
# ASCII only (PowerShell 5.1 reads .ps1 without BOM as ANSI).
|
||||
$ErrorActionPreference = "Stop"
|
||||
Set-Location $PSScriptRoot
|
||||
$REPO_ROOT = Resolve-Path (Join-Path $PSScriptRoot "..\..") # ...\SonicForgeStudio
|
||||
$NB_DIR = Join-Path $REPO_ROOT "native_bridge"
|
||||
|
||||
# 1. vcpkg (bootstrap once; skip if already present)
|
||||
$VCPKG_ROOT = Join-Path $env:USERPROFILE "vcpkg"
|
||||
if (-not (Test-Path (Join-Path $VCPKG_ROOT "vcpkg.exe"))) {
|
||||
Write-Host "== [1/6] Bootstrap vcpkg =="
|
||||
git clone https://github.com/microsoft/vcpkg $VCPKG_ROOT
|
||||
& (Join-Path $VCPKG_ROOT "bootstrap-vcpkg.bat") -disableMetrics
|
||||
}
|
||||
|
||||
# 2. vcpkg deps: fluidsynth + sfizz (+ pkgconf so pkg_check_modules may work)
|
||||
Write-Host "== [2/6] vcpkg install fluidsynth sfizz =="
|
||||
& (Join-Path $VCPKG_ROOT "vcpkg.exe") install fluidsynth sfizz --triplet x64-windows
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: vcpkg install failed" -ForegroundColor Red; exit 1 }
|
||||
|
||||
# 3. VST3 SDK submodule (Steinberg; NOT in vcpkg)
|
||||
Write-Host "== [3/6] VST3 SDK (submodule) =="
|
||||
if (-not (Test-Path (Join-Path $NB_DIR "vst3sdk"))) {
|
||||
Push-Location $NB_DIR
|
||||
git submodule add https://github.com/steinbergmedia/vst3sdk.git vst3sdk
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
# 4. Configure with CMake + vcpkg toolchain (MSVC)
|
||||
Write-Host "== [4/6] CMake configure =="
|
||||
$BUILD_DIR = Join-Path $NB_DIR "build"
|
||||
New-Item -ItemType Directory -Force $BUILD_DIR | Out-Null
|
||||
cmake -S $NB_DIR -B $BUILD_DIR `
|
||||
-DCMAKE_TOOLCHAIN_FILE=(Join-Path $VCPKG_ROOT "scripts\buildsystems\vcpkg.cmake") `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: cmake configure failed" -ForegroundColor Red; exit 1 }
|
||||
|
||||
# 5. Build Release
|
||||
Write-Host "== [5/6] CMake build =="
|
||||
cmake --build $BUILD_DIR --config Release
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: cmake build failed" -ForegroundColor Red; exit 1 }
|
||||
|
||||
# 6. Copy sidecar to Tauri externalBin location
|
||||
Write-Host "== [6/6] Copy to src-tauri\binaries =="
|
||||
$BIN_DIR = Join-Path $REPO_ROOT "src-tauri\binaries"
|
||||
New-Item -ItemType Directory -Force $BIN_DIR | Out-Null
|
||||
$EXE = Join-Path $BUILD_DIR "Release\daw_vst_bridge.exe"
|
||||
if (-not (Test-Path $EXE)) { Write-Host "ERROR: $EXE not found" -ForegroundColor Red; exit 1 }
|
||||
Copy-Item $EXE (Join-Path $BIN_DIR "daw_vst_bridge-x86_64-pc-windows-msvc.exe") -Force
|
||||
Write-Host "== DONE: daw_vst_bridge.exe copied to src-tauri\binaries =="
|
||||
@@ -0,0 +1,60 @@
|
||||
# build_windows.ps1 - ONE-COMMAND build: daw_engine.exe + daw_vst_bridge.exe + Tauri (NSIS+MSI)
|
||||
# Run on Windows: powershell -ExecutionPolicy Bypass -File build_windows.ps1
|
||||
# ASCII only (PowerShell 5.1 ANSI limitation).
|
||||
$ErrorActionPreference = "Stop"
|
||||
Set-Location (Join-Path $PSScriptRoot "..\..") # repo root
|
||||
|
||||
Write-Host "== [0/7] Kill old sidecars (daw_engine, daw_vst_bridge) =="
|
||||
Get-Process -Name "daw_engine" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||
Get-Process -Name "daw_vst_bridge" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||
Start-Sleep -Milliseconds 500
|
||||
|
||||
Write-Host "== [1/7] Python dependencies =="
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install -r requirements.txt pyinstaller pywin32
|
||||
|
||||
Write-Host "== [2/7] Frontend bundle (app.jsx -> app.precompiled.js) =="
|
||||
npm install
|
||||
if (-not (Test-Path "node_modules\@babel\standalone")) { npm install @babel/standalone --no-audit --no-fund }
|
||||
if (-not (Test-Path "node_modules\@babel\standalone")) { Write-Host "ERROR: @babel/standalone missing" -ForegroundColor Red; exit 1 }
|
||||
node build.mjs
|
||||
|
||||
Write-Host "== [3/7] Build daw_engine.exe (PyInstaller ONEDIR) =="
|
||||
pyinstaller engine.spec --clean --noconfirm
|
||||
$specContent = Get-Content "engine.spec" -Raw -ErrorAction SilentlyContinue
|
||||
if ($specContent -notmatch "collect_data_files\('app'") { Write-Host "ERROR: engine.spec too old" -ForegroundColor Red; exit 1 }
|
||||
python tools\verify_bundle.py
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: bundle missing assets" -ForegroundColor Red; exit 1 }
|
||||
|
||||
Write-Host "== [4/7] Copy engine onedir -> src-tauri\resources\daw_engine =="
|
||||
if (-not (Test-Path "dist\daw_engine\daw_engine.exe")) { Write-Host "ERROR: onedir engine missing" -ForegroundColor Red; exit 1 }
|
||||
if (Test-Path "src-tauri\resources\daw_engine") { Remove-Item -Recurse -Force "src-tauri\resources\daw_engine" }
|
||||
New-Item -ItemType Directory -Force "src-tauri\resources\daw_engine" | Out-Null
|
||||
Copy-Item "dist\daw_engine\*" "src-tauri\resources\daw_engine\" -Recurse -Force
|
||||
|
||||
Write-Host "== [5/7] Build native bridge (daw_vst_bridge.exe) =="
|
||||
& (Join-Path $PSScriptRoot "build_native_bridge.ps1")
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: native bridge build failed" -ForegroundColor Red; exit 1 }
|
||||
if (-not (Test-Path "src-tauri\binaries\daw_vst_bridge-x86_64-pc-windows-msvc.exe")) {
|
||||
Write-Host "ERROR: bridge sidecar missing" -ForegroundColor Red; exit 1
|
||||
}
|
||||
|
||||
Write-Host "== [6/7] VC++ Redistributable for hooks.nsh =="
|
||||
if (-not (Test-Path src-tauri\vc_redist.x64.exe)) {
|
||||
Invoke-WebRequest -Uri "https://aka.ms/vs/17/release/vc_redist.x64.exe" -OutFile src-tauri\vc_redist.x64.exe
|
||||
}
|
||||
|
||||
Write-Host "== [7/7] Tauri build (NSIS + MSI) =="
|
||||
if (-not (Test-Path "src-tauri\resources\daw_engine\daw_engine.exe")) { Write-Host "ERROR: engine resource missing" -ForegroundColor Red; exit 1 }
|
||||
if (-not (Test-Path "src-tauri\resources\daw_engine\_internal")) { Write-Host "ERROR: engine _internal missing" -ForegroundColor Red; exit 1 }
|
||||
if (-not (Test-Path "src-tauri\binaries\daw_vst_bridge-x86_64-pc-windows-msvc.exe")) { Write-Host "ERROR: bridge externalBin missing" -ForegroundColor Red; exit 1 }
|
||||
python tools\verify_bundle.py --check-bridge
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "ERROR: bridge sidecar missing" -ForegroundColor Red; exit 1 }
|
||||
npm install -D @tauri-apps/cli
|
||||
npx tauri build
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "== DONE =="
|
||||
Write-Host " NSIS: src-tauri\target\release\bundle\nsis\SonicForgeDAW_1.0.0_x64-setup.exe"
|
||||
Write-Host " MSI : src-tauri\target\release\bundle\msi\SonicForgeDAW_1.0.0_x64_en-US.msi"
|
||||
Write-Host " Verify after install: %APPDATA%\SonicForgeDAW\logs\spawn.log exists=True for daw_engine AND daw_vst_bridge"
|
||||
@@ -0,0 +1,81 @@
|
||||
# test_standalone.ps1 - Post-install verification for Native Host Bridge
|
||||
# Run on the Windows machine AFTER installing the NSIS setup (or from dev tree).
|
||||
# ASCII only (PowerShell 5.1 ANSI limitation).
|
||||
$ErrorActionPreference = "Continue"
|
||||
|
||||
$APPDATA = Join-Path $env:APPDATA "SonicForgeDAW"
|
||||
$LOG_DIR = Join-Path $APPDATA "logs"
|
||||
$BRIDGE_LOG = Join-Path $LOG_DIR "bridge.log"
|
||||
|
||||
Write-Host "== [1/8] daw_vst_bridge.exe process =="
|
||||
$proc = Get-Process -Name "daw_vst_bridge" -ErrorAction SilentlyContinue
|
||||
if ($proc) { Write-Host " OK: running (PID $($proc.Id))" } else { Write-Host " FAIL: not running" }
|
||||
|
||||
Write-Host "== [2/8] daw_engine.exe process =="
|
||||
$eng = Get-Process -Name "daw_engine" -ErrorAction SilentlyContinue
|
||||
if ($eng) { Write-Host " OK: running (PID $($eng.Id))" } else { Write-Host " FAIL: not running" }
|
||||
|
||||
Write-Host "== [3/8] spawn.log (engine + bridge lines) =="
|
||||
$spawn = Join-Path $LOG_DIR "spawn.log"
|
||||
if (Test-Path $spawn) {
|
||||
$content = Get-Content $spawn -Raw
|
||||
if ($content -match "daw_vst_bridge.*exists=True") { Write-Host " OK: bridge found in spawn.log" }
|
||||
else { Write-Host " WARN: no daw_vst_bridge line in spawn.log (old build?)" }
|
||||
if ($content -match "daw_engine.*exists=True") { Write-Host " OK: engine found in spawn.log" }
|
||||
else { Write-Host " FAIL: engine not found in spawn.log" }
|
||||
} else { Write-Host " FAIL: spawn.log missing" }
|
||||
|
||||
Write-Host "== [4/8] Shared Memory mapping SonicForge_DAW_IPC =="
|
||||
# Requires admin? Usually not for read. Use a tiny C# helper to OpenFileMapping.
|
||||
$shmCheck = @'
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
public static class ShmCheck {
|
||||
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
|
||||
static extern IntPtr OpenFileMapping(uint dwDesiredAccess, bool bInheritHandle, string lpName);
|
||||
public static int Check() {
|
||||
IntPtr h = OpenFileMapping(0x6, false, "SonicForge_DAW_IPC"); // FILE_MAP_READ|FILE_MAP_WRITE
|
||||
if (h == IntPtr.Zero) return Marshal.GetLastWin32Error();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
'@
|
||||
try {
|
||||
Add-Type -TypeDefinition $shmCheck -Language CSharp -ErrorAction Stop
|
||||
$r = [ShmCheck]::Check()
|
||||
if ($r -eq 0) { Write-Host " OK: SonicForge_DAW_IPC exists" } else { Write-Host " FAIL: shared memory not found (win32 err $r)" }
|
||||
} catch { Write-Host " SKIP: cannot check SHM (compile error: $($_.Exception.Message))" }
|
||||
|
||||
Write-Host "== [5/8] Engine HTTP health =="
|
||||
$health = $null
|
||||
for ($p = 8000; $p -le 8010 -and -not $health; $p++) {
|
||||
try { $health = Invoke-RestMethod -Uri "http://127.0.0.1:$p/health" -TimeoutSec 2 } catch {}
|
||||
}
|
||||
if ($health) { Write-Host " OK: engine health on port $p -> $($health | ConvertTo-Json -Compress)" }
|
||||
else { Write-Host " FAIL: no engine on 8000-8010" }
|
||||
|
||||
Write-Host "== [6/8] Bridge status via engine API =="
|
||||
try {
|
||||
$st = Invoke-RestMethod -Uri "http://127.0.0.1:$p/api/v1/bridge/status" -TimeoutSec 3
|
||||
Write-Host " bridge_status: $($st | ConvertTo-Json -Compress)"
|
||||
} catch { Write-Host " WARN: /api/v1/bridge/status not available yet (endpoint added in Giai doan 6)" }
|
||||
|
||||
Write-Host "== [7/8] Audio smoke test: load SF2 + note -> PCM in SHM =="
|
||||
# Requires a test soundfont; adjust path to a real .sf2 on this machine.
|
||||
$sf = "C:\SonicForgeDAW\soundfonts\SGM-V2.01.sf2"
|
||||
if (Test-Path $sf) {
|
||||
try {
|
||||
$body = @{ path = $sf; type = "SF2" } | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri "http://127.0.0.1:$p/api/v1/bridge/load" -Method Post -Body $body -ContentType "application/json" -TimeoutSec 30 | Out-Null
|
||||
Write-Host " OK: bridge load SF2 requested. Press keys on MIDI keyboard and watch VU meters."
|
||||
} catch { Write-Host " WARN: bridge/load failed ($($_.Exception.Message))" }
|
||||
} else { Write-Host " SKIP: $sf not found - set a real .sf2 path" }
|
||||
|
||||
Write-Host "== [8/8] Manual checklist (spec VII) =="
|
||||
Write-Host " [ ] Load VST3 (Vital.vst3) -> open floating GUI -> tweak knobs"
|
||||
Write-Host " [ ] Load SF2 (SGM-V2.01.sf2) bank 0 prog 0 -> piano"
|
||||
Write-Host " [ ] Load SFZ (SalamanderPiano.sfz) -> samples play"
|
||||
Write-Host " [ ] MIDI keyboard live + Timeline play together -> no choke/dropout"
|
||||
Write-Host " [ ] Track EQ cutoff + Master maximizer affect bridge audio instantly"
|
||||
Write-Host " [ ] Kill daw_vst_bridge.exe -> app falls back to SonicSF WASM (no crash)"
|
||||
Write-Host "== DONE =="
|
||||
Reference in New Issue
Block a user