Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AudioEmitterComponent.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.Collections.Specialized;
6 
7 using SiliconStudio.Core;
8 using SiliconStudio.Core.Serialization.Converters;
9 using SiliconStudio.Paradox.Audio;
10 using SiliconStudio.Paradox.EntityModel;
11 
12 namespace SiliconStudio.Paradox.Engine
13 {
14  /// <summary>
15  /// Component representing an audio emitter.
16  /// </summary>
17  /// <remarks>
18  /// <para>
19  /// Associate this component to an entity to simulate a 3D localized source of sound coming from the entity center.
20  /// Use the component <see cref="DistanceScale"/> and <see cref="DopplerScale"/> properties to attenuate or exaggerate the
21  /// effect the distance sound attenuation and Doppler effect.
22  /// </para>
23  /// <para>
24  /// Several sounds can be associated to a single AudioEmitterComponent.
25  /// Use the functions <see cref="AttachSoundEffect"/> and <see cref="DetachSoundEffect"/> to associate or dissociate a <see cref="SoundEffect"/> to the emitter component.
26  /// Each SoundEffect associated to the emitter component can be controlled (played, paused, stopped, ...) independently for the others.
27  /// Once attached to the emitter component, a SoundEffect is controlled using a <see cref="AudioEmitterSoundController"/>.
28  /// To get the AudioEmitterSoundController associated to a SoundEffect use the <see cref="GetSoundEffectController"/> function.
29  /// </para>
30  /// </remarks>
31  public sealed class AudioEmitterComponent : EntityComponent
32  {
34 
35  /// <summary>
36  /// Dictionary associating each soundEffect to a single soundController.
37  /// The controller a valid as long as the corresponding SoundEffect is present in the dictionary.
38  /// </summary>
39  internal readonly Dictionary<SoundEffect, AudioEmitterSoundController> SoundEffectToController = new Dictionary<SoundEffect, AudioEmitterSoundController>();
40 
41  /// <summary>
42  /// Create an instance of <see cref="AudioEmitterComponent"/> with a list default <see cref="SoundEffect"/> associated.
43  /// </summary>
44  /// <param name="soundEffectToAttach">The SoundEffect to attach to the emitter by default.</param>
45  public AudioEmitterComponent(IEnumerable<SoundEffect> soundEffectToAttach)
46  {
47  DistanceScale = 1;
48  DopplerScale = 1;
49 
50  AttachSoundEffects(soundEffectToAttach);
51  }
52 
53  /// <summary>
54  /// Create an instance of <see cref="AudioEmitterComponent"/> with no default <see cref="SoundEffect"/> associated.
55  /// </summary>
57  : this(new List<SoundEffect>())
58  {
59  }
60 
61  /// <summary>
62  /// Event argument class used to signal the <see cref="AudioEmitterProcessor"/> that a new AudioEmitterSoundController has new added or removed to the component.
63  /// </summary>
64  internal class ControllerCollectionChangedEventArgs
65  {
66  /// <summary>
67  /// The entity associated the current component.
68  /// </summary>
69  public Entity Entity;
70 
71  /// <summary>
72  /// The controller that have been added or removed to the component.
73  /// </summary>
74  public AudioEmitterSoundController Controller;
75 
76  /// <summary>
77  /// Action indication if the controller has been added or removed.
78  /// </summary>
79  public NotifyCollectionChangedAction Action;
80 
81  public ControllerCollectionChangedEventArgs(Entity entity, AudioEmitterSoundController controller, NotifyCollectionChangedAction action)
82  {
83  Entity = entity;
84  Controller = controller;
85  Action = action;
86  }
87  }
88 
89  /// <summary>
90  /// Event triggered when an <see cref="AudioEmitterSoundController"/> has be attached or detached to the component.
91  /// </summary>
92  internal event EventHandler<ControllerCollectionChangedEventArgs> ControllerCollectionChanged;
93 
94  /// <summary>
95  /// Return a <see cref="AudioEmitterSoundController"/> that can be used to control the provided <see cref="SoundEffect"/>.
96  /// </summary>
97  /// <param name="soundEffect">The soundEffect that the user want to control.</param>
98  /// <returns>The controller that can control the <paramref name="soundEffect"/></returns>
99  /// <exception cref="ArgumentNullException">The provided <paramref name="soundEffect"/> is null.</exception>
100  /// <exception cref="ArgumentException">The provided <paramref name="soundEffect"/> is not attached to this component.</exception>
101  /// <remarks>The return AudioEmitterSoundController is valid as long as
102  /// (1) the associated soundEffect is attached to the emitter,
103  /// (2) the associated soundEffect is not disposed and,
104  /// (3) the emitter component's entity is present into Entity system.</remarks>
106  {
107  if (soundEffect == null)
108  {
109  throw new ArgumentNullException("soundEffect");
110  }
111  if (!SoundEffectToController.ContainsKey(soundEffect))
112  {
113  throw new ArgumentException("The provided soundEffect has not been attached to the EmitterComponent.");
114  }
115 
116  return SoundEffectToController[soundEffect];
117  }
118 
119  /// <summary>
120  /// Attach a <see cref="SoundEffect"/> to this emitter component.
121  /// Once attached a <see cref="AudioEmitterSoundController"/> can be queried using <see cref="GetSoundEffectController"/> to control the attached SoundEffect.
122  /// </summary>
123  /// <param name="soundEffect">The SoundEffect to attach</param>
124  /// <exception cref="ArgumentNullException">The provided <paramref name="soundEffect"/> is null.</exception>
125  /// <exception cref="InvalidOperationException">The provided <paramref name="soundEffect"/> can not be localized (contains more than one channel).</exception>
126  /// <remarks>Attaching a soundEffect already attached has no effects.</remarks>
127  public void AttachSoundEffect(SoundEffect soundEffect)
128  {
129  if (soundEffect == null)
130  {
131  throw new ArgumentNullException("soundEffect");
132  }
133  if (soundEffect.WaveFormat.Channels > 1)
134  {
135  throw new InvalidOperationException("The provided SoundEffect has more than one channel. It can not be localized in the 3D scene.");
136  }
137 
138  if(SoundEffectToController.ContainsKey(soundEffect))
139  return;
140 
141  var newController = new AudioEmitterSoundController(this, soundEffect);
142  SoundEffectToController[soundEffect] = newController;
143  if(ControllerCollectionChanged != null)
144  ControllerCollectionChanged.Invoke(this, new ControllerCollectionChangedEventArgs(Entity, newController, NotifyCollectionChangedAction.Add ));
145  }
146 
147  /// <summary>
148  /// Attach a list of <see cref="SoundEffect"/> to this emitter component.
149  /// Once attached a <see cref="AudioEmitterSoundController"/> can be queried using <see cref="GetSoundEffectController"/> to control the attached SoundEffect.
150  /// </summary>
151  /// <param name="soundEffects">The SoundEffects to attach</param>
152  /// <exception cref="ArgumentNullException">The provided <paramref name="soundEffects"/> list is null.</exception>
153  /// <exception cref="InvalidOperationException">One or more of the provided SoundEffect can not be localized (contains more than one channel).</exception>
154  /// <remarks>Attaching a soundEffect already attached has no effects.</remarks>
156  {
157  if (soundEffects == null)
158  {
159  throw new ArgumentNullException("soundEffects");
160  }
161 
162  foreach (var soundEffect in soundEffects)
163  {
164  AttachSoundEffect(soundEffect);
165  }
166  }
167 
168  /// <summary>
169  /// Detach a <see cref="SoundEffect"/> from this emitter component.
170  /// Once detach the controller previously associated to the SoundEffect is invalid.
171  /// </summary>
172  /// <param name="soundEffect">The soundEffect to detach.</param>
173  /// <exception cref="ArgumentNullException">The provided <paramref name="soundEffect"/> is null.</exception>
174  /// <exception cref="ArgumentException">The provided <paramref name="soundEffect"/> is not currently attached to the emitter component.</exception>
175  public void DetachSoundEffect(SoundEffect soundEffect)
176  {
177  if (soundEffect == null)
178  {
179  throw new ArgumentNullException("soundEffect");
180  }
181  if (!SoundEffectToController.ContainsKey(soundEffect))
182  {
183  throw new ArgumentException("The provided soundEffect is not currently attached to this emitter component.");
184  }
185 
186  var oldController = SoundEffectToController[soundEffect];
187  SoundEffectToController.Remove(soundEffect);
188  if (ControllerCollectionChanged != null)
189  ControllerCollectionChanged.Invoke(this, new ControllerCollectionChangedEventArgs(Entity, oldController, NotifyCollectionChangedAction.Remove));
190  }
191 
192  /// <summary>
193  /// Detach a list of <see cref="SoundEffect"/> from this emitter component.
194  /// Once detach the controller previously associated to the SoundEffect is invalid.
195  /// </summary>
196  /// <param name="soundEffects">The soundEffects to detach.</param>
197  /// <exception cref="ArgumentNullException">The provided <paramref name="soundEffects"/> is null.</exception>
198  /// <exception cref="ArgumentException">One or more of the provided SoundEffect is not currently attached to the emitter component.</exception>
200  {
201  if (soundEffects == null)
202  {
203  throw new ArgumentNullException("soundEffects");
204  }
205 
206  foreach (var soundEffect in soundEffects)
207  {
208  DetachSoundEffect(soundEffect);
209  }
210  }
211 
212  /// <summary>
213  /// Distance scale used to calculate the signal attenuation with the listener
214  /// </summary>
215  /// <remarks>
216  /// By default, this value is 1.0.
217  /// This value represent the distance unit and determines how quickly the signal attenuates between this object and the AudioListener.
218  /// Values below 1.0 exaggerate the attenuation to make it more apparent.
219  /// Values above 1.0 scale down the attenuation. A value of 1.0 leaves the default attenuation unchanged.
220  /// Note that this value modifies only the calculated attenuation between this object and a AudioListener.
221  /// The calculated attenuation is a product of the relationship between AudioEmitter.Position and AudioListener.Position.
222  /// If the calculation yields a result of no attenuation effect, this value has no effect.
223  /// </remarks>
224  /// <exception cref="ArgumentOutOfRangeException">The distance scale of an audio emitter must be greater than or equal to zero.</exception>
225  [DataMemberConvert]
226  public float DistanceScale
227  {
228  get { return distanceScale; }
229 
230  set
231  {
232  if (value < 0)
233  throw new ArgumentOutOfRangeException();
234 
235  distanceScale = value;
236  }
237  }
238  private float distanceScale;
239 
240  /// <summary>
241  /// The scalar applied to the level of Doppler effect calculated between this and the listener
242  /// </summary>
243  /// <remarks>
244  /// By default, this value is 1.0.
245  /// This value determines how much to modify the calculated Doppler effect between this object and a AudioListener.
246  /// Values below 1.0 scale down the Doppler effect to make it less apparent.
247  /// Values above 1.0 exaggerate the Doppler effect. A value of 1.0 leaves the effect unmodified.
248  /// Note that this value modifies only the calculated Doppler between this object and a AudioListener.
249  /// The calculated Doppler is a product of the relationship between AudioEmitter.Velocity and AudioListener.Velocity.
250  /// If the calculation yields a result of no Doppler effect, this value has no effect.
251  /// </remarks>
252  /// <exception cref="ArgumentOutOfRangeException">The Doppler scale of an audio emitter must be greater than or equal to zero.</exception>
253  [DataMemberConvert]
254  public float DopplerScale
255  {
256  get { return dopplerScale; }
257 
258  set
259  {
260  if (value < 0)
261  throw new ArgumentOutOfRangeException();
262 
263  dopplerScale = value;
264  }
265  }
266  private float dopplerScale;
267 
268  /// <summary>
269  /// Boolean indicating to the <see cref="AudioEmitterProcessor"/> if the AudioEmitterComponent need to be processed or can be skipped.
270  /// </summary>
271  internal bool ShouldBeProcessed { get; set; }
272 
273  public override PropertyKey DefaultKey
274  {
275  get { return Key; }
276  }
277  }
278 }
void AttachSoundEffects(IEnumerable< SoundEffect > soundEffects)
Attach a list of SoundEffect to this emitter component. Once attached a AudioEmitterSoundController c...
Game entity. It usually aggregates multiple EntityComponent
Definition: Entity.cs:28
AudioEmitterComponent()
Create an instance of AudioEmitterComponent with no default SoundEffect associated.
void DetachSoundEffect(SoundEffect soundEffect)
Detach a SoundEffect from this emitter component. Once detach the controller previously associated to...
This class is used to control a SiliconStudio.Paradox.Audio.SoundEffect associated to a AudioEmitterC...
AudioEmitterSoundController GetSoundEffectController(SoundEffect soundEffect)
Return a AudioEmitterSoundController that can be used to control the provided SoundEffect.
This class provides a loaded sound resource which is localizable in the 3D scene. ...
Definition: SoundEffect.cs:46
AudioEmitterComponent(IEnumerable< SoundEffect > soundEffectToAttach)
Create an instance of AudioEmitterComponent with a list default SoundEffect associated.
Component representing an audio emitter.
void Add(EntityComponent component)
Adds the specified component using the EntityComponent.DefaultKey.
Definition: Entity.cs:125
A class that represents a tag propety.
Definition: PropertyKey.cs:17
void AttachSoundEffect(SoundEffect soundEffect)
Attach a SoundEffect to this emitter component. Once attached a AudioEmitterSoundController can be qu...
void DetachSoundEffects(IEnumerable< SoundEffect > soundEffects)
Detach a list of SoundEffect from this emitter component. Once detach the controller previously assoc...