OSDN Git Service

Remove timer loop from stream class.
authorMojito Sorbet <mojitotech@gmail.com>
Thu, 6 May 2010 18:05:45 +0000 (18:05 +0000)
committerMojito Sorbet <mojitotech@gmail.com>
Thu, 6 May 2010 18:05:45 +0000 (18:05 +0000)
Preliminary shared buffer support.

git-svn-id: https://radegast.googlecode.com/svn/branches/sounds@610 f7a694da-4d33-11de-9ad6-1127a62b9fcd

Radegast/Core/Media/BufferSound.cs
Radegast/Core/Media/MediaManager.cs
Radegast/Core/Media/MediaObject.cs
Radegast/Core/Media/Speech.cs
Radegast/Core/Media/Stream.cs

index 3dfb74e..7616f39 100644 (file)
@@ -38,116 +38,133 @@ namespace Radegast.Media
 \r
     public class BufferSound : MediaObject\r
     {\r
-       /// <summary>\r
-        /// Is sound currently playing\r
-        /// </summary>\r
-        public bool Playing { get { return playing; } }\r
-        private bool playing = false;\r
-\r
-        /// <summary>\r
-        /// Is sound currently paused\r
-        /// </summary>\r
-        public bool Paused { get { return paused; } }\r
-        private bool paused = false;\r
-\r
-        private bool soundcreated = false;\r
-        private System.Timers.Timer timer;\r
-\r
         private FMOD.CREATESOUNDEXINFO extendedInfo;\r
+        private FMOD.VECTOR position;\r
+\r
+        public Sound Sound { get { return sound; } }\r
 \r
         /// <summary>\r
         /// Creates a new sound object\r
         /// </summary>\r
         /// <param name="system">Sound system</param>\r
-        public BufferSound()\r
+        public BufferSound(byte[] buffer, bool loop, bool global, Vector3 worldpos )\r
             :base()\r
         {\r
-            timer = new System.Timers.Timer();\r
-            timer.Interval = 50d;\r
-            timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);\r
-            timer.Enabled = false;\r
-        }\r
+            position = FromOMVSpace(worldpos);\r
 \r
+            extendedInfo.format = SOUND_FORMAT.PCM16;\r
+            extendedInfo.nonblockcallback += DispatchNonBlockCallback;\r
 \r
-        /// <summary>\r
-        /// Releases resources of this sound object\r
-        /// </summary>\r
-        public override void Dispose()\r
-        {\r
-            if (timer != null)\r
-            {\r
-                timer.Enabled = false;\r
-                timer.Dispose();\r
-                timer = null;\r
-            }\r
-\r
-            if (sound != null)\r
-            {\r
-                sound.release();\r
-                sound = null;\r
-            }\r
-            base.Dispose();\r
-        }\r
+            // Set flags to determine how it will be played.\r
+            FMOD.MODE mode = FMOD.MODE.SOFTWARE | FMOD.MODE._3D;\r
+            position = FromOMVSpace(worldpos);\r
 \r
