Exemplo n.º 1
0
        // --- functions ---

        /// <summary>Reads wave data from a WAVE file.</summary>
        /// <param name="fileName">The file name of the WAVE file.</param>
        /// <returns>The wave data.</returns>
        private static Sound LoadFromFile(string fileName)
        {
            byte[] fileBytes = File.ReadAllBytes(fileName);
            using (MemoryStream stream = new MemoryStream(fileBytes)) {
                using (BinaryReader reader = new BinaryReader(stream)) {
                    // RIFF/RIFX chunk
                    Endianness endianness;
                    uint       headerCkID = reader.ReadUInt32();
                    if (headerCkID == 0x46464952)
                    {
                        endianness = Endianness.Little;
                    }
                    else if (headerCkID == 0x58464952)
                    {
                        endianness = Endianness.Big;
                    }
                    else
                    {
                        throw new InvalidDataException("Invalid chunk ID");
                    }
                    //Skip the header checksum
                    ReadUInt32(reader, endianness);
                    uint formType = ReadUInt32(reader, endianness);
                    if (formType != 0x45564157)
                    {
                        throw new InvalidDataException("Unsupported format");
                    }
                    // data chunks
                    WaveFormat format    = new WaveFormat();
                    FormatData data      = null;
                    byte[]     dataBytes = null;
                    while (stream.Position + 8 <= stream.Length)
                    {
                        uint ckID   = reader.ReadUInt32();
                        uint ckSize = ReadUInt32(reader, endianness);
                        if (ckID == 0x20746d66)
                        {
                            // "fmt " chunk
                            if (ckSize < 14)
                            {
                                throw new InvalidDataException("Unsupported fmt chunk size");
                            }
                            ushort wFormatTag      = ReadUInt16(reader, endianness);
                            ushort wChannels       = ReadUInt16(reader, endianness);
                            uint   dwSamplesPerSec = ReadUInt32(reader, endianness);
                            if (dwSamplesPerSec >= 0x80000000)
                            {
                                throw new InvalidDataException("Unsupported dwSamplesPerSec");
                            }
                            //Skip the average bytes per second declaration
                            ReadUInt32(reader, endianness);
                            ushort wBlockAlign = ReadUInt16(reader, endianness);
                            if (wFormatTag == 1)
                            {
                                // PCM
                                if (ckSize < 16)
                                {
                                    throw new InvalidDataException("Unsupported fmt chunk size");
                                }
                                ushort wBitsPerSample = ReadUInt16(reader, endianness);
                                stream.Position += ckSize - 16;
                                if (wBitsPerSample < 1)
                                {
                                    throw new InvalidDataException("Unsupported wBitsPerSample");
                                }
                                if (wBlockAlign != ((wBitsPerSample + 7) / 8) * wChannels)
                                {
                                    throw new InvalidDataException("Unexpected wBlockAlign");
                                }
                                format = new WaveFormat((int)dwSamplesPerSec, (int)wBitsPerSample, wChannels);
                                PcmData pcmData = new PcmData {
                                    BlockSize = (int)wBlockAlign
                                };
                                data = pcmData;
                            }
                            else if (wFormatTag == 2)
                            {
                                // Microsoft ADPCM
                                if (ckSize < 22)
                                {
                                    throw new InvalidDataException("Unsupported fmt chunk size");
                                }
                                ushort wBitsPerSample = ReadUInt16(reader, endianness);
                                if (wBitsPerSample != 4)
                                {
                                    throw new InvalidDataException("Unsupported wBitsPerSample");
                                }
                                ReadUInt16(reader, endianness);
                                MicrosoftAdPcmData adpcmData = new MicrosoftAdPcmData {
                                    SamplesPerBlock = ReadUInt16(reader, endianness)
                                };
                                if (adpcmData.SamplesPerBlock == 0 | adpcmData.SamplesPerBlock > 2 * ((int)wBlockAlign - 6))
                                {
                                    throw new InvalidDataException("Unexpected nSamplesPerBlock");
                                }
                                ushort wNumCoef = ReadUInt16(reader, endianness);
                                if (ckSize < 22 + 4 * wNumCoef)
                                {
                                    throw new InvalidDataException("Unsupported fmt chunk size");
                                }
                                adpcmData.Coefficients = new short[wNumCoef][];
                                unchecked {
                                    for (int i = 0; i < wNumCoef; i++)
                                    {
                                        adpcmData.Coefficients[i] = new short[] {
                                            (short)ReadUInt16(reader, endianness),
                                            (short)ReadUInt16(reader, endianness)
                                        };
                                    }
                                }
                                stream.Position    += ckSize - (22 + 4 * wNumCoef);
                                format              = new WaveFormat((int)dwSamplesPerSec, 16, (int)wChannels);
                                adpcmData.BlockSize = wBlockAlign;
                                data = adpcmData;
                            }
                            else if (wFormatTag == 85)
                            {
                                //This is actually a WAV encapsulated mp3 file....
                                return(NlayerLoadFromFile(fileName));
                            }
                            else
                            {
                                // unsupported format
                                throw new InvalidDataException("Unsupported wFormatTag");
                            }
                        }
                        else if (ckID == 0x61746164)
                        {
                            // "data" chunk
                            if (ckSize >= 0x80000000)
                            {
                                throw new InvalidDataException("Unsupported data chunk size");
                            }
                            if (data is PcmData)
                            {
                                // PCM
                                int bytesPerSample = (format.BitsPerSample + 7) / 8;
                                int samples        = (int)ckSize / (format.Channels * bytesPerSample);
                                int dataSize       = samples * format.Channels * bytesPerSample;
                                dataBytes        = reader.ReadBytes(dataSize);
                                stream.Position += ckSize - dataSize;
                            }
                            else if (data != null)
                            {
                                // Microsoft ADPCM
                                MicrosoftAdPcmData adpcmData = (MicrosoftAdPcmData)data;
                                int blocks = (int)ckSize / adpcmData.BlockSize;
                                dataBytes = new byte[2 * blocks * format.Channels * adpcmData.SamplesPerBlock];
                                int position = 0;
                                for (int i = 0; i < blocks; i++)
                                {
                                    unchecked {
                                        MicrosoftAdPcmData.ChannelData[] channelData = new MicrosoftAdPcmData.ChannelData[format.Channels];
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            channelData[j].bPredictor = (int)reader.ReadByte();
                                            if (channelData[j].bPredictor >= adpcmData.Coefficients.Length)
                                            {
                                                throw new InvalidDataException("Invalid bPredictor");
                                            }
                                            else
                                            {
                                                channelData[j].iCoef1 = (int)adpcmData.Coefficients[channelData[j].bPredictor][0];
                                                channelData[j].iCoef2 = (int)adpcmData.Coefficients[channelData[j].bPredictor][1];
                                            }
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            channelData[j].iDelta = (short)ReadUInt16(reader, endianness);
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            channelData[j].iSamp1 = (short)ReadUInt16(reader, endianness);
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            channelData[j].iSamp2 = (short)ReadUInt16(reader, endianness);
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            dataBytes[position]     = (byte)(ushort)channelData[j].iSamp2;
                                            dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp2 >> 8);
                                            position += 2;
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            dataBytes[position]     = (byte)(ushort)channelData[j].iSamp1;
                                            dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp1 >> 8);
                                            position += 2;
                                        }
                                        uint nibbleByte  = 0;
                                        bool nibbleFirst = true;
                                        for (int j = 0; j < adpcmData.SamplesPerBlock - 2; j++)
                                        {
                                            for (int k = 0; k < format.Channels; k++)
                                            {
                                                int lPredSample =
                                                    (int)channelData[k].iSamp1 * channelData[k].iCoef1 +
                                                    (int)channelData[k].iSamp2 * channelData[k].iCoef2 >> 8;
                                                int iErrorDeltaUnsigned;
                                                if (nibbleFirst)
                                                {
                                                    nibbleByte          = (uint)reader.ReadByte();
                                                    iErrorDeltaUnsigned = (int)(nibbleByte >> 4);
                                                    nibbleFirst         = false;
                                                }
                                                else
                                                {
                                                    iErrorDeltaUnsigned = (int)(nibbleByte & 15);
                                                    nibbleFirst         = true;
                                                }
                                                int iErrorDeltaSigned =
                                                    iErrorDeltaUnsigned >= 8 ? iErrorDeltaUnsigned - 16 : iErrorDeltaUnsigned;
                                                int lNewSampInt =
                                                    lPredSample + (int)channelData[k].iDelta * iErrorDeltaSigned;
                                                short lNewSamp =
                                                    lNewSampInt <= -32768 ? (short)-32768 :
                                                    lNewSampInt >= 32767 ? (short)32767 :
                                                    (short)lNewSampInt;
                                                channelData[k].iDelta = (short)(
                                                    (int)channelData[k].iDelta *
                                                    (int)MicrosoftAdPcmData.AdaptionTable[iErrorDeltaUnsigned] >> 8
                                                    );
                                                if (channelData[k].iDelta < 16)
                                                {
                                                    channelData[k].iDelta = 16;
                                                }
                                                channelData[k].iSamp2   = channelData[k].iSamp1;
                                                channelData[k].iSamp1   = lNewSamp;
                                                dataBytes[position]     = (byte)(ushort)lNewSamp;
                                                dataBytes[position + 1] = (byte)((ushort)lNewSamp >> 8);
                                                position += 2;
                                            }
                                        }
                                    }
                                    stream.Position += adpcmData.BlockSize - (format.Channels * (adpcmData.SamplesPerBlock - 2) + 1 >> 1) - 7 * format.Channels;
                                }
                                stream.Position += (int)ckSize - blocks * adpcmData.BlockSize;
                            }
                            else
                            {
                                // invalid
                                throw new InvalidDataException("No fmt chunk before the data chunk");
                            }
                        }
                        else
                        {
                            // unsupported chunk
                            stream.Position += (long)ckSize;
                        }
                        // pad byte
                        if ((ckSize & 1) == 1)
                        {
                            stream.Position++;
                        }
                    }
                    // finalize
                    if (dataBytes == null)
                    {
                        throw new InvalidDataException("No data chunk before the end of the file");
                    }
                    else if (format.Channels == 1)
                    {
                        return(new Sound(format.SampleRate, ((format.BitsPerSample + 7) >> 3) << 3, new byte[][] { dataBytes }));
                    }
                    else
                    {
                        byte[][] bytes = new byte[format.Channels][];
                        for (int i = 0; i < format.Channels; i++)
                        {
                            bytes[i] = new byte[dataBytes.Length / format.Channels];
                        }
                        int bytesPerSample = (format.BitsPerSample + 7) >> 3;
                        int samples        = dataBytes.Length / (format.Channels * bytesPerSample);
                        int pos1           = 0;
                        int pos2           = 0;
                        for (int i = 0; i < samples; i++)
                        {
                            for (int j = 0; j < format.Channels; j++)
                            {
                                for (int k = 0; k < bytesPerSample; k++)
                                {
                                    bytes[j][pos1 + k] = dataBytes[pos2 + k];
                                }
                                pos2 += bytesPerSample;
                            }
                            pos1 += bytesPerSample;
                        }
                        return(new Sound(format.SampleRate, ((format.BitsPerSample + 7) >> 3) << 3, bytes));
                    }
                }
            }
        }
