aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Sound/SoundModule.cs')
-rw-r--r--OpenSim/Region/CoreModules/World/Sound/SoundModule.cs371
1 files changed, 371 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs b/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs
new file mode 100644
index 0000000..1db6519
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs
@@ -0,0 +1,371 @@
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 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30
31using Nini.Config;
32using OpenMetaverse;
33using log4net;
34using Mono.Addins;
35
36using OpenSim.Framework;
37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes;
39
40namespace OpenSim.Region.CoreModules.World.Sound
41{
42 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SoundModule")]
43 public class SoundModule : INonSharedRegionModule, ISoundModule
44 {
45 private static readonly ILog m_log = LogManager.GetLogger(
46 MethodBase.GetCurrentMethod().DeclaringType);
47
48 private Scene m_scene;
49
50 public bool Enabled { get; private set; }
51
52 public float MaxDistance { get; private set; }
53
54 #region INonSharedRegionModule
55
56 public void Initialise(IConfigSource configSource)
57 {
58 IConfig config = configSource.Configs["Sounds"];
59
60 if (config == null)
61 {
62 Enabled = true;
63 MaxDistance = 100.0f;
64 }
65 else
66 {
67 Enabled = config.GetString("Module", "SoundModule") == "SoundModule";
68 MaxDistance = config.GetFloat("MaxDistance", 100.0f);
69 }
70 }
71
72 public void AddRegion(Scene scene) { }
73
74 public void RemoveRegion(Scene scene)
75 {
76 m_scene.EventManager.OnClientLogin -= OnNewClient;
77 }
78
79 public void RegionLoaded(Scene scene)
80 {
81 if (!Enabled)
82 return;
83
84 m_scene = scene;
85 m_scene.EventManager.OnClientLogin += OnNewClient;
86
87 m_scene.RegisterModuleInterface<ISoundModule>(this);
88 }
89
90 public void Close() { }
91
92 public Type ReplaceableInterface
93 {
94 get { return typeof(ISoundModule); }
95 }
96
97 public string Name { get { return "Sound Module"; } }
98
99 #endregion
100
101 #region Event Handlers
102
103 private void OnNewClient(IClientAPI client)
104 {
105 client.OnSoundTrigger += TriggerSound;
106 }
107
108 #endregion
109
110 #region ISoundModule
111
112 public virtual void PlayAttachedSound(
113 UUID soundID, UUID ownerID, UUID objectID, double gain, Vector3 position, byte flags, float radius)
114 {
115 SceneObjectPart part;
116 if (!m_scene.TryGetSceneObjectPart(objectID, out part))
117 return;
118
119 SceneObjectGroup grp = part.ParentGroup;
120
121 if (radius == 0)
122 radius = MaxDistance;
123
124 m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
125 {
126 double dis = Util.GetDistanceTo(sp.AbsolutePosition, position);
127 if (dis > MaxDistance) // Max audio distance
128 return;
129
130 if (grp.IsAttachment)
131 {
132 if (grp.HasPrivateAttachmentPoint && sp.ControllingClient.AgentId != grp.OwnerID)
133 return;
134
135 if (sp.ControllingClient.AgentId == grp.OwnerID)
136 dis = 0;
137 }
138
139 // Scale by distance
140 double thisSpGain = gain * ((radius - dis) / radius);
141
142 sp.ControllingClient.SendPlayAttachedSound(soundID, objectID,
143 ownerID, (float)thisSpGain, flags);
144 });
145 }
146
147 public virtual void TriggerSound(
148 UUID soundId, UUID ownerID, UUID objectID, UUID parentID, double gain, Vector3 position, UInt64 handle, float radius)
149 {
150 SceneObjectPart part;
151 if (!m_scene.TryGetSceneObjectPart(objectID, out part))
152 {
153 ScenePresence sp;
154 if (!m_scene.TryGetScenePresence(ownerID, out sp))
155 return;
156 }
157 else
158 {
159 SceneObjectGroup grp = part.ParentGroup;
160
161 if (grp.IsAttachment && grp.AttachmentPoint > 30)
162 {
163 objectID = ownerID;
164 parentID = ownerID;
165 }
166 }
167
168 if (radius == 0)
169 radius = MaxDistance;
170
171 m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
172 {
173 double dis = Util.GetDistanceTo(sp.AbsolutePosition, position);
174
175 if (dis > MaxDistance) // Max audio distance
176 return;
177
178 // Scale by distance
179 double thisSpGain = gain * ((radius - dis) / radius);
180
181 sp.ControllingClient.SendTriggeredSound(soundId, ownerID,
182 objectID, parentID, handle, position,
183 (float)thisSpGain);
184 });
185 }
186
187 public virtual void StopSound(UUID objectID)
188 {
189 SceneObjectPart m_host;
190 if (!m_scene.TryGetSceneObjectPart(objectID, out m_host))
191 return;
192
193 StopSound(m_host);
194 }
195
196 private static void StopSound(SceneObjectPart m_host)
197 {
198 m_host.AdjustSoundGain(0);
199 // Xantor 20080528: Clear prim data of sound instead
200 if (m_host.ParentGroup.LoopSoundSlavePrims.Contains(m_host))
201 {
202 if (m_host.ParentGroup.LoopSoundMasterPrim == m_host)
203 {
204 foreach (SceneObjectPart part in m_host.ParentGroup.LoopSoundSlavePrims)
205 {
206 part.Sound = UUID.Zero;
207 part.SoundFlags = 1 << 5;
208 part.SoundRadius = 0;
209 part.ScheduleFullUpdate();
210 part.SendFullUpdateToAllClients();
211 }
212 m_host.ParentGroup.LoopSoundMasterPrim = null;
213 m_host.ParentGroup.LoopSoundSlavePrims.Clear();
214 }
215 else
216 {
217 m_host.Sound = UUID.Zero;
218 m_host.SoundFlags = 1 << 5;
219 m_host.SoundRadius = 0;
220 m_host.ScheduleFullUpdate();
221 m_host.SendFullUpdateToAllClients();
222 }
223 }
224 else
225 {
226 m_host.Sound = UUID.Zero;
227 m_host.SoundFlags = 1 << 5;
228 m_host.SoundRadius = 0;
229 m_host.ScheduleFullUpdate();
230 m_host.SendFullUpdateToAllClients();
231 }
232 }
233
234 public virtual void PreloadSound(UUID objectID, UUID soundID, float radius)
235 {
236 SceneObjectPart part;
237 if (soundID == UUID.Zero
238 || !m_scene.TryGetSceneObjectPart(objectID, out part))
239 {
240 return;
241 }
242
243 if (radius == 0)
244 radius = MaxDistance;
245
246 m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
247 {
248 if (!(Util.GetDistanceTo(sp.AbsolutePosition, part.AbsolutePosition) >= MaxDistance))
249 sp.ControllingClient.SendPreLoadSound(objectID, objectID, soundID);
250 });
251 }
252
253 // Xantor 20080528 we should do this differently.
254 // 1) apply the sound to the object
255 // 2) schedule full update
256 // just sending the sound out once doesn't work so well when other avatars come in view later on
257 // or when the prim gets moved, changed, sat on, whatever
258 // see large number of mantises (mantes?)
259 // 20080530 Updated to remove code duplication
260 // 20080530 Stop sound if there is one, otherwise volume only changes don't work
261 public void LoopSound(UUID objectID, UUID soundID,
262 double volume, double radius, bool isMaster)
263 {
264 SceneObjectPart m_host;
265 if (!m_scene.TryGetSceneObjectPart(objectID, out m_host))
266 return;
267
268 if (isMaster)
269 m_host.ParentGroup.LoopSoundMasterPrim = m_host;
270
271 if (m_host.Sound != UUID.Zero)
272 StopSound(m_host);
273
274 m_host.Sound = soundID;
275 m_host.SoundGain = volume;
276 m_host.SoundFlags = 1; // looping
277 m_host.SoundRadius = radius;
278
279 m_host.ScheduleFullUpdate();
280 m_host.SendFullUpdateToAllClients();
281 }
282
283 public void SendSound(UUID objectID, UUID soundID, double volume,
284 bool triggered, byte flags, float radius, bool useMaster,
285 bool isMaster)
286 {
287 if (soundID == UUID.Zero)
288 return;
289
290 SceneObjectPart part;
291 if (!m_scene.TryGetSceneObjectPart(objectID, out part))
292 return;
293
294 volume = Util.Clip((float)volume, 0, 1);
295
296 UUID parentID = part.ParentGroup.UUID;
297
298 Vector3 position = part.AbsolutePosition; // region local
299 ulong regionHandle = m_scene.RegionInfo.RegionHandle;
300
301 if (useMaster)
302 {
303 if (isMaster)
304 {
305 if (triggered)
306 TriggerSound(soundID, part.OwnerID, part.UUID, parentID, volume, position, regionHandle, radius);
307 else
308 PlayAttachedSound(soundID, part.OwnerID, part.UUID, volume, position, flags, radius);
309 part.ParentGroup.PlaySoundMasterPrim = part;
310 if (triggered)
311 TriggerSound(soundID, part.OwnerID, part.UUID, parentID, volume, position, regionHandle, radius);
312 else
313 PlayAttachedSound(soundID, part.OwnerID, part.UUID, volume, position, flags, radius);
314 foreach (SceneObjectPart prim in part.ParentGroup.PlaySoundSlavePrims)
315 {
316 position = prim.AbsolutePosition; // region local
317 if (triggered)
318 TriggerSound(soundID, part.OwnerID, prim.UUID, parentID, volume, position, regionHandle, radius);
319 else
320 PlayAttachedSound(soundID, part.OwnerID, prim.UUID, volume, position, flags, radius);
321 }
322 part.ParentGroup.PlaySoundSlavePrims.Clear();
323 part.ParentGroup.PlaySoundMasterPrim = null;
324 }
325 else
326 {
327 part.ParentGroup.PlaySoundSlavePrims.Add(part);
328 }
329 }
330 else
331 {
332 if (triggered)
333 TriggerSound(soundID, part.OwnerID, part.UUID, parentID, volume, position, regionHandle, radius);
334 else
335 PlayAttachedSound(soundID, part.OwnerID, part.UUID, volume, position, flags, radius);
336 }
337 }
338
339 public void TriggerSoundLimited(UUID objectID, UUID sound,
340 double volume, Vector3 min, Vector3 max)
341 {
342 if (sound == UUID.Zero)
343 return;
344
345 SceneObjectPart part;
346 if (!m_scene.TryGetSceneObjectPart(objectID, out part))
347 return;
348
349 m_scene.ForEachRootScenePresence(delegate(ScenePresence sp)
350 {
351 double dis = Util.GetDistanceTo(sp.AbsolutePosition,
352 part.AbsolutePosition);
353
354 if (dis > MaxDistance) // Max audio distance
355 return;
356 else if (!Util.IsInsideBox(sp.AbsolutePosition, min, max))
357 return;
358
359 // Scale by distance
360 double thisSpGain = volume * ((MaxDistance - dis) / MaxDistance);
361
362 sp.ControllingClient.SendTriggeredSound(sound, part.OwnerID,
363 part.UUID, part.ParentGroup.UUID,
364 m_scene.RegionInfo.RegionHandle,
365 part.AbsolutePosition, (float)thisSpGain);
366 });
367 }
368
369 #endregion
370 }
371}