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;
}
}
}