-        /// <summary>\r
-        /// Plays audio buffer\r
-        /// </summary>\r
-        /// <param name="url">URL of the stream</param>\r
-        public void Play( byte[] buffer, bool loop)\r
-        {\r
-            extendedInfo.format = SOUND_FORMAT.PCM16;\r
+            // Set coordinate space interpretation.\r
+            if (global)\r
+                mode |= FMOD.MODE._3D_WORLDRELATIVE;\r
+            else\r
+                mode |= FMOD.MODE._3D_HEADRELATIVE;\r
 \r
             invoke(new SoundDelegate(delegate\r
             {\r
-                MediaManager.FMODExec(system.createSound(buffer,\r
-                    (MODE.HARDWARE | MODE._3D | MODE.NONBLOCKING),\r
+                FMODExec(system.createSound(buffer,\r
+                    mode,\r
                     ref extendedInfo,\r
                     ref sound));\r
+\r
+                // Register for callbacks.\r
+                RegisterSound(sound);\r
+\r
+                if (loop)\r
+                    FMODExec(sound.setLoopCount(-1));\r
             }));\r
         }\r
 \r
+        public BufferSound( BufferSound shared, bool loop, Vector3 worldpos)\r
+            : base()\r
+        {\r
+            position = FromOMVSpace(worldpos);\r
+\r
+            // Share a previously loaded sound.\r
+            Cloned = true;\r
+            sound = shared.Sound;\r
+\r
+            NonBlockCallbackHandler(RESULT.OK );\r
+        }\r
+\r
         /// <summary>\r
-        /// Toggles sound pause\r
+        /// Releases resources of this sound object\r
         /// </summary>\r
-        public void TogglePaused()\r
+        public override void Dispose()\r
         {\r
-            if (channel != null)\r
-            {\r
-                channel.getPaused(ref paused);\r
-                channel.setPaused(!paused);\r
-            }\r
+            base.Dispose();\r
         }\r
 \r
-        void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)\r
+        protected override RESULT NonBlockCallbackHandler(RESULT instatus)\r
         {\r
-            OPENSTATE openstate = 0;\r
-            uint percentbuffered = 0;\r
-            bool starving = false;\r
+            if (instatus != RESULT.OK)\r
+            {\r
+                Logger.Log("Error opening stream: ", Helpers.LogLevel.Debug);\r
+                return RESULT.OK;\r
+            }\r
 \r
             try\r
             {\r
-                if (soundcreated)\r
-                {\r
-                    MediaManager.FMODExec(sound.getOpenState(ref openstate, ref percentbuffered, ref starving));\r
-\r
-                    if (openstate == OPENSTATE.READY && channel == null)\r
-                    {\r
-                        MediaManager.FMODExec(system.playSound(CHANNELINDEX.FREE, sound, false, ref channel));\r
-                        MediaManager.FMODExec(channel.setVolume(volume));\r
-                    }\r
-                }\r
-\r
-                if (system != null)\r
-                {\r
-                    system.update();\r
-                }\r
+                // Allocate a channel and set initial volume.  Initially paused.\r
+                FMODExec(system.playSound(CHANNELINDEX.FREE, sound, true, ref channel));\r
+                volume = 0.5f;\r
+                FMODExec(channel.setVolume(volume));\r
+\r
+                // Take note of when the sound is finished playing.\r
+                FMODExec(channel.setCallback(EndCallback));\r
+\r
+                // Set speech attenuation limits.\r
+                FMODExec(sound.set3DMinMaxDistance(\r
+                    1.2f,       // Any closer than this gets no louder\r
+                    8.0f));     // Further than this gets no softer.\r
+\r
+                // Set the sound point of origin.\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
-                playing = paused = false;\r
-                timer.Enabled = false;\r
-                Logger.Log("Error playing sound: ", Helpers.LogLevel.Debug, ex);\r
+                Logger.Log("Error starting stream: ", Helpers.LogLevel.Debug, ex);\r
+            }\r
+\r
+            return RESULT.OK;\r
+        }\r
+\r
+        /// <summary>\r
+        /// Callback handler for reaching the end of a sound.\r
+        /// </summary>\r
+        /// <param name="channelraw"></param>\r
+        /// <param name="type"></param>\r
+        /// <param name="commanddata1"></param>\r
+        /// <param name="commanddata2"></param>\r
+        /// <returns></returns>\r
+        private RESULT EndCallback(\r
+            IntPtr channelraw,\r
+            CHANNEL_CALLBACKTYPE type,\r
+            IntPtr commanddata1,\r
+            IntPtr commanddata2)\r
+        {\r
+            // Ignore other callback types.\r
+            if (type != CHANNEL_CALLBACKTYPE.END) return RESULT.OK;\r
+\r
+            // Release the buffer to avoid a big memory leak.\r
+            //TODO Should be a reference count on cloned sound buffers.\r
+            if (!Cloned && sound != null)\r
+            {\r
+                sound.release();\r
+                sound = null;\r
             }\r
+            channel = null;\r
+\r
+            return RESULT.OK;\r
         }\r
+\r
     }\r
 }\r
index 3979a2c..4217ab3 100644 (file)
@@ -45,12 +45,15 @@ namespace Radegast.Media
         public bool SoundSystemAvailable { get { return soundSystemAvailable; } }
         private bool soundSystemAvailable = false;
         private Thread soundThread;
