1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 /*****************************************************************************
25  * name:		snd_dma.c
26  *
27  * desc:		main control for any streaming sound output device
28  *
29  *
30  *****************************************************************************/
31 #include "sdl/sdl_sound.h"
32 #include "snd_local.h"
33 #include "snd_mp3.h"
34 #include "snd_music.h"
35 #include "client.h"
36 #define __STDC_FORMAT_MACROS
37 #include <inttypes.h>
38 
39 #if defined(_WIN32)
40 #include <windows.h>
41 #endif
42 
43 qboolean s_shutUp = qfalse;
44 
45 static void S_Play_f(void);
46 static void S_SoundList_f(void);
47 static void S_Music_f(void);
48 static void S_StopMusic_f(void);
49 static void S_SetDynamicMusic_f(void);
50 
51 void S_Update_();
52 void S_StopAllSounds(void);
53 static void S_UpdateBackgroundTrack( void );
54 sfx_t *S_FindName( const char *name );
55 static int SND_FreeSFXMem(sfx_t *sfx);
56 
57 extern qboolean Sys_LowPhysicalMemory();
58 
59 //////////////////////////
60 //
61 // vars for bgrnd music track...
62 //
63 const int iMP3MusicStream_DiskBytesToRead = 10000;//4096;
64 const int iMP3MusicStream_DiskBufferSize = iMP3MusicStream_DiskBytesToRead*2; //*10;
65 
66 typedef struct MusicInfo_s {
67 	qboolean	bIsMP3;
68 	//
69 	// MP3 specific...
70 	//
71 	sfx_t		sfxMP3_Bgrnd;
72 	MP3STREAM	streamMP3_Bgrnd;	// this one is pointed at by the sfx_t's ptr, and is NOT the one the decoder uses every cycle
73 	channel_t	chMP3_Bgrnd;		// ... but the one in this struct IS.
74 	//
75 	// MP3 disk streamer stuff... (if music is non-dynamic)
76 	//
77 	byte		byMP3MusicStream_DiskBuffer[iMP3MusicStream_DiskBufferSize];
78 	int			iMP3MusicStream_DiskReadPos;
79 	int			iMP3MusicStream_DiskWindowPos;
80 	//
81 	// MP3 disk-load stuff (for use during dynamic music, which is mem-resident)
82 	//
83 	byte		*pLoadedData;	// Z_Malloc, Z_Free	// these two MUST be kept as valid/invalid together
84 	char		sLoadedDataName[MAX_QPATH];			//  " " " " "
85 	int			iLoadedDataLen;
86 	//
87 	// remaining dynamic fields...
88 	//
89 	int			iXFadeVolumeSeekTime;
90 	int			iXFadeVolumeSeekTo;	// when changing this, set the above timer to Sys_Milliseconds().
91 									//	Note that this should be thought of more as an up/down bool rather than as a
92 									//	number now, in other words set it only to 0 or 255. I'll probably change this
93 									//	to actually be a bool later.
94 	int			iXFadeVolume;		// 0 = silent, 255 = max mixer vol, though still modulated via overall music_volume
95 	float		fSmoothedOutVolume;
96 	qboolean	bActive;			// whether playing or not
97 	qboolean	bExists;			// whether was even loaded for this level (ie don't try and start playing it)
98 	//
99 	// new dynamic fields...
100 	//
101 	qboolean	bTrackSwitchPending;
102 	MusicState_e eTS_NewState;
103 	float		 fTS_NewTime;
104 	//
105 	// Generic...
106 	//
107 	fileHandle_t s_backgroundFile;	// valid handle, else -1 if an MP3 (so that NZ compares still work)
108 	wavinfo_t	s_backgroundInfo;
109 	int			s_backgroundSamples;
110 
RewindMusicInfo_s111 	void Rewind()
112 	{
113 		MP3Stream_Rewind( &chMP3_Bgrnd );
114 		s_backgroundSamples = sfxMP3_Bgrnd.iSoundLengthInSamples;
115 	}
116 
SeekToMusicInfo_s117 	void SeekTo(float fTime)
118 	{
119 		chMP3_Bgrnd.iMP3SlidingDecodeWindowPos = 0;
120 		chMP3_Bgrnd.iMP3SlidingDecodeWritePos = 0;
121 		MP3Stream_SeekTo( &chMP3_Bgrnd, fTime );
122 		s_backgroundSamples = sfxMP3_Bgrnd.iSoundLengthInSamples;
123 	}
124 } MusicInfo_t;
125 
126 static void S_SetDynamicMusicState( MusicState_e musicState );
127 
128 #define fDYNAMIC_XFADE_SECONDS (1.0f)
129 
130 static MusicInfo_t	tMusic_Info[eBGRNDTRACK_NUMBEROF]	= {};
131 static qboolean		bMusic_IsDynamic					= qfalse;
132 static MusicState_e	eMusic_StateActual					= eBGRNDTRACK_EXPLORE;	// actual state, can be any enum
133 static MusicState_e	eMusic_StateRequest					= eBGRNDTRACK_EXPLORE;	// requested state, can only be explore, action, boss, or silence
134 static char			sMusic_BackgroundLoop[MAX_QPATH]	= {0};	// only valid for non-dynamic music
135 static char			sInfoOnly_CurrentDynamicMusicSet[64];	// any old reasonable size, only has to fit stuff like "kejim_post"
136 //
137 //////////////////////////
138 
139 
140 // =======================================================================
141 // Internal sound data & structures
142 // =======================================================================
143 
144 // only begin attenuating sound volumes when outside the FULLVOLUME range
145 #define		SOUND_FULLVOLUME	256
146 
147 #define		SOUND_ATTENUATE		0.0008f
148 #define		VOICE_ATTENUATE		0.004f
149 
150 const float	SOUND_FMAXVOL=0.75;//1.0;
151 const int	SOUND_MAXVOL=255;
152 
153 channel_t   s_channels[MAX_CHANNELS];
154 
155 int			s_soundStarted;
156 qboolean	s_soundMuted;
157 
158 dma_t		dma;
159 
160 int			listener_number;
161 vec3_t		listener_origin;
162 matrix3_t	listener_axis;
163 
164 int			s_soundtime;		// sample PAIRS
165 int   		s_paintedtime; 		// sample PAIRS
166 
167 // MAX_SFX may be larger than MAX_SOUNDS because
168 // of custom player sounds
169 #define		MAX_SFX			10000	//512 * 2
170 sfx_t		s_knownSfx[MAX_SFX];
171 int			s_numSfx;
172 
173 #define		LOOP_HASH		128
174 static	sfx_t		*sfxHash[LOOP_HASH];
175 
176 cvar_t		*s_volume;
177 cvar_t		*s_volumeVoice;
178 cvar_t		*s_testsound;
179 cvar_t		*s_khz;
180 cvar_t		*s_allowDynamicMusic;
181 cvar_t		*s_show;
182 cvar_t		*s_mixahead;
183 cvar_t		*s_mixPreStep;
184 cvar_t		*s_musicVolume;
185 cvar_t		*s_separation;
186 cvar_t		*s_lip_threshold_1;
187 cvar_t		*s_lip_threshold_2;
188 cvar_t		*s_lip_threshold_3;
189 cvar_t		*s_lip_threshold_4;
190 cvar_t		*s_language;	// note that this is distinct from "g_language"
191 cvar_t		*s_dynamix;
192 cvar_t		*s_debugdynamic;
193 
194 cvar_t		*s_doppler;
195 
196 typedef struct
197 {
198 	unsigned char	volume;
199 	vec3_t			origin;
200 	vec3_t			velocity;
201 	sfx_t		*sfx;
202 	int				mergeFrame;
203 	int			entnum;
204 
205 	qboolean	doppler;
206 	float		dopplerScale;
207 
208 	// For Open AL
209 	bool	bProcessed;
210 	bool	bRelative;
211 } loopSound_t;
212 
213 #define	MAX_LOOP_SOUNDS		32
214 
215 int			numLoopSounds;
216 loopSound_t	loopSounds[MAX_LOOP_SOUNDS];
217 
218 int			s_rawend;
219 portable_samplepair_t	s_rawsamples[MAX_RAW_SAMPLES];
220 vec3_t		s_entityPosition[MAX_GENTITIES];
221 int			s_entityWavVol[MAX_GENTITIES];
222 int			s_entityWavVol_back[MAX_GENTITIES];
223 
224 int			s_numChannels;			// Number of AL Sources == Num of Channels
225 
226 #ifdef USE_OPENAL
227 
228 /**************************************************************************************************\
229 *
230 *	Open AL Specific
231 *
232 \**************************************************************************************************/
233 
234 #define sqr(a)			((a)*(a))
235 #define ENV_UPDATE_RATE	100			// Environmental audio update rate (in ms)
236 
237 //#define DISPLAY_CLOSEST_ENVS		// Displays the closest env. zones (including the one the listener is in)
238 
239 #define DEFAULT_REF_DISTANCE		300.0f		// Default reference distance
240 #define DEFAULT_VOICE_REF_DISTANCE	1500.0f		// Default voice reference distance
241 
242 int			s_UseOpenAL	= 0;	// Determines if using Open AL or the default software mixer
243 
244 ALfloat		listener_pos[3];		// Listener Position
245 ALfloat		listener_ori[6];		// Listener Orientation
246 
247 short		s_rawdata[MAX_RAW_SAMPLES*2];	// Used for Raw Samples (Music etc...)
248 
249 channel_t *S_OpenALPickChannel(int entnum, int entchannel);
250 int  S_MP3PreProcessLipSync(channel_t *ch, short *data);
251 void UpdateSingleShotSounds();
252 void UpdateLoopingSounds();
253 void AL_UpdateRawSamples();
254 void S_SetLipSyncs();
255 
256 // EAX Related
257 
258 typedef struct ENVTABLE_s {
259 	ALuint		ulNumApertures;
260 	ALint		lFXSlotID;
261 	ALboolean	bUsed;
262 	struct
263 	{
264 		ALfloat vPos1[3];
265 		ALfloat vPos2[3];
266 		ALfloat vCenter[3];
267 	} Aperture[64];
268 } ENVTABLE, *LPENVTABLE;
269 
270 typedef struct REVERBDATA_s {
271 	long lEnvID;
272 	long lApertureNum;
273 	float flDist;
274 } REVERBDATA, *LPREVERBDATA;
275 
276 typedef struct FXSLOTINFO_s {
277 	GUID	FXSlotGuid;
278 	ALint	lEnvID;
279 } FXSLOTINFO, *LPFXSLOTINFO;
280 
281 ALboolean				s_bEAX;					// Is EAX 4.0 support available
282 bool					s_bEALFileLoaded;		// Has an .eal file been loaded for the current level
283 bool					s_bInWater;				// Underwater effect currently active
284 int						s_EnvironmentID;		// EAGLE ID of current environment
285 
286 LPEAXMANAGER			s_lpEAXManager;			// Pointer to EAXManager object
287 HINSTANCE				s_hEAXManInst;			// Handle of EAXManager DLL
288 EAXSet					s_eaxSet;				// EAXSet() function
289 EAXGet					s_eaxGet;				// EAXGet() function
290 EAXREVERBPROPERTIES		s_eaxLPCur;				// Current EAX Parameters
291 LPENVTABLE				s_lpEnvTable=NULL;		// Stores information about each environment zone
292 long					s_lLastEnvUpdate;		// Time of last EAX update
293 long					s_lNumEnvironments;		// Number of environment zones
294 long					s_NumFXSlots;			// Number of EAX 4.0 FX Slots
295 FXSLOTINFO				s_FXSlotInfo[EAX_MAX_FXSLOTS];	// Stores information about the EAX 4.0 FX Slots
296 
297 void InitEAXManager();
298 void ReleaseEAXManager();
299 bool LoadEALFile(char *szEALFilename);
300 void UnloadEALFile();
301 void UpdateEAXListener();
302 void UpdateEAXBuffer(channel_t *ch);
303 void EALFileInit(const char *level);
304 float CalcDistance(EMPOINT A, EMPOINT B);
305 
Normalize(EAXVECTOR * v)306 void Normalize(EAXVECTOR *v)
307 {
308 	float flMagnitude;
309 
310 	flMagnitude = (float)sqrt(sqr(v->x) + sqr(v->y) + sqr(v->z));
311 
312 	v->x = v->x / flMagnitude;
313 	v->y = v->y / flMagnitude;
314 	v->z = v->z / flMagnitude;
315 }
316 
317 // EAX 4.0 GUIDS ... confidential information ...
318 
319 const GUID EAXPROPERTYID_EAX40_FXSlot0 = { 0xc4d79f1e, 0xf1ac, 0x436b, { 0xa8, 0x1d, 0xa7, 0x38, 0xe7, 0x4, 0x54, 0x69} };
320 
321 const GUID EAXPROPERTYID_EAX40_FXSlot1 = { 0x8c00e96, 0x74be, 0x4491, { 0x93, 0xaa, 0xe8, 0xad, 0x35, 0xa4, 0x91, 0x17} };
322 
323 const GUID EAXPROPERTYID_EAX40_FXSlot2 = { 0x1d433b88, 0xf0f6, 0x4637, { 0x91, 0x9f, 0x60, 0xe7, 0xe0, 0x6b, 0x5e, 0xdd} };
324 
325 const GUID EAXPROPERTYID_EAX40_FXSlot3 = { 0xefff08ea, 0xc7d8, 0x44ab, { 0x93, 0xad, 0x6d, 0xbd, 0x5f, 0x91, 0x0, 0x64} };
326 
327 const GUID EAXPROPERTYID_EAX40_Context = { 0x1d4870ad, 0xdef, 0x43c0, { 0xa4, 0xc, 0x52, 0x36, 0x32, 0x29, 0x63, 0x42} };
328 
329 const GUID EAXPROPERTYID_EAX40_Source = { 0x1b86b823, 0x22df, 0x4eae, { 0x8b, 0x3c, 0x12, 0x78, 0xce, 0x54, 0x42, 0x27} };
330 
331 const GUID EAX_NULL_GUID = { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
332 
333 const GUID EAX_PrimaryFXSlotID = { 0xf317866d, 0x924c, 0x450c, { 0x86, 0x1b, 0xe6, 0xda, 0xa2, 0x5e, 0x7c, 0x20} };
334 
335 const GUID EAX_REVERB_EFFECT = { 0xcf95c8f, 0xa3cc, 0x4849, { 0xb0, 0xb6, 0x83, 0x2e, 0xcc, 0x18, 0x22, 0xdf} };
336 
337 /**************************************************************************************************\
338 *
339 *	End of Open AL Specific
340 *
341 \**************************************************************************************************/
342 #endif /* USE_OPENAL */
343 
344 // instead of clearing a whole channel_t struct, we're going to skip the MP3SlidingDecodeBuffer[] buffer in the middle...
345 //
346 #ifndef offsetof
347 #include <stddef.h>
348 #endif
Channel_Clear(channel_t * ch)349 static inline void Channel_Clear(channel_t *ch)
350 {
351 	// memset (ch, 0, sizeof(*ch));
352 
353 	memset(ch,0,offsetof(channel_t,MP3SlidingDecodeBuffer));
354 
355 	byte *const p = (byte *)ch + offsetof(channel_t,MP3SlidingDecodeBuffer) + sizeof(ch->MP3SlidingDecodeBuffer);
356 
357 	memset(p,0,(sizeof(*ch) - offsetof(channel_t,MP3SlidingDecodeBuffer)) - sizeof(ch->MP3SlidingDecodeBuffer));
358 }
359 
360 // ====================================================================
361 // User-setable variables
362 // ====================================================================
DynamicMusicInfoPrint(void)363 static void DynamicMusicInfoPrint(void)
364 {
365 	if (bMusic_IsDynamic)
366 	{
367 		// horribly lazy... ;-)
368 		//
369 		const char *psRequestMusicState	= Music_BaseStateToString( eMusic_StateRequest );
370 		const char *psActualMusicState	= Music_BaseStateToString( eMusic_StateActual, qtrue );
371 		if (psRequestMusicState == NULL)
372 		{
373 			psRequestMusicState = "<unknown>";
374 		}
375 		if (psActualMusicState	== NULL)
376 		{
377 			psActualMusicState	= "<unknown>";
378 		}
379 
380 		Com_Printf("( Dynamic music ON, request state: '%s'(%d), actual: '%s' (%d) )\n", psRequestMusicState, eMusic_StateRequest, psActualMusicState, eMusic_StateActual);
381 	}
382 	else
383 	{
384 		Com_Printf("( Dynamic music OFF )\n");
385 	}
386 }
387 
S_SoundInfo_f(void)388 void S_SoundInfo_f(void) {
389 	Com_Printf("----- Sound Info -----\n" );
390 
391 	if (!s_soundStarted) {
392 		Com_Printf ("sound system not started\n");
393 	} else {
394 #ifdef USE_OPENAL
395 		if (s_UseOpenAL)
396 		{
397 			Com_Printf("EAX 4.0 %s supported\n",s_bEAX?"is":"not");
398 			Com_Printf("Eal file %s loaded\n",s_bEALFileLoaded?"is":"not");
399 			Com_Printf("s_EnvironmentID = %d\n",s_EnvironmentID);
400 			Com_Printf("s_bInWater = %s\n",s_bInWater?"true":"false");
401 		}
402 		else
403 		{
404 #endif
405 			Com_Printf("%5d stereo\n", dma.channels - 1);
406 			Com_Printf("%5d samples\n", dma.samples);
407 			Com_Printf("%5d samplebits\n", dma.samplebits);
408 			Com_Printf("%5d submission_chunk\n", dma.submission_chunk);
409 			Com_Printf("%5d speed\n", dma.speed);
410 			Com_Printf( "0x%" PRIxPTR " dma buffer\n", dma.buffer );
411 #ifdef USE_OPENAL
412 		}
413 #endif
414 
415 		if (bMusic_IsDynamic)
416 		{
417 			DynamicMusicInfoPrint();
418 			Com_Printf("( Dynamic music set name: \"%s\" )\n",sInfoOnly_CurrentDynamicMusicSet);
419 		}
420 		else
421 		{
422 			if (!s_allowDynamicMusic->integer)
423 			{
424 				Com_Printf("( Dynamic music inhibited (s_allowDynamicMusic == 0) )\n", sMusic_BackgroundLoop );
425 			}
426 			if ( tMusic_Info[eBGRNDTRACK_NONDYNAMIC].s_backgroundFile )
427 			{
428 				Com_Printf("Background file: %s\n", sMusic_BackgroundLoop );
429 			}
430 			else
431 			{
432 				Com_Printf("No background file.\n" );
433 			}
434 		}
435 	}
436 	S_DisplayFreeMemory();
437 	Com_Printf("----------------------\n" );
438 }
439 
440 /*
441 ================
442 S_Init
443 ================
444 */
S_Init(void)445 void S_Init( void ) {
446 	cvar_t	*cv;
447 	qboolean	r;
448 
449 	Com_Printf("\n------- sound initialization -------\n");
450 
451 	s_volume = Cvar_Get ("s_volume", "0.5", CVAR_ARCHIVE, "Volume" );
452 	s_volumeVoice= Cvar_Get ("s_volumeVoice", "1.0", CVAR_ARCHIVE, "Volume for voice channels" );
453 	s_musicVolume = Cvar_Get ("s_musicvolume", "0.25", CVAR_ARCHIVE, "Music Volume" );
454 	s_separation = Cvar_Get ("s_separation", "0.5", CVAR_ARCHIVE);
455 	s_khz = Cvar_Get ("s_khz", "44", CVAR_ARCHIVE|CVAR_LATCH);
456 	s_allowDynamicMusic = Cvar_Get ("s_allowDynamicMusic", "1", CVAR_ARCHIVE_ND);
457 	s_mixahead = Cvar_Get ("s_mixahead", "0.2", CVAR_ARCHIVE);
458 
459 	s_mixPreStep = Cvar_Get ("s_mixPreStep", "0.05", CVAR_ARCHIVE);
460 	s_show = Cvar_Get ("s_show", "0", CVAR_CHEAT);
461 	s_testsound = Cvar_Get ("s_testsound", "0", CVAR_CHEAT);
462 	s_debugdynamic = Cvar_Get("s_debugdynamic","0", CVAR_CHEAT);
463 	s_lip_threshold_1 = Cvar_Get("s_threshold1" , "0.5",0);
464 	s_lip_threshold_2 = Cvar_Get("s_threshold2" , "4.0",0);
465 	s_lip_threshold_3 = Cvar_Get("s_threshold3" , "7.0",0);
466 	s_lip_threshold_4 = Cvar_Get("s_threshold4" , "8.0",0);
467 
468 	s_language = Cvar_Get("s_language","english",CVAR_ARCHIVE | CVAR_NORESTART, "Sound language" );
469 
470 	s_doppler = Cvar_Get("s_doppler", "1", CVAR_ARCHIVE_ND);
471 
472 	MP3_InitCvars();
473 
474 	cv = Cvar_Get ("s_initsound", "1", 0);
475 	if ( !cv->integer ) {
476 		s_soundStarted = 0;	// needed in case you set s_initsound to 0 midgame then snd_restart (div0 err otherwise later)
477 		Com_Printf ("not initializing.\n");
478 		Com_Printf("------------------------------------\n");
479 		return;
480 	}
481 
482 	Cmd_AddCommand("play", S_Play_f, "Plays a sound fx file" );
483 	Cmd_AddCommand("music", S_Music_f, "Plays a music file" );
484 	Cmd_AddCommand("stopmusic", S_StopMusic_f, "Stops all music" );
485 	Cmd_AddCommand("soundlist", S_SoundList_f, "Lists all cached sound and music files" );
486 	Cmd_AddCommand("soundinfo", S_SoundInfo_f, "Display information about the sound backend" );
487 	Cmd_AddCommand("soundstop", S_StopAllSounds, "Stops all sounds including music" );
488 	Cmd_AddCommand("mp3_calcvols", S_MP3_CalcVols_f);
489 	Cmd_AddCommand("s_dynamic", S_SetDynamicMusic_f, "Change dynamic music state" );
490 
491 #ifdef USE_OPENAL
492 	cv = Cvar_Get("s_UseOpenAL" , "0",CVAR_ARCHIVE|CVAR_LATCH);
493 	s_UseOpenAL = !!(cv->integer);
494 
495 	if (s_UseOpenAL)
496 	{
497 		int i, j;
498 
499 		ALCdevice *ALCDevice = alcOpenDevice((ALubyte*)"DirectSound3D");
500 		if (!ALCDevice)
501 			return;
502 
503 		//Create context(s)
504 		ALCcontext *ALCContext = alcCreateContext(ALCDevice, NULL);
505 		if (!ALCContext)
506 			return;
507 
508 		//Set active context
509 		alcMakeContextCurrent(ALCContext);
510 		if (alcGetError(ALCDevice) != ALC_NO_ERROR)
511 			return;
512 
513 		s_soundStarted = 1;
514 		s_soundMuted = qtrue;
515 		s_soundtime = 0;
516 		s_paintedtime = 0;
517 		s_rawend = 0;
518 
519 		S_StopAllSounds();
520 
521 		S_SoundInfo_f();
522 
523 		// Set Listener attributes
524 		ALfloat listenerPos[]={0.0,0.0,0.0};
525 		ALfloat listenerVel[]={0.0,0.0,0.0};
526 		ALfloat	listenerOri[]={0.0,0.0,-1.0, 0.0,1.0,0.0};
527 		alListenerfv(AL_POSITION,listenerPos);
528 		alListenerfv(AL_VELOCITY,listenerVel);
529 		alListenerfv(AL_ORIENTATION,listenerOri);
530 
531 		InitEAXManager();
532 
533 		memset(s_channels, 0, sizeof(s_channels));
534 
535 		s_numChannels = 0;
536 
537 		// Create as many AL Sources (up to Max) as possible
538 		for (i = 0; i < MAX_CHANNELS; i++)
539 		{
540 			alGenSources(1, &s_channels[i].alSource); // &g_Sources[i]);
541 			if (alGetError() != AL_NO_ERROR)
542 			{
543 				// Reached limit of sources
544 				break;
545 			}
546 			alSourcef(s_channels[i].alSource, AL_REFERENCE_DISTANCE, DEFAULT_REF_DISTANCE);
547 			if (alGetError() != AL_NO_ERROR)
548 			{
549 				break;
550 			}
551 
552 			// Sources / Channels are not sending to any Slots (other than the Listener / Primary FX Slot)
553 			s_channels[i].lSlotID = -1;
554 
555 			if (s_bEAX)
556 			{
557 				// Remove the RoomAuto flag from each Source (to remove Reverb Engine Statistical
558 				// model that is assuming units are in metres)
559 				// Without this call reverb sends from the sources will attenuate too quickly
560 				// with distance, especially for the non-primary reverb zones.
561 
562 				unsigned long ulFlags = 0;
563 
564 				s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_FLAGS,
565 							s_channels[i].alSource, &ulFlags, sizeof(ulFlags));
566 			}
567 
568 			s_numChannels++;
569 		}
570 
571 		// Generate AL Buffers for streaming audio playback (used for MP3s)
572 		channel_t *ch = s_channels + 1;
573 		for (i = 1; i < s_numChannels; i++, ch++)
574 		{
575 			for (j = 0; j < NUM_STREAMING_BUFFERS; j++)
576 			{
577 				alGenBuffers(1, &(ch->buffers[j].BufferID));
578 				ch->buffers[j].Status = UNQUEUED;
579 				ch->buffers[j].Data = (char *)Z_Malloc(STREAMING_BUFFER_SIZE, TAG_SND_RAWDATA, qfalse);
580 			}
581 		}
582 
583 		// clear out the lip synching override array
584 		memset(s_entityWavVol, 0, sizeof(s_entityWavVol));
585 
586 		// These aren't really relevant for Open AL, but for completeness ...
587 		dma.speed = 22050;
588 		dma.channels = 2;
589 		dma.samplebits = 16;
590 		dma.samples = 0;
591 		dma.submission_chunk = 0;
592 		dma.buffer = NULL;
593 
594 		// Clamp sound volumes between 0.0f and 1.0f (just in case they aren't already)
595 		if (s_volume->value < 0.f)
596 			s_volume->value = 0.f;
597 		if (s_volume->value > 1.f)
598 			s_volume->value = 1.f;
599 
600 		if (s_volumeVoice->value < 0.f)
601 			s_volumeVoice->value = 0.f;
602 		if (s_volumeVoice->value > 1.f)
603 			s_volumeVoice->value = 1.f;
604 
605 		if (s_musicVolume->value < 0.f)
606 			s_musicVolume->value = 0.f;
607 		if (s_musicVolume->value > 1.f)
608 			s_musicVolume->value = 1.f;
609 
610 		// s_init could be called in game, if so there may be an .eal file
611 		// for this level
612 
613 		const char *mapname = Cvar_VariableString( "mapname" );
614 		EALFileInit(mapname);
615 
616 	}
617 	else
618 	{
619 #endif
620 		r = SNDDMA_Init(s_khz->integer);
621 
622 		if ( r ) {
623 			s_soundStarted = 1;
624 			s_soundMuted = qtrue;
625 	//		s_numSfx = 0;	// do NOT do this here now!!!
626 
627 			s_soundtime = 0;
628 			s_paintedtime = 0;
629 
630 			S_StopAllSounds ();
631 
632 			S_SoundInfo_f();
633 		}
634 #ifdef USE_OPENAL
635 	}
636 #endif
637 
638 	Com_Printf("------------------------------------\n");
639 
640 	Com_Printf("\n--- ambient sound initialization ---\n");
641 
642 	AS_Init();
643 }
644 
645 // only called from snd_restart. QA request...
646 //
S_ReloadAllUsedSounds(void)647 void S_ReloadAllUsedSounds(void)
648 {
649 	if (s_soundStarted && !s_soundMuted )
650 	{
651 		// new bit, reload all soundsthat are used on the current level...
652 		//
653 		for (int i=1 ; i < s_numSfx ; i++)	// start @ 1 to skip freeing default sound
654 		{
655 			sfx_t *sfx = &s_knownSfx[i];
656 
657 			if (!sfx->bInMemory && !sfx->bDefaultSound && sfx->iLastLevelUsedOn == re->RegisterMedia_GetLevel()){
658 				S_memoryLoad(sfx);
659 			}
660 		}
661 	}
662 }
663 
664 // =======================================================================
665 // Shutdown sound engine
666 // =======================================================================
667 
S_Shutdown(void)668 void S_Shutdown( void )
669 {
670 	if ( !s_soundStarted ) {
671 		return;
672 	}
673 
674 	S_FreeAllSFXMem();
675 	S_UnCacheDynamicMusic();
676 
677 #ifdef USE_OPENAL
678 	if (s_UseOpenAL)
679 	{
680 		int i, j;
681 		// Release all the AL Sources (including Music channel (Source 0))
682 		for (i = 0; i < s_numChannels; i++)
683 		{
684 			alDeleteSources(1, &(s_channels[i].alSource));
685 		}
686 
687 		// Release Streaming AL Buffers
688 		channel_t *ch = s_channels + 1;
689 		for (i = 1; i < s_numChannels; i++, ch++)
690 		{
691 			for (j = 0; j < NUM_STREAMING_BUFFERS; j++)
692 			{
693 				alDeleteBuffers(1, &(ch->buffers[j].BufferID));
694 				ch->buffers[j].BufferID = 0;
695 				ch->buffers[j].Status = UNQUEUED;
696 				if (ch->buffers[j].Data)
697 				{
698 					Z_Free(ch->buffers[j].Data);
699 					ch->buffers[j].Data = NULL;
700 				}
701 			}
702 		}
703 
704 		// Get active context
705 		ALCcontext *ALCContext = alcGetCurrentContext();
706 		// Get device for active context
707 		ALCdevice *ALCDevice = alcGetContextsDevice(ALCContext);
708 		// Release context(s)
709 		alcDestroyContext(ALCContext);
710 		// Close device
711 		alcCloseDevice(ALCDevice);
712 
713 		ReleaseEAXManager();
714 
715 		s_numChannels = 0;
716 
717 	}
718 	else
719 	{
720 #endif
721 		SNDDMA_Shutdown();
722 #ifdef USE_OPENAL
723 	}
724 #endif
725 
726 	s_soundStarted = 0;
727 
728 	Cmd_RemoveCommand("play");
729 	Cmd_RemoveCommand("music");
730 	Cmd_RemoveCommand("stopmusic");
731 	Cmd_RemoveCommand("stopsound");
732 	Cmd_RemoveCommand("soundlist");
733 	Cmd_RemoveCommand("soundinfo");
734 	Cmd_RemoveCommand("soundstop");
735 	Cmd_RemoveCommand("mp3_calcvols");
736 	Cmd_RemoveCommand("s_dynamic");
737 	AS_Free();
738 }
739 
740 /*
741 	Mutes / Unmutes all OpenAL sound
742 */
743 #ifdef USE_OPENAL
S_AL_MuteAllSounds(qboolean bMute)744 void S_AL_MuteAllSounds(qboolean bMute)
745 {
746      if (!s_soundStarted)
747           return;
748 
749      if (!s_UseOpenAL)
750           return;
751 
752      if (bMute)
753      {
754           alListenerf(AL_GAIN, 0.0f);
755      }
756      else
757      {
758           alListenerf(AL_GAIN, 1.0f);
759      }
760 }
761 #endif
762 
763 // =======================================================================
764 // Load a sound
765 // =======================================================================
766 /*
767 ================
768 return a hash value for the sfx name
769 ================
770 */
S_HashSFXName(const char * name)771 static long S_HashSFXName(const char *name) {
772 	int		i;
773 	long	hash;
774 	char	letter;
775 
776 	hash = 0;
777 	i = 0;
778 	while (name[i] != '\0') {
779 		letter = tolower(name[i]);
780 		if (letter =='.') break;				// don't include extension
781 		if (letter =='\\') letter = '/';		// damn path names
782 		hash+=(long)(letter)*(i+119);
783 		i++;
784 	}
785 	hash &= (LOOP_HASH-1);
786 	return hash;
787 }
788 
789 /*
790 ==================
791 S_FindName
792 
793 Will allocate a new sfx if it isn't found
794 ==================
795 */
S_FindName(const char * name)796 sfx_t *S_FindName( const char *name ) {
797 	int		i;
798 	int		hash;
799 
800 	sfx_t	*sfx;
801 
802 	if (!name) {
803 		Com_Error (ERR_FATAL, "S_FindName: NULL");
804 	}
805 	if (!name[0]) {
806 		Com_Error (ERR_FATAL, "S_FindName: empty name");
807 	}
808 
809 	if (strlen(name) >= MAX_QPATH) {
810 		Com_Error (ERR_FATAL, "Sound name too long: %s", name);
811 	}
812 
813 	char sSoundNameNoExt[MAX_QPATH];
814 	COM_StripExtension(name,sSoundNameNoExt, sizeof( sSoundNameNoExt ));
815 
816 	hash = S_HashSFXName(sSoundNameNoExt);
817 
818 	sfx = sfxHash[hash];
819 	// see if already loaded
820 	while (sfx) {
821 		if (!Q_stricmp(sfx->sSoundName, sSoundNameNoExt) ) {
822 			return sfx;
823 		}
824 		sfx = sfx->next;
825 	}
826 /*
827 	// find a free sfx
828 	for (i=0 ; i < s_numSfx ; i++) {
829 		if (!s_knownSfx[i].soundName[0]) {
830 			break;
831 		}
832 	}
833 */
834 	i = s_numSfx;	//we don't clear the soundName after failed loads any more, so it'll always be the last entry
835 
836 	if (s_numSfx == MAX_SFX)
837 	{
838 		// ok, no sfx's free, but are there any with defaultSound set? (which the registering ent will never
839 		//	see because he gets zero returned if it's default...)
840 		//
841 		for (i=0 ; i < s_numSfx ; i++) {
842 			if (s_knownSfx[i].bDefaultSound) {
843 				break;
844 			}
845 		}
846 
847 		if (i==s_numSfx)
848 		{
849 			// genuinely out of handles...
850 
851 			// if we ever reach this, let me know and I'll either boost the array or put in a map-used-on
852 			//	reference to enable sfx_t recycling. TA codebase relies on being able to have structs for every sound
853 			//	used anywhere, ever, all at once (though audio bit-buffer gets recycled). SOF1 used about 1900 distinct
854 			//	events, so current MAX_SFX limit should do, or only need a small boost...	-ste
855 			//
856 
857 			Com_Error (ERR_FATAL, "S_FindName: out of sfx_t");
858 		}
859 	}
860 	else
861 	{
862 		s_numSfx++;
863 	}
864 
865 	sfx = &s_knownSfx[i];
866 	memset (sfx, 0, sizeof(*sfx));
867 	Q_strncpyz(sfx->sSoundName, sSoundNameNoExt, sizeof(sfx->sSoundName));
868 	Q_strlwr(sfx->sSoundName);//force it down low
869 
870 	sfx->next = sfxHash[hash];
871 	sfxHash[hash] = sfx;
872 
873 	return sfx;
874 }
875 
876 /*
877 =================
878 S_DefaultSound
879 =================
880 */
S_DefaultSound(sfx_t * sfx)881 void S_DefaultSound( sfx_t *sfx ) {
882 
883 	int		i;
884 
885 	sfx->iSoundLengthInSamples	= 512;								// #samples, ie shorts
886 	sfx->pSoundData				= (short *)	SND_malloc(512*2, sfx);	// ... so *2 for alloc bytes
887 	sfx->bInMemory				= qtrue;
888 
889 	for ( i=0 ; i < sfx->iSoundLengthInSamples ; i++ )
890 	{
891 		sfx->pSoundData[i] = i;
892 	}
893 }
894 
895 /*
896 ===================
897 S_DisableSounds
898 
899 Disables sounds until the next S_BeginRegistration.
900 This is called when the hunk is cleared and the sounds
901 are no longer valid.
902 ===================
903 */
S_DisableSounds(void)904 void S_DisableSounds( void ) {
905 	S_StopAllSounds();
906 	s_soundMuted = qtrue;
907 }
908 
909 /*
910 =====================
911 S_BeginRegistration
912 
913 =====================
914 */
S_BeginRegistration(void)915 void S_BeginRegistration( void )
916 {
917 	s_soundMuted = qfalse;		// we can play again
918 
919 #ifdef USE_OPENAL
920 	// Find name of level so we can load in the appropriate EAL file
921 	if (s_UseOpenAL)
922 	{
923 		const char *mapname = Cvar_VariableString( "mapname" );
924 		EALFileInit(mapname);
925 		// clear carry crap from previous map
926 		for (int i = 0; i < EAX_MAX_FXSLOTS; i++)
927 		{
928 			s_FXSlotInfo[i].lEnvID = -1;
929 		}
930 	}
931 #endif
932 
933 	if (s_numSfx == 0) {
934 		SND_setup();
935 
936 		s_numSfx = 0;
937 		memset( s_knownSfx, 0, sizeof( s_knownSfx ) );
938 		memset(sfxHash, 0, sizeof(sfx_t *)*LOOP_HASH);
939 
940 #ifdef _DEBUG
941 		sfx_t *sfx = S_FindName( "***DEFAULT***" );
942 		S_DefaultSound( sfx );
943 #else
944 		S_RegisterSound("sound/null.wav");
945 #endif
946 	}
947 }
948 
949 #ifdef USE_OPENAL
EALFileInit(const char * level)950 void EALFileInit(const char *level)
951 {
952 	// If an EAL File is already unloaded, remove it
953 	if (s_bEALFileLoaded)
954 	{
955 		UnloadEALFile();
956 	}
957 
958 	// Reset variables
959 	s_bInWater = false;
960 
961 	// Try and load an EAL file for the new level
962 	char		name[MAX_QPATH];
963 	char		szEALFilename[MAX_QPATH];
964 	COM_StripExtension(level, name, sizeof( name ));
965 	Com_sprintf(szEALFilename, MAX_QPATH, "eagle/%s.eal", name);
966 
967 	s_bEALFileLoaded = LoadEALFile(szEALFilename);
968 
969 	if (!s_bEALFileLoaded)
970 	{
971 		Com_sprintf(szEALFilename, MAX_QPATH, "base/eagle/%s.eal", name);
972 		s_bEALFileLoaded = LoadEALFile(szEALFilename);
973 	}
974 
975 	if (s_bEALFileLoaded)
976 	{
977 		s_lLastEnvUpdate = timeGetTime();
978 	}
979 	else
980 	{
981 		// Mute reverbs if no EAL file is found
982 		if ((s_bEAX)&&(s_eaxSet))
983 		{
984 			long lRoom = -10000;
985 			for (int i = 0; i < s_NumFXSlots; i++)
986 			{
987 				s_eaxSet(&s_FXSlotInfo[i].FXSlotGuid, EAXREVERB_ROOM, NULL,
988 					&lRoom, sizeof(long));
989 			}
990 		}
991 	}
992 }
993 #endif
994 
995 /*
996 ==================
997 S_RegisterSound
998 
999 Creates a default buzz sound if the file can't be loaded
1000 ==================
1001 */
S_RegisterSound(const char * name)1002 sfxHandle_t	S_RegisterSound( const char *name)
1003 {
1004 	sfx_t	*sfx;
1005 
1006 	if (!s_soundStarted) {
1007 		return 0;
1008 	}
1009 
1010 	if ( strlen( name ) >= MAX_QPATH ) {
1011 		Com_Printf( S_COLOR_RED"Sound name exceeds MAX_QPATH - %s\n", name );
1012 		return 0;
1013 	}
1014 
1015 	sfx = S_FindName( name );
1016 
1017 	SND_TouchSFX(sfx);
1018 
1019 	if ( sfx->bDefaultSound )
1020 		return 0;
1021 
1022 #ifdef USE_OPENAL
1023 	if (s_UseOpenAL)
1024 	{
1025 		if ((sfx->pSoundData) || (sfx->Buffer))
1026 			return sfx - s_knownSfx;
1027 	}
1028 	else
1029 #endif
1030 	{
1031 		if ( sfx->pSoundData )
1032 		{
1033 			return sfx - s_knownSfx;
1034 		}
1035 	}
1036 
1037 	sfx->bInMemory = qfalse;
1038 
1039 	S_memoryLoad(sfx);
1040 
1041 	if ( sfx->bDefaultSound ) {
1042 #ifndef FINAL_BUILD
1043 		if (!s_shutUp)
1044 		{
1045 			Com_Printf( S_COLOR_YELLOW "WARNING: could not find %s - using default\n", sfx->sSoundName );
1046 		}
1047 #endif
1048 		return 0;
1049 	}
1050 
1051 	return sfx - s_knownSfx;
1052 }
1053 
S_memoryLoad(sfx_t * sfx)1054 void S_memoryLoad(sfx_t	*sfx)
1055 {
1056 	// load the sound file...
1057 	//
1058 	if ( !S_LoadSound( sfx ) )
1059 	{
1060 //		Com_Printf( S_COLOR_YELLOW "WARNING: couldn't load sound: %s\n", sfx->sSoundName );
1061 		sfx->bDefaultSound = qtrue;
1062 	}
1063 	sfx->bInMemory = qtrue;
1064 }
1065 
1066 //=============================================================================
S_CheckChannelStomp(int chan1,int chan2)1067 static qboolean S_CheckChannelStomp( int chan1, int chan2 )
1068 {
1069 #ifdef USE_OPENAL
1070 	if (!s_UseOpenAL)
1071 #endif
1072 	{
1073 		if ( chan1 == chan2 )
1074 		{
1075 			return qtrue;
1076 		}
1077 	}
1078 
1079 	if ( ( chan1 == CHAN_VOICE || chan1 == CHAN_VOICE_ATTEN || chan1 == CHAN_VOICE_GLOBAL  ) && ( chan2 == CHAN_VOICE || chan2 == CHAN_VOICE_ATTEN || chan2 == CHAN_VOICE_GLOBAL ) )
1080 	{
1081 		return qtrue;
1082 	}
1083 
1084 	return qfalse;
1085 }
1086 
1087 /*
1088 =================
1089 S_PickChannel
1090 =================
1091 */
S_PickChannel(int entnum,int entchannel)1092 channel_t *S_PickChannel(int entnum, int entchannel)
1093 {
1094     int			ch_idx;
1095 	channel_t	*ch, *firstToDie;
1096 	qboolean	foundChan = qfalse;
1097 
1098 #ifdef USE_OPENAL
1099 	if (s_UseOpenAL)
1100 		return S_OpenALPickChannel(entnum, entchannel);
1101 #endif
1102 
1103 	if ( entchannel<0 ) {
1104 		Com_Error (ERR_DROP, "S_PickChannel: entchannel<0");
1105 	}
1106 
1107 	// Check for replacement sound, or find the best one to replace
1108 
1109     firstToDie = &s_channels[0];
1110 
1111 	for ( int pass = 0; (pass < ((entchannel == CHAN_AUTO || entchannel == CHAN_LESS_ATTEN)?1:2)) && !foundChan; pass++ )
1112 	{
1113 		for (ch_idx = 0, ch = &s_channels[0]; ch_idx < MAX_CHANNELS ; ch_idx++, ch++ )
1114 		{
1115 			if ( entchannel == CHAN_AUTO || entchannel == CHAN_LESS_ATTEN || pass > 0 )
1116 			{//if we're on the second pass, just find the first open chan
1117 				if ( !ch->thesfx )
1118 				{//grab the first open channel
1119 					firstToDie = ch;
1120 					break;
1121 				}
1122 
1123 			}
1124 			else if ( ch->entnum == entnum && S_CheckChannelStomp( ch->entchannel, entchannel ) )
1125 			{
1126 				// always override sound from same entity
1127 				if ( s_show->integer == 1 && ch->thesfx ) {
1128 					Com_Printf( S_COLOR_YELLOW"...overrides %s\n", ch->thesfx->sSoundName );
1129 					ch->thesfx = 0;	//just to clear the next error msg
1130 				}
1131 				firstToDie = ch;
1132 				foundChan = qtrue;
1133 				break;
1134 			}
1135 
1136 			// don't let anything else override local player sounds
1137 			if ( ch->entnum == listener_number 	&& entnum != listener_number && ch->thesfx) {
1138 				continue;
1139 			}
1140 
1141 			// don't override loop sounds
1142 			if ( ch->loopSound ) {
1143 				continue;
1144 			}
1145 
1146 			if ( ch->startSample < firstToDie->startSample ) {
1147 				firstToDie = ch;
1148 			}
1149 		}
1150 	}
1151 
1152 	if ( s_show->integer == 1 && firstToDie->thesfx ) {
1153 		Com_Printf( S_COLOR_RED"***kicking %s\n", firstToDie->thesfx->sSoundName );
1154 	}
1155 
1156 	Channel_Clear(firstToDie);	// memset(firstToDie, 0, sizeof(*firstToDie));
1157 
1158 	return firstToDie;
1159 }
1160 
1161 /*
1162 	For use with Open AL
1163 
1164 	Allows more than one sound of the same type to emanate from the same entity - sounds much better
1165 	on hardware this way esp. rapid fire modes of weapons!
1166 */
1167 #ifdef USE_OPENAL
S_OpenALPickChannel(int entnum,int entchannel)1168 channel_t *S_OpenALPickChannel(int entnum, int entchannel)
1169 {
1170     int			ch_idx;
1171 	channel_t	*ch, *ch_firstToDie;
1172 	bool	foundChan = false;
1173 	float	source_pos[3];
1174 
1175 	if ( entchannel < 0 )
1176 	{
1177 		Com_Error (ERR_DROP, "S_PickChannel: entchannel<0");
1178 	}
1179 
1180 	// Check for replacement sound, or find the best one to replace
1181 
1182     ch_firstToDie = s_channels + 1;	// channel 0 is reserved for Music
1183 
1184 	for (ch_idx = 1, ch = s_channels + ch_idx; ch_idx < s_numChannels; ch_idx++, ch++)
1185 	{
1186 		if ( ch->entnum == entnum && S_CheckChannelStomp( ch->entchannel, entchannel ) )
1187 		{
1188 			// always override sound from same entity
1189 			if ( s_show->integer == 1 && ch->thesfx ) {
1190 				Com_Printf( S_COLOR_YELLOW"...overrides %s\n", ch->thesfx->sSoundName );
1191 				ch->thesfx = 0;	//just to clear the next error msg
1192 			}
1193 			ch_firstToDie = ch;
1194 			foundChan = true;
1195 			break;
1196 		}
1197 	}
1198 
1199 	if (!foundChan)
1200 	for (ch_idx = 1, ch = s_channels + ch_idx; ch_idx < s_numChannels; ch_idx++, ch++)
1201 	{
1202 		// See if the channel is free
1203 		if (!ch->thesfx)
1204 		{
1205 			ch_firstToDie = ch;
1206 			foundChan = true;
1207 			break;
1208 		}
1209 	}
1210 
1211 	if (!foundChan)
1212 	{
1213 		for (ch_idx = 1, ch = s_channels + ch_idx; ch_idx < s_numChannels; ch_idx++, ch++)
1214 		{
1215 			if (	(ch->entnum == entnum) && (ch->entchannel == entchannel) && (ch->entchannel != CHAN_AMBIENT)
1216 				 && (ch->entnum != listener_number) )
1217 			{
1218 				// Same entity and same type of sound effect (entchannel)
1219 				ch_firstToDie = ch;
1220 				foundChan = true;
1221 				break;
1222 			}
1223 		}
1224 	}
1225 
1226 	int longestDist;
1227 	int dist;
1228 
1229 	if (!foundChan)
1230 	{
1231 		// Find sound effect furthest from listener
1232 		ch = s_channels + 1;
1233 
1234 		if (ch->fixed_origin)
1235 		{
1236 			// Convert to Open AL co-ordinates
1237 			source_pos[0] = ch->origin[0];
1238 			source_pos[1] = ch->origin[2];
1239 			source_pos[2] = -ch->origin[1];
1240 
1241 			longestDist = ((listener_pos[0] - source_pos[0]) * (listener_pos[0] - source_pos[0])) +
1242 						  ((listener_pos[1] - source_pos[1]) * (listener_pos[1] - source_pos[1])) +
1243 						  ((listener_pos[2] - source_pos[2]) * (listener_pos[2] - source_pos[2]));
1244 		}
1245 		else
1246 		{
1247 			if (ch->entnum == listener_number)
1248 				longestDist = 0;
1249 			else
1250 			{
1251 				if (ch->bLooping)
1252 				{
1253 					// Convert to Open AL co-ordinates
1254 					source_pos[0] = loopSounds[ch->entnum].origin[0];
1255 					source_pos[1] = loopSounds[ch->entnum].origin[2];
1256 					source_pos[2] = -loopSounds[ch->entnum].origin[1];
1257 				}
1258 				else
1259 				{
1260 					// Convert to Open AL co-ordinates
1261 					source_pos[0] = s_entityPosition[ch->entnum][0];
1262 					source_pos[1] = s_entityPosition[ch->entnum][2];
1263 					source_pos[2] = -s_entityPosition[ch->entnum][1];
1264 				}
1265 
1266 				longestDist = ((listener_pos[0] - source_pos[0]) * (listener_pos[0] - source_pos[0])) +
1267 							  ((listener_pos[1] - source_pos[1]) * (listener_pos[1] - source_pos[1])) +
1268 							  ((listener_pos[2] - source_pos[2]) * (listener_pos[2] - source_pos[2]));
1269 			}
1270 		}
1271 
1272 		for (ch_idx = 2, ch = s_channels + ch_idx; ch_idx < s_numChannels; ch_idx++, ch++)
1273 		{
1274 			if (ch->fixed_origin)
1275 			{
1276 				// Convert to Open AL co-ordinates
1277 				source_pos[0] = ch->origin[0];
1278 				source_pos[1] = ch->origin[2];
1279 				source_pos[2] = -ch->origin[1];
1280 
1281 				dist = ((listener_pos[0] - source_pos[0]) * (listener_pos[0] - source_pos[0])) +
1282 					   ((listener_pos[1] - source_pos[1]) * (listener_pos[1] - source_pos[1])) +
1283 					   ((listener_pos[2] - source_pos[2]) * (listener_pos[2] - source_pos[2]));
1284 			}
1285 			else
1286 			{
1287 				if (ch->entnum == listener_number)
1288 					dist = 0;
1289 				else
1290 				{
1291 					if (ch->bLooping)
1292 					{
1293 						// Convert to Open AL co-ordinates
1294 						source_pos[0] = loopSounds[ch->entnum].origin[0];
1295 						source_pos[1] = loopSounds[ch->entnum].origin[2];
1296 						source_pos[2] = -loopSounds[ch->entnum].origin[1];
1297 					}
1298 					else
1299 					{
1300 						// Convert to Open AL co-ordinates
1301 						source_pos[0] = s_entityPosition[ch->entnum][0];
1302 						source_pos[1] = s_entityPosition[ch->entnum][2];
1303 						source_pos[2] = -s_entityPosition[ch->entnum][1];
1304 					}
1305 
1306 					dist = ((listener_pos[0] - source_pos[0]) * (listener_pos[0] - source_pos[0])) +
1307 					       ((listener_pos[1] - source_pos[1]) * (listener_pos[1] - source_pos[1])) +
1308 						   ((listener_pos[2] - source_pos[2]) * (listener_pos[2] - source_pos[2]));
1309 				}
1310 			}
1311 
1312 			if (dist > longestDist)
1313 			{
1314 				longestDist = dist;
1315 				ch_firstToDie = ch;
1316 			}
1317 		}
1318 	}
1319 
1320 	if (ch_firstToDie->bPlaying)
1321 	{
1322 		if (s_show->integer == 1  && ch_firstToDie->thesfx )
1323 		{
1324 			Com_Printf(S_COLOR_RED"***kicking %s\n", ch_firstToDie->thesfx->sSoundName );
1325 		}
1326 
1327 		// Stop sound
1328 		alSourceStop(ch_firstToDie->alSource);
1329 		ch_firstToDie->bPlaying = false;
1330 	}
1331 
1332 	// Reset channel variables
1333 	memset(&ch_firstToDie->MP3StreamHeader, 0, sizeof(MP3STREAM));
1334 	ch_firstToDie->bLooping = false;
1335 	ch_firstToDie->bProcessed = false;
1336 	ch_firstToDie->bStreaming = false;
1337 
1338     return ch_firstToDie;
1339 }
1340 #endif
1341 
1342 /*
1343 =================
1344 S_SpatializeOrigin
1345 
1346 Used for spatializing s_channels
1347 =================
1348 */
S_SpatializeOrigin(const vec3_t origin,float master_vol,int * left_vol,int * right_vol,int channel)1349 void S_SpatializeOrigin (const vec3_t origin, float master_vol, int *left_vol, int *right_vol, int channel)
1350 {
1351     float		dot;
1352     float		dist;
1353     float		lscale, rscale, scale;
1354     vec3_t		source_vec;
1355 	float		dist_mult = SOUND_ATTENUATE;
1356 
1357 	// calculate stereo seperation and distance attenuation
1358 	VectorSubtract(origin, listener_origin, source_vec);
1359 
1360 	dist = VectorNormalize(source_vec);
1361 	if ( channel == CHAN_VOICE )
1362 	{
1363 		dist -= SOUND_FULLVOLUME * 3.0f;
1364 //		dist_mult = VOICE_ATTENUATE;	// tweak added (this fixes an NPC dialogue "in your ears" bug, but we're not sure if it'll make a bunch of others fade too early. Too close to shipping...)
1365 	}
1366 	else if ( channel == CHAN_LESS_ATTEN )
1367 	{
1368 		dist -= SOUND_FULLVOLUME * 8.0f; // maybe is too large
1369 	}
1370 	else if ( channel == CHAN_VOICE_ATTEN )
1371 	{
1372 		dist -= SOUND_FULLVOLUME * 1.35f; // used to be 0.15f, dropped off too sharply - dmv
1373 		dist_mult = VOICE_ATTENUATE;
1374 	}
1375 	else if ( channel == CHAN_VOICE_GLOBAL )
1376 	{
1377 		dist = -1;
1378 	}
1379 	else	// use normal attenuation.
1380 	{
1381 		dist -= SOUND_FULLVOLUME;
1382 	}
1383 
1384 	if (dist < 0)
1385 	{
1386 		dist = 0;			// close enough to be at full volume
1387 	}
1388 	dist *= dist_mult;		// different attenuation levels
1389 
1390 	dot = -DotProduct(listener_axis[1], source_vec);
1391 
1392 	if (dma.channels == 1)	// || !dist_mult)
1393 	{ // no attenuation = no spatialization
1394 		rscale = SOUND_FMAXVOL;
1395 		lscale = SOUND_FMAXVOL;
1396 	}
1397 	else
1398 	{
1399 		//rscale = 0.5 * (1.0 + dot);
1400 		//lscale = 0.5 * (1.0 - dot);
1401 		rscale = s_separation->value + ( 1.0f - s_separation->value ) * dot;
1402 		lscale = s_separation->value - ( 1.0f - s_separation->value ) * dot;
1403 		if ( rscale < 0 )
1404 		{
1405 			rscale = 0;
1406 		}
1407 		if ( lscale < 0 )
1408 		{
1409 			lscale = 0;
1410 		}
1411 	}
1412 
1413 	// add in distance effect
1414 	scale = (1.0f - dist) * rscale;
1415 	*right_vol = (int) (master_vol * scale);
1416 	if (*right_vol < 0)
1417 	{
1418 		*right_vol = 0;
1419 	}
1420 
1421 	scale = (1.0f - dist) * lscale;
1422 	*left_vol = (int) (master_vol * scale);
1423 	if (*left_vol < 0)
1424 	{
1425 		*left_vol = 0;
1426 	}
1427 }
1428 
1429 // =======================================================================
1430 // Start a sound effect
1431 // =======================================================================
1432 
1433 /*
1434 ====================
1435 S_StartAmbientSound
1436 
1437 Starts an ambient, 'one-shot" sound.
1438 ====================
1439 */
1440 
S_StartAmbientSound(const vec3_t origin,int entityNum,unsigned char volume,sfxHandle_t sfxHandle)1441 void S_StartAmbientSound( const vec3_t origin, int entityNum, unsigned char volume, sfxHandle_t sfxHandle )
1442 {
1443 	channel_t	*ch;
1444 	/*const*/ sfx_t *sfx;
1445 
1446 	if ( !s_soundStarted || s_soundMuted ) {
1447 		return;
1448 	}
1449 	if ( !origin && ( entityNum < 0 || entityNum >= MAX_GENTITIES ) )
1450 		Com_Error( ERR_DROP, "S_StartAmbientSound: bad entitynum %i", entityNum );
1451 
1452 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx )
1453 		Com_Error( ERR_DROP, "S_StartAmbientSound: handle %i out of range", sfxHandle );
1454 
1455 	sfx = &s_knownSfx[ sfxHandle ];
1456 	if (sfx->bInMemory == qfalse){
1457 		S_memoryLoad(sfx);
1458 	}
1459 	SND_TouchSFX(sfx);
1460 
1461 #ifdef USE_OPENAL
1462 	if (s_UseOpenAL)
1463 	{
1464 		if (volume==0)
1465 			return;
1466 	}
1467 #endif
1468 
1469 	if ( s_show->integer == 1 ) {
1470 		Com_Printf( "%i : %s on (%d) Ambient\n", s_paintedtime, sfx->sSoundName, entityNum );
1471 	}
1472 
1473 	// pick a channel to play on
1474 	ch = S_PickChannel( entityNum, CHAN_AMBIENT );
1475 	if (!ch) {
1476 		return;
1477 	}
1478 
1479 	if (origin)
1480 	{
1481 		VectorCopy (origin, ch->origin);
1482 		ch->fixed_origin = qtrue;
1483 	}
1484 	else
1485 	{
1486 		ch->fixed_origin = qfalse;
1487 	}
1488 
1489 	ch->master_vol = volume;
1490 	ch->entnum = entityNum;
1491 	ch->entchannel = CHAN_AMBIENT;
1492 	ch->thesfx = sfx;
1493 	ch->startSample = START_SAMPLE_IMMEDIATE;
1494 
1495 	ch->leftvol = ch->master_vol;		// these will get calced at next spatialize
1496 	ch->rightvol = ch->master_vol;		// unless the game isn't running
1497 
1498 	if (sfx->pMP3StreamHeader)
1499 	{
1500 		memcpy(&ch->MP3StreamHeader,sfx->pMP3StreamHeader,	sizeof(ch->MP3StreamHeader));
1501 		//ch->iMP3SlidingDecodeWritePos = 0; // These will be zero from the memset in S_PickChannel(), but keep them here for reference...
1502 		//ch->iMP3SlidingDecodeWindowPos= 0; //
1503 	}
1504 	else
1505 	{
1506 		memset(&ch->MP3StreamHeader,0,						sizeof(ch->MP3StreamHeader));
1507 	}
1508 }
1509 
1510 /*
1511 ====================
1512 S_MuteSound
1513 
1514 Mutes sound on specified channel for specified entity.
1515 ====================
1516 */
S_MuteSound(int entityNum,int entchannel)1517 void S_MuteSound(int entityNum, int entchannel)
1518 {
1519 	//I guess this works.
1520 	channel_t *ch = S_PickChannel( entityNum, entchannel );
1521 
1522 	if (!ch)
1523 	{
1524 		return;
1525 	}
1526 
1527 	ch->master_vol = 0;
1528 	ch->entnum = 0;
1529 	ch->entchannel = 0;
1530 	ch->thesfx = 0;
1531 	ch->startSample = 0;
1532 
1533 	ch->leftvol = 0;
1534 	ch->rightvol = 0;
1535 }
1536 
1537 /*
1538 ====================
1539 S_StartSound
1540 
1541 Validates the parms and ques the sound up
1542 if pos is NULL, the sound will be dynamically sourced from the entity
1543 entchannel 0 will never override a playing sound
1544 ====================
1545 */
S_StartSound(const vec3_t origin,int entityNum,int entchannel,sfxHandle_t sfxHandle)1546 void S_StartSound(const vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfxHandle )
1547 {
1548 	channel_t	*ch;
1549 	/*const*/ sfx_t *sfx;
1550 
1551 	if ( !s_soundStarted || s_soundMuted ) {
1552 		return;
1553 	}
1554 
1555 	if ( !origin && ( entityNum < 0 || entityNum >= MAX_GENTITIES ) ) {
1556 		Com_Error( ERR_DROP, "S_StartSound: bad entitynum %i", entityNum );
1557 	}
1558 
1559 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
1560 		Com_Error( ERR_DROP, "S_StartSound: handle %i out of range", sfxHandle );
1561 	}
1562 
1563 	sfx = &s_knownSfx[ sfxHandle ];
1564 	if (sfx->bInMemory == qfalse){
1565 		S_memoryLoad(sfx);
1566 	}
1567 	SND_TouchSFX(sfx);
1568 
1569 	if ( s_show->integer == 1 ) {
1570 		Com_Printf( "%i : %s on (%d)\n", s_paintedtime, sfx->sSoundName, entityNum );
1571 	}
1572 
1573 #ifdef USE_OPENAL
1574 	if (s_UseOpenAL)
1575 	{
1576 		int i;
1577 		if (entchannel == CHAN_WEAPON)
1578 		{
1579 			// Check if we are playing a 'charging' sound, if so, stop it now ..
1580 			ch = s_channels + 1;
1581 			for (i = 1; i < s_numChannels; i++, ch++)
1582 			{
1583 				if ((ch->entnum == entityNum) && (ch->entchannel == CHAN_WEAPON) && (ch->thesfx) && (strstr(ch->thesfx->sSoundName, "altcharge") != NULL))
1584 				{
1585 					// Stop this sound
1586 					alSourceStop(ch->alSource);
1587 					alSourcei(ch->alSource, AL_BUFFER, NULL);
1588 					ch->bPlaying = false;
1589 					ch->thesfx = NULL;
1590 					break;
1591 				}
1592 			}
1593 		}
1594 		else
1595 		{
1596 			ch = s_channels + 1;
1597 			for (i = 1; i < s_numChannels; i++, ch++)
1598 			{
1599 				if ((ch->entnum == entityNum) && (ch->thesfx) && (strstr(ch->thesfx->sSoundName, "falling") != NULL))
1600 				{
1601 					// Stop this sound
1602 					alSourceStop(ch->alSource);
1603 					alSourcei(ch->alSource, AL_BUFFER, NULL);
1604 					ch->bPlaying = false;
1605 					ch->thesfx = NULL;
1606 					break;
1607 				}
1608 			}
1609 		}
1610 	}
1611 #endif
1612 
1613 	// pick a channel to play on
1614 
1615 	ch = S_PickChannel( entityNum, entchannel );
1616 	if (!ch) {
1617 		return;
1618 	}
1619 
1620 	if (origin) {
1621 		VectorCopy (origin, ch->origin);
1622 		ch->fixed_origin = qtrue;
1623 	} else {
1624 		ch->fixed_origin = qfalse;
1625 	}
1626 
1627 	ch->master_vol = SOUND_MAXVOL;	//FIXME: Um.. control?
1628 	ch->entnum = entityNum;
1629 	ch->entchannel = entchannel;
1630 	ch->thesfx = sfx;
1631 	ch->startSample = START_SAMPLE_IMMEDIATE;
1632 
1633 	ch->leftvol = ch->master_vol;		// these will get calced at next spatialize
1634 	ch->rightvol = ch->master_vol;		// unless the game isn't running
1635 
1636 	if (entchannel < CHAN_AMBIENT && entityNum == listener_number) {	//only do it for body sounds not local sounds
1637 		ch->master_vol = SOUND_MAXVOL * SOUND_FMAXVOL;	//this won't be attenuated so let it scale down
1638 	}
1639 	if ( entchannel == CHAN_VOICE || entchannel == CHAN_VOICE_ATTEN || entchannel == CHAN_VOICE_GLOBAL )
1640 	{
1641 		s_entityWavVol[ ch->entnum ] = -1;	//we've started the sound but it's silent for now
1642 	}
1643 
1644 	if (sfx->pMP3StreamHeader)
1645 	{
1646 		memcpy(&ch->MP3StreamHeader,sfx->pMP3StreamHeader,	sizeof(ch->MP3StreamHeader));
1647 		//ch->iMP3SlidingDecodeWritePos = 0; // These will be zero from the memset in S_PickChannel(), but keep them here for reference...
1648 		//ch->iMP3SlidingDecodeWindowPos= 0; //
1649 	}
1650 	else
1651 	{
1652 		memset(&ch->MP3StreamHeader,0,						sizeof(ch->MP3StreamHeader));
1653 	}
1654 }
1655 
1656 /*
1657 ==================
1658 S_StartLocalSound
1659 ==================
1660 */
S_StartLocalSound(sfxHandle_t sfxHandle,int channelNum)1661 void S_StartLocalSound( sfxHandle_t sfxHandle, int channelNum ) {
1662 	if ( !s_soundStarted || s_soundMuted ) {
1663 		return;
1664 	}
1665 
1666 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
1667 		Com_Error( ERR_DROP, "S_StartLocalSound: handle %i out of range", sfxHandle );
1668 	}
1669 
1670 	S_StartSound (NULL, listener_number, channelNum, sfxHandle );
1671 }
1672 
1673 
1674 /*
1675 ==================
1676 S_StartLocalLoopingSound
1677 ==================
1678 */
S_StartLocalLoopingSound(sfxHandle_t sfxHandle)1679 void S_StartLocalLoopingSound( sfxHandle_t sfxHandle) {
1680 	vec3_t nullVec = {0,0,0};
1681 
1682 	if ( !s_soundStarted || s_soundMuted ) {
1683 		return;
1684 	}
1685 
1686 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
1687 		Com_Error( ERR_DROP, "S_StartLocalLoopingSound: handle %i out of range", sfxHandle );
1688 	}
1689 
1690 	S_AddLoopingSound( listener_number, nullVec, nullVec, sfxHandle );
1691 
1692 }
1693 
1694 // returns length in milliseconds of supplied sound effect...  (else 0 for bad handle now)
1695 //
S_GetSampleLengthInMilliSeconds(sfxHandle_t sfxHandle)1696 float S_GetSampleLengthInMilliSeconds( sfxHandle_t sfxHandle)
1697 {
1698 	sfx_t *sfx;
1699 
1700 	if (!s_soundStarted)
1701 	{	//we have no sound, so let's just make a reasonable guess
1702 		return 512 * 1000;
1703 	}
1704 
1705 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx )
1706 		return 0.0f;
1707 
1708 	sfx = &s_knownSfx[ sfxHandle ];
1709 
1710 	float f = (float)sfx->iSoundLengthInSamples / (float)dma.speed;
1711 
1712 	return (f * 1000);
1713 }
1714 
1715 
1716 /*
1717 ==================
1718 S_ClearSoundBuffer
1719 
1720 If we are about to perform file access, clear the buffer
1721 so sound doesn't stutter.
1722 ==================
1723 */
S_ClearSoundBuffer(void)1724 void S_ClearSoundBuffer( void ) {
1725 	int		clear;
1726 
1727 	if ( !s_soundStarted || s_soundMuted ) {
1728 		return;
1729 	}
1730 #if 0	//this causes scripts to freak when the sounds get cut...
1731 	// clear all the sounds so they don't
1732 	// start back up after the load finishes
1733 	memset( s_channels, 0, sizeof( s_channels ) );
1734 	// clear out the lip synching override array
1735 	memset(s_entityWavVol, 0,sizeof(s_entityWavVol));
1736 #endif
1737 	s_rawend = 0;
1738 
1739 #ifdef USE_OPENAL
1740 	if (!s_UseOpenAL)
1741 #endif
1742 	{
1743 		if (dma.samplebits == 8)
1744 			clear = 0x80;
1745 		else
1746 			clear = 0;
1747 
1748 		SNDDMA_BeginPainting ();
1749 		if (dma.buffer)
1750 			memset(dma.buffer, clear, dma.samples * dma.samplebits/8);
1751 		SNDDMA_Submit ();
1752 	}
1753 #ifdef USE_OPENAL
1754 	else
1755 	{
1756 		s_paintedtime = 0;
1757 		s_soundtime = 0;
1758 	}
1759 #endif
1760 }
1761 
1762 
1763 // kinda kludgy way to stop a special-use sfx_t playing...
1764 //
S_CIN_StopSound(sfxHandle_t sfxHandle)1765 void S_CIN_StopSound(sfxHandle_t sfxHandle)
1766 {
1767 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
1768 		Com_Error( ERR_DROP, "S_CIN_StopSound: handle %i out of range", sfxHandle );
1769 	}
1770 
1771 	sfx_t *sfx = &s_knownSfx[ sfxHandle ];
1772 	channel_t *ch = s_channels;
1773 	int i;
1774 
1775 	for ( i = 0; i < MAX_CHANNELS ; i++, ch++ )
1776 	{
1777 		if ( !ch->thesfx || (ch->leftvol<0.25 && ch->rightvol<0.25 )) {
1778 			continue;
1779 		}
1780 		if (ch->thesfx == sfx)
1781 		{
1782 #ifdef USE_OPENAL
1783 			if (s_UseOpenAL)
1784 			{
1785 				alSourceStop(s_channels[i].alSource);
1786 			}
1787 #endif
1788 			SND_FreeSFXMem(ch->thesfx);	// heh, may as well...
1789 			ch->thesfx = NULL;
1790 			memset(&ch->MP3StreamHeader, 0, sizeof(MP3STREAM));
1791 			ch->bLooping = false;
1792 			ch->bProcessed = false;
1793 			ch->bPlaying = false;
1794 			ch->bStreaming = false;
1795 			break;
1796 		}
1797 	}
1798 }
1799 
1800 
1801 /*
1802 ==================
1803 S_StopAllSounds
1804 ==================
1805 */
S_StopSounds(void)1806 void S_StopSounds(void)
1807 {
1808 
1809 	if ( !s_soundStarted ) {
1810 		return;
1811 	}
1812 
1813 	// stop looping sounds
1814 	S_ClearLoopingSounds();
1815 
1816 #ifdef USE_OPENAL
1817 	// clear all the s_channels
1818 	if (s_UseOpenAL)
1819 	{
1820 		int i; //, j;
1821 		channel_t *ch = s_channels;
1822 		for (i = 0; i < s_numChannels; i++, ch++)
1823 		{
1824 			alSourceStop(s_channels[i].alSource);
1825 			alSourcei(s_channels[i].alSource, AL_BUFFER, NULL);
1826 			ch->thesfx = NULL;
1827 			memset(&ch->MP3StreamHeader, 0, sizeof(MP3STREAM));
1828 			ch->bLooping = false;
1829 			ch->bProcessed = false;
1830 			ch->bPlaying = false;
1831 			ch->bStreaming = false;
1832 		}
1833 	}
1834 	else
1835 	{
1836 #endif
1837 		memset(s_channels, 0, sizeof(s_channels));
1838 #ifdef USE_OPENAL
1839 	}
1840 #endif
1841 
1842 	// clear out the lip synching override array
1843 	memset(s_entityWavVol, 0,sizeof(s_entityWavVol));
1844 
1845 	S_ClearSoundBuffer ();
1846 }
1847 
1848 /*
1849 ==================
1850 S_StopAllSounds
1851  and music
1852 ==================
1853 */
S_StopAllSounds(void)1854 void S_StopAllSounds(void) {
1855 	if ( !s_soundStarted ) {
1856 		return;
1857 	}
1858 	// stop the background music
1859 	S_StopBackgroundTrack();
1860 
1861 	S_StopSounds();
1862 }
1863 
1864 /*
1865 ==============================================================
1866 
1867 continuous looping sounds are added each frame
1868 
1869 ==============================================================
1870 */
1871 
1872 /*
1873 ==================
1874 S_ClearLoopingSounds
1875 
1876 ==================
1877 */
S_ClearLoopingSounds(void)1878 void S_ClearLoopingSounds( void )
1879 {
1880 #ifdef USE_OPENAL
1881 	if (s_UseOpenAL)
1882 	{
1883 		for (int i = 0; i < MAX_LOOP_SOUNDS; i++)
1884 			loopSounds[i].bProcessed = false;
1885 	}
1886 #endif
1887 	numLoopSounds = 0;
1888 }
1889 
1890 /*
1891 ==================
1892 S_StopLoopingSound
1893 
1894 Stops all active looping sounds on a specified entity.
1895 Sort of a slow method though, isn't there some better way?
1896 ==================
1897 */
S_StopLoopingSound(int entityNum)1898 void S_StopLoopingSound( int entityNum )
1899 {
1900 	int i = 0;
1901 
1902 	while (i < numLoopSounds)
1903 	{
1904 		if (loopSounds[i].entnum == entityNum)
1905 		{
1906 			int x = i+1;
1907 			while (x < numLoopSounds)
1908 			{
1909 				memcpy(&loopSounds[x-1], &loopSounds[x], sizeof(loopSounds[x]));
1910 				x++;
1911 			}
1912 			numLoopSounds--;
1913 		}
1914 		i++;
1915 	}
1916 }
1917 
1918 #define MAX_DOPPLER_SCALE 50.0f //arbitrary
1919 
1920 /*
1921 ==================
1922 S_AddLoopingSound
1923 
1924 Called during entity generation for a frame
1925 Include velocity in case I get around to doing doppler...
1926 ==================
1927 */
S_AddLoopingSound(int entityNum,const vec3_t origin,const vec3_t velocity,sfxHandle_t sfxHandle)1928 void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) {
1929 	/*const*/ sfx_t *sfx;
1930 
1931   	if ( !s_soundStarted || s_soundMuted ) {
1932 		return;
1933 	}
1934 	if ( numLoopSounds >= MAX_LOOP_SOUNDS ) {
1935 		return;
1936 	}
1937 
1938 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
1939 		Com_Error( ERR_DROP, "S_AddLoopingSound: handle %i out of range", sfxHandle );
1940 	}
1941 
1942 	sfx = &s_knownSfx[ sfxHandle ];
1943 	if (sfx->bInMemory == qfalse) {
1944 		S_memoryLoad(sfx);
1945 	}
1946 	SND_TouchSFX(sfx);
1947 
1948 	if ( !sfx->iSoundLengthInSamples ) {
1949 		Com_Error( ERR_DROP, "%s has length 0", sfx->sSoundName );
1950 	}
1951 	assert(!sfx->pMP3StreamHeader);
1952 	VectorCopy( origin, loopSounds[numLoopSounds].origin );
1953 	VectorCopy( velocity, loopSounds[numLoopSounds].velocity );
1954 	loopSounds[numLoopSounds].doppler = qfalse;
1955 	loopSounds[numLoopSounds].dopplerScale = 1.0;
1956 	loopSounds[numLoopSounds].sfx = sfx;
1957 	loopSounds[numLoopSounds].volume = SOUND_MAXVOL;
1958 	loopSounds[numLoopSounds].entnum = entityNum;
1959 
1960 	if ( s_doppler->integer && VectorLengthSquared(velocity) > 0.0 ) {
1961 		vec3_t	out;
1962 		float	lena, lenb;
1963 
1964 		loopSounds[numLoopSounds].doppler = qtrue;
1965 		lena = DistanceSquared(listener_origin, loopSounds[numLoopSounds].origin);
1966 		VectorAdd(loopSounds[numLoopSounds].origin, loopSounds[numLoopSounds].velocity, out);
1967 		lenb = DistanceSquared(listener_origin, out);
1968 
1969 		loopSounds[numLoopSounds].dopplerScale = lenb/(lena*100);
1970 		if (loopSounds[numLoopSounds].dopplerScale > MAX_DOPPLER_SCALE) {
1971 			loopSounds[numLoopSounds].dopplerScale = MAX_DOPPLER_SCALE;
1972 		} else if (loopSounds[numLoopSounds].dopplerScale <= 1.0) {
1973 			loopSounds[numLoopSounds].doppler = qfalse;			// don't bother doing the math
1974 		}
1975 	}
1976 
1977 	numLoopSounds++;
1978 }
1979 
1980 
1981 /*
1982 ==================
1983 S_AddAmbientLoopingSound
1984 ==================
1985 */
S_AddAmbientLoopingSound(const vec3_t origin,unsigned char volume,sfxHandle_t sfxHandle)1986 void S_AddAmbientLoopingSound( const vec3_t origin, unsigned char volume, sfxHandle_t sfxHandle )
1987 {
1988 	/*const*/ sfx_t *sfx;
1989 
1990 	if ( !s_soundStarted || s_soundMuted ) {
1991 		return;
1992 	}
1993 	if ( numLoopSounds >= MAX_LOOP_SOUNDS ) {
1994 		return;
1995 	}
1996 
1997 #ifdef USE_OPENAL
1998 	if (s_UseOpenAL)
1999 	{
2000 		if (volume == 0)
2001 			return;
2002 	}
2003 #endif
2004 
2005 	if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) {
2006 		Com_Error( ERR_DROP, "S_StartSound: handle %i out of range", sfxHandle );
2007 	}
2008 
2009 	sfx = &s_knownSfx[ sfxHandle ];
2010 	if (sfx->bInMemory == qfalse){
2011 		S_memoryLoad(sfx);
2012 	}
2013 	SND_TouchSFX(sfx);
2014 
2015 	if ( !sfx->iSoundLengthInSamples ) {
2016 		Com_Error( ERR_DROP, "%s has length 0", sfx->sSoundName );
2017 	}
2018 	VectorCopy( origin, loopSounds[numLoopSounds].origin );
2019 	loopSounds[numLoopSounds].doppler = qfalse;
2020 	loopSounds[numLoopSounds].dopplerScale = 1.0;
2021 	loopSounds[numLoopSounds].sfx = sfx;
2022 	assert(!sfx->pMP3StreamHeader);
2023 
2024 	//TODO: Calculate the distance falloff
2025 	loopSounds[numLoopSounds].volume = volume;
2026 	numLoopSounds++;
2027 }
2028 
2029 
2030 
2031 /*
2032 ==================
2033 S_AddLoopSounds
2034 
2035 Spatialize all of the looping sounds.
2036 All sounds are on the same cycle, so any duplicates can just
2037 sum up the channel multipliers.
2038 ==================
2039 */
S_AddLoopSounds(void)2040 void S_AddLoopSounds (void)
2041 {
2042 	int			i, j;
2043 	int			left, right, left_total, right_total;
2044 	channel_t	*ch;
2045 	loopSound_t	*loop, *loop2;
2046 	static int	loopFrame;
2047 
2048 	loopFrame++;
2049 	for ( i = 0 ; i < numLoopSounds ; i++) {
2050 		loop = &loopSounds[i];
2051 		if ( loop->mergeFrame == loopFrame ) {
2052 			continue;	// already merged into an earlier sound
2053 		}
2054 
2055 		// find the total contribution of all sounds of this type
2056 		left_total = right_total = 0;
2057 
2058 		for ( j = i ; j < numLoopSounds ; j++) {
2059 			loop2 = &loopSounds[j];
2060 			if ( loop2->sfx != loop->sfx ) {
2061 				continue;
2062 			}
2063 			loop2->mergeFrame = loopFrame;	// don't check this again later
2064 
2065 			S_SpatializeOrigin( loop2->origin, loop2->volume, &left, &right, CHAN_AUTO);	//FIXME: Allow for volume change!!
2066 
2067 			left_total += left;
2068 			right_total += right;
2069 		}
2070 
2071 		if (left_total == 0 && right_total == 0)
2072 			continue;		// not audible
2073 
2074 		// allocate a channel
2075 		ch = S_PickChannel(0, 0);
2076 		if (!ch)
2077 			return;
2078 
2079 		if (left_total > SOUND_MAXVOL)
2080 			left_total = SOUND_MAXVOL;
2081 		if (right_total > SOUND_MAXVOL)
2082 			right_total = SOUND_MAXVOL;
2083 		ch->leftvol = left_total;
2084 		ch->rightvol = right_total;
2085 		ch->loopSound = qtrue;	// remove next frame
2086 		ch->thesfx = loop->sfx;
2087 
2088 		ch->doppler = loop->doppler;
2089 		ch->dopplerScale = loop->dopplerScale;
2090 
2091 		// you cannot use MP3 files here because they offer only streaming access, not random
2092 		//
2093 		if (loop->sfx->pMP3StreamHeader)
2094 		{
2095 			Com_Error( ERR_DROP, "S_AddLoopSounds(): Cannot use streamed MP3 files here for random access (%s)\n",loop->sfx->sSoundName );
2096 		}
2097 		else
2098 		{
2099 			memset(&ch->MP3StreamHeader,0,						sizeof(ch->MP3StreamHeader));
2100 		}
2101 	}
2102 }
2103 
2104 //=============================================================================
2105 
2106 /*
2107 =================
2108 S_ByteSwapRawSamples
2109 
2110 If raw data has been loaded in little endian binary form, this must be done.
2111 If raw data was calculated, as with ADPCM, this should not be called.
2112 =================
2113 */
S_ByteSwapRawSamples(int samples,int width,int s_channels,const byte * data)2114 void S_ByteSwapRawSamples( int samples, int width, int s_channels, const byte *data ) {
2115 	int		i;
2116 
2117 	if ( width != 2 ) {
2118 		return;
2119 	}
2120 	if ( LittleShort( 256 ) == 256 ) {
2121 		return;
2122 	}
2123 
2124 	if ( s_channels == 2 ) {
2125 		samples <<= 1;
2126 	}
2127 	for ( i = 0 ; i < samples ; i++ ) {
2128 		((short *)data)[i] = LittleShort( ((short *)data)[i] );
2129 	}
2130 }
2131 
2132 
S_GetRawSamplePointer()2133 portable_samplepair_t *S_GetRawSamplePointer() {
2134 	return s_rawsamples;
2135 }
2136 
2137 
2138 /*
2139 ============
2140 S_RawSamples
2141 
2142 Music streaming
2143 ============
2144 */
S_RawSamples(int samples,int rate,int width,int channels,const byte * data,float volume,int bFirstOrOnlyUpdateThisFrame)2145 void S_RawSamples( int samples, int rate, int width, int channels, const byte *data, float volume, int bFirstOrOnlyUpdateThisFrame )
2146 {
2147 	int		i;
2148 	int		src, dst;
2149 	float	scale;
2150 	int		intVolume;
2151 
2152 	if ( !s_soundStarted || s_soundMuted ) {
2153 		return;
2154 	}
2155 
2156 	intVolume = 256 * volume;
2157 
2158 	if ( s_rawend < s_soundtime ) {
2159 		Com_DPrintf( "S_RawSamples: resetting minimum: %i < %i\n", s_rawend, s_soundtime );
2160 		s_rawend = s_soundtime;
2161 	}
2162 
2163 	scale = (float)rate / dma.speed;
2164 
2165 //Com_Printf ("%i < %i < %i\n", s_soundtime, s_paintedtime, s_rawend);
2166 	if (channels == 2 && width == 2)
2167 	{
2168 		if (scale == 1.0)
2169 		{	// optimized case
2170 			if (bFirstOrOnlyUpdateThisFrame)
2171 			{
2172 				for (i=0 ; i<samples ; i++)
2173 				{
2174 					dst = s_rawend&(MAX_RAW_SAMPLES-1);
2175 					s_rawend++;
2176 					s_rawsamples[dst].left = ((short *)data)[i*2] * intVolume;
2177 					s_rawsamples[dst].right = ((short *)data)[i*2+1] * intVolume;
2178 				}
2179 			}
2180 			else
2181 			{
2182 				for (i=0 ; i<samples ; i++)
2183 				{
2184 					dst = s_rawend&(MAX_RAW_SAMPLES-1);
2185 					s_rawend++;
2186 					s_rawsamples[dst].left  += ((short *)data)[i*2] * intVolume;
2187 					s_rawsamples[dst].right += ((short *)data)[i*2+1] * intVolume;
2188 				}
2189 			}
2190 		}
2191 		else
2192 		{
2193 			if (bFirstOrOnlyUpdateThisFrame)
2194 			{
2195 				for (i=0 ; ; i++)
2196 				{
2197 					src = i*scale;
2198 					if (src >= samples)
2199 						break;
2200 					dst = s_rawend&(MAX_RAW_SAMPLES-1);
2201 					s_rawend++;
2202 					s_rawsamples[dst].left = ((short *)data)[src*2] * intVolume;
2203 					s_rawsamples[dst].right = ((short *)data)[src*2+1] * intVolume;
2204 				}
2205 			}
2206 			else
2207 			{
2208 				for (i=0 ; ; i++)
2209 				{
2210 					src = i*scale;
2211 					if (src >= samples)
2212 						break;
2213 					dst = s_rawend&(MAX_RAW_SAMPLES-1);
2214 					s_rawend++;
2215 					s_rawsamples[dst].left  += ((short *)data)[src*2] * intVolume;
2216 					s_rawsamples[dst].right += ((short *)data)[src*2+1] * intVolume;
2217 				}
2218 			}
2219 		}
2220 	}
2221 	else if (channels == 1 && width == 2)
2222 	{
2223 		if (bFirstOrOnlyUpdateThisFrame)
2224 		{
2225 			for (i=0 ; ; i++)
2226 			{
2227 				src = i*scale;
2228 				if (src >= samples)
2229 					break;
2230 				dst = s_rawend&(MAX_RAW_SAMPLES-1);
2231 				s_rawend++;
2232 				s_rawsamples[dst].left = ((short *)data)[src] * intVolume;
2233 				s_rawsamples[dst].right = ((short *)data)[src] * intVolume;
2234 			}
2235 		}
2236 		else
2237 		{
2238 			for (i=0 ; ; i++)
2239 			{
2240 				src = i*scale;
2241 				if (src >= samples)
2242 					break;
2243 				dst = s_rawend&(MAX_RAW_SAMPLES-1);
2244 				s_rawend++;
2245 				s_rawsamples[dst].left  += ((short *)data)[src] * intVolume;
2246 				s_rawsamples[dst].right += ((short *)data)[src] * intVolume;
2247 			}
2248 		}
2249 	}
2250 	else if (channels == 2 && width == 1)
2251 	{
2252 		intVolume *= 256;
2253 
2254 		if (bFirstOrOnlyUpdateThisFrame)
2255 		{
2256 			for (i=0 ; ; i++)
2257 			{
2258 				src = i*scale;
2259 				if (src >= samples)
2260 					break;
2261 				dst = s_rawend&(MAX_RAW_SAMPLES-1);
2262 				s_rawend++;
2263 				s_rawsamples[dst].left = ((char *)data)[src*2] * intVolume;
2264 				s_rawsamples[dst].right = ((char *)data)[src*2+1] * intVolume;
2265 			}
2266 		}
2267 		else
2268 		{
2269 			for (i=0 ; ; i++)
2270 			{
2271 				src = i*scale;
2272 				if (src >= samples)
2273 					break;
2274 				dst = s_rawend&(MAX_RAW_SAMPLES-1);
2275 				s_rawend++;
2276 				s_rawsamples[dst].left  += ((char *)data)[src*2] * intVolume;
2277 				s_rawsamples[dst].right += ((char *)data)[src*2+1] * intVolume;
2278 			}
2279 		}
2280 	}
2281 	else if (channels == 1 && width == 1)
2282 	{
2283 		intVolume *= 256;
2284 
2285 		if (bFirstOrOnlyUpdateThisFrame)
2286 		{
2287 			for (i=0 ; ; i++)
2288 			{
2289 				src = i*scale;
2290 				if (src >= samples)
2291 					break;
2292 				dst = s_rawend&(MAX_RAW_SAMPLES-1);
2293 				s_rawend++;
2294 				s_rawsamples[dst].left = (((byte *)data)[src]-128) * intVolume;
2295 				s_rawsamples[dst].right = (((byte *)data)[src]-128) * intVolume;
2296 			}
2297 		}
2298 		else
2299 		{
2300 			for (i=0 ; ; i++)
2301 			{
2302 				src = i*scale;
2303 				if (src >= samples)
2304 					break;
2305 				dst = s_rawend&(MAX_RAW_SAMPLES-1);
2306 				s_rawend++;
2307 				s_rawsamples[dst].left  += (((byte *)data)[src]-128) * intVolume;
2308 				s_rawsamples[dst].right += (((byte *)data)[src]-128) * intVolume;
2309 			}
2310 		}
2311 	}
2312 
2313 	if ( s_rawend > s_soundtime + MAX_RAW_SAMPLES ) {
2314 		Com_DPrintf( "S_RawSamples: overflowed %i > %i\n", s_rawend, s_soundtime );
2315 	}
2316 }
2317 
2318 //=============================================================================
2319 
2320 /*
2321 =====================
2322 S_UpdateEntityPosition
2323 
2324 let the sound system know where an entity currently is
2325 ======================
2326 */
S_UpdateEntityPosition(int entityNum,const vec3_t origin)2327 void S_UpdateEntityPosition( int entityNum, const vec3_t origin )
2328 {
2329 	if ( entityNum < 0 || entityNum >= MAX_GENTITIES ) {
2330 		Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
2331 	}
2332 
2333 #ifdef USE_OPENAL
2334 	if (s_UseOpenAL)
2335 	{
2336 		if (entityNum == 0)
2337 			return;
2338 
2339 		int i;
2340 		channel_t *ch = s_channels + 1;
2341 		for (i = 1; i < s_numChannels; i++, ch++)
2342 		{
2343 			if ((s_channels[i].bPlaying) && (s_channels[i].entnum == entityNum) && (!s_channels[i].bLooping))
2344 			{
2345 				// Ignore position updates for CHAN_VOICE_GLOBAL
2346 				if (ch->entchannel != CHAN_VOICE_GLOBAL && ch->entchannel != CHAN_ANNOUNCER)
2347 				{
2348 					ALfloat pos[3];
2349 					pos[0] = origin[0];
2350 					pos[1] = origin[2];
2351 					pos[2] = -origin[1];
2352 					alSourcefv(s_channels[i].alSource, AL_POSITION, pos);
2353 
2354 					UpdateEAXBuffer(ch);
2355 				}
2356 
2357 /*				pos[0] = origin[0];
2358 				pos[1] = origin[2];
2359 				pos[2] = -origin[1];
2360 				alSourcefv(s_channels[i].alSource, AL_POSITION, pos);
2361 
2362 				if ((s_bEALFileLoaded) && !( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL ) )
2363 				{
2364 					UpdateEAXBuffer(ch);
2365 				}
2366 */
2367 			}
2368 		}
2369 	}
2370 #endif
2371 
2372 	VectorCopy( origin, s_entityPosition[entityNum] );
2373 }
2374 
2375 
2376 // Given a current wav we are playing, and our position within it, lets figure out its volume...
2377 //
2378 // (this is mostly Jake's code from EF1, which explains a lot...:-)
2379 //
2380 static int next_amplitude = 0;
S_CheckAmplitude(channel_t * ch,const unsigned int s_oldpaintedtime)2381 static int S_CheckAmplitude(channel_t	*ch, const unsigned int s_oldpaintedtime )
2382 {
2383 	// now, is this a cycle - or have we just started a new sample - where we should update the backup table, and write this value
2384 	// into the new table? or should we just take the value FROM the back up table and feed it out.
2385 	assert( ch->startSample != START_SAMPLE_IMMEDIATE );
2386 	if ( ch->startSample == s_oldpaintedtime || (next_amplitude < s_soundtime) )//(ch->startSample == START_SAMPLE_IMMEDIATE)//!s_entityWavVol_back[ch->entnum]
2387 	{
2388 		int	sample;
2389 		int	sample_total = 0;
2390 		int	count = 0;
2391 		short *current_pos_s;
2392 //		char *current_pos_c;
2393 		int	offset = 0;
2394 
2395 		// if we haven't started the sample yet, we must be at the beginning
2396 		current_pos_s = ((short*)ch->thesfx->pSoundData);
2397 //		current_pos_c = ((char*)ch->thesfx->data);
2398 
2399 		//if (ch->startSample != START_SAMPLE_IMMEDIATE)
2400 		//{
2401 			// figure out where we are in the sample right now.
2402 			offset = s_oldpaintedtime - ch->startSample;//s_paintedtime
2403 			current_pos_s += offset;
2404 //			current_pos_c += offset;
2405 		//}
2406 
2407 		// scan through 10 samples 100( at 11hz or 200 at 22hz) samples apart.
2408 		//
2409 		for (int i=0; i<10; i++)
2410 		{
2411 			//
2412 			// have we run off the end?
2413 			if ((offset + (i*100)) > ch->thesfx->iSoundLengthInSamples)
2414 			{
2415 				break;
2416 			}
2417 //			if (ch->thesfx->width == 1)
2418 //			{
2419 //				sample = current_pos_c[i*100];
2420 //			}
2421 //			else
2422 			{
2423 				switch (ch->thesfx->eSoundCompressionMethod)
2424 				{
2425 					case ct_16:
2426 					{
2427 						sample = current_pos_s[i*100];
2428 					}
2429 					break;
2430 
2431 					case ct_MP3:
2432 					{
2433 						const int iIndex = (i*100) + ((offset * /*ch->thesfx->width*/2) - ch->iMP3SlidingDecodeWindowPos);
2434 						const short* pwSamples = (short*) (ch->MP3SlidingDecodeBuffer + iIndex);
2435 
2436 						sample = *pwSamples;
2437 					}
2438 					break;
2439 
2440 					default:
2441 					{
2442 						assert(0);
2443 						sample = 0;
2444 					}
2445 					break;
2446 				}
2447 
2448 //				if (sample < 0)
2449 //					sample = -sample;
2450 				sample = sample>>8;
2451 			}
2452 			// square it for better accuracy
2453 			sample_total += (sample * sample);
2454 			count++;
2455 		}
2456 
2457 		// if we are already done with this sample, then its silence
2458 		if (!count)
2459 		{
2460 			return(0);
2461 		}
2462 		sample_total /= count;
2463 
2464 		// I hate doing this, but its the simplest way
2465 		if (sample_total < ch->thesfx->fVolRange * s_lip_threshold_1->value)
2466 		{
2467 		// tell the scripts that are relying on this that we are still going, but actually silent right now.
2468 			sample = -1;
2469 		}
2470 		else
2471 		if (sample_total < ch->thesfx->fVolRange * s_lip_threshold_2->value)
2472 		{
2473 			sample = 1;
2474 		}
2475 		else
2476 		if (sample_total < ch->thesfx->fVolRange * s_lip_threshold_3->value)
2477 		{
2478 			sample = 2;
2479 		}
2480 		else
2481 		if (sample_total < ch->thesfx->fVolRange * s_lip_threshold_4->value)
2482 		{
2483 			sample = 3;
2484 		}
2485 		else
2486 		{
2487 			sample = 4;
2488 		}
2489 
2490 //		Com_OPrintf("Returning sample %d\n",sample);
2491 
2492 		// store away the value we got into the back up table
2493 		s_entityWavVol_back[ ch->entnum ] = sample;
2494 		return (sample);
2495 	}
2496 	// no, just get last value calculated from backup table
2497 	assert( s_entityWavVol_back[ch->entnum] );
2498 	return (s_entityWavVol_back[ ch->entnum]);
2499 }
2500 /*
2501 ============
2502 S_Respatialize
2503 
2504 Change the volumes of all the playing sounds for changes in their positions
2505 ============
2506 */
S_Respatialize(int entityNum,const vec3_t head,matrix3_t axis,int inwater)2507 void S_Respatialize( int entityNum, const vec3_t head, matrix3_t axis, int inwater )
2508 {
2509 #ifdef USE_OPENAL
2510 	EAXOCCLUSIONPROPERTIES eaxOCProp;
2511 	EAXACTIVEFXSLOTS eaxActiveSlots;
2512 #endif
2513 	int			i;
2514 	channel_t	*ch;
2515 	vec3_t		origin;
2516 
2517 	if ( !s_soundStarted || s_soundMuted ) {
2518 		return;
2519 	}
2520 
2521 #ifdef USE_OPENAL
2522 	if (s_UseOpenAL)
2523 	{
2524 		listener_pos[0] = head[0];
2525 		listener_pos[1] = head[2];
2526 		listener_pos[2] = -head[1];
2527 		alListenerfv(AL_POSITION, listener_pos);
2528 
2529 		listener_ori[0] = axis[0][0];
2530 		listener_ori[1] = axis[0][2];
2531 		listener_ori[2] = -axis[0][1];
2532 		listener_ori[3] = axis[2][0];
2533 		listener_ori[4] = axis[2][2];
2534 		listener_ori[5] = -axis[2][1];
2535 		alListenerfv(AL_ORIENTATION, listener_ori);
2536 
2537 		// Update EAX effects here
2538 		if (s_bEALFileLoaded)
2539 		{
2540 			// Check if the Listener is underwater
2541 			if (inwater)
2542 			{
2543 				// Check if we have already applied Underwater effect
2544 				if (!s_bInWater)
2545 				{
2546 					// New underwater fix
2547 					for (i = 0; i < EAX_MAX_FXSLOTS; i++)
2548 					{
2549 						s_FXSlotInfo[i].lEnvID = -1;
2550 					}
2551 
2552 					// Load underwater reverb effect into FX Slot 0, and set this as the Primary FX Slot
2553 					unsigned int ulEnvironment = EAX_ENVIRONMENT_UNDERWATER;
2554 					s_eaxSet(&EAXPROPERTYID_EAX40_FXSlot0, EAXREVERB_ENVIRONMENT,
2555 						NULL, &ulEnvironment, sizeof(unsigned int));
2556 					s_EnvironmentID = 999;
2557 
2558 					s_eaxSet(&EAXPROPERTYID_EAX40_Context, EAXCONTEXT_PRIMARYFXSLOTID, NULL, (ALvoid*)&EAXPROPERTYID_EAX40_FXSlot0,
2559 						sizeof(GUID));
2560 
2561 					// Occlude all sounds into this environment, and mute all their sends to other reverbs
2562 					eaxOCProp.lOcclusion = -3000;
2563 					eaxOCProp.flOcclusionLFRatio = 0.0f;
2564 					eaxOCProp.flOcclusionRoomRatio = 1.37f;
2565 					eaxOCProp.flOcclusionDirectRatio = 1.0f;
2566 
2567 					eaxActiveSlots.guidActiveFXSlots[0] = EAX_NULL_GUID;
2568 					eaxActiveSlots.guidActiveFXSlots[1] = EAX_PrimaryFXSlotID;
2569 
2570 					ch = s_channels + 1;
2571 					for (i = 1; i < s_numChannels; i++, ch++)
2572 					{
2573 						// New underwater fix
2574 						s_channels[i].lSlotID = -1;
2575 
2576 						s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_OCCLUSIONPARAMETERS,
2577 							ch->alSource, &eaxOCProp, sizeof(EAXOCCLUSIONPROPERTIES));
2578 
2579 						s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_ACTIVEFXSLOTID, ch->alSource,
2580 							&eaxActiveSlots, 2*sizeof(GUID));
2581 					}
2582 
2583 					s_bInWater = true;
2584 				}
2585 			}
2586 			else
2587 			{
2588 				// Not underwater ... check if the underwater effect is still present
2589 				if (s_bInWater)
2590 				{
2591 					s_bInWater = false;
2592 
2593 					// Remove underwater Reverb effect, and reset Occlusion / Obstruction amount on all Sources
2594 					UpdateEAXListener();
2595 
2596 					ch = s_channels + 1;
2597 					for (i = 1; i < s_numChannels; i++, ch++)
2598 					{
2599 						UpdateEAXBuffer(ch);
2600 					}
2601 				}
2602 				else
2603 				{
2604 					UpdateEAXListener();
2605 				}
2606 			}
2607 		}
2608 	}
2609 	else
2610 	{
2611 #endif
2612 		listener_number = entityNum;
2613 		VectorCopy(head, listener_origin);
2614 		VectorCopy(axis[0], listener_axis[0]);
2615 		VectorCopy(axis[1], listener_axis[1]);
2616 		VectorCopy(axis[2], listener_axis[2]);
2617 
2618 		// update spatialization for dynamic sounds
2619 		ch = s_channels;
2620 		for ( i = 0 ; i < MAX_CHANNELS ; i++, ch++ ) {
2621 			if ( !ch->thesfx ) {
2622 				continue;
2623 			}
2624 			if ( ch->loopSound ) {	// loopSounds are regenerated fresh each frame
2625 				Channel_Clear(ch);	// memset (ch, 0, sizeof(*ch));
2626 				continue;
2627 			}
2628 
2629 			// anything coming from the view entity will always be full volume
2630 			if (ch->entnum == listener_number) {
2631 				ch->leftvol = ch->master_vol;
2632 				ch->rightvol = ch->master_vol;
2633 			} else {
2634 				if (ch->fixed_origin) {
2635 					VectorCopy( ch->origin, origin );
2636 				} else {
2637 					VectorCopy( s_entityPosition[ ch->entnum ], origin );
2638 				}
2639 
2640 				S_SpatializeOrigin (origin, (float)ch->master_vol, &ch->leftvol, &ch->rightvol, ch->entchannel);
2641 			}
2642 
2643 			//NOTE: Made it so that voice sounds keep playing, even out of range
2644 			//		so that tasks waiting for sound completion keep proper timing
2645 			if ( !( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL ) && !ch->leftvol && !ch->rightvol ) {
2646 				Channel_Clear(ch);	// memset (ch, 0, sizeof(*ch));
2647 				continue;
2648 			}
2649 		}
2650 
2651 		// add loopsounds
2652 		S_AddLoopSounds ();
2653 #ifdef USE_OPENAL
2654 	}
2655 #endif
2656 
2657 	return;
2658 }
2659 
2660 
2661 /*
2662 ========================
2663 S_ScanChannelStarts
2664 
2665 Returns qtrue if any new sounds were started since the last mix
2666 ========================
2667 */
S_ScanChannelStarts(void)2668 qboolean S_ScanChannelStarts( void ) {
2669 	channel_t		*ch;
2670 	int				i;
2671 	qboolean		newSamples;
2672 
2673 	newSamples = qfalse;
2674 	ch = s_channels;
2675 	for (i=0; i<MAX_CHANNELS ; i++, ch++) {
2676 		if ( !ch->thesfx ) {
2677 			continue;
2678 		}
2679 		if ( ch->loopSound ) {
2680 			continue;
2681 		}
2682 
2683 		// if this channel was just started this frame,
2684 		// set the sample count to it begins mixing
2685 		// into the very first sample
2686 		if ( ch->startSample == START_SAMPLE_IMMEDIATE ) {
2687 			ch->startSample = s_paintedtime;
2688 			newSamples = qtrue;
2689 			continue;
2690 		}
2691 
2692 		// if it is completely finished by now, clear it
2693 		if ( (int)(ch->startSample + ch->thesfx->iSoundLengthInSamples) <= s_paintedtime ) {
2694 			ch->thesfx = NULL;
2695 			continue;
2696 		}
2697 	}
2698 
2699 	return newSamples;
2700 }
2701 
2702 // this is now called AFTER the DMA painting, since it's only the painter calls that cause the MP3s to be unpacked,
2703 //	and therefore to have data readable by the lip-sync volume calc code.
2704 //
S_DoLipSynchs(const unsigned s_oldpaintedtime)2705 void S_DoLipSynchs( const unsigned s_oldpaintedtime )
2706 {
2707 	channel_t		*ch;
2708 	int				i;
2709 
2710 	// clear out the lip synching override array for this frame
2711 	memset(s_entityWavVol, 0,(MAX_GENTITIES * 4));
2712 
2713 	ch = s_channels;
2714 	for (i=0; i<MAX_CHANNELS ; i++, ch++) {
2715 		if ( !ch->thesfx ) {
2716 			continue;
2717 		}
2718 		if ( ch->loopSound ) {
2719 			continue;
2720 		}
2721 
2722 		// if we are playing a sample that should override the lip texture on its owning model, lets figure out
2723 		// what the amplitude is, stick it in a table, then return it
2724 		if ( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL )
2725 		{
2726 			// go away and work out amplitude for this sound we are playing right now.
2727 			s_entityWavVol[ ch->entnum ] = S_CheckAmplitude( ch, s_oldpaintedtime );
2728 			if ( s_show->integer == 3 ) {
2729 				Com_Printf( "(%i)%i %s vol = %i\n", ch->entnum, i, ch->thesfx->sSoundName, s_entityWavVol[ ch->entnum ] );
2730 			}
2731 		}
2732 	}
2733 
2734 	if (next_amplitude < s_soundtime)	{
2735 		next_amplitude = s_soundtime + 800;
2736 	}
2737 }
2738 
2739 /*
2740 ============
2741 S_Update
2742 
2743 Called once each time through the main loop
2744 ============
2745 */
S_Update(void)2746 void S_Update( void ) {
2747 	int			i;
2748 	int			total;
2749 	channel_t	*ch;
2750 
2751 	if ( !s_soundStarted || s_soundMuted ) {
2752 		return;
2753 	}
2754 
2755 	//
2756 	// debugging output
2757 	//
2758 	if ( s_show->integer == 2 ) {
2759 		total = 0;
2760 		int totalMeg =0;
2761 		ch = s_channels;
2762 		for (i=0 ; i<MAX_CHANNELS; i++, ch++) {
2763 			if (ch->thesfx && (ch->leftvol || ch->rightvol) ) {
2764 				Com_Printf ("(%i) %3i %3i %s\n", ch->entnum, ch->leftvol, ch->rightvol, ch->thesfx->sSoundName);
2765 				total++;
2766 				totalMeg += Z_Size(ch->thesfx->pSoundData);
2767 				if (ch->thesfx->pMP3StreamHeader)
2768 				{
2769 					totalMeg += sizeof(*ch->thesfx->pMP3StreamHeader);
2770 				}
2771 			}
2772 		}
2773 
2774 		if (total)
2775 			Com_Printf ("----(%i)---- painted: %i, SND %.2fMB\n", total, s_paintedtime, totalMeg/1024.0f/1024.0f);
2776 	}
2777 
2778 	// The Open AL code, handles background music in the S_UpdateRawSamples function
2779 #ifdef USE_OPENAL
2780 	if (!s_UseOpenAL)
2781 #endif
2782 	{
2783 		// add raw data from streamed samples
2784 		S_UpdateBackgroundTrack();
2785 	}
2786 
2787 	// mix some sound
2788 	S_Update_();
2789 }
2790 
S_GetSoundtime(void)2791 void S_GetSoundtime(void)
2792 {
2793 	int		samplepos;
2794 	static	int		buffers;
2795 	static	int		oldsamplepos;
2796 	int		fullsamples;
2797 
2798 	fullsamples = dma.samples / dma.channels;
2799 
2800 	if( CL_VideoRecording( ) )
2801 	{
2802 		float fps = Q_min(cl_aviFrameRate->value, 1000.0f);
2803 		float frameDuration = Q_max(dma.speed / fps, 1.0f) + clc.aviSoundFrameRemainder;
2804 		int msec = (int)frameDuration;
2805 		s_soundtime += msec;
2806 		clc.aviSoundFrameRemainder = frameDuration - msec;
2807 		return;
2808 	}
2809 
2810 	// it is possible to miscount buffers if it has wrapped twice between
2811 	// calls to S_Update.  Oh well.
2812 	samplepos = SNDDMA_GetDMAPos();
2813 	if (samplepos < oldsamplepos)
2814 	{
2815 		buffers++;					// buffer wrapped
2816 
2817 		if (s_paintedtime > 0x40000000)
2818 		{	// time to chop things off to avoid 32 bit limits
2819 			buffers = 0;
2820 			s_paintedtime = fullsamples;
2821 			S_StopAllSounds ();
2822 		}
2823 	}
2824 	oldsamplepos = samplepos;
2825 
2826 	s_soundtime = buffers*fullsamples + samplepos/dma.channels;
2827 
2828 #if 0
2829 // check to make sure that we haven't overshot
2830 	if (s_paintedtime < s_soundtime)
2831 	{
2832 		Com_DPrintf ("S_Update_ : overflow\n");
2833 		s_paintedtime = s_soundtime;
2834 	}
2835 #endif
2836 
2837 	if ( dma.submission_chunk < 256 ) {
2838 		s_paintedtime = (int)(s_soundtime + s_mixPreStep->value * dma.speed);
2839 	} else {
2840 		s_paintedtime = s_soundtime + dma.submission_chunk;
2841 	}
2842 }
2843 
2844 
S_Update_(void)2845 void S_Update_(void) {
2846 	unsigned        endtime;
2847 	int				samps;
2848 
2849 	if ( !s_soundStarted || s_soundMuted ) {
2850 		return;
2851 	}
2852 
2853 #ifdef USE_OPENAL
2854 	if (s_UseOpenAL)
2855 	{
2856 		int i,j;
2857 		float		pos[3];
2858 		UpdateSingleShotSounds();
2859 
2860 		channel_t *ch = s_channels + 1;
2861 		for ( i = 1; i < MAX_CHANNELS ; i++, ch++ )
2862 		{
2863 			if ( !ch->thesfx || (ch->bPlaying))
2864 				continue;
2865 
2866 			int source = ch - s_channels;
2867 
2868 			if (ch->entchannel == CHAN_VOICE_GLOBAL || ch->entchannel == CHAN_ANNOUNCER)
2869 			{
2870 				// Always play these sounds at 0,0,-1 (in front of listener)
2871 				pos[0] = 0.0f;
2872 				pos[1] = 0.0f;
2873 				pos[2] = -1.0f;
2874 
2875 				alSourcefv(s_channels[source].alSource, AL_POSITION, pos);
2876 				alSourcei(s_channels[source].alSource, AL_LOOPING, AL_FALSE);
2877 				alSourcei(s_channels[source].alSource, AL_SOURCE_RELATIVE, AL_TRUE);
2878 				if (ch->entchannel == CHAN_ANNOUNCER)
2879 				{
2880 					alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.0f);
2881 				}
2882 				else
2883 				{
2884 					alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volumeVoice->value) / 255.0f);
2885 				}
2886 			}
2887 			else
2888 			{
2889 				// Get position of source
2890 				if (ch->fixed_origin)
2891 				{
2892 					pos[0] = ch->origin[0];
2893 					pos[1] = ch->origin[2];
2894 					pos[2] = -ch->origin[1];
2895 					alSourcei(s_channels[source].alSource, AL_SOURCE_RELATIVE, AL_FALSE);
2896 				}
2897 				else
2898 				{
2899 					if (ch->entnum == listener_number)
2900 					{
2901 						pos[0] = 0.0f;
2902 						pos[1] = 0.0f;
2903 						pos[2] = 0.0f;
2904 						alSourcei(s_channels[source].alSource, AL_SOURCE_RELATIVE, AL_TRUE);
2905 					}
2906 					else
2907 					{
2908 						// Get position of Entity
2909 						if (ch->bLooping)
2910 						{
2911 							pos[0] = loopSounds[ ch->entnum ].origin[0];
2912 							pos[1] = loopSounds[ ch->entnum ].origin[2];
2913 							pos[2] = -loopSounds[ ch->entnum ].origin[1];
2914 						}
2915 						else
2916 						{
2917 							pos[0] = s_entityPosition[ch->entnum][0];
2918 							pos[1] = s_entityPosition[ch->entnum][2];
2919 							pos[2] = -s_entityPosition[ch->entnum][1];
2920 						}
2921 						alSourcei(s_channels[source].alSource, AL_SOURCE_RELATIVE, AL_FALSE);
2922 					}
2923 				}
2924 
2925 				alSourcefv(s_channels[source].alSource, AL_POSITION, pos);
2926 				alSourcei(s_channels[source].alSource, AL_LOOPING, AL_FALSE);
2927 
2928 				if (ch->entchannel == CHAN_VOICE)
2929 				{
2930 					// Reduced fall-off (Large Reference Distance), affected by Voice Volume
2931 					alSourcef(s_channels[source].alSource, AL_REFERENCE_DISTANCE, DEFAULT_VOICE_REF_DISTANCE);
2932 					alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volumeVoice->value) / 255.0f);
2933 				}
2934 				else if (ch->entchannel == CHAN_VOICE_ATTEN)
2935 				{
2936 					// Normal fall-off, affected by Voice Volume
2937 					alSourcef(s_channels[source].alSource, AL_REFERENCE_DISTANCE, DEFAULT_REF_DISTANCE);
2938 					alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volumeVoice->value) / 255.0f);
2939 				}
2940 				else if (ch->entchannel == CHAN_LESS_ATTEN)
2941 				{
2942 					// Reduced fall-off, affected by Sound Effect Volume
2943 					alSourcef(s_channels[source].alSource, AL_REFERENCE_DISTANCE, DEFAULT_VOICE_REF_DISTANCE);
2944 					alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f);
2945 				}
2946 				else
2947 				{
2948 					// Normal fall-off, affect by Sound Effect Volume
2949 					alSourcef(s_channels[source].alSource, AL_REFERENCE_DISTANCE, DEFAULT_REF_DISTANCE);
2950 					alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f);
2951 				}
2952 			}
2953 
2954 			if (s_bEALFileLoaded)
2955 				UpdateEAXBuffer(ch);
2956 
2957 			int nBytesDecoded = 0;
2958 			int nTotalBytesDecoded = 0;
2959 			int nBuffersToAdd = 0;
2960 
2961 			if (ch->thesfx->pMP3StreamHeader)
2962 			{
2963 				memcpy(&ch->MP3StreamHeader, ch->thesfx->pMP3StreamHeader,	sizeof(ch->MP3StreamHeader));
2964 				ch->iMP3SlidingDecodeWritePos = 0;
2965 				ch->iMP3SlidingDecodeWindowPos= 0;
2966 
2967 				// Reset streaming buffers status's
2968 				for (i = 0; i < NUM_STREAMING_BUFFERS; i++)
2969 					ch->buffers[i].Status = UNQUEUED;
2970 
2971 				// Decode (STREAMING_BUFFER_SIZE / 1152) MP3 frames for each of the NUM_STREAMING_BUFFERS AL Buffers
2972 				for (i = 0; i < NUM_STREAMING_BUFFERS; i++)
2973 				{
2974 					nTotalBytesDecoded = 0;
2975 
2976 					for (j = 0; j < (STREAMING_BUFFER_SIZE / 1152); j++)
2977 					{
2978 						nBytesDecoded = C_MP3Stream_Decode(&ch->MP3StreamHeader, 0);	// added ,0 ?
2979 						memcpy(ch->buffers[i].Data + nTotalBytesDecoded, ch->MP3StreamHeader.bDecodeBuffer, nBytesDecoded);
2980 						if (ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL )
2981 						{
2982 							if (ch->thesfx->lipSyncData)
2983 							{
2984 								ch->thesfx->lipSyncData[(i*NUM_STREAMING_BUFFERS)+j] = S_MP3PreProcessLipSync(ch, (short *)(ch->MP3StreamHeader.bDecodeBuffer));
2985 							}
2986 							else
2987 							{
2988 #ifdef _DEBUG
2989 								Com_OPrintf("Missing lip-sync info. for %s\n", ch->thesfx->sSoundName);
2990 #endif
2991 							}
2992 						}
2993 						nTotalBytesDecoded += nBytesDecoded;
2994 					}
2995 
2996 					if (nTotalBytesDecoded != STREAMING_BUFFER_SIZE)
2997 					{
2998 						memset(ch->buffers[i].Data + nTotalBytesDecoded, 0, (STREAMING_BUFFER_SIZE - nTotalBytesDecoded));
2999 						break;
3000 					}
3001 				}
3002 
3003 				if (i >= NUM_STREAMING_BUFFERS)
3004 					nBuffersToAdd = NUM_STREAMING_BUFFERS;
3005 				else
3006 					nBuffersToAdd = i + 1;
3007 
3008 				// Make sure queue is empty first
3009 				alSourcei(s_channels[source].alSource, AL_BUFFER, NULL);
3010 
3011 				for (i = 0; i < nBuffersToAdd; i++)
3012 				{
3013 					// Copy decoded data to AL Buffer
3014 					alBufferData(ch->buffers[i].BufferID, AL_FORMAT_MONO16, ch->buffers[i].Data, STREAMING_BUFFER_SIZE, 22050);
3015 
3016 					// Queue AL Buffer on Source
3017 					alSourceQueueBuffers(s_channels[source].alSource, 1, &(ch->buffers[i].BufferID));
3018 					if (alGetError() == AL_NO_ERROR)
3019 					{
3020 						ch->buffers[i].Status = QUEUED;
3021 					}
3022 				}
3023 
3024 				// Clear error state, and check for successful Play call
3025 				alGetError();
3026 				alSourcePlay(s_channels[source].alSource);
3027 				if (alGetError() == AL_NO_ERROR)
3028 					s_channels[source].bPlaying = true;
3029 
3030 				ch->bStreaming = true;
3031 
3032 				if ( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL )
3033 				{
3034 					if (ch->thesfx->lipSyncData)
3035 					{
3036 						// Record start time for Lip-syncing
3037 						s_channels[source].iStartTime = timeGetTime();
3038 
3039 						// Prepare lipsync value(s)
3040 						s_entityWavVol[ ch->entnum ] = ch->thesfx->lipSyncData[0];
3041 					}
3042 					else
3043 					{
3044 #ifdef _DEBUG
3045 						Com_OPrintf("Missing lip-sync info. for %s\n", ch->thesfx->sSoundName);
3046 #endif
3047 					}
3048 				}
3049 
3050 				return;
3051 			}
3052 			else
3053 			{
3054 				// Attach buffer to source
3055 				alSourcei(s_channels[source].alSource, AL_BUFFER, ch->thesfx->Buffer);
3056 
3057 				ch->bStreaming = false;
3058 
3059 				// Clear error state, and check for successful Play call
3060 				alGetError();
3061 				alSourcePlay(s_channels[source].alSource);
3062 				if (alGetError() == AL_NO_ERROR)
3063 					s_channels[source].bPlaying = true;
3064 
3065 				if ( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL )
3066 				{
3067 					if (ch->thesfx->lipSyncData)
3068 					{
3069 						// Record start time for Lip-syncing
3070 						s_channels[source].iStartTime = timeGetTime();
3071 
3072 						// Prepare lipsync value(s)
3073 						s_entityWavVol[ ch->entnum ] = ch->thesfx->lipSyncData[0];
3074 					}
3075 					else
3076 					{
3077 #ifdef _DEBUG
3078 						Com_OPrintf("Missing lip-sync info. for %s\n", ch->thesfx->sSoundName);
3079 #endif
3080 					}
3081 				}
3082 			}
3083 		}
3084 
3085 		S_SetLipSyncs();
3086 
3087 		UpdateLoopingSounds();
3088 
3089 		AL_UpdateRawSamples();
3090 	}
3091 	else
3092 	{
3093 #endif
3094 		// Updates s_soundtime
3095 		S_GetSoundtime();
3096 
3097 		const unsigned s_oldpaintedtime = s_paintedtime;
3098 
3099 		// clear any sound effects that end before the current time,
3100 		// and start any new sounds
3101 		S_ScanChannelStarts();
3102 
3103 		// mix ahead of current position
3104 		endtime = (int)(s_soundtime + s_mixahead->value * dma.speed);
3105 
3106 		// mix to an even submission block size
3107 		endtime = (endtime + dma.submission_chunk-1)
3108 			& ~(dma.submission_chunk-1);
3109 
3110 		// never mix more than the complete buffer
3111 		samps = dma.samples >> (dma.channels-1);
3112 		if (endtime - s_soundtime > (unsigned)samps)
3113 			endtime = s_soundtime + samps;
3114 
3115 
3116 		SNDDMA_BeginPainting ();
3117 
3118 		S_PaintChannels (endtime);
3119 
3120 		SNDDMA_Submit ();
3121 
3122 		S_DoLipSynchs( s_oldpaintedtime );
3123 #ifdef USE_OPENAL
3124 	}
3125 #endif
3126 }
3127 
3128 #ifdef USE_OPENAL
UpdateSingleShotSounds()3129 void UpdateSingleShotSounds()
3130 {
3131 	int i, j, k;
3132 	ALint state;
3133 	ALint processed;
3134 	channel_t *ch;
3135 
3136 	// Firstly, check if any single-shot sounds have completed, or if they need more data (for streaming Sources),
3137 	// and/or if any of the currently playing (non-Ambient) looping sounds need to be stopped
3138 	ch = s_channels + 1;
3139 	for (i = 1; i < s_numChannels; i++, ch++)
3140 	{
3141 		ch->bProcessed = false;
3142 		if ((s_channels[i].bPlaying) && (!ch->bLooping))
3143 		{
3144 			// Single-shot
3145 			if (s_channels[i].bStreaming == false)
3146 			{
3147 				alGetSourcei(s_channels[i].alSource, AL_SOURCE_STATE, &state);
3148 				if (state == AL_STOPPED)
3149 				{
3150 					s_channels[i].thesfx = NULL;
3151 					s_channels[i].bPlaying = false;
3152 				}
3153 			}
3154 			else
3155 			{
3156 				// Process streaming sample
3157 
3158 				// Procedure :-
3159 				// if more data to play
3160 				//		if any UNQUEUED Buffers
3161 				//			fill them with data
3162 				//		(else ?)
3163 				//			get number of buffers processed
3164 				//			fill them with data
3165 				//		restart playback if it has stopped (buffer underrun)
3166 				// else
3167 				//		free channel
3168 
3169 				int nBytesDecoded;
3170 
3171 				if (ch->thesfx->pMP3StreamHeader)
3172 				{
3173 					if (ch->MP3StreamHeader.iSourceBytesRemaining == 0)
3174 					{
3175 						// Finished decoding data - if the source has finished playing then we're done
3176 						alGetSourcei(ch->alSource, AL_SOURCE_STATE, &state);
3177 						if (state == AL_STOPPED)
3178 						{
3179 							// Attach NULL buffer to Source to remove any buffers left in the queue
3180 							alSourcei(ch->alSource, AL_BUFFER, NULL);
3181 							ch->thesfx = NULL;
3182 							ch->bPlaying = false;
3183 						}
3184 						// Move on to next channel ...
3185 						continue;
3186 					}
3187 
3188 					// Check to see if any Buffers have been processed
3189 					alGetSourcei(ch->alSource, AL_BUFFERS_PROCESSED, &processed);
3190 
3191 					ALuint buffer;
3192 					while (processed)
3193 					{
3194 						alSourceUnqueueBuffers(ch->alSource, 1, &buffer);
3195 						for (j = 0; j < NUM_STREAMING_BUFFERS; j++)
3196 						{
3197 							if (ch->buffers[j].BufferID == buffer)
3198 							{
3199 								ch->buffers[j].Status = UNQUEUED;
3200 								break;
3201 							}
3202 						}
3203 						processed--;
3204 					}
3205 
3206 					int nTotalBytesDecoded = 0;
3207 
3208 					for (j = 0; j < NUM_STREAMING_BUFFERS; j++)
3209 					{
3210 						if ((ch->buffers[j].Status == UNQUEUED) && (ch->MP3StreamHeader.iSourceBytesRemaining > 0))
3211 						{
3212 							nTotalBytesDecoded = 0;
3213 
3214 							for (k = 0; k < (STREAMING_BUFFER_SIZE / 1152); k++)
3215 							{
3216 								nBytesDecoded = C_MP3Stream_Decode(&ch->MP3StreamHeader, 0); // added ,0
3217 
3218 								if (nBytesDecoded > 0)
3219 								{
3220 									memcpy(ch->buffers[j].Data + nTotalBytesDecoded, ch->MP3StreamHeader.bDecodeBuffer, nBytesDecoded);
3221 
3222 									if (ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL )
3223 									{
3224 										if (ch->thesfx->lipSyncData)
3225 										{
3226 											ch->thesfx->lipSyncData[(j*4)+k] = S_MP3PreProcessLipSync(ch, (short *)(ch->buffers[j].Data));
3227 										}
3228 										else
3229 										{
3230 #ifdef _DEBUG
3231 											Com_OPrintf("Missing lip-sync info. for %s\n", ch->thesfx->sSoundName);
3232 #endif
3233 										}
3234 									}
3235 									nTotalBytesDecoded += nBytesDecoded;
3236 								}
3237 								else
3238 								{
3239 									// Make sure that iSourceBytesRemaining is 0
3240 									if (ch->MP3StreamHeader.iSourceBytesRemaining != 0)
3241 									{
3242 										ch->MP3StreamHeader.iSourceBytesRemaining = 0;
3243 										break;
3244 									}
3245 								}
3246 							}
3247 
3248 							if (nTotalBytesDecoded != STREAMING_BUFFER_SIZE)
3249 							{
3250 								memset(ch->buffers[j].Data + nTotalBytesDecoded, 0, (STREAMING_BUFFER_SIZE - nTotalBytesDecoded));
3251 
3252 								// Move data to buffer
3253 								alBufferData(ch->buffers[j].BufferID, AL_FORMAT_MONO16, ch->buffers[j].Data, STREAMING_BUFFER_SIZE, 22050);
3254 
3255 								// Queue Buffer on Source
3256 								alSourceQueueBuffers(ch->alSource, 1, &(ch->buffers[j].BufferID));
3257 
3258 								// Update status of Buffer
3259 								ch->buffers[j].Status = QUEUED;
3260 
3261 								break;
3262 							}
3263 							else
3264 							{
3265 								// Move data to buffer
3266 								alBufferData(ch->buffers[j].BufferID, AL_FORMAT_MONO16, ch->buffers[j].Data, STREAMING_BUFFER_SIZE, 22050);
3267 
3268 								// Queue Buffer on Source
3269 								alSourceQueueBuffers(ch->alSource, 1, &(ch->buffers[j].BufferID));
3270 
3271 								// Update status of Buffer
3272 								ch->buffers[j].Status = QUEUED;
3273 							}
3274 						}
3275 					}
3276 
3277 					// Get state of Buffer
3278 					alGetSourcei(ch->alSource, AL_SOURCE_STATE, &state);
3279 					if (state != AL_PLAYING)
3280 					{
3281 						alSourcePlay(ch->alSource);
3282 #ifdef _DEBUG
3283 						Com_OPrintf("[%d] Restarting playback of single-shot streaming MP3 sample - still have %d bytes to decode\n", i, ch->MP3StreamHeader.iSourceBytesRemaining);
3284 #endif
3285 					}
3286 				}
3287 			}
3288 		}
3289 	}
3290 }
3291 
UpdateLoopingSounds()3292 void UpdateLoopingSounds()
3293 {
3294 	int i,j;
3295 	ALuint source;
3296 	channel_t *ch;
3297 	loopSound_t	*loop;
3298 	float pos[3];
3299 
3300 	// First check to see if any of the looping sounds are already playing at the correct positions
3301 	ch = s_channels + 1;
3302 	for (i = 1; i < s_numChannels; i++, ch++)
3303 	{
3304 		if (ch->bLooping && s_channels[i].bPlaying)
3305 		{
3306 			for (j = 0; j < numLoopSounds; j++)
3307 			{
3308 				loop = &loopSounds[j];
3309 
3310 				// If this channel is playing the right sound effect at the right position then mark this channel and looping sound
3311 				// as processed
3312 				if ((loop->bProcessed == false) && (ch->thesfx == loop->sfx) )
3313 				{
3314 					if ( (loop->origin[0] == listener_pos[0]) && (loop->origin[1] == -listener_pos[2])
3315 						&& (loop->origin[2] == listener_pos[1]) )
3316 					{
3317 						// Assume that this sound is head relative
3318 						if (!loop->bRelative)
3319 						{
3320 							// Set position to 0,0,0 and turn on Head Relative Mode
3321 							float pos[3];
3322 							pos[0] = 0.f;
3323 							pos[1] = 0.f;
3324 							pos[2] = 0.f;
3325 
3326 							alSourcefv(s_channels[i].alSource, AL_POSITION, pos);
3327 							alSourcei(s_channels[i].alSource, AL_SOURCE_RELATIVE, AL_TRUE);
3328 							loop->bRelative = true;
3329 						}
3330 
3331 						// Make sure Gain is set correctly
3332 						if (ch->master_vol != loop->volume)
3333 						{
3334 							ch->master_vol = loop->volume;
3335 							alSourcef(s_channels[i].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f);
3336 						}
3337 
3338 						ch->bProcessed = true;
3339 						loop->bProcessed = true;
3340 					}
3341 					else if ((loop->bProcessed == false) && (ch->thesfx == loop->sfx) && (!memcmp(ch->origin, loop->origin, sizeof(ch->origin))))
3342 					{
3343 						// Match !
3344 						ch->bProcessed = true;
3345 						loop->bProcessed = true;
3346 
3347 						// Make sure Gain is set correctly
3348 						if (ch->master_vol != loop->volume)
3349 						{
3350 							ch->master_vol = loop->volume;
3351 							alSourcef(s_channels[i].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f);
3352 						}
3353 
3354 						break;
3355 					}
3356 				}
3357 			}
3358 		}
3359 	}
3360 
3361 	// Next check if the correct looping sound is playing, but at the wrong position
3362 	ch = s_channels + 1;
3363 	for (i = 1; i < s_numChannels; i++, ch++)
3364 	{
3365 		if ((ch->bLooping) && (ch->bProcessed == false) && s_channels[i].bPlaying)
3366 		{
3367 			for (j = 0; j < numLoopSounds; j++)
3368 			{
3369 				loop = &loopSounds[j];
3370 
3371 				if ((loop->bProcessed == false) && (ch->thesfx == loop->sfx))
3372 				{
3373 					// Same sound - wrong position
3374 					ch->origin[0] = loop->origin[0];
3375 					ch->origin[1] = loop->origin[1];
3376 					ch->origin[2] = loop->origin[2];
3377 
3378 					pos[0] = loop->origin[0];
3379 					pos[1] = loop->origin[2];
3380 					pos[2] = -loop->origin[1];
3381 					alSourcefv(s_channels[i].alSource, AL_POSITION, pos);
3382 
3383 					ch->master_vol = loop->volume;
3384 					alSourcef(s_channels[i].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.f);
3385 
3386 					if (s_bEALFileLoaded)
3387 						UpdateEAXBuffer(ch);
3388 
3389 					ch->bProcessed = true;
3390 					loop->bProcessed = true;
3391 					break;
3392 				}
3393 			}
3394 		}
3395 	}
3396 
3397 	// If any non-procesed looping sounds are still playing on a channel, they can be removed as they are no longer
3398 	// required
3399 	ch = s_channels + 1;
3400 	for (i = 1; i < s_numChannels; i++, ch++)
3401 	{
3402 		if (s_channels[i].bPlaying && ch->bLooping && !ch->bProcessed)
3403 		{
3404 			// Sound no longer needed
3405 			alSourceStop(s_channels[i].alSource);
3406 			ch->thesfx = NULL;
3407 			ch->bPlaying = false;
3408 		}
3409 	}
3410 
3411 #ifdef _DEBUG
3412 	alGetError();
3413 #endif
3414 	// Finally if there are any non-processed sounds left, we need to try and play them
3415 	for (j = 0; j < numLoopSounds; j++)
3416 	{
3417 		loop = &loopSounds[j];
3418 
3419 		if (loop->bProcessed == false)
3420 		{
3421 			ch = S_PickChannel(0,0);
3422 
3423 			ch->master_vol = loop->volume;
3424 			ch->entnum = loop->entnum;
3425 			ch->entchannel = CHAN_AMBIENT;	// Make sure this gets set to something
3426 			ch->thesfx = loop->sfx;
3427 			ch->bLooping = true;
3428 
3429 			// Check if the Source is positioned at exactly the same location as the listener
3430 			if ( (loop->origin[0] == listener_pos[0]) && (loop->origin[1] == -listener_pos[2])
3431 						&& (loop->origin[2] == listener_pos[1]) )
3432 			{
3433 				// Assume that this sound is head relative
3434 				loop->bRelative = true;
3435 				ch->origin[0] = 0.f;
3436 				ch->origin[1] = 0.f;
3437 				ch->origin[2] = 0.f;
3438 			}
3439 			else
3440 			{
3441 				ch->origin[0] = loop->origin[0];
3442 				ch->origin[1] = loop->origin[1];
3443 				ch->origin[2] = loop->origin[2];
3444 				loop->bRelative = false;
3445 			}
3446 
3447 			ch->fixed_origin = (qboolean)loop->bRelative;
3448 			pos[0] = ch->origin[0];
3449 			pos[1] = ch->origin[2];
3450 			pos[2] = -ch->origin[1];
3451 
3452 			source = ch - s_channels;
3453 			alSourcei(s_channels[source].alSource, AL_BUFFER, ch->thesfx->Buffer);
3454 			alSourcefv(s_channels[source].alSource, AL_POSITION, pos);
3455 			alSourcei(s_channels[source].alSource, AL_LOOPING, AL_TRUE);
3456 			alSourcef(s_channels[source].alSource, AL_REFERENCE_DISTANCE, DEFAULT_REF_DISTANCE);
3457 			alSourcef(s_channels[source].alSource, AL_GAIN, ((float)(ch->master_vol) * s_volume->value) / 255.0f);
3458 			alSourcei(s_channels[source].alSource, AL_SOURCE_RELATIVE, ch->fixed_origin ? AL_TRUE : AL_FALSE);
3459 
3460 			if (s_bEALFileLoaded)
3461 				UpdateEAXBuffer(ch);
3462 
3463 			alGetError();
3464 			alSourcePlay(s_channels[source].alSource);
3465 			if (alGetError() == AL_NO_ERROR)
3466 				s_channels[source].bPlaying = true;
3467 		}
3468 	}
3469 }
3470 
AL_UpdateRawSamples()3471 void AL_UpdateRawSamples()
3472 {
3473 	ALuint buffer;
3474 	ALint size;
3475 	ALint processed;
3476 	ALint state;
3477 	int i,j,src;
3478 
3479 #ifdef _DEBUG
3480 	// Clear Open AL Error
3481 	alGetError();
3482 #endif
3483 
3484 	S_UpdateBackgroundTrack();
3485 
3486 	// Find out how many buffers have been processed (played) by the Source
3487 	alGetSourcei(s_channels[0].alSource, AL_BUFFERS_PROCESSED, &processed);
3488 
3489 	while (processed)
3490 	{
3491 		// Unqueue each buffer, determine the length of the buffer, and then delete it
3492 		alSourceUnqueueBuffers(s_channels[0].alSource, 1, &buffer);
3493 		alGetBufferi(buffer, AL_SIZE, &size);
3494 		alDeleteBuffers(1, &buffer);
3495 
3496 		// Update sg.soundtime (+= number of samples played (number of bytes / 4))
3497 		s_soundtime += (size >> 2);
3498 
3499 		processed--;
3500 	}
3501 
3502 	// Add new data to a new Buffer and queue it on the Source
3503 	if (s_rawend > s_paintedtime)
3504 	{
3505 		size = (s_rawend - s_paintedtime)<<2;
3506 		if (size > (MAX_RAW_SAMPLES<<2))
3507 		{
3508 			Com_OPrintf("UpdateRawSamples :- Raw Sample buffer has overflowed !!!\n");
3509 			size = MAX_RAW_SAMPLES<<2;
3510 			s_paintedtime = s_rawend - MAX_RAW_SAMPLES;
3511 		}
3512 
3513 		// Copy samples from RawSamples to audio buffer (sg.rawdata)
3514 		for (i = s_paintedtime, j = 0; i < s_rawend; i++, j+=2)
3515 		{
3516 			src = i & (MAX_RAW_SAMPLES - 1);
3517 			s_rawdata[j] = (short)(s_rawsamples[src].left>>8);
3518 			s_rawdata[j+1] = (short)(s_rawsamples[src].right>>8);
3519 		}
3520 
3521 		// Need to generate more than 1 buffer for music playback
3522 		// iterations = 0;
3523 		// largestBufferSize = (MAX_RAW_SAMPLES / 4) * 4
3524 		// while (size)
3525 		//	generate a buffer
3526 		//	if size > largestBufferSize
3527 		//		copy sg.rawdata + ((iterations * largestBufferSize)>>1) to buffer
3528 		//		size -= largestBufferSize
3529 		//	else
3530 		//		copy remainder
3531 		//		size = 0
3532 		//	queue the buffer
3533 		//  iterations++;
3534 
3535 		int iterations = 0;
3536 		int largestBufferSize = MAX_RAW_SAMPLES;	// in bytes (== quarter of Raw Samples data)
3537 		while (size)
3538 		{
3539 			alGenBuffers(1, &buffer);
3540 
3541 			if (size > largestBufferSize)
3542 			{
3543 				alBufferData(buffer, AL_FORMAT_STEREO16, (char*)(s_rawdata + ((iterations * largestBufferSize)>>1)), largestBufferSize, 22050);
3544 				size -= largestBufferSize;
3545 			}
3546 			else
3547 			{
3548 				alBufferData(buffer, AL_FORMAT_STEREO16, (char*)(s_rawdata + ((iterations * largestBufferSize)>>1)), size, 22050);
3549 				size = 0;
3550 			}
3551 
3552 			alSourceQueueBuffers(s_channels[0].alSource, 1, &buffer);
3553 			iterations++;
3554 		}
3555 
3556 		// Update paintedtime
3557 		s_paintedtime = s_rawend;
3558 
3559 		// Check that the Source is actually playing
3560 		alGetSourcei(s_channels[0].alSource, AL_SOURCE_STATE, &state);
3561 		if (state != AL_PLAYING)
3562 		{
3563 			// Stopped playing ... due to buffer underrun
3564 			// Unqueue any buffers still on the Source (they will be PROCESSED), and restart playback
3565 			alGetSourcei(s_channels[0].alSource, AL_BUFFERS_PROCESSED, &processed);
3566 			while (processed)
3567 			{
3568 				alSourceUnqueueBuffers(s_channels[0].alSource, 1, &buffer);
3569 				processed--;
3570 				alGetBufferi(buffer, AL_SIZE, &size);
3571 				alDeleteBuffers(1, &buffer);
3572 
3573 				// Update sg.soundtime (+= number of samples played (number of bytes / 4))
3574 				s_soundtime += (size >> 2);
3575 			}
3576 
3577 #ifdef _DEBUG
3578 			Com_OPrintf("Restarting / Starting playback of Raw Samples\n");
3579 #endif
3580 			alSourcePlay(s_channels[0].alSource);
3581 		}
3582 	}
3583 
3584 #ifdef _DEBUG
3585 	if (alGetError() != AL_NO_ERROR)
3586 		Com_OPrintf("OAL Error : UpdateRawSamples\n");
3587 #endif
3588 }
3589 #endif
3590 
S_MP3PreProcessLipSync(channel_t * ch,short * data)3591 int S_MP3PreProcessLipSync(channel_t *ch, short *data)
3592 {
3593 	int i;
3594 	int sample;
3595 	int sampleTotal = 0;
3596 
3597 	for (i = 0; i < 576; i += 100)
3598 	{
3599 		sample = LittleShort(data[i]);
3600 		sample = sample >> 8;
3601 		sampleTotal += sample * sample;
3602 	}
3603 
3604 	sampleTotal /= 6;
3605 
3606 	if (sampleTotal < ch->thesfx->fVolRange * s_lip_threshold_1->value)
3607 		sample = -1;
3608 	else if (sampleTotal < ch->thesfx->fVolRange * s_lip_threshold_2->value)
3609 		sample = 1;
3610 	else if (sampleTotal < ch->thesfx->fVolRange * s_lip_threshold_3->value)
3611 		sample = 2;
3612 	else if (sampleTotal < ch->thesfx->fVolRange * s_lip_threshold_4->value)
3613 		sample = 3;
3614 	else
3615 		sample = 4;
3616 
3617 	return sample;
3618 }
3619 
S_SetLipSyncs()3620 void S_SetLipSyncs()
3621 {
3622 	int i;
3623 	unsigned int samples;
3624 	int currentTime, timePlayed;
3625 	channel_t *ch;
3626 
3627 #ifdef _WIN32
3628 	currentTime = timeGetTime();
3629 #else
3630 	// FIXME: alternative to timeGetTime ?
3631 	currentTime = 0;
3632 #endif
3633 
3634 	memset(s_entityWavVol, 0, sizeof(s_entityWavVol));
3635 
3636 	ch = s_channels + 1;
3637 	for (i = 1; i < s_numChannels; i++, ch++)
3638 	{
3639 		if ((!ch->thesfx)||(!ch->bPlaying))
3640 			continue;
3641 
3642 		if ( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_VOICE_ATTEN || ch->entchannel == CHAN_VOICE_GLOBAL )
3643 		{
3644 			// Calculate how much time has passed since the sample was started
3645 			timePlayed = currentTime - ch->iStartTime;
3646 
3647 			if (ch->thesfx->eSoundCompressionMethod==ct_16)
3648 			{
3649 				// There is a new computed lip-sync value every 1000 samples - so find out how many samples
3650 				// have been played and lookup the value in the lip-sync table
3651 				samples = (timePlayed * 22050) / 1000;
3652 
3653 				if (ch->thesfx->lipSyncData == NULL)
3654 				{
3655 #ifdef _DEBUG
3656 					Com_OPrintf("Missing lip-sync info. for %s\n", ch->thesfx->sSoundName);
3657 #endif
3658 				}
3659 
3660 				if ((ch->thesfx->lipSyncData) && ((int)samples < ch->thesfx->iSoundLengthInSamples))
3661 				{
3662 					s_entityWavVol[ ch->entnum ] = ch->thesfx->lipSyncData[samples / 1000];
3663 					if ( s_show->integer == 3 )
3664 					{
3665 						Com_Printf( "(%i)%i %s vol = %i\n", ch->entnum, i, ch->thesfx->sSoundName, s_entityWavVol[ ch->entnum ] );
3666 					}
3667 				}
3668 			}
3669 			else
3670 			{
3671 				// MP3
3672 
3673 				// There is a new computed lip-sync value every 576 samples - so find out how many samples
3674 				// have been played and lookup the value in the lip-sync table
3675 				samples = (timePlayed * 22050) / 1000;
3676 
3677 				if (ch->thesfx->lipSyncData == NULL)
3678 				{
3679 #ifdef _DEBUG
3680 					Com_OPrintf("Missing lip-sync info. for %s\n", ch->thesfx->sSoundName);
3681 #endif
3682 				}
3683 
3684 				if ((ch->thesfx->lipSyncData) && (samples < (unsigned)ch->thesfx->iSoundLengthInSamples))
3685 				{
3686 					s_entityWavVol[ ch->entnum ] = ch->thesfx->lipSyncData[(samples / 576) % 16];
3687 
3688 					if ( s_show->integer == 3 )
3689 					{
3690 						Com_Printf( "(%i)%i %s vol = %i\n", ch->entnum, i, ch->thesfx->sSoundName, s_entityWavVol[ ch->entnum ] );
3691 					}
3692 				}
3693 			}
3694 		}
3695 	}
3696 }
3697 
3698 /*
3699 ===============================================================================
3700 
3701 console functions
3702 
3703 ===============================================================================
3704 */
3705 
S_Play_f(void)3706 static void S_Play_f( void ) {
3707 	int 	i;
3708 	sfxHandle_t	h;
3709 	char name[256];
3710 
3711 	i = 1;
3712 	while ( i<Cmd_Argc() ) {
3713 		if ( !strrchr(Cmd_Argv(i), '.') ) {
3714 			Com_sprintf( name, sizeof(name), "%s.wav", Cmd_Argv(1) );
3715 		} else {
3716 			Q_strncpyz( name, Cmd_Argv(i), sizeof(name) );
3717 		}
3718 		h = S_RegisterSound( name );
3719 		if( h ) {
3720 			S_StartLocalSound( h, CHAN_LOCAL_SOUND );
3721 		}
3722 		i++;
3723 	}
3724 }
3725 
S_Music_f(void)3726 static void S_Music_f( void ) {
3727 	int		c;
3728 
3729 	c = Cmd_Argc();
3730 
3731 	if ( c == 2 ) {
3732 		S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(1), qfalse );
3733 	} else if ( c == 3 ) {
3734 		S_StartBackgroundTrack( Cmd_Argv(1), Cmd_Argv(2), qfalse );
3735 	} else {
3736 		Com_Printf ("music <musicfile> [loopfile]\n");
3737 		return;
3738 	}
3739 }
3740 
S_StopMusic_f(void)3741 static void S_StopMusic_f( void ) {
3742 	S_StopBackgroundTrack();
3743 }
3744 
3745 // a debug function, but no harm to leave in...
3746 //
S_SetDynamicMusic_f(void)3747 static void S_SetDynamicMusic_f(void)
3748 {
3749 	int c = Cmd_Argc();
3750 
3751 	if ( c == 2 )
3752 	{
3753 		if (bMusic_IsDynamic)
3754 		{
3755 			// don't need to check existance of 'explore' or 'action' music, since music wouldn't
3756 			//	be counted as dynamic if either were missing, but other types are optional...
3757 			//
3758 			if (!Q_stricmp(Cmd_Argv(1),"explore"))
3759 			{
3760 				S_SetDynamicMusicState( eBGRNDTRACK_EXPLORE );
3761 				return;
3762 			}
3763 			else
3764 			if (!Q_stricmp(Cmd_Argv(1),"action"))
3765 			{
3766 				S_SetDynamicMusicState( eBGRNDTRACK_ACTION );
3767 				return;
3768 			}
3769 			else
3770 			if (!Q_stricmp(Cmd_Argv(1),"silence"))
3771 			{
3772 				S_SetDynamicMusicState( eBGRNDTRACK_SILENCE );
3773 				return;
3774 			}
3775 			else
3776 			if (!Q_stricmp(Cmd_Argv(1),"boss"))
3777 			{
3778 				if (tMusic_Info[ eBGRNDTRACK_BOSS ].bExists)
3779 				{
3780 					S_SetDynamicMusicState( eBGRNDTRACK_BOSS );
3781 				}
3782 				else
3783 				{
3784 					Com_Printf("No 'boss' music defined in current dynamic set\n");
3785 				}
3786 				return;
3787 			}
3788 			else
3789 			if (!Q_stricmp(Cmd_Argv(1),"death"))
3790 			{
3791 				if (tMusic_Info[ eBGRNDTRACK_DEATH ].bExists)
3792 				{
3793 					S_SetDynamicMusicState( eBGRNDTRACK_DEATH );
3794 				}
3795 				else
3796 				{
3797 					Com_Printf("No 'death' music defined in current dynamic set\n");
3798 				}
3799 				return;
3800 			}
3801 		}
3802 		else
3803 		{
3804 			DynamicMusicInfoPrint();	// print "inactive" string
3805 			return;
3806 		}
3807 	}
3808 
3809 	// show usage...
3810 	//
3811 	Com_Printf("Usage: s_dynamic <explore/action/silence/boss/death>\n");
3812 	DynamicMusicInfoPrint();
3813 }
3814 
3815 // this table needs to be in-sync with the typedef'd enum "SoundCompressionMethod_t"...	-ste
3816 //
3817 static const char *sSoundCompressionMethodStrings[ct_NUMBEROF] =
3818 {
3819 	"16b",	// ct_16
3820 	"mp3"	// ct_MP3
3821 };
S_SoundList_f(void)3822 void S_SoundList_f( void ) {
3823 	int		i;
3824 	sfx_t	*sfx;
3825 	int		size, total;
3826 	int		iVariantCap = -1;	// for %d-inquiry stuff
3827 	int		iTotalBytes = 0;
3828 	char	*arg = NULL;
3829 
3830 	qboolean bWavOnly = qfalse;
3831 	qboolean bShouldBeMP3 = qfalse;
3832 
3833 	if ( Cmd_Argc() == 2 )
3834 	{
3835 		arg = Cmd_Argv(1);
3836 		if (!Q_stricmp(arg, "shouldbeMP3"))
3837 		{
3838 			bShouldBeMP3 = qtrue;
3839 		}
3840 		else if (!Q_stricmp(arg, "wavonly"))
3841 		{
3842 			bWavOnly = qtrue;
3843 		}
3844 		else
3845 		{
3846 			if (!Q_stricmp(arg, "1"))
3847 			{
3848 				iVariantCap = 1;
3849 			}
3850 			else
3851 			if (!Q_stricmp(arg, "2"))
3852 			{
3853 				iVariantCap = 2;
3854 			}
3855 			else
3856 			if (!Q_stricmp(arg, "3"))
3857 			{
3858 				iVariantCap = 3;
3859 			}
3860 		}
3861 	}
3862 	else
3863 	{
3864 		Com_Printf("( additional (mutually exclusive) options available:\n'wavonly', 'ShouldBeMP3', '1'/'2'/'3' for %%d-variant capping )\n" );
3865 	}
3866 
3867 	total = 0;
3868 
3869 	Com_Printf("\n");
3870 	Com_Printf("                    InMemory?\n");
3871 	Com_Printf("                    |\n");
3872 	Com_Printf("                    |  LevelLastUsedOn\n");
3873 	Com_Printf("                    |  |\n");
3874 	Com_Printf("                    |  |\n");
3875 	Com_Printf(" Slot   Smpls Type  |  |   Name\n");
3876 //	Com_Printf(" Slot   Smpls Type  InMem?   Name\n");
3877 
3878 	for (sfx=s_knownSfx, i=0 ; i<s_numSfx ; i++, sfx++)
3879 	{
3880 		extern cvar_t *cv_MP3overhead;
3881 		qboolean bMP3DumpOverride = (qboolean)(bShouldBeMP3 && cv_MP3overhead && !sfx->bDefaultSound && !sfx->pMP3StreamHeader && sfx->pSoundData && (Z_Size(sfx->pSoundData) > cv_MP3overhead->integer));
3882 
3883 		if (bMP3DumpOverride || (!bShouldBeMP3 && (!bWavOnly || sfx->eSoundCompressionMethod == ct_16)))
3884 		{
3885 			qboolean bDumpThisOne = qtrue;
3886 			if (iVariantCap >= 1 && iVariantCap <= 3)
3887 			{
3888 				int iStrLen = strlen(sfx->sSoundName);
3889 				if (iStrLen > 2)	// crash-safety, jic.
3890 				{
3891 					char c  = sfx->sSoundName[iStrLen-1];
3892 					char c2 = sfx->sSoundName[iStrLen-2];
3893 					if (!isdigit(c2) // quick-avoid of stuff like "pain75"
3894 						&& isdigit(c) && atoi(va("%c",c)) > iVariantCap)
3895 					{
3896 						// need to see if this %d-variant should be omitted, in other words if there's a %1 version then skip this...
3897 						//
3898 						char sFindName[MAX_QPATH];
3899 						Q_strncpyz(sFindName,sfx->sSoundName,sizeof(sFindName));
3900 						sFindName[iStrLen-1] = '1';
3901 						int i2;
3902 						sfx_t *sfx2;
3903 						for (sfx2 = s_knownSfx, i2=0 ; i2<s_numSfx ; i2++, sfx2++)
3904 						{
3905 							if (!Q_stricmp(sFindName,sfx2->sSoundName))
3906 							{
3907 								bDumpThisOne = qfalse;	// found a %1-variant of this, so use variant capping and ignore this sfx_t
3908 								break;
3909 							}
3910 						}
3911 					}
3912 				}
3913 			}
3914 
3915 			size = sfx->iSoundLengthInSamples;
3916 			if (sfx->bDefaultSound)
3917 			{
3918 				Com_Printf("%5d Missing file: \"%s\"\n", i, sfx->sSoundName );
3919 			}
3920 			else
3921 			{
3922 				if (bDumpThisOne)
3923 				{
3924 					iTotalBytes += (sfx->bInMemory && sfx->pSoundData) ? Z_Size(sfx->pSoundData) : 0;
3925 					iTotalBytes += (sfx->bInMemory && sfx->pMP3StreamHeader) ? sizeof(*sfx->pMP3StreamHeader) : 0;
3926 					total		+=  sfx->bInMemory ? size : 0;
3927 				}
3928 				Com_Printf("%5d %7i [%s] %s %2d %s", i, size, sSoundCompressionMethodStrings[sfx->eSoundCompressionMethod], sfx->bInMemory?"y":"n", sfx->iLastLevelUsedOn, sfx->sSoundName );
3929 
3930 				if (!bDumpThisOne)
3931 				{
3932 					Com_Printf("   ( Skipping, variant capped )");
3933 					//Com_OPrintf("Variant capped: %s\n",sfx->sSoundName);
3934 				}
3935 				Com_Printf("\n");
3936 			}
3937 		}
3938 	}
3939 	Com_Printf(" Slot   Smpls Type  In? Lev  Name\n");
3940 
3941 	Com_Printf ("Total resident samples: %i %s ( not mem usage, see 'meminfo' ).\n", total, bWavOnly?"(WAV only)":"");
3942 	Com_Printf ("%d out of %d sfx_t slots used\n", s_numSfx, MAX_SFX);
3943 	Com_Printf ("%.2fMB bytes used when counting sfx_t->pSoundData + MP3 headers (if any)\n", (float)iTotalBytes / 1024.0f / 1024.0f);
3944 	S_DisplayFreeMemory();
3945 }
3946 
3947 /*
3948 ===============================================================================
3949 
3950 background music functions
3951 
3952 ===============================================================================
3953 */
3954 
FGetLittleLong(fileHandle_t f)3955 int	FGetLittleLong( fileHandle_t f ) {
3956 	int		v;
3957 
3958 	FS_Read( &v, sizeof(v), f );
3959 
3960 	return LittleLong( v);
3961 }
3962 
FGetLittleShort(fileHandle_t f)3963 int	FGetLittleShort( fileHandle_t f ) {
3964 	short	v;
3965 
3966 	FS_Read( &v, sizeof(v), f );
3967 
3968 	return LittleShort( v);
3969 }
3970 
3971 // returns the length of the data in the chunk, or 0 if not found
S_FindWavChunk(fileHandle_t f,char * chunk)3972 int S_FindWavChunk( fileHandle_t f, char *chunk ) {
3973 	char	name[5];
3974 	int		len;
3975 	int		r;
3976 
3977 	name[4] = 0;
3978 	len = 0;
3979 	r = FS_Read( name, 4, f );
3980 	if ( r != 4 ) {
3981 		return 0;
3982 	}
3983 	len = FGetLittleLong( f );
3984 	if ( len < 0 || len > 0xfffffff ) {
3985 		len = 0;
3986 		return 0;
3987 	}
3988 	len = (len + 1 ) & ~1;		// pad to word boundary
3989 //	s_nextWavChunk += len + 8;
3990 
3991 	if ( strcmp( name, chunk ) ) {
3992 		return 0;
3993 	}
3994 	return len;
3995 }
3996 
3997 // fixme: need to move this into qcommon sometime?, but too much stuff altered by other people and I won't be able
3998 //	to compile again for ages if I check that out...
3999 //
4000 // DO NOT replace this with a call to FS_FileExists, that's for checking about writing out, and doesn't work for this.
4001 //
S_FileExists(const char * psFilename)4002 qboolean S_FileExists( const char *psFilename )
4003 {
4004 	fileHandle_t fhTemp;
4005 
4006 	FS_FOpenFileRead (psFilename, &fhTemp, qtrue);	// qtrue so I can fclose the handle without closing a PAK
4007 	if (!fhTemp)
4008 		return qfalse;
4009 
4010 	FS_FCloseFile(fhTemp);
4011 	return qtrue;
4012 }
4013 
4014 // some stuff for streaming MP3 files from disk (not pleasant, but nothing about MP3 is, other than compression ratios...)
4015 //
MP3MusicStream_Reset(MusicInfo_t * pMusicInfo)4016 static void MP3MusicStream_Reset(MusicInfo_t *pMusicInfo)
4017 {
4018 	pMusicInfo->iMP3MusicStream_DiskReadPos		= 0;
4019 	pMusicInfo->iMP3MusicStream_DiskWindowPos	= 0;
4020 }
4021 
4022 //
4023 // return is where the decoder should read from...
4024 //
MP3MusicStream_ReadFromDisk(MusicInfo_t * pMusicInfo,int iReadOffset,int iReadBytesNeeded)4025 static byte *MP3MusicStream_ReadFromDisk(MusicInfo_t *pMusicInfo, int iReadOffset, int iReadBytesNeeded)
4026 {
4027 	if (iReadOffset < pMusicInfo->iMP3MusicStream_DiskWindowPos)
4028 	{
4029 		assert(0);											// should never happen
4030 		return pMusicInfo->byMP3MusicStream_DiskBuffer;		// ...but return something safe anyway
4031 	}
4032 
4033 	while (iReadOffset + iReadBytesNeeded > pMusicInfo->iMP3MusicStream_DiskReadPos)
4034 	{
4035 		int iBytesRead = FS_Read( pMusicInfo->byMP3MusicStream_DiskBuffer + (pMusicInfo->iMP3MusicStream_DiskReadPos - pMusicInfo->iMP3MusicStream_DiskWindowPos), iMP3MusicStream_DiskBytesToRead, pMusicInfo->s_backgroundFile );
4036 
4037 		pMusicInfo->iMP3MusicStream_DiskReadPos += iBytesRead;
4038 
4039 		if (iBytesRead != iMP3MusicStream_DiskBytesToRead)	// quietly ignore any requests to read past file end
4040 		{
4041 			break;		// we need to do this because the disk read code can't know how much source data we need to
4042 						//	read for a given number of requested output bytes, so we'll always be asking for too many
4043 		}
4044 	}
4045 
4046 	// if reached halfway point in buffer (approx 20k), backscroll it...
4047 	//
4048 	if (pMusicInfo->iMP3MusicStream_DiskReadPos - pMusicInfo->iMP3MusicStream_DiskWindowPos > iMP3MusicStream_DiskBufferSize/2)
4049 	{
4050 		int iMoveSrcOffset = iReadOffset - pMusicInfo->iMP3MusicStream_DiskWindowPos;
4051 		int iMoveCount     = (pMusicInfo->iMP3MusicStream_DiskReadPos - pMusicInfo->iMP3MusicStream_DiskWindowPos ) - iMoveSrcOffset;
4052 		memmove( &pMusicInfo->byMP3MusicStream_DiskBuffer, &pMusicInfo->byMP3MusicStream_DiskBuffer[iMoveSrcOffset], iMoveCount);
4053 		pMusicInfo->iMP3MusicStream_DiskWindowPos += iMoveSrcOffset;
4054 	}
4055 
4056 	return pMusicInfo->byMP3MusicStream_DiskBuffer + (iReadOffset - pMusicInfo->iMP3MusicStream_DiskWindowPos);
4057 }
4058 
4059 // does NOT set s_rawend!...
4060 //
S_StopBackgroundTrack_Actual(MusicInfo_t * pMusicInfo)4061 static void S_StopBackgroundTrack_Actual( MusicInfo_t *pMusicInfo )
4062 {
4063 	if ( pMusicInfo->s_backgroundFile )
4064 	{
4065 		if ( pMusicInfo->s_backgroundFile != -1)
4066 		{
4067 			FS_FCloseFile( pMusicInfo->s_backgroundFile );
4068 		}
4069 		pMusicInfo->s_backgroundFile = 0;
4070 	}
4071 }
4072 
FreeMusic(MusicInfo_t * pMusicInfo)4073 static void FreeMusic( MusicInfo_t *pMusicInfo )
4074 {
4075 	if (pMusicInfo->pLoadedData)
4076 	{
4077 		Z_Free(pMusicInfo->pLoadedData);
4078 		pMusicInfo->pLoadedData		= NULL;		// these two MUST be kept as valid/invalid together
4079 		pMusicInfo->sLoadedDataName[0]= '\0';	//
4080 		pMusicInfo->iLoadedDataLen	= 0;
4081 	}
4082 }
4083 
4084 // called only by snd_shutdown (from snd_restart or app exit)
4085 //
S_UnCacheDynamicMusic(void)4086 void S_UnCacheDynamicMusic( void )
4087 {
4088 	for (int i = eBGRNDTRACK_DATABEGIN; i != eBGRNDTRACK_DATAEND; i++)
4089 	{
4090 		FreeMusic( &tMusic_Info[i]);
4091 	}
4092 }
4093 
S_StartBackgroundTrack_Actual(MusicInfo_t * pMusicInfo,qboolean qbDynamic,const char * intro,const char * loop)4094 static qboolean S_StartBackgroundTrack_Actual( MusicInfo_t *pMusicInfo, qboolean qbDynamic, const char *intro, const char *loop )
4095 {
4096 	int		len;
4097 	char	dump[16];
4098 	char	name[MAX_QPATH];
4099 
4100 	Q_strncpyz( sMusic_BackgroundLoop, loop, sizeof( sMusic_BackgroundLoop ));
4101 
4102 	Q_strncpyz( name, intro, sizeof( name ) - 4 );	// this seems to be so that if the filename hasn't got an extension
4103 													//	but doesn't have the room to append on either then you'll just
4104 													//	get the "soft" fopen() error, rather than the ERR_DROP you'd get
4105 													//	if COM_DefaultExtension didn't have room to add it on.
4106 	COM_DefaultExtension( name, sizeof( name ), ".mp3" );
4107 
4108 	// close the background track, but DON'T reset s_rawend (or remaining music bits that haven't been output yet will be cut off)
4109 	//
4110 	S_StopBackgroundTrack_Actual( pMusicInfo );
4111 
4112 	pMusicInfo->bIsMP3 = qfalse;
4113 
4114 	if ( !intro[0] ) {
4115 		return qfalse;
4116 	}
4117 
4118 	// new bit, if file requested is not same any loaded one (if prev was in-mem), ditch it...
4119 	//
4120 	if (Q_stricmp(name, pMusicInfo->sLoadedDataName))
4121 	{
4122 		FreeMusic( pMusicInfo );
4123 	}
4124 
4125 	if (!Q_stricmpn(name+(strlen(name)-4),".mp3",4))
4126 	{
4127 		if (pMusicInfo->pLoadedData)
4128 		{
4129 			pMusicInfo->s_backgroundFile = -1;
4130 		}
4131 		else
4132 		{
4133 			pMusicInfo->iLoadedDataLen = FS_FOpenFileRead( name, &pMusicInfo->s_backgroundFile, qtrue );
4134 		}
4135 
4136 		if (!pMusicInfo->s_backgroundFile)
4137 		{
4138 			Com_Printf( S_COLOR_RED"Couldn't open music file %s\n", name );
4139 			return qfalse;
4140 		}
4141 
4142 		MP3MusicStream_Reset( pMusicInfo );
4143 
4144 		byte *pbMP3DataSegment	= NULL;
4145 		int iInitialMP3ReadSize = 8192;		// fairly arbitrary, whatever size this is then the decoder is allowed to
4146 											// scan up to halfway of it to find floating headers, so don't make it
4147 											// too small. 8k works fine.
4148 		qboolean bMusicSucceeded = qfalse;
4149 		if (qbDynamic)
4150 		{
4151 			if (!pMusicInfo->pLoadedData)
4152 			{
4153 				pMusicInfo->pLoadedData = (byte *) Z_Malloc(pMusicInfo->iLoadedDataLen, TAG_SND_DYNAMICMUSIC, qfalse);
4154 
4155 				S_ClearSoundBuffer();
4156 				FS_Read(pMusicInfo->pLoadedData, pMusicInfo->iLoadedDataLen, pMusicInfo->s_backgroundFile);
4157 				Q_strncpyz(pMusicInfo->sLoadedDataName, name, sizeof(pMusicInfo->sLoadedDataName));
4158 			}
4159 
4160 			// enable the rest of the code to work as before...
4161 			//
4162 			pbMP3DataSegment	= pMusicInfo->pLoadedData;
4163 			iInitialMP3ReadSize = pMusicInfo->iLoadedDataLen;
4164 		}
4165 		else
4166 		{
4167 			pbMP3DataSegment = MP3MusicStream_ReadFromDisk(pMusicInfo, 0, iInitialMP3ReadSize);
4168 		}
4169 
4170 		if (MP3_IsValid(name, pbMP3DataSegment, iInitialMP3ReadSize, qtrue /*bStereoDesired*/))
4171 		{
4172 			// init stream struct...
4173 			//
4174 			memset(&pMusicInfo->streamMP3_Bgrnd,0,sizeof(pMusicInfo->streamMP3_Bgrnd));
4175 			char *psError = C_MP3Stream_DecodeInit( &pMusicInfo->streamMP3_Bgrnd, pbMP3DataSegment, pMusicInfo->iLoadedDataLen,
4176 													dma.speed,
4177 													16,		// sfx->width * 8,
4178 													qtrue	// bStereoDesired
4179 													);
4180 
4181 			if (psError == NULL)
4182 			{
4183 				// init sfx struct & setup the few fields I actually need...
4184 				//
4185 				memset(	   &pMusicInfo->sfxMP3_Bgrnd,0,sizeof(pMusicInfo->sfxMP3_Bgrnd));
4186 				//			pMusicInfo->sfxMP3_Bgrnd.width					= 2;			// read by MP3_GetSamples()
4187 							pMusicInfo->sfxMP3_Bgrnd.iSoundLengthInSamples	= 0x7FFFFFFF;	// max possible +ve int, since music finishes when decoder stops
4188 							pMusicInfo->sfxMP3_Bgrnd.pMP3StreamHeader		= &pMusicInfo->streamMP3_Bgrnd;
4189 				Q_strncpyz( pMusicInfo->sfxMP3_Bgrnd.sSoundName, name, sizeof(pMusicInfo->sfxMP3_Bgrnd.sSoundName) );
4190 
4191 				if (qbDynamic)
4192 				{
4193 					MP3Stream_InitPlayingTimeFields ( &pMusicInfo->streamMP3_Bgrnd, name, pbMP3DataSegment, pMusicInfo->iLoadedDataLen, qtrue);
4194 				}
4195 
4196 				pMusicInfo->s_backgroundInfo.format		= WAV_FORMAT_MP3;	// not actually used this way, but just ensures we don't match one of the legit formats
4197 				pMusicInfo->s_backgroundInfo.channels	= 2;		// always, for our MP3s when used for music (else 1 for FX)
4198 				pMusicInfo->s_backgroundInfo.rate		= dma.speed;
4199 				pMusicInfo->s_backgroundInfo.width		= 2;		// always, for our MP3s
4200 				pMusicInfo->s_backgroundInfo.samples	= pMusicInfo->sfxMP3_Bgrnd.iSoundLengthInSamples;
4201 				pMusicInfo->s_backgroundSamples			= pMusicInfo->sfxMP3_Bgrnd.iSoundLengthInSamples;
4202 
4203 				memset(&pMusicInfo->chMP3_Bgrnd,0,sizeof(pMusicInfo->chMP3_Bgrnd));
4204 						pMusicInfo->chMP3_Bgrnd.thesfx = &pMusicInfo->sfxMP3_Bgrnd;
4205 				memcpy(&pMusicInfo->chMP3_Bgrnd.MP3StreamHeader, pMusicInfo->sfxMP3_Bgrnd.pMP3StreamHeader, sizeof(*pMusicInfo->sfxMP3_Bgrnd.pMP3StreamHeader));
4206 
4207 				if (qbDynamic)
4208 				{
4209 					if (pMusicInfo->s_backgroundFile != -1)
4210 					{
4211 						FS_FCloseFile( pMusicInfo->s_backgroundFile );
4212 						pMusicInfo->s_backgroundFile = -1;	// special mp3 value for "valid, but not a real file"
4213 					}
4214 				}
4215 
4216 				pMusicInfo->bIsMP3 = qtrue;
4217 				bMusicSucceeded = qtrue;
4218 			}
4219 			else
4220 			{
4221 				Com_Printf(S_COLOR_RED"Error streaming file %s: %s\n", name, psError);
4222 				if (pMusicInfo->s_backgroundFile != -1)
4223 				{
4224 					FS_FCloseFile( pMusicInfo->s_backgroundFile );
4225 				}
4226 				pMusicInfo->s_backgroundFile = 0;
4227 			}
4228 		}
4229 		else
4230 		{
4231 			// MP3_IsValid() will already have printed any errors via Com_Printf at this point...
4232 			//
4233 			if (pMusicInfo->s_backgroundFile != -1)
4234 			{
4235 				FS_FCloseFile( pMusicInfo->s_backgroundFile );
4236 			}
4237 			pMusicInfo->s_backgroundFile = 0;
4238 		}
4239 
4240 		return bMusicSucceeded;
4241 	}
4242 	else	// not an mp3 file
4243 	{
4244 		//
4245 		// open up a wav file and get all the info
4246 		//
4247 		FS_FOpenFileRead( name, &pMusicInfo->s_backgroundFile, qtrue );
4248 		if ( !pMusicInfo->s_backgroundFile ) {
4249 			Com_Printf( S_COLOR_YELLOW "WARNING: couldn't open music file %s\n", name );
4250 			return qfalse;
4251 		}
4252 
4253 		// skip the riff wav header
4254 
4255 		FS_Read(dump, 12, pMusicInfo->s_backgroundFile);
4256 
4257 		if ( !S_FindWavChunk( pMusicInfo->s_backgroundFile, "fmt " ) ) {
4258 			Com_Printf( S_COLOR_YELLOW "WARNING: No fmt chunk in %s\n", name );
4259 			FS_FCloseFile( pMusicInfo->s_backgroundFile );
4260 			pMusicInfo->s_backgroundFile = 0;
4261 			return qfalse;
4262 		}
4263 
4264 		// save name for soundinfo
4265 		pMusicInfo->s_backgroundInfo.format = FGetLittleShort( pMusicInfo->s_backgroundFile );
4266 		pMusicInfo->s_backgroundInfo.channels = FGetLittleShort( pMusicInfo->s_backgroundFile );
4267 		pMusicInfo->s_backgroundInfo.rate = FGetLittleLong( pMusicInfo->s_backgroundFile );
4268 		FGetLittleLong(  pMusicInfo->s_backgroundFile );
4269 		FGetLittleShort(  pMusicInfo->s_backgroundFile );
4270 		pMusicInfo->s_backgroundInfo.width = FGetLittleShort( pMusicInfo->s_backgroundFile ) / 8;
4271 
4272 		if ( pMusicInfo->s_backgroundInfo.format != WAV_FORMAT_PCM ) {
4273 			FS_FCloseFile( pMusicInfo->s_backgroundFile );
4274 			pMusicInfo->s_backgroundFile = 0;
4275 			Com_Printf(S_COLOR_YELLOW "WARNING: Not a microsoft PCM format wav: %s\n", name);
4276 			return qfalse;
4277 		}
4278 
4279 		if ( pMusicInfo->s_backgroundInfo.channels != 2 || pMusicInfo->s_backgroundInfo.rate != 22050 ) {
4280 			Com_Printf(S_COLOR_YELLOW "WARNING: music file %s is not 22k stereo\n", name );
4281 		}
4282 
4283 		if ( ( len = S_FindWavChunk( pMusicInfo->s_backgroundFile, "data" ) ) == 0 ) {
4284 			FS_FCloseFile( pMusicInfo->s_backgroundFile );
4285 			pMusicInfo->s_backgroundFile = 0;
4286 			Com_Printf(S_COLOR_YELLOW "WARNING: No data chunk in %s\n", name);
4287 			return qfalse;
4288 		}
4289 
4290 		pMusicInfo->s_backgroundInfo.samples = len / (pMusicInfo->s_backgroundInfo.width * pMusicInfo->s_backgroundInfo.channels);
4291 
4292 		pMusicInfo->s_backgroundSamples = pMusicInfo->s_backgroundInfo.samples;
4293 	}
4294 
4295 	return qtrue;
4296 }
4297 
S_SwitchDynamicTracks(MusicState_e eOldState,MusicState_e eNewState,qboolean bNewTrackStartsFullVolume)4298 static void S_SwitchDynamicTracks( MusicState_e eOldState, MusicState_e eNewState, qboolean bNewTrackStartsFullVolume )
4299 {
4300 	// copy old track into fader...
4301 	//
4302 	tMusic_Info[ eBGRNDTRACK_FADE ] = tMusic_Info[ eOldState ];
4303 //	tMusic_Info[ eBGRNDTRACK_FADE ].bActive = qtrue;	// inherent
4304 //	tMusic_Info[ eBGRNDTRACK_FADE ].bExists = qtrue;	// inherent
4305 	tMusic_Info[ eBGRNDTRACK_FADE ].iXFadeVolumeSeekTime= Sys_Milliseconds();
4306 	tMusic_Info[ eBGRNDTRACK_FADE ].iXFadeVolumeSeekTo	= 0;
4307 	//
4308 	// ... and deactivate...
4309 	//
4310 	tMusic_Info[ eOldState ].bActive = qfalse;
4311 	//
4312 	// set new track to either full volume or fade up...
4313 	//
4314 	tMusic_Info[eNewState].bActive				= qtrue;
4315 	tMusic_Info[eNewState].iXFadeVolumeSeekTime	= Sys_Milliseconds();
4316 	tMusic_Info[eNewState].iXFadeVolumeSeekTo	= 255;
4317 	tMusic_Info[eNewState].iXFadeVolume			= bNewTrackStartsFullVolume ? 255 : 0;
4318 
4319 	eMusic_StateActual = eNewState;
4320 
4321 	if (s_debugdynamic->integer)
4322 	{
4323 		const char *psNewStateString = Music_BaseStateToString( eNewState, qtrue );
4324 				psNewStateString = psNewStateString?psNewStateString:"<unknown>";
4325 
4326 		Com_Printf( S_COLOR_MAGENTA "S_SwitchDynamicTracks( \"%s\" )\n", psNewStateString );
4327 	}
4328 }
4329 
4330 // called by both the config-string parser and the console-command state-changer...
4331 //
4332 // This either changes the music right now (copying track structures etc), or leaves the new state as pending
4333 //	so it gets picked up by the general music player if in a transition that can't be overridden...
4334 //
S_SetDynamicMusicState(MusicState_e eNewState)4335 static void S_SetDynamicMusicState( MusicState_e eNewState )
4336 {
4337 	if (eMusic_StateRequest != eNewState)
4338 	{
4339 		eMusic_StateRequest  = eNewState;
4340 
4341 		if (s_debugdynamic->integer)
4342 		{
4343 			const char *psNewStateString = Music_BaseStateToString( eNewState, qtrue );
4344 					psNewStateString = psNewStateString?psNewStateString:"<unknown>";
4345 
4346 			Com_Printf( S_COLOR_MAGENTA "S_SetDynamicMusicState( Request: \"%s\" )\n", psNewStateString );
4347 		}
4348 	}
4349 }
4350 
S_HandleDynamicMusicStateChange(void)4351 static void S_HandleDynamicMusicStateChange( void )
4352 {
4353 	if (eMusic_StateRequest != eMusic_StateActual)
4354 	{
4355 		// check whether or not the new request can be honoured, given what's currently playing...
4356 		//
4357 		if (Music_StateCanBeInterrupted( eMusic_StateActual, eMusic_StateRequest ))
4358 		{
4359 			LP_MP3STREAM pMP3StreamActual = &tMusic_Info[ eMusic_StateActual ].chMP3_Bgrnd.MP3StreamHeader;
4360 
4361 			switch (eMusic_StateRequest)
4362 			{
4363 				case eBGRNDTRACK_EXPLORE:	// ... from action or silence
4364 				{
4365 					switch (eMusic_StateActual)
4366 					{
4367 						case eBGRNDTRACK_ACTION:	// action->explore
4368 						{
4369 							// find the transition track to play, and the entry point for explore when we get there,
4370 							//	and also see if we're at a permitted exit point to switch at all...
4371 							//
4372 							float fPlayingTimeElapsed = MP3Stream_GetPlayingTimeInSeconds( pMP3StreamActual ) - MP3Stream_GetRemainingTimeInSeconds( pMP3StreamActual );
4373 
4374 							// supply:
4375 							//
4376 							// playing point in float seconds
4377 							// enum of track being queried
4378 							//
4379 							// get:
4380 							//
4381 							// enum of transition track to switch to
4382 							// float time of entry point of new track *after* transition
4383 
4384 							MusicState_e	eTransition;
4385 							float			fNewTrackEntryTime = 0.0f;
4386 							if (Music_AllowedToTransition( fPlayingTimeElapsed, eBGRNDTRACK_ACTION, &eTransition, &fNewTrackEntryTime))
4387 							{
4388 								S_SwitchDynamicTracks( eMusic_StateActual, eTransition, qfalse );	// qboolean bNewTrackStartsFullVolume
4389 
4390 								tMusic_Info[eTransition].Rewind();
4391 								tMusic_Info[eTransition].bTrackSwitchPending	= qtrue;
4392 								tMusic_Info[eTransition].eTS_NewState			= eMusic_StateRequest;
4393 								tMusic_Info[eTransition].fTS_NewTime			= fNewTrackEntryTime;
4394 							}
4395 						}
4396 						break;
4397 
4398 						case eBGRNDTRACK_SILENCE:	// silence->explore
4399 						{
4400 							S_SwitchDynamicTracks( eMusic_StateActual, eMusic_StateRequest, qfalse );	// qboolean bNewTrackStartsFullVolume
4401 
4402 //							float fEntryTime = Music_GetRandomEntryTime( eMusic_StateRequest );
4403 //							tMusic_Info[ eMusic_StateRequest ].SeekTo(fEntryTime);
4404 							tMusic_Info[ eMusic_StateRequest ].Rewind();
4405 						}
4406 						break;
4407 
4408 						default:	// trying to transition from some state I wasn't aware you could transition from (shouldn't happen), so ignore
4409 						{
4410 							assert(0);
4411 							S_SwitchDynamicTracks( eMusic_StateActual, eBGRNDTRACK_SILENCE, qfalse );	// qboolean bNewTrackStartsFullVolume
4412 						}
4413 						break;
4414 					}
4415 				}
4416 				break;
4417 
4418 				case eBGRNDTRACK_SILENCE:	// from explore or action
4419 				{
4420 					switch (eMusic_StateActual)
4421 					{
4422 						case eBGRNDTRACK_ACTION:	// action->silence
4423 						case eBGRNDTRACK_EXPLORE:	// explore->silence
4424 						{
4425 							// find the transition track to play, and the entry point for explore when we get there,
4426 							//	and also see if we're at a permitted exit point to switch at all...
4427 							//
4428 							float fPlayingTimeElapsed = MP3Stream_GetPlayingTimeInSeconds( pMP3StreamActual ) - MP3Stream_GetRemainingTimeInSeconds( pMP3StreamActual );
4429 
4430 							MusicState_e	eTransition;
4431 							float			fNewTrackEntryTime = 0.0f;
4432 							if (Music_AllowedToTransition( fPlayingTimeElapsed, eMusic_StateActual, &eTransition, &fNewTrackEntryTime))
4433 							{
4434 								S_SwitchDynamicTracks( eMusic_StateActual, eTransition, qfalse );	// qboolean bNewTrackStartsFullVolume
4435 
4436 								tMusic_Info[eTransition].Rewind();
4437 								tMusic_Info[eTransition].bTrackSwitchPending	= qtrue;
4438 								tMusic_Info[eTransition].eTS_NewState			= eMusic_StateRequest;
4439 								tMusic_Info[eTransition].fTS_NewTime			= 0.0f;	//fNewTrackEntryTime;  irrelevant when switching to silence
4440 							}
4441 						}
4442 						break;
4443 
4444 						default:		// some unhandled type switching to silence
4445 							assert(0);	// fall through since boss case just does silence->switch anyway
4446 
4447 						case eBGRNDTRACK_BOSS:	// boss->silence
4448 						{
4449 							S_SwitchDynamicTracks( eMusic_StateActual, eBGRNDTRACK_SILENCE, qfalse );	// qboolean bNewTrackStartsFullVolume
4450 						}
4451 						break;
4452 					}
4453 				}
4454 				break;
4455 
4456 				case eBGRNDTRACK_ACTION:	// anything->action
4457 				{
4458 					switch (eMusic_StateActual)
4459 					{
4460 						case eBGRNDTRACK_SILENCE:	// silence->action
4461 						{
4462 							S_SwitchDynamicTracks( eMusic_StateActual, eMusic_StateRequest, qfalse );	// qboolean bNewTrackStartsFullVolume
4463 							tMusic_Info[ eMusic_StateRequest ].Rewind();
4464 						}
4465 						break;
4466 
4467 						default:	// !silence->action
4468 						{
4469 							S_SwitchDynamicTracks( eMusic_StateActual, eMusic_StateRequest, qtrue );	// qboolean bNewTrackStartsFullVolume
4470 							float fEntryTime = Music_GetRandomEntryTime( eMusic_StateRequest );
4471 							tMusic_Info[ eMusic_StateRequest ].SeekTo(fEntryTime);
4472 						}
4473 						break;
4474 					}
4475 				}
4476 				break;
4477 
4478 				case eBGRNDTRACK_BOSS:
4479 				{
4480 					S_SwitchDynamicTracks( eMusic_StateActual, eMusic_StateRequest, qfalse );	// qboolean bNewTrackStartsFullVolume
4481 					//
4482 					// ( no need to fast forward or rewind, boss track is only entered into once, at start, and can't exit )
4483 					//
4484 				}
4485 				break;
4486 
4487 				case eBGRNDTRACK_DEATH:
4488 				{
4489 					S_SwitchDynamicTracks( eMusic_StateActual, eMusic_StateRequest, qtrue );	// qboolean bNewTrackStartsFullVolume
4490 					//
4491 					// ( no need to fast forward or rewind, death track is only entered into once, at start, and can't exit or loop)
4492 					//
4493 				}
4494 				break;
4495 
4496 				default: assert(0); break;	// unknown new mode request, so just ignore it
4497 			}
4498 		}
4499 	}
4500 }
4501 
4502 static char gsIntroMusic[MAX_QPATH]={0};
4503 static char gsLoopMusic [MAX_QPATH]={0};
4504 
S_RestartMusic(void)4505 void S_RestartMusic( void )
4506 {
4507 	if (s_soundStarted && !s_soundMuted )
4508 	{
4509 		//if (gsIntroMusic[0] || gsLoopMusic[0])	// dont test this anymore (but still *use* them), they're blank for JK2 dynamic-music levels anyway
4510 		{
4511 			MusicState_e ePrevState	= eMusic_StateRequest;
4512 			S_StartBackgroundTrack( gsIntroMusic, gsLoopMusic, qfalse );	// ( default music start will set the state to EXPLORE )
4513 			S_SetDynamicMusicState( ePrevState );					// restore to prev state
4514 		}
4515 	}
4516 }
4517 
4518 // Basic logic here is to see if the intro file specified actually exists, and if so, then it's not dynamic music,
4519 //	When called by the cgame start it loads up, then stops the playback (because of stutter issues), so that when the
4520 //	actual snapshot is received and the real play request is processed the data has already been loaded so will be quicker.
4521 //
4522 // to be honest, although the code still plays WAVs some of the file-check logic only works for MP3s, so if you ever want
4523 //	to use WAV music you'll have to do some tweaking below (but I've got other things to do so it'll have to wait - Ste)
4524 //
S_StartBackgroundTrack(const char * intro,const char * loop,qboolean bCalledByCGameStart)4525 void S_StartBackgroundTrack( const char *intro, const char *loop, qboolean bCalledByCGameStart )
4526 {
4527 	bMusic_IsDynamic = qfalse;
4528 
4529 	if (!s_soundStarted)
4530 	{	//we have no sound, so don't even bother trying
4531 		return;
4532 	}
4533 
4534 	if ( !intro ) {
4535 		intro = "";
4536 	}
4537 	if ( !loop || !loop[0] ) {
4538 		loop = intro;
4539 	}
4540 
4541 	if ( intro != gsIntroMusic ) {
4542 		Q_strncpyz( gsIntroMusic, intro, sizeof(gsIntroMusic) );
4543 	}
4544 	if ( loop != gsLoopMusic ) {
4545 		Q_strncpyz( gsLoopMusic, loop, sizeof(gsLoopMusic) );
4546 	}
4547 
4548 	char sNameIntro[MAX_QPATH];
4549 	char sNameLoop [MAX_QPATH];
4550 	Q_strncpyz(sNameIntro,	intro,	sizeof(sNameIntro));
4551 	Q_strncpyz(sNameLoop,	loop,	sizeof(sNameLoop));
4552 
4553 	COM_DefaultExtension( sNameIntro, sizeof( sNameIntro ), ".mp3" );
4554 	COM_DefaultExtension( sNameLoop,  sizeof( sNameLoop),	".mp3" );
4555 
4556 	// if dynamic music not allowed, then just stream the explore music instead of playing dynamic...
4557 	//
4558 	if (!s_allowDynamicMusic->integer && Music_DynamicDataAvailable(intro))	// "intro", NOT "sName" (i.e. don't use version with ".mp3" extension)
4559 	{
4560 		const char *psMusicName = Music_GetFileNameForState( eBGRNDTRACK_DATABEGIN );
4561 		if (psMusicName && S_FileExists( psMusicName ))
4562 		{
4563 			Q_strncpyz(sNameIntro,psMusicName,sizeof(sNameIntro));
4564 			Q_strncpyz(sNameLoop, psMusicName,sizeof(sNameLoop ));
4565 		}
4566 	}
4567 
4568 	// conceptually we always play the 'intro'[/sName] track, intro-to-loop transition is handled in UpdateBackGroundTrack().
4569 	//
4570 	if ( (strstr(sNameIntro,"/") && S_FileExists( sNameIntro )) )	// strstr() check avoids extra file-exists check at runtime if reverting from streamed music to dynamic since literal files all need at least one slash in their name (eg "music/blah")
4571 	{
4572 		const char *psLoopName = S_FileExists( sNameLoop ) ? sNameLoop : sNameIntro;
4573 		Com_DPrintf("S_StartBackgroundTrack: Found/using non-dynamic music track '%s' (loop: '%s')\n", sNameIntro, psLoopName);
4574 		S_StartBackgroundTrack_Actual( &tMusic_Info[eBGRNDTRACK_NONDYNAMIC], bMusic_IsDynamic, sNameIntro, psLoopName );
4575 	}
4576 	else
4577 	{
4578 		if (Music_DynamicDataAvailable(intro))	// "intro", NOT "sName" (i.e. don't use version with ".mp3" extension)
4579 		{
4580 			extern const char *Music_GetLevelSetName(void);
4581 			Q_strncpyz(sInfoOnly_CurrentDynamicMusicSet, Music_GetLevelSetName(), sizeof(sInfoOnly_CurrentDynamicMusicSet));
4582 			for (int i = eBGRNDTRACK_DATABEGIN; i != eBGRNDTRACK_DATAEND; i++)
4583 			{
4584 				qboolean bOk = qfalse;
4585 				const char *psMusicName = Music_GetFileNameForState( (MusicState_e) i);
4586 				if (psMusicName && (!Q_stricmp(tMusic_Info[i].sLoadedDataName, psMusicName) || S_FileExists( psMusicName )) )
4587 				{
4588 					bOk = S_StartBackgroundTrack_Actual( &tMusic_Info[i], qtrue, psMusicName, loop );
4589 				}
4590 
4591 				tMusic_Info[i].bExists = bOk;
4592 
4593 				if (!tMusic_Info[i].bExists)
4594 				{
4595 					FreeMusic( &tMusic_Info[i] );
4596 				}
4597 			}
4598 
4599 			//
4600 			// default all tracks to OFF first (and set any other vars)
4601 			//
4602 			for (int i=0; i<eBGRNDTRACK_NUMBEROF; i++)
4603 			{
4604 				tMusic_Info[i].bActive				= qfalse;
4605 				tMusic_Info[i].bTrackSwitchPending	= qfalse;
4606 				tMusic_Info[i].fSmoothedOutVolume	= 0.25f;
4607 			}
4608 
4609 			if (tMusic_Info[eBGRNDTRACK_EXPLORE].bExists &&
4610 				tMusic_Info[eBGRNDTRACK_ACTION ].bExists
4611 				)
4612 			{
4613 				Com_DPrintf("S_StartBackgroundTrack: Found dynamic music tracks\n");
4614 				bMusic_IsDynamic = qtrue;
4615 
4616 				//
4617 				// ... then start the default music state...
4618 				//
4619 				eMusic_StateActual = eMusic_StateRequest = eBGRNDTRACK_EXPLORE;
4620 
4621 				MusicInfo_t *pMusicInfo = &tMusic_Info[ eMusic_StateActual ];
4622 
4623 				pMusicInfo->bActive				= qtrue;
4624 				pMusicInfo->iXFadeVolumeSeekTime= Sys_Milliseconds();
4625 				pMusicInfo->iXFadeVolumeSeekTo	= 255;
4626 				pMusicInfo->iXFadeVolume		= 0;
4627 
4628 	//#ifdef _DEBUG
4629 	//			float fRemaining = MP3Stream_GetPlayingTimeInSeconds( &pMusicInfo->chMP3_Bgrnd.MP3StreamHeader);
4630 	//#endif
4631 			}
4632 			else
4633 			{
4634 				Com_Printf( S_COLOR_RED "Dynamic music did not have both 'action' and 'explore' versions, inhibiting...\n");
4635 				S_StopBackgroundTrack();
4636 			}
4637 		}
4638 		else
4639 		{
4640 			if (sNameIntro[0]!='.')	// blank name with ".mp3" or whatever attached - no error print out
4641 			{
4642 				Com_Printf( S_COLOR_RED "Unable to find music \"%s\" as explicit track or dynamic music entry!\n",sNameIntro);
4643 				S_StopBackgroundTrack();
4644 			}
4645 		}
4646 	}
4647 
4648 	if (bCalledByCGameStart)
4649 	{
4650 		S_StopBackgroundTrack();
4651 	}
4652 }
4653 
S_StopBackgroundTrack(void)4654 void S_StopBackgroundTrack( void )
4655 {
4656 	for (int i=0; i<eBGRNDTRACK_NUMBEROF; i++)
4657 	{
4658 		S_StopBackgroundTrack_Actual( &tMusic_Info[i] );
4659 	}
4660 
4661 	s_rawend = 0;
4662 }
4663 
4664 // qboolean return is true only if we're changing from a streamed intro to a dynamic loop...
4665 //
S_UpdateBackgroundTrack_Actual(MusicInfo_t * pMusicInfo,qboolean bFirstOrOnlyMusicTrack,float fDefaultVolume)4666 static qboolean S_UpdateBackgroundTrack_Actual( MusicInfo_t *pMusicInfo, qboolean bFirstOrOnlyMusicTrack, float fDefaultVolume)
4667 {
4668 	int		bufferSamples;
4669 	int		fileSamples;
4670 	byte	raw[30000];		// just enough to fit in a mac stack frame  (note that MP3 doesn't use full size of it)
4671 	int		fileBytes;
4672 	int		r;
4673 
4674 	float fMasterVol = fDefaultVolume; // s_musicVolume->value;
4675 
4676 	if (bMusic_IsDynamic)
4677 	{
4678 		// step xfade volume...
4679 		//
4680 		if ( pMusicInfo->iXFadeVolume != pMusicInfo->iXFadeVolumeSeekTo )
4681 		{
4682 			int iFadeMillisecondsElapsed = Sys_Milliseconds() - pMusicInfo->iXFadeVolumeSeekTime;
4683 
4684 			if (iFadeMillisecondsElapsed > (fDYNAMIC_XFADE_SECONDS * 1000))
4685 			{
4686 				pMusicInfo->iXFadeVolume = pMusicInfo->iXFadeVolumeSeekTo;
4687 			}
4688 			else
4689 			{
4690 				pMusicInfo->iXFadeVolume = (int) (255.0f * ((float)iFadeMillisecondsElapsed/(fDYNAMIC_XFADE_SECONDS * 1000.0f)));
4691 				if (pMusicInfo->iXFadeVolumeSeekTo == 0)	// bleurgh
4692 					pMusicInfo->iXFadeVolume = 255 - pMusicInfo->iXFadeVolume;
4693 			}
4694 		}
4695 		fMasterVol *= (float)((float)pMusicInfo->iXFadeVolume / 255.0f);
4696 	}
4697 
4698 // this is to work around an obscure issue to do with sliding decoder windows and amounts being requested, since the
4699 //	original MP3 stream-decoder wrapper was designed to work with audio-paintbuffer sized pieces... Basically 30000
4700 //	is far too big for the window decoder to handle in one request because of the time-travel issue associated with
4701 //	normal sfx buffer painting, and allowing sufficient sliding room, even though the music file never goes back in time.
4702 //
4703 #define SIZEOF_RAW_BUFFER_FOR_MP3 4096
4704 #define RAWSIZE (pMusicInfo->bIsMP3?SIZEOF_RAW_BUFFER_FOR_MP3:sizeof(raw))
4705 
4706 	if ( !pMusicInfo->s_backgroundFile ) {
4707 		return qfalse;
4708 	}
4709 
4710 	pMusicInfo->fSmoothedOutVolume = (pMusicInfo->fSmoothedOutVolume + fMasterVol)/2.0f;
4711 //	Com_OPrintf("%f\n",pMusicInfo->fSmoothedOutVolume);
4712 
4713 	// don't bother playing anything if musicvolume is 0
4714 	if ( pMusicInfo->fSmoothedOutVolume <= 0 ) {
4715 		return qfalse;
4716 	}
4717 
4718 	// see how many samples should be copied into the raw buffer
4719 	if ( s_rawend < s_soundtime ) {
4720 		s_rawend = s_soundtime;
4721 	}
4722 
4723 	while ( s_rawend < s_soundtime + MAX_RAW_SAMPLES )
4724 	{
4725 		bufferSamples = MAX_RAW_SAMPLES - (s_rawend - s_soundtime);
4726 
4727 		// decide how much data needs to be read from the file
4728 		fileSamples = bufferSamples * pMusicInfo->s_backgroundInfo.rate / dma.speed;
4729 
4730 		// don't try to play if there are no more samples in the file
4731 		if (!fileSamples) {
4732 			return qfalse;
4733 		}
4734 
4735 		// don't try and read past the end of the file
4736 		if ( fileSamples > pMusicInfo->s_backgroundSamples ) {
4737 			fileSamples = pMusicInfo->s_backgroundSamples;
4738 		}
4739 
4740 		// our max buffer size
4741 		fileBytes = fileSamples * (pMusicInfo->s_backgroundInfo.width * pMusicInfo->s_backgroundInfo.channels);
4742 		if (fileBytes > (int)RAWSIZE ) {
4743 			fileBytes = RAWSIZE;
4744 			fileSamples = fileBytes / (pMusicInfo->s_backgroundInfo.width * pMusicInfo->s_backgroundInfo.channels);
4745 		}
4746 
4747 		qboolean qbForceFinish = qfalse;
4748 		if (pMusicInfo->bIsMP3)
4749 		{
4750 			int iStartingSampleNum = pMusicInfo->chMP3_Bgrnd.thesfx->iSoundLengthInSamples - pMusicInfo->s_backgroundSamples;	// but this IS relevant
4751 			// Com_Printf(S_COLOR_YELLOW "Requesting MP3 samples: sample %d\n",iStartingSampleNum);
4752 
4753 
4754 			if (pMusicInfo->s_backgroundFile == -1)
4755 			{
4756 				// in-mem...
4757 				//
4758 				qbForceFinish = (MP3Stream_GetSamples( &pMusicInfo->chMP3_Bgrnd, iStartingSampleNum, fileBytes/2, (short*) raw, qtrue ))?qfalse:qtrue;
4759 
4760 				//Com_Printf(S_COLOR_YELLOW "Music time remaining: %f seconds\n", MP3Stream_GetRemainingTimeInSeconds( &pMusicInfo->chMP3_Bgrnd.MP3StreamHeader ));
4761 			}
4762 			else
4763 			{
4764 				// streaming an MP3 file instead... (note that the 'fileBytes' request size isn't that relevant for MP3s,
4765 				//										since code here can't know how much the MP3 needs to decompress)
4766 				//
4767 				byte *pbScrolledStreamData = MP3MusicStream_ReadFromDisk(pMusicInfo, pMusicInfo->chMP3_Bgrnd.MP3StreamHeader.iSourceReadIndex, fileBytes);
4768 
4769 				pMusicInfo->chMP3_Bgrnd.MP3StreamHeader.pbSourceData = pbScrolledStreamData - pMusicInfo->chMP3_Bgrnd.MP3StreamHeader.iSourceReadIndex;
4770 
4771 				qbForceFinish = (MP3Stream_GetSamples( &pMusicInfo->chMP3_Bgrnd, iStartingSampleNum, fileBytes/2, (short*) raw, qtrue ))?qfalse:qtrue;
4772 			}
4773 		}
4774 		else
4775 		{
4776 			// streaming a WAV off disk...
4777 			//
4778 			r = FS_Read ( raw, fileBytes, pMusicInfo->s_backgroundFile );
4779 			if ( r != fileBytes ) {
4780 				Com_Printf(S_COLOR_RED"StreamedRead failure on music track\n");
4781 				S_StopBackgroundTrack();
4782 				return qfalse;
4783 			}
4784 
4785 			// byte swap if needed (do NOT do for MP3 decoder, that has an internal big/little endian handler)
4786 			//
4787 			S_ByteSwapRawSamples( fileSamples, pMusicInfo->s_backgroundInfo.width, pMusicInfo->s_backgroundInfo.channels, raw );
4788 		}
4789 
4790 		// add to raw buffer
4791 		S_RawSamples(	fileSamples, pMusicInfo->s_backgroundInfo.rate,
4792 						pMusicInfo->s_backgroundInfo.width, pMusicInfo->s_backgroundInfo.channels, raw, pMusicInfo->fSmoothedOutVolume,
4793 						bFirstOrOnlyMusicTrack
4794 					);
4795 
4796 		pMusicInfo->s_backgroundSamples -= fileSamples;
4797 		if ( !pMusicInfo->s_backgroundSamples || qbForceFinish )
4798 		{
4799 			// loop the music, or play the next piece if we were on the intro...
4800 			//	(but not for dynamic, that can only be used for loop music)
4801 			//
4802 			if (bMusic_IsDynamic)	// needs special logic for this, different call
4803 			{
4804 				pMusicInfo->Rewind();
4805 			}
4806 			else
4807 			{
4808 				// for non-dynamic music we need to check if "sMusic_BackgroundLoop" is an actual filename,
4809 				//	or if it's a dynamic music specifier (which can't literally exist), in which case it should set
4810 				//	a return flag then exit...
4811 				//
4812 				char sTestName[MAX_QPATH*2];// *2 so COM_DefaultExtension doesn't do an ERR_DROP if there was no space
4813 											//	for an extension, since this is a "soft" test
4814 				Q_strncpyz( sTestName, sMusic_BackgroundLoop, sizeof(sTestName));
4815 				COM_DefaultExtension(sTestName, sizeof(sTestName), ".mp3");
4816 
4817 				if (S_FileExists( sTestName ))
4818 				{
4819 					S_StartBackgroundTrack_Actual( pMusicInfo, qfalse, sMusic_BackgroundLoop, sMusic_BackgroundLoop );
4820 				}
4821 				else
4822 				{
4823 					// proposed file doesn't exist, but this may be a dynamic track we're wanting to loop,
4824 					//	so exit with a special flag...
4825 					//
4826 					return qtrue;
4827 				}
4828 			}
4829 			if ( !pMusicInfo->s_backgroundFile )
4830 			{
4831 				return qfalse;		// loop failed to restart
4832 			}
4833 		}
4834 	}
4835 
4836 #undef SIZEOF_RAW_BUFFER_FOR_MP3
4837 #undef RAWSIZE
4838 
4839 	return qfalse;
4840 }
4841 
4842 // used to be just for dynamic, but now even non-dynamic music has to know whether it should be silent or not...
4843 //
S_Music_GetRequestedState(void)4844 static const char *S_Music_GetRequestedState(void)
4845 {
4846 	/*
4847 	int iStringOffset = cl.gameState.stringOffsets[CS_DYNAMIC_MUSIC_STATE];
4848 	if (iStringOffset)
4849 	{
4850 		const char *psCommand = cl.gameState.stringData+iStringOffset;
4851 
4852 		return psCommand;
4853 	}
4854 	*/
4855 	//rwwFIXMEFIXME: Maybe use the above for something in MP?
4856 
4857 	return NULL;
4858 }
4859 
4860 // scan the configstring to see if there's been a state-change requested...
4861 // (note that even if the state doesn't change it still gets here, so do a same-state check for applying)
4862 //
4863 // then go on to do transition handling etc...
4864 //
S_CheckDynamicMusicState(void)4865 static void S_CheckDynamicMusicState(void)
4866 {
4867 	const char *psCommand = S_Music_GetRequestedState();
4868 
4869 	if (psCommand)
4870 	{
4871 		MusicState_e eNewState;
4872 
4873 		if ( !Q_stricmpn( psCommand, "silence", 7) )
4874 		{
4875 			eNewState = eBGRNDTRACK_SILENCE;
4876 		}
4877 		else if ( !Q_stricmpn( psCommand, "action", 6) )
4878 		{
4879 			eNewState = eBGRNDTRACK_ACTION;
4880 		}
4881 		else if ( !Q_stricmpn( psCommand, "boss", 4) )
4882 		{
4883 			// special case, boss music is optional and may not be defined...
4884 			//
4885 			if (tMusic_Info[ eBGRNDTRACK_BOSS ].bExists)
4886 			{
4887 				eNewState = eBGRNDTRACK_BOSS;
4888 			}
4889 			else
4890 			{
4891 				// ( leave it playing current track )
4892 				//
4893 				eNewState = eMusic_StateActual;
4894 			}
4895 		}
4896 		else if ( !Q_stricmpn( psCommand, "death", 5) )
4897 		{
4898 			// special case, death music is optional and may not be defined...
4899 			//
4900 			if (tMusic_Info[ eBGRNDTRACK_DEATH ].bExists)
4901 			{
4902 				eNewState = eBGRNDTRACK_DEATH;
4903 			}
4904 			else
4905 			{
4906 				// ( leave it playing current track, typically either boss or action )
4907 				//
4908 				eNewState = eMusic_StateActual;
4909 			}
4910 		}
4911 		else
4912 		{
4913 			// seems a reasonable default...
4914 			//
4915 			eNewState = eBGRNDTRACK_EXPLORE;
4916 		}
4917 
4918 		S_SetDynamicMusicState( eNewState );
4919 	}
4920 
4921 	S_HandleDynamicMusicStateChange();
4922 }
4923 
S_UpdateBackgroundTrack(void)4924 static void S_UpdateBackgroundTrack( void )
4925 {
4926 	if (bMusic_IsDynamic)
4927 	{
4928 		if (s_debugdynamic->integer == 2)
4929 		{
4930 			DynamicMusicInfoPrint();
4931 		}
4932 
4933 		S_CheckDynamicMusicState();
4934 
4935 		if (eMusic_StateActual != eBGRNDTRACK_SILENCE)
4936 		{
4937 			MusicInfo_t *pMusicInfoCurrent = &tMusic_Info[ (eMusic_StateActual == eBGRNDTRACK_FADE)?eBGRNDTRACK_EXPLORE:eMusic_StateActual ];
4938 			MusicInfo_t *pMusicInfoFadeOut = &tMusic_Info[ eBGRNDTRACK_FADE ];
4939 
4940 			if ( pMusicInfoCurrent->s_backgroundFile == -1)
4941 			{
4942 				int iRawEnd = s_rawend;
4943 				S_UpdateBackgroundTrack_Actual( pMusicInfoCurrent, qtrue, s_musicVolume->value );
4944 
4945 	/*			static int iPrevFrontVol = 0;
4946 				if (iPrevFrontVol != pMusicInfoCurrent->iXFadeVolume)
4947 				{
4948 					iPrevFrontVol  = pMusicInfoCurrent->iXFadeVolume;
4949 					Com_Printf("front vol = %d\n",pMusicInfoCurrent->iXFadeVolume);
4950 				}
4951 	*/
4952 				if (pMusicInfoFadeOut->bActive)
4953 				{
4954 					s_rawend = iRawEnd;
4955 					S_UpdateBackgroundTrack_Actual( pMusicInfoFadeOut, qfalse, s_musicVolume->value );	// inactive-checked internally
4956 	/*
4957 					static int iPrevFadeVol = 0;
4958 					if (iPrevFadeVol != pMusicInfoFadeOut->iXFadeVolume)
4959 					{
4960 						iPrevFadeVol  = pMusicInfoFadeOut->iXFadeVolume;
4961 						Com_Printf("fade vol = %d\n",pMusicInfoFadeOut->iXFadeVolume);
4962 					}
4963 	*/
4964 					//
4965 					// only do this for the fader!...
4966 					//
4967 					if (pMusicInfoFadeOut->iXFadeVolume == 0)
4968 					{
4969 						pMusicInfoFadeOut->bActive = qfalse;
4970 					}
4971 				}
4972 
4973 				float fRemainingTimeInSeconds = MP3Stream_GetRemainingTimeInSeconds( &pMusicInfoCurrent->chMP3_Bgrnd.MP3StreamHeader );
4974 				// Com_Printf("Remaining: %3.3f\n",fRemainingTimeInSeconds);
4975 
4976 				if ( fRemainingTimeInSeconds < fDYNAMIC_XFADE_SECONDS*2 )
4977 				{
4978 					// now either loop current track, switch if finishing a transition, or stop if finished a death...
4979 					//
4980 					if (pMusicInfoCurrent->bTrackSwitchPending)
4981 					{
4982 						pMusicInfoCurrent->bTrackSwitchPending = qfalse;	// ack
4983 						S_SwitchDynamicTracks( eMusic_StateActual, pMusicInfoCurrent->eTS_NewState, qfalse);	// qboolean bNewTrackStartsFullVolume
4984 						if (tMusic_Info[ pMusicInfoCurrent->eTS_NewState ].bExists)	// don't do this if switching to silence
4985 						{
4986 							tMusic_Info[ pMusicInfoCurrent->eTS_NewState ].SeekTo(pMusicInfoCurrent->fTS_NewTime);
4987 						}
4988 					}
4989 					else
4990 					{
4991 						// normal looping, so set rewind current track, set volume to 0 and fade up to full (unless death track playing, then stays quiet)
4992 						//	(while fader copy of end-section fades down)
4993 						//
4994 						// copy current track to fader...
4995 						//
4996 						*pMusicInfoFadeOut = *pMusicInfoCurrent;	// struct copy
4997 						pMusicInfoFadeOut->iXFadeVolumeSeekTime	= Sys_Milliseconds();
4998 						pMusicInfoFadeOut->iXFadeVolumeSeekTo	= 0;
4999 						//
5000 						pMusicInfoCurrent->Rewind();
5001 						pMusicInfoCurrent->iXFadeVolumeSeekTime	= Sys_Milliseconds();
5002 						pMusicInfoCurrent->iXFadeVolumeSeekTo	= (eMusic_StateActual == eBGRNDTRACK_DEATH) ? 0: 255;
5003 						pMusicInfoCurrent->iXFadeVolume			= 0;
5004 					}
5005 				}
5006 			}
5007 		}
5008 		else
5009 		{
5010 			// special case, when foreground music is shut off but fader still running to fade off previous track...
5011 			//
5012 			MusicInfo_t *pMusicInfoFadeOut = &tMusic_Info[ eBGRNDTRACK_FADE ];
5013 			if (pMusicInfoFadeOut->bActive)
5014 			{
5015 				S_UpdateBackgroundTrack_Actual( pMusicInfoFadeOut, qtrue, s_musicVolume->value );
5016 				if (pMusicInfoFadeOut->iXFadeVolume == 0)
5017 				{
5018 					pMusicInfoFadeOut->bActive = qfalse;
5019 				}
5020 			}
5021 		}
5022 	}
5023 	else
5024 	{
5025 		// standard / non-dynamic one-track music...
5026 		//
5027 		const char *psCommand = S_Music_GetRequestedState();	// special check just for "silence" case...
5028 		qboolean bShouldBeSilent = (qboolean)(psCommand && !Q_stricmp(psCommand,"silence"));
5029 		float fDesiredVolume = bShouldBeSilent ? 0.0f : s_musicVolume->value;
5030 		//
5031 		// internal to this code is a volume-smoother...
5032 		//
5033 		qboolean bNewTrackDesired = S_UpdateBackgroundTrack_Actual(&tMusic_Info[eBGRNDTRACK_NONDYNAMIC], qtrue, fDesiredVolume);
5034 
5035 		if (bNewTrackDesired)
5036 		{
5037 			S_StartBackgroundTrack( sMusic_BackgroundLoop, sMusic_BackgroundLoop, qfalse );
5038 		}
5039 	}
5040 }
5041 
5042 cvar_t *s_soundpoolmegs = NULL;
5043 
5044 // currently passing in sfx as a param in case I want to do something with it later.
5045 //
SND_malloc(int iSize,sfx_t * sfx)5046 byte *SND_malloc(int iSize, sfx_t *sfx)
5047 {
5048 	byte *pData = (byte *) Z_Malloc(iSize, TAG_SND_RAWDATA, qfalse);	// don't bother asking for zeroed mem
5049 
5050 	// if "s_soundpoolmegs" is < 0, then the -ve of the value is the maximum amount of sounds we're allowed to have loaded...
5051 	//
5052 	if (s_soundpoolmegs && s_soundpoolmegs->integer < 0)
5053 	{
5054 		while ( (Z_MemSize(TAG_SND_RAWDATA) + Z_MemSize(TAG_SND_MP3STREAMHDR)) > ((-s_soundpoolmegs->integer) * 1024 * 1024))
5055 		{
5056 			int iBytesFreed = SND_FreeOldestSound(sfx);
5057 			if (iBytesFreed == 0)
5058 				break;	// sanity
5059 		}
5060 	}
5061 
5062 	return pData;
5063 }
5064 
5065 // called once-only in EXE lifetime...
5066 //
SND_setup()5067 void SND_setup()
5068 {
5069 	s_soundpoolmegs = Cvar_Get("s_soundpoolmegs", "25", CVAR_ARCHIVE);
5070 	if (Sys_LowPhysicalMemory() )
5071 	{
5072 		Cvar_Set("s_soundpoolmegs", "0");
5073 	}
5074 
5075 	Com_Printf("Sound memory manager started\n");
5076 }
5077 
5078 // ask how much mem an sfx has allocated...
5079 //
SND_MemUsed(sfx_t * sfx)5080 static int SND_MemUsed(sfx_t *sfx)
5081 {
5082 	int iSize = 0;
5083 	if (sfx->pSoundData){
5084 		iSize += Z_Size(sfx->pSoundData);
5085 	}
5086 
5087 	if (sfx->pMP3StreamHeader) {
5088 		iSize += Z_Size(sfx->pMP3StreamHeader);
5089 	}
5090 
5091 	return iSize;
5092 }
5093 
5094 // free any allocated sfx mem...
5095 //
5096 // now returns # bytes freed to help with z_malloc()-fail recovery
5097 //
SND_FreeSFXMem(sfx_t * sfx)5098 static int SND_FreeSFXMem(sfx_t *sfx)
5099 {
5100 	int iBytesFreed = 0;
5101 
5102 #ifdef USE_OPENAL
5103 	if (s_UseOpenAL)
5104 	{
5105 		alGetError();
5106 		if (sfx->Buffer)
5107 		{
5108 			alDeleteBuffers(1, &(sfx->Buffer));
5109 #ifdef _DEBUG
5110 			if (alGetError() != AL_NO_ERROR)
5111 			{
5112 				Com_OPrintf("Failed to delete AL Buffer (%s) ... !\n", sfx->sSoundName);
5113 			}
5114 #endif
5115 			sfx->Buffer = 0;
5116 		}
5117 
5118 		if (sfx->lipSyncData)
5119 		{
5120 			iBytesFreed +=	Z_Size(	sfx->lipSyncData);
5121 							Z_Free(	sfx->lipSyncData);
5122 									sfx->lipSyncData = NULL;
5123 		}
5124 	}
5125 #endif
5126 
5127 	if (						sfx->pSoundData) {
5128 		iBytesFreed +=	Z_Size(	sfx->pSoundData);
5129 						Z_Free(	sfx->pSoundData );
5130 								sfx->pSoundData = NULL;
5131 	}
5132 
5133 	sfx->bInMemory = qfalse;
5134 
5135 	if (						sfx->pMP3StreamHeader) {
5136 		iBytesFreed +=	Z_Size(	sfx->pMP3StreamHeader);
5137 						Z_Free(	sfx->pMP3StreamHeader );
5138 								sfx->pMP3StreamHeader = NULL;
5139 	}
5140 
5141 	return iBytesFreed;
5142 }
5143 
S_DisplayFreeMemory()5144 void S_DisplayFreeMemory()
5145 {
5146 	int iSoundDataSize = Z_MemSize ( TAG_SND_RAWDATA ) + Z_MemSize( TAG_SND_MP3STREAMHDR );
5147 	int iMusicDataSize = Z_MemSize ( TAG_SND_DYNAMICMUSIC );
5148 
5149 	if (iSoundDataSize || iMusicDataSize)
5150 	{
5151 		Com_Printf("\n%.2fMB audio data:  ( %.2fMB WAV/MP3 ) + ( %.2fMB Music )\n",
5152 					((float)(iSoundDataSize+iMusicDataSize))/1024.0f/1024.0f,
5153 										((float)(iSoundDataSize))/1024.0f/1024.0f,
5154 																((float)(iMusicDataSize))/1024.0f/1024.0f
5155 					);
5156 
5157 		// now count up amount used on this level...
5158 		//
5159 		iSoundDataSize = 0;
5160 		for (int i=1; i<s_numSfx; i++)
5161 		{
5162 			sfx_t *sfx = &s_knownSfx[i];
5163 
5164 			if (sfx->iLastLevelUsedOn == re->RegisterMedia_GetLevel()){
5165 				iSoundDataSize += SND_MemUsed(sfx);
5166 			}
5167 		}
5168 
5169 		Com_Printf("%.2fMB in sfx_t alloc data (WAV/MP3) loaded this level\n",(float)iSoundDataSize/1024.0f/1024.0f);
5170 	}
5171 }
5172 
SND_TouchSFX(sfx_t * sfx)5173 void SND_TouchSFX(sfx_t *sfx)
5174 {
5175 	sfx->iLastTimeUsed		= Com_Milliseconds()+1;
5176 	sfx->iLastLevelUsedOn	= re->RegisterMedia_GetLevel();
5177 }
5178 
5179 // currently this is only called during snd_shutdown or snd_restart
5180 //
S_FreeAllSFXMem(void)5181 void S_FreeAllSFXMem(void)
5182 {
5183 	for (int i=1 ; i < s_numSfx ; i++)	// start @ 1 to skip freeing default sound
5184 	{
5185 		SND_FreeSFXMem(&s_knownSfx[i]);
5186 	}
5187 }
5188 
5189 // returns number of bytes freed up...
5190 //
5191 // new param is so we can be usre of not freeing ourselves (without having to rely on possible uninitialised timers etc)
5192 //
SND_FreeOldestSound(sfx_t * pButNotThisOne)5193 int SND_FreeOldestSound(sfx_t *pButNotThisOne /* = NULL */)
5194 {
5195 	int iBytesFreed = 0;
5196 	sfx_t *sfx;
5197 
5198 	int	iOldest = Com_Milliseconds();
5199 	int	iUsed	= 0;
5200 
5201 	// start on 1 so we never dump the default sound...
5202 	//
5203 	for (int i=1 ; i < s_numSfx ; i++)
5204 	{
5205 		sfx = &s_knownSfx[i];
5206 
5207 		if (sfx != pButNotThisOne)
5208 		{
5209 			if (!sfx->bDefaultSound && sfx->bInMemory && sfx->iLastTimeUsed < iOldest)
5210 			{
5211 				// new bit, we can't throw away any sfx_t struct in use by a channel, else the paint code will crash...
5212 				//
5213 				int iChannel;
5214 				for (iChannel=0; iChannel<MAX_CHANNELS; iChannel++)
5215 				{
5216 					channel_t *ch = & s_channels[iChannel];
5217 
5218 					if (ch->thesfx == sfx)
5219 						break;	// damn, being used
5220 				}
5221 				if (iChannel == MAX_CHANNELS)
5222 				{
5223 					// this sfx_t struct wasn't used by any channels, so we can lose it...
5224 					//
5225 					iUsed = i;
5226 					iOldest = sfx->iLastTimeUsed;
5227 				}
5228 			}
5229 		}
5230 	}
5231 
5232 	if (iUsed)
5233 	{
5234 		sfx = &s_knownSfx[ iUsed ];
5235 
5236 		Com_DPrintf("SND_FreeOldestSound: freeing sound %s\n", sfx->sSoundName);
5237 
5238 		iBytesFreed = SND_FreeSFXMem(sfx);
5239 	}
5240 
5241 	return iBytesFreed;
5242 }
SND_FreeOldestSound(void)5243 int SND_FreeOldestSound(void)
5244 {
5245 	return SND_FreeOldestSound(NULL);	// I had to add a void-arg version of this because of link issues, sigh
5246 }
5247 
5248 // just before we drop into a level, ensure the audio pool is under whatever the maximum
5249 //	pool size is (but not by dropping out sounds used by the current level)...
5250 //
5251 // returns qtrue if at least one sound was dropped out, so z_malloc-fail recovery code knows if anything changed
5252 //
5253 extern qboolean gbInsideLoadSound;
SND_RegisterAudio_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel)5254 qboolean SND_RegisterAudio_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLevel /* 99% qfalse */)
5255 {
5256 	qboolean bAtLeastOneSoundDropped = qfalse;
5257 
5258 	Com_DPrintf( "SND_RegisterAudio_LevelLoadEnd():\n");
5259 
5260 	if (gbInsideLoadSound)
5261 	{
5262 		Com_DPrintf( "(Inside S_LoadSound (z_malloc recovery?), exiting...\n");
5263 	}
5264 	else
5265 	{
5266 		int iLoadedAudioBytes	 = Z_MemSize ( TAG_SND_RAWDATA ) + Z_MemSize( TAG_SND_MP3STREAMHDR );
5267 		const int iMaxAudioBytes = s_soundpoolmegs->integer * 1024 * 1024;
5268 
5269 		for (int i=1; i<s_numSfx && ( iLoadedAudioBytes > iMaxAudioBytes || bDeleteEverythingNotUsedThisLevel) ; i++) // i=1 so we never page out default sound
5270 		{
5271 			sfx_t *sfx = &s_knownSfx[i];
5272 
5273 			if (sfx->bInMemory)
5274 			{
5275 				qboolean bDeleteThis = qfalse;
5276 
5277 				if (bDeleteEverythingNotUsedThisLevel)
5278 				{
5279 					bDeleteThis = (qboolean)(sfx->iLastLevelUsedOn != re->RegisterMedia_GetLevel());
5280 				}
5281 				else
5282 				{
5283 					bDeleteThis = (qboolean)(sfx->iLastLevelUsedOn < re->RegisterMedia_GetLevel());
5284 				}
5285 
5286 				if (bDeleteThis)
5287 				{
5288 					Com_DPrintf( "Dumping sfx_t \"%s\"\n",sfx->sSoundName);
5289 
5290 					if (SND_FreeSFXMem(sfx))
5291 					{
5292 						bAtLeastOneSoundDropped = qtrue;
5293 					}
5294 
5295 					iLoadedAudioBytes = Z_MemSize ( TAG_SND_RAWDATA ) + Z_MemSize( TAG_SND_MP3STREAMHDR );
5296 				}
5297 			}
5298 		}
5299 	}
5300 
5301 	Com_DPrintf( "SND_RegisterAudio_LevelLoadEnd(): Ok\n");
5302 
5303 	return bAtLeastOneSoundDropped;
5304 }
5305 
5306 #ifdef USE_OPENAL
5307 /****************************************************************************************************\
5308 *
5309 *	EAX Related
5310 *
5311 \****************************************************************************************************/
5312 
5313 /*
5314 	Initialize the EAX Manager
5315 */
InitEAXManager()5316 void InitEAXManager()
5317 {
5318 	LPEAXMANAGERCREATE lpEAXManagerCreateFn;
5319 	EAXFXSLOTPROPERTIES FXSlotProp;
5320 	GUID	Effect;
5321 	GUID	FXSlotGuids[4];
5322 	int i;
5323 
5324 	s_bEALFileLoaded = false;
5325 
5326 	// Check for EAX 4.0 support
5327 	s_bEAX = alIsExtensionPresent((ALubyte*)"EAX4.0");
5328 
5329 	if (s_bEAX)
5330 	{
5331 		Com_Printf("Found EAX 4.0 native support\n");
5332 	}
5333 	else
5334 	{
5335 		// Support for EAXUnified (automatic translation of EAX 4.0 calls into EAX 3.0)
5336 		if ((alIsExtensionPresent((ALubyte*)"EAX3.0")) && (alIsExtensionPresent((ALubyte*)"EAX4.0Emulated")))
5337 		{
5338 			s_bEAX = AL_TRUE;
5339 			Com_Printf("Found EAX 4.0 EMULATION support\n");
5340 		}
5341 	}
5342 
5343 	if (s_bEAX)
5344 	{
5345 		s_eaxSet = (EAXSet)alGetProcAddress((ALubyte*)"EAXSet");
5346 		if (s_eaxSet == NULL)
5347 			s_bEAX = false;
5348 		s_eaxGet = (EAXGet)alGetProcAddress((ALubyte*)"EAXGet");
5349 		if (s_eaxGet == NULL)
5350 			s_bEAX = false;
5351 	}
5352 
5353 	// If we have detected EAX support, then try and load the EAX Manager DLL
5354 	if (s_bEAX)
5355 	{
5356 		s_hEAXManInst = LoadLibrary("EAXMan.dll");
5357 		if (s_hEAXManInst)
5358 		{
5359 			lpEAXManagerCreateFn = (LPEAXMANAGERCREATE)GetProcAddress(s_hEAXManInst, "EaxManagerCreate");
5360 			if (lpEAXManagerCreateFn)
5361 			{
5362 				if (lpEAXManagerCreateFn(&s_lpEAXManager)==EM_OK)
5363 				{
5364 					// Configure our EAX 4.0 Effect Slots
5365 
5366 					s_NumFXSlots = 0;
5367 					for (i = 0; i < EAX_MAX_FXSLOTS; i++)
5368 					{
5369 						s_FXSlotInfo[i].FXSlotGuid = EAX_NULL_GUID;
5370 						s_FXSlotInfo[i].lEnvID = -1;
5371 					}
5372 
5373 					FXSlotGuids[0] = EAXPROPERTYID_EAX40_FXSlot0;
5374 					FXSlotGuids[1] = EAXPROPERTYID_EAX40_FXSlot1;
5375 					FXSlotGuids[2] = EAXPROPERTYID_EAX40_FXSlot2;
5376 					FXSlotGuids[3] = EAXPROPERTYID_EAX40_FXSlot3;
5377 
5378 					// For each effect slot, try and load a reverb and lock the slot
5379 					FXSlotProp.guidLoadEffect = EAX_REVERB_EFFECT;
5380 					FXSlotProp.lVolume = 0;
5381 					FXSlotProp.lLock = EAXFXSLOT_LOCKED;
5382 					FXSlotProp.ulFlags = EAXFXSLOTFLAGS_ENVIRONMENT;
5383 
5384 					for (i = 0; i < EAX_MAX_FXSLOTS; i++)
5385 					{
5386 						if (s_eaxSet(&FXSlotGuids[i], EAXFXSLOT_ALLPARAMETERS, NULL, &FXSlotProp, sizeof(EAXFXSLOTPROPERTIES))==AL_NO_ERROR)
5387 						{
5388 							// We can use this slot
5389 							s_FXSlotInfo[s_NumFXSlots].FXSlotGuid = FXSlotGuids[i];
5390 							s_NumFXSlots++;
5391 						}
5392 						else
5393 						{
5394 							// If this slot already contains a reverb, then we will use it anyway (Slot 0 will
5395 							// be in this category).  (It probably means that Slot 0 is locked)
5396 							if (s_eaxGet(&FXSlotGuids[i], EAXFXSLOT_LOADEFFECT, NULL, &Effect, sizeof(GUID))==AL_NO_ERROR)
5397 							{
5398 								if (Effect == EAX_REVERB_EFFECT)
5399 								{
5400 									// We can use this slot
5401 									// Make sure the environment flag is on
5402 									s_eaxSet(&FXSlotGuids[i], EAXFXSLOT_FLAGS, NULL, &FXSlotProp.ulFlags, sizeof(unsigned long));
5403 									s_FXSlotInfo[s_NumFXSlots].FXSlotGuid = FXSlotGuids[i];
5404 									s_NumFXSlots++;
5405 								}
5406 							}
5407 						}
5408 					}
5409 
5410 					return;
5411 				}
5412 			}
5413 		}
5414 	}
5415 
5416 	// If the EAXManager library was loaded (and there was a problem), then unload it
5417 	if (s_hEAXManInst)
5418 	{
5419 		FreeLibrary(s_hEAXManInst);
5420 		s_hEAXManInst = NULL;
5421 	}
5422 
5423 	s_lpEAXManager = NULL;
5424 	s_bEAX = false;
5425 
5426 	return;
5427 }
5428 
5429 /*
5430 	Release the EAX Manager
5431 */
ReleaseEAXManager()5432 void ReleaseEAXManager()
5433 {
5434 	s_bEAX = false;
5435 
5436 	UnloadEALFile();
5437 
5438 	if (s_lpEAXManager)
5439 	{
5440 		s_lpEAXManager->Release();
5441 		s_lpEAXManager = NULL;
5442 	}
5443 	if (s_hEAXManInst)
5444 	{
5445 		FreeLibrary(s_hEAXManInst);
5446 		s_hEAXManInst = NULL;
5447 	}
5448 }
5449 
5450 /*
5451 	Try to load the given .eal file
5452 */
LoadEALFile(char * szEALFilename)5453 bool LoadEALFile(char *szEALFilename)
5454 {
5455 	char		*ealData = NULL;
5456 	HRESULT		hr;
5457 	long		i, j, lID, lEnvID;
5458 	EMPOINT		EMPoint;
5459 	char		szAperture[128];
5460 	char		szFullEALFilename[MAX_QPATH];
5461 	long		lNumInst, lNumInstA, lNumInstB;
5462 	bool		bLoaded = false;
5463 	bool		bValid = true;
5464 	int			result;
5465 	char		szString[256];
5466 
5467 	if ((!s_lpEAXManager) || (!s_bEAX))
5468 		return false;
5469 
5470 	if (strstr(szEALFilename, "nomap"))
5471 		return false;
5472 
5473 	s_EnvironmentID = 0xFFFFFFFF;
5474 
5475 	// Assume there is no aperture information in the .eal file
5476 	s_lpEnvTable = NULL;
5477 
5478 	// Load EAL file from PAK file
5479 	result = FS_ReadFile(szEALFilename, (void **)&ealData);
5480 
5481 	if ((ealData) && (result != -1))
5482 	{
5483 		hr = s_lpEAXManager->LoadDataSet(ealData, EMFLAG_LOADFROMMEMORY);
5484 
5485 		// Unload EAL file
5486 		FS_FreeFile (ealData);
5487 
5488 		if (hr == EM_OK)
5489 		{
5490 			Com_DPrintf("Loaded %s by Quake loader\n", szEALFilename);
5491 			bLoaded = true;
5492 		}
5493 	}
5494 	else
5495 	{
5496 		// Failed to load via Quake loader, try manually
5497 		Com_sprintf(szFullEALFilename, MAX_QPATH, "base/%s", szEALFilename);
5498 		if (SUCCEEDED(s_lpEAXManager->LoadDataSet(szFullEALFilename, 0)))
5499 		{
5500 			Com_DPrintf("Loaded %s by EAXManager\n", szEALFilename);
5501 			bLoaded = true;
5502 		}
5503 	}
5504 
5505 	if (bLoaded)
5506 	{
5507 		// For a valid eal file ... need to find 'Center' tag, record num of instances,  and then find
5508 		// the right number of instances of 'Aperture0a' and 'Aperture0b'.
5509 
5510 		if (s_lpEAXManager->GetSourceID("Center", &lID)==EM_OK)
5511 		{
5512 			if (s_lpEAXManager->GetSourceNumInstances(lID, &s_lNumEnvironments)==EM_OK)
5513 			{
5514 				if (s_lpEAXManager->GetSourceID("Aperture0a", &lID)==EM_OK)
5515 				{
5516 					if (s_lpEAXManager->GetSourceNumInstances(lID, &lNumInst)==EM_OK)
5517 					{
5518 						if (lNumInst == s_lNumEnvironments)
5519 						{
5520 							if (s_lpEAXManager->GetSourceID("Aperture0b", &lID)==EM_OK)
5521 							{
5522 								if (s_lpEAXManager->GetSourceNumInstances(lID, &lNumInst)==EM_OK)
5523 								{
5524 									if (lNumInst == s_lNumEnvironments)
5525 									{
5526 										// Check equal numbers of ApertureXa and ApertureXb
5527 										i = 1;
5528 										while (true)
5529 										{
5530 											lNumInstA = lNumInstB = 0;
5531 
5532 											sprintf(szAperture,"Aperture%da",i);
5533 											if ((s_lpEAXManager->GetSourceID(szAperture, &lID)==EM_OK) && (s_lpEAXManager->GetSourceNumInstances(lID, &lNumInstA)==EM_OK))
5534 											{
5535 												sprintf(szAperture,"Aperture%db",i);
5536 												s_lpEAXManager->GetSourceID(szAperture, &lID);
5537 												s_lpEAXManager->GetSourceNumInstances(lID, &lNumInstB);
5538 
5539 												if (lNumInstA!=lNumInstB)
5540 												{
5541 													Com_DPrintf( S_COLOR_YELLOW "Invalid EAL file - %d Aperture%da tags, and %d Aperture%db tags\n", lNumInstA, i, lNumInstB, i);
5542 													bValid = false;
5543 												}
5544 											}
5545 											else
5546 											{
5547 												break;
5548 											}
5549 
5550 											i++;
5551 										}
5552 
5553 										if (bValid)
5554 										{
5555 											s_lpEnvTable = (LPENVTABLE)Z_Malloc(s_lNumEnvironments * sizeof(ENVTABLE), TAG_GENERAL, qtrue);
5556 										}
5557 									}
5558 									else
5559 										Com_DPrintf( S_COLOR_YELLOW "Invalid EAL File - expected %d instances of Aperture0b, found %d\n", s_lNumEnvironments, lNumInst);
5560 								}
5561 								else
5562 									Com_DPrintf( S_COLOR_YELLOW "EAXManager- failed GetSourceNumInstances()\n");
5563 							}
5564 							else
5565 								Com_DPrintf( S_COLOR_YELLOW "Invalid EAL File - no instances of 'Aperture0b' source-tag\n");
5566 						}
5567 						else
5568 							Com_DPrintf( S_COLOR_YELLOW "Invalid EAL File - found %d instances of the 'Center' tag, but only %d instances of 'Aperture0a'\n", s_lNumEnvironments, lNumInst);
5569 					}
5570 					else
5571 						Com_DPrintf( S_COLOR_YELLOW "EAXManager- failed GetSourceNumInstances()\n");
5572 				}
5573 				else
5574 					Com_DPrintf( S_COLOR_YELLOW "Invalid EAL File - no instances of 'Aperture0a' source-tag\n");
5575 			}
5576 			else
5577 				Com_DPrintf( S_COLOR_YELLOW "EAXManager- failed GetSourceNumInstances()\n");
5578 		}
5579 		else
5580 			Com_DPrintf( S_COLOR_YELLOW "Invalid EAL File - no instances of 'Center' source-tag\n");
5581 
5582 
5583 		if (s_lpEnvTable)
5584 		{
5585 			i = 0;
5586 			while (true)
5587 			{
5588 				sprintf(szAperture, "Aperture%da", i);
5589 				if (s_lpEAXManager->GetSourceID(szAperture, &lID)==EM_OK)
5590 				{
5591 					if (s_lpEAXManager->GetSourceNumInstances(lID, &lNumInst)==EM_OK)
5592 					{
5593 						for (j = 0; j < s_lNumEnvironments; j++)
5594 						{
5595 							s_lpEnvTable[j].bUsed = false;
5596 						}
5597 
5598 						for (j = 0; j < lNumInst; j++)
5599 						{
5600 							if (s_lpEAXManager->GetSourceInstancePos(lID, j, &EMPoint)==EM_OK)
5601 							{
5602 								if (s_lpEAXManager->GetListenerDynamicAttributes(0, &EMPoint, &lEnvID, 0)==EM_OK)
5603 								{
5604 									if ((lEnvID >= 0) && (lEnvID < s_lNumEnvironments))
5605 									{
5606 										if (!s_lpEnvTable[lEnvID].bUsed)
5607 										{
5608 											s_lpEnvTable[lEnvID].bUsed = true;
5609 											s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[0] = EMPoint.fX;
5610 											s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[1] = EMPoint.fY;
5611 											s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[2] = EMPoint.fZ;
5612 										}
5613 										else
5614 										{
5615 											s_lpEAXManager->GetEnvironmentName(lEnvID, szString, 256);
5616 											Com_DPrintf( S_COLOR_YELLOW "Found more than one occurance of Aperture%da in %s sub-space\n", i, szString);
5617 											Com_DPrintf( S_COLOR_YELLOW "One tag at %.3f,%.3f,%.3f, other at %.3f,%.3f,%.3f\n", EMPoint.fX, EMPoint.fY, EMPoint.fZ,
5618 												s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[0], s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[1],
5619 												s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[2]);
5620 											bValid = false;
5621 										}
5622 									}
5623 									else
5624 									{
5625 										if (lEnvID==-1)
5626 											Com_DPrintf( S_COLOR_YELLOW "%s (%.3f,%.3f,%.3f) in Default Environment - please remove\n", szAperture, EMPoint.fX, EMPoint.fY, EMPoint.fZ);
5627 										else
5628 											Com_DPrintf( S_COLOR_YELLOW "Detected more reverb presets than zones - please delete unused presets\n");
5629 										bValid = false;
5630 									}
5631 								}
5632 							}
5633 						}
5634 					}
5635 				}
5636 				else
5637 				{
5638 					break;
5639 				}
5640 
5641 				if (bValid)
5642 				{
5643 					sprintf(szAperture, "Aperture%db", i);
5644 					if (s_lpEAXManager->GetSourceID(szAperture, &lID)==EM_OK)
5645 					{
5646 						if (s_lpEAXManager->GetSourceNumInstances(lID, &lNumInst)==EM_OK)
5647 						{
5648 							for (j = 0; j < s_lNumEnvironments; j++)
5649 							{
5650 								s_lpEnvTable[j].bUsed = false;
5651 							}
5652 
5653 							for (j = 0; j < lNumInst; j++)
5654 							{
5655 								if (s_lpEAXManager->GetSourceInstancePos(lID, j, &EMPoint)==EM_OK)
5656 								{
5657 									if (s_lpEAXManager->GetListenerDynamicAttributes(0, &EMPoint, &lEnvID, 0)==EM_OK)
5658 									{
5659 										if ((lEnvID >= 0) && (lEnvID < s_lNumEnvironments))
5660 										{
5661 											if (!s_lpEnvTable[lEnvID].bUsed)
5662 											{
5663 												s_lpEnvTable[lEnvID].bUsed = true;
5664 												s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos2[0] = EMPoint.fX;
5665 												s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos2[1] = EMPoint.fY;
5666 												s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos2[2] = EMPoint.fZ;
5667 											}
5668 											else
5669 											{
5670 												s_lpEAXManager->GetEnvironmentName(lEnvID, szString, 256);
5671 												Com_DPrintf( S_COLOR_YELLOW "Found more than one occurance of Aperture%db in %s sub-space\n", i, szString);
5672 												bValid = false;
5673 											}
5674 
5675 											// Calculate center position of aperture (average of 2 points)
5676 
5677 											s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vCenter[0] =
5678 												(s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[0] +
5679 												s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos2[0]) / 2;
5680 
5681 											s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vCenter[1] =
5682 												(s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[1] +
5683 												s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos2[1]) / 2;
5684 
5685 											s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vCenter[2] =
5686 												(s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos1[2] +
5687 												s_lpEnvTable[lEnvID].Aperture[s_lpEnvTable[lEnvID].ulNumApertures].vPos2[2]) / 2;
5688 
5689 											s_lpEnvTable[lEnvID].ulNumApertures++;
5690 										}
5691 										else
5692 										{
5693 											if (lEnvID==-1)
5694 												Com_DPrintf( S_COLOR_YELLOW "%s (%.3f,%.3f,%.3f) in Default Environment - please remove\n", szAperture, EMPoint.fX, EMPoint.fY, EMPoint.fZ);
5695 											else
5696 												Com_DPrintf( S_COLOR_YELLOW "Detected more reverb presets than zones - please delete unused presets\n");
5697 											bValid = false;
5698 										}
5699 									}
5700 								}
5701 							}
5702 						}
5703 					}
5704 				}
5705 
5706 				if (!bValid)
5707 				{
5708 					// Found a problem
5709 					Com_DPrintf( S_COLOR_YELLOW "EAX legacy behaviour invoked (one reverb)\n");
5710 
5711 					Z_Free( s_lpEnvTable );
5712 					s_lpEnvTable = NULL;
5713 					break;
5714 				}
5715 
5716 				i++;
5717 			}
5718 		}
5719 		else
5720 		{
5721 			Com_DPrintf( S_COLOR_YELLOW "EAX legacy behaviour invoked (one reverb)\n");
5722 		}
5723 
5724 		return true;
5725 	}
5726 
5727 	Com_DPrintf( S_COLOR_YELLOW "Failed to load %s\n", szEALFilename);
5728 	return false;
5729 }
5730 
5731 /*
5732 	Unload current .eal file
5733 */
UnloadEALFile()5734 void UnloadEALFile()
5735 {
5736 	HRESULT hr;
5737 
5738 	if ((!s_lpEAXManager) || (!s_bEAX))
5739 		return;
5740 
5741 	hr = s_lpEAXManager->FreeDataSet(0);
5742 	s_bEALFileLoaded = false;
5743 
5744 	if (s_lpEnvTable)
5745 	{
5746 		Z_Free( s_lpEnvTable );
5747 		s_lpEnvTable = NULL;
5748 	}
5749 
5750 	return;
5751 }
5752 
5753 /*
5754 	Updates the current EAX Reverb setting, based on the location of the listener
5755 */
UpdateEAXListener()5756 void UpdateEAXListener()
5757 {
5758 	EMPOINT ListPos, ListOri;
5759 	EMPOINT EMAperture;
5760 	EMPOINT EMSourcePoint;
5761 	long lID, lSourceID, lApertureNum;
5762 	int i, j, k;
5763 	float flDistance, flNearest;
5764 	EAXREVERBPROPERTIES Reverb;
5765 	bool bFound;
5766 	long lVolume;
5767 	long lCurTime;
5768 	channel_t	*ch;
5769 	EAXVECTOR	LR, LP1, LP2, Pan;
5770 	REVERBDATA ReverbData[3]; // Hardcoded to three (maximum no of reverbs)
5771 #ifdef DISPLAY_CLOSEST_ENVS
5772 	char szEnvName[256];
5773 #endif
5774 
5775 	if ((!s_lpEAXManager) || (!s_bEAX))
5776 		return;
5777 
5778 	lCurTime = timeGetTime();
5779 
5780 	if ((s_lLastEnvUpdate + ENV_UPDATE_RATE) < lCurTime)
5781 	{
5782 		// Update closest reverbs
5783 		s_lLastEnvUpdate = lCurTime;
5784 
5785 		// No panning information in .eal file, or we only have 1 FX Slot to use, revert to legacy
5786 		// behaviour (i.e only one reverb)
5787 		if ((!s_lpEnvTable) || (s_NumFXSlots==1))
5788 		{
5789 			// Convert Listener co-ordinate to left-handed system
5790 			ListPos.fX = listener_pos[0];
5791 			ListPos.fY = listener_pos[1];
5792 			ListPos.fZ = -listener_pos[2];
5793 
5794 			if (SUCCEEDED(s_lpEAXManager->GetListenerDynamicAttributes(0, &ListPos, &lID, EMFLAG_LOCKPOSITION)))
5795 			{
5796 				if (lID != s_EnvironmentID)
5797 				{
5798 #ifdef DISPLAY_CLOSEST_ENVS
5799 					if (SUCCEEDED(s_lpEAXManager->GetEnvironmentName(lID, szEnvName, 256)))
5800 						Com_Printf("Changing to '%s' zone !\n", szEnvName);
5801 #endif
5802 					// Get EAX Preset info.
5803 					if (SUCCEEDED(s_lpEAXManager->GetEnvironmentAttributes(lID, &s_eaxLPCur)))
5804 					{
5805 						// Override
5806 						s_eaxLPCur.flAirAbsorptionHF = 0.0f;
5807 
5808 						// Set Environment
5809 						s_eaxSet(&EAXPROPERTYID_EAX40_FXSlot0, EAXREVERB_ALLPARAMETERS,
5810 							NULL, &s_eaxLPCur, sizeof(EAXREVERBPROPERTIES));
5811 
5812 						s_EnvironmentID = lID;
5813 					}
5814 				}
5815 			}
5816 
5817 			return;
5818 		}
5819 
5820 		// Convert Listener position and orientation to left-handed system
5821 		ListPos.fX = listener_pos[0];
5822 		ListPos.fY = listener_pos[1];
5823 		ListPos.fZ = -listener_pos[2];
5824 
5825 		ListOri.fX = listener_ori[0];
5826 		ListOri.fY = listener_ori[1];
5827 		ListOri.fZ = -listener_ori[2];
5828 
5829 		// Need to find closest s_NumFXSlots (including the Listener's slot)
5830 
5831 		if (s_lpEAXManager->GetListenerDynamicAttributes(0, &ListPos, &lID, EMFLAG_LOCKPOSITION)==EM_OK)
5832 		{
5833 			if (lID == -1)
5834 			{
5835 				// Found default environment
5836 //				Com_Printf( S_COLOR_YELLOW "Listener in default environment - ignoring zone !\n");
5837 				return;
5838 			}
5839 
5840 			ReverbData[0].lEnvID = -1;
5841 			ReverbData[0].lApertureNum = -1;
5842 			ReverbData[0].flDist = FLT_MAX;
5843 
5844 			ReverbData[1].lEnvID = -1;
5845 			ReverbData[1].lApertureNum = -1;
5846 			ReverbData[1].flDist = FLT_MAX;
5847 
5848 			ReverbData[2].lEnvID = lID;
5849 			ReverbData[2].lApertureNum = -1;
5850 			ReverbData[2].flDist = 0.0f;
5851 
5852 			for (i = 0; i < s_lNumEnvironments; i++)
5853 			{
5854 				// Ignore Environment id lID as this one will always be used
5855 				if (i != lID)
5856 				{
5857 					flNearest = FLT_MAX;
5858 					lApertureNum = 0;	//shut up compile warning
5859 
5860 					for (j = 0; j < s_lpEnvTable[i].ulNumApertures; j++)
5861 					{
5862 						EMAperture.fX = s_lpEnvTable[i].Aperture[j].vCenter[0];
5863 						EMAperture.fY = s_lpEnvTable[i].Aperture[j].vCenter[1];
5864 						EMAperture.fZ = s_lpEnvTable[i].Aperture[j].vCenter[2];
5865 
5866 						flDistance = CalcDistance(EMAperture, ListPos);
5867 
5868 						if (flDistance < flNearest)
5869 						{
5870 							flNearest = flDistance;
5871 							lApertureNum = j;
5872 						}
5873 					}
5874 
5875 					// Now have closest point for this Environment - see if this is closer than any others
5876 
5877 					if (flNearest < ReverbData[1].flDist)
5878 					{
5879 						if (flNearest < ReverbData[0].flDist)
5880 						{
5881 							ReverbData[1] = ReverbData[0];
5882 							ReverbData[0].flDist = flNearest;
5883 							ReverbData[0].lApertureNum = lApertureNum;
5884 							ReverbData[0].lEnvID = i;
5885 						}
5886 						else
5887 						{
5888 							ReverbData[1].flDist = flNearest;
5889 							ReverbData[1].lApertureNum = lApertureNum;
5890 							ReverbData[1].lEnvID = i;
5891 						}
5892 					}
5893 				}
5894 			}
5895 
5896 		}
5897 
5898 #ifdef DISPLAY_CLOSEST_ENVS
5899 		char szEnvName1[256] = {0};
5900 		char szEnvName2[256] = {0};
5901 		char szEnvName3[256] = {0};
5902 
5903 		s_lpEAXManager->GetEnvironmentName(ReverbData[0].lEnvID, szEnvName1, 256);
5904 		s_lpEAXManager->GetEnvironmentName(ReverbData[1].lEnvID, szEnvName2, 256);
5905 		s_lpEAXManager->GetEnvironmentName(ReverbData[2].lEnvID, szEnvName3, 256);
5906 
5907 		Com_Printf("Closest zones are %s, %s (Listener in %s)\n", szEnvName1,
5908 			szEnvName2, szEnvName3);
5909 #endif
5910 
5911 		// Mute any reverbs no longer required ...
5912 
5913 		for (i = 0; i < s_NumFXSlots; i++)
5914 		{
5915 			if ((s_FXSlotInfo[i].lEnvID != -1) && (s_FXSlotInfo[i].lEnvID != ReverbData[0].lEnvID) && (s_FXSlotInfo[i].lEnvID != ReverbData[1].lEnvID)
5916 				&& (s_FXSlotInfo[i].lEnvID != ReverbData[2].lEnvID))
5917 			{
5918 				// This environment is no longer needed
5919 
5920 				// Mute it
5921 				lVolume = -10000;
5922 				if (s_eaxSet(&s_FXSlotInfo[i].FXSlotGuid, EAXFXSLOT_VOLUME, NULL, &lVolume, sizeof(long))!=AL_NO_ERROR)
5923 					Com_OPrintf("Failed to Mute FX Slot\n");
5924 
5925 				// If any source is sending to this Slot ID then we need to stop them sending to the slot
5926 				for (j = 1; j < s_numChannels; j++)
5927 				{
5928 					if (s_channels[j].lSlotID == i)
5929 					{
5930 						if (s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_ACTIVEFXSLOTID, s_channels[j].alSource, (void*)&EAX_NULL_GUID, sizeof(GUID))!=AL_NO_ERROR)
5931 						{
5932 							Com_OPrintf("Failed to set Source ActiveFXSlotID to NULL\n");
5933 						}
5934 
5935 						s_channels[j].lSlotID = -1;
5936 					}
5937 				}
5938 
5939 				assert(s_FXSlotInfo[i].lEnvID < s_lNumEnvironments && s_FXSlotInfo[i].lEnvID >= 0);
5940 				if (s_FXSlotInfo[i].lEnvID < s_lNumEnvironments && s_FXSlotInfo[i].lEnvID >= 0)
5941 				{
5942 					s_lpEnvTable[s_FXSlotInfo[i].lEnvID].lFXSlotID = -1;
5943 				}
5944 				s_FXSlotInfo[i].lEnvID = -1;
5945 			}
5946 		}
5947 
5948 
5949 		// Make sure all the reverbs we want are being rendered, if not, find an empty slot
5950 		// and apply appropriate reverb settings
5951 		for (j = 0; j < 3; j++)
5952 		{
5953 			bFound = false;
5954 
5955 			for (i = 0; i < s_NumFXSlots; i++)
5956 			{
5957 				if (s_FXSlotInfo[i].lEnvID == ReverbData[j].lEnvID)
5958 				{
5959 					bFound = true;
5960 					break;
5961 				}
5962 			}
5963 
5964 			if (!bFound)
5965 			{
5966 				// Find the first available slot and use that one
5967 				for (i = 0; i < s_NumFXSlots; i++)
5968 				{
5969 					if (s_FXSlotInfo[i].lEnvID == -1)
5970 					{
5971 						// Found slot
5972 
5973 						// load reverb here
5974 
5975 						// Retrieve reverb properties from EAX Manager
5976 						if (s_lpEAXManager->GetEnvironmentAttributes(ReverbData[j].lEnvID, &Reverb)==EM_OK)
5977 						{
5978 							// Override Air Absorption HF
5979 							Reverb.flAirAbsorptionHF = 0.0f;
5980 
5981 							s_eaxSet(&s_FXSlotInfo[i].FXSlotGuid, EAXREVERB_ALLPARAMETERS, NULL, &Reverb, sizeof(EAXREVERBPROPERTIES));
5982 
5983 							// See if any Sources are in this environment, if they are, enable their sends
5984 							ch = s_channels + 1;
5985 							for (k = 1; k < s_numChannels; k++, ch++)
5986 							{
5987 								if (ch->fixed_origin)
5988 								{
5989 									// Converting from Quake -> DS3D (for EAGLE) ... swap Y and Z
5990 									EMSourcePoint.fX = ch->origin[0];
5991 									EMSourcePoint.fY = ch->origin[2];
5992 									EMSourcePoint.fZ = ch->origin[1];
5993 								}
5994 								else
5995 								{
5996 									if (ch->entnum == listener_number)
5997 									{
5998 										// Source at same position as listener
5999 										// Probably won't be any Occlusion / Obstruction effect -- unless the listener is underwater
6000 										// Converting from Open AL -> DS3D (for EAGLE) ... invert Z
6001 										EMSourcePoint.fX = listener_pos[0];
6002 										EMSourcePoint.fY = listener_pos[1];
6003 										EMSourcePoint.fZ = -listener_pos[2];
6004 									}
6005 									else
6006 									{
6007 										// Get position of Entity
6008 										// Converting from Quake -> DS3D (for EAGLE) ... swap Y and Z
6009 										EMSourcePoint.fX = loopSounds[ ch->entnum ].origin[0];
6010 										EMSourcePoint.fY = loopSounds[ ch->entnum ].origin[2];
6011 										EMSourcePoint.fZ = loopSounds[ ch->entnum ].origin[1];
6012 									}
6013 								}
6014 
6015 								// Get Source Environment point
6016 								if (s_lpEAXManager->GetListenerDynamicAttributes(0, &EMSourcePoint, &lSourceID, 0)!=EM_OK)
6017 									Com_OPrintf("Failed to get environment zone for Source\n");
6018 
6019 								if (lSourceID == i)
6020 								{
6021 									if (s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_ACTIVEFXSLOTID, ch->alSource, (void*)&(s_FXSlotInfo[i].FXSlotGuid), sizeof(GUID))!=AL_NO_ERROR)
6022 									{
6023 										Com_OPrintf("Failed to set Source ActiveFXSlotID to new environment\n");
6024 									}
6025 
6026 									ch->lSlotID = i;
6027 								}
6028 							}
6029 
6030 							assert(ReverbData[j].lEnvID < s_lNumEnvironments && ReverbData[j].lEnvID >= 0);
6031 							if (ReverbData[j].lEnvID < s_lNumEnvironments && ReverbData[j].lEnvID >= 0)
6032 							{
6033 								s_FXSlotInfo[i].lEnvID = ReverbData[j].lEnvID;
6034 								s_lpEnvTable[ReverbData[j].lEnvID].lFXSlotID = i;
6035 							}
6036 							break;
6037 						}
6038 					}
6039 				}
6040 			}
6041 		}
6042 
6043 		// Make sure Primary FX Slot ID is set correctly
6044 		if (s_EnvironmentID != ReverbData[2].lEnvID)
6045 		{
6046 			s_eaxSet(&EAXPROPERTYID_EAX40_Context, EAXCONTEXT_PRIMARYFXSLOTID, NULL, &(s_FXSlotInfo[s_lpEnvTable[ReverbData[2].lEnvID].lFXSlotID].FXSlotGuid), sizeof(GUID));
6047 			s_EnvironmentID = ReverbData[2].lEnvID;
6048 		}
6049 
6050 		// Have right reverbs loaded ... now to pan them and adjust volume
6051 
6052 
6053 		// We need to rotate the vector from the Listener to the reverb Aperture by minus the listener
6054 		// orientation
6055 
6056 		// Need dot product of Listener Orientation and the straight ahead vector (0, 0, 1)
6057 
6058 		// Since both vectors are already normalized, and two terms cancel out (0's), the angle
6059 		// is the arc cosine of the z component of the Listener Orientation
6060 
6061 		float flTheta = (float)acos(ListOri.fZ);
6062 
6063 		// If the Listener Orientation is to the left of straight ahead, then invert the angle
6064 		if (ListOri.fX < 0)
6065 			flTheta = -flTheta;
6066 
6067 		float flSin = (float)sin(-flTheta);
6068 		float flCos = (float)cos(-flTheta);
6069 
6070 		for (i = 0; i < Q_min(s_NumFXSlots,s_lNumEnvironments); i++)
6071 		{
6072 			if (s_FXSlotInfo[i].lEnvID == s_EnvironmentID)
6073 			{
6074 				// Listener's environment
6075 
6076 				// Find the closest Aperture in *this* environment
6077 
6078 				flNearest = FLT_MAX;
6079 				lApertureNum = 0;	//shut up compile warning
6080 
6081 				for (j = 0; j < s_lpEnvTable[s_EnvironmentID].ulNumApertures; j++)
6082 				{
6083 					EMAperture.fX = s_lpEnvTable[s_EnvironmentID].Aperture[j].vCenter[0];
6084 					EMAperture.fY = s_lpEnvTable[s_EnvironmentID].Aperture[j].vCenter[1];
6085 					EMAperture.fZ = s_lpEnvTable[s_EnvironmentID].Aperture[j].vCenter[2];
6086 
6087 					flDistance = CalcDistance(EMAperture, ListPos);
6088 
6089 					if (flDistance < flNearest)
6090 					{
6091 						flNearest = flDistance;
6092 						lApertureNum = j;
6093 					}
6094 				}
6095 
6096 				// Have closest environment, work out pan vector direction
6097 
6098 				LR.x = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vCenter[0] - ListPos.fX;
6099 				LR.y = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vCenter[1] - ListPos.fY;
6100 				LR.z = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vCenter[2] - ListPos.fZ;
6101 
6102 				Pan.x = (LR.x * flCos) + (LR.z * flSin);
6103 				Pan.y = 0.0f;
6104 				Pan.z = (LR.x * -flSin) + (LR.z * flCos);
6105 
6106 				Normalize(&Pan);
6107 
6108 
6109 				// Adjust magnitude ...
6110 
6111 				// Magnitude is based on the angle subtended by the aperture, so compute the angle between
6112 				// the vector from the Listener to Pos1 of the aperture, and the vector from the
6113 				// Listener to Pos2 of the aperture.
6114 
6115 
6116 				LP1.x = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vPos1[0] - ListPos.fX;
6117 				LP1.y = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vPos1[1] - ListPos.fY;
6118 				LP1.z = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vPos1[2] - ListPos.fZ;
6119 
6120 				LP2.x = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vPos2[0] - ListPos.fX;
6121 				LP2.y = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vPos2[1] - ListPos.fY;
6122 				LP2.z = s_lpEnvTable[s_EnvironmentID].Aperture[lApertureNum].vPos2[2] - ListPos.fZ;
6123 
6124 				Normalize(&LP1);
6125 				Normalize(&LP2);
6126 
6127 				float flGamma = acos((LP1.x * LP2.x) + (LP1.y * LP2.y) + (LP1.z * LP2.z));
6128 
6129 				// We want opposite magnitude (because we are 'in' this environment)
6130 				float flMagnitude = 1.0f - ((2.0f * (float)sin(flGamma/2.0f)) / flGamma);
6131 
6132 				// Negative (because pan should be 180 degrees)
6133 				Pan.x *= -flMagnitude;
6134 				Pan.y *= -flMagnitude;
6135 				Pan.z *= -flMagnitude;
6136 
6137 				if (s_eaxSet(&s_FXSlotInfo[i].FXSlotGuid, EAXREVERB_REVERBPAN, NULL, &Pan, sizeof(EAXVECTOR))!=AL_NO_ERROR)
6138 					Com_OPrintf("Failed to set Listener Reverb Pan\n");
6139 
6140 				if (s_eaxSet(&s_FXSlotInfo[i].FXSlotGuid, EAXREVERB_REFLECTIONSPAN, NULL, &Pan, sizeof(EAXVECTOR))!=AL_NO_ERROR)
6141 					Com_OPrintf("Failed to set Listener Reflections Pan\n");
6142 			}
6143 			else
6144 			{
6145 				// Find out which Reverb this is
6146 				if (ReverbData[0].lEnvID == s_FXSlotInfo[i].lEnvID)
6147 					k = 0;
6148 				else
6149 					k = 1;
6150 
6151 				LR.x = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vCenter[0] - ListPos.fX;
6152 				LR.y = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vCenter[1] - ListPos.fY;
6153 				LR.z = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vCenter[2] - ListPos.fZ;
6154 
6155 				// Rotate the vector
6156 
6157 				Pan.x = (LR.x * flCos) + (LR.z * flSin);
6158 				Pan.y = 0.0f;
6159 				Pan.z = (LR.x * -flSin) + (LR.z * flCos);
6160 
6161 				Normalize(&Pan);
6162 
6163 				// Adjust magnitude ...
6164 
6165 				// Magnitude is based on the angle subtended by the aperture, so compute the angle between
6166 				// the vector from the Listener to Pos1 of the aperture, and the vector from the
6167 				// Listener to Pos2 of the aperture.
6168 
6169 
6170 				LP1.x = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vPos1[0] - ListPos.fX;
6171 				LP1.y = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vPos1[1] - ListPos.fY;
6172 				LP1.z = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vPos1[2] - ListPos.fZ;
6173 
6174 				LP2.x = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vPos2[0] - ListPos.fX;
6175 				LP2.y = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vPos2[1] - ListPos.fY;
6176 				LP2.z = s_lpEnvTable[ReverbData[k].lEnvID].Aperture[ReverbData[k].lApertureNum].vPos2[2] - ListPos.fZ;
6177 
6178 				Normalize(&LP1);
6179 				Normalize(&LP2);
6180 
6181 				float flGamma = acos((LP1.x * LP2.x) + (LP1.y * LP2.y) + (LP1.z * LP2.z));
6182 				float flMagnitude = (2.0f * (float)sin(flGamma/2.0f)) / flGamma;
6183 
6184 				Pan.x *= flMagnitude;
6185 				Pan.y *= flMagnitude;
6186 				Pan.z *= flMagnitude;
6187 
6188 				if (s_eaxSet(&s_FXSlotInfo[i].FXSlotGuid, EAXREVERB_REVERBPAN, NULL, &Pan, sizeof(EAXVECTOR))!=AL_NO_ERROR)
6189 					Com_OPrintf("Failed to set Reverb Pan\n");
6190 
6191 				if (s_eaxSet(&s_FXSlotInfo[i].FXSlotGuid, EAXREVERB_REFLECTIONSPAN, NULL, &Pan, sizeof(EAXVECTOR))!=AL_NO_ERROR)
6192 					Com_OPrintf("Failed to set Reflections Pan\n");
6193 			}
6194 		}
6195 
6196 		lVolume = 0;
6197 		for (i = 0; i < s_NumFXSlots; i++)
6198 		{
6199 			if (s_eaxSet(&s_FXSlotInfo[i].FXSlotGuid, EAXFXSLOT_VOLUME, NULL, &lVolume, sizeof(long))!=AL_NO_ERROR)
6200 				Com_OPrintf("Failed to set FX Slot Volume to 0\n");
6201 		}
6202 	}
6203 
6204 	return;
6205 }
6206 
6207 /*
6208 	Updates the EAX Buffer related effects on the given Source
6209 */
UpdateEAXBuffer(channel_t * ch)6210 void UpdateEAXBuffer(channel_t *ch)
6211 {
6212 	HRESULT hr;
6213 	EMPOINT EMSourcePoint;
6214 	EMPOINT EMVirtualSourcePoint;
6215 	EAXOBSTRUCTIONPROPERTIES eaxOBProp;
6216 	EAXOCCLUSIONPROPERTIES eaxOCProp;
6217 	int i;
6218 	long lSourceID;
6219 
6220 	// If EAX Manager is not initialized, or there is no EAX support, or the listener
6221 	// is underwater, return
6222 	if ((!s_lpEAXManager) || (!s_bEAX) || (s_bInWater))
6223 		return;
6224 
6225 	// Set Occlusion Direct Ratio to the default value (it won't get set by the current version of
6226 	// EAX Manager)
6227 	eaxOCProp.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO;
6228 
6229 	// Convert Source co-ordinate to left-handed system
6230 	if (ch->fixed_origin)
6231 	{
6232 		// Converting from Quake -> DS3D (for EAGLE) ... swap Y and Z
6233 		EMSourcePoint.fX = ch->origin[0];
6234 		EMSourcePoint.fY = ch->origin[2];
6235 		EMSourcePoint.fZ = ch->origin[1];
6236 	}
6237 	else
6238 	{
6239 		if (ch->entnum == listener_number)
6240 		{
6241 			// Source at same position as listener
6242 			// Probably won't be any Occlusion / Obstruction effect -- unless the listener is underwater
6243 			// Converting from Open AL -> DS3D (for EAGLE) ... invert Z
6244 			EMSourcePoint.fX = listener_pos[0];
6245 			EMSourcePoint.fY = listener_pos[1];
6246 			EMSourcePoint.fZ = -listener_pos[2];
6247 		}
6248 		else
6249 		{
6250 			// Get position of Entity
6251 			// Converting from Quake -> DS3D (for EAGLE) ... swap Y and Z
6252 			if (ch->bLooping)
6253 			{
6254 				EMSourcePoint.fX = loopSounds[ ch->entnum ].origin[0];
6255 				EMSourcePoint.fY = loopSounds[ ch->entnum ].origin[2];
6256 				EMSourcePoint.fZ = loopSounds[ ch->entnum ].origin[1];
6257 			}
6258 			else
6259 			{
6260 				EMSourcePoint.fX = s_entityPosition[ch->entnum][0];
6261 				EMSourcePoint.fY = s_entityPosition[ch->entnum][2];
6262 				EMSourcePoint.fZ = s_entityPosition[ch->entnum][1];
6263 			}
6264 		}
6265 	}
6266 
6267 	long lExclusion;
6268 
6269 	// Just determine what environment the source is in
6270 	if (s_lpEAXManager->GetListenerDynamicAttributes(0, &EMSourcePoint, &lSourceID, 0)==EM_OK)
6271 	{
6272 		// See if a Slot is rendering this environment
6273 		for (i = 0; i < s_NumFXSlots; i++)
6274 		{
6275 			if (s_FXSlotInfo[i].lEnvID == lSourceID)
6276 			{
6277 				// If the Source is not sending to this slot, then enable the send now
6278 				if (ch->lSlotID != i)
6279 				{
6280 					// Set this
6281 					if (s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_ACTIVEFXSLOTID, ch->alSource, &s_FXSlotInfo[i].FXSlotGuid, sizeof(GUID))!=AL_NO_ERROR)
6282 						Com_OPrintf("UpdateEAXBuffer = failed to set ActiveFXSlotID\n");
6283 
6284 					ch->lSlotID = i;
6285 				}
6286 
6287 				break;
6288 			}
6289 		}
6290 	}
6291 	else
6292 	{
6293 		Com_OPrintf("UpdateEAXBuffer::Failed to get Source environment zone\n");
6294 	}
6295 
6296 	// Add some Exclusion to sounds that are not located in the Listener's environment
6297 	if (s_FXSlotInfo[ch->lSlotID].lEnvID == s_EnvironmentID)
6298 	{
6299 		lExclusion = 0;
6300 		if (s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_EXCLUSION, ch->alSource, &lExclusion, sizeof(long))!=AL_NO_ERROR)
6301 			Com_OPrintf("UpdateEAXBuffer : Failed to set exclusion to 0\n");
6302 	}
6303 	else
6304 	{
6305 		lExclusion = -1000;
6306 		if (s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_EXCLUSION, ch->alSource, &lExclusion, sizeof(long))!=AL_NO_ERROR)
6307 			Com_OPrintf("UpdateEAXBuffer : Failed to set exclusion to -1000\n");
6308 	}
6309 
6310 	if ((ch->entchannel == CHAN_VOICE) || (ch->entchannel == CHAN_VOICE_ATTEN) || (ch->entchannel == CHAN_VOICE_GLOBAL))
6311 	{
6312 		// Remove any Occlusion + Obstruction
6313 		eaxOBProp.lObstruction = EAXSOURCE_DEFAULTOBSTRUCTION;
6314 		eaxOBProp.flObstructionLFRatio = EAXSOURCE_DEFAULTOBSTRUCTIONLFRATIO;
6315 
6316 		eaxOCProp.lOcclusion = EAXSOURCE_DEFAULTOCCLUSION;
6317 		eaxOCProp.flOcclusionLFRatio = EAXSOURCE_DEFAULTOCCLUSIONLFRATIO;
6318 		eaxOCProp.flOcclusionRoomRatio = EAXSOURCE_DEFAULTOCCLUSIONROOMRATIO;
6319 		eaxOCProp.flOcclusionDirectRatio = EAXSOURCE_DEFAULTOCCLUSIONDIRECTRATIO;
6320 
6321 		s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_OBSTRUCTIONPARAMETERS,
6322 			ch->alSource, &eaxOBProp, sizeof(EAXOBSTRUCTIONPROPERTIES));
6323 
6324 		s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_OCCLUSIONPARAMETERS,
6325 			ch->alSource, &eaxOCProp, sizeof(EAXOCCLUSIONPROPERTIES));
6326 	}
6327 	else
6328 	{
6329 		// Check for Occlusion + Obstruction
6330 		hr = s_lpEAXManager->GetSourceDynamicAttributes(0, &EMSourcePoint, &eaxOBProp.lObstruction, &eaxOBProp.flObstructionLFRatio,
6331 			&eaxOCProp.lOcclusion, &eaxOCProp.flOcclusionLFRatio, &eaxOCProp.flOcclusionRoomRatio, &EMVirtualSourcePoint, 0);
6332 		if (hr == EM_OK)
6333 		{
6334 			// Set EAX effect !
6335 			s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_OBSTRUCTIONPARAMETERS,
6336 				ch->alSource, &eaxOBProp, sizeof(EAXOBSTRUCTIONPROPERTIES));
6337 
6338 			s_eaxSet(&EAXPROPERTYID_EAX40_Source, EAXSOURCE_OCCLUSIONPARAMETERS,
6339 				ch->alSource, &eaxOCProp, sizeof(EAXOCCLUSIONPROPERTIES));
6340 		}
6341 	}
6342 
6343 	return;
6344 }
6345 
CalcDistance(EMPOINT A,EMPOINT B)6346 float CalcDistance(EMPOINT A, EMPOINT B)
6347 {
6348 	return (float)sqrt(sqr(A.fX - B.fX)+sqr(A.fY - B.fY) + sqr(A.fZ - B.fZ));
6349 }
6350 #endif
6351