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);
}
ReadUInt32(reader, endianness); //uint headerCkSize
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);
}
ReadUInt32(reader, endianness); // uint dwAvgBytesPerSec =
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);
}
ReadUInt16(reader, endianness); // ushort cbSize =
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);
} 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 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);
} else {
return new WaveData(format, dataBytes);
}
}
}
}