using System; using System.IO; using ANX.Framework.NonXNA; using OpenTK.Audio; // 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 ANX.SoundSystem.OpenAL { /// /// 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 /// /// Load all information from a wave file. /// /// The stream containing the wave file data. public static WaveInfo LoadData(Stream stream) { 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.OpenALFormat = (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; } // 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; } ConvertFormat(result); 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 #region ConvertFormat private static void ConvertFormat(WaveInfo info) { switch (info.WaveFormat) { case WaveFormat.PCM: #region Convert 32 to 16 bps (TODO) //if (info.BitsPerSample == 32) //{ // BinaryReader sourceReader = // new BinaryReader(new MemoryStream(info.Data)); // MemoryStream destStream = new MemoryStream(); // BinaryWriter destWriter = new BinaryWriter(destStream); // int length = info.Data.Length / 4; // for (int index = 0; index < length; index++) // { // int value = sourceReader.ReadInt32(); // destWriter.Write((short)(value / 2)); // } // sourceReader.Close(); // destWriter.Close(); // info.Data = destStream.ToArray(); // destStream.Dispose(); //} #endregion break; case WaveFormat.ALAW: ALaw.ConvertToPcm(info); break; case WaveFormat.MULAW: MuLaw.ConvertToPcm(info); break; case WaveFormat.IEEE_FLOAT: { #region Convert float to pcm bool is64BitFloat = info.BitsPerSample == 64; BinaryReader sourceReader = new BinaryReader(new MemoryStream(info.Data)); MemoryStream destStream = new MemoryStream(); BinaryWriter destWriter = new BinaryWriter(destStream); int length = info.Data.Length / (is64BitFloat ? 8 : 4); for (int index = 0; index < length; index++) { double value = is64BitFloat ? sourceReader.ReadDouble() : sourceReader.ReadSingle(); destWriter.Write((short)(value * 32767)); } sourceReader.Close(); destWriter.Close(); info.Data = destStream.ToArray(); destStream.Dispose(); #endregion } break; case WaveFormat.MS_ADPCM: MsAdpcm.ConvertToPcm(info); break; default: throw new NotSupportedException("The WAVE format " + info.WaveFormat + " is not supported yet. Unable to load!"); } } #endregion } }