diff --git a/LanguageMaster.xml b/LanguageMaster.xml index 5ea9edf2a..072db5cff 100644 --- a/LanguageMaster.xml +++ b/LanguageMaster.xml @@ -2021,6 +2021,7 @@ can edit in same subtitle file (collaboration) Go to previous scene change Go to next scene change Toggle scene change + Auto adjust start via volume/scene change One frame back One frame forward One frame back (with play) diff --git a/libse/Language.cs b/libse/Language.cs index f308ccf44..1b18fec68 100644 --- a/libse/Language.cs +++ b/libse/Language.cs @@ -2317,6 +2317,7 @@ can edit in same subtitle file (collaboration)", WaveformGoToPreviousSceneChange = "Go to previous scene change", WaveformGoToNextSceneChange = "Go to next scene change", WaveformToggleSceneChange = "Toggle scene change", + WaveformGuessStart = "Auto adjust start via volume/scene change", GoBack1Frame = "One frame back", GoForward1Frame = "One frame forward", GoBack1FrameWithPlay = "One frame back (with play)", diff --git a/libse/LanguageDeserializer.cs b/libse/LanguageDeserializer.cs index bbc576eba..b19e25c5b 100644 --- a/libse/LanguageDeserializer.cs +++ b/libse/LanguageDeserializer.cs @@ -5485,6 +5485,9 @@ namespace Nikse.SubtitleEdit.Core case "Settings/WaveformToggleSceneChange": language.Settings.WaveformToggleSceneChange = reader.Value; break; + case "Settings/WaveformGuessStart": + language.Settings.WaveformGuessStart = reader.Value; + break; case "Settings/GoBack1Frame": language.Settings.GoBack1Frame = reader.Value; break; diff --git a/libse/LanguageStructure.cs b/libse/LanguageStructure.cs index e9d4d9e54..bdaf1b36f 100644 --- a/libse/LanguageStructure.cs +++ b/libse/LanguageStructure.cs @@ -2186,6 +2186,7 @@ public string WaveformGoToPreviousSceneChange { get; set; } public string WaveformGoToNextSceneChange { get; set; } public string WaveformToggleSceneChange { get; set; } + public string WaveformGuessStart { get; set; } public string GoBack1Frame { get; set; } public string GoForward1Frame { get; set; } public string GoBack1FrameWithPlay { get; set; } diff --git a/libse/Settings.cs b/libse/Settings.cs index b3031a786..7621e0d77 100644 --- a/libse/Settings.cs +++ b/libse/Settings.cs @@ -1557,6 +1557,7 @@ $HorzAlign = Center public string WaveformGoToPreviousSceneChange { get; set; } public string WaveformGoToNextSceneChange { get; set; } public string WaveformToggleSceneChange { get; set; } + public string WaveformGuessStart { get; set; } public string MainTranslateGoogleIt { get; set; } public string MainTranslateGoogleTranslate { get; set; } public string MainTranslateCustomSearch1 { get; set; } @@ -6103,6 +6104,12 @@ $HorzAlign = Center settings.Shortcuts.WaveformToggleSceneChange = subNode.InnerText; } + subNode = node.SelectSingleNode("WaveformGuessStart"); + if (subNode != null) + { + settings.Shortcuts.WaveformGuessStart = subNode.InnerText; + } + subNode = node.SelectSingleNode("MainTranslateGoogleIt"); if (subNode != null) { @@ -7109,6 +7116,7 @@ $HorzAlign = Center textWriter.WriteElementString("WaveformGoToPreviousSceneChange", settings.Shortcuts.WaveformGoToPreviousSceneChange); textWriter.WriteElementString("WaveformGoToNextSceneChange", settings.Shortcuts.WaveformGoToNextSceneChange); textWriter.WriteElementString("WaveformToggleSceneChange", settings.Shortcuts.WaveformToggleSceneChange); + textWriter.WriteElementString("WaveformGuessStart", settings.Shortcuts.WaveformGuessStart); textWriter.WriteElementString("MainTranslateGoogleIt", settings.Shortcuts.MainTranslateGoogleIt); textWriter.WriteElementString("MainTranslateGoogleTranslate", settings.Shortcuts.MainTranslateGoogleTranslate); textWriter.WriteElementString("MainTranslateCustomSearch1", settings.Shortcuts.MainTranslateCustomSearch1); diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index a78cd837e..e52ca0343 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -1926,6 +1926,7 @@ namespace Nikse.SubtitleEdit.Controls return -1; } + /// video position in seconds, -1 if not found public double FindDataBelowThresholdBack(double thresholdPercent, double durationInSeconds) { int begin = SecondsToSampleIndex(_currentVideoPositionSeconds - 1); @@ -1946,7 +1947,7 @@ namespace Nikse.SubtitleEdit.Controls if (hitCount > length) { - double seconds = SampleIndexToSeconds(i + (length / 2)); + double seconds = SampleIndexToSeconds(i + length / 2); if (seconds >= 0) { StartPositionSeconds = seconds; @@ -1968,6 +1969,36 @@ namespace Nikse.SubtitleEdit.Controls return -1; } + /// + /// Seeks silence in volume + /// + /// video position in seconds, -1 if not found + public double FindDataBelowThresholdBackForStart(double thresholdPercent, double durationInSeconds, double startSeconds) + { + var min = SecondsToSampleIndex(startSeconds - 1); + var max = SecondsToSampleIndex(startSeconds + durationInSeconds + 0.05); + int length = SecondsToSampleIndex(durationInSeconds); + var threshold = thresholdPercent / 100.0 * _wavePeaks.HighestPeak; + int hitCount = 0; + for (int i = max; i > min; i--) + { + if (i > 0 && i < _wavePeaks.Peaks.Count && _wavePeaks.Peaks[i].Abs <= threshold) + { + hitCount++; + } + else + { + hitCount = 0; + } + + if (hitCount > length) + { + return Math.Max(0, SampleIndexToSeconds(i + length) - 0.01); + } + } + return -1; + } + public void ZoomIn() { ZoomFactor += 0.1; diff --git a/src/Forms/Main.cs b/src/Forms/Main.cs index 16faae211..2c5ee0f9f 100644 --- a/src/Forms/Main.cs +++ b/src/Forms/Main.cs @@ -14245,6 +14245,11 @@ namespace Nikse.SubtitleEdit.Forms e.SuppressKeyPress = true; } + else if (audioVisualizer.SceneChanges != null && mediaPlayer.IsPaused && e.KeyData == _shortcuts.WaveformGuessStart) + { + AutoGuessStartTime(_subtitleListViewIndex); + e.SuppressKeyPress = true; + } else if (audioVisualizer.Focused && e.KeyCode == Keys.Delete) { ToolStripMenuItemDeleteClick(null, null); @@ -14602,6 +14607,76 @@ namespace Nikse.SubtitleEdit.Forms // put new entries above tabs } + private void AutoGuessStartTime(int index) + { + var silenceLengthInSeconds = 0.11; + var p = _subtitle.GetParagraphOrDefault(index); + for (var startVolume = 8.5; startVolume < 14; startVolume += 0.3) + { + var pos = audioVisualizer.FindDataBelowThresholdBackForStart(startVolume, silenceLengthInSeconds, p.StartTime.TotalSeconds); + var pos2 = audioVisualizer.FindDataBelowThresholdBackForStart(startVolume + 0.3, silenceLengthInSeconds, p.StartTime.TotalSeconds); + if (pos >= 0 && pos > p.StartTime.TotalSeconds - 1) + { + if (pos2 > pos && pos2 >= 0 && pos2 > p.StartTime.TotalSeconds - 1) + { + pos = pos2; + } + + var newStartTimeMs = pos * TimeCode.BaseUnit; + var prev = _subtitle.GetParagraphOrDefault(index - 1); + if (prev != null && prev.EndTime.TotalMilliseconds + Configuration.Settings.General.MinimumMillisecondsBetweenLines >= newStartTimeMs) + { + newStartTimeMs = prev.EndTime.TotalMilliseconds + Configuration.Settings.General.MinimumMillisecondsBetweenLines; + if (newStartTimeMs >= p.StartTime.TotalMilliseconds) + { + break; // cannot move start time + } + } + + // check for scene changes + if (audioVisualizer.SceneChanges != null) + { + var matchingSceneChanges = audioVisualizer.SceneChanges + .Where(sc => sc > p.StartTime.TotalSeconds - 0.3 && sc < p.StartTime.TotalSeconds + 0.2) + .OrderBy(sc => Math.Abs(sc - p.StartTime.TotalSeconds)); + if (matchingSceneChanges.Count() > 0) + { + newStartTimeMs = matchingSceneChanges.First() * TimeCode.BaseUnit; + } + } + + if (Math.Abs(p.StartTime.TotalMilliseconds - newStartTimeMs) < 10) + { + break; // diff too small + } + + var newEndTimeMs = p.EndTime.TotalMilliseconds; + if (newStartTimeMs > p.StartTime.TotalMilliseconds) + { + var temp = new Paragraph(p); + temp.StartTime.TotalMilliseconds = newStartTimeMs; + if (temp.Duration.TotalMilliseconds < Configuration.Settings.General.SubtitleMinimumDisplayMilliseconds || + Utilities.GetCharactersPerSecond(temp) > Configuration.Settings.General.SubtitleMaximumCharactersPerSeconds) + { + var next = _subtitle.GetParagraphOrDefault(index + 1); + if (next == null || + next.StartTime.TotalMilliseconds > newStartTimeMs + p.Duration.TotalMilliseconds + Configuration.Settings.General.MinimumMillisecondsBetweenLines) + { + newEndTimeMs = newStartTimeMs + p.Duration.TotalMilliseconds; + } + } + } + + MakeHistoryForUndo(string.Format(Configuration.Settings.Language.Main.BeforeX, Configuration.Settings.Language.Settings.WaveformGuessStart)); + p.StartTime.TotalMilliseconds = newStartTimeMs; + p.EndTime.TotalMilliseconds = newEndTimeMs; + RefreshSelectedParagraph(); + SubtitleListview1.SetStartTimeAndDuration(index, p, _subtitle.GetParagraphOrDefault(index + 1), _subtitle.GetParagraphOrDefault(index - 1)); + break; + } + } + } + private void GoToBookmark() { using (var form = new BookmarksGoTo(_subtitle)) diff --git a/src/Forms/Settings.cs b/src/Forms/Settings.cs index 3b55d9112..db8bfb9a8 100644 --- a/src/Forms/Settings.cs +++ b/src/Forms/Settings.cs @@ -1270,6 +1270,7 @@ namespace Nikse.SubtitleEdit.Forms AddNode(audioVisualizerNode, language.WaveformGoToPreviousSceneChange, nameof(Configuration.Settings.Shortcuts.WaveformGoToPreviousSceneChange)); AddNode(audioVisualizerNode, language.WaveformGoToNextSceneChange, nameof(Configuration.Settings.Shortcuts.WaveformGoToNextSceneChange)); AddNode(audioVisualizerNode, language.WaveformToggleSceneChange, nameof(Configuration.Settings.Shortcuts.WaveformToggleSceneChange)); + AddNode(audioVisualizerNode, language.WaveformGuessStart, nameof(Configuration.Settings.Shortcuts.WaveformGuessStart)); if (audioVisualizerNode.Nodes.Count > 0) { _shortcuts.Nodes.Add(audioVisualizerNode); diff --git a/src/Logic/MainShortcuts.cs b/src/Logic/MainShortcuts.cs index 8a0b25158..7d7bde778 100644 --- a/src/Logic/MainShortcuts.cs +++ b/src/Logic/MainShortcuts.cs @@ -132,6 +132,7 @@ namespace Nikse.SubtitleEdit.Logic public Keys WaveformGoToPreviousSceneChange { get; set; } public Keys WaveformGoToNextSceneChange { get; set; } public Keys WaveformToggleSceneChange { get; set; } + public Keys WaveformGuessStart { get; set; } public Keys MainTranslateGoogleIt { get; set; } public Keys MainTranslateGoogleTranslate { get; set; } public Keys MainTranslateCustomSearch1 { get; set; } @@ -269,6 +270,7 @@ namespace Nikse.SubtitleEdit.Logic WaveformGoToPreviousSceneChange = UiUtil.GetKeys(Configuration.Settings.Shortcuts.WaveformGoToPreviousSceneChange); WaveformGoToNextSceneChange = UiUtil.GetKeys(Configuration.Settings.Shortcuts.WaveformGoToNextSceneChange); WaveformToggleSceneChange = UiUtil.GetKeys(Configuration.Settings.Shortcuts.WaveformToggleSceneChange); + WaveformGuessStart = UiUtil.GetKeys(Configuration.Settings.Shortcuts.WaveformGuessStart); MainTranslateGoogleIt = UiUtil.GetKeys(Configuration.Settings.Shortcuts.MainTranslateGoogleIt); MainTranslateGoogleTranslate = UiUtil.GetKeys(Configuration.Settings.Shortcuts.MainTranslateGoogleTranslate); MainTranslateCustomSearch1 = UiUtil.GetKeys(Configuration.Settings.Shortcuts.MainTranslateCustomSearch1); diff --git a/src/UpdateLanguageFiles/Program.cs b/src/UpdateLanguageFiles/Program.cs index ce9ddf0f6..a5d66bb86 100644 --- a/src/UpdateLanguageFiles/Program.cs +++ b/src/UpdateLanguageFiles/Program.cs @@ -66,7 +66,15 @@ namespace UpdateLanguageFiles if (oldLanguageDeserializerContent != languageDeserializerContent) { - File.WriteAllText(args[1], languageDeserializerContent, Encoding.UTF8); + try + { + File.WriteAllText(args[1], languageDeserializerContent, Encoding.UTF8); + } + catch + { + System.Threading.Thread.Sleep(100); + File.WriteAllText(args[1], languageDeserializerContent, Encoding.UTF8); + } noOfChanges++; Console.Write(" {0} generated...", Path.GetFileName(args[1])); }