fix(export): progress bar hien thi duoc - footer ngoai vung scroll + encode chunked yield + yield frame dau
This commit is contained in:
+31
-19
@@ -12626,6 +12626,8 @@ 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>
|
||||
</div>
|
||||
<div className="p-4 pt-3 border-t border-slate-700 space-y-2">
|
||||
{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">
|
||||
@@ -12636,7 +12638,7 @@ const ExportModal = ({ open, onClose, exportSettings, setExportSettings, isExpor
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-2 gap-2 pt-1">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<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">
|
||||
<i data-lucide="download-cloud" className="w-3 h-3" /> {isExporting ? '...' : 'Export'}
|
||||
@@ -28481,6 +28483,9 @@ const App = () => {
|
||||
setIsExporting(true);
|
||||
setExportProgress(5);
|
||||
showToast("Đang trộn âm thanh đa kênh (Offline Mixdown)...", "info");
|
||||
// Yield 1 frame → React vẽ thanh "Đang xử lý..." trước khi graph build +
|
||||
// OfflineAudioContext render chặn main thread.
|
||||
await new Promise(res => setTimeout(res, 30));
|
||||
try {
|
||||
const targetRate = parseInt(exportSettings.sampleRate);
|
||||
const isFloatWav = exportSettings.bitDepth === 'float';
|
||||
@@ -28673,26 +28678,33 @@ const App = () => {
|
||||
view.setUint32(40, exportLength * bytesPerSample * numExportCh, true);
|
||||
let offset = 44;
|
||||
let peak = 0;
|
||||
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]));
|
||||
if (Math.abs(sample) > peak) peak = Math.abs(sample);
|
||||
if (bitDepth === 8) {
|
||||
view.setUint8(offset, Math.floor((sample + 1.0) * 127.5));
|
||||
} else if (bitDepth === 16) {
|
||||
view.setInt16(offset, Math.floor(sample < 0 ? sample * 0x8000 : sample * 0x7FFF), true);
|
||||
} else if (bitDepth === 24) {
|
||||
const val24 = Math.floor(sample < 0 ? sample * 0x800000 : sample * 0x7FFFFF);
|
||||
view.setUint8(offset, val24 & 0xFF);
|
||||
view.setUint8(offset + 1, val24 >> 8 & 0xFF);
|
||||
view.setUint8(offset + 2, val24 >> 16 & 0xFF);
|
||||
} else if (isFloatWav) {
|
||||
view.setFloat32(offset, sample, true);
|
||||
// Chunk encode + yield giữa các chunk → React vẽ thanh tiến độ thật sự
|
||||
// (vòng lặp đồng bộ dài sẽ chặn main thread, bar không bao giờ hiện).
|
||||
const ENC_CHUNK = 65536;
|
||||
for (let base = 0; base < exportLength; base += ENC_CHUNK) {
|
||||
const encEnd = Math.min(exportLength, base + ENC_CHUNK);
|
||||
for (let i = base; i < encEnd; i++) {
|
||||
for (let ch = 0; ch < numExportCh; ch++) {
|
||||
const chData = renderedBuffer.getChannelData(ch);
|
||||
const sample = Math.max(-1, Math.min(1, chData[i]));
|
||||
if (Math.abs(sample) > peak) peak = Math.abs(sample);
|
||||
if (bitDepth === 8) {
|
||||
view.setUint8(offset, Math.floor((sample + 1.0) * 127.5));
|
||||
} else if (bitDepth === 16) {
|
||||
view.setInt16(offset, Math.floor(sample < 0 ? sample * 0x8000 : sample * 0x7FFF), true);
|
||||
} else if (bitDepth === 24) {
|
||||
const val24 = Math.floor(sample < 0 ? sample * 0x800000 : sample * 0x7FFFFF);
|
||||
view.setUint8(offset, val24 & 0xFF);
|
||||
view.setUint8(offset + 1, val24 >> 8 & 0xFF);
|
||||
view.setUint8(offset + 2, val24 >> 16 & 0xFF);
|
||||
} else if (isFloatWav) {
|
||||
view.setFloat32(offset, sample, true);
|
||||
}
|
||||
offset += bytesPerSample;
|
||||
}
|
||||
offset += bytesPerSample;
|
||||
}
|
||||
setExportProgress(60 + Math.round((encEnd / Math.max(1, exportLength)) * 32));
|
||||
await new Promise(res => setTimeout(res, 0));
|
||||
}
|
||||
if (peak < 0.001) {
|
||||
showToast("Cảnh báo: file export gần như CÂM (không có tín hiệu âm) — kiểm tra nguồn track / MIDI cache preview.", "warning");
|
||||
|
||||
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=202608240010" defer></script>
|
||||
<script src="/static/js/app.precompiled.js?v=202608240040" defer></script>
|
||||
<link rel="stylesheet" href="/static/css/styles.css?v=202607271016">
|
||||
<style>
|
||||
:root {
|
||||
|
||||
Reference in New Issue
Block a user