+        RadegastInstance instance;
 
         private List<MediaObject> sounds = new List<MediaObject>();
 
         public MediaManager(RadegastInstance instance)
             : base()
         {
+            this.instance = instance;
+
             // Start the background thread that does all the FMOD calls.
             soundThread = new Thread(new ThreadStart(CommandLoop));
             soundThread.IsBackground = true;
@@ -62,6 +65,16 @@ namespace Radegast.Media
         {
             SoundDelegate action;
 
+            UpVector.x = 0.0f;
+            UpVector.y = 1.0f;
+            UpVector.z = 0.0f;
+            ZeroVector.x = 0.0f;
+            ZeroVector.y = 0.0f;
+            ZeroVector.z = 0.0f;
+
+            allSounds = new Dictionary<IntPtr,MediaObject>();
+            allChannels = new Dictionary<IntPtr, MediaObject>();
+
             InitFMOD();
             if (!this.soundSystemAvailable) return;
 
@@ -93,6 +106,9 @@ namespace Radegast.Media
             }
         }
 
+        /// <summary>
+        /// Initialize the FMOD sound system.
+        /// </summary>
         private void InitFMOD()
         {
             try
@@ -204,13 +220,56 @@ namespace Radegast.Media
             base.Dispose();
         }
 
-        public static void FMODExec(FMOD.RESULT result)
+        private void OnListenerUpdate()
         {
-            if (result != FMOD.RESULT.OK)
+            Vector3 lastpos = new Vector3(0.0f, 0.0f, 0.0f );
+            float lastface = 0.0f;
+
+            while (true)
             {
-                throw new MediaException("FMOD error! " + result + " - " + FMOD.Error.String(result));
+                // TODO would be nice if there was an event for this.
+                Thread.Sleep(500);
+
+                if (system == null) continue;
+
+                AgentManager my = instance.Client.Self;
+
+                // If we are standing still, nothing to update.
+                if (my.SimPosition.Equals( lastpos ) &&
+                    my.Movement.BodyRotation.W == lastface)
+                    continue;
+
+                lastpos = my.SimPosition;
+                lastface = my.Movement.BodyRotation.W;
+
+                // Convert coordinate spaces.
+                FMOD.VECTOR listenerpos = FromOMVSpace(my.SimPosition);
+
+                // Get azimuth from the facing Quaternion.  Note we assume the
+                // avatar is standing upright.  Avatars in unusual positions
+                // hear things from unpredictable directions.
+                // By definition, facing.W = Cos( angle/2 )
+                double angle = 2.0 * Math.Acos(my.Movement.BodyRotation.W);
+                FMOD.VECTOR forward = new FMOD.VECTOR();
+                forward.x = (float)Math.Asin(angle);
+                forward.y = 0.0f;
+                forward.z = (float)Math.Acos(angle);
+
+                // Tell FMOD the new orientation.
+                invoke( new SoundDelegate( delegate
+                {
+                    MediaManager.FMODExec( system.set3DListenerAttributes(
+                        0,
+                        ref listenerpos,       // Position
+                        ref ZeroVector,                // Velocity
+                        ref forward,           // Facing direction
+                        ref UpVector ));       // Top of head
+
+                    system.update();
+                }));
             }
         }
+
     }
 
     public class MediaException : Exception
index 7d2e4e0..6ca67f6 100644 (file)
@@ -59,12 +59,17 @@ namespace Radegast.Media
         public Channel FMODChannel { get { return channel; } }
         protected Channel channel = null;
 
+        protected FMOD.CREATESOUNDEXINFO extraInfo;
+
         /// <summary>
         /// FMOD sound object, should not be used directly, add methods to Radegast.Media.Sound
         /// </summary>
         public FMOD.Sound FMODSound { get { return sound; } }
         protected FMOD.Sound sound = null;
 
+        protected static FMOD.VECTOR UpVector;
+        protected static FMOD.VECTOR ZeroVector;
+
         public FMOD.System FMODSystem { get { return system; } }
         /// <summary>
         /// Base FMOD system object, of which there is only one.
@@ -73,11 +78,15 @@ namespace Radegast.Media
 
         public MediaObject()
         {
+            extraInfo = new FMOD.CREATESOUNDEXINFO();
+            extraInfo.cbsize = 10;
+
         }
 
+        protected bool Cloned = false;
         public virtual void Dispose()
         {
-            if (sound != null)
+            if (!Cloned && sound != null)
             {
                 sound.release();
                 sound = null;
@@ -110,7 +119,8 @@ namespace Radegast.Media
         ///  Common actgions for all sound types.
         /// </summary>
         protected float volume = 0.5f;
-        public float Volume {
+        public float Volume
+        {
             get
             {
                 return volume;
@@ -124,5 +134,110 @@ namespace Radegast.Media
                 }));
             }
         }
