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