Browse Source

Merge pull request #2444 from capdevon/capdevon-new-audio-filters

New audio filters: HighPassFilter and BandPassFilter
Ryan McDonough 3 tháng trước cách đây
mục cha
commit
c1106983f1

+ 152 - 0
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);
+    }
+}

+ 131 - 0
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);
+    }
+}

+ 18 - 1
jme3-core/src/main/java/com/jme3/audio/openal/ALAudioRenderer.java

@@ -40,8 +40,10 @@ import com.jme3.audio.AudioSource.Status;
 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 @@ public class ALAudioRenderer implements AudioRenderer, Runnable {
             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();
     }
 
     /**

+ 26 - 26
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. */
     //

+ 32 - 1
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 class AudioFilterTest {
         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);
+    }
+
 }