Browse Source

* Added javadoc to AudioNode
* Added methods play(), playInstance() and stop() to AudioNode

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7334 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

sha..rd 14 years ago
parent
commit
f640e5de21
1 changed files with 330 additions and 25 deletions
  1. 330 25
      engine/src/core/com/jme3/audio/AudioNode.java

+ 330 - 25
engine/src/core/com/jme3/audio/AudioNode.java

@@ -42,8 +42,20 @@ import com.jme3.scene.Node;
 import java.io.IOException;
 
 /**
- *
+ * An <code>AudioNode</code> is used in jME3 for playing audio files.
+ * <br/>
+ * First, an {@link AudioNode} is loaded from file, and then assigned
+ * to an audio node for playback. Once the audio node is attached to the 
+ * scene, its location will influence the position it is playing from relative
+ * to the {@link Listener}.
+ * <br/>
+ * An audio node can also play in "headspace", meaning its location
+ * or velocity does not influence how it is played. 
+ * The "positional" property of an AudioNode can be set via 
+ * {@link AudioNode#setPositional(boolean) }.
+ * 
  * @author normenhansen
+ * @author Kirill Vainer
  */
 public class AudioNode extends Node {
 
@@ -67,35 +79,108 @@ public class AudioNode extends Node {
     protected Vector3f direction = new Vector3f(0, 0, 1);
     protected float innerAngle = 360;
     protected float outerAngle = 360;
-    private boolean positional = true;
+    protected boolean positional = true;
 
+    /**
+     * <code>Status</code> indicates the current status of the audio node.
+     */
     public enum Status {
+        /**
+         * The audio node is currently playing. This will be set if
+         * {@link AudioNode#play() } is called.
+         */
         Playing,
+        
+        /**
+         * The audio node is currently paused.
+         */
         Paused,
+        
+        /**
+         * The audio node is currently stopped.
+         * This will be set if {@link AudioNode#stop() } is called 
+         * or the audio has reached the end of the file.
+         */
         Stopped,
     }
 
+    /**
+     * Creates a new <code>AudioNode</code> without any audio data set.
+     * 
+     * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+     */
     public AudioNode(AudioRenderer audioRenderer) {
+        if (audioRenderer == null)
+            throw new IllegalArgumentException("audioRenderer cannot be null");
+        
         this.renderer = audioRenderer;
     }
 
-    public AudioNode(AudioRenderer audioRenderer, AudioData ad, AudioKey key) {
+    /**
+     * Creates a new <code>AudioNode</code> with the given data and key.
+     * 
+     * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+     * @param audioData The audio data contains the audio track to play.
+     * @param key The audio key that was used to load the AudioData
+     */
+    public AudioNode(AudioRenderer audioRenderer, AudioData audioData, AudioKey key) {
         this(audioRenderer);
-        setAudioData(ad, key);
-    }
-
+        setAudioData(audioData, key);
+    }
+
+    /**
+     * Creates a new <code>AudioNode</code> with the given audio file.
+     * 
+     * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+     * @param assetManager The asset manager to use to load the audio file
+     * @param name The filename of the audio file
+     * @param stream If true, the audio will be streamed gradually from disk, 
+     *               otherwise, it will be buffered.
+     */
     public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name, boolean stream) {
         this(audioRenderer);
         this.key = new AudioKey(name, stream);
         this.data = (AudioData) assetManager.loadAsset(key);
     }
 
+    /**
+     * Creates a new <code>AudioNode</code> with the given audio file.
+     * 
+     * @param audioRenderer The audio renderer to use for playing. Cannot be null.
+     * @param assetManager The asset manager to use to load the audio file
+     * @param name The filename of the audio file
+     */
     public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) {
         this(audioRenderer, assetManager, name, false);
     }
