T6: scheduler items qua router - note-on/off TIMELINE_SCHEDULER (startTime audio clock support trong router+TrackInstrument); 8 site scheduler (timeline/section/local/piano-roll/ghost) route UnifiedMidiRouter, fallback SonicSF, tracker doi xung qua note-off setTimeout
This commit is contained in:
+97
-74
@@ -226,7 +226,7 @@ const _makeCtx = (ch, program, synthEngine, dest) => ({
|
||||
});
|
||||
// Note-on qua router. Trả true nếu router xử lý, false nếu caller phải fallback
|
||||
// SonicSF trực tiếp. ctx: TrackInstrumentCtx (ch/program/synthEngine/dest).
|
||||
const _routeNoteOn = (trackId, ctx, pitch, velocity, durationMs, sourceType) => {
|
||||
const _routeNoteOn = (trackId, ctx, pitch, velocity, durationMs, sourceType, startTime) => {
|
||||
if (trackId === undefined || trackId === null || !ctx) return false;
|
||||
const router = _getMidiRouter();
|
||||
if (!router) return false;
|
||||
@@ -240,6 +240,7 @@ const _routeNoteOn = (trackId, ctx, pitch, velocity, durationMs, sourceType) =>
|
||||
pitch,
|
||||
velocity: velocity != null ? velocity : 0.8,
|
||||
durationMs,
|
||||
startTime,
|
||||
source: sourceType || 'PREVIEW'
|
||||
});
|
||||
return true;
|
||||
@@ -262,10 +263,16 @@ const _routeNoteOff = (trackId, ctx, pitch, sourceType) => {
|
||||
};
|
||||
// Fire-and-forget: note-on + note-off sau durationMs+60 (giữ tracker đối xứng
|
||||
// cho click/draw/playing-tick không có note-off riêng).
|
||||
const _routePreviewNote = (trackId, ctx, pitch, velocity, durationMs, sourceType) => {
|
||||
if (!_routeNoteOn(trackId, ctx, pitch, velocity, durationMs, sourceType)) return false;
|
||||
const _routePreviewNote = (trackId, ctx, pitch, velocity, durationMs, sourceType, startTime) => {
|
||||
if (!_routeNoteOn(trackId, ctx, pitch, velocity, durationMs, sourceType, startTime)) return false;
|
||||
const dur = Math.max(50, durationMs || 500);
|
||||
setTimeout(() => { _routeNoteOff(trackId, ctx, pitch, sourceType); }, dur + 60);
|
||||
let delayMs = dur + 60;
|
||||
// Scheduler (TIMELINE_SCHEDULER) schedule theo audio clock → note-off đợi
|
||||
// sau khi note bắt đầu + duration (giữ tracker đối xứng cho scheduled note).
|
||||
if (startTime) {
|
||||
try { delayMs = Math.max(0, (startTime - getAudioContext().currentTime) * 1000) + dur + 60; } catch (e) {}
|
||||
}
|
||||
setTimeout(() => { _routeNoteOff(trackId, ctx, pitch, sourceType); }, delayMs);
|
||||
return true;
|
||||
};
|
||||
const _stopMidiFilePreview = () => {
|
||||
@@ -21803,17 +21810,19 @@ const App = () => {
|
||||
if (offsetTime < noteStartSec) {
|
||||
const delay = noteStartSec - offsetTime;
|
||||
const startTime = context.currentTime + delay;
|
||||
if (!routeToCarla && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
durationMs,
|
||||
startTime,
|
||||
program,
|
||||
gainNode,
|
||||
trkCh,
|
||||
track.synth_engine
|
||||
);
|
||||
if (!routeToCarla) {
|
||||
if (!_routePreviewNote(track.id, _makeCtx(trkCh, program, track.synth_engine, gainNode), note.pitch || 60, note.velocity || 0.8, durationMs, 'TIMELINE_SCHEDULER', startTime) && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
durationMs,
|
||||
startTime,
|
||||
program,
|
||||
gainNode,
|
||||
trkCh,
|
||||
track.synth_engine
|
||||
);
|
||||
}
|
||||
}
|
||||
// MIDI items → Carla bridge (track VSTi + Carla local): phát VSTi
|
||||
// realtime — yêu cầu: MIDI item PHẢI play qua Carla khi VSTi loaded.
|
||||
@@ -21831,17 +21840,19 @@ const App = () => {
|
||||
} else {
|
||||
const playOffset = offsetTime - noteStartSec;
|
||||
const remainingDurMs = (noteEndSec - offsetTime) * 1000;
|
||||
if (!routeToCarla && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
remainingDurMs,
|
||||
context.currentTime,
|
||||
program,
|
||||
gainNode,
|
||||
trkCh,
|
||||
track.synth_engine
|
||||
);
|
||||
if (!routeToCarla) {
|
||||
if (!_routePreviewNote(track.id, _makeCtx(trkCh, program, track.synth_engine, gainNode), note.pitch || 60, note.velocity || 0.8, remainingDurMs, 'TIMELINE_SCHEDULER', context.currentTime) && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
remainingDurMs,
|
||||
context.currentTime,
|
||||
program,
|
||||
gainNode,
|
||||
trkCh,
|
||||
track.synth_engine
|
||||
);
|
||||
}
|
||||
}
|
||||
// MIDI items → Carla bridge (track VSTi + Carla local)
|
||||
if (routeToCarla) scheduleCarlaNote(track.synth_engine, trkCh, note.pitch || 60, note.velocity || 0.8, context.currentTime, remainingDurMs);
|
||||
@@ -21960,17 +21971,19 @@ const App = () => {
|
||||
const delay = noteStartMain - offsetTime;
|
||||
const startTime = context.currentTime + delay;
|
||||
const playDurMs = (notePlayEndMain - noteStartMain) * 1000;
|
||||
if (!subRouteCarla && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
playDurMs,
|
||||
startTime,
|
||||
program,
|
||||
subNode,
|
||||
subCh,
|
||||
subTrack.synth_engine
|
||||
);
|
||||
if (!subRouteCarla) {
|
||||
if (!_routePreviewNote(subTrack.id, _makeCtx(subCh, program, subTrack.synth_engine, subNode), note.pitch || 60, note.velocity || 0.8, playDurMs, 'TIMELINE_SCHEDULER', startTime) && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
playDurMs,
|
||||
startTime,
|
||||
program,
|
||||
subNode,
|
||||
subCh,
|
||||
subTrack.synth_engine
|
||||
);
|
||||
}
|
||||
}
|
||||
if (subRouteCarla) scheduleCarlaNote(subTrack.synth_engine, subCh, note.pitch || 60, note.velocity || 0.8, startTime, playDurMs);
|
||||
// Trigger VU meter flash đúng nhịp (cả subTrack — VU
|
||||
@@ -21990,17 +22003,19 @@ const App = () => {
|
||||
}, delay * 1000);
|
||||
} else {
|
||||
const remainingDurMs = (notePlayEndMain - offsetTime) * 1000;
|
||||
if (!subRouteCarla && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
remainingDurMs,
|
||||
context.currentTime,
|
||||
program,
|
||||
subNode,
|
||||
subCh,
|
||||
subTrack.synth_engine
|
||||
);
|
||||
if (!subRouteCarla) {
|
||||
if (!_routePreviewNote(subTrack.id, _makeCtx(subCh, program, subTrack.synth_engine, subNode), note.pitch || 60, note.velocity || 0.8, remainingDurMs, 'TIMELINE_SCHEDULER', context.currentTime) && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
remainingDurMs,
|
||||
context.currentTime,
|
||||
program,
|
||||
subNode,
|
||||
subCh,
|
||||
subTrack.synth_engine
|
||||
);
|
||||
}
|
||||
}
|
||||
if (subRouteCarla) scheduleCarlaNote(subTrack.synth_engine, subCh, note.pitch || 60, note.velocity || 0.8, context.currentTime, remainingDurMs);
|
||||
if (window.triggerMidiVuActivity) {
|
||||
@@ -22089,33 +22104,37 @@ const App = () => {
|
||||
if (offsetTime < noteStartSec) {
|
||||
const delay = noteStartSec - offsetTime;
|
||||
const startTime = context.currentTime + delay;
|
||||
if (!routeToCarla && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
durationMs,
|
||||
startTime,
|
||||
program,
|
||||
gainNode,
|
||||
lcCh,
|
||||
track.synth_engine
|
||||
);
|
||||
if (!routeToCarla) {
|
||||
if (!_routePreviewNote(track.id, _makeCtx(lcCh, program, track.synth_engine, gainNode), note.pitch || 60, note.velocity || 0.8, durationMs, 'TIMELINE_SCHEDULER', startTime) && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
durationMs,
|
||||
startTime,
|
||||
program,
|
||||
gainNode,
|
||||
lcCh,
|
||||
track.synth_engine
|
||||
);
|
||||
}
|
||||
}
|
||||
// MIDI items → Carla bridge (track VSTi + Carla local)
|
||||
if (routeToCarla) scheduleCarlaNote(track.synth_engine, lcCh, note.pitch || 60, note.velocity || 0.8, startTime, durationMs);
|
||||
} else {
|
||||
const remainingDurMs = (noteEndSec - offsetTime) * 1000;
|
||||
if (!routeToCarla && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
remainingDurMs,
|
||||
context.currentTime,
|
||||
program,
|
||||
gainNode,
|
||||
lcCh,
|
||||
track.synth_engine
|
||||
);
|
||||
if (!routeToCarla) {
|
||||
if (!_routePreviewNote(track.id, _makeCtx(lcCh, program, track.synth_engine, gainNode), note.pitch || 60, note.velocity || 0.8, remainingDurMs, 'TIMELINE_SCHEDULER', context.currentTime) && window.SonicSF) {
|
||||
window.SonicSF.playNote(
|
||||
note.pitch || 60,
|
||||
note.velocity || 0.8,
|
||||
remainingDurMs,
|
||||
context.currentTime,
|
||||
program,
|
||||
gainNode,
|
||||
lcCh,
|
||||
track.synth_engine
|
||||
);
|
||||
}
|
||||
}
|
||||
// MIDI items → Carla bridge (track VSTi + Carla local)
|
||||
if (routeToCarla) scheduleCarlaNote(track.synth_engine, lcCh, note.pitch || 60, note.velocity || 0.8, context.currentTime, remainingDurMs);
|
||||
@@ -22171,8 +22190,10 @@ const App = () => {
|
||||
// Track VSTi + Carla local → note PHẢI play qua Carla bridge (chỉ route
|
||||
// Carla — không play FluidSynth GM sai âm chồng lên)
|
||||
const routeToCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(synthEngine));
|
||||
if (!routeToCarla && window.SonicSF) {
|
||||
window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, scheduledTime, instrumentProgram, destNode, mainCh, synthEngine);
|
||||
if (!routeToCarla) {
|
||||
if (!_routePreviewNote(track.id, _makeCtx(mainCh, instrumentProgram, synthEngine, destNode), note.pitch || 60, note.velocity || 0.8, durMs, 'TIMELINE_SCHEDULER', scheduledTime) && window.SonicSF) {
|
||||
window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, scheduledTime, instrumentProgram, destNode, mainCh, synthEngine);
|
||||
}
|
||||
}
|
||||
// MIDI items → Carla (track VSTi + Carla local): phát VSTi realtime
|
||||
// (schedule theo audio clock bằng setTimeout — preview, timing gần đúng).
|
||||
@@ -22207,8 +22228,10 @@ const App = () => {
|
||||
var durMs = effDur * 1000;
|
||||
// Ghost note → Carla bridge khi ghost track VSTi (chỉ route Carla)
|
||||
var gRouteCarla = !!(window.SonicCarlaMidi && window.SonicCarlaMidi.shouldRoutePlayback(ghostSynth));
|
||||
if (!gRouteCarla && window.SonicSF) {
|
||||
window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, schedTime, ghostProg, ghostDest, ghostCh, ghostSynth);
|
||||
if (!gRouteCarla) {
|
||||
if (!_routePreviewNote(ghostTrack && ghostTrack.id, _makeCtx(ghostCh, ghostProg, ghostSynth, ghostDest), note.pitch || 60, note.velocity || 0.8, durMs, 'TIMELINE_SCHEDULER', schedTime) && window.SonicSF) {
|
||||
window.SonicSF.playNote(note.pitch || 60, note.velocity || 0.8, durMs, schedTime, ghostProg, ghostDest, ghostCh, ghostSynth);
|
||||
}
|
||||
}
|
||||
if (gRouteCarla) scheduleCarlaNote(ghostSynth, ghostCh, note.pitch || 60, note.velocity || 0.8, schedTime, durMs);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user