mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-26 07:45:34 +00:00
Merge pull request #722 from mvanhorn/fix/642-preview-audio-drops
fix: preview source audio stops around 20s due to unreliable reported duration
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
enablePitchPreservingPlayback,
|
||||
estimateCompanionAudioStartDelaySeconds,
|
||||
getMediaSyncPlaybackRate,
|
||||
resolvePreviewMediaDuration,
|
||||
} from "@/lib/mediaTiming";
|
||||
import type { AudioRegion, SpeedRegion } from "../types";
|
||||
|
||||
@@ -381,7 +382,7 @@ export function useAudioPreviewSync({
|
||||
audio.volume = Math.max(0, Math.min(1, getSourceTrackPreviewGain(sourceAudioPath) * (isCurrentClipMuted ? 0 : previewVolume)));
|
||||
|
||||
enablePitchPreservingPlayback(audio);
|
||||
const audioDuration = Number.isFinite(audio.duration) ? audio.duration : null;
|
||||
const audioDuration = resolvePreviewMediaDuration(audio.duration, duration);
|
||||
const isMicCompanionTrack = /\.mic\./i.test(sourceAudioPath);
|
||||
const rawStartDelaySeconds = estimateCompanionAudioStartDelaySeconds(
|
||||
duration,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getEffectiveRecordingDurationMs,
|
||||
getEffectiveVideoStreamDurationSeconds,
|
||||
getMediaSyncPlaybackRate,
|
||||
resolvePreviewMediaDuration,
|
||||
} from "./mediaTiming";
|
||||
|
||||
describe("clampMediaTimeToDuration", () => {
|
||||
@@ -21,6 +22,30 @@ describe("clampMediaTimeToDuration", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolvePreviewMediaDuration", () => {
|
||||
it("keeps reported durations that approximately match the timeline", () => {
|
||||
expect(resolvePreviewMediaDuration(120, 120)).toBe(120);
|
||||
expect(resolvePreviewMediaDuration(119.25, 120)).toBe(119.25);
|
||||
});
|
||||
|
||||
it("treats non-finite or missing reported durations as unknown", () => {
|
||||
expect(resolvePreviewMediaDuration(Number.POSITIVE_INFINITY, 120)).toBeNull();
|
||||
expect(resolvePreviewMediaDuration(Number.NaN, 120)).toBeNull();
|
||||
expect(resolvePreviewMediaDuration(null, 120)).toBeNull();
|
||||
});
|
||||
|
||||
it("treats implausibly short reported durations as unknown", () => {
|
||||
expect(resolvePreviewMediaDuration(20, 120)).toBeNull();
|
||||
expect(resolvePreviewMediaDuration(0, 120)).toBeNull();
|
||||
});
|
||||
|
||||
it("falls back to the reported duration when the timeline duration is unknown", () => {
|
||||
expect(resolvePreviewMediaDuration(20, Number.NaN)).toBe(20);
|
||||
expect(resolvePreviewMediaDuration(20, null)).toBe(20);
|
||||
expect(resolvePreviewMediaDuration(Number.NaN, Number.NaN)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("estimateCompanionAudioStartDelaySeconds", () => {
|
||||
it("keeps small inferred offsets when the companion audio is only slightly shorter", () => {
|
||||
expect(estimateCompanionAudioStartDelaySeconds(10, 9.6)).toBeCloseTo(0.4);
|
||||
|
||||
@@ -7,6 +7,47 @@ export function clampMediaTimeToDuration(targetTime: number, duration?: number |
|
||||
return Math.max(0, Math.min(safeTargetTime, Math.max(0, duration)));
|
||||
}
|
||||
|
||||
const MIN_PREVIEW_MEDIA_DURATION_SHORTFALL_TOLERANCE_SECONDS = 0.5;
|
||||
const MAX_PREVIEW_MEDIA_DURATION_SHORTFALL_TOLERANCE_SECONDS = 1;
|
||||
const PREVIEW_MEDIA_DURATION_SHORTFALL_TOLERANCE_RATIO = 0.01;
|
||||
|
||||
export function resolvePreviewMediaDuration(
|
||||
reportedDuration?: number | null,
|
||||
timelineDuration?: number | null,
|
||||
): number | null {
|
||||
if (!Number.isFinite(reportedDuration)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const safeReportedDuration = Math.max(0, reportedDuration ?? 0);
|
||||
if (
|
||||
!Number.isFinite(timelineDuration) ||
|
||||
timelineDuration === null ||
|
||||
timelineDuration === undefined
|
||||
) {
|
||||
return safeReportedDuration;
|
||||
}
|
||||
|
||||
const safeTimelineDuration = Math.max(0, timelineDuration);
|
||||
if (safeTimelineDuration === 0) {
|
||||
return safeReportedDuration;
|
||||
}
|
||||
|
||||
const shortfallToleranceSeconds = Math.min(
|
||||
MAX_PREVIEW_MEDIA_DURATION_SHORTFALL_TOLERANCE_SECONDS,
|
||||
Math.max(
|
||||
MIN_PREVIEW_MEDIA_DURATION_SHORTFALL_TOLERANCE_SECONDS,
|
||||
safeTimelineDuration * PREVIEW_MEDIA_DURATION_SHORTFALL_TOLERANCE_RATIO,
|
||||
),
|
||||
);
|
||||
|
||||
if (safeTimelineDuration - safeReportedDuration > shortfallToleranceSeconds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return safeReportedDuration;
|
||||
}
|
||||
|
||||
const MIN_COMPANION_AUDIO_DELAY_SECONDS = 0.025;
|
||||
const MAX_INFERRED_COMPANION_AUDIO_DELAY_SECONDS = 0.5;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user