fix(audio): robust async buffer loading with state merging + API fallback
- Rewrite loadAudioBuffersForTracks to use functional setTracks preventing loss of intermediate state updates - Add API download endpoint as fallback when static fetch fails - Also update sessionTabs tracks with loaded buffers - return decodeAudioFile result (audioBuffer+channelInfo) instead of just audioBuffer
This commit is contained in:
+56
-40
@@ -9860,65 +9860,81 @@ const App = () => {
|
|||||||
|
|
||||||
const loadAudioBuffersForTracks = async (tracksList) => {
|
const loadAudioBuffersForTracks = async (tracksList) => {
|
||||||
let hasLoadedAny = false;
|
let hasLoadedAny = false;
|
||||||
|
const loadBuffer = async (url) => {
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (!res.ok) return null;
|
||||||
|
const blob = await res.blob();
|
||||||
|
return await window.SonicAudio.decodeAudioFile(blob);
|
||||||
|
};
|
||||||
|
const tryLoad = async (fileId) => {
|
||||||
|
if (!fileId) return null;
|
||||||
|
try {
|
||||||
|
const result = await loadBuffer('/static/audio/uploads/' + fileId);
|
||||||
|
if (result) return result;
|
||||||
|
} catch (_) {}
|
||||||
|
try {
|
||||||
|
const result = await loadBuffer(`${API_AUDIO}/download/${fileId}`);
|
||||||
|
if (result) return result;
|
||||||
|
} catch (_) {}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
const updatedTracks = await Promise.all(tracksList.map(async t => {
|
const updatedTracks = await Promise.all(tracksList.map(async t => {
|
||||||
let trackBuffer = t.buffer;
|
let trackBuffer = t.buffer;
|
||||||
let trackChannelInfo = t.channelInfo;
|
let trackChannelInfo = t.channelInfo;
|
||||||
if (t.serverFileId && !trackBuffer) {
|
if (t.serverFileId && !trackBuffer) {
|
||||||
try {
|
const result = await tryLoad(t.serverFileId);
|
||||||
const res = await fetch('/static/audio/uploads/' + t.serverFileId);
|
if (result) {
|
||||||
if (res.ok) {
|
trackBuffer = result.audioBuffer;
|
||||||
const blob = await res.blob();
|
trackChannelInfo = result.channelInfo;
|
||||||
const { audioBuffer, channelInfo } = await window.SonicAudio.decodeAudioFile(blob);
|
hasLoadedAny = true;
|
||||||
trackBuffer = audioBuffer;
|
|
||||||
trackChannelInfo = channelInfo;
|
|
||||||
hasLoadedAny = true;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Failed to autoload track buffer for " + t.serverFileId, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let clipsUpdated = false;
|
|
||||||
const updatedClips = await Promise.all((t.clips || []).map(async c => {
|
const updatedClips = await Promise.all((t.clips || []).map(async c => {
|
||||||
let clipBuffer = c.buffer;
|
let clipBuffer = c.buffer;
|
||||||
const targetFileId = c.serverFileId || t.serverFileId;
|
const targetFileId = c.serverFileId || t.serverFileId;
|
||||||
if (targetFileId && !clipBuffer) {
|
if (targetFileId && !clipBuffer) {
|
||||||
try {
|
const result = await tryLoad(targetFileId);
|
||||||
const res = await fetch('/static/audio/uploads/' + targetFileId);
|
if (result) {
|
||||||
if (res.ok) {
|
clipBuffer = result.audioBuffer;
|
||||||
const blob = await res.blob();
|
hasLoadedAny = true;
|
||||||
const { audioBuffer } = await window.SonicAudio.decodeAudioFile(blob);
|
|
||||||
clipBuffer = audioBuffer;
|
|
||||||
clipsUpdated = true;
|
|
||||||
hasLoadedAny = true;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Failed to autoload clip buffer for " + targetFileId, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { ...c, buffer: clipBuffer };
|
return { ...c, buffer: clipBuffer };
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (trackBuffer && updatedClips.length === 0) {
|
if (trackBuffer && updatedClips.length === 0) {
|
||||||
const clipId = `default_${t.id}`;
|
const clipId = `default_${t.id}`;
|
||||||
return {
|
return { ...t, buffer: trackBuffer, channelInfo: trackChannelInfo,
|
||||||
...t,
|
clips: [{ id: clipId, buffer: trackBuffer, startTime: t.startTime || 0, name: t.name, speed: 1.0 }] };
|
||||||
buffer: trackBuffer,
|
|
||||||
channelInfo: trackChannelInfo,
|
|
||||||
clips: [{ id: clipId, buffer: trackBuffer, startTime: t.startTime || 0, name: t.name, speed: 1.0 }]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
return { ...t, buffer: trackBuffer, channelInfo: trackChannelInfo, clips: updatedClips };
|
||||||
return {
|
|
||||||
...t,
|
|
||||||
buffer: trackBuffer,
|
|
||||||
channelInfo: trackChannelInfo,
|
|
||||||
clips: updatedClips
|
|
||||||
};
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (hasLoadedAny) {
|
if (hasLoadedAny) {
|
||||||
setTracks(updatedTracks);
|
setTracks(prev => {
|
||||||
|
const merged = [...updatedTracks];
|
||||||
|
(prev || []).forEach((pt, i) => {
|
||||||
|
if (!merged[i]) merged[i] = pt;
|
||||||
|
else {
|
||||||
|
merged[i] = { ...merged[i] };
|
||||||
|
merged[i].clips = (pt.clips || []).map((pc, j) => {
|
||||||
|
if (merged[i].clips && merged[i].clips[j] && (merged[i].clips[j].buffer || pc.buffer)) {
|
||||||
|
return { ...pc, buffer: pc.buffer || merged[i].clips[j].buffer };
|
||||||
|
}
|
||||||
|
if ((pc.buffer || (merged[i].clips && merged[i].clips[j] && merged[i].clips[j].buffer))) {
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
return merged[i].clips && merged[i].clips[j] ? merged[i].clips[j] : pc;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return merged;
|
||||||
|
});
|
||||||
|
setSessionTabs(prev => prev.map(st => ({
|
||||||
|
...st,
|
||||||
|
tracks: (st.tracks || []).map(t => {
|
||||||
|
const found = updatedTracks.find(u => u.id === t.id);
|
||||||
|
return found || t;
|
||||||
|
})
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user