diff --git a/jme3-core/src/main/java/com/jme3/audio/Filter.java b/jme3-core/src/main/java/com/jme3/audio/Filter.java index 83bc99e753..86bf987cd6 100644 --- a/jme3-core/src/main/java/com/jme3/audio/Filter.java +++ b/jme3-core/src/main/java/com/jme3/audio/Filter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2020 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,9 +35,12 @@ import com.jme3.export.JmeImporter; import com.jme3.export.Savable; import com.jme3.util.NativeObject; +import com.jme3.util.clone.Cloner; +import com.jme3.util.clone.JmeCloneable; + import java.io.IOException; -public abstract class Filter extends NativeObject implements Savable { +public abstract class Filter extends NativeObject implements Savable, JmeCloneable { public Filter() { super(); @@ -49,12 +52,28 @@ protected Filter(int id) { @Override public void write(JmeExporter ex) throws IOException { - // nothing to save + // no-op } @Override public void read(JmeImporter im) throws IOException { - // nothing to read + // no-op + } + + /** + * Called internally by com.jme3.util.clone.Cloner. Do not call directly. + */ + @Override + public Object jmeClone() { + return super.clone(); + } + + /** + * Called internally by com.jme3.util.clone.Cloner. Do not call directly. + */ + @Override + public void cloneFields(Cloner cloner, Object original) { + // no-op } @Override diff --git a/jme3-core/src/main/java/com/jme3/audio/LowPassFilter.java b/jme3-core/src/main/java/com/jme3/audio/LowPassFilter.java index daa49adb65..518d1c2af8 100644 --- a/jme3-core/src/main/java/com/jme3/audio/LowPassFilter.java +++ b/jme3-core/src/main/java/com/jme3/audio/LowPassFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2023 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,26 +36,63 @@ import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.util.NativeObject; + import java.io.IOException; +/** + * A filter that attenuates frequencies above a specified threshold, allowing lower + * frequencies to pass through with less attenuation. Commonly used to simulate effects + * such as muffling or underwater acoustics. + */ public class LowPassFilter extends Filter { - protected float volume, highFreqVolume; + /** + * The overall volume scaling of the filtered sound + */ + protected float volume; + /** + * The volume scaling of the high frequencies allowed to pass through. Valid values range + * from 0.0 to 1.0, where 0.0 completely eliminates high frequencies and 1.0 lets them pass + * through unchanged. + */ + protected float highFreqVolume; + /** + * Constructs a low-pass filter. + * + * @param volume the overall volume scaling of the filtered sound (0.0 - 1.0). + * @param highFreqVolume the volume scaling of high frequencies (0.0 - 1.0). + * @throws IllegalArgumentException if {@code volume} or {@code highFreqVolume} is out of range. + */ public LowPassFilter(float volume, float highFreqVolume) { super(); setVolume(volume); setHighFreqVolume(highFreqVolume); } + /** + * For internal cloning + * @param id the native object ID + */ protected LowPassFilter(int id) { super(id); } + /** + * Retrieves the current volume scaling of high frequencies. + * + * @return the high-frequency volume scaling. + */ public float getHighFreqVolume() { return highFreqVolume; } + /** + * Sets the high-frequency volume. + * + * @param highFreqVolume the new high-frequency volume scaling (0.0 - 1.0). + * @throws IllegalArgumentException if {@code highFreqVolume} is out of range. + */ public void setHighFreqVolume(float highFreqVolume) { if (highFreqVolume < 0 || highFreqVolume > 1) throw new IllegalArgumentException("High freq volume must be between 0 and 1"); @@ -64,10 +101,21 @@ public void setHighFreqVolume(float highFreqVolume) { this.updateNeeded = true; } + /** + * Retrieves the current overall volume scaling of the filtered sound. + * + * @return the overall volume scaling. + */ public float getVolume() { return volume; } + /** + * Sets the overall volume. + * + * @param volume the new overall volume scaling (0.0 - 1.0). + * @throws IllegalArgumentException if {@code volume} is out of range. + */ public void setVolume(float volume) { if (volume < 0 || volume > 1) throw new IllegalArgumentException("Volume must be between 0 and 1"); @@ -92,11 +140,21 @@ public void read(JmeImporter im) throws IOException { highFreqVolume = ic.readFloat("hf_volume", 0); } + /** + * Creates a native object clone of this filter for internal usage. + * + * @return a new {@code LowPassFilter} instance with the same native ID. + */ @Override public NativeObject createDestructableClone() { return new LowPassFilter(id); } + /** + * Retrieves a unique identifier for this filter. Used internally for native object management. + * + * @return a unique long identifier. + */ @Override public long getUniqueId() { return ((long) OBJTYPE_FILTER << 32) | (0xffffffffL & (long) id); diff --git a/jme3-core/src/test/java/com/jme3/audio/AudioNodeTest.java b/jme3-core/src/test/java/com/jme3/audio/AudioNodeTest.java new file mode 100644 index 0000000000..e7e32012f8 --- /dev/null +++ b/jme3-core/src/test/java/com/jme3/audio/AudioNodeTest.java @@ -0,0 +1,48 @@ +package com.jme3.audio; + +import com.jme3.asset.AssetManager; +import com.jme3.math.Vector3f; +import com.jme3.system.JmeSystem; +import org.junit.Assert; +import org.junit.Test; + +/** + * Automated tests for the {@code AudioNode} class. + * + * @author capdevon + */ +public class AudioNodeTest { + + @Test + public void testAudioNodeClone() { + AssetManager assetManager = JmeSystem.newAssetManager(AudioNodeTest.class.getResource("/com/jme3/asset/Desktop.cfg")); + + AudioNode audio = new AudioNode(assetManager, + "Sound/Effects/Bang.wav", AudioData.DataType.Buffer); + audio.setDirection(new Vector3f(0, 1, 0)); + audio.setVelocity(new Vector3f(1, 1, 1)); + audio.setDryFilter(new LowPassFilter(1f, .1f)); + audio.setReverbFilter(new LowPassFilter(.5f, .5f)); + + AudioNode clone = audio.clone(); + + Assert.assertNotNull(clone.previousWorldTranslation); + Assert.assertNotSame(audio.previousWorldTranslation, clone.previousWorldTranslation); + Assert.assertEquals(audio.previousWorldTranslation, clone.previousWorldTranslation); + + Assert.assertNotNull(clone.getDirection()); + Assert.assertNotSame(audio.getDirection(), clone.getDirection()); + Assert.assertEquals(audio.getDirection(), clone.getDirection()); + + Assert.assertNotNull(clone.getVelocity()); + Assert.assertNotSame(audio.getVelocity(), clone.getVelocity()); + Assert.assertEquals(audio.getVelocity(), clone.getVelocity()); + + Assert.assertNotNull(clone.getDryFilter()); + Assert.assertNotSame(audio.getDryFilter(), clone.getDryFilter()); + + Assert.assertNotNull(clone.getReverbFilter()); + Assert.assertNotSame(audio.getReverbFilter(), clone.getReverbFilter()); + } + +}