From 7fa4accd641068b9219dde7fd19cf216e195488f Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Wed, 2 Sep 2015 23:37:56 -0400 Subject: [PATCH] Optimize pixel drawing. Optimize byte to double conversion. Other small optimizations/cleanups/fixes. --- libse/FastBitmap.cs | 16 +++++++++- libse/WaveToVisualizer.cs | 67 +++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/libse/FastBitmap.cs b/libse/FastBitmap.cs index ee1ac1fab..16fff346d 100644 --- a/libse/FastBitmap.cs +++ b/libse/FastBitmap.cs @@ -8,13 +8,21 @@ namespace Nikse.SubtitleEdit.Core { unsafe public class FastBitmap { - private struct PixelData + public struct PixelData { public byte Blue; public byte Green; public byte Red; public byte Alpha; + public PixelData(Color c) + { + Alpha = c.A; + Red = c.R; + Green = c.G; + Blue = c.B; + } + public override string ToString() { return "(" + Alpha + ", " + Red + ", " + Green + ", " + Blue + ")"; @@ -82,6 +90,12 @@ namespace Nikse.SubtitleEdit.Core data->Blue = color.B; } + public void SetPixel(int x, int y, PixelData color) + { + var data = (PixelData*)(_pBase + y * _width + x * sizeof(PixelData)); + *data = color; + } + public void SetPixel(int x, int y, Color color, int length) { var data = (PixelData*)(_pBase + y * _width + x * sizeof(PixelData)); diff --git a/libse/WaveToVisualizer.cs b/libse/WaveToVisualizer.cs index ec6b236b1..4f2df823c 100644 --- a/libse/WaveToVisualizer.cs +++ b/libse/WaveToVisualizer.cs @@ -227,7 +227,7 @@ namespace Nikse.SubtitleEdit.Core { PeaksPerSecond = peaksPerSecond; - ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataRerader(); + ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataReader(); DataMinValue = int.MaxValue; DataMaxValue = int.MinValue; PeakSamples = new List(); @@ -266,7 +266,7 @@ namespace Nikse.SubtitleEdit.Core public void GenerateAllSamples() { // determine how to read sample values - ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataRerader(); + ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataReader(); // load data _data = new byte[Header.DataChunkSize]; @@ -333,7 +333,9 @@ namespace Nikse.SubtitleEdit.Core private int ReadValue16Bit(ref int index) { - int result = BitConverter.ToInt16(_data, index); + int result = (short)( + (_data[index ] ) | + (_data[index + 1] << 8)); index += 2; return result; } @@ -361,7 +363,7 @@ namespace Nikse.SubtitleEdit.Core /// Determine how to read sample values /// /// Sample data reader that matches bits per sample - private ReadSampleDataValueDelegate GetSampleDataRerader() + private ReadSampleDataValueDelegate GetSampleDataReader() { ReadSampleDataValueDelegate readSampleDataValue; switch (Header.BitsPerSample) @@ -403,8 +405,8 @@ namespace Nikse.SubtitleEdit.Core List bitmaps = new List(); SpectrogramDrawer drawer = new SpectrogramDrawer(nfft); - ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataRerader(); - double sampleScale = 1.0 / Math.Pow(2.0, Header.BitsPerSample - 1); + ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataReader(); + double sampleScale = 1.0 / (Math.Pow(2.0, Header.BitsPerSample - 1) * Header.NumberOfChannels); int delaySampleCount = (int)(Header.SampleRate * (delayInMilliseconds / TimeCode.BaseUnit)); @@ -467,9 +469,6 @@ namespace Nikse.SubtitleEdit.Core { value += readSampleDataValue(ref dataByteOffset); } - value /= Header.NumberOfChannels; - if (value < DataMinValue) DataMinValue = value; - if (value > DataMaxValue) DataMaxValue = value; chunkSamples[chunkSampleOffset] = value * sampleScale; chunkSampleOffset += 1; } @@ -504,10 +503,12 @@ namespace Nikse.SubtitleEdit.Core private class SpectrogramDrawer { + private const double raisedCosineWindowScale = 0.5; + private int _nfft; private MagnitudeToIndexMapper _mapper; private RealFFT _fft; - private Color[] _palette; + private FastBitmap.PixelData[] _palette; private double[] _segment; private double[] _window; private double[] _magnitude; @@ -521,43 +522,39 @@ namespace Nikse.SubtitleEdit.Core _segment = new double[nfft]; _window = CreateRaisedCosineWindow(nfft); _magnitude = new double[nfft / 2]; + + double scaleCorrection = 1.0 / (raisedCosineWindowScale * _fft.ForwardScaleFactor); + for (int i = 0; i < _window.Length; i++) + { + _window[i] *= scaleCorrection; + } } public Bitmap Draw(double[] samples) { - const int overlap = 0; - int numSamples = samples.Length; - int colIncrement = _nfft * (1 - overlap); - - int numcols = numSamples / colIncrement; - // make sure we don't step beyond the end of the recording - while ((numcols - 1) * colIncrement + _nfft > numSamples) - numcols--; - - const double raisedCosineWindowScale = 0.5; - - double scaleCorrection = 1.0 / (raisedCosineWindowScale * _fft.ForwardScaleFactor); - var bmp = new FastBitmap(new Bitmap(numcols, _nfft / 2)); + int width = samples.Length / _nfft; + int height = _nfft / 2; + var bmp = new FastBitmap(new Bitmap(width, height)); bmp.LockImage(); - for (int col = 0; col < numcols; col++) + for (int x = 0; x < width; x++) { // read a segment of the recorded signal - for (int c = 0; c < _nfft; c++) + for (int i = 0; i < _nfft; i++) { - _segment[c] = samples[col * colIncrement + c] * _window[c] * scaleCorrection; + _segment[i] = samples[x * _nfft + i] * _window[i]; } // transform to the frequency domain _fft.ComputeForward(_segment); - // and compute the magnitude spectrum + // compute the magnitude of the spectrum MagnitudeSpectrum(_segment, _magnitude); - // Draw - for (int newY = 0; newY < _nfft / 2 - 1; newY++) + // draw + for (int y = 0; y < height; y++) { - int colorIndex = _mapper.Map(_magnitude[newY]); - bmp.SetPixel(col, (_nfft / 2 - 1) - newY, _palette[colorIndex]); + int colorIndex = _mapper.Map(_magnitude[y]); + bmp.SetPixel(x, height - y - 1, _palette[colorIndex]); } } bmp.UnlockImage(); @@ -585,13 +582,13 @@ namespace Nikse.SubtitleEdit.Core return a * a + b * b; } - private static Color[] GeneratePalette(int nfft) + private static FastBitmap.PixelData[] GeneratePalette(int nfft) { - Color[] palette = new Color[nfft]; + var palette = new FastBitmap.PixelData[nfft]; if (Configuration.Settings.VideoControls.SpectrogramAppearance == "Classic") { for (int colorIndex = 0; colorIndex < nfft; colorIndex++) - palette[colorIndex] = PaletteValue(colorIndex, nfft); + palette[colorIndex] = new FastBitmap.PixelData(PaletteValue(colorIndex, nfft)); } else { @@ -599,7 +596,7 @@ namespace Nikse.SubtitleEdit.Core Configuration.Settings.VideoControls.WaveformColor.G, Configuration.Settings.VideoControls.WaveformColor.B, nfft); for (int i = 0; i < nfft; i++) - palette[i] = list[i]; + palette[i] = new FastBitmap.PixelData(list[i]); } return palette; }