From: Rene S. <sa...@us...> - 2004-12-31 14:09:17
|
Update of /cvsroot/jake2/jake2/src/jake2/sound/lwjgl In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1235/src/jake2/sound/lwjgl Modified Files: Tag: RST LWJGLSoundImpl.java Channel.java Added Files: Tag: RST PlaySound.java Log Message: --- NEW FILE: PlaySound.java --- /* * Created on Dec 22, 2004 * * Copyright (C) 2003 * * $Id: PlaySound.java,v 1.1.2.1 2004/12/31 14:08:34 salomo Exp $ */ /* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package jake2.sound.lwjgl; import jake2.Globals; import jake2.util.Math3D; /** * PlaySound * * @author cwei */ public class PlaySound { final static int MAX_PLAYSOUNDS = 128; // list with sentinel private static PlaySound freeList; private static PlaySound playableList; private static PlaySound[] backbuffer = new PlaySound[MAX_PLAYSOUNDS]; static { for (int i = 0; i < backbuffer.length; i++) { backbuffer[i] = new PlaySound(); } // init the sentinels freeList = new PlaySound(); playableList = new PlaySound(); // reset the lists reset(); } // sound attributes int type; int entnum; int entchannel; int bufferId; // float volume; float attenuation; float[] origin = {0,0,0}; // begin time in ms private long beginTime; // for linked list private PlaySound prev, next; private PlaySound() { prev = next = null; this.clear(); } private void clear() { type = bufferId = entnum = entchannel = -1; // volume = attenuation = beginTime = 0; attenuation = beginTime = 0; // Math3D.VectorClear(origin); } static void reset() { // init the sentinels freeList.next = freeList.prev = freeList; playableList.next = playableList.prev = playableList; // concat the the freeList PlaySound ps; for (int i = 0; i < backbuffer.length; i++) { ps = backbuffer[i]; ps.clear(); ps.prev = freeList; ps.next = freeList.next; ps.prev.next = ps; ps.next.prev = ps; } } static PlaySound nextPlayableSound() { PlaySound ps = null; while (true) { ps = playableList.next; if (ps == playableList || ps.beginTime > Globals.cl.time) return null; PlaySound.release(ps); return ps; } } private static PlaySound get() { PlaySound ps = freeList.next; if (ps == freeList) return null; ps.prev.next = ps.next; ps.next.prev = ps.prev; return ps; } private static void add(PlaySound ps) { PlaySound sort = playableList.next; for (; sort != playableList && sort.beginTime < ps.beginTime; sort = sort.next); ps.next = sort; ps.prev = sort.prev; ps.next.prev = ps; ps.prev.next = ps; } private static void release(PlaySound ps) { ps.prev.next = ps.next; ps.next.prev = ps.prev; // add to free list ps.next = freeList.next; freeList.next.prev = ps; ps.prev = freeList; freeList.next = ps; } static void allocate(float[] origin, int entnum, int entchannel, int bufferId, float volume, float attenuation, float timeoffset) { PlaySound ps = PlaySound.get(); if (ps != null) { // find the right sound type if (entnum == Globals.cl.playernum + 1) { ps.type = Channel.LISTENER; } else if (origin != null) { ps.type = Channel.FIXED; Math3D.VectorCopy(origin, ps.origin); } else { ps.type = Channel.DYNAMIC; } ps.entnum = entnum; ps.entchannel = entchannel; ps.bufferId = bufferId; // ps.volume = volume; ps.attenuation = attenuation; ps.beginTime = Globals.cl.time + (long)(timeoffset * 1000); PlaySound.add(ps); } else { System.err.println("PlaySounds out of Limit"); } } } Index: LWJGLSoundImpl.java =================================================================== RCS file: /cvsroot/jake2/jake2/src/jake2/sound/lwjgl/LWJGLSoundImpl.java,v retrieving revision 1.2 retrieving revision 1.2.2.1 diff -C2 -d -r1.2 -r1.2.2.1 *** LWJGLSoundImpl.java 16 Dec 2004 20:33:08 -0000 1.2 --- LWJGLSoundImpl.java 31 Dec 2004 14:08:34 -0000 1.2.2.1 *************** *** 9,32 **** import jake2.Defines; import jake2.Globals; - import jake2.client.CL; - import jake2.client.CL_ents; import jake2.game.*; import jake2.qcommon.*; import jake2.sound.*; ! import jake2.util.*; - import java.io.IOException; - import java.io.RandomAccessFile; import java.nio.*; - import java.util.*; - import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.openal.*; ! import org.lwjgl.openal.eax.EAX20; ! import org.lwjgl.openal.eax.EAXListenerProperties; /** * LWJGLSoundImpl */ public final class LWJGLSoundImpl implements Sound { --- 9,28 ---- import jake2.Defines; import jake2.Globals; import jake2.game.*; import jake2.qcommon.*; import jake2.sound.*; ! import jake2.util.Lib; ! import jake2.util.Vargs; import java.nio.*; import org.lwjgl.LWJGLException; import org.lwjgl.openal.*; ! import org.lwjgl.openal.eax.*; /** * LWJGLSoundImpl + * + * @author dsanders/cwei */ public final class LWJGLSoundImpl implements Sound { *************** *** 41,50 **** private static final int MAX_SFX = Defines.MAX_SOUNDS * 2; - private static final int MAX_CHANNELS = 32; ! private IntBuffer buffers = BufferUtils.createIntBuffer(MAX_SFX); ! private IntBuffer sources = BufferUtils.createIntBuffer(MAX_CHANNELS); ! private Channel[] channels = null; ! private int num_channels = 0; // singleton --- 37,42 ---- private static final int MAX_SFX = Defines.MAX_SOUNDS * 2; ! private IntBuffer buffers = Lib.newIntBuffer(MAX_SFX); // singleton *************** *** 71,75 **** AL10.alGenBuffers(buffers); s_volume = Cvar.Get("s_volume", "0.7", Defines.CVAR_ARCHIVE); ! initChannels(); AL10.alDistanceModel(AL10.AL_INVERSE_DISTANCE_CLAMPED); Cmd.AddCommand("play", new xcommand_t() { --- 63,68 ---- AL10.alGenBuffers(buffers); s_volume = Cvar.Get("s_volume", "0.7", Defines.CVAR_ARCHIVE); ! int count = Channel.init(buffers, s_volume.value); ! Com.Printf("... using " + count + " channels\n"); AL10.alDistanceModel(AL10.AL_INVERSE_DISTANCE_CLAMPED); Cmd.AddCommand("play", new xcommand_t() { *************** *** 96,100 **** num_sfx = 0; - Com.Printf("sound sampling rate: 44100Hz\n"); --- 89,92 ---- *************** *** 131,136 **** if (AL10.alIsExtensionPresent("EAX2.0")) { ! Com.Printf("... using EAX2.0\n"); ! hasEAX=true; } else --- 123,134 ---- if (AL10.alIsExtensionPresent("EAX2.0")) { ! try { ! EAX.create(); ! Com.Printf("... using EAX2.0\n"); ! hasEAX=true; ! } catch (LWJGLException e) { ! Com.Printf("... can't create EAX2.0\n"); ! hasEAX=false; ! } } else *************** *** 144,185 **** void exitOpenAL() { // Release the context and the device. AL.destroy(); } ! private void initChannels() { ! ! // create channels ! channels = new Channel[MAX_CHANNELS]; ! ! int sourceId; ! IntBuffer tmp = BufferUtils.createIntBuffer(1); ! int error; ! for (int i = 0; i < MAX_CHANNELS; i++) { ! ! AL10.alGenSources(tmp); ! sourceId = tmp.get(0); ! ! //if ((error = al.alGetError()) != AL.AL_NO_ERROR) break; ! if (sourceId <= 0) break; ! ! sources.put(i, sourceId); ! ! channels[i] = new Channel(sourceId); ! num_channels++; ! ! // set default values for AL sources ! AL10.alSourcef (sourceId, AL10.AL_GAIN, s_volume.value); ! 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_BUFFER,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); ! } ! Com.Printf("... using " + num_channels + " channels\n"); ! } ! /* (non-Javadoc) --- 142,156 ---- void exitOpenAL() { + // Release the EAX context. + if (hasEAX){ + EAX.destroy(); + } // Release the context and the device. AL.destroy(); } ! // TODO check the sfx direct buffer size ! // 2MB sfx buffer ! ByteBuffer sfxDataBuffer = Lib.newByteBuffer(2 * 1024 * 1024); /* (non-Javadoc) *************** *** 193,199 **** int format = AL10.AL_FORMAT_MONO16; ! ByteBuffer data = BufferUtils.createByteBuffer(sfx.cache.data.length); ! data.put(sfx.cache.data); data.rewind(); int freq = sfx.cache.speed; --- 164,171 ---- int format = AL10.AL_FORMAT_MONO16; ! ByteBuffer data = sfxDataBuffer.slice(); ! data.put(sfx.cache.data, 0, sfx.cache.data.length); data.rewind(); + data.limit(sfx.cache.data.length); int freq = sfx.cache.speed; *************** *** 225,230 **** public void Shutdown() { StopAllSounds(); ! ! AL10.alDeleteSources(sources); AL10.alDeleteBuffers(buffers); exitOpenAL(); --- 197,201 ---- public void Shutdown() { StopAllSounds(); ! Channel.shutdown(); AL10.alDeleteBuffers(buffers); exitOpenAL(); *************** *** 242,253 **** } num_sfx = 0; - num_channels = 0; } - //private final static float[] NULLVECTOR = {0, 0, 0}; - private final static FloatBuffer NULLVECTOR_BUFFER=Lib.newFloatBuffer(3); - private float[] entityOrigin = {0, 0, 0}; - private float[] sourceOrigin = {0, 0, 0}; - /* (non-Javadoc) * @see jake2.sound.SoundImpl#StartSound(float[], int, int, jake2.sound.sfx_t, float, float, float) --- 213,218 ---- *************** *** 267,338 **** attenuation *= 0.5f; ! Channel ch = pickChannel(entnum, entchannel, buffers.get(sfx.bufferId), attenuation); ! ! if (ch == null) return; ! ! if (entnum == Globals.cl.playernum + 1) { ! ch.addListener(); ! } else if (origin != null) { ! ch.addFixed(origin); ! } else { ! ch.addDynamic(entnum); ! } ! } ! ! Channel pickChannel(int entnum, int entchannel, int bufferId, float rolloff) { ! ! Channel ch = null; ! int state; ! int i; ! ! for (i = 0; i < num_channels; i++) { ! ch = channels[i]; ! ! if (entchannel != 0 && ch.entnum == entnum && ch.entchannel == entchannel) { ! // always override sound from same entity ! break; ! } ! ! // don't let monster sounds override player sounds ! if ((ch.entnum == Globals.cl.playernum+1) && (entnum != Globals.cl.playernum+1) && ch.bufferId != -1) ! continue; ! ! // looking for a free AL source ! if (!ch.active) { ! break; ! } ! } ! ! if (i == num_channels) ! return null; ! ! ch.entnum = entnum; ! ch.entchannel = entchannel; ! if (ch.bufferId != bufferId) { ! ch.bufferId = bufferId; ! ch.bufferChanged = true; ! } ! ch.rolloff = rolloff * 2; ! ch.active = true; ! ch.modified = true; ! ! return ch; } ! private float[] listenerOrigin = {0, 0, 0}; ! private FloatBuffer listenerOriginBuffer=FloatBuffer.wrap(listenerOrigin); ! ! private float[] listenerOrientation = {0, 0, 0, 0, 0, 0}; ! private FloatBuffer listenerOrientationBuffer=FloatBuffer.wrap(listenerOrientation); ! ! private IntBuffer eaxEnv = Lib.newIntBuffer(1); ! private int currentEnv = -1; ! private boolean changeEnv = true; ! ! // TODO workaround for JOAL-bug ! // should be EAX.LISTENER ! private final static int EAX_LISTENER = 0; ! // should be EAX.SOURCE ! private final static int EAX_SOURCE = 1; /* (non-Javadoc) --- 232,240 ---- attenuation *= 0.5f; ! PlaySound.allocate(origin, entnum, entchannel, buffers.get(sfx.bufferId), fvol, attenuation, timeofs); } ! private FloatBuffer listenerOrigin = Lib.newFloatBuffer(3); ! private FloatBuffer listenerOrientation = Lib.newFloatBuffer(6); /* (non-Javadoc) *************** *** 341,516 **** public void Update(float[] origin, float[] forward, float[] right, float[] up) { ! convertVector(origin, listenerOrigin); ! AL10.nalListenerfv(AL10.AL_POSITION, listenerOriginBuffer,0); ! convertOrientation(forward, up, listenerOrientation); ! AL10.nalListenerfv(AL10.AL_ORIENTATION, listenerOrientationBuffer,0); ! if (hasEAX) ! { ! // workaround for environment initialisation ! if (currentEnv == -1) ! { ! eaxEnv.put(0, EAX20.EAX_ENVIRONMENT_UNDERWATER); ! EAX20.eaxSet(EAX_LISTENER, EAXListenerProperties.EAXLISTENER_ENVIRONMENT | EAXListenerProperties.EAXLISTENER_DEFERRED, 0, eaxEnv, 4); ! changeEnv = true; ! } ! if ((GameBase.gi.pointcontents.pointcontents(origin)& Defines.MASK_WATER)!= 0) { ! changeEnv = currentEnv != EAX20.EAX_ENVIRONMENT_UNDERWATER; ! currentEnv = EAX20.EAX_ENVIRONMENT_UNDERWATER; } else { ! changeEnv = currentEnv != EAX20.EAX_ENVIRONMENT_GENERIC; ! currentEnv = EAX20.EAX_ENVIRONMENT_GENERIC; ! } ! if (changeEnv) { ! eaxEnv.put(0, currentEnv); ! EAX20.eaxSet(EAX_LISTENER, EAXListenerProperties.EAXLISTENER_ENVIRONMENT | EAXListenerProperties.EAXLISTENER_DEFERRED, 0, eaxEnv, 4); } } ! AddLoopSounds(origin); ! playChannels(listenerOrigin); } ! Map looptable = new Hashtable(MAX_CHANNELS); ! ! /* ! ================== ! S_AddLoopSounds ! ! 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 ! ================== ! */ ! void AddLoopSounds(float[] listener) { ! ! if (Globals.cl_paused.value != 0.0f) { ! removeUnusedLoopSounds(); ! return; ! } ! ! if (Globals.cls.state != Globals.ca_active) { ! removeUnusedLoopSounds(); ! return; ! } ! ! if (!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; ! ch.origin = ent.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 = pickChannel(0, 0, buffers.get(sfx.bufferId), 6); ! if (ch == null) ! break; ! ! ch.addFixed(ent.origin); ! ch.autosound = true; ! ! looptable.put(key, ch); ! AL10.alSourcei(ch.sourceId, AL10.AL_LOOPING, AL10.AL_TRUE); ! } ! ! removeUnusedLoopSounds(); ! ! } ! 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(); ! } ! } ! } ! ! void playChannels(float[] listenerOrigin) ! { ! float[] sourceOrigin = {0, 0, 0}; ! FloatBuffer sourceOriginBuffer=FloatBuffer.wrap(sourceOrigin); ! ! float[] entityOrigin = {0, 0, 0}; ! Channel ch; ! int sourceId; ! int state; ! ! for (int i = 0; i < num_channels; i++) { ! ch = channels[i]; ! if (ch.active) { ! sourceId = ch.sourceId; ! ! switch (ch.type) { ! case Channel.LISTENER: ! Math3D.VectorCopy(listenerOrigin, sourceOrigin); ! break; ! case Channel.DYNAMIC: ! CL_ents.GetEntitySoundOrigin(ch.entity, 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, s_volume.value); ! AL10.alSourcef (sourceId, AL10.AL_ROLLOFF_FACTOR, ch.rolloff); ! AL10.nalSourcefv(sourceId, AL10.AL_POSITION, sourceOriginBuffer,0); ! AL10.alSourcePlay(sourceId); ! ch.modified = false; ! } else { ! state = AL10.alGetSourcei(ch.sourceId, AL10.AL_SOURCE_STATE); ! if (state == AL10.AL_PLAYING) { ! AL10.nalSourcefv(sourceId, AL10.AL_POSITION, sourceOriginBuffer,0); ! } else { ! ch.clear(); ! } ! } ! ch.autosound = false; ! } ! } } --- 243,273 ---- public void Update(float[] origin, float[] forward, float[] right, float[] up) { ! Channel.convertVector(origin, listenerOrigin); ! AL10.nalListenerfv(AL10.AL_POSITION, listenerOrigin, 0); ! Channel.convertOrientation(forward, up, listenerOrientation); ! AL10.nalListenerfv(AL10.AL_ORIENTATION, listenerOrientation, 0); ! if (hasEAX){ if ((GameBase.gi.pointcontents.pointcontents(origin)& Defines.MASK_WATER)!= 0) { ! changeEnvironment(EAX20.EAX_ENVIRONMENT_UNDERWATER); } else { ! changeEnvironment(EAX20.EAX_ENVIRONMENT_GENERIC); } } ! Channel.addLoopSounds(); ! Channel.addPlaySounds(); ! Channel.playAllSounds(listenerOrigin, s_volume.value); } ! private IntBuffer eaxEnv = Lib.newIntBuffer(1); ! private int currentEnv = EAX20.EAX_ENVIRONMENT_UNDERWATER; ! private void changeEnvironment(int env) { ! if (env == currentEnv) return; ! currentEnv = env; ! eaxEnv.put(0, currentEnv); ! EAX20.eaxSet(EAX20.LISTENER_GUID, EAXListenerProperties.EAXLISTENER_ENVIRONMENT | EAXListenerProperties.EAXLISTENER_DEFERRED, 0, eaxEnv, 4); } *************** *** 519,542 **** */ public void StopAllSounds() { ! for (int i = 0; i < num_channels; i++) { ! AL10.alSourceStop(sources.get(i)); ! AL10.alSourcei(sources.get(i), AL10.AL_BUFFER, 0); ! channels[i].clear(); ! } ! } ! ! static void convertVector(float[] from, float[] to) { ! to[0] = from[0]; ! to[1] = from[2]; ! to[2] = -from[1]; ! } ! ! static void convertOrientation(float[] forward, float[] up, float[] orientation) { ! orientation[0] = forward[0]; ! orientation[1] = forward[2]; ! orientation[2] = -forward[1]; ! orientation[3] = up[0]; ! orientation[4] = up[2]; ! orientation[5] = -up[1]; } --- 276,281 ---- */ public void StopAllSounds() { ! PlaySound.reset(); ! Channel.reset(); } *************** *** 548,552 **** } - int s_registration_sequence; boolean s_registering; --- 287,290 ---- *************** *** 608,613 **** // determine what model the client is using ! // TODO configstrings for player male and female are wrong ! String model = "male"; int n = Globals.CS_PLAYERSKINS + ent.number - 1; if (Globals.cl.configstrings[n] != null) { --- 346,350 ---- // determine what model the client is using ! String model = null; int n = Globals.CS_PLAYERSKINS + ent.number - 1; if (Globals.cl.configstrings[n] != null) { *************** *** 631,653 **** sfx = FindName(sexedFilename, false); ! if (sfx == null) { ! // no, so see if it exists ! RandomAccessFile f = null; ! try { ! f = FS.FOpenFile(sexedFilename.substring(1)); ! } catch (IOException e) {} ! if (f != null) { ! // yes, close the file and register it ! try { ! FS.FCloseFile(f); ! } catch (IOException e1) {} ! sfx = RegisterSound(sexedFilename); ! } else { ! // no, revert to the male sound in the pak0.pak ! String maleFilename = "player/male/" + base.substring(1); ! sfx = AliasName(sexedFilename, maleFilename); ! } } ! return sfx; } --- 368,390 ---- sfx = FindName(sexedFilename, false); ! if (sfx != null) return sfx; ! ! // ! // fall back strategies ! // ! // not found , so see if it exists ! if (FS.FileLength(sexedFilename.substring(1)) > 0) { ! // yes, register it ! return RegisterSound(sexedFilename); } ! // try it with the female sound in the pak0.pak ! if (model.equalsIgnoreCase("female")) { ! String femaleFilename = "player/female/" + base.substring(1); ! if (FS.FileLength("sound/" + femaleFilename) > 0) ! return AliasName(sexedFilename, femaleFilename); ! } ! // no chance, revert to the male sound in the pak0.pak ! String maleFilename = "player/male/" + base.substring(1); ! return AliasName(sexedFilename, maleFilename); } Index: Channel.java =================================================================== RCS file: /cvsroot/jake2/jake2/src/jake2/sound/lwjgl/Channel.java,v retrieving revision 1.1 retrieving revision 1.1.2.1 diff -C2 -d -r1.1 -r1.1.2.1 *** Channel.java 16 Dec 2004 20:17:55 -0000 1.1 --- Channel.java 31 Dec 2004 14:08:34 -0000 1.1.2.1 *************** *** 27,34 **** package jake2.sound.lwjgl; /** * Channel * ! * @author cwei */ public class Channel { --- 27,49 ---- 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 dsanders/cwei */ public class Channel { *************** *** 37,84 **** 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; // sound attributes ! int type; ! int entity; ! float[] origin = {0, 0, 0}; ! ! Channel(int sourceId) { this.sourceId = sourceId; clear(); } ! void addListener() { ! type = LISTENER; } ! void addFixed(float[] origin) { ! type = FIXED; ! this.origin = origin; } - void addDynamic(int entity) { - type = DYNAMIC; - this.entity = entity; } ! void clear() { ! entnum = -1; ! entchannel = -1; ! bufferId = -1; ! bufferChanged = false; ! rolloff = 0; ! autosound = false; ! active = false; ! modified = false; } } --- 52,350 ---- final static int FIXED = 1; final static int DYNAMIC = 2; ! 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 ! 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(); } ! 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; + } + } ! 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; ! ! 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; ! } ! } } + + /* + * 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]); + } + } |