+
+        public void Stop()
+        {
+            if (channel != null)
+            {
+                invoke(new SoundDelegate(delegate
+                {
+                    MediaManager.FMODExec(channel.stop());
+                }));
+            }
+        }
+
+        /// <summary>
+        /// Convert OpenMetaVerse to FMOD coordinate space.
+        /// </summary>
+        /// <param name="omvV"></param>
+        /// <returns></returns>
+        protected FMOD.VECTOR FromOMVSpace(OpenMetaverse.Vector3 omvV)
+        {
+            // OMV  X is forward, Y is left, Z is up.
+            // FMOD Z is forward, X is right, Y is up.
+            FMOD.VECTOR v = new FMOD.VECTOR();
+            v.x = -omvV.Y;
+            v.y = omvV.Z;
+            v.z = omvV.X;
+            return v;
+        }
+
+        protected static Dictionary<IntPtr,MediaObject> allSounds;
+        protected static Dictionary<IntPtr, MediaObject> allChannels;
+        protected void RegisterSound(FMOD.Sound sound)
+        {
+            allSounds.Add(sound.getRaw(), this);
+        }
+        protected void RegisterChannel(FMOD.Channel channel)
+        {
+            allSounds.Add(channel.getRaw(), this);
+        }
+        protected void UnRegisterSound()
+        {
+            if (sound == null) return;
+            IntPtr raw = sound.getRaw();
+            if (allSounds.ContainsKey( raw ))
+            {
+                allSounds.Remove( raw );
+            }
+        }
+        protected void UnRegisterChannel()
+        {
+            if (channel == null) return;
+            IntPtr raw = channel.getRaw();
+            if (allChannels.ContainsKey(raw))
+            {
+                allChannels.Remove(raw);
+            }
+        }
+
+        /// <summary>
+        /// A callback for asynchronous FMOD calls.
+        /// </summary>
+        /// <returns></returns>
+        protected virtual FMOD.RESULT NonBlockCallbackHandler( RESULT result ) { return RESULT.OK; }
+        protected virtual FMOD.RESULT EndCallbackHandler() { return RESULT.OK; }
+
+        protected RESULT DispatchNonBlockCallback(IntPtr soundraw, RESULT result)
+        {
+            if (allSounds.ContainsKey(soundraw))
+            {
+                MediaObject sndobj = allSounds[soundraw];
+                return sndobj.NonBlockCallbackHandler( result );
+            }
+
+            return FMOD.RESULT.OK;
+        }
+
+        protected RESULT DispatchEndCallback(
+            IntPtr channelraw,
+            CHANNEL_CALLBACKTYPE type,
+            IntPtr commanddata1,
+            IntPtr commanddata2)
+        {
+            // Ignore other callback types.
+            if (type != CHANNEL_CALLBACKTYPE.END) return RESULT.OK;
+
+            if (allChannels.ContainsKey(channelraw))
+            {
+                MediaObject sndobj = allSounds[channelraw];
+                return sndobj.EndCallbackHandler();
+            }
+
+            return RESULT.OK;
+        }
+
+        public delegate RESULT SOUND_NONBLOCKCALLBACK(IntPtr soundraw, RESULT result);
+
+        protected static void FMODExec(FMOD.RESULT result)
+        {
+            if (result != FMOD.RESULT.OK)
+            {
+                throw new MediaException("FMOD error! " + result + " - " + FMOD.Error.String(result));
+            }
+        }
+
+
     }
 }
