mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-10-27 22:42:38 +01:00
Display actual wave peaks instead of a sampled waveform.
This commit is contained in:
parent
4dc2760da8
commit
ea5e748072
@ -141,12 +141,13 @@ namespace Nikse.SubtitleEdit.Core
|
||||
}
|
||||
}
|
||||
|
||||
internal static void WriteHeader(Stream toStream, int sampleRate, int numberOfChannels, int bitsPerSample, int dataSize)
|
||||
internal static void WriteHeader(Stream toStream, int sampleRate, int numberOfChannels, int bitsPerSample, int sampleCount)
|
||||
{
|
||||
const int headerSize = 44;
|
||||
int bytesPerSample = (bitsPerSample + 7) / 8;
|
||||
int blockAlign = numberOfChannels * bytesPerSample;
|
||||
int byteRate = sampleRate * blockAlign;
|
||||
int dataSize = sampleCount * bytesPerSample * numberOfChannels;
|
||||
byte[] header = new byte[headerSize];
|
||||
WriteStringToByteArray(header, 0, "RIFF");
|
||||
WriteInt32ToByteArray(header, 4, headerSize + dataSize - 8); // size of RIFF chunk's data
|
||||
@ -183,47 +184,69 @@ namespace Nikse.SubtitleEdit.Core
|
||||
}
|
||||
}
|
||||
|
||||
public struct WavePeak
|
||||
{
|
||||
public readonly short Max;
|
||||
public readonly short Min;
|
||||
|
||||
public WavePeak(short max, short min)
|
||||
{
|
||||
Max = max;
|
||||
Min = min;
|
||||
}
|
||||
|
||||
public int Abs
|
||||
{
|
||||
get { return Math.Max(Math.Abs((int)Max), Math.Abs((int)Min)); }
|
||||
}
|
||||
}
|
||||
|
||||
public class WavePeakData
|
||||
{
|
||||
public WavePeakData(int sampleRate, IList<WavePeak> peaks)
|
||||
{
|
||||
SampleRate = sampleRate;
|
||||
LengthInSeconds = (double)peaks.Count / sampleRate;
|
||||
Peaks = peaks;
|
||||
CalculateHighestPeak();
|
||||
}
|
||||
|
||||
public int SampleRate { get; private set; }
|
||||
|
||||
public double LengthInSeconds { get; private set; }
|
||||
|
||||
public IList<WavePeak> Peaks { get; private set; }
|
||||
|
||||
public int HighestPeak { get; private set; }
|
||||
|
||||
private void CalculateHighestPeak()
|
||||
{
|
||||
HighestPeak = 0;
|
||||
foreach (var peak in Peaks)
|
||||
{
|
||||
int abs = peak.Abs;
|
||||
if (abs > HighestPeak)
|
||||
HighestPeak = abs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class WavePeakGenerator : IDisposable
|
||||
{
|
||||
private Stream _stream;
|
||||
private byte[] _data;
|
||||
private WaveHeader _header;
|
||||
|
||||
private delegate int ReadSampleDataValueDelegate(ref int index);
|
||||
private delegate int ReadSampleDataValue(byte[] data, ref int index);
|
||||
|
||||
public WaveHeader Header { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Lowest data value
|
||||
/// </summary>
|
||||
public int DataMinValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Highest data value
|
||||
/// </summary>
|
||||
public int DataMaxValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of peaks per second (should be divideable by SampleRate)
|
||||
/// </summary>
|
||||
public int PeaksPerSecond { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of all peak samples (channels are merged)
|
||||
/// </summary>
|
||||
public List<int> PeakSamples { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of all samples (channels are merged)
|
||||
/// </summary>
|
||||
public List<int> AllSamples { get; private set; }
|
||||
private delegate void WriteSampleDataValue(byte[] buffer, int offset, int value);
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="fileName">Wave file name</param>
|
||||
public WavePeakGenerator(string fileName)
|
||||
: this(File.OpenRead(fileName))
|
||||
{
|
||||
Initialize(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -232,214 +255,250 @@ namespace Nikse.SubtitleEdit.Core
|
||||
/// <param name="stream">Stream of a wave file</param>
|
||||
public WavePeakGenerator(Stream stream)
|
||||
{
|
||||
Initialize(stream);
|
||||
_stream = stream;
|
||||
_header = new WaveHeader(_stream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the current wave file can be processed. Compressed wave files or 32-bit files are not supported
|
||||
/// Returns true if the current wave file can be processed. Compressed wave files are not supported.
|
||||
/// </summary>
|
||||
public bool IsSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return Header.AudioFormat == WaveHeader.AudioFormatPcm && Header.Format == "WAVE" && Header.BytesPerSample < 4;
|
||||
return _header.AudioFormat == WaveHeader.AudioFormatPcm && _header.Format == "WAVE";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate peaks (samples with some interval) for an uncompressed wave file
|
||||
/// Generates peaks and saves them to disk.
|
||||
/// </summary>
|
||||
/// <param name="delayInMilliseconds">Delay in milliseconds (normally zero)</param>
|
||||
public void GeneratePeakSamples(int delayInMilliseconds)
|
||||
/// <param name="peakFileName">Path of the output file</param>
|
||||
public void GeneratePeaks(int delayInMilliseconds, string peakFileName)
|
||||
{
|
||||
if (Header.BytesPerSample == 4)
|
||||
int peaksPerSecond = Math.Min(Configuration.Settings.VideoControls.WaveformMinimumSampleRate, _header.SampleRate);
|
||||
|
||||
// ensure that peaks per second is a factor of the sample rate
|
||||
while (_header.SampleRate % peaksPerSecond != 0)
|
||||
peaksPerSecond++;
|
||||
|
||||
int delaySampleCount = (int)(_header.SampleRate * (delayInMilliseconds / TimeCode.BaseUnit));
|
||||
|
||||
// ignore negative delays for now (pretty sure it can't happen in mkv and some places pass in -1 by mistake)
|
||||
delaySampleCount = Math.Max(delaySampleCount, 0);
|
||||
|
||||
var peaks = new List<WavePeak>();
|
||||
var readSampleDataValue = GetSampleDataReader();
|
||||
float sampleAndChannelScale = GetSampleAndChannelScale();
|
||||
long fileSampleCount = _header.LengthInSamples;
|
||||
long fileSampleOffset = -delaySampleCount;
|
||||
int chunkSampleCount = _header.SampleRate / peaksPerSecond;
|
||||
byte[] data = new byte[chunkSampleCount * _header.BlockAlign];
|
||||
float[] chunkSamples = new float[chunkSampleCount];
|
||||
|
||||
_stream.Seek(_header.DataStartPosition, SeekOrigin.Begin);
|
||||
|
||||
// for negative delays, skip samples at the beginning
|
||||
if (fileSampleOffset > 0)
|
||||
{
|
||||
// Can't handle 32-bit samples due to the way the channel averaging is done
|
||||
throw new Exception("32-bit samples are unsupported.");
|
||||
_stream.Seek(fileSampleOffset * _header.BlockAlign, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
PeaksPerSecond = Math.Min(Configuration.Settings.VideoControls.WaveformMinimumSampleRate, Header.SampleRate);
|
||||
|
||||
// Ensure that peaks per second is a factor of the sample rate
|
||||
while (Header.SampleRate % PeaksPerSecond != 0)
|
||||
PeaksPerSecond++;
|
||||
|
||||
ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataReader();
|
||||
DataMinValue = int.MaxValue;
|
||||
DataMaxValue = int.MinValue;
|
||||
PeakSamples = new List<int>();
|
||||
|
||||
if (delayInMilliseconds > 0)
|
||||
while (fileSampleOffset < fileSampleCount)
|
||||
{
|
||||
for (int i = 0; i < PeaksPerSecond * delayInMilliseconds / 1000; i++)
|
||||
PeakSamples.Add(0);
|
||||
}
|
||||
|
||||
int bytesInterval = (int)Header.BytesPerSecond / PeaksPerSecond;
|
||||
_data = new byte[Header.BytesPerSecond];
|
||||
_stream.Position = Header.DataStartPosition;
|
||||
int bytesRead = _stream.Read(_data, 0, _data.Length);
|
||||
while (bytesRead > 0)
|
||||
{
|
||||
for (int i = 0; i < bytesRead; i += bytesInterval)
|
||||
// calculate how many samples to skip at the beginning (for positive delays)
|
||||
int startSkipSampleCount = 0;
|
||||
if (fileSampleOffset < 0)
|
||||
{
|
||||
int index = i;
|
||||
int value = 0;
|
||||
for (int channelNumber = 0; channelNumber < Header.NumberOfChannels; channelNumber++)
|
||||
{
|
||||
value += readSampleDataValue.Invoke(ref index);
|
||||
}
|
||||
value /= Header.NumberOfChannels;
|
||||
if (value < DataMinValue)
|
||||
DataMinValue = value;
|
||||
if (value > DataMaxValue)
|
||||
DataMaxValue = value;
|
||||
PeakSamples.Add(value);
|
||||
startSkipSampleCount = (int)Math.Min(-fileSampleOffset, chunkSampleCount);
|
||||
fileSampleOffset += startSkipSampleCount;
|
||||
}
|
||||
|
||||
// calculate how many samples to read from the file
|
||||
long fileSamplesRemaining = fileSampleCount - Math.Max(fileSampleOffset, 0);
|
||||
int fileReadSampleCount = (int)Math.Min(fileSamplesRemaining, chunkSampleCount - startSkipSampleCount);
|
||||
|
||||
// read samples from the file
|
||||
if (fileReadSampleCount > 0)
|
||||
{
|
||||
int fileReadByteCount = fileReadSampleCount * _header.BlockAlign;
|
||||
_stream.Read(data, 0, fileReadByteCount);
|
||||
fileSampleOffset += fileReadSampleCount;
|
||||
|
||||
int chunkSampleOffset = 0;
|
||||
int dataByteOffset = 0;
|
||||
while (dataByteOffset < fileReadByteCount)
|
||||
{
|
||||
float value = 0F;
|
||||
for (int iChannel = 0; iChannel < _header.NumberOfChannels; iChannel++)
|
||||
{
|
||||
value += readSampleDataValue(data, ref dataByteOffset);
|
||||
}
|
||||
chunkSamples[chunkSampleOffset] = value * sampleAndChannelScale;
|
||||
chunkSampleOffset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate peaks
|
||||
peaks.Add(CalculatePeak(chunkSamples, fileReadSampleCount));
|
||||
}
|
||||
|
||||
// save results to file
|
||||
using (var stream = File.Create(peakFileName))
|
||||
{
|
||||
WaveHeader.WriteHeader(stream, peaksPerSecond, 2, 16, peaks.Count);
|
||||
byte[] buffer = new byte[4];
|
||||
foreach (var peak in peaks)
|
||||
{
|
||||
WriteValue16Bit(buffer, 0, peak.Max);
|
||||
WriteValue16Bit(buffer, 2, peak.Min);
|
||||
stream.Write(buffer, 0, 4);
|
||||
}
|
||||
bytesRead = _stream.Read(_data, 0, _data.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateAllSamples()
|
||||
private static WavePeak CalculatePeak(float[] chunk, int count)
|
||||
{
|
||||
if (Header.BytesPerSample == 4)
|
||||
{
|
||||
// Can't handle 32-bit samples due to the way the channel averaging is done
|
||||
throw new Exception("32-bit samples are unsupported.");
|
||||
}
|
||||
if (count == 0)
|
||||
return new WavePeak();
|
||||
|
||||
// determine how to read sample values
|
||||
ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataReader();
|
||||
float max = chunk[0];
|
||||
float min = chunk[0];
|
||||
for (int i = 1; i < count; i++)
|
||||
{
|
||||
float value = chunk[i];
|
||||
if (value > max)
|
||||
max = value;
|
||||
if (value < min)
|
||||
min = value;
|
||||
}
|
||||
return new WavePeak((short)(short.MaxValue * max), (short)(short.MaxValue * min));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads previously generated peaks from disk.
|
||||
/// </summary>
|
||||
public WavePeakData LoadPeaks()
|
||||
{
|
||||
if (_header.BitsPerSample != 16)
|
||||
throw new Exception("Peaks file must be 16 bits per sample.");
|
||||
|
||||
if (_header.NumberOfChannels != 1 && _header.NumberOfChannels != 2)
|
||||
throw new Exception("Peaks file must have 1 or 2 channels.");
|
||||
|
||||
// load data
|
||||
_data = new byte[Header.DataChunkSize];
|
||||
_stream.Position = Header.DataStartPosition;
|
||||
_stream.Read(_data, 0, _data.Length);
|
||||
byte[] data = new byte[_header.DataChunkSize];
|
||||
_stream.Position = _header.DataStartPosition;
|
||||
_stream.Read(data, 0, data.Length);
|
||||
|
||||
// read sample values
|
||||
DataMinValue = int.MaxValue;
|
||||
DataMaxValue = int.MinValue;
|
||||
AllSamples = new List<int>();
|
||||
int index = 0;
|
||||
while (index < Header.DataChunkSize)
|
||||
// read peak values
|
||||
WavePeak[] peaks = new WavePeak[_header.LengthInSamples];
|
||||
int peakIndex = 0;
|
||||
if (_header.NumberOfChannels == 2)
|
||||
{
|
||||
int value = 0;
|
||||
for (int channelNumber = 0; channelNumber < Header.NumberOfChannels; channelNumber++)
|
||||
// max value in left channel, min value in right channel
|
||||
int byteIndex = 0;
|
||||
while (byteIndex < data.Length)
|
||||
{
|
||||
value += readSampleDataValue.Invoke(ref index);
|
||||
short max = (short)ReadValue16Bit(data, ref byteIndex);
|
||||
short min = (short)ReadValue16Bit(data, ref byteIndex);
|
||||
peaks[peakIndex++] = new WavePeak(max, min);
|
||||
}
|
||||
value /= Header.NumberOfChannels;
|
||||
if (value < DataMinValue)
|
||||
DataMinValue = value;
|
||||
if (value > DataMaxValue)
|
||||
DataMaxValue = value;
|
||||
AllSamples.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void WritePeakSamples(string fileName)
|
||||
{
|
||||
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
|
||||
else if (_header.NumberOfChannels == 1)
|
||||
{
|
||||
WritePeakSamples(fs);
|
||||
// single sample value (for backwards compatibility)
|
||||
int byteIndex = 0;
|
||||
while (byteIndex < data.Length)
|
||||
{
|
||||
short value = (short)ReadValue16Bit(data, ref byteIndex);
|
||||
if (value == short.MinValue)
|
||||
value = -short.MaxValue;
|
||||
value = Math.Abs(value);
|
||||
peaks[peakIndex++] = new WavePeak(value, (short)-value);
|
||||
}
|
||||
}
|
||||
|
||||
return new WavePeakData(_header.SampleRate, peaks);
|
||||
}
|
||||
|
||||
public void WritePeakSamples(Stream stream)
|
||||
private static int ReadValue8Bit(byte[] data, ref int index)
|
||||
{
|
||||
WaveHeader.WriteHeader(stream, PeaksPerSecond, 1, Header.BytesPerSample * 8, PeakSamples.Count * Header.BytesPerSample);
|
||||
WritePeakData(stream);
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
private void WritePeakData(Stream stream)
|
||||
{
|
||||
var writeSample = GetSampleDataWriter();
|
||||
byte[] buffer = new byte[4];
|
||||
int bytesPerSample = Header.BytesPerSample;
|
||||
foreach (var value in PeakSamples)
|
||||
{
|
||||
writeSample(buffer, value);
|
||||
stream.Write(buffer, 0, bytesPerSample);
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize(Stream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
Header = new WaveHeader(_stream);
|
||||
}
|
||||
|
||||
private int ReadValue8Bit(ref int index)
|
||||
{
|
||||
int result = sbyte.MinValue + _data[index];
|
||||
int result = sbyte.MinValue + data[index];
|
||||
index += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
private int ReadValue16Bit(ref int index)
|
||||
private static int ReadValue16Bit(byte[] data, ref int index)
|
||||
{
|
||||
int result = (short)
|
||||
((_data[index ] ) |
|
||||
(_data[index + 1] << 8));
|
||||
((data[index ] ) |
|
||||
(data[index + 1] << 8));
|
||||
index += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
private int ReadValue24Bit(ref int index)
|
||||
private static int ReadValue24Bit(byte[] data, ref int index)
|
||||
{
|
||||
int result =
|
||||
((_data[index ] << 8) |
|
||||
(_data[index + 1] << 16) |
|
||||
(_data[index + 2] << 24)) >> 8;
|
||||
((data[index ] << 8) |
|
||||
(data[index + 1] << 16) |
|
||||
(data[index + 2] << 24)) >> 8;
|
||||
index += 3;
|
||||
return result;
|
||||
}
|
||||
|
||||
private int ReadValue32Bit(ref int index)
|
||||
private static int ReadValue32Bit(byte[] data, ref int index)
|
||||
{
|
||||
int result =
|
||||
(_data[index ] ) |
|
||||
(_data[index + 1] << 8) |
|
||||
(_data[index + 2] << 16) |
|
||||
(_data[index + 3] << 24);
|
||||
(data[index ] ) |
|
||||
(data[index + 1] << 8) |
|
||||
(data[index + 2] << 16) |
|
||||
(data[index + 3] << 24);
|
||||
index += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
private void WriteValue8Bit(byte[] buffer, int value)
|
||||
private static void WriteValue8Bit(byte[] buffer, int offset, int value)
|
||||
{
|
||||
buffer[0] = (byte)(value - sbyte.MinValue);
|
||||
buffer[offset] = (byte)(value - sbyte.MinValue);
|
||||
}
|
||||
|
||||
private void WriteValue16Bit(byte[] buffer, int value)
|
||||
private static void WriteValue16Bit(byte[] buffer, int offset, int value)
|
||||
{
|
||||
buffer[0] = (byte)value;
|
||||
buffer[1] = (byte)(value >> 8);
|
||||
buffer[offset ] = (byte)value;
|
||||
buffer[offset + 1] = (byte)(value >> 8);
|
||||
}
|
||||
|
||||
private void WriteValue24Bit(byte[] buffer, int value)
|
||||
private static void WriteValue24Bit(byte[] buffer, int offset, int value)
|
||||
{
|
||||
buffer[0] = (byte)value;
|
||||
buffer[1] = (byte)(value >> 8);
|
||||
buffer[2] = (byte)(value >> 16);
|
||||
buffer[offset ] = (byte)value;
|
||||
buffer[offset + 1] = (byte)(value >> 8);
|
||||
buffer[offset + 2] = (byte)(value >> 16);
|
||||
}
|
||||
|
||||
private void WriteValue32Bit(byte[] buffer, int value)
|
||||
private static void WriteValue32Bit(byte[] buffer, int offset, int value)
|
||||
{
|
||||
buffer[0] = (byte)value;
|
||||
buffer[1] = (byte)(value >> 8);
|
||||
buffer[2] = (byte)(value >> 16);
|
||||
buffer[3] = (byte)(value >> 24);
|
||||
buffer[offset ] = (byte)value;
|
||||
buffer[offset + 1] = (byte)(value >> 8);
|
||||
buffer[offset + 2] = (byte)(value >> 16);
|
||||
buffer[offset + 3] = (byte)(value >> 24);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine how to read sample values
|
||||
/// </summary>
|
||||
/// <returns>Sample data reader that matches bits per sample</returns>
|
||||
private ReadSampleDataValueDelegate GetSampleDataReader()
|
||||
private float GetSampleScale()
|
||||
{
|
||||
switch (Header.BytesPerSample)
|
||||
return (float)(1.0 / Math.Pow(2.0, _header.BytesPerSample * 8 - 1));
|
||||
}
|
||||
|
||||
private float GetSampleAndChannelScale()
|
||||
{
|
||||
return GetSampleScale() / _header.NumberOfChannels;
|
||||
}
|
||||
|
||||
private ReadSampleDataValue GetSampleDataReader()
|
||||
{
|
||||
switch (_header.BytesPerSample)
|
||||
{
|
||||
case 1:
|
||||
return ReadValue8Bit;
|
||||
@ -450,13 +509,13 @@ namespace Nikse.SubtitleEdit.Core
|
||||
case 4:
|
||||
return ReadValue32Bit;
|
||||
default:
|
||||
throw new InvalidDataException("Cannot read bits per sample of " + Header.BitsPerSample);
|
||||
throw new InvalidDataException("Cannot read bits per sample of " + _header.BitsPerSample);
|
||||
}
|
||||
}
|
||||
|
||||
private Action<byte[], int> GetSampleDataWriter()
|
||||
private WriteSampleDataValue GetSampleDataWriter()
|
||||
{
|
||||
switch (Header.BytesPerSample)
|
||||
switch (_header.BytesPerSample)
|
||||
{
|
||||
case 1:
|
||||
return WriteValue8Bit;
|
||||
@ -467,7 +526,7 @@ namespace Nikse.SubtitleEdit.Core
|
||||
case 4:
|
||||
return WriteValue32Bit;
|
||||
default:
|
||||
throw new InvalidDataException("Cannot write bits per sample of " + Header.BitsPerSample);
|
||||
throw new InvalidDataException("Cannot write bits per sample of " + _header.BitsPerSample);
|
||||
}
|
||||
}
|
||||
|
||||
@ -486,40 +545,33 @@ namespace Nikse.SubtitleEdit.Core
|
||||
|
||||
public List<Bitmap> GenerateFourierData(int nfft, string spectrogramDirectory, int delayInMilliseconds)
|
||||
{
|
||||
if (Header.BytesPerSample == 4)
|
||||
{
|
||||
// Can't handle 32-bit samples due to the way the channel averaging is done
|
||||
throw new Exception("32-bit samples are unsupported.");
|
||||
}
|
||||
|
||||
const int bitmapWidth = 1024;
|
||||
|
||||
List<Bitmap> bitmaps = new List<Bitmap>();
|
||||
SpectrogramDrawer drawer = new SpectrogramDrawer(nfft);
|
||||
Task saveImageTask = null;
|
||||
ReadSampleDataValueDelegate readSampleDataValue = GetSampleDataReader();
|
||||
double sampleScale = 1.0 / (Math.Pow(2.0, Header.BytesPerSample * 8 - 1) * Header.NumberOfChannels);
|
||||
int delaySampleCount = (int)(_header.SampleRate * (delayInMilliseconds / TimeCode.BaseUnit));
|
||||
|
||||
int delaySampleCount = (int)(Header.SampleRate * (delayInMilliseconds / TimeCode.BaseUnit));
|
||||
|
||||
// other code (e.g. generating peaks) doesn't handle negative delays, so we'll do the same for now
|
||||
// ignore negative delays for now (pretty sure it can't happen in mkv and some places pass in -1 by mistake)
|
||||
delaySampleCount = Math.Max(delaySampleCount, 0);
|
||||
|
||||
long fileSampleCount = Header.LengthInSamples;
|
||||
var bitmaps = new List<Bitmap>();
|
||||
var drawer = new SpectrogramDrawer(nfft);
|
||||
var readSampleDataValue = GetSampleDataReader();
|
||||
Task saveImageTask = null;
|
||||
float sampleAndChannelScale = GetSampleAndChannelScale();
|
||||
long fileSampleCount = _header.LengthInSamples;
|
||||
long fileSampleOffset = -delaySampleCount;
|
||||
int chunkSampleCount = nfft * bitmapWidth;
|
||||
int chunkCount = (int)Math.Ceiling((double)(fileSampleCount + delaySampleCount) / chunkSampleCount);
|
||||
byte[] data = new byte[chunkSampleCount * _header.BlockAlign];
|
||||
double[] chunkSamples = new double[chunkSampleCount];
|
||||
|
||||
Directory.CreateDirectory(spectrogramDirectory);
|
||||
|
||||
_data = new byte[chunkSampleCount * Header.BlockAlign];
|
||||
_stream.Seek(Header.DataStartPosition, SeekOrigin.Begin);
|
||||
_stream.Seek(_header.DataStartPosition, SeekOrigin.Begin);
|
||||
|
||||
// for negative delays, skip samples at the beginning
|
||||
if (fileSampleOffset > 0)
|
||||
{
|
||||
_stream.Seek(fileSampleOffset * Header.BlockAlign, SeekOrigin.Current);
|
||||
_stream.Seek(fileSampleOffset * _header.BlockAlign, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
for (int iChunk = 0; iChunk < chunkCount; iChunk++)
|
||||
@ -551,19 +603,19 @@ namespace Nikse.SubtitleEdit.Core
|
||||
// read samples from the file
|
||||
if (fileReadSampleCount > 0)
|
||||
{
|
||||
int fileReadByteCount = fileReadSampleCount * Header.BlockAlign;
|
||||
_stream.Read(_data, 0, fileReadByteCount);
|
||||
int fileReadByteCount = fileReadSampleCount * _header.BlockAlign;
|
||||
_stream.Read(data, 0, fileReadByteCount);
|
||||
fileSampleOffset += fileReadSampleCount;
|
||||
|
||||
int dataByteOffset = 0;
|
||||
while (dataByteOffset < fileReadByteCount)
|
||||
{
|
||||
int value = 0;
|
||||
for (int iChannel = 0; iChannel < Header.NumberOfChannels; iChannel++)
|
||||
float value = 0F;
|
||||
for (int iChannel = 0; iChannel < _header.NumberOfChannels; iChannel++)
|
||||
{
|
||||
value += readSampleDataValue(ref dataByteOffset);
|
||||
value += readSampleDataValue(data, ref dataByteOffset);
|
||||
}
|
||||
chunkSamples[chunkSampleOffset] = value * sampleScale;
|
||||
chunkSamples[chunkSampleOffset] = value * sampleAndChannelScale;
|
||||
chunkSampleOffset += 1;
|
||||
}
|
||||
}
|
||||
@ -598,10 +650,10 @@ namespace Nikse.SubtitleEdit.Core
|
||||
var doc = new XmlDocument();
|
||||
var culture = CultureInfo.InvariantCulture;
|
||||
doc.LoadXml("<SpectrogramInfo><SampleDuration/><NFFT/><ImageWidth/><SecondsPerImage/></SpectrogramInfo>");
|
||||
doc.DocumentElement.SelectSingleNode("SampleDuration").InnerText = ((double)nfft / Header.SampleRate).ToString(culture);
|
||||
doc.DocumentElement.SelectSingleNode("SampleDuration").InnerText = ((double)nfft / _header.SampleRate).ToString(culture);
|
||||
doc.DocumentElement.SelectSingleNode("NFFT").InnerText = nfft.ToString(culture);
|
||||
doc.DocumentElement.SelectSingleNode("ImageWidth").InnerText = bitmapWidth.ToString(culture);
|
||||
doc.DocumentElement.SelectSingleNode("SecondsPerImage").InnerText = ((double)chunkSampleCount / Header.SampleRate).ToString(culture); // currently unused; for backwards compatibility
|
||||
doc.DocumentElement.SelectSingleNode("SecondsPerImage").InnerText = ((double)chunkSampleCount / _header.SampleRate).ToString(culture); // currently unused; for backwards compatibility
|
||||
doc.Save(Path.Combine(spectrogramDirectory, "Info.xml"));
|
||||
|
||||
return bitmaps;
|
||||
|
@ -78,7 +78,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
private Paragraph _nextParagraph;
|
||||
private bool _firstMove = true;
|
||||
private double _currentVideoPositionSeconds = -1;
|
||||
private WavePeakGenerator _wavePeaks;
|
||||
private WavePeakData _wavePeaks;
|
||||
private Subtitle _subtitle;
|
||||
private bool _noClear;
|
||||
private double _gapAtStart = -1;
|
||||
@ -136,8 +136,8 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
}
|
||||
|
||||
public const double VerticalZoomMinimum = 1.0;
|
||||
public const double VerticalZoomMaximum = 40.0;
|
||||
private double _verticalZoomFactor = 2.0; // 1.0=no zoom
|
||||
public const double VerticalZoomMaximum = 20.0;
|
||||
private double _verticalZoomFactor = 1.0; // 1.0=no zoom
|
||||
public double VerticalZoomFactor
|
||||
{
|
||||
get
|
||||
@ -238,9 +238,9 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
{
|
||||
if (_wavePeaks != null)
|
||||
{
|
||||
double endPositionSeconds = value + ((double)Width / _wavePeaks.Header.SampleRate) / _zoomFactor;
|
||||
if (endPositionSeconds > _wavePeaks.Header.LengthInSeconds)
|
||||
value -= endPositionSeconds - _wavePeaks.Header.LengthInSeconds;
|
||||
double endPositionSeconds = value + ((double)Width / _wavePeaks.SampleRate) / _zoomFactor;
|
||||
if (endPositionSeconds > _wavePeaks.LengthInSeconds)
|
||||
value -= endPositionSeconds - _wavePeaks.LengthInSeconds;
|
||||
}
|
||||
if (value < 0)
|
||||
value = 0;
|
||||
@ -279,7 +279,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public WavePeakGenerator WavePeaks
|
||||
public WavePeakData WavePeaks
|
||||
{
|
||||
get
|
||||
{
|
||||
@ -404,23 +404,20 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
private static int CalculateHeight(double value, int imageHeight, int maxHeight)
|
||||
{
|
||||
double percentage = value / maxHeight;
|
||||
var result = (int)Math.Round((percentage / 2.0 + 0.5) * imageHeight);
|
||||
return imageHeight - result;
|
||||
}
|
||||
|
||||
private class IsSelectedHelper
|
||||
{
|
||||
private readonly List<SelectionRange> _ranges = new List<SelectionRange>();
|
||||
private int _lastPosition = int.MaxValue;
|
||||
private SelectionRange _nextSelection;
|
||||
|
||||
public IsSelectedHelper(IEnumerable<Paragraph> paragraphs, Func<double, int> secondsToPosition)
|
||||
public IsSelectedHelper(IEnumerable<Paragraph> paragraphs, int sampleRate)
|
||||
{
|
||||
foreach (Paragraph p in paragraphs)
|
||||
_ranges.Add(new SelectionRange(secondsToPosition(p.StartTime.TotalSeconds), secondsToPosition(p.EndTime.TotalSeconds)));
|
||||
{
|
||||
int start = (int)Math.Round(p.StartTime.TotalSeconds * sampleRate);
|
||||
int end = (int)Math.Round(p.EndTime.TotalSeconds * sampleRate);
|
||||
_ranges.Add(new SelectionRange(start, end));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSelected(int position)
|
||||
@ -459,7 +456,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
internal void WaveformPaint(object sender, PaintEventArgs e)
|
||||
{
|
||||
Graphics graphics = e.Graphics;
|
||||
if (_wavePeaks != null && _wavePeaks.AllSamples != null)
|
||||
if (_wavePeaks != null)
|
||||
{
|
||||
bool showSpectrogram = IsSpectrogramAvailable && ShowSpectrogram;
|
||||
bool showSpectrogramOnly = showSpectrogram && !ShowWaveform;
|
||||
@ -486,23 +483,35 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
using (var penNormal = new Pen(Color))
|
||||
using (var penSelected = new Pen(SelectedColor)) // selected paragraph
|
||||
{
|
||||
var pen = penNormal;
|
||||
var isSelectedHelper = new IsSelectedHelper(_allSelectedParagraphs, SecondsToXPositionNoZoom);
|
||||
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;
|
||||
float x = 0;
|
||||
int y;
|
||||
for (int i = 0; i < _wavePeaks.AllSamples.Count - start && x < Width; i++)
|
||||
var isSelectedHelper = new IsSelectedHelper(_allSelectedParagraphs, _wavePeaks.SampleRate);
|
||||
int baseHeight = (int)(_wavePeaks.HighestPeak / VerticalZoomFactor);
|
||||
int halfWaveformHeight = waveformHeight / 2;
|
||||
Func<float, float> calculateY = (value) =>
|
||||
{
|
||||
int n = start + i;
|
||||
x = (float)(_zoomFactor * i);
|
||||
y = CalculateHeight(_wavePeaks.AllSamples[n], waveformHeight, maxHeight);
|
||||
graphics.DrawLine(pen, xPrev, yPrev, x, y);
|
||||
xPrev = x;
|
||||
yPrev = y;
|
||||
pen = isSelectedHelper.IsSelected(n) ? penSelected : penNormal;
|
||||
float offset = (value / baseHeight) * halfWaveformHeight;
|
||||
if (offset > halfWaveformHeight)
|
||||
offset = halfWaveformHeight;
|
||||
if (offset < -halfWaveformHeight)
|
||||
offset = -halfWaveformHeight;
|
||||
return halfWaveformHeight - offset;
|
||||
};
|
||||
for (int x = 0; x < Width; x++)
|
||||
{
|
||||
float pos = (float)XPositionToSeconds(x) * _wavePeaks.SampleRate;
|
||||
int pos0 = (int)pos;
|
||||
int pos1 = pos0 + 1;
|
||||
if (pos1 >= _wavePeaks.Peaks.Count)
|
||||
break;
|
||||
float pos1Weight = pos - pos0;
|
||||
float pos0Weight = 1 - pos1Weight;
|
||||
var peak0 = _wavePeaks.Peaks[pos0];
|
||||
var peak1 = _wavePeaks.Peaks[pos1];
|
||||
float max = peak0.Max * pos0Weight + peak1.Max * pos1Weight;
|
||||
float min = peak0.Min * pos0Weight + peak1.Min * pos1Weight;
|
||||
float yMax = calculateY(max);
|
||||
float yMin = Math.Max(calculateY(min), yMax + 0.1F);
|
||||
var pen = isSelectedHelper.IsSelected(pos0) ? penSelected : penNormal;
|
||||
graphics.DrawLine(pen, x, yMax, x, yMin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -561,7 +570,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
if (currentRegionWidth > 40)
|
||||
{
|
||||
using (var brush = new SolidBrush(Color.Turquoise))
|
||||
graphics.DrawString(string.Format("{0:0.###} {1}", ((double)currentRegionWidth / _wavePeaks.Header.SampleRate / _zoomFactor), Configuration.Settings.Language.Waveform.Seconds), Font, brush, new PointF(currentRegionLeft + 3, Height - 32));
|
||||
graphics.DrawString(string.Format("{0:0.###} {1}", ((double)currentRegionWidth / _wavePeaks.SampleRate / _zoomFactor), Configuration.Settings.Language.Waveform.Seconds), Font, brush, new PointF(currentRegionLeft + 3, Height - 32));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -617,8 +626,8 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
else
|
||||
{
|
||||
double interval = ZoomFactor >= 0.4 ?
|
||||
0.1 * _wavePeaks.Header.SampleRate * _zoomFactor : // a pixel is 0.1 second
|
||||
1.0 * _wavePeaks.Header.SampleRate * _zoomFactor; // a pixel is 1.0 second
|
||||
0.1 * _wavePeaks.SampleRate * _zoomFactor : // a pixel is 0.1 second
|
||||
1.0 * _wavePeaks.SampleRate * _zoomFactor; // a pixel is 1.0 second
|
||||
using (var pen = new Pen(new SolidBrush(GridColor)))
|
||||
{
|
||||
for (double i = SecondsToXPosition(StartPositionSeconds) % ((int)Math.Round(interval)); i < Width; i += interval)
|
||||
@ -645,7 +654,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
{
|
||||
while (position < Width)
|
||||
{
|
||||
var n = _zoomFactor * _wavePeaks.Header.SampleRate;
|
||||
var n = _zoomFactor * _wavePeaks.SampleRate;
|
||||
if (n > 38 || (int)Math.Round(StartPositionSeconds + seconds) % 5 == 0)
|
||||
{
|
||||
graphics.DrawLine(pen, position, imageHeight, position, imageHeight - 10);
|
||||
@ -707,7 +716,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
};
|
||||
|
||||
const int padding = 3;
|
||||
double n = _zoomFactor * _wavePeaks.Header.SampleRate;
|
||||
double n = _zoomFactor * _wavePeaks.SampleRate;
|
||||
|
||||
// paragraph text
|
||||
if (n > 80)
|
||||
@ -735,17 +744,12 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
|
||||
private double XPositionToSeconds(double x)
|
||||
{
|
||||
return StartPositionSeconds + (x / _wavePeaks.Header.SampleRate) / _zoomFactor;
|
||||
return StartPositionSeconds + (x / _wavePeaks.SampleRate) / _zoomFactor;
|
||||
}
|
||||
|
||||
private int SecondsToXPosition(double seconds)
|
||||
{
|
||||
return (int)Math.Round(seconds * _wavePeaks.Header.SampleRate * _zoomFactor);
|
||||
}
|
||||
|
||||
private int SecondsToXPositionNoZoom(double seconds)
|
||||
{
|
||||
return (int)Math.Round(seconds * _wavePeaks.Header.SampleRate);
|
||||
return (int)Math.Round(seconds * _wavePeaks.SampleRate * _zoomFactor);
|
||||
}
|
||||
|
||||
private void WaveformMouseDown(object sender, MouseEventArgs e)
|
||||
@ -1095,7 +1099,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
if (_mouseDownParagraph == null)
|
||||
{
|
||||
_mouseMoveEndX = 0;
|
||||
_mouseMoveStartX += (int)(_wavePeaks.Header.SampleRate * 0.1);
|
||||
_mouseMoveStartX += (int)(_wavePeaks.SampleRate * 0.1);
|
||||
OnPositionSelected.Invoke(this, new ParagraphEventArgs(StartPositionSeconds, null));
|
||||
}
|
||||
}
|
||||
@ -1103,7 +1107,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
Invalidate();
|
||||
return;
|
||||
}
|
||||
if (e.X > Width && StartPositionSeconds + 0.1 < _wavePeaks.Header.LengthInSeconds && _mouseDown)
|
||||
if (e.X > Width && StartPositionSeconds + 0.1 < _wavePeaks.LengthInSeconds && _mouseDown)
|
||||
{
|
||||
//if (e.X > _mouseMoveLastX) // not much room for moving mouse cursor, so just scroll right
|
||||
{
|
||||
@ -1111,7 +1115,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
if (_mouseDownParagraph == null)
|
||||
{
|
||||
_mouseMoveEndX = Width;
|
||||
_mouseMoveStartX -= (int)(_wavePeaks.Header.SampleRate * 0.1);
|
||||
_mouseMoveStartX -= (int)(_wavePeaks.SampleRate * 0.1);
|
||||
OnPositionSelected.Invoke(this, new ParagraphEventArgs(StartPositionSeconds, null));
|
||||
}
|
||||
}
|
||||
@ -1375,7 +1379,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
|
||||
private void WaveformMouseEnter(object sender, EventArgs e)
|
||||
{
|
||||
if (_wavePeaks == null || _wavePeaks.Header == null)
|
||||
if (_wavePeaks == null)
|
||||
return;
|
||||
|
||||
if (_noClear)
|
||||
@ -1547,7 +1551,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
}
|
||||
else if (e.Modifiers == Keys.None && e.KeyCode == Keys.X)
|
||||
{
|
||||
if (StartPositionSeconds + 0.1 < _wavePeaks.Header.LengthInSeconds)
|
||||
if (StartPositionSeconds + 0.1 < _wavePeaks.LengthInSeconds)
|
||||
{
|
||||
StartPositionSeconds += 0.1;
|
||||
OnPositionSelected.Invoke(this, new ParagraphEventArgs(StartPositionSeconds, null));
|
||||
@ -1577,15 +1581,15 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
int length = SecondsToXPosition(durationInSeconds);
|
||||
|
||||
int hitCount = 0;
|
||||
for (int i = begin; i < _wavePeaks.AllSamples.Count; i++)
|
||||
for (int i = begin; i < _wavePeaks.Peaks.Count; i++)
|
||||
{
|
||||
if (i > 0 && i < _wavePeaks.AllSamples.Count && Math.Abs(_wavePeaks.AllSamples[i]) <= threshold)
|
||||
if (i > 0 && i < _wavePeaks.Peaks.Count && _wavePeaks.Peaks[i].Abs <= threshold)
|
||||
hitCount++;
|
||||
else
|
||||
hitCount = 0;
|
||||
if (hitCount > length)
|
||||
{
|
||||
double seconds = ((i - (length / 2)) / (double)_wavePeaks.Header.SampleRate) / _zoomFactor;
|
||||
double seconds = ((i - (length / 2)) / (double)_wavePeaks.SampleRate) / _zoomFactor;
|
||||
if (seconds >= 0)
|
||||
{
|
||||
StartPositionSeconds = seconds;
|
||||
@ -1608,13 +1612,13 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
int hitCount = 0;
|
||||
for (int i = begin; i > 0; i--)
|
||||
{
|
||||
if (i > 0 && i < _wavePeaks.AllSamples.Count && Math.Abs(_wavePeaks.AllSamples[i]) <= threshold)
|
||||
if (i > 0 && i < _wavePeaks.Peaks.Count && _wavePeaks.Peaks[i].Abs <= threshold)
|
||||
hitCount++;
|
||||
else
|
||||
hitCount = 0;
|
||||
if (hitCount > length)
|
||||
{
|
||||
double seconds = (i + (length / 2)) / (double)_wavePeaks.Header.SampleRate / _zoomFactor;
|
||||
double seconds = (i + (length / 2)) / (double)_wavePeaks.SampleRate / _zoomFactor;
|
||||
if (seconds >= 0)
|
||||
{
|
||||
StartPositionSeconds = seconds;
|
||||
@ -1807,9 +1811,9 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
int count = 0;
|
||||
for (int i = sampleIndex; i < sampleIndex + length; i++)
|
||||
{
|
||||
if (i > 0 && i < _wavePeaks.AllSamples.Count)
|
||||
if (i > 0 && i < _wavePeaks.Peaks.Count)
|
||||
{
|
||||
v += Math.Abs(_wavePeaks.AllSamples[i]);
|
||||
v += _wavePeaks.Peaks[i].Abs;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@ -1823,11 +1827,11 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
int begin = SecondsToXPosition(startFromSeconds);
|
||||
|
||||
double average = 0;
|
||||
for (int k = begin; k < _wavePeaks.AllSamples.Count; k++)
|
||||
average += Math.Abs(_wavePeaks.AllSamples[k]);
|
||||
average = average / (_wavePeaks.AllSamples.Count - begin);
|
||||
for (int k = begin; k < _wavePeaks.Peaks.Count; k++)
|
||||
average += _wavePeaks.Peaks[k].Abs;
|
||||
average = average / (_wavePeaks.Peaks.Count - begin);
|
||||
|
||||
var maxThreshold = (int)(_wavePeaks.DataMaxValue * (maximumVolumePercent / 100.0));
|
||||
var maxThreshold = (int)(_wavePeaks.HighestPeak * (maximumVolumePercent / 100.0));
|
||||
var silenceThreshold = (int)(average * (minimumVolumePercent / 100.0));
|
||||
|
||||
int length50Ms = SecondsToXPosition(0.050);
|
||||
@ -1835,7 +1839,7 @@ namespace Nikse.SubtitleEdit.Controls
|
||||
int minBetween = SecondsToXPosition(Configuration.Settings.General.MinimumMillisecondsBetweenLines / TimeCode.BaseUnit);
|
||||
bool subtitleOn = false;
|
||||
int i = begin;
|
||||
while (i < _wavePeaks.AllSamples.Count)
|
||||
while (i < _wavePeaks.Peaks.Count)
|
||||
{
|
||||
if (subtitleOn)
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
{
|
||||
public string SourceVideoFileName { get; private set; }
|
||||
private bool _cancel;
|
||||
private string _peakWaveFileName;
|
||||
private string _wavFileName;
|
||||
private string _spectrogramDirectory;
|
||||
public List<Bitmap> SpectrogramBitmaps { get; private set; }
|
||||
@ -31,10 +32,9 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
labelInfo.Text = string.Empty;
|
||||
}
|
||||
|
||||
public WavePeakGenerator WavePeak { get; private set; }
|
||||
|
||||
public void Initialize(string videoFile, string spectrogramDirectory, int audioTrackNumber)
|
||||
public void Initialize(string videoFile, string peakWaveFileName, string spectrogramDirectory, int audioTrackNumber)
|
||||
{
|
||||
_peakWaveFileName = peakWaveFileName;
|
||||
_audioTrackNumber = audioTrackNumber;
|
||||
if (_audioTrackNumber < 0)
|
||||
_audioTrackNumber = 0;
|
||||
@ -236,7 +236,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
|
||||
using (var waveFile = new WavePeakGenerator(targetFile))
|
||||
{
|
||||
waveFile.GeneratePeakSamples(delayInMilliseconds);
|
||||
waveFile.GeneratePeaks(delayInMilliseconds, _peakWaveFileName);
|
||||
|
||||
if (Configuration.Settings.VideoControls.GenerateSpectrogram)
|
||||
{
|
||||
@ -244,8 +244,6 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
Refresh();
|
||||
SpectrogramBitmaps = waveFile.GenerateFourierData(256, _spectrogramDirectory, delayInMilliseconds); // image height = nfft / 2
|
||||
}
|
||||
|
||||
WavePeak = waveFile;
|
||||
}
|
||||
|
||||
labelPleaseWait.Visible = false;
|
||||
@ -379,8 +377,9 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
_cancel = true;
|
||||
}
|
||||
|
||||
internal void InitializeViaWaveFile(string fileName, string spectrogramFolder)
|
||||
internal void InitializeViaWaveFile(string fileName, string peakWaveFileName, string spectrogramFolder)
|
||||
{
|
||||
_peakWaveFileName = peakWaveFileName;
|
||||
_wavFileName = fileName;
|
||||
_spectrogramDirectory = spectrogramFolder;
|
||||
}
|
||||
|
@ -329,8 +329,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
{
|
||||
using (var waveFile = new WavePeakGenerator(targetFile))
|
||||
{
|
||||
waveFile.GeneratePeakSamples(delayInMilliseconds);
|
||||
waveFile.WritePeakSamples(Main.GetPeakWaveFileName(videoFileName));
|
||||
waveFile.GeneratePeaks(delayInMilliseconds, Main.GetPeakWaveFileName(videoFileName));
|
||||
|
||||
if (Configuration.Settings.VideoControls.GenerateSpectrogram)
|
||||
{
|
||||
|
@ -12678,12 +12678,11 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
string spectrogramFolder = GetSpectrogramFolder(fileName);
|
||||
if (File.Exists(peakWaveFileName))
|
||||
{
|
||||
audioVisualizer.WavePeaks = new WavePeakGenerator(peakWaveFileName);
|
||||
using (var peakGenerator = new WavePeakGenerator(peakWaveFileName))
|
||||
audioVisualizer.WavePeaks = peakGenerator.LoadPeaks();
|
||||
audioVisualizer.ResetSpectrogram();
|
||||
audioVisualizer.InitializeSpectrogram(spectrogramFolder);
|
||||
toolStripComboBoxWaveform_SelectedIndexChanged(null, null);
|
||||
audioVisualizer.WavePeaks.GenerateAllSamples();
|
||||
audioVisualizer.WavePeaks.Close();
|
||||
SetWaveformPosition(0, 0, 0);
|
||||
timerWaveform.Start();
|
||||
}
|
||||
@ -14818,19 +14817,16 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
|
||||
if (IsFileValidForVisualizer(_videoFileName))
|
||||
{
|
||||
addWaveform.InitializeViaWaveFile(_videoFileName, spectrogramFolder);
|
||||
addWaveform.InitializeViaWaveFile(_videoFileName, peakWaveFileName, spectrogramFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
addWaveform.Initialize(_videoFileName, spectrogramFolder, _videoAudioTrackNumber);
|
||||
addWaveform.Initialize(_videoFileName, peakWaveFileName, spectrogramFolder, _videoAudioTrackNumber);
|
||||
}
|
||||
if (addWaveform.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
addWaveform.WavePeak.WritePeakSamples(peakWaveFileName);
|
||||
var audioPeakWave = new WavePeakGenerator(peakWaveFileName);
|
||||
audioPeakWave.GenerateAllSamples();
|
||||
audioPeakWave.Close();
|
||||
audioVisualizer.WavePeaks = audioPeakWave;
|
||||
using (var peakGenerator = new WavePeakGenerator(peakWaveFileName))
|
||||
audioVisualizer.WavePeaks = peakGenerator.LoadPeaks();
|
||||
if (addWaveform.SpectrogramBitmaps != null)
|
||||
audioVisualizer.InitializeSpectrogram(addWaveform.SpectrogramBitmaps, spectrogramFolder);
|
||||
timerWaveform.Start();
|
||||
@ -15208,14 +15204,12 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
using (var addWaveform = new AddWaveform())
|
||||
{
|
||||
string spectrogramFolder = GetSpectrogramFolder(_videoFileName);
|
||||
addWaveform.InitializeViaWaveFile(fileName, spectrogramFolder);
|
||||
string peakWaveFileName = GetPeakWaveFileName(_videoFileName);
|
||||
addWaveform.InitializeViaWaveFile(fileName, peakWaveFileName, spectrogramFolder);
|
||||
if (addWaveform.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string peakWaveFileName = GetPeakWaveFileName(_videoFileName);
|
||||
addWaveform.WavePeak.WritePeakSamples(peakWaveFileName);
|
||||
var audioPeakWave = new WavePeakGenerator(peakWaveFileName);
|
||||
audioPeakWave.GenerateAllSamples();
|
||||
audioVisualizer.WavePeaks = audioPeakWave;
|
||||
using (var peakGenerator = new WavePeakGenerator(peakWaveFileName))
|
||||
audioVisualizer.WavePeaks = peakGenerator.LoadPeaks();
|
||||
timerWaveform.Start();
|
||||
}
|
||||
}
|
||||
@ -16539,7 +16533,7 @@ namespace Nikse.SubtitleEdit.Forms
|
||||
}
|
||||
}
|
||||
|
||||
if (mediaPlayer.VideoPlayer != null && audioVisualizer != null && audioVisualizer.WavePeaks != null && audioVisualizer.WavePeaks.AllSamples.Count > 0)
|
||||
if (mediaPlayer.VideoPlayer != null && audioVisualizer != null && audioVisualizer.WavePeaks != null && audioVisualizer.WavePeaks.Peaks.Count > 0)
|
||||
{
|
||||
toolStripMenuItemImportSceneChanges.Visible = true;
|
||||
toolStripMenuItemRemoveSceneChanges.Visible = audioVisualizer.SceneChanges.Count > 0;
|
||||
|
Loading…
Reference in New Issue
Block a user