T3: Autosample UX - fail reason + toast + pre-warm feedback
vstiAutosample: fail map thanh object {ts, reason} (cooldown check
object-aware), recordFail o API-missing/response loi/catch, export
failReasonFor. soundfontPlayer: toast ly do khi ensure null (throttle
10s) roi fallback oscillator. app.jsx: pre-warm VSTi hien thi toast khi
autosample fail.
This commit is contained in:
@@ -15496,7 +15496,13 @@ const App = () => {
|
||||
// VSTi live playback: pre-warm autosample (SF2 backend) — note đầu không bị
|
||||
// delay (SonicSF cũng ensure lazily trong _playNoteFluid nếu chưa có).
|
||||
if (window.SonicVstiAutosample && instrumentId && !isSfInstrument) {
|
||||
window.SonicVstiAutosample.ensure({ plugin_id: instrumentId, type: 'vst3' });
|
||||
const _warmEngine = { plugin_id: instrumentId, type: 'vst3' };
|
||||
window.SonicVstiAutosample.ensure(_warmEngine).then(_sfId => {
|
||||
if (!_sfId && window.SonicVstiAutosample.failReasonFor) {
|
||||
const _reason = window.SonicVstiAutosample.failReasonFor(_warmEngine);
|
||||
if (_reason && window.showToast) window.showToast('Autosample VSTi that bai: ' + _reason + ' - dung am default', 'warning');
|
||||
}
|
||||
});
|
||||
}
|
||||
// Trigger FluidSynth load + program change when soundfont instrument selected
|
||||
if (window.SonicSF && window.SonicSF.selectInstrument && instrumentId && isSfInstrument) {
|
||||
|
||||
@@ -786,7 +786,7 @@ const[instrumentDropdownTrackId,setInstrumentDropdownTrackId]=useState(null);con
|
||||
// play qua Carla bridge (âm sai instrument + âm kẹt không dừng được).
|
||||
try{const _hasInst=!!instrumentId;const _wasVst=curTrk&&curTrk.synth_engine&&String(curTrk.synth_engine.type||'').indexOf('vst')!==-1;const _nowVst=_hasInst&&!isSfInstrument;if(_wasVst&&!_nowVst&&window.SonicCarlaMidi&&window.SonicCarlaMidi.stopBridge){window.SonicCarlaMidi.stopBridge();try{if(window.SonicSF&&window.SonicSF.stopAll)window.SonicSF.stopAll();}catch(e){}stopAllNativeSfNotes();console.log('[Instrument] Carla bridge unloaded — track',trackId,'switched from VSTi to non-VST');}}catch(e){console.warn('[Instrument] carla-stop on instrument switch error:',e);}var mch=curTrk?assignTrackMidiChannel(curTrk,mt):sfBank===128?9:0;updateActiveTracks(prev=>prev.map(t=>{if(t.id!==trackId)return t;const hasInstrument=!!instrumentId;const instrType=isSfInstrument?'soundfont':hasInstrument?'vst3':'default';const synthEngine=hasInstrument?{type:instrType,plugin_id:instrumentId,soundfont_bank:sfBank!==undefined?sfBank:0,soundfont_program:sfProg!==undefined?sfProg:0,soundfont_id:isSfInstrument?instrumentId.replace('sf_',''):''}:undefined;return{...t,midiChannel:mch,instrumentId,instrumentProgram:sfProg,instrumentName:displayName,soundfont_bank:sfBank,soundfont_program:sfProg,synth_engine:synthEngine,type:hasInstrument?'MIDI':t.type==='MIDI'?'audio':t.type};}));setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);setSynthCategory(null);// VSTi live playback: pre-warm autosample (SF2 backend) — note đầu không bị
|
||||
// delay (SonicSF cũng ensure lazily trong _playNoteFluid nếu chưa có).
|
||||
if(window.SonicVstiAutosample&&instrumentId&&!isSfInstrument){window.SonicVstiAutosample.ensure({plugin_id:instrumentId,type:'vst3'});}// Trigger FluidSynth load + program change when soundfont instrument selected
|
||||
if(window.SonicVstiAutosample&&instrumentId&&!isSfInstrument){const _warmEngine={plugin_id:instrumentId,type:'vst3'};window.SonicVstiAutosample.ensure(_warmEngine).then(_sfId=>{if(!_sfId&&window.SonicVstiAutosample.failReasonFor){const _reason=window.SonicVstiAutosample.failReasonFor(_warmEngine);if(_reason&&window.showToast)window.showToast('Autosample VSTi that bai: '+_reason+' - dung am default','warning');}});}// Trigger FluidSynth load + program change when soundfont instrument selected
|
||||
if(window.SonicSF&&window.SonicSF.selectInstrument&&instrumentId&&isSfInstrument){const sfId=instrumentId.replace('sf_','');window.SonicSF.selectInstrument(mch,sfBank||0,sfProg||0,sfId);}setSubTabs(prev=>prev.map(s=>{if(s.trackId!==trackId)return s;return{...s,instrumentProgram:programNumber!==undefined?programNumber:undefined,instrumentName:displayName,instrumentId};}));const playingSub=subTabs.find(s=>s.trackId===trackId&&s.type==='PIANO_ROLL'&&s.isPlaying);if(playingSub){const pid=playingSub.id;const ctx=getAudioContext();const tNode=activeTrackNodesRef.current[trackId];if(tNode&&tNode.gainNode){tNode.gainNode.gain.setValueAtTime(tNode.gainNode.gain.value||1,ctx.currentTime);tNode.gainNode.gain.linearRampToValueAtTime(0.001,ctx.currentTime+0.04);}setTimeout(()=>{stopAllPlayback();if(tNode&&tNode.gainNode){const trackData=activeTracksRef.current?activeTracksRef.current.find(t=>t.id===trackId):null;const volDb=trackData?trackData.volumeDb??0:0;const volLinear=volDb<=-50?0:Math.pow(10,volDb/20);tNode.gainNode.gain.setValueAtTime(0.001,ctx.currentTime);tNode.gainNode.gain.linearRampToValueAtTime(volLinear||0.8,ctx.currentTime+0.015);}const context=getAudioContext();const offset=playingSub.currentTime||0;startOffsetTimeRef.current=offset;startAudioTimeRef.current=context.currentTime;startBufferOffsetRef.current=offset*(playingSub.speed||1.0);schedulePianoRollMidi(playingSub,offset);startSubTabPlayback(playingSub,offset);setSubTabs(prev=>prev.map(s=>s.id===pid?{...s,isPlaying:true}:s));if(subTabsRef.current){subTabsRef.current=subTabsRef.current.map(s=>s.id===pid?{...s,isPlaying:true}:s);}animationFrameIdRef.current=requestAnimationFrame(updatePlayhead);},60);}setTimeout(()=>lucide.createIcons(),50);};const setTrackInstrument=(trackId,instrumentId,displayName)=>{setInstrumentDropdownTrackId(null);setInstrumentDropdownBtnRect(null);if(instrumentId&&instrumentId.startsWith('sf_')){// Set instrument on track immediately so Synth button shows the name
|
||||
var mt2=activeTracksRef.current||tracks;var qTrk=null;for(var qi=0;qi<mt2.length;qi++){if(mt2[qi].id===trackId){qTrk=mt2[qi];break;}}var qch=qTrk?assignTrackMidiChannel(qTrk,mt2):0;updateActiveTracks(prev=>prev.map(t=>{if(t.id!==trackId)return t;const sfClean=instrumentId.replace('sf_','');const synthEngine={type:'soundfont',plugin_id:instrumentId,soundfont_bank:0,soundfont_program:0,soundfont_id:sfClean};return{...t,midiChannel:qch,instrumentId,instrumentProgram:undefined,instrumentName:displayName,synth_engine:synthEngine};}));setSelectedSoundFontId(instrumentId);setSynthCategory('soundfont');setInstrumentSelectorTrackId(trackId);setSfPresets(null);setSfPresetSearchQuery('');const sfIdParam=instrumentId.replace('sf_','');// Use cached presets from instrumentSelectorData
|
||||
const cachedSf=(instrumentSelectorData?.soundfonts||[]).find(s=>s.id===instrumentId||s.id===sfIdParam);if(cachedSf&&cachedSf.presets){setSfPresets(cachedSf.presets);}else{window.SonicAPI.listSoundfontInstruments(sfIdParam).then(data=>setSfPresets(data.presets||[])).catch(e=>{console.error('listSoundfontInstruments failed:',e);setSfPresets([]);});}}else{setTrackInstrumentWithUndo(trackId,instrumentId,displayName);}};const[activeTool,setActiveTool]=useState('select');// 'select' | 'grab' | 'razor'
|
||||
|
||||
@@ -609,7 +609,14 @@
|
||||
if (!sfId) {
|
||||
// Không autosample được (404/501) → fallback oscillator để note
|
||||
// KHÔNG câm; cooldown trong ensure chặn re-request.
|
||||
try { self._playNoteFallback(note, velocity, durationMs, startTime, program, null, channel, synthEngine); } catch (e) {}
|
||||
try {
|
||||
var _reason = window.SonicVstiAutosample.failReasonFor ? window.SonicVstiAutosample.failReasonFor(synthEngine) : null;
|
||||
if (_reason && window.showToast && (!self._vstiToastAt || Date.now() - self._vstiToastAt > 10000)) {
|
||||
self._vstiToastAt = Date.now();
|
||||
window.showToast('Autosample VSTi that bai: ' + _reason + ' - dung am default', 'warning');
|
||||
}
|
||||
self._playNoteFallback(note, velocity, durationMs, startTime, program, null, channel, synthEngine);
|
||||
} catch (e) {}
|
||||
return;
|
||||
}
|
||||
synthEngine.autosampled_sf_id = sfId;
|
||||
|
||||
@@ -19,6 +19,13 @@ window.SonicVstiAutosample = window.SonicVstiAutosample || {};
|
||||
function readFails() {
|
||||
try { return JSON.parse(localStorage.getItem(LS_FAIL_KEY) || '{}') || {}; } catch (e) { return {}; }
|
||||
}
|
||||
function recordFail(k, reason) {
|
||||
try {
|
||||
var fails2 = readFails();
|
||||
fails2[k] = { ts: Date.now(), reason: reason || 'Autosample that bai' };
|
||||
localStorage.setItem(LS_FAIL_KEY, JSON.stringify(fails2));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// Key = plugin + preset → autosample lại khi đổi preset (âm preset mới).
|
||||
function keyFor(synthEngine) {
|
||||
@@ -51,10 +58,13 @@ window.SonicVstiAutosample = window.SonicVstiAutosample || {};
|
||||
if (cached) return Promise.resolve(cached);
|
||||
if (_inFlight[k]) return _inFlight[k];
|
||||
var fails = readFails();
|
||||
if (fails[k] && (Date.now() - fails[k]) < FAIL_COOLDOWN_MS) {
|
||||
var _f = fails[k];
|
||||
var _lastTs = (_f && typeof _f === 'object') ? _f.ts : _f;
|
||||
if (_lastTs && (Date.now() - _lastTs) < FAIL_COOLDOWN_MS) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
if (!window.SonicAPI || !window.SonicAPI.autosampleVsti) {
|
||||
recordFail(k, 'API autosample khong kha dung');
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
_inFlight[k] = new Promise(function (resolve) {
|
||||
@@ -72,19 +82,29 @@ window.SonicVstiAutosample = window.SonicVstiAutosample || {};
|
||||
writeMap(map);
|
||||
resolve(res.sf_id);
|
||||
} else {
|
||||
recordFail(k, (res && res.error) ? String(res.error) : 'Server khong tra sf_id');
|
||||
resolve(null);
|
||||
}
|
||||
}).catch(function () {
|
||||
}).catch(function (err) {
|
||||
delete _inFlight[k];
|
||||
var fails2 = readFails();
|
||||
fails2[k] = Date.now();
|
||||
try { localStorage.setItem(LS_FAIL_KEY, JSON.stringify(fails2)); } catch (e) {}
|
||||
recordFail(k, (err && err.message) ? String(err.message) : 'Loi mang / server autosample');
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
return _inFlight[k];
|
||||
}
|
||||
|
||||
// Lý do autosample thất bại gần nhất (fail map) — null nếu chưa từng fail
|
||||
// (hoặc entry cũ định dạng number). Dùng cho UI toast khi ensure() trả null.
|
||||
function failReasonFor(synthEngine) {
|
||||
var k = keyFor(synthEngine);
|
||||
if (!k) return null;
|
||||
var fails = readFails();
|
||||
var f = fails[k];
|
||||
if (!f || typeof f !== 'object') return null;
|
||||
return f.reason || null;
|
||||
}
|
||||
|
||||
// Xoá cache + cooldown — cho phép re-sample (đổi preset/plugin hoặc debug).
|
||||
function clearCache() {
|
||||
try { localStorage.removeItem(LS_MAP_KEY); } catch (e) {}
|
||||
@@ -96,6 +116,7 @@ window.SonicVstiAutosample = window.SonicVstiAutosample || {};
|
||||
sfIdFor: sfIdFor,
|
||||
entryFor: entryFor,
|
||||
ensure: ensure,
|
||||
failReasonFor: failReasonFor,
|
||||
clearCache: clearCache
|
||||
};
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user