码迷,mamicode.com
首页 > 其他好文 > 详细

Wav文件操作类

时间:2014-09-22 17:26:23      阅读:261      评论:0      收藏:0      [点我收藏+]

标签:style   blog   color   io   os   ar   for   文件   数据   

    internal class WaveFile
    {
        #region 字段和属性
        //文件路径
        private string filepath;
        //文件详情对象
        private FileInfo fileInfo;
        //文件流
        private FileStream fileStream;

        private byte[] fileByteData;
        //临时存放byte数组
        private byte[] tempByte;
        //是否需要追加信息的标记
        private Boolean isNeedAppend;

        private DataBlock dataBlock;
        /// <summary>
        /// Data块
        /// </summary>
        public DataBlock Data
        {
            get { return dataBlock; }
        }

        private FactBlock factBlock;
        /// <summary>
        /// fact块
        /// </summary>
        public FactBlock Fact
        {
            get { return factBlock; }
        }

        private FmtBlock fmtBlock;
        /// <summary>
        /// fmt块
        /// </summary>
        public FmtBlock Format
        {
            get { return fmtBlock; }
        }

        private RiffBlock riffBlock;
        /// <summary>
        /// riff块
        /// </summary>
        public RiffBlock Riff
        {
            get { return riffBlock; }
        }

        /// <summary>
        /// 记录文件长度
        /// </summary>
        public float FileLenth
        {
            get { return (float)dataBlock.DataSize / (float)fmtBlock.AverageBytesPerSec; }
        }

        private Boolean isWave;
        /// <summary>
        /// 记录是否是正确的wave文件
        /// </summary>
        public Boolean IsWAVE
        {
            get { return isWave; }
        }
        #endregion

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="inFilepath"></param>
        public WaveFile(String inFilepath)
        {
            tempByte = new byte[4];
            filepath = inFilepath;
            riffBlock = new RiffBlock();
            fmtBlock = new FmtBlock();
            factBlock = new FactBlock();
            dataBlock = new DataBlock();

            fileInfo = new FileInfo(inFilepath);
            fileStream = new FileStream(inFilepath, FileMode.Open, FileAccess.ReadWrite);// m_FileInfo.OpenRead();

            fileByteData = new byte[fileStream.Length];
            fileStream.Read(fileByteData, 0, (int)fileStream.Length);
            fileStream.Dispose();

            if(JudgeIsHaveFmt(fileByteData))
            {
                if (!JudgeFmtIsRight(fileByteData))
                {
                    fileInfo = new FileInfo(inFilepath);
                    fileStream = new FileStream(inFilepath, FileMode.Open, FileAccess.ReadWrite);// m_FileInfo.OpenRead();

                    fileByteData = new byte[fileStream.Length];
                    fileStream.Read(fileByteData, 0, (int)fileStream.Length);
                    fileStream.Dispose();
                }
            }
            else
            {
                isWave = false;
                return;
            }

            Read(fileByteData);
            Dispose();
            //Read(fileStream);
            //fileStream.Dispose();
        }
         /// <summary>
        /// 判断是否有fmt块
        /// </summary>
        /// <param name="byteData"></param>
        private bool JudgeIsHaveFmt(byte[] byteData)
        {
            byte[] temp = new byte[4];
            int index = 12;
            for (int i = 0; i < 4; i++)
            {
                temp[i] = byteData[index];
                index++;
            }
            if (temp[0] != WaveMark.szRiffFormat[0] || temp[1] != WaveMark.szRiffFormat[1] || temp[2] != WaveMark.szRiffFormat[2] || temp[3] != WaveMark.szRiffFormat[3])
                return false;
            return true;
        }

        /// <summary>
        /// 判断fmt块是否正确 不正确的话,重新生成文件
        /// </summary>
        /// <param name="byteData"></param>
        private bool JudgeFmtIsRight(byte[] byteData)
        {
            #region format块

            fmtBlock.FmtSize = System.BitConverter.ToUInt32(byteData, 16);
            fmtBlock.FmtTag = System.BitConverter.ToUInt16(byteData, 20);

            if ((fmtBlock.FmtTag == 6 || fmtBlock.FmtTag == 7) && fmtBlock.FmtSize == 16)
            {
                byteData[16] = 18;

                byte[] tempByteData = new byte[byteData.Length + 2];
                for (int i = 0; i < 16 + 20; i++)
                {
                    tempByteData[i] = byteData[i];
                }
                tempByteData[36] = 0;
                tempByteData[37] = 0;

                for (int i = 36; i < byteData.Length; i++)
                {
                    tempByteData[i+2] = byteData[i];
                }

                File.WriteAllBytes(filepath,tempByteData);
                return false;
            }
            return true;

            #endregion
        }

        /// <summary>
        /// 通过byte[]来读取数据
        /// </summary>
        private void Read(byte[] byteData)
        {
            #region riff块
            isWave = false;
            for (int i = 0; i < 4; i++)
            {
                riffBlock.RiffID[i] = byteData[i];
            }
            if (riffBlock.RiffID[0] != WaveMark.szRiffID[0] || riffBlock.RiffID[1] != WaveMark.szRiffID[1] || riffBlock.RiffID[2] != WaveMark.szRiffID[2] || riffBlock.RiffID[3] != WaveMark.szRiffID[3])
            {
                return;
            }

            riffBlock.RiffSize = System.BitConverter.ToUInt32(byteData, 4);

            if ((uint)fileInfo.Length - 8 != riffBlock.RiffSize)
            {
                //return;
            }
            int y = 0;
            for (int i = 8; i < 12; i++)
            {
                riffBlock.RiffFormat[y] = byteData[i];
                y++;
            }
            #endregion

            #region format块
            y = 0;
            for (int i = 12; i < 16; i++)
            {
                fmtBlock.FmtID[y] = byteData[i];
                y++;
            }
            if (fmtBlock.FmtID[0] != WaveMark.szRiffFormat[0] || fmtBlock.FmtID[1] != WaveMark.szRiffFormat[1] || fmtBlock.FmtID[2] != WaveMark.szRiffFormat[2] || fmtBlock.FmtID[3] != WaveMark.szRiffFormat[3])
                return;

            fmtBlock.FmtSize = System.BitConverter.ToUInt32(byteData, 16);
            fmtBlock.FmtTag = System.BitConverter.ToUInt16(byteData, 20);

            fmtBlock.Channels = System.BitConverter.ToUInt16(byteData, 22);
            fmtBlock.SamplesPerSec = System.BitConverter.ToUInt32(byteData, 24);
            fmtBlock.AverageBytesPerSec = System.BitConverter.ToUInt32(byteData, 28);
            fmtBlock.BlockAlign = System.BitConverter.ToUInt16(byteData, 32);
            fmtBlock.BitsPerSample = System.BitConverter.ToUInt16(byteData, 34);


            #endregion

            #region fact块
            y = 0;
            int index = (int)fmtBlock.FmtSize + 20;
            for (int i = index; i < index + 4; i++)
            {
                tempByte[y] = byteData[i];
                y++;
            }
            index = index + 4;
            factBlock.FactID = tempByte;
            if (factBlock.FactID[0] == WaveMark.factID[0] && factBlock.FactID[1] == WaveMark.factID[1] && factBlock.FactID[2] == WaveMark.factID[2] && factBlock.FactID[3] == WaveMark.factID[3])
            {
                #region  Fact_Chunk
                factBlock.Size = System.BitConverter.ToUInt32(byteData, index);
                index = index + 4;
                index = index +(int)factBlock.Size;
                #endregion
                y = 0;
                for (int i = index; i < index + 4; i++)
                {
                    tempByte[y] = byteData[i];
                    y++;
                }
                index = index + 4;
            }
            #endregion

            #region data块
            dataBlock.DataID = tempByte;
            if (dataBlock.DataID[0] == WaveMark.dataID[0] && dataBlock.DataID[1] == WaveMark.dataID[1] && dataBlock.DataID[2] == WaveMark.dataID[2] && dataBlock.DataID[3] == WaveMark.dataID[3])
            {
                y = 0;
                for (int i = index; i < index + 4; i++)
                {
                    tempByte[y] = byteData[i];
                    y++;
                }
                index = index + 4;

                dataBlock.DataSize = BitConverter.ToUInt32(tempByte, 0);
            }

            dataBlock.DataArray = new Int16[dataBlock.DataSize];
            dataBlock.NumSamples = (int)(dataBlock.DataSize);
            dataBlock.ByteDataArray = new byte[dataBlock.DataSize];

            int z = 0;
            for (int i = index; z< dataBlock.DataSize; i++)
            {
                dataBlock.ByteDataArray[z] = byteData[i];
                z++;
            }
            

            if (fmtBlock.FmtTag == 1)//pcm数据
            {
                dataBlock.NumSamples = (int)(dataBlock.DataSize / 2);
                for (int i = 0; i < dataBlock.NumSamples; i++)
                {
                    dataBlock.DataArray[i] = System.BitConverter.ToInt16(byteData, index);// byteData[index];
                    index += 2;
                }
            }
            else
            {
                byte[] bytedata = new byte[dataBlock.DataSize];
                y = 0;
                for (int i = index; y < dataBlock.DataSize; i++)
                {
                    bytedata[y] = byteData[i];
                    y++;
                }

                dataBlock.NumSamples = (int)(dataBlock.DataSize);

                if (fmtBlock.FmtTag == 6)//alaw数据
                {
                    for (int i = 0; i < dataBlock.NumSamples; i++)
                    {
                        dataBlock.DataArray[i] = (short)AlawToPcm(bytedata[i]);
                    }
                }
                if (fmtBlock.FmtTag == 7)//ulaw数据
                {
                    for (int i = 0; i < dataBlock.NumSamples; i++)
                    {
                        dataBlock.DataArray[i] = (short)UlawToPCM(bytedata[i]);
                    }
                }
            }

            isWave = true;

            #endregion

        }

        /// <summary>
        /// 读取数据
        /// </summary>
        private void Read(FileStream inFS)
        {
            #region riff块
            isWave = false;
            inFS.Read(riffBlock.RiffID, 0, 4);

            if (riffBlock.RiffID[0] != WaveMark.szRiffID[0] || riffBlock.RiffID[1] != WaveMark.szRiffID[1] || riffBlock.RiffID[2] != WaveMark.szRiffID[2] || riffBlock.RiffID[3] != WaveMark.szRiffID[3])
            {
                return;
            }
            BinaryReader binRead = new BinaryReader(inFS);
            riffBlock.RiffSize = binRead.ReadUInt32();
            if ((uint)fileInfo.Length - 8 != riffBlock.RiffSize)
            {
                //return;
            }
            inFS.Read(riffBlock.RiffFormat, 0, 4);
            #endregion

            #region format块
            inFS.Read(fmtBlock.FmtID, 0, 4);

            if (fmtBlock.FmtID[0] != WaveMark.szRiffFormat[0] || fmtBlock.FmtID[1] != WaveMark.szRiffFormat[1] || fmtBlock.FmtID[2] != WaveMark.szRiffFormat[2] || fmtBlock.FmtID[3] != WaveMark.szRiffFormat[3])
                return;

            fmtBlock.FmtSize = binRead.ReadUInt32();
            fmtBlock.FmtTag = binRead.ReadUInt16();
            fmtBlock.Channels = binRead.ReadUInt16();
            fmtBlock.SamplesPerSec = binRead.ReadUInt32();
            fmtBlock.AverageBytesPerSec = binRead.ReadUInt32();
            fmtBlock.BlockAlign = binRead.ReadUInt16();
            fmtBlock.BitsPerSample = binRead.ReadUInt16();

            // This accounts for the variable format header size  
            // 12 bytes of Riff Header, 4 bytes for FormatId, 4 bytes for FormatSize & the Actual size of the Format Header  
            inFS.Seek(fmtBlock.FmtSize + 20, System.IO.SeekOrigin.Begin);
            #endregion

            #region fact块

            inFS.Read(tempByte, 0, 4);
            factBlock.FactID = tempByte;
            if (factBlock.FactID[0] == WaveMark.factID[0] && factBlock.FactID[1] == WaveMark.factID[1] && factBlock.FactID[2] == WaveMark.factID[2] && factBlock.FactID[3] == WaveMark.factID[3])
            {
                #region  Fact_Chunk
                inFS.Read(tempByte, 0, 4);
                factBlock.Size = BitConverter.ToUInt32(tempByte, 0);
                inFS.Position += factBlock.Size;
                #endregion
                inFS.Read(tempByte, 0, 4);
            }
            #endregion

            #region data块
            dataBlock.DataID = tempByte;
            if (dataBlock.DataID[0] == WaveMark.dataID[0] && dataBlock.DataID[1] == WaveMark.dataID[1] && dataBlock.DataID[2] == WaveMark.dataID[2] && dataBlock.DataID[3] == WaveMark.dataID[3])
            {
                inFS.Read(tempByte, 0, 4);
                dataBlock.DataSize = BitConverter.ToUInt32(tempByte, 0);
                //if (m_Data.DataSize != (uint)m_FileInfo.Length - (uint)inFS.Position)
                //{
                //    uint size2 = (uint)m_FileInfo.Length - (uint)inFS.Position;
                //    byte[] bpara = System.BitConverter.GetBytes(size2);
                //    inFS.Seek(inFS.Position - 4, SeekOrigin.Begin);//定位文件头
                //    foreach (byte bt in bpara)
                //        inFS.WriteByte(bt);
                //}
            }
            else
            {
                //wave文件数据
                dataBlock.DataSize = riffBlock.RiffSize + 8 - (uint)inFS.Position;
            }
            //m_Data.DataSize = (uint)m_FileInfo.Length - (uint)inFS.Position; 

            dataBlock.DataArray = new Int16[dataBlock.DataSize];
            dataBlock.NumSamples = (int)(dataBlock.DataSize);

            if (fmtBlock.FmtTag == 1)//pcm数据
            {
                inFS.Seek(40, System.IO.SeekOrigin.Begin);
                dataBlock.NumSamples = (int)(dataBlock.DataSize / 2);
                for (int i = 0; i < dataBlock.NumSamples; i++)
                {
                    dataBlock.DataArray[i] = binRead.ReadInt16();
                }
            }
            else
            {
                byte[] bytedata = new byte[dataBlock.DataSize];
                inFS.Read(bytedata, 0, (int)dataBlock.DataSize);
                dataBlock.NumSamples = (int)(dataBlock.DataSize);

                if (fmtBlock.FmtTag == 6)//alaw数据
                {
                    for (int i = 0; i < dataBlock.NumSamples; i++)
                    {
                        dataBlock.DataArray[i] = (short)AlawToPcm(bytedata[i]);
                    }
                }
                if (fmtBlock.FmtTag == 7)//ulaw数据
                {
                    for (int i = 0; i < dataBlock.NumSamples; i++)
                    {
                        dataBlock.DataArray[i] = (short)UlawToPCM(bytedata[i]);
                    }
                }
            }

            isWave = true;

            #endregion

        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            fileInfo = null;
            fileStream.Dispose();
        }

        #region alaw转pcm 、ulaw转pcm
        /// <summary>
        /// alaw转pcm
        /// </summary>
        /// <param name="a_val"></param>
        /// <returns></returns>
        private int AlawToPcm(byte a_val)
        {
            int t;
            int seg;
            a_val ^= 0x55;
            t = (a_val & 0xf) << 4;
            seg = (a_val & 0x70) >> 4;
            switch (seg)
            {
                case 0:
                    t += 8;
                    break;
                case 1:
                    t += 0x108;
                    break;
                default:
                    t += 0x108;
                    t <<= seg - 1;
                    break;
            }
            if ((a_val & 0x80) == 0)
            {
                return -t;
            }
            return t;
            //return ((a_val & 0x80) ? t : -t);
        }

        /// <summary>
        /// ulaw转pcm
        /// </summary>
        /// <param name="u_val"></param>
        /// <returns></returns>
        private int UlawToPCM(byte u_val)
        {
            int t;
            int u = (int)u_val;
            /* Complement to obtain normal u-law value. */
            u = ~u;

            t = ((u & 0xf) << 3) + 0x84;
            t <<= (u & 0x70) >> 4;
            if ((u & 0x80) == 0)
                return t - 0x84;
            return (0x84 - t);
        }
        #endregion

        #region 类: riff、fmt、fact、data
        /// <summary> 
        /// riff块
        /// </summary> 
        public class RiffBlock
        {
            public RiffBlock()
            {
                m_RiffID = new byte[4];
                m_RiffFormat = new byte[4];
            }

            private byte[] m_RiffID;
            //文件前四个字节 为RIFF 
            public byte[] RiffID
            {
                get { return m_RiffID; }
                set { m_RiffID = value; }
            }

            private uint m_RiffSize;
            /// <summary> 
            /// 数据大小 文件大小= 这个数字+8 
            /// </summary>  
            public uint RiffSize
            {
                get { return m_RiffSize; }
                set { m_RiffSize = value; }
            }

            private byte[] m_RiffFormat;
            /// <summary>
            /// wave
            /// </summary>
            public byte[] RiffFormat
            {
                get { return m_RiffFormat; }
                set { m_RiffFormat = value; }
            }
        }

        /// <summary> 
        /// format块
        /// </summary> 
        public class FmtBlock
        {
            public FmtBlock()
            {
                fmtID = new byte[4];
            }

            private byte[] fmtID;
            /// <summary> 
            /// 固定为  是"fmt "
            /// 以‘fmt ‘作为标示
            /// </summary>  
            public byte[] FmtID
            {
                get { return fmtID; }
                set { fmtID = value; }
            }

            private uint fmtSize;
            /// <summary> 
            /// 一般情况下Size为16,此时最后附加信息没有;如果为18
            /// 则最后多了2个字节的附加信息
            /// </summary>  
            public uint FmtSize
            {
                get { return fmtSize; }
                set { fmtSize = value; }
            }

            private ushort fmtTag;
            /// <summary>
            /// fmt
            /// </summary>
            public ushort FmtTag
            {
                get { return fmtTag; }
                set { fmtTag = value; }
            }

            private ushort channels;
            /// <summary>
            /// 声道数
            /// </summary>
            public ushort Channels
            {
                get { return channels; }
                set { channels = value; }
            }

            private uint samplesPerSec;
            // 采样率(每秒样本数),表示每个通道的播放速度,
            public uint SamplesPerSec
            {
                get { return samplesPerSec; }
                set { samplesPerSec = value; }
            }

            private uint averageBytesPerSec;
            /// <summary> 
            /// 波形音频数据传送速率,其值为通道数*每秒数据位数*每样
            /// 本的数据位数/8。播放软件利用此值可以估计缓冲区的大小。 
            /// </summary>  
            public uint AverageBytesPerSec
            {
                get { return averageBytesPerSec; }
                set { averageBytesPerSec = value; }
            }

            private ushort blockAlign;
            public ushort BlockAlign
            {
                get { return blockAlign; }
                set { blockAlign = value; }
            }

            private ushort bitsPerSample;
            /// <summary> 
            ///  每样本的数据位数,表示每个声道中各个样本的数据位数。如果有多
            /// 个声道,对每个声道而言,样本大小都一样。  
            /// </summary>  
            public ushort BitsPerSample
            {
                get { return bitsPerSample; }
                set { bitsPerSample = value; }
            }
        }

        /// <summary> 
        /// fact块
        /// </summary> 
        public class FactBlock
        {
            /// <summary>
            /// 构造函数
            /// </summary>
            public FactBlock()
            {
                factId = new byte[4];
            }

            #region 字段、属性
            private byte[] factId;
            /// <summary> 
            /// 文件前四个字节 为fact 
            /// </summary> 
            public byte[] FactID
            {
                get { return factId; }
                set { factId = value; }
            }

            private uint size;
            /// <summary> 
            /// 数据大小 
            /// </summary> 
            public uint Size
            {
                get { return size; }
                set { size = value; }
            }
            #endregion
        }

        /// <summary>
        /// data块
        /// </summary> 
        public class DataBlock
        {
            /// <summary>
            /// 构造函数
            /// </summary>
            public DataBlock()
            {
                DataID = new byte[4];
            }

            #region 字段、属性
            private byte[] m_DataID;
            /// <summary> 
            /// 文件前四个字节 为data
            /// </summary>  
            public byte[] DataID
            {
                get { return m_DataID; }
                set { m_DataID = value; }
            }

            private uint m_DataSize;
            /// <summary> 
            /// 大小 
            /// </summary> 
            public uint DataSize
            {
                get { return m_DataSize; }
                set { m_DataSize = value; }
            }

            private Int16[] m_Data;
            /// <summary>
            /// 数据
            /// </summary>
            public Int16[] DataArray
            {
                get { return m_Data; }
                set { m_Data = value; }
            }

            private Byte[] byteDataArray;
            /// <summary>
            /// 数据
            /// </summary>
            public Byte[] ByteDataArray
            {
                get { return byteDataArray; }
                set { byteDataArray = value; }
            }
            //public Int16 this[int pos]
            //{
            //    get { return m_Data[pos]; }
            //    set { m_DataID = value; }
            //}

            private int m_NumSamples;
            /// <summary>
            /// 采样数
            /// </summary>
            public int NumSamples
            {
                get { return m_NumSamples; }
                set { m_NumSamples = value; }
            }

            #endregion

        }

        #endregion
    }
    /// <summary> 
    /// 文件头 标识类
    /// </summary> 
    public static class WaveMark
    {
        /// <summary> 
        /// 文件前四个字节 为RIFF 
        /// </summary> 
        public static byte[] szRiffID = new byte[] { 0x52, 0x49, 0x46, 0x46 };   // ‘R‘,‘I‘,‘F‘,‘F‘ 

        /// <summary> 
        ///WAVE文件定义 为WAVE 
        /// </summary> 
        public static byte[] szRiffWaveID = new byte[] { 0x57, 0x41, 0x56, 0x45 }; // ‘W‘,‘A‘,‘V‘,‘E‘        

        /// <summary> 
        /// 固定为  是"fmt "字后一位为0x20  
        /// </summary> 
        public static byte[] szRiffFormat = new byte[] { 0x66, 0x6D, 0x74, 0x20 }; // fmt

        /// <summary> 
        /// 文件前四个字节 为fact 
        /// </summary> 
        public static byte[] factID = new byte[] { 0x66, 0x61, 0x63, 0x74 };   // ‘f‘,‘a‘,‘c‘,‘t‘ 

        /// <summary> 
        /// 文件前四个字节 为RIFF 
        /// </summary> 
        public static byte[] dataID = new byte[] { 0x64, 0x61, 0x74, 0x61 };   // ‘d‘,‘a‘,‘t‘,‘a‘ 
    }

    /// <summary>
    /// 音频格式
    /// </summary>
    public enum EnumAudioType
    {       
        /// <summary>
        /// 未知格式
        /// </summary>
        UnKnown = 0,
        /// <summary>
        /// 8k U-law 单声道
        /// </summary>
        Ulaw8k1 = 1,
        /// <summary>
        /// 8k U-law 双声道
        /// </summary>
        Ulaw8k2 = 2,
        /// <summary>
        /// 8K A-law单声道
        /// </summary>
        Alaw8k1 = 3,
        /// <summary>
        /// 8K A-law双声道
        /// </summary>
        Alaw8k2 = 4,
        /// <summary>
        /// 16K A-law单声道
        /// </summary>
        Alaw16k1 = 5,
        /// <summary>
        /// 16K A-law双声道
        /// </summary>
        Alaw16k2 = 6,
        /// <summary>
        /// 8K 8bit PCM单声道
        /// </summary>
        Linear8k8bit1 = 7,
        /// <summary>
        /// 8K 8bit PCM双声道
        /// </summary>
        Linear8k8bit2 = 8,
        /// <summary>
        /// 8K 16bit PCM单声道
        /// </summary>
        Linear8k16bit1 = 9,
        /// <summary>
        /// 8K 16bit PCM双声道
        /// </summary>
        Linear8k16bit2 = 10,
        /// <summary>
        /// 16K 16bit PCM单声道
        /// </summary>
        Linear16k16bit1 = 11,
        /// <summary>
        /// 16K 16bit PCM双声道
        /// </summary>
        Linear16k16bit2 = 12,
        /// <summary>
        /// 16K 8bit PCM单声道
        /// </summary>
        Linear16k8bit1 = 13,
        /// <summary>
        /// 16K 8bit PCM双声道
        /// </summary>
        Linear16k8bit2 = 14,
        /// <summary>
        /// 16k U-law 单声道
        /// </summary>
        Ulaw16k1 = 15,
        /// <summary>
        /// 16k U-law 双声道
        /// </summary>
        Ulaw16k2 = 16
    }

 

Wav文件操作类

标签:style   blog   color   io   os   ar   for   文件   数据   

原文地址:http://www.cnblogs.com/chengjunwei/p/3986074.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!