T16: autosample VST2 via native bridge + SF2/SF3 export hoàn chỉnh, UI chip size/note_count, re-sample on preset change, tests
This commit is contained in:
+21
-2
@@ -7823,6 +7823,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
|
||||
React.useEffect(() => {
|
||||
try { localStorage.setItem('sf_pr_zoom', String(rollZoom)); } catch (e) { }
|
||||
}, [rollZoom]);
|
||||
// Autosample VSTi xong → re-render chip size/note_count (subtab header)
|
||||
React.useEffect(function () {
|
||||
const _h = () => setRenderTick(t => t + 1);
|
||||
window.addEventListener('sf:autosample-update', _h);
|
||||
return () => window.removeEventListener('sf:autosample-update', _h);
|
||||
}, []);
|
||||
const [aiBarStart, setAiBarStart] = React.useState(0);
|
||||
const [aiBarEnd, setAiBarEnd] = React.useState(4);
|
||||
|
||||
@@ -9915,7 +9921,10 @@ const beatSec = 60.0 / (parseInt(bpm) || 120);
|
||||
onClick: () => onInstrumentSelect && onInstrumentSelect(st.trackId),
|
||||
title: st.instrumentName || "Synth",
|
||||
className: `px-1.5 py-0.5 text-[10px] rounded font-mono font-bold border transition flex items-center gap-0.5 max-w-[70px] ${st.instrumentName ? 'bg-violet-900 text-violet-300 border-violet-700' : 'bg-zinc-800 text-zinc-500 border-transparent hover:text-zinc-300'}`
|
||||
}, React.createElement("i", { "data-lucide": "music", className: "w-3 h-3 shrink-0" }), React.createElement("span", { className: "truncate text-[9px]" }, activeParentTrackName ? ('(' + activeParentTrackName + ') ' + (st.instrumentName || 'Synth')) : (st.instrumentName || 'Synth'))), React.createElement("div", {
|
||||
}, React.createElement("i", { "data-lucide": "music", className: "w-3 h-3 shrink-0" }), React.createElement("span", { className: "truncate text-[9px]" }, activeParentTrackName ? ('(' + activeParentTrackName + ') ' + (st.instrumentName || 'Synth')) : (st.instrumentName || 'Synth'))), (function() {
|
||||
var _e = (typeof window.SonicVstiAutosample !== 'undefined') ? window.SonicVstiAutosample.entryFor(st.synth_engine) : null;
|
||||
return _e ? /*#__PURE__*/React.createElement("span", { className: "px-1 py-0.5 bg-zinc-900 border border-zinc-800 text-emerald-400/90 rounded text-[9px] font-mono whitespace-nowrap shrink-0", title: "Autosampled: " + (_e.note_count || 0) + " notes" }, window.SonicVstiAutosample.formatSize(_e.size_bytes) + " \u00b7 " + (_e.note_count || 0) + "n") : null;
|
||||
})(), React.createElement("div", {
|
||||
className: "flex items-center gap-1 ml-1 text-xs"
|
||||
}, React.createElement("span", { className: "text-zinc-500" }, "AI:"), React.createElement("input", {
|
||||
type: "number", value: aiBarStart, onChange: e => setAiBarStart(parseInt(e.target.value) || 0),
|
||||
@@ -15374,6 +15383,13 @@ const App = () => {
|
||||
};
|
||||
const [instrumentSelectorData, setInstrumentSelectorData] = useState(null);
|
||||
const [instrumentRefreshKey, setInstrumentRefreshKey] = useState(0);
|
||||
// Autosample VSTi xong → re-render chip size/note_count (track strip)
|
||||
const [autosampleVersion, setAutosampleVersion] = React.useState(0);
|
||||
React.useEffect(() => {
|
||||
const _h = () => setAutosampleVersion(v => v + 1);
|
||||
window.addEventListener('sf:autosample-update', _h);
|
||||
return () => window.removeEventListener('sf:autosample-update', _h);
|
||||
}, []);
|
||||
const openInstrumentSelector = trackId => {
|
||||
setInstrumentSelectorTrackId(trackId);
|
||||
setSfPresetSearchQuery('');
|
||||
@@ -29708,7 +29724,10 @@ STRICT CONSTRAINTS:
|
||||
}, /*#__PURE__*/React.createElement("i", {
|
||||
"data-lucide": "music",
|
||||
className: "w-3 h-3"
|
||||
})), /*#__PURE__*/React.createElement("span", { className: "truncate text-[10px]" }, track.instrumentName || track.instrumentId || "Synth"), /*#__PURE__*/React.createElement("i", { "data-lucide": "chevron-down", className: "w-3 h-3 shrink-0" }))), /*#__PURE__*/React.createElement("div", {
|
||||
})), /*#__PURE__*/React.createElement("span", { className: "truncate text-[10px]" }, track.instrumentName || track.instrumentId || "Synth"), /*#__PURE__*/React.createElement("i", { "data-lucide": "chevron-down", className: "w-3 h-3 shrink-0" }))), (function() {
|
||||
var _e = (typeof window.SonicVstiAutosample !== 'undefined') ? window.SonicVstiAutosample.entryFor(track.synth_engine) : null;
|
||||
return _e ? /*#__PURE__*/React.createElement("span", { className: "px-1.5 py-0.5 bg-zinc-900 border border-zinc-800 text-emerald-400/90 rounded text-[9px] font-mono whitespace-nowrap shrink-0", title: "Autosampled: " + (_e.note_count || 0) + " notes" }, window.SonicVstiAutosample.formatSize(_e.size_bytes) + " \u00b7 " + (_e.note_count || 0) + "n") : null;
|
||||
})(), /*#__PURE__*/React.createElement("div", {
|
||||
onMouseDown: e => handleTrackResizeMouseDown(e, track.id),
|
||||
className: "absolute bottom-0 left-0 right-0 h-1 cursor-ns-resize z-30 hover:bg-cyan-500/50 transition-colors",
|
||||
onClick: e => e.stopPropagation()
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -27,6 +27,17 @@ window.SonicVstiAutosample = window.SonicVstiAutosample || {};
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (!bytes) return '0 KB';
|
||||
if (bytes >= 1048576) return (bytes / 1048576).toFixed(1) + ' MB';
|
||||
return Math.max(1, Math.round(bytes / 1024)) + ' KB';
|
||||
}
|
||||
|
||||
// UI re-render (chip hien thi note_count/size) khi autosample xong/fail
|
||||
function notify(k) {
|
||||
try { window.dispatchEvent(new CustomEvent('sf:autosample-update', { detail: { key: k } })); } catch (e) {}
|
||||
}
|
||||
|
||||
// Key = plugin + preset → autosample lại khi đổi preset (âm preset mới).
|
||||
function keyFor(synthEngine) {
|
||||
if (!synthEngine) return '';
|
||||
@@ -65,6 +76,7 @@ window.SonicVstiAutosample = window.SonicVstiAutosample || {};
|
||||
}
|
||||
if (!window.SonicAPI || !window.SonicAPI.autosampleVsti) {
|
||||
recordFail(k, 'API autosample khong kha dung');
|
||||
notify(k);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
_inFlight[k] = new Promise(function (resolve) {
|
||||
@@ -80,14 +92,17 @@ window.SonicVstiAutosample = window.SonicVstiAutosample || {};
|
||||
var map = readMap();
|
||||
map[k] = { sf_id: res.sf_id, size_bytes: res.size_bytes || 0, note_count: res.note_count || 0, plugin_id: synthEngine.plugin_id, preset_id: synthEngine.preset_id || synthEngine.presetId || '', ts: Date.now() };
|
||||
writeMap(map);
|
||||
notify(k);
|
||||
resolve(res.sf_id);
|
||||
} else {
|
||||
recordFail(k, (res && res.error) ? String(res.error) : 'Server khong tra sf_id');
|
||||
notify(k);
|
||||
resolve(null);
|
||||
}
|
||||
}).catch(function (err) {
|
||||
delete _inFlight[k];
|
||||
recordFail(k, (err && err.message) ? String(err.message) : 'Loi mang / server autosample');
|
||||
notify(k);
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
@@ -115,6 +130,7 @@ window.SonicVstiAutosample = window.SonicVstiAutosample || {};
|
||||
keyFor: keyFor,
|
||||
sfIdFor: sfIdFor,
|
||||
entryFor: entryFor,
|
||||
formatSize: formatSize,
|
||||
ensure: ensure,
|
||||
failReasonFor: failReasonFor,
|
||||
clearCache: clearCache
|
||||
|
||||
Reference in New Issue
Block a user