Wednesday, June 4, 2025

c sharp code to copy 66 shrutis peoperly

 

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

  1. 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.
  2. Concatenate all outputs into one array.
  3. 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:

  1. 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.
  2. 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.
  3. Concatenation: All 66 pitch-shifted audio segments (now in a float format) will be concatenated into one long list of samples.
  4. 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:

  1. 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.
  2. 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.
  3. 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:

  1. Save the code as ShrutiPitchShifterApp.cs (or any .cs file).
  2. 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)
  3. 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

  1. 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.
  2. Concatenate all outputs into one array.
  3. 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