Public/Timestamps/Invoke-SubtitleStretch.ps1
|
function Invoke-SubtitleStretch { <# .SYNOPSIS Applies linear time-stretch using two sync point pairs to correct A/V drift. .DESCRIPTION When subtitles drift non-linearly (e.g., they start in sync but gradually fall behind), provide two reference points: where a subtitle currently appears and where it should appear. The function calculates a linear scale + offset and applies it to all entries. Formula: NewTime = (OldTime - SourcePoint1) * Scale + TargetPoint1 Where: Scale = (TargetPoint2 - TargetPoint1) / (SourcePoint2 - SourcePoint1) .PARAMETER InputObject A SubtitleFile object. .PARAMETER SourcePoint1 The current (wrong) time of the first sync reference point. .PARAMETER TargetPoint1 The correct time for the first sync reference point. .PARAMETER SourcePoint2 The current (wrong) time of the second sync reference point. .PARAMETER TargetPoint2 The correct time for the second sync reference point. .EXAMPLE # Subtitle at 0:01:00 should be 0:01:02; at 1:30:00 should be 1:30:10 Invoke-SubtitleStretch -InputObject $sub ` -SourcePoint1 '00:01:00' -TargetPoint1 '00:01:02' ` -SourcePoint2 '01:30:00' -TargetPoint2 '01:30:10' #> [CmdletBinding()] [OutputType('SubtitleFile')] param( [Parameter(Mandatory, ValueFromPipeline)] [SubtitleFile] $InputObject, [Parameter(Mandatory)] [TimeSpan] $SourcePoint1, [Parameter(Mandatory)] [TimeSpan] $TargetPoint1, [Parameter(Mandatory)] [TimeSpan] $SourcePoint2, [Parameter(Mandatory)] [TimeSpan] $TargetPoint2 ) process { $srcRange = ($SourcePoint2 - $SourcePoint1).TotalMilliseconds $tgtRange = ($TargetPoint2 - $TargetPoint1).TotalMilliseconds if ($srcRange -eq 0) { throw 'SourcePoint1 and SourcePoint2 must be different times.' } $scale = $tgtRange / $srcRange foreach ($entry in $InputObject.Entries) { $entry.Start = [TimeSpan]::FromMilliseconds( ($entry.Start - $SourcePoint1).TotalMilliseconds * $scale + $TargetPoint1.TotalMilliseconds ) $entry.End = [TimeSpan]::FromMilliseconds( ($entry.End - $SourcePoint1).TotalMilliseconds * $scale + $TargetPoint1.TotalMilliseconds ) # Clamp negatives if ($entry.Start -lt [TimeSpan]::Zero) { $entry.Start = [TimeSpan]::Zero } if ($entry.End -lt [TimeSpan]::Zero) { $entry.End = [TimeSpan]::Zero } } return $InputObject } } |