public static CWavStreamReadHeaders ReadWavStreamReadHeaders(BinaryReader r)
    {
        CWavStreamReadHeaders info = new CWavStreamReadHeaders();
        info.bIsValid = false;
        string riff = new string(r.ReadChars(4));
        if (!riff.Equals("RIFF"))
            throw new WavCutException("No 'RIFF' Tag, probably not a valid wav file.");

        info.nCompleteLength = r.ReadUInt32(); // (length of file in bytes) - 8

        string wave = new string(r.ReadChars(4));
        if (!wave.Equals("WAVE"))
            throw new WavCutException("No 'WAVE' tag, probably not a valid wav file.");

        string format = new string(r.ReadChars(4)); // assume that fmt tag is first
        if (!format.Equals("fmt "))
            throw new WavCutException("No 'fmt ' tag");

        uint size = r.ReadUInt32(); // size of fmt header
        if (size != 16)
            throw new WavCutException("Size of fmt header != 16");

        info.nAudioFormat = r.ReadUInt16(); // audio format. 1 refers to uncompressed PCM
       
        // read the format header
        info.nChannels = r.ReadUInt16();
        info.nSampleRate = r.ReadUInt32();
        info.byteRate = r.ReadUInt32();
        info.blockAlign = r.ReadUInt16();
        info.nBitsPerSample = r.ReadUInt16();
        info.bIsValid = true;
        return info;
    }
    public static CWavStreamReadHeaders ReadWavStreamReadHeaders(BinaryReader r)
    {
        CWavStreamReadHeaders info = new CWavStreamReadHeaders();

        info.bIsValid = false;
        string riff = new string(r.ReadChars(4));

        if (!riff.Equals("RIFF"))
        {
            throw new WavCutException("No 'RIFF' Tag, probably not a valid wav file.");
        }

        info.nCompleteLength = r.ReadUInt32(); // (length of file in bytes) - 8

        string wave = new string(r.ReadChars(4));

        if (!wave.Equals("WAVE"))
        {
            throw new WavCutException("No 'WAVE' tag, probably not a valid wav file.");
        }

        string format = new string(r.ReadChars(4)); // assume that fmt tag is first

        if (!format.Equals("fmt "))
        {
            throw new WavCutException("No 'fmt ' tag");
        }

        uint size = r.ReadUInt32(); // size of fmt header

        if (size != 16)
        {
            throw new WavCutException("Size of fmt header != 16");
        }

        info.nAudioFormat = r.ReadUInt16(); // audio format. 1 refers to uncompressed PCM

        // read the format header
        info.nChannels      = r.ReadUInt16();
        info.nSampleRate    = r.ReadUInt32();
        info.byteRate       = r.ReadUInt32();
        info.blockAlign     = r.ReadUInt16();
        info.nBitsPerSample = r.ReadUInt16();
        info.bIsValid       = true;
        return(info);
    }
    public static void StartWavAnalyze(BinaryReader r, long nActualLength)
    {
        CWavStreamReadHeaders info = CWavStreamReadHeaders.ReadWavStreamReadHeaders(r);

        // display header info
        Console.WriteLine("Audio format: " + info.nAudioFormat + " (1 is uncompressed PCM)");
        Console.WriteLine("Sample rate: " + info.nSampleRate);
        Console.WriteLine("BitsPerSample: " + info.nBitsPerSample);
        Console.WriteLine("Channels: " + info.nChannels);
        if (nActualLength != info.nCompleteLength + 8)
        {
            Console.WriteLine("Warning: length of file is " + nActualLength + " but expected " + info.nCompleteLength);
        }

        while (true)
        {
            // are we at the end of the file? if so, exit loop
            byte[] arTest = r.ReadBytes(4);
            if (arTest.Length == 0)
            {
                break;
            }
            else
            {
                r.BaseStream.Seek(-arTest.Length, SeekOrigin.Current);
            }

            // read the next chunk
            string sDatatag  = new string(r.ReadChars(4));
            uint   nDataSize = r.ReadUInt32();
            Console.WriteLine("TYPE:" + sDatatag + " SIZE:" + nDataSize);
            if (sDatatag == "data")
            {
                long nLengthInSamples = nDataSize / (info.nChannels * (info.nBitsPerSample / 8));
                Console.WriteLine("\tlength in samples " + nLengthInSamples +
                                  " length in secs " + (nLengthInSamples / ((double)info.nSampleRate)));
            }
            if (sDatatag != "data" && sDatatag != "LIST")
            {
                Console.WriteLine("warning, datatag is not 'data' or 'LIST'.");
            }
            r.BaseStream.Seek(nDataSize, SeekOrigin.Current);
        }
        Console.WriteLine("Looks ok.");
    }
    public static void StreamThroughWaveFile(BinaryReader r, IReadWaveData objCallback)
    {
        bool bRaw = objCallback.PreferRawSample();
        CWavStreamReadHeaders winfo = CWavStreamReadHeaders.ReadWavStreamReadHeaders(r);

        if (winfo.nAudioFormat != 1)
        {
            throw new WavCutException("Only audio format 1 is supported");
        }
        if (winfo.nSampleRate != 44100)
        {
            throw new WavCutException("expect samplerate==44100");
        }
        if (winfo.nBitsPerSample != 16)
        {
            throw new WavCutException("expect nBitsPerSample==16");
        }
        if (winfo.nChannels != 2)
        {
            throw new WavCutException("expect nChannels==2");
        }

        uint nDataSize = 0;

        while (true)
        {
            // Go through chunks. We are looking for "data" chunk
            string sDatatag = new string(r.ReadChars(4));
            nDataSize = r.ReadUInt32();
            if (nDataSize > int.MaxValue)
            {
                throw new WavCutException("File too large.");
            }

            if (sDatatag == "data") // found the data section
            {
                break;
            }
            else // something else, continue looping
            {
                r.BaseStream.Seek(nDataSize, SeekOrigin.Current);
            }
        }

        uint      nLengthOfDataChunk = (uint)(nDataSize / (winfo.nChannels * (winfo.nBitsPerSample / 8)));
        int       nSample            = 0;
        const int nReadsize          = 1024;
        bool      bComplete          = false;

        // loop through wave until we reach the expected number of samples.
        // we should not read until eof because there might be a later riff chunk.
        while (!bComplete)
        {
            byte[] rawdata = r.ReadBytes((int)nReadsize);
            if (rawdata == null || rawdata.Length == 0)
            {
                break;
            }

            for (int j = 0; j < rawdata.Length; j += 4)
            {
                if (bRaw)
                {
                    objCallback.ReceiveRawSample(rawdata[j], rawdata[j + 1], rawdata[j + 2], rawdata[j + 3]);
                }
                else
                {
                    short  sha1 = rawdata[j + 0]; // intel byte order
                    short  sha2 = (short)(((short)rawdata[j + 1]) << 8);
                    short  sha  = (short)(sha1 + sha2);
                    double sa   = sha / ((double)short.MaxValue);
                    short  shb1 = rawdata[j + 2]; // intel byte order
                    short  shb2 = (short)(((short)rawdata[j + 3]) << 8);
                    short  shb  = (short)(sha1 + sha2);
                    double sb   = shb / ((double)short.MaxValue);
                    if (sa != sb)
                    {
                        throw new WavCutException("different here");
                    }

                    objCallback.ReceiveProcessedSample(sa, sb);
                }

                nSample++;
                if (nSample >= nLengthOfDataChunk)
                {
                    bComplete = true;
                    break;
                }
            }
        }
        if (nSample != nLengthOfDataChunk)
        {
            Console.WriteLine("warning: eof reached earlier than expected. Expected " + nLengthOfDataChunk + " got " + nSample);
        }

        objCallback.OnFinished();
    }