Files
olivv-cs-ComfyUI-FunPack/app/ui/features/preview/lanes.js
T

48 lines
2.6 KiB
JavaScript

// The audio lanes on the monitor: each lane's sound plays while the playhead is inside it and the monitor is playing.
const view = (m) => `/view?filename=${encodeURIComponent(m.filename || "")}&subfolder=${encodeURIComponent(m.subfolder || "")}&type=${encodeURIComponent(m.type || "output")}`;
const bin = (id) => `/funpack/api/media/${encodeURIComponent(id)}/file`;
/** Where a lane's sound comes from, and which part of it plays: {url, from, dur} or null. */
export function laneSource(t, open) {
if (t.kind === "separated") {
const url = t.pinned_bin_ref ? bin(t.pinned_bin_ref) : t.pinned_media && t.pinned_media.filename ? view(t.pinned_media) : null;
return url && { url, from: t.pinned_in_sec ?? t.source_in_sec ?? 0, dur: t.pinned_dur ?? t.source_dur ?? 0 };
}
if (t.media_ref) return { url: bin(t.media_ref), from: t.source_in_sec || 0, dur: t.source_dur != null ? t.source_dur : 86400 };
return null;
}
export default {
id: "preview-lanes",
mount: "preview",
needs: ["project", "playhead"],
setup({ app }) {
const p = app.project, head = app.playhead, pool = new Map();
const stopAll = () => pool.forEach((a) => a.pause());
function tick() {
const open = p.project, live = new Set();
if (open && head.playing) {
for (const t of open.audio_tracks || []) {
const src = laneSource(t, open);
if (!src) continue;
live.add(t.id);
let a = pool.get(t.id);
if (!a) { a = new Audio(); a.preload = "auto"; pool.set(t.id, a); }
if (a.dataset.src !== src.url) { a.dataset.src = src.url; a.src = src.url; }
const start = t.start_sec || 0, within = head.at - start, inside = within >= 0 && within < src.dur;
const vol = Math.max(0, Math.min(1, t.volume != null ? +t.volume : 1));
if (a.volume !== vol && Number.isFinite(vol)) a.volume = vol;
if (!inside) { if (!a.paused) a.pause(); continue; }
if (Math.abs(a.currentTime - (src.from + within)) > 0.3) a.currentTime = src.from + within;
if (a.paused) a.play().catch(() => {});
}
}
for (const [id, a] of pool) if (!live.has(id)) { a.pause(); a.removeAttribute("src"); a.load(); delete a.dataset.src; if (!head.playing) pool.delete(id); } // not playing: every lane gives its connection back (the 6-per-origin pool)
}
const off = [head.on(tick), app.on(() => tick())];
const timer = setInterval(tick, 200); // the playhead only moves while playing; this catches pause/stop
return () => { off.forEach((f) => f()); clearInterval(timer); stopAll(); pool.forEach((a) => { a.removeAttribute("src"); a.load(); }); pool.clear(); };
},
};