package com.bitwaffle.spaceguts.audio; import java.nio.FloatBuffer; import java.util.ArrayList; import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.openal.AL; import org.lwjgl.openal.AL10; import org.lwjgl.openal.AL11; import org.lwjgl.util.vector.Vector3f; import com.bitwaffle.spaceguts.entities.Entities; import com.bitwaffle.spaceguts.util.QuaternionHelper; import com.bitwaffle.spaceout.resources.Sounds; /** * Manages intiializing OpenAL and upating the listener location to be at the camera's location * @author TranquilMarmot * */ public class Audio { /** Used for deleting all sound sources on shutdown (see {@link SoundSource}'s constructor, each SoundSource gets added to this when it's created */ protected static ArrayList<SoundSource> soundSources = new ArrayList<SoundSource>(); /** Factor to use for doppler effect */ private static final float DOPPLER_FACTOR = 0.0f; /** Velocity to use for doppler effect*/ private static final float DOPPLER_VELOCITY = 1.0f; /** Whether or not the game is muted */ private static boolean muted = false; /** Current volume */ private static float volume = 1.5f; /** Buffers for transferring data to OpenAL */ private static FloatBuffer listenerPos, listenerVel, listenerOrient; /** For checking for errors */ private static int err; /** * Initializes OpenAL */ public static void init(){ try { AL.create(); } catch (LWJGLException e) { e.printStackTrace(); } // zero out error AL10.alGetError(); // set doppler values AL10.alDopplerFactor(DOPPLER_FACTOR); AL10.alDopplerVelocity(DOPPLER_VELOCITY); AL11.alSpeedOfSound(700.0f); // initialize buffers listenerPos = BufferUtils.createFloatBuffer(3); listenerVel = BufferUtils.createFloatBuffer(3); listenerOrient = BufferUtils.createFloatBuffer(6); } /** * Updates the listener's position, velocity and orientation to match the camera */ public static void update(){ // clear buffers listenerPos.clear(); listenerVel.clear(); listenerOrient.clear(); // if there's no camera, then we're playing 2D sounds if(Entities.camera == null){ listenerPos.put(0.0f); listenerPos.put(0.0f); listenerPos.put(0.0f); listenerVel.put(0.0f); listenerVel.put(0.0f); listenerVel.put(0.0f); listenerOrient.put(0.0f); listenerOrient.put(0.0f); listenerOrient.put(-1.0f); listenerOrient.put(0.0f); listenerOrient.put(1.0f); listenerOrient.put(0.0f); } else{ // position Vector3f realPos = Entities.camera.getLocationWithOffset(); listenerPos.put(realPos.x); listenerPos.put(realPos.y); listenerPos.put(realPos.z); listenerPos.rewind(); // velocity javax.vecmath.Vector3f linvec = new javax.vecmath.Vector3f(); // if we're following anything, we want its velocity if(Entities.camera.buildMode || Entities.camera.freeMode) Entities.camera.rigidBody.getLinearVelocity(linvec); else Entities.camera.following.rigidBody.getLinearVelocity(linvec); listenerVel.put(linvec.x); listenerVel.put(linvec.y); listenerVel.put(linvec.z); // orientation Vector3f at = new Vector3f(0.0f, 0.0f, 1.0f); Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f); at = QuaternionHelper.rotateVectorByQuaternion(at, Entities.camera.rotation); up = QuaternionHelper.rotateVectorByQuaternion(up, Entities.camera.rotation); listenerOrient.put(at.x); listenerOrient.put(at.y); listenerOrient.put(at.z); listenerOrient.put(up.x); listenerOrient.put(up.y); listenerOrient.put(up.z); } listenerPos.rewind(); listenerVel.rewind(); listenerOrient.rewind(); // set AL's listener data AL10.alListener(AL10.AL_POSITION, listenerPos); AL10.alListener(AL10.AL_VELOCITY, listenerVel); AL10.alListener(AL10.AL_ORIENTATION, listenerOrient); // check for errors err = AL10.alGetError(); if(err != AL10.AL_NO_ERROR){ System.out.println("Error in OpenAL! number: " + err + " string: " + AL10.alGetString(err)); } // get rid of any straggling sound sources ArrayList<SoundSource> toRemove = new ArrayList<SoundSource>(); for(SoundSource src : soundSources){ if(src.removeFlag){ // only remove something if it's not playing anything if(!src.isPlaying()){ src.shutdown(); toRemove.add(src); } } } for(SoundSource src : toRemove) soundSources.remove(src); } /** * Changes the volume of everything * @param gain New volume level (0 = none, 0.5 = 50%, 1 = 100%, 2 = 200% etc.) */ public static void setVolume(float gain){ volume = gain; for(SoundSource src : soundSources) src.setGain(volume); } /** * @return Current volume level */ public static float currentVolume(){ return volume; } /** * Pauses all sounds */ public static void pause(){ for(SoundSource src : soundSources) src.pauseSound(); } /** * Plays all sounds */ public static void play(){ for(SoundSource src : soundSources) if(!src.isPlaying() && src.isLooping()) src.playSound(); } /** * Mutes/un-mutes audio */ public static void mute(){ muted = !muted; for(SoundSource src : soundSources) src.setGain(muted ? 0.0f : src.getDefaultGain()); } /** * @return Whether or not audio is muted */ public static boolean isMuted(){ return muted; } /** * Shuts down OpenAL */ public static void shutdown(){ // get rid of all sound sources for(SoundSource src : soundSources) src.shutdown(); AL.destroy(); } /** * Plays a sound from {@link Sounds} once and then sets it to be removed from the list of sound sources. * This is useful for making noises in menus/item pickups/whatever that shouldn't be effected by the listener's * location or velocity. * @param sound Sound to play */ public static void playSoundOnceAtListener(Sounds sound){ Vector3f location; javax.vecmath.Vector3f velocity = new javax.vecmath.Vector3f(); // if there's no camera, then the listener gets set to be at (0,0,0) - see update() method if(Entities.camera == null){ location = new Vector3f(0.0f, 0.0f, 0.0f); velocity = new javax.vecmath.Vector3f(0.0f, 0.0f, 0.0f); } else{ // position location = Entities.camera.getLocationWithOffset(); // if we're following anything, we want its velocity if(Entities.camera.buildMode || Entities.camera.freeMode) Entities.camera.rigidBody.getLinearVelocity(velocity); else Entities.camera.following.rigidBody.getLinearVelocity(velocity); } // create a sound source, play it, and set it up to be removed SoundSource tmp = new SoundSource(sound, false, location, new Vector3f(velocity.x, velocity.y, velocity.z)); tmp.playSound(); tmp.removeFlag = true; } }