connect solution into app: VSTi live native (vst3 attach/vst2 bridge) with WASM autosample fallback, VST GUI window (Bug 1), native-first track instrument
This commit is contained in:
@@ -20,9 +20,23 @@ window.TrackInstrument = window.TrackInstrument || {};
|
||||
this.dest = ctx ? (ctx.dest || null) : null;
|
||||
}
|
||||
|
||||
// Native engine sẵn sàng cho track SF? (chỉ khi có sfId — đường native)
|
||||
// Loại engine native cho track: 'sf' (soundfont), 'vst3'/'vst2' (VSTi có
|
||||
// plugin_id) hay null (không native được → fallback WASM/autosample).
|
||||
TrackInstrument.prototype._nativeKind = function () {
|
||||
try {
|
||||
if (this.sfId) return 'sf';
|
||||
if (window.SonicNativeAudio && this.synthEngine && this.synthEngine.plugin_id) {
|
||||
var t = String(this.synthEngine.type || '');
|
||||
if (t.indexOf('vst2') !== -1) return 'vst2';
|
||||
if (t.indexOf('vst3') !== -1 || t.indexOf('vst') !== -1) return 'vst3';
|
||||
}
|
||||
} catch (e) { }
|
||||
return null;
|
||||
};
|
||||
|
||||
// Native engine sẵn sàng cho track? (sfId cho SF, plugin_id cho VSTi)
|
||||
TrackInstrument.prototype._nativeReady = function () {
|
||||
try { return !!(window.SonicNativeAudio && this.sfId); } catch (e) { return false; }
|
||||
return this._nativeKind() !== null;
|
||||
};
|
||||
|
||||
// Đảm bảo channel đã select đúng instrument trước khi play (fire-and-forget:
|
||||
@@ -40,17 +54,48 @@ window.TrackInstrument = window.TrackInstrument || {};
|
||||
}
|
||||
};
|
||||
|
||||
// Phát qua SonicSF WASM (fallback khi native fail). SonicSF tự autosample
|
||||
// VSTi (soundfontPlayer._playNoteFluid) nên không cần ensure riêng ở đây.
|
||||
TrackInstrument.prototype._fallbackPlayNote = function (pitch, velocity, durationMs, startTime) {
|
||||
try {
|
||||
if (!window.SonicSF || !window.SonicSF.playNote) return;
|
||||
this._ensure();
|
||||
var durF = (durationMs != null ? durationMs : 500) || 500;
|
||||
window.SonicSF.playNote(pitch, velocity != null ? velocity : 0.8, durF, startTime, this.program, this.dest, this.ch, this.synthEngine);
|
||||
} catch (e) {
|
||||
console.warn('[TrackInstrument] fallback playNote error:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// velocity: router đã normalize int 1-127 (unifiedMidiRouter.normalizeVelocity).
|
||||
TrackInstrument.prototype.playNote = function (pitch, velocity, durationMs, startTime) {
|
||||
var vel = velocity != null ? velocity : 100;
|
||||
if (this._nativeReady()) {
|
||||
var kind = this._nativeKind();
|
||||
if (kind) {
|
||||
try {
|
||||
var self = this;
|
||||
var dur = (durationMs != null ? durationMs : 500) || 500;
|
||||
// ensure native SF engine cho track (server dedup theo track_id)
|
||||
window.SonicNativeAudio.ensureSf(this.trackId, this.sfId, this.bank, this.prog)
|
||||
.catch(function () {});
|
||||
window.SonicNativeAudio.noteOn('sf', this.trackId, this.ch, pitch, vel);
|
||||
if (kind === 'sf') {
|
||||
// ensure native SF engine cho track (server dedup theo track_id)
|
||||
window.SonicNativeAudio.ensureSf(this.trackId, this.sfId, this.bank, this.prog)
|
||||
.catch(function () {});
|
||||
} else {
|
||||
// VSTi live native (Phase 2): ensure + note-on qua bridge DLL.
|
||||
// Server resolve plugin_id -> path; lỗi -> fallback WASM autosample.
|
||||
if (kind === 'vst3') {
|
||||
window.SonicNativeAudio.ensureVst3(this.trackId, this.synthEngine.plugin_id, this.synthEngine.plugin_path)
|
||||
.catch(function () {});
|
||||
} else {
|
||||
window.SonicNativeAudio.ensureVst2(this.trackId, this.synthEngine.plugin_id, this.synthEngine.plugin_path)
|
||||
.catch(function () {});
|
||||
}
|
||||
}
|
||||
window.SonicNativeAudio.noteOn(kind, this.trackId, this.ch, pitch, vel).catch(function (err) {
|
||||
// Native khong phat duoc (DLL/plugin loi) -> fallback WASM:
|
||||
// SF track di SonicSF, VSTi di autosample (SonicSF tu ensure).
|
||||
console.warn('[TrackInstrument] native noteOn fail -> WASM fallback:', err && err.message);
|
||||
self._fallbackPlayNote(pitch, velocity, dur, startTime);
|
||||
});
|
||||
// ponytail: TrackInstrument không biết audioCtx → bỏ startTime
|
||||
// offset (delay = duration); thêm scheduling chính xác khi cần
|
||||
setTimeout(function () { self.noteOff(pitch); }, dur + 40);
|
||||
@@ -60,20 +105,20 @@ window.TrackInstrument = window.TrackInstrument || {};
|
||||
}
|
||||
}
|
||||
// Fallback SonicSF (WASM)
|
||||
try {
|
||||
if (!window.SonicSF || !window.SonicSF.playNote) return;
|
||||
this._ensure();
|
||||
var durF = (durationMs != null ? durationMs : 500) || 500;
|
||||
window.SonicSF.playNote(pitch, velocity != null ? velocity : 0.8, durF, startTime, this.program, this.dest, this.ch, this.synthEngine);
|
||||
} catch (e) {
|
||||
console.warn('[TrackInstrument] playNote error:', e);
|
||||
}
|
||||
this._fallbackPlayNote(pitch, velocity, durationMs, startTime);
|
||||
};
|
||||
|
||||
TrackInstrument.prototype.noteOff = function (pitch) {
|
||||
if (this._nativeReady()) {
|
||||
try { window.SonicNativeAudio.noteOff('sf', this.trackId, this.ch, pitch); return; } catch (e) {}
|
||||
var kind = this._nativeKind();
|
||||
if (kind) {
|
||||
try {
|
||||
window.SonicNativeAudio.noteOff(kind, this.trackId, this.ch, pitch).catch(function () {
|
||||
// Native note-off fail -> stop qua WASM (neu note da fallback)
|
||||
try { if (window.SonicSF && window.SonicSF.stopNote) window.SonicSF.stopNote(this.ch, pitch); } catch (e) {}
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
// Belt-and-suspenders: stopNote WASM vo hai neu khong co note dang phat.
|
||||
try {
|
||||
if (window.SonicSF && window.SonicSF.stopNote) window.SonicSF.stopNote(this.ch, pitch);
|
||||
} catch (e) {}
|
||||
|
||||
Reference in New Issue
Block a user