Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
SoundMusic.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 using System;
4 using System.IO;
5 using System.Threading;
6 using SiliconStudio.Core.Serialization;
7 
8 namespace SiliconStudio.Paradox.Audio
9 {
10  /// <summary>
11  /// This class provides a sound resource which is playable but not localizable.
12  /// <para>
13  /// SoundMusics are usually "long" sounds that need neither to be localized nor to be played with low latency.
14  /// Classical examples are background music or explanations. SoundMusics are progressively streamed to minimize memory usage.
15  /// The user can also reduce the assets global size using the MP3 file format to encode SoundMusic.
16  /// If low latency or sound localization is required take a look at the <see cref="SoundEffect"/> or <see cref="DynamicSoundEffectInstance"/> classes.
17  /// </para>
18  /// </summary>
19  /// <remarks>
20  /// <para>Only one instance of SoundMusic can be played at a time. Thus, playing a new SoundMusic stops the previous instance.</para>
21  /// <para>
22  /// You can create a SoundMusics by calling the static <see cref="Load"/> load function.
23  /// Currently only mono and stereo wav and mp3 files are supported for SoundMusics.
24  /// </para>
25  /// </remarks>
26  /// <seealso cref="SoundEffect"/>
27  /// <seealso cref="IPlayableSound"/>
28  /// <seealso cref="DynamicSoundEffectInstance"/>
29  public sealed class SoundMusic : SoundInstanceBase
30  {
31 #if SILICONSTUDIO_PLATFORM_ANDROID
32  internal readonly string FileName;
33  internal readonly long StartPosition;
34  internal readonly long Length;
35 #else
36  internal readonly MemoryStream Stream;
37 #endif
38 
39  /// <summary>
40  /// Create and Load a sound music from an input file.
41  /// </summary>
42  /// <param name="engine">The audio engine in which to load the soundMusic</param>
43  /// <param name="stream">The stream.</param>
44  /// <returns>A new instance of soundMusic ready to be played</returns>
45  /// <exception cref="System.ArgumentNullException">engine
46  /// or
47  /// filename</exception>
48  /// <exception cref="System.ObjectDisposedException">The AudioEngine in which to create the voice is disposed.</exception>
49  /// <exception cref="System.ArgumentException">engine or stream</exception>
50  /// <exception cref="ObjectDisposedException">The AudioEngine in which to create the voice is disposed.</exception>
51  /// <exception cref="ArgumentNullException">File ' + filename + ' does not exist.</exception>
52  /// <remarks>On all platform the wav format is supported.
53  /// For compressed formats, it is the task of the build engine to automatically adapt the original files to the best hardware specific format.</remarks>
54  public static SoundMusic Load(AudioEngine engine, Stream stream)
55  {
56  if(engine == null)
57  throw new ArgumentNullException("engine");
58 
59  if (stream == null)
60  throw new ArgumentNullException("stream");
61 
62  if(engine.IsDisposed)
63  throw new ObjectDisposedException("The AudioEngine in which to create the voice is disposed.");
64 
65  // TODO: Not portable on WindowsStore
66 
67  var ret = new SoundMusic(engine, stream);
68 
69  ret.ResetStateToDefault();
70  ret.Name = "SoundMusic " + soundMusicCreationCount;
71 
72  engine.RegisterSound(ret);
73 
74  Interlocked.Increment(ref soundMusicCreationCount);
75 
76  return ret;
77  }
78 
79  /// <summary>
80  /// The number of SoundMusic Created so far. Used only to give a unique name to the SoundEffect.
81  /// </summary>
82  private static int soundMusicCreationCount;
83 
84  private SoundMusic(AudioEngine engine, Stream stream)
85  : base(engine)
86  {
87 #if SILICONSTUDIO_PLATFORM_ANDROID
88  var virtualStream = stream as VirtualFileStream;
89  if (virtualStream == null)
90  throw new InvalidOperationException("Expecting VirtualFileStream. Music files needs to be stored on the virtual file system in a non-compressed form.");
91 
92  var fileStream = virtualStream.InternalStream as FileStream;
93  if (fileStream == null)
94  throw new InvalidOperationException("Expecting FileStream in VirtualFileStream.InternalStream. Music files needs to be stored on the virtual file system in a non-compressed form.");
95 
96  FileName = fileStream.Name;
97  StartPosition = virtualStream.StartPosition;
98  Length = virtualStream.Length;
99 #else
100  // Make a memory copy of the stream so that the source can be properly disposed
101  var memoryStream = new MemoryStream();
102  stream.CopyTo(memoryStream);
103  Stream = memoryStream;
104  Stream.Position = 0;
105 #endif
106  }
107 
108  private void ResetStateToDefault()
109  {
110  Volume = 1;
111  IsLooped = false;
112  Stop();
113  }
114 
115  private static SoundMusic previousPlayingInstance;
116  private static readonly object PreviousPlayingInstanceLock = new object();
117 
118  internal override void PlayImpl()
119  {
120  AudioEngine.SubmitMusicActionRequest(new SoundMusicActionRequest(this, SoundMusicAction.Play));
121 
122  // Actual Playing is happening during the Audio Engine update
123  // but we can not wait this long to update the PlayState of the currently playing SoundMusic
124  // after this call to Play, PlayState of the previous playing music should directly be set to Stopped
125  // this is why we use here the static field PreviousPlayingInstance
126  lock (PreviousPlayingInstanceLock) // protection again possible future multithreading.
127  {
128  if (previousPlayingInstance != this)
129  {
130  if (previousPlayingInstance != null)
131  previousPlayingInstance.SetStateToStopped();
132 
133  previousPlayingInstance = this;
134  }
135  }
136  }
137 
138  internal override void PauseImpl()
139  {
140  AudioEngine.SubmitMusicActionRequest(new SoundMusicActionRequest(this, SoundMusicAction.Pause));
141  }
142 
143  internal override void StopImpl()
144  {
145  ShouldExitLoop = false;
146 
147  AudioEngine.SubmitMusicActionRequest(new SoundMusicActionRequest(this, SoundMusicAction.Stop));
148  }
149 
150  internal override void UpdateVolume()
151  {
152  AudioEngine.SubmitMusicActionRequest(new SoundMusicActionRequest(this, SoundMusicAction.Volume));
153  }
154 
155  internal override void UpdateLooping()
156  {
157  // Nothing to do.
158  // music looping system is directly based on the value of SoundMusic.IsLooped.
159  }
160 
161  internal void SetStateToStopped()
162  {
163  PlayState = SoundPlayState.Stopped;
164  DataBufferLoaded = false;
165  ShouldExitLoop = false;
166  }
167 
168  internal bool ShouldExitLoop { get; private set; }
169 
170  internal override void ExitLoopImpl()
171  {
172  ShouldExitLoop = true;
173  }
174 
175  internal override void DestroyImpl()
176  {
177  AudioEngine.UnregisterSound(this);
178  // mediaInputStream is disposed by AudioEngine.ProcessPlayerClosed()
179  }
180  }
181 }
bool IsDisposed
Has the component been disposed or not yet.
Base class for sound that creates voices
Represents the audio engine. In current version, the audio engine necessarily creates its context on ...
Definition: AudioEngine.cs:80
This class provides a sound resource which is playable but not localizable.
Definition: SoundMusic.cs:29
static SoundMusic Load(AudioEngine engine, Stream stream)
Create and Load a sound music from an input file.
Definition: SoundMusic.cs:54
A multithreaded wrapper over a Stream, used by the VirtualFileSystem. It also allows restricted acces...