OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
SoundModule.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 using System;
28 using System.IO;
29 using System.Collections.Generic;
30 using System.Reflection;
31 
32 using Nini.Config;
33 using OpenMetaverse;
34 using log4net;
35 using Mono.Addins;
36 
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40 
41 namespace OpenSim.Region.CoreModules.World.Sound
42 {
43  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SoundModule")]
45  {
46 // private static readonly ILog m_log = LogManager.GetLogger(
47 // MethodBase.GetCurrentMethod().DeclaringType);
48 
49  private Scene m_scene;
50 
51  public enum SoundFlags: byte
52  {
53  NONE = 0,
54  LOOP = 1 << 0,
55  SYNC_MASTER = 1<<1,
56  SYNC_SLAVE = 1<<2,
57  SYNC_PENDING = 1<<3,
58  QUEUE = 1<<4,
59  STOP = 1<<5,
60  SYNC_MASK = SYNC_MASTER | SYNC_SLAVE | SYNC_PENDING
61  }
62 
63  public bool Enabled { get; private set; }
64 
65  public float MaxDistance { get; private set; }
66 
67  #region INonSharedRegionModule
68 
69  public void Initialise(IConfigSource configSource)
70  {
71  IConfig config = configSource.Configs["Sounds"];
72 
73  if (config == null)
74  {
75  Enabled = true;
76  MaxDistance = 100.0f;
77  }
78  else
79  {
80  Enabled = config.GetString("Module", "OpenSim.Region.CoreModules.dll:SoundModule") ==
81  Path.GetFileName(Assembly.GetExecutingAssembly().Location)
82  + ":" + MethodBase.GetCurrentMethod().DeclaringType.Name;
83  MaxDistance = config.GetFloat("MaxDistance", 100.0f);
84  }
85  }
86 
87  public void AddRegion(Scene scene) { }
88 
89  public void RemoveRegion(Scene scene)
90  {
91  m_scene.EventManager.OnNewClient -= OnNewClient;
92  }
93 
94  public void RegionLoaded(Scene scene)
95  {
96  if (!Enabled)
97  return;
98 
99  m_scene = scene;
100  m_scene.EventManager.OnNewClient += OnNewClient;
101 
102  m_scene.RegisterModuleInterface<ISoundModule>(this);
103  }
104 
105  public void Close() { }
106 
107  public Type ReplaceableInterface
108  {
109  get { return typeof(ISoundModule); }
110  }
111 
112  public string Name { get { return "Sound Module"; } }
113 
114  #endregion
115 
116  #region Event Handlers
117 
118  private void OnNewClient(IClientAPI client)
119  {
120  client.OnSoundTrigger += TriggerSound;
121  }
122 
123  #endregion
124 
125  #region ISoundModule
126 
127  public virtual void PlayAttachedSound(
128  UUID soundID, UUID ownerID, UUID objectID, double gain, Vector3 position, byte flags, float radius)
129  {
130  SceneObjectPart part;
131  if (!m_scene.TryGetSceneObjectPart(objectID, out part))
132  return;
133 
134  SceneObjectGroup grp = part.ParentGroup;
135 
136  if (radius == 0)
137  radius = MaxDistance;
138 
139  if (part.SoundQueueing)
140  flags |= (byte)SoundFlags.QUEUE;
141 
142  if (grp.IsAttachment)
143  {
144  ScenePresence ssp = null;
145  if (!m_scene.TryGetScenePresence(grp.AttachedAvatar, out ssp))
146  return;
147 
148  if (!ssp.ParcelAllowThisAvatarSounds)
149  return;
150 
152  {
153  ssp.ControllingClient.SendPlayAttachedSound(soundID, objectID,
154  ownerID, (float)gain, flags);
155  return;
156  }
157  }
158 
159  m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
160  {
161  sp.ControllingClient.SendPlayAttachedSound(soundID, objectID,
162  ownerID, (float)gain, flags);
163  });
164  }
165 
166  public virtual void TriggerSound(
167  UUID soundId, UUID ownerID, UUID objectID, UUID parentID, double gain, Vector3 position, UInt64 handle, float radius)
168  {
169  SceneObjectPart part;
170  ScenePresence ssp = null;
171  if (!m_scene.TryGetSceneObjectPart(objectID, out part))
172  {
173  if (!m_scene.TryGetScenePresence(ownerID, out ssp))
174  return;
175  if (!ssp.ParcelAllowThisAvatarSounds)
176  return;
177  }
178  else
179  {
180  SceneObjectGroup grp = part.ParentGroup;
181 
182  if (grp.IsAttachment)
183  {
184  if (!m_scene.TryGetScenePresence(grp.AttachedAvatar, out ssp))
185  return;
186 
187  if (!ssp.ParcelAllowThisAvatarSounds)
188  return;
189 
191  {
192  ssp.ControllingClient.SendTriggeredSound(soundId, ownerID,
193  objectID, parentID, handle, position,
194  (float)gain);
195  return;
196  }
197  }
198  }
199 
200  if (radius == 0)
201  radius = MaxDistance;
202 
203  m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
204  {
205  double dis = Util.GetDistanceTo(sp.AbsolutePosition, position);
206  if (dis > radius) // Max audio distance
207  return;
208 
209  sp.ControllingClient.SendTriggeredSound(soundId, ownerID,
210  objectID, parentID, handle, position,
211  (float)gain);
212  });
213  }
214 
215  public virtual void StopSound(UUID objectID)
216  {
217  SceneObjectPart m_host;
218  if (!m_scene.TryGetSceneObjectPart(objectID, out m_host))
219  return;
220 
221  StopSound(m_host);
222  }
223 
224  private static void StopSound(SceneObjectPart m_host)
225  {
226 // m_host.AdjustSoundGain(0);
227  m_host.Sound = UUID.Zero;
228  m_host.SoundFlags = (byte)SoundFlags.STOP;
229  m_host.SoundRadius = 0;
230  m_host.SoundGain = 0;
231  m_host.ScheduleFullUpdate();
232  m_host.SendFullUpdateToAllClients();
233  }
234 
235  public virtual void PreloadSound(UUID objectID, UUID soundID, float radius)
236  {
237  SceneObjectPart part;
238  if (soundID == UUID.Zero
239  || !m_scene.TryGetSceneObjectPart(objectID, out part))
240  {
241  return;
242  }
243 
244  if (radius == 0)
245  radius = MaxDistance;
246 
247  m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
248  {
249  if (Util.GetDistanceTo(sp.AbsolutePosition, part.AbsolutePosition) < radius)
250  sp.ControllingClient.SendPreLoadSound(objectID, objectID, soundID);
251  });
252  }
253 
254  // Xantor 20080528 we should do this differently.
255  // 1) apply the sound to the object
256  // 2) schedule full update
257  // just sending the sound out once doesn't work so well when other avatars come in view later on
258  // or when the prim gets moved, changed, sat on, whatever
259  // see large number of mantises (mantes?)
260  // 20080530 Updated to remove code duplication
261  // 20080530 Stop sound if there is one, otherwise volume only changes don't work
262  public void LoopSound(UUID objectID, UUID soundID,
263  double volume, double radius, bool isMaster, bool isSlave)
264  {
265  SceneObjectPart m_host;
266  if (!m_scene.TryGetSceneObjectPart(objectID, out m_host))
267  return;
268 
269  byte iflags = 1; // looping
270  if (isMaster)
271  iflags |= (byte)SoundFlags.SYNC_MASTER;
272  // TODO check viewer seems to accept both
273  if (isSlave)
274  iflags |= (byte)SoundFlags.SYNC_SLAVE;
275  if (m_host.SoundQueueing)
276  iflags |= (byte)SoundFlags.QUEUE;
277 
278  m_host.Sound = soundID;
279  m_host.SoundGain = volume;
280  m_host.SoundFlags = iflags;
281  m_host.SoundRadius = radius;
282 
283  m_host.ScheduleFullUpdate();
284  m_host.SendFullUpdateToAllClients();
285  }
286 
287  public void SendSound(UUID objectID, UUID soundID, double volume,
288  bool triggered, byte flags, float radius, bool useMaster,
289  bool isMaster)
290  {
291  if (soundID == UUID.Zero)
292  return;
293 
294  SceneObjectPart part;
295  if (!m_scene.TryGetSceneObjectPart(objectID, out part))
296  return;
297 
298  volume = Util.Clip((float)volume, 0, 1);
299 
300  UUID parentID = part.ParentGroup.UUID;
301 
302  Vector3 position = part.AbsolutePosition; // region local
303  ulong regionHandle = m_scene.RegionInfo.RegionHandle;
304 
305  if(triggered)
306  TriggerSound(soundID, part.OwnerID, part.UUID, parentID, volume, position, regionHandle, radius);
307  else
308  {
309  byte bflags = 0;
310 
311  if (isMaster)
312  bflags |= (byte)SoundFlags.SYNC_MASTER;
313  // TODO check viewer seems to accept both
314  if (useMaster)
315  bflags |= (byte)SoundFlags.SYNC_SLAVE;
316  PlayAttachedSound(soundID, part.OwnerID, part.UUID, volume, position, bflags, radius);
317  }
318  }
319 
320  public void TriggerSoundLimited(UUID objectID, UUID sound,
321  double volume, Vector3 min, Vector3 max)
322  {
323  if (sound == UUID.Zero)
324  return;
325 
326  SceneObjectPart part;
327  if (!m_scene.TryGetSceneObjectPart(objectID, out part))
328  return;
329 
330  m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
331  {
332  double dis = Util.GetDistanceTo(sp.AbsolutePosition,
333  part.AbsolutePosition);
334 
335  if (dis > MaxDistance) // Max audio distance
336  return;
337  else if (!Util.IsInsideBox(sp.AbsolutePosition, min, max))
338  return;
339 
340  // Scale by distance
341  double thisSpGain = volume * ((MaxDistance - dis) / MaxDistance);
342 
343  sp.ControllingClient.SendTriggeredSound(sound, part.OwnerID,
344  part.UUID, part.ParentGroup.UUID,
345  m_scene.RegionInfo.RegionHandle,
346  part.AbsolutePosition, (float)thisSpGain);
347  });
348  }
349 
350  public void SetSoundQueueing(UUID objectID, bool shouldQueue)
351  {
352  SceneObjectPart part;
353  if (!m_scene.TryGetSceneObjectPart(objectID, out part))
354  return;
355 
356  part.SoundQueueing = shouldQueue;
357  }
358 
359  #endregion
360  }
361 }
void SetSoundQueueing(UUID objectID, bool shouldQueue)
Set whether sounds on the given prim should be queued.
Definition: SoundModule.cs:350
bool HasPrivateAttachmentPoint
If this scene object has an attachment point then indicate whether there is a point where attachments...
void SendSound(UUID objectID, UUID soundID, double volume, bool triggered, byte flags, float radius, bool useMaster, bool isMaster)
Trigger or play an attached sound in this part's inventory.
Definition: SoundModule.cs:287
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
Definition: SoundModule.cs:94
virtual void TriggerSound(UUID soundId, UUID ownerID, UUID objectID, UUID parentID, double gain, Vector3 position, UInt64 handle, float radius)
Trigger a sound in the scene.
Definition: SoundModule.cs:166
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
Definition: SoundModule.cs:105
A scene object group is conceptually an object in the scene. The object is constituted of SceneObject...
void LoopSound(UUID objectID, UUID soundID, double volume, double radius, bool isMaster, bool isSlave)
Loop specified sound at specified volume with specified radius, optionally declaring object as new sy...
Definition: SoundModule.cs:262
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
Definition: SoundModule.cs:89
override Vector3 AbsolutePosition
Position of this avatar relative to the region the avatar is in
virtual void StopSound(UUID objectID)
Stop sounds eminating from an object.
Definition: SoundModule.cs:215
Interactive OpenSim region server
Definition: OpenSim.cs:55
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
Definition: SoundModule.cs:87
virtual void PlayAttachedSound(UUID soundID, UUID ownerID, UUID objectID, double gain, Vector3 position, byte flags, float radius)
Play a sound from an object.
Definition: SoundModule.cs:127
void Initialise(IConfigSource configSource)
This is called to initialize the region module. For shared modules, this is called exactly once...
Definition: SoundModule.cs:69
UUID AttachedAvatar
The avatar to which this scene object is attached.
virtual void PreloadSound(UUID objectID, UUID soundID, float radius)
Preload sound to viewers within range.
Definition: SoundModule.cs:235
void SendPreLoadSound(UUID objectID, UUID ownerID, UUID soundID)
bool IsAttachment
Is this scene object acting as an attachment?
void TriggerSoundLimited(UUID objectID, UUID sound, double volume, Vector3 min, Vector3 max)
Trigger a sound to be played to all agents within an axis-aligned bounding box.
Definition: SoundModule.cs:320
void ScheduleFullUpdate()
Schedules this prim for a full update