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,72 @@
|
||||
// app/static/js/services/bridgeAudioNode.js
|
||||
// Audio sink: consumes 'bridge-audio' PCM frames (native bridge) and plays them
|
||||
// through a ScriptProcessorNode into the WebAudio graph (track FX / master bus).
|
||||
(function () {
|
||||
var RING_DEPTH = 8; // 8 blocks * 256 samples ~= 46ms anti-underrun
|
||||
var SP_BUFFER = 4096; // ScriptProcessor chunk (16 bridge blocks)
|
||||
var _queue = [];
|
||||
var _spn = null;
|
||||
var _gainNode = null;
|
||||
var _ctx = null;
|
||||
var _initialized = false;
|
||||
|
||||
function _getCtx() {
|
||||
if (_ctx) return _ctx;
|
||||
if (typeof getAudioContext === 'function') _ctx = getAudioContext();
|
||||
else if (window.__sharedAudioCtx) _ctx = window.__sharedAudioCtx;
|
||||
else _ctx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
return _ctx;
|
||||
}
|
||||
|
||||
function init(audioCtx) {
|
||||
if (_initialized) return;
|
||||
_ctx = audioCtx || _getCtx();
|
||||
_gainNode = _ctx.createGain();
|
||||
_gainNode.gain.value = 1.0;
|
||||
_spn = _ctx.createScriptProcessor(SP_BUFFER, 0, 2);
|
||||
_spn.onaudioprocess = function (e) {
|
||||
var L = e.outputBuffer.getChannelData(0);
|
||||
var R = e.outputBuffer.getChannelData(1);
|
||||
L.fill(0); R.fill(0);
|
||||
if (!_queue.length) return;
|
||||
var f = _queue.shift();
|
||||
L.set(f.l); R.set(f.r);
|
||||
};
|
||||
_spn.connect(_gainNode);
|
||||
_initialized = true;
|
||||
console.log('[BridgeAudioNode] initialized (ring depth ' + RING_DEPTH + ')');
|
||||
}
|
||||
|
||||
function onAudio(l, r) {
|
||||
if (!_initialized) init();
|
||||
_queue.push({ l: new Float32Array(l), r: new Float32Array(r) });
|
||||
if (_queue.length > RING_DEPTH) _queue.shift(); // drop oldest on overrun
|
||||
}
|
||||
|
||||
function flush() { _queue = []; }
|
||||
|
||||
function getOutputNode() { return _gainNode; }
|
||||
|
||||
function disconnect() {
|
||||
flush();
|
||||
if (_spn && _gainNode) {
|
||||
try { _spn.disconnect(); } catch (e) {}
|
||||
try { _gainNode.disconnect(); } catch (e) {}
|
||||
}
|
||||
// Reset state so a later init() can rebuild (AudioRoutingEngine re-connect).
|
||||
_initialized = false;
|
||||
_spn = null;
|
||||
_gainNode = null;
|
||||
_ctx = null;
|
||||
}
|
||||
|
||||
window.BridgeAudioNode = {
|
||||
init: init,
|
||||
onAudio: onAudio,
|
||||
flush: flush,
|
||||
getOutputNode: getOutputNode,
|
||||
connect: function (dest) { if (_gainNode) _gainNode.connect(dest); },
|
||||
disconnect: disconnect,
|
||||
isReady: function () { return _initialized; }
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user