using System;
using System.Collections.Generic;
using System.IO;
// This file is part of the ANX.Framework and originally taken from
// the AC.AL OpenAL library, released under the MIT License.
// For details see: http://acal.codeplex.com/license
namespace WaveUtils
{
///
/// This class contains all the loading process of a wave file.
///
/// http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html
///
/// Chunk information: http://www.sonicspot.com/guide/wavefiles.html
///
/// Audio Codecs: http://wiki.multimedia.cx/index.php?title=Category:Audio_Codecs
///
/// http://netghost.narod.ru/gff/vendspec/micriff/ms_riff.txt
///
/// Most interesting file about many formats:
/// http://icculus.org/SDL_sound/downloads/external_documentation/wavecomp.htm
///
/// http://sharkysoft.com/archive/lava/docs/javadocs/lava/riff/wave/doc-files/riffwave-content.htm
///
public static class WaveFile
{
#region LoadData
public static WaveInfo LoadData(Stream stream, bool rememberUnloadedChunks = false)
{
WaveInfo result = new WaveInfo();
using (BinaryReader reader = new BinaryReader(stream))
{
if (CheckHeader(reader) == false)
{
throw new FormatException("The provided stream is not a valid WAVE file. Unable to load!");
}
#region Read Chunks
while (stream.Position < stream.Length - 8)
{
string identifier = new string(reader.ReadChars(4));
int chunkLength = reader.ReadInt32();
if (stream.Position + chunkLength > stream.Length)
{
break;
}
int startPosition = (int)reader.BaseStream.Position;
switch (identifier.ToLower())
{
case "fmt ":
{
#region Load fmt chunk
result.WaveFormat = (WaveFormat)reader.ReadInt16();
result.Channels = reader.ReadInt16();
result.SampleRate = reader.ReadInt32();
int avgBytesPerSec = reader.ReadInt32();
result.BlockAlign = reader.ReadInt16();
result.BitsPerSample = reader.ReadInt16();
if (chunkLength > 16)
{
short extensionSize = reader.ReadInt16();
if (chunkLength > 18)
{
result.ExtSamplesPerBlock = reader.ReadInt16();
int speakerPositionMask = reader.ReadInt32();
WaveFormat extFormat = (WaveFormat)reader.ReadInt16();
if (result.WaveFormat < 0)
{
result.WaveFormat = extFormat;
}
byte[] subFormat = reader.ReadBytes(14);
}
}
result.ALFormat = (result.Channels == 1 ?
(result.BitsPerSample == 8 ?
ALFormat.Mono8 :
ALFormat.Mono16) :
(result.BitsPerSample == 8 ?
ALFormat.Stereo8 :
ALFormat.Stereo16));
#endregion
}
break;
case "fact":
{
#region Load fact chunk
// per channel
int numberOfSamples = reader.ReadInt32();
// TODO: more
#endregion
}
break;
case "data":
result.Data = reader.ReadBytes(chunkLength);
break;
default:
if (rememberUnloadedChunks)
{
var value = new KeyValuePair(identifier, reader.ReadBytes(chunkLength));
result.UnloadedChunks.Add(value);
}
break;
}
// If some chunks are incorrect in data length, we ensure that we
// end up in the right position.
int lengthRead = (int)reader.BaseStream.Position - startPosition;
if (lengthRead != chunkLength)
{
reader.BaseStream.Seek(chunkLength - lengthRead,
SeekOrigin.Current);
}
}
#endregion
}
if (result.Data == null)
{
//Logger.Error("There was no data chunk available. Unable to load!");
return null;
}
return result;
}
#endregion
#region CheckHeader
private static bool CheckHeader(BinaryReader reader)
{
string RIFFmagic = new string(reader.ReadChars(4));
if(RIFFmagic != "RIFF")
{
return false;
}
// filesize
reader.ReadInt32();
string identifierWAVE = new string(reader.ReadChars(4));
if(identifierWAVE != "WAVE")
{
return false;
}
return true;
}
#endregion
}
}