diff --git a/BACKLOG.md b/BACKLOG.md index e8ee187..07a84cf 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -119,15 +119,6 @@ NVENC, если он есть). Это разрозненно и не даёт неподъёмным. Пока цифры замеряны только на одной машине, у человека должна быть возможность выключить любую из этих трат. -## Отмена ненужной генерации не проверена живьём - -Правка монтажа прекращает создание вставок, ставших ненужными, — код на -месте и вызывается на каждой пересборке предпросмотра. Но поймать отмену -глазами не на чем: на мелком проверочном материале минутная чёрная -вставка делается мгновенно. Нужен прогон на настоящем материале в -2688x1512: завести дыру, дождаться начала генерации, закрыть дыру и -убедиться по журналу, что ffmpeg остановлен. - ## Мелочи, до которых не дошли руки **Вид дорожки не виден в её заголовке.** Дорожки картинки, звука и текста diff --git a/src/uFlatten.pas b/src/uFlatten.pas index 09b5d78..6d5c53f 100644 --- a/src/uFlatten.pas +++ b/src/uFlatten.pas @@ -1043,28 +1043,54 @@ var Req: TGenRequest; Path: string; + { Уже заказан ли такой файл. } + function Requested(const APath: string): Boolean; + var + K: Integer; + begin + Result := False; + for K := 0 to High(AMissing) do + if AMissing[K].TargetPath = APath then + Exit(True); + end; + + { Кусок тишины. Длинный набирается повторами минутного файла — как и + чёрные вставки: готовых длин всего две, и просить у минутного файла + три минуты нельзя, плеер на это ругается, а звук обрывается. } procedure PushSilence(const ALen: Double); + var + Left, Take: Double; begin if ALen <= 0.0005 then Exit; - Seg := Default(TFlatSegment); - Seg.Volume := 1; - Seg.Kind := skBlack; - Seg.Duration := ALen; - { Тишина без картинки: см. пояснение у gkSilence. } - Req.Kind := gkSilence; - Req.SourcePath := ''; - Req.Duration := BucketFor(ALen); - Req.TargetPath := GeneratedPath(AProject.CacheDir, Req); - Seg.FilePath := Req.TargetPath; - if not FileExists(Req.TargetPath) then + + Left := ALen; + while Left > 0.0005 do begin - SetLength(AMissing, Length(AMissing) + 1); - AMissing[High(AMissing)] := Req; + Take := Min(MaxInsertLength, Left); + Seg := Default(TFlatSegment); + Seg.Volume := 1; + Seg.Kind := skBlack; + Seg.Duration := Take; + { Тишина без картинки: см. пояснение у gkSilence. } + Req.Kind := gkSilence; + Req.SourcePath := ''; + Req.Duration := BucketFor(Take); + Req.TargetPath := GeneratedPath(AProject.CacheDir, Req); + Seg.FilePath := Req.TargetPath; + { Один и тот же файл не заказываем дважды: длинная тишина набирается + повторами, и без этой проверки минутный кусок делался бы столько + раз, сколько его повторов. } + if (not FileExists(Req.TargetPath)) and (not Requested(Req.TargetPath)) then + begin + SetLength(AMissing, Length(AMissing) + 1); + AMissing[High(AMissing)] := Req; + end; + SetLength(ASegments, N + 1); + ASegments[N] := Seg; + Inc(N); + Left := Left - Take; end; - SetLength(ASegments, N + 1); - ASegments[N] := Seg; - Inc(N); end; begin diff --git a/src/uMainForm.pas b/src/uMainForm.pas index a77791e..cc5d244 100644 --- a/src/uMainForm.pas +++ b/src/uMainForm.pas @@ -3098,14 +3098,26 @@ end; procedure TMainForm.GenerateDone(Sender: TObject); var - Ok: Boolean; + Ok, WasCancelled: Boolean; Msg: string; begin Ok := FGenerate.Succeeded; + WasCancelled := FGenerate.Cancelled; Msg := FGenerate.Message; FGenerate := nil; FGenerateBusy := False; + { Отмену просили мы сами, когда правка сделала работу ненужной. Это не + беда, и говорить «не удалось» тут нельзя: в журнале появлялась строка + об ошибке там, где всё шло по плану. Пересобираем предпросмотр по + свежей расстановке. } + if WasCancelled then + begin + Log('Подготовка вставок прекращена: расстановка изменилась.'); + SchedulePreview; + Exit; + end; + if not Ok then begin Log('Не удалось подготовить вставки: ' + Msg); diff --git a/tests/nlvetool.lpr b/tests/nlvetool.lpr index 5fabfe1..5453504 100644 --- a/tests/nlvetool.lpr +++ b/tests/nlvetool.lpr @@ -971,6 +971,39 @@ begin end; WriteLn; + { --- 15. Длинная тишина тоже набирается повторами --- } + WriteLn(' 15. Дыра на звуковой дорожке'); + Proj := MakeProject(1, Media); + try + Proj.Master := Media.Info; + Proj.EnsureAudioTracks; + { Два звуковых клипа с промежутком в семьдесят секунд: он разложится + на минуту и десять секунд, то есть на два разных файла. } + Clip := Proj.AddClip(Proj.Tracks[1], Media, 0, 5, 0); + Clip.Kind := ckAudio; + Clip := Proj.AddClip(Proj.Tracks[1], Media, 10, 5, 75); + Clip.Kind := ckAudio; + + FlattenAudioTrack(Proj, Proj.Tracks[1], 80, Segs, Missing); + DumpSegments(Segs); + + N := 0; + for I := 0 to High(Segs) do + if Segs[I].Kind = skBlack then + begin + Inc(N); + Check(Segs[I].Duration <= 60.0005, + 'кусок тишины не длиннее минуты: ' + SecondsToStr(Segs[I].Duration)); + end; + Check(N = 2, 'тишина разложена на два куска: ' + IntToStr(N)); + Check(Length(Missing) = 2, + 'заказаны две длины, минутная и пятнадцатисекундная: ' + + IntToStr(Length(Missing))); + finally + Proj.Free; + end; + WriteLn; + WriteLn; if FailCount = 0 then begin