Renderer/src/main/java/electrosphere/audio/AudioEngine.java
austin d7619692ed
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
movement audio system for client
2024-08-08 11:33:51 -04:00

292 lines
8.2 KiB
Java

package electrosphere.audio;
import electrosphere.engine.Globals;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.logger.LoggerInterface;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
import org.joml.Vector3f;
import org.lwjgl.BufferUtils;
import org.lwjgl.openal.AL;
import org.lwjgl.openal.ALC;
import org.lwjgl.openal.ALC10;
import org.lwjgl.openal.ALC11;
import org.lwjgl.openal.ALCCapabilities;
import org.lwjgl.openal.SOFTHRTF;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.openal.ALC10.alcDestroyContext;
import static org.lwjgl.openal.ALC10.alcCloseDevice;
import static org.lwjgl.system.MemoryUtil.NULL;
/**
* Main class that handles audio processing
*/
public class AudioEngine {
//Controls whether the engine initialized or not
boolean initialized = false;
//openal device
private long device;
//openal context
private long context;
//the listener data for the audio landscape
private AudioListener listener;
//the current gain level of the engine
private float engineGain = 1.0f;
//The current device
String currentDevice = "";
//the default device
String defaultDevice = "";
//if true, hrtf present and active
boolean hasHRTF = false;
//if true, efx present and active
boolean hasEFX = false;
//The list of sources being tracked
private List<AudioSource> openALSources = new CopyOnWriteArrayList<AudioSource>();
/**
* Creates an audio engine
*/
public AudioEngine() {
}
/**
* Initializes the audio engine
*/
public void init() {
try {
initDevice();
echoJavaAudioSupport();
} catch (Exception ex) {
LoggerInterface.loggerEngine.ERROR("Error initializing audio device", ex);
}
if(initialized){
//recursively load all audio files
listener = new AudioListener();
}
}
/**
* Lists all available devices
*/
public void listAllDevices(){
currentDevice = ALC11.alcGetString(NULL,ALC11.ALC_ALL_DEVICES_SPECIFIER);
LoggerInterface.loggerAudio.INFO("AL device: " + currentDevice);
defaultDevice = ALC11.alcGetString(NULL,ALC11.ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
LoggerInterface.loggerAudio.INFO("AL default device: " + defaultDevice);
}
/**
* Initializes audio devices
* @throws Exception Thrown if there are no audio devices or fails to create openal context
*/
void initDevice() throws Exception {
//create device
LoggerInterface.loggerAudio.DEBUG("Open ALC device");
this.device = ALC10.alcOpenDevice((ByteBuffer) null);
if (device == NULL) {
throw new IllegalStateException("Failed to open the default OpenAL device.");
}
//create capabilities
LoggerInterface.loggerAudio.DEBUG("Create device capabilities");
ALCCapabilities deviceCaps = ALC.createCapabilities(device);
//create context
LoggerInterface.loggerAudio.DEBUG("Create context");
IntBuffer attrBuffer = getContextAttrs(deviceCaps);
this.context = ALC10.alcCreateContext(device, attrBuffer);
MemoryUtil.memFree(attrBuffer);
if (context == NULL) {
throw new IllegalStateException("Failed to create OpenAL context.");
}
LoggerInterface.loggerAudio.DEBUG("Make Context Current");
ALC10.alcMakeContextCurrent(context);
AL.createCapabilities(deviceCaps);
this.initialized = true;
}
/**
* Gets the attrs buffer for creating a context
* @param deviceCaps The device capabilities
* @return The buffer (may be null if no desired extensions present)
*/
IntBuffer getContextAttrs(ALCCapabilities deviceCaps){
int bufferSize = 0;
//check for available extensions
if(deviceCaps.ALC_EXT_EFX){
LoggerInterface.loggerAudio.INFO("EFX PRESENT");
hasEFX = true;
} else {
LoggerInterface.loggerAudio.INFO("EFX NOT PRESENT");
}
if(deviceCaps.ALC_SOFT_HRTF){
LoggerInterface.loggerAudio.INFO("SOFT HRTF PRESENT");
// hasHRTF = true;
// bufferSize++;
} else {
LoggerInterface.loggerAudio.INFO("SOFT HRTF NOT PRESENT");
}
IntBuffer rVal = null;
//construct buffer if any were found
if(bufferSize > 0 ){
rVal = BufferUtils.createIntBuffer(bufferSize * 2 + 1);
if(deviceCaps.ALC_SOFT_HRTF){
rVal.put(SOFTHRTF.ALC_HRTF_SOFT);
rVal.put(ALC11.ALC_TRUE);
}
rVal.put(0);
rVal.flip();
}
LoggerInterface.loggerAudio.INFO("Create attributes with size: " + bufferSize);
return rVal;
}
/**
* Echos the available support for different audio types from JRE itself
*/
private void echoJavaAudioSupport(){
LoggerInterface.loggerAudio.INFO("Check JRE-supported audio file types");
for(AudioFileFormat.Type audioType : AudioSystem.getAudioFileTypes()){
LoggerInterface.loggerAudio.INFO(audioType.getExtension() + " support: " + AudioSystem.isFileTypeSupported(audioType));
}
}
/**
* Updates the orientation of the listener based on the global player camera
*/
private void updateListener(){
if(Globals.playerCamera != null){
Vector3f cameraPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
Vector3f cameraEye = new Vector3f(CameraEntityUtils.getCameraEye(Globals.playerCamera)).mul(-1);
Vector3f cameraUp = new Vector3f(0,1,0);
listener.setPosition(cameraPos);
listener.setOrientation(cameraEye, cameraUp);
}
}
/**
* Updates the audio engine
*/
public void update(){
updateListener();
updateOpenALSources();
}
/**
* Shuts down the engine
*/
public void shutdown(){
alcDestroyContext(context);
alcCloseDevice(device);
}
/**
* Registers an openal audio source with the engine
* @param source The audio source
*/
protected void registerSource(AudioSource source){
this.openALSources.add(source);
}
/**
* Updates the status of all tracked sources
*/
private void updateOpenALSources(){
List<AudioSource> toRemove = new LinkedList<AudioSource>();
for(AudioSource source : this.openALSources){
if(!source.isPlaying()){
toRemove.add(source);
}
}
for(AudioSource source : toRemove){
this.openALSources.remove(source);
source.cleanup();
}
}
/**
* Gets the list of openal sources
* @return The list of sources
*/
public List<AudioSource> getOpenALSources(){
return Collections.unmodifiableList(this.openALSources);
}
/**
* Sets the gain of the engine
* @param gain The gain value
*/
public void setGain(float gain){
engineGain = gain;
}
/**
* Gets the gain of the engine
* @return The gain value
*/
public float getGain(){
return engineGain;
}
/**
* Gets the current openal device
* @return The current openal device
*/
public String getDevice(){
return currentDevice;
}
/**
* Gets the default openal device
* @return the default openal device
*/
public String getDefaultDevice(){
return defaultDevice;
}
/**
* Gets the HRTF status
* @return The HRTF status
*/
public boolean getHRTFStatus(){
return hasHRTF;
}
/**
* Gets the listener for the audio engine
* @return the listener
*/
public AudioListener getListener(){
return listener;
}
/**
* Checks if the engine has initialized or not
* @return true if initialized, false otherwise
*/
public boolean initialized(){
return this.initialized;
}
}