Exemplo n.º 2
0
		// --- functions ---
		
		/// <summary>Reads wave data from a RIFF/WAVE/PCM file.</summary>
		/// <param name="fileName">The file name of the RIFF/WAVE/PCM file.</param>
		/// <returns>The wave data.</returns>
		/// <remarks>Both RIFF and RIFX container formats are supported by this function.</remarks>
		internal static WaveData LoadFromFile(string fileName) {
			string fileTitle = Path.GetFileName(fileName);
			byte[] fileBytes = File.ReadAllBytes(fileName);
			using (MemoryStream stream = new MemoryStream(fileBytes)) {
				using (BinaryReader reader = new BinaryReader(stream)) {
					// RIFF/RIFX chunk
					Endianness endianness;
					uint headerCkID = reader.ReadUInt32(); /* Chunk ID is character-based */
					if (headerCkID == 0x46464952) {
						endianness = Endianness.Little;
					} else if (headerCkID == 0x58464952) {
						endianness = Endianness.Big;
					} else {
						throw new InvalidDataException("Invalid chunk ID in " + fileTitle);
					}
					uint headerCkSize = ReadUInt32(reader, endianness);
					uint formType = ReadUInt32(reader, endianness);
					if (formType != 0x45564157) {
						throw new InvalidDataException("Unsupported format in " + fileTitle);
					}
					// data chunks
					WaveFormat format = new WaveFormat();
					FormatData data = null;
					byte[] dataBytes = null;
					while (stream.Position + 8 <= stream.Length) {
						uint ckID = reader.ReadUInt32(); /* Chunk ID is character-based */
						uint ckSize = ReadUInt32(reader, endianness);
						if (ckID == 0x20746d66) {
							// "fmt " chunk
							if (ckSize < 14) {
								throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle);
							}
							ushort wFormatTag = ReadUInt16(reader, endianness);
							ushort wChannels = ReadUInt16(reader, endianness);
							uint dwSamplesPerSec = ReadUInt32(reader, endianness);
							if (dwSamplesPerSec >= 0x80000000) {
								throw new InvalidDataException("Unsupported dwSamplesPerSec in " + fileTitle);
							}
							uint dwAvgBytesPerSec = ReadUInt32(reader, endianness);
							ushort wBlockAlign = ReadUInt16(reader, endianness);
							if (wFormatTag == 1) {
								// PCM
								if (ckSize < 16) {
									throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle);
								}
								ushort wBitsPerSample = ReadUInt16(reader, endianness);
								stream.Position += ckSize - 16;
								if (wBitsPerSample < 1) {
									throw new InvalidDataException("Unsupported wBitsPerSample in " + fileTitle);
								}
								if (wBlockAlign != ((wBitsPerSample + 7) / 8) * wChannels) {
									throw new InvalidDataException("Unexpected wBlockAlign in " + fileTitle);
								}
								format.SampleRate = (int)dwSamplesPerSec;
								format.BitsPerSample = (int)wBitsPerSample;
								format.Channels = (int)wChannels;
								PcmData pcmData = new PcmData();
								pcmData.BlockSize = (int)wBlockAlign;
								data = pcmData;
							} else if (wFormatTag == 2) {
								// Microsoft ADPCM
								if (ckSize < 22) {
									throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle);
								}
								ushort wBitsPerSample = ReadUInt16(reader, endianness);
								if (wBitsPerSample != 4) {
									throw new InvalidDataException("Unsupported wBitsPerSample in " + fileTitle);
								}
								ushort cbSize = ReadUInt16(reader, endianness);
								MicrosoftAdPcmData adpcmData = new MicrosoftAdPcmData();
								adpcmData.SamplesPerBlock = ReadUInt16(reader, endianness);
								if (adpcmData.SamplesPerBlock == 0 | adpcmData.SamplesPerBlock > 2 * ((int)wBlockAlign - 6)) {
									throw new InvalidDataException("Unexpected nSamplesPerBlock in " + fileTitle);
								}
								ushort wNumCoef = ReadUInt16(reader, endianness);
								if (ckSize < 22 + 4 * wNumCoef) {
									throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle);
								}
								adpcmData.Coefficients = new short[wNumCoef][];
								for (int i = 0; i < wNumCoef; i++) {
									unchecked {
										adpcmData.Coefficients[i] = new short[] {
											(short)ReadUInt16(reader, endianness),
											(short)ReadUInt16(reader, endianness)
										};
									}
								}
								stream.Position += ckSize - (22 + 4 * wNumCoef);
								format.SampleRate = (int)dwSamplesPerSec;
								format.BitsPerSample = 16;
								format.Channels = (int)wChannels;
								adpcmData.BlockSize = wBlockAlign;
								data = adpcmData;
							} else {
								// unsupported format
								throw new InvalidDataException("Unsupported wFormatTag in " + fileTitle);
							}
						} else if (ckID == 0x61746164) {
							// "data" chunk
							if (ckSize >= 0x80000000) {
								throw new InvalidDataException("Unsupported data chunk size in " + fileTitle);
							}
							if (data is PcmData) {
								// PCM
								int bytesPerSample = (format.BitsPerSample + 7) / 8;
								int samples = (int)ckSize / (format.Channels * bytesPerSample);
								int dataSize = samples * format.Channels * bytesPerSample;
								dataBytes = reader.ReadBytes(dataSize);
								stream.Position += ckSize - dataSize;
							} else if (data is MicrosoftAdPcmData) {
								// Microsoft ADPCM
								MicrosoftAdPcmData adpcmData = (MicrosoftAdPcmData)data;
								int blocks = (int)ckSize / adpcmData.BlockSize;
								dataBytes = new byte[2 * blocks * format.Channels * adpcmData.SamplesPerBlock];
								int position = 0;
								for (int i = 0; i < blocks; i++) {
									unchecked {
										MicrosoftAdPcmData.ChannelData[] channelData = new MicrosoftAdPcmData.ChannelData[format.Channels];
										for (int j = 0; j < format.Channels; j++) {
											channelData[j].bPredictor = (int)reader.ReadByte();
											if (channelData[j].bPredictor >= adpcmData.Coefficients.Length) {
												throw new InvalidDataException("Invalid bPredictor in " + fileTitle);
											}
											channelData[j].iCoef1 = (int)adpcmData.Coefficients[channelData[j].bPredictor][0];
											channelData[j].iCoef2 = (int)adpcmData.Coefficients[channelData[j].bPredictor][1];
										}
										for (int j = 0; j < format.Channels; j++) {
											channelData[j].iDelta = (short)ReadUInt16(reader, endianness);
										}
										for (int j = 0; j < format.Channels; j++) {
											channelData[j].iSamp1 = (short)ReadUInt16(reader, endianness);
										}
										for (int j = 0; j < format.Channels; j++) {
											channelData[j].iSamp2 = (short)ReadUInt16(reader, endianness);
										}
										for (int j = 0; j < format.Channels; j++) {
											dataBytes[position] = (byte)(ushort)channelData[j].iSamp2;
											dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp2 >> 8);
											position += 2;
										}
										for (int j = 0; j < format.Channels; j++) {
											dataBytes[position] = (byte)(ushort)channelData[j].iSamp1;
											dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp1 >> 8);
											position += 2;
										}
										uint nibbleByte = 0;
										bool nibbleFirst = true;
										for (int j = 0; j < adpcmData.SamplesPerBlock - 2; j++) {
											for (int k = 0; k < format.Channels; k++) {
												int lPredSample =
													(int)channelData[k].iSamp1 * channelData[k].iCoef1 +
													(int)channelData[k].iSamp2 * channelData[k].iCoef2 >> 8;
												int iErrorDeltaUnsigned;
												if (nibbleFirst) {
													nibbleByte = (uint)reader.ReadByte();
													iErrorDeltaUnsigned = (int)(nibbleByte >> 4);
													nibbleFirst = false;
												} else {
													iErrorDeltaUnsigned = (int)(nibbleByte & 15);
													nibbleFirst = true;
												}
												int iErrorDeltaSigned =
													iErrorDeltaUnsigned >= 8 ? iErrorDeltaUnsigned - 16 : iErrorDeltaUnsigned;
												int lNewSampInt =
													lPredSample + (int)channelData[k].iDelta * iErrorDeltaSigned;
												short lNewSamp =
													lNewSampInt <= -32768 ? (short)-32768 :
													lNewSampInt >= 32767 ? (short)32767 :
													(short)lNewSampInt;
												channelData[k].iDelta = (short)(
													(int)channelData[k].iDelta *
													(int)MicrosoftAdPcmData.AdaptionTable[iErrorDeltaUnsigned] >> 8
												);
												if (channelData[k].iDelta < 16) {
													channelData[k].iDelta = 16;
												}
												channelData[k].iSamp2 = channelData[k].iSamp1;
												channelData[k].iSamp1 = lNewSamp;
												dataBytes[position] = (byte)(ushort)lNewSamp;
												dataBytes[position + 1] = (byte)((ushort)lNewSamp >> 8);
												position += 2;
											}
										}
									}
									stream.Position += adpcmData.BlockSize - (format.Channels * (adpcmData.SamplesPerBlock - 2) + 1 >> 1) - 7 * format.Channels;
								}
								stream.Position += (int)ckSize - blocks * adpcmData.BlockSize;
							} else {
								// invalid
								throw new InvalidDataException("No fmt chunk before the data chunk in " + fileTitle);
							}
						} else {
							// unsupported chunk
							stream.Position += (long)ckSize;
						}
						// pad byte
						if ((ckSize & 1) == 1) {
							stream.Position++;
						}
					}
					// finalize
					if (dataBytes == null)
						throw new InvalidDataException("No data chunk before the end of the file in " + fileTitle);
					return new WaveData(format, dataBytes);
				}
			}
		}
