From 40ca729b48f05f3ac4fa32b97dac8333b32c299c Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Sun, 6 Sep 2015 17:13:31 -0400 Subject: [PATCH 1/8] Cleanup WaveformPaint. --- src/Controls/AudioVisualizer.cs | 223 ++++++++++++-------------------- 1 file changed, 80 insertions(+), 143 deletions(-) diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index d37e9221b..dedd0918e 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -126,7 +126,7 @@ namespace Nikse.SubtitleEdit.Controls else if (value > ZoomMaximum) _zoomFactor = ZoomMaximum; else - _zoomFactor = value; + _zoomFactor = (float)Math.Round(value, 2); // round to prevent accumulated rounding errors Invalidate(); } } @@ -416,7 +416,6 @@ namespace Nikse.SubtitleEdit.Controls var selectedParagraphs = new List(); if (_selectedIndices != null) { - var n = _wavePeaks.Header.SampleRate * _zoomFactor; try { foreach (int index in _selectedIndices) @@ -426,8 +425,8 @@ namespace Nikse.SubtitleEdit.Controls { p = new Paragraph(p); // not really frames... just using them as position markers for better performance - p.StartFrame = (int)(p.StartTime.TotalSeconds * n); - p.EndFrame = (int)(p.EndTime.TotalSeconds * n); + p.StartFrame = SecondsToXPositionNoZoom(p.StartTime.TotalSeconds); + p.EndFrame = SecondsToXPositionNoZoom(p.EndTime.TotalSeconds); selectedParagraphs.Add(p); } } @@ -436,20 +435,6 @@ namespace Nikse.SubtitleEdit.Controls { } } - //var otherParagraphs = new List(); - //var nOther = _wavePeaks.Header.SampleRate * _zoomFactor; - //try - //{ - // foreach (Paragraph p in _subtitle.Paragraphs) //int index in _selectedIndices) - // { - // var p2 = new Paragraph(p) { StartFrame = (int)(p.StartTime.TotalSeconds*nOther), EndFrame = (int)(p.EndTime.TotalSeconds*nOther) }; - // // not really frames... just using them as position markers for better performance - // otherParagraphs.Add(p2); - // } - //} - //catch - //{ - //} if (StartPositionSeconds < 0) StartPositionSeconds = 0; @@ -458,25 +443,10 @@ namespace Nikse.SubtitleEdit.Controls StartPositionSeconds = _wavePeaks.Header.LengthInSeconds - ((Width / (double)_wavePeaks.Header.SampleRate) / _zoomFactor); Graphics graphics = e.Graphics; - int begin = SecondsToXPosition(StartPositionSeconds); - var beginNoZoomFactor = (int)Math.Round(StartPositionSeconds * _wavePeaks.Header.SampleRate); // do not use zoom factor here! - int start = -1; - int end = -1; - if (_selectedParagraph != null) - { - start = SecondsToXPosition(_selectedParagraph.StartTime.TotalSeconds); - end = SecondsToXPosition(_selectedParagraph.EndTime.TotalSeconds); - } int imageHeight = Height; - var maxHeight = (int)(Math.Max(Math.Abs(_wavePeaks.DataMinValue), Math.Abs(_wavePeaks.DataMaxValue)) + 0.5); - maxHeight = (int)(maxHeight * VerticalZoomPercent); - if (maxHeight < 0) - maxHeight = 1000; DrawBackground(graphics); - int x = 0; - int y = Height / 2; if (IsSpectrogramAvailable && ShowSpectrogram) { @@ -485,131 +455,94 @@ namespace Nikse.SubtitleEdit.Controls imageHeight -= SpectrogramDisplayHeight; } - // using (var penOther = new Pen(ParagraphColor)) - using (var penNormal = new Pen(Color)) - using (var penSelected = new Pen(SelectedColor)) // selected paragraph + // waveform + if (ShowWaveform) { - var pen = penNormal; - int lastCurrentEnd = -1; - // int lastOtherCurrentEnd = -1; - - if (ShowWaveform) + using (var penNormal = new Pen(Color)) + using (var penSelected = new Pen(SelectedColor)) // selected paragraph { - if (_zoomFactor > 0.9999 && ZoomFactor < 1.00001) + var pen = penNormal; + int maxHeight = (int)(Math.Max(Math.Abs(_wavePeaks.DataMinValue), Math.Abs(_wavePeaks.DataMaxValue)) * VerticalZoomPercent); + int start = SecondsToXPositionNoZoom(StartPositionSeconds); + int selectionStart = _selectedParagraph != null ? SecondsToXPositionNoZoom(_selectedParagraph.StartTime.TotalSeconds) : -1; + int selectionEnd = _selectedParagraph != null ? SecondsToXPositionNoZoom(_selectedParagraph.EndTime.TotalSeconds) : -1; + int lastCurrentEnd = -1; + float xPrev = 0; + int yPrev = Height / 2; + float x = 0; + int y; + for (int i = 0; i < _wavePeaks.AllSamples.Count - start && x < Width; i++) { - for (int i = 0; i < _wavePeaks.AllSamples.Count && i < Width; i++) - { - int n = begin + i; - if (n < _wavePeaks.AllSamples.Count) - { - int newY = CalculateHeight(_wavePeaks.AllSamples[n], imageHeight, maxHeight); - //for (int tempX = x; tempX <= i; tempX++) - // graphics.DrawLine(pen, tempX, y, tempX, newY); - graphics.DrawLine(pen, x, y, i, newY); - //graphics.FillRectangle(new SolidBrush(Color), x, y, 1, 1); // draw pixel instead of line - - x = i; - y = newY; - if (n <= end && n >= start) - pen = penSelected; - else if (IsSelectedIndex(n, ref lastCurrentEnd, selectedParagraphs)) - pen = penSelected; - //else if (IsSelectedIndex(n, ref lastOtherCurrentEnd, otherParagraphs)) - // pen = penOther; - else - pen = penNormal; - } - } + int n = start + i; + x = (float)(_zoomFactor * i); + y = CalculateHeight(_wavePeaks.AllSamples[n], imageHeight, maxHeight); + graphics.DrawLine(pen, xPrev, yPrev, x, y); + xPrev = x; + yPrev = y; + bool isSelected = (n >= selectionStart && n <= selectionEnd) || + IsSelectedIndex(n, ref lastCurrentEnd, selectedParagraphs); + pen = isSelected ? penSelected : penNormal; } - else + } + } + + // time line + DrawTimeLine(StartPositionSeconds, e, imageHeight); + + // scene changes + if (_sceneChanges != null) + { + foreach (double time in _sceneChanges) + { + int pos = SecondsToXPosition(time - StartPositionSeconds); + if (pos > 0 && pos < Width) { - // calculate lines with zoom factor - float x2 = 0; - float x3 = 0; - for (int i = 0; i < _wavePeaks.AllSamples.Count && ((int)Math.Round(x3)) < Width; i++) + using (var p = new Pen(Color.AntiqueWhite)) { - if (beginNoZoomFactor + i < _wavePeaks.AllSamples.Count) - { - int newY = CalculateHeight(_wavePeaks.AllSamples[beginNoZoomFactor + i], imageHeight, maxHeight); - x3 = (float)(_zoomFactor * i); - graphics.DrawLine(pen, x2, y, x3, newY); - x2 = x3; - y = newY; - var n = (int)(begin + x3); - if (n <= end && n >= start) - pen = penSelected; - else if (IsSelectedIndex(n, ref lastCurrentEnd, selectedParagraphs)) - pen = penSelected; - //else if (IsSelectedIndex(n, ref lastOtherCurrentEnd, otherParagraphs)) - // pen = penOther; - else - pen = penNormal; - } + graphics.DrawLine(p, pos, 0, pos, Height); } } } - DrawTimeLine(StartPositionSeconds, e, imageHeight); + } - // scene changes - if (_sceneChanges != null) + // current video position + if (_currentVideoPositionSeconds > 0) + { + int pos = SecondsToXPosition(_currentVideoPositionSeconds - StartPositionSeconds); + if (pos > 0 && pos < Width) { - foreach (var d in _sceneChanges) + using (var p = new Pen(Color.Turquoise)) { - if (d > StartPositionSeconds && d < StartPositionSeconds + 20) - { - int pos = SecondsToXPosition(d) - begin; - if (pos > 0 && pos < Width) - { - using (var p = new Pen(Color.AntiqueWhite)) - { - graphics.DrawLine(p, pos, 0, pos, Height); - } - } - } + graphics.DrawLine(p, pos, 0, pos, Height); } } + } - // current video position - if (_currentVideoPositionSeconds > 0) + // paragraphs + using (var textBrush = new SolidBrush(TextColor)) + { + DrawParagraph(_currentParagraph, e, textBrush); + foreach (Paragraph p in _previousAndNextParagraphs) + DrawParagraph(p, e, textBrush); + } + + // current selection + if (NewSelectionParagraph != null) + { + int currentRegionLeft = SecondsToXPosition(NewSelectionParagraph.StartTime.TotalSeconds - StartPositionSeconds); + int currentRegionRight = SecondsToXPosition(NewSelectionParagraph.EndTime.TotalSeconds - StartPositionSeconds); + int currentRegionWidth = currentRegionRight - currentRegionLeft; + using (var brush = new SolidBrush(Color.FromArgb(128, 255, 255, 255))) { - int videoPosition = SecondsToXPosition(_currentVideoPositionSeconds); - videoPosition -= begin; - if (videoPosition > 0 && videoPosition < Width) + if (currentRegionLeft >= 0 && currentRegionLeft <= Width) { - using (var p = new Pen(Color.Turquoise)) + graphics.FillRectangle(brush, currentRegionLeft, 0, currentRegionWidth, graphics.VisibleClipBounds.Height); + + if (currentRegionWidth > 40) { - graphics.DrawLine(p, videoPosition, 0, videoPosition, Height); - } - } - } - - // mark paragraphs - using (var textBrush = new SolidBrush(TextColor)) - { - DrawParagraph(_currentParagraph, e, begin, textBrush); - foreach (Paragraph p in _previousAndNextParagraphs) - DrawParagraph(p, e, begin, textBrush); - } - - // current selection - if (NewSelectionParagraph != null) - { - int currentRegionLeft = SecondsToXPosition(NewSelectionParagraph.StartTime.TotalSeconds - StartPositionSeconds); - int currentRegionRight = SecondsToXPosition(NewSelectionParagraph.EndTime.TotalSeconds - StartPositionSeconds); - - int currentRegionWidth = currentRegionRight - currentRegionLeft; - using (var brush = new SolidBrush(Color.FromArgb(128, 255, 255, 255))) - { - if (currentRegionLeft >= 0 && currentRegionLeft <= Width) - { - graphics.FillRectangle(brush, currentRegionLeft, 0, currentRegionWidth, graphics.VisibleClipBounds.Height); - - if (currentRegionWidth > 40) + using (var tBrush = new SolidBrush(Color.Turquoise)) { - using (var tBrush = new SolidBrush(Color.Turquoise)) - { - graphics.DrawString(string.Format("{0:0.###} {1}", ((double)currentRegionWidth / _wavePeaks.Header.SampleRate / _zoomFactor), Configuration.Settings.Language.Waveform.Seconds), Font, tBrush, new PointF(currentRegionLeft + 3, Height - 32)); - } + graphics.DrawString(string.Format("{0:0.###} {1}", ((double)currentRegionWidth / _wavePeaks.Header.SampleRate / _zoomFactor), Configuration.Settings.Language.Waveform.Seconds), Font, tBrush, new PointF(currentRegionLeft + 3, Height - 32)); } } } @@ -624,7 +557,6 @@ namespace Nikse.SubtitleEdit.Controls { using (var textFont = new Font(Font.FontFamily, 8)) { - if (Width > 90) { e.Graphics.DrawString(WaveformNotLoadedText, textFont, textBrush, new PointF(Width / 2 - 65, Height / 2 - 10)); @@ -729,15 +661,15 @@ namespace Nikse.SubtitleEdit.Controls return string.Format("{0:00}:{1:00}:{2:00}", ts.Hours, ts.Minutes, ts.Seconds); } - private void DrawParagraph(Paragraph paragraph, PaintEventArgs e, int begin, SolidBrush textBrush) + private void DrawParagraph(Paragraph paragraph, PaintEventArgs e, SolidBrush textBrush) { if (paragraph == null) { return; } - int currentRegionLeft = SecondsToXPosition(paragraph.StartTime.TotalSeconds) - begin; - int currentRegionRight = SecondsToXPosition(paragraph.EndTime.TotalSeconds) - begin; + int currentRegionLeft = SecondsToXPosition(paragraph.StartTime.TotalSeconds - StartPositionSeconds); + int currentRegionRight = SecondsToXPosition(paragraph.EndTime.TotalSeconds - StartPositionSeconds); int currentRegionWidth = currentRegionRight - currentRegionLeft; var drawingStyle = TextBold ? FontStyle.Bold : FontStyle.Regular; using (var brush = new SolidBrush(Color.FromArgb(42, 255, 255, 255))) // back color for paragraphs @@ -831,6 +763,11 @@ namespace Nikse.SubtitleEdit.Controls return (int)Math.Round(seconds * _wavePeaks.Header.SampleRate * _zoomFactor); } + private int SecondsToXPositionNoZoom(double seconds) + { + return (int)Math.Round(seconds * _wavePeaks.Header.SampleRate); + } + private void WaveformMouseDown(object sender, MouseEventArgs e) { if (_wavePeaks == null) From 9e3248a8c0aeb47b2015dcafbd97426d61a3e248 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Sun, 6 Sep 2015 22:01:31 -0400 Subject: [PATCH 2/8] Cleanup WaveformPaint. --- src/Controls/AudioVisualizer.cs | 99 +++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index dedd0918e..8b66adb1f 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; +using System.Linq; using System.Windows.Forms; using System.Xml; @@ -79,7 +80,7 @@ namespace Nikse.SubtitleEdit.Controls private double _currentVideoPositionSeconds = -1; private WavePeakGenerator _wavePeaks; private Subtitle _subtitle; - private ListView.SelectedIndexCollection _selectedIndices; + private IEnumerable _selectedIndices = Enumerable.Empty(); private bool _noClear; private double _gapAtStart = -1; @@ -370,7 +371,7 @@ namespace Nikse.SubtitleEdit.Controls public void SetPosition(double startPositionSeconds, Subtitle subtitle, double currentVideoPositionSeconds, int subtitleIndex, ListView.SelectedIndexCollection selectedIndexes) { StartPositionSeconds = startPositionSeconds; - _selectedIndices = selectedIndexes; + _selectedIndices = selectedIndexes.Cast(); _subtitle.Paragraphs.Clear(); foreach (var p in subtitle.Paragraphs) { @@ -390,52 +391,69 @@ namespace Nikse.SubtitleEdit.Controls return imageHeight - result; } - private bool IsSelectedIndex(int pos, ref int lastCurrentEnd, List selectedParagraphs) + private class IsSelectedHelper { - if (pos < lastCurrentEnd) - return true; + private readonly List _ranges = new List(); + private int _lastPosition = Int32.MaxValue; + private SelectionRange _nextSelection; - if (_selectedIndices == null) - return false; - - foreach (Paragraph p in selectedParagraphs) + public IsSelectedHelper(IList paragraphs, IEnumerable selectedIndices, Paragraph additionalSelectedParagraph, Func secondsToPosition) { - if (pos >= p.StartFrame && pos <= p.EndFrame) // not really frames... + // I'm not sure why there's a try/catch, I copied it from the original code. It + // seems unnecessary but perhaps there's a thread safety issue. + try { - lastCurrentEnd = p.EndFrame; - return true; + foreach (int index in selectedIndices) + AddParagraph(paragraphs[index], secondsToPosition); + } + catch { } + + AddParagraph(additionalSelectedParagraph, secondsToPosition); + } + + public bool IsSelected(int position) + { + if (position < _lastPosition || position > _nextSelection.End) + FindNextSelection(position); + + _lastPosition = position; + + return position >= _nextSelection.Start && position <= _nextSelection.End; + } + + private void FindNextSelection(int position) + { + _nextSelection = new SelectionRange(Int32.MaxValue, Int32.MaxValue); + foreach (SelectionRange range in _ranges) + { + if (range.End >= position && (range.Start < _nextSelection.Start || (range.Start == _nextSelection.Start && range.End > _nextSelection.End))) + _nextSelection = range; + } + } + + private void AddParagraph(Paragraph p, Func secondsToPosition) + { + if (p == null) return; + _ranges.Add(new SelectionRange(secondsToPosition(p.StartTime.TotalSeconds), secondsToPosition(p.EndTime.TotalSeconds))); + } + + private struct SelectionRange + { + public readonly int Start; + public readonly int End; + + public SelectionRange(int start, int end) + { + Start = start; + End = end; } } - return false; } internal void WaveformPaint(object sender, PaintEventArgs e) { if (_wavePeaks != null && _wavePeaks.AllSamples != null) { - var selectedParagraphs = new List(); - if (_selectedIndices != null) - { - try - { - foreach (int index in _selectedIndices) - { - Paragraph p = _subtitle.Paragraphs[index]; - if (p != null) - { - p = new Paragraph(p); - // not really frames... just using them as position markers for better performance - p.StartFrame = SecondsToXPositionNoZoom(p.StartTime.TotalSeconds); - p.EndFrame = SecondsToXPositionNoZoom(p.EndTime.TotalSeconds); - selectedParagraphs.Add(p); - } - } - } - catch - { - } - } - if (StartPositionSeconds < 0) StartPositionSeconds = 0; @@ -443,7 +461,6 @@ namespace Nikse.SubtitleEdit.Controls StartPositionSeconds = _wavePeaks.Header.LengthInSeconds - ((Width / (double)_wavePeaks.Header.SampleRate) / _zoomFactor); Graphics graphics = e.Graphics; - int imageHeight = Height; DrawBackground(graphics); @@ -462,11 +479,9 @@ namespace Nikse.SubtitleEdit.Controls using (var penSelected = new Pen(SelectedColor)) // selected paragraph { var pen = penNormal; + var isSelectedHelper = new IsSelectedHelper(_subtitle.Paragraphs, _selectedIndices, _selectedParagraph, SecondsToXPositionNoZoom); int maxHeight = (int)(Math.Max(Math.Abs(_wavePeaks.DataMinValue), Math.Abs(_wavePeaks.DataMaxValue)) * VerticalZoomPercent); int start = SecondsToXPositionNoZoom(StartPositionSeconds); - int selectionStart = _selectedParagraph != null ? SecondsToXPositionNoZoom(_selectedParagraph.StartTime.TotalSeconds) : -1; - int selectionEnd = _selectedParagraph != null ? SecondsToXPositionNoZoom(_selectedParagraph.EndTime.TotalSeconds) : -1; - int lastCurrentEnd = -1; float xPrev = 0; int yPrev = Height / 2; float x = 0; @@ -479,9 +494,7 @@ namespace Nikse.SubtitleEdit.Controls graphics.DrawLine(pen, xPrev, yPrev, x, y); xPrev = x; yPrev = y; - bool isSelected = (n >= selectionStart && n <= selectionEnd) || - IsSelectedIndex(n, ref lastCurrentEnd, selectedParagraphs); - pen = isSelected ? penSelected : penNormal; + pen = isSelectedHelper.IsSelected(n) ? penSelected : penNormal; } } } From 7dfc5113c483aaa6a937001143d47a5c1744f221 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Sun, 6 Sep 2015 23:05:58 -0400 Subject: [PATCH 3/8] Improve vertical zoom behavior. --- src/Controls/AudioVisualizer.cs | 33 +++++++++++++++++++++++---------- src/Forms/Main.Designer.cs | 2 +- src/Forms/Main.cs | 6 ++---- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index 8b66adb1f..3dabc7d66 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -111,6 +111,7 @@ namespace Nikse.SubtitleEdit.Controls private System.ComponentModel.BackgroundWorker _spectrogramBackgroundWorker; public Keys InsertAtVideoPositionShortcut = Keys.None; public bool MouseWheelScrollUpIsForward = true; + public const double ZoomMinimum = 0.1; public const double ZoomMaximum = 2.5; private double _zoomFactor = 1.0; // 1.0=no zoom @@ -122,12 +123,27 @@ namespace Nikse.SubtitleEdit.Controls } set { - if (value < ZoomMinimum) - _zoomFactor = ZoomMinimum; - else if (value > ZoomMaximum) - _zoomFactor = ZoomMaximum; - else - _zoomFactor = (float)Math.Round(value, 2); // round to prevent accumulated rounding errors + if (value < ZoomMinimum) value = ZoomMinimum; + if (value > ZoomMaximum) value = ZoomMaximum; + _zoomFactor = Math.Round(value, 2); // round to prevent accumulated rounding errors + Invalidate(); + } + } + + public const double VerticalZoomMinimum = 1.0; + public const double VerticalZoomMaximum = 20.0; + private double _verticalZoomFactor = 1.0; // 1.0=no zoom + public double VerticalZoomFactor + { + get + { + return _verticalZoomFactor; + } + set + { + if (value < VerticalZoomMinimum) value = VerticalZoomMinimum; + if (value > VerticalZoomMaximum) value = VerticalZoomMaximum; + _verticalZoomFactor = Math.Round(value, 2); // round to prevent accumulated rounding errors Invalidate(); } } @@ -203,8 +219,6 @@ namespace Nikse.SubtitleEdit.Controls public bool Locked { get; set; } - public double VerticalZoomPercent { get; set; } - public double EndPositionSeconds { get @@ -287,7 +301,6 @@ namespace Nikse.SubtitleEdit.Controls AllowNewSelection = true; ShowSpectrogram = true; ShowWaveform = true; - VerticalZoomPercent = 1.0; InsertAtVideoPositionShortcut = Utilities.GetKeys(Configuration.Settings.Shortcuts.MainWaveformInsertAtCurrentPosition); } @@ -480,7 +493,7 @@ namespace Nikse.SubtitleEdit.Controls { var pen = penNormal; var isSelectedHelper = new IsSelectedHelper(_subtitle.Paragraphs, _selectedIndices, _selectedParagraph, SecondsToXPositionNoZoom); - int maxHeight = (int)(Math.Max(Math.Abs(_wavePeaks.DataMinValue), Math.Abs(_wavePeaks.DataMaxValue)) * VerticalZoomPercent); + int maxHeight = (int)(Math.Max(Math.Abs(_wavePeaks.DataMinValue), Math.Abs(_wavePeaks.DataMaxValue)) / VerticalZoomFactor); int start = SecondsToXPositionNoZoom(StartPositionSeconds); float xPrev = 0; int yPrev = Height / 2; diff --git a/src/Forms/Main.Designer.cs b/src/Forms/Main.Designer.cs index 8c5f279f7..cf90cdc27 100644 --- a/src/Forms/Main.Designer.cs +++ b/src/Forms/Main.Designer.cs @@ -4450,7 +4450,7 @@ this.audioVisualizer.TextBold = true; this.audioVisualizer.TextColor = System.Drawing.Color.Gray; this.audioVisualizer.TextSize = 9F; - this.audioVisualizer.VerticalZoomPercent = 1D; + this.audioVisualizer.VerticalZoomFactor = 1D; this.audioVisualizer.WaveformNotLoadedText = "Click to add waveform"; this.audioVisualizer.WavePeaks = null; this.audioVisualizer.ZoomFactor = 1D; diff --git a/src/Forms/Main.cs b/src/Forms/Main.cs index f77324a2e..a75a202e4 100644 --- a/src/Forms/Main.cs +++ b/src/Forms/Main.cs @@ -10201,14 +10201,12 @@ namespace Nikse.SubtitleEdit.Forms } else if (audioVisualizer != null && audioVisualizer.Visible && e.KeyData == _waveformVerticalZoom) { - if (audioVisualizer.VerticalZoomPercent >= 0.1) - audioVisualizer.VerticalZoomPercent -= 0.05; + audioVisualizer.VerticalZoomFactor *= 1.1; e.SuppressKeyPress = true; } else if (audioVisualizer != null && audioVisualizer.Visible && e.KeyData == _waveformVerticalZoomOut) { - if (audioVisualizer.VerticalZoomPercent < 1) - audioVisualizer.VerticalZoomPercent += 0.05; + audioVisualizer.VerticalZoomFactor /= 1.1; e.SuppressKeyPress = true; } if (audioVisualizer != null && audioVisualizer.Visible && e.KeyData == _waveformZoomIn) From 5e694ba4bb2cdc5f3cb6e8ca70db14b4a3964cf5 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Sun, 6 Sep 2015 23:32:21 -0400 Subject: [PATCH 4/8] Fix crash when using mouse wheel over waveform display if the waveform isn't loaded. --- src/Controls/AudioVisualizer.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index 3dabc7d66..ce3334d6b 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -1711,6 +1711,11 @@ namespace Nikse.SubtitleEdit.Controls private void WaveformMouseWheel(object sender, MouseEventArgs e) { + // The scroll wheel could work in theory without the waveform loaded (it would be + // just like dragging the slider, which does work without the waveform), but the + // code below doesn't support it, so bail out until someone feels like fixing it. + if (_wavePeaks == null) return; + int delta = e.Delta; if (!MouseWheelScrollUpIsForward) delta = delta * -1; From 3a26037ed9f7dc4a246e3586ac324aceff632d78 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Sun, 6 Sep 2015 23:59:16 -0400 Subject: [PATCH 5/8] Small change to match your coding style. --- src/Controls/AudioVisualizer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index ce3334d6b..764972a93 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -407,7 +407,7 @@ namespace Nikse.SubtitleEdit.Controls private class IsSelectedHelper { private readonly List _ranges = new List(); - private int _lastPosition = Int32.MaxValue; + private int _lastPosition = int.MaxValue; private SelectionRange _nextSelection; public IsSelectedHelper(IList paragraphs, IEnumerable selectedIndices, Paragraph additionalSelectedParagraph, Func secondsToPosition) @@ -436,7 +436,7 @@ namespace Nikse.SubtitleEdit.Controls private void FindNextSelection(int position) { - _nextSelection = new SelectionRange(Int32.MaxValue, Int32.MaxValue); + _nextSelection = new SelectionRange(int.MaxValue, int.MaxValue); foreach (SelectionRange range in _ranges) { if (range.End >= position && (range.Start < _nextSelection.Start || (range.Start == _nextSelection.Start && range.End > _nextSelection.End))) From 706f6393ac1e377890d16c50f21dcceb1fb83afe Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Mon, 7 Sep 2015 14:48:55 -0400 Subject: [PATCH 6/8] Requested coding style fixes. --- src/Controls/AudioVisualizer.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index 764972a93..4f77fce8c 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -123,8 +123,10 @@ namespace Nikse.SubtitleEdit.Controls } set { - if (value < ZoomMinimum) value = ZoomMinimum; - if (value > ZoomMaximum) value = ZoomMaximum; + if (value < ZoomMinimum) + value = ZoomMinimum; + if (value > ZoomMaximum) + value = ZoomMaximum; _zoomFactor = Math.Round(value, 2); // round to prevent accumulated rounding errors Invalidate(); } @@ -141,8 +143,10 @@ namespace Nikse.SubtitleEdit.Controls } set { - if (value < VerticalZoomMinimum) value = VerticalZoomMinimum; - if (value > VerticalZoomMaximum) value = VerticalZoomMaximum; + if (value < VerticalZoomMinimum) + value = VerticalZoomMinimum; + if (value > VerticalZoomMaximum) + value = VerticalZoomMaximum; _verticalZoomFactor = Math.Round(value, 2); // round to prevent accumulated rounding errors Invalidate(); } @@ -446,7 +450,9 @@ namespace Nikse.SubtitleEdit.Controls private void AddParagraph(Paragraph p, Func secondsToPosition) { - if (p == null) return; + if (p == null) + return; + _ranges.Add(new SelectionRange(secondsToPosition(p.StartTime.TotalSeconds), secondsToPosition(p.EndTime.TotalSeconds))); } @@ -1714,7 +1720,8 @@ namespace Nikse.SubtitleEdit.Controls // The scroll wheel could work in theory without the waveform loaded (it would be // just like dragging the slider, which does work without the waveform), but the // code below doesn't support it, so bail out until someone feels like fixing it. - if (_wavePeaks == null) return; + if (_wavePeaks == null) + return; int delta = e.Delta; if (!MouseWheelScrollUpIsForward) From e44aea6609cccb474414e10f3db5c237591127a8 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Mon, 7 Sep 2015 18:05:03 -0400 Subject: [PATCH 7/8] Audio visualizer: Fix issues with HH:MM:SS:FF time code format. Audio visualizer: Localize the decimal separator for HH:MM:SS.MS time code format. --- libse/TimeCode.cs | 18 ++++-- src/Controls/AudioVisualizer.cs | 103 ++++++++++++++------------------ 2 files changed, 57 insertions(+), 64 deletions(-) diff --git a/libse/TimeCode.cs b/libse/TimeCode.cs index c70f66e44..dfbe98cf5 100644 --- a/libse/TimeCode.cs +++ b/libse/TimeCode.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using Nikse.SubtitleEdit.Core.SubtitleFormats; namespace Nikse.SubtitleEdit.Core @@ -173,25 +174,32 @@ namespace Nikse.SubtitleEdit.Core } public override string ToString() + { + return ToString(false); + } + + public string ToString(bool localize) { var ts = TimeSpan; - string s = string.Format("{0:00}:{1:00}:{2:00},{3:000}", ts.Hours + ts.Days * 24, ts.Minutes, ts.Seconds, ts.Milliseconds); + string decimalSeparator = localize ? CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator : ","; + string s = string.Format("{0:00}:{1:00}:{2:00}{3}{4:000}", ts.Hours + ts.Days * 24, ts.Minutes, ts.Seconds, decimalSeparator, ts.Milliseconds); if (TotalMilliseconds >= 0) return s; return "-" + s.Replace("-", string.Empty); } - public string ToShortString() + public string ToShortString(bool localize = false) { var ts = TimeSpan; + string decimalSeparator = localize ? CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator : ","; string s; if (ts.Minutes == 0 && ts.Hours == 0 && ts.Days == 0) - s = string.Format("{0:0},{1:000}", ts.Seconds, ts.Milliseconds); + s = string.Format("{0:0}{1}{2:000}", ts.Seconds, decimalSeparator, ts.Milliseconds); else if (ts.Hours == 0 && ts.Days == 0) - s = string.Format("{0:0}:{1:00},{2:000}", ts.Minutes, ts.Seconds, ts.Milliseconds); + s = string.Format("{0:0}:{1:00}{2}{3:000}", ts.Minutes, ts.Seconds, decimalSeparator, ts.Milliseconds); else - s = string.Format("{0:0}:{1:00}:{2:00},{3:000}", ts.Hours + ts.Days * 24, ts.Minutes, ts.Seconds, ts.Milliseconds); + s = string.Format("{0:0}:{1:00}:{2:00}{3}{4:000}", ts.Hours + ts.Days * 24, ts.Minutes, ts.Seconds, decimalSeparator, ts.Milliseconds); if (TotalMilliseconds >= 0) return s; diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index 4f77fce8c..81a2718f1 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -714,73 +714,58 @@ namespace Nikse.SubtitleEdit.Controls pen = new Pen(new SolidBrush(Color.FromArgb(175, 110, 10, 10))) { DashStyle = System.Drawing.Drawing2D.DashStyle.Dash, Width = 2 }; e.Graphics.DrawLine(pen, currentRegionRight - 1, 0, currentRegionRight - 1, e.Graphics.VisibleClipBounds.Height); - var n = _zoomFactor * _wavePeaks.Header.SampleRate; + string durationStr; if (Configuration.Settings != null && Configuration.Settings.General.UseTimeFormatHHMMSSFF) - { - if (n > 80) - { - using (var font = new Font(Font.FontFamily, TextSize, drawingStyle)) - { - e.Graphics.DrawString(paragraph.Text.Replace(Environment.NewLine, " "), font, textBrush, new PointF(currentRegionLeft + 3, 10)); - e.Graphics.DrawString("#" + paragraph.Number + " " + paragraph.StartTime.ToShortStringHHMMSSFF() + " --> " + paragraph.EndTime.ToShortStringHHMMSSFF(), font, textBrush, new PointF(currentRegionLeft + 3, Height - 32)); - } - } - else if (n > 51) - e.Graphics.DrawString("#" + paragraph.Number + " " + paragraph.StartTime.ToShortStringHHMMSSFF(), Font, textBrush, new PointF(currentRegionLeft + 3, Height - 32)); - else if (n > 25) - e.Graphics.DrawString("#" + paragraph.Number, Font, textBrush, new PointF(currentRegionLeft + 3, Height - 32)); - } + durationStr = paragraph.Duration.ToShortStringHHMMSSFF(); else + durationStr = paragraph.Duration.ToShortString(true); + + var n = _zoomFactor * _wavePeaks.Header.SampleRate; + if (n > 80) { - if (n > 80) + using (var font = new Font(Configuration.Settings.General.SubtitleFontName, TextSize, drawingStyle)) + using (var blackBrush = new SolidBrush(Color.Black)) { - using (var font = new Font(Configuration.Settings.General.SubtitleFontName, TextSize, drawingStyle)) + var text = HtmlUtil.RemoveHtmlTags(paragraph.Text, true); + text = text.Replace(Environment.NewLine, " "); + + int actualWidth = (int)e.Graphics.MeasureString(text, font).Width; + bool shortned = false; + while (actualWidth > currentRegionWidth - 12 && text.Length > 1) { - using (var blackBrush = new SolidBrush(Color.Black)) - { - var text = HtmlUtil.RemoveHtmlTags(paragraph.Text, true); - text = text.Replace(Environment.NewLine, " "); - - int w = currentRegionRight - currentRegionLeft; - int actualWidth = (int)e.Graphics.MeasureString(text, font).Width; - bool shortned = false; - while (actualWidth > w - 12 && text.Length > 1) - { - text = text.Remove(text.Length - 1); - actualWidth = (int)e.Graphics.MeasureString(text, font).Width; - shortned = true; - } - if (shortned) - { - text = text.TrimEnd() + "…"; - } - - // poor mans outline + text - e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 3, 11 - 7)); - e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 3, 9 - 7)); - e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 2, 10 - 7)); - e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 4, 10 - 7)); - e.Graphics.DrawString(text, font, textBrush, new PointF(currentRegionLeft + 3, 10 - 7)); - - text = "#" + paragraph.Number + " " + paragraph.Duration.ToShortString(); - actualWidth = (int)e.Graphics.MeasureString(text, font).Width; - if (actualWidth >= w) - text = paragraph.Duration.ToShortString(); - int top = Height - 14 - (int)e.Graphics.MeasureString("#", font).Height; - // poor mans outline + text - e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 3, top + 1)); - e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 3, top - 1)); - e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 2, top)); - e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 4, top)); - e.Graphics.DrawString(text, font, textBrush, new PointF(currentRegionLeft + 3, top)); - } + text = text.Remove(text.Length - 1); + actualWidth = (int)e.Graphics.MeasureString(text, font).Width; + shortned = true; } + if (shortned) + { + text = text.TrimEnd() + "…"; + } + + // poor mans outline + text + e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 3, 11 - 7)); + e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 3, 9 - 7)); + e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 2, 10 - 7)); + e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 4, 10 - 7)); + e.Graphics.DrawString(text, font, textBrush, new PointF(currentRegionLeft + 3, 10 - 7)); + + text = "#" + paragraph.Number + " " + durationStr; + actualWidth = (int)e.Graphics.MeasureString(text, font).Width; + if (actualWidth >= currentRegionWidth) + text = durationStr; + int top = Height - 14 - (int)e.Graphics.MeasureString("#", font).Height; + // poor mans outline + text + e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 3, top + 1)); + e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 3, top - 1)); + e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 2, top)); + e.Graphics.DrawString(text, font, blackBrush, new PointF(currentRegionLeft + 4, top)); + e.Graphics.DrawString(text, font, textBrush, new PointF(currentRegionLeft + 3, top)); } - else if (n > 51) - e.Graphics.DrawString("#" + paragraph.Number + " " + paragraph.StartTime.ToShortString(), Font, textBrush, new PointF(currentRegionLeft + 3, Height - 32)); - else if (n > 25) - e.Graphics.DrawString("#" + paragraph.Number, Font, textBrush, new PointF(currentRegionLeft + 3, Height - 32)); } + else if (n > 51) + e.Graphics.DrawString("#" + paragraph.Number + " " + durationStr, Font, textBrush, new PointF(currentRegionLeft + 3, Height - 32)); + else if (n > 25) + e.Graphics.DrawString("#" + paragraph.Number, Font, textBrush, new PointF(currentRegionLeft + 3, Height - 32)); pen.Dispose(); } } From 41df7363c820fdff3b59645fd4ce04169c6e2c92 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Mon, 7 Sep 2015 18:46:50 -0400 Subject: [PATCH 8/8] Audio visualizer: zoom with control key + scroll wheel. --- src/Controls/AudioVisualizer.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Controls/AudioVisualizer.cs b/src/Controls/AudioVisualizer.cs index 81a2718f1..3f8464249 100644 --- a/src/Controls/AudioVisualizer.cs +++ b/src/Controls/AudioVisualizer.cs @@ -1708,6 +1708,16 @@ namespace Nikse.SubtitleEdit.Controls if (_wavePeaks == null) return; + if (ModifierKeys == Keys.Control) + { + if (e.Delta > 0) + ZoomIn(); + else + ZoomOut(); + + return; + } + int delta = e.Delta; if (!MouseWheelScrollUpIsForward) delta = delta * -1;