#region CPL License /* Nuclex Framework Copyright (C) 2002-2009 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; namespace Nuclex.Audio.Formats.Wave { /// Carries the informations stored in a wave format chunk [StructLayout(LayoutKind.Sequential)] public struct WaveFormat { /// FormatTag specifier for normal, uncompressed PCM audio public const ushort WAVEFORMATPCM = 1; /// Size of the WaveFormat structure /// /// The chunk can change size to encompass additional data that may be added /// to the wave format by Microsoft or whoever is responsible for the wave file /// format. The number of bytes on disk will be this size plus 8 additional /// bytes from the chunk's header. /// public const int Size = 14; /// Signature by which this chunk can be recognized public readonly static byte[] Signature = new byte[] { 0x66, 0x6D, 0x74, 0x20 }; /// Wave format, mainly used to indicate compression scheme public ushort FormatTag; /// Number of audio channels in the file public ushort ChannelCount; /// Playback sample frames per second public uint SamplesPerSecond; /// Average number of bytes per second when playing back the file /// /// /// This value should include padding and alignment. It is intended for systems /// to estimate whether they can play back the audio file without any skips or /// pauses on their hardware. /// /// /// Can be calculated using the formula SamplesPerSecond * BlockAlignment /// rounded up to the nearest whole number. /// /// public uint AverageBytesPerSecond; /// Size of a single sample frame /// /// /// A sample frame is a single sample for each channels. Audio data in a wave file /// is stored in interleaved form, eg. the first frame contains sample 0 for all /// channels, the next frame then contains sample 1 for all channels. /// /// /// On platforms with a byte size of 8 bits, the block alignment can be calculated /// using the formular ChannelCount * (BitsPerSample / 8) /// /// public ushort BlockAlignment; /// Reads the WaveFormat information structure from a binary stream /// Reader from which to read the WaveFormat structure /// Wave format structure to be written to the file /// The total size of the structure as indicated in the size field public static int Load(BinaryReader reader, out WaveFormat waveFormat) { byte[] actualSignature = reader.ReadBytes(4); bool signatureIsValid = (actualSignature[0] == WaveFormat.Signature[0]) && // 'f' (actualSignature[1] == WaveFormat.Signature[1]) && // 'm' (actualSignature[2] == WaveFormat.Signature[2]) && // 't' (actualSignature[3] == WaveFormat.Signature[3]); // ' ' if(!signatureIsValid) { throw new IOException( "Offset mismatch or binary data does not contain a wave format structure" ); } // Obtain the total length of the chunk // The old style format info for uncompressed audio was always 16 bytes long int chunkLength = reader.ReadInt32(); // Read the remainder of the wave format info structure waveFormat = new WaveFormat(); waveFormat.FormatTag = reader.ReadUInt16(); waveFormat.ChannelCount = reader.ReadUInt16(); waveFormat.SamplesPerSecond = reader.ReadUInt32(); waveFormat.AverageBytesPerSecond = reader.ReadUInt32(); waveFormat.BlockAlignment = reader.ReadUInt16(); // Done, return the chunk's indicated length, the caller might be interested // in this number to either skip additional data or process it return chunkLength; } /// Saves the WaveFormat information structure into a binary stream /// Writer through which to write the WaveFormat structure /// Wave format structure to be written to the file /// /// The default header has a total size of 24 bytes, 16 bytes are taken up by /// the actual chunk and 8 additional bytes by the header that specifies the /// chunk's signature and size. /// public static void Save(BinaryWriter writer, ref WaveFormat waveFormat) { Save(writer, WaveFormat.Size, ref waveFormat); } /// Saves the WaveFormat information structure into a binary stream /// Writer through which to write the WaveFormat structure /// Total size of the format info chunk /// Wave format structure to be written to the file /// /// Note that this writes chunkSize + 8 bytes. The additional 8 bytes result from /// the chunk's header that provides the chunk signature and size you indicate. /// public static void Save(BinaryWriter writer, int chunkSize, ref WaveFormat waveFormat) { writer.Write(Signature); // 'f', 'm', 't', ' ' writer.Write((Int32)chunkSize); writer.Write((UInt16)waveFormat.FormatTag); writer.Write((UInt16)waveFormat.ChannelCount); writer.Write((UInt32)waveFormat.SamplesPerSecond); writer.Write((UInt32)waveFormat.AverageBytesPerSecond); writer.Write((UInt16)waveFormat.BlockAlignment); } } } // namespace Nuclex.Audio.Formats.Wave