-
-        
-
+    
+    /**
+     * Start playing the audio.
+     */
+    public void play(){
+        renderer.playSource(this);
+    }
+
+    /**
+     * Start playing an instance of this audio. This method can be used
+     * to play the same <code>AudioNode</code> multiple times. Note
+     * that changes to the parameters of this AudioNode will not effect the 
+     * instances already playing.
+     */
+    public void playInstance(){
+        renderer.playSourceInstance(this);
+    }
+    
+    /**
+     * Stop playing the audio that was started with {@link AudioNode#play() }.
+     */
+    public void stop(){
+        renderer.stopSource(this);
+    }
+    
+    /**
+     * Do not use.
+     */
     public void setChannel(int channel) {
         if (status != Status.Stopped) {
             throw new IllegalStateException("Can only set source id when stopped");
@@ -104,59 +189,117 @@ public class AudioNode extends Node {
         this.channel = channel;
     }
 
+    /**
+     * Do not use.
+     */
     public int getChannel() {
         return channel;
     }
 
+    /**
+     * @return The {#link Filter dry filter} that is set.
+     * @see AudioNode#setDryFilter(com.jme3.audio.Filter) 
+     */
     public Filter getDryFilter() {
         return dryFilter;
     }
 
+    /**
+     * Set the dry filter to use for this audio node.
+     * 
+     * When {@link AudioNode#setReverbEnabled(boolean) reverb} is used, 
+     * the dry filter will only influence the "dry" portion of the audio, 
+     * e.g. not the reverberated parts of the AudioNode playing.
+     * 
+     * See the relevent documentation for the {@link Filter} to determine
+     * the effect.
+     * 
+     * @param dryFilter The filter to set, or null to disable dry filter.
+     */
     public void setDryFilter(Filter dryFilter) {
-        if (this.dryFilter != null) {
-            throw new IllegalStateException("Filter already set");
-        }
-
         this.dryFilter = dryFilter;
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.DryFilter);
     }
 
-    public void setAudioData(AudioData ad, AudioKey key) {
+    /**
+     * Set the audio data to use for the audio. Note that this method
+     * can only be called once, if for example the audio node was initialized
+     * without an {@link AudioData}.
+     * 
+     * @param audioData The audio data contains the audio track to play.
+     * @param key The audio key that was used to load the AudioData
+     */
+    public void setAudioData(AudioData audioData, AudioKey key) {
         if (data != null) {
             throw new IllegalStateException("Cannot change data once its set");
         }
 
-        data = ad;
+        data = audioData;
         this.key = key;
     }
 
+    /**
+     * @return The {@link AudioData} set previously with 
+     * {@link AudioNode#setAudioData(com.jme3.audio.AudioData, com.jme3.audio.AudioKey) }
+     * or any of the constructors that initialize the audio data.
+     */
     public AudioData getAudioData() {
         return data;
     }
 
+    /**
+     * @return The {@link Status} of the audio node. 
+     * The status will be changed when either the {@link AudioNode#play() }
+     * or {@link AudioNode#stop() } methods are called.
+     */
     public Status getStatus() {
         return status;
     }
 
+    /**
+     * Do not use.
+     */
     public void setStatus(Status status) {
         this.status = status;
     }
 
+    /**
+     * @return True if the audio will keep looping after it is done playing,
+     * otherwise, false.
+     * @see AudioNode#setLooping(boolean)
+     */
     public boolean isLooping() {
         return loop;
     }
 
+    /**
+     * Set the looping mode for the audio node. The default is false.
+     * 
+     * @param loop True if the audio should keep looping after it is done playing.
+     */
     public void setLooping(boolean loop) {
         this.loop = loop;
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.Looping);
     }
 
+    /**
+     * @return The pitch of the audio, also the speed of playback.
+     * 
+     * @see AudioNode#setPitch(float) 
+     */
     public float getPitch() {
         return pitch;
     }
 
+    /**
+     * Set the pitch of the audio, also the speed of playback.
+     * The value must be between 0.5 and 2.0.
+     * 
+     * @param pitch The pitch to set.
+     * @throws IllegalArgumentException If pitch is not between 0.5 and 2.0.
+     */
     public void setPitch(float pitch) {
         if (pitch < 0.5f || pitch > 2.0f) {
             throw new IllegalArgumentException("Pitch must be between 0.5 and 2.0");
@@ -167,10 +310,23 @@ public class AudioNode extends Node {
             renderer.updateSourceParam(this, AudioParam.Pitch);
     }
 
+    /**
+     * @return The volume of this audio node.
+     * 
+     * @see AudioNode#setVolume(float)
+     */
     public float getVolume() {
         return volume;
     }
 
+    /**
+     * Set the volume of this audio node.
+     * 
+     * The volume is specified as gain. 1.0 is the default.
+     * 
+     * @param volume The volume to set.
+     * @throws IllegalArgumentException If volume is negative
+     */
     public void setVolume(float volume) {
         if (volume < 0f) {
             throw new IllegalArgumentException("Volume cannot be negative");
@@ -181,10 +337,19 @@ public class AudioNode extends Node {
             renderer.updateSourceParam(this, AudioParam.Volume);
     }
 
+    /**
+     * @return The time offset in seconds when the sound will start playing.
+     */
     public float getTimeOffset() {
         return timeOffset;
     }
 
+    /**
+     * Set the time offset in seconds when the sound will start playing.
+     * 
+     * @param timeOffset The time offset
+     * @throws IllegalArgumentException If timeOffset is negative
+     */
     public void setTimeOffset(float timeOffset) {
         if (timeOffset < 0f) {
             throw new IllegalArgumentException("Time offset cannot be negative");
@@ -193,30 +358,72 @@ public class AudioNode extends Node {
         this.timeOffset = timeOffset;
     }
 
+    /**
+     * @return The velocity of the audio node.
+     * 
+     * @see AudioNode#setVelocity(com.jme3.math.Vector3f)
+     */
     public Vector3f getVelocity() {
         return velocity;
     }
 
+    /**
+     * Set the velocity of the audio node. The velocity is expected
+     * to be in meters. Does nothing if the audio node is not positional.
+     * 
+     * @param velocity The velocity to set.
+     * @see AudioNode#setPositional(boolean)
+     */
     public void setVelocity(Vector3f velocity) {
         this.velocity.set(velocity);
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.Velocity);
     }
 
+    /**
+     * @return True if reverb is enabled, otherwise false.
+     * 
+     * @see AudioNode#setReverbEnabled(boolean)
+     */
     public boolean isReverbEnabled() {
         return reverbEnabled;
     }
 
+    /**
+     * Set to true to enable reverberation effects for this audio node.
+     * Does nothing if the audio node is not positional.
+     * <br/>
+     * When enabled, the audio environment set with 
+     * {@link AudioRenderer#setEnvironment(com.jme3.audio.Environment) }
+     * will apply a reverb effect to the audio playing from this audio node.
+     * 
+     * @param reverbEnabled True to enable reverb.
+     */
     public void setReverbEnabled(boolean reverbEnabled) {
         this.reverbEnabled = reverbEnabled;
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.ReverbEnabled);
     }
 
+    /**
+     * @return Filter for the reverberations of this audio node.
+     * 
+     * @see AudioNode#setReverbFilter(com.jme3.audio.Filter) 
+     */
     public Filter getReverbFilter() {
         return reverbFilter;
     }
 
+    /**
+     * Set the reverb filter for this audio node.
+     * <br/>
+     * The reverb filter will influence the reverberations
+     * of the audio node playing. This only has an effect if
+     * reverb is enabled.
+     * 
+     * @param reverbFilter The reverb filter to set.
+     * @see AudioNode#setDryFilter(com.jme3.audio.Filter)
+     */
     public void setReverbFilter(Filter reverbFilter) {
         if (this.reverbFilter != null) {
             throw new IllegalStateException("Filter already set");
@@ -227,10 +434,25 @@ public class AudioNode extends Node {
             renderer.updateSourceParam(this, AudioParam.ReverbFilter);
     }
 
+    /**
+     * @return Max distance for this audio node.
+     * 
+     * @see AudioNode#setMaxDistance(float)
+     */
     public float getMaxDistance() {
         return maxDistance;
     }
 
+    /**
+     * Set the maximum playing distance for the audio node.
+     * Does nothing if the audio node is not positional.
+     * <br/>
+     * The maximum distance is the distance beyond which the audio
+     * node will no longer be heard.
+     * 
+     * @param maxDistance The maximum playing distance.
+     * @throws IllegalArgumentException If maxDistance is negative
+     */
     public void setMaxDistance(float maxDistance) {
         if (maxDistance < 0) {
             throw new IllegalArgumentException("Max distance cannot be negative");
@@ -241,10 +463,25 @@ public class AudioNode extends Node {
             renderer.updateSourceParam(this, AudioParam.MaxDistance);
     }
 
+    /**
+     * @return The reference playing distance for the audio node.
+     * 
+     * @see AudioNode#setRefDistance(float) 
+     */
     public float getRefDistance() {
         return refDistance;
     }
 
+    /**
+     * Set the reference playing distance for the audio node.
+     * Does nothing if the audio node is not positional.
+     * <br/>
+     * The reference playing distance is the distance at which the
+     * audio node will be exactly half of its volume.
+     * 
+     * @param refDistance The reference playing distance.
+     * @throws  IllegalArgumentException If refDistance is negative
+     */
     public void setRefDistance(float refDistance) {
         if (refDistance < 0) {
             throw new IllegalArgumentException("Reference distance cannot be negative");
@@ -255,52 +492,114 @@ public class AudioNode extends Node {
             renderer.updateSourceParam(this, AudioParam.RefDistance);
     }
 
+    /**
+     * @return True if the audio node is directional
+     * 
+     * @see AudioNode#setDirectional(boolean) 
+     */
     public boolean isDirectional() {
         return directional;
     }
 
+    /**
+     * Set the audio node to be directional.
+     * Does nothing if the audio node is not positional.
+     * <br/>
+     * After setting directional, you should call 
+     * {@link AudioNode#setDirection(com.jme3.math.Vector3f) }
+     * to set the audio node's direction.
+     * 
+     * @param directional If the audio node is directional
+     */
     public void setDirectional(boolean directional) {
         this.directional = directional;
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.IsDirectional);
     }
 
+    /**
+     * @return The direction of this audio node.
+     * 
+     * @see AudioNode#setDirection(com.jme3.math.Vector3f)
+     */
     public Vector3f getDirection() {
         return direction;
     }
 
+    /**
+     * Set the direction of this audio node.
+     * Does nothing if the audio node is not directional.
+     * 
+     * @param direction 
+     * @see AudioNode#setDirectional(boolean) 
+     */
     public void setDirection(Vector3f direction) {
         this.direction = direction;
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.Direction);
     }
 
+    /**
+     * @return The directional audio node, cone inner angle.
+     * 
+     * @see AudioNode#setInnerAngle(float) 
+     */
     public float getInnerAngle() {
         return innerAngle;
     }
 
+    /**
+     * Set the directional audio node cone inner angle.
+     * Does nothing if the audio node is not directional.
+     * 
+     * @param innerAngle The cone inner angle.
+     */
     public void setInnerAngle(float innerAngle) {
         this.innerAngle = innerAngle;
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.InnerAngle);
     }
 
+    /**
+     * @return The directional audio node, cone outer angle.
+     * 
+     * @see AudioNode#setOuterAngle(float) 
+     */
     public float getOuterAngle() {
         return outerAngle;
     }
 
+    /**
+     * Set the directional audio node cone outer angle.
+     * Does nothing if the audio node is not directional.
+     * 
+     * @param outerAngle The cone outer angle.
+     */
     public void setOuterAngle(float outerAngle) {
         this.outerAngle = outerAngle;
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.OuterAngle);
     }
 
+    /**
+     * @return True if the audio node is positional.
+     * 
+     * @see AudioNode#setPositional(boolean) 
+     */
     public boolean isPositional() {
         return positional;
     }
 
-    public void setPositional(boolean inHeadspace) {
-        this.positional = inHeadspace;
+    /**
+     * Set the audio node as positional.
+     * The position, velocity, and distance parameters effect positional
+     * audio nodes. Set to false if the audio node should play in "headspace".
+     * 
+     * @param positional True if the audio node should be positional, otherwise
+     * false if it should be headspace.
+     */
+    public void setPositional(boolean positional) {
+        this.positional = positional;
         if (channel >= 0)
             renderer.updateSourceParam(this, AudioParam.IsPositional);
     }
@@ -318,14 +617,15 @@ public class AudioNode extends Node {
             renderer.updateSourceParam(this, AudioParam.Position);
     }
 
-//    @Override
-//    public AudioNode clone(){
-//        try{
-//            return (AudioNode) super.clone();
-//        }catch (CloneNotSupportedException ex){
-//            return null;
-//        }
-//    }
+    @Override
+    public AudioNode clone(){
+        AudioNode clone = (AudioNode) super.clone();
+        
+        clone.direction = direction.clone();
+        clone.velocity  = velocity.clone();
+        
+        return clone;
+    }
     
     @Override
     public void write(JmeExporter ex) throws IOException {
@@ -348,6 +648,8 @@ public class AudioNode extends Node {
         oc.write(direction, "direction", null);
         oc.write(innerAngle, "inner_angle", 360);
         oc.write(outerAngle, "outer_angle", 360);
+        
+        oc.write(positional, "positional", false);
     }
 
     @Override
@@ -371,6 +673,9 @@ public class AudioNode extends Node {
         direction = (Vector3f) ic.readSavable("direction", null);
         innerAngle = ic.readFloat("inner_angle", 360);
         outerAngle = ic.readFloat("outer_angle", 360);
+        
+        positional = ic.readBoolean("positional", false);
+        
         data = im.getAssetManager().loadAudio(key);
     }