////////////Qhenomenology.Waveform Analyzer
////////////-------------------------------
////////////- Target: .NET Framework 4.8, C# 7.0
////////////- Usage: Load 8000Hz mono 16-bit WAV file
////////////- Shows ProgressBar while processing
////////////- Detects waveform crests/troughs using vector turning
////////////- Classifies waveform shape using angle bins and
microsecond resolution
////////////- Generates data segments with metadata for CSV
exportusing System;
////////////using System.Collections.Generic;
////////////using System.Windows.Forms;
//////namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////{
////// public class
WaveformAnalyzer
////// {
////// private
readonly float[] samples;
////// private
readonly int sampleRate;
////// private
readonly ProgressBar progressBar;
////// private
readonly List<WaveSegment> segments = new List<WaveSegment>();
////// public
WaveformAnalyzer(float[] samples, int sampleRate, ref ProgressBar progressBar)
////// {
////// this.samples
= samples;
//////
this.sampleRate = sampleRate;
//////
this.progressBar = progressBar;
//////
}//public WaveformAnalyzer(float[] samples, int sampleRate, ProgressBar
progressBar)
////// public
List<WaveSegment> Analyze()
////// {
////// int
samplesPerMs = sampleRate / 1000;
////// int
zeroCrossing = 0;
////// bool
inCrest = false;
//////
WaveSegment current = null;
//////
progressBar.Minimum = 0;
//////
progressBar.Maximum = samples.Length;
//////
progressBar.Value = 0;
////// for
(int i = 1; i < samples.Length - 1; i++)
////// {
//////
if (i % 5000 == 0 && progressBar.Value < progressBar.Maximum)
//////
{ progressBar.Value = i; }
//////
float a = samples[i - 1];
//////
float b = samples[i];
//////
float c = samples[i + 1];
//////
if (a <= 0 && b > 0)
////// {
//////
if (current != null) segments.Add(current);
//////
current = new WaveSegment
//////
{
//////
StartMicrosecond = i * 1000000 / sampleRate,
////// IsCrest = true,
//////
MaxAmplitude = b
//////
};
//////
inCrest = true;
//////
}
//////
else if (a >= 0 && b < 0)
//////
{
////// if (current != null)
segments.Add(current);
//////
current = new WaveSegment
//////
{
//////
StartMicrosecond = i * 1000000 / sampleRate,
//////
IsCrest = false,
//////
MinAmplitude = b
//////
};
//////
inCrest = false;
//////
}//END OF ELSE OF if (a <= 0 && b > 0)
//////
if (current != null)
//////
{
//////
float v1x = 1f, v1y = b - a;
//////
float v2x = 1f, v2y = c - b;
//////
double angle = Math.Acos((v1x * v2x + v1y * v2y) /
//////
(Math.Sqrt(v1x * v1x + v1y * v1y) * Math.Sqrt(v2x * v2x + v2y * v2y))) *
180 / Math.PI;
//////
int bin = angle < 30 ? 0 : angle < 45 ? 1 : angle < 60 ? 2 : 3;
//////
current.AngleBins[bin]++;
//////
current.AngleChangeCount++;
////// if (inCrest && b > a
&& b > c) current.LocalPeaks++;
//////
if (!inCrest && b < a && b < c)
current.LocalMins++;
//////
if (inCrest) current.MaxAmplitude = Math.Max(current.MaxAmplitude, b);
////// else current.MinAmplitude =
Math.Min(current.MinAmplitude, b);
//////
current.EndMicrosecond = i * 1000000 / sampleRate;
//////
}//if (current != null)
////// }
////// if
(current != null) segments.Add(current);
//////
progressBar.Value = progressBar.Maximum;
//////
return segments;
//////
}//public List<WaveSegment> Analyze()
////// }//public
class WaveformAnalyzer
////// public class
WaveSegment
////// {
////// public
int StartMicrosecond;
////// public
int EndMicrosecond;
////// public
bool IsCrest;
////// public
float MaxAmplitude;
////// public
float MinAmplitude;
////// public
int AngleChangeCount;
////// public
int[] AngleBins = new int[4]; // [0-30, 30-45, 45-60, 60-90]
////// public
int LocalPeaks;
////// public
int LocalMins;
////// }//public
class WaveSegment
////// public
static class WavLoader
////// {
////// public
static float[] LoadMono16BitPcmWav(string path, out int sampleRate)
////// {
//////
sampleRate = 8000;
//////
using (var br = new BinaryReader(File.OpenRead(path)))
////// {
//////
string riff = new string(br.ReadChars(4));
//////
if (riff != "RIFF") throw new Exception("Invalid WAV
file");
//////
br.ReadInt32(); // skip file size
//////
string wave = new string(br.ReadChars(4));
////// if (wave != "WAVE") throw
new Exception("Invalid WAV file");
//////
string fmt = new string(br.ReadChars(4));
//////
if (fmt != "fmt ") throw new Exception("Expected 'fmt
'");
//////
int fmtLength = br.ReadInt32();
//////
short audioFormat = br.ReadInt16();
//////
short numChannels = br.ReadInt16();
//////
sampleRate = br.ReadInt32();
//////
br.ReadInt32(); // byte rate
////// br.ReadInt16(); // block align
//////
short bitsPerSample = br.ReadInt16();
//////
if (audioFormat != 1 || bitsPerSample != 16 || numChannels != 1)
//////
throw new Exception("Only 16-bit mono PCM supported");
//////
br.BaseStream.Seek(fmtLength - 16, SeekOrigin.Current);
//////
string dataChunkId = new string(br.ReadChars(4));
//////
while (dataChunkId != "data")
//////
{
//////
int chunkSize = br.ReadInt32();
//////
br.BaseStream.Seek(chunkSize, SeekOrigin.Current);
//////
dataChunkId = new string(br.ReadChars(4));
//////
}//while (dataChunkId != "data")
////// int dataSize = br.ReadInt32();
//////
int numSamples = dataSize / 2;
//////
float[] samples = new float[numSamples];
//////
for (int i = 0; i < numSamples; i++)
////// {
samples[i] = br.ReadInt16() / 32768f; }
//////
return samples;
//////
}//using (var br = new BinaryReader(File.OpenRead(path)))
//////
}//public static float[] LoadMono16BitPcmWav(string path, out int
sampleRate)
////// }//public
static class WavLoader
//////}//namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
///
//////namespace
DISCARDING___SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////{
////// public class
WaveformAnalyzer
////// {
////// private
readonly float[] samples;
////// private
readonly int sampleRate;
////// private
readonly ProgressBar progressBar;
////// private
readonly List<WaveSegment> segments = new List<WaveSegment>();
////// public
WaveformAnalyzer(float[] samples, int sampleRate, ref ProgressBar progressBar)
////// {
//////
this.samples = samples;
//////
this.sampleRate = sampleRate;
//////
this.progressBar = progressBar;
////// }
////// public
List<WaveSegment> Analyze()
////// {
////// int
totalSamples = samples.Length;
//////
progressBar.Minimum = 0;
//////
progressBar.Maximum = totalSamples;
//////
progressBar.Value = 0;
////// int
samplesPerMs = sampleRate / 1000;
////// bool
inCrest = false;
//////
WaveSegment current = null;
////// for
(int i = 1; i < totalSamples - 1; i++)
////// {
//////
if (i % 5000 == 0)
//////
{
//////
progressBar.Value = Math.Min(i, progressBar.Maximum);
//////
}
//////
float a = samples[i - 1];
//////
float b = samples[i];
//////
float c = samples[i + 1];
//////
// Detect zero-crossing from negative to positive: start of crest
//////
if (a <= 0 && b > 0)
//////
{
//////
if (current != null) segments.Add(current);
//////
current = new WaveSegment
//////
{
//////
StartMicrosecond = i * 1000000 / sampleRate,
//////
IsCrest = true,
////// MaxAmplitude = b
//////
};
//////
inCrest = true;
//////
}
//////
// Detect zero-crossing from positive to negative: start of trough
//////
else if (a >= 0 && b < 0)
//////
{
//////
if (current != null) segments.Add(current);
//////
current = new WaveSegment
//////
{
//////
StartMicrosecond = i * 1000000 / sampleRate,
//////
IsCrest = false,
//////
MinAmplitude = b
//////
};
//////
inCrest = false;
//////
}
//////
if (current != null)
//////
{
//////
// Angle calculation between vectors
//////
float v1x = 1f, v1y = b - a;
//////
float v2x = 1f, v2y = c - b;
//////
double angle = Math.Acos(
//////
(v1x * v2x + v1y * v2y) /
//////
(Math.Sqrt(v1x * v1x + v1y * v1y) * Math.Sqrt(v2x * v2x + v2y * v2y))
//////
) * 180.0 / Math.PI;
//////
int bin = angle < 30 ? 0 : angle < 45 ? 1 : angle < 60 ? 2 : 3;
//////
current.AngleBins[bin]++;
//////
current.AngleChangeCount++;
//////
if (inCrest && b > a && b > c)
current.LocalPeaks++;
//////
if (!inCrest && b < a && b < c) current.LocalMins++;
//////
if (inCrest) current.MaxAmplitude = Math.Max(current.MaxAmplitude, b);
//////
else current.MinAmplitude = Math.Min(current.MinAmplitude, b);
//////
current.EndMicrosecond = i * 1000000 / sampleRate;
//////
}
////// }
////// if
(current != null) segments.Add(current);
//////
progressBar.Value = progressBar.Maximum;
//////
return segments;
////// }
////// }//public
class WaveformAnalyzer
////// public class
WaveSegment
////// {
////// public
int StartMicrosecond;
////// public
int EndMicrosecond;
////// public
bool IsCrest;
////// public
float MaxAmplitude;
////// public
float MinAmplitude;
////// public
int AngleChangeCount;
////// public
int[] AngleBins = new int[4]; // [0-30, 30-45, 45-60, 60-90]
////// public
int LocalPeaks;
////// public
int LocalMins;
////// public
string ToCsv()
////// {
//////
return string.Join(",",
//////
StartMicrosecond,
//////
EndMicrosecond,
//////
IsCrest ? "Crest" : "Trough",
//////
MaxAmplitude.ToString("F5"),
////// MinAmplitude.ToString("F5"),
//////
AngleChangeCount,
//////
string.Join("|", AngleBins),
//////
LocalPeaks,
//////
LocalMins
////// );
////// }
////// }// public
class WaveSegment
////// public
static class WavLoader
////// {
////// public
static float[][] LoadPcmWav(string path, out int sampleRate, out bool isStereo)
////// {
//////
sampleRate = 8000;
//////
isStereo = false;
//////
using (var br = new BinaryReader(File.OpenRead(path)))
////// {
//////
string riff = new string(br.ReadChars(4));
//////
if (riff != "RIFF") throw new Exception("Invalid WAV (no
RIFF)");
//////
br.ReadInt32(); // skip file size
//////
string wave = new string(br.ReadChars(4));
//////
if (wave != "WAVE") throw new Exception("Invalid WAV (no
WAVE)");
////// string fmt = new string(br.ReadChars(4));
//////
if (fmt != "fmt ") throw new Exception("Expected fmt
chunk");
//////
int fmtLen = br.ReadInt32();
//////
short format = br.ReadInt16();
////// short channels = br.ReadInt16();
//////
sampleRate = br.ReadInt32();
//////
br.ReadInt32(); // byte rate
//////
br.ReadInt16(); // block align
//////
short bits = br.ReadInt16();
////// if (format != 1 || bits != 16)
//////
throw new Exception("Only PCM 16-bit supported");
//////
isStereo = (channels == 2);
//////
if (fmtLen > 16) br.ReadBytes(fmtLen - 16);
////// //
Skip to "data" chunk
//////
string chunkId = new string(br.ReadChars(4));
//////
while (chunkId != "data")
//////
{
//////
int skip = br.ReadInt32();
//////
br.BaseStream.Seek(skip, SeekOrigin.Current);
//////
chunkId = new string(br.ReadChars(4));
//////
}
//////
int dataSize = br.ReadInt32();
//////
int totalSamples = dataSize / 2;
//////
int frames = totalSamples / channels;
//////
float[] left = new float[frames];
//////
float[] right = isStereo ? new float[frames] : null;
//////
for (int i = 0; i < frames; i++)
//////
{
////// left[i] = br.ReadInt16() /
32768f;
//////
if (isStereo)
//////
right[i] = br.ReadInt16() / 32768f;
//////
}
//////
return isStereo ? new[] { left, right } : new[] { left };
////// }
////// }
////// }// public
static class WavLoader
////// public
static class WavLoader___only_mono_8000sps_16bitfloats
////// {
////// public
static float[] LoadMono16BitPcmWav(string path, out int sampleRate)
////// {
//////
sampleRate = 8000;
//////
using (var br = new BinaryReader(File.OpenRead(path)))
////// {
//////
string riff = new string(br.ReadChars(4));
//////
if (riff != "RIFF") throw new Exception("Invalid WAV file
(no RIFF)");
//////
br.ReadInt32(); // skip chunk size
//////
string wave = new string(br.ReadChars(4));
////// if (wave != "WAVE")
throw new Exception("Invalid WAV file (no WAVE)");
//////
string fmt = new string(br.ReadChars(4));
//////
if (fmt != "fmt ") throw new Exception("Invalid WAV file
(no fmt)");
////// int fmtSize = br.ReadInt32();
//////
short format = br.ReadInt16();
//////
short channels = br.ReadInt16();
//////
sampleRate = br.ReadInt32();
//////
br.ReadInt32(); // byte rate
////// br.ReadInt16(); // block align
//////
short bits = br.ReadInt16();
//////
if (format != 1 || channels != 1 || bits != 16)
//////
throw new Exception("Only mono 16-bit PCM supported");
////// // Skip any extra fmt bytes
//////
if (fmtSize > 16)
//////
br.ReadBytes(fmtSize - 16);
//////
string dataChunkId = new string(br.ReadChars(4));
//////
while (dataChunkId != "data")
//////
{
//////
int skip = br.ReadInt32();
//////
br.ReadBytes(skip);
//////
dataChunkId = new string(br.ReadChars(4));
//////
}
//////
int dataSize = br.ReadInt32();
//////
int numSamples = dataSize / 2;
//////
float[] samples = new float[numSamples];
//////
for (int i = 0; i < numSamples; i++)
//////
samples[i] = br.ReadInt16() / 32768f;
//////
return samples;
////// }
////// }
////// }//public
static class WavLoader
////// public
static class CsvExporter
////// {
////// public
static void ExportSegments(string outputPath, List<WaveSegment> segments)
////// {
//////
using (var sw = new StreamWriter(outputPath))
////// {
//////
sw.WriteLine("Start(us),End(us),Type,MaxAmp,MinAmp,AngleChanges,Bins,LocalPeaks,LocalMins");
////// foreach (var seg in segments)
//////
sw.WriteLine(seg.ToCsv());
////// }
////// }
////// }//public
static class CsvExporter
//////}//namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
// Namespace: SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////using System;
//////using System.Collections.Generic;
//////using System.ComponentModel;
//////using System.IO;
//////using System.Windows.Forms;
//////using System.Threading;
//////namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////{
////// public class
WaveSegment
////// {
////// public
int StartMicrosecond;
////// public
int EndMicrosecond;
////// public
bool IsCrest;
////// public
float MaxAmplitude;
////// public
float MinAmplitude;
////// public
int AngleChangeCount;
////// public
int[] AngleBins = new int[4];
////// public
int LocalPeaks;
////// public
int LocalMins;
////// public int
Channel;
////// }//public
class WaveSegment
////// public
static class WavLoader
////// {
////// public
static float[][] LoadPcmWav(string path, out int sampleRate, out bool isStereo)
////// {
//////
sampleRate = 8000;
//////
isStereo = false;
//////
using (var br = new BinaryReader(File.OpenRead(path)))
////// {
//////
string riff = new string(br.ReadChars(4));
//////
if (riff != "RIFF") throw new Exception("Invalid WAV (no
RIFF)");
//////
br.ReadInt32(); // skip file size
//////
string wave = new string(br.ReadChars(4));
//////
if (wave != "WAVE") throw new Exception("Invalid WAV (no
WAVE)");
////// string fmt = new string(br.ReadChars(4));
//////
if (fmt != "fmt ") throw new Exception("Expected fmt
chunk");
//////
int fmtLen = br.ReadInt32();
//////
short format = br.ReadInt16();
////// short channels = br.ReadInt16();
//////
sampleRate = br.ReadInt32();
//////
br.ReadInt32(); // byte rate
//////
br.ReadInt16(); // block align
//////
short bits = br.ReadInt16();
////// if (format != 1 || bits != 16)
//////
throw new Exception("Only PCM 16-bit supported");
//////
isStereo = (channels == 2);
//////
if (fmtLen > 16) br.ReadBytes(fmtLen - 16);
////// //
Skip to "data" chunk
//////
string chunkId = new string(br.ReadChars(4));
//////
while (chunkId != "data")
//////
{
//////
int skip = br.ReadInt32();
//////
br.BaseStream.Seek(skip, SeekOrigin.Current);
//////
chunkId = new string(br.ReadChars(4));
//////
}
//////
int dataSize = br.ReadInt32();
//////
int totalSamples = dataSize / 2;
//////
int frames = totalSamples / channels;
//////
float[] left = new float[frames];
//////
float[] right = isStereo ? new float[frames] : null;
//////
for (int i = 0; i < frames; i++)
//////
{
////// left[i] = br.ReadInt16() /
32768f;
//////
if (isStereo)
//////
right[i] = br.ReadInt16() / 32768f;
//////
}
//////
return isStereo ? new[] { left, right } : new[] { left };
////// }
////// }
////// }// public
static class WavLoader
////// public class
WaveformAnalyzer
////// {
////// private
readonly float[][] channels;
////// private
readonly int sampleRate;
////// private
readonly ProgressBar progressBar;
////// private
readonly List<WaveSegment> segments = new List<WaveSegment>();
//////
//////public WaveformAnalyzer(float[][] channels, int sampleRate, ref
ProgressBar progressBar)
////// //////{
//////
////// this.channels =
channels;
//////
////// this.sampleRate =
sampleRate;
//////
////// this.progressBar =
progressBar;
////// //////}//public
WaveformAnalyzer(float[][] channels, int sampleRate, ref ProgressBar
progressBar)
////// public
WaveformAnalyzer(float[][] channels, int sampleRate, ProgressBar progressBar)
////// {
//////
this.channels = channels;
//////
this.sampleRate = sampleRate;
//////
this.progressBar = progressBar;
//////
}//public WaveformAnalyzer(float[][] channels, int sampleRate, ref
ProgressBar progressBar)
////// public
List<WaveSegment> Analyze()
////// {
////// int
samples = channels[0].Length;
////// int
samplesPerMs = sampleRate / 1000;
//////
progressBar.Minimum = 0;
//////
progressBar.Maximum = samples;
//////
progressBar.Value = 0;
////// for
(int ch = 0; ch < channels.Length; ch++)
////// {
//////
var data = channels[ch];
//////
bool inCrest = false;
//////
WaveSegment current = null;
//////
for (int i = 1; i < data.Length - 1; i++)
//////
{
//////
if (ch == 0 && i % 5000 == 0 && progressBar.Value <
progressBar.Maximum)
//////
progressBar.Value = i;
//////
float a = data[i - 1];
//////
float b = data[i];
//////
float c = data[i + 1];
//////
if (a <= 0 && b > 0)
//////
{
//////
if (current != null) segments.Add(current);
//////
current = new WaveSegment { StartMicrosecond = i * 1000000 / sampleRate,
IsCrest = true, MaxAmplitude = b, Channel = ch };
//////
inCrest = true;
//////
}
////// else if (a >= 0 && b < 0)
//////
{
//////
if (current != null) segments.Add(current);
//////
current = new WaveSegment { StartMicrosecond = i * 1000000 / sampleRate,
IsCrest = false, MinAmplitude = b, Channel = ch };
//////
inCrest = false;
//////
}
//////
if (current != null)
//////
{
//////
float v1x = 1f, v1y = b - a;
////// float v2x = 1f, v2y = c - b;
//////
double angle = Math.Acos((v1x * v2x + v1y * v2y) / (Math.Sqrt(v1x * v1x
+ v1y * v1y) * Math.Sqrt(v2x * v2x + v2y * v2y))) * 180 / Math.PI;
//////
int bin = angle < 30 ? 0 : angle < 45 ? 1 : angle < 60 ? 2 : 3;
//////
current.AngleBins[bin]++;
//////
current.AngleChangeCount++;
//////
if (inCrest && b > a && b > c)
current.LocalPeaks++;
//////
if (!inCrest && b < a && b < c)
current.LocalMins++;
//////
if (inCrest) current.MaxAmplitude = Math.Max(current.MaxAmplitude, b);
//////
else current.MinAmplitude = Math.Min(current.MinAmplitude, b);
//////
current.EndMicrosecond = i * 1000000 / sampleRate;
//////
}
//////
}
//////
if (current != null) segments.Add(current);
////// }
//////
progressBar.Value = progressBar.Maximum;
//////
return segments;
////// }
////// }
////// public
static class CsvExporter
////// {
////// public
static void ExportSegments(string path, List<WaveSegment> segments)
////// {
//////
using (var sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("Channel,Type,StartUs,EndUs,MaxAmp,MinAmp,AngleChanges,Bin0,Bin1,Bin2,Bin3,Peaks,Mins");
//////
foreach (var s in segments)
//////
{
//////
sw.WriteLine($"{s.Channel},{(s.IsCrest ? "Crest" :
"Trough")},{s.StartMicrosecond},{s.EndMicrosecond},{s.MaxAmplitude},{s.MinAmplitude},{s.AngleChangeCount},{s.AngleBins[0]},{s.AngleBins[1]},{s.AngleBins[2]},{s.AngleBins[3]},{s.LocalPeaks},{s.LocalMins}");
//////
}
////// }
////// }
////// public
static void ExportPerMillisecond(string path, float[][] samples, int
sampleRate)
////// {
//////
using (var sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("Millisecond,Channel,AvgAmplitude");
//////
int totalMs = samples[0].Length * 1000 / sampleRate;
//////
for (int ch = 0; ch < samples.Length; ch++)
//////
{
//////
var data = samples[ch];
//////
for (int ms = 0; ms < totalMs; ms++)
//////
{
//////
int start = ms * sampleRate / 1000;
//////
int end = Math.Min(start + sampleRate / 1000, data.Length);
//////
float sum = 0;
////// for (int i = start; i
< end; i++) sum += Math.Abs(data[i]);
//////
float avg = sum / (end - start);
//////
sw.WriteLine($"{ms},{ch},{avg:F4}");
//////
}
////// }
////// }
////// }
////// }
////// public
static class WavRegenerator
////// {
////// public
static void SaveWav(string path, float[][] channels, int sampleRate)
////// {
//////
using (var bw = new BinaryWriter(File.Create(path)))
////// {
//////
int samples = channels[0].Length;
//////
int channelsCount = channels.Length;
//////
int byteRate = sampleRate * channelsCount * 2;
////// int blockAlign = channelsCount * 2;
//////
int dataSize = samples * blockAlign;
//////
bw.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));
//////
bw.Write(36 + dataSize);
//////
bw.Write(System.Text.Encoding.ASCII.GetBytes("WAVEfmt "));
//////
bw.Write(16);
//////
bw.Write((short)1);
//////
bw.Write((short)channelsCount);
//////
bw.Write(sampleRate);
//////
bw.Write(byteRate);
//////
bw.Write((short)blockAlign);
//////
bw.Write((short)16);
//////
bw.Write(System.Text.Encoding.ASCII.GetBytes("data"));
//////
bw.Write(dataSize);
//////
for (int i = 0; i < samples; i++)
//////
{
//////
for (int ch = 0; ch < channelsCount; ch++)
//////
{
//////
short val = (short)(Math.Max(-1f, Math.Min(1f, channels[ch][i])) *
32767);
//////
bw.Write(val);
//////
}
//////
}
////// }
////// }
////// }
////// //////public
class ThreadedWaveProcessor
////// //////{
////// ////// //????????????progressBar.Invoke((MethodInvoker)(()
=> progressBar.Value = Math.Min(value, progressBar.Maximum)));
////// ////// public void Start(string wavPath, ProgressBar bar,
Action<List<WaveSegment>> onDone)
////// ////// {
////// ////// BackgroundWorker bw = new
BackgroundWorker();
////// ////// bw.DoWork += (s, e) =>
////// ////// {
////// ////// int rate;
////// ////// bool isStereo;
////// ////// var samples = WavLoader.LoadPcmWav(wavPath,
out rate, out isStereo);
////// ////// //
var analyzer = new WaveformAnalyzer(samples, rate, ref bar);
////// ////// var analyzer = new
WaveformAnalyzer(samples, rate,ref bar);
////// ////// var result = analyzer.Analyze();
////// ////// e.Result = result;
////// ////// };
////// ////// bw.RunWorkerCompleted += (s, e) =>
////// ////// {
////// ////// onDone?.Invoke(e.Result as List<WaveSegment>);
////// ////// };
////// ////// bw.RunWorkerAsync();
////// ////// }
//////
//////}//public class ThreadedWaveProcessor
////// //////public
class ThreadedWaveProcessor
////// //////{
////// ////// public void Start(string wavPath,
ProgressBar bar, Action<List<WaveSegment>> onDone)
////// ////// {
////// ////// BackgroundWorker bw = new
BackgroundWorker();
////// ////// bw.WorkerReportsProgress = true;
////// ////// bw.DoWork += (s, e) =>
////// ////// {
////// ////// try
////// ////// {
////// ////// int sampleRate;
////// ////// bool isStereo;
////// ////// // Load stereo/mono WAV
////// ////// float[][] samples =
WavLoader.LoadPcmWav(wavPath, out sampleRate, out isStereo);
////// ////// // Analyze both channels
independently
////// ////// var allSegments = new List<WaveSegment>();
////// ////// for (int ch = 0; ch <
samples.Length; ch++)
////// ////// {
////// ////// var analyzer = new
WaveformAnalyzer(samples[ch], sampleRate, bar);
////// ////// var segments = analyzer.Analyze();
////// //////
allSegments.AddRange(segments);
////// ////// }//for (int ch = 0; ch <
samples.Length; ch++)
////// ////// e.Result = allSegments;
////// ////// }
////// ////// catch (Exception ex)
////// ////// {
////// ////// MessageBox.Show("Waveform
analysis failed:\n" + ex.Message, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
////// ////// e.Result = new
List<WaveSegment>(); // return empty result on error
////// ////// }
////// ////// };
////// ////// bw.RunWorkerCompleted += (s, e) =>
////// ////// {
////// ////// try
////// ////// {
////// ////// onDone?.Invoke(e.Result as
List<WaveSegment>);
////// ////// }
////// ////// catch (Exception ex)
////// ////// {
////// ////// MessageBox.Show("Post-processing
failed:\n" + ex.Message, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
////// ////// }
////// ////// };
////// ////// bw.RunWorkerAsync();
////// ////// }
////// //////}//public class ThreadedWaveProcessor
////// public class
ThreadedWaveProcessor___for_mono
////// {
////// public
void Start(string wavPath, ProgressBar bar,
Action<List<WaveSegment>> onDone)
////// {
////// BackgroundWorker
bw = new BackgroundWorker();
//////
bw.DoWork += (s, e) =>
////// {
//////
try
//////
{
//////
int rate;
//////
bool isStereo;
////// // Assuming your current WavLoader returns
float[] for mono
//////
float[] samples = WavLoader.LoadPcmWav(wavPath, out rate, out isStereo);
//////
var analyzer = new WaveformAnalyzer(samples, rate, ref bar);
//////
var segments = analyzer.Analyze();
//////
e.Result = segments;
//////
}
//////
catch (Exception ex)
//////
{
//////
MessageBox.Show("Waveform analysis failed:\n" + ex.Message,
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
//////
e.Result = new List<WaveSegment>();
//////
}
////// };
//////
bw.RunWorkerCompleted += (s, e) =>
////// {
//////
try
//////
{
//////
onDone?.Invoke(e.Result as List<WaveSegment>);
//////
}
//////
catch (Exception ex)
//////
{
//////
MessageBox.Show("Post-processing failed:\n" + ex.Message,
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
//////
}
////// };
//////
bw.RunWorkerAsync();
////// }
////// }// public
class ThreadedWaveProcessor___for_mono
////// public
static class DxfVisualizer
////// {
////// public
static void DrawDxf(string path, List<WaveSegment> segments, int
sampleRate)
////// {
//////
using (StreamWriter sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("0\nSECTION\n2\nENTITIES");
//////
foreach (var seg in segments)
//////
{
//////
double x1 = seg.StartMicrosecond / 1000.0;
//////
double x2 = seg.EndMicrosecond / 1000.0;
//////
double y = seg.Channel * 10 + (seg.IsCrest ? 5 : -5);
//////
sw.WriteLine("0\nLINE\n8\nwave\n10\n" + x1 +
"\n20\n" + y + "\n11\n" + x2 + "\n21\n" + y);
////// }
//////
sw.WriteLine("0\nENDSEC\n0\nEOF");
////// }
////// }
////// }
//////}//namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
///thread safe systems disturbs
//////namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////{
////// public class
CrestTroughSegment
////// {
////// public
int StartIndex { get; set; }
////// public
int EndIndex { get; set; }
////// public
float PeakValue { get; set; } // For
crests
////// public
float MinValue { get; set; } // For
troughs
////// public
string Label { get; set; } //
"crest" or "trough"
////// public
int DurationInSamples
////// {
////// get
{ return EndIndex - StartIndex + 1; }
////// }
////// public
float AverageAmplitude
////// {
////// get
{ return (Math.Abs(PeakValue) + Math.Abs(MinValue)) / 2f; }
////// }
////// public
override string ToString()
////// {
//////
return $"{Label.ToUpper()} from {StartIndex} to {EndIndex},
ΔSamples: {DurationInSamples}";
////// }
////// }// public
class CrestTroughSegment
////// public class
ThreadedWaveProcessor___for_mono
////// {
////// public
void Start(
//////
string wavFilePath,
////// ref
ProgressBar progressBar,
//////
Action<List<
//////
CrestTroughSegment
//////
>> callbackAfterProcessing)
////// {
//////
Thread processingThread = new Thread(() =>
////// {
//////
try
//////
{
//////
int sampleRate;
//////
bool isStereo;
//////
float[] samples = WavLoader.LoadPcmWav(wavFilePath, out sampleRate, out
isStereo);
//////
if (isStereo)
//////
throw new Exception("Stereo WAV not supported in mono
processor.");
////// progressBar.Invoke((MethodInvoker)(()
=>
//////
{
//////
progressBar.Minimum = 0;
//////
progressBar.Maximum = samples.Length;
//////
progressBar.Value = 0;
//////
}));
//////
List<CrestTroughSegment> segments =
CrestTroughAnalyzer.AnalyzeCrestsAndTroughs(
//////
samples, sampleRate,
//////
ref progressBar);
////// callbackAfterProcessing?.Invoke(segments);
//////
}
//////
catch (Exception ex)
//////
{
//////
MessageBox.Show("Error during waveform processing:\n" +
ex.Message);
////// }
////// });
//////
processingThread.IsBackground = true;
//////
processingThread.Start();
////// }
////// }
////// public class
WaveSegment
////// {
////// public
int StartMicrosecond;
////// public
int EndMicrosecond;
////// public
bool IsCrest;
////// public
float MaxAmplitude;
////// public
float MinAmplitude;
////// public
int AngleChangeCount;
////// public
int[] AngleBins = new int[4];
////// public
int LocalPeaks;
////// public
int LocalMins;
////// public
int Channel = 0;
////// }
////// public
static class WavLoader
////// {
////// public
static float[] LoadPcmWav(string path, out int sampleRate, out bool isStereo)
////// {
//////
sampleRate = 8000;
//////
isStereo = false;
//////
using (var br = new BinaryReader(File.OpenRead(path)))
////// {
//////
if (new string(br.ReadChars(4)) != "RIFF") throw new
Exception("Invalid WAV (no RIFF)");
//////
br.ReadInt32(); // Skip size
//////
if (new string(br.ReadChars(4)) != "WAVE") throw new Exception("Invalid
WAV (no WAVE)");
//////
if (new string(br.ReadChars(4)) != "fmt ") throw new
Exception("Expected fmt chunk");
//////
int fmtLen = br.ReadInt32();
//////
short format = br.ReadInt16();
////// short channels = br.ReadInt16();
//////
sampleRate = br.ReadInt32();
//////
br.ReadInt32(); // byte rate
//////
br.ReadInt16(); // block align
//////
short bits = br.ReadInt16();
////// if (format != 1 || bits != 16)
throw new Exception("Only PCM 16-bit supported");
//////
isStereo = channels == 2;
//////
if (isStereo) throw new Exception("Stereo not supported in this
analyzer.");
////// if (fmtLen > 16) br.ReadBytes(fmtLen
- 16);
//////
while (new string(br.ReadChars(4)) != "data")
//////
{
//////
int skip = br.ReadInt32();
//////
br.BaseStream.Seek(skip, SeekOrigin.Current);
//////
}
//////
int dataSize = br.ReadInt32();
//////
int totalSamples = dataSize / 2;
//////
float[] data = new float[totalSamples];
////// for (int i = 0; i < totalSamples;
i++)
//////
data[i] = br.ReadInt16() / 32768f;
//////
return data;
////// }
////// }
////// }
////// public class
WaveformAnalyzer
////// {
////// private readonly float[] samples;
////// private
readonly int sampleRate;
////// private
readonly ProgressBar progressBar;
////// private
readonly List<WaveSegment> segments = new List<WaveSegment>();
////// public
WaveformAnalyzer(float[] samples, int sampleRate, ref ProgressBar progressBar)
////// {
//////
this.samples = samples;
//////
this.sampleRate = sampleRate;
//////
this.progressBar = progressBar;
////// }
////// public List<WaveSegment> Analyze()
////// {
////// int
samplesPerMs = sampleRate / 1000;
//////
progressBar.Minimum = 0;
//////
progressBar.Maximum = samples.Length;
//////
progressBar.Value = 0;
////// bool
inCrest = false;
//////
WaveSegment current = null;
////// for
(int i = 1; i < samples.Length - 1; i++)
////// {
//////
if (i % 5000 == 0 && progressBar.Value < progressBar.Maximum)
////// progressBar.Invoke((MethodInvoker)(()
=> progressBar.Value = i));
//////
float a = samples[i - 1];
//////
float b = samples[i];
//////
float c = samples[i + 1];
//////
if (a <= 0 && b > 0)
//////
{
//////
if (current != null) segments.Add(current);
//////
current = new WaveSegment { StartMicrosecond = i * 1000000 / sampleRate,
IsCrest = true, MaxAmplitude = b };
////// inCrest = true;
//////
}
//////
else if (a >= 0 && b < 0)
//////
{
//////
if (current != null) segments.Add(current);
//////
current = new WaveSegment { StartMicrosecond = i * 1000000 / sampleRate,
IsCrest = false, MinAmplitude = b };
//////
inCrest = false;
//////
}
//////
if (current != null)
//////
{
//////
float v1x = 1f, v1y = b - a;
//////
float v2x = 1f, v2y = c - b;
//////
double angle = Math.Acos((v1x * v2x + v1y * v2y) /
//////
(Math.Sqrt(v1x * v1x + v1y * v1y) * Math.Sqrt(v2x * v2x + v2y * v2y))) *
180 / Math.PI;
//////
int bin = angle < 30 ? 0 : angle < 45 ? 1 : angle < 60 ? 2 : 3;
//////
current.AngleBins[bin]++;
//////
current.AngleChangeCount++;
//////
if (inCrest && b > a
&& b > c) current.LocalPeaks++;
//////
if (!inCrest && b < a && b < c)
current.LocalMins++;
//////
if (inCrest) current.MaxAmplitude = Math.Max(current.MaxAmplitude, b);
//////
else current.MinAmplitude = Math.Min(current.MinAmplitude, b);
//////
current.EndMicrosecond = i * 1000000 / sampleRate;
//////
}
////// }
////// if
(current != null) segments.Add(current);
////// progressBar.Invoke((MethodInvoker)(()
=> progressBar.Value = progressBar.Maximum));
//////
return segments;
////// }
////// }
////// public
static class CsvExporter
////// {
////// public
static void ExportSegments(string path, List<WaveSegment> segments)
////// {
//////
using (var sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("Channel,Type,StartUs,EndUs,MaxAmp,MinAmp,AngleChanges,Bin0,Bin1,Bin2,Bin3,Peaks,Mins");
//////
foreach (var s in segments)
//////
sw.WriteLine($"{s.Channel},{(s.IsCrest ? "Crest" :
"Trough")},{s.StartMicrosecond},{s.EndMicrosecond},{s.MaxAmplitude},{s.MinAmplitude},{s.AngleChangeCount},{s.AngleBins[0]},{s.AngleBins[1]},{s.AngleBins[2]},{s.AngleBins[3]},{s.LocalPeaks},{s.LocalMins}");
////// }
////// }
////// public
static void ExportPerMillisecond(string path, float[] samples, int sampleRate)
////// {
//////
using (var sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("Millisecond,AvgAmplitude");
//////
int totalMs = samples.Length * 1000 / sampleRate;
//////
for (int ms = 0; ms < totalMs; ms++)
//////
{
//////
int start = ms * sampleRate / 1000;
//////
int end = Math.Min(start + sampleRate / 1000, samples.Length);
//////
float sum = 0;
//////
for (int i = start; i < end; i++) sum += Math.Abs(samples[i]);
//////
float avg = sum / (end - start);
//////
sw.WriteLine($"{ms},{avg:F4}");
////// }
////// }
////// }
////// }
////// //////public
class ThreadedWaveProcessor___for_mono
////// //////{
////// ////// public void Start(string wavPath, ref
ProgressBar bar, Action<List<WaveSegment>> onDone)
////// ////// {
////// ////// BackgroundWorker bw = new
BackgroundWorker();
////// ////// bw.DoWork += (s, e) =>
////// ////// {
////// ////// try
////// ////// {
////// ////// int rate;
////// ////// bool isStereo;
////// ////// float[] samples =
WavLoader.LoadPcmWav(wavPath, out rate, out isStereo);
////// ////// var analyzer = new
WaveformAnalyzer(samples, rate, ref bar);
////// ////// var segments =
analyzer.Analyze();
////// ////// e.Result = segments;
////// ////// }
////// ////// catch (Exception ex)
////// ////// {
////// ////// MessageBox.Show("Waveform analysis
failed:\n" + ex.Message, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
////// ////// e.Result = new
List<WaveSegment>();
////// ////// }
////// ////// };
////// //////
bw.RunWorkerCompleted += (s, e) =>
////// ////// {
////// ////// try
////// ////// {
////// ////// onDone?.Invoke(e.Result as
List<WaveSegment>);
////// ////// }
////// ////// catch (Exception ex)
////// ////// {
////// //////
MessageBox.Show("Post-processing failed:\n" + ex.Message,
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
////// ////// }
////// ////// };
////// ////// bw.RunWorkerAsync();
////// ////// }
////// //////}
////// public
static class DxfVisualizer
////// {
////// public
static void DrawDxf(string path, List<WaveSegment> segments)
////// {
//////
using (StreamWriter sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("0\nSECTION\n2\nENTITIES");
//////
foreach (var seg in segments)
//////
{
//////
double x1 = seg.StartMicrosecond / 1000.0;
//////
double x2 = seg.EndMicrosecond / 1000.0;
//////
double y = seg.IsCrest ? 10 : -10;
//////
sw.WriteLine("0\nLINE\n8\nwave\n10\n" + x1 +
"\n20\n" + y + "\n11\n" + x2 + "\n21\n" + y);
//////
}
//////
sw.WriteLine("0\nENDSEC\n0\nEOF");
////// }
////// }
////// }
//////}//namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////{
////// public class
CrestTroughSegment
////// {
////// public
int StartSample;
////// public
int EndSample;
////// public
float PeakAmplitude;
////// public
bool IsCrest;
////// }
////// public
static class WavLoader
////// {
////// //////public
static float[][] LoadPcmWav(string path, out int sampleRate, out bool stereo)
////// //////{
//////
////// using (var reader = new
BinaryReader(File.OpenRead(path)))
//////
////// {
//////
////// reader.BaseStream.Seek(24,
SeekOrigin.Begin);
//////
////// sampleRate =
reader.ReadInt32();
//////
//////
reader.BaseStream.Seek(22, SeekOrigin.Begin);
//////
////// int channels =
reader.ReadInt16();
////// ////// stereo = (channels == 2);
//////
//////
reader.BaseStream.Seek(44, SeekOrigin.Begin);
//////
////// List<float>
left = new List<float>();
//////
////// List<float>
right = new List<float>();
////// //////
while (reader.BaseStream.Position < reader.BaseStream.Length)
//////
////// {
//////
////// short sampleL =
reader.ReadInt16();
//////
////// left.Add(sampleL
/ 32768f);
////// ////// if (stereo)
//////
////// {
//////
////// short
sampleR = reader.ReadInt16();
//////
//////
right.Add(sampleR / 32768f);
//////
////// }
//////
////// }
//////
////// return stereo ? new
float[][] { left.ToArray(), right.ToArray() } : new float[][] { left.ToArray()
};
//////
////// }
////// //////}
////// public
static float[] LoadMonoPcmWav(string path, out int sampleRate)
////// {
//////
using (var reader = new BinaryReader(File.OpenRead(path)))
////// {
//////
// Read sample rate
//////
reader.BaseStream.Seek(24, SeekOrigin.Begin);
//////
sampleRate = reader.ReadInt32();
//////
// Ensure it's mono (1 channel)
//////
reader.BaseStream.Seek(22, SeekOrigin.Begin);
//////
int channels = reader.ReadInt16();
//////
if (channels != 1)
//////
throw new InvalidOperationException("Only mono WAV files are
supported.");
//////
// Seek to data section
//////
reader.BaseStream.Seek(44, SeekOrigin.Begin);
//////
List<float> samples = new List<float>();
//////
while (reader.BaseStream.Position < reader.BaseStream.Length)
//////
{
//////
short sample = reader.ReadInt16(); // 16-bit PCM
//////
samples.Add(sample / 32768f);
// Normalize to [-1, +1]
//////
}
//////
return samples.ToArray();
////// }
////// }//public
static float[] LoadMonoPcmWav(string path, out int sampleRate)
////// }
////// public
static class CrestTroughAnalyzer
////// {
////// public
static List<CrestTroughSegment> Analyze(float[] samples)
////// {
////// List<CrestTroughSegment>
segments = new List<CrestTroughSegment>();
////// for
(int i = 1; i < samples.Length - 1; i++)
////// {
//////
bool isCrest = samples[i] > samples[i - 1] && samples[i] >
samples[i + 1];
//////
bool isTrough = samples[i] < samples[i - 1] && samples[i]
< samples[i + 1];
//////
if (isCrest || isTrough)
//////
{
//////
segments.Add(new CrestTroughSegment
////// {
//////
StartSample = i,
//////
EndSample = i,
//////
PeakAmplitude = samples[i],
//////
IsCrest = isCrest
//////
});
////// }
////// }
//////
return segments;
////// }
////// }
////// public
static class CsvExporter
////// {
////// public
static void ExportSegments(string path, List<CrestTroughSegment>
segments)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Start,End,Amplitude,Type");
//////
foreach (var seg in segments)
//////
{
////// writer.WriteLine($"{seg.StartSample},{seg.EndSample},{seg.PeakAmplitude},{(seg.IsCrest
? "Crest" : "Trough")}");
//////
}
////// }
////// }
//////
//////public static void ExportPerMillisecond(string path, float[][]
samples, int sampleRate)
////// //////{
//////
////// using (StreamWriter
writer = new StreamWriter(path))
//////
////// {
//////
//////
writer.WriteLine("Millisecond,Amplitude");
//////
////// float[] mono =
samples[0];
//////
////// int samplesPerMs =
sampleRate / 1000;
//////
////// for (int i = 0; i
< mono.Length; i += samplesPerMs)
//////
////// {
//////
////// float sum = 0;
////// ////// int count = 0;
//////
////// for (int j = i;
j < i + samplesPerMs && j < mono.Length; j++)
//////
////// {
//////
////// sum +=
Math.Abs(mono[j]);
//////
////// count++;
//////
////// }
//////
////// float avg =
(count > 0) ? sum / count : 0;
//////
//////
writer.WriteLine($"{i / samplesPerMs},{avg}");
//////
////// }
////// ////// }
//////
//////}//public static void ExportPerMillisecond(string path, float[][]
samples, int sampleRate)
////// public
static void ExportPerMillisecond(string path, float[] samples, int sampleRate)
////// {
////// using
(StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Millisecond,Amplitude");
//////
float[] mono = samples; // samples[0];
//////
int samplesPerMs = sampleRate / 1000;
//////
for (int i = 0; i < mono.Length; i += samplesPerMs)
//////
{
//////
float sum = 0;
//////
int count = 0;
//////
for (int j = i; j < i + samplesPerMs && j < mono.Length;
j++)
//////
{
//////
sum += Math.Abs(mono[j]);
//////
count++;
//////
}
//////
float avg = (count > 0) ? sum / count : 0;
//////
writer.WriteLine($"{i /
samplesPerMs},{avg}");
//////
}
////// }
//////
}//public static void ExportPerMillisecond(string path, float[] samples,
int sampleRate)
////// }
////// public
static class DxfVisualizer
////// {
////// public
static void DrawDxf(string path, List<CrestTroughSegment> segments, int
sampleRate)
////// {
//////
using (StreamWriter sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("0\nSECTION\n2\nENTITIES");
//////
foreach (var seg in segments)
//////
{
//////
double x = seg.StartSample * (1.0 / sampleRate);
//////
double y = seg.PeakAmplitude * 100;
//////
sw.WriteLine("0\nTEXT");
//////
sw.WriteLine($"8\nCRESTTROUGHS\n10\n{x}\n20\n{y}\n40\n1.0\n1\n{(seg.IsCrest
? "C" : "T")}");
//////
}
//////
sw.WriteLine("0\nENDSEC\n0\nEOF");
////// }
////// }
////// }
////// public
class WaveProcessor
////// {
//////
////// // public void Start(string wavPath, ProgressBar progressBar, Action<List<CrestTroughSegment>>
onComplete)
//////
//////public List<CrestTroughSegment> Start(string path, ref
ProgressBar progressBar)
//////
////// {
//////
////// int sampleRate;
//////
////// bool stereo;
////// ////// // float[][] wavSamples =
WavLoader.LoadPcmWav(wavPath, out sampleRate, out stereo);
//////
////// //float[][]
wavSamples = WavLoader.LoadPcmWav(wavPath, out sampleRate, out stereo);
//////
////// float[]
monoSamples = SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform.WavLoader
//////
//////
.LoadMonoPcmWav(wavPath, out sampleRate);
//////
////// // .LoadPcmWav(wavPath, out sampleRate);
//////
////// float[] mono = monoSamples;//
wavSamples[0];
//////
//////
progressBar.Minimum = 0;
//////
//////
progressBar.Maximum = mono.Length;
//////
//////
progressBar.Value = 0;
//////
////// List<CrestTroughSegment> segments =
CrestTroughAnalyzer.Analyze(mono);
//////
////// for (int i = 0;
i < mono.Length; i += 1000)
//////
////// {
//////
//////
progressBar.Value = Math.Min(i, mono.Length);
//////
//////
Application.DoEvents();
//////
////// }
//////
//////
onComplete?.Invoke(segments);
//////
////// }
////// public
List<CrestTroughSegment> Start(string wavPath, ref ProgressBar
progressBar)
////// {
////// int
sampleRate;
//////
float[] monoSamples = WavLoader.LoadMonoPcmWav(wavPath, out sampleRate);
//////
progressBar.Minimum = 0;
//////
progressBar.Maximum = monoSamples.Length;
//////
progressBar.Value = 0;
//////
List<CrestTroughSegment> segments =
CrestTroughAnalyzer.Analyze(monoSamples);
////// //
Simulate progress for visual feedback
////// for
(int i = 0; i < monoSamples.Length; i += 1000)
////// {
//////
progressBar.Value = Math.Min(i, monoSamples.Length - 1);
//////
Application.DoEvents();
////// }
////// progressBar.Value
= monoSamples.Length;
//////
return segments;
////// }//
public List<CrestTroughSegment> Start(string wavPath, ref ProgressBar
progressBar)
////// }// public
class WaveProcessor
//////}//namespace SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////{
////// public class
CrestTroughSegment
////// {
////// public
int StartSample;
////// public
int EndSample;
////// public
float PeakAmplitude;
////// public
bool IsCrest;
////// }
////// public
static class WavLoader
////// {
////// public
static float[] LoadMonoPcmWav(string path, out int sampleRate)
////// {
//////
using (var reader = new BinaryReader(File.OpenRead(path)))
////// {
//////
reader.BaseStream.Seek(24, SeekOrigin.Begin);
//////
sampleRate = reader.ReadInt32();
//////
reader.BaseStream.Seek(22, SeekOrigin.Begin);
//////
int channels = reader.ReadInt16();
//////
if (channels != 1)
//////
throw new InvalidOperationException("Only mono WAV files are
supported.");
////// reader.BaseStream.Seek(44,
SeekOrigin.Begin);
//////
List<float> samples = new List<float>();
//////
while (reader.BaseStream.Position < reader.BaseStream.Length)
//////
{
//////
short sample = reader.ReadInt16();
//////
samples.Add(sample / 32768f);
//////
}
//////
return samples.ToArray();
////// }
////// }
////// }
////// public
static class CrestTroughAnalyzer
////// {
////// public
static List<CrestTroughSegment> Analyze(float[] samples)
////// {
//////
List<CrestTroughSegment> segments = new
List<CrestTroughSegment>();
////// for
(int i = 1; i < samples.Length - 1; i++)
////// {
//////
bool isCrest = samples[i] > samples[i - 1] && samples[i] >
samples[i + 1];
//////
bool isTrough = samples[i] < samples[i - 1] && samples[i]
< samples[i + 1];
//////
if (isCrest || isTrough)
//////
{
//////
segments.Add(new CrestTroughSegment
//////
{
//////
StartSample = i,
//////
EndSample = i,
//////
PeakAmplitude = samples[i],
//////
IsCrest = isCrest
//////
});
//////
}
////// }
//////
return segments;
////// }
////// }
////// public
static class CrestTroughFilter
////// {
////// public
static bool IsSmoothCrest(float[] samples, int start, int end)
////// {
////// for
(int i = start + 1; i < end - 1; i++)
////// {
//////
float prev = samples[i - 1];
////// float curr = samples[i];
//////
float next = samples[i + 1];
//////
if (curr < prev && curr < next) return false;
////// }
//////
return true;
////// }
////// public
static bool IsSmoothTrough(float[] samples, int start, int end)
////// {
////// for
(int i = start + 1; i < end - 1; i++)
////// {
//////
float prev = samples[i - 1];
//////
float curr = samples[i];
//////
float next = samples[i + 1];
//////
if (curr > prev && curr > next) return false;
////// }
//////
return true;
////// }
////// public
static List<CrestTroughSegment> FilterSmoothSegments(List<CrestTroughSegment>
segments, float[] samples)
////// {
//////
List<CrestTroughSegment> result = new
List<CrestTroughSegment>();
//////
foreach (var seg in segments)
////// {
////// bool
isSmooth = seg.IsCrest
//////
? IsSmoothCrest(samples, seg.StartSample, seg.EndSample)
//////
: IsSmoothTrough(samples, seg.StartSample, seg.EndSample);
//////
if (isSmooth)
//////
result.Add(seg);
////// }
//////
return result;
////// }
////// }
////// public
static class CsvExporter
////// {
////// public
static void ExportSegments(string path, List<CrestTroughSegment>
segments)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Start,End,Amplitude,Type");
//////
foreach (var seg in segments)
//////
{
//////
writer.WriteLine($"{seg.StartSample},{seg.EndSample},{seg.PeakAmplitude},{(seg.IsCrest
? "Crest" : "Trough")}");
//////
}
////// }
////// }
////// public
static void ExportPerMillisecond(string path, float[] samples, int sampleRate)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Millisecond,Amplitude");
//////
int samplesPerMs = sampleRate / 1000;
//////
for (int i = 0; i < samples.Length; i += samplesPerMs)
//////
{
//////
float sum = 0;
//////
int count = 0;
//////
for (int j = i; j < i + samplesPerMs && j <
samples.Length; j++)
//////
{
//////
sum += Math.Abs(samples[j]);
//////
count++;
//////
}
//////
float avg = (count > 0) ? sum / count : 0;
//////
writer.WriteLine($"{i / samplesPerMs},{avg}");
//////
}
////// }
////// }
////// public
static void ExportFilteredSegments(string path, List<CrestTroughSegment>
segments)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Type,StartSample,EndSample,DurationSamples");
//////
foreach (var seg in segments)
//////
{
//////
writer.WriteLine($"{(seg.IsCrest ? "Crest" :
"Trough")},{seg.StartSample},{seg.EndSample},{seg.EndSample -
seg.StartSample}");
//////
}
////// }
////// }
////// }
////// public
static class DxfVisualizer
////// {
////// public
static void DrawDxf(string path, List<CrestTroughSegment> segments, int
sampleRate)
////// {
//////
using (StreamWriter sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("0\nSECTION\n2\nENTITIES");
//////
foreach (var seg in segments)
//////
{
//////
double x = seg.StartSample * (1.0 / sampleRate);
//////
double y = seg.PeakAmplitude * 100;
////// sw.WriteLine("0\nTEXT");
//////
sw.WriteLine($"8\nCRESTTROUGHS\n10\n{x}\n20\n{y}\n40\n1.0\n1\n{(seg.IsCrest
? "C" : "T")}");
//////
}
//////
sw.WriteLine("0\nENDSEC\n0\nEOF");
////// }
////// }
////// }
////// public
static class WavFilterSaver
////// {
//////
//////public static void SaveFilteredWav(string outputPath, float[]
originalSamples, List<CrestTroughSegment> segments)
////// //////{
//////
////// float[] filtered = new
float[originalSamples.Length];
//////
////// foreach (var segment in
segments)
//////
////// {
//////
////// for (int i =
segment.StartSample; i <= segment.EndSample; i++)
////// ////// {
//////
////// if (i >= 0
&& i < originalSamples.Length)
//////
////// filtered[i]
= originalSamples[i];
//////
////// }
//////
////// }
//////
////// using (var writer = new
BinaryWriter(File.Create(outputPath)))
//////
////// {
//////
////// int sampleRate =
8000;
//////
////// int bitsPerSample =
16;
//////
////// short numChannels =
1;
//////
////// short blockAlign = (short)(numChannels *
bitsPerSample / 8);
//////
////// int byteRate =
sampleRate * blockAlign;
//////
////// int dataLength =
filtered.Length * blockAlign;
//////
////// writer.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));
//////
////// writer.Write(36 +
dataLength);
//////
//////
writer.Write(System.Text.Encoding.ASCII.GetBytes("WAVE"));
//////
//////
writer.Write(System.Text.Encoding.ASCII.GetBytes("fmt "));
//////
////// writer.Write(16);
//////
//////
writer.Write((short)1);
//////
//////
writer.Write(numChannels);
//////
//////
writer.Write(sampleRate);
//////
////// writer.Write(byteRate);
//////
//////
writer.Write(blockAlign);
//////
//////
writer.Write((short)bitsPerSample);
//////
//////
writer.Write(System.Text.Encoding.ASCII.GetBytes("data"));
//////
////// writer.Write(dataLength);
//////
////// foreach (float s in
filtered)
//////
//////
writer.Write((short)(Math.Max(-1f, Math.Min(1f, s)) * 32767));
//////
////// }
////// //////}
////// public
static void SaveFilteredWav(string outputPath, float[] samples,
List<CrestTroughSegment> segments, int sampleRate)
////// {
//////
using (BinaryWriter writer = new BinaryWriter(File.Create(outputPath)))
////// {
////// int byteRate = sampleRate * 2; //
mono, 16-bit
//////
int dataSize = samples.Length * 2;
//////
// WAV header
//////
writer.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));
////// writer.Write(36 + dataSize); // file size - 8
//////
writer.Write(System.Text.Encoding.ASCII.GetBytes("WAVEfmt "));
//////
writer.Write(16); // PCM chunk size
//////
writer.Write((short)1); // audio format
//////
writer.Write((short)1); // mono
//////
writer.Write(sampleRate);
//////
writer.Write(byteRate);
//////
writer.Write((short)2); // block align
//////
writer.Write((short)16); // bits per sample
//////
writer.Write(System.Text.Encoding.ASCII.GetBytes("data"));
//////
writer.Write(dataSize);
//////
// Segment filtering: only include samples in the filtered
crests/troughs
////// HashSet<int> included = new
HashSet<int>();
//////
foreach (var seg in segments)
//////
{
//////
for (int i = seg.StartSample; i <= seg.EndSample && i <
samples.Length; i++)
//////
included.Add(i);
//////
}
//////
for (int i = 0; i < samples.Length; i++)
//////
{
//////
short sample = (short)(included.Contains(i) ? samples[i] * 32767f : 0);
//////
writer.Write(sample);
//////
}
////// }
////// }
////// }
////// public class
WaveProcessor
////// {
////// public
List<CrestTroughSegment> Start(string wavPath, ref ProgressBar
progressBar)
////// {
////// int
sampleRate;
//////
float[] monoSamples = WavLoader.LoadMonoPcmWav(wavPath, out sampleRate);
//////
progressBar.Minimum = 0;
//////
progressBar.Maximum = monoSamples.Length;
//////
progressBar.Value = 0;
//////
List<CrestTroughSegment> segments =
CrestTroughAnalyzer.Analyze(monoSamples);
////// for
(int i = 0; i < monoSamples.Length; i += 1000)
////// {
//////
progressBar.Value = Math.Min(i, monoSamples.Length - 1);
//////
Application.DoEvents();
////// }
//////
progressBar.Value = monoSamples.Length;
//////
return segments;
////// }
////// }
//////}//namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////{
////// public class
CrestTroughSegment
////// {
////// public
int StartSample;
////// public int
EndSample;
////// public
float PeakAmplitude;
////// public
bool IsCrest;
////// }
////// public
static class WavLoader
////// {
////// public
static float[] LoadMonoPcmWav(string path, out int sampleRate)
////// {
//////
using (var reader = new BinaryReader(File.OpenRead(path)))
////// {
//////
reader.BaseStream.Seek(24, SeekOrigin.Begin);
//////
sampleRate = reader.ReadInt32();
////// reader.BaseStream.Seek(22,
SeekOrigin.Begin);
//////
int channels = reader.ReadInt16();
//////
if (channels != 1)
//////
throw new InvalidOperationException("Only mono WAV files are
supported.");
////// reader.BaseStream.Seek(44,
SeekOrigin.Begin);
//////
List<float> samples = new List<float>();
//////
while (reader.BaseStream.Position < reader.BaseStream.Length)
//////
{
//////
short sample = reader.ReadInt16();
//////
samples.Add(sample / 32768f);
//////
}
//////
return samples.ToArray();
////// }
////// }
////// }
////// public
static class CrestTroughAnalyzer
////// {
////// public
static List<CrestTroughSegment> Analyze(float[] samples)
////// {
//////
List<CrestTroughSegment> segments = new
List<CrestTroughSegment>();
////// for
(int i = 1; i < samples.Length - 1; i++)
////// {
//////
bool isCrest = samples[i] > samples[i - 1] && samples[i] >
samples[i + 1];
//////
bool isTrough = samples[i] < samples[i - 1] && samples[i]
< samples[i + 1];
////// if
(isCrest || isTrough)
//////
{
//////
segments.Add(new CrestTroughSegment
//////
{
//////
StartSample = i,
//////
EndSample = i,
//////
PeakAmplitude = samples[i],
//////
IsCrest = isCrest
//////
});
//////
}
////// }
//////
return segments;
////// }
////// }
////// public
static class CsvExporter
////// {
////// public
static void ExportSegments(string path, List<CrestTroughSegment>
segments)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Start,End,Amplitude,Type");
//////
foreach (var seg in segments)
//////
{
//////
writer.WriteLine($"{seg.StartSample},{seg.EndSample},{seg.PeakAmplitude},{(seg.IsCrest
? "Crest" : "Trough")}");
////// }
////// }
////// }
////// public
static void ExportPerMillisecond(string path, float[] samples, int sampleRate)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Millisecond,Amplitude");
//////
int samplesPerMs = sampleRate / 1000;
//////
for (int i = 0; i < samples.Length; i += samplesPerMs)
//////
{
//////
float sum = 0;
//////
int count = 0;
//////
for (int j = i; j < i + samplesPerMs && j <
samples.Length; j++)
//////
{
//////
sum += Math.Abs(samples[j]);
//////
count++;
//////
}
//////
float avg = (count > 0) ? sum / count : 0;
//////
writer.WriteLine($"{i / samplesPerMs},{avg}");
//////
}
////// }
////// }
////// public static void
ExportFrequencies(string path,
List<WaveformFrequencyEstimator.FrequencyPoint> frequencies)
////// {
//////
using (StreamWriter sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("StartMs,DurationMs,FrequencyHz,MidiNote");
//////
foreach (var f in frequencies)
//////
{
//////
sw.WriteLine($"{f.StartMs},{f.DurationMs},{f.FrequencyHz:F2},{f.MidiNote}");
//////
}
////// }
////// }
////// public
static void ExportSimilarSegmentCount(string path, Dictionary<string,
int> patternCounts)
////// {
//////
using (StreamWriter sw = new StreamWriter(path))
////// {
////// sw.WriteLine("PatternHash,Count");
//////
foreach (var kvp in patternCounts)
//////
{
//////
sw.WriteLine($"{kvp.Key},{kvp.Value}");
//////
}
////// }
////// }
////// }
////// public
static class DxfVisualizer
////// {
////// public
static void DrawDxf(string path, List<CrestTroughSegment> segments, int
sampleRate)
////// {
//////
using (StreamWriter sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("0\nSECTION\n2\nENTITIES");
//////
foreach (var seg in segments)
//////
{
//////
double x = seg.StartSample * (1.0 / sampleRate);
//////
double y = seg.PeakAmplitude * 100;
//////
sw.WriteLine("0\nTEXT");
//////
sw.WriteLine($"8\nCRESTTROUGHS\n10\n{x}\n20\n{y}\n40\n1.0\n1\n{(seg.IsCrest
? "C" : "T")}");
//////
}
////// sw.WriteLine("0\nENDSEC\n0\nEOF");
////// }
////// }
////// }
////// public
static class WaveformFrequencyEstimator
////// {
////// public
class FrequencyPoint
////// {
//////
public int StartMs;
////// public int DurationMs;
//////
public float FrequencyHz;
//////
public int MidiNote;
////// }
////// public
static List<FrequencyPoint> EstimateFrequencies(float[] samples, int
sampleRate)
////// {
////// int
samplesPerMs = sampleRate / 1000;
//////
List<FrequencyPoint> result = new List<FrequencyPoint>();
////// for
(int ms = 0; ms < samples.Length / samplesPerMs; ms++)
////// {
//////
int start = ms * samplesPerMs;
//////
int end = Math.Min(start + samplesPerMs, samples.Length);
//////
int zeroCrossings = 0;
//////
for (int i = start + 1; i < end; i++)
//////
{
//////
if ((samples[i - 1] >= 0
&& samples[i] < 0) || (samples[i - 1] < 0 && samples[i]
>= 0))
//////
zeroCrossings++;
//////
}
//////
float frequency = zeroCrossings * (sampleRate / (2f * samplesPerMs));
//////
int midiNote = (int)(69 + 12 * Math.Log(frequency / 440.0, 2));
//////
if (frequency > 20 && frequency < 20000)
//////
{
//////
result.Add(new FrequencyPoint
//////
{
//////
StartMs = ms,
//////
DurationMs = 1,
//////
FrequencyHz = frequency,
//////
MidiNote = midiNote
//////
});
//////
}
////// }
//////
return result;
////// }
////// }
////// public
static class WaveformPatternComparer
////// {
////// public
static Dictionary<string, int>
CountSimilarSegments(List<CrestTroughSegment> segments, float[] samples)
////// {
//////
Dictionary<string, int> patternCount = new Dictionary<string,
int>();
//////
foreach (var seg in segments)
////// {
//////
int length = seg.EndSample - seg.StartSample + 1;
//////
if (length <= 0 || seg.StartSample + length > samples.Length)
//////
continue;
//////
string pattern = "";
//////
for (int i = 1; i < length; i++)
//////
{
////// float delta =
samples[seg.StartSample + i] - samples[seg.StartSample + i - 1];
//////
pattern += delta >= 0 ? "+" : "-";
//////
}
//////
if (!patternCount.ContainsKey(pattern))
////// patternCount[pattern] = 0;
//////
patternCount[pattern]++;
////// }
//////
return patternCount;
////// }
////// }
////// public class
WaveProcessor
////// {
////// public
List<CrestTroughSegment> Start(string wavPath, ref ProgressBar
progressBar)
////// {
////// int
sampleRate;
//////
float[] monoSamples = WavLoader.LoadMonoPcmWav(wavPath, out sampleRate);
//////
progressBar.Minimum = 0;
////// progressBar.Maximum =
monoSamples.Length;
//////
progressBar.Value = 0;
//////
List<CrestTroughSegment> segments =
CrestTroughAnalyzer.Analyze(monoSamples);
////// for
(int i = 0; i < monoSamples.Length; i += 1000)
////// {
//////
progressBar.Value = Math.Min(i, monoSamples.Length - 1);
//////
Application.DoEvents();
////// }
//////
progressBar.Value = monoSamples.Length;
//////
return segments;
////// }
////// }
//////}//namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
////////////namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
////////////{
//////////// public
class CrestTroughSegment
//////////// {
////////////
public int StartSample;
////////////
public int EndSample;
////////////
public float PeakAmplitude;
////////////
public bool IsCrest;
//////////// }
//////////// public
class FrequencyInfo
//////////// {
////////////
public int StartMs;
////////////
public int DurationMs;
////////////
public float Frequency;
////////////
public int MidiNote;
//////////// }
//////////// public
static class WavLoader
//////////// {
////////////
public static float[] LoadMonoPcmWav(string path, out int sampleRate)
//////////// {
////////////
using (var reader = new BinaryReader(File.OpenRead(path)))
////////////
{
////////////
reader.BaseStream.Seek(24,
SeekOrigin.Begin);
////////////
sampleRate = reader.ReadInt32();
////////////
reader.BaseStream.Seek(22, SeekOrigin.Begin);
////////////
int channels = reader.ReadInt16();
////////////
if (channels != 1)
//////////// throw new
InvalidOperationException("Only mono WAV files are supported.");
////////////
reader.BaseStream.Seek(44, SeekOrigin.Begin);
////////////
List<float> samples = new List<float>();
////////////
while (reader.BaseStream.Position < reader.BaseStream.Length)
////////////
{
//////////// short sample =
reader.ReadInt16();
//////////// samples.Add(sample /
32768f);
////////////
}
////////////
return samples.ToArray();
////////////
}
//////////// }
//////////// }
//////////// public
static class CrestTroughAnalyzer
//////////// {
////////////
public static List<CrestTroughSegment> Analyze(float[] samples)
//////////// {
////////////
List<CrestTroughSegment> segments = new
List<CrestTroughSegment>();
////////////
for (int i = 1; i < samples.Length - 1; i++)
////////////
{
////////////
bool isCrest = samples[i] > samples[i - 1] && samples[i] >
samples[i + 1];
////////////
bool isTrough = samples[i] < samples[i - 1] && samples[i]
< samples[i + 1];
//////////// if (isCrest || isTrough)
////////////
{
//////////// segments.Add(new
CrestTroughSegment
//////////// {
//////////// StartSample = i,
//////////// EndSample = i,
//////////// PeakAmplitude =
samples[i],
//////////// IsCrest = isCrest
//////////// });
////////////
}
////////////
}
////////////
return segments;
//////////// }
//////////// }
//////////// public
static class CrestTroughFilter
//////////// {
////////////
public static List<CrestTroughSegment>
FilterSmoothSegments(List<CrestTroughSegment> segments, float[] samples)
//////////// {
////////////
List<CrestTroughSegment> filtered = new
List<CrestTroughSegment>();
////////////
foreach (var seg in segments)
////////////
{
////////////
bool sharp = false;
//////////// for (int i = seg.StartSample -
2; i <= seg.EndSample + 2 && i + 2 < samples.Length && i
> 1; i++)
////////////
{
//////////// float a = samples[i - 1];
//////////// float b = samples[i];
//////////// float c = samples[i + 1];
//////////// float angle =
Math.Abs((float)(Math.Atan2(c - b, 1) - Math.Atan2(b - a, 1)));
//////////// if (angle > Math.PI / 4)
//////////// {
//////////// sharp = true;
//////////// break;
//////////// }
////////////
}
////////////
if (!sharp)
//////////// filtered.Add(seg);
////////////
}
////////////
return filtered;
//////////// }
//////////// }
//////////// public
static class CsvExporter
//////////// {
////////////
public static void ExportSegments(string path, List<CrestTroughSegment>
segments)
//////////// {
////////////
using (StreamWriter writer = new StreamWriter(path))
////////////
{
////////////
writer.WriteLine("Start,End,Amplitude,Type");
////////////
foreach (var seg in segments)
////////////
{
////////////
writer.WriteLine($"{seg.StartSample},{seg.EndSample},{seg.PeakAmplitude},{(seg.IsCrest
? "Crest" : "Trough")}");
////////////
}
////////////
}
//////////// }
////////////
public static void ExportFilteredSegments(string path,
List<CrestTroughSegment> segments)
//////////// {
////////////
ExportSegments(path, segments);
//////////// }
//////////// public
static void ExportPerMillisecond(string path, float[] samples, int sampleRate)
//////////// {
////////////
using (StreamWriter writer = new StreamWriter(path))
////////////
{
////////////
writer.WriteLine("Millisecond,Amplitude");
////////////
int samplesPerMs = sampleRate / 1000;
////////////
for (int i = 0; i < samples.Length; i += samplesPerMs)
////////////
{
//////////// float sum = 0;
//////////// int count = 0;
//////////// for (int j = i; j < i +
samplesPerMs && j < samples.Length; j++)
//////////// {
//////////// sum +=
Math.Abs(samples[j]);
//////////// count++;
//////////// }
//////////// float avg = (count > 0)
? sum / count : 0;
//////////// writer.WriteLine($"{i
/ samplesPerMs},{avg}");
////////////
}
////////////
}
//////////// }
////////////
public static void ExportFrequencies(string path,
List<FrequencyInfo> freqs)
//////////// {
////////////
using (StreamWriter writer = new StreamWriter(path))
//////////// {
////////////
writer.WriteLine("StartMs,DurationMs,Frequency,MIDINote");
////////////
foreach (var f in freqs)
////////////
{
//////////// writer.WriteLine($"{f.StartMs},{f.DurationMs},{f.Frequency},{f.MidiNote}");
////////////
}
////////////
}
//////////// }
//////////// }
//////////// public
static class WavFilterSaver
//////////// {
////////////
public static void SaveFilteredWav(string outPath, float[] allSamples,
List<CrestTroughSegment> segments, int sampleRate)
//////////// {
////////////
short[] output = new short[allSamples.Length];
////////////
foreach (var seg in segments)
////////////
{
////////////
for (int i = seg.StartSample; i <= seg.EndSample && i <
allSamples.Length; i++)
////////////
{
//////////// output[i] =
(short)(Math.Max(-1f, Math.Min(1f, allSamples[i])) * 32767);
////////////
}
////////////
}
////////////
using (var writer = new BinaryWriter(File.Create(outPath)))
////////////
{
////////////
int byteRate = sampleRate * 2;
//////////// writer.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));
////////////
writer.Write(36 + output.Length * 2);
////////////
writer.Write(System.Text.Encoding.ASCII.GetBytes("WAVEfmt "));
////////////
writer.Write(16);
////////////
writer.Write((short)1);
////////////
writer.Write((short)1);
////////////
writer.Write(sampleRate);
////////////
writer.Write(byteRate);
////////////
writer.Write((short)2);
////////////
writer.Write((short)16);
////////////
writer.Write(System.Text.Encoding.ASCII.GetBytes("data"));
////////////
writer.Write(output.Length * 2);
////////////
foreach (short s in output)
////////////
{
//////////// writer.Write(s);
////////////
}
////////////
}
//////////// }
//////////// }
//////////// public
class WaveProcessor
//////////// {
////////////
public List<CrestTroughSegment> Start(string wavPath, ref
ProgressBar progressBar)
//////////// {
////////////
int sampleRate;
////////////
float[] monoSamples = WavLoader.LoadMonoPcmWav(wavPath, out sampleRate);
////////////
progressBar.Minimum = 0;
////////////
progressBar.Maximum = monoSamples.Length;
////////////
progressBar.Value = 0;
////////////
List<CrestTroughSegment> segments = CrestTroughAnalyzer.Analyze(monoSamples);
////////////
for (int i = 0; i < monoSamples.Length; i += 1000)
////////////
{
////////////
progressBar.Value = Math.Min(i, monoSamples.Length - 1);
////////////
Application.DoEvents();
////////////
}
////////////
progressBar.Value = monoSamples.Length;
////////////
return segments;
//////////// }
//////////// }
////////////} // end namespace
//////namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
//////{
////// public class
CrestTroughSegment
////// {
////// public
int StartSample;
////// public
int EndSample;
////// public
float PeakAmplitude;
////// public
bool IsCrest;
////// }
////// public class
FrequencyInfo
////// {
////// public
int StartMillisecond;
////// public
int DurationMs;
////// public
float FrequencyHz;
////// public
int MidiNoteNumber;
////// }
////// public
static class WavLoader
////// {
////// public
static float[] LoadMonoPcmWav(string path, out int sampleRate)
////// {
//////
using (var reader = new BinaryReader(File.OpenRead(path)))
////// {
////// reader.BaseStream.Seek(24,
SeekOrigin.Begin);
//////
sampleRate = reader.ReadInt32();
//////
reader.BaseStream.Seek(22, SeekOrigin.Begin);
//////
int channels = reader.ReadInt16();
////// if (channels != 1)
//////
throw new InvalidOperationException("Only mono WAV files are
supported.");
//////
reader.BaseStream.Seek(44, SeekOrigin.Begin);
//////
List<float> samples = new List<float>();
//////
while (reader.BaseStream.Position < reader.BaseStream.Length)
//////
{
//////
short sample = reader.ReadInt16();
//////
samples.Add(sample / 32768f);
//////
}
//////
return samples.ToArray();
////// }
////// }
////// }
////// public
static class CrestTroughAnalyzer
////// {
////// public
static List<CrestTroughSegment> Analyze(float[] samples)
////// {
////// List<CrestTroughSegment>
segments = new List<CrestTroughSegment>();
////// for
(int i = 1; i < samples.Length - 1; i++)
////// {
//////
bool isCrest = samples[i] > samples[i - 1] && samples[i] >
samples[i + 1];
//////
bool isTrough = samples[i] < samples[i - 1] && samples[i]
< samples[i + 1];
//////
if (isCrest || isTrough)
//////
{
//////
segments.Add(new CrestTroughSegment
//////
{
//////
StartSample = i,
//////
EndSample = i,
//////
PeakAmplitude = samples[i],
//////
IsCrest = isCrest
//////
});
//////
}
////// }
//////
return segments;
////// }
////// }
////// public
static class CrestTroughFilter
////// {
////// public
static List<CrestTroughSegment>
FilterSmoothSegments(List<CrestTroughSegment> segments, float[] samples)
////// {
//////
List<CrestTroughSegment> result = new
List<CrestTroughSegment>();
//////
foreach (var seg in segments)
////// {
//////
int idx = seg.StartSample;
//////
if (idx < 2 || idx > samples.Length - 3) continue;
//////
float prev = samples[idx - 1];
//////
float curr = samples[idx];
//////
float next = samples[idx + 1];
////// float
angle1 = Math.Abs(curr - prev);
//////
float angle2 = Math.Abs(curr - next);
//////
if (angle1 < 0.2f && angle2 < 0.2f)
//////
result.Add(seg);
////// }
//////
return result;
////// }
////// }
////// public
static class CsvExporter
////// {
////// public
static void ExportSegments(string path, List<CrestTroughSegment>
segments)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Start,End,Amplitude,Type");
//////
foreach (var seg in segments)
//////
{
//////
writer.WriteLine($"{seg.StartSample},{seg.EndSample},{seg.PeakAmplitude},{(seg.IsCrest
? "Crest" : "Trough")}");
//////
}
////// }
////// }
////// public
static void ExportFilteredSegments(string path, List<CrestTroughSegment>
segments)
////// {
////// ExportSegments(path,
segments);
////// }
////// public
static void ExportPerMillisecond(string path, float[] samples, int sampleRate)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Millisecond,Amplitude");
//////
int samplesPerMs = sampleRate / 1000;
//////
for (int i = 0; i < samples.Length; i += samplesPerMs)
//////
{
//////
float sum = 0;
//////
int count = 0;
//////
for (int j = i; j < i + samplesPerMs && j <
samples.Length; j++)
//////
{
//////
sum += Math.Abs(samples[j]);
//////
count++;
//////
}
//////
float avg = (count > 0) ? sum / count : 0;
//////
writer.WriteLine($"{i / samplesPerMs},{avg}");
//////
}
////// }
////// }
////// public static void
ExportSimilarSegmentCount(string path, Dictionary<string, int>
patternCounts)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("Pattern,Count");
//////
foreach (var pair in patternCounts)
//////
writer.WriteLine($"{pair.Key},{pair.Value}");
////// }
////// }
////// public
static void ExportFrequencies(string path, List<FrequencyInfo> freqList)
////// {
//////
using (StreamWriter writer = new StreamWriter(path))
////// {
//////
writer.WriteLine("StartMs,DurationMs,Freq,MidiNote");
//////
foreach (var f in freqList)
////// writer.WriteLine($"{f.StartMillisecond},{f.DurationMs},{f.FrequencyHz},{f.MidiNoteNumber}");
////// }
////// }
////// }
////// public
static class DxfVisualizer
////// {
////// public
static void DrawDxf(string path, List<CrestTroughSegment> segments, int
sampleRate)
////// {
//////
using (StreamWriter sw = new StreamWriter(path))
////// {
//////
sw.WriteLine("0\nSECTION\n2\nENTITIES");
//////
foreach (var seg in segments)
//////
{
//////
double x = seg.StartSample * (1.0 / sampleRate);
//////
double y = seg.PeakAmplitude * 100;
//////
sw.WriteLine("0\nTEXT");
////// sw.WriteLine($"8\nCRESTTROUGHS\n10\n{x}\n20\n{y}\n40\n1.0\n1\n{(seg.IsCrest
? "C" : "T")}");
//////
}
//////
sw.WriteLine("0\nENDSEC\n0\nEOF");
////// }
////// }
////// }
////// public
static class WavFilterSaver
////// {
////// public
static void SaveFilteredWav(string outputPath, float[] originalSamples,
List<CrestTroughSegment> filteredSegments, int sampleRate)
////// {
//////
short[] output = new short[originalSamples.Length];
//////
foreach (var seg in filteredSegments)
////// {
//////
output[seg.StartSample] = (short)(originalSamples[seg.StartSample] *
32767);
////// }
//////
using (var writer = new BinaryWriter(File.Create(outputPath)))
////// {
//////
int byteRate = sampleRate * 2;
//////
writer.Write(new[] { 'R', 'I', 'F', 'F' });
//////
writer.Write(36 + output.Length * 2);
////// writer.Write(new[] { 'W', 'A',
'V', 'E' });
//////
writer.Write(new[] { 'f', 'm', 't', ' ' });
//////
writer.Write(16);
//////
writer.Write((short)1);
//////
writer.Write((short)1);
//////
writer.Write(sampleRate);
//////
writer.Write(byteRate);
//////
writer.Write((short)2);
//////
writer.Write((short)16);
//////
writer.Write(new[] { 'd', 'a', 't', 'a' });
//////
writer.Write(output.Length * 2);
//////
foreach (short sample in output)
//////
writer.Write(sample);
////// }
////// }
////// }
////// public class
WaveProcessor
////// {
////// public
List<CrestTroughSegment> Start(string wavPath, ref ProgressBar
progressBar)
////// {
////// int
sampleRate;
//////
float[] monoSamples = WavLoader.LoadMonoPcmWav(wavPath, out sampleRate);
////// progressBar.Minimum
= 0;
//////
progressBar.Maximum = monoSamples.Length;
//////
progressBar.Value = 0;
//////
List<CrestTroughSegment> segments =
CrestTroughAnalyzer.Analyze(monoSamples);
////// for
(int i = 0; i < monoSamples.Length; i += 1000)
////// {
//////
progressBar.Value = Math.Min(i, monoSamples.Length - 1);
//////
Application.DoEvents();
////// }
//////
progressBar.Value = monoSamples.Length;
//////
return segments;
////// }
////// }
////// public
static class WaveformFrequencyEstimator
////// {
////// public
class FrequencyPoint
////// {
//////
public int StartMs;
////// public
int DurationMs;
//////
public float Frequency;
////// }
////// public
static List<FrequencyPoint> EstimateFrequencies(float[] samples, int
sampleRate)
////// {
//////
List<FrequencyPoint> list = new List<FrequencyPoint>();
////// int
samplesPerMs = sampleRate / 1000;
////// for
(int i = 0; i + samplesPerMs < samples.Length; i += samplesPerMs)
////// {
//////
float sum = 0;
//////
for (int j = 0; j < samplesPerMs; j++)
//////
{
//////
sum += Math.Abs(samples[i + j]);
//////
}
//////
float freq = sum * 20; // Dummy linear frequency proxy
//////
list.Add(new FrequencyPoint { StartMs = i / samplesPerMs, DurationMs =
1, Frequency = freq });
////// }
//////
return list;
////// }
////// }
////// public
static class WaveformPatternComparer
////// {
////// public
static Dictionary<string, int>
ComparePatterns(List<WaveformFrequencyEstimator.FrequencyPoint>
freqPoints)
////// {
////// var
result = new Dictionary<string, int>();
//////
foreach (var point in freqPoints)
////// {
////// string key = ((int)(point.Frequency /
10)).ToString();
//////
if (!result.ContainsKey(key))
//////
result[key] = 0;
//////
result[key]++;
////// }
//////
return result;
////// }
////// }
//////}//namespace
SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
namespace SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
{
public class
FrequencyInfo
{
public int
StartMillis;
public int
DurationMillis;
public float
Frequency;
public int
MidiNote;
}
public static
class WaveformFrequencyEstimator
{
public class
FrequencyPoint
{
public int
StartMillis;
public int
DurationMillis;
public
float Frequency;
}
public static
List<FrequencyPoint> EstimateFrequencies(List<CrestTroughSegment>
segments, int sampleRate)
{
var
results = new List<FrequencyPoint>();
foreach
(var seg in segments)
{
int
startMs = (int)(seg.StartSample * 1000.0 / sampleRate);
int
endMs = (int)(seg.EndSample * 1000.0 / sampleRate);
int
duration = Math.Max(1, endMs - startMs);
float
freq = 0;
if
(duration > 0)
{
float samples = seg.EndSample - seg.StartSample;
freq = 1000.0f / duration * (samples / 2); // approx half-wave count
}
results.Add(new FrequencyPoint { StartMillis = startMs, DurationMillis =
duration, Frequency = freq });
}
return
results;
}
}
public static class WaveformPatternComparer
{
public static
int CountSimilarSegments(List<CrestTroughSegment> segments,
List<WaveformFrequencyEstimator.FrequencyPoint> frequencies)
{
int
similarCount = 0;
for (int i
= 1; i < segments.Count; i++)
{
float
angleDelta = Math.Abs(segments[i].AverageAngle - segments[i - 1].AverageAngle);
float
freqDelta = Math.Abs(frequencies[i].Frequency - frequencies[i - 1].Frequency);
if
(angleDelta < 10 && freqDelta < 3)
similarCount++;
}
return
similarCount;
}
}
public static
class CsvExporter
{
public static
void ExportSegments(string path, List<CrestTroughSegment> segments)
{
using (var
sw = new StreamWriter(path))
{
sw.WriteLine("Index,StartSample,EndSample,StartValue,EndValue,Type,AverageAngle");
for
(int i = 0; i < segments.Count; i++)
{
var seg = segments[i];
sw.WriteLine($"{i},{seg.StartSample},{seg.EndSample},{seg.StartValue},{seg.EndValue},{seg.Type},{seg.AverageAngle}");
}
}
}
public static void
ExportFilteredSegments(string path, List<CrestTroughSegment> segments)
{
ExportSegments(path, segments);
}
public static
void ExportPerMillisecond(string path, float[] samples, int sampleRate)
{
using (var
sw = new StreamWriter(path))
{
sw.WriteLine("Millis,AvgAmplitude");
int
samplesPerMs = sampleRate / 1000;
for
(int ms = 0; ms < samples.Length / samplesPerMs; ms++)
{
float sum = 0;
for (int j = 0; j < samplesPerMs; j++)
{
int idx = ms * samplesPerMs + j;
if (idx >= samples.Length) break;
sum += Math.Abs(samples[idx]);
}
sw.WriteLine($"{ms},{sum / samplesPerMs}");
}
}
}
public static
void ExportFrequencyInfo(string path, List<WaveformFrequencyEstimator.FrequencyPoint>
freqs)
{
using (var
sw = new StreamWriter(path))
{
sw.WriteLine("StartMillis,DurationMillis,Frequency,MidiNote");
foreach (var f in freqs)
{
int midi =
FrequencyToMidi(f.Frequency);
sw.WriteLine($"{f.StartMillis},{f.DurationMillis},{f.Frequency},{midi}");
}
}
}
private static
int FrequencyToMidi(float freq)
{
if (freq <= 0) return -1;
return
(int)Math.Round(69 + 12 * Math.Log(freq / 440.0, 2));
}
public static
void ExportSimilarSegmentCount(string path, int count)
{
File.WriteAllText(path, $"Similar segment count = {count}");
}
}
public class
CrestTroughSegment
{
public int
StartSample;
public int
EndSample;
public float
StartValue;
public float
EndValue;
public string
Type;
public float
AverageAngle;
}
}//namespace SAANS_WAVEFILES_CREST_TROUGHS_ANALYSISWaveform
No comments:
Post a Comment