diff --git a/jme3-core/src/main/java/com/jme3/audio/BandPassFilter.java b/jme3-core/src/main/java/com/jme3/audio/BandPassFilter.java new file mode 100644 index 0000000000..950371278c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/audio/BandPassFilter.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2009-2025 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.audio; + +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.util.NativeObject; + +import java.io.IOException; + +/** + * Represents an OpenAL EFX Band-Pass Filter. + */ +public class BandPassFilter extends Filter { + + // Default values based on OpenAL EFX specification defaults + protected float volume = 1.0f; + protected float highFreqVolume = 1.0f; + protected float lowFreqVolume = 1.0f; + + /** + * Constructs a band-pass filter with default settings. + * Required for jME deserialization + */ + public BandPassFilter() {} + + protected BandPassFilter(int id) { + super(id); + } + + public BandPassFilter(float volume, float highFreqVolume, float lowFreqVolume) { + super(); + setVolume(volume); + setHighFreqVolume(highFreqVolume); + setLowFreqVolume(lowFreqVolume); + } + + public float getVolume() { + return volume; + } + + /** + * Sets the overall gain of the Band-Pass filter. + * + * @param volume The gain value (0.0 to 1.0). + */ + public void setVolume(float volume) { + if (volume < 0 || volume > 1) + throw new IllegalArgumentException("Volume must be between 0 and 1"); + + this.volume = volume; + this.updateNeeded = true; + } + + public float getHighFreqVolume() { + return highFreqVolume; + } + + /** + * Sets the gain at high frequencies for the Band-Pass filter. + * + * @param highFreqVolume The high-frequency gain value (0.0 to 1.0). + */ + public void setHighFreqVolume(float highFreqVolume) { + if (highFreqVolume < 0 || highFreqVolume > 1) + throw new IllegalArgumentException("High freq volume must be between 0 and 1"); + + this.highFreqVolume = highFreqVolume; + this.updateNeeded = true; + } + + public float getLowFreqVolume() { + return lowFreqVolume; + } + + /** + * Sets the gain at low frequencies for the Band-Pass filter. + * + * @param lowFreqVolume The low-frequency gain value (0.0 to 1.0). + */ + public void setLowFreqVolume(float lowFreqVolume) { + if (lowFreqVolume < 0 || lowFreqVolume > 1) + throw new IllegalArgumentException("Low freq volume must be between 0 and 1"); + + this.lowFreqVolume = lowFreqVolume; + this.updateNeeded = true; + } + + @Override + public NativeObject createDestructableClone() { + return new BandPassFilter(this.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); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + oc.write(this.volume, "volume", 1f); + oc.write(this.lowFreqVolume, "lf_volume", 1f); + oc.write(this.highFreqVolume, "hf_volume", 1f); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + this.volume = ic.readFloat("volume", 1f); + this.lowFreqVolume = ic.readFloat("lf_volume", 1f); + this.highFreqVolume = ic.readFloat("hf_volume", 1f); + } +} diff --git a/jme3-core/src/main/java/com/jme3/audio/HighPassFilter.java b/jme3-core/src/main/java/com/jme3/audio/HighPassFilter.java new file mode 100644 index 0000000000..f3838abc0e --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/audio/HighPassFilter.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2009-2025 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.audio; + +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.util.NativeObject; + +import java.io.IOException; + +/** + * Represents an OpenAL EFX High-Pass Filter. + */ +public class HighPassFilter extends Filter { + + // Default values based on OpenAL EFX specification defaults + protected float volume = 1.0f; + protected float lowFreqVolume = 1.0f; + + /** + * Constructs a high-pass filter with default settings. + * Required for jME deserialization + */ + public HighPassFilter(){} + + protected HighPassFilter(int id) { + super(id); + } + + public HighPassFilter(float volume, float lowFreqVolume) { + super(); + setVolume(volume); + setLowFreqVolume(lowFreqVolume); + } + + public float getVolume() { + return volume; + } + + /** + * Sets the gain of the High-Pass filter. + * + * @param volume The gain value (0.0 to 1.0). + */ + public void setVolume(float volume) { + if (volume < 0 || volume > 1) + throw new IllegalArgumentException("Volume must be between 0 and 1"); + + this.volume = volume; + this.updateNeeded = true; + } + + public float getLowFreqVolume() { + return lowFreqVolume; + } + + /** + * Sets the gain at low frequencies for the High-Pass filter. + * + * @param lowFreqVolume The low-frequency gain value (0.0 to 1.0). + */ + public void setLowFreqVolume(float lowFreqVolume) { + if (lowFreqVolume < 0 || lowFreqVolume > 1) + throw new IllegalArgumentException("Low freq volume must be between 0 and 1"); + + this.lowFreqVolume = lowFreqVolume; + this.updateNeeded = true; + } + + @Override + public NativeObject createDestructableClone() { + return new HighPassFilter(this.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); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + oc.write(this.volume, "volume", 1f); + oc.write(this.lowFreqVolume, "lf_volume", 1f); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule ic = im.getCapsule(this); + this.volume = ic.readFloat("volume", 1f); + this.lowFreqVolume = ic.readFloat("lf_volume", 1f); + } +} diff --git a/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java b/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java index d86b07885c..ec89b9df06 100644 --- a/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java +++ b/jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java @@ -40,8 +40,10 @@ import static com.jme3.audio.openal.AL.*; import com.jme3.audio.AudioStream; +import com.jme3.audio.BandPassFilter; import com.jme3.audio.Environment; import com.jme3.audio.Filter; +import com.jme3.audio.HighPassFilter; import com.jme3.audio.Listener; import com.jme3.audio.ListenerParam; import com.jme3.audio.LowPassFilter; @@ -403,10 +405,25 @@ private void updateFilter(Filter f) { efx.alFilteri(id, EFX.AL_FILTER_TYPE, EFX.AL_FILTER_LOWPASS); efx.alFilterf(id, EFX.AL_LOWPASS_GAIN, lowPass.getVolume()); efx.alFilterf(id, EFX.AL_LOWPASS_GAINHF, lowPass.getHighFreqVolume()); - f.clearUpdateNeeded(); + + } else if (f instanceof HighPassFilter) { + HighPassFilter highPass = (HighPassFilter) f; + efx.alFilteri(id, EFX.AL_FILTER_TYPE, EFX.AL_FILTER_HIGHPASS); + efx.alFilterf(id, EFX.AL_HIGHPASS_GAIN, highPass.getVolume()); + efx.alFilterf(id, EFX.AL_HIGHPASS_GAINLF, highPass.getLowFreqVolume()); + + } else if (f instanceof BandPassFilter) { + BandPassFilter bandPass = (BandPassFilter) f; + efx.alFilteri(id, EFX.AL_FILTER_TYPE, EFX.AL_FILTER_BANDPASS); + efx.alFilterf(id, EFX.AL_BANDPASS_GAIN, bandPass.getVolume()); + efx.alFilterf(id, EFX.AL_BANDPASS_GAINHF, bandPass.getHighFreqVolume()); + efx.alFilterf(id, EFX.AL_BANDPASS_GAINLF, bandPass.getLowFreqVolume()); + } else { throw new UnsupportedOperationException("Unsupported filter type: " + f.getClass().getName()); } + + f.clearUpdateNeeded(); } /** diff --git a/jme3-core/src/main/java/com/jme3/audio/openal/EFX.java b/jme3-core/src/main/java/com/jme3/audio/openal/EFX.java index 86170c142a..32d572da09 100644 --- a/jme3-core/src/main/java/com/jme3/audio/openal/EFX.java +++ b/jme3-core/src/main/java/com/jme3/audio/openal/EFX.java @@ -27,19 +27,19 @@ public interface EFX { /* Effect properties. */ /* Reverb effect parameters */ - public static final int AL_REVERB_DENSITY = 0x0001; - public static final int AL_REVERB_DIFFUSION = 0x0002; - public static final int AL_REVERB_GAIN = 0x0003; - public static final int AL_REVERB_GAINHF = 0x0004; - public static final int AL_REVERB_DECAY_TIME = 0x0005; - public static final int AL_REVERB_DECAY_HFRATIO = 0x0006; - public static final int AL_REVERB_REFLECTIONS_GAIN = 0x0007; - public static final int AL_REVERB_REFLECTIONS_DELAY = 0x0008; - public static final int AL_REVERB_LATE_REVERB_GAIN = 0x0009; - public static final int AL_REVERB_LATE_REVERB_DELAY = 0x000A; + public static final int AL_REVERB_DENSITY = 0x0001; + public static final int AL_REVERB_DIFFUSION = 0x0002; + public static final int AL_REVERB_GAIN = 0x0003; + public static final int AL_REVERB_GAINHF = 0x0004; + public static final int AL_REVERB_DECAY_TIME = 0x0005; + public static final int AL_REVERB_DECAY_HFRATIO = 0x0006; + public static final int AL_REVERB_REFLECTIONS_GAIN = 0x0007; + public static final int AL_REVERB_REFLECTIONS_DELAY = 0x0008; + public static final int AL_REVERB_LATE_REVERB_GAIN = 0x0009; + public static final int AL_REVERB_LATE_REVERB_DELAY = 0x000A; public static final int AL_REVERB_AIR_ABSORPTION_GAINHF = 0x000B; - public static final int AL_REVERB_ROOM_ROLLOFF_FACTOR = 0x000C; - public static final int AL_REVERB_DECAY_HFLIMIT = 0x000D; + public static final int AL_REVERB_ROOM_ROLLOFF_FACTOR = 0x000C; + public static final int AL_REVERB_DECAY_HFLIMIT = 0x000D; /* EAX Reverb effect parameters */ //#define AL_EAXREVERB_DENSITY 0x0001 @@ -171,28 +171,28 @@ public interface EFX { ///* Filter properties. */ /* Lowpass filter parameters */ - public static final int AL_LOWPASS_GAIN = 0x0001; - public static final int AL_LOWPASS_GAINHF = 0x0002; + public static final int AL_LOWPASS_GAIN = 0x0001; + public static final int AL_LOWPASS_GAINHF = 0x0002; - ///* Highpass filter parameters */ - //#define AL_HIGHPASS_GAIN 0x0001 - //#define AL_HIGHPASS_GAINLF 0x0002 + // * Highpass filter parameters */ + public static final int AL_HIGHPASS_GAIN = 0x0001; + public static final int AL_HIGHPASS_GAINLF = 0x0002; - ///* Bandpass filter parameters */ - //#define AL_BANDPASS_GAIN 0x0001 - //#define AL_BANDPASS_GAINLF 0x0002 - //#define AL_BANDPASS_GAINHF 0x0003 + // * Bandpass filter parameters */ + public static final int AL_BANDPASS_GAIN = 0x0001; + public static final int AL_BANDPASS_GAINLF = 0x0002; + public static final int AL_BANDPASS_GAINHF = 0x0003; /* Filter type */ //#define AL_FILTER_FIRST_PARAMETER 0x0000 //#define AL_FILTER_LAST_PARAMETER 0x8000 - public static final int AL_FILTER_TYPE = 0x8001; + public static final int AL_FILTER_TYPE = 0x8001; /* Filter types, used with the AL_FILTER_TYPE property */ - public static final int AL_FILTER_NULL = 0x0000; - public static final int AL_FILTER_LOWPASS = 0x0001; - public static final int AL_FILTER_HIGHPASS = 0x0002; - //#define AL_FILTER_BANDPASS 0x0003 + public static final int AL_FILTER_NULL = 0x0000; + public static final int AL_FILTER_LOWPASS = 0x0001; + public static final int AL_FILTER_HIGHPASS = 0x0002; + public static final int AL_FILTER_BANDPASS = 0x0003; ///* Filter ranges and defaults. */ // diff --git a/jme3-core/src/test/java/com/jme3/audio/AudioFilterTest.java b/jme3-core/src/test/java/com/jme3/audio/AudioFilterTest.java index ad9016b4c0..5ccf79d6c0 100644 --- a/jme3-core/src/test/java/com/jme3/audio/AudioFilterTest.java +++ b/jme3-core/src/test/java/com/jme3/audio/AudioFilterTest.java @@ -17,7 +17,7 @@ public class AudioFilterTest { * Tests serialization and de-serialization of a {@code LowPassFilter}. */ @Test - public void testSaveAndLoad() { + public void testSaveAndLoad_LowPassFilter() { AssetManager assetManager = new DesktopAssetManager(true); LowPassFilter f = new LowPassFilter(.5f, .5f); @@ -28,4 +28,35 @@ public void testSaveAndLoad() { Assert.assertEquals(f.getHighFreqVolume(), copy.getHighFreqVolume(), delta); } + /** + * Tests serialization and de-serialization of a {@code HighPassFilter}. + */ + @Test + public void testSaveAndLoad_HighPassFilter() { + AssetManager assetManager = new DesktopAssetManager(true); + + HighPassFilter f = new HighPassFilter(.5f, .5f); + HighPassFilter copy = BinaryExporter.saveAndLoad(assetManager, f); + + float delta = 0.001f; + Assert.assertEquals(f.getVolume(), copy.getVolume(), delta); + Assert.assertEquals(f.getLowFreqVolume(), copy.getLowFreqVolume(), delta); + } + + /** + * Tests serialization and de-serialization of a {@code BandPassFilter}. + */ + @Test + public void testSaveAndLoad_BandPassFilter() { + AssetManager assetManager = new DesktopAssetManager(true); + + BandPassFilter f = new BandPassFilter(.5f, .5f, .5f); + BandPassFilter copy = BinaryExporter.saveAndLoad(assetManager, f); + + float delta = 0.001f; + Assert.assertEquals(f.getVolume(), copy.getVolume(), delta); + Assert.assertEquals(f.getHighFreqVolume(), copy.getHighFreqVolume(), delta); + Assert.assertEquals(f.getLowFreqVolume(), copy.getLowFreqVolume(), delta); + } + }