diff --git a/src/org/jitsi/impl/neomedia/notify/AudioNotifierServiceImpl.java b/src/org/jitsi/impl/neomedia/notify/AudioNotifierServiceImpl.java
index 7fc5a3595899b38732e15d4f7c39afab0efd564d..2867bb40ca8a19f1814d794d65db513a17295b78 100644
--- a/src/org/jitsi/impl/neomedia/notify/AudioNotifierServiceImpl.java
+++ b/src/org/jitsi/impl/neomedia/notify/AudioNotifierServiceImpl.java
@@ -10,14 +10,14 @@
 import java.net.*;
 import java.util.*;
 
+import javax.media.*;
+
 import org.jitsi.impl.neomedia.*;
 import org.jitsi.impl.neomedia.device.*;
 import org.jitsi.service.audionotifier.*;
 import org.jitsi.service.libjitsi.*;
 import org.jitsi.service.resources.*;
 
-import javax.media.*;
-
 /**
  * The implementation of the AudioNotifierService.
  *
@@ -30,8 +30,8 @@ public class AudioNotifierServiceImpl
     /**
      * Map of different audio clips.
      */
-    private static final Map<AudioClipsKey, SCAudioClipImpl> audioClips =
-        new HashMap<AudioClipsKey, SCAudioClipImpl>();
+    private static final Map<AudioClipsKey, AbstractSCAudioClip> audioClips
+        = new HashMap<AudioClipsKey, AbstractSCAudioClip>();
 
     /**
      * If the sound is currently disabled.
@@ -63,7 +63,7 @@ public AudioNotifierServiceImpl()
      * @param uri the path where the audio file could be found
      * @return a newly created <tt>SCAudioClip</tt> from <tt>uri</tt>
      */
-    public SCAudioClipImpl createAudio(String uri)
+    public AbstractSCAudioClip createAudio(String uri)
     {
         return createAudio(uri, false);
     }
@@ -76,9 +76,9 @@ public SCAudioClipImpl createAudio(String uri)
      * @param playback use or not the playback device.
      * @return a newly created <tt>SCAudioClip</tt> from <tt>uri</tt>
      */
