diff --git a/src/native/sceneDescription.test.ts b/src/native/sceneDescription.test.ts index 567fe925e..63a682072 100644 --- a/src/native/sceneDescription.test.ts +++ b/src/native/sceneDescription.test.ts @@ -2723,6 +2723,18 @@ describe("buildSceneDescription.audioTracks", () => { expect(entries[1]).toMatchObject({ startSec: 4, trimStartSec: 6, trimEndSec: 10 }); }); + it("stops a piece where the next one starts when a speed region sits before the cut", () => { + // Issue #1283. A 2x region over raw 0..2 saves 1s, so the piece after the cut lands at + // output 3. Played whole at 1x, raw 0..4 ran to output 4: two pieces at once for 1s. + const doc = { ...cutDoc([voice]) }; + doc.legacyEditor = { speedRegions: [{ id: "s", startMs: 0, endMs: 2000, speed: 2 }] }; + const entries = buildSceneDescription(doc).audioTracks; + expect(entries.map((e) => [e.startSec, e.trimStartSec, e.trimEndSec])).toEqual([ + [0, 0, 3], + [3, 6, 10], + ]); + }); + it("keeps the fades on the take's outer edges across a split", () => { const entries = buildSceneDescription( cutDoc([{ ...voice, fadeInMs: 500, fadeOutMs: 500 }]), diff --git a/src/native/sceneDescription.ts b/src/native/sceneDescription.ts index 71e920b38..20caf7a20 100644 --- a/src/native/sceneDescription.ts +++ b/src/native/sceneDescription.ts @@ -982,11 +982,22 @@ export function buildSceneDescription( })) .filter((entry) => entry.trimEndSec > entry.trimStartSec); // The fades belong to the TAKE's edges, not to every piece a cut left behind. - return kept.map((entry, i) => ({ - ...entry, - fadeInSec: i === 0 ? base.fadeInSec : 0, - fadeOutSec: i === kept.length - 1 ? base.fadeOutSec : 0, - })); + return kept.map((entry, i) => { + // A piece plays at 1x, but its head and the next one's are projected through the + // speed regions: a sped-up stretch between them leaves the piece longer than the + // output gap, so it would still be playing when the next one starts (#1283). It + // stops where the next piece begins. + const next = kept[i + 1]; + const trimEndSec = next + ? Math.min(entry.trimEndSec, entry.trimStartSec + next.startSec - entry.startSec) + : entry.trimEndSec; + return { + ...entry, + trimEndSec, + fadeInSec: i === 0 ? base.fadeInSec : 0, + fadeOutSec: i === kept.length - 1 ? base.fadeOutSec : 0, + }; + }); } if (!track.loop) { return [