From eda92656dd3f16992b7d551322514a874033e06e Mon Sep 17 00:00:00 2001 From: Paul Speed Date: Sat, 26 Mar 2016 04:19:59 -0400 Subject: [PATCH] Updated AudioNode with a JmeCloneable cloneFields() method to clone its fields. Some small change in behavior since the new methods will clone the filters, too, to avoid 'user surprise'. --- .../main/java/com/jme3/audio/AudioNode.java | 215 ++++++++++-------- 1 file changed, 119 insertions(+), 96 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/audio/AudioNode.java b/jme3-core/src/main/java/com/jme3/audio/AudioNode.java index 2c5cc5b98..bf4705d87 100644 --- a/jme3-core/src/main/java/com/jme3/audio/AudioNode.java +++ b/jme3-core/src/main/java/com/jme3/audio/AudioNode.java @@ -41,26 +41,27 @@ import com.jme3.export.OutputCapsule; import com.jme3.math.Vector3f; import com.jme3.scene.Node; import com.jme3.util.PlaceholderAssets; +import com.jme3.util.clone.Cloner; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; /** - * An AudioNode is a scene Node which can play audio assets. - * - * An AudioNode is either positional or ambient, with positional being the - * default. Once a positional node is attached to the scene, its location and - * velocity relative to the {@link Listener} affect how it sounds when played. - * Positional nodes can only play monoaural (single-channel) assets, not stereo - * ones. - * - * An ambient AudioNode plays in "headspace", meaning that the node's location - * and velocity do not affect how it sounds when played. Ambient audio nodes can - * play stereo assets. - * - * The "positional" property of an AudioNode can be set via + * An AudioNode is a scene Node which can play audio assets. + * + * An AudioNode is either positional or ambient, with positional being the + * default. Once a positional node is attached to the scene, its location and + * velocity relative to the {@link Listener} affect how it sounds when played. + * Positional nodes can only play monoaural (single-channel) assets, not stereo + * ones. + * + * An ambient AudioNode plays in "headspace", meaning that the node's location + * and velocity do not affect how it sounds when played. Ambient audio nodes can + * play stereo assets. + * + * The "positional" property of an AudioNode can be set via * {@link AudioNode#setPositional(boolean) }. - * + * * @author normenhansen * @author Kirill Vainer */ @@ -99,15 +100,15 @@ public class AudioNode extends Node implements AudioSource { * {@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 + * This will be set if {@link AudioNode#stop() } is called * or the audio has reached the end of the file. */ Stopped, @@ -121,14 +122,14 @@ public class AudioNode extends Node implements AudioSource { /** * Creates a new AudioNode with the given data and key. - * + * * @param audioData The audio data contains the audio track to play. * @param audioKey The audio key that was used to load the AudioData */ public AudioNode(AudioData audioData, AudioKey audioKey) { setAudioData(audioData, audioKey); } - + /** * Creates a new AudioNode with the given audio file. * @param assetManager The asset manager to use to load the audio file @@ -142,16 +143,16 @@ public class AudioNode extends Node implements AudioSource { /** * Creates a new AudioNode with the given audio file. - * + * * @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, + * @param stream If true, the audio will be streamed gradually from disk, * otherwise, it will be buffered. * @param streamCache If stream is also true, then this specifies if * the stream cache is used. When enabled, the audio stream will - * be read entirely but not decoded, allowing features such as + * be read entirely but not decoded, allowing features such as * seeking, looping and determining duration. - * + * * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead */ public AudioNode(AssetManager assetManager, String name, boolean stream, boolean streamCache) { @@ -161,12 +162,12 @@ public class AudioNode extends Node implements AudioSource { /** * Creates a new AudioNode with the given audio file. - * + * * @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, + * @param stream If true, the audio will be streamed gradually from disk, * otherwise, it will be buffered. - * + * * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType)} instead */ public AudioNode(AssetManager assetManager, String name, boolean stream) { @@ -175,20 +176,20 @@ public class AudioNode extends Node implements AudioSource { /** * Creates a new AudioNode 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 - * + * * @deprecated AudioRenderer parameter is ignored. */ public AudioNode(AudioRenderer audioRenderer, AssetManager assetManager, String name) { this(assetManager, name, DataType.Buffer); } - + /** * Creates a new AudioNode with the given audio file. - * + * * @param assetManager The asset manager to use to load the audio file * @param name The filename of the audio file * @deprecated Use {@link AudioNode#AudioNode(com.jme3.asset.AssetManager, java.lang.String, com.jme3.audio.AudioData.DataType) } instead @@ -196,14 +197,14 @@ public class AudioNode extends Node implements AudioSource { public AudioNode(AssetManager assetManager, String name) { this(assetManager, name, DataType.Buffer); } - + protected AudioRenderer getRenderer() { AudioRenderer result = AudioContext.getAudioRenderer(); if( result == null ) throw new IllegalStateException( "No audio renderer available, make sure call is being performed on render thread." ); - return result; + return result; } - + /** * Start playing the audio. */ @@ -217,7 +218,7 @@ public class AudioNode extends Node implements AudioSource { /** * Start playing an instance of this audio. This method can be used * to play the same AudioNode multiple times. Note - * that changes to the parameters of this AudioNode will not effect the + * that changes to the parameters of this AudioNode will not effect the * instances already playing. */ public void playInstance(){ @@ -226,21 +227,21 @@ public class AudioNode extends Node implements AudioSource { } getRenderer().playSourceInstance(this); } - + /** * Stop playing the audio that was started with {@link AudioNode#play() }. */ public void stop(){ getRenderer().stopSource(this); } - + /** * Pause the audio that was started with {@link AudioNode#play() }. */ public void pause(){ getRenderer().pauseSource(this); } - + /** * Do not use. */ @@ -261,7 +262,7 @@ public class AudioNode extends Node implements AudioSource { /** * @return The {#link Filter dry filter} that is set. - * @see AudioNode#setDryFilter(com.jme3.audio.Filter) + * @see AudioNode#setDryFilter(com.jme3.audio.Filter) */ public Filter getDryFilter() { return dryFilter; @@ -269,14 +270,14 @@ public class AudioNode extends Node implements AudioSource { /** * 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, + * + * 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) { @@ -289,7 +290,7 @@ public class AudioNode extends Node implements AudioSource { * 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 audioKey The audio key that was used to load the AudioData */ @@ -303,7 +304,7 @@ public class AudioNode extends Node implements AudioSource { } /** - * @return The {@link AudioData} set previously with + * @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. */ @@ -312,7 +313,7 @@ public class AudioNode extends Node implements AudioSource { } /** - * @return The {@link Status} of the audio node. + * @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. */ @@ -339,7 +340,7 @@ public class AudioNode extends Node implements AudioSource { else return data.getDataType(); } - + /** * @return True if the audio will keep looping after it is done playing, * otherwise, false. @@ -351,7 +352,7 @@ public class AudioNode extends Node implements AudioSource { /** * 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) { @@ -362,8 +363,8 @@ public class AudioNode extends Node implements AudioSource { /** * @return The pitch of the audio, also the speed of playback. - * - * @see AudioNode#setPitch(float) + * + * @see AudioNode#setPitch(float) */ public float getPitch() { return pitch; @@ -372,7 +373,7 @@ public class AudioNode extends Node implements AudioSource { /** * 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. */ @@ -388,7 +389,7 @@ public class AudioNode extends Node implements AudioSource { /** * @return The volume of this audio node. - * + * * @see AudioNode#setVolume(float) */ public float getVolume() { @@ -397,9 +398,9 @@ public class AudioNode extends Node implements AudioSource { /** * 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 */ @@ -422,7 +423,7 @@ public class AudioNode extends Node implements AudioSource { /** * Set the time offset in the sound sample when to start playing. - * + * * @param timeOffset The time offset * @throws IllegalArgumentException If timeOffset is negative */ @@ -439,7 +440,7 @@ public class AudioNode extends Node implements AudioSource { play(); } } - + @Override public float getPlaybackTime() { if (channel >= 0) @@ -451,10 +452,10 @@ public class AudioNode extends Node implements AudioSource { public Vector3f getPosition() { return getWorldTranslation(); } - + /** * @return The velocity of the audio node. - * + * * @see AudioNode#setVelocity(com.jme3.math.Vector3f) */ public Vector3f getVelocity() { @@ -464,7 +465,7 @@ public class AudioNode extends Node implements AudioSource { /** * 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) */ @@ -476,7 +477,7 @@ public class AudioNode extends Node implements AudioSource { /** * @return True if reverb is enabled, otherwise false. - * + * * @see AudioNode#setReverbEnabled(boolean) */ public boolean isReverbEnabled() { @@ -487,10 +488,10 @@ public class AudioNode extends Node implements AudioSource { * Set to true to enable reverberation effects for this audio node. * Does nothing if the audio node is not positional. *
- * When enabled, the audio environment set with + * 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) { @@ -502,8 +503,8 @@ public class AudioNode extends Node implements AudioSource { /** * @return Filter for the reverberations of this audio node. - * - * @see AudioNode#setReverbFilter(com.jme3.audio.Filter) + * + * @see AudioNode#setReverbFilter(com.jme3.audio.Filter) */ public Filter getReverbFilter() { return reverbFilter; @@ -515,7 +516,7 @@ public class AudioNode extends Node implements AudioSource { * 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) */ @@ -527,7 +528,7 @@ public class AudioNode extends Node implements AudioSource { /** * @return Max distance for this audio node. - * + * * @see AudioNode#setMaxDistance(float) */ public float getMaxDistance() { @@ -545,7 +546,7 @@ public class AudioNode extends Node implements AudioSource { * get any quieter than at that distance. If you want a sound to fall-off * very quickly then set ref distance very short and leave this distance * very long. - * + * * @param maxDistance The maximum playing distance. * @throws IllegalArgumentException If maxDistance is negative */ @@ -561,8 +562,8 @@ public class AudioNode extends Node implements AudioSource { /** * @return The reference playing distance for the audio node. - * - * @see AudioNode#setRefDistance(float) + * + * @see AudioNode#setRefDistance(float) */ public float getRefDistance() { return refDistance; @@ -574,7 +575,7 @@ public class AudioNode extends Node implements AudioSource { *
* 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 */ @@ -590,8 +591,8 @@ public class AudioNode extends Node implements AudioSource { /** * @return True if the audio node is directional - * - * @see AudioNode#setDirectional(boolean) + * + * @see AudioNode#setDirectional(boolean) */ public boolean isDirectional() { return directional; @@ -601,10 +602,10 @@ public class AudioNode extends Node implements AudioSource { * Set the audio node to be directional. * Does nothing if the audio node is not positional. *
- * After setting directional, you should call + * 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) { @@ -615,7 +616,7 @@ public class AudioNode extends Node implements AudioSource { /** * @return The direction of this audio node. - * + * * @see AudioNode#setDirection(com.jme3.math.Vector3f) */ public Vector3f getDirection() { @@ -625,9 +626,9 @@ public class AudioNode extends Node implements AudioSource { /** * Set the direction of this audio node. * Does nothing if the audio node is not directional. - * - * @param direction - * @see AudioNode#setDirectional(boolean) + * + * @param direction + * @see AudioNode#setDirectional(boolean) */ public void setDirection(Vector3f direction) { this.direction = direction; @@ -637,8 +638,8 @@ public class AudioNode extends Node implements AudioSource { /** * @return The directional audio node, cone inner angle. - * - * @see AudioNode#setInnerAngle(float) + * + * @see AudioNode#setInnerAngle(float) */ public float getInnerAngle() { return innerAngle; @@ -647,7 +648,7 @@ public class AudioNode extends Node implements AudioSource { /** * 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) { @@ -658,8 +659,8 @@ public class AudioNode extends Node implements AudioSource { /** * @return The directional audio node, cone outer angle. - * - * @see AudioNode#setOuterAngle(float) + * + * @see AudioNode#setOuterAngle(float) */ public float getOuterAngle() { return outerAngle; @@ -668,7 +669,7 @@ public class AudioNode extends Node implements AudioSource { /** * 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) { @@ -679,8 +680,8 @@ public class AudioNode extends Node implements AudioSource { /** * @return True if the audio node is positional. - * - * @see AudioNode#setPositional(boolean) + * + * @see AudioNode#setPositional(boolean) */ public boolean isPositional() { return positional; @@ -690,7 +691,7 @@ public class AudioNode extends Node implements AudioSource { * 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. */ @@ -707,7 +708,7 @@ public class AudioNode extends Node implements AudioSource { if ((refreshFlags & RF_TRANSFORM) != 0){ updatePos = true; } - + super.updateGeometricState(); if (updatePos && channel >= 0) @@ -717,13 +718,35 @@ public class AudioNode extends Node implements AudioSource { @Override public AudioNode clone(){ AudioNode clone = (AudioNode) super.clone(); - + clone.direction = direction.clone(); clone.velocity = velocity.clone(); - + return clone; } - + + /** + * Called internally by com.jme3.util.clone.Cloner. Do not call directly. + */ + @Override + public void cloneFields( Cloner cloner, Object original ) { + this.direction = cloner.clone(direction); + this.velocity = cloner.clone(velocity); + + // Change in behavior: the filters were not cloned before meaning + // that two cloned audio nodes would share the same filter instance. + // While settings will only be applied when the filter is actually + // set, I think it's probably surprising to callers if the values of + // a filter change from one AudioNode when a different AudioNode's + // filter attributes are updated. + // Plus if they disable and re-enable the thing using the filter then + // the settings get reapplied and it might be surprising to have them + // suddenly be strange. + // ...so I'll clone them. -pspeed + this.dryFilter = cloner.clone(dryFilter); + this.reverbFilter = cloner.clone(reverbFilter); + } + @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -745,7 +768,7 @@ public class AudioNode extends Node implements AudioSource { oc.write(direction, "direction", null); oc.write(innerAngle, "inner_angle", 360); oc.write(outerAngle, "outer_angle", 360); - + oc.write(positional, "positional", false); } @@ -753,7 +776,7 @@ public class AudioNode extends Node implements AudioSource { public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule ic = im.getCapsule(this); - + // NOTE: In previous versions of jME3, audioKey was actually // written with the name "key". This has been changed // to "audio_key" in case Spatial's key will be written as "key". @@ -762,7 +785,7 @@ public class AudioNode extends Node implements AudioSource { }else{ audioKey = (AudioKey) ic.readSavable("audio_key", null); } - + loop = ic.readBoolean("looping", false); volume = ic.readFloat("volume", 1); pitch = ic.readFloat("pitch", 1); @@ -779,9 +802,9 @@ public class AudioNode extends Node implements AudioSource { direction = (Vector3f) ic.readSavable("direction", null); innerAngle = ic.readFloat("inner_angle", 360); outerAngle = ic.readFloat("outer_angle", 360); - + positional = ic.readBoolean("positional", false); - + if (audioKey != null) { try { data = im.getAssetManager().loadAsset(audioKey);