Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AudioEmitterSoundController.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.Collections.Generic;
5 using System.Diagnostics;
6 
7 using SiliconStudio.Core.Extensions;
8 using SiliconStudio.Core.Mathematics;
9 using SiliconStudio.Paradox.Audio;
10 
11 using System.Linq;
12 
13 namespace SiliconStudio.Paradox.Engine
14 {
15  /// <summary>
16  /// This class is used to control a <see cref="SiliconStudio.Paradox.Audio.SoundEffect"/> associated to a <see cref="AudioEmitterComponent"/>.
17  /// </summary>
18  /// <remarks>
19  /// <para>
20  /// Instances of this class can not be directly created by the user, but need to queried from an <see cref="AudioEmitterComponent"/>
21  /// instance using the <see cref="AudioEmitterComponent.GetSoundEffectController"/> function.
22  /// </para>
23  /// <para>
24  /// An instance <see cref="AudioEmitterSoundController"/> is not valid anymore if any of those situations arrives:
25  /// <list type="bullet">
26  /// <item><description>The underlying <see cref="SoundEffect"/> is disposed.</description></item>
27  /// <item><description>The <see cref="AudioEmitterComponent"/> is detached from its entity.</description></item>
28  /// <item><description>The entity to which it is attached is removed from the Entity System.</description></item>
29  /// </list>
30  /// </para>
31  /// </remarks>
32  [DebuggerDisplay("Controller for {soundEffect.Name}")]
34  {
35  /// <summary>
36  /// The underlying <see cref="SoundEffect"/>
37  /// </summary>
38  private readonly SoundEffect soundEffect;
39 
40  /// <summary>
41  /// The parent <see cref="AudioEmitterComponent"/> to which to controller is associated.
42  /// </summary>
43  private readonly AudioEmitterComponent parent;
44 
45  /// <summary>
46  /// The instances of <see cref="soundEffect"/> currently created by this controller (one for each listener).
47  /// </summary>
48  private readonly HashSet<SoundEffectInstance> associatedSoundEffectInstances = new HashSet<SoundEffectInstance>();
49 
50  /// <summary>
51  /// Created a new <see cref="AudioEmitterSoundController"/> instance.
52  /// </summary>
53  /// <param name="parent">The parent AudioEmitterComponent to which the controller is associated.</param>
54  /// <param name="soundEffect">The underlying SoundEffect to be controlled</param>
55  /// <remarks>A <see cref="SoundEffect"/> can be associated to several controllers.</remarks>
57  {
58  if(soundEffect == null)
59  throw new ArgumentNullException("soundEffect");
60 
61  this.soundEffect = soundEffect;
62  this.parent = parent;
63 
64  Volume = 1;
65  }
66 
67  /// <summary>
68  /// Create an new instance of underlying sound, and register it in the controller's sound instance list.
69  /// </summary>
70  /// <returns>The new sound effect instance created</returns>
71  internal SoundEffectInstance CreateSoundInstance()
72  {
73  var newInstance = soundEffect.CreateInstance();
74 
75  associatedSoundEffectInstances.Add(newInstance);
76 
77  return newInstance;
78  }
79 
80  /// <summary>
81  /// Dispose and sound instance and removes it from the controller sound instance list.
82  /// </summary>
83  /// <param name="soundInstance">Sound instance to destroy</param>
84  internal void DestroySoundInstance(SoundEffectInstance soundInstance)
85  {
86  soundInstance.Dispose();
87  associatedSoundEffectInstances.Remove(soundInstance);
88  }
89 
90  /// <summary>
91  /// Dispose and removes all the controller sound instances.
92  /// </summary>
93  internal void DestroyAllSoundInstances()
94  {
95  foreach (var instance in associatedSoundEffectInstances)
96  {
97  instance.Dispose();
98  }
99  associatedSoundEffectInstances.Clear();
100  }
101 
102  private SoundPlayState playState;
103  public SoundPlayState PlayState
104  {
105  get
106  {
107  // force the play status to 'stopped' if there is no listeners.
108  if (!associatedSoundEffectInstances.Any())
109  return SoundPlayState.Stopped;
110 
111  // return the controller playStatus if not started playing.
112  if (playState != SoundPlayState.Playing || ShouldBePlayed)
113  return playState;
114 
115  // returns the playStatus of the underlying instances if controller is playing
116 
117  // A desynchronization between instances' playState can appear due to asynchronous callbacks
118  // setting the state of the sound to Stopped when reaching the end of the track.
119  // For coherency, we consider a controller as stopped only when all its instances are stopped.
120  // (if not the case, a play call to a stopped controller would restart only some of the underlying instances)
121  if(associatedSoundEffectInstances.Any(x=>x.PlayState == SoundPlayState.Playing))
122  return SoundPlayState.Playing;
123 
124  return playState = SoundPlayState.Stopped;
125  }
126  }
127 
128  private bool isLooped;
129  public bool IsLooped
130  {
131  get
132  {
133  return isLooped;
134  }
135  set
136  {
137  foreach (var instance in associatedSoundEffectInstances)
138  {
139  instance.IsLooped = value;
140  }
141  isLooped = value;
142  }
143  }
144 
145  /// <summary>
146  /// Indicate the <see cref="AudioListenerProcessor"/> if the controller's sound instances need to be played.
147  /// This variable is need because <see cref="Play"/> is asynchronous and actually starts playing only on next system update.
148  /// </summary>
149  internal bool ShouldBePlayed;
150 
151  public void Play()
152  {
153  playState = SoundPlayState.Playing;
154 
155  // Controller play function is asynchronous.
156  // underlying sound instances actually start playing only after the next system update.
157  // Such a asynchronous behavior is required in order to be able to update the associated AudioEmitter
158  // and apply localization to the sound before starting to play.
159 
160  parent.ShouldBeProcessed = true; // tells the EmitterProcessor to update to AudioEmiter values.
161  ShouldBePlayed = true; // tells the EmitterProcessor to start playing the underlying instances.
162  }
163 
164  public void Pause()
165  {
166  if (PlayState != SoundPlayState.Playing)
167  return;
168 
169  playState = SoundPlayState.Paused;
170 
171  foreach (var instance in associatedSoundEffectInstances)
172  {
173  instance.Pause();
174  }
175  ShouldBePlayed = false;
176  }
177 
178  public void Stop()
179  {
180  playState = SoundPlayState.Stopped;
181 
182  foreach (var instance in associatedSoundEffectInstances)
183  {
184  instance.Stop();
185  }
186  ShouldBePlayed = false;
187  }
188 
189  internal bool ShouldExitLoop;
190  public void ExitLoop()
191  {
192  if (ShouldBePlayed)
193  ShouldExitLoop = true;
194 
195  foreach (var instance in associatedSoundEffectInstances)
196  {
197  instance.ExitLoop();
198  }
199  }
200 
201  private float volume;
202  public float Volume
203  {
204  get
205  {
206  return volume;
207  }
208  set
209  {
210  volume = MathUtil.Clamp(value, 0, 1);
211 
212  foreach (var instance in associatedSoundEffectInstances)
213  {
214  instance.Volume = volume;
215  }
216  }
217  }
218  }
219 }
This class is used to control a SiliconStudio.Paradox.Audio.SoundEffect associated to a AudioEmitterC...
This class provides a loaded sound resource which is localizable in the 3D scene. ...
Definition: SoundEffect.cs:46
SoundPlayState
Current state (playing, paused, or stopped) of a sound implementing the IPlayableSound interface...
void ExitLoop()
Stop looping. That is, do not start over at the end of the current loop, continue to play until the e...
Interface for a playable sound. A playable sound can loop (ref IsLooped), be played (ref Play)...
void Stop()
Stop playing the sound immediately and reset the sound to the beginning of the track.
Component representing an audio emitter.
Instance of a SoundEffect sound which can be independently localized and played.