Skip to content

Commit 6f95af8

Browse files
authored
Merge pull request #2410 from capdevon/capdevon-audio-filter
Fix #2408 - com.jme3.audio.LowPassFilter not cloneable
2 parents 59b3349 + 20a2b74 commit 6f95af8

3 files changed

Lines changed: 131 additions & 6 deletions

File tree

jme3-core/src/main/java/com/jme3/audio/Filter.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2020 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -35,9 +35,12 @@
3535
import com.jme3.export.JmeImporter;
3636
import com.jme3.export.Savable;
3737
import com.jme3.util.NativeObject;
38+
import com.jme3.util.clone.Cloner;
39+
import com.jme3.util.clone.JmeCloneable;
40+
3841
import java.io.IOException;
3942

40-
public abstract class Filter extends NativeObject implements Savable {
43+
public abstract class Filter extends NativeObject implements Savable, JmeCloneable {
4144

4245
public Filter() {
4346
super();
@@ -49,12 +52,28 @@ protected Filter(int id) {
4952

5053
@Override
5154
public void write(JmeExporter ex) throws IOException {
52-
// nothing to save
55+
// no-op
5356
}
5457

5558
@Override
5659
public void read(JmeImporter im) throws IOException {
57-
// nothing to read
60+
// no-op
61+
}
62+
63+
/**
64+
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
65+
*/
66+
@Override
67+
public Object jmeClone() {
68+
return super.clone();
69+
}
70+
71+
/**
72+
* Called internally by com.jme3.util.clone.Cloner. Do not call directly.
73+
*/
74+
@Override
75+
public void cloneFields(Cloner cloner, Object original) {
76+
// no-op
5877
}
5978

6079
@Override

jme3-core/src/main/java/com/jme3/audio/LowPassFilter.java

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2023 jMonkeyEngine
2+
* Copyright (c) 2009-2025 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -36,26 +36,63 @@
3636
import com.jme3.export.JmeImporter;
3737
import com.jme3.export.OutputCapsule;
3838
import com.jme3.util.NativeObject;
39+
3940
import java.io.IOException;
4041

42+
/**
43+
* A filter that attenuates frequencies above a specified threshold, allowing lower
44+
* frequencies to pass through with less attenuation. Commonly used to simulate effects
45+
* such as muffling or underwater acoustics.
46+
*/
4147
public class LowPassFilter extends Filter {
4248

43-
protected float volume, highFreqVolume;
49+
/**
50+
* The overall volume scaling of the filtered sound
51+
*/
52+
protected float volume;
53+
/**
54+
* The volume scaling of the high frequencies allowed to pass through. Valid values range
55+
* from 0.0 to 1.0, where 0.0 completely eliminates high frequencies and 1.0 lets them pass
56+
* through unchanged.
57+
*/
58+
protected float highFreqVolume;
4459

60+
/**
61+
* Constructs a low-pass filter.
62+
*
63+
* @param volume the overall volume scaling of the filtered sound (0.0 - 1.0).
64+
* @param highFreqVolume the volume scaling of high frequencies (0.0 - 1.0).
65+
* @throws IllegalArgumentException if {@code volume} or {@code highFreqVolume} is out of range.
66+
*/
4567
public LowPassFilter(float volume, float highFreqVolume) {
4668
super();
4769
setVolume(volume);
4870
setHighFreqVolume(highFreqVolume);
4971
}
5072

73+
/**
74+
* For internal cloning
75+
* @param id the native object ID
76+
*/
5177
protected LowPassFilter(int id) {
5278
super(id);
5379
}
5480

81+
/**
82+
* Retrieves the current volume scaling of high frequencies.
83+
*
84+
* @return the high-frequency volume scaling.
85+
*/
5586
public float getHighFreqVolume() {
5687
return highFreqVolume;
5788
}
5889

90+
/**
91+
* Sets the high-frequency volume.
92+
*
93+
* @param highFreqVolume the new high-frequency volume scaling (0.0 - 1.0).
94+
* @throws IllegalArgumentException if {@code highFreqVolume} is out of range.
95+
*/
5996
public void setHighFreqVolume(float highFreqVolume) {
6097
if (highFreqVolume < 0 || highFreqVolume > 1)
6198
throw new IllegalArgumentException("High freq volume must be between 0 and 1");
@@ -64,10 +101,21 @@ public void setHighFreqVolume(float highFreqVolume) {
64101
this.updateNeeded = true;
65102
}
66103

104+
/**
105+
* Retrieves the current overall volume scaling of the filtered sound.
106+
*
107+
* @return the overall volume scaling.
108+
*/
67109
public float getVolume() {
68110
return volume;
69111
}
70112

113+
/**
114+
* Sets the overall volume.
115+
*
116+
* @param volume the new overall volume scaling (0.0 - 1.0).
117+
* @throws IllegalArgumentException if {@code volume} is out of range.
118+
*/
71119
public void setVolume(float volume) {
72120
if (volume < 0 || volume > 1)
73121
throw new IllegalArgumentException("Volume must be between 0 and 1");
@@ -92,11 +140,21 @@ public void read(JmeImporter im) throws IOException {
92140
highFreqVolume = ic.readFloat("hf_volume", 0);
93141
}
94142

143+
/**
144+
* Creates a native object clone of this filter for internal usage.
145+
*
146+
* @return a new {@code LowPassFilter} instance with the same native ID.
147+
*/
95148
@Override
96149
public NativeObject createDestructableClone() {
97150
return new LowPassFilter(id);
98151
}
99152

153+
/**
154+
* Retrieves a unique identifier for this filter. Used internally for native object management.
155+
*
156+
* @return a unique long identifier.
157+
*/
100158
@Override
101159
public long getUniqueId() {
102160
return ((long) OBJTYPE_FILTER << 32) | (0xffffffffL & (long) id);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.jme3.audio;
2+
3+
import com.jme3.asset.AssetManager;
4+
import com.jme3.math.Vector3f;
5+
import com.jme3.system.JmeSystem;
6+
import org.junit.Assert;
7+
import org.junit.Test;
8+
9+
/**
10+
* Automated tests for the {@code AudioNode} class.
11+
*
12+
* @author capdevon
13+
*/
14+
public class AudioNodeTest {
15+
16+
@Test
17+
public void testAudioNodeClone() {
18+
AssetManager assetManager = JmeSystem.newAssetManager(AudioNodeTest.class.getResource("/com/jme3/asset/Desktop.cfg"));
19+
20+
AudioNode audio = new AudioNode(assetManager,
21+
"Sound/Effects/Bang.wav", AudioData.DataType.Buffer);
22+
audio.setDirection(new Vector3f(0, 1, 0));
23+
audio.setVelocity(new Vector3f(1, 1, 1));
24+
audio.setDryFilter(new LowPassFilter(1f, .1f));
25+
audio.setReverbFilter(new LowPassFilter(.5f, .5f));
26+
27+
AudioNode clone = audio.clone();
28+
29+
Assert.assertNotNull(clone.previousWorldTranslation);
30+
Assert.assertNotSame(audio.previousWorldTranslation, clone.previousWorldTranslation);
31+
Assert.assertEquals(audio.previousWorldTranslation, clone.previousWorldTranslation);
32+
33+
Assert.assertNotNull(clone.getDirection());
34+
Assert.assertNotSame(audio.getDirection(), clone.getDirection());
35+
Assert.assertEquals(audio.getDirection(), clone.getDirection());
36+
37+
Assert.assertNotNull(clone.getVelocity());
38+
Assert.assertNotSame(audio.getVelocity(), clone.getVelocity());
39+
Assert.assertEquals(audio.getVelocity(), clone.getVelocity());
40+
41+
Assert.assertNotNull(clone.getDryFilter());
42+
Assert.assertNotSame(audio.getDryFilter(), clone.getDryFilter());
43+
44+
Assert.assertNotNull(clone.getReverbFilter());
45+
Assert.assertNotSame(audio.getReverbFilter(), clone.getReverbFilter());
46+
}
47+
48+
}

0 commit comments

Comments
 (0)