diff --git a/jme3-core/src/main/java/com/jme3/audio/Environment.java b/jme3-core/src/main/java/com/jme3/audio/Environment.java index d6d39b8a76..05bec8770c 100644 --- a/jme3-core/src/main/java/com/jme3/audio/Environment.java +++ b/jme3-core/src/main/java/com/jme3/audio/Environment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2025 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,49 +34,80 @@ import com.jme3.math.FastMath; /** - * Audio environment, for reverb effects. + * Represents an audio environment, primarily used to define reverb effects. + * This class provides parameters that correspond to the properties controllable + * through the OpenAL EFX (Environmental Effects Extension) library. + * By adjusting these parameters, developers can simulate various acoustic spaces + * like rooms, caves, and concert halls, adding depth and realism to the audio experience. + * * @author Kirill */ public class Environment { - private float airAbsorbGainHf = 0.99426f; + /** High-frequency air absorption gain (0.0f to 1.0f). */ + private float airAbsorbGainHf = 0.99426f; + /** Factor controlling room effect rolloff with distance. */ private float roomRolloffFactor = 0; - - private float decayTime = 1.49f; - private float decayHFRatio = 0.54f; - - private float density = 1.0f; - private float diffusion = 0.3f; - - private float gain = 0.316f; - private float gainHf = 0.022f; - - private float lateReverbDelay = 0.088f; - private float lateReverbGain = 0.768f; - - private float reflectDelay = 0.162f; - private float reflectGain = 0.052f; - - private boolean decayHfLimit = true; - - public static final Environment Garage, Dungeon, Cavern, AcousticLab, Closet; - - static { - Garage = new Environment(1, 1, 1, 1, .9f, .5f, .751f, .0039f, .661f, .0137f); - Dungeon = new Environment(.75f, 1, 1, .75f, 1.6f, 1, 0.95f, 0.0026f, 0.93f, 0.0103f); - Cavern = new Environment(.5f, 1, 1, .5f, 2.25f, 1, .908f, .0103f, .93f, .041f); - AcousticLab = new Environment(.5f, 1, 1, 1, .28f, 1, .87f, .002f, .81f, .008f); - Closet = new Environment(1, 1, 1, 1, .15f, 1, .6f, .0025f, .5f, .0006f); - } - + /** Overall decay time of the reverberation (in seconds). */ + private float decayTime = 1.49f; + /** Ratio of high-frequency decay time to overall decay time (0.0f to 1.0f). */ + private float decayHFRatio = 0.54f; + /** Density of the medium affecting reverb smoothness (0.0f to 1.0f). */ + private float density = 1.0f; + /** Diffusion of reflections affecting echo distinctness (0.0f to 1.0f). */ + private float diffusion = 0.3f; + /** Overall gain of the environment effect (linear scale). */ + private float gain = 0.316f; + /** High-frequency gain of the environment effect (linear scale). */ + private float gainHf = 0.022f; + /** Delay time for late reverberation relative to early reflections (in seconds). */ + private float lateReverbDelay = 0.088f; + /** Gain of the late reverberation (linear scale). */ + private float lateReverbGain = 0.768f; + /** Delay time for the initial reflections (in seconds). */ + private float reflectDelay = 0.162f; + /** Gain of the initial reflections (linear scale). */ + private float reflectGain = 0.052f; + /** Flag limiting high-frequency decay by the overall decay time. */ + private boolean decayHfLimit = true; + + public static final Environment Garage = new Environment( + 1, 1, 1, 1, .9f, .5f, .751f, .0039f, .661f, .0137f); + public static final Environment Dungeon = new Environment( + .75f, 1, 1, .75f, 1.6f, 1, 0.95f, 0.0026f, 0.93f, 0.0103f); + public static final Environment Cavern = new Environment( + .5f, 1, 1, .5f, 2.25f, 1, .908f, .0103f, .93f, .041f); + public static final Environment AcousticLab = new Environment( + .5f, 1, 1, 1, .28f, 1, .87f, .002f, .81f, .008f); + public static final Environment Closet = new Environment( + 1, 1, 1, 1, .15f, 1, .6f, .0025f, .5f, .0006f); + + /** + * Utility method to convert an EAX decibel value to an amplitude factor. + * EAX often expresses gain and attenuation in decibels scaled by 1000. + * This method performs the reverse of that conversion to obtain a linear + * amplitude value suitable for OpenAL. + * + * @param eaxDb The EAX decibel value (scaled by 1000). + * @return The corresponding amplitude factor. + */ private static float eaxDbToAmp(float eaxDb) { float dB = eaxDb / 2000f; return FastMath.pow(10f, dB); } + /** + * Constructs a new, default {@code Environment}. The default values are + * typically chosen to represent a neutral or common acoustic space. + */ public Environment() { } + /** + * Creates a new {@code Environment} as a copy of the provided {@code Environment}. + * + * @param source The {@code Environment} to copy the settings from. + */ public Environment(Environment source) { this.airAbsorbGainHf = source.airAbsorbGainHf; this.roomRolloffFactor = source.roomRolloffFactor; @@ -93,9 +124,24 @@ public Environment(Environment source) { this.decayHfLimit = source.decayHfLimit; } + /** + * Creates a new {@code Environment} with the specified parameters. These parameters + * directly influence the properties of the reverb effect as managed by OpenAL EFX. + * + * @param density The density of the medium. + * @param diffusion The diffusion of the reflections. + * @param gain Overall gain applied to the environment effect. + * @param gainHf High-frequency gain applied to the environment effect. + * @param decayTime The overall decay time of the reflected sound. + * @param decayHf Ratio of high-frequency decay time to the overall decay time. + * @param reflectGain Gain applied to the initial reflections. + * @param reflectDelay Delay time for the initial reflections. + * @param lateGain Gain applied to the late reverberation. + * @param lateDelay Delay time for the late reverberation. + */ public Environment(float density, float diffusion, float gain, float gainHf, - float decayTime, float decayHf, float reflectGain, - float reflectDelay, float lateGain, float lateDelay) { + float decayTime, float decayHf, float reflectGain, float reflectDelay, + float lateGain, float lateDelay) { this.decayTime = decayTime; this.decayHFRatio = decayHf; this.density = density; @@ -108,6 +154,16 @@ public Environment(float density, float diffusion, float gain, float gainHf, this.reflectGain = reflectGain; } + /** + * Creates a new {@code Environment} by interpreting an array of 28 float values + * as an EAX preset. This constructor attempts to map the EAX preset values to + * the corresponding OpenAL EFX parameters. Note that not all EAX parameters + * have a direct equivalent in standard OpenAL EFX, so some values might be + * approximated or ignored. + * + * @param e An array of 28 float values representing an EAX preset. + * @throws IllegalArgumentException If the provided array does not have a length of 28. + */ public Environment(float[] e) { if (e.length != 28) throw new IllegalArgumentException("Not an EAX preset"); @@ -254,27 +310,71 @@ public void setRoomRolloffFactor(float roomRolloffFactor) { } @Override - public boolean equals(Object env2) { - if (env2 == null) + public boolean equals(Object obj) { + + if (!(obj instanceof Environment)) return false; - if (env2 == this) + + if (obj == this) return true; - if (!(env2 instanceof Environment)) - return false; - Environment e2 = (Environment) env2; - return (e2.airAbsorbGainHf == airAbsorbGainHf - && e2.decayHFRatio == decayHFRatio - && e2.decayHfLimit == decayHfLimit - && e2.decayTime == decayTime - && e2.density == density - && e2.diffusion == diffusion - && e2.gain == gain - && e2.gainHf == gainHf - && e2.lateReverbDelay == lateReverbDelay - && e2.lateReverbGain == lateReverbGain - && e2.reflectDelay == reflectDelay - && e2.reflectGain == reflectGain - && e2.roomRolloffFactor == roomRolloffFactor); - } + Environment other = (Environment) obj; + float epsilon = 1e-6f; + + float[] thisFloats = { + this.airAbsorbGainHf, + this.decayHFRatio, + this.decayTime, + this.density, + this.diffusion, + this.gain, + this.gainHf, + this.lateReverbDelay, + this.lateReverbGain, + this.reflectDelay, + this.reflectGain, + this.roomRolloffFactor + }; + + float[] otherFloats = { + other.airAbsorbGainHf, + other.decayHFRatio, + other.decayTime, + other.density, + other.diffusion, + other.gain, + other.gainHf, + other.lateReverbDelay, + other.lateReverbGain, + other.reflectDelay, + other.reflectGain, + other.roomRolloffFactor + }; + + for (int i = 0; i < thisFloats.length; i++) { + if (Math.abs(thisFloats[i] - otherFloats[i]) >= epsilon) { + return false; + } + } + + return this.decayHfLimit == other.decayHfLimit; + } + + @Override + public int hashCode() { + int result = (airAbsorbGainHf != +0.0f ? Float.floatToIntBits(airAbsorbGainHf) : 0); + result = 31 * result + (roomRolloffFactor != +0.0f ? Float.floatToIntBits(roomRolloffFactor) : 0); + result = 31 * result + (decayTime != +0.0f ? Float.floatToIntBits(decayTime) : 0); + result = 31 * result + (decayHFRatio != +0.0f ? Float.floatToIntBits(decayHFRatio) : 0); + result = 31 * result + (density != +0.0f ? Float.floatToIntBits(density) : 0); + result = 31 * result + (diffusion != +0.0f ? Float.floatToIntBits(diffusion) : 0); + result = 31 * result + (gain != +0.0f ? Float.floatToIntBits(gain) : 0); + result = 31 * result + (gainHf != +0.0f ? Float.floatToIntBits(gainHf) : 0); + result = 31 * result + (lateReverbDelay != +0.0f ? Float.floatToIntBits(lateReverbDelay) : 0); + result = 31 * result + (lateReverbGain != +0.0f ? Float.floatToIntBits(lateReverbGain) : 0); + result = 31 * result + (reflectDelay != +0.0f ? Float.floatToIntBits(reflectDelay) : 0); + result = 31 * result + (reflectGain != +0.0f ? Float.floatToIntBits(reflectGain) : 0); + result = 31 * result + (decayHfLimit ? 1 : 0); + return result; + } }