-// \r
-// Radegast Metaverse Client\r
-// Copyright (c) 2009-2012, Radegast Development Team\r
-// All rights reserved.\r
-// \r
-// Redistribution and use in source and binary forms, with or without\r
-// modification, are permitted provided that the following conditions are met:\r
-// \r
-// * Redistributions of source code must retain the above copyright notice,\r
-// this list of conditions and the following disclaimer.\r
-// * Redistributions in binary form must reproduce the above copyright\r
-// notice, this list of conditions and the following disclaimer in the\r
-// documentation and/or other materials provided with the distribution.\r
-// * Neither the name of the application "Radegast", nor the names of its\r
-// contributors may be used to endorse or promote products derived from\r
-// this software without specific prior written permission.\r
-// \r
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
-// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
-// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
-// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-//\r
-// $Id: Sound.cs 502 2010-03-14 23:13:46Z latifer $\r
-//\r
-// Uncomment this to get lots more logging\r
-//#define TRACE_SOUND\r
-using System;\r
-using System.Runtime.InteropServices;\r
-using System.Collections.Generic;\r
-using FMOD;\r
-using OpenMetaverse;\r
-using OpenMetaverse.Assets;\r
-using System.Threading;\r
-\r
-namespace Radegast.Media\r
-{\r
-\r
- public class BufferSound : MediaObject\r
- {\r
- private UUID Id;\r
- private UUID ContainerId;\r
- private Boolean prefetchOnly = false;\r
- private FMOD.MODE mode;\r
- public Sound Sound { get { return sound; } }\r
- private Boolean loopSound = false;\r
- /// <summary>\r
- /// The individual volume setting for THIS object\r
- /// </summary>\r
- private float volumeSetting = 0.5f;\r
-\r
- /// <summary>\r
- /// Creates a new sound object\r
- /// </summary>\r
- /// <param name="system">Sound system</param>\r
- public BufferSound(UUID objectId, UUID soundId, bool loop, bool global, Vector3 worldpos, float vol)\r
- : base()\r
- {\r
- InitBuffer(objectId, soundId, loop, global, worldpos, vol);\r
- }\r
-\r
- public BufferSound(UUID objectId, UUID soundId, bool loop, bool global, Vector3d worldpos, float vol)\r
- : base()\r
- {\r
- InitBuffer(objectId, soundId, loop, global, new Vector3(worldpos), vol);\r
- }\r
-\r
- private void InitBuffer(UUID objectId, UUID soundId, bool loop, bool global, Vector3 worldpos, float vol)\r
- {\r
- if (manager == null || !manager.SoundSystemAvailable) return;\r
-\r
- // Do not let this get garbage-collected.\r
- lock (allBuffers)\r
- allBuffers[objectId] = this;\r
-\r
- ContainerId = objectId;\r
- Id = soundId;\r
- position = FromOMVSpace(worldpos);\r
- volumeSetting = vol;\r
- loopSound = loop;\r
-\r
- Logger.Log(\r
- String.Format(\r
- "Playing sound at <{0:0.0},{1:0.0},{2:0.0}> ID {3}",\r
- position.x,\r
- position.y,\r
- position.z,\r
- Id.ToString()),\r
- Helpers.LogLevel.Debug);\r
-\r
- // Set flags to determine how it will be played.\r
- mode = FMOD.MODE.SOFTWARE | // Need software processing for all the features\r
- FMOD.MODE._3D | // Need 3D effects for placement\r
- FMOD.MODE.OPENMEMORY; // Use sound data in memory\r
-\r
- // Set coordinate space interpretation.\r
- if (global)\r
- mode |= FMOD.MODE._3D_WORLDRELATIVE;\r
- else\r
- mode |= FMOD.MODE._3D_HEADRELATIVE;\r
-\r
- if (loopSound)\r
- mode |= FMOD.MODE.LOOP_NORMAL;\r
-\r
- // Fetch the sound data.\r
- manager.Instance.Client.Assets.RequestAsset(\r
- Id,\r
- AssetType.Sound,\r
- false,\r
- new AssetManager.AssetReceivedCallback(Assets_OnSoundReceived));\r
- }\r
-\r
- public static void Kill(UUID id)\r
- {\r
- if (allBuffers.ContainsKey(id))\r
- {\r
- BufferSound bs = allBuffers[id];\r
- bs.StopSound(true);\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// Stop all playing sounds in the environment\r
- /// </summary>\r
- public static void KillAll()\r
- {\r
- // Make a list from the dictionary so we do not get a deadlock\r
- // on it when removing entries.\r
- List<BufferSound> list = new List<BufferSound>(allBuffers.Values);\r
-\r
- foreach (BufferSound s in list)\r
- {\r
- s.StopSound();\r
- }\r
-\r
- List<MediaObject> objs = new List<MediaObject>(allChannels.Values);\r
- foreach (MediaObject obj in objs)\r
- {\r
- if (obj is BufferSound)\r
- ((BufferSound)obj).StopSound();\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// Adjust volumes of all playing sounds to observe the new global sound volume\r
- /// </summary>\r
- public static void AdjustVolumes()\r
- {\r
- // Make a list from the dictionary so we do not get a deadlock\r
- List<BufferSound> list = new List<BufferSound>(allBuffers.Values);\r
-\r
- foreach (BufferSound s in list)\r
- {\r
- s.AdjustVolume();\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// Adjust the volume of THIS sound when all are being adjusted.\r
- /// </summary>\r
- private void AdjustVolume()\r
- {\r
- Volume = volumeSetting * AllObjectVolume;\r
- }\r
-\r
- // A simpler constructor used by PreFetchSound.\r
- public BufferSound(UUID soundId)\r
- : base()\r
- {\r
- prefetchOnly = true;\r
- ContainerId = UUID.Zero;\r
- Id = soundId;\r
-\r
- manager.Instance.Client.Assets.RequestAsset(\r
- Id,\r
- AssetType.Sound,\r
- false,\r
- new AssetManager.AssetReceivedCallback(Assets_OnSoundReceived));\r
- }\r
-\r
- /// <summary>\r
- /// Releases resources of this sound object\r
- /// </summary>\r
- public override void Dispose()\r
- {\r
- base.Dispose();\r
- }\r
-\r
- /**\r
- * Handle arrival of a sound resource.\r
- */\r
- void Assets_OnSoundReceived(AssetDownload transfer, Asset asset)\r
- {\r
- if (transfer.Success)\r
- {\r
- // If this was a Prefetch, just stop here.\r
- if (prefetchOnly)\r
- {\r
- return;\r
- }\r
-\r
- // Logger.Log("Opening sound " + Id.ToString(), Helpers.LogLevel.Debug);\r
-\r
- // Decode the Ogg Vorbis buffer.\r
- AssetSound s = asset as AssetSound;\r
- s.Decode();\r
- byte[] data = s.AssetData;\r
-\r
- // Describe the data to FMOD\r
- extraInfo.length = (uint)data.Length;\r
- extraInfo.cbsize = Marshal.SizeOf(extraInfo);\r
-\r
- invoke(new SoundDelegate(delegate\r
- {\r
- try\r
- {\r
- // Create an FMOD sound of this Ogg data.\r
- FMODExec(system.createSound(\r
- data,\r
- mode,\r
- ref extraInfo,\r
- ref sound));\r
-\r
- // Register for callbacks.\r
- RegisterSound(sound);\r
-\r
-\r
- // If looping is requested, loop the entire thing.\r
- if (loopSound)\r
- {\r
- uint soundlen = 0;\r
- FMODExec(sound.getLength(ref soundlen, TIMEUNIT.PCM));\r
- FMODExec(sound.setLoopPoints(0, TIMEUNIT.PCM, soundlen - 1, TIMEUNIT.PCM));\r
- FMODExec(sound.setLoopCount(-1));\r
- }\r
-\r
- // Allocate a channel and set initial volume. Initially paused.\r
- FMODExec(system.playSound(CHANNELINDEX.FREE, sound, true, ref channel));\r
-#if TRACE_SOUND\r
- Logger.Log(\r
- String.Format("Channel {0} for {1} assigned to {2}",\r
- channel.getRaw().ToString("X"),\r
- sound.getRaw().ToString("X"),\r
- Id),\r
- Helpers.LogLevel.Debug);\r
-#endif\r
- RegisterChannel(channel);\r
-\r
- FMODExec(channel.setVolume(volumeSetting * AllObjectVolume));\r
-\r
- // Take note of when the sound is finished playing.\r
- FMODExec(channel.setCallback(endCallback));\r
-\r
- // Set attenuation limits.\r
- FMODExec(sound.set3DMinMaxDistance(\r
- 1.2f, // Any closer than this gets no louder\r
- 100.0f)); // Further than this gets no softer.\r
-\r
- // Set the sound point of origin. This is in SIM coordinates.\r
- FMODExec(channel.set3DAttributes(ref position, ref ZeroVector));\r
-\r
- // Turn off pause mode. The sound will start playing now.\r
- FMODExec(channel.setPaused(false));\r
- }\r
- catch (Exception ex)\r
- {\r
- Logger.Log("Error playing sound: ", Helpers.LogLevel.Error, ex);\r
- }\r
- }));\r
- }\r
- else\r
- {\r
- Logger.Log("Failed to download sound: " + transfer.Status.ToString(),\r
- Helpers.LogLevel.Error);\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// Handles stop sound even from FMOD\r
- /// </summary>\r
- /// <returns>RESULT.OK</returns>\r
- protected override RESULT EndCallbackHandler()\r
- {\r
- StopSound();\r
- return RESULT.OK;\r
- }\r
-\r
- protected void StopSound()\r
- {\r
- StopSound(false);\r
- }\r
-\r
- protected void StopSound(bool blocking)\r
- {\r
- ManualResetEvent stopped = null;\r
- if (blocking)\r
- stopped = new ManualResetEvent(false);\r
-\r
- finished = true;\r
-\r
- invoke(new SoundDelegate(delegate\r
- {\r
- string chanStr = "none";\r
- string soundStr = "none";\r
-\r
- // Release the buffer to avoid a big memory leak.\r
- if (channel != null)\r
- {\r
- lock (allChannels)\r
- allChannels.Remove(channel.getRaw());\r
- chanStr = channel.getRaw().ToString("X");\r
- channel.stop();\r
- channel = null;\r
- }\r
-\r
- if (sound != null)\r
- {\r
- soundStr = sound.getRaw().ToString("X");\r
- sound.release();\r
- sound = null;\r
- }\r
-#if TRACE_SOUND\r
- Logger.Log(String.Format("Removing channel {0} sound {1} ID {2}",\r
- chanStr,\r
- soundStr,\r
- Id.ToString()),\r
- Helpers.LogLevel.Debug);\r
-#endif\r
- lock (allBuffers)\r
- allBuffers.Remove(ContainerId);\r
-\r
- if (blocking)\r
- stopped.Set();\r
- }));\r
-\r
- if (blocking)\r
- stopped.WaitOne();\r
-\r
- }\r
-\r
- }\r
-}\r
+//
+// Radegast Metaverse Client
+// Copyright (c) 2009-2012, Radegast Development Team
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the application "Radegast", nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// $Id: Sound.cs 502 2010-03-14 23:13:46Z latifer $
+//
+// Uncomment this to get lots more logging
+//#define TRACE_SOUND
+using System;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using FMOD;
+using OpenMetaverse;
+using OpenMetaverse.Assets;
+using System.Threading;
+
+namespace Radegast.Media
+{
+
+ public class BufferSound : MediaObject
+ {
+ private UUID Id;
+ private UUID ContainerId;
+ private Boolean prefetchOnly = false;
+ private FMOD.MODE mode;
+ public Sound Sound { get { return sound; } }
+ private Boolean loopSound = false;
+ /// <summary>
+ /// The individual volume setting for THIS object
+ /// </summary>
+ private float volumeSetting = 0.5f;
+
+ /// <summary>
+ /// Creates a new sound object
+ /// </summary>
+ /// <param name="system">Sound system</param>
+ public BufferSound(UUID objectId, UUID soundId, bool loop, bool global, Vector3 worldpos, float vol)
+ : base()
+ {
+ InitBuffer(objectId, soundId, loop, global, worldpos, vol);
+ }
+
+ public BufferSound(UUID objectId, UUID soundId, bool loop, bool global, Vector3d worldpos, float vol)
+ : base()
+ {
+ InitBuffer(objectId, soundId, loop, global, new Vector3(worldpos), vol);
+ }
+
+ private void InitBuffer(UUID objectId, UUID soundId, bool loop, bool global, Vector3 worldpos, float vol)
+ {
+ if (manager == null || !manager.SoundSystemAvailable) return;
+
+ // Do not let this get garbage-collected.
+ lock (allBuffers)
+ allBuffers[objectId] = this;
+
+ ContainerId = objectId;
+ Id = soundId;
+ position = FromOMVSpace(worldpos);
+ volumeSetting = vol;
+ loopSound = loop;
+
+ Logger.Log(
+ String.Format(
+ "Playing sound at <{0:0.0},{1:0.0},{2:0.0}> ID {3}",
+ position.x,
+ position.y,
+ position.z,
+ Id.ToString()),
+ Helpers.LogLevel.Debug);
+
+ // Set flags to determine how it will be played.
+ mode = FMOD.MODE.SOFTWARE | // Need software processing for all the features
+ FMOD.MODE._3D | // Need 3D effects for placement
+ FMOD.MODE.OPENMEMORY; // Use sound data in memory
+
+ // Set coordinate space interpretation.
+ if (global)
+ mode |= FMOD.MODE._3D_WORLDRELATIVE;
+ else
+ mode |= FMOD.MODE._3D_HEADRELATIVE;
+
+ if (loopSound)
+ mode |= FMOD.MODE.LOOP_NORMAL;
+
+ // Fetch the sound data.
+ manager.Instance.Client.Assets.RequestAsset(
+ Id,
+ AssetType.Sound,
+ false,
+ new AssetManager.AssetReceivedCallback(Assets_OnSoundReceived));
+ }
+
+ public static void Kill(UUID id)
+ {
+ if (allBuffers.ContainsKey(id))
+ {
+ BufferSound bs = allBuffers[id];
+ bs.StopSound(true);
+ }
+ }
+
+ /// <summary>
+ /// Stop all playing sounds in the environment
+ /// </summary>
+ public static void KillAll()
+ {
+ // Make a list from the dictionary so we do not get a deadlock
+ // on it when removing entries.
+ List<BufferSound> list = new List<BufferSound>(allBuffers.Values);
+
+ foreach (BufferSound s in list)
+ {
+ s.StopSound();
+ }
+
+ List<MediaObject> objs = new List<MediaObject>(allChannels.Values);
+ foreach (MediaObject obj in objs)
+ {
+ if (obj is BufferSound)
+ ((BufferSound)obj).StopSound();
+ }
+ }
+
+ /// <summary>
+ /// Adjust volumes of all playing sounds to observe the new global sound volume
+ /// </summary>
+ public static void AdjustVolumes()
+ {
+ // Make a list from the dictionary so we do not get a deadlock
+ List<BufferSound> list = new List<BufferSound>(allBuffers.Values);
+
+ foreach (BufferSound s in list)
+ {
+ s.AdjustVolume();
+ }
+ }
+
+ /// <summary>
+ /// Adjust the volume of THIS sound when all are being adjusted.
+ /// </summary>
+ private void AdjustVolume()
+ {
+ Volume = volumeSetting * AllObjectVolume;
+ }
+
+ // A simpler constructor used by PreFetchSound.
+ public BufferSound(UUID soundId)
+ : base()
+ {
+ prefetchOnly = true;
+ ContainerId = UUID.Zero;
+ Id = soundId;
+
+ manager.Instance.Client.Assets.RequestAsset(
+ Id,
+ AssetType.Sound,
+ false,
+ new AssetManager.AssetReceivedCallback(Assets_OnSoundReceived));
+ }
+
+ /// <summary>
+ /// Releases resources of this sound object
+ /// </summary>
+ public override void Dispose()
+ {
+ base.Dispose();
+ }
+
+ /**
+ * Handle arrival of a sound resource.
+ */
+ void Assets_OnSoundReceived(AssetDownload transfer, Asset asset)
+ {
+ if (transfer.Success)
+ {
+ // If this was a Prefetch, just stop here.
+ if (prefetchOnly)
+ {
+ return;
+ }
+
+ // Logger.Log("Opening sound " + Id.ToString(), Helpers.LogLevel.Debug);
+
+ // Decode the Ogg Vorbis buffer.
+ AssetSound s = asset as AssetSound;
+ s.Decode();
+ byte[] data = s.AssetData;
+
+ // Describe the data to FMOD
+ extraInfo.length = (uint)data.Length;
+ extraInfo.cbsize = Marshal.SizeOf(extraInfo);
+
+ invoke(new SoundDelegate(delegate
+ {
+ try
+ {
+ // Create an FMOD sound of this Ogg data.
+ FMODExec(system.createSound(
+ data,
+ mode,
+ ref extraInfo,
+ ref sound));
+
+ // Register for callbacks.
+ RegisterSound(sound);
+
+
+ // If looping is requested, loop the entire thing.
+ if (loopSound)
+ {
+ uint soundlen = 0;
+ FMODExec(sound.getLength(ref soundlen, TIMEUNIT.PCM));
+ FMODExec(sound.setLoopPoints(0, TIMEUNIT.PCM, soundlen - 1, TIMEUNIT.PCM));
+ FMODExec(sound.setLoopCount(-1));
+ }
+
+ // Allocate a channel and set initial volume. Initially paused.
+ FMODExec(system.playSound(CHANNELINDEX.FREE, sound, true, ref channel));
+#if TRACE_SOUND
+ Logger.Log(
+ String.Format("Channel {0} for {1} assigned to {2}",
+ channel.getRaw().ToString("X"),
+ sound.getRaw().ToString("X"),
+ Id),
+ Helpers.LogLevel.Debug);
+#endif
+ RegisterChannel(channel);
+
+ FMODExec(channel.setVolume(volumeSetting * AllObjectVolume));
+
+ // Take note of when the sound is finished playing.
+ FMODExec(channel.setCallback(endCallback));
+
+ // Set attenuation limits.
+ FMODExec(sound.set3DMinMaxDistance(
+ 1.2f, // Any closer than this gets no louder
+ 100.0f)); // Further than this gets no softer.
+
+ // Set the sound point of origin. This is in SIM coordinates.
+ FMODExec(channel.set3DAttributes(ref position, ref ZeroVector));
+
+ // Turn off pause mode. The sound will start playing now.
+ FMODExec(channel.setPaused(false));
+ }
+ catch (Exception ex)
+ {
+ Logger.Log("Error playing sound: ", Helpers.LogLevel.Error, ex);
+ }
+ }));
+ }
+ else
+ {
+ Logger.Log("Failed to download sound: " + transfer.Status.ToString(),
+ Helpers.LogLevel.Error);
+ }
+ }
+
+ /// <summary>
+ /// Handles stop sound even from FMOD
+ /// </summary>
+ /// <returns>RESULT.OK</returns>
+ protected override RESULT EndCallbackHandler()
+ {
+ StopSound();
+ return RESULT.OK;
+ }
+
+ protected void StopSound()
+ {
+ StopSound(false);
+ }
+
+ protected void StopSound(bool blocking)
+ {
+ ManualResetEvent stopped = null;
+ if (blocking)
+ stopped = new ManualResetEvent(false);
+
+ finished = true;
+
+ invoke(new SoundDelegate(delegate
+ {
+ string chanStr = "none";
+ string soundStr = "none";
+
+ // Release the buffer to avoid a big memory leak.
+ if (channel != null)
+ {
+ lock (allChannels)
+ allChannels.Remove(channel.getRaw());
+ chanStr = channel.getRaw().ToString("X");
+ channel.stop();
+ channel = null;
+ }
+
+ if (sound != null)
+ {
+ soundStr = sound.getRaw().ToString("X");
+ sound.release();
+ sound = null;
+ }
+#if TRACE_SOUND
+ Logger.Log(String.Format("Removing channel {0} sound {1} ID {2}",
+ chanStr,
+ soundStr,
+ Id.ToString()),
+ Helpers.LogLevel.Debug);
+#endif
+ lock (allBuffers)
+ allBuffers.Remove(ContainerId);
+
+ if (blocking)
+ stopped.Set();
+ }));
+
+ if (blocking)
+ stopped.WaitOne();
+
+ }
+
+ }
+}