context: using System;
  using System.Diagnostics;
  using System.IO;
  using System.Runtime.InteropServices;
  using xServer.Core.NAudio.Utils;
  
  namespace xServer.Core.NAudio.Wave.WaveFormats
  {
      /// <summary>
      /// Represents a Wave file format
      /// </summary>
      [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=2)]
      public class WaveFormat
      {
          /// <summary>format type</summary>
          protected WaveFormatEncoding waveFormatTag;
          /// <summary>number of channels</summary>
          protected short channels;
          /// <summary>sample rate</summary>
          protected int sampleRate;
          /// <summary>for buffer estimation</summary>
          protected int averageBytesPerSecond;
          /// <summary>block size of data</summary>
          protected short blockAlign;
          /// <summary>number of bits per sample of mono data</summary>
          protected short bitsPerSample;
          /// <summary>number of following bytes</summary>
          protected short extraSize;
  
          /// <summary>
          /// Creates a new PCM 44.1Khz stereo 16 bit format
          /// </summary>
          public WaveFormat() : this(44100,16,2)
          {
  
          }
          
          /// <summary>
          /// Creates a new 16 bit wave format with the specified sample
          /// rate and channel count
          /// </summary>
          /// <param name="sampleRate">Sample Rate</param>
          /// <param name="channels">Number of channels</param>
          public WaveFormat(int sampleRate, int channels)
              : this(sampleRate, 16, channels)
          {
          }
  
          /// <summary>
          /// Gets the size of a wave buffer equivalent to the latency in milliseconds.
          /// </summary>
          /// <param name="milliseconds">The milliseconds.</param>
          /// <returns></returns>
          public int ConvertLatencyToByteSize(int milliseconds)
          {
              int bytes = (int) ((AverageBytesPerSecond/1000.0)*milliseconds);
              if ((bytes%BlockAlign) != 0)
              {
                  // Return the upper BlockAligned
                  bytes = bytes + BlockAlign - (bytes % BlockAlign);
              }
              return bytes;
          }
  
          /// <summary>
          /// Creates a WaveFormat with custom members
          /// </summary>
          /// <param name="tag">The encoding</param>
          /// <param name="sampleRate">Sample Rate</param>
          /// <param name="channels">Number of channels</param>
          /// <param name="averageBytesPerSecond">Average Bytes Per Second</param>
          /// <param name="blockAlign">Block Align</param>
          /// <param name="bitsPerSample">Bits Per Sample</param>
          /// <returns></returns>
          public static WaveFormat CreateCustomFormat(WaveFormatEncoding tag, int sampleRate, int channels, int averageBytesPerSecond, int blockAlign, int bitsPerSample)
          {
              WaveFormat waveFormat = new WaveFormat();
              waveFormat.waveFormatTag = tag;
              waveFormat.channels = (short)channels;
              waveFormat.sampleRate = sampleRate;
              waveFormat.averageBytesPerSecond = averageBytesPerSecond;
              waveFormat.blockAlign = (short)blockAlign;
              waveFormat.bitsPerSample = (short)bitsPerSample;
              waveFormat.extraSize = 0;
              return waveFormat;
          }
  
          /// <summary>
          /// Creates an A-law wave format
          /// </summary>
          /// <param name="sampleRate">Sample Rate</param>
          /// <param name="channels">Number of Channels</param>
          /// <returns>Wave Format</returns>
          public static WaveFormat CreateALawFormat(int sampleRate, int channels)
          {
              return CreateCustomFormat(WaveFormatEncoding.ALaw, sampleRate, channels, sampleRate * channels, channels, 8);
          }
  
          /// <summary>
          /// Creates a Mu-law wave format
          /// </summary>
          /// <param name="sampleRate">Sample Rate</param>
          /// <param name="channels">Number of Channels</param>
          /// <returns>Wave Format</returns>
          public static WaveFormat CreateMuLawFormat(int sampleRate, int channels)
          {
              return CreateCustomFormat(WaveFormatEncoding.MuLaw, sampleRate, channels, sampleRate * channels, channels, 8);
          }
  
          /// <summary>
          /// Creates a new PCM format with the specified sample rate, bit depth and channels
          /// </summary>
          public WaveFormat(int rate, int bits, int channels)
          {
              if (channels < 1)
              {
                  throw new ArgumentOutOfRangeException("channels", "Channels must be 1 or greater");
              }
              // minimum 16 bytes, sometimes 18 for PCM
              this.waveFormatTag = WaveFormatEncoding.Pcm;
              this.channels = (short)channels;
              this.sampleRate = rate;
              this.bitsPerSample = (short)bits;
              this.extraSize = 0;
                     
              this.blockAlign = (short)(channels * (bits / 8));
              this.averageBytesPerSecond = this.sampleRate * this.blockAlign;
          }
  
          /// <summary>
          /// Creates a new 32 bit IEEE floating point wave format
          /// </summary>
          /// <param name="sampleRate">sample rate</param>
          /// <param name="channels">number of channels</param>
          public static WaveFormat CreateIeeeFloatWaveFormat(int sampleRate, int channels)
          {
              WaveFormat wf = new WaveFormat();
              wf.waveFormatTag = WaveFormatEncoding.IeeeFloat;
              wf.channels = (short)channels;
              wf.bitsPerSample = 32;
              wf.sampleRate = sampleRate;
              wf.blockAlign = (short) (4*channels);
              wf.averageBytesPerSecond = sampleRate * wf.blockAlign;
              wf.extraSize = 0;
              return wf;
          }
  
          /// <summary>
          /// Helper function to retrieve a WaveFormat structure from a pointer
          /// </summary>
          /// <param name="pointer">WaveFormat structure</param>
          /// <returns></returns>
          /*public static WaveFormat MarshalFromPtr(IntPtr pointer)
          {
              var waveFormat = MarshalHelpers.PtrToStructure<WaveFormat>(pointer);
              switch (waveFormat.Encoding)
              {
                  case WaveFormatEncoding.Pcm:
                      // can't rely on extra size even being there for PCM so blank it to avoid reading
                      // corrupt data
                      waveFormat.extraSize = 0;
                      break;
                  case WaveFormatEncoding.Extensible:
                      waveFormat = MarshalHelpers.PtrToStructure<WaveFormatExtensible>(pointer);
                      break;
                  case WaveFormatEncoding.Adpcm:
                      waveFormat = MarshalHelpers.PtrToStructure<AdpcmWaveFormat>(pointer);
                      break;
                  case WaveFormatEncoding.Gsm610:
                      waveFormat = MarshalHelpers.PtrToStructure<Gsm610WaveFormat>(pointer);
                      break;
                  default:
                      if (waveFormat.ExtraSize > 0)
                      {
                          waveFormat = MarshalHelpers.PtrToStructure<WaveFormatExtraData>(pointer);
                      }
                      break;
              }
              return waveFormat;            
          }*/
  
          /// <summary>
          /// Helper function to marshal WaveFormat to an IntPtr
          /// </summary>
          /// <param name="format">WaveFormat</param>
          /// <returns>IntPtr to WaveFormat structure (needs to be freed by callee)</returns>
          public static IntPtr MarshalToPtr(WaveFormat format)
          {
              int formatSize = Marshal.SizeOf(format);
              IntPtr formatPointer = Marshal.AllocHGlobal(formatSize);
              Marshal.StructureToPtr(format, formatPointer, false);            
              return formatPointer;
          }
  
          /// <summary>
          /// Reads in a WaveFormat (with extra data) from a fmt chunk (chunk identifier and
          /// length should already have been read)
          /// </summary>
          /// <param name="br">Binary reader</param>
          /// <param name="formatChunkLength">Format chunk length</param>
          /// <returns>A WaveFormatExtraData</returns>
          public static WaveFormat FromFormatChunk(BinaryReader br, int formatChunkLength)
          {
              var waveFormat = new WaveFormatExtraData();
              waveFormat.ReadWaveFormat(br, formatChunkLength);
              waveFormat.ReadExtraData(br);
              return waveFormat;
          }
  
          private void ReadWaveFormat(BinaryReader br, int formatChunkLength)
          {
              if (formatChunkLength < 16)
                  throw new InvalidDataException("Invalid WaveFormat Structure");
              this.waveFormatTag = (WaveFormatEncoding)br.ReadUInt16();
              this.channels = br.ReadInt16();
  
gt:             this.sampleRate = br.ReadInt32();