Your
mistake (as i have tested) you have not done the resampling calculations with
these in mind // after doing this all the samples are getting same durations
and same frequencies exact copies // we know when the frequency changes then
the duration for the sample also change because the total samples are fixed //
we need the resampling logic to change Total_Frequency * Total_Duration = fixed
which means F1*T1 = F2*T2=Sample_Count fixed // We need the sample count to
vary as per necessary conditions and interpolate the samples (as required) in
the 66 copies of samples in same file where these copies are all having
different frequencies and different durations because the frequencies in every
copy and durations are also changing in every copy but all the copies are on
same file (the output file has same sample rate 8000 samples per second while
playing the ear will listen different frequencies for different copies //all
copies are changing the frequencies and durations so all copies dont have same
number of samples now after resampling but the current program is not doing
that (current program is doing the resampling for frequency and then resetting
that to fixed durations and so the whole effect nullifies at the end of double
transformations) // say the original sample has n1 samples s1 samples per
second f1 frequency T1 duration then the next copy will have n2 samples f2
frequency T2 time s2 samples per second and the total things are stored in a
single file with fixed samples per second so we need proper calculations for
these readjustments but different number of samples but whole file will have
fixed samples per second still playing different feels (ear feeling) at
different regions for different copies of same input file... revise the whole
class public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES___GPT
{ public static void GenerateShrutiVariants___GPT(string inputWavPath, string
outputWavPath) { float[] sourceSamples = ReadWavMonoAsFloats(inputWavPath, out
int sourceRate); int outputRate = 8000; double durationSec =
sourceSamples.Length / (double)sourceRate; int targetSamplesPerVariant =
(int)(durationSec * outputRate); List<float> allVariants = new
List<float>(); for (int i = -33; i <= 32; i++) // 66 shrutis: -33 to
+32 { double semitoneShift = i * (1.0 / 3); // 22 shrutis per octave double resampleFactor
= Math.Pow(2, semitoneShift / 12.0); int resampledLength =
(int)(sourceSamples.Length / resampleFactor); float[] pitchShifted =
ResampleWithFactor(sourceSamples, resampledLength); float[] durationMatched =
StretchToLength(pitchShifted, targetSamplesPerVariant);
allVariants.AddRange(durationMatched); }//for (int i = -33; i <= 32; i++) //
66 shrutis: -33 to +32 SaveWav(outputWavPath, allVariants.ToArray(),
outputRate, 1); Console.WriteLine("66 shruti-pitched variants
generated."); MessageBox.Show("Generated 66 shruti-pitched variants:
\n" + outputWavPath); }//public static void GenerateShrutiVariants(string
inputWavPath, string outputWavPath) public static float[]
ResampleWithFactor(float[] input, int targetLength) { // after doing this all
the samples are getting same durations and same frequencies exact copies // we
know when the frequency changes then the duration for the sample also change
because the total samples are fixed // we need the resampling logic to change
Total_Frequency * Total_Duration = fixed which means F1*T1 = F2*T2=Sample_Count
fixed // We need the sample count to vary as per necessary conditions and
interpolate the samples (as required) in the 66 copies of samples in same file
where these copies are all having different frequencies and different durations
because the frequencies in every copy and durations are also changing in every
copy but all the copies are on same file (the output file has same sample rate
8000 samples per second while playing the ear will listen different frequencies
for different copies //all copies are changing the frequencies and durations so
all copies dont have same number of samples now after resampling but the
current program is not doing that (current program is doing the resampling for
frequency and then resetting that to fixed durations and so the whole effect
nullifies at the end of double transformations) // say the original sample has
n1 samples s1 samples per second f1 frequency T1 duration then the next copy
will have n2 samples f2 frequency T2 time s2 samples per second and the total
things are stored in a single file with fixed samples per second so we need
proper calculations for these readjustments but different number of samples but
whole float[] result = new float[targetLength]; double step =
(double)(input.Length - 1) / (targetLength - 1); for (int i = 0; i <
targetLength; i++) { double pos = i * step; int idx = (int)pos; double frac =
pos - idx; result[i] = (idx + 1 < input.Length) ? (float)((1 - frac) *
input[idx] + frac * input[idx + 1]) : input[idx]; }//for (int i = 0; i <
targetLength; i++) return result; }//public static float[]
ResampleWithFactor(float[] input, int targetLength) public static float[]
ReadWavMonoAsFloats(string path, out int sampleRate) { using (BinaryReader br =
new BinaryReader(File.OpenRead(path))) { br.BaseStream.Seek(0,
SeekOrigin.Begin); br.ReadBytes(12); // Skip RIFF header int channels = 1;
sampleRate = 8000; int bitsPerSample = 16; while (br.BaseStream.Position <
br.BaseStream.Length) { string chunkID = new string(br.ReadChars(4)); int
chunkSize = br.ReadInt32(); if (chunkID == "fmt ") { br.ReadInt16();
// audio format channels = br.ReadInt16(); sampleRate = br.ReadInt32();
br.ReadInt32(); // byte rate br.ReadInt16(); // block align bitsPerSample = br.ReadInt16();
if (chunkSize > 16) br.ReadBytes(chunkSize - 16); } else if (chunkID ==
"data") { int bytesPerSample = bitsPerSample / 8; int totalSamples =
chunkSize / bytesPerSample; float[] samples = new float[totalSamples /
channels]; for (int i = 0, j = 0; i < totalSamples; i++) { short sample =
br.ReadInt16(); if (channels == 1 || i % channels == 0) samples[j++] = sample /
32768f; if (channels == 2) br.ReadInt16(); } return samples; } else {
br.ReadBytes(chunkSize); } }//while (br.BaseStream.Position < br.BaseStream.Length)
}//using (BinaryReader br = new BinaryReader(File.OpenRead(path))) sampleRate =
8000; return new float[0]; }//public static float[] ReadWavMonoAsFloats(string
path, out int sampleRate) public static void SaveWav(string path, float[]
samples, int sampleRate, int channels) { int bitsPerSample = 16; int byteRate =
sampleRate * channels * bitsPerSample / 8; int blockAlign = channels *
bitsPerSample / 8; int dataSize = samples.Length * channels * 2; using
(BinaryWriter bw = new BinaryWriter(File.Create(path))) {
bw.Write(Encoding.ASCII.GetBytes("RIFF")); bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt ")); bw.Write(16);
bw.Write((short)1); bw.Write((short)channels); bw.Write(sampleRate); bw.Write(byteRate);
bw.Write((short)blockAlign); bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data")); bw.Write(dataSize);
foreach (float sample in samples) { short s = (short)(sample * 32767); for (int
c = 0; c < channels; c++) bw.Write(s); } } }//public static void
SaveWav(string path, float[] samples, int sampleRate, int channels) public
static float[] StretchToLength(float[] input, int targetLength) { return
ResampleWithFactor(input, targetLength); }//public static float[]
StretchToLength(float[] input, int targetLength) }//public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES
ChatGPT said:
Edited
Answer
in chat instead
✅ Your class has been revised to preserve the
frequency-duration product, ensuring that each shruti-shifted variant:
- Changes both pitch and playback
duration
- Produces a different number of
samples based on pitch
- Maintains realistic ear-feel at 8000
Hz in the final WAV
You can now
directly test the updated GenerateShrutiVariants___GPT method. Let me
know if you'd like the originalFrequency to be
auto-calculated from pitch detection or a separate f1 to be passed in.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace ShrutiResampler
{
public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES___GPT
{
public static
void GenerateShrutiVariants___GPT(string inputWavPath, string outputWavPath)
{
float[]
sourceSamples = ReadWavMonoAsFloats(inputWavPath, out int sourceRate);
int
outputRate = 8000;
double
originalFrequency = 440.0; // Assume input sample is A4 (for calculation)
double
durationSec = sourceSamples.Length / (double)sourceRate;
double
totalCycleProduct = originalFrequency * durationSec;
List<float> allVariants = new List<float>();
for (int i
= -33; i <= 32; i++) // 66 shrutis: -33 to +32
{
double semitoneShift = i * (1.0 /
3);
double
freqMultiplier = Math.Pow(2, semitoneShift / 12.0);
double
newFreq = originalFrequency * freqMultiplier;
double
newDuration = totalCycleProduct / newFreq;
int
newSampleCount = (int)(newDuration * outputRate);
float[] pitchShifted = ResampleWithFactor(sourceSamples,
newSampleCount);
allVariants.AddRange(pitchShifted);
}
SaveWav(outputWavPath,
allVariants.ToArray(), outputRate, 1);
Console.WriteLine("66 shruti-pitched variants generated with
frequency-duration relationship preserved.");
MessageBox.Show("Generated 66 shruti-pitched variants: \n" +
outputWavPath);
}
public static
float[] ResampleWithFactor(float[] input, int targetLength)
{
float[]
result = new float[targetLength];
double
step = (double)(input.Length - 1) / (targetLength - 1);
for (int i
= 0; i < targetLength; i++)
{
double
pos = i * step;
int
idx = (int)pos;
double
frac = pos - idx;
result[i] = (idx + 1 < input.Length)
? (float)((1
- frac) * input[idx] + frac * input[idx + 1])
:
input[idx];
}
return
result;
}
public static
float[] ReadWavMonoAsFloats(string path, out int sampleRate)
{
using (BinaryReader
br = new BinaryReader(File.OpenRead(path)))
{
br.BaseStream.Seek(0, SeekOrigin.Begin);
br.ReadBytes(12);
int
channels = 1;
sampleRate = 8000;
int
bitsPerSample = 16;
while
(br.BaseStream.Position < br.BaseStream.Length)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "fmt ")
{
br.ReadInt16();
channels = br.ReadInt16();
sampleRate = br.ReadInt32();
br.ReadInt32();
br.ReadInt16();
bitsPerSample = br.ReadInt16();
if (chunkSize > 16)
br.ReadBytes(chunkSize - 16);
}
else if (chunkID == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
float[] samples = new float[totalSamples / channels];
for (int i = 0, j = 0; i <
totalSamples; i++)
{
short sample = br.ReadInt16();
if (channels == 1 || i % channels == 0)
samples[j++] =
sample / 32768f;
if (channels == 2) br.ReadInt16();
}
return samples;
}
else
{
br.ReadBytes(chunkSize);
}
}
}
sampleRate
= 8000;
return new
float[0];
}
public static
void SaveWav(string path, float[] samples, int sampleRate, int channels)
{
int
bitsPerSample = 16;
int
byteRate = sampleRate * channels * bitsPerSample / 8;
int
blockAlign = channels * bitsPerSample / 8;
int
dataSize = samples.Length * channels * 2;
using (BinaryWriter bw = new
BinaryWriter(File.Create(path)))
{
bw.Write(Encoding.ASCII.GetBytes("RIFF"));
bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt
"));
bw.Write(16);
bw.Write((short)1);
bw.Write((short)channels);
bw.Write(sampleRate);
bw.Write(byteRate);
bw.Write((short)blockAlign);
bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data"));
bw.Write(dataSize);
foreach (float sample in samples)
{
short s = (short)(sample * 32767);
for (int c = 0; c < channels; c++)
bw.Write(s);
}
}
}
public static
float[] StretchToLength(float[] input, int targetLength)
{
return
ResampleWithFactor(input, targetLength);
}
}
}
read this file thoroughly and guide me (write c sharp code) how to enhance
this to use the resampling (WELL CALCULATED RESAMPLING TO DO for each copy of
samples WITHOUT USING ANY THIRD PARTY API ) where any given T millisecond
sample will get exactly (T' millisecond (all samples to cover * 66 shrutis (22
lower octave 22 for middle octave 22 for upper octave ) and the 66 transformed
copy of these all the samples need play fully (not truncated nor extended) i
need all the copies in single wav file (i will supply the raw pcm wav file 8000
samples per second or whatever 16000 sps or 44100 sps or 96000 sps , mono or
stereo what ever with what ever bit depth i will supply (specially 16 bit
default)) the output will generate as 8000 samples per second with 66 * whole
songs (durations auto calculated to render whole songs even the frequencies are
higher or lower ) RESAMPLING methods to explain properly ... no truncation
allowed no extension allowed... i will listen 66 copies of given sample with
all altered pitches as necessary example first one will have 33 shruti up
notes(lower frequencies but durations of playtime for each note to remain
intact as original sample ) (shifter as necessary) and the last one will have
33 shrutis down notes (higher frequencies but durations of playtime for each
note to remain intact as original sample )... write teh c sharp code and
explain all lines of code... algorithms also ...........please read the codes
which are working
namespace
SAAN_FRESH___RAW_NON_APIS_WAVES_TO_SHRUTIES_COPIER_GPTS
{
public class
SwaralipiConcatenator_ResampleAware
{
public static
void SwaralipiConcatenator___Main(string swaralipiFile)
{
int durationMs = 220;//default
string
wavFolder = Path.GetDirectoryName(swaralipiFile);
int
outputSampleRate = 8000;
string
outputStereo = swaralipiFile +
"_(yesssss)_merged_trimmed_RESAMPLED_8000_stereo.wav";
string
outputMono = swaralipiFile +
"_(yesssss)_merged_trimmed_RESAMPLED_8000_mono.wav";
var lines
= File.ReadAllLines(swaralipiFile)
.Where(l => !string.IsNullOrWhiteSpace(l)).ToList();
var allSegments
= new List<float>();
foreach
(var line in lines)
{
var
parts = line.Split(new[] { ' ', '\t', ',' },
StringSplitOptions.RemoveEmptyEntries);
if
(parts.Length < 2) { durationMs = 220;
continue; }
string
symbol = parts[0].Trim()
.Replace(".", "_DOT_")
.Replace("-", "dash")
.Replace("#", "hash");
// if (!int.TryParse(parts[1],
out int durationMs)) continue;
if
(!int.TryParse(parts[1], out durationMs)) { continue; }
if(durationMs==0)
{
durationMs = 220;//default
}//if(durationMs==0)
string
wavPath = Path.Combine(wavFolder, symbol + ".wav");
float[] originalSamples = ReadWavMonoAsFloats(wavPath, out int
originalSampleRate);
//
Step 1: Resample from originalSampleRate → outputSampleRate
float[] resampled = ResampleToRate(originalSamples, originalSampleRate,
outputSampleRate);
//
Step 2: Extract only as many samples as needed for desired duration
int
neededSamples = (int)((durationMs / 1000.0) * outputSampleRate);
neededSamples = Math.Min(neededSamples, resampled.Length);
float[] trimmed = new float[neededSamples];
Array.Copy(resampled, trimmed, neededSamples);
allSegments.AddRange(trimmed);
}//
foreach (var line in lines)
float[]
final = allSegments.ToArray();
SaveWav(outputStereo, final, outputSampleRate, 2);
SaveWav(outputMono, final, outputSampleRate, 1);
Console.WriteLine("Done writing merged WAV with preserved
pitches.");
System.Windows.Forms.MessageBox.Show("Done writing WAV:\n" +
outputStereo + "\n" + outputMono);
}//public
static void SwaralipiConcatenator___Main(string swaralipiFile)
public static
float[] ResampleToRate(float[] input, int fromRate, int toRate)
{
if
(fromRate == toRate) return input;
int
newLength = (int)(input.Length * (toRate / (double)fromRate));
float[]
result = new float[newLength];
Array.Clear(result, 0, result.Length);
double
step = (double)(input.Length - 1) / (newLength - 1);
for (int i
= 0; i < newLength; i++)
{
double
idx = i * step;
int
index = (int)idx;
double
frac = idx - index;
float
interpolated = ( (index + 1) <
input.Length)
?
(float)((1 - frac) * input[index] + frac * input[index + 1])
: input[index];
result[i] = interpolated;
}//for
(int i = 0; i < newLength; i++)
return
result;
}// public
static float[] ResampleToRate(float[] input, int fromRate, int toRate)
static float[]
ReadWavMonoAsFloats(string path, out int sampleRate)
{
using
(BinaryReader br = new BinaryReader(File.OpenRead(path)))
{
string
riff = new string(br.ReadChars(4));
br.ReadInt32();
string wave = new
string(br.ReadChars(4));
int
channels = 1;
sampleRate = 8000;
int
bitsPerSample = 16;
while
(true)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "fmt ")
{
br.ReadInt16(); // format
channels = br.ReadInt16();
sampleRate = br.ReadInt32();
br.ReadInt32();
br.ReadInt16();
bitsPerSample = br.ReadInt16();
if (chunkSize > 16) br.ReadBytes(chunkSize - 16);
break;
}
else br.ReadBytes(chunkSize);
}//
while (true)
while
(true)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
float[] samples = new float[totalSamples / channels];
for (int i = 0, j = 0; i < totalSamples; i++)
{
short s = br.ReadInt16();
if (channels == 1 || (i
% channels == 0))
samples[j++] =
s / 32768f;
if (channels == 2) br.ReadInt16(); // skip right
}// for (int i = 0, j = 0; i < totalSamples; i++)
return samples;
}
else br.ReadBytes(chunkSize);
}//
while (true)
}// using
(BinaryReader br = new BinaryReader(File.OpenRead(path)))
}//static
float[] ReadWavMonoAsFloats(string path, out int sampleRate)
//need to try
to implement this also
public static
float[] ResampleWithPitchBend
(
float[]
input, int fromRate, int toRate, int targetLength, double bendSemitones
)
{
float[]
output = new float[targetLength];
// Compute
shift range
double
shiftStart = 1.0;
double
shiftEnd = Math.Pow(2, bendSemitones / 12.0);
//
Resample with time-varying rate
double
inputPos = 0.0;
for (int i
= 0; i < targetLength; i++)
{
double
t = i / (double)(targetLength - 1); // [0,1]
double
currentShift = shiftStart + t * (shiftEnd - shiftStart);
double
step = (fromRate * currentShift) / toRate;
int
idx = (int)inputPos;
double
frac = inputPos - idx;
float
sample = 0;
if (idx + 1 < input.Length)
sample = (float)((1 - frac) * input[idx] + frac * input[idx + 1]);
else
if (idx < input.Length)
sample = input[idx];
output[i] = sample;
inputPos += step;
if
(inputPos >= input.Length - 1) break; // stop if overshoot
}//for
(int i = 0; i < targetLength; i++)
return
output;
}// public
static float[] ResampleWithPitchBend
static void SaveWav(string path, float[]
samples, int sampleRate, int channels)
{
int
bitsPerSample = 16;
int
byteRate = sampleRate * channels * bitsPerSample / 8;
int
blockAlign = channels * bitsPerSample / 8;
int
dataSize = samples.Length * channels * 2;
using
(BinaryWriter bw = new BinaryWriter(File.Create(path)))
{
bw.Write(Encoding.ASCII.GetBytes("RIFF"));
bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt "));
bw.Write(16);
bw.Write((short)1);
bw.Write((short)channels);
bw.Write(sampleRate);
bw.Write(byteRate);
bw.Write((short)blockAlign);
bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data"));
bw.Write(dataSize);
foreach (float sample in samples)
{
short s = (short)(sample * 32767);
for (int c = 0; c < channels; c++) bw.Write(s);
}//foreach (float sample in samples)
}// using
(BinaryWriter bw = new BinaryWriter(File.Create(path)))
}// static
void SaveWav(string path, float[] samples, int sampleRate, int channels)
}// public class
SwaralipiConcatenator_ResampleAware
///
<summary>
///
////////////////////////////////////////// the above resampler code looks like
it is working properly
///
</summary>
public class
SwaralipiConcatenator_DurationAware
{
public static
void SwaralipiConcatenator___Main(string swaralipiFile)
{
string wavFolder =
Path.GetDirectoryName(swaralipiFile);
string
outputStereo = swaralipiFile +
"_(durationwise)_NOT_GOOD_output_8000_stereo.wav"; ;//
"_output_44100_stereo.wav";
string
outputMono = swaralipiFile + "_(durationwise)_NOT_GOOD_output_8000_mono.wav";
var lines
= File.ReadAllLines(swaralipiFile)
.Where(l => !string.IsNullOrWhiteSpace(l)).ToList();
var
allSegments = new List<float>();
foreach
(var line in lines)
{
int
durationMs = 220;
var
parts = line.Split(new[] { ' ', '\t', ',' },
StringSplitOptions.RemoveEmptyEntries);
if
(parts.Length < 2) { durationMs = 220; continue; }
string
symbol = parts[0].Trim();
// if
(!int.TryParse(parts[1], out int durationMs)) continue;
if
(!int.TryParse(parts[1], out durationMs)) continue;
symbol
= symbol.Replace(".", "_DOT_").Replace("-",
"dash").Replace("#", "hash");
//.Replace("#", "hash").Replace(".",
"_DOT_");
string
wavPath = Path.Combine(wavFolder, symbol + ".wav");
float[]
fullSamples = { };
Array.Clear(fullSamples, 0, fullSamples.Length);
fullSamples = ReadWavMonoAsFloats(wavPath, out int sampleRate);
int
requiredSamples = (int)((durationMs / 1000.0) * sampleRate);
requiredSamples =
Math.Min(requiredSamples, fullSamples.Length);
float[] trimmed = new float[requiredSamples];
Array.Copy(fullSamples, trimmed, requiredSamples);
allSegments.AddRange(trimmed);
}
float[]
final = allSegments.ToArray();
SaveWav(outputStereo, final, 44100, 2);
SaveWav(outputStereo, final, 8000, 2);
SaveWav(outputMono, final, 8000, 1);
Console.WriteLine("WAVs written with trimmed durations.");
System.Windows.Forms.MessageBox.Show("Done writing WAVs with
duration control:\n" + outputStereo + "\n" + outputMono);
}
static float[]
ReadWavMonoAsFloats(string path, out int sampleRate)
{
using (BinaryReader br = new
BinaryReader(File.OpenRead(path)))
{
string
riff = new string(br.ReadChars(4));
br.ReadInt32();
string
wave = new string(br.ReadChars(4));
int channels
= 1;
sampleRate = 8000;// 44100;
int
bitsPerSample = 16;
while
(true)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "fmt ")
{
br.ReadInt16(); // format
channels = br.ReadInt16();
sampleRate = br.ReadInt32();
br.ReadInt32(); // byte rate
br.ReadInt16(); // block align
bitsPerSample = br.ReadInt16();
if (chunkSize > 16) br.ReadBytes(chunkSize - 16);
break;
}
else
{
br.ReadBytes(chunkSize);
}
}
while
(true)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
float[] samples = { };
samples =
new float[totalSamples / channels];
Array.Clear(samples, 0, samples.Length);
for (int i = 0, j = 0; i < totalSamples; i++)
{
short sample = br.ReadInt16();
if (channels == 1 || (i % channels == 0))
samples[j++] = sample / 32768f;
if (channels == 2) br.ReadInt16(); // skip R
}
return samples;
}
else
{
br.ReadBytes(chunkSize);
}
}
}
}
static void
SaveWav(string path, float[] samples, int sampleRate, int channels)
{
int
bitsPerSample = 16;
int
byteRate = sampleRate * channels * bitsPerSample / 8;
int
blockAlign = channels * bitsPerSample / 8;
int
dataSize = samples.Length * channels * 2;
using
(BinaryWriter bw = new BinaryWriter(File.Create(path)))
{
bw.Write(Encoding.ASCII.GetBytes("RIFF"));
bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt "));
bw.Write(16);
bw.Write((short)1);
bw.Write((short)channels);
bw.Write(sampleRate);
bw.Write(byteRate);
bw.Write((short)blockAlign);
bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data"));
bw.Write(dataSize);
foreach (float sample in samples)
{
short s = (short)(sample * 32767);
for (int c = 0; c < channels; c++)
{
bw.Write(s);
}//for (int c = 0; c < channels; c++)
}//foreach (float sample in samples)
}//using
(BinaryWriter bw = new BinaryWriter(File.Create(path)))
}
}
///
<summary>
///
//////////////////////////////////////////////////////////////////////////////////////////////////
///
</summary>
public class
SwaralipiConcatenator___all_same_durations
{
public static
void SwaralipiConcatenator___Main(string swaralipifile)
{
string
swaralipiFile = swaralipifile;// "swaralipi.txt"; // path to your
text file
string
wavFolder
//=
string folderPath =
=Path.GetDirectoryName(swaralipifile); ;// "."; // directory
containing all the 66 wavs
string
outputStereo = swaralipiFile +
"_all_sames_durs_NOT_GOOD_output_8000_stereo.wav";//
"_output_44100_stereo.wav";
string outputMono
= swaralipiFile + "_all_sames_durs_NOT_GOOD_output_8000_mono.wav";
var
symbols = File.ReadAllLines(swaralipiFile).Where(l =>
!string.IsNullOrWhiteSpace(l)).ToList();
var
allSamples = new List<float[]>();
int maxSamples
= 0;
// Step 1:
Load all WAVs and store sample data
foreach
(var symbol in symbols)
{
// var
parts = line.Split(new[] { ' ', '\t', ',' },
StringSplitOptions.RemoveEmptyEntries);
string[]
splitted_durations_notes= symbol.Split(new[] { ' ', '\t', ',' },
StringSplitOptions.RemoveEmptyEntries);
//
splitted_durations_notes
string
newsymbol = splitted_durations_notes[0].Replace(".",
"_DOT_").Replace("-", "dash").Replace("#",
"hash");
// string wavPath =
Path.Combine(wavFolder, $"{symbol}.wav");
string
wavPath = "";
wavPath= Path.Combine(wavFolder, $"{newsymbol}.wav");
//
List<float> samples = new List<float>();// null;
float[] samples = { };// new List<float>();// null;
//samples.Clear();
Array.Clear(samples, 0, samples.Length);
samples
=
ReadWavMonoAsFloats(wavPath, out int sourceRate);
if
(samples.Length > maxSamples) maxSamples = samples.Length;
allSamples.Add(samples);
}//foreach
(var symbol in symbols)
// Step 2:
Resample all to maxSamples length
var
normalized = allSamples.Select(s => StretchToLength(s,
maxSamples)).ToList();
// Step 3:
Concatenate all
var
finalFloat = normalized.SelectMany(s => s).ToList();
// Step
4a: Save as 44100Hz stereo
//
SaveWav(outputStereo+"_output_44100_stereo.wav",
finalFloat.ToArray(), 44100, 2);
SaveWav(outputStereo + "_output_8000_stereo.wav",
finalFloat.ToArray(), 8000, 2);
//SwaralipiConcatenator___all_same_durations
// Step
4b: Save as 8000Hz mono
SaveWav(outputMono+"_output_8000_mono.wav",
finalFloat.ToArray(), 8000, 1);
Console.WriteLine("Done writing output WAVs.");
System.Windows.Forms.MessageBox.Show("Done
writing output WAVs." + outputStereo +" " + outputMono);
}//public
static void SwaralipiConcatenator___Main(string swaralipifile)
static float[]
ReadWavMonoAsFloats(string path, out int sampleRate)
{
using
(BinaryReader br = new BinaryReader(File.OpenRead(path)))
{
string
riff = new string(br.ReadChars(4));
br.ReadInt32(); // file size
string
wave = new string(br.ReadChars(4));
int
channels = 1;
sampleRate = 8000;// 44100;
int
bitsPerSample = 16;
//
Read fmt chunk
while
(true)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "fmt ")
{
br.ReadInt16(); // audio format
channels = br.ReadInt16();
sampleRate = br.ReadInt32();
br.ReadInt32(); // byte rate
br.ReadInt16(); // block align
bitsPerSample = br.ReadInt16();
if (chunkSize > 16) br.ReadBytes(chunkSize - 16);
break;
}
else
{
br.ReadBytes(chunkSize); // skip unknown chunk
}
}
// Read data chunk
while
(true)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "data")
{
int bytesPerSample =
bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
float[] samples = new float[totalSamples / channels];
for (int i = 0, j = 0; i < totalSamples; i++)
{
short sample = br.ReadInt16();
if (channels == 1 || (i % channels == 0))
samples[j++] =
sample / 32768f;
if (channels == 2) br.ReadInt16(); // skip right channel
}
return samples;
}
else
{
br.ReadBytes(chunkSize); // skip unknown chunk
}
}
} // using
BinaryReader
}// static
float[] ReadWavMonoAsFloats(string path, out int sampleRate)
static float[]
StretchToLength(float[] original, int targetLength)
{
float[]
result = new float[targetLength];
double
step = (double)(original.Length - 1) / (targetLength - 1);
for (int i
= 0; i < targetLength; i++)
{
double
index = i * step;
int
idx = (int)index;
double
frac = index - idx;
result[i] = (idx + 1 < original.Length)
?
(float)((1 - frac) * original[idx] + frac * original[idx + 1])
:
original[idx];
}
return
result;
}//static
float[] StretchToLength(float[] original, int targetLength)
static void
SaveWav(string path, float[] samples, int sampleRate, int channels)
{
int
bitsPerSample = 16;
int
byteRate = sampleRate * channels * bitsPerSample / 8;
int
blockAlign = channels * bitsPerSample / 8;
int
dataSize = samples.Length * channels * 2;
using
(BinaryWriter bw = new BinaryWriter(File.Create(path)))
{
//
RIFF header
bw.Write(Encoding.ASCII.GetBytes("RIFF"));
bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
// fmt chunk
bw.Write(Encoding.ASCII.GetBytes("fmt "));
bw.Write(16); // Subchunk1Size for PCM
bw.Write((short)1); // Audio format = PCM
bw.Write((short)channels);
bw.Write(sampleRate);
bw.Write(byteRate);
bw.Write((short)blockAlign);
bw.Write((short)bitsPerSample);
//
data chunk
bw.Write(Encoding.ASCII.GetBytes("data"));
bw.Write(dataSize);
foreach (float sample in samples)
{
short s = (short)(sample * 32767);
for (int c = 0; c < channels; c++)
{
bw.Write(s);
}
}
} // end
using
}//static void
SaveWav(string path, float[] samples, int sampleRate, int channels)
}//public class
SwaralipiConcatenator
public class
WAVS_TO_MIDS_DYNAMIK_THRESHOLDS_YinPitchDetector___from_wav_files_8000_samples_per_seconds_16_bitsdepth_pcm_riff_files
{
const int
SampleRate = 8000;
const int
FrameSize = 1024;
const int
StepSize = 512;
public static
List<FrameStats> public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
= new List<FrameStats>();
public static
List<FrameStats>
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches
= new List<FrameStats>();
public class
FrameStats
{
public
double TimeMs;
public
double RMS;
public
double MinCMND;
public
double Frequency;
public
double ThresholdUsed;
public int
midsnotesnum;
public
double durations_millis;
public int
velocity;
public
List<double>
public_list_of_doubles_simples_durations_gaps_millis_for_for_pitchbends = new
List<double>();
public
List<int> public_list_of_ints_0_8192_16384_vals_for_pitchbends = new
List<int>();
public
List<double>
public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends
= new List<double>();
public
string leftsideof_slash_in_indinotes_found = "";
public
bool ALREADY_DONES_DURATIONS_CLUBBED_FOR_CONTINUOUS_SAMES_NOTES = false;
}//public
class FrameStats
public static
int FrequencyToMidiNote(double frequency)
{
if
(frequency <= 0) return 0;
// return (int)Math.Round(69 + 12 *
Math.Log(frequency / 440.0, 2));
int
note_number_found_from_the_nearest_frequency_Hertz
=
SAANAUDS.PUBLIC_STATIC_CLASS_TO_GENERATE_CSVMIDI_LIKE_SAANDATA
// . PUBLIC_STATIC_STRING_ARRAY_FOR_SAAN_NOTES_MAPPER
//
.public_static_global_list______ACCUMULATED_AFTER_ALL_DATA_READING_DONE___INSIDE___FUNCTION___SAAN_Log_tracks_channels_events_______BETTER_SCALES____STILL___NOT_GETTING_PROPER_PATCH_NUMBERS___SAAN_CLASS_FOR_MIDI_DATA_TO_LINE_SEGMENT_DATA_HOLDING
//
.public_static_int_get_note_number_from_frequency_Hertz
.public_static_int_get_note_number_from_frequency_Hertz
(
frequency
);
return
note_number_found_from_the_nearest_frequency_Hertz;
}// public
static int FrequencyToMidiNote(double frequency)
public static
void
ToProcess_with___inPitchDetector___from_wav_files_8000_samples_per_seconds_16_bitsdepth_pcm_riff_files___Main(string
wav_filename)
{
////string _returned_notes_number_string
//// =
//////
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES
//////.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING
//////.
//DO_POPULATE_INDIANIZED_NOTES___AND_RETURN_NOTENUMBER_FROM_THE_FILLED_NOTES_NAMES("");
var
samples = ReadWavFile(wav_filename);
var
frameStats = DetectPitchWithStats(samples);
string
csvPath = wav_filename + "_dynamik_thresholds_YinPitchDetector___Deep_Stats___csv";
ExportDeepCsv(frameStats, csvPath);
Console.WriteLine("Pitch + Stats saved to: " + csvPath);
MessageBox.Show("Pitch + Stats saved to: " + csvPath);
}//public
static void
ToProcess_with___inPitchDetector___from_wav_files_8000_samples_per_seconds_16_bitsdepth_pcm_riff_files___Main(string
wav_filename)
public static
List<double> ReadWavFile(string filePath)
{
var samples
= new List<double>();
using (var
reader = new BinaryReader(File.OpenRead(filePath)))
{
reader.BaseStream.Seek(44, SeekOrigin.Begin);
while
(reader.BaseStream.Position < reader.BaseStream.Length)
{
short s = reader.ReadInt16();
samples.Add(s / 32768.0);
}//while (reader.BaseStream.Position < reader.BaseStream.Length)
}//using
(var reader = new BinaryReader(File.OpenRead(filePath)))
return
samples;
}//public
static List<double> ReadWavFile(string filePath)
public static
List<FrameStats> DetectPitchWithStats(List<double> samples)
{
List<FrameStats> stats = new List<FrameStats>();
stats.Clear();
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Clear();
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches.Clear();
for (int i
= 0; i + FrameSize < samples.Count; i += StepSize)
{
double[] frame = samples.GetRange(i, FrameSize).ToArray();
double
rms = Math.Sqrt(frame.Sum(x => x * x) / frame.Length);
double threshold =
EstimateDynamicThreshold(rms);
double
pitch = YinWithStats(frame, SampleRate, threshold, out double minCMND);
int
notes_number = FrequencyToMidiNote(pitch);
//CALLING CONVENTIONS
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING.___TEMP_STRING_INDIAN_NOTES_NAME___________PUBLIC_STATIC_STRING_FOR_GLOBAL_ACCESS
//CALLING CONVENTIONS
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING.___TEMP_INT_INDIAN_NOTES_NUMBER___________PUBLIC_STATIC_INT_FOR_GLOBAL_ACCESS
/// THESE WILL GET FILLED
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING.___TEMP_STRING_INDIAN_NOTES_NAME___________PUBLIC_STATIC_STRING_FOR_GLOBAL_ACCESS
= ___TEMP_STRING_INDIAN_NOTES_NAME;
/// THESE WILL GET FILLED
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING.___TEMP_INT_INDIAN_NOTES_NUMBER___________PUBLIC_STATIC_INT_FOR_GLOBAL_ACCESS
= ___TEMP_INT_INDIAN_NOTES_NUMBER;
string
___TEMP_STRING_INDIAN_NOTES_NAME
=
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING.___TEMP_STRING_INDIAN_NOTES_NAME___________PUBLIC_STATIC_STRING_FOR_GLOBAL_ACCESS;
int
___TEMP_INT_INDIAN_NOTES_NUMBER
=
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING.___TEMP_INT_INDIAN_NOTES_NUMBER___________PUBLIC_STATIC_INT_FOR_GLOBAL_ACCESS;
string
filled_notes_name = "";
string
___report_for_populations
=
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES
.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING
.PUBLIC_STATIC_STRING___return_NOTES_NUMBER_STRING_FROM_PRECONFIGURED_KEY_NAMES_TO_NUMBER______FROM___PUBLIC_STATIC_STRING_ARRAY_FOR_36_NOTES___50_X_3
(___TEMP_STRING_INDIAN_NOTES_NAME, ___TEMP_INT_INDIAN_NOTES_NUMBER,
notes_number.ToString());//saan has changed this code with notes number
checking also includes
//////returned_note_number = ___report_for_populations;
string
found_leftsidepart_of_indinote_slash =
SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES
.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING
.
PUBLIC_STATIC_STRING_GET_INDINOTESNAME_FIRST_OCCURANCES___from_____PUBLIC_STATIC_STRING_ARRAY_FOR_36_NOTES___50_X_3____FOR_GIVENS_MIDSNOTESNUMBER
(notes_number);
if
(pitch > 0)
{
//to take care for the
pitch>0 cases seperately
stats.Add
(new FrameStats
{
TimeMs = i *
1000.0 / SampleRate,
RMS = rms,
MinCMND =
minCMND,
Frequency =
pitch,
ThresholdUsed =
threshold,
midsnotesnum =
notes_number,
velocity = 127,
leftsideof_slash_in_indinotes_found =
found_leftsidepart_of_indinote_slash
}
);
}else
{
try
{
stats.Last().velocity = 0;
/////////////////////////////////////////////////////////////////////////////////////
stats.Last()
.public_list_of_ints_0_8192_16384_vals_for_pitchbends
.Add
(
8192 + (int)((Math.Max(stats.Last().MinCMND, stats.Last().RMS)) * 16384)
);
//
stats.Last()
.public_list_of_doubles_simples_durations_gaps_millis_for_for_pitchbends
.Add
(
(i * 1000.0 / SampleRate) - (stats.Last().TimeMs)
);
}
catch (Exception ___excp_to_fill_pitchbends)
{
}//catch(Exception ___excp_to_fill_pitchbends)
}//end
of else of if (pitch > 0)
if
(pitch > 0)
{
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.Add
(new FrameStats
{
TimeMs = i * 1000.0 / SampleRate,
RMS = rms,
MinCMND = minCMND,
Frequency = pitch,
ThresholdUsed = threshold,
midsnotesnum = notes_number,
velocity=127,
leftsideof_slash_in_indinotes_found =
found_leftsidepart_of_indinote_slash
}
);
}//
if(pitch > 0)
else
{
try
{
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.Last()
.public_list_of_ints_0_8192_16384_vals_for_pitchbends
.Add
(
8192 + (int)((Math.Max(stats.Last().MinCMND, stats.Last().RMS)) * 16384)
);
//
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.Last()
.public_list_of_doubles_simples_durations_gaps_millis_for_for_pitchbends
.Add
(
(i * 1000.0 / SampleRate) - (stats.Last().TimeMs)
);
}
catch (Exception ___excp_to_fill_pitchbends)
{
}//catch(Exception ___excp_to_fill_pitchbends)
}//end
of else of if (pitch > 0)
}//for
(int i = 0; i + FrameSize < samples.Count; i += StepSize)
//WE WILL
NOW CLUB THE DURATIONS FOR CONTIGUOUS NOTES
//////public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Reverse();
//////for(int reversecrawler = 0;
reversecrawler<public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches)
//////public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches
////// //DOING THE REVERSING
AGAIN TO KEEP THAT STRAIGHT
////// public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Reverse();
///
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches
.
Add
(public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.First());
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.First()
.ALREADY_DONES_DURATIONS_CLUBBED_FOR_CONTINUOUS_SAMES_NOTES = true;
///////////////////////////////////////////////////////////////////////////////////////////////////////
for (int
durations_clubbing_Crawler=1; durations_clubbing_Crawler<
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Count;
durations_clubbing_Crawler++)
{
for(int lookforwardcrawler= durations_clubbing_Crawler+1;
lookforwardcrawler<
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Count;
lookforwardcrawler++)
{
if
(
( !
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(lookforwardcrawler).ALREADY_DONES_DURATIONS_CLUBBED_FOR_CONTINUOUS_SAMES_NOTES
)
&&
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(lookforwardcrawler).midsnotesnum
==
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches
.Last().midsnotesnum
)
{
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches
.Last()
.durations_millis
=
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches
.Last()
.durations_millis
+
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(lookforwardcrawler)
.durations_millis;
/////////////////////////////////////////////////////////////////////////////////////
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(lookforwardcrawler)
.ALREADY_DONES_DURATIONS_CLUBBED_FOR_CONTINUOUS_SAMES_NOTES
= true;
}
else
{
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches
.
Add
(
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(lookforwardcrawler)
);
///////////////////////////////////////////////////////////////////////////////////////////////
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(lookforwardcrawler)
.ALREADY_DONES_DURATIONS_CLUBBED_FOR_CONTINUOUS_SAMES_NOTES
= true;
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches
.Last()
.TimeMs
=
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(lookforwardcrawler)
.TimeMs;
durations_clubbing_Crawler = lookforwardcrawler;
// goto
gotohere_since_new_notes_number_found;
}/////////////////////////////// else case completes
}//for(int lookforwardcrawler= durations_clubbing_Crawler;
lookforwardcrawler< public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Count;
lookforwardcrawler++)
gotohere_since_new_notes_number_found:;
}//for(int
durations_clubbing_Crawler=0; durations_clubbing_Crawler<
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Count;
durations_clubbing_Crawler++)
return
stats;
}//public
static List<FrameStats> DetectPitchWithStats(List<double> samples)
public static
double EstimateDynamicThreshold(double rms)
{
if (rms
< 0.02) return 0.30;
if (rms
< 0.05) return 0.20;
return
0.10;
}//public
static double EstimateDynamicThreshold(double rms)
public static
double YinWithStats(double[] buffer, int sampleRate, double threshold, out
double minCMND)
{
int tauMax
= buffer.Length / 2;
double[]
difference = new double[tauMax];
double[]
cmnd = new double[tauMax];
for (int
tau = 1; tau < tauMax; tau++)
{
double sum = 0;
for
(int i = 0; i < tauMax; i++)
{
double delta = buffer[i] - buffer[i + tau];
sum += delta * delta;
}//for
(int i = 0; i < tauMax; i++)
difference[tau] = sum;
}//for
(int tau = 1; tau < tauMax; tau++)
cmnd[0] =
1;
double
runningSum = 0;
minCMND =
1.0;
for (int
tau = 1; tau < tauMax; tau++)
{
runningSum += difference[tau];
cmnd[tau] = difference[tau] / ((runningSum / tau) + 1e-10);
if
(cmnd[tau] < minCMND) minCMND = cmnd[tau];
}//for
(int tau = 1; tau < tauMax; tau++)
int tauEstimate
= -1;
for (int
tau = 2; tau < tauMax; tau++)
{
if
(cmnd[tau] < threshold)
{
while (tau + 1 < tauMax && cmnd[tau + 1] < cmnd[tau])
tau++;
tauEstimate = tau;
break;
}//if
(cmnd[tau] < threshold)
}//for
(int tau = 2; tau < tauMax; tau++)
if
(tauEstimate != -1)
{
double
betterTau = tauEstimate;
if (tauEstimate > 0
&& tauEstimate < tauMax - 1)
{
double s0 = cmnd[tauEstimate - 1];
double s1 = cmnd[tauEstimate];
double s2 = cmnd[tauEstimate + 1];
betterTau = tauEstimate + (s2 - s0) / (2
* (2 * s1 - s2 - s0));
}//if
(tauEstimate > 0 && tauEstimate < tauMax - 1)
return
sampleRate / betterTau;
}//if
(tauEstimate != -1)
return 0;
}// public
static double YinWithStats(double[] buffer, int sampleRate, double threshold,
out double minCMND)
public static
void ExportDeepCsv(List<FrameStats> stats, string csvPath)
{
StringBuilder _strbldr = new StringBuilder();
_strbldr.Clear();
StringBuilder strbld______for_dot_dataGridView___COMPOSER = new
StringBuilder();
strbld______for_dot_dataGridView___COMPOSER.Clear();
StringBuilder strbld______for_CLUBBED_DURATIONS___dot_dataGridView___COMPOSER
= new StringBuilder();
strbld______for_CLUBBED_DURATIONS___dot_dataGridView___COMPOSER.Clear();
//////
LYRSYLLS ######
INDINOTES ######
TRYS_NOTES_NOS ###### MILLISECOND
###### CURR_NOTES_VELOCITY
###### CURR_NOTES_PATCH
######
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS ######
WITHIN_NOTES_PTCHBENDS_VALUES ######
CHANNEL ###### TRACK
###### NOTES_NUMBERS ######
NOTES_FREQUENCYS ######
JERK_MILLIS_GAP ######
JERK_UP_PITCHBEND ######
JERK_DOWN_PITCH_BEND ######
FORCED_DECORATIONS_START_TIMES_MILLIS
###### START_MILLIS_PERCUSSIONS
######
START_MILLIS_FOR_PUREMELODYNOTES ######
REFERENCE_CUMULATIVE_START_MILLIS
###### PITCHBENDRANGE_2X2_DEFAULT
###### PAN_0_TO_127
######
////// ###### S
###### 56 ######
220 ###### 127
###### ###### 0;100
###### 8192;8192
###### ###### ###### ###### ###### 30
###### 30 ######
30 ###### ###### ###### 220
###### 220 ###### ###### ###### ROW_NUMBER=0
//////
###### r ######
57 ###### 220
###### 127 ###### ###### 0;100
###### 8192;8192 ###### ###### ###### ###### ###### 30
###### 30 ######
30 ###### ###### ###### 440
###### 440 ###### ###### ###### ROW_NUMBER=1
////// ###### g
###### 58 ######
220 ###### 127
###### ###### 0;100
###### 8192;8192
###### ###### ###### ###### ###### 30
###### 30 ######
30 ###### ###### ###### 660
###### 660 ###### ###### ###### ROW_NUMBER=2
strbld______for_dot_dataGridView___COMPOSER
.AppendLine
(
"LYRSYLLS ######
INDINOTES ######
TRYS_NOTES_NOS ######
MILLISECOND ######
CURR_NOTES_VELOCITY ######
CURR_NOTES_PATCH ######
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
###### WITHIN_NOTES_PTCHBENDS_VALUES
###### CHANNEL ######
TRACK ######
NOTES_NUMBERS ######
NOTES_FREQUENCYS ######
JERK_MILLIS_GAP ###### JERK_UP_PITCHBEND
###### JERK_DOWN_PITCH_BEND
######
FORCED_DECORATIONS_START_TIMES_MILLIS
###### START_MILLIS_PERCUSSIONS ######
START_MILLIS_FOR_PUREMELODYNOTES
######
REFERENCE_CUMULATIVE_START_MILLIS
###### PITCHBENDRANGE_2X2_DEFAULT
###### PAN_0_TO_127
###### "
);
strbld______for_CLUBBED_DURATIONS___dot_dataGridView___COMPOSER
.AppendLine
(
"LYRSYLLS ######
INDINOTES ######
TRYS_NOTES_NOS ######
MILLISECOND ######
CURR_NOTES_VELOCITY ######
CURR_NOTES_PATCH ######
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
###### WITHIN_NOTES_PTCHBENDS_VALUES
###### CHANNEL
###### TRACK ######
NOTES_NUMBERS ######
NOTES_FREQUENCYS ######
JERK_MILLIS_GAP ######
JERK_UP_PITCHBEND ######
JERK_DOWN_PITCH_BEND ######
FORCED_DECORATIONS_START_TIMES_MILLIS
###### START_MILLIS_PERCUSSIONS
###### START_MILLIS_FOR_PUREMELODYNOTES
######
REFERENCE_CUMULATIVE_START_MILLIS
###### PITCHBENDRANGE_2X2_DEFAULT
###### PAN_0_TO_127
###### "
);
using (var
writer = new StreamWriter(csvPath))
{
//
writer.WriteLine("Time (ms),Frequency
(Hz),RMS,MinCMND,ThresholdUsed");
//
writer.WriteLine($"{s.TimeMs:F2},{s.Frequency:F2},{s.midsnotesnum:F2},{
s.durations_millis:F2},{s.velocity:F2},{s.RMS:F4},{s.MinCMND:F4},{s.ThresholdUsed:F3}");
writer.WriteLine("Time (ms),Frequency (Hz),Midsnotes ,Indinotes,
dursmillis , velocity , RMS,MinCMND,ThresholdUsed");
_strbldr.AppendLine("Time (ms),Frequency (Hz),Midsnotes ,IndiNotes,
dursmillis , velocity , RMS,MinCMND,ThresholdUsed");
for
(int rrr=0;rrr< (stats.Count-1);rrr++)
{
stats.ElementAt(rrr).durations_millis
=
(stats.ElementAt(rrr + 1).TimeMs
-
stats.ElementAt(rrr ).TimeMs
);
/////////////////////////////// to avoid
divisions with zeros for pitch bends
///////////////////////////////////////////
stats.ElementAt(rrr).durations_millis = Math.Max(1,
stats.ElementAt(rrr).durations_millis);
/////////////////////////////// to avoid
divisions with zeros for pitch bends
///////////////////////////////////////////
//
stats.ElementAt(rrr).midsnotesnum =
stats.ElementAt(rrr).velocity = 127;// (int)((stats.ElementAt(rrr).MinCMND)
* 127);
//(int)((stats.ElementAt(rrr).RMS) * 127);
try
{
//////////////////////////////////////////////////////////////////////
///
for (int countptchbnds=0; countptchbnds
<
stats.ElementAt(rrr).public_list_of_ints_0_8192_16384_vals_for_pitchbends.Count;
countptchbnds++)
{
stats.ElementAt(rrr)
.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends
.
Add
(
(
(
stats.ElementAt(rrr).public_list_of_ints_0_8192_16384_vals_for_pitchbends.ElementAt(countptchbnds)
-
stats.ElementAt(rrr).TimeMs
)
/
stats.ElementAt(rrr).durations_millis
)*100
);
}// for(int countptchbnds=0; countptchbnds <
stats.ElementAt(rrr).public_list_of_ints_0_8192_16384_vals_for_pitchbends.Count;
countptchbnds++)
}
catch (Exception ___excp_to_fill_pitchbends)
{
}//catch(Exception ___excp_to_fill_pitchbends)
}//
for(int rrr=0;rrr< (stats.Count-1);rrr++)
for
(int ggg = 0; ggg <
(public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Count
- 1); ggg++)
{
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).durations_millis
=
(public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg
+ 1).TimeMs
-
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).TimeMs
);
/////////////////////////////// to avoid divisions with zeros for pitch
bends ///////////////////////////////////////////
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).durations_millis
= Math.Max(1,
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).durations_millis);
/////////////////////////////// to avoid divisions with zeros for pitch
bends ///////////////////////////////////////////
//
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).midsnotesnum
= midsnotesnum
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).velocity
= 127;//
(int)((public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).MinCMND)
* 127); //(int)((stats.ElementAt(rrr).RMS)
* 127);
try
{
for (int countptchbnds = 0; countptchbnds <
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).public_list_of_ints_0_8192_16384_vals_for_pitchbends.Count;
countptchbnds++)
{
stats.ElementAt(ggg)
.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends
.
Add
(
(
(
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(ggg).public_list_of_ints_0_8192_16384_vals_for_pitchbends.ElementAt(countptchbnds)
-
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(ggg).TimeMs
)
/
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches
.ElementAt(ggg).durations_millis
)*100
);
}// for (int countptchbnds = 0; countptchbnds <
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.ElementAt(ggg).public_list_of_ints_0_8192_16384_vals_for_pitchbends.Count;
countptchbnds++)
}
catch (Exception ___excp_to_fill_pitchbends)
{
}//catch(Exception ___excp_to_fill_pitchbends)
}//
for(int ggg=0;ggg<
(public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches.Count-1);ggg++)
/////////////////////////////////////////////////////////////////////////////
foreach (var s in stats)
{
writer.WriteLine($"{s.TimeMs:F2},{s.Frequency:F2},{s.midsnotesnum:F2},{s.leftsideof_slash_in_indinotes_found},{
s.durations_millis:F2},{s.velocity:F2},{s.RMS:F4},{s.MinCMND:F4},{s.ThresholdUsed:F3}");
}//foreach (var s in stats)
string
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS = "";
string
WITHIN_NOTES_PTCHBENDS_VALUES = "";
string
___indinotefound___firstoccurances_before_slash
=
"";
foreach (var s_mids in
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches)
{
_strbldr.AppendLine($"{s_mids.TimeMs:F2},{s_mids.Frequency:F2},{s_mids.midsnotesnum:F2},{s_mids.leftsideof_slash_in_indinotes_found},{
s_mids.durations_millis:F2},{s_mids.velocity:F2},{s_mids.RMS:F4},{s_mids.MinCMND:F4},{s_mids.ThresholdUsed:F3}");
//////___indinotefound___firstoccurances_before_slash
////// =
//////SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES
//////
.PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING
////// .
//////PUBLIC_STATIC_STRING_GET_INDINOTESNAME_FIRST_OCCURANCES___from_____PUBLIC_STATIC_STRING_ARRAY_FOR_36_NOTES___50_X_3____FOR_GIVENS_MIDSNOTESNUMBER
//////(s_mids.midsnotesnum);
if
(s_mids.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends.Count==
s_mids.public_list_of_ints_0_8192_16384_vals_for_pitchbends.Count)
{
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS = "0";
WITHIN_NOTES_PTCHBENDS_VALUES = "8192";
foreach (double perc in
s_mids.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends)
{
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
=
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
+
";"+
perc.ToString("F2");
}//foreach (double perc in
s_mids.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends)
foreach(int ptchbnd in
s_mids.public_list_of_ints_0_8192_16384_vals_for_pitchbends)
{
WITHIN_NOTES_PTCHBENDS_VALUES
=
WITHIN_NOTES_PTCHBENDS_VALUES
+ ";"
+ ((int)ptchbnd).ToString();
}//foreach(int ptchbnd in
s_mids.public_list_of_ints_0_8192_16384_vals_for_pitchbends)
}//if(s_mids.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends.Count==
s_mids.public_list_of_ints_0_8192_16384_vals_for_pitchbends.Count)
else
{
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS = "0;100";
WITHIN_NOTES_PTCHBENDS_VALUES =
"8192;8192";
}//filling the lists
///////////////////////////////////////////////////////////////////////////////////////////
strbld______for_dot_dataGridView___COMPOSER
.AppendLine
(
// "######"
// +
"LYRSYLLS "
+ " ######
"+
// INDINOTES
// ""
s_mids.leftsideof_slash_in_indinotes_found
+ " ###### " +
// TRYS_NOTES_NOS
s_mids.midsnotesnum
+ " ###### " +
// MILLISECOND
s_mids.durations_millis
+ " ###### " +
// CURR_NOTES_VELOCITY
+s_mids.velocity
+ " ###### " +
// CURR_NOTES_PATCH
"73"
+ " ###### " +
//
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
//"0;100"
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
+ " ###### " +
// WITHIN_NOTES_PTCHBENDS_VALUES
//"8192;8192"
WITHIN_NOTES_PTCHBENDS_VALUES
+ " ###### " +
// CHANNEL
6
+ " ###### " +
// TRACK
6
+ " ###### " +
// NOTES_NUMBERS names
"to_fill"
+ " ###### " +
// NOTES_FREQUENCYS
s_mids.Frequency
+ " ###### " +
// JERK_MILLIS_GAP
6
+ "
###### " +
// JERK_UP_PITCHBEND
6
+ " ###### " +
// JERK_DOWN_PITCH_BEND
6
+ " ###### " +
//
FORCED_DECORATIONS_START_TIMES_MILLIS
s_mids.TimeMs
+ " ###### " +
// START_MILLIS_PERCUSSIONS
s_mids.TimeMs
+ " ###### " +
//
START_MILLIS_FOR_PUREMELODYNOTES
s_mids.TimeMs
+ " ###### " +
//
REFERENCE_CUMULATIVE_START_MILLIS
+s_mids.TimeMs
+"
###### " +
// PITCHBENDRANGE_2X2_DEFAULT
"6"
+ " ###### " +
// PAN_0_TO_127
"64"
+ " ###### "
);
}//
foreach (var s_mids in
public_static___List_of___FrameStats__stats__pure_notes_nonzero_pitches)
}//using
(var writer = new StreamWriter(csvPath))
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
System.IO.File.WriteAllText
(
csvPath+"_midsnotesfromwavs.csvs_dat"
,
_strbldr.ToString()
);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
System.IO.File.WriteAllText
(
csvPath + "___combined_wavstomids.dataGridView___COMPOSER"
,
strbld______for_dot_dataGridView___COMPOSER.ToString()
);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
foreach
(var s_CONTINUED in
public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches)
{
//outside the writer streams
string
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS = "0";
string
WITHIN_NOTES_PTCHBENDS_VALUES = "8192";
//_strbldr.AppendLine($"{s_mids.TimeMs:F2},{s_mids.Frequency:F2},{s_mids.midsnotesnum:F2},{s_mids.leftsideof_slash_in_indinotes_found},{
s_mids.durations_millis:F2},{s_mids.velocity:F2},{s_mids.RMS:F4},{s_mids.MinCMND:F4},{s_mids.ThresholdUsed:F3}");
//////___indinotefound___firstoccurances_before_slash
////// =
//////SAANS_ROUGH_BEATS_COMPOSER_FOR_ON_SELECTIONS_LOOP_CYCLES
////// .PUBLIC_STATIC_CLASS_TO_PRELISTEN_HANDLING
////// .
//////PUBLIC_STATIC_STRING_GET_INDINOTESNAME_FIRST_OCCURANCES___from_____PUBLIC_STATIC_STRING_ARRAY_FOR_36_NOTES___50_X_3____FOR_GIVENS_MIDSNOTESNUMBER
//////(s_mids.midsnotesnum);
if
(s_CONTINUED.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends.Count
== s_CONTINUED.public_list_of_ints_0_8192_16384_vals_for_pitchbends.Count)
{
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS = "0";
WITHIN_NOTES_PTCHBENDS_VALUES = "8192";
foreach (double perc in
s_CONTINUED.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends)
{
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
=
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
+
";" + perc.ToString("F2");
}//foreach (double perc in
s_CONTINUED.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends)
foreach (int ptchbnd in
s_CONTINUED.public_list_of_ints_0_8192_16384_vals_for_pitchbends)
{
WITHIN_NOTES_PTCHBENDS_VALUES
=
WITHIN_NOTES_PTCHBENDS_VALUES
+ ";" + ((int)ptchbnd).ToString();
}//foreach(int ptchbnd in
s_CONTINUED.public_list_of_ints_0_8192_16384_vals_for_pitchbends)
}//if(s_CONTINUED.public_list_of_percentages_of_doubles_simples_durations_gaps_0_to_100_for_for_pitchbends.Count==
s_CONTINUED.public_list_of_ints_0_8192_16384_vals_for_pitchbends.Count)
else
{
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS = "0;100";
WITHIN_NOTES_PTCHBENDS_VALUES = "8192;8192";
}//filling the lists
///////////////////////////////////////////////////////////////////////////////////////////
strbld______for_CLUBBED_DURATIONS___dot_dataGridView___COMPOSER
.AppendLine
(
// "######"
//
+
"LYRSYLLS "
+
" ###### " +
//
INDINOTES
//
""
s_CONTINUED.leftsideof_slash_in_indinotes_found
+
" ###### " +
//
TRYS_NOTES_NOS
s_CONTINUED.midsnotesnum
+
" ###### " +
//
MILLISECOND
s_CONTINUED.durations_millis
+
" ###### " +
//
CURR_NOTES_VELOCITY
+s_CONTINUED.velocity
+
" ###### " +
// CURR_NOTES_PATCH
"73"
+
" ###### " +
//
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
//"0;100"
PERCENTAGES_DURATIONS_COMMASEPS_PTCHBNDS
+
" ###### " +
// WITHIN_NOTES_PTCHBENDS_VALUES
//"8192;8192"
WITHIN_NOTES_PTCHBENDS_VALUES
+
" ###### " +
//
CHANNEL
6
+
" ###### " +
// TRACK
6
+
" ###### " +
// NOTES_NUMBERS names
"to_fill"
+
" ###### " +
// NOTES_FREQUENCYS
s_CONTINUED.Frequency
+
" ###### " +
//
JERK_MILLIS_GAP
6
+
" ###### " +
//
JERK_UP_PITCHBEND
6
+
" ###### " +
// JERK_DOWN_PITCH_BEND
6
+
" ###### " +
//
FORCED_DECORATIONS_START_TIMES_MILLIS
s_CONTINUED.TimeMs
+
" ###### " +
//
START_MILLIS_PERCUSSIONS
s_CONTINUED.TimeMs
+
" ###### " +
//
START_MILLIS_FOR_PUREMELODYNOTES
s_CONTINUED.TimeMs
+
" ###### " +
//
REFERENCE_CUMULATIVE_START_MILLIS
+s_CONTINUED.TimeMs
+
" ###### " +
// PITCHBENDRANGE_2X2_DEFAULT
"6"
+
" ###### " +
//
PAN_0_TO_127
"64"
+
" ###### "
);
}//
foreach (var s_CONTINUED in public_static___List_of___CLUBBED_CONTIGUOUS_NOTES___DURATIONS_SUMMED___FrameStats__stats__pure_notes_nonzero_pitches)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
System.IO.File.WriteAllText
(
csvPath +
"___CLUBBED_DURATIONS_FOR_SAMES_NOTES_combined_wavstomids.dataGridView___COMPOSER"
,
strbld______for_CLUBBED_DURATIONS___dot_dataGridView___COMPOSER.ToString()
);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}//public
static void ExportDeepCsv(List<FrameStats> stats, string csvPath)
}//public class
WAVS_TO_MIDS_DYNAMIK_THRESHOLDS_YinPitchDetector___from_wav_files_8000_samples_per_seconds_16_bitsdepth_pcm_riff_files
public class
YinPitchDetector___from_wav_files_8000_samples_per_seconds_16_bitsdepth_pcm_riff_files
{
const int
SampleRate = 8000;
const int
FrameSize = 1024;
const int StepSize
= 512;
const double
Threshold = 0.15;
public static
int FrequencyToMidiNote(double frequency)
{
if
(frequency <= 0) return 0;
return
(int)Math.Round(69 + 12 * Math.Log(frequency / 440.0, 2));
}
public static
void
ToProcess_with___inPitchDetector___from_wav_files_8000_samples_per_seconds_16_bitsdepth_pcm_riff_files___Main(string
wav_filename)
{
string
filePath = wav_filename;//"your_file.wav";
var samples =
ReadWavFile(filePath);
var pitchTrack
= DetectPitch(samples);
////Console.WriteLine("Time (ms) | Frequency (Hz)");
////for (int i
= 0; i < pitchTrack.Count; i++)
////{
//// double timeMs = i * StepSize * 1000.0 /
SampleRate;
//// Console.WriteLine($"{timeMs:F1}\t|
{pitchTrack[i]:F2}");
////}//for
(int i = 0; i < pitchTrack.Count; i++)
string
csvPath = filePath + "_YinPitchDetector_pitch.times_freqs___csv";
using (var
writer = new StreamWriter(csvPath))
{
writer.WriteLine("Time (ms),Frequency (Hz)");
for
(int i = 0; i < pitchTrack.Count; i++)
{
double timeMs = i * StepSize * 1000.0 / SampleRate;
double frequency = pitchTrack[i];
writer.WriteLine($"{timeMs:F2},{frequency:F2}");
}//for
(int i = 0; i < pitchTrack.Count; i++)
}//using
(var writer = new StreamWriter(csvPath))
Console.WriteLine("Pitch data saved to: " + csvPath);
System.Windows.Forms.MessageBox.Show("Times Freqs_Pitch data saved
to: " + csvPath);
}//public static
void
ToProcess_with___inPitchDetector___from_wav_files_8000_samples_per_seconds_16_bitsdepth+pcm_riff_files___Main(string
wav_filename)
// Reads 16-bit
PCM mono WAV
public static
List<double> ReadWavFile(string filePath)
{
var samples =
new List<double>();
using (var
reader = new BinaryReader(File.OpenRead(filePath)))
{
// Skip
header (44 bytes)
reader.BaseStream.Seek(44, SeekOrigin.Begin);
while
(reader.BaseStream.Position < reader.BaseStream.Length)
{
short
s = reader.ReadInt16();
samples.Add(s / 32768.0);
}//while
(reader.BaseStream.Position < reader.BaseStream.Length)
}//using (var
reader = new BinaryReader(File.OpenRead(filePath)))
return
samples;
}//public static
List<double> ReadWavFile(string filePath)
// Main YIN pitch
detection
public static
List<double> DetectPitch(List<double> samples)
{
var pitchList
= new List<double>();
for (int i =
0; i + FrameSize < samples.Count; i += StepSize)
{
double[]
frame = samples.GetRange(i, FrameSize).ToArray();
double
pitch = Yin(frame, SampleRate, Threshold);
pitchList.Add(pitch);
}//for (int i
= 0; i + FrameSize < samples.Count; i += StepSize)
return
pitchList;
}//public static
List<double> DetectPitch(List<double> samples)
// YIN algorithm
implementation
public static
double Yin(double[] buffer, int sampleRate, double threshold)
{
int tauMax =
buffer.Length / 2;
double[]
difference = new double[tauMax];
double[]
cumulativeMeanNormalizedDifference = new double[tauMax];
// Step 1:
Difference function
for (int tau =
1; tau < tauMax; tau++)
{
double sum
= 0;
for (int i
= 0; i < tauMax; i++)
{
double
delta = buffer[i] - buffer[i + tau];
sum +=
delta * delta;
}//for
(int i = 0; i < tauMax; i++)
difference[tau] = sum;
}//for (int
tau = 1; tau < tauMax; tau++)
// Step 2:
Cumulative mean normalized difference
cumulativeMeanNormalizedDifference[0] = 1;
double
runningSum = 0;
for (int tau =
1; tau < tauMax; tau++)
{
runningSum
+= difference[tau];
cumulativeMeanNormalizedDifference[tau] = difference[tau] / ((runningSum
/ tau) + 1e-10); // avoid divide by zero
}//for (int
tau = 1; tau < tauMax; tau++)
// Step 3:
Absolute threshold
int
tauEstimate = -1;
for (int tau =
2; tau < tauMax; tau++)
{
if
(cumulativeMeanNormalizedDifference[tau] < threshold)
{
while
(tau + 1 < tauMax && cumulativeMeanNormalizedDifference[tau + 1]
< cumulativeMeanNormalizedDifference[tau])
{
tau++;
}//while (tau + 1 < tauMax &&
cumulativeMeanNormalizedDifference[tau + 1] <
cumulativeMeanNormalizedDifference[tau])
tauEstimate = tau;
break;
}//if
(cumulativeMeanNormalizedDifference[tau] < threshold)
}//for (int
tau = 2; tau < tauMax; tau++)
// Step 4:
Parabolic interpolation
if
(tauEstimate != -1)
{
double
betterTau = tauEstimate;
if
(tauEstimate > 0 && tauEstimate < tauMax - 1)
{
double
s0 = cumulativeMeanNormalizedDifference[tauEstimate - 1];
double
s1 = cumulativeMeanNormalizedDifference[tauEstimate];
double
s2 = cumulativeMeanNormalizedDifference[tauEstimate + 1];
betterTau = tauEstimate + (s2 - s0) / (2 *
(2 * s1 - s2 - s0));
}//if
(tauEstimate > 0 && tauEstimate < tauMax - 1)
return
sampleRate / betterTau;
}
else
{
return 0;
// unvoiced or no pitch detected
}//end of else
of if (tauEstimate != -1)
} // public static
double Yin(double[] buffer, int sampleRate, double threshold)
}//YinPitchDetector___from_wav_files_8000_samples_per_seconds_16_bitsdepth_pcm_riff_files
///
<summary>
///
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
</summary>
public class
SAANS_NO_3RD_PARTY_GPT_STYLES_WAV_FILES_READER_TO_GENERATE_8000_HZ_16_BIT_PCMS_GTAMPS
{
//////public
static void Write_SPLITTED_WavFile(string path, List<float>
normalizedSamples, int sampleRate)
//////{
////// short bitsPerSample = 16;
////// short channels = 1;
////// int byteRate = sampleRate * channels *
bitsPerSample / 8;
////// int dataSize = normalizedSamples.Count * 2;
////// using (var writer = new
BinaryWriter(File.Create(path)))
////// {
//////
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
////// writer.Write(36 + dataSize);
//////
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
//////
writer.Write(Encoding.ASCII.GetBytes("fmt "));
////// writer.Write(16);
////// writer.Write((short)1); // PCM
////// writer.Write((short)channels);
////// writer.Write(sampleRate);
////// writer.Write(byteRate);
////// writer.Write((short)(channels *
bitsPerSample / 8));
////// writer.Write(bitsPerSample);
//////
writer.Write(Encoding.ASCII.GetBytes("data"));
////// writer.Write(dataSize);
////// foreach (float sample in
normalizedSamples)
////// writer.Write((short)(sample *
32767));
////// }//using (var writer = new
BinaryWriter(File.Create(path)))
//////}//public static void Write_SPLITTED_WavFile(string path,
List<float> normalizedSamples, int sampleRate)
public static void
SplitWavByAmplitudePercentilewith_dynamik_thresholds(string wavPath)
{
string
fileNameOnly = Path.GetFileNameWithoutExtension(wavPath);
string
folder = Path.GetDirectoryName(wavPath);
short
channels = 1;
int
sampleRate = 8000;
short
bitsPerSample = 16;
List<float> amplitudes = new List<float>();
// ----
Read WAV and extract normalized amplitudes ----
using (var
reader = new BinaryReader(File.OpenRead(wavPath)))
{
if
(new string(reader.ReadChars(4)) != "RIFF") throw new
Exception("Invalid RIFF");
reader.ReadInt32(); // size
if
(new string(reader.ReadChars(4)) != "WAVE") throw new Exception("Invalid
WAVE");
while
(reader.BaseStream.Position < reader.BaseStream.Length)
{
string chunkId = new string(reader.ReadChars(4));
int chunkSize = reader.ReadInt32();
if (chunkId == "fmt ")
{
reader.ReadInt16(); // audioFormat
channels = reader.ReadInt16();
sampleRate = reader.ReadInt32();
reader.ReadInt32(); // byteRate
reader.ReadInt16(); // blockAlign
bitsPerSample = reader.ReadInt16();
if (chunkSize > 16) reader.ReadBytes(chunkSize - 16);
}
else if (chunkId == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
for (int i = 0; i < totalSamples; i++)
{
if (reader.BaseStream.Position + bytesPerSample >
reader.BaseStream.Length)
break;
short sample = reader.ReadInt16();
float norm = sample / 32768f;
amplitudes.Add(norm);
if (channels == 2) reader.ReadInt16(); // skip right channel
}
break;
}
else
reader.ReadBytes(chunkSize); // skip unknown
}
}
// ----
Calculate 20th percentile of absolute amplitudes ----
var
absSorted = amplitudes.Select(Math.Abs).OrderBy(v => v).ToList();
float percentile20 =
absSorted[(int)(absSorted.Count * 0.2)];
Console.WriteLine($"Using 20th Percentile Amplitude as Threshold:
{percentile20:F6}");
// ----
Detect rising/falling threshold crossings ----
List<(int
Start, int End)> regions = new List<(int, int)>();
bool
inRegion = false;
int
regionStart = 0;
int
minSegmentSamples = 800; // ~100 ms
for (int i
= 0; i < amplitudes.Count; i++)
{
float absAmp = Math.Abs(amplitudes[i]);
if
(!inRegion && absAmp >= percentile20)
{
inRegion = true;
regionStart = i;
}
else
if (inRegion && absAmp < percentile20)
{
int regionEnd = i;
if
(regionEnd - regionStart >= minSegmentSamples)
regions.Add((regionStart, regionEnd));
inRegion = false;
}
}
// Handle
edge case if region reaches file end
if
(inRegion && amplitudes.Count - regionStart >= minSegmentSamples)
regions.Add((regionStart, amplitudes.Count - 1));
// ----
Save split WAVs ----
for (int i
= 0; i < regions.Count; i++)
{
int
start = regions[i].Start;
int
end = regions[i].End;
var
segment = amplitudes.GetRange(start, end - start);
string
outputFile = Path.Combine(folder,
$"{fileNameOnly}_piece_{i +
1}_samples_{segment.Count}_ms_{(int)(segment.Count / (float)sampleRate *
1000)}.wav");
Write_SPLITTED_WavFile(outputFile, segment, sampleRate);
Console.WriteLine($"Saved: {outputFile}");
}
if
(regions.Count == 0)
Console.WriteLine("No regions above the 20th percentile threshold
were long enough to save.");
}//public static void
SplitWavByAmplitudePercentilewith_dynamik_thresholds(string wavPath)
public static
void SplitWavByRisingAbove20dBRegions(string wavPath)
{
string
fileNameOnly = Path.GetFileNameWithoutExtension(wavPath);
string
folder = Path.GetDirectoryName(wavPath);
short
channels = 1;
int
sampleRate = 8000;
short
bitsPerSample = 16;
List<float>
amplitudes = new List<float>();
// ----
Read WAV and extract normalized amplitudes ----
using (var
reader = new BinaryReader(File.OpenRead(wavPath)))
{
//
Read RIFF/WAVE
if
(new string(reader.ReadChars(4)) != "RIFF") throw new
Exception("Invalid RIFF");
reader.ReadInt32(); // size
if
(new string(reader.ReadChars(4)) != "WAVE") throw new
Exception("Invalid WAVE");
while
(reader.BaseStream.Position < reader.BaseStream.Length)
{
string chunkId = new string(reader.ReadChars(4));
int chunkSize = reader.ReadInt32();
if
(chunkId == "fmt ")
{
reader.ReadInt16(); //
audioFormat
channels = reader.ReadInt16();
sampleRate = reader.ReadInt32();
reader.ReadInt32(); // byteRate
reader.ReadInt16(); // blockAlign
bitsPerSample = reader.ReadInt16();
if (chunkSize > 16) reader.ReadBytes(chunkSize - 16);
}
else if (chunkId == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
for (int i = 0; i < totalSamples; i++)
{
if (reader.BaseStream.Position + bytesPerSample >
reader.BaseStream.Length)
break;
short sample = reader.ReadInt16();
float norm = sample / 32768f;
amplitudes.Add(norm);
if (channels == 2) reader.ReadInt16(); // skip right
}
break;
}
else reader.ReadBytes(chunkSize);
}
}
// ----
SPLITTING BASED ON AMPLITUDE THRESHOLD ----
float
threshold = 0.1f; // ~ -20 dB
bool
inRegion = false;
int
regionStart = 0;
int
minSegmentSamples = 800; // = 100ms worth (8000Hz)
List<(int Start, int End)> regions = new List<(int, int)>();
for (int i
= 0; i < amplitudes.Count; i++)
{
float
absAmp = Math.Abs(amplitudes[i]);
if
(!inRegion && absAmp >= threshold)
{
inRegion = true;
regionStart = i;
}
else
if (inRegion && absAmp < threshold)
{
//
End of region
int regionEnd = i;
if
(regionEnd - regionStart >= minSegmentSamples)
regions.Add((regionStart, regionEnd));
inRegion = false;
}
}//for
(int i = 0; i < amplitudes.Count; i++)
// Edge
case: still in region at end
if
(inRegion && amplitudes.Count - regionStart >= minSegmentSamples)
regions.Add((regionStart, amplitudes.Count - 1));
// ----
SAVE SPLIT WAVS ----
for (int i
= 0; i < regions.Count; i++)
{
int
start = regions[i].Start;
int
end = regions[i].End;
var
segment = amplitudes.GetRange(start, end - start);
string
outputFile = Path.Combine(folder,
$"{fileNameOnly}_piece_{i +
1}_samples_{segment.Count}_ms_{(int)(segment.Count / (float)sampleRate *
1000)}.wav");
Write_SPLITTED_WavFile(outputFile, segment, sampleRate);
Console.WriteLine($"Saved:
{outputFile}");
}//for
(int i = 0; i < regions.Count; i++)
}//public
static void SplitWavByRisingAbove20dBRegions(string wavPath)
public static
void DISCARDING___THIS_GENERATES_LARGE_NUMBER_OF_SPLITTED_FILES______SplitWavByLocalMinimals_Of_Amplitudes(string
wavPath)
{
string
fileNameOnly = Path.GetFileNameWithoutExtension(wavPath);
string
folder = Path.GetDirectoryName(wavPath);
short
channels = 1;
int
sampleRate = 8000;
short
bitsPerSample = 16;
List<float> amplitudes = new List<float>();
// ----
Read WAV and extract normalized amplitudes ----
using (var
reader = new BinaryReader(File.OpenRead(wavPath)))
{
//
Validate RIFF and WAVE
if
(new string(reader.ReadChars(4)) != "RIFF") throw new
Exception("Invalid RIFF");
reader.ReadInt32(); // size
if
(new string(reader.ReadChars(4)) != "WAVE") throw new
Exception("Invalid WAVE");
while
(reader.BaseStream.Position < reader.BaseStream.Length)
{
string chunkId = new string(reader.ReadChars(4));
int chunkSize = reader.ReadInt32();
if
(chunkId == "fmt ")
{
reader.ReadInt16(); // audioFormat
channels = reader.ReadInt16();
sampleRate = reader.ReadInt32();
reader.ReadInt32(); // byteRate
reader.ReadInt16(); // blockAlign
bitsPerSample = reader.ReadInt16();
if (chunkSize > 16) reader.ReadBytes(chunkSize - 16);
}
else if (chunkId == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
for (int i = 0; i < totalSamples; i++)
{
if (reader.BaseStream.Position + bytesPerSample >
reader.BaseStream.Length)
break;
short sample =
reader.ReadInt16();
float norm = sample / 32768f;
amplitudes.Add(norm);
if (channels == 2) reader.ReadInt16(); // skip right
}
break;
}
else reader.ReadBytes(chunkSize); // skip unknown chunk
}
}
// ----
Analyze: average and percentiles ----
float
avgAmp = amplitudes.Average(a => Math.Abs(a));
float
percentile20 = amplitudes.OrderBy(Math.Abs).ElementAt((int)(amplitudes.Count *
0.2f));
float
percentile50 = amplitudes.OrderBy(Math.Abs).ElementAt((int)(amplitudes.Count *
0.5f));
Console.WriteLine($"Average
Amplitude: {avgAmp:F4}");
Console.WriteLine($"20th Percentile: {percentile20:F4}, 50th
Percentile: {percentile50:F4}");
// ----
Detect local minima ----
List<int> minimaIndexes = new List<int>();
for (int i = 1; i < amplitudes.Count
- 1; i++)
{
if
(Math.Abs(amplitudes[i]) < Math.Abs(amplitudes[i - 1]) &&
Math.Abs(amplitudes[i]) < Math.Abs(amplitudes[i + 1]))
{
if (Math.Abs(amplitudes[i]) <
percentile20)
minimaIndexes.Add(i);
}
}//for
(int i = 1; i < amplitudes.Count - 1; i++)
// ----
Extract segments between minima and save WAVs ----
for (int i
= 0; i < minimaIndexes.Count - 1; i++)
{
int
start = minimaIndexes[i];
int
end = minimaIndexes[i + 1];
var
segment = amplitudes.GetRange(start, end - start);
string outputFile = Path.Combine(folder,
$"{fileNameOnly}_piece_{i +
1}_samples_{segment.Count}_ms_{(int)(segment.Count / (float)sampleRate *
1000)}.wav");
Write_SPLITTED_WavFile(outputFile, segment, sampleRate);
Console.WriteLine($"Saved:
{outputFile}");
}//for
(int i = 0; i < minimaIndexes.Count - 1; i++)
}//public
static void
DISCARDING___THIS_GENERATES_LARGE_NUMBER_OF_SPLITTED_FILES______SplitWavByLocalMinimals_Of_Amplitudes(string
wavPath)
public static
void Write_SPLITTED_WavFile(string path, List<float> normalizedSamples,
int sampleRate)
{
short
bitsPerSample = 16;
short
channels = 1;
int
byteRate = sampleRate * channels * bitsPerSample / 8;
int
dataSize = normalizedSamples.Count * 2;
using (var
writer = new BinaryWriter(File.Create(path)))
{
//
RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
// fmt
chunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16);
writer.Write((short)1); // PCM
writer.Write((short)channels);
writer.Write(sampleRate);
writer.Write(byteRate);
writer.Write((short)(channels * bitsPerSample / 8));
writer.Write(bitsPerSample);
//
data chunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
foreach (float sample in normalizedSamples)
writer.Write((short)(sample * 32767));
}
}//public
static void Write_SPLITTED_WavFile(string path, List<float>
normalizedSamples, int sampleRate)
public static
double PrintWavDurationAndSampleCount(string wavPath)
{
double
durationMs = 0;
string
___report = "";
using (var
reader = new BinaryReader(File.OpenRead(wavPath)))
{
//
Skip RIFF header
reader.ReadChars(4);
reader.ReadInt32();
reader.ReadChars(4);
int
sampleRate = 0;
short
channels = 1;
short
bitsPerSample = 16;
int
dataSize = 0;
//
Read chunks
while
(reader.BaseStream.Position < reader.BaseStream.Length)
{
string chunkId = new string(reader.ReadChars(4));
int chunkSize = reader.ReadInt32();
if
(chunkId == "fmt ")
{
short audioFormat = reader.ReadInt16();
channels = reader.ReadInt16();
sampleRate = reader.ReadInt32();
reader.ReadInt32(); // byteRate
reader.ReadInt16(); // blockAlign
bitsPerSample = reader.ReadInt16();
if (chunkSize > 16)
reader.ReadBytes(chunkSize - 16);
}
else if (chunkId == "data")
{
dataSize = chunkSize;
break;
}
else
{
reader.ReadBytes(chunkSize); // skip unknown chunk
}
}//while (reader.BaseStream.Position < reader.BaseStream.Length)
if
(sampleRate == 0 || dataSize == 0)
{
Console.WriteLine("Failed to read sample rate or data size.");
return 0;
}//if
(sampleRate == 0 || dataSize == 0)
int
bytesPerSample = bitsPerSample / 8 * channels;
int
totalSamples = dataSize / bytesPerSample;
durationMs = (totalSamples / (double)sampleRate) * 1000;
Console.WriteLine($"Sample Rate
: {sampleRate} Hz");
Console.WriteLine($"Channels
: {channels}");
Console.WriteLine($"Bits Per Sample
: {bitsPerSample}");
Console.WriteLine($"Total Samples
: {totalSamples}");
Console.WriteLine($"Total Duration
: {durationMs:F2} ms");
___report= ___report + "\r\n"+ ($"Sample Rate : {sampleRate} Hz");
___report = ___report + "\r\n" + ($"Channels : {channels}");
___report = ___report + "\r\n" + ($"Bits Per Sample : {bitsPerSample}");
___report = ___report + "\r\n" + ($"Total Samples : {totalSamples}");
___report = ___report + "\r\n" + ($"Total Duration : {durationMs:F2} ms");
System.IO.File.WriteAllText(wavPath + "_reportfor_gtsmps.txt",
___report);
}//using
(var reader = new BinaryReader(File.OpenRead(wavPath)))
return
durationMs;
}//public
static void PrintWavDurationAndSampleCount(string wavPath)
public static
void ConvertWavToGTAMPS___with_corrections_GPT(string wavPath, string
outputGtampsPath)
{
short
channels = 1;
int
sampleRate = 8000;
short
bitsPerSample = 16;
using (var
reader = new BinaryReader(File.OpenRead(wavPath)))
{
// 1.
Read RIFF header
if (new
string(reader.ReadChars(4)) != "RIFF")
throw new Exception("Not a valid RIFF file.");
reader.ReadInt32(); // File size
if
(new string(reader.ReadChars(4)) != "WAVE")
throw new Exception("Not a valid WAVE file.");
// 2.
Find 'fmt ' subchunk
channels = 1;
sampleRate = 8000;
bitsPerSample = 16;
while
(true)
{
string chunkId = new string(reader.ReadChars(4));
int chunkSize = reader.ReadInt32();
if
(chunkId == "fmt ")
{
short audioFormat = reader.ReadInt16();
channels = reader.ReadInt16();
sampleRate = reader.ReadInt32();
reader.ReadInt32(); // byteRate
reader.ReadInt16(); // blockAlign
bitsPerSample = reader.ReadInt16();
if (chunkSize > 16)
reader.ReadBytes(chunkSize - 16); // skip extra fmt bytes
break;
}
else
{
reader.ReadBytes(chunkSize); // skip unknown chunk
}
}
// 3.
Find 'data' subchunk
while
(true)
{
string chunkId = new string(reader.ReadChars(4));
int chunkSize = reader.ReadInt32();
if
(chunkId == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples =
chunkSize / bytesPerSample;
var amplitudes = new List<float>();
for (int i = 0; i < totalSamples; i++)
{
if (reader.BaseStream.Position + bytesPerSample >
reader.BaseStream.Length)
break;
short sample = reader.ReadInt16();
float norm = sample / 32768f;
amplitudes.Add(norm);
if (channels == 2)
reader.ReadInt16(); // skip right channel
}//for (int i = 0; i < totalSamples; i++)
// 4. Resample if needed
if (sampleRate != 8000)
{
var resampled = new List<float>();
double factor = sampleRate / 8000.0;
for (int i = 0; i < amplitudes.Count / factor; i++)
{
double idx = i
* factor;
int i0 =
(int)idx;
double frac =
idx - i0;
float val = (i0
+ 1 < amplitudes.Count)
?
(float)((1 - frac) * amplitudes[i0] + frac * amplitudes[i0 + 1])
:
amplitudes[i0];
resampled.Add(val);
}//for (int i = 0; i < amplitudes.Count / factor; i++)
amplitudes = resampled;
}//if (sampleRate != 8000)
// 5. Save GTAMPS
File.WriteAllLines(outputGtampsPath, amplitudes.Select(a =>
a.ToString("F8")));
Console.WriteLine($"Saved GTAMPS: {outputGtampsPath}");
System.Windows.Forms.MessageBox.Show($"Saved GTAMPS:
{outputGtampsPath} amplitudes =
{amplitudes.Count}");
// string
outputGtampsPath_generated = wavPath + "_actual_sampleRate=" +
sampleRate + "_actual_bps=" + bitsPerSample + "_numSamples="
+ numSamples + "_smpls=" + samples + "_8000Hz_16_bits_pcms_"
+ ".GTAMPS";
//samples
// Save as .GTAMPS
//
File.WriteAllLines(outputGtampsPath_generated, samples.Select(s =>
s.ToString("F8")));
return;
}
else
{
reader.ReadBytes(chunkSize); // skip unknown chunk
}
}
throw
new Exception("Data chunk not found.");
}
}//public
static void ConvertWavToGTAMPS___with_corrections_GPT(string wavPath, string
outputGtampsPath)
////// public static void
ConvertWavToGTAMPS___with_corrections_GPT(string wavPath, string
outputGtampsPath)
////// {
////// using (var reader = new
BinaryReader(File.OpenRead(wavPath)))
////// {
////// // 1. Read RIFF header
////// if (new string(reader.ReadChars(4))
!= "RIFF")
////// throw new Exception("Not a
valid RIFF file.");
////// reader.ReadInt32(); // File size
////// if (new string(reader.ReadChars(4))
!= "WAVE")
////// throw new Exception("Not a
valid WAVE file.");
////// // 2. Find 'fmt ' subchunk
////// short channels = 1;
////// int sampleRate = 8000;
////// short bitsPerSample = 16;
////// while (true)
////// {
////// string chunkId = new
string(reader.ReadChars(4));
////// int chunkSize =
reader.ReadInt32();
////// if (chunkId == "fmt
")
////// {
////// short audioFormat =
reader.ReadInt16();
////// channels =
reader.ReadInt16();
////// sampleRate =
reader.ReadInt32();
////// reader.ReadInt32(); //
byteRate
////// reader.ReadInt16(); //
blockAlign
////// bitsPerSample =
reader.ReadInt16();
////// if (chunkSize > 16)
////// reader.ReadBytes(chunkSize - 16); // skip
extra fmt bytes
////// break;
////// }
////// else
////// {
////// reader.ReadBytes(chunkSize);
// skip unknown chunk
////// }
////// }
////// // 3. Find 'data' subchunk
////// while (true)
////// {
////// string chunkId = new
string(reader.ReadChars(4));
////// int chunkSize =
reader.ReadInt32();
////// if (chunkId ==
"data")
////// {
////// int bytesPerSample = bitsPerSample /
8;
////// int totalSamples =
chunkSize / bytesPerSample;
////// var amplitudes = new
List<float>();
////// for (int i = 0; i < totalSamples;
i++)
////// {
////// if
(reader.BaseStream.Position + bytesPerSample > reader.BaseStream.Length)
////// { break; }
////// short sample =
reader.ReadInt16();
////// float norm = sample /
32768f;
////// amplitudes.Add(norm);
////// if (channels == 2)
////// {
////// reader.ReadInt16();
// skip right channel}
////// }// for (int i = 0; i
< totalSamples; i++)
////// // 4. Resample if
needed
////// if (sampleRate != 8000)
////// {
////// var resampled = new
List<float>();
////// double factor =
sampleRate / 8000.0;
////// for (int rrr = 0; rrr < amplitudes.Count
/ factor; rrr++)
////// {
////// double idx = rrr *
factor;
////// int i0 = (int)idx;
////// double frac = idx -
i0;
////// float val = (i0 + 1
< amplitudes.Count)
////// ? (float)((1 -
frac) * amplitudes[i0] + frac * amplitudes[i0 + 1])
////// :
amplitudes[i0];
////// resampled.Add(val);
////// }//for (int rrr = 0;
rrr < amplitudes.Count / factor; rrr++)
////// amplitudes = resampled;
////// }//if (sampleRate != 8000)
////// // 5. Save GTAMPS
////// // File.WriteAllLines(outputGtampsPath,
amplitudes.Select(a => a.ToString("F8")));
////// // Console.WriteLine($"Saved GTAMPS:
{outputGtampsPath}");
////// string
outputGtampsPath_generated = wavPath + "_actual_sampleRate=" +
sampleRate + "_actual_bps=" + bitsPerSample +
"_numSamples=" + numSamples + "_smpls=" + samples +
"_8000Hz_16_bits_pcms_" + ".GTAMPS";
////// //samples
////// // Save as .GTAMPS
//////
File.WriteAllLines(outputGtampsPath_generated, samples.Select(s =>
s.ToString("F8")));
////// return;
////// }
////// else
////// {
//////
reader.ReadBytes(chunkSize); // skip unknown chunk
////// }
////// }
////// throw new Exception("Data
chunk not found.");
////// }
////// }//public static void
ConvertWavToGTAMPS___with_corrections_GPT(string wavPath, string
outputGtampsPath)
////// public static void
ConvertANY_KIND_OF_WavToGTAMPS(string wavPath, string outputGtampsPath)
////// {
////// using (var reader = new
BinaryReader(File.OpenRead(wavPath)))
////// {
////// // Read RIFF header
////// string riff = new
string(reader.ReadChars(4));
////// int fileSize = reader.ReadInt32();
////// string wave = new
string(reader.ReadChars(4));
////// // Read format chunk
////// string fmt = new
string(reader.ReadChars(4));
////// int fmtSize = reader.ReadInt32();
////// short audioFormat =
reader.ReadInt16();
////// short channels =
reader.ReadInt16();
////// int sampleRate =
reader.ReadInt32();
////// int byteRate = reader.ReadInt32();
////// short blockAlign =
reader.ReadInt16();
////// short bitsPerSample =
reader.ReadInt16();
////// // Skip any extra bytes if fmt
chunk > 16
////// if (fmtSize > 16)
////// { reader.ReadBytes(fmtSize - 16); }
////// // Seek to data chunk
////// while (new
string(reader.ReadChars(4)) != "data")
////// {
////// reader.BaseStream.Position -= 3;
////// }//while (new
string(reader.ReadChars(4)) != "data")
////// int dataSize = reader.ReadInt32();
////// int numSamples = dataSize /
(bitsPerSample / 8);
////// var samples = new
List<float>(numSamples);
////// for (int i = 0; i < numSamples;
i++)
////// {
////// short sample16 =
reader.ReadInt16();
////// float normalized = sample16 /
32768f;
////// samples.Add(normalized);
////// if (channels == 2)
reader.ReadInt16(); // Skip second channel if stereo
////// }//for (int i = 0; i <
numSamples; i++)
////// // Resample if needed (e.g., from
44100Hz to 8000Hz)
////// if (sampleRate != 8000)
////// {
////// var resampled = new
List<float>();
////// double factor = sampleRate /
8000.0;
////// for (int i = 0; i <
samples.Count / factor; i++)
////// {
////// double index = i * factor;
////// int idx = (int)index;
////// double frac = index - idx;
////// float sample = (idx + 1
< samples.Count)
////// ? (float)((1 - frac) *
samples[idx] + frac * samples[idx + 1])
////// : samples[idx];
////// resampled.Add(sample);
////// }//for (int i = 0; i <
samples.Count / factor; i++)
////// samples = resampled;
////// }//if (sampleRate != 8000)
////// string outputGtampsPath_generated= wavPath+
"_actual_sampleRate=" + sampleRate + "_actual_bps="+
bitsPerSample + "_numSamples=" + numSamples + "_smpls="+
samples+ "_8000Hz_16_bits_pcms_" + ".GTAMPS";
////// //samples
////// // Save as .GTAMPS
//////
File.WriteAllLines(outputGtampsPath_generated, samples.Select(s =>
s.ToString("F8")));
////// // Console.WriteLine($"Saved
GTAMPS to: {outputGtampsPath}");
//////
System.Windows.Forms.MessageBox.Show($"Saved GTAMPS to:
{outputGtampsPath}");
////// }//using (var reader = new
BinaryReader(File.OpenRead(wavPath)))
////// }//public static void ConvertWavToGTAMPS(string
wavPath, string outputGtampsPath)
}// public class
SAANS_NO_3RD_PARTY_GPT_STYLES_WAV_FILES_READER_TO_GENERATE_8000_HZ_16_BIT_PCMS_GTAMPS
// this changes
speed when frequencies changes
public class
WavGenerator___44_SHRUTIS_BACKWARD_TO_FORWARDS_Reads_GTAMPS_MONO_8000_Hz_Samples_16_bits_no_3rd_party_apis
{
const int
SampleRate = 8000;
const int
BitsPerSample = 16;
const int
Channels = 1;
const double
Pi = Math.PI;
public static
void
SAANS___44_SAMPLES_WavGenerator___Reads_GTAMPS_MONO_8000_Hz_Samples_16_bits_no_3rd_party_apis___Main(string
gtamps_filesnames)
{
string
inputPath = gtamps_filesnames;// "your_file_path_here.txt";
// Read
float samples
var
samples = File.ReadLines(inputPath)
.Where(line => double.TryParse(line, out _))
.Select(double.Parse)
.ToList();
var
allShifted = new List<short>();
for (int r
= 1; r <= 22; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0);
var
shifted = ResampleWithFrequencyShift(samples, shiftFactor);
allShifted.AddRange(shifted);
}//for
(int r = 1; r <= 22; r++)
for (int r
= -22; r <= 0; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0);
var
shifted = ResampleWithFrequencyShift(samples, shiftFactor);
allShifted.AddRange(shifted);
}//for
(int r = -22; r <= 0; r++)
string
outputPath = inputPath + "___44_SCALES_8000hZ_16_BITS_PCMS_" +
allShifted.Count.ToString() + "_output.wav";
WriteWavFile(outputPath, allShifted);
Console.WriteLine("WAV file written to: " + outputPath);
}//public
static void
SAANS___44_SAMPLES_WavGenerator___Reads_GTAMPS_MONO_8000_Hz_Samples_16_bits_no_3rd_party_apis___Main(STRING
gtamps_filesnames)
public static
List<short> ResampleWithFrequencyShift(List<double> samples, double
shiftFactor)
{
int
newLength = (int)(samples.Count / shiftFactor);
var
shifted = new List<short>(newLength);
for (int i = 0; i < newLength; i++)
{
double
srcIndex = i * shiftFactor;
int
idx = (int)srcIndex;
double
frac = srcIndex - idx;
double
value = 0;
if
(idx + 1 < samples.Count)
value = (1 - frac) * samples[idx] + frac * samples[idx + 1];
else
if (idx < samples.Count)
value = samples[idx];
short
pcmVal = (short)(value * short.MaxValue);
shifted.Add(pcmVal);
}//for
(int i = 0; i < newLength; i++)
return
shifted;
}//public
static List<short> ResampleWithFrequencyShift(List<double> samples,
double shiftFactor)
public static
void WriteWavFile(string filePath, List<short> samples)
{
using (var
stream = new FileStream(filePath, FileMode.Create))
using (var
writer = new BinaryWriter(stream))
{
int
byteRate = SampleRate * Channels * BitsPerSample / 8;
int
dataSize = samples.Count * BitsPerSample / 8;
//
RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
// fmt
subchunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16); // PCM
writer.Write((short)1); // AudioFormat = PCM
writer.Write((short)Channels);
writer.Write(SampleRate);
writer.Write(byteRate);
writer.Write((short)(Channels * BitsPerSample / 8));
writer.Write((short)BitsPerSample);
// data subchunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
foreach (short s in samples)
{
writer.Write(s); }
}//using
(var writer = new BinaryWriter(stream))
}//public
static void WriteWavFile(string filePath, List<short> samples)
}// public class
WavGenerator___44_SHRUTIS_BACKWARD_TO_FORWARDS_Reads_GTAMPS_MONO_8000_Hz_Samples_16_bits_no_3rd_party_apis
public class
WavGeneratorFixedDuration___with_resamplers
{
const int
SampleRate = 8000;
const int
BitsPerSample = 16;
const int
Channels = 1;
public static
void Process_WavGeneratorFixedDuration___with_resamplers(string inputFilePath)
{
var
samples = File.ReadLines(inputFilePath)
.Where(line => double.TryParse(line, out _))
.Select(double.Parse)
.ToList();
int
originalSampleCount = samples.Count;
var
allShiftedSamples = new List<short>();
//
Positive shrutis (r = 1 to 22)
for (int r
= 1; r <= 22; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0); // e.g., r=12 -> pitch up by ~1
semitone
var
shifted = PitchShiftPreserveDuration(samples, shiftFactor,
originalSampleCount);
allShiftedSamples.AddRange(shifted);
}// for
(int r = 1; r <= 22; r++)
// Negative shrutis (r = -22 to 0)
for (int r
= -22; r <= 0; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0); // pitch down
var
shifted = PitchShiftPreserveDuration(samples, shiftFactor, originalSampleCount);
allShiftedSamples.AddRange(shifted);
}// for
(int r = -22; r <= 0; r++)
string
outputPath = inputFilePath +
"_(44_COPIES_PITCH_CHANGED_)RESAMPLER_USED_FixedDur_ShiftedPitches.wav";
WriteWavFile(outputPath,
allShiftedSamples);
Console.WriteLine("WAV file written to: " + outputPath);
MessageBox.Show("WAV file written to: " + outputPath);
}// public
static void Process_WavGeneratorFixedDuration___with_resamplers(string
inputFilePath)
public static
T Clamp<T>(T val, T min, T max) where T : IComparable<T>
{
if
(val.CompareTo(min) < 0) return min;
if
(val.CompareTo(max) > 0) return max;
return
val;
}// public
static T Clamp<T>(T val, T min, T max) where T : IComparable<T>
// Pitch
shifts and resamples to preserve original duration
public static
List<short> PitchShiftPreserveDuration(List<double> samples, double
shiftFactor, int targetLength)
{
var output
= new List<short>(targetLength);
double
position = 0.0;
double
step = shiftFactor;
for (int i
= 0; i < targetLength; i++)
{
int
idx = (int)position;
double
frac = position - idx;
double
value = 0;
if
(idx + 1 < samples.Count)
value = (1 - frac) * samples[idx] + frac * samples[idx + 1];
else
if (idx < samples.Count)
value = samples[idx];
//
Normalize and convert to 16-bit PCM
// short pcmVal = (short)(Math.Clamp(value,
-1.0, 1.0) * short.MaxValue);
short pcmVal = (short)(Clamp(value, -1.0, 1.0) * short.MaxValue);
output.Add(pcmVal);
position += step;
if
(position >= samples.Count - 1)
position = 0; // Loop if needed to fill up
}// for
(int i = 0; i < targetLength; i++)
return
output;
}// public
static List<short> PitchShiftPreserveDuration(List<double> samples,
double shiftFactor, int targetLength)
public static
void WriteWavFile(string filePath, List<short> samples)
{
using (var
stream = new FileStream(filePath, FileMode.Create))
{
using
(var writer = new BinaryWriter(stream))
{
int byteRate = SampleRate * Channels * BitsPerSample / 8;
int dataSize = samples.Count * BitsPerSample / 8;
//
RIFF Header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
//
fmt chunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16); // Subchunk1Size for PCM
writer.Write((short)1); // AudioFormat = PCM
writer.Write((short)Channels);
writer.Write(SampleRate);
writer.Write(byteRate);
writer.Write((short)(Channels * BitsPerSample / 8)); // BlockAlign
writer.Write((short)BitsPerSample);
//
data chunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
foreach (short s in samples)
writer.Write(s);
}//
using (var writer = new BinaryWriter(stream))
}// using
(var stream = new FileStream(filePath, FileMode.Create))
}// public
static void WriteWavFile(string filePath, List<short> samples)
}// public class
WavGeneratorFixedDuration___with_resamplers
// this dont
change speed when frequencies changes
public class
WavGenerator___44_KEEPING_SHRUTIS_FIXED_DURATION
{
const int
SampleRate = 8000;
const int
BitsPerSample = 16;
const int
Channels = 1;
public static
void
function_to___WavGenerator___44_KEEPING_SHRUTIS_FIXED_DURATION___Main(string
gtampsFilePath)
{
var
samples = File.ReadLines(gtampsFilePath)
.Where(line => double.TryParse(line, out _))
.Select(double.Parse)
.ToList();
int
originalLength = samples.Count;
var
allShifted = new List<short>();
//
Positive shrutis
for (int r
= 1; r <= 22; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0);
var
shifted = PitchShiftPreserveDuration(samples, shiftFactor, originalLength);
allShifted.AddRange(shifted);
}//for
(int r = 1; r <= 22; r++)
//
Negative shrutis
for (int r
= -22; r <= 0; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0);
var
shifted = PitchShiftPreserveDuration(samples, shiftFactor, originalLength);
allShifted.AddRange(shifted);
}//for
(int r = -22; r <= 0; r++)
string
outputPath = gtampsFilePath +
"(speed_intact)___44_FIXEDDUR_SCALES_8000Hz_16bit_" +
allShifted.Count + "_output.wav";
//
WriteWavFile(outputPath, allShifted);
WriteWavFile___another_keeping_speed_same(outputPath, allShifted);
Console.WriteLine("WAV file written to: " + outputPath);
System.Windows.Forms.MessageBox.Show("(Speeds intact)WAV file
written to: " + outputPath);
}//public
static void
function_to___WavGenerator___44_KEEPING_SHRUTIS_FIXED_DURATION___Main(string
gtampsFilePath)
// Shifts
pitch and then stretches result back to original duration
//////public
static List<short> PitchShiftPreserveDuration(List<double> samples,
double shiftFactor, int targetLength)
//////{
////// // Step 1: Resample with frequency shift
(changes duration)
////// int shiftedLength = (int)(samples.Count /
shiftFactor);
////// var shifted = new
List<double>(shiftedLength);
////// for (int i = 0; i < shiftedLength; i++)
////// {
////// double srcIndex = i * shiftFactor;
////// int idx = (int)srcIndex;
////// double frac = srcIndex - idx;
////// double value = 0;
////// if (idx + 1 < samples.Count)
////// { value = (1 - frac) * samples[idx] +
frac * samples[idx + 1]; }
////// else if (idx < samples.Count)
////// { value = samples[idx]; }
////// shifted.Add(value);
////// }//for (int i = 0; i < shiftedLength;
i++)
//////
// Step 2: Time-stretch to match original length
////// var stretched = new
List<short>(targetLength);
////// double step = (double)(shifted.Count - 1) /
(targetLength - 1);
////// for (int i = 0; i < targetLength; i++)
////// {
////// double index = i * step;
////// int idx = (int)index;
////// double frac = index - idx;
////// double val = 0;
////// if (idx + 1 < shifted.Count)
////// { val = (1 - frac) * shifted[idx] +
frac * shifted[idx + 1]; }
////// else if (idx < shifted.Count)
////// { val = shifted[idx]; }
////// short pcmVal = (short)(val *
short.MaxValue);
////// stretched.Add(pcmVal);
////// }//for (int i = 0; i < targetLength;
i++)
////// return stretched;
//////}//public static List<short> PitchShiftPreserveDuration(List<double>
samples, double shiftFactor, int targetLength)
public static
List<short> PitchShiftPreserveDuration(List<double> samples, double
shiftFactor, int targetLength)
{
var output
= new List<short>(targetLength);
double position = 0.0;
double
positionStep = shiftFactor;
for (int i
= 0; i < targetLength; i++)
{
int
idx = (int)position;
double
frac = position - idx;
double
value = 0;
if
(idx + 1 < samples.Count)
value = (1 - frac) * samples[idx] + frac * samples[idx + 1];
else
if (idx < samples.Count)
value = samples[idx];
short
pcmVal = (short)(value * short.MaxValue);
output.Add(pcmVal);
position += positionStep;
if
(position >= samples.Count - 1)
position = 0; // loop back if needed to keep filling output
}//for
(int i = 0; i < targetLength; i++)
return
output;
}// public
static List<short> PitchShiftPreserveDuration(List<double> samples,
double shiftFactor, int targetLength)
public static
void WriteWavFile___another_keeping_speed_same(string filePath,
List<short> samples)
{
using (var
stream = new FileStream(filePath, FileMode.Create))
{
using
(var writer = new BinaryWriter(stream))
{
int
byteRate = SampleRate * Channels * BitsPerSample / 8;
int dataSize = samples.Count * BitsPerSample / 8;
//
RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
//
fmt chunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16);
writer.Write((short)1); // PCM
writer.Write((short)Channels);
writer.Write(SampleRate);
writer.Write(byteRate);
writer.Write((short)(Channels * BitsPerSample / 8));
writer.Write((short)BitsPerSample);
//
data chunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
foreach (short s in samples)
{
writer.Write(s); }
}//using (var writer = new BinaryWriter(stream))
}//using
(var stream = new FileStream(filePath, FileMode.Create))
}//public
static void WriteWavFile___another_keeping_speed_same(string filePath,
List<short> samples)
}//public class
WavGenerator___44_KEEPING_SHRUTIS_FIXED_DURATION
public class
WavGenerator___44_KEEPING_DURATION_VARIABLE_SAMPLE_RATE
{
const int
BaseSampleRate = 8000;
const int
BitsPerSample = 16;
const int
Channels = 1;
public static
void
function_to___WavGenerator___44_KEEPING_DURATION_VARIABLE_SAMPLE_RATE___Main(string
gtampsFilePath)
{
var samples =
File.ReadLines(gtampsFilePath)
.Where(line => double.TryParse(line, out _))
.Select(double.Parse)
.ToList();
int
totalSamples = samples.Count;
for (int r
= 1; r <= 22; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0);
int
newSampleRate = (int)(BaseSampleRate * shiftFactor);
string
outputPath = gtampsFilePath + $"___shruti_up_{r}_samplerate_{newSampleRate}.wav";
WriteWavFile_WithCustomSampleRate(outputPath, samples, newSampleRate);
}//for
(int r = 1; r <= 22; r++)
for (int r
= -22; r <= 0; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0);
int
newSampleRate = (int)(BaseSampleRate * shiftFactor);
string
outputPath = gtampsFilePath +
$"___shruti_down_{Math.Abs(r)}_samplerate_{newSampleRate}.wav";
WriteWavFile_WithCustomSampleRate(outputPath, samples, newSampleRate);
}//for
(int r = -22; r <= 0; r++)
Console.WriteLine("44 pitch-shifted files written with variable
sample rates (durations preserved).");
System.Windows.Forms.MessageBox.Show("WAVs written: duration
preserved, pitch changed, sample rates varied.");
}//public
static void
function_to___WavGenerator___44_KEEPING_DURATION_VARIABLE_SAMPLE_RATE___Main(string
gtampsFilePath)
public static void
WriteWavFile_WithCustomSampleRate(string filePath, List<double> samples,
int sampleRate)
{
using (var
stream = new FileStream(filePath, FileMode.Create))
{
using
(var writer = new BinaryWriter(stream))
{
int byteRate = sampleRate * Channels * BitsPerSample / 8;
int dataSize = samples.Count * BitsPerSample / 8;
//
RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
//
fmt chunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16);
writer.Write((short)1); // PCM
writer.Write((short)Channels);
writer.Write(sampleRate); // <-- variable sample rate here
writer.Write(byteRate);
writer.Write((short)(Channels
* BitsPerSample / 8));
writer.Write((short)BitsPerSample);
//
data chunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
foreach (double sample in samples)
{
short s = (short)(sample * short.MaxValue);
writer.Write(s);
}
}//using writer
}//using
stream
}//public
static void WriteWavFile_WithCustomSampleRate(...)
}//class
WavGenerator___44_KEEPING_DURATION_VARIABLE_SAMPLE_RATE
///
<summary>
///
/////////////////////////////////////////////////////////////////////////////////////
///
</summary>
public class
WavGenerator___12x3_ChromaticShrutis_From_SAA_GTAMPS
{
const int
BaseSampleRate = 8000;
const int
BitsPerSample = 16;
const int
Channels = 1;
public static
StringBuilder ___combined_all_gtamps = new StringBuilder();
public static
void function_to___Generate_All_12x3_SemitoneNotes_From_SAA_GTAMPS(string
gtampsFilePath)
{
___combined_all_gtamps.Clear();
var
samples = File.ReadLines(gtampsFilePath)
.Where(line => double.TryParse(line, out _))
.Select(double.Parse)
.ToList();
// These
36 labels are expected exactly from your shruti table
string[]
noteLabels = new string[]
{
".S", ".R", ".R#", ".G",
".G#", ".M", ".M#", ".P",
".D", ".D#", ".N", ".N#",
"S", "R", "R#", "G",
"G#", "M", "M#", "P", "D",
"D#", "N", "N#",
"S.", "R.", "R#.", "G.",
"G#.", "M.", "M#.", "P.",
"D.", "D#.", "N.", "N#.","dash"
};
for (int i
= 0; i < noteLabels.Length; i++)
{
double
semitoneShift = i - 12; // Make "S" (index 12) the base reference (0)
double
shiftFactor = Math.Pow(2.0, semitoneShift / 12.0);
int
newSampleRate = (int)(BaseSampleRate * shiftFactor);
string
fileName = noteLabels[i].Replace("#",
"hash").Replace(".", "_DOT_");
string
outputPath = Path.Combine(Path.GetDirectoryName(gtampsFilePath), fileName +
".wav");
WriteWavFile_WithCustomSampleRate(outputPath, samples, newSampleRate,
fileName);
}//for
(int i = 0; i < noteLabels.Length; i++)
// Save
the entire concatenated GTAMPS (optional master file)
File.WriteAllText(gtampsFilePath +
"_36_notes_combined.gtamps", ___combined_all_gtamps.ToString());
Console.WriteLine("36 chromatic notes across 3 octaves
generated.");
System.Windows.Forms.MessageBox.Show("36 chromatic WAVs written
from Sa sample.");
}//public
static void
function_to___Generate_All_12x3_SemitoneNotes_From_SAA_GTAMPS(string
gtampsFilePath)
public static
void WriteWavFile_WithCustomSampleRate(string filePath, List<double>
samples, int sampleRate, string noteLabel)
{
using (var
stream = new FileStream(filePath, FileMode.Create))
using (var
writer = new BinaryWriter(stream))
{
int
byteRate = sampleRate * Channels * BitsPerSample / 8;
int
dataSize = samples.Count * BitsPerSample / 8;
//
RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
// fmt
chunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16);
writer.Write((short)1); // PCM
writer.Write((short)Channels);
writer.Write(sampleRate);
writer.Write(byteRate);
writer.Write((short)(Channels * BitsPerSample / 8));
writer.Write((short)BitsPerSample);
//
data chunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
var sb
= new StringBuilder();
foreach (double sample in samples)
{
short s = (short)(sample * short.MaxValue);
if(filePath.Contains("dash"))
{
writer.Write((short)0.0000001);
string textVal = "0.0000001";// s.ToString("F8");
sb.AppendLine(textVal);
___combined_all_gtamps.AppendLine(textVal);
}
else
{
writer.Write(s);
string textVal = s.ToString("F8");
sb.AppendLine(textVal);
___combined_all_gtamps.AppendLine(textVal);
}//end of else of if(filePath.Contains("dash"))
}//foreach (double sample in samples)
File.WriteAllText(filePath + ".gtamps", sb.ToString());
}//using
(var writer = new BinaryWriter(stream))
} //public
static void WriteWavFile_WithCustomSampleRate(string filePath,
List<double> samples, int sampleRate, string noteLabel)
}//public class
WavGenerator___12x3_ChromaticShrutis_From_SAA_GTAMPS
///
<summary>
///
//////////////////////////////////////////////////////////////////////////////
///
</summary>
public class
WavGenerator___ShrutiLibrary_From_SAA_Sample
{
const int
BaseSampleRate = 8000;
const int
BitsPerSample = 16;
const int
Channels = 1;
public static
StringBuilder
___public_static_combined_for_88_shrutis_in_one_gtamps_stringBuilder = new
StringBuilder();
public static
void function_to___Generate_All_Shrutis_From_SAA_GTAMPS(string gtampsFilePath)
{
___public_static_combined_for_88_shrutis_in_one_gtamps_stringBuilder
= new StringBuilder();
___public_static_combined_for_88_shrutis_in_one_gtamps_stringBuilder.Clear();
var
samples = File.ReadLines(gtampsFilePath)
.Where(line =>
double.TryParse(line, out _))
.Select(double.Parse)
.ToList();
// All
shruti names mapped to their shift positions (-22 to +22)
string[]
shrutiNames = new string[]
{
"L_Re1", "L_Re2", "L_Ma2S",
"L_Pa2", "M_Sa", "M_Re1", "M_Re2",
"M_Re", "M_Ga1", "M_Ga2", "M_Ga",
"M_Ma", "M_Ma1", "M_Ma2",
"M_Pa", "M_Dha1", "M_Dha2", "L_Re",
"L_Ga1", "L_Ga2", "L_Ga", "L_Ma",
"L_Ma1", "L_Ma2",
"L_Pa", "L_Dha1", "L_Dha2", "L_Dha",
"L_Ni1", "L_Ni2", "L_Ni2", "L_Re2S",
"L_Ga2S", "M_Dha", "M_Ni1",
"M_Ni2", "M_Ni", "M_Sa2", "M_Re2S",
"M_Ga2S", "M_Ma2S", "M_Pa2",
"U_Sa", "U_Re1", "U_Re2",
"U_Re", "L_Ma2S", "L_Pa2", "M_Sa",
"M_Re1", "M_Re2", "M_Re", "M_Ga1",
"M_Ga2", "M_Ga", "M_Ma",
"M_Ma1", "M_Ma2", "M_Pa", "M_Dha1",
"M_Dha2", "L_Re", "L_Ga1", "L_Ga2",
"L_Ga", "L_Ma", "L_Ma1",
"L_Ma2", "L_Pa", "L_Dha1", "U_Ga1",
"U_Ga2", "U_Ga", "U_Ma", "U_Ma1",
"U_Ma2", "U_Pa", "U_Dha1",
"U_Dha2", "U_Dha", "U_Ni1", "U_Ni2",
"U_Ni", "U_Sa2", "U_Re2S",
"U_Ga2S", "U_Ma2S",
"U_Pa2","dash"
};
int
totalShrutis = shrutiNames.Length;
for (int r
= 0; r < totalShrutis; r++)
{
int
shrutiIndex = r - 22;
double
shiftFactor = Math.Pow(2, shrutiIndex / 22.0);
int newSampleRate = (int)(BaseSampleRate *
shiftFactor);
string
fileName = shrutiNames[r];
//////string outputPath =
Path.Combine(Path.GetDirectoryName(gtampsFilePath),
//////
fileName +
"_shruti.wav");
///
string
outputPath = Path.Combine(Path.GetDirectoryName(gtampsFilePath),
fileName + ".wav");
WriteWavFile_WithCustomSampleRate(outputPath, samples, newSampleRate);
}//for
(int r = 0; r < totalShrutis; r++)
System.IO.File.WriteAllText
(
gtampsFilePath+"_combined_88_shrutis.GTAMPS"
,
___public_static_combined_for_88_shrutis_in_one_gtamps_stringBuilder.ToString()
);
Console.WriteLine($"{totalShrutis} shruti files written using
sample rate modulation.");
System.Windows.Forms.MessageBox.Show($"{totalShrutis} shruti WAVs
written from SAA sample.");
}//public
static void function_to___Generate_All_Shrutis_From_SAA_GTAMPS
public static
void WriteWavFile_WithCustomSampleRate(string filePath, List<double>
samples, int sampleRate)
{
using (var
stream = new FileStream(filePath, FileMode.Create))
{
using (var
writer = new BinaryWriter(stream))
{
int
byteRate = sampleRate * Channels * BitsPerSample / 8;
int dataSize
= samples.Count * BitsPerSample / 8;
//
RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
// fmt chunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16);
writer.Write((short)1); // PCM
writer.Write((short)Channels);
writer.Write(sampleRate);
writer.Write(byteRate);
writer.Write((short)(Channels * BitsPerSample / 8));
writer.Write((short)BitsPerSample);
//
data chunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
StringBuilder ___temp_stringbuilder_for_current_shrutis_GTAMPS = new
StringBuilder();
___temp_stringbuilder_for_current_shrutis_GTAMPS.Clear();
foreach (double sample in samples)
{
short s = (short)(sample * short.MaxValue);
if(filePath.Contains("dash"))
{
writer.Write((short)0.000001);
___public_static_combined_for_88_shrutis_in_one_gtamps_stringBuilder
.AppendLine("0.000001");
___temp_stringbuilder_for_current_shrutis_GTAMPS
.AppendLine("0.000001");
}
else
{
writer.Write(s);
___public_static_combined_for_88_shrutis_in_one_gtamps_stringBuilder
.AppendLine(s.ToString("F8"));
___temp_stringbuilder_for_current_shrutis_GTAMPS
.AppendLine(s.ToString("F8"));
}//endof else of if(filePath.Contains("dash"))
}//foreach (double sample in samples)
///////////////////////////////////////////////////////////////////////////////////////////
System.IO.File.WriteAllText
(
filePath + "_bytrts_" + byteRate+"_sps_"+
sampleRate+ "_dtsizes_"+ dataSize + "_samples_" +
samples.Count + "_" + ".gtamps"
,
___temp_stringbuilder_for_current_shrutis_GTAMPS.ToString()
);
}//
using (var writer = new BinaryWriter(stream))
}//using
(var stream = new FileStream(filePath, FileMode.Create))
}//public
static void WriteWavFile_WithCustomSampleRate
}//public class
WavGenerator___ShrutiLibrary_From_SAA_Sample
//////public class
WavGenerator___MultiPitch_SingleFile_FixedDurations
//////{
////// const int BaseSampleRate = 44100;// 8000;
// can be increased for higher resolution
////// const int BitsPerSample = 16;
////// const int Channels = 1;
////// public static void
function_to___ApplyPerRegionPitchShift_AND_SaveUnifiedWav(string
gtampsFilePath, int numRegions)
////// {
////// var originalSamples =
File.ReadLines(gtampsFilePath)
////// .Where(line
=> double.TryParse(line, out _))
//////
.Select(double.Parse)
////// .ToList();
//////
// Apply pitch shifts to each region
////// List<short> finalPcm =
ApplyPerRegionPitchShift(originalSamples, numRegions);
////// // Save to single WAV
////// string outputPath = gtampsFilePath +
$"CONCATENATED____{numRegions}_Regions_MultiPitch_FixedDuration_output.wav";
//////
WriteWavFile___SingleFile_ConstantSampleRate(outputPath, finalPcm,
BaseSampleRate);
////// Console.WriteLine("Unified WAV
written with pitch-shifted segments (duration preserved).");
//////
System.Windows.Forms.MessageBox.Show("Unified WAV written with
pitch-shifted segments (duration preserved): " + outputPath);
////// }//public static void
function_to___ApplyPerRegionPitchShift_AND_SaveUnifiedWav(...)
////// public static List<short>
ApplyPerRegionPitchShift(List<double> original, int numRegions)
////// {
////// //////int totalSamples =
original.Count;
////// //////int segmentLength = totalSamples
/ numRegions;
////// //////List<short> output = new
List<short>();
////// int totalSamples = original.Count *
numRegions;//SAAN CHANGES THIS SINCE I THINK IF I NEED 44 COPIES THEN THERE
NEEDS 44 TIMES ORIGINAL DURATION IN CONCATENATED FILES PLAY TIMES
////// int segmentLength = totalSamples /
numRegions;
////// List<short> output = new
List<short>();// OBVIOUSLY IF I AM GENERATING N NUMBERS OF PITCH CHANGED
COPIES THEN ATLEAST N NUMBER OF TIMES ORIGINAL SAMPLES COUNT TO REPEAT
////// Random rnd = new Random();//??? WHY IS
THIS??????????
////// for (int region = 0; region <
numRegions; region++)
////// {
////// var segment = original.Skip(region
* segmentLength).Take(segmentLength).ToList();
////// // Choose a shift factor
(shruti-style): use 2^(r/22.0), r from -22 to +22
////// int shrutiStep = -22 + (44 * region
/ numRegions); // evenly spaced across shruti scale
////// // ALL OF THESE COPIED REGIONS NEED SAME DURATION OF PLAY
TIME AS ACTUAL totalSamples PLAY TIME FREQUENCIES ARE TO CHANGE
////// double shiftFactor = Math.Pow(2,
shrutiStep / 22.0);
////// var regionPcm =
PitchShift_PreserveDuration(segment, shiftFactor, segmentLength);
////// output.AddRange(regionPcm);
////// }
////// return output;
////// }//public static List<short>
ApplyPerRegionPitchShift(List<double> original, int numRegions)
////// public static List<short>
PitchShift_PreserveDuration(List<double> input, double shiftFactor, int
outputLength)
////// {
////// var result = new
List<short>(outputLength);
////// double pos = 0.0;
////// double posStep = shiftFactor;
////// for (int i = 0; i < outputLength;
i++)
////// {
////// int idx = (int)pos;
////// double frac = pos - idx;
////// double value = 0;
////// if (idx + 1 < input.Count)
////// value = (1 - frac) * input[idx]
+ frac * input[idx + 1];
////// else if (idx < input.Count)
////// value = input[idx];
////// short pcmVal = (short)(value *
short.MaxValue);
////// result.Add(pcmVal);
////// pos += posStep;
////// if (pos >= input.Count - 1)
////// pos = 0; // loop or clamp,
depending on preference
//////
}
////// return result;
////// }//public static List<short>
PitchShift_PreserveDuration(...)
////// public static void
WriteWavFile___SingleFile_ConstantSampleRate(string filePath, List<short>
samples, int sampleRate)
////// {
////// using (var stream = new
FileStream(filePath, FileMode.Create))
////// using (var writer = new
BinaryWriter(stream))
////// {
////// int byteRate = sampleRate *
Channels * BitsPerSample / 8;
////// int dataSize = samples.Count *
BitsPerSample / 8;
////// // RIFF header
//////
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
////// writer.Write(36 + dataSize);
//////
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
////// // fmt chunk
////// writer.Write(Encoding.ASCII.GetBytes("fmt
"));
////// writer.Write(16);
////// writer.Write((short)1); // PCM
////// writer.Write((short)Channels);
////// writer.Write(sampleRate);
////// writer.Write(byteRate);
////// writer.Write((short)(Channels *
BitsPerSample / 8));
////// writer.Write((short)BitsPerSample);
////// // data chunk
//////
writer.Write(Encoding.ASCII.GetBytes("data"));
////// writer.Write(dataSize);
////// foreach (short s in samples)
////// writer.Write(s);
////// }
////// }//public static void
WriteWavFile___SingleFile_ConstantSampleRate(...)
//////}//class
WavGenerator___MultiPitch_SingleFile_FixedDurations
//////public class
WavGenerator___MultiPitch_SingleFile_FixedDurations
//////{
////// const int BaseSampleRate = 44100;
////// const int BitsPerSample = 16;
////// const int Channels = 1;
////// public static void
function_to___ApplyPerRegionPitchShift_AND_SaveUnifiedWav(string
gtampsFilePath, int numRegions)
////// {
////// var originalSamples =
File.ReadLines(gtampsFilePath)
////// .Where(line
=> double.TryParse(line, out _))
//////
.Select(double.Parse)
////// .ToList();
////// // Generate shifted versions
////// List<short> finalPcm = new
List<short>();
////// for (int region = 0; region <
numRegions; region++)
////// {
////// // Shruti step evenly from -22 to
+22
////// int shrutiStep = -22 + (44 * region
/ numRegions);
////// double shiftFactor = Math.Pow(2,
shrutiStep / 22.0);
////// var pitchShifted =
ResamplePitchShift(originalSamples, shiftFactor);
////// // Now stretch/truncate
pitch-shifted to same duration as original (i.e., same sample count)
////// var fixedDuration =
StretchToLength(pitchShifted, originalSamples.Count);
////// finalPcm.AddRange(fixedDuration);
////// }
////// // Save to WAV
////// string outputPath = gtampsFilePath +
$"__{numRegions}_Regions_MultiPitch_FIXED_T_Duration_output.wav";
////// WriteWavFile(outputPath, finalPcm,
BaseSampleRate);
////// Console.WriteLine("Saved WAV:
pitch-shifted regions, each fixed to original duration.");
//////
System.Windows.Forms.MessageBox.Show("Output WAV: multi-pitch, all
segments same duration.");
////// }
////// public static List<double>
ResamplePitchShift(List<double> input, double shiftFactor)
////// {
////// int newLength = (int)(input.Count /
shiftFactor);
////// List<double> result = new
List<double>(newLength);
//////
for (int i = 0; i < newLength; i++)
////// {
////// double srcIndex = i * shiftFactor;
////// int idx = (int)srcIndex;
////// double frac = srcIndex - idx;
////// double val = 0;
////// if (idx + 1 < input.Count)
////// val = (1 - frac) * input[idx] +
frac * input[idx + 1];
////// else if (idx < input.Count)
////// val = input[idx];
////// result.Add(val);
////// }
////// return result;
////// }
////// public static List<short>
StretchToLength(List<double> input, int targetLength)
////// {
////// List<short> output = new
List<short>(targetLength);
////// double step = (double)(input.Count - 1)
/ (targetLength - 1);
////// for (int i = 0; i < targetLength;
i++)
////// {
////// double srcIndex = i * step;
////// int idx = (int)srcIndex;
////// double frac = srcIndex - idx;
////// double val = 0;
////// if (idx + 1 < input.Count)
////// val = (1 - frac) * input[idx] +
frac * input[idx + 1];
////// else if (idx < input.Count)
////// val = input[idx];
////// output.Add((short)(val *
short.MaxValue));
////// }
////// return output;
////// }
////// public static void WriteWavFile(string
filePath, List<short> samples, int sampleRate)
////// {
////// using (var stream = new
FileStream(filePath, FileMode.Create))
////// using (var writer = new
BinaryWriter(stream))
////// {
////// int byteRate = sampleRate *
Channels * BitsPerSample / 8;
////// int dataSize = samples.Count *
BitsPerSample / 8;
////// // RIFF header
//////
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
////// writer.Write(36 + dataSize);
//////
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
////// // fmt chunk
//////
writer.Write(Encoding.ASCII.GetBytes("fmt "));
////// writer.Write(16);
////// writer.Write((short)1); // PCM
////// writer.Write((short)Channels);
////// writer.Write(sampleRate);
////// writer.Write(byteRate);
////// writer.Write((short)(Channels *
BitsPerSample / 8));
////// writer.Write((short)BitsPerSample);
////// // data chunk
//////
writer.Write(Encoding.ASCII.GetBytes("data"));
////// writer.Write(dataSize);
////// foreach (short s in samples)
////// writer.Write(s);
////// }
////// }
//////}//public
class WavGenerator___MultiPitch_SingleFile_FixedDurations
public class
WavGenerator___MultiPitch___IT_GENERATES_SAME_PITCH_COPIES_ONLY____SingleFile_FixedDurations
{
//WE CANNOT
CHANGE THE SAMPLE RATE FROM THE ORIGINAL GTAMPS SAMPLE RATES
const int
BaseSampleRate = 8000;//44100;//8000;// 16000;//8000;//44100; // or 8000, if
desired
const int BitsPerSample = 16;
const int
Channels = 1;
public static
void function_to___ApplyPerRegionPitchShift_AND_SaveUnifiedWav(string
gtampsFilePath, int numRegions)
{
var
originalSamples = File.ReadLines(gtampsFilePath)
.Where(line => double.TryParse(line, out _))
.Select(double.Parse)
.ToList();
List<short> finalPcm = ApplyPerRegionPitchShift(originalSamples,
numRegions);
// string outputPath = gtampsFilePath +
$"___{numRegions}_Regions_MultiPitch_FIXED_T_Duration_output.wav";
string
outputPath = gtampsFilePath +
$"___{numRegions}_Regions_SAMEPitchCOPY_FIXED_T_Duration_output.wav";
WriteWavFile(outputPath, finalPcm, BaseSampleRate);
Console.WriteLine("Unified WAV written with pitch-shifted regions
(duration preserved).");
System.Windows.Forms.MessageBox.Show("WAV with multiple pitches and
fixed durations saved:\n" + outputPath);
}//public
static void function_to___ApplyPerRegionPitchShift_AND_SaveUnifiedWav
public static
List<short> ApplyPerRegionPitchShift(List<double> original, int
numRegions)
{
//
double segmentDurationSec = original.Count / (double)BaseSampleRate;
// double
segmentDurationSec = (original.Count* numRegions) / (double)BaseSampleRate;
double
segmentDurationSec = original.Count / (double)BaseSampleRate; //WHEN I DO THIS
THEN ALL ARE PLAYING WITH SAME FREWQUENCY
List<short> output = new List<short>();
for (int
region = 0; region < numRegions; region++)
{
//int shrutiStep = -22 + (44 * region / numRegions);
int
shrutiStep = -(int)(numRegions/2) + (44 * region / numRegions);
double
shiftFactor = Math.Pow(2, shrutiStep / 22.0);
var
pitchShifted = ResamplePitchShift(original, shiftFactor);
int
desiredSampleCount = (int)(segmentDurationSec * BaseSampleRate);
var
adjusted = StretchToLength(pitchShifted, desiredSampleCount);
output.AddRange(adjusted);
}//for (int region = 0; region <
numRegions; region++)
return
output;
}//public
static List<short> ApplyPerRegionPitchShift
public static
List<double> ResamplePitchShift(List<double> input, double
shiftFactor)
{
int
newLength = (int)(input.Count / shiftFactor);
var result
= new List<double>(newLength);
for (int i
= 0; i < newLength; i++)
{
double
srcIndex = i * shiftFactor;
int
idx = (int)srcIndex;
double
frac = srcIndex - idx;
double
val = 0;
if
(idx + 1 < input.Count)
val = (1 - frac) * input[idx] + frac * input[idx + 1];
else
if (idx < input.Count)
val = input[idx];
result.Add(val);
}//for
(int i = 0; i < newLength; i++)
return
result;
}//public
static List<double> ResamplePitchShift
public static
List<short> StretchToLength(List<double> input, int targetLength)
{
var output
= new List<short>(targetLength);
double
step = (double)(input.Count - 1) / (targetLength - 1);
for (int i
= 0; i < targetLength; i++)
{
double
srcIndex = i * step;
int
idx = (int)srcIndex;
double
frac = srcIndex - idx;
double
val = 0;
if
(idx + 1 < input.Count)
{
val = (1 - frac) * input[idx] + frac * input[idx + 1];
}
else
if (idx < input.Count)
{
val = input[idx];
}
short
pcm = (short)(val * short.MaxValue);
output.Add(pcm);
}
return
output;
}//public
static List<short> StretchToLength
public static
void WriteWavFile(string filePath, List<short> samples, int sampleRate)
{
using (var
stream = new FileStream(filePath, FileMode.Create))
using (var
writer = new BinaryWriter(stream))
{
int
byteRate = sampleRate * Channels * BitsPerSample / 8;
int
dataSize = samples.Count * BitsPerSample / 8;
//
RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
// fmt
chunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16);
writer.Write((short)1); // PCM
writer.Write((short)Channels);
writer.Write(sampleRate);
writer.Write(byteRate);
writer.Write((short)(Channels * BitsPerSample / 8));
writer.Write((short)BitsPerSample);
//
data chunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
foreach (short s in samples)
writer.Write(s);
}
}//public
static void WriteWavFile
}//public class
WavGenerator___MultiPitch_SingleFile_FixedDurations
public class
WavGenerator___Reads_GTAMPS_MONO_8000_Hz_Samples_16_bits_no_3rd_party_apis
{
const int
SampleRate = 8000;
const int
BitsPerSample = 16;
const int
Channels = 1;
const double
Pi = Math.PI;
public static
void
SAANS___WavGenerator___Reads_GTAMPS_MONO_8000_Hz_Samples_16_bits_no_3rd_party_apis___Main(string
gtamps_filesnames)
{
string
inputPath = gtamps_filesnames;// "your_file_path_here.txt";
// Read
float samples
var
samples = File.ReadLines(inputPath)
.Where(line => double.TryParse(line, out _))
.Select(double.Parse)
.ToList();
var
allShifted = new List<short>();
for (int r
= 1; r <= 22; r++)
{
double
shiftFactor = Math.Pow(2, r / 22.0);
var
shifted = ResampleWithFrequencyShift(samples, shiftFactor);
allShifted.AddRange(shifted);
}//for
(int r = 1; r <= 22; r++)
string
outputPath = inputPath +
"___22_times_8000hZ_16_BITS_PCMS_"+allShifted.Count.ToString() +
"_output.wav";
WriteWavFile(outputPath, allShifted);
Console.WriteLine("WAV file written to: " + outputPath);
}//public
static void
SAANS___WavGenerator___Reads_GTAMPS_MONO_8000_Hz_Samples_16_bits_no_3rd_party_apis___Main(STRING
gtamps_filesnames)
public static
List<short> ResampleWithFrequencyShift(List<double> samples, double
shiftFactor)
{
int
newLength = (int)(samples.Count / shiftFactor);
var
shifted = new List<short>(newLength);
for (int i
= 0; i < newLength; i++)
{
double
srcIndex = i * shiftFactor;
int
idx = (int)srcIndex;
double
frac = srcIndex - idx;
double
value = 0;
if
(idx + 1 < samples.Count)
value = (1 - frac) * samples[idx] + frac * samples[idx + 1];
else
if (idx < samples.Count)
value = samples[idx];
short
pcmVal = (short)(value * short.MaxValue);
shifted.Add(pcmVal);
}//for
(int i = 0; i < newLength; i++)
return
shifted;
}//public
static List<short> ResampleWithFrequencyShift(List<double> samples,
double shiftFactor)
public static
void WriteWavFile(string filePath, List<short> samples)
{
using (var
stream = new FileStream(filePath, FileMode.Create))
using (var
writer = new BinaryWriter(stream))
{
int
byteRate = SampleRate * Channels * BitsPerSample / 8;
int dataSize = samples.Count *
BitsPerSample / 8;
//
RIFF header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataSize);
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
// fmt
subchunk
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16); // PCM
writer.Write((short)1); // AudioFormat = PCM
writer.Write((short)Channels);
writer.Write(SampleRate);
writer.Write(byteRate);
writer.Write((short)(Channels * BitsPerSample / 8));
writer.Write((short)BitsPerSample);
//
data subchunk
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataSize);
foreach (short s in samples)
{
writer.Write(s); }
}//using
(var writer = new BinaryWriter(stream))
}//public
static void WriteWavFile(string filePath, List<short> samples)
}//public class
WavGenerator___Reads_GTAMPS_MONO_8000_Hz_Samples_16_bits_no_3rd_party_apis
}//namespace
SAAN_FRESH___RAW_NON_APIS_WAVES_TO_SHRUTIES_COPIER_GPTS
Excellent — your
code is already strong, but here’s a precise enhancement for exact-duration,
pitch-shifted resampling of one input into 66 shrutis, all packed in a
single 8000Hz WAV file, using no third-party API and well-calculated
resample factors.
🔧
GOAL
Given a sample of
duration T ms, create 66 pitch-shifted copies that:
- Cover 22 shrutis ↓ (lower), 22 base
(no shift), 22 shrutis ↑ (higher)
- Each copy plays for the same
duration as original
- Output is a single WAV at 8000 Hz
with all 66 concatenated
✅
KEY CONCEPT — Sample Rate Shifting for Pitch
To pitch shift without
changing duration, resample using:
ini
CopyEdit
resampleFactor
= pow(2, semitoneShift / 12.0)
Then resample the input
to simulate the pitch change, and trim or stretch to match the original
time T.
✅
ENHANCED METHOD OUTLINE
Here’s a complete
new function to add in your class:
csharp
CopyEdit
public
static void GenerateShrutiVariants(string inputWavPath, string outputWavPath)
{
// Read source WAV
float[] sourceSamples =
ReadWavMonoAsFloats(inputWavPath, out int sourceRate);
int outputRate = 8000;
double durationSec = sourceSamples.Length /
(double)sourceRate;
int targetSamplesPerVariant = (int)(durationSec
* outputRate);
List<float> all66 = new List<float>();
for (int i = -33; i <= 32; i++) // 66
shifts: -33 to +32
{
double semitoneShift = i * (1.0 / 3); //
22 shrutis = 1 octave = 12 semitones
double resampleFactor = Math.Pow(2,
semitoneShift / 12.0);
int resampledLength = (int)(sourceSamples.Length
/ resampleFactor);
// Step 1: Resample to pitch-shift (no
time correction yet)
float[] pitchShifted =
ResampleWithFactor(sourceSamples, resampledLength);
// Step 2: Time-correct by
stretching/compressing to match original duration
float[] durationMatched =
StretchToLength(pitchShifted, targetSamplesPerVariant);
all66.AddRange(durationMatched);
}
SaveWav(outputWavPath, all66.ToArray(),
outputRate, 1);
Console.WriteLine("All 66 shruti
variants saved.");
MessageBox.Show("Done: 66 shruti
pitch-shifted variants created.");
}
✅
SUPPORT FUNCTIONS
Pitch-based
Resampler:
csharp
CopyEdit
public
static float[] ResampleWithFactor(float[] input, int targetLength)
{
float[] result = new float[targetLength];
double step = (double)(input.Length - 1) /
(targetLength - 1);
for (int i = 0; i < targetLength; i++)
{
double pos = i * step;
int idx = (int)pos;
double frac = pos - idx;
result[i] = (idx + 1 < input.Length)
? (float)((1 - frac) * input[idx] +
frac * input[idx + 1])
: input[idx];
}
return result;
}
Stretch (Time
Correction):
csharp
CopyEdit
public
static float[] StretchToLength(float[] input, int targetLength)
{
return ResampleWithFactor(input,
targetLength); // same method, reused
}
📊
ALGORITHM SUMMARY
- For each shruti index (-33 to +32):
- Convert index
to semitone fraction.
- Calculate
pitch shift factor using 2^(x/12).
- Resample the
waveform to alter pitch.
- Adjust the
output length to T ms at 8000 Hz
using interpolation.
- Concatenate all outputs into one
array.
- Write to single mono WAV at 8000 Hz.
✅
Output
- One outputWavPath.wav
- Format: Mono, 8000 Hz
- Duration = 66 × T ms
❓To
Proceed
Would you like me
to integrate this logic directly into one of your existing SwaralipiConcatenator classes or create
a new self-contained class file?
Okay, I can help
you with that! This is an interesting audio processing task. We'll create a C#
program that takes a WAV file, generates 66 pitch-shifted versions of it, each
maintaining the original's perceived duration, and then concatenates them into
a single output WAV file. The output will be mono, 16-bit, at an 8000 Hz sample
rate.
The 66 versions
will cover a range of pitches equivalent to -33 to +32 shrutis, assuming 22
shrutis per octave. This will span approximately 3 octaves.
Here's the plan:
- WAV File Reading: We'll
implement a utility to read the input WAV file. This utility will:
- Parse the WAV
header to get information like sample rate, bit depth, and number of
channels.
- Read the
audio data.
- Convert the
audio data to a standardized mono floating-point format (samples ranging
from -1.0 to 1.0). This makes processing consistent regardless of the
input WAV's specifics.
- Pitch Shifting with Duration
Preservation: This is the core of the process. For each
of the 66 shruti variations:
- We'll
calculate a pitchShiftFactor. A factor
greater than 1.0 raises the pitch, and less than 1.0 lowers it. The
formula Math.Pow(2.0, shrutiValue /
22.0) will be used, where shrutiValue ranges from
-33 to +32.
- The original
duration of the input audio will be determined. Each pitch-shifted
segment must have this same duration when played back.
- A resampling
algorithm will generate the audio samples for the current shruti-shifted
version. This algorithm will:
- Determine
the number of output samples needed to match the original duration at
the target 8000 Hz sample rate.
- Iterate
through each output sample. For each output sample, it will calculate
the corresponding position to read from in the input audio data.
This calculation involves the pitchShiftFactor and the
ratio of input to output sample rates.
- Use linear
interpolation to calculate the sample value if the read position
falls between two input samples. This is a common and computationally
reasonable way to resample.
- If the read
position goes beyond the end of the input audio (which can happen when
pitching up, as we read through the input faster), the input audio will
be effectively "looped" to ensure the output segment is filled
for its entire target duration. This addresses the "no
truncation" requirement.
- Concatenation: All 66
pitch-shifted audio segments (now in a float format) will be concatenated
into one long list of samples.
- WAV File Writing: Finally, a
utility will write this concatenated list of samples to a new WAV file.
This output file will be:
- Mono.
- 8000 Hz
sample rate.
- 16-bit depth.
I'll provide the C#
code within an immersive document, with detailed explanations for each part.
C#
Shruti Pitch Shifter
Jun
5, 12:24 AM
Try
again without Canvas
Explanation of the Code:
- WavUtils Class:
- WavHeader Struct: Defines the
structure of a standard WAV file header. This helps in parsing and
understanding the input WAV file.
- ReadWav Method:
- Reads the
entire WAV file into a byte array.
- Uses a BinaryReader to parse the RIFF, "fmt
", and "data" chunks of the WAV header.
- It supports
reading 8-bit, 16-bit, 24-bit, and 32-bit (integer and float) PCM WAV
files.
- Converts the
audio samples into a float[] array where
each sample is normalized to the range [-1.0, 1.0].
- If the input
is stereo, it converts it to mono by averaging the left and right
channels.
- Returns the
mono float samples and the parsed WavHeader.
- WriteWav Method:
- Takes a
collection of float samples
(which will be the concatenated, pitch-shifted audio).
- Writes a
standard WAV file header for a mono, 16-bit PCM file with the specified sampleRate (which will be 8000 Hz for the
output).
- Converts the
float samples (clamped to [-1.0, 1.0]) back to 16-bit short integers.
- Writes the
sample data to the file.
- ShrutiPitchProcessor Class:
- Constants: OutputSampleRate (8000 Hz) and OutputBitsPerSample (16) define the output format.
- CreateShrutiVariations Method:
- This is the
main orchestrator. It calls WavUtils.ReadWav to get the
input audio data.
- It
calculates originalDurationSeconds from the
input samples and their sample rate.
- Then, it
calculates targetSegmentLengthSamples, which is
the number of samples each pitch-shifted segment needs to have to
maintain the original duration at the OutputSampleRate of 8000 Hz.
- It loops 66
times, for shrutiOffset from -33 to
+32.
- Inside the
loop, pitchShiftFactor is
calculated using Math.Pow(2.0,
(double)shrutiOffset / 22.0). Here, 22.0 signifies 22 shrutis per octave. A shrutiOffset of -33 will result in the lowest
frequency (about 1.5 octaves down), and +32 will result in the highest
frequency (about 1.45 octaves up).
- It calls ResampleAndShiftPitch for each shrutiOffset to get the pitch-shifted audio
segment.
- All
generated segments are added to allShiftedAudioSamples.
- Finally, it
calls WavUtils.WriteWav to save the
concatenated audio to the output file.
- ResampleAndShiftPitch Method (The
Core Algorithm):
- This
function takes the original mono input samples, their sample rate, the
desired pitchShiftFactor, and the targetOutputLengthSamples for the
segment.
- It
calculates readStep = pitchShiftFactor *
((double)inputSampleRate / OutputSampleRate). This readStep is crucial:
- pitchShiftFactor: If > 1, we read the input
faster (higher pitch). If < 1, we read slower (lower pitch).
- (double)inputSampleRate / OutputSampleRate: This
ratio accounts for any difference between the input WAV's original
sample rate and our fixed output sample rate (8000 Hz).
- It iterates targetOutputLengthSamples times to
generate each sample of the output segment.
- In each
iteration i, currentInputIndexFloat = i * readStep determines
the precise (floating-point) position to read from in the inputSamples array.
- Looping/Wrapping: currentInputIndexFloat = currentInputIndexFloat %
inputSamples.Length; ensures that if currentInputIndexFloat goes beyond the length of inputSamples (common when pitching up
significantly), it wraps around to the beginning of inputSamples. This fulfills the "play
fully" requirement by effectively looping the source audio to fill
the target duration.
- Linear
Interpolation:
- indexBefore =
(int)Math.Floor(currentInputIndexFloat) gives the sample index before
the target read position.
- indexAfter = (indexBefore + 1) % inputSamples.Length gives the
sample index after (with wrap-around).
- fraction = currentInputIndexFloat - indexBefore is the
fractional part.
- The output
sample is calculated as (float)(sampleBefore
* (1.0 - fraction) + sampleAfter * fraction). This is
linear interpolation, which provides a good balance between quality and
computational cost for this type of resampling.
- Program Class:
- Contains the Main method, which handles command-line
arguments for input and output file paths.
- It calls ShrutiPitchProcessor.CreateShrutiVariations to start the
processing.
- Includes
basic error handling for file existence and unexpected exceptions.
How to Compile and Run:
- Save the code as ShrutiPitchShifterApp.cs (or any .cs file).
- Compile it using a C# compiler (like
the one included with .NET SDK): csc
ShrutiPitchShifterApp.cs (If you are using .NET Core/5/6/7/8, you
might create a project: dotnet new
console -o ShrutiApp, replace the generated Program.cs with this code, and then run dotnet build)
- Run the compiled executable from the
command line: ShrutiPitchShifterApp.exe
YourInputFile.wav OutputFileWith66Shrutis.wav
This program should
provide the functionality you're looking for, creating the 66 pitch-shifted
copies with preserved duration, all in a single output WAV file. The resampling
is "well-calculated" in the sense that it correctly adjusts the read
speed through the input samples based on the desired pitch shift and sample
rate conversion, using linear interpolation to construct the new samples.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace ShrutiPitchShifterApp
{
/// <summary>
/// Contains utility methods for
reading and writing WAV files.
/// </summary>
public static class WavUtils
{
// Represents the WAV file header
structure.
public struct WavHeader
{
public byte[] RiffId; // "RIFF"
public uint FileSize; // Total file size minus 8 bytes
public byte[] WaveId; // "WAVE"
public byte[] FmtId; // "fmt "
public uint FmtSize; // Size of the format chunk
(usually 16)
public ushort AudioFormat; // Audio format (1 for PCM)
public ushort NumChannels; // Number of channels (1 for mono, 2 for stereo)
public uint SampleRate; // Samples per second (e.g., 44100,
8000)
public uint ByteRate; // SampleRate * NumChannels *
BitsPerSample / 8
public ushort BlockAlign; // NumChannels * BitsPerSample / 8
public ushort BitsPerSample; // Bits per sample (e.g., 8, 16, 24)
public byte[] DataId; // "data"
public uint DataSize; // Size of the data chunk
}
/// <summary>
/// Reads a WAV file and returns
its audio data as mono float samples [-1.0, 1.0].
/// </summary>
/// <param
name="filePath">Path to the WAV file.</param>
/// <param
name="header">Output: The read WAV header.</param>
/// <returns>A float array of
mono samples, or null if an error occurs.</returns>
public static float[] ReadWav(string filePath, out WavHeader header)
{
header = new WavHeader();
byte[] wavBytes =
File.ReadAllBytes(filePath);
using (MemoryStream ms = new MemoryStream(wavBytes))
using (BinaryReader reader = new BinaryReader(ms))
{
try
{
// Read RIFF Chunk
header.RiffId = reader.ReadBytes(4);
if (Encoding.ASCII.GetString(header.RiffId) != "RIFF") throw new FormatException("Not a RIFF file.");
header.FileSize = reader.ReadUInt32();
header.WaveId = reader.ReadBytes(4);
if (Encoding.ASCII.GetString(header.WaveId) != "WAVE") throw new FormatException("Not a WAVE file.");
// Find and Read "fmt " Chunk
bool fmtChunkFound = false;
while (reader.BaseStream.Position < reader.BaseStream.Length)
{
byte[] chunkIdBytes = reader.ReadBytes(4);
string chunkId =
Encoding.ASCII.GetString(chunkIdBytes);
uint chunkSize = reader.ReadUInt32();
if (chunkId == "fmt ")
{
header.FmtId =
chunkIdBytes;
header.FmtSize =
chunkSize;
header.AudioFormat
= reader.ReadUInt16();
header.NumChannels = reader.ReadUInt16();
header.SampleRate = reader.ReadUInt32();
header.ByteRate
= reader.ReadUInt32();
header.BlockAlign = reader.ReadUInt16();
header.BitsPerSample = reader.ReadUInt16();
if (header.FmtSize > 16) // Skip extra format bytes
{
reader.ReadBytes((int)(header.FmtSize - 16));
}
fmtChunkFound = true;
break;
}
else
{
reader.BaseStream.Seek(chunkSize, SeekOrigin.Current); // Skip other chunks
}
}
if (!fmtChunkFound) throw new FormatException("fmt chunk not found.");
if (header.AudioFormat != 1) throw new NotSupportedException("Only PCM audio format is supported.");
// Find and Read "data" Chunk
bool dataChunkFound = false;
while (reader.BaseStream.Position < reader.BaseStream.Length)
{
byte[] chunkIdBytes = reader.ReadBytes(4);
string chunkId =
Encoding.ASCII.GetString(chunkIdBytes);
uint chunkSize = reader.ReadUInt32();
if (chunkId == "data")
{
header.DataId =
chunkIdBytes;
header.DataSize
= chunkSize;
int numSamples = (int)(header.DataSize /
header.BlockAlign);
List<float> monoSamples = new List<float>(numSamples);
for (int i = 0; i < numSamples; i++)
{
float leftSample = 0;
float rightSample = 0;
if (header.BitsPerSample == 16)
{
leftSample = reader.ReadInt16() / 32768.0f;
if (header.NumChannels == 2)
rightSample = reader.ReadInt16() / 32768.0f;
}
else if (header.BitsPerSample == 8)
{
leftSample = (reader.ReadByte() - 128) / 128.0f;
if (header.NumChannels == 2)
rightSample = (reader.ReadByte() - 128) / 128.0f;
}
else if (header.BitsPerSample == 24)
{
byte[] sampleBytes = reader.ReadBytes(3);
int val = (sampleBytes[2] << 16) | (sampleBytes[1] << 8) | sampleBytes[0];
if ((val & 0x800000) > 0) val |= ~0xFFFFFF; // Sign extend
leftSample = val / 8388608.0f; // 2^23
if (header.NumChannels == 2)
{
sampleBytes = reader.ReadBytes(3);
val = (sampleBytes[2] << 16) | (sampleBytes[1] << 8) | sampleBytes[0];
if ((val & 0x800000) > 0) val |= ~0xFFFFFF;
rightSample = val / 8388608.0f;
}
}
else if (header.BitsPerSample == 32 && header.AudioFormat == 1) // PCM 32-bit int
{
leftSample = reader.ReadInt32() / 2147483648.0f; // 2^31
if (header.NumChannels == 2)
rightSample = reader.ReadInt32() / 2147483648.0f;
}
else if (header.BitsPerSample == 32 && header.AudioFormat == 3) // PCM 32-bit float
{
leftSample = reader.ReadSingle();
if (header.NumChannels == 2)
rightSample = reader.ReadSingle();
}
else
{
throw new NotSupportedException($"BitsPerSample {header.BitsPerSample} not supported for PCM.");
}
monoSamples.Add(header.NumChannels == 2 ? (leftSample + rightSample) * 0.5f : leftSample);
}
dataChunkFound =
true;
return monoSamples.ToArray();
}
else
{
// Ensure we don't read past the
end of the stream if chunkSize is too large
if (reader.BaseStream.Position +
chunkSize > reader.BaseStream.Length) {
break;
}
reader.BaseStream.Seek(chunkSize, SeekOrigin.Current);
}
}
if (!dataChunkFound) throw new FormatException("data chunk not found.");
return null; // Should be unreachable if logic is correct
}
catch (Exception ex)
{
Console.WriteLine($"Error reading WAV: {ex.Message}");
return null;
}
}
}
/// <summary>
/// Writes a list of float samples
to a WAV file.
/// </summary>
/// <param
name="filePath">Path to save the WAV file.</param>
/// <param
name="samples">List of float samples (expected to be mono for this
application).</param>
/// <param name="sampleRate">The
sample rate for the output WAV.</param>
/// <param
name="bitsPerSample">The bit depth for the output WAV (typically
16).</param>
public static void WriteWav(string filePath, IEnumerable<float> samples, uint sampleRate, ushort bitsPerSample)
{
using (FileStream fs = new FileStream(filePath,
FileMode.Create))
using (BinaryWriter writer = new BinaryWriter(fs))
{
ushort numChannels = 1; // Output is always mono
int dataLength = samples.Count() * numChannels * (bitsPerSample / 8);
// RIFF Header
writer.Write(Encoding.ASCII.GetBytes("RIFF"));
writer.Write(36 + dataLength); // FileSize
writer.Write(Encoding.ASCII.GetBytes("WAVE"));
// Format Chunk ("fmt ")
writer.Write(Encoding.ASCII.GetBytes("fmt "));
writer.Write(16); // FmtSize (PCM)
writer.Write((ushort)1); // AudioFormat (PCM)
writer.Write(numChannels);
writer.Write(sampleRate);
writer.Write(sampleRate * numChannels * (bitsPerSample / 8)); // ByteRate
writer.Write((ushort)(numChannels * (bitsPerSample / 8))); // BlockAlign
writer.Write(bitsPerSample);
// Data Chunk ("data")
writer.Write(Encoding.ASCII.GetBytes("data"));
writer.Write(dataLength);
// Write audio data
foreach (float sampleFloat in samples)
{
// Clamp sample to [-1.0, 1.0] before conversion
float clampedSample = Math.Max(-1.0f, Math.Min(1.0f, sampleFloat));
if (bitsPerSample == 16)
{
short pcmSample = (short)(clampedSample * short.MaxValue);
writer.Write(pcmSample);
}
else if (bitsPerSample == 8)
{
byte pcmSample = (byte)((clampedSample * 127.0f) + 128);
writer.Write(pcmSample);
}
// Add other bit depths if necessary
}
}
}
}
/// <summary>
/// Handles the core logic of pitch
shifting and resampling.
/// </summary>
public class ShrutiPitchProcessor
{
private const int OutputSampleRate = 8000;
private const ushort OutputBitsPerSample = 16; // As per requirement
/// <summary>
/// Processes the input WAV file to
generate 66 pitch-shifted versions,
/// concatenated into a single
output WAV file.
/// </summary>
/// <param
name="inputWavPath">Path to the input WAV file.</param>
/// <param
name="outputWavPath">Path to save the resulting WAV
file.</param>
public static void CreateShrutiVariations(string inputWavPath, string outputWavPath)
{
Console.WriteLine($"Reading input WAV: {inputWavPath}");
WavUtils.WavHeader inputHeader;
float[] monoInputSamples =
WavUtils.ReadWav(inputWavPath, out inputHeader);
if (monoInputSamples == null || monoInputSamples.Length == 0)
{
Console.WriteLine("Failed to read input WAV file or the file is
empty.");
return;
}
Console.WriteLine($"Input WAV: {inputHeader.SampleRate} Hz, {inputHeader.BitsPerSample}-bit, {(inputHeader.NumChannels == 1 ? "Mono" : "Stereo")}, Length: {monoInputSamples.Length} samples.");
// Calculate the
original duration of the input audio.
// This duration
will be maintained for each pitch-shifted segment.
double originalDurationSeconds = (double)monoInputSamples.Length /
inputHeader.SampleRate;
// Calculate the
number of samples each shifted segment should have to maintain the original
duration at the OutputSampleRate.
int targetSegmentLengthSamples = (int)(originalDurationSeconds *
OutputSampleRate);
if (targetSegmentLengthSamples == 0 && originalDurationSeconds
> 0) {
Console.WriteLine("Warning: Target segment
length is 0, possibly due to very short input or rounding. Output might be
empty or unexpected.");
} else if (targetSegmentLengthSamples == 0 && originalDurationSeconds
== 0) {
Console.WriteLine("Input audio has zero
duration. Output will be empty.");
WavUtils.WriteWav(outputWavPath, new List<float>(), OutputSampleRate,
OutputBitsPerSample);
Console.WriteLine($"Empty output WAV file
created at: {outputWavPath}");
return;
}
List<float> allShiftedAudioSamples = new List<float>();
Console.WriteLine($"Original duration: {originalDurationSeconds:F3} seconds.");
Console.WriteLine($"Target segment length for each shruti: {targetSegmentLengthSamples} samples at {OutputSampleRate} Hz.");
// Generate 66
shruti versions.
// We'll iterate
shruti offsets from -33 to +32 (inclusive), giving 66 distinct shifts.
// shrutiOffset =
-33 will be the lowest pitch (most "up" in user's description of
lower frequency).
// shrutiOffset =
+32 will be the highest pitch (most "down" in user's description of
higher frequency).
int totalShrutisGenerated = 0;
for (int shrutiOffset = -33; shrutiOffset <= 32; shrutiOffset++)
{
// Calculate the pitch shift factor.
// 22.0 represents the number of shrutis in one octave.
double pitchShiftFactor = Math.Pow(2.0, (double)shrutiOffset / 22.0);
Console.WriteLine($"Processing shruti offset: {shrutiOffset,3} (Factor: {pitchShiftFactor:F4})...");
// Perform resampling to achieve pitch shift while preserving
duration.
float[] shiftedSegment = ResampleAndShiftPitch(
monoInputSamples,
(int)inputHeader.SampleRate,
pitchShiftFactor,
targetSegmentLengthSamples
);
allShiftedAudioSamples.AddRange(shiftedSegment);
totalShrutisGenerated++;
}
Console.WriteLine($"Total {totalShrutisGenerated} shruti versions generated.");
Console.WriteLine($"Writing concatenated output WAV: {outputWavPath}");
WavUtils.WriteWav(outputWavPath, allShiftedAudioSamples,
OutputSampleRate, OutputBitsPerSample);
Console.WriteLine("Processing complete.");
}
/// <summary>
/// Resamples the input audio to a
new pitch while attempting to preserve its original perceived duration.
/// The output will have
'targetOutputLengthSamples' samples.
/// </summary>
/// <param
name="inputSamples">The original mono audio samples (float -1.0 to
1.0).</param>
/// <param
name="inputSampleRate">Sample rate of the
inputSamples.</param>
/// <param
name="pitchShiftFactor">Factor to shift pitch by (>1.0 higher,
<1.0 lower).</param>
/// <param
name="targetOutputLengthSamples">The desired number of samples for
the output segment.</param>
/// <returns>A new float
array containing the pitch-shifted and resampled audio.</returns>
private static float[] ResampleAndShiftPitch(
float[] inputSamples,
int inputSampleRate,
double pitchShiftFactor,
int targetOutputLengthSamples)
{
if (inputSamples == null || inputSamples.Length == 0) return new float[0];
if (targetOutputLengthSamples == 0) return new float[0];
float[] outputSamples = new float[targetOutputLengthSamples];
// Calculate the
step for advancing through the input samples for each output sample.
// This
'readStep' determines how quickly we "read" through the input audio.
// If
pitchShiftFactor > 1 (higher pitch), readStep is larger, so we move through
input faster.
// If
pitchShiftFactor < 1 (lower pitch), readStep is smaller, so we move through
input slower.
// The ratio
(inputSampleRate / OutputSampleRate) accounts for any difference in native
sample rates.
double readStep = pitchShiftFactor * ((double)inputSampleRate / OutputSampleRate);
for (int i = 0; i < targetOutputLengthSamples; i++)
{
// Calculate the floating-point index in the input samples
array.
double currentInputIndexFloat = i * readStep;
// Handle looping: if currentInputIndexFloat exceeds input
length, it should wrap around.
// This ensures that the output segment is filled to its
targetOutputLengthSamples,
// effectively looping the input if it's "consumed"
too quickly (e.g., high pitch shift).
currentInputIndexFloat = currentInputIndexFloat % inputSamples.Length;
// Ensure positivity if modulo result is negative (can happen if
readStep or i is negative, though not in this loop)
if (currentInputIndexFloat < 0)
{
currentInputIndexFloat +=
inputSamples.Length;
}
// Get integer indices for interpolation.
int indexBefore = (int)Math.Floor(currentInputIndexFloat);
int indexAfter = (indexBefore + 1);
// Ensure indexAfter also wraps around correctly.
if (indexAfter >= inputSamples.Length) {
indexAfter = indexAfter %
inputSamples.Length;
}
// Handle edge case for single sample input to avoid out of
bounds with indexAfter
if (inputSamples.Length == 1) {
indexBefore = 0;
indexAfter = 0;
}
// Get the fraction for linear interpolation.
double fraction = currentInputIndexFloat - indexBefore;
float sampleBefore = inputSamples[indexBefore];
// If input has only one sample, sampleAfter is the same as
sampleBefore.
float sampleAfter = (inputSamples.Length > 1) ? inputSamples[indexAfter] :
sampleBefore;
// Linear interpolation.
outputSamples[i] = (float)(sampleBefore * (1.0 - fraction) + sampleAfter * fraction);
}
return outputSamples;
}
}
/// <summary>
/// Main program class.
/// </summary>
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Shruti Pitch Shifter Application");
Console.WriteLine("---------------------------------");
if (args.Length < 2)
{
Console.WriteLine("Usage: ShrutiPitchShifterApp.exe
<input_wav_file> <output_wav_file>");
Console.WriteLine("Example: ShrutiPitchShifterApp.exe input.wav
output_shrutis.wav");
return;
}
string inputFilePath = args[0];
string outputFilePath = args[1];
if (!File.Exists(inputFilePath))
{
Console.WriteLine($"Error: Input file not found: {inputFilePath}");
return;
}
try
{
ShrutiPitchProcessor.CreateShrutiVariations(inputFilePath,
outputFilePath);
}
catch (Exception ex)
{
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
}
}
}
}
Excellent — your code is already strong, but here’s a precise enhancement
for exact-duration,
pitch-shifted resampling of one input into 66 shrutis, all
packed in a single 8000Hz WAV file, using no third-party API and well-calculated
resample factors.
🔧 GOAL
Given a sample of duration T
ms, create 66
pitch-shifted copies that:
- Cover 22 shrutis ↓ (lower), 22 base (no shift), 22 shrutis ↑
(higher)
- Each copy plays for the same duration as original
- Output is a single WAV at 8000 Hz with all 66 concatenated
✅ KEY CONCEPT — Sample Rate
Shifting for Pitch
To pitch shift without changing
duration, resample using:
ini
resampleFactor = pow(
2, semitoneShift /
12.0)
Then resample the input to simulate the pitch
change, and
trim or stretch to match the original time T
.
✅ ENHANCED METHOD OUTLINE
Here’s a complete new function to add in your
class:
csharp
public
static
void
GenerateShrutiVariants(
string inputWavPath,
string outputWavPath)
{
// Read source WAV
float[] sourceSamples = ReadWavMonoAsFloats(inputWavPath,
out
int sourceRate);
int outputRate =
8000;
double durationSec = sourceSamples.Length / (
double)sourceRate;
int targetSamplesPerVariant = (
int)(durationSec * outputRate);
List<
float> all66 =
new List<
float>();
for (
int i =
-33; i <=
32; i++)
// 66 shifts: -33 to +32
{
double semitoneShift = i * (
1.0 /
3);
// 22 shrutis = 1 octave = 12 semitones
double resampleFactor = Math.Pow(
2, semitoneShift /
12.0);
int resampledLength = (
int)(sourceSamples.Length / resampleFactor);
// Step 1: Resample to pitch-shift (no time correction yet)
float[] pitchShifted = ResampleWithFactor(sourceSamples, resampledLength);
// Step 2: Time-correct by stretching/compressing to match original duration
float[] durationMatched = StretchToLength(pitchShifted, targetSamplesPerVariant);
all66.AddRange(durationMatched);
}
SaveWav(outputWavPath, all66.ToArray(), outputRate,
1);
Console.WriteLine(
"All 66 shruti variants saved.");
MessageBox.Show(
"Done: 66 shruti pitch-shifted variants created.");
}
✅ SUPPORT FUNCTIONS
Pitch-based Resampler:
csharp
public
static
float[]
ResampleWithFactor(
float[] input,
int targetLength)
{
float[] result =
new
float[targetLength];
double step = (
double)(input.Length -
1) / (targetLength -
1);
for (
int i =
0; i < targetLength; i++)
{
double pos = i * step;
int idx = (
int)pos;
double frac = pos - idx;
result[i] = (idx +
1 < input.Length)
? (
float)((
1 - frac) * input[idx] + frac * input[idx +
1])
: input[idx];
}
return result;
}
Stretch (Time Correction):
csharp
public
static
float[]
StretchToLength(
float[] input,
int targetLength)
{
return ResampleWithFactor(input, targetLength);
// same method, reused
}
📊 ALGORITHM SUMMARY
- For each shruti index (-33 to +32):
- Convert index to semitone fraction.
- Calculate pitch shift factor using
2^(x/12)
. - Resample the waveform to alter pitch.
- Adjust the output length to
T ms
at 8000 Hz using interpolation. - Concatenate all outputs into one
array.
- Write to single mono WAV at 8000 Hz.
✅ Output
- One
outputWavPath.wav
- Format: Mono, 8000 Hz
- Duration =
66 × T
ms
❓To Proceed
Would
you like me to integrate this logic directly into one of your existing SwaralipiConcatenator
classes or create a new self-contained class file?
You
said:
please write the whole c sharp class for this
ChatGPT
said:
Shruti
Resampler8000hz
Here's the complete ShrutiResamplerGenerator
class — it reads a WAV file, generates 66 shruti-shifted versions without
changing their duration, and outputs a single mono WAV file at 8000 Hz.
Let me
know if you'd like stereo output, support for GTAMPS filenames, or per-note
customization.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace ShrutiResampler
{
public class
ShrutiResamplerGenerator
{
public static
void GenerateShrutiVariants(string inputWavPath, string outputWavPath)
{
float[]
sourceSamples = ReadWavMonoAsFloats(inputWavPath, out int sourceRate);
int
outputRate = 8000;
double
durationSec = sourceSamples.Length / (double)sourceRate;
int
targetSamplesPerVariant = (int)(durationSec * outputRate);
List<float> allVariants = new List<float>();
for (int i
= -33; i <= 32; i++) // 66 shrutis: -33 to +32
{
double
semitoneShift = i * (1.0 / 3); // 22 shrutis per octave
double
resampleFactor = Math.Pow(2, semitoneShift / 12.0);
int
resampledLength = (int)(sourceSamples.Length / resampleFactor);
float[] pitchShifted = ResampleWithFactor(sourceSamples,
resampledLength);
float[] durationMatched = StretchToLength(pitchShifted,
targetSamplesPerVariant);
allVariants.AddRange(durationMatched);
}
SaveWav(outputWavPath, allVariants.ToArray(), outputRate, 1);
Console.WriteLine("66 shruti-pitched variants generated.");
MessageBox.Show("Generated 66 shruti-pitched variants: \n" +
outputWavPath);
}
public static float[]
ResampleWithFactor(float[] input, int targetLength)
{
float[]
result = new float[targetLength];
double
step = (double)(input.Length - 1) / (targetLength - 1);
for (int i
= 0; i < targetLength; i++)
{
double
pos = i * step;
int
idx = (int)pos;
double
frac = pos - idx;
result[i] = (idx + 1 < input.Length)
?
(float)((1 - frac) * input[idx] + frac * input[idx + 1])
:
input[idx];
}
return
result;
}
public static
float[] ReadWavMonoAsFloats(string path, out int sampleRate)
{
using
(BinaryReader br = new BinaryReader(File.OpenRead(path)))
{
br.BaseStream.Seek(0, SeekOrigin.Begin);
br.ReadBytes(12); // Skip RIFF header
int
channels = 1;
sampleRate
= 8000;
int
bitsPerSample = 16;
while
(br.BaseStream.Position < br.BaseStream.Length)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "fmt ")
{
br.ReadInt16(); // audio format
channels = br.ReadInt16();
sampleRate = br.ReadInt32();
br.ReadInt32(); // byte rate
br.ReadInt16(); // block align
bitsPerSample = br.ReadInt16();
if (chunkSize > 16)
br.ReadBytes(chunkSize - 16);
}
else if (chunkID == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
float[] samples = new float[totalSamples / channels];
for (int i = 0, j = 0; i < totalSamples; i++)
{
short sample = br.ReadInt16();
if (channels == 1 || i % channels == 0)
samples[j++] =
sample / 32768f;
if (channels == 2) br.ReadInt16();
}
return samples;
}
else
{
br.ReadBytes(chunkSize);
}
}
}
sampleRate
= 8000;
return new
float[0];
}
public static
void SaveWav(string path, float[] samples, int sampleRate, int channels)
{
int
bitsPerSample = 16;
int
byteRate = sampleRate * channels * bitsPerSample / 8;
int
blockAlign = channels * bitsPerSample / 8;
int
dataSize = samples.Length * channels * 2;
using
(BinaryWriter bw = new BinaryWriter(File.Create(path)))
{
bw.Write(Encoding.ASCII.GetBytes("RIFF"));
bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt "));
bw.Write(16);
bw.Write((short)1);
bw.Write((short)channels);
bw.Write(sampleRate);
bw.Write(byteRate);
bw.Write((short)blockAlign);
bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data"));
bw.Write(dataSize);
foreach (float sample in samples)
{
short s = (short)(sample * 32767);
for (int c = 0; c < channels; c++)
bw.Write(s);
}
}
}
public static
float[] StretchToLength(float[] input, int targetLength)
{
return
ResampleWithFactor(input, targetLength);
}
}
}
Your mistake (as i
have tested) you have not done the resampling calculations with these in mind
// after doing this all the samples are getting same durations and same
frequencies exact copies
// we know when the
frequency changes then the duration for the sample also change because the
total samples are fixed
// we need the
resampling logic to change Total_Frequency * Total_Duration = fixed which means
F1*T1 = F2*T2=Sample_Count fixed
// We need the
sample count to vary as per necessary conditions and interpolate the samples
(as required) in the 66 copies of samples in same file where these copies are
all having different frequencies and different durations because the
frequencies in every copy and durations are also changing in every copy but all
the copies are on same file (the output file has same sample rate 8000 samples
per second while playing the ear will listen different frequencies for
different copies
//all copies are
changing the frequencies and durations so all copies dont have same number of
samples now after resampling but the current program is not doing that (current
program is doing the resampling for frequency and then resetting that to fixed
durations and so the whole effect nullifies at the end of double
transformations)
// say the original
sample has n1 samples s1 samples per second f1 frequency T1 duration then the
next copy will have n2 samples f2 frequency T2 time s2 samples per second and
the total things are stored in a single file with fixed samples per second so
we need proper calculations for these readjustments but different number of
samples but whole file will have fixed samples per second still playing
different feels (ear feeling) at different regions for different copies of same
input file... revise the whole class public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES___GPT
{
public static void
GenerateShrutiVariants___GPT(string inputWavPath, string outputWavPath)
{
float[]
sourceSamples = ReadWavMonoAsFloats(inputWavPath, out int sourceRate);
int outputRate =
8000;
double durationSec
= sourceSamples.Length / (double)sourceRate;
int
targetSamplesPerVariant = (int)(durationSec * outputRate);
List<float>
allVariants = new List<float>();
for (int i = -33; i
<= 32; i++) // 66 shrutis: -33 to +32
{
double
semitoneShift = i * (1.0 / 3); // 22 shrutis per octave
double
resampleFactor = Math.Pow(2, semitoneShift / 12.0);
int resampledLength
= (int)(sourceSamples.Length / resampleFactor);
float[]
pitchShifted = ResampleWithFactor(sourceSamples, resampledLength);
float[]
durationMatched = StretchToLength(pitchShifted, targetSamplesPerVariant);
allVariants.AddRange(durationMatched);
}//for (int i =
-33; i <= 32; i++) // 66 shrutis: -33 to +32
SaveWav(outputWavPath,
allVariants.ToArray(), outputRate, 1);
Console.WriteLine("66
shruti-pitched variants generated.");
MessageBox.Show("Generated
66 shruti-pitched variants: \n" + outputWavPath);
}//public static
void GenerateShrutiVariants(string inputWavPath, string outputWavPath)
public static
float[] ResampleWithFactor(float[] input, int targetLength)
{
// after doing this
all the samples are getting same durations and same frequencies exact copies
// we know when the
frequency changes then the duration for the sample also change because the
total samples are fixed
// we need the
resampling logic to change Total_Frequency * Total_Duration = fixed which means
F1*T1 = F2*T2=Sample_Count fixed
// We need the
sample count to vary as per necessary conditions and interpolate the samples
(as required) in the 66 copies of samples in same file where these copies are
all having different frequencies and different durations because the
frequencies in every copy and durations are also changing in every copy but all
the copies are on same file (the output file has same sample rate 8000 samples
per second while playing the ear will listen different frequencies for
different copies
//all copies are
changing the frequencies and durations so all copies dont have same number of
samples now after resampling but the current program is not doing that (current
program is doing the resampling for frequency and then resetting that to fixed
durations and so the whole effect nullifies at the end of double
transformations)
// say the original
sample has n1 samples s1 samples per second f1 frequency T1 duration then the
next copy will have n2 samples f2 frequency T2 time s2 samples per second and
the total things are stored in a single file with fixed samples per second so
we need proper calculations for these readjustments but different number of
samples but whole
float[] result =
new float[targetLength];
double step =
(double)(input.Length - 1) / (targetLength - 1);
for (int i = 0; i
< targetLength; i++)
{
double pos = i *
step;
int idx = (int)pos;
double frac = pos -
idx;
result[i] = (idx +
1 < input.Length)
? (float)((1 -
frac) * input[idx] + frac * input[idx + 1])
: input[idx];
}//for (int i = 0;
i < targetLength; i++)
return result;
}//public static
float[] ResampleWithFactor(float[] input, int targetLength)
public static
float[] ReadWavMonoAsFloats(string path, out int sampleRate)
{
using (BinaryReader
br = new BinaryReader(File.OpenRead(path)))
{
br.BaseStream.Seek(0,
SeekOrigin.Begin);
br.ReadBytes(12);
// Skip RIFF header
int channels = 1;
sampleRate = 8000;
int bitsPerSample =
16;
while
(br.BaseStream.Position < br.BaseStream.Length)
{
string chunkID =
new string(br.ReadChars(4));
int chunkSize =
br.ReadInt32();
if (chunkID ==
"fmt ")
{
br.ReadInt16(); //
audio format
channels =
br.ReadInt16();
sampleRate =
br.ReadInt32();
br.ReadInt32(); //
byte rate
br.ReadInt16(); //
block align
bitsPerSample =
br.ReadInt16();
if (chunkSize > 16)
br.ReadBytes(chunkSize
- 16);
}
else if (chunkID ==
"data")
{
int bytesPerSample
= bitsPerSample / 8;
int totalSamples =
chunkSize / bytesPerSample;
float[] samples =
new float[totalSamples / channels];
for (int i = 0, j =
0; i < totalSamples; i++)
{
short sample =
br.ReadInt16();
if (channels == 1
|| i % channels == 0)
samples[j++] =
sample / 32768f;
if (channels == 2)
br.ReadInt16();
}
return samples;
}
else
{
br.ReadBytes(chunkSize);
}
}//while
(br.BaseStream.Position < br.BaseStream.Length)
}//using
(BinaryReader br = new BinaryReader(File.OpenRead(path)))
sampleRate = 8000;
return new
float[0];
}//public static
float[] ReadWavMonoAsFloats(string path, out int sampleRate)
public static void
SaveWav(string path, float[] samples, int sampleRate, int channels)
{
int bitsPerSample =
16;
int byteRate =
sampleRate * channels * bitsPerSample / 8;
int blockAlign =
channels * bitsPerSample / 8;
int dataSize =
samples.Length * channels * 2;
using (BinaryWriter
bw = new BinaryWriter(File.Create(path)))
{
bw.Write(Encoding.ASCII.GetBytes("RIFF"));
bw.Write(36 +
dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt
"));
bw.Write(16);
bw.Write((short)1);
bw.Write((short)channels);
bw.Write(sampleRate);
bw.Write(byteRate);
bw.Write((short)blockAlign);
bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data"));
bw.Write(dataSize);
foreach (float
sample in samples)
{
short s =
(short)(sample * 32767);
for (int c = 0; c
< channels; c++)
bw.Write(s);
}
}
}//public static
void SaveWav(string path, float[] samples, int sampleRate, int channels)
public static
float[] StretchToLength(float[] input, int targetLength)
{
return
ResampleWithFactor(input, targetLength);
}//public static
float[] StretchToLength(float[] input, int targetLength)
}//public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace ShrutiResampler
{
public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES___GPT
{
public static
void GenerateShrutiVariants___GPT(string inputWavPath, string outputWavPath)
{
float[]
sourceSamples = ReadWavMonoAsFloats(inputWavPath, out int sourceRate);
int
outputRate = 8000;
double
originalFrequency = 440.0; // Assume input sample is A4 (for calculation)
double durationSec =
sourceSamples.Length / (double)sourceRate;
double
totalCycleProduct = originalFrequency * durationSec;
List<float> allVariants = new List<float>();
for (int i
= -33; i <= 32; i++) // 66 shrutis: -33 to +32
{
double
semitoneShift = i * (1.0 / 3);
double
freqMultiplier = Math.Pow(2, semitoneShift / 12.0);
double
newFreq = originalFrequency * freqMultiplier;
double
newDuration = totalCycleProduct / newFreq;
int
newSampleCount = (int)(newDuration * outputRate);
float[] pitchShifted = ResampleWithFactor(sourceSamples,
newSampleCount);
allVariants.AddRange(pitchShifted);
}
SaveWav(outputWavPath, allVariants.ToArray(), outputRate, 1);
Console.WriteLine("66 shruti-pitched variants generated with
frequency-duration relationship preserved.");
MessageBox.Show("Generated 66 shruti-pitched variants: \n" +
outputWavPath);
}
public static
float[] ResampleWithFactor(float[] input, int targetLength)
{
float[]
result = new float[targetLength];
double step = (double)(input.Length - 1) /
(targetLength - 1);
for (int i
= 0; i < targetLength; i++)
{
double
pos = i * step;
int
idx = (int)pos;
double
frac = pos - idx;
result[i] = (idx + 1 < input.Length)
?
(float)((1 - frac) * input[idx] + frac * input[idx + 1])
:
input[idx];
}
return
result;
}
public static
float[] ReadWavMonoAsFloats(string path, out int sampleRate)
{
using
(BinaryReader br = new BinaryReader(File.OpenRead(path)))
{
br.BaseStream.Seek(0, SeekOrigin.Begin);
br.ReadBytes(12);
int
channels = 1;
sampleRate = 8000;
int
bitsPerSample = 16;
while
(br.BaseStream.Position < br.BaseStream.Length)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "fmt ")
{
br.ReadInt16();
channels = br.ReadInt16();
sampleRate = br.ReadInt32();
br.ReadInt32();
br.ReadInt16();
bitsPerSample = br.ReadInt16();
if (chunkSize > 16)
br.ReadBytes(chunkSize - 16);
}
else if (chunkID == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
float[] samples = new float[totalSamples / channels];
for (int i = 0, j = 0; i < totalSamples; i++)
{
short sample = br.ReadInt16();
if (channels == 1 || i % channels == 0)
samples[j++] =
sample / 32768f;
if (channels == 2) br.ReadInt16();
}
return samples;
}
else
{
br.ReadBytes(chunkSize);
}
}
}
sampleRate
= 8000;
return new
float[0];
}
public static
void SaveWav(string path, float[] samples, int sampleRate, int channels)
{
int
bitsPerSample = 16;
int
byteRate = sampleRate * channels * bitsPerSample / 8;
int
blockAlign = channels * bitsPerSample / 8;
int
dataSize = samples.Length * channels * 2;
using
(BinaryWriter bw = new BinaryWriter(File.Create(path)))
{
bw.Write(Encoding.ASCII.GetBytes("RIFF"));
bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt "));
bw.Write(16);
bw.Write((short)1);
bw.Write((short)channels);
bw.Write(sampleRate);
bw.Write(byteRate);
bw.Write((short)blockAlign);
bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data"));
bw.Write(dataSize);
foreach (float sample in samples)
{
short s = (short)(sample * 32767);
for (int c = 0; c < channels; c++)
bw.Write(s);
}
}
}
public static
float[] StretchToLength(float[] input, int targetLength)
{
return
ResampleWithFactor(input, targetLength);
}
}
}
Your
mistake (as i have tested) you have not done the resampling calculations with
these in mind // after doing this all the samples are getting same durations
and same frequencies exact copies // we know when the frequency changes then
the duration for the sample also change because the total samples are fixed //
we need the resampling logic to change Total_Frequency * Total_Duration = fixed
which means F1*T1 = F2*T2=Sample_Count fixed // We need the sample count to
vary as per necessary conditions and interpolate the samples (as required) in
the 66 copies of samples in same file where these copies are all having
different frequencies and different durations because the frequencies in every
copy and durations are also changing in every copy but all the copies are on
same file (the output file has same sample rate 8000 samples per second while
playing the ear will listen different frequencies for different copies //all
copies are changing the frequencies and durations so all copies dont have same
number of samples now after resampling but the current program is not doing
that (current program is doing the resampling for frequency and then resetting
that to fixed durations and so the whole effect nullifies at the end of double
transformations) // say the original sample has n1 samples s1 samples per
second f1 frequency T1 duration then the next copy will have n2 samples f2
frequency T2 time s2 samples per second and the total things are stored in a
single file with fixed samples per second so we need proper calculations for
these readjustments but different number of samples but whole file will have
fixed samples per second still playing different feels (ear feeling) at
different regions for different copies of same input file... revise the whole
class public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES___GPT
{ public static void GenerateShrutiVariants___GPT(string inputWavPath, string
outputWavPath) { float[] sourceSamples = ReadWavMonoAsFloats(inputWavPath, out
int sourceRate); int outputRate = 8000; double durationSec =
sourceSamples.Length / (double)sourceRate; int targetSamplesPerVariant =
(int)(durationSec * outputRate); List<float> allVariants = new
List<float>(); for (int i = -33; i <= 32; i++) // 66 shrutis: -33 to
+32 { double semitoneShift = i * (1.0 / 3); // 22 shrutis per octave double resampleFactor
= Math.Pow(2, semitoneShift / 12.0); int resampledLength =
(int)(sourceSamples.Length / resampleFactor); float[] pitchShifted =
ResampleWithFactor(sourceSamples, resampledLength); float[] durationMatched =
StretchToLength(pitchShifted, targetSamplesPerVariant);
allVariants.AddRange(durationMatched); }//for (int i = -33; i <= 32; i++) //
66 shrutis: -33 to +32 SaveWav(outputWavPath, allVariants.ToArray(),
outputRate, 1); Console.WriteLine("66 shruti-pitched variants
generated."); MessageBox.Show("Generated 66 shruti-pitched variants:
\n" + outputWavPath); }//public static void GenerateShrutiVariants(string
inputWavPath, string outputWavPath) public static float[]
ResampleWithFactor(float[] input, int targetLength) { // after doing this all
the samples are getting same durations and same frequencies exact copies // we
know when the frequency changes then the duration for the sample also change
because the total samples are fixed // we need the resampling logic to change
Total_Frequency * Total_Duration = fixed which means F1*T1 = F2*T2=Sample_Count
fixed // We need the sample count to vary as per necessary conditions and
interpolate the samples (as required) in the 66 copies of samples in same file
where these copies are all having different frequencies and different durations
because the frequencies in every copy and durations are also changing in every
copy but all the copies are on same file (the output file has same sample rate
8000 samples per second while playing the ear will listen different frequencies
for different copies //all copies are changing the frequencies and durations so
all copies dont have same number of samples now after resampling but the
current program is not doing that (current program is doing the resampling for
frequency and then resetting that to fixed durations and so the whole effect
nullifies at the end of double transformations) // say the original sample has
n1 samples s1 samples per second f1 frequency T1 duration then the next copy
will have n2 samples f2 frequency T2 time s2 samples per second and the total
things are stored in a single file with fixed samples per second so we need
proper calculations for these readjustments but different number of samples but
whole float[] result = new float[targetLength]; double step =
(double)(input.Length - 1) / (targetLength - 1); for (int i = 0; i <
targetLength; i++) { double pos = i * step; int idx = (int)pos; double frac =
pos - idx; result[i] = (idx + 1 < input.Length) ? (float)((1 - frac) *
input[idx] + frac * input[idx + 1]) : input[idx]; }//for (int i = 0; i <
targetLength; i++) return result; }//public static float[]
ResampleWithFactor(float[] input, int targetLength) public static float[]
ReadWavMonoAsFloats(string path, out int sampleRate) { using (BinaryReader br =
new BinaryReader(File.OpenRead(path))) { br.BaseStream.Seek(0,
SeekOrigin.Begin); br.ReadBytes(12); // Skip RIFF header int channels = 1;
sampleRate = 8000; int bitsPerSample = 16; while (br.BaseStream.Position <
br.BaseStream.Length) { string chunkID = new string(br.ReadChars(4)); int
chunkSize = br.ReadInt32(); if (chunkID == "fmt ") { br.ReadInt16();
// audio format channels = br.ReadInt16(); sampleRate = br.ReadInt32();
br.ReadInt32(); // byte rate br.ReadInt16(); // block align bitsPerSample = br.ReadInt16();
if (chunkSize > 16) br.ReadBytes(chunkSize - 16); } else if (chunkID ==
"data") { int bytesPerSample = bitsPerSample / 8; int totalSamples =
chunkSize / bytesPerSample; float[] samples = new float[totalSamples /
channels]; for (int i = 0, j = 0; i < totalSamples; i++) { short sample =
br.ReadInt16(); if (channels == 1 || i % channels == 0) samples[j++] = sample /
32768f; if (channels == 2) br.ReadInt16(); } return samples; } else {
br.ReadBytes(chunkSize); } }//while (br.BaseStream.Position < br.BaseStream.Length)
}//using (BinaryReader br = new BinaryReader(File.OpenRead(path))) sampleRate =
8000; return new float[0]; }//public static float[] ReadWavMonoAsFloats(string
path, out int sampleRate) public static void SaveWav(string path, float[]
samples, int sampleRate, int channels) { int bitsPerSample = 16; int byteRate =
sampleRate * channels * bitsPerSample / 8; int blockAlign = channels *
bitsPerSample / 8; int dataSize = samples.Length * channels * 2; using
(BinaryWriter bw = new BinaryWriter(File.Create(path))) {
bw.Write(Encoding.ASCII.GetBytes("RIFF")); bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt ")); bw.Write(16);
bw.Write((short)1); bw.Write((short)channels); bw.Write(sampleRate); bw.Write(byteRate);
bw.Write((short)blockAlign); bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data")); bw.Write(dataSize);
foreach (float sample in samples) { short s = (short)(sample * 32767); for (int
c = 0; c < channels; c++) bw.Write(s); } } }//public static void
SaveWav(string path, float[] samples, int sampleRate, int channels) public
static float[] StretchToLength(float[] input, int targetLength) { return
ResampleWithFactor(input, targetLength); }//public static float[]
StretchToLength(float[] input, int targetLength) }//public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES
ChatGPT said:
Edited
Answer
in chat instead
✅ Your class has been revised to preserve the
frequency-duration product, ensuring that each shruti-shifted variant:
- Changes both pitch and playback
duration
- Produces a different number of
samples based on pitch
- Maintains realistic ear-feel at 8000
Hz in the final WAV
You can now
directly test the updated GenerateShrutiVariants___GPT method. Let me
know if you'd like the originalFrequency to be
auto-calculated from pitch detection or a separate f1 to be passed in.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace ShrutiResampler
{
public class
NEW_KIND_OF_ShrutiResamplerGenerator___33_PLUS_33_66_COPIES_PLAYING_ALL_SAMPLES___GPT
{
public static
void GenerateShrutiVariants___GPT(string inputWavPath, string outputWavPath)
{
float[]
sourceSamples = ReadWavMonoAsFloats(inputWavPath, out int sourceRate);
int
outputRate = 8000;
double
originalFrequency = 440.0; // Assume input sample is A4 (for calculation)
double
durationSec = sourceSamples.Length / (double)sourceRate;
double
totalCycleProduct = originalFrequency * durationSec;
List<float> allVariants = new List<float>();
for (int i
= -33; i <= 32; i++) // 66 shrutis: -33 to +32
{
double semitoneShift = i * (1.0 /
3);
double
freqMultiplier = Math.Pow(2, semitoneShift / 12.0);
double
newFreq = originalFrequency * freqMultiplier;
double
newDuration = totalCycleProduct / newFreq;
int
newSampleCount = (int)(newDuration * outputRate);
float[] pitchShifted = ResampleWithFactor(sourceSamples,
newSampleCount);
allVariants.AddRange(pitchShifted);
}
SaveWav(outputWavPath,
allVariants.ToArray(), outputRate, 1);
Console.WriteLine("66 shruti-pitched variants generated with
frequency-duration relationship preserved.");
MessageBox.Show("Generated 66 shruti-pitched variants: \n" +
outputWavPath);
}
public static
float[] ResampleWithFactor(float[] input, int targetLength)
{
float[]
result = new float[targetLength];
double
step = (double)(input.Length - 1) / (targetLength - 1);
for (int i
= 0; i < targetLength; i++)
{
double
pos = i * step;
int
idx = (int)pos;
double
frac = pos - idx;
result[i] = (idx + 1 < input.Length)
? (float)((1
- frac) * input[idx] + frac * input[idx + 1])
:
input[idx];
}
return
result;
}
public static
float[] ReadWavMonoAsFloats(string path, out int sampleRate)
{
using (BinaryReader
br = new BinaryReader(File.OpenRead(path)))
{
br.BaseStream.Seek(0, SeekOrigin.Begin);
br.ReadBytes(12);
int
channels = 1;
sampleRate = 8000;
int
bitsPerSample = 16;
while
(br.BaseStream.Position < br.BaseStream.Length)
{
string chunkID = new string(br.ReadChars(4));
int chunkSize = br.ReadInt32();
if
(chunkID == "fmt ")
{
br.ReadInt16();
channels = br.ReadInt16();
sampleRate = br.ReadInt32();
br.ReadInt32();
br.ReadInt16();
bitsPerSample = br.ReadInt16();
if (chunkSize > 16)
br.ReadBytes(chunkSize - 16);
}
else if (chunkID == "data")
{
int bytesPerSample = bitsPerSample / 8;
int totalSamples = chunkSize / bytesPerSample;
float[] samples = new float[totalSamples / channels];
for (int i = 0, j = 0; i <
totalSamples; i++)
{
short sample = br.ReadInt16();
if (channels == 1 || i % channels == 0)
samples[j++] =
sample / 32768f;
if (channels == 2) br.ReadInt16();
}
return samples;
}
else
{
br.ReadBytes(chunkSize);
}
}
}
sampleRate
= 8000;
return new
float[0];
}
public static
void SaveWav(string path, float[] samples, int sampleRate, int channels)
{
int
bitsPerSample = 16;
int
byteRate = sampleRate * channels * bitsPerSample / 8;
int
blockAlign = channels * bitsPerSample / 8;
int
dataSize = samples.Length * channels * 2;
using (BinaryWriter bw = new
BinaryWriter(File.Create(path)))
{
bw.Write(Encoding.ASCII.GetBytes("RIFF"));
bw.Write(36 + dataSize);
bw.Write(Encoding.ASCII.GetBytes("WAVE"));
bw.Write(Encoding.ASCII.GetBytes("fmt
"));
bw.Write(16);
bw.Write((short)1);
bw.Write((short)channels);
bw.Write(sampleRate);
bw.Write(byteRate);
bw.Write((short)blockAlign);
bw.Write((short)bitsPerSample);
bw.Write(Encoding.ASCII.GetBytes("data"));
bw.Write(dataSize);
foreach (float sample in samples)
{
short s = (short)(sample * 32767);
for (int c = 0; c < channels; c++)
bw.Write(s);
}
}
}
public static
float[] StretchToLength(float[] input, int targetLength)
{
return
ResampleWithFactor(input, targetLength);
}
}
}
No comments:
Post a Comment