/// <summary>
        /// Loads a *.utk file, setting things up for decompression.
        /// Should always be called before DecompressFile().
        /// </summary>
        /// <param name="Path">The path to the *.utk file to load.</param>
        public void LoadFile(string Path)
        {
            m_Reader = new BinaryReader(File.Open(Path, FileMode.Open));

            m_ID = new string(m_Reader.ReadChars(4));
            m_DecompressedSize = m_Reader.ReadUInt32();
            m_WaveFormatXSize = m_Reader.ReadUInt32();
            m_FormatTag = m_Reader.ReadUInt16();
            m_NumChannels = m_Reader.ReadUInt16();
            m_SamplesPerSec = m_Reader.ReadUInt32();
            m_AvgBytesPerSec = m_Reader.ReadUInt32();
            m_BlockAlign = m_Reader.ReadUInt16();
            m_BitsPerSample = m_Reader.ReadUInt16();
            m_AppendSize = m_Reader.ReadUInt32();

            m_DecompressedStream = new MemoryStream();
            m_Writer = new BinaryWriter(m_DecompressedStream);

            //Write wav-header.
            uint dwDataSize = m_DecompressedSize;
            uint dwFMTSize = 16;
            uint dwRIFFSize = 36 + dwDataSize;

            m_Writer.Write(new char[] { 'R', 'I', 'F', 'F' });
            m_Writer.Write(dwRIFFSize); //Size of file minus this field and the above field.
            m_Writer.Write(new char[] { 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' });
            m_Writer.Write(dwFMTSize); //Size of WAVEFORMATEX structure (all the data that comes after this field).
            m_Writer.Write(m_FormatTag);
            m_Writer.Write(m_NumChannels);
            m_Writer.Write(m_SamplesPerSec);
            m_Writer.Write(m_AvgBytesPerSec);
            m_Writer.Write(m_BlockAlign);
            m_Writer.Write(m_BitsPerSample);
            m_Writer.Write(new char[] { 'd', 'a', 't', 'a' });
            m_Writer.Write(dwDataSize);

            UTKParams P = new UTKParams();
            P.UnreadBitsValue = m_Reader.ReadByte();
            P.UnreadBitsCount = 8;

            P.UseLattice = (int)ReadBits(P, 1);
            P.NoiseFloor = (uint)32 - ReadBits(P, 4);
            P.FixedCodeBook[0] = (ReadBits(P, 4) + 1) * 8;

            float S = (float)ReadBits(P, 6) / 1000 + 1.04f;

            for (int i = 1; i < 64; i++)
                P.FixedCodeBook[i] = P.FixedCodeBook[i - 1] * S;

            //memset(p->ImpulseTrain, 0, 12*sizeof(float));
            Array.Clear(P.ImpulseTrain, 0, 12);

            //memset(p->R, 0, 12*sizeof(float));
            Array.Clear(P.R, 0, 12);

            //memset(p->Delay, 0, 324*sizeof(float));
            Array.Clear(P.Delay, 0, 324);

            uint Frames = (uint)(m_DecompressedSize / m_BlockAlign);

            UTKDecode(P, Frames);
        }
        private void UTKDecode(UTKParams P, uint Frames)
        {
            while (Frames > 0)
            {
                uint BlockSize = (Frames > 432) ? 432 : Frames;
                DecompressBlock(ref P);

                for (uint i = 0; i < BlockSize; i++)
                {
                    int value = Round(P.DecompressedBlock[i]);

                    if (value < -32767)
                        value = 32767;
                    else if (value > 32768)
                        value = 32768;

                    //Writing out as byte seems to fix the filesize.
                    m_Writer.Write((byte)((value & 0x00FF/*u*/) >> (8 * 0)));
                    m_Writer.Write((byte)((value & 0xFF00/*u*/) >> (8 * 1)));
                }

                Frames -= BlockSize;
            }
        }
        private unsafe void Synthesize(UTKParams P, int Sample, uint Blocks)
        {
            float[] Residual = new float[12];
            uint Samples = Blocks*12;
            int offset = -1;

            fixed (float* C64 = &P.ImpulseTrain[0])
            {
                PredictionFilter(C64, ref Residual);
            }

            while(Samples > 0)
            {
                Samples--;

                float x = P.DecompressedBlock[Sample];

                for (int i = 0; i < 12; i++)
                {
                    if (++offset == 12) offset = 0;
                    x += P.R[offset] * Residual[i];
                }

                P.R[offset--] = x;
                P.DecompressedBlock[Sample++] = x;
            }
        }
        private byte ReadBits(UTKParams P, byte NumBits)
        {
            byte ReturnValue = (byte)((255 >> (8 - NumBits)) & P.UnreadBitsValue);
            P.UnreadBitsValue >>= NumBits;
            P.UnreadBitsCount -= NumBits;

            try
            {
                if (P.UnreadBitsCount < 8)
                {
                    uint Value = (byte)m_Reader.ReadByte();
                    P.UnreadBitsValue |= (byte)(Value << P.UnreadBitsCount/* & 0xFF*/);
                    P.UnreadBitsCount += 8;
                }
            }
            catch (EndOfStreamException)
            {
                return ReturnValue;
            }

            return ReturnValue;
        }
        private unsafe void LatticeFilter(ref UTKParams P, int Voiced, float* Window, int Interval)
        {
            if (Voiced != 0)
            {
                int t = 0;
                int i = 0;

                while (i < 108)
                {
                    uint code = m_UTKTable2[(t << 8) | (P.UnreadBitsValue & 0xFF)];

                    t = (code < 2 || code > 8) ? 1 : 0;

                    ReadBits(P, m_UTKTable3[code]);

                    if (code >= 4)
                    {
                        Window[i] = m_UTKTable4[code];
                        i += Interval;
                    }
                    else
                    {
                        if (code > 1)
                        {
                            int x = (int)ReadBits(P, 6) + 7;
                            if (x > (108 - i) / Interval)
                                x = (108 - i) / Interval;

                            while(x > 0)
                            {
                                x--;
                                Window[i] = 0;
                                i += Interval;
                            }
                        }
                        else
                        {
                            Window[i] = 7;
                            while (ReadBits(P, 1) > 0)
                                Window[i]++;

                            if (ReadBits(P, 1) != 0)
                                Window[i] *= -1;

                            i += Interval;
                        }
                    }
                }
            }
            else
            {
                //Unvoiced signal; load noise
                int i;
                for (i = 0; i < 108; i += Interval)
                {
                    byte b;

                    switch (P.UnreadBitsValue & 3)
                    {
                        case 3:
                            Window[i] = 2.0f;
                            b = 2;
                            break;
                        case 1:
                            Window[i] = -2.0f;
                            b = 2;
                            break;
                        default:
                            Window[i] = 0.0f;
                            b = 1;
                            break;
                    }

                    ReadBits(P, b);
                }
            }
        }
        private unsafe void DecompressBlock(ref UTKParams P)
        {
            float[] Window = new float[118];
            float[] Matrix = new float[12];
            int Voiced = 0;

            //memset(&Window[0], 0, 5*sizeof(float));
            Array.Clear(Window, 0, 5);

            //memset(&Window[113], 0, 5 * sizeof(float));
            Array.Clear(Window, 113, 5);

            for (int i = 0; i < 12; i++)
            {
                uint result = ReadBits(P, (byte)((i < 4) ? 6 : 5));
                if (i == 0 && P.NoiseFloor > result) Voiced++;
                Matrix[i] = (m_UTKTable1[result + ((i < 4) ? 0 : 16)] - P.ImpulseTrain[i]) / 4;
            }

            for (int i = 0; i < 4; i++)
            {
                float PitchGain, InnovationGain;
                int Phase = (int)ReadBits(P, 8);
                PitchGain = ((float)ReadBits(P, 4)) / 15;
                InnovationGain = P.FixedCodeBook[ReadBits(P, 6)];

                if (P.UseLattice == 0)
                {
                    fixed (float* Wnd = &Window[5])
                    {
                        LatticeFilter(ref P, Voiced, Wnd, 1);
                    }
                }
                else
                {
                    int o = ReadBits(P, 1); //Order
                    int y = ReadBits(P, 1);

                    fixed (float* Wnd = &Window[5 + o])
                    {
                        LatticeFilter(ref P, Voiced, Wnd, 2);
                    }

                    if (y != 0)
                    {
                        for (int j = 0; j < 108; j += 2)
                            Window[6 - o + j] = 0;
                    }
                    else
                    {
                        //Vector quantization
                        fixed (float* Wnd = &Window[6 - o])
                        {
                            float* Z = Wnd;
                            for (int j = 0; j < 54; j++, Z += 2)
                                *Z =
                                      (Z[-5] + Z[+5]) * .0180326793f
                                    - (Z[-3] + Z[+3]) * .1145915613f
                                    + (Z[-1] + Z[+1]) * .5973859429f;

                            InnovationGain /= 2;
                        }
                    }
                }

                //Excitation
                for (int j = 0; j < 108; j++)
                {
                    try
                    {
                        P.DecompressedBlock[108 * i + j] = InnovationGain * Window[5 + j] + PitchGain * P.Delay[216 - Phase + 108 * i + j];
                    }
                    catch (IndexOutOfRangeException)
                    {
                        break;
                    }
                }
            }

            //Not sure about the last part of this statement...
            Array.Copy(P.Delay, 0, P.DecompressedBlock, 108, 324 /** sizeof(uint)*/);

            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 12; j++)
                    P.ImpulseTrain[j] += Matrix[j];

                Synthesize(P, i * 12, (uint)((i != 3) ? 1 : 33));
            }
        }