Exemplo n.º 3
0
        // --- functions ---

        /// <summary>Reads wave data from a RIFF/WAVE/PCM file.</summary>
        /// <param name="fileName">The file name of the RIFF/WAVE/PCM file.</param>
        /// <returns>The wave data.</returns>
        /// <remarks>Both RIFF and RIFX container formats are supported by this function.</remarks>
        internal static WaveData LoadFromFile(string fileName)
        {
            string fileTitle = Path.GetFileName(fileName);

            byte[] fileBytes = File.ReadAllBytes(fileName);
            using (MemoryStream stream = new MemoryStream(fileBytes)) {
                using (BinaryReader reader = new BinaryReader(stream)) {
                    // RIFF/RIFX chunk
                    Endianness endianness;
                    uint       headerCkID = reader.ReadUInt32();               /* Chunk ID is character-based */
                    if (headerCkID == 0x46464952)
                    {
                        endianness = Endianness.Little;
                    }
                    else if (headerCkID == 0x58464952)
                    {
                        endianness = Endianness.Big;
                    }
                    else
                    {
                        throw new InvalidDataException("Invalid chunk ID in " + fileTitle);
                    }
                    uint headerCkSize = ReadUInt32(reader, endianness);
                    uint formType     = ReadUInt32(reader, endianness);
                    if (formType != 0x45564157)
                    {
                        throw new InvalidDataException("Unsupported format in " + fileTitle);
                    }
                    // data chunks
                    WaveFormat format    = new WaveFormat();
                    FormatData data      = null;
                    byte[]     dataBytes = null;
                    while (stream.Position + 8 <= stream.Length)
                    {
                        uint ckID   = reader.ReadUInt32();                       /* Chunk ID is character-based */
                        uint ckSize = ReadUInt32(reader, endianness);
                        if (ckID == 0x20746d66)
                        {
                            // "fmt " chunk
                            if (ckSize < 14)
                            {
                                throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle);
                            }
                            ushort wFormatTag      = ReadUInt16(reader, endianness);
                            ushort wChannels       = ReadUInt16(reader, endianness);
                            uint   dwSamplesPerSec = ReadUInt32(reader, endianness);
                            if (dwSamplesPerSec >= 0x80000000)
                            {
                                throw new InvalidDataException("Unsupported dwSamplesPerSec in " + fileTitle);
                            }
                            uint   dwAvgBytesPerSec = ReadUInt32(reader, endianness);
                            ushort wBlockAlign      = ReadUInt16(reader, endianness);
                            if (wFormatTag == 1)
                            {
                                // PCM
                                if (ckSize < 16)
                                {
                                    throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle);
                                }
                                ushort wBitsPerSample = ReadUInt16(reader, endianness);
                                stream.Position += ckSize - 16;
                                if (wBitsPerSample < 1)
                                {
                                    throw new InvalidDataException("Unsupported wBitsPerSample in " + fileTitle);
                                }
                                if (wBlockAlign != ((wBitsPerSample + 7) / 8) * wChannels)
                                {
                                    throw new InvalidDataException("Unexpected wBlockAlign in " + fileTitle);
                                }
                                format.SampleRate    = (int)dwSamplesPerSec;
                                format.BitsPerSample = (int)wBitsPerSample;
                                format.Channels      = (int)wChannels;
                                PcmData pcmData = new PcmData();
                                pcmData.BlockSize = (int)wBlockAlign;
                                data = pcmData;
                            }
                            else if (wFormatTag == 2)
                            {
                                // Microsoft ADPCM
                                if (ckSize < 22)
                                {
                                    throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle);
                                }
                                ushort wBitsPerSample = ReadUInt16(reader, endianness);
                                if (wBitsPerSample != 4)
                                {
                                    throw new InvalidDataException("Unsupported wBitsPerSample in " + fileTitle);
                                }
                                ushort             cbSize    = ReadUInt16(reader, endianness);
                                MicrosoftAdPcmData adpcmData = new MicrosoftAdPcmData();
                                adpcmData.SamplesPerBlock = ReadUInt16(reader, endianness);
                                if (adpcmData.SamplesPerBlock == 0 | adpcmData.SamplesPerBlock > 2 * ((int)wBlockAlign - 6))
                                {
                                    throw new InvalidDataException("Unexpected nSamplesPerBlock in " + fileTitle);
                                }
                                ushort wNumCoef = ReadUInt16(reader, endianness);
                                if (ckSize < 22 + 4 * wNumCoef)
                                {
                                    throw new InvalidDataException("Unsupported fmt chunk size in " + fileTitle);
                                }
                                adpcmData.Coefficients = new short[wNumCoef][];
                                for (int i = 0; i < wNumCoef; i++)
                                {
                                    unchecked {
                                        adpcmData.Coefficients[i] = new short[] {
                                            (short)ReadUInt16(reader, endianness),
                                            (short)ReadUInt16(reader, endianness)
                                        };
                                    }
                                }
                                stream.Position     += ckSize - (22 + 4 * wNumCoef);
                                format.SampleRate    = (int)dwSamplesPerSec;
                                format.BitsPerSample = 16;
                                format.Channels      = (int)wChannels;
                                adpcmData.BlockSize  = wBlockAlign;
                                data = adpcmData;
                            }
                            else
                            {
                                // unsupported format
                                throw new InvalidDataException("Unsupported wFormatTag in " + fileTitle);
                            }
                        }
                        else if (ckID == 0x61746164)
                        {
                            // "data" chunk
                            if (ckSize >= 0x80000000)
                            {
                                throw new InvalidDataException("Unsupported data chunk size in " + fileTitle);
                            }
                            if (data is PcmData)
                            {
                                // PCM
                                int bytesPerSample = (format.BitsPerSample + 7) / 8;
                                int samples        = (int)ckSize / (format.Channels * bytesPerSample);
                                int dataSize       = samples * format.Channels * bytesPerSample;
                                dataBytes        = reader.ReadBytes(dataSize);
                                stream.Position += ckSize - dataSize;
                            }
                            else if (data is MicrosoftAdPcmData)
                            {
                                // Microsoft ADPCM
                                MicrosoftAdPcmData adpcmData = (MicrosoftAdPcmData)data;
                                int blocks = (int)ckSize / adpcmData.BlockSize;
                                dataBytes = new byte[2 * blocks * format.Channels * adpcmData.SamplesPerBlock];
                                int position = 0;
                                for (int i = 0; i < blocks; i++)
                                {
                                    unchecked {
                                        MicrosoftAdPcmData.ChannelData[] channelData = new MicrosoftAdPcmData.ChannelData[format.Channels];
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            channelData[j].bPredictor = (int)reader.ReadByte();
                                            if (channelData[j].bPredictor >= adpcmData.Coefficients.Length)
                                            {
                                                throw new InvalidDataException("Invalid bPredictor in " + fileTitle);
                                            }
                                            channelData[j].iCoef1 = (int)adpcmData.Coefficients[channelData[j].bPredictor][0];
                                            channelData[j].iCoef2 = (int)adpcmData.Coefficients[channelData[j].bPredictor][1];
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            channelData[j].iDelta = (short)ReadUInt16(reader, endianness);
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            channelData[j].iSamp1 = (short)ReadUInt16(reader, endianness);
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            channelData[j].iSamp2 = (short)ReadUInt16(reader, endianness);
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            dataBytes[position]     = (byte)(ushort)channelData[j].iSamp2;
                                            dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp2 >> 8);
                                            position += 2;
                                        }
                                        for (int j = 0; j < format.Channels; j++)
                                        {
                                            dataBytes[position]     = (byte)(ushort)channelData[j].iSamp1;
                                            dataBytes[position + 1] = (byte)((ushort)channelData[j].iSamp1 >> 8);
                                            position += 2;
                                        }
                                        uint nibbleByte  = 0;
                                        bool nibbleFirst = true;
                                        for (int j = 0; j < adpcmData.SamplesPerBlock - 2; j++)
                                        {
                                            for (int k = 0; k < format.Channels; k++)
                                            {
                                                int lPredSample =
                                                    (int)channelData[k].iSamp1 * channelData[k].iCoef1 +
                                                    (int)channelData[k].iSamp2 * channelData[k].iCoef2 >> 8;
                                                int iErrorDeltaUnsigned;
                                                if (nibbleFirst)
                                                {
                                                    nibbleByte          = (uint)reader.ReadByte();
                                                    iErrorDeltaUnsigned = (int)(nibbleByte >> 4);
                                                    nibbleFirst         = false;
                                                }
                                                else
                                                {
                                                    iErrorDeltaUnsigned = (int)(nibbleByte & 15);
                                                    nibbleFirst         = true;
                                                }
                                                int iErrorDeltaSigned =
                                                    iErrorDeltaUnsigned >= 8 ? iErrorDeltaUnsigned - 16 : iErrorDeltaUnsigned;
                                                int lNewSampInt =
                                                    lPredSample + (int)channelData[k].iDelta * iErrorDeltaSigned;
                                                short lNewSamp =
                                                    lNewSampInt <= -32768 ? (short)-32768 :
                                                    lNewSampInt >= 32767 ? (short)32767 :
                                                    (short)lNewSampInt;
                                                channelData[k].iDelta = (short)(
                                                    (int)channelData[k].iDelta *
                                                    (int)MicrosoftAdPcmData.AdaptionTable[iErrorDeltaUnsigned] >> 8
                                                    );
                                                if (channelData[k].iDelta < 16)
                                                {
                                                    channelData[k].iDelta = 16;
                                                }
                                                channelData[k].iSamp2   = channelData[k].iSamp1;
                                                channelData[k].iSamp1   = lNewSamp;
                                                dataBytes[position]     = (byte)(ushort)lNewSamp;
                                                dataBytes[position + 1] = (byte)((ushort)lNewSamp >> 8);
                                                position += 2;
                                            }
                                        }
                                    }
                                    stream.Position += adpcmData.BlockSize - (format.Channels * (adpcmData.SamplesPerBlock - 2) + 1 >> 1) - 7 * format.Channels;
                                }
                                stream.Position += (int)ckSize - blocks * adpcmData.BlockSize;
                            }
                            else
                            {
                                // invalid
                                throw new InvalidDataException("No fmt chunk before the data chunk in " + fileTitle);
                            }
                        }
                        else
                        {
                            // unsupported chunk
                            stream.Position += (long)ckSize;
                        }
                        // pad byte
                        if ((ckSize & 1) == 1)
                        {
                            stream.Position++;
                        }
                    }
                    // finalize
                    if (dataBytes == null)
                    {
                        throw new InvalidDataException("No data chunk before the end of the file in " + fileTitle);
                    }
                    return(new WaveData(format, dataBytes));
                }
            }
        }