Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
SoundStream.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 using System;
24 using System.Collections.Generic;
25 using System.IO;
26 
27 namespace SiliconStudio.Paradox.Audio.Wave
28 {
29  /// <summary>
30  /// Generic sound input stream supporting WAV (Pcm,Float), ADPCM sound file formats.
31  /// </summary>
32  internal class SoundStream : Stream
33  {
34  private Stream input;
35  private long startPositionOfData;
36  private long length;
37 
38  /// <summary>
39  /// Initializes a new instance of the <see cref="SoundStream"/> class.
40  /// </summary>
41  /// <param name="stream">The sound stream.</param>
42  public SoundStream(Stream stream)
43  {
44  if (stream == null) throw new ArgumentNullException("stream");
45  input = stream;
46  Initialize(stream);
47  }
48 
49  /// <summary>
50  /// Initializes the specified stream.
51  /// </summary>
52  /// <param name="stream">The stream.</param>
53  private unsafe void Initialize(Stream stream)
54  {
55  var parser = new RiffParser(stream);
56 
57  FileFormatName = "Unknown";
58 
59  // Parse Header
60  if (!parser.MoveNext() || parser.Current == null)
61  {
62  ThrowInvalidFileFormat();
63  return;
64  }
65 
66  // Check that WAVE header is present
67  FileFormatName = parser.Current.Type;
68  if (FileFormatName != "WAVE")
69  throw new InvalidOperationException("Unsupported " + FileFormatName + " file format. Only WAVE.");
70 
71  // Parse inside the first chunk
72  parser.Descend();
73 
74  // Get all the chunk
75  var chunks = parser.GetAllChunks();
76 
77  // Get "fmt" chunk
78  var fmtChunk = Chunk(chunks, "fmt ");
79  if (fmtChunk.Size < sizeof(WaveFormat.__PcmNative))
80  ThrowInvalidFileFormat();
81 
82  try
83  {
84  Format = WaveFormat.MarshalFrom(fmtChunk.GetData());
85  }
86  catch (InvalidOperationException ex)
87  {
88  ThrowInvalidFileFormat(ex);
89  }
90 
91  switch (Format.Encoding)
92  {
93  case WaveFormatEncoding.Pcm:
94  case WaveFormatEncoding.IeeeFloat:
95  case WaveFormatEncoding.Extensible:
96  case WaveFormatEncoding.Adpcm:
97  break;
98  default:
99  ThrowInvalidFileFormat();
100  break;
101  }
102 
103  // Check for "data" chunk
104  var dataChunk = Chunk(chunks, "data");
105  startPositionOfData = dataChunk.DataPosition;
106  length = dataChunk.Size;
107 
108  input.Position = startPositionOfData;
109  }
110 
111  protected void ThrowInvalidFileFormat(Exception nestedException = null)
112  {
113  throw new InvalidOperationException("Invalid " + FileFormatName + " file format", nestedException);
114  }
115 
116  /// <summary>
117  /// Gets the wave format of this instance.
118  /// </summary>
119  public WaveFormat Format { get; protected set; }
120 
121  /// <summary>
122  /// When overridden in a derived class, gets a value indicating whether the current stream supports reading.
123  /// </summary>
124  /// <returns>true if the stream supports reading; otherwise, false.
125  /// </returns>
126  public override bool CanRead
127  {
128  get { return true; }
129  }
130 
131  /// <summary>
132  /// When overridden in a derived class, gets a value indicating whether the current stream supports seeking.
133  /// </summary>
134  /// <returns>true if the stream supports seeking; otherwise, false.
135  /// </returns>
136  public override bool CanSeek
137  {
138  get { return true; }
139  }
140 
141  /// <summary>
142  /// When overridden in a derived class, gets a value indicating whether the current stream supports writing.
143  /// </summary>
144  /// <returns>true if the stream supports writing; otherwise, false.
145  /// </returns>
146  public override bool CanWrite
147  {
148  get { return false; }
149  }
150 
151  /// <summary>
152  /// When overridden in a derived class, gets or sets the position within the current stream.
153  /// </summary>
154  /// <returns>
155  /// The current position within the stream.
156  /// </returns>
157  ///
158  /// <exception cref="T:System.IO.IOException">
159  /// An I/O error occurs.
160  /// </exception>
161  ///
162  /// <exception cref="T:System.NotSupportedException">
163  /// The stream does not support seeking.
164  /// </exception>
165  ///
166  /// <exception cref="T:System.ObjectDisposedException">
167  /// Methods were called after the stream was closed.
168  /// </exception>
169  public override long Position
170  {
171  get
172  {
173  return input.Position - startPositionOfData;
174  }
175  set
176  {
177  Seek(value, SeekOrigin.Begin);
178  }
179  }
180 
181  protected override void Dispose(bool disposing)
182  {
183  if (input != null)
184  {
185  input.Dispose();
186  input = null;
187  }
188  base.Dispose(disposing);
189  }
190 
191  protected RiffChunk Chunk(IEnumerable<RiffChunk> chunks, string id)
192  {
193  RiffChunk chunk = null;
194  foreach (var riffChunk in chunks)
195  {
196  if (riffChunk.Type == id)
197  {
198  chunk = riffChunk;
199  break;
200  }
201  }
202  if (chunk == null || chunk.Type != id)
203  throw new InvalidOperationException("Invalid " + FileFormatName + " file format");
204  return chunk;
205  }
206 
207  private string FileFormatName { get; set; }
208 
209  /// <summary>
210  /// When overridden in a derived class, clears all buffers for this stream and causes any buffered data to be written to the underlying device.
211  /// </summary>
212  /// <exception cref="T:System.IO.IOException">
213  /// An I/O error occurs.
214  /// </exception>
215  public override void Flush()
216  {
217  throw new NotSupportedException();
218  }
219 
220  /// <summary>
221  /// When overridden in a derived class, sets the position within the current stream.
222  /// </summary>
223  /// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param>
224  /// <param name="origin">A value of type <see cref="T:System.IO.SeekOrigin"/> indicating the reference point used to obtain the new position.</param>
225  /// <returns>
226  /// The new position within the current stream.
227  /// </returns>
228  /// <exception cref="T:System.IO.IOException">
229  /// An I/O error occurs.
230  /// </exception>
231  ///
232  /// <exception cref="T:System.NotSupportedException">
233  /// The stream does not support seeking, such as if the stream is constructed from a pipe or console output.
234  /// </exception>
235  ///
236  /// <exception cref="T:System.ObjectDisposedException">
237  /// Methods were called after the stream was closed.
238  /// </exception>
239  public override long Seek(long offset, SeekOrigin origin)
240  {
241  var newPosition = input.Position;
242  switch (origin)
243  {
244  case SeekOrigin.Begin:
245  newPosition = startPositionOfData + offset;
246  break;
247  case SeekOrigin.Current:
248  newPosition = input.Position + offset;
249  break;
250  case SeekOrigin.End:
251  newPosition = startPositionOfData + length + offset;
252  break;
253  }
254 
255  if (newPosition < startPositionOfData || newPosition > (startPositionOfData + length))
256  throw new InvalidOperationException("Cannot seek outside the range of this stream");
257 
258  return input.Seek(offset, origin);
259  }
260 
261  /// <summary>
262  /// When overridden in a derived class, sets the length of the current stream.
263  /// </summary>
264  /// <param name="value">The desired length of the current stream in bytes.</param>
265  /// <exception cref="T:System.IO.IOException">
266  /// An I/O error occurs.
267  /// </exception>
268  ///
269  /// <exception cref="T:System.NotSupportedException">
270  /// The stream does not support both writing and seeking, such as if the stream is constructed from a pipe or console output.
271  /// </exception>
272  ///
273  /// <exception cref="T:System.ObjectDisposedException">
274  /// Methods were called after the stream was closed.
275  /// </exception>
276  public override void SetLength(long value)
277  {
278  throw new NotSupportedException();
279  }
280 
281  /// <summary>
282  /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
283  /// </summary>
284  /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between <paramref name="offset"/> and (<paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the current source.</param>
285  /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.</param>
286  /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
287  /// <returns>
288  /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
289  /// </returns>
290  /// <exception cref="T:System.ArgumentException">
291  /// The sum of <paramref name="offset"/> and <paramref name="count"/> is larger than the buffer length.
292  /// </exception>
293  ///
294  /// <exception cref="T:System.ArgumentNullException">
295  /// <paramref name="buffer"/> is null.
296  /// </exception>
297  ///
298  /// <exception cref="T:System.ArgumentOutOfRangeException">
299  /// <paramref name="offset"/> or <paramref name="count"/> is negative.
300  /// </exception>
301  ///
302  /// <exception cref="T:System.IO.IOException">
303  /// An I/O error occurs.
304  /// </exception>
305  ///
306  /// <exception cref="T:System.NotSupportedException">
307  /// The stream does not support reading.
308  /// </exception>
309  ///
310  /// <exception cref="T:System.ObjectDisposedException">
311  /// Methods were called after the stream was closed.
312  /// </exception>
313  public override int Read(byte[] buffer, int offset, int count)
314  {
315  if ((input.Position + count) > (startPositionOfData + length))
316  throw new InvalidOperationException("Cannot read more than the length of the stream");
317  return input.Read(buffer, offset, count);
318  }
319 
320  /// <summary>
321  /// When overridden in a derived class, gets the length in bytes of the stream.
322  /// </summary>
323  /// <returns>
324  /// A long value representing the length of the stream in bytes.
325  /// </returns>
326  ///
327  /// <exception cref="T:System.NotSupportedException">
328  /// A class derived from Stream does not support seeking.
329  /// </exception>
330  ///
331  /// <exception cref="T:System.ObjectDisposedException">
332  /// Methods were called after the stream was closed.
333  /// </exception>
334  public override long Length
335  {
336  get { return length; }
337  }
338 
339  /// <summary>
340  /// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
341  /// </summary>
342  /// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref name="buffer"/> to the current stream.</param>
343  /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes to the current stream.</param>
344  /// <param name="count">The number of bytes to be written to the current stream.</param>
345  /// <exception cref="T:System.ArgumentException">
346  /// The sum of <paramref name="offset"/> and <paramref name="count"/> is greater than the buffer length.
347  /// </exception>
348  ///
349  /// <exception cref="T:System.ArgumentNullException">
350  /// <paramref name="buffer"/> is null.
351  /// </exception>
352  ///
353  /// <exception cref="T:System.ArgumentOutOfRangeException">
354  /// <paramref name="offset"/> or <paramref name="count"/> is negative.
355  /// </exception>
356  ///
357  /// <exception cref="T:System.IO.IOException">
358  /// An I/O error occurs.
359  /// </exception>
360  ///
361  /// <exception cref="T:System.NotSupportedException">
362  /// The stream does not support writing.
363  /// </exception>
364  ///
365  /// <exception cref="T:System.ObjectDisposedException">
366  /// Methods were called after the stream was closed.
367  /// </exception>
368  public override void Write(byte[] buffer, int offset, int count)
369  {
370  throw new NotSupportedException();
371  }
372  }
373 }
_In_ size_t count
Definition: DirectXTexP.h:174