Merge pull request #1274 from jdpurcell/audiovis1

Some more wave visualizer enhancements
This commit is contained in:
Nikolaj Olsson 2015-09-13 20:08:50 +02:00
commit 2af71513f3
7 changed files with 83 additions and 62 deletions

View File

@ -249,9 +249,8 @@ namespace Nikse.SubtitleEdit.Core
/// <summary>
/// Generate peaks (samples with some interval) for an uncompressed wave file
/// </summary>
/// <param name="peaksPerSecond">Sampeles per second / sample rate</param>
/// <param name="delayInMilliseconds">Delay in milliseconds (normally zero)</param>
public void GeneratePeakSamples(int peaksPerSecond, int delayInMilliseconds)
public void GeneratePeakSamples(int delayInMilliseconds)
{
if (Header.BytesPerSample == 4)
{
@ -259,7 +258,11 @@ namespace Nikse.SubtitleEdit.Core
throw new Exception("32-bit samples are unsupported.");
}
PeaksPerSecond = peaksPerSecond;
PeaksPerSecond = Math.Min(Configuration.Settings.VideoControls.WaveformMinimumSampleRate, Header.SampleRate);
// Ensure that peaks per second is a multiple of the sample rate
while (Header.SampleRate % PeaksPerSecond != 0)
PeaksPerSecond++;
ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataReader();
DataMinValue = int.MaxValue;
@ -268,7 +271,7 @@ namespace Nikse.SubtitleEdit.Core
if (delayInMilliseconds > 0)
{
for (int i = 0; i < peaksPerSecond * delayInMilliseconds / 1000; i++)
for (int i = 0; i < PeaksPerSecond * delayInMilliseconds / 1000; i++)
PeakSamples.Add(0);
}
@ -276,9 +279,9 @@ namespace Nikse.SubtitleEdit.Core
_data = new byte[Header.BytesPerSecond];
_stream.Position = Header.DataStartPosition;
int bytesRead = _stream.Read(_data, 0, _data.Length);
while (bytesRead == Header.BytesPerSecond)
while (bytesRead > 0)
{
for (int i = 0; i < Header.BytesPerSecond; i += bytesInterval)
for (int i = 0; i < bytesRead; i += bytesInterval)
{
int index = i;
int value = 0;
@ -286,7 +289,7 @@ namespace Nikse.SubtitleEdit.Core
{
value += readSampleDataValue.Invoke(ref index);
}
value = value / Header.NumberOfChannels;
value /= Header.NumberOfChannels;
if (value < DataMinValue)
DataMinValue = value;
if (value > DataMaxValue)
@ -318,14 +321,14 @@ namespace Nikse.SubtitleEdit.Core
DataMaxValue = int.MinValue;
AllSamples = new List<int>();
int index = 0;
while (index + Header.NumberOfChannels < Header.DataChunkSize)
while (index < Header.DataChunkSize)
{
int value = 0;
for (int channelNumber = 0; channelNumber < Header.NumberOfChannels; channelNumber++)
{
value += readSampleDataValue.Invoke(ref index);
}
value = value / Header.NumberOfChannels;
value /= Header.NumberOfChannels;
if (value < DataMinValue)
DataMinValue = value;
if (value > DataMaxValue)
@ -508,6 +511,8 @@ namespace Nikse.SubtitleEdit.Core
int chunkCount = (int)Math.Ceiling((double)(fileSampleCount + delaySampleCount) / chunkSampleCount);
double[] chunkSamples = new double[chunkSampleCount];
Directory.CreateDirectory(spectrogramDirectory);
_data = new byte[chunkSampleCount * Header.BlockAlign];
_stream.Seek(Header.DataStartPosition, SeekOrigin.Begin);
@ -604,8 +609,8 @@ namespace Nikse.SubtitleEdit.Core
private class SpectrogramDrawer
{
private const double raisedCosineWindowScale = 0.5;
private const int magnitudeIndexRange = 256;
private const double RaisedCosineWindowScale = 0.5;
private const int MagnitudeIndexRange = 256;
private readonly int _nfft;
private readonly MagnitudeToIndexMapper _mapper;
@ -619,7 +624,7 @@ namespace Nikse.SubtitleEdit.Core
public SpectrogramDrawer(int nfft)
{
_nfft = nfft;
_mapper = new MagnitudeToIndexMapper(100.0, magnitudeIndexRange - 1);
_mapper = new MagnitudeToIndexMapper(100.0, MagnitudeIndexRange - 1);
_fft = new RealFFT(nfft);
_palette = GeneratePalette();
_segment = new double[nfft];
@ -627,7 +632,7 @@ namespace Nikse.SubtitleEdit.Core
_magnitude1 = new double[nfft / 2];
_magnitude2 = new double[nfft / 2];
double scaleCorrection = 1.0 / (raisedCosineWindowScale * _fft.ForwardScaleFactor);
double scaleCorrection = 1.0 / (RaisedCosineWindowScale * _fft.ForwardScaleFactor);
for (int i = 0; i < _window.Length; i++)
{
_window[i] *= scaleCorrection;
@ -694,18 +699,18 @@ namespace Nikse.SubtitleEdit.Core
private static FastBitmap.PixelData[] GeneratePalette()
{
var palette = new FastBitmap.PixelData[magnitudeIndexRange];
var palette = new FastBitmap.PixelData[MagnitudeIndexRange];
if (Configuration.Settings.VideoControls.SpectrogramAppearance == "Classic")
{
for (int colorIndex = 0; colorIndex < magnitudeIndexRange; colorIndex++)
palette[colorIndex] = new FastBitmap.PixelData(PaletteValue(colorIndex, magnitudeIndexRange));
for (int colorIndex = 0; colorIndex < MagnitudeIndexRange; colorIndex++)
palette[colorIndex] = new FastBitmap.PixelData(PaletteValue(colorIndex, MagnitudeIndexRange));
}
else
{
var list = SmoothColors(0, 0, 0, Configuration.Settings.VideoControls.WaveformColor.R,
Configuration.Settings.VideoControls.WaveformColor.G,
Configuration.Settings.VideoControls.WaveformColor.B, magnitudeIndexRange);
for (int i = 0; i < magnitudeIndexRange; i++)
Configuration.Settings.VideoControls.WaveformColor.B, MagnitudeIndexRange);
for (int i = 0; i < MagnitudeIndexRange; i++)
palette[i] = new FastBitmap.PixelData(list[i]);
}
return palette;

View File

@ -133,8 +133,8 @@ namespace Nikse.SubtitleEdit.Controls
}
public const double VerticalZoomMinimum = 1.0;
public const double VerticalZoomMaximum = 20.0;
private double _verticalZoomFactor = 1.0; // 1.0=no zoom
public const double VerticalZoomMaximum = 40.0;
private double _verticalZoomFactor = 2.0; // 1.0=no zoom
public double VerticalZoomFactor
{
get
@ -404,7 +404,7 @@ namespace Nikse.SubtitleEdit.Controls
private static int CalculateHeight(double value, int imageHeight, int maxHeight)
{
double percentage = value / maxHeight;
var result = (int)Math.Round((percentage * imageHeight) + (imageHeight / 2.0));
var result = (int)Math.Round((percentage / 2.0 + 0.5) * imageHeight);
return imageHeight - result;
}
@ -1690,18 +1690,28 @@ namespace Nikse.SubtitleEdit.Controls
public void ZoomIn()
{
ZoomFactor = ZoomFactor + 0.1;
ZoomFactor += 0.1;
if (OnZoomedChanged != null)
OnZoomedChanged.Invoke(this, null);
}
public void ZoomOut()
{
ZoomFactor = ZoomFactor - 0.1;
ZoomFactor -= 0.1;
if (OnZoomedChanged != null)
OnZoomedChanged.Invoke(this, null);
}
private void VerticalZoomIn()
{
VerticalZoomFactor *= 1.1;
}
private void VerticalZoomOut()
{
VerticalZoomFactor /= 1.1;
}
private void WaveformMouseWheel(object sender, MouseEventArgs e)
{
// The scroll wheel could work in theory without the waveform loaded (it would be
@ -1720,6 +1730,16 @@ namespace Nikse.SubtitleEdit.Controls
return;
}
if (ModifierKeys == (Keys.Control | Keys.Shift))
{
if (e.Delta > 0)
VerticalZoomIn();
else
VerticalZoomOut();
return;
}
int delta = e.Delta;
if (!MouseWheelScrollUpIsForward)
delta = delta * -1;

View File

@ -138,8 +138,8 @@
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Generate waveform data";
this.Shown += new System.EventHandler(this.AddWareForm_Shown);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.AddWareForm_KeyDown);
this.Shown += new System.EventHandler(this.AddWaveform_Shown);
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.AddWaveform_KeyDown);
this.ResumeLayout(false);
this.PerformLayout();

View File

@ -234,28 +234,24 @@ namespace Nikse.SubtitleEdit.Forms
labelProgress.Text = Configuration.Settings.Language.AddWaveform.GeneratingPeakFile;
Refresh();
var waveFile = new WavePeakGenerator(targetFile);
int sampleRate = Configuration.Settings.VideoControls.WaveformMinimumSampleRate; // Normally 128
while (waveFile.Header.SampleRate % sampleRate != 0 && sampleRate < 5000)
sampleRate++; // old sample-rate / new sample-rate must have rest = 0
waveFile.GeneratePeakSamples(sampleRate, delayInMilliseconds); // samples per second - SampleRate
if (Configuration.Settings.VideoControls.GenerateSpectrogram)
using (var waveFile = new WavePeakGenerator(targetFile))
{
labelProgress.Text = Configuration.Settings.Language.AddWaveform.GeneratingSpectrogram;
Refresh();
Directory.CreateDirectory(_spectrogramDirectory);
SpectrogramBitmaps = waveFile.GenerateFourierData(256, _spectrogramDirectory, delayInMilliseconds); // image height = nfft / 2
waveFile.GeneratePeakSamples(delayInMilliseconds);
if (Configuration.Settings.VideoControls.GenerateSpectrogram)
{
labelProgress.Text = Configuration.Settings.Language.AddWaveform.GeneratingSpectrogram;
Refresh();
SpectrogramBitmaps = waveFile.GenerateFourierData(256, _spectrogramDirectory, delayInMilliseconds); // image height = nfft / 2
}
WavePeak = waveFile;
}
WavePeak = waveFile;
waveFile.Close();
labelPleaseWait.Visible = false;
}
private void AddWareForm_Shown(object sender, EventArgs e)
private void AddWaveform_Shown(object sender, EventArgs e)
{
Refresh();
var audioTrackNames = new List<string>();
@ -367,7 +363,7 @@ namespace Nikse.SubtitleEdit.Forms
}
}
private void AddWareForm_KeyDown(object sender, KeyEventArgs e)
private void AddWaveform_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
DialogResult = DialogResult.Cancel;

View File

@ -211,7 +211,12 @@ namespace Nikse.SubtitleEdit.Forms
while (index < listViewInputFiles.Items.Count && _abort == false)
{
var item = listViewInputFiles.Items[index];
item.SubItems[3].Text = Configuration.Settings.Language.AddWaveformBatch.ExtractingAudio;
Action<string> updateStatus = status =>
{
item.SubItems[3].Text = status;
Refresh();
};
updateStatus(Configuration.Settings.Language.AddWaveformBatch.ExtractingAudio);
string fileName = item.Text;
try
{
@ -285,7 +290,7 @@ namespace Nikse.SubtitleEdit.Forms
}
}
item.SubItems[3].Text = Configuration.Settings.Language.AddWaveformBatch.Calculating;
updateStatus(Configuration.Settings.Language.AddWaveformBatch.Calculating);
MakeWaveformAndSpectrogram(fileName, targetFile, _delayInMilliseconds);
// cleanup
@ -300,13 +305,13 @@ namespace Nikse.SubtitleEdit.Forms
IncrementAndShowProgress();
item.SubItems[3].Text = Configuration.Settings.Language.AddWaveformBatch.Done;
updateStatus(Configuration.Settings.Language.AddWaveformBatch.Done);
}
catch
{
IncrementAndShowProgress();
item.SubItems[3].Text = Configuration.Settings.Language.AddWaveformBatch.Error;
updateStatus(Configuration.Settings.Language.AddWaveformBatch.Error);
}
index++;
}
@ -322,21 +327,16 @@ namespace Nikse.SubtitleEdit.Forms
private void MakeWaveformAndSpectrogram(string videoFileName, string targetFile, int delayInMilliseconds)
{
var waveFile = new WavePeakGenerator(targetFile);
using (var waveFile = new WavePeakGenerator(targetFile))
{
waveFile.GeneratePeakSamples(delayInMilliseconds);
waveFile.WritePeakSamples(Main.GetPeakWaveFileName(videoFileName));
int sampleRate = Configuration.Settings.VideoControls.WaveformMinimumSampleRate; // Normally 128
while (waveFile.Header.SampleRate % sampleRate != 0 && sampleRate < 5000)
sampleRate++; // old sample-rate / new sample-rate must have rest = 0
waveFile.GeneratePeakSamples(sampleRate, delayInMilliseconds); // samples per second - SampleRate
//if (Configuration.Settings.VideoControls.GenerateSpectrogram)
//{
// //Directory.CreateDirectory(_spectrogramDirectory);
// //SpectrogramBitmaps = waveFile.GenerateFourierData(256, _spectrogramDirectory, delayInMilliseconds); // image height = nfft / 2
//}
waveFile.WritePeakSamples(Main.GetPeakWaveFileName(videoFileName));
waveFile.Close();
if (Configuration.Settings.VideoControls.GenerateSpectrogram)
{
waveFile.GenerateFourierData(256, Main.GetSpectrogramFolder(videoFileName), delayInMilliseconds); // image height = nfft / 2
}
}
}
private void IncrementAndShowProgress()

View File

@ -4450,7 +4450,7 @@
this.audioVisualizer.TextBold = true;
this.audioVisualizer.TextColor = System.Drawing.Color.Gray;
this.audioVisualizer.TextSize = 9F;
this.audioVisualizer.VerticalZoomFactor = 1D;
this.audioVisualizer.VerticalZoomFactor = 2D;
this.audioVisualizer.WaveformNotLoadedText = "Click to add waveform";
this.audioVisualizer.WavePeaks = null;
this.audioVisualizer.ZoomFactor = 1D;

View File

@ -14803,7 +14803,7 @@ namespace Nikse.SubtitleEdit.Forms
return wavePeakName;
}
private static string GetSpectrogramFolder(string videoFileName)
public static string GetSpectrogramFolder(string videoFileName)
{
var dir = Configuration.SpectrogramsFolder.TrimEnd(Path.DirectorySeparatorChar);
if (!Directory.Exists(dir))