diff options
Diffstat (limited to 'src/jake2/sound/lwjgl/Channel.java')
-rw-r--r-- | src/jake2/sound/lwjgl/Channel.java | 334 |
1 files changed, 300 insertions, 34 deletions
diff --git a/src/jake2/sound/lwjgl/Channel.java b/src/jake2/sound/lwjgl/Channel.java index c197191..c136331 100644 --- a/src/jake2/sound/lwjgl/Channel.java +++ b/src/jake2/sound/lwjgl/Channel.java @@ -3,7 +3,7 @@ * * Copyright (C) 2003 * - * $Id: Channel.java,v 1.1 2004-12-16 20:17:55 cawe Exp $ + * $Id: Channel.java,v 1.2 2004-12-23 00:52:12 cawe Exp $ */ /* Copyright (C) 1997-2001 Id Software, Inc. @@ -26,59 +26,325 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package jake2.sound.lwjgl; +import jake2.Defines; +import jake2.Globals; +import jake2.client.CL_ents; +import jake2.game.entity_state_t; +import jake2.sound.sfx_t; +import jake2.sound.sfxcache_t; +import jake2.util.Lib; +import jake2.util.Math3D; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.*; + +import org.lwjgl.openal.AL10; + /** * Channel * - * @author cwei + * @author dsanders/cwei */ public class Channel { final static int LISTENER = 0; final static int FIXED = 1; final static int DYNAMIC = 2; - - int entnum; - int entchannel; - int bufferId; - float rolloff; - boolean autosound = false; - int sourceId; - boolean active = false; - boolean modified = false; - boolean bufferChanged = false; + final static int MAX_CHANNELS = 32; + private final static FloatBuffer NULLVECTOR = Lib.newFloatBuffer(3); + private static Channel[] channels = new Channel[MAX_CHANNELS]; + private static IntBuffer sources = Lib.newIntBuffer(MAX_CHANNELS); + // a reference of L:WJGLSoundImpl.buffers + private static IntBuffer buffers; + private static Map looptable = new Hashtable(MAX_CHANNELS); + + private static boolean isInitialized = false; + private static int numChannels; + // sound attributes - int type; - int entity; - float[] origin = {0, 0, 0}; - - Channel(int sourceId) { + private int type; + private int entnum; + private int entchannel; + private int bufferId; + private int sourceId; + // private float volume; + private float rolloff; + private float[] origin = {0, 0, 0}; + + // update flags + private boolean autosound = false; + private boolean active = false; + private boolean modified = false; + private boolean bufferChanged = false; + + private Channel(int sourceId) { this.sourceId = sourceId; clear(); } - void addListener() { - type = LISTENER; + private void clear() { + entnum = entchannel = bufferId = -1; + bufferChanged = false; + // volume = 1.0f; + rolloff = 0; + autosound = false; + active = false; + modified = false; + } + + private static IntBuffer tmp = Lib.newIntBuffer(1); + + static int init(IntBuffer buffers, float masterVolume) { + Channel.buffers = buffers; + // create channels + int sourceId; + int error; + for (int i = 0; i < MAX_CHANNELS; i++) { + + AL10.alGenSources(tmp); + sourceId = tmp.get(0); + + if (sourceId <= 0) break; + + sources.put(i, sourceId); + + channels[i] = new Channel(sourceId); + numChannels++; + + // set default values for AL sources + AL10.alSourcef (sourceId, AL10.AL_GAIN, masterVolume); + AL10.alSourcef (sourceId, AL10.AL_PITCH, 1.0f); + AL10.alSourcei (sourceId, AL10.AL_SOURCE_ABSOLUTE, AL10.AL_TRUE); + AL10.nalSourcefv(sourceId, AL10.AL_VELOCITY, NULLVECTOR, 0); + AL10.alSourcei (sourceId, AL10.AL_LOOPING, AL10.AL_FALSE); + AL10.alSourcef (sourceId, AL10.AL_REFERENCE_DISTANCE, 300.0f); + AL10.alSourcef (sourceId, AL10.AL_MIN_GAIN, 0.0005f); + AL10.alSourcef (sourceId, AL10.AL_MAX_GAIN, 1.0f); + } + isInitialized = true; + return numChannels; + } + + static void reset() { + for (int i = 0; i < numChannels; i++) { + AL10.alSourceStop(sources.get(i)); + AL10.alSourcei(sources.get(i), AL10.AL_BUFFER, 0); + channels[i].clear(); + } + } + + static void shutdown() { + AL10.alDeleteSources(sources); + numChannels = 0; + isInitialized = false; + } + + static void addPlaySounds() { + while (Channel.assign(PlaySound.nextPlayableSound())); } + + private static boolean assign(PlaySound ps) { + if (ps == null) return false; + Channel ch = null; + int i; + for (i = 0; i < numChannels; i++) { + ch = channels[i]; + + if (ps.entchannel != 0 && ch.entnum == ps.entnum && ch.entchannel == ps.entchannel) { + // always override sound from same entity + if (ch.bufferId != ps.bufferId) { + AL10.alSourceStop(ch.sourceId); + } + break; + } + + // don't let monster sounds override player sounds + if ((ch.entnum == Globals.cl.playernum+1) && (ps.entnum != Globals.cl.playernum+1) && ch.bufferId != -1) + continue; + + // looking for a free AL source + if (!ch.active) { + break; + } + } - void addFixed(float[] origin) { - type = FIXED; - this.origin = origin; + if (i == numChannels) + return false; + + ch.type = ps.type; + if (ps.type == Channel.FIXED) + Math3D.VectorCopy(ps.origin, ch.origin); + ch.entnum = ps.entnum; + ch.entchannel = ps.entchannel; + ch.bufferChanged = (ch.bufferId != ps.bufferId); + ch.bufferId = ps.bufferId; + ch.rolloff = ps.attenuation * 2; + //ch.volume = ps.volume; + ch.active = true; + ch.modified = true; + return true; } + + private static Channel pickForLoop(int bufferId, float attenuation) { + Channel ch; + for (int i = 0; i < numChannels; i++) { + ch = channels[i]; + // looking for a free AL source + if (!ch.active) { + ch.entnum = 0; + ch.entchannel = 0; + ch.bufferChanged = (ch.bufferId != bufferId); + ch.bufferId = bufferId; + ch.rolloff = attenuation * 2; + ch.active = true; + ch.modified = true; + return ch; + } + } + return null; + } + + private static FloatBuffer sourceOriginBuffer = Lib.newFloatBuffer(3); + + static void playAllSounds(FloatBuffer listenerOrigin, float masterVolume) { + float[] entityOrigin = {0, 0, 0}; + FloatBuffer sourceOrigin = sourceOriginBuffer; + Channel ch; + int sourceId; + int state; - void addDynamic(int entity) { - type = DYNAMIC; - this.entity = entity; + for (int i = 0; i < numChannels; i++) { + ch = channels[i]; + if (ch.active) { + sourceId = ch.sourceId; + switch (ch.type) { + case Channel.LISTENER: + sourceOrigin = listenerOrigin; + break; + case Channel.DYNAMIC: + CL_ents.GetEntitySoundOrigin(ch.entnum, entityOrigin); + convertVector(entityOrigin, sourceOrigin); + break; + case Channel.FIXED: + convertVector(ch.origin, sourceOrigin); + break; + } + + if (ch.modified) { + if (ch.bufferChanged) { + AL10.alSourcei(sourceId, AL10.AL_BUFFER, ch.bufferId); + } +// AL10.alSourcef (sourceId, AL10.AL_GAIN, masterVolume * ch.volume); + AL10.alSourcef (sourceId, AL10.AL_GAIN, masterVolume); + AL10.alSourcef (sourceId, AL10.AL_ROLLOFF_FACTOR, ch.rolloff); + AL10.nalSourcefv(sourceId, AL10.AL_POSITION, sourceOrigin, 0); + AL10.alSourcePlay(sourceId); + ch.modified = false; + } else { + state = AL10.alGetSourcei(sourceId, AL10.AL_SOURCE_STATE); + if (state == AL10.AL_PLAYING) { + AL10.nalSourcefv(sourceId, AL10.AL_POSITION, sourceOrigin, 0); + } else { + ch.clear(); + } + } + ch.autosound = false; + } + } } - void clear() { - entnum = -1; - entchannel = -1; - bufferId = -1; - bufferChanged = false; - rolloff = 0; - autosound = false; - active = false; - modified = false; + /* + * adddLoopSounds + * Entities with a ->sound field will generated looped sounds + * that are automatically started, stopped, and merged together + * as the entities are sent to the client + */ + static void addLoopSounds() { + + if ((Globals.cl_paused.value != 0.0f) || (Globals.cls.state != Globals.ca_active) || !Globals.cl.sound_prepped) { + removeUnusedLoopSounds(); + return; + } + + Channel ch; + sfx_t sfx; + sfxcache_t sc; + int num; + entity_state_t ent; + Object key; + int sound = 0; + + for (int i=0 ; i<Globals.cl.frame.num_entities ; i++) { + num = (Globals.cl.frame.parse_entities + i)&(Defines.MAX_PARSE_ENTITIES-1); + ent = Globals.cl_parse_entities[num]; + sound = ent.sound; + + if (sound == 0) continue; + + key = new Integer(ent.number); + ch = (Channel)looptable.get(key); + + if (ch != null) { + // keep on looping + ch.autosound = true; + Math3D.VectorCopy(ent.origin, ch.origin); + continue; + } + + sfx = Globals.cl.sound_precache[sound]; + if (sfx == null) + continue; // bad sound effect + + sc = sfx.cache; + if (sc == null) + continue; + + // allocate a channel + ch = Channel.pickForLoop(buffers.get(sfx.bufferId), 6); + if (ch == null) + break; + + ch.type = FIXED; + Math3D.VectorCopy(ent.origin, ch.origin); + ch.autosound = true; + + looptable.put(key, ch); + AL10.alSourcei(ch.sourceId, AL10.AL_LOOPING, AL10.AL_TRUE); + } + + removeUnusedLoopSounds(); + } + + private static void removeUnusedLoopSounds() { + Channel ch; + // stop unused loopsounds + for (Iterator iter = looptable.values().iterator(); iter.hasNext();) { + ch = (Channel)iter.next(); + if (!ch.autosound) { + AL10.alSourceStop(ch.sourceId); + AL10.alSourcei(ch.sourceId, AL10.AL_LOOPING, AL10.AL_FALSE); + iter.remove(); + ch.clear(); + } + } + } + + static void convertVector(float[] from, FloatBuffer to) { + to.put(0, from[0]); + to.put(1, from[2]); + to.put(2, -from[1]); + } + + static void convertOrientation(float[] forward, float[] up, FloatBuffer orientation) { + orientation.put(0, forward[0]); + orientation.put(1, forward[2]); + orientation.put(2, -forward[1]); + orientation.put(3, up[0]); + orientation.put(4, up[2]); + orientation.put(5, -up[1]); + } + } |