/**
   * Checks whether the playback and notification configuration share the same device.
   *
   * @return are audio out and notifications using the same device.
   */
  public boolean audioOutAndNotificationsShareSameDevice() {
    AudioSystem audioSystem = getDeviceConfiguration().getAudioSystem();
    CaptureDeviceInfo notify = audioSystem.getSelectedDevice(AudioSystem.DataFlow.NOTIFY);
    CaptureDeviceInfo playback = audioSystem.getSelectedDevice(AudioSystem.DataFlow.PLAYBACK);

    if (notify == null) return (playback == null);
    else {
      if (playback == null) return false;
      else return notify.getLocator().equals(playback.getLocator());
    }
  }
  /**
   * Creates an SCAudioClip from the given URI and adds it to the list of available audio-s.
   *
   * @param uri the path where the audio file could be found
   * @param playback use or not the playback device.
   * @return a newly created <tt>SCAudioClip</tt> from <tt>uri</tt>
   */
  public SCAudioClip createAudio(String uri, boolean playback) {
    SCAudioClip audio;

    synchronized (audiosSyncRoot) {
      final AudioKey key = new AudioKey(uri, playback);

      /*
       * While we want to reuse the SCAudioClip instances, they may be
       * used by a single user at a time. That's why we'll forget about
       * them while they are in use and we'll reclaim them when they are
       * no longer in use.
       */
      audio = (audios == null) ? null : audios.remove(key);

      if (audio == null) {
        try {
          AudioSystem audioSystem = getDeviceConfiguration().getAudioSystem();

          if (audioSystem == null) {
            audio = new JavaSoundClipImpl(uri, this);
          } else if (NoneAudioSystem.LOCATOR_PROTOCOL.equalsIgnoreCase(
              audioSystem.getLocatorProtocol())) {
            audio = null;
          } else {
            audio = new AudioSystemClipImpl(uri, this, audioSystem, playback);
          }
        } catch (Throwable t) {
          if (t instanceof ThreadDeath) throw (ThreadDeath) t;
          else {
            /*
             * Could not initialize a new SCAudioClip instance to be
             * played.
             */
            return null;
          }
        }
      }

      /*
       * Make sure that the SCAudioClip will be reclaimed for reuse when
       * it is no longer in use.
       */
      if (audio != null) {
        if (audios == null) audios = new HashMap<AudioKey, SCAudioClip>();

        /*
         * We have to return in the Map which was active at the time the
         * SCAudioClip was initialized because it may have become
         * invalid if the playback or notify audio device changed.
         */
        final Map<AudioKey, SCAudioClip> finalAudios = audios;
        final SCAudioClip finalAudio = audio;

        audio =
            new SCAudioClip() {
              /**
               * Evaluates a specific <tt>loopCondition</tt> as defined by {@link
               * SCAudioClip#play(int,Callable)}.
               *
               * @param loopCondition the <tt>Callable&lt;Boolean&gt;</tt> which represents the
               *     <tt>loopCondition</tt> to be evaluated
               * @return {@link Boolean#FALSE} if <tt>loopCondition</tt> is <tt>null</tt>;
               *     otherwise, the value returned by invoking {@link Callable#call()} on the
               *     specified <tt>loopCondition</tt>
               * @throws Exception if the specified <tt>loopCondition</tt> throws an
               *     <tt>Exception</tt>
               */
              private Boolean evaluateLoopCondition(Callable<Boolean> loopCondition)
                  throws Exception {
                /*
                 * SCAudioClip.play(int,Callable<Boolean>) is
                 * documented to play the SCAudioClip once only if
                 * the loopCondition is null. The same will be
                 * accomplished by returning Boolean.FALSE.
                 */
                return (loopCondition == null) ? Boolean.FALSE : loopCondition.call();
              }

              /**
               * {@inheritDoc}
               *
               * <p>Returns the wrapped <tt>SCAudioClip</tt> into the cache from it has earlier been
               * retrieved in order to allow its reuse.
               */
              @Override
              protected void finalize() throws Throwable {
                try {
                  synchronized (audios) {
                    finalAudios.put(key, finalAudio);
                  }
                } finally {
                  super.finalize();
                }
              }

              public void play() {
                /*
                 * SCAudioClip.play() is documented to behave as if
                 * loopInterval is negative and/or loopCondition is
                 * null. We have to take care that this instance
                 * does not get garbage collected until the
                 * finalAudio finishes playing so we will delegate
                 * to this instance's implementation of
                 * SCAudioClip.play(int,Callable<Boolean>) instead
                 * of to the finalAudio's.
                 */
                play(-1, null);
              }

              public void play(int loopInterval, final Callable<Boolean> finalLoopCondition) {
                /*
                 * We have to make sure that this instance does not
                 * get garbage collected before the finalAudio
                 * finishes playing. The argument loopCondition of
                 * the method
                 * SCAudioClip.play(int,Callable<Boolean>) will
                 * live/be referenced during that time so we will
                 * use it to hold on to this instance.
                 */
                Callable<Boolean> loopCondition =
                    new Callable<Boolean>() {
                      public Boolean call() throws Exception {
                        return evaluateLoopCondition(finalLoopCondition);
                      }
                    };

                finalAudio.play(loopInterval, loopCondition);
              }

              public void stop() {
                finalAudio.stop();
              }

              /**
               * Determines whether this audio is started i.e. a <tt>play</tt> method was invoked
               * and no subsequent <tt>stop</tt> has been invoked yet.
               *
               * @return <tt>true</tt> if this audio is started; otherwise, <tt>false</tt>
               */
              public boolean isStarted() {
                return finalAudio.isStarted();
              }
            };
      }
    }

    return audio;
  }