/** * 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<Boolean></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; }