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