index 8189ae0..e8b94b3 100644 (file)
@@ -101,7 +101,7 @@ namespace Radegast.Media
         {\r
             if (!soundcreated)\r
             {\r
-                MediaManager.FMODExec(system.createSound(filename,\r
+                FMODExec(system.createSound(filename,\r
                     (MODE.HARDWARE | MODE._2D | MODE.CREATESTREAM | MODE.NONBLOCKING),\r
                     ref sound));\r
                 soundcreated = true;\r
@@ -132,12 +132,12 @@ namespace Radegast.Media
             {\r
                 if (soundcreated)\r
                 {\r
-                    MediaManager.FMODExec(sound.getOpenState(ref openstate, ref percentbuffered, ref starving));\r
+                    FMODExec(sound.getOpenState(ref openstate, ref percentbuffered, ref starving));\r
 \r
                     if (openstate == OPENSTATE.READY && channel == null)\r
                     {\r
-                        MediaManager.FMODExec(system.playSound(CHANNELINDEX.FREE, sound, false, ref channel));\r
-                        MediaManager.FMODExec(channel.setVolume(volume));\r
+                        FMODExec(system.playSound(CHANNELINDEX.FREE, sound, false, ref channel));\r
+                        FMODExec(channel.setVolume(volume));\r
                     }\r
                 }\r
 \r
index 39a5240..94bd9f6 100644 (file)
@@ -50,25 +50,6 @@ namespace Radegast.Media
     public class Stream : MediaObject\r
     {\r
         /// <summary>\r
-        /// Returns current position of the sound played in ms\r
-        /// Do not confuse with the spatial Position on other suonds.\r
-        /// </summary>\r
-        public uint Position { get { return position; } }\r
-        private uint position = 0;\r
-\r
-        /// <summary>\r
-        /// Is sound currently playing\r
-        /// </summary>\r
-        public bool Playing { get { return playing; } }\r
-        private bool playing = false;\r
-\r
-        /// <summary>\r
-        /// Is sound currently paused\r
-        /// </summary>\r
-        public bool Paused { get { return paused; } }\r
-        private bool paused = false;\r
-\r
-        /// <summary>\r
         /// Fired when a stream meta data is received\r
         /// </summary>\r
         /// <param name="sender">Sender</param>\r
@@ -80,8 +61,6 @@ namespace Radegast.Media
         /// </summary>\r
         public event StreamInfoCallback OnStreamInfo;\r
 \r
-        private System.Timers.Timer timer;\r
-\r
         /// <summary>\r
         /// Creates a new sound object\r
         /// </summary>\r
@@ -89,10 +68,6 @@ namespace Radegast.Media
         public Stream()\r
             :base()\r
         {\r
-            timer = new System.Timers.Timer();\r
-            timer.Interval = 50d;\r
-            timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);\r
-            timer.Enabled = false;\r
         }\r
 \r
 \r
@@ -101,13 +76,6 @@ namespace Radegast.Media
         /// </summary>\r
         public override void Dispose()\r
         {\r
-            if (timer != null)\r
-            {\r
-                timer.Enabled = false;\r
-                timer.Dispose();\r
-                timer = null;\r
-            }\r
-\r
             base.Dispose();\r
         }\r
 \r
@@ -117,99 +85,87 @@ namespace Radegast.Media
         /// <param name="url">URL of the stream</param>\r
         public void PlayStream(string url)\r
         {\r
-            if (!Active)\r
+            // Stop old stream first.\r
+            if (channel != null)\r
             {\r
-                invoke( new SoundDelegate(\r
-                    delegate {\r
-                        MediaManager.FMODExec(system.createSound(url,\r
-                            (MODE.HARDWARE | MODE._2D | MODE.CREATESTREAM | MODE.NONBLOCKING), ref sound));\r
-  \r
-                        timer.Enabled = true;\r
-                        timer_Elapsed(null, null);\r
+                invoke(new SoundDelegate(\r
+                    delegate\r
+                    {\r
+                        FMODExec(channel.stop());\r
+                        channel = null;\r
+                        UnRegisterSound();\r
+                        FMODExec(sound.release());\r
+                        sound = null;\r
                     }));\r
             }\r
+\r
+            extraInfo.nonblockcallback = new FMOD.SOUND_NONBLOCKCALLBACK(DispatchNonBlockCallback);\r
+\r
+            invoke( new SoundDelegate(\r
+                delegate {\r
+                    FMODExec(\r
+                        system.createSound(url,\r
+                        (MODE.HARDWARE | MODE._2D | MODE.CREATESTREAM | MODE.NONBLOCKING),\r
+                        ref extraInfo,\r
+                        ref sound));\r
+                    // Register for callbacks.\r
+                    RegisterSound(sound);\r
+                }));\r
         }\r
 \r
         /// <summary>\r
-        /// Toggles sound pause\r
+        /// Callback when a stream has been loaded\r
         /// </summary>\r
-        public void TogglePaused()\r
+        /// <param name="instatus"></param>\r
+        /// <returns></returns>\r
+        protected override RESULT NonBlockCallbackHandler(RESULT instatus)\r
         {\r
-            invoke(new SoundDelegate(delegate\r
-              {\r
-                  if (channel != null)\r
-                  {\r
-                      channel.getPaused(ref paused);\r
-                      channel.setPaused(!paused);\r
-                  }\r
-              }));\r
-        }\r
-\r
-         void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)\r
-        {\r
-            OPENSTATE openstate = 0;\r
-            uint percentbuffered = 0;\r
-            bool starving = false;\r
-\r
-            try\r
+            if (instatus != RESULT.OK)\r
             {\r
-                if (Active)\r
-                {\r
-                    // Query what the sound is doing.\r
-                    MediaManager.FMODExec(sound.getOpenState(ref openstate, ref percentbuffered, ref starving));\r
+                Logger.Log("Error opening stream: ", Helpers.LogLevel.Debug);\r
+                return RESULT.OK;\r
+            }\r
 \r
-                    // If a channel is not allocated yet, ask for one.\r
-                    if (openstate == OPENSTATE.READY && channel == null)\r
+            invoke(new SoundDelegate(\r
+                delegate\r
+                {\r
+                    try\r
                     {\r
                         // Allocate a channel and set initial volume.\r
-                        MediaManager.FMODExec(system.playSound( CHANNELINDEX.FREE, sound, false, ref channel));\r
+                        FMODExec(system.playSound(CHANNELINDEX.FREE, sound, false, ref channel));\r
                         volume = 0.5f;\r
-                        MediaManager.FMODExec(channel.setVolume(volume));\r
-                    }\r
-                }\r
+                        FMODExec(channel.setVolume(volume));\r
 \r
-                if (channel != null)\r
-                {\r
-                    // Do not try to get MP3 tags om Unix.  It breaks.\r
-                    if (Environment.OSVersion.Platform != PlatformID.Unix)\r
-                    {\r
-                        for (; ; )\r
+                        // If this is not Unix, try to get MP3 tags.\r
+                        // getTag seems to break on Unix.\r
+                        if (Environment.OSVersion.Platform != PlatformID.Unix)\r
                         {\r
                             TAG tag = new TAG();\r
-                            if (sound.getTag(null, -1, ref tag) != RESULT.OK)\r
-                            {\r
-                                break;\r
-                            }\r
-                            if (tag.datatype != TAGDATATYPE.STRING)\r
-                            {\r
-                                break;\r
-                            }\r
-                            else\r
+\r
+                            while (sound.getTag(null, -1, ref tag) == RESULT.OK)\r
                             {\r
+                                if (tag.datatype != TAGDATATYPE.STRING) continue;\r
+\r
+                                // Tell listeners about the Stream tag.  This can be\r
+                                // displayed to the user.\r
                                 if (OnStreamInfo != null)\r
-                                    try { OnStreamInfo(this, new StreamInfoArgs(tag.name.ToLower(), Marshal.PtrToStringAnsi(tag.data))); }\r
+                                    try\r
+                                    {\r
+                                        OnStreamInfo(this, new StreamInfoArgs(tag.name.ToLower(), Marshal.PtrToStringAnsi(tag.data)));\r
+                                    }\r
                                     catch (Exception) { }\r
                             }\r
                         }\r
-                    }\r
 \r
-                    // Get pause/play status\r
-                    MediaManager.FMODExec(channel.getPaused(ref paused));\r
-                    MediaManager.FMODExec(channel.isPlaying(ref playing));\r
-                    MediaManager.FMODExec(channel.getPosition(ref position, TIMEUNIT.MS));\r
-                }\r
+                        system.update();\r
+                    }\r
+                    catch (Exception ex)\r
+                    {\r
+                        Logger.Log("Error playing stream: ", Helpers.LogLevel.Debug, ex);\r
+                    }\r
+                }));\r
 \r
-                if (system != null)\r
-                {\r
-                    system.update();\r
-                }\r
-            }\r
-            catch (Exception ex)\r
-            {\r
-                playing = paused = false;\r
-                timer.Enabled = false;\r
-                Logger.Log("Error playing stream: ", Helpers.LogLevel.Debug, ex);\r
-            }\r
+            return RESULT.OK;\r
         }\r
     }\r
 }\r