-    public SCAudioClipImpl createAudio(String uri, boolean playback)
+    public AbstractSCAudioClip createAudio(String uri, boolean playback)
     {
-        SCAudioClipImpl audioClip;
+        AbstractSCAudioClip audioClip;
 
         synchronized (audioClips)
         {
@@ -122,8 +122,11 @@ else if (NoneAudioSystem.LOCATOR_PROTOCOL.equalsIgnoreCase(
                         audioClip = null;
                     else
                     {
-                        audioClip = new AudioSystemClipImpl(
-                                            url, this, audioSystem, playback);
+                        audioClip
+                            = new AudioSystemClipImpl(
+                                    url,
+                                    this, audioSystem,
+                                    playback);
                     }
                 }
                 catch (Throwable t)
@@ -151,7 +154,7 @@ public void destroyAudio(SCAudioClip audioClip)
         synchronized (audioClips)
         {
             AudioClipsKey keyToRemove = null;
-            for(Map.Entry<AudioClipsKey, SCAudioClipImpl> entry
+            for(Map.Entry<AudioClipsKey, AbstractSCAudioClip> entry
                 : audioClips.entrySet())
             {
                 if(entry.getValue().equals(audioClip))
@@ -175,7 +178,7 @@ public void setMute(boolean isMute)
     {
         this.isMute = isMute;
 
-        for (SCAudioClipImpl audioClip : audioClips.values())
+        for (AbstractSCAudioClip audioClip : audioClips.values())
         {
             if (isMute)
             {
@@ -183,7 +186,7 @@ public void setMute(boolean isMute)
             }
             else if (audioClip.isLooping())
             {
-                audioClip.playInLoop(audioClip.getLoopInterval());
+                // TODO Auto-generated method stub
             }
         }
     }
diff --git a/src/org/jitsi/impl/neomedia/notify/AudioSystemClipImpl.java b/src/org/jitsi/impl/neomedia/notify/AudioSystemClipImpl.java
index d62c6dcc9d38342790212348eaeac18c1caa9260..c5be2d527d87495395a1b546e888a9bba5d58493 100644
--- a/src/org/jitsi/impl/neomedia/notify/AudioSystemClipImpl.java
+++ b/src/org/jitsi/impl/neomedia/notify/AudioSystemClipImpl.java
@@ -13,16 +13,17 @@
 import javax.sound.sampled.*;
 
 import org.jitsi.impl.neomedia.codec.audio.speex.*;
+import org.jitsi.service.audionotifier.*;
 import org.jitsi.util.*;
 
 /**
  * Implementation of SCAudioClip using PortAudio.
  *
  * @author Damyian Minkov
- * @author Lubomir Marinov
+ * @author Lyubomir Marinov
  */
 public class AudioSystemClipImpl
-    extends SCAudioClipImpl
+    extends AbstractSCAudioClip
 {
     /**
      * The <tt>Logger</tt> used by the <tt>AudioSystemClipImpl</tt> class and
@@ -31,18 +32,16 @@ public class AudioSystemClipImpl
     private static final Logger logger
         = Logger.getLogger(AudioSystemClipImpl.class);
 
-    private final AudioNotifierServiceImpl audioNotifier;
-
     private final org.jitsi.impl.neomedia.device.AudioSystem
         audioSystem;
 
-    private boolean started = false;
-    
-    private final Object syncObject = new Object();
+    private Buffer buffer;
+
+    private byte[] bufferData;
 
-    private final URL url;
+    private final boolean playback;
 
-    private boolean isPlayback = false;
+    private Renderer renderer;
 
     /**
      * Creates the audio clip and initializes the listener used from the
@@ -55,154 +54,58 @@ public class AudioSystemClipImpl
      */
     public AudioSystemClipImpl(
             URL url,
-            AudioNotifierServiceImpl audioNotifier,
+            AudioNotifierService audioNotifier,
             org.jitsi.impl.neomedia.device.AudioSystem audioSystem,
             boolean playback)
         throws IOException
     {
-        this.url = url;
-        this.audioNotifier = audioNotifier;
-        this.audioSystem = audioSystem;
-        this.isPlayback = playback;
-    }
+        super(url, audioNotifier);
 
-    /**
-     * Plays this audio.
-     */
-    public void play()
-    {
-        if ((url != null) && !audioNotifier.isMute())
-        {
-            started = true;
-            new Thread()
-                    {
-                        @Override
-                        public void run()
-                        {
-                            runInPlayThread();
-                        }
-                    }.start();
-        }
+        this.audioSystem = audioSystem;
+        this.playback = playback;
     }
 
     /**
-     * Plays this audio in loop.
-     *
-     * @param interval the loop interval
+     * {@inheritDoc}
      */
-    public void playInLoop(int interval)
+    @Override
+    protected void enterRunInPlayThread()
     {
-        setLoopInterval(interval);
-        setIsLooping(true);
+        buffer = new Buffer();
+        bufferData = new byte[1024];
+        buffer.setData(bufferData);
 
-        play();
+        renderer = audioSystem.createRenderer(playback);
     }
 
     /**
-     * Stops this audio.
+     * {@inheritDoc}
      */
-    public void stop()
+    @Override
+    protected void exitRunInPlayThread()
     {
-        internalStop();
-        setIsLooping(false);
+        buffer = null;
+        bufferData = null;
+        renderer = null;
     }
 
     /**
-     * Stops this audio without setting the isLooping property in the case of
-     * a looping audio. The AudioNotifier uses this method to stop the audio
-     * when setMute(true) is invoked. This allows us to restore all looping
-     * audios when the sound is restored by calling setMute(false).
+     * {@inheritDoc}
      */
-    public void internalStop()
+    @Override
+    protected void exitRunOnceInPlayThread()
     {
-        synchronized (syncObject) 
+        try
         {
-            if (url != null && started) 
-            {
-                started = false;
-                syncObject.notifyAll();
-            }
+            renderer.stop();
         }
-    }
-
-    /**
-     * Runs in a separate thread to perform the actual playback of the audio
-     * stream pointed to by {@link #url} looping as necessary.
-     */
-    private void runInPlayThread()
-    {
-        Buffer buffer = new Buffer();
-        byte[] bufferData = new byte[1024];
-        // don't enable volume control for notifications
-
-        Renderer renderer = audioSystem.createRenderer(isPlayback);
-
-        buffer.setData(bufferData);
-        while (started)
+        finally
         {
-            try
-            {
-                if (!runOnceInPlayThread(renderer, buffer, bufferData))
-                    break;
-            }
-            finally
-            {
-                try
-                {
-                    renderer.stop();
-                }
-                finally
-                {
-                    renderer.close();
-                }
-            }
-
-            if(isLooping())
-            {
-                synchronized(syncObject)
-                {
-                    if (started)
-                    {
-                        try
-                        {
-                            // only wait if lonnger than 0
-                            // or we will wait forever
-                            if(getLoopInterval() > 0)
-                                syncObject.wait(getLoopInterval());
-                        }
-                        catch (InterruptedException e)
-                        {
-                        }
-                    }
-                }
-            }
-            else
-                break;
+            renderer.close();
         }
     }
 
-    /**
-     * Runs in a separate thread to perform the actual playback of the audio
-     * stream pointed to by {@link #url} once using a specific
-     * <tt>PortAudioRenderer</tt> and giving it the audio data for processing
-     * through a specific JMF <tt>Buffer</tt>.
-     *
-     * @param renderer the <tt>PortAudioRenderer</tt> which is to render the
-     * audio data read from the audio stream pointed to by {@link #url}
-     * @param buffer the JMF <tt>Buffer</tt> through which the audio data to be
-     * rendered is to be given to <tt>renderer</tt>
-     * @param bufferData the value of the <tt>data</tt> property of
-     * <tt>buffer</tt> explicitly specified for performance reasons so that it
-     * doesn't have to be read and cast during every iteration of the playback
-     * loop
-     * @return <tt>true</tt> if the playback was successful and it is to be
-     * carried out again in accord with the <tt>looping</tt> property value of
-     * this <tt>SCAudioClipImpl</tt>; otherwise, <tt>false</tt>
-     */
-    private boolean runOnceInPlayThread(
-            Renderer renderer,
-            Buffer buffer,
-            byte[] bufferData)
+    protected boolean runOnceInPlayThread()
     {
         AudioInputStream audioStream = null;
 
@@ -288,7 +191,7 @@ private boolean runOnceInPlayThread(
 
                 int bufferLength;
 
-                while(started
+                while(isStarted()
                         && ((bufferLength = audioStream.read(bufferData))
                                 != -1))
                 {
diff --git a/src/org/jitsi/impl/neomedia/notify/JavaSoundClipImpl.java b/src/org/jitsi/impl/neomedia/notify/JavaSoundClipImpl.java
index 26f705d215a9f6ffeddfa804dd20ce37479f807e..2d42a48b25b21a5fd5b89da9f0514258d7771714 100644
--- a/src/org/jitsi/impl/neomedia/notify/JavaSoundClipImpl.java
+++ b/src/org/jitsi/impl/neomedia/notify/JavaSoundClipImpl.java
@@ -7,14 +7,11 @@
 package org.jitsi.impl.neomedia.notify;
 
 import java.applet.*;
-import java.awt.event.*;
 import java.io.*;
 import java.lang.reflect.*;
 import java.net.*;
 import java.security.*;
 
-import javax.swing.*;
-
 import org.jitsi.service.audionotifier.*;
 
 /**
@@ -23,98 +20,33 @@
  * @author Yana Stamcheva
  */
 public class JavaSoundClipImpl
-    extends SCAudioClipImpl
-    implements ActionListener
+    extends AbstractSCAudioClip
                
 {
     private static Constructor<AudioClip> acConstructor = null;
 
-    private final Timer playAudioTimer = new Timer(1000, null);
-
-    private final AudioClip audioClip;
-
-    private final AudioNotifierService audioNotifier;
-
-    /**
-     * Creates the audio clip and initialize the listener used from the
-     * loop timer.
-     *
-     * @param url the url pointing to the audio file
-     * @param audioNotifier the audio notify service
-     * @throws IOException cannot audio clip with supplied url.
-     */
-    public JavaSoundClipImpl(URL url, AudioNotifierService audioNotifier)
-        throws IOException
-    {
-        this.audioClip = createAppletAudioClip(url.openStream());
-        this.audioNotifier = audioNotifier;
-
-        this.playAudioTimer.addActionListener(this);
-    }
-
-    /**
-     * Plays this audio.
-     */
-    public void play()
-    {
-        if ((audioClip != null) && !audioNotifier.isMute())
-            audioClip.play();
-    }
-
-    /**
-     * Plays this audio in loop.
-     *
-     * @param interval the loop interval
-     */
-    public void playInLoop(int interval)
+    @SuppressWarnings("unchecked")
+    private static Constructor<AudioClip> createAcConstructor()
+        throws ClassNotFoundException,
+               NoSuchMethodException,
+               SecurityException
     {
-        if(!audioNotifier.isMute())
+        Class<?> class1;
+        try
         {
-            if(interval == 0)
-                audioClip.loop();
-            else
-            {
-                //first play the audio and then start the timer and wait
-                audioClip.play();
-                playAudioTimer.setDelay(interval);
-                playAudioTimer.setRepeats(true);
-
-                playAudioTimer.start();
-            }
+            class1
+                = Class.forName(
+                    "com.sun.media.sound.JavaSoundAudioClip",
+                    true,
+                    ClassLoader.getSystemClassLoader());
         }
-
-        setLoopInterval(interval);
-        setIsLooping(true);
-    }
-
-    /**
-     * Stops this audio.
-     */
-    public void stop()
-    {
-        if (audioClip != null)
-            audioClip.stop();
-
-        if (isLooping())
+        catch (ClassNotFoundException cnfex)
         {
-            playAudioTimer.stop();
-            setIsLooping(false);
+            class1
+                = Class.forName("sun.audio.SunAudioClip", true, null);
         }
-    }
-
-    /**
-     * Stops this audio without setting the isLooping property in the case of
-     * a looping audio. The AudioNotifier uses this method to stop the audio
-     * when setMute(true) is invoked. This allows us to restore all looping
-     * audios when the sound is restored by calling setMute(false).
-     */
-    public void internalStop()
-    {
-        if (audioClip != null)
-            audioClip.stop();
-
-        if (isLooping())
-            playAudioTimer.stop();
+        return
+            (Constructor<AudioClip>) class1.getConstructor(InputStream.class);
     }
 
     /**
@@ -162,40 +94,52 @@ public Constructor<AudioClip> run()
         }
     }
 
-    @SuppressWarnings("unchecked")
-    private static Constructor<AudioClip> createAcConstructor()
-        throws ClassNotFoundException,
-               NoSuchMethodException,
-               SecurityException
+    private final AudioClip audioClip;
+
+    /**
+     * Creates the audio clip and initialize the listener used from the
+     * loop timer.
+     *
+     * @param url the url pointing to the audio file
+     * @param audioNotifier the audio notify service
+     * @throws IOException cannot audio clip with supplied url.
+     */
+    public JavaSoundClipImpl(URL url, AudioNotifierService audioNotifier)
+            throws IOException
+    {
+        super(url, audioNotifier);
+
+        audioClip = createAppletAudioClip(url.openStream());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void internalStop()
     {
-        Class<?> class1;
         try
         {
-            class1
-                = Class.forName(
-                    "com.sun.media.sound.JavaSoundAudioClip",
-                    true,
-                    ClassLoader.getSystemClassLoader());
+            if (audioClip != null)
+                audioClip.stop();
         }
-        catch (ClassNotFoundException cnfex)
+        finally
         {
-            class1
-                = Class.forName("sun.audio.SunAudioClip", true, null);
+            super.internalStop();
         }
-        return
-            (Constructor<AudioClip>) class1.getConstructor(InputStream.class);
     }
 
     /**
-     * Plays an audio clip. Used in the playAudioTimer to play an audio in loop.
-     * @param e the event.
+     * {@inheritDoc}
      */
-    public void actionPerformed(ActionEvent e)
+    protected boolean runOnceInPlayThread()
     {
-        if (audioClip != null)
+        if (audioClip == null)
+            return false;
+        else
         {
-            audioClip.stop();
             audioClip.play();
+            return true;
         }
     }
 }
diff --git a/src/org/jitsi/impl/neomedia/notify/SCAudioClipImpl.java b/src/org/jitsi/impl/neomedia/notify/SCAudioClipImpl.java
deleted file mode 100644
index 94657eb7ef6fb2cf3742b67b18faf60d0ab6d5e3..0000000000000000000000000000000000000000
--- a/src/org/jitsi/impl/neomedia/notify/SCAudioClipImpl.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-package org.jitsi.impl.neomedia.notify;
-
-import org.jitsi.service.audionotifier.*;
-
-/**
- * Common properties impl for SCAudioClip.
- *
- * @author Damian Minkov
- */
-public abstract class SCAudioClipImpl
-    implements SCAudioClip
-{
-    private boolean isLooping;
-
-    private int loopInterval;
-
-    private boolean isInvalid;
-
-    /**
-     * Returns TRUE if this audio is invalid, FALSE otherwise.
-     *
-     * @return TRUE if this audio is invalid, FALSE otherwise
-     */
-    public boolean isInvalid()
-    {
-        return isInvalid;
-    }
-
-    /**
-     * Marks this audio as invalid or not.
-     *
-     * @param isInvalid TRUE to mark this audio as invalid, FALSE otherwise
-     */
-    public void setInvalid(boolean isInvalid)
-    {
-        this.setIsInvalid(isInvalid);
-    }
-
-    /**
-     * Returns TRUE if this audio is currently playing in loop, FALSE otherwise.
-     * @return TRUE if this audio is currently playing in loop, FALSE otherwise.
-     */
-    public boolean isLooping()
-    {
-        return isLooping;
-    }
-
-    /**
-     * Returns the loop interval if this audio is looping.
-     * @return the loop interval if this audio is looping
-     */
-    public int getLoopInterval()
-    {
-        return loopInterval;
-    }
-
-    /**
-     * @param isLooping the isLooping to set
-     */
-    public void setIsLooping(boolean isLooping)
-    {
-        this.isLooping = isLooping;
-    }
-
-    /**
-     * @param loopInterval the loopInterval to set
-     */
-    public void setLoopInterval(int loopInterval)
-    {
-        this.loopInterval = loopInterval;
-    }
-
-    /**
-     * @param isInvalid the isInvalid to set
-     */
-    public void setIsInvalid(boolean isInvalid)
-    {
-        this.isInvalid = isInvalid;
-    }
-
-    /**
-     * Stops this audio without setting the isLooping property in the case of
-     * a looping audio. The AudioNotifier uses this method to stop the audio
-     * when setMute(true) is invoked. This allows us to restore all looping
-     * audios when the sound is restored by calling setMute(false).
-     */
-    public abstract void internalStop();
-}
diff --git a/src/org/jitsi/service/audionotifier/AbstractSCAudioClip.java b/src/org/jitsi/service/audionotifier/AbstractSCAudioClip.java
new file mode 100644
index 0000000000000000000000000000000000000000..161dea63851802e0bf1df3102d99b18dcf84f55b
--- /dev/null
+++ b/src/org/jitsi/service/audionotifier/AbstractSCAudioClip.java
@@ -0,0 +1,276 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.service.audionotifier;
+
+import java.net.*;
+import java.util.concurrent.*;
+
+/**
+ * An abstract base implementation of {@link SCAudioClip}. 
+ *
+ * @author Damian Minkov
+ * @author Lyubomir Marinov
+ */
+public abstract class AbstractSCAudioClip
+    implements SCAudioClip
+{
+    protected final AudioNotifierService audioNotifier;
+
+    private boolean isInvalid;
+
+    private boolean isLooping;
+
+    private int loopInterval;
+
+    private boolean started = false;
+    
+    private final Object sync = new Object();
+
+    protected final URL url;
+
+    protected AbstractSCAudioClip(
+            URL url,
+            AudioNotifierService audioNotifier)
+    {
+        this.url = url;
+        this.audioNotifier = audioNotifier;
+    }
+
+    protected void enterRunInPlayThread()
+    {
+        // TODO Auto-generated method stub
+    }
+
+    protected void enterRunOnceInPlayThread()
+    {
+        // TODO Auto-generated method stub
+    }
+
+    protected void exitRunInPlayThread()
+    {
+        // TODO Auto-generated method stub
+    }
+
+    protected void exitRunOnceInPlayThread()
+    {
+        // TODO Auto-generated method stub
+    }
+
+    /**
+     * Returns the loop interval if this audio is looping.
+     * @return the loop interval if this audio is looping
+     */
+    public int getLoopInterval()
+    {
+        return loopInterval;
+    }
+
+    /**
+     * Stops this audio without setting the isLooping property in the case of
+     * a looping audio. The AudioNotifier uses this method to stop the audio
+     * when setMute(true) is invoked. This allows us to restore all looping
+     * audios when the sound is restored by calling setMute(false).
+     */
+    public void internalStop()
+    {
+        synchronized (sync) 
+        {
+            if (url != null && started) 
+            {
+                started = false;
+                sync.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Returns TRUE if this audio is invalid, FALSE otherwise.
+     *
+     * @return TRUE if this audio is invalid, FALSE otherwise
+     */
+    public boolean isInvalid()
+    {
+        return isInvalid;
+    }
+
+    /**
+     * Returns TRUE if this audio is currently playing in loop, FALSE otherwise.
+     * @return TRUE if this audio is currently playing in loop, FALSE otherwise.
+     */
+    public boolean isLooping()
+    {
+        return isLooping;
+    }
+
+    protected boolean isStarted()
+    {
+        return started;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void play()
+    {
+        play(-1, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void play(int loopInterval, final Callable<Boolean> loopCondition)
+    {
+        if ((loopInterval >= 0) && (loopCondition == null))
+            loopInterval = -1;
+
+        setLoopInterval(loopInterval);
+        setIsLooping(loopInterval >= 0);
+
+        if ((url != null) && !audioNotifier.isMute())
+        {
+            started = true;
+            new Thread()
+                    {
+                        @Override
+                        public void run()
+                        {
+                            runInPlayThread(loopCondition);
+                        }
+                    }.start();
+        }
+    }
+
+    private void runInPlayThread(Callable<Boolean> loopCondition)
+    {
+        enterRunInPlayThread();
+        try
+        {
+            while (started)
+            {
+                enterRunOnceInPlayThread();
+                try
+                {
+                    if (!runOnceInPlayThread())
+                        break;
+                }
+                finally
+                {
+                    exitRunOnceInPlayThread();
+                }
+
+                if(isLooping())
+                {
+                    synchronized(sync)
+                    {
+                        if (started)
+                        {
+                            try
+                            {
+                                /*
+                                 * Only wait if longer than 0; otherwise, we
+                                 * will wait forever.
+                                 */
+                                if(getLoopInterval() > 0)
+                                    sync.wait(getLoopInterval());
+                            }
+                            catch (InterruptedException e)
+                            {
+                            }
+                        }
+                    }
+
+                    if (started)
+                    {
+                        if (loopCondition == null)
+                        {
+                            /*
+                             * The interface contract is that this audio plays
+                             * once only if the loopCondition is null.
+                             */
+                            break;
+                        }
+                        else
+                        {
+                            boolean loop = false;
+
+                            try
+                            {
+                                loop = loopCondition.call();
+                            }
+                            catch (Throwable t)
+                            {
+                                if (t instanceof ThreadDeath)
+                                    throw (ThreadDeath) t;
+                            }
+                            if (!loop)
+                            {
+                                /*
+                                 * The loopCondition failed to evaluate to true
+                                 * so the loop will not continue.
+                                 */
+                                break;
+                            }
+                        }
+                    }
+                    else
+                        break;
+                }
+                else
+                    break;
+            }
+        }
+        finally
+        {
+            exitRunInPlayThread();
+        }
+    }
+
+    protected abstract boolean runOnceInPlayThread();
+
+    /**
+     * Marks this audio as invalid or not.
+     *
+     * @param isInvalid TRUE to mark this audio as invalid, FALSE otherwise
+     */
+    public void setInvalid(boolean isInvalid)
+    {
+        this.setIsInvalid(isInvalid);
+    }
+
+    /**
+     * @param isInvalid the isInvalid to set
+     */
+    public void setIsInvalid(boolean isInvalid)
+    {
+        this.isInvalid = isInvalid;
+    }
+
+    /**
+     * @param isLooping the isLooping to set
+     */
+    public void setIsLooping(boolean isLooping)
+    {
+        this.isLooping = isLooping;
+    }
+
+    /**
+     * @param loopInterval the loopInterval to set
+     */
+    public void setLoopInterval(int loopInterval)
+    {
+        this.loopInterval = loopInterval;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void stop()
+    {
+        internalStop();
+        setIsLooping(false);
+    }
+}
diff --git a/src/org/jitsi/service/audionotifier/SCAudioClip.java b/src/org/jitsi/service/audionotifier/SCAudioClip.java
index 146ac16cfaa3243bffc42ec9af46445459609706..9699c0765efea1c3ff8d7462582dabc010d56417 100644
--- a/src/org/jitsi/service/audionotifier/SCAudioClip.java
+++ b/src/org/jitsi/service/audionotifier/SCAudioClip.java
@@ -6,28 +6,40 @@
  */
 package org.jitsi.service.audionotifier;
 
+import java.util.concurrent.*;
+
 /**
  * SCAudioClip represents an audio clip created using the AudioNotifierService.
  * Like  any audio it could be played, stopped or played in loop.
  *
  * @author Yana Stamcheva
+ * @author Lyubomir Marinov
  */
 public interface SCAudioClip
 {
     /**
-     * Plays this audio.
+     * Starts playing this audio once only. The method behaves as if
+     * {@link #play(int, Callable)} was invoked with a negative
+     * <tt>loopInterval</tt> and/or <tt>null</tt> <tt>loopCondition</tt>.
      */
     public void play();
 
     /**
-     * Plays this audio in loop.
+     * Starts playing this audio. Optionally, the playback is looped.
      *
-     * @param silenceInterval interval between loops
+     * @param loopInterval the interval of time in milliseconds between
+     * consecutive plays of this audio. If negative, this audio is played once
+     * only and <tt>loopCondition</tt> is ignored.
+     * @param loopCondition a <tt>Callable</tt> which is called at the beginning
+     * of each iteration of looped playback of this audio except the first one
+     * to determine whether to continue the loop. If <tt>loopInterval</tt> is
+     * negative or <tt>loopCondition</tt> is <tt>null</tt>, this audio is played
+     * once only.
      */
-    public void playInLoop(int silenceInterval);
+    public void play(int loopInterval, Callable<Boolean> loopCondition);
 
     /**
-     * Stops this audio.
+     * Stops playing this audio.
      */
     public void stop();
 }