Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
WaveFormat.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 //
4 // Copyright (c) 2010-2013 SharpDX - Alexandre Mutel
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 // THE SOFTWARE.
23 // -----------------------------------------------------------------------------
24 // Original code from NAudio project. http://naudio.codeplex.com/
25 // Greetings to Mark Heath.
26 // -----------------------------------------------------------------------------
27 
28 using System;
29 using System.Globalization;
30 using System.IO;
31 using System.Runtime.InteropServices;
32 
33 using SiliconStudio.Core;
34 
35 namespace SiliconStudio.Paradox.Audio.Wave
36 {
37  /// <summary>
38  /// Represents a Wave file format
39  /// </summary>
40  internal class WaveFormat
41  {
42  /// <summary>format type</summary>
43  protected WaveFormatEncoding waveFormatTag;
44  /// <summary>number of channels</summary>
45  protected short channels;
46  /// <summary>sample rate</summary>
47  protected int sampleRate;
48  /// <summary>for buffer estimation</summary>
49  protected int averageBytesPerSecond;
50  /// <summary>block size of data</summary>
51  protected short blockAlign;
52  /// <summary>number of bits per sample of mono data</summary>
53  protected short bitsPerSample;
54  /// <summary>number of following bytes</summary>
55  protected short extraSize;
56 
57  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
58  internal struct __Native
59  {
60  public __PcmNative pcmWaveFormat;
61  /// <summary>number of following bytes</summary>
62  public short extraSize;
63  // Method to free native struct
64  internal void __MarshalFree()
65  {
66  }
67  }
68 
69  internal void __MarshalFree(ref __Native @ref)
70  {
71  @ref.__MarshalFree();
72  }
73 
74  // Method to marshal from native to managed struct
75  internal void __MarshalFrom(ref __Native @ref)
76  {
77  waveFormatTag = @ref.pcmWaveFormat.waveFormatTag;
78  channels = @ref.pcmWaveFormat.channels;
79  sampleRate = @ref.pcmWaveFormat.sampleRate;
80  averageBytesPerSecond = @ref.pcmWaveFormat.averageBytesPerSecond;
81  blockAlign = @ref.pcmWaveFormat.blockAlign;
82  bitsPerSample = @ref.pcmWaveFormat.bitsPerSample;
83  extraSize = @ref.extraSize;
84  }
85  // Method to marshal from managed struct tot native
86  internal void __MarshalTo(ref __Native @ref)
87  {
88  @ref.pcmWaveFormat.waveFormatTag = waveFormatTag;
89  @ref.pcmWaveFormat.channels = channels;
90  @ref.pcmWaveFormat.sampleRate = sampleRate;
91  @ref.pcmWaveFormat.averageBytesPerSecond = averageBytesPerSecond;
92  @ref.pcmWaveFormat.blockAlign = blockAlign;
93  @ref.pcmWaveFormat.bitsPerSample = bitsPerSample;
94  @ref.extraSize = extraSize;
95  }
96 
97  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
98  internal struct __PcmNative
99  {
100  /// <summary>format type</summary>
101  public WaveFormatEncoding waveFormatTag;
102  /// <summary>number of channels</summary>
103  public short channels;
104  /// <summary>sample rate</summary>
105  public int sampleRate;
106  /// <summary>for buffer estimation</summary>
107  public int averageBytesPerSecond;
108  /// <summary>block size of data</summary>
109  public short blockAlign;
110  /// <summary>number of bits per sample of mono data</summary>
111  public short bitsPerSample;
112 
113  // Method to free native struct
114  internal unsafe void __MarshalFree()
115  {
116  }
117  }
118 
119  internal unsafe void __MarshalFree(ref __PcmNative @ref)
120  {
121  @ref.__MarshalFree();
122  }
123 
124  // Method to marshal from native to managed struct
125  internal void __MarshalFrom(ref __PcmNative @ref)
126  {
127  waveFormatTag = @ref.waveFormatTag;
128  channels = @ref.channels;
129  sampleRate = @ref.sampleRate;
130  averageBytesPerSecond = @ref.averageBytesPerSecond;
131  blockAlign = @ref.blockAlign;
132  bitsPerSample = @ref.bitsPerSample;
133  extraSize = 0;
134  }
135  // Method to marshal from managed struct tot native
136  internal void __MarshalTo(ref __PcmNative @ref)
137  {
138  @ref.waveFormatTag = waveFormatTag;
139  @ref.channels = channels;
140  @ref.sampleRate = sampleRate;
141  @ref.averageBytesPerSecond = averageBytesPerSecond;
142  @ref.blockAlign = blockAlign;
143  @ref.bitsPerSample = bitsPerSample;
144  }
145 
146 
147  /// <summary>
148  /// Creates a new PCM 44.1Khz stereo 16 bit format
149  /// </summary>
150  public WaveFormat()
151  : this(44100, 16, 2)
152  {
153 
154  }
155 
156  /// <summary>
157  /// Creates a new 16 bit wave format with the specified sample
158  /// rate and channel count
159  /// </summary>
160  /// <param name="sampleRate">Sample Rate</param>
161  /// <param name="channels">Number of channels</param>
162  public WaveFormat(int sampleRate, int channels)
163  : this(sampleRate, 16, channels)
164  {
165  }
166 
167  /// <summary>
168  /// Gets the size of a wave buffer equivalent to the latency in milliseconds.
169  /// </summary>
170  /// <param name="milliseconds">The milliseconds.</param>
171  /// <returns></returns>
172  public int ConvertLatencyToByteSize(int milliseconds)
173  {
174  int bytes = (int)((AverageBytesPerSecond / 1000.0) * milliseconds);
175  if ((bytes % BlockAlign) != 0)
176  {
177  // Return the upper BlockAligned
178  bytes = bytes + BlockAlign - (bytes % BlockAlign);
179  }
180  return bytes;
181  }
182 
183  /// <summary>
184  /// Creates a WaveFormat with custom members
185  /// </summary>
186  /// <param name="tag">The encoding</param>
187  /// <param name="sampleRate">Sample Rate</param>
188  /// <param name="channels">Number of channels</param>
189  /// <param name="averageBytesPerSecond">Average Bytes Per Second</param>
190  /// <param name="blockAlign">Block Align</param>
191  /// <param name="bitsPerSample">Bits Per Sample</param>
192  /// <returns></returns>
193  public static WaveFormat CreateCustomFormat(WaveFormatEncoding tag, int sampleRate, int channels, int averageBytesPerSecond, int blockAlign, int bitsPerSample)
194  {
195  var waveFormat = new WaveFormat
196  {
197  waveFormatTag = tag,
198  channels = (short)channels,
199  sampleRate = sampleRate,
200  averageBytesPerSecond = averageBytesPerSecond,
201  blockAlign = (short)blockAlign,
202  bitsPerSample = (short)bitsPerSample,
203  extraSize = 0
204  };
205  return waveFormat;
206  }
207 
208  /// <summary>
209  /// Creates a new PCM format with the specified sample rate, bit depth and channels
210  /// </summary>
211  public WaveFormat(int rate, int bits, int channels)
212  {
213  if (channels < 1)
214  {
215  throw new ArgumentOutOfRangeException("channels", "Channels must be 1 or greater");
216  }
217  // minimum 16 bytes, sometimes 18 for PCM
218  waveFormatTag = bits < 32 ? WaveFormatEncoding.Pcm : WaveFormatEncoding.IeeeFloat;
219  this.channels = (short)channels;
220  sampleRate = rate;
221  bitsPerSample = (short)bits;
222  extraSize = 0;
223 
224  blockAlign = (short)(channels * (bits / 8));
225  averageBytesPerSecond = sampleRate * blockAlign;
226  }
227 
228  /// <summary>
229  /// Creates a new 32 bit IEEE floating point wave format
230  /// </summary>
231  /// <param name="sampleRate">sample rate</param>
232  /// <param name="channels">number of channels</param>
233  public static WaveFormat CreateIeeeFloatWaveFormat(int sampleRate, int channels)
234  {
235  var wf = new WaveFormat
236  {
237  waveFormatTag = WaveFormatEncoding.IeeeFloat,
238  channels = (short)channels,
239  bitsPerSample = 32,
240  sampleRate = sampleRate,
241  blockAlign = (short)(4 * channels)
242  };
243  wf.averageBytesPerSecond = sampleRate * wf.blockAlign;
244  wf.extraSize = 0;
245  return wf;
246  }
247 
248  /// <summary>
249  /// Helper function to retrieve a WaveFormat structure from a pointer
250  /// </summary>
251  /// <param name="rawdata">Buffer to the WaveFormat rawdata</param>
252  /// <returns>WaveFormat structure</returns>
253  public unsafe static WaveFormat MarshalFrom(byte[] rawdata)
254  {
255  fixed (void* pRawData = rawdata)
256  return MarshalFrom((IntPtr)pRawData);
257  }
258 
259  /// <summary>
260  /// Helper function to retrieve a WaveFormat structure from a pointer
261  /// </summary>
262  /// <param name="pointer">Pointer to the WaveFormat rawdata</param>
263  /// <returns>WaveFormat structure</returns>
264  public unsafe static WaveFormat MarshalFrom(IntPtr pointer)
265  {
266  if (pointer == IntPtr.Zero) return null;
267 
268  var pcmWaveFormat = *(__PcmNative*)pointer;
269  var encoding = pcmWaveFormat.waveFormatTag;
270 
271  // Load simple PcmWaveFormat if channels <= 2 and encoding is Pcm, IeeFloat, Wmaudio2, Wmaudio3
272  // See http://msdn.microsoft.com/en-us/library/microsoft.directx_sdk.xaudio2.waveformatex%28v=vs.85%29.aspx
273  if (pcmWaveFormat.channels <= 2 && (encoding == WaveFormatEncoding.Pcm || encoding == WaveFormatEncoding.IeeeFloat))
274  {
275  var waveFormat = new WaveFormat();
276  waveFormat.__MarshalFrom(ref pcmWaveFormat);
277  return waveFormat;
278  }
279 
280  if (encoding == WaveFormatEncoding.Extensible)
281  {
282  var waveFormat = new WaveFormatExtensible();
283  waveFormat.__MarshalFrom(ref *(WaveFormatExtensible.__Native*)pointer);
284  return waveFormat;
285  }
286 
287  if (encoding == WaveFormatEncoding.Adpcm)
288  {
289  var waveFormat = new WaveFormatAdpcm();
290  waveFormat.__MarshalFrom(ref *(WaveFormatAdpcm.__Native*)pointer);
291  return waveFormat;
292  }
293 
294  throw new InvalidOperationException(string.Format("Unsupported WaveFormat [{0}]", encoding));
295  }
296 
297  protected unsafe virtual IntPtr MarshalToPtr()
298  {
299  var result = Marshal.AllocHGlobal(Utilities.SizeOf<__Native>());
300  __MarshalTo(ref *(__Native*)result);
301  return result;
302  }
303 
304  /// <summary>
305  /// Helper function to marshal WaveFormat to an IntPtr
306  /// </summary>
307  /// <param name="format">WaveFormat</param>
308  /// <returns>IntPtr to WaveFormat structure (needs to be freed by callee)</returns>
309  public static IntPtr MarshalToPtr(WaveFormat format)
310  {
311  if (format == null) return IntPtr.Zero;
312  return format.MarshalToPtr();
313  }
314 
315  /// <summary>
316  /// Reads a new WaveFormat object from a stream
317  /// </summary>
318  /// <param name="br">A binary reader that wraps the stream</param>
319  public WaveFormat(BinaryReader br)
320  {
321  int formatChunkLength = br.ReadInt32();
322  if (formatChunkLength < 16)
323  throw new ArgumentException("Invalid WaveFormat Structure");
324  waveFormatTag = (WaveFormatEncoding)br.ReadUInt16();
325  channels = br.ReadInt16();
326  sampleRate = br.ReadInt32();
327  averageBytesPerSecond = br.ReadInt32();
328  blockAlign = br.ReadInt16();
329  bitsPerSample = br.ReadInt16();
330  extraSize = 0;
331  if (formatChunkLength > 16)
332  {
333 
334  extraSize = br.ReadInt16();
335  if (extraSize > formatChunkLength - 18)
336  {
337  //RRL GSM exhibits this bug. Don't throw an exception
338  //throw new ApplicationException("Format chunk length mismatch");
339 
340  extraSize = (short)(formatChunkLength - 18);
341  }
342 
343  // read any extra data
344  // br.ReadBytes(extraSize);
345 
346  }
347  }
348 
349  /// <summary>
350  /// Reports this WaveFormat as a string
351  /// </summary>
352  /// <returns>String describing the wave format</returns>
353  public override string ToString()
354  {
355  switch (waveFormatTag)
356  {
357  case WaveFormatEncoding.Pcm:
358  case WaveFormatEncoding.Extensible:
359  // extensible just has some extra bits after the PCM header
360  return string.Format(CultureInfo.InvariantCulture, "{0} bit PCM: {1}kHz {2} channels",
361  bitsPerSample, sampleRate / 1000, channels);
362  default:
363  return waveFormatTag.ToString();
364  }
365  }
366 
367  /// <summary>
368  /// Compares with another WaveFormat object
369  /// </summary>
370  /// <param name="obj">Object to compare to</param>
371  /// <returns>True if the objects are the same</returns>
372  public override bool Equals(object obj)
373  {
374  if (!(obj is WaveFormat))
375  return false;
376 
377  WaveFormat other = (WaveFormat)obj;
378  return waveFormatTag == other.waveFormatTag &&
379  channels == other.channels &&
380  sampleRate == other.sampleRate &&
381  averageBytesPerSecond == other.averageBytesPerSecond &&
382  blockAlign == other.blockAlign &&
383  bitsPerSample == other.bitsPerSample;
384  }
385 
386  /// <summary>
387  /// Provides a Hashcode for this WaveFormat
388  /// </summary>
389  /// <returns>A hashcode</returns>
390  public override int GetHashCode()
391  {
392  return (int)waveFormatTag ^
393  channels ^
394  sampleRate ^
395  averageBytesPerSecond ^
396  blockAlign ^
397  bitsPerSample;
398  }
399 
400  /// <summary>
401  /// Returns the encoding type used
402  /// </summary>
403  public WaveFormatEncoding Encoding
404  {
405  get
406  {
407  return waveFormatTag;
408  }
409  }
410 
411  /// <summary>
412  /// Returns the number of channels (1=mono,2=stereo etc)
413  /// </summary>
414  public int Channels
415  {
416  get
417  {
418  return channels;
419  }
420  }
421 
422  /// <summary>
423  /// Returns the sample rate (samples per second)
424  /// </summary>
425  public int SampleRate
426  {
427  get
428  {
429  return sampleRate;
430  }
431  }
432 
433  /// <summary>
434  /// Returns the average number of bytes used per second
435  /// </summary>
436  public int AverageBytesPerSecond
437  {
438  get
439  {
440  return averageBytesPerSecond;
441  }
442  }
443 
444  /// <summary>
445  /// Returns the block alignment
446  /// </summary>
447  public int BlockAlign
448  {
449  get
450  {
451  return blockAlign;
452  }
453  }
454 
455  /// <summary>
456  /// Returns the number of bits per sample (usually 16 or 32, sometimes 24 or 8)
457  /// Can be 0 for some codecs
458  /// </summary>
459  public int BitsPerSample
460  {
461  get
462  {
463  return bitsPerSample;
464  }
465  }
466 
467  /// <summary>
468  /// Returns the number of extra bytes used by this waveformat. Often 0,
469  /// except for compressed formats which store extra data after the WAVEFORMATEX header
470  /// </summary>
471  public int ExtraSize
472  {
473  get
474  {
475  return extraSize;
476  }
477  }
478  }
479 }
System.Text.Encoding Encoding
_In_ size_t _In_ size_t _In_ DXGI_FORMAT format
Definition: DirectXTexP.h:175