javafx.scene.media.AudioClip Maven / Gradle / Ivy
/*
* Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.media;
import java.net.URI;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import javafx.beans.NamedArg;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.DoublePropertyBase;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.IntegerPropertyBase;
/**
* An AudioClip
represents a segment of audio that can be played
* with minimal latency. Clips are loaded similarly to Media
* objects but have different behavior, for example, a Media
cannot
* play itself. AudioClip
s are also usable immediately. Playback
* behavior is fire and forget: once one of the play methods is called the only
* operable control is {@link #stop()}. An AudioClip
may also be
* played multiple times simultaneously. To accomplish the same task using
* Media
one would have to create a new MediaPlayer
* object for each sound played in parallel. Media
objects are
* however better suited for long-playing sounds. This is primarily because
* AudioClip
stores in memory the raw, uncompressed audio data for
* the entire sound, which can be quite large for long audio clips. A
* MediaPlayer
will only have enough decompressed audio data
* pre-rolled in memory to play for a short amount of time so it is much more
* memory efficient for long clips, especially if they are compressed.
*
* Example usage:
* {@code
* AudioClip plonkSound = new AudioClip("http://somehost/path/plonk.aiff");
* plonkSound.play();
* }
*
* @since JavaFX 2.0
*/
public final class AudioClip {
private String sourceURL;
private com.sun.media.jfxmedia.AudioClip audioClip;
/**
* Create an AudioClip
loaded from the supplied source URL.
*
* @param source URL string from which to load the audio clip. This can be an
* HTTP, HTTPS, FILE or JAR source.
* @throws NullPointerException if the parameter is null
.
* @throws IllegalArgumentException if the parameter violates
* RFC 2396.
* @throws MediaException if there is some other problem loading the media.
*/
public AudioClip(@NamedArg("source") String source) {
URI srcURI = URI.create(source);
sourceURL = source;
try {
audioClip = com.sun.media.jfxmedia.AudioClip.load(srcURI);
} catch(URISyntaxException use) {
throw new IllegalArgumentException(use);
} catch(FileNotFoundException fnfe) {
throw new MediaException(MediaException.Type.MEDIA_UNAVAILABLE, fnfe.getMessage());
} catch(IOException ioe) {
throw new MediaException(MediaException.Type.MEDIA_INACCESSIBLE, ioe.getMessage());
} catch(com.sun.media.jfxmedia.MediaException me) {
throw new MediaException(MediaException.Type.MEDIA_UNSUPPORTED, me.getMessage());
}
}
/**
* Get the source URL used to create this AudioClip
.
* @return source URL as provided to the constructor
*/
public String getSource() {
return sourceURL;
}
/**
* The relative volume level at which the clip is played. Valid range is 0.0
* (muted) to 1.0 (full volume). Values are clamped to this range internally
* so values outside this range will have no additional effect. Volume is
* controlled by attenuation, so values below 1.0 will reduce the sound
* level accordingly.
*/
private DoubleProperty volume;
/**
* Set the default volume level. The new setting will only take effect on
* subsequent plays.
* @see #volumeProperty()
* @param value new default volume level for this clip
*/
public final void setVolume(double value) {
volumeProperty().set(value);
}
/**
* Get the default volume level.
* @see #volumeProperty()
* @return the default volume level for this clip
*/
public final double getVolume() {
return (null == volume) ? 1.0 : volume.get();
}
public DoubleProperty volumeProperty() {
if (volume == null) {
volume = new DoublePropertyBase(1.0) {
@Override
protected void invalidated() {
if (null != audioClip) {
audioClip.setVolume(volume.get());
}
}
@Override
public Object getBean() {
return AudioClip.this;
}
@Override
public String getName() {
return "volume";
}
};
}
return volume;
}
/**
* The relative left and right volume levels of the clip.
* Valid range is -1.0 to 1.0 where -1.0 gives full volume to the left
* channel while muting the right channel, 0.0 gives full volume to both
* channels and 1.0 gives full volume to right channel and mutes the left
* channel. Values outside this range are clamped internally.
*/
private DoubleProperty balance;
/**
* Set the default balance level. The new value will only affect subsequent
* plays.
* @see #balanceProperty()
* @param balance new default balance
*/
public void setBalance(double balance) {
balanceProperty().set(balance);
}
/**
* Get the default balance level for this clip.
* @see #balanceProperty()
* @return the default balance for this clip
*/
public double getBalance() {
return (null != balance) ? balance.get() : 0.0;
}
public DoubleProperty balanceProperty() {
if (null == balance) {
balance = new DoublePropertyBase(0.0) {
@Override
protected void invalidated() {
if (null != audioClip) {
audioClip.setBalance(balance.get());
}
}
@Override
public Object getBean() {
return AudioClip.this;
}
@Override
public String getName() {
return "balance";
}
};
}
return balance;
}
/**
* The relative rate at which the clip is played. Valid range is 0.125
* (1/8 speed) to 8.0 (8x speed); values outside this range are clamped
* internally. Normal playback for a clip is 1.0; any other rate will affect
* pitch and duration accordingly.
*/
private DoubleProperty rate;
/**
* Set the default playback rate. The new value will only affect subsequent
* plays.
* @see #rateProperty()
* @param rate the new default playback rate
*/
public void setRate(double rate) {
rateProperty().set(rate);
}
/**
* Get the default playback rate.
* @see #rateProperty()
* @return default playback rate for this clip
*/
public double getRate() {
return (null != rate) ? rate.get() : 1.0;
}
public DoubleProperty rateProperty() {
if (null == rate) {
rate = new DoublePropertyBase(1.0) {
@Override
protected void invalidated() {
if (null != audioClip) {
audioClip.setPlaybackRate(rate.get());
}
}
@Override
public Object getBean() {
return AudioClip.this;
}
@Override
public String getName() {
return "rate";
}
};
}
return rate;
}
/**
* The relative "center" of the clip. A pan value of 0.0 plays
* the clip normally where a -1.0 pan shifts the clip entirely to the left
* channel and 1.0 shifts entirely to the right channel. Unlike balance this
* setting mixes both channels so neither channel loses data. Setting
* pan on a mono clip has the same effect as setting balance, but with a
* much higher cost in CPU overhead so this is not recommended for mono
* clips.
*/
private DoubleProperty pan;
/**
* Set the default pan value. The new value will only affect subsequent
* plays.
* @see #panProperty()
* @param pan the new default pan value
*/
public void setPan(double pan) {
panProperty().set(pan);
}
/**
* Get the default pan value.
* @see #panProperty()
* @return the default pan value for this clip
*/
public double getPan() {
return (null != pan) ? pan.get() : 0.0;
}
public DoubleProperty panProperty() {
if (null == pan) {
pan = new DoublePropertyBase(0.0) {
@Override
protected void invalidated() {
if (null != audioClip) {
audioClip.setPan(pan.get());
}
}
@Override
public Object getBean() {
return AudioClip.this;
}
@Override
public String getName() {
return "pan";
}
};
}
return pan;
}
/**
* The relative priority of the clip with respect to other clips. This value
* is used to determine which clips to remove when the maximum allowed number
* of clips is exceeded. The lower the priority, the more likely the
* clip is to be stopped and removed from the mixer channel it is occupying.
* Valid range is any integer; there are no constraints. The default priority
* is zero for all clips until changed. The number of simultaneous sounds
* that can be played is implementation- and possibly system-dependent.
*/
private IntegerProperty priority;
/**
* Set the default playback priority. The new value will only affect
* subsequent plays.
* @see #priorityProperty()
* @param priority the new default playback priority
*/
public void setPriority(int priority) {
priorityProperty().set(priority);
}
/**
* Get the default playback priority.
* @see #priorityProperty()
* @return the default playback priority of this clip
*/
public int getPriority() {
return (null != priority) ? priority.get() : 0;
}
public IntegerProperty priorityProperty() {
if (null == priority) {
priority = new IntegerPropertyBase(0) {
@Override
protected void invalidated() {
if (null != audioClip) {
audioClip.setPriority(priority.get());
}
}
@Override
public Object getBean() {
return AudioClip.this;
}
@Override
public String getName() {
return "priority";
}
};
}
return priority;
}
/**
* When {@link #cycleCountProperty cycleCount} is set to this value, the
* AudioClip
will loop continuously until stopped. This value is
* synonymous with {@link MediaPlayer#INDEFINITE} and
* {@link javafx.animation.Animation#INDEFINITE}, these values may be used
* interchangeably.
*/
public static final int INDEFINITE = -1;
/**
* The number of times the clip will be played when {@link #play()}
* is called. A cycleCount of 1 plays exactly once, a cycleCount of 2
* plays twice and so on. Valid range is 1 or more, but setting this to
* {@link #INDEFINITE INDEFINITE} will cause the clip to continue looping
* until {@link #stop} is called.
*/
private IntegerProperty cycleCount;
/**
* Set the default cycle count. The new value will only affect subsequent
* plays.
* @see #cycleCountProperty()
* @param count the new default cycle count for this clip
*/
public void setCycleCount(int count) {
cycleCountProperty().set(count);
}
/**
* Get the default cycle count.
* @see #cycleCountProperty()
* @return the default cycleCount for this audio clip
*/
public int getCycleCount() {
return (null != cycleCount) ? cycleCount.get() : 1;
}
public IntegerProperty cycleCountProperty() {
if (null == cycleCount) {
cycleCount = new IntegerPropertyBase(1) {
@Override
protected void invalidated() {
if (null != audioClip) {
int value = cycleCount.get();
if (INDEFINITE != value) {
value = Math.max(1, value);
audioClip.setLoopCount(value - 1);
} else {
audioClip.setLoopCount(value); // INDEFINITE is the same there
}
}
}
@Override
public Object getBean() {
return AudioClip.this;
}
@Override
public String getName() {
return "cycleCount";
}
};
}
return cycleCount;
}
/**
* Play the AudioClip
using all the default parameters.
*/
public void play() {
if (null != audioClip) {
audioClip.play();
}
}
/**
* Play the AudioClip
using all the default parameters except volume.
* This method does not modify the clip's default parameters.
* @param volume the volume level at which to play the clip
*/
public void play(double volume) {
if (null != audioClip) {
audioClip.play(volume);
}
}
/**
* Play the AudioClip
using the given parameters. Values outside
* the ranges as specified by their associated properties are clamped.
* This method does not modify the clip's default parameters.
*
* @param volume Volume level at which to play this clip. Valid volume range is
* 0.0 to 1.0, where 0.0 is effectively muted and 1.0 is full volume.
* @param balance Left/right balance or relative channel volumes for stereo
* effects.
* @param rate Playback rate multiplier. 1.0 will play at the normal
* rate while 2.0 will double the rate.
* @param pan Left/right shift to be applied to the clip. A pan value of
* -1.0 means full left channel, 1.0 means full right channel, 0.0 has no
* effect.
* @param priority Audio effect priority. Lower priority effects will be
* dropped first if too many effects are trying to play simultaneously.
*/
public void play(double volume, double balance, double rate, double pan, int priority) {
if (null != audioClip) {
audioClip.play(volume, balance, rate, pan, audioClip.loopCount(), priority);
}
}
/**
* Indicate whether this AudioClip
is playing. If this returns true
* then play()
has been called at least once and it is still playing.
* @return true if any mixer channel has this clip queued, false otherwise
*/
public boolean isPlaying() {
return null != audioClip && audioClip.isPlaying();
}
/**
* Immediately stop all playback of this AudioClip
.
*/
public void stop() {
if (null != audioClip) {
audioClip.stop();
}
}
}