private void ReadWaveHeader(WaveFormat format, bool expectHeader) { _ok = false; _pos = 0; // Trace.WriteLine("ReadWaveHeader {0}", format); if (!expectHeader) { // Input file is raw data _riff = "(no header)"; _length = 0; _wave = "(no header)"; _format = "(no header)"; _size = 16; _data = "data"; _dataSize = 0; _max = uint.MaxValue; _audioFormat = format; // Assume defaults, they can be overridden later NumChannels = 2; SampleRate = 44100; if (format == WaveFormat.PCM || format == WaveFormat.EXTENSIBLE) { // Raw PCM, assume 16 bit 44k1 stereo PCM _bitsPerSample = 16; _ok = true; } else if (format == WaveFormat.IEEE_FLOAT) { // IEEE Float; assume 32 bit 44k1 stereo IEEE_FLOAT _bitsPerSample = 32; _ok = true; } else if (format == WaveFormat.INTERNAL_DOUBLE) { // our 64 bit 44k1 stereo 'double-precision' _bitsPerSample = 32; _ok = true; } _blockAlign = (ushort)((NumChannels * _bitsPerSample) >> 3); _byteRate = (uint)(_blockAlign * SampleRate); return; } // Read 'RIFF' (WAV) or 'FORM' (AIFF) tag /////////////////////////////////////////////// char[] hdr = _rdr.ReadChars(4); _riff = new string(hdr); if (_riff != "RIFF" && _riff != "FORM") { if (hdr.Length == 0) { throw (new Exception("File could not be read: no data.")); } string x = ""; for (int j = 0; j < hdr.Length; j++) { x += String.Format("{0:X} ", (int)hdr[j]); } throw (new Exception(String.Format("File is not WAV: no 'RIFF' tag found, instead '{0}'.", x))); } // File length int fileLen = _rdr.ReadInt32(); _length = (uint)fileLen; // Read Wave ////////////////////////////////////////////// _wave = new string(_rdr.ReadChars(4)); if (_wave != "WAVE" && _wave != "AIFF") throw (new Exception(String.Format("File is not WAV: no 'WAVE' tag found, instead {0}", _wave))); if (_wave == "AIFF") { // The whole file is big-endian, including lengths in the header BigEndian = true; _length = (uint)System.Net.IPAddress.NetworkToHostOrder(fileLen); } // Read Format //////////////////////////////////////////// _format = new string(_rdr.ReadChars(4)); // WMP11-ripped WAV files begin with 'LIST' metadata - even before the format tag. // AIFF files could have this too (haven't seen it yet). // Skip any chunks up to the format header. while (_format.Length > 0 && _format != "fmt " && _format != "COMM") { int chunkSize = _rdr.ReadInt32(); if (BigEndian) chunkSize = System.Net.IPAddress.NetworkToHostOrder(chunkSize); Trace.WriteLine("Skipping {0} ({1} bytes)", _format, chunkSize); _rdr.ReadBytes(chunkSize); _format = new string(_rdr.ReadChars(4)); } if (_format == "fmt ") { // WAV file-format chunk _size = _rdr.ReadUInt32(); if (_size < 16) throw (new Exception("File could not be read: don't know how to read 'fmt' size " + _size)); _audioFormat = (WaveFormat)_rdr.ReadUInt16(); if (_audioFormat == WaveFormat.PCM || _audioFormat == WaveFormat.ADPCM || _audioFormat == WaveFormat.IEEE_FLOAT || _audioFormat == WaveFormat.INTERNAL_DOUBLE || _audioFormat == WaveFormat.EXTENSIBLE) { // // WAVEFORMATEX wFormatTag 2 bytes NumChannels = _rdr.ReadUInt16(); // WAVEFORMATEX nChannels 2 SampleRate = _rdr.ReadUInt32(); // WAVEFORMATEX nSamplesPerSec 4 _byteRate = _rdr.ReadUInt32(); // WAVEFORMATEX nAvgBytesPerSec 4 _blockAlign = _rdr.ReadUInt16(); // WAVEFORMATEX nBlockAlign 2 (channels * bitspersample / 8) _bitsPerSample = _rdr.ReadUInt16(); // WAVEFORMATEX wBitsPerSample 2 (the *container* size) if (_size > 16) { uint skip = 16; if (_audioFormat == WaveFormat.EXTENSIBLE) { UInt16 kip = _rdr.ReadUInt16(); UInt16 union = _rdr.ReadUInt16(); // the Samples union, wdc _channelMask = _rdr.ReadUInt32(); // channel mask // then the GUID, 16 bytes _formatEx = new WaveFormatEx(_rdr.ReadBytes(16)); skip = 40; if (_formatEx.Equals(WaveFormatEx.PCM) || _formatEx.Equals(WaveFormatEx.AMBISONIC_B_FORMAT_PCM)) { _audioFormat = WaveFormat.PCM; } else if (_formatEx.Equals(WaveFormatEx.IEEE_FLOAT) || _formatEx.Equals(WaveFormatEx.AMBISONIC_B_FORMAT_IEEE_FLOAT)) { _audioFormat = WaveFormat.IEEE_FLOAT; } } // Read and discard the rest of the 'fmt' structure _rdr.ReadBytes((int)(_size - skip)); } } else { throw (new Exception("File could not be read: don't know how to read audio format " + _audioFormat)); } } else if (_format == "COMM") { // AIFF file-format chunk _size = (uint)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt32()); if (_size < 18) throw (new Exception("File could not be read: don't know how to read 'COMM' size " + _size)); _audioFormat = WaveFormat.PCM; NumChannels = (ushort)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt16()); uint numFrames = (uint)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt32()); // number of sample frames _bitsPerSample = (ushort)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt16()); // SampleRate is 10-byte IEEE_extended format. Don't bother converting that // properly, just check for good known values (yuk!) byte[] ext = _rdr.ReadBytes(10); if (ext[0] == 64 && ext[1] == 14 && ext[2] == 172 && ext[3] == 68) { SampleRate = 44100; } else if (ext[0] == 64 && ext[1] == 14 && ext[2] == 187 && ext[3] == 128) { SampleRate = 48000; } else if (ext[0] == 64 && ext[1] == 15 && ext[2] == 187 && ext[3] == 128) { SampleRate = 96000; } else { throw (new Exception("File could not be read: don't know how to interpret sample rate.")); } _blockAlign = (ushort)((NumChannels * _bitsPerSample) / 8); _byteRate = (uint)(_blockAlign * SampleRate); if (_size > 18) { // Read and discard the rest of the 'fmt' structure _rdr.ReadBytes((int)(_size - 18)); } } else { throw (new Exception(String.Format("File could not be read: no 'fmt' tag found, instead {0}", _format))); } // Read Data /////////////////////////////////////////////// _data = new string(_rdr.ReadChars(4)); while (_data.Length > 0 && _data != "data" && _data != "SSND") { // Not a data chunk, ignore int miscSize = _rdr.ReadInt32(); if (BigEndian) miscSize = System.Net.IPAddress.NetworkToHostOrder(miscSize); _rdr.ReadBytes(miscSize); _data = new string(_rdr.ReadChars(4)); } // Read the data size if (BigEndian) { _dataSize = (uint)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt32()); } else { _dataSize = _rdr.ReadUInt32(); } // See if we can read this if (NumChannels > 0) { switch (_audioFormat) { case WaveFormat.PCM: case WaveFormat.EXTENSIBLE: switch (_bitsPerSample) { case 8: case 16: case 24: case 32: _ok = true; break; default: break; } break; case WaveFormat.IEEE_FLOAT: switch (_bitsPerSample) { case 32: case 64: _ok = true; break; } break; case WaveFormat.INTERNAL_DOUBLE: switch (_bitsPerSample) { case 64: _ok = true; break; } break; default: break; } } if (_ok) { _max = (uint)((_dataSize / (_bitsPerSample / 8)) / NumChannels); if (_dataSize==4294967292) { Trace.WriteLine("Wave file: unknown size from header"); _max = uint.MaxValue; } if (_max == 0) { Trace.WriteLine("Wave file: zero size from header"); _max = uint.MaxValue; } } }
private void ReadWaveHeader(WaveFormat format, bool expectHeader) { _ok = false; _pos = 0; // Trace.WriteLine("ReadWaveHeader {0}", format); if (!expectHeader) { // Input file is raw data _riff = "(no header)"; _length = 0; _wave = "(no header)"; _format = "(no header)"; _size = 16; _data = "data"; _dataSize = 0; _max = uint.MaxValue; _audioFormat = format; // Assume defaults, they can be overridden later NumChannels = 2; SampleRate = 44100; if (format == WaveFormat.PCM || format == WaveFormat.EXTENSIBLE) { // Raw PCM, assume 16 bit 44k1 stereo PCM _bitsPerSample = 16; _ok = true; } else if (format == WaveFormat.IEEE_FLOAT) { // IEEE Float; assume 32 bit 44k1 stereo IEEE_FLOAT _bitsPerSample = 32; _ok = true; } else if (format == WaveFormat.INTERNAL_DOUBLE) { // our 64 bit 44k1 stereo 'double-precision' _bitsPerSample = 32; _ok = true; } _blockAlign = (ushort)((NumChannels * _bitsPerSample) >> 3); _byteRate = (uint)(_blockAlign * SampleRate); return; } // Read 'RIFF' (WAV) or 'FORM' (AIFF) tag /////////////////////////////////////////////// char[] hdr = _rdr.ReadChars(4); _riff = new string(hdr); if (_riff != "RIFF" && _riff != "FORM") { if (hdr.Length == 0) { throw (new Exception("File could not be read: no data.")); } string x = ""; for (int j = 0; j < hdr.Length; j++) { x += String.Format("{0:X} ", (int)hdr[j]); } throw (new Exception(String.Format("File is not WAV: no 'RIFF' tag found, instead '{0}'.", x))); } // File length int fileLen = _rdr.ReadInt32(); _length = (uint)fileLen; // Read Wave ////////////////////////////////////////////// _wave = new string(_rdr.ReadChars(4)); if (_wave != "WAVE" && _wave != "AIFF") { throw (new Exception(String.Format("File is not WAV: no 'WAVE' tag found, instead {0}", _wave))); } if (_wave == "AIFF") { // The whole file is big-endian, including lengths in the header BigEndian = true; _length = (uint)System.Net.IPAddress.NetworkToHostOrder(fileLen); } // Read Format //////////////////////////////////////////// _format = new string(_rdr.ReadChars(4)); // WMP11-ripped WAV files begin with 'LIST' metadata - even before the format tag. // AIFF files could have this too (haven't seen it yet). // Skip any chunks up to the format header. while (_format.Length > 0 && _format != "fmt " && _format != "COMM") { int chunkSize = _rdr.ReadInt32(); if (BigEndian) { chunkSize = System.Net.IPAddress.NetworkToHostOrder(chunkSize); } Trace.WriteLine("Skipping {0} ({1} bytes)", _format, chunkSize); _rdr.ReadBytes(chunkSize); _format = new string(_rdr.ReadChars(4)); } if (_format == "fmt ") { // WAV file-format chunk _size = _rdr.ReadUInt32(); if (_size < 16) { throw (new Exception("File could not be read: don't know how to read 'fmt' size " + _size)); } _audioFormat = (WaveFormat)_rdr.ReadUInt16(); if (_audioFormat == WaveFormat.PCM || _audioFormat == WaveFormat.ADPCM || _audioFormat == WaveFormat.IEEE_FLOAT || _audioFormat == WaveFormat.INTERNAL_DOUBLE || _audioFormat == WaveFormat.EXTENSIBLE) { // // WAVEFORMATEX wFormatTag 2 bytes NumChannels = _rdr.ReadUInt16(); // WAVEFORMATEX nChannels 2 SampleRate = _rdr.ReadUInt32(); // WAVEFORMATEX nSamplesPerSec 4 _byteRate = _rdr.ReadUInt32(); // WAVEFORMATEX nAvgBytesPerSec 4 _blockAlign = _rdr.ReadUInt16(); // WAVEFORMATEX nBlockAlign 2 (channels * bitspersample / 8) _bitsPerSample = _rdr.ReadUInt16(); // WAVEFORMATEX wBitsPerSample 2 (the *container* size) if (_size > 16) { uint skip = 16; if (_audioFormat == WaveFormat.EXTENSIBLE) { UInt16 kip = _rdr.ReadUInt16(); UInt16 union = _rdr.ReadUInt16(); // the Samples union, wdc _channelMask = _rdr.ReadUInt32(); // channel mask // then the GUID, 16 bytes _formatEx = new WaveFormatEx(_rdr.ReadBytes(16)); skip = 40; if (_formatEx.Equals(WaveFormatEx.PCM) || _formatEx.Equals(WaveFormatEx.AMBISONIC_B_FORMAT_PCM)) { _audioFormat = WaveFormat.PCM; } else if (_formatEx.Equals(WaveFormatEx.IEEE_FLOAT) || _formatEx.Equals(WaveFormatEx.AMBISONIC_B_FORMAT_IEEE_FLOAT)) { _audioFormat = WaveFormat.IEEE_FLOAT; } } // Read and discard the rest of the 'fmt' structure _rdr.ReadBytes((int)(_size - skip)); } } else { throw (new Exception("File could not be read: don't know how to read audio format " + _audioFormat)); } } else if (_format == "COMM") { // AIFF file-format chunk _size = (uint)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt32()); if (_size < 18) { throw (new Exception("File could not be read: don't know how to read 'COMM' size " + _size)); } _audioFormat = WaveFormat.PCM; NumChannels = (ushort)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt16()); uint numFrames = (uint)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt32()); // number of sample frames _bitsPerSample = (ushort)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt16()); // SampleRate is 10-byte IEEE_extended format. Don't bother converting that // properly, just check for good known values (yuk!) byte[] ext = _rdr.ReadBytes(10); if (ext[0] == 64 && ext[1] == 14 && ext[2] == 172 && ext[3] == 68) { SampleRate = 44100; } else if (ext[0] == 64 && ext[1] == 14 && ext[2] == 187 && ext[3] == 128) { SampleRate = 48000; } else if (ext[0] == 64 && ext[1] == 15 && ext[2] == 187 && ext[3] == 128) { SampleRate = 96000; } else { throw (new Exception("File could not be read: don't know how to interpret sample rate.")); } _blockAlign = (ushort)((NumChannels * _bitsPerSample) / 8); _byteRate = (uint)(_blockAlign * SampleRate); if (_size > 18) { // Read and discard the rest of the 'fmt' structure _rdr.ReadBytes((int)(_size - 18)); } } else { throw (new Exception(String.Format("File could not be read: no 'fmt' tag found, instead {0}", _format))); } // Read Data /////////////////////////////////////////////// _data = new string(_rdr.ReadChars(4)); while (_data.Length > 0 && _data != "data" && _data != "SSND") { // Not a data chunk, ignore int miscSize = _rdr.ReadInt32(); if (BigEndian) { miscSize = System.Net.IPAddress.NetworkToHostOrder(miscSize); } _rdr.ReadBytes(miscSize); _data = new string(_rdr.ReadChars(4)); } // Read the data size if (BigEndian) { _dataSize = (uint)System.Net.IPAddress.NetworkToHostOrder(_rdr.ReadInt32()); } else { _dataSize = _rdr.ReadUInt32(); } // See if we can read this if (NumChannels > 0) { switch (_audioFormat) { case WaveFormat.PCM: case WaveFormat.EXTENSIBLE: switch (_bitsPerSample) { case 8: case 16: case 24: case 32: _ok = true; break; default: break; } break; case WaveFormat.IEEE_FLOAT: switch (_bitsPerSample) { case 32: case 64: _ok = true; break; } break; case WaveFormat.INTERNAL_DOUBLE: switch (_bitsPerSample) { case 64: _ok = true; break; } break; default: break; } } if (_ok) { _max = (uint)((_dataSize / (_bitsPerSample / 8)) / NumChannels); if (_dataSize == 4294967292) { Trace.WriteLine("Wave file: unknown size from header"); _max = uint.MaxValue; } if (_max == 0) { Trace.WriteLine("Wave file: zero size from header"); _max = uint.MaxValue; } } }