FIX: (1) masteringConnected default true + ensureMasteringRouting/refreshCarlaStatus ở mọi đường play — MIDI item/preview qua mastering ngay khi chạy app; (2) _instrumentEpoch chống cache stale + retry bank 0 — hết chọn Synth String nghe Piano; (3) /carla-status + auto-open Carla + route exclusive chỉ khi Carla sống (fallback FluidSynth tránh câm); (4) keybed giữ note theo thời gian bấm (auto-off 5s phòng hờ, mouseup dừng ngay)
This commit is contained in:
@@ -79,6 +79,9 @@ window.API_BASE_URL = window.API_BASE_URL || window.location.origin;
|
||||
// Unload Carla bridge: tắt mọi note đang ngân + terminate tiến trình Carla
|
||||
// do app spawn (gọi khi track chuyển từ VSTi sang instrument soundfont)
|
||||
stopCarla: () => apiRequest('/api/v1/plugins/carla-stop', { method: 'POST', body: JSON.stringify({}) }),
|
||||
// Kiểm tra Carla còn sống không (app-spawn) + cổng OSC — quyết định
|
||||
// route MIDI item exclusive qua Carla hay fallback FluidSynth
|
||||
carlaStatus: () => apiRequest('/api/v1/plugins/carla-status', { method: 'GET' }),
|
||||
// Gửi MIDI note (track ARM → Carla OSC) để preview VSTi realtime
|
||||
carlaMidi: (payload) => apiRequest('/api/v1/plugins/carla-midi', { method: 'POST', body: JSON.stringify(payload) }),
|
||||
// Quick-render preview VSTi (âm thật = âm export, cùng code path)
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
// note khi thả phím (trước đây stopNote không dừng oscillator → âm kêu
|
||||
// liên tục dù đã thả phím khi FluidSynth chưa sẵn sàng / font load fail).
|
||||
let _activeOscillatorsByKey = {};
|
||||
// Epoch chống cache channel STALE: tăng mỗi khi selectInstrument/load
|
||||
// soundfont — channel cache chỉ được tin khi khớp epoch hiện tại, nếu
|
||||
// không → note KẾ TIẾP luôn program_select lại (hết "chọn Synth String
|
||||
// nghe Piano" do cache cũ skip program_select).
|
||||
let _instrumentEpoch = 0;
|
||||
let _gainNode = null;
|
||||
let _pendingOutputDestination = null;
|
||||
let _outputDestination = null; // cache đích route — dedupe swap dư giữa stream
|
||||
@@ -332,6 +337,7 @@
|
||||
_sfHandleMap.set(sfId, cachedOk);
|
||||
_currentSfId = sfId;
|
||||
_loadedFonts[sfId] = true;
|
||||
_instrumentEpoch++;
|
||||
console.log("[SonicSF] SoundFont loaded from cache:", sfId, "handle:", cachedOk);
|
||||
return true;
|
||||
}
|
||||
@@ -367,6 +373,7 @@
|
||||
_sfHandleMap.set(sfId, sfHandle);
|
||||
_currentSfId = sfId;
|
||||
_loadedFonts[sfId] = true;
|
||||
_instrumentEpoch++;
|
||||
console.log("[SonicSF] SoundFont loaded:", sfId, "handle:", sfHandle);
|
||||
return true;
|
||||
} catch (e) {
|
||||
@@ -396,9 +403,17 @@
|
||||
_engineChMap[engKey] = channel;
|
||||
}
|
||||
var sfHandle = _sfHandleMap.get(sfId);
|
||||
// ⚠️ FIX âm sai instrument: tăng epoch → channel cache cũ thành
|
||||
// stale → note kế tiếp LUÔN program_select lại (hết "chọn Synth
|
||||
// String nghe Piano" do cache cũ skip).
|
||||
_instrumentEpoch++;
|
||||
if (sfHandle !== undefined) {
|
||||
try {
|
||||
_fluidModule._fluid_synth_program_select(_synthPtr, channel, sfHandle, bank, program);
|
||||
var _sr = _fluidModule._fluid_synth_program_select(_synthPtr, channel, sfHandle, bank, program);
|
||||
if (_sr !== 0) {
|
||||
// Preset không tồn tại ở bank/program này → thử bank 0
|
||||
try { _fluidModule._fluid_synth_program_select(_synthPtr, channel, sfHandle, 0, program); } catch (e2) {}
|
||||
}
|
||||
} catch (e) {}
|
||||
} else {
|
||||
try { _fluidModule._fluid_synth_bank_select(_synthPtr, channel, bank); } catch (e) {}
|
||||
@@ -408,6 +423,7 @@
|
||||
_channels[channel].program = program;
|
||||
_channels[channel].isPercussion = (bank === 128);
|
||||
_channels[channel].sfId = sfId;
|
||||
_channels[channel]._epoch = _instrumentEpoch;
|
||||
},
|
||||
|
||||
controllerChange: function (channel, controller, value) {
|
||||
@@ -632,6 +648,7 @@
|
||||
// ⚠️ Chỉ skip khi handle SF vẫn CÒN HỢP LỆ trong map — nếu
|
||||
// không → vẫn program_select lại (tránh dùng handle đã unload).
|
||||
var progAlreadySet = cachedCh && cachedCh.program === finalProg && cachedCh.bank === finalBank && cachedCh.sfId === finalSfId
|
||||
&& cachedCh._epoch === _instrumentEpoch
|
||||
&& (finalSfId ? _sfHandleMap.has(finalSfId) : true);
|
||||
if ((synthEngine || program !== undefined) && !progAlreadySet) {
|
||||
var sfHandle = finalSfId ? _sfHandleMap.get(finalSfId) : undefined;
|
||||
@@ -666,6 +683,11 @@
|
||||
}
|
||||
}
|
||||
var _selRet = _fluidModule._fluid_synth_program_select(_synthPtr, ch, sfHandle, finalBank, finalProg);
|
||||
if (_selRet !== 0 && finalBank !== 0) {
|
||||
// Preset không tồn tại ở bank này → thử bank 0
|
||||
// (hết "noteon preset rỗng = âm sai/không đúng")
|
||||
try { _fluidModule._fluid_synth_program_select(_synthPtr, ch, sfHandle, 0, finalProg); } catch (e2) {}
|
||||
}
|
||||
} catch (e) {}
|
||||
} else {
|
||||
try { _fluidModule._fluid_synth_bank_select(_synthPtr, ch, finalBank); } catch (e) {}
|
||||
@@ -675,6 +697,7 @@
|
||||
_channels[ch].bank = finalBank;
|
||||
_channels[ch].program = finalProg;
|
||||
_channels[ch].sfId = finalSfId;
|
||||
_channels[ch]._epoch = _instrumentEpoch;
|
||||
}
|
||||
console.log('[SonicSF] noteon channel:', ch, 'pitch:', midiPitch, 'vel:', midiVel, 'sfId:', finalSfId);
|
||||
_fluidModule._fluid_synth_noteon(_synthPtr, ch, midiPitch, midiVel);
|
||||
|
||||
Reference in New Issue
Block a user