diff options
author | Carsten Weisse <[email protected]> | 2004-12-16 20:17:55 +0000 |
---|---|---|
committer | Carsten Weisse <[email protected]> | 2004-12-16 20:17:55 +0000 |
commit | 658ee5aca7229340a408e0ab30dd23ba439b2176 (patch) | |
tree | 619b1a7df406e530c72b28b543634ff62a57ca94 /src/jake2/sound | |
parent | 611ff00bd30ccff3bc47aaaa0aa37f3f4c827a52 (diff) |
initial checkin of the lwjgl sound from david sanders
Diffstat (limited to 'src/jake2/sound')
-rw-r--r-- | src/jake2/sound/S.java | 4 | ||||
-rw-r--r-- | src/jake2/sound/lwjgl/Channel.java | 84 | ||||
-rw-r--r-- | src/jake2/sound/lwjgl/LWJGLSoundImpl.java | 836 |
3 files changed, 922 insertions, 2 deletions
diff --git a/src/jake2/sound/S.java b/src/jake2/sound/S.java index a58b8f4..3d25164 100644 --- a/src/jake2/sound/S.java +++ b/src/jake2/sound/S.java @@ -2,7 +2,7 @@ * S.java * Copyright (C) 2003 * - * $Id: S.java,v 1.3 2004-11-03 14:33:06 hzi Exp $ + * $Id: S.java,v 1.4 2004-12-16 20:17:55 cawe Exp $ */ /* Copyright (C) 1997-2001 Id Software, Inc. @@ -46,7 +46,7 @@ public class S { try { Class.forName("jake2.sound.DummyDriver"); Class.forName("jake2.sound.joal.JOALSoundImpl"); - Class.forName("jake2.sound.jsound.JSoundImpl"); + Class.forName("jake2.sound.lwjgl.LWJGLSoundImpl"); } catch (Throwable e) { } diff --git a/src/jake2/sound/lwjgl/Channel.java b/src/jake2/sound/lwjgl/Channel.java new file mode 100644 index 0000000..c197191 --- /dev/null +++ b/src/jake2/sound/lwjgl/Channel.java @@ -0,0 +1,84 @@ +/* + * Created on Jun 19, 2004 + * + * Copyright (C) 2003 + * + * $Id: Channel.java,v 1.1 2004-12-16 20:17:55 cawe 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; + +/** + * Channel + * + * @author 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; + + // 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; + } +} diff --git a/src/jake2/sound/lwjgl/LWJGLSoundImpl.java b/src/jake2/sound/lwjgl/LWJGLSoundImpl.java new file mode 100644 index 0000000..e82e32a --- /dev/null +++ b/src/jake2/sound/lwjgl/LWJGLSoundImpl.java @@ -0,0 +1,836 @@ +/*
+ * LWJGLSoundImpl.java
+ * Copyright (C) 2004
+ *
+ * $Id: LWJGLSoundImpl.java,v 1.1 2004-12-16 20:17:55 cawe Exp $
+ */
+package jake2.sound.lwjgl;
+
+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 {
+
+ static {
+ S.register(new LWJGLSoundImpl());
+ };
+
+ private boolean hasEAX;
+
+ private cvar_t s_volume;
+
+ 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
+ private LWJGLSoundImpl() {
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#Init()
+ */
+ public boolean Init() {
+
+ try {
+ initOpenAL();
+ checkError();
+ initOpenALExtensions();
+ } catch (OpenALException e) {
+ Com.Printf(e.getMessage() + '\n');
+ return false;
+ } catch (Exception e) {
+ Com.DPrintf(e.getMessage() + '\n');
+ return false;
+ }
+
+ 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() {
+ public void execute() {
+ Play();
+ }
+ });
+ Cmd.AddCommand("stopsound", new xcommand_t() {
+ public void execute() {
+ StopAllSounds();
+ }
+ });
+ Cmd.AddCommand("soundlist", new xcommand_t() {
+ public void execute() {
+ SoundList();
+ }
+ });
+ Cmd.AddCommand("soundinfo", new xcommand_t() {
+ public void execute() {
+ SoundInfo_f();
+ }
+ });
+
+ num_sfx = 0;
+
+
+ Com.Printf("sound sampling rate: 44100Hz\n");
+
+ StopAllSounds();
+ Com.Printf("------------------------------------\n");
+ return true;
+ }
+
+
+ private void initOpenAL() throws OpenALException
+ {
+ try { AL.create(); } catch (LWJGLException e) { throw new OpenALException(e); }
+ String deviceName = null;
+
+ String os = System.getProperty("os.name");
+ if (os.startsWith("Windows")) {
+ deviceName = "DirectSound3D";
+ }
+
+ String deviceSpecifier = ALC.alcGetString(ALC.ALC_DEVICE_SPECIFIER);
+ String defaultSpecifier = ALC.alcGetString(ALC.ALC_DEFAULT_DEVICE_SPECIFIER);
+
+ Com.Printf(os + " using " + ((deviceName == null) ? defaultSpecifier : deviceName) + '\n');
+
+ // Check for an error.
+ if (ALC.alcGetError() != ALC.ALC_NO_ERROR)
+ {
+ Com.DPrintf("Error with SoundDevice");
+ }
+ }
+
+ private void initOpenALExtensions() throws OpenALException
+ {
+ if (AL10.alIsExtensionPresent("EAX2.0"))
+ {
+ Com.Printf("... using EAX2.0\n");
+ hasEAX=true;
+ }
+ else
+ {
+ Com.Printf("... EAX2.0 not found\n");
+ hasEAX=false;
+ }
+ }
+
+
+ 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)
+ * @see jake2.sound.SoundImpl#RegisterSound(jake2.sound.sfx_t)
+ */
+ private void initBuffer(sfx_t sfx) {
+ if (sfx.cache == null ) {
+ //System.out.println(sfx.name + " " + sfx.cache.length+ " " + sfx.cache.loopstart + " " + sfx.cache.speed + " " + sfx.cache.stereo + " " + sfx.cache.width);
+ return;
+ }
+
+ 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;
+
+ AL10.alBufferData( buffers.get(sfx.bufferId), format, data, freq);
+ }
+
+ private void checkError() {
+ Com.DPrintf("AL Error: " + alErrorString() +'\n');
+ }
+
+ private String alErrorString(){
+ int error;
+ String message = "";
+ if ((error = AL10.alGetError()) != AL10.AL_NO_ERROR) {
+ switch(error) {
+ case AL10.AL_INVALID_OPERATION: message = "invalid operation"; break;
+ case AL10.AL_INVALID_VALUE: message = "invalid value"; break;
+ case AL10.AL_INVALID_ENUM: message = "invalid enum"; break;
+ case AL10.AL_INVALID_NAME: message = "invalid name"; break;
+ default: message = "" + error;
+ }
+ }
+ return message;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#Shutdown()
+ */
+ public void Shutdown() {
+ StopAllSounds();
+
+ AL10.alDeleteSources(sources);
+ AL10.alDeleteBuffers(buffers);
+ exitOpenAL();
+
+ Cmd.RemoveCommand("play");
+ Cmd.RemoveCommand("stopsound");
+ Cmd.RemoveCommand("soundlist");
+ Cmd.RemoveCommand("soundinfo");
+
+ // free all sounds
+ for (int i = 0; i < num_sfx; i++) {
+ if (known_sfx[i].name == null)
+ continue;
+ known_sfx[i].clear();
+ }
+ 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)
+ */
+ public void StartSound(float[] origin, int entnum, int entchannel, sfx_t sfx, float fvol, float attenuation, float timeofs) {
+
+ if (sfx == null)
+ return;
+
+ if (sfx.name.charAt(0) == '*')
+ sfx = RegisterSexedSound(Globals.cl_entities[entnum].current, sfx.name);
+
+ if (LoadSound(sfx) == null)
+ return; // can't load sound
+
+ if (attenuation != Defines.ATTN_STATIC)
+ 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)
+ * @see jake2.sound.SoundImpl#Update(float[], float[], float[], float[])
+ */
+ 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;
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.SoundImpl#StopAllSounds()
+ */
+ 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];
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#getName()
+ */
+ public String getName() {
+ return "lwjgl_joal";
+ }
+
+
+ int s_registration_sequence;
+ boolean s_registering;
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#BeginRegistration()
+ */
+ public void BeginRegistration() {
+ s_registration_sequence++;
+ s_registering = true;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#RegisterSound(java.lang.String)
+ */
+ public sfx_t RegisterSound(String name) {
+ sfx_t sfx = FindName(name, true);
+ sfx.registration_sequence = s_registration_sequence;
+
+ if (!s_registering)
+ LoadSound(sfx);
+
+ return sfx;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#EndRegistration()
+ */
+ public void EndRegistration() {
+ int i;
+ sfx_t sfx;
+ int size;
+
+ // free any sounds not from this registration sequence
+ for (i = 0; i < num_sfx; i++) {
+ sfx = known_sfx[i];
+ if (sfx.name == null)
+ continue;
+ if (sfx.registration_sequence != s_registration_sequence) {
+ // don't need this sound
+ sfx.clear();
+ }
+ }
+
+ // load everything in
+ for (i = 0; i < num_sfx; i++) {
+ sfx = known_sfx[i];
+ if (sfx.name == null)
+ continue;
+ LoadSound(sfx);
+ }
+
+ s_registering = false;
+ }
+
+ sfx_t RegisterSexedSound(entity_state_t ent, String base) {
+
+ sfx_t sfx = null;
+
+ // 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) {
+ int p = Globals.cl.configstrings[n].indexOf('\\');
+ if (p >= 0) {
+ p++;
+ model = Globals.cl.configstrings[n].substring(p);
+ //strcpy(model, p);
+ p = model.indexOf('/');
+ if (p > 0)
+ model = model.substring(0, p);
+ }
+ }
+ // if we can't figure it out, they're male
+ if (model == null || model.length() == 0)
+ model = "male";
+
+ // see if we already know of the model specific sound
+ String sexedFilename = "#players/" + model + "/" + base.substring(1);
+ //Com_sprintf (sexedFilename, sizeof(sexedFilename), "#players/%s/%s", model, base+1);
+ 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;
+ }
+
+
+ static sfx_t[] known_sfx = new sfx_t[MAX_SFX];
+ static {
+ for (int i = 0; i< known_sfx.length; i++)
+ known_sfx[i] = new sfx_t();
+ }
+ static int num_sfx;
+
+ sfx_t FindName(String name, boolean create) {
+ int i;
+ sfx_t sfx = null;
+
+ if (name == null)
+ Com.Error(Defines.ERR_FATAL, "S_FindName: NULL\n");
+ if (name.length() == 0)
+ Com.Error(Defines.ERR_FATAL, "S_FindName: empty name\n");
+
+ if (name.length() >= Defines.MAX_QPATH)
+ Com.Error(Defines.ERR_FATAL, "Sound name too long: " + name);
+
+ // see if already loaded
+ for (i = 0; i < num_sfx; i++)
+ if (name.equals(known_sfx[i].name)) {
+ return known_sfx[i];
+ }
+
+ if (!create)
+ return null;
+
+ // find a free sfx
+ for (i = 0; i < num_sfx; i++)
+ if (known_sfx[i].name == null)
+ // registration_sequence < s_registration_sequence)
+ break;
+
+ if (i == num_sfx) {
+ if (num_sfx == MAX_SFX)
+ Com.Error(Defines.ERR_FATAL, "S_FindName: out of sfx_t");
+ num_sfx++;
+ }
+
+ sfx = known_sfx[i];
+ sfx.clear();
+ sfx.name = name;
+ sfx.registration_sequence = s_registration_sequence;
+ sfx.bufferId = i;
+
+ return sfx;
+ }
+
+ /*
+ ==================
+ S_AliasName
+
+ ==================
+ */
+ sfx_t AliasName(String aliasname, String truename)
+ {
+ sfx_t sfx = null;
+ String s;
+ int i;
+
+ s = new String(truename);
+
+ // find a free sfx
+ for (i=0 ; i < num_sfx ; i++)
+ if (known_sfx[i].name == null)
+ break;
+
+ if (i == num_sfx)
+ {
+ if (num_sfx == MAX_SFX)
+ Com.Error(Defines.ERR_FATAL, "S_FindName: out of sfx_t");
+ num_sfx++;
+ }
+
+ sfx = known_sfx[i];
+ sfx.clear();
+ sfx.name = new String(aliasname);
+ sfx.registration_sequence = s_registration_sequence;
+ sfx.truename = s;
+ // set the AL bufferId
+ sfx.bufferId = i;
+
+ return sfx;
+ }
+
+ /*
+ ==============
+ S_LoadSound
+ ==============
+ */
+ public sfxcache_t LoadSound(sfx_t s) {
+ sfxcache_t sc = WaveLoader.LoadSound(s);
+ initBuffer(s);
+ return sc;
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#StartLocalSound(java.lang.String)
+ */
+ public void StartLocalSound(String sound) {
+ sfx_t sfx;
+
+ sfx = RegisterSound(sound);
+ if (sfx == null) {
+ Com.Printf("S_StartLocalSound: can't cache " + sound + "\n");
+ return;
+ }
+ StartSound(null, Globals.cl.playernum + 1, 0, sfx, 1, 1, 0);
+ }
+
+ /* (non-Javadoc)
+ * @see jake2.sound.Sound#RawSamples(int, int, int, int, byte[])
+ */
+ public void RawSamples(int samples, int rate, int width, int channels, byte[] data) {
+ // TODO implement RawSamples
+ }
+
+ /*
+ ===============================================================================
+
+ console functions
+
+ ===============================================================================
+ */
+
+ void Play() {
+ int i;
+ String name;
+ sfx_t sfx;
+
+ i = 1;
+ while (i < Cmd.Argc()) {
+ name = new String(Cmd.Argv(i));
+ if (name.indexOf('.') == -1)
+ name += ".wav";
+
+ sfx = RegisterSound(name);
+ StartSound(null, Globals.cl.playernum + 1, 0, sfx, 1.0f, 1.0f, 0.0f);
+ i++;
+ }
+ }
+
+ void SoundList() {
+ int i;
+ sfx_t sfx;
+ sfxcache_t sc;
+ int size, total;
+
+ total = 0;
+ for (i = 0; i < num_sfx; i++) {
+ sfx = known_sfx[i];
+ if (sfx.registration_sequence == 0)
+ continue;
+ sc = sfx.cache;
+ if (sc != null) {
+ size = sc.length * sc.width * (sc.stereo + 1);
+ total += size;
+ if (sc.loopstart >= 0)
+ Com.Printf("L");
+ else
+ Com.Printf(" ");
+ Com.Printf("(%2db) %6i : %s\n", new Vargs(3).add(sc.width * 8).add(size).add(sfx.name));
+ } else {
+ if (sfx.name.charAt(0) == '*')
+ Com.Printf(" placeholder : " + sfx.name + "\n");
+ else
+ Com.Printf(" not loaded : " + sfx.name + "\n");
+ }
+ }
+ Com.Printf("Total resident: " + total + "\n");
+ }
+
+ void SoundInfo_f() {
+
+ Com.Printf("%5d stereo\n", new Vargs(1).add(1));
+ Com.Printf("%5d samples\n", new Vargs(1).add(22050));
+ Com.Printf("%5d samplebits\n", new Vargs(1).add(16));
+ Com.Printf("%5d speed\n", new Vargs(1).add(44100));
+ }
+
+}
|