/// <summary>
        /// Save the all-in-one cmp file.
        /// </summary>
        /// <param name="savingEnv">Working environment.</param>
        /// <param name="cmpSetInfo">Information of the cmp set.</param>
        private static void SaveAllInOneFile(SavingEnv savingEnv, CmpSetInfo cmpSetInfo)
        {
            FileStream stream = File.Open(savingEnv.ResultCmpFile, FileMode.Create);
            try
            {
                using (BinaryWriter writer = new BinaryWriter(stream))
                {
                    stream = null;
                    writer.Write(cmpSetInfo.SamplePeriod);
                    writer.Write(cmpSetInfo.RecordSize);
                    writer.Write(cmpSetInfo.LspOrder);
                    writer.Write(cmpSetInfo.MbeOrder);
                    writer.Write(cmpSetInfo.PowerOrder);
                    writer.Write(cmpSetInfo.GuidanceLspOrder);
                    writer.Write(cmpSetInfo.SentenceCount);

                    for (int i = 0; i < savingEnv.CmpInfoDict.Count; i++)
                    {
                        SingleCmpInfo singleCmpInfo = savingEnv.CmpInfoDict.Values.ElementAt(i);

                        writer.Write(singleCmpInfo.SentID);
                        writer.Write(singleCmpInfo.SampleCount);

                        // temporarily use indexIntoFile to denotes the position of itself in file
                        singleCmpInfo.IndexIntoFile = (int)writer.BaseStream.Position;
                        writer.Write(singleCmpInfo.IndexIntoFile);

                        savingEnv.CmpInfoDict[savingEnv.CmpInfoDict.Keys.ElementAt(i)] = singleCmpInfo;
                    }

                    int offset = (int)writer.BaseStream.Position;

                    foreach (KeyValuePair<string, SingleCmpInfo> kvp in savingEnv.CmpInfoDict)
                    {
                        SingleCmpInfo singleCmpInfo = kvp.Value;

                        writer.Seek(singleCmpInfo.IndexIntoFile, SeekOrigin.Begin);
                        singleCmpInfo.IndexIntoFile = offset;
                        writer.Write(singleCmpInfo.IndexIntoFile);

                        offset += cmpSetInfo.RecordSize * singleCmpInfo.SampleCount;
                    }

                    writer.Seek(0, SeekOrigin.End);

                    const int SingleCmpHeadSize = 12;

                    for (int i = 0; i < savingEnv.SentPaths.Count; i++)
                    {
                        string sentID = savingEnv.SentIDs[i];
                        string partialPath = savingEnv.SentPaths[i];

                        FileStream file = File.Open(FormCmpFullPath(savingEnv.WorkingPath, partialPath), FileMode.Open);
                        try
                        {
                            using (BinaryReader reader = new BinaryReader(file))
                            {
                                file = null;
                                reader.BaseStream.Seek(SingleCmpHeadSize, SeekOrigin.Begin);

                                CopyStreamData(reader, writer);
                            }
                        }
                        finally
                        {
                            if (null != file)
                            {
                                file.Dispose();
                            }
                        }
                    }
                }
            }
            finally
            {
                if (null != stream)
                {
                    stream.Dispose();
                }
            }
        }
        /// <summary>
        /// Open the all-in-one cmp set file.
        /// </summary>
        /// <param name="cmpFile">Name of the cmp file.</param>
        /// <param name="cmpSetInfo">Information of the cmp set.</param>
        /// <returns>Success or failure.</returns>
        public bool OpenCmpFile(string cmpFile, ref CmpSetInfo cmpSetInfo)
        {
            bool result = true;

            CloseCmpFile();

            if (File.Exists(cmpFile))
            {
                _loadingEnv.CmpFileName = cmpFile;
                _loadingEnv.CmpFileStream = File.Open(_loadingEnv.CmpFileName, FileMode.Open, FileAccess.Read);
                _loadingEnv.BinaryReader = new BinaryReader(_loadingEnv.CmpFileStream);

                BinaryReader reader = _loadingEnv.BinaryReader;

                cmpSetInfo.SamplePeriod = reader.ReadInt32();
                cmpSetInfo.RecordSize = reader.ReadInt32();
                cmpSetInfo.LspOrder = reader.ReadInt32();
                cmpSetInfo.MbeOrder = reader.ReadInt32();
                cmpSetInfo.PowerOrder = reader.ReadInt32();
                cmpSetInfo.GuidanceLspOrder = reader.ReadInt32();
                cmpSetInfo.SentenceCount = reader.ReadInt32();

                _loadingEnv.CmpSetInfo = cmpSetInfo;

                for (int i = 0; i < cmpSetInfo.SentenceCount; i++)
                {
                    SingleCmpInfo singleCmpInfo;

                    singleCmpInfo.SentID = reader.ReadString();
                    singleCmpInfo.SampleCount = reader.ReadInt32();
                    singleCmpInfo.IndexIntoFile = reader.ReadInt32();

                    _loadingEnv.CmpInfoDict.Add(singleCmpInfo.SentID, singleCmpInfo);
                }

                result = true;
            }
            else
            {
                result = false;
            }

            return result;
        }
        /// <summary>
        /// Get general information of the cmp set.
        /// </summary>
        /// <param name="savingEnv">Working environment.</param>
        /// <param name="cmpSetInfo">Information of the cmp set.</param>
        private static void GetCmpSetInfo(SavingEnv savingEnv, ref CmpSetInfo cmpSetInfo)
        {
            cmpSetInfo.SentenceCount = savingEnv.SentIDs.Count;

            if (cmpSetInfo.SentenceCount > 0)
            {
                FileStream file = File.Open(FormCmpFullPath(savingEnv.WorkingPath, savingEnv.SentPaths[0]), FileMode.Open);
                try
                {
                    using (BinaryReader reader = new BinaryReader(file))
                    {
                        file = null;
                        reader.BaseStream.Seek(sizeof(int), SeekOrigin.Begin);    // number of samples in file

                        int samplePeriod = reader.ReadInt32();        // sample period in 100ns units
                        cmpSetInfo.SamplePeriod = samplePeriod / 10000;  // make it in milliseconds

                        short sampleSize = reader.ReadInt16();  // number of bytes per sample
                        cmpSetInfo.RecordSize = sampleSize;

                        int gainAndF0Order = 2;
                        int mbeOrder = cmpSetInfo.MbeOrder;
                        int powerOrder = cmpSetInfo.PowerOrder;
                        int guidanceLspOrder = cmpSetInfo.GuidanceLspOrder;
                        int placeHolder = gainAndF0Order + mbeOrder + powerOrder + guidanceLspOrder;

                        cmpSetInfo.LspOrder = (sampleSize / (3 * sizeof(float))) - placeHolder;
                    }
                }
                finally
                {
                    if (null != file)
                    {
                        file.Dispose();
                    }
                }
            }
        }
        /// <summary>
        /// Compile an all-in-one cmp set file from a set of separate cmp files.
        /// </summary>
        /// <param name="workingPath">Working path.</param>
        /// <param name="fileMapName">File map name.</param>
        /// <param name="resultCmpFile">Name of the all-in-one cmp file.</param>
        /// <param name="cmpSetInfo">Information of the cmp set.</param>
        /// <returns>Success or failure.</returns>
        public static bool CompileCmpFiles(string workingPath, string fileMapName,
            string resultCmpFile, ref CmpSetInfo cmpSetInfo)
        {
            SavingEnv savingEnv = new SavingEnv();
            savingEnv.WorkingPath = workingPath;
            savingEnv.FileMapName = fileMapName;
            savingEnv.ResultCmpFile = resultCmpFile;

            ParseSentPaths(savingEnv);

            GetCmpSetInfo(savingEnv, ref cmpSetInfo);

            GetCmpInfoDict(savingEnv);

            SaveAllInOneFile(savingEnv, cmpSetInfo);

            return true;
        }