Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
AudioEmitterProcessor.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.Linq;
5 using System.Collections.Generic;
6 using System.Collections.Specialized;
7 
8 using SiliconStudio.Core;
9 using SiliconStudio.Core.Collections;
10 
11 using SiliconStudio.Paradox.EntityModel;
12 using SiliconStudio.Paradox.Audio;
13 using SiliconStudio.Paradox.Games;
14 
15 namespace SiliconStudio.Paradox.Engine
16 {
17  /// <summary>
18  /// Processor in charge of updating the <see cref="AudioEmitterComponent"/>s.
19  /// </summary>
20  /// <remarks>
21  /// <para>More precisely it updates the <see cref="AudioEmitter"/>s and
22  /// then applies 3D localization to each couple <see cref="AudioEmitterComponent"/>-<see cref="AudioListenerComponent"/>.
23  /// When a new emitter or a new listener is added to the system, its creates the required SoundInstances and associate them with the new emitter/listener tuples.
24  /// </para>
25  /// </remarks>
26  public class AudioEmitterProcessor: EntityProcessor<AudioEmitterProcessor.AssociatedData>
27  {
28  /// <summary>
29  /// Reference to the audioSystem.
30  /// </summary>
31  private AudioSystem audioSystem;
32 
33  // exposes field internally to allow debug (use directly matchingEntities if not for debug purpose)
34  internal Dictionary<Entity, AssociatedData> MatchingEntitiesForDebug { get { return matchingEntities; } }
35 
36  /// <summary>
37  /// Data associated to each <see cref="Entity"/> instances of the system having an <see cref="AudioEmitterComponent"/> and an <see cref="TransformationComponent"/>.
38  /// </summary>
39  public class AssociatedData
40  {
41  /// <summary>
42  /// The <see cref="Paradox.Audio.AudioEmitter"/> associated to the <see cref="AudioEmitterComponent"/>.
43  /// </summary>
45 
46  /// <summary>
47  /// The <see cref="Paradox.Engine.AudioEmitterComponent"/> associated to the entity
48  /// </summary>
50 
51  /// <summary>
52  /// The <see cref="Engine.TransformationComponent"/> associated to the entity
53  /// </summary>
55 
56  /// <summary>
57  /// A dictionary associating each activated listener of the AudioSystem and each sound controller of the <see cref="AudioEmitterComponent"/> to a valid sound effect instance.
58  /// </summary>
59  public Dictionary<Tuple<AudioListenerComponent, AudioEmitterSoundController>, SoundEffectInstance> ListenerControllerToSoundInstance;
60  }
61 
62  /// <summary>
63  /// Create a new instance of the processor.
64  /// </summary>
66  : base(new PropertyKey[] { AudioEmitterComponent.Key, TransformationComponent.Key })
67  {
68  }
69 
70  protected internal override void OnSystemAdd()
71  {
72  base.OnSystemAdd();
73 
74  audioSystem = Services.GetServiceAs<AudioSystem>();
75 
76  audioSystem.Listeners.CollectionChanged += OnListenerCollectionChanged;
77  }
78 
79  protected override AssociatedData GenerateAssociatedData(Entity entity)
80  {
81  return new AssociatedData
82  {
83  AudioEmitterComponent = entity.Get(AudioEmitterComponent.Key),
85  ListenerControllerToSoundInstance = new Dictionary<Tuple<AudioListenerComponent, AudioEmitterSoundController>, SoundEffectInstance>()
86  };
87  }
88 
89  protected internal override void OnSystemRemove()
90  {
91  base.OnSystemRemove();
92 
93  // Destroy all the SoundEffectInstance created by the processor before closing.
94  foreach (var soundInstance in matchingEntities.Values.SelectMany(x => x.AudioEmitterComponent.SoundEffectToController.Values))
95  soundInstance.DestroyAllSoundInstances();
96 
97  audioSystem.Listeners.CollectionChanged -= OnListenerCollectionChanged;
98  }
99 
100  protected override void OnEntityAdding(Entity entity, AssociatedData data)
101  {
102  base.OnEntityAdding(entity, data);
103 
104  // initialize the AudioEmitter first position
105  data.TransformationComponent.UpdateWorldMatrix(); // ensure the worldMatrix is correct
106  data.AudioEmitter = new AudioEmitter { Position = data.TransformationComponent.WorldMatrix.TranslationVector }; // valid position is needed at first Update loop to compute velocity.
107 
108  // create a SoundEffectInstance for each listener activated and for each sound controller of the EmitterComponent.
109  foreach (var listener in audioSystem.Listeners.Keys)
110  {
111  foreach (var soundController in data.AudioEmitterComponent.SoundEffectToController.Values)
112  data.ListenerControllerToSoundInstance[Tuple.Create(listener, soundController)] = soundController.CreateSoundInstance();
113  }
114 
115  data.AudioEmitterComponent.ControllerCollectionChanged += OnSoundControllerListChanged;
116  }
117 
118  public override void Draw(GameTime time)
119  {
120  base.Draw(time);
121 
122  foreach (var associatedData in matchingEntities.Values)
123  {
124  var emitter = associatedData.AudioEmitter;
125  var worldMatrix = associatedData.TransformationComponent.WorldMatrix;
126  var newPosition = worldMatrix.TranslationVector;
127 
128  if (!associatedData.AudioEmitterComponent.ShouldBeProcessed)
129  { // to be sure to have a valid velocity at any time we are forced to affect position even if Component need not to be processed.
130  emitter.Position = newPosition;
131  continue;
132  }
133 
134  // First update the emitter data if required.
135  emitter.DistanceScale = associatedData.AudioEmitterComponent.DistanceScale;
136  emitter.DopplerScale = associatedData.AudioEmitterComponent.DopplerScale;
137  emitter.Velocity = newPosition - emitter.Position;
138  emitter.Position = newPosition;
139 
140  // Then apply 3D localization
141  var performedAtLeastOneApply = false;
142  foreach (var controller in associatedData.AudioEmitterComponent.SoundEffectToController.Values)
143  {
144  foreach (var listenerComponent in audioSystem.Listeners.Keys)
145  {
146  var currentTupple = Tuple.Create(listenerComponent, controller);
147  var instance = associatedData.ListenerControllerToSoundInstance[currentTupple];
148  var listener = audioSystem.Listeners[listenerComponent];
149 
150  if (listener == null) // ListenerComponent activated but not present into the entity system anymore/yet.
151  { // Thus it can not be processed by the AudioListenerProcessor and does not contain valid AudioListener data.
152  instance.Stop(); // Thus stops any instances that was possibly playing.
153  continue; // and ignore any possible play request
154  }
155 
156  // Apply3D localization
157  if (instance.PlayState == SoundPlayState.Playing || controller.ShouldBePlayed)
158  {
159  instance.Apply3D(listener, emitter);
160  performedAtLeastOneApply = true;
161  }
162 
163  // Finally start playing the sounds if needed
164  if (controller.ShouldBePlayed)
165  {
166  instance.Volume = controller.Volume; // ensure that instance volume is valid
167  if(instance.PlayState == SoundPlayState.Stopped)
168  instance.IsLooped = controller.IsLooped && !controller.ShouldExitLoop; // update instances' IsLooped value, if was set by the user when when not listeners where activated.
169  instance.Play(false);
170  }
171  }
172  controller.ShouldBePlayed = false;
173  controller.ShouldExitLoop = false;
174  }
175 
176  associatedData.AudioEmitterComponent.ShouldBeProcessed = performedAtLeastOneApply;
177  }
178  }
179 
180  protected override void OnEntityRemoved(Entity entity, AssociatedData data)
181  {
182  base.OnEntityRemoved(entity, data);
183 
184  // dispose and delete all SoundEffectInstances associated to the EmitterComponent.
185  foreach (var soundController in data.AudioEmitterComponent.SoundEffectToController.Values)
186  soundController.DestroyAllSoundInstances();
187 
188  data.AudioEmitterComponent.ControllerCollectionChanged -= OnSoundControllerListChanged;
189  }
190 
191  private void OnListenerCollectionChanged(object o, TrackingCollectionChangedEventArgs args)
192  {
193  if (!args.CollectionChanged)// no keys have been added or removed, only one of the values changed
194  return;
195 
196  // A listener have been Added or Removed.
197  // We need to create/destroy all SoundEffectInstances associated to that listener for each AudioEmitterComponent.
198 
199  foreach (var associatedData in matchingEntities.Values)
200  {
201  var listenerControllerToSoundInstance = associatedData.ListenerControllerToSoundInstance;
202  var soundControllers = associatedData.AudioEmitterComponent.SoundEffectToController.Values;
203 
204  foreach (var soundController in soundControllers)
205  {
206  var currentTupple = Tuple.Create((AudioListenerComponent)args.Key, soundController);
207 
208  if (args.Action == NotifyCollectionChangedAction.Add) // A new listener have been added
209  {
210  listenerControllerToSoundInstance[currentTupple] = soundController.CreateSoundInstance();
211  }
212  else if (args.Action == NotifyCollectionChangedAction.Remove) // A listener have been removed
213  {
214  soundController.DestroySoundInstance(listenerControllerToSoundInstance[currentTupple]);
215  listenerControllerToSoundInstance.Remove(currentTupple);
216  }
217  }
218  }
219  }
220 
221  private void OnSoundControllerListChanged(object o, AudioEmitterComponent.ControllerCollectionChangedEventArgs args)
222  {
223  AssociatedData associatedData;
224  if (!matchingEntities.TryGetValue(args.Entity, out associatedData))
225  return;
226 
227  // A new SoundEffect have been associated to the AudioEmitterComponenent or an old SoundEffect have been deleted.
228  // We need to create/destroy the corresponding SoundEffectInstances.
229 
230  var listeners = audioSystem.Listeners.Keys;
231  foreach (var listener in listeners)
232  {
233  var currentTuple = Tuple.Create(listener, args.Controller);
234 
235  if (args.Action == NotifyCollectionChangedAction.Add)
236  {
237  associatedData.ListenerControllerToSoundInstance[currentTuple] = args.Controller.CreateSoundInstance();
238  }
239  else if(args.Action == NotifyCollectionChangedAction.Remove )
240  {
241  args.Controller.DestroySoundInstance(associatedData.ListenerControllerToSoundInstance[currentTuple]);
242  associatedData.ListenerControllerToSoundInstance.Remove(currentTuple);
243  }
244  }
245  }
246  }
247 }
override AssociatedData GenerateAssociatedData(Entity entity)
Game entity. It usually aggregates multiple EntityComponent
Definition: Entity.cs:28
Keys
Enumeration for keys.
Definition: Keys.cs:8
Data associated to each Entity instances of the system having an AudioEmitterComponent and an Transfo...
Defines Position, Rotation and Scale of its Entity.
override void OnEntityRemoved(Entity entity, AssociatedData data)
Component representing an audio listener.
TransformationComponent TransformationComponent
The Engine.TransformationComponent associated to the entity
SoundPlayState
Current state (playing, paused, or stopped) of a sound implementing the IPlayableSound interface...
AudioEmitterProcessor()
Create a new instance of the processor.
Entity processor, triggered on various EntitySystem events such as Entity and Component additions and...
AudioEmitterComponent AudioEmitterComponent
The Paradox.Engine.AudioEmitterComponent associated to the entity
override void OnEntityAdding(Entity entity, AssociatedData data)
Current timing used for variable-step (real time) or fixed-step (game time) games.
Definition: GameTime.cs:31
AudioEmitter AudioEmitter
The Paradox.Audio.AudioEmitter associated to the AudioEmitterComponent.
NotifyCollectionChangedAction Action
Gets the type of action performed. Allowed values are NotifyCollectionChangedAction.Add and NotifyCollectionChangedAction.Remove.
Represents a 3D audio emitter in the audio scene. This object, used in combination with an AudioListe...
Definition: AudioEmitter.cs:17
bool CollectionChanged
Gets a value indicating whether [collection changed (not a replacement but real insertion/removal)].
The Audio System. It creates an underlying instance of AudioEngine.
Definition: AudioSystem.cs:16
Component representing an audio emitter.
Dictionary< Tuple< AudioListenerComponent, AudioEmitterSoundController >, SoundEffectInstance > ListenerControllerToSoundInstance
A dictionary associating each activated listener of the AudioSystem and each sound controller of the ...
static PropertyKey< TransformationComponent > Key
A class that represents a tag propety.
Definition: PropertyKey.cs:17
Instance of a SoundEffect sound which can be independently localized and played.
Processor in charge of updating the AudioEmitterComponents.