Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 153 additions & 53 deletions jme3-core/src/main/java/com/jme3/audio/Environment.java
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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");
Expand Down Expand Up @@ -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;
}
}