diff --git a/src/components/ai-edition/VirtualPreview.tsx b/src/components/ai-edition/VirtualPreview.tsx index 4e4ec9e550..19c0fb1175 100644 --- a/src/components/ai-edition/VirtualPreview.tsx +++ b/src/components/ai-edition/VirtualPreview.tsx @@ -11,8 +11,8 @@ import { takeCrossedClickHits, } from "@/lib/ai-edition/clickSound"; import { - collapseTracksToPills, resolveFadeSecs, + takesByHeadFragment, trackGroupId, } from "@/lib/ai-edition/document/audioTracks"; import { @@ -875,23 +875,22 @@ export function VirtualPreview({ // One walk per take, recomputed only when the cuts or the insertions move. The rAF asks // it every frame per track, and walking on each would be wasteful. const takePiecesRef = useRef>(new Map()); - const takeHeadsRef = useRef>(new Map()); + // Each take keyed by the fragment whose element plays it, as the export mixes it. + const takesRef = useRef>(new Map()); const removedRef = useRef(removedRawSpans(clips, trimRanges)); removedRef.current = useMemo(() => removedRawSpans(clips, trimRanges), [clips, trimRanges]); const takeWalks = useMemo(() => { const pieces = new Map(); - const heads = new Map(); const removed = removedRawSpans(clips, trimRanges); - for (const pill of collapseTracksToPills(audioTracks)) { + const takes = takesByHeadFragment(audioTracks); + for (const pill of takes.values()) { if (pill.kind !== "voiceover" || pill.loop) continue; - const groupId = trackGroupId(pill); - heads.set(groupId, pill.id); - pieces.set(groupId, takeProgramme(pill, removed)); + pieces.set(trackGroupId(pill), takeProgramme(pill, removed)); } - return { pieces, heads }; + return { pieces, takes }; }, [audioTracks, clips, trimRanges]); takePiecesRef.current = takeWalks.pieces; - takeHeadsRef.current = takeWalks.heads; + takesRef.current = takeWalks.takes; // Trim-narrowed (`resolvePlaybackSegments`) — used ONLY to detect "has the