Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
SoundEffectInstance.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 using System;
5 
6 using SiliconStudio.Paradox.Audio.Wave;
7 using SiliconStudio.Core.Mathematics;
8 
9 namespace SiliconStudio.Paradox.Audio
10 {
11  /// <summary>
12  /// Instance of a SoundEffect sound which can be independently localized and played.
13  /// </summary>
14  /// <remarks>
15  /// <para>
16  /// You can create a SoundEffectInstance by calling <see cref="SoundEffect.CreateInstance"/>.
17  /// Initially, the SoundEffectInstance is created as stopped, but you can play it by calling <see cref="SoundInstanceBase.Play"/>.
18  /// You can modify the volume and the panning of the SoundEffectInstance by setting the <see cref="SoundInstanceBase.Volume"/> and <see cref="Pan"/> properties
19  /// or directly apply a localization to the sound by calling the <see cref="IPositionableSound.Apply3D"/> function.
20  /// </para>
21  /// <para>
22  /// A SoundEffectInstance is invalidated when the corresponding <see cref="SoundEffect"/> is Disposed.
23  /// </para>
24  /// <para>
25  /// By default, playing an sound effect stops all the sibling instances currently playing. That is, the instances coming from the same <see cref="SoundEffect"/>.
26  /// This behavior is important on mobile devices which most of the time have only a limited number audio tracks.
27  /// It prevents all the audio tracks from being occupied by the same short repeating sound and favors the variety of sounds.
28  /// This behavior can nevertheless be overridden by using the <see cref="SoundEffectInstance.Play(bool)"/> function.
29  /// </para>
30  /// </remarks>
31  /// <seealso cref="SoundEffect"/>
32  /// <seealso cref="IPositionableSound"/>"/>
33  /// <seealso cref="DynamicSoundEffectInstance"/>
35  {
36  private readonly SoundEffect soundEffect;
37 
38  internal virtual WaveFormat WaveFormat
39  {
40  get { return soundEffect.WaveFormat; }
41  }
42 
43  //prevent creation of SoundEffectInstance to the user
44  internal SoundEffectInstance(SoundEffect correspSoundEffect)
45  : base(correspSoundEffect.AudioEngine)
46  {
47  soundEffect = correspSoundEffect;
48 
49  if (EngineState != AudioEngineState.Invalidated)
50  CreateVoice(soundEffect.WaveFormat);
51 
52  ResetStateToDefault();
53  }
54 
55  //prevent creation of SoundEffectInstance to the user and other classes
56  internal SoundEffectInstance(AudioEngine engine)
57  : base(engine)
58  {
59  soundEffect = null;
60  }
61 
62  internal void ResetStateToDefault()
63  {
64  Reset3D();
65  Pan = 0;
66  Volume = 1;
67  IsLooped = false;
68  Stop();
69  }
70 
71  protected override void StopConcurrentInstances()
72  {
73  if(soundEffect != null)
74  soundEffect.StopConcurrentInstances(this);
75  }
76 
77  /// <summary>
78  /// Play or resume the sound effect instance, specifying explicitly how to deal with sibling instances.
79  /// </summary>
80  /// <param name="stopSiblingInstances">Indicate if sibling instances (instances coming from the same <see cref="SoundEffect"/>) currently playing should be stopped or not.</param>
81  public void Play(bool stopSiblingInstances)
82  {
83  PlayExtended(stopSiblingInstances);
84  }
85 
86  #region Implementation of the ILocalizable Interface
87 
88  public float Pan
89  {
90  get
91  {
92  CheckNotDisposed();
93  return pan;
94  }
95  set
96  {
97  CheckNotDisposed();
98 
99  Reset3D();
100  pan = MathUtil.Clamp(value, -1, 1);
101 
102  if (pan < 0)
103  panChannelVolumes = new[] { 1f, 1f + pan };
104  else
105  panChannelVolumes = new[] { 1f - pan, 1f };
106 
107  if (EngineState != AudioEngineState.Invalidated)
108  UpdatePan();
109  }
110  }
111  private float pan;
112 
113  /// <summary>
114  /// Channel Volume multiplicative factors that come from the user panning.
115  /// </summary>
116  private float[] panChannelVolumes = { 1f, 1f };
117 
118  public void Apply3D(AudioListener listener, AudioEmitter emitter)
119  {
120  CheckNotDisposed();
121 
122  if (listener == null)
123  throw new ArgumentNullException("listener");
124 
125  if(emitter == null)
126  throw new ArgumentNullException("emitter");
127 
128  if(soundEffect.WaveFormat.Channels > 1)
129  throw new InvalidOperationException("Apply3D cannot be used on multi-channels sounds.");
130 
131  // reset Pan its default values.
132  if (Pan != 0)
133  Pan = 0;
134 
135  if (EngineState != AudioEngineState.Invalidated)
136  Apply3DImpl(listener, emitter);
137  }
138  /// <summary>
139  /// Channel Volume multiplicative factors that come from the 3D localization.
140  /// </summary>
141  private float[] localizationChannelVolumes;
142 
143  internal float Pitch
144  {
145  get
146  {
147  CheckNotDisposed();
148  return pitch;
149  }
150  set
151  {
152  CheckNotDisposed();
153  Reset3D();
154  pitch = MathUtil.Clamp(value, -1, 1);
155 
156  if (EngineState != AudioEngineState.Invalidated)
157  UpdatePitch();
158  }
159  }
160  private float pitch;
161 
162  /// <summary>
163  /// Multiplicative factor to apply to the pitch that comes from the Doppler effect.
164  /// </summary>
165  private float dopplerPitchFactor;
166 
167  private void ComputeDopplerFactor(AudioListener listener, AudioEmitter emitter)
168  {
169  // To evaluate the Doppler effect we calculate the distance to the listener from one wave to the next one and divide it by the sound speed
170  // we use 343m/s for the sound speed which correspond to the sound speed in the air.
171  // we use 600Hz for the sound frequency which correspond to the middle of the human hearable sounds frequencies.
172 
173  const float SoundSpeed = 343f;
174  const float SoundFreq = 600f;
175  const float SoundPeriod = 1 / SoundFreq;
176 
177  // avoid useless calculations.
178  if (emitter.DopplerScale <= float.Epsilon || (emitter.Velocity == Vector3.Zero && listener.Velocity == Vector3.Zero))
179  {
180  dopplerPitchFactor = 1f;
181  return;
182  }
183 
184  var vecListEmit = emitter.Position - listener.Position;
185  var distListEmit = vecListEmit.Length();
186 
187  var vecListEmitSpeed = emitter.Velocity - listener.Velocity;
188  if (Vector3.Dot(vecListEmitSpeed, Vector3.Normalize(vecListEmit)) < -SoundSpeed) // emitter and listener are getting closer more quickly than the speed of the sound.
189  {
190  dopplerPitchFactor = float.PositiveInfinity; // will be clamped later
191  return;
192  }
193 
194  var timeSinceLastWaveArrived = 0f; // time elapsed since the previous wave arrived to the listener.
195  var lastWaveDistToListener = 0f; // the distance that the last wave still have to travel to arrive to the listener.
196  const float DistLastWave = SoundPeriod * SoundSpeed; // distance traveled by the previous wave.
197  if (DistLastWave > distListEmit)
198  timeSinceLastWaveArrived = (DistLastWave - distListEmit) / SoundSpeed;
199  else
200  lastWaveDistToListener = distListEmit - DistLastWave;
201  var nextVecListEmit = vecListEmit + SoundPeriod * vecListEmitSpeed;
202  var nextWaveDistToListener = nextVecListEmit.Length();
203  var timeBetweenTwoWaves = timeSinceLastWaveArrived + (nextWaveDistToListener - lastWaveDistToListener) / SoundSpeed;
204  var apparentFrequency = 1 / timeBetweenTwoWaves;
205  dopplerPitchFactor = (float) Math.Pow(apparentFrequency / SoundFreq, emitter.DopplerScale);
206  }
207 
208  #endregion
209 
210  internal override void DestroyImpl()
211  {
212  if(soundEffect != null)
213  soundEffect.UnregisterInstance(this);
214 
215  PlatformSpecificDisposeImpl();
216  }
217 
218  public void Reset3D()
219  {
220  dopplerPitchFactor = 1f;
221  localizationChannelVolumes = new[] { 0.5f, 0.5f };
222 
223  if (EngineState == AudioEngineState.Invalidated)
224  return;
225 
226  UpdatePitch();
227  UpdateStereoVolumes();
228 
229  Reset3DImpl();
230  }
231  }
232 }
Represents a 3D audio listener in the audio scene. This object, used in combination with an AudioEmit...
static void Dot(ref Vector3 left, ref Vector3 right, out float result)
Calculates the dot product of two vectors.
Definition: Vector3.cs:585
AudioEngineState
Describe the possible states of the AudioEngine.
This class provides a loaded sound resource which is localizable in the 3D scene. ...
Definition: SoundEffect.cs:46
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
void Apply3D(AudioListener listener, AudioEmitter emitter)
Applies 3D positioning to the sound. More precisely adjust the channel volumes and pitch of the sound...
Base class for sound that creates voices
static readonly Vector3 Zero
A SiliconStudio.Core.Mathematics.Vector3 with all of its components set to zero.
Definition: Vector3.cs:52
Represents the audio engine. In current version, the audio engine necessarily creates its context on ...
Definition: AudioEngine.cs:80
Represents a 3D audio emitter in the audio scene. This object, used in combination with an AudioListe...
Definition: AudioEmitter.cs:17
void Play(bool stopSiblingInstances)
Play or resume the sound effect instance, specifying explicitly how to deal with sibling instances...
void Normalize()
Converts the vector into a unit vector.
Definition: Vector3.cs:216
void Reset3D()
Cancel the effect of possible previous calls to Apply3D.
Instance of a SoundEffect sound which can be independently localized and played.
override void StopConcurrentInstances()
Stops the sound instances in competition with this sound instance.
Interface for 3D localizable sound. The interface currently supports only mono and stereo sounds (ref...