/* * Created on Jun 19, 2004 * * Copyright (C) 2003 * * $Id: Channel.java,v 1.2 2004-10-27 16:51:32 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.joal; import java.util.*; import net.java.games.joal.AL; import jake2.Defines; import jake2.Globals; import jake2.client.CL_ents; import jake2.game.entity_state_t; import jake2.qcommon.Com; import jake2.sound.sfx_t; import jake2.sound.sfxcache_t; import jake2.util.Math3D; /** * Channel * * @author cwei */ public class Channel { final static int LISTENER = 0; final static int FIXED = 1; final static int DYNAMIC = 2; final static int MAX_CHANNELS = 32; final static float[] NULLVECTOR = {0, 0, 0}; private static AL al; private static Channel[] channels = new Channel[MAX_CHANNELS]; private static int[] sources = new int[MAX_CHANNELS]; // a reference of JOALSoundImpl.buffers private static int[] 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; } static int init(AL al, int[] buffers, float masterVolume) { Channel.al = al; Channel.buffers = buffers; // create channels int sourceId; int[] tmp = {0}; int error; for (int i = 0; i < MAX_CHANNELS; i++) { al.alGenSources(1, tmp); sourceId = tmp[0]; if (sourceId <= 0) break; sources[i] = sourceId; channels[i] = new Channel(sourceId); numChannels++; // set default values for AL sources al.alSourcef (sourceId, AL.AL_GAIN, masterVolume); al.alSourcef (sourceId, AL.AL_PITCH, 1.0f); al.alSourcei (sourceId, AL.AL_SOURCE_ABSOLUTE, AL.AL_TRUE); al.alSourcefv(sourceId, AL.AL_VELOCITY, NULLVECTOR); al.alSourcei (sourceId, AL.AL_LOOPING, AL.AL_FALSE); al.alSourcef (sourceId, AL.AL_REFERENCE_DISTANCE, 300.0f); al.alSourcef (sourceId, AL.AL_MIN_GAIN, 0.0005f); al.alSourcef (sourceId, AL.AL_MAX_GAIN, 1.0f); } isInitialized = true; return numChannels; } static void reset() { for (int i = 0; i < numChannels; i++) { al.alSourceStop(sources[i]); al.alSourcei(sources[i], AL.AL_BUFFER, 0); channels[i].clear(); } } static void shutdown() { al.alDeleteSources(numChannels, 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) { al.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; } static void playAllSounds(float[] listenerOrigin, float masterVolume) { float[] sourceOrigin = {0, 0, 0}; float[] entityOrigin = {0, 0, 0}; 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: Math3D.VectorCopy(listenerOrigin, sourceOrigin); 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) { al.alSourcei(sourceId, AL.AL_BUFFER, ch.bufferId); } // al.alSourcef (sourceId, AL.AL_GAIN, masterVolume * ch.volume); al.alSourcef (sourceId, AL.AL_GAIN, masterVolume); al.alSourcef (sourceId, AL.AL_ROLLOFF_FACTOR, ch.rolloff); al.alSourcefv(sourceId, AL.AL_POSITION, sourceOrigin); al.alSourcePlay(sourceId); ch.modified = false; } else { state = al.alGetSourcei(sourceId, AL.AL_SOURCE_STATE); if (state == AL.AL_PLAYING) { al.alSourcefv(sourceId, AL.AL_POSITION, sourceOrigin); } 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