feat(export): thanh tien do offline export (render track VST -> encode -> save) trong ExportModal
This commit is contained in:
+23
-1
@@ -12430,7 +12430,7 @@ const InteractiveEqPro = ({ track, params, onChange, getModule, applyTo, spectru
|
||||
);
|
||||
};
|
||||
|
||||
const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExporting, onExport }) => {
|
||||
const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExporting, exportProgress, onExport }) => {
|
||||
if (!open) return null;
|
||||
const pickDir = async () => {
|
||||
try {
|
||||
@@ -12626,6 +12626,16 @@ const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExpor
|
||||
<span className="text-xs text-zinc-300">Realtime (bounce — chạy lại project thật, giữ nguyên MIDI + FX Rack + Mastering Chain)</span>
|
||||
</label>
|
||||
</div>
|
||||
{isExporting && (
|
||||
<div className="bg-[#141414] border border-zinc-800 rounded px-2 py-1.5">
|
||||
<div className="h-1.5 bg-zinc-800 rounded-full overflow-hidden">
|
||||
<div className="h-full bg-cyan-500 transition-all duration-200" style={{ width: (exportProgress >= 0 ? exportProgress : 25) + '%' }} />
|
||||
</div>
|
||||
<div className="text-[8px] text-zinc-500 mt-1">
|
||||
{exportProgress >= 0 ? 'Đang xuất... ' + Math.round(exportProgress) + '%' : 'Đang xử lý...'}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-2 gap-2 pt-1">
|
||||
<button onClick={onExport} disabled={isExporting}
|
||||
className="w-full py-1 bg-cyan-700 hover:bg-cyan-600 text-zinc-100 font-bold rounded text-xs flex items-center justify-center gap-1">
|
||||
@@ -17827,6 +17837,7 @@ const App = () => {
|
||||
}, [selectionStart, selectionEnd, bpm]);
|
||||
const [subTabHeight, setSubTabHeight] = useState(96);
|
||||
const [isExporting, setIsExporting] = useState(false);
|
||||
const [exportProgress, setExportProgress] = useState(-1);
|
||||
const [projectName, setProjectName] = useState(() => localStorage.getItem('sonic_project_name') || '');
|
||||
const [currentProjectId, setCurrentProjectId] = useState(() => localStorage.getItem('sonic_project_id') || null);
|
||||
const [saveProjectModalOpen, setSaveProjectModalOpen] = useState(false);
|
||||
@@ -28025,6 +28036,7 @@ const App = () => {
|
||||
if (!exportSettings.loop && allOnServer && serverStatus === 'connected') {
|
||||
// Use server-side export
|
||||
setIsExporting(true);
|
||||
setExportProgress(-1);
|
||||
showToast("Đang gửi yêu cầu xuất âm thanh đến máy chủ...", "info");
|
||||
try {
|
||||
const sessionId = `session_${Date.now()}`;
|
||||
@@ -28464,6 +28476,7 @@ const App = () => {
|
||||
};
|
||||
const clientSideExport = async (activeTracks, loopInfo) => {
|
||||
setIsExporting(true);
|
||||
setExportProgress(5);
|
||||
showToast("Đang trộn âm thanh đa kênh (Offline Mixdown)...", "info");
|
||||
try {
|
||||
const targetRate = parseInt(exportSettings.sampleRate);
|
||||
@@ -28579,7 +28592,10 @@ const App = () => {
|
||||
// trên tổng → master VST chain cuối chain. Đúng thứ tự live: track fx
|
||||
// → (sum) → master fx → out.
|
||||
let mix = null;
|
||||
let vstTi = 0;
|
||||
for (const t of activeTracks) {
|
||||
setExportProgress(10 + Math.round((vstTi / Math.max(1, activeTracks.length)) * 40));
|
||||
vstTi++;
|
||||
const chain = activeVstChain(t.vstFxChain || []);
|
||||
const tctx = new OfflineAudioContext(2, Math.ceil(targetRate * Math.max(0.1, durationLimit)), targetRate);
|
||||
const tNodes = {};
|
||||
@@ -28626,6 +28642,7 @@ const App = () => {
|
||||
});
|
||||
renderedBuffer = await offlineCtx.startRendering();
|
||||
}
|
||||
setExportProgress(60);
|
||||
const numExportCh = renderedBuffer.numberOfChannels;
|
||||
const exportLength = renderedBuffer.length;
|
||||
const bytesPerSample = bitDepth / 8;
|
||||
@@ -28653,6 +28670,7 @@ const App = () => {
|
||||
view.setUint32(40, exportLength * bytesPerSample * numExportCh, true);
|
||||
let offset = 44;
|
||||
for (let i = 0; i < exportLength; i++) {
|
||||
if ((i & 0x3FFF) === 0) setExportProgress(60 + Math.round((i / Math.max(1, exportLength)) * 32));
|
||||
for (let ch = 0; ch < numExportCh; ch++) {
|
||||
const chData = renderedBuffer.getChannelData(ch);
|
||||
const sample = Math.max(-1, Math.min(1, chData[i]));
|
||||
@@ -28675,8 +28693,10 @@ const App = () => {
|
||||
type: 'audio/wav'
|
||||
});
|
||||
const wavName = `sonicforge-session-${targetRate}hz-${bitDepth}bit.wav`;
|
||||
setExportProgress(95);
|
||||
if ((exportSettings.format === 'mp3' || exportSettings.format === 'ogg') && serverStatus === 'connected') {
|
||||
setIsExporting(true);
|
||||
setExportProgress(-1);
|
||||
showToast("Đang gửi yêu cầu nén định dạng lên máy chủ...", "info");
|
||||
try {
|
||||
const file = new File([blob], `session_export.wav`, { type: 'audio/wav' });
|
||||
@@ -28765,6 +28785,7 @@ const App = () => {
|
||||
} catch (err) {
|
||||
showToast("Lỗi xuất âm thanh: " + err.message, "error");
|
||||
} finally {
|
||||
setExportProgress(-1);
|
||||
setIsExporting(false);
|
||||
}
|
||||
};
|
||||
@@ -34563,6 +34584,7 @@ STRICT CONSTRAINTS:
|
||||
exportSettings: exportSettings,
|
||||
setExportSettings: setExportSettings,
|
||||
isExporting: isExporting,
|
||||
exportProgress: exportProgress,
|
||||
onExport: () => exportSettings.realtime ? triggerBounceExport() : triggerWavExport()
|
||||
}), instrumentSelectorTrackId && /*#__PURE__*/React.createElement("div", {
|
||||
className: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -51,7 +51,7 @@
|
||||
<script src="/static/js/services/promptTemplateManager.js?v=202607281039"></script>
|
||||
<script src="/static/js/services/undoRedoEngine.js?v=202607290941"></script>
|
||||
<script src="/static/js/services/chordTheory.js?v=202608211300"></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608232330" defer></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608232345" defer></script>
|
||||
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
|
||||
<style>
|
||||
:root {
|
||||
|
||||
Reference in New Issue
Block a user