1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)
5 
6 This file is part of Quake III Arena source code.
7 
8 Quake III Arena source code is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 Quake III Arena source code is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Quake III Arena source code; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 
24 #include "snd_local.h"
25 #include "snd_codec.h"
26 #include "client.h"
27 
28 #ifdef USE_OPENAL
29 
30 #include "qal.h"
31 
32 // Console variables specific to OpenAL
33 cvar_t *s_alPrecache;
34 cvar_t *s_alGain;
35 cvar_t *s_alSources;
36 cvar_t *s_alDopplerFactor;
37 cvar_t *s_alDopplerSpeed;
38 cvar_t *s_alMinDistance;
39 cvar_t *s_alMaxDistance;
40 cvar_t *s_alRolloff;
41 cvar_t *s_alGraceDistance;
42 cvar_t *s_alDriver;
43 cvar_t *s_alDevice;
44 cvar_t *s_alAvailableDevices;
45 
46 /*
47 =================
48 S_AL_Format
49 =================
50 */
51 static
S_AL_Format(int width,int channels)52 ALuint S_AL_Format(int width, int channels)
53 {
54 	ALuint format = AL_FORMAT_MONO16;
55 
56 	// Work out format
57 	if(width == 1)
58 	{
59 		if(channels == 1)
60 			format = AL_FORMAT_MONO8;
61 		else if(channels == 2)
62 			format = AL_FORMAT_STEREO8;
63 	}
64 	else if(width == 2)
65 	{
66 		if(channels == 1)
67 			format = AL_FORMAT_MONO16;
68 		else if(channels == 2)
69 			format = AL_FORMAT_STEREO16;
70 	}
71 
72 	return format;
73 }
74 
75 /*
76 =================
77 S_AL_ErrorMsg
78 =================
79 */
S_AL_ErrorMsg(ALenum error)80 static const char *S_AL_ErrorMsg(ALenum error)
81 {
82 	switch(error)
83 	{
84 		case AL_NO_ERROR:
85 			return "No error";
86 		case AL_INVALID_NAME:
87 			return "Invalid name";
88 		case AL_INVALID_ENUM:
89 			return "Invalid enumerator";
90 		case AL_INVALID_VALUE:
91 			return "Invalid value";
92 		case AL_INVALID_OPERATION:
93 			return "Invalid operation";
94 		case AL_OUT_OF_MEMORY:
95 			return "Out of memory";
96 		default:
97 			return "Unknown error";
98 	}
99 }
100 
101 
102 //===========================================================================
103 
104 
105 typedef struct alSfx_s
106 {
107 	char			filename[MAX_QPATH];
108 	ALuint		buffer;					// OpenAL buffer
109 	qboolean	isDefault;			// Couldn't be loaded - use default FX
110 	qboolean	inMemory;				// Sound is stored in memory
111 	qboolean	isLocked;				// Sound is locked (can not be unloaded)
112 	int				lastUsedTime;		// Time last used
113 } alSfx_t;
114 
115 static qboolean alBuffersInitialised = qfalse;
116 
117 // Sound effect storage, data structures
118 #define MAX_SFX 4096
119 static alSfx_t knownSfx[MAX_SFX];
120 static int numSfx = 0;
121 
122 static sfxHandle_t default_sfx;
123 
124 /*
125 =================
126 S_AL_BufferFindFree
127 
128 Find a free handle
129 =================
130 */
S_AL_BufferFindFree(void)131 static sfxHandle_t S_AL_BufferFindFree( void )
132 {
133 	int i;
134 
135 	for(i = 0; i < MAX_SFX; i++)
136 	{
137 		// Got one
138 		if(knownSfx[i].filename[0] == '\0')
139 		{
140 			if(i >= numSfx)
141 				numSfx = i + 1;
142 			return i;
143 		}
144 	}
145 
146 	// Shit...
147 	Com_Error(ERR_FATAL, "S_AL_BufferFindFree: No free sound handles");
148 	return -1;
149 }
150 
151 /*
152 =================
153 S_AL_BufferFind
154 
155 Find a sound effect if loaded, set up a handle otherwise
156 =================
157 */
S_AL_BufferFind(const char * filename)158 static sfxHandle_t S_AL_BufferFind(const char *filename)
159 {
160 	// Look it up in the table
161 	sfxHandle_t sfx = -1;
162 	int i;
163 
164 	for(i = 0; i < numSfx; i++)
165 	{
166 		if(!Q_stricmp(knownSfx[i].filename, filename))
167 		{
168 			sfx = i;
169 			break;
170 		}
171 	}
172 
173 	// Not found in table?
174 	if(sfx == -1)
175 	{
176 		alSfx_t *ptr;
177 
178 		sfx = S_AL_BufferFindFree();
179 
180 		// Clear and copy the filename over
181 		ptr = &knownSfx[sfx];
182 		memset(ptr, 0, sizeof(*ptr));
183 		strcpy(ptr->filename, filename);
184 	}
185 
186 	// Return the handle
187 	return sfx;
188 }
189 
190 /*
191 =================
192 S_AL_BufferUseDefault
193 =================
194 */
S_AL_BufferUseDefault(sfxHandle_t sfx)195 static void S_AL_BufferUseDefault(sfxHandle_t sfx)
196 {
197 	if(sfx == default_sfx)
198 		Com_Error(ERR_FATAL, "Can't load default sound effect %s\n", knownSfx[sfx].filename);
199 
200 	Com_Printf( S_COLOR_YELLOW "WARNING: Using default sound for %s\n", knownSfx[sfx].filename);
201 	knownSfx[sfx].isDefault = qtrue;
202 	knownSfx[sfx].buffer = knownSfx[default_sfx].buffer;
203 }
204 
205 /*
206 =================
207 S_AL_BufferUnload
208 =================
209 */
S_AL_BufferUnload(sfxHandle_t sfx)210 static void S_AL_BufferUnload(sfxHandle_t sfx)
211 {
212 	ALenum error;
213 
214 	if(knownSfx[sfx].filename[0] == '\0')
215 		return;
216 
217 	if(!knownSfx[sfx].inMemory)
218 		return;
219 
220 	// Delete it
221 	qalDeleteBuffers(1, &knownSfx[sfx].buffer);
222 	if((error = qalGetError()) != AL_NO_ERROR)
223 		Com_Printf( S_COLOR_RED "ERROR: Can't delete sound buffer for %s\n",
224 				knownSfx[sfx].filename);
225 
226 	knownSfx[sfx].inMemory = qfalse;
227 }
228 
229 /*
230 =================
231 S_AL_BufferEvict
232 =================
233 */
S_AL_BufferEvict(void)234 static qboolean S_AL_BufferEvict( void )
235 {
236 	int	i, oldestBuffer = -1;
237 	int	oldestTime = Sys_Milliseconds( );
238 
239 	for( i = 0; i < numSfx; i++ )
240 	{
241 		if( !knownSfx[ i ].filename[ 0 ] )
242 			continue;
243 
244 		if( !knownSfx[ i ].inMemory )
245 			continue;
246 
247 		if( knownSfx[ i ].lastUsedTime < oldestTime )
248 		{
249 			oldestTime = knownSfx[ i ].lastUsedTime;
250 			oldestBuffer = i;
251 		}
252 	}
253 
254 	if( oldestBuffer >= 0 )
255 	{
256 		S_AL_BufferUnload( oldestBuffer );
257 		return qtrue;
258 	}
259 	else
260 		return qfalse;
261 }
262 
263 /*
264 =================
265 S_AL_BufferLoad
266 =================
267 */
S_AL_BufferLoad(sfxHandle_t sfx)268 static void S_AL_BufferLoad(sfxHandle_t sfx)
269 {
270 	ALenum error;
271 
272 	void *data;
273 	snd_info_t info;
274 	ALuint format;
275 
276 	// Nothing?
277 	if(knownSfx[sfx].filename[0] == '\0')
278 		return;
279 
280 	// Player SFX
281 	if(knownSfx[sfx].filename[0] == '*')
282 		return;
283 
284 	// Already done?
285 	if((knownSfx[sfx].inMemory) || (knownSfx[sfx].isDefault))
286 		return;
287 
288 	// Try to load
289 	data = S_CodecLoad(knownSfx[sfx].filename, &info);
290 	if(!data)
291 	{
292 		S_AL_BufferUseDefault(sfx);
293 		return;
294 	}
295 
296 	format = S_AL_Format(info.width, info.channels);
297 
298 	// Create a buffer
299 	qalGenBuffers(1, &knownSfx[sfx].buffer);
300 	if((error = qalGetError()) != AL_NO_ERROR)
301 	{
302 		S_AL_BufferUseDefault(sfx);
303 		Z_Free(data);
304 		Com_Printf( S_COLOR_RED "ERROR: Can't create a sound buffer for %s - %s\n",
305 				knownSfx[sfx].filename, S_AL_ErrorMsg(error));
306 		return;
307 	}
308 
309 	// Fill the buffer
310 	if( info.size == 0 )
311 	{
312 		// We have no data to buffer, so buffer silence
313 		byte dummyData[ 2 ] = { 0 };
314 
315 		qalBufferData(knownSfx[sfx].buffer, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050);
316 	}
317 	else
318 		qalBufferData(knownSfx[sfx].buffer, format, data, info.size, info.rate);
319 
320 	error = qalGetError();
321 
322 	// If we ran out of memory, start evicting the least recently used sounds
323 	while(error == AL_OUT_OF_MEMORY)
324 	{
325 		if( !S_AL_BufferEvict( ) )
326 		{
327 			S_AL_BufferUseDefault(sfx);
328 			Z_Free(data);
329 			Com_Printf( S_COLOR_RED "ERROR: Out of memory loading %s\n", knownSfx[sfx].filename);
330 			return;
331 		}
332 
333 		// Try load it again
334 		qalBufferData(knownSfx[sfx].buffer, format, data, info.size, info.rate);
335 		error = qalGetError();
336 	}
337 
338 	// Some other error condition
339 	if(error != AL_NO_ERROR)
340 	{
341 		S_AL_BufferUseDefault(sfx);
342 		Z_Free(data);
343 		Com_Printf( S_COLOR_RED "ERROR: Can't fill sound buffer for %s - %s\n",
344 				knownSfx[sfx].filename, S_AL_ErrorMsg(error));
345 		return;
346 	}
347 
348 	// Free the memory
349 	Z_Free(data);
350 
351 	// Woo!
352 	knownSfx[sfx].inMemory = qtrue;
353 }
354 
355 /*
356 =================
357 S_AL_BufferUse
358 =================
359 */
360 static
S_AL_BufferUse(sfxHandle_t sfx)361 void S_AL_BufferUse(sfxHandle_t sfx)
362 {
363 	if(knownSfx[sfx].filename[0] == '\0')
364 		return;
365 
366 	if((!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
367 		S_AL_BufferLoad(sfx);
368 	knownSfx[sfx].lastUsedTime = Sys_Milliseconds();
369 }
370 
371 /*
372 =================
373 S_AL_BufferInit
374 =================
375 */
376 static
S_AL_BufferInit(void)377 qboolean S_AL_BufferInit( void )
378 {
379 	if(alBuffersInitialised)
380 		return qtrue;
381 
382 	// Clear the hash table, and SFX table
383 	memset(knownSfx, 0, sizeof(knownSfx));
384 	numSfx = 0;
385 
386 	// Load the default sound, and lock it
387 	default_sfx = S_AL_BufferFind("sound/feedback/hit.wav");
388 	S_AL_BufferUse(default_sfx);
389 	knownSfx[default_sfx].isLocked = qtrue;
390 
391 	// All done
392 	alBuffersInitialised = qtrue;
393 	return qtrue;
394 }
395 
396 /*
397 =================
398 S_AL_BufferShutdown
399 =================
400 */
401 static
S_AL_BufferShutdown(void)402 void S_AL_BufferShutdown( void )
403 {
404 	int i;
405 
406 	if(!alBuffersInitialised)
407 		return;
408 
409 	// Unlock the default sound effect
410 	knownSfx[default_sfx].isLocked = qfalse;
411 
412 	// Free all used effects
413 	for(i = 0; i < numSfx; i++)
414 		S_AL_BufferUnload(i);
415 
416 	// Clear the tables
417 	memset(knownSfx, 0, sizeof(knownSfx));
418 
419 	// All undone
420 	alBuffersInitialised = qfalse;
421 }
422 
423 /*
424 =================
425 S_AL_RegisterSound
426 =================
427 */
428 static
S_AL_RegisterSound(const char * sample,qboolean compressed)429 sfxHandle_t S_AL_RegisterSound( const char *sample, qboolean compressed )
430 {
431 	sfxHandle_t sfx = S_AL_BufferFind(sample);
432 
433 	if( s_alPrecache->integer && (!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
434 		S_AL_BufferLoad(sfx);
435 	knownSfx[sfx].lastUsedTime = Com_Milliseconds();
436 
437 	return sfx;
438 }
439 
440 /*
441 =================
442 S_AL_BufferGet
443 
444 Return's an sfx's buffer
445 =================
446 */
447 static
S_AL_BufferGet(sfxHandle_t sfx)448 ALuint S_AL_BufferGet(sfxHandle_t sfx)
449 {
450 	return knownSfx[sfx].buffer;
451 }
452 
453 
454 //===========================================================================
455 
456 
457 typedef struct src_s
458 {
459 	ALuint					alSource;		// OpenAL source object
460 	sfxHandle_t 		sfx;				// Sound effect in use
461 
462 	int							lastUsedTime;		// Last time used
463 	alSrcPriority_t	priority;		// Priority
464 	int							entity;			// Owning entity (-1 if none)
465 	int							channel;		// Associated channel (-1 if none)
466 
467 	int							isActive;		// Is this source currently in use?
468 	int							isLocked;		// This is locked (un-allocatable)
469 	int							isLooping;	// Is this a looping effect (attached to an entity)
470 	int							isTracking;	// Is this object tracking it's owner
471 
472 	float							curGain;	// gain employed if source is within maxdistance.
473 	float							scaleGain;	// Last gain value for this source. 0 if muted.
474 
475 	qboolean				local;			// Is this local (relative to the cam)
476 } src_t;
477 
478 #ifdef MACOS_X
479 	#define MAX_SRC 64
480 #else
481 	#define MAX_SRC 128
482 #endif
483 static src_t srcList[MAX_SRC];
484 static int srcCount = 0;
485 static qboolean alSourcesInitialised = qfalse;
486 static vec3_t lastListenerOrigin = { 0.0f, 0.0f, 0.0f };
487 
488 typedef struct sentity_s
489 {
490 	vec3_t					origin;
491 
492 	int							srcAllocated; // If a src_t has been allocated to this entity
493 	int							srcIndex;
494 
495 	qboolean				loopAddedThisFrame;
496 	alSrcPriority_t	loopPriority;
497 	sfxHandle_t			loopSfx;
498 	qboolean				startLoopingSound;
499 } sentity_t;
500 
501 static sentity_t entityList[MAX_GENTITIES];
502 
503 /*
504 =================
505 S_AL_SanitiseVector
506 =================
507 */
508 #define S_AL_SanitiseVector(v) _S_AL_SanitiseVector(v,__LINE__)
_S_AL_SanitiseVector(vec3_t v,int line)509 static void _S_AL_SanitiseVector( vec3_t v, int line )
510 {
511 	if( Q_isnan( v[ 0 ] ) || Q_isnan( v[ 1 ] ) || Q_isnan( v[ 2 ] ) )
512 	{
513 		Com_DPrintf( S_COLOR_YELLOW "WARNING: vector with one or more NaN components "
514 				"being passed to OpenAL at %s:%d -- zeroing\n", __FILE__, line );
515 		VectorClear( v );
516 	}
517 }
518 
519 
520 #define AL_THIRD_PERSON_THRESHOLD_SQ (48.0f*48.0f)
521 
522 /*
523 =================
524 S_AL_ScaleGain
525 Adapt the gain if necessary to get a quicker fadeout when the source is too far away.
526 =================
527 */
528 
S_AL_ScaleGain(src_t * chksrc,vec3_t origin)529 static void S_AL_ScaleGain(src_t *chksrc, vec3_t origin)
530 {
531 	float distance;
532 
533 	if(!chksrc->local)
534 		distance = Distance(origin, lastListenerOrigin);
535 
536 	// If we exceed a certain distance, scale the gain linearly until the sound
537 	// vanishes into nothingness.
538 	if(!chksrc->local && (distance -= s_alMaxDistance->value) > 0)
539 	{
540 		float scaleFactor;
541 
542 		if(distance >= s_alGraceDistance->value)
543 			scaleFactor = 0.0f;
544 		else
545 			scaleFactor = 1.0f - distance / s_alGraceDistance->value;
546 
547 		scaleFactor *= chksrc->curGain;
548 
549 		if(chksrc->scaleGain != scaleFactor);
550 		{
551 			chksrc->scaleGain = scaleFactor;
552 			// if(scaleFactor > 0.0f)
553 			// Com_Printf("%f\n", scaleFactor);
554 			qalSourcef(chksrc->alSource, AL_GAIN, chksrc->scaleGain);
555 		}
556 	}
557 	else if(chksrc->scaleGain != chksrc->curGain)
558 	{
559 		chksrc->scaleGain = chksrc->curGain;
560 		qalSourcef(chksrc->alSource, AL_GAIN, chksrc->scaleGain);
561 	}
562 }
563 
564 /*
565 =================
566 S_AL_HearingThroughEntity
567 =================
568 */
S_AL_HearingThroughEntity(int entityNum)569 static qboolean S_AL_HearingThroughEntity( int entityNum )
570 {
571 	float	distanceSq;
572 
573 	if( clc.clientNum == entityNum )
574 	{
575 		// FIXME: <tim@ngus.net> 28/02/06 This is an outrageous hack to detect
576 		// whether or not the player is rendering in third person or not. We can't
577 		// ask the renderer because the renderer has no notion of entities and we
578 		// can't ask cgame since that would involve changing the API and hence mod
579 		// compatibility. I don't think there is any way around this, but I'll leave
580 		// the FIXME just in case anyone has a bright idea.
581 		distanceSq = DistanceSquared(
582 				entityList[ entityNum ].origin,
583 				lastListenerOrigin );
584 
585 		if( distanceSq > AL_THIRD_PERSON_THRESHOLD_SQ )
586 			return qfalse; //we're the player, but third person
587 		else
588 			return qtrue;  //we're the player
589 	}
590 	else
591 		return qfalse; //not the player
592 }
593 
594 /*
595 =================
596 S_AL_SrcInit
597 =================
598 */
599 static
S_AL_SrcInit(void)600 qboolean S_AL_SrcInit( void )
601 {
602 	int i;
603 	int limit;
604 	ALenum error;
605 
606 	// Clear the sources data structure
607 	memset(srcList, 0, sizeof(srcList));
608 	srcCount = 0;
609 
610 	// Cap s_alSources to MAX_SRC
611 	limit = s_alSources->integer;
612 	if(limit > MAX_SRC)
613 		limit = MAX_SRC;
614 	else if(limit < 16)
615 		limit = 16;
616 
617 	// Allocate as many sources as possible
618 	for(i = 0; i < limit; i++)
619 	{
620 		qalGenSources(1, &srcList[i].alSource);
621 		if((error = qalGetError()) != AL_NO_ERROR)
622 			break;
623 		srcCount++;
624 	}
625 
626 	// All done. Print this for informational purposes
627 	Com_Printf( "Allocated %d sources.\n", srcCount);
628 	alSourcesInitialised = qtrue;
629 	return qtrue;
630 }
631 
632 /*
633 =================
634 S_AL_SrcShutdown
635 =================
636 */
637 static
S_AL_SrcShutdown(void)638 void S_AL_SrcShutdown( void )
639 {
640 	int i;
641 
642 	if(!alSourcesInitialised)
643 		return;
644 
645 	// Destroy all the sources
646 	for(i = 0; i < srcCount; i++)
647 	{
648 		if(srcList[i].isLocked)
649 			Com_DPrintf( S_COLOR_YELLOW "WARNING: Source %d is locked\n", i);
650 
651 		qalSourceStop(srcList[i].alSource);
652 		qalDeleteSources(1, &srcList[i].alSource);
653 	}
654 
655 	memset(srcList, 0, sizeof(srcList));
656 
657 	alSourcesInitialised = qfalse;
658 }
659 
660 /*
661 =================
662 S_AL_SrcSetup
663 =================
664 */
S_AL_SrcSetup(srcHandle_t src,sfxHandle_t sfx,alSrcPriority_t priority,int entity,int channel,qboolean local)665 static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t priority,
666 		int entity, int channel, qboolean local)
667 {
668 	ALuint buffer;
669 	src_t *curSource;
670 
671 	// Mark the SFX as used, and grab the raw AL buffer
672 	S_AL_BufferUse(sfx);
673 	buffer = S_AL_BufferGet(sfx);
674 
675 	// Set up src struct
676 	curSource = &srcList[src];
677 
678 	curSource->lastUsedTime = Sys_Milliseconds();
679 	curSource->sfx = sfx;
680 	curSource->priority = priority;
681 	curSource->entity = entity;
682 	curSource->channel = channel;
683 	curSource->isActive = qtrue;
684 	curSource->isLocked = qfalse;
685 	curSource->isLooping = qfalse;
686 	curSource->isTracking = qfalse;
687 	curSource->curGain = s_alGain->value * s_volume->value;
688 	curSource->scaleGain = curSource->curGain;
689 	curSource->local = local;
690 
691 	// Set up OpenAL source
692 	qalSourcei(curSource->alSource, AL_BUFFER, buffer);
693 	qalSourcef(curSource->alSource, AL_PITCH, 1.0f);
694 	qalSourcef(curSource->alSource, AL_GAIN, curSource->curGain);
695 	qalSourcefv(curSource->alSource, AL_POSITION, vec3_origin);
696 	qalSourcefv(curSource->alSource, AL_VELOCITY, vec3_origin);
697 	qalSourcei(curSource->alSource, AL_LOOPING, AL_FALSE);
698 	qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value);
699 
700 	if(local)
701 	{
702 		qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_TRUE);
703 		qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, 0.0f);
704 	}
705 	else
706 	{
707 		qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE);
708 		qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value);
709 	}
710 }
711 
712 /*
713 =================
714 S_AL_SrcKill
715 =================
716 */
S_AL_SrcKill(srcHandle_t src)717 static void S_AL_SrcKill(srcHandle_t src)
718 {
719 	// I'm not touching it. Unlock it first.
720 	if(srcList[src].isLocked)
721 		return;
722 
723 	// Stop it if it's playing
724 	if(srcList[src].isActive)
725 		qalSourceStop(srcList[src].alSource);
726 
727 	// Remove the entity association
728 	if((srcList[src].isLooping) && (srcList[src].entity != -1))
729 	{
730 		int ent = srcList[src].entity;
731 		entityList[ent].srcAllocated = qfalse;
732 		entityList[ent].srcIndex = -1;
733 		entityList[ent].loopAddedThisFrame = qfalse;
734 		entityList[ent].startLoopingSound = qfalse;
735 	}
736 
737 	// Remove the buffer
738 	qalSourcei(srcList[src].alSource, AL_BUFFER, 0);
739 
740 	srcList[src].sfx = 0;
741 	srcList[src].lastUsedTime = 0;
742 	srcList[src].priority = 0;
743 	srcList[src].entity = -1;
744 	srcList[src].channel = -1;
745 	srcList[src].isActive = qfalse;
746 	srcList[src].isLocked = qfalse;
747 	srcList[src].isLooping = qfalse;
748 	srcList[src].isTracking = qfalse;
749 	srcList[src].local = qfalse;
750 }
751 
752 /*
753 =================
754 S_AL_SrcAlloc
755 =================
756 */
757 static
S_AL_SrcAlloc(alSrcPriority_t priority,int entnum,int channel)758 srcHandle_t S_AL_SrcAlloc( alSrcPriority_t priority, int entnum, int channel )
759 {
760 	int i;
761 	int empty = -1;
762 	int weakest = -1;
763 	int weakest_time = Sys_Milliseconds();
764 	int weakest_pri = 999;
765 
766 	for(i = 0; i < srcCount; i++)
767 	{
768 		// If it's locked, we aren't even going to look at it
769 		if(srcList[i].isLocked)
770 			continue;
771 
772 		// Is it empty or not?
773 		if((!srcList[i].isActive) && (empty == -1))
774 			empty = i;
775 		else if(srcList[i].priority < priority)
776 		{
777 			// If it's older or has lower priority, flag it as weak
778 			if((srcList[i].priority < weakest_pri) ||
779 				(srcList[i].lastUsedTime < weakest_time))
780 			{
781 				weakest_pri = srcList[i].priority;
782 				weakest_time = srcList[i].lastUsedTime;
783 				weakest = i;
784 			}
785 		}
786 
787 		// The channel system is not actually adhered to by baseq3, and not
788 		// implemented in snd_dma.c, so while the following is strictly correct, it
789 		// causes incorrect behaviour versus defacto baseq3
790 #if 0
791 		// Is it an exact match, and not on channel 0?
792 		if((srcList[i].entity == entnum) && (srcList[i].channel == channel) && (channel != 0))
793 		{
794 			S_AL_SrcKill(i);
795 			return i;
796 		}
797 #endif
798 	}
799 
800 	// Do we have an empty one?
801 	if(empty != -1)
802 	{
803 		S_AL_SrcKill( empty );
804 		return empty;
805 	}
806 
807 	// No. How about an overridable one?
808 	if(weakest != -1)
809 	{
810 		S_AL_SrcKill(weakest);
811 		return weakest;
812 	}
813 
814 	// Nothing. Return failure (cries...)
815 	return -1;
816 }
817 
818 /*
819 =================
820 S_AL_SrcFind
821 
822 Finds an active source with matching entity and channel numbers
823 Returns -1 if there isn't one
824 =================
825 */
826 #if 0
827 static
828 srcHandle_t S_AL_SrcFind(int entnum, int channel)
829 {
830 	int i;
831 	for(i = 0; i < srcCount; i++)
832 	{
833 		if(!srcList[i].isActive)
834 			continue;
835 		if((srcList[i].entity == entnum) && (srcList[i].channel == channel))
836 			return i;
837 	}
838 	return -1;
839 }
840 #endif
841 
842 /*
843 =================
844 S_AL_SrcLock
845 
846 Locked sources will not be automatically reallocated or managed
847 =================
848 */
849 static
S_AL_SrcLock(srcHandle_t src)850 void S_AL_SrcLock(srcHandle_t src)
851 {
852 	srcList[src].isLocked = qtrue;
853 }
854 
855 /*
856 =================
857 S_AL_SrcUnlock
858 
859 Once unlocked, the source may be reallocated again
860 =================
861 */
862 static
S_AL_SrcUnlock(srcHandle_t src)863 void S_AL_SrcUnlock(srcHandle_t src)
864 {
865 	srcList[src].isLocked = qfalse;
866 }
867 
868 /*
869 =================
870 S_AL_UpdateEntityPosition
871 =================
872 */
873 static
S_AL_UpdateEntityPosition(int entityNum,const vec3_t origin)874 void S_AL_UpdateEntityPosition( int entityNum, const vec3_t origin )
875 {
876 	vec3_t sanOrigin;
877 
878 	VectorCopy( origin, sanOrigin );
879 	S_AL_SanitiseVector( sanOrigin );
880 	if ( entityNum < 0 || entityNum > MAX_GENTITIES )
881 		Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
882 	VectorCopy( sanOrigin, entityList[entityNum].origin );
883 }
884 
885 /*
886 =================
887 S_AL_CheckInput
888 Check whether input values from mods are out of range.
889 Necessary for i.g. Western Quake3 mod which is buggy.
890 =================
891 */
S_AL_CheckInput(int entityNum,sfxHandle_t sfx)892 static qboolean S_AL_CheckInput(int entityNum, sfxHandle_t sfx)
893 {
894 	if (entityNum < 0 || entityNum > MAX_GENTITIES)
895 		Com_Error(ERR_DROP, "S_StartSound: bad entitynum %i", entityNum);
896 
897 	if (sfx < 0 || sfx >= numSfx)
898 	{
899 		Com_Printf(S_COLOR_RED "ERROR: S_AL_CheckInput: handle %i out of range\n", sfx);
900 		return qtrue;
901 	}
902 
903 	return qfalse;
904 }
905 
906 /*
907 =================
908 S_AL_StartLocalSound
909 
910 Play a local (non-spatialized) sound effect
911 =================
912 */
913 static
S_AL_StartLocalSound(sfxHandle_t sfx,int channel)914 void S_AL_StartLocalSound(sfxHandle_t sfx, int channel)
915 {
916 	srcHandle_t src;
917 
918 	if(S_AL_CheckInput(0, sfx))
919 		return;
920 
921 	// Try to grab a source
922 	src = S_AL_SrcAlloc(SRCPRI_LOCAL, -1, channel);
923 
924 	if(src == -1)
925 		return;
926 
927 	// Set up the effect
928 	S_AL_SrcSetup(src, sfx, SRCPRI_LOCAL, -1, channel, qtrue);
929 
930 	// Start it playing
931 	qalSourcePlay(srcList[src].alSource);
932 }
933 
934 /*
935 =================
936 S_AL_StartSound
937 
938 Play a one-shot sound effect
939 =================
940 */
941 static
S_AL_StartSound(vec3_t origin,int entnum,int entchannel,sfxHandle_t sfx)942 void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx )
943 {
944 	vec3_t sorigin;
945 	srcHandle_t src;
946 
947 	if(S_AL_CheckInput(origin ? 0 : entnum, sfx))
948 		return;
949 
950 	// Try to grab a source
951 	src = S_AL_SrcAlloc(SRCPRI_ONESHOT, entnum, entchannel);
952 	if(src == -1)
953 		return;
954 
955 	// Set up the effect
956 	if( origin == NULL )
957 	{
958 		if( S_AL_HearingThroughEntity( entnum ) )
959 		{
960 			// Where the entity is the local player, play a local sound
961 			S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qtrue );
962 			VectorClear( sorigin );
963 		}
964 		else
965 		{
966 			S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse );
967 			VectorCopy( entityList[ entnum ].origin, sorigin );
968 		}
969 		srcList[ src ].isTracking = qtrue;
970 	}
971 	else
972 	{
973 		S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse );
974 		VectorCopy( origin, sorigin );
975 	}
976 
977 	S_AL_SanitiseVector( sorigin );
978 	qalSourcefv( srcList[ src ].alSource, AL_POSITION, sorigin );
979 	S_AL_ScaleGain(&srcList[src], sorigin);
980 
981 	// Start it playing
982 	qalSourcePlay(srcList[src].alSource);
983 }
984 
985 /*
986 =================
987 S_AL_ClearLoopingSounds
988 =================
989 */
990 static
S_AL_ClearLoopingSounds(qboolean killall)991 void S_AL_ClearLoopingSounds( qboolean killall )
992 {
993 	int i;
994 	for(i = 0; i < srcCount; i++)
995 	{
996 		if((srcList[i].isLooping) && (srcList[i].entity != -1))
997 			entityList[srcList[i].entity].loopAddedThisFrame = qfalse;
998 	}
999 }
1000 
1001 /*
1002 =================
1003 S_AL_SrcLoop
1004 =================
1005 */
S_AL_SrcLoop(alSrcPriority_t priority,sfxHandle_t sfx,const vec3_t origin,const vec3_t velocity,int entityNum)1006 static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx,
1007 		const vec3_t origin, const vec3_t velocity, int entityNum )
1008 {
1009 	int				src;
1010 	sentity_t	*sent = &entityList[ entityNum ];
1011 	src_t		*curSource;
1012 
1013 	// Do we need to allocate a new source for this entity
1014 	if( !sent->srcAllocated )
1015 	{
1016 		// Try to get a channel
1017 		src = S_AL_SrcAlloc( priority, entityNum, -1 );
1018 		if( src == -1 )
1019 		{
1020 			Com_DPrintf( S_COLOR_YELLOW "WARNING: Failed to allocate source "
1021 					"for loop sfx %d on entity %d\n", sfx, entityNum );
1022 			return;
1023 		}
1024 
1025 		sent->startLoopingSound = qtrue;
1026 	}
1027 	else
1028 		src = sent->srcIndex;
1029 
1030 	sent->srcAllocated = qtrue;
1031 	sent->srcIndex = src;
1032 
1033 	sent->loopPriority = priority;
1034 	sent->loopSfx = sfx;
1035 
1036 	// If this is not set then the looping sound is removed
1037 	sent->loopAddedThisFrame = qtrue;
1038 
1039 	curSource = &srcList[src];
1040 
1041 	// UGH
1042 	// These lines should be called via S_AL_SrcSetup, but we
1043 	// can't call that yet as it buffers sfxes that may change
1044 	// with subsequent calls to S_AL_SrcLoop
1045 	curSource->entity = entityNum;
1046 	curSource->isLooping = qtrue;
1047 	curSource->isActive = qtrue;
1048 
1049 	if( S_AL_HearingThroughEntity( entityNum ) )
1050 	{
1051 		curSource->local = qtrue;
1052 
1053 		qalSourcefv( curSource->alSource, AL_POSITION, vec3_origin );
1054 		qalSourcefv( curSource->alSource, AL_VELOCITY, vec3_origin );
1055 	}
1056 	else
1057 	{
1058 		curSource->local = qfalse;
1059 
1060 		qalSourcefv( curSource->alSource, AL_POSITION, (ALfloat *)sent->origin );
1061 		qalSourcefv( curSource->alSource, AL_VELOCITY, (ALfloat *)velocity );
1062 
1063 	}
1064 
1065 	S_AL_ScaleGain(curSource, sent->origin);
1066 }
1067 
1068 /*
1069 =================
1070 S_AL_AddLoopingSound
1071 =================
1072 */
1073 static
S_AL_AddLoopingSound(int entityNum,const vec3_t origin,const vec3_t velocity,sfxHandle_t sfx)1074 void S_AL_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
1075 {
1076 	vec3_t sanOrigin, sanVelocity;
1077 
1078 	if(S_AL_CheckInput(entityNum, sfx))
1079 		return;
1080 
1081 	VectorCopy( origin, sanOrigin );
1082 	VectorCopy( velocity, sanVelocity );
1083 	S_AL_SanitiseVector( sanOrigin );
1084 	S_AL_SanitiseVector( sanVelocity );
1085 
1086 	S_AL_SrcLoop(SRCPRI_ENTITY, sfx, sanOrigin, sanVelocity, entityNum);
1087 }
1088 
1089 /*
1090 =================
1091 S_AL_AddRealLoopingSound
1092 =================
1093 */
1094 static
S_AL_AddRealLoopingSound(int entityNum,const vec3_t origin,const vec3_t velocity,sfxHandle_t sfx)1095 void S_AL_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx )
1096 {
1097 	vec3_t sanOrigin, sanVelocity;
1098 
1099 	if(S_AL_CheckInput(entityNum, sfx))
1100 		return;
1101 
1102 	VectorCopy( origin, sanOrigin );
1103 	VectorCopy( velocity, sanVelocity );
1104 	S_AL_SanitiseVector( sanOrigin );
1105 	S_AL_SanitiseVector( sanVelocity );
1106 
1107 	// There are certain maps (*cough* Q3:TA mpterra*) that have large quantities
1108 	// of ET_SPEAKERS in the PVS at any given time. OpenAL can't cope with mixing
1109 	// large numbers of sounds, so this culls them by distance
1110 	if( DistanceSquared( sanOrigin, lastListenerOrigin ) > (s_alMaxDistance->value + s_alGraceDistance->value) *
1111 							    (s_alMaxDistance->value + s_alGraceDistance->value) )
1112 		return;
1113 
1114 	S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, sanOrigin, sanVelocity, entityNum);
1115 }
1116 
1117 /*
1118 =================
1119 S_AL_StopLoopingSound
1120 =================
1121 */
1122 static
S_AL_StopLoopingSound(int entityNum)1123 void S_AL_StopLoopingSound(int entityNum )
1124 {
1125 	if(entityList[entityNum].srcAllocated)
1126 		S_AL_SrcKill(entityList[entityNum].srcIndex);
1127 }
1128 
1129 /*
1130 =================
1131 S_AL_SrcUpdate
1132 
1133 Update state (move things around, manage sources, and so on)
1134 =================
1135 */
1136 static
S_AL_SrcUpdate(void)1137 void S_AL_SrcUpdate( void )
1138 {
1139 	int i;
1140 	int entityNum;
1141 	ALint state;
1142 	src_t *curSource;
1143 
1144 	for(i = 0; i < srcCount; i++)
1145 	{
1146 		entityNum = srcList[i].entity;
1147 		curSource = &srcList[i];
1148 
1149 		if(curSource->isLocked)
1150 			continue;
1151 
1152 		if(!curSource->isActive)
1153 			continue;
1154 
1155 		// Update source parameters
1156 		if((s_alGain->modified)||(s_volume->modified))
1157 			curSource->curGain = s_alGain->value * s_volume->value;
1158 		if((s_alRolloff->modified)&&(!curSource->local))
1159 			qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value);
1160 		if(s_alMinDistance->modified)
1161 			qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value);
1162 
1163 		if(curSource->isLooping)
1164 		{
1165 			sentity_t *sent = &entityList[ entityNum ];
1166 
1167 			// If a looping effect hasn't been touched this frame, kill it
1168 			if(sent->loopAddedThisFrame)
1169 			{
1170 				// The sound has changed without an intervening removal
1171 				if(curSource->isActive && !sent->startLoopingSound &&
1172 						curSource->sfx != sent->loopSfx)
1173 				{
1174 					qalSourceStop(curSource->alSource);
1175 					qalSourcei(curSource->alSource, AL_BUFFER, 0);
1176 					sent->startLoopingSound = qtrue;
1177 				}
1178 
1179 				// The sound hasn't been started yet
1180 				if(sent->startLoopingSound)
1181 				{
1182 					S_AL_SrcSetup(i, sent->loopSfx, sent->loopPriority,
1183 							entityNum, -1, curSource->local);
1184 					curSource->isLooping = qtrue;
1185 					qalSourcei(curSource->alSource, AL_LOOPING, AL_TRUE);
1186 					qalSourcePlay(curSource->alSource);
1187 
1188 					sent->startLoopingSound = qfalse;
1189 				}
1190 
1191 				// Update locality
1192 				if(curSource->local)
1193 				{
1194 					qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_TRUE);
1195 					qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, 0.0f);
1196 				}
1197 				else
1198 				{
1199 					qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE);
1200 					qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value);
1201 				}
1202 			}
1203 			else
1204 				S_AL_SrcKill( i );
1205 
1206 			continue;
1207 		}
1208 
1209 		// Check if it's done, and flag it
1210 		qalGetSourcei(curSource->alSource, AL_SOURCE_STATE, &state);
1211 		if(state == AL_STOPPED)
1212 		{
1213 			S_AL_SrcKill(i);
1214 			continue;
1215 		}
1216 
1217 		// Query relativity of source, don't move if it's true
1218 		qalGetSourcei(curSource->alSource, AL_SOURCE_RELATIVE, &state);
1219 
1220 		// See if it needs to be moved
1221 		if(curSource->isTracking && !state)
1222 		{
1223 			qalSourcefv(curSource->alSource, AL_POSITION, entityList[entityNum].origin);
1224  			S_AL_ScaleGain(curSource, entityList[entityNum].origin);
1225 		}
1226 	}
1227 }
1228 
1229 /*
1230 =================
1231 S_AL_SrcShutup
1232 =================
1233 */
1234 static
S_AL_SrcShutup(void)1235 void S_AL_SrcShutup( void )
1236 {
1237 	int i;
1238 	for(i = 0; i < srcCount; i++)
1239 		S_AL_SrcKill(i);
1240 }
1241 
1242 /*
1243 =================
1244 S_AL_SrcGet
1245 =================
1246 */
1247 static
S_AL_SrcGet(srcHandle_t src)1248 ALuint S_AL_SrcGet(srcHandle_t src)
1249 {
1250 	return srcList[src].alSource;
1251 }
1252 
1253 
1254 //===========================================================================
1255 
1256 static srcHandle_t streamSourceHandles[MAX_RAW_STREAMS];
1257 static qboolean streamPlaying[MAX_RAW_STREAMS];
1258 static ALuint streamSources[MAX_RAW_STREAMS];
1259 
1260 /*
1261 =================
1262 S_AL_AllocateStreamChannel
1263 =================
1264 */
S_AL_AllocateStreamChannel(int stream)1265 static void S_AL_AllocateStreamChannel( int stream )
1266 {
1267 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1268 		return;
1269 
1270 	// Allocate a streamSource at high priority
1271 	streamSourceHandles[stream] = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0);
1272 	if(streamSourceHandles[stream] == -1)
1273 		return;
1274 
1275 	// Lock the streamSource so nobody else can use it, and get the raw streamSource
1276 	S_AL_SrcLock(streamSourceHandles[stream]);
1277 	streamSources[stream] = S_AL_SrcGet(streamSourceHandles[stream]);
1278 
1279 	// Set some streamSource parameters
1280 	qalSourcei (streamSources[stream], AL_BUFFER,          0            );
1281 	qalSourcei (streamSources[stream], AL_LOOPING,         AL_FALSE     );
1282 	qalSource3f(streamSources[stream], AL_POSITION,        0.0, 0.0, 0.0);
1283 	qalSource3f(streamSources[stream], AL_VELOCITY,        0.0, 0.0, 0.0);
1284 	qalSource3f(streamSources[stream], AL_DIRECTION,       0.0, 0.0, 0.0);
1285 	qalSourcef (streamSources[stream], AL_ROLLOFF_FACTOR,  0.0          );
1286 	qalSourcei (streamSources[stream], AL_SOURCE_RELATIVE, AL_TRUE      );
1287 }
1288 
1289 /*
1290 =================
1291 S_AL_FreeStreamChannel
1292 =================
1293 */
S_AL_FreeStreamChannel(int stream)1294 static void S_AL_FreeStreamChannel( int stream )
1295 {
1296 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1297 		return;
1298 
1299 	// Release the output streamSource
1300 	S_AL_SrcUnlock(streamSourceHandles[stream]);
1301 	streamSources[stream] = 0;
1302 	streamSourceHandles[stream] = -1;
1303 }
1304 
1305 /*
1306 =================
1307 S_AL_RawSamples
1308 =================
1309 */
1310 static
S_AL_RawSamples(int stream,int samples,int rate,int width,int channels,const byte * data,float volume)1311 void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels, const byte *data, float volume)
1312 {
1313 	ALuint buffer;
1314 	ALuint format;
1315 
1316 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1317 		return;
1318 
1319 	format = S_AL_Format( width, channels );
1320 
1321 	// Create the streamSource if necessary
1322 	if(streamSourceHandles[stream] == -1)
1323 	{
1324 		S_AL_AllocateStreamChannel(stream);
1325 
1326 		// Failed?
1327 		if(streamSourceHandles[stream] == -1)
1328 		{
1329 			Com_Printf( S_COLOR_RED "ERROR: Can't allocate streaming streamSource\n");
1330 			return;
1331 		}
1332 	}
1333 
1334 	// Create a buffer, and stuff the data into it
1335 	qalGenBuffers(1, &buffer);
1336 	qalBufferData(buffer, format, (ALvoid *)data, (samples * width * channels), rate);
1337 
1338 	// Shove the data onto the streamSource
1339 	qalSourceQueueBuffers(streamSources[stream], 1, &buffer);
1340 
1341 	// Volume
1342 	qalSourcef (streamSources[stream], AL_GAIN, volume * s_volume->value * s_alGain->value);
1343 }
1344 
1345 /*
1346 =================
1347 S_AL_StreamUpdate
1348 =================
1349 */
1350 static
S_AL_StreamUpdate(int stream)1351 void S_AL_StreamUpdate( int stream )
1352 {
1353 	int		numBuffers;
1354 	ALint	state;
1355 
1356 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1357 		return;
1358 
1359 	if(streamSourceHandles[stream] == -1)
1360 		return;
1361 
1362 	// Un-queue any buffers, and delete them
1363 	qalGetSourcei( streamSources[stream], AL_BUFFERS_PROCESSED, &numBuffers );
1364 	while( numBuffers-- )
1365 	{
1366 		ALuint buffer;
1367 		qalSourceUnqueueBuffers(streamSources[stream], 1, &buffer);
1368 		qalDeleteBuffers(1, &buffer);
1369 	}
1370 
1371 	// Start the streamSource playing if necessary
1372 	qalGetSourcei( streamSources[stream], AL_BUFFERS_QUEUED, &numBuffers );
1373 
1374 	qalGetSourcei(streamSources[stream], AL_SOURCE_STATE, &state);
1375 	if(state == AL_STOPPED)
1376 	{
1377 		streamPlaying[stream] = qfalse;
1378 
1379 		// If there are no buffers queued up, release the streamSource
1380 		if( !numBuffers )
1381 			S_AL_FreeStreamChannel( stream );
1382 	}
1383 
1384 	if( !streamPlaying[stream] && numBuffers )
1385 	{
1386 		qalSourcePlay( streamSources[stream] );
1387 		streamPlaying[stream] = qtrue;
1388 	}
1389 }
1390 
1391 /*
1392 =================
1393 S_AL_StreamDie
1394 =================
1395 */
1396 static
S_AL_StreamDie(int stream)1397 void S_AL_StreamDie( int stream )
1398 {
1399 	int		numBuffers;
1400 
1401 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1402 		return;
1403 
1404 	if(streamSourceHandles[stream] == -1)
1405 		return;
1406 
1407 	streamPlaying[stream] = qfalse;
1408 	qalSourceStop(streamSources[stream]);
1409 
1410 	// Un-queue any buffers, and delete them
1411 	qalGetSourcei( streamSources[stream], AL_BUFFERS_PROCESSED, &numBuffers );
1412 	while( numBuffers-- )
1413 	{
1414 		ALuint buffer;
1415 		qalSourceUnqueueBuffers(streamSources[stream], 1, &buffer);
1416 		qalDeleteBuffers(1, &buffer);
1417 	}
1418 
1419 	S_AL_FreeStreamChannel(stream);
1420 }
1421 
1422 
1423 //===========================================================================
1424 
1425 
1426 #define NUM_MUSIC_BUFFERS	4
1427 #define	MUSIC_BUFFER_SIZE 4096
1428 
1429 static qboolean musicPlaying = qfalse;
1430 static srcHandle_t musicSourceHandle = -1;
1431 static ALuint musicSource;
1432 static ALuint musicBuffers[NUM_MUSIC_BUFFERS];
1433 
1434 static snd_stream_t *mus_stream;
1435 static snd_stream_t *intro_stream;
1436 static char s_backgroundLoop[MAX_QPATH];
1437 
1438 static byte decode_buffer[MUSIC_BUFFER_SIZE];
1439 
1440 /*
1441 =================
1442 S_AL_MusicSourceGet
1443 =================
1444 */
S_AL_MusicSourceGet(void)1445 static void S_AL_MusicSourceGet( void )
1446 {
1447 	// Allocate a musicSource at high priority
1448 	musicSourceHandle = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0);
1449 	if(musicSourceHandle == -1)
1450 		return;
1451 
1452 	// Lock the musicSource so nobody else can use it, and get the raw musicSource
1453 	S_AL_SrcLock(musicSourceHandle);
1454 	musicSource = S_AL_SrcGet(musicSourceHandle);
1455 
1456 	// Set some musicSource parameters
1457 	qalSource3f(musicSource, AL_POSITION,        0.0, 0.0, 0.0);
1458 	qalSource3f(musicSource, AL_VELOCITY,        0.0, 0.0, 0.0);
1459 	qalSource3f(musicSource, AL_DIRECTION,       0.0, 0.0, 0.0);
1460 	qalSourcef (musicSource, AL_ROLLOFF_FACTOR,  0.0          );
1461 	qalSourcei (musicSource, AL_SOURCE_RELATIVE, AL_TRUE      );
1462 }
1463 
1464 /*
1465 =================
1466 S_AL_MusicSourceFree
1467 =================
1468 */
S_AL_MusicSourceFree(void)1469 static void S_AL_MusicSourceFree( void )
1470 {
1471 	// Release the output musicSource
1472 	S_AL_SrcUnlock(musicSourceHandle);
1473 	musicSource = 0;
1474 	musicSourceHandle = -1;
1475 }
1476 
1477 /*
1478 =================
1479 S_AL_CloseMusicFiles
1480 =================
1481 */
S_AL_CloseMusicFiles(void)1482 static void S_AL_CloseMusicFiles(void)
1483 {
1484 	if(intro_stream)
1485 	{
1486 		S_CodecCloseStream(intro_stream);
1487 		intro_stream = NULL;
1488 	}
1489 
1490 	if(mus_stream)
1491 	{
1492 		S_CodecCloseStream(mus_stream);
1493 		mus_stream = NULL;
1494 	}
1495 }
1496 
1497 /*
1498 =================
1499 S_AL_StopBackgroundTrack
1500 =================
1501 */
1502 static
S_AL_StopBackgroundTrack(void)1503 void S_AL_StopBackgroundTrack( void )
1504 {
1505 	if(!musicPlaying)
1506 		return;
1507 
1508 	// Stop playing
1509 	qalSourceStop(musicSource);
1510 
1511 	// De-queue the musicBuffers
1512 	qalSourceUnqueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
1513 
1514 	// Destroy the musicBuffers
1515 	qalDeleteBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
1516 
1517 	// Free the musicSource
1518 	S_AL_MusicSourceFree();
1519 
1520 	// Unload the stream
1521 	S_AL_CloseMusicFiles();
1522 
1523 	musicPlaying = qfalse;
1524 }
1525 
1526 /*
1527 =================
1528 S_AL_MusicProcess
1529 =================
1530 */
1531 static
S_AL_MusicProcess(ALuint b)1532 void S_AL_MusicProcess(ALuint b)
1533 {
1534 	ALenum error;
1535 	int l;
1536 	ALuint format;
1537 	snd_stream_t *curstream;
1538 
1539 	if(intro_stream)
1540 		curstream = intro_stream;
1541 	else
1542 		curstream = mus_stream;
1543 
1544 	if(!curstream)
1545 		return;
1546 
1547 	l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer);
1548 
1549 	// Run out data to read, start at the beginning again
1550 	if(l == 0)
1551 	{
1552 		S_CodecCloseStream(curstream);
1553 
1554 		// the intro stream just finished playing so we don't need to reopen
1555 		// the music stream.
1556 		if(intro_stream)
1557 			intro_stream = NULL;
1558 		else
1559 			mus_stream = S_CodecOpenStream(s_backgroundLoop);
1560 
1561 		curstream = mus_stream;
1562 
1563 		if(!curstream)
1564 		{
1565 			S_AL_StopBackgroundTrack();
1566 			return;
1567 		}
1568 
1569 		l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer);
1570 	}
1571 
1572 	format = S_AL_Format(curstream->info.width, curstream->info.channels);
1573 
1574 	if( l == 0 )
1575 	{
1576 		// We have no data to buffer, so buffer silence
1577 		byte dummyData[ 2 ] = { 0 };
1578 
1579 		qalBufferData( b, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050 );
1580 	}
1581 	else
1582 		qalBufferData(b, format, decode_buffer, l, curstream->info.rate);
1583 
1584 	if( ( error = qalGetError( ) ) != AL_NO_ERROR )
1585 	{
1586 		S_AL_StopBackgroundTrack( );
1587 		Com_Printf( S_COLOR_RED "ERROR: while buffering data for music stream - %s\n",
1588 				S_AL_ErrorMsg( error ) );
1589 		return;
1590 	}
1591 }
1592 
1593 /*
1594 =================
1595 S_AL_StartBackgroundTrack
1596 =================
1597 */
1598 static
S_AL_StartBackgroundTrack(const char * intro,const char * loop)1599 void S_AL_StartBackgroundTrack( const char *intro, const char *loop )
1600 {
1601 	int i;
1602 	qboolean issame;
1603 
1604 	// Stop any existing music that might be playing
1605 	S_AL_StopBackgroundTrack();
1606 
1607 	if((!intro || !*intro) && (!loop || !*loop))
1608 		return;
1609 
1610 	// Allocate a musicSource
1611 	S_AL_MusicSourceGet();
1612 	if(musicSourceHandle == -1)
1613 		return;
1614 
1615 	if (!loop || !*loop)
1616 	{
1617 		loop = intro;
1618 		issame = qtrue;
1619 	}
1620 	else if(intro && *intro && !strcmp(intro, loop))
1621 		issame = qtrue;
1622 	else
1623 		issame = qfalse;
1624 
1625 	// Copy the loop over
1626 	strncpy( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
1627 
1628 	if(!issame)
1629 	{
1630 		// Open the intro and don't mind whether it succeeds.
1631 		// The important part is the loop.
1632 		intro_stream = S_CodecOpenStream(intro);
1633 	}
1634 	else
1635 		intro_stream = NULL;
1636 
1637 	mus_stream = S_CodecOpenStream(s_backgroundLoop);
1638 	if(!mus_stream)
1639 	{
1640 		S_AL_CloseMusicFiles();
1641 		S_AL_MusicSourceFree();
1642 		return;
1643 	}
1644 
1645 	// Generate the musicBuffers
1646 	qalGenBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
1647 
1648 	// Queue the musicBuffers up
1649 	for(i = 0; i < NUM_MUSIC_BUFFERS; i++)
1650 	{
1651 		S_AL_MusicProcess(musicBuffers[i]);
1652 	}
1653 
1654 	qalSourceQueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
1655 
1656 	// Set the initial gain property
1657 	qalSourcef(musicSource, AL_GAIN, s_alGain->value * s_musicVolume->value);
1658 
1659 	// Start playing
1660 	qalSourcePlay(musicSource);
1661 
1662 	musicPlaying = qtrue;
1663 }
1664 
1665 /*
1666 =================
1667 S_AL_MusicUpdate
1668 =================
1669 */
1670 static
S_AL_MusicUpdate(void)1671 void S_AL_MusicUpdate( void )
1672 {
1673 	int		numBuffers;
1674 	ALint	state;
1675 
1676 	if(!musicPlaying)
1677 		return;
1678 
1679 	qalGetSourcei( musicSource, AL_BUFFERS_PROCESSED, &numBuffers );
1680 	while( numBuffers-- )
1681 	{
1682 		ALuint b;
1683 		qalSourceUnqueueBuffers(musicSource, 1, &b);
1684 		S_AL_MusicProcess(b);
1685 		qalSourceQueueBuffers(musicSource, 1, &b);
1686 	}
1687 
1688 	// Hitches can cause OpenAL to be starved of buffers when streaming.
1689 	// If this happens, it will stop playback. This restarts the source if
1690 	// it is no longer playing, and if there are buffers available
1691 	qalGetSourcei( musicSource, AL_SOURCE_STATE, &state );
1692 	qalGetSourcei( musicSource, AL_BUFFERS_QUEUED, &numBuffers );
1693 	if( state == AL_STOPPED && numBuffers )
1694 	{
1695 		Com_DPrintf( S_COLOR_YELLOW "Restarted OpenAL music\n" );
1696 		qalSourcePlay(musicSource);
1697 	}
1698 
1699 	// Set the gain property
1700 	qalSourcef(musicSource, AL_GAIN, s_alGain->value * s_musicVolume->value);
1701 }
1702 
1703 
1704 //===========================================================================
1705 
1706 
1707 // Local state variables
1708 static ALCdevice *alDevice;
1709 static ALCcontext *alContext;
1710 
1711 #ifdef USE_VOIP
1712 static ALCdevice *alCaptureDevice;
1713 static cvar_t *s_alCapture;
1714 #endif
1715 
1716 #ifdef _WIN32
1717 #define ALDRIVER_DEFAULT "OpenAL32.dll"
1718 #define ALDEVICE_DEFAULT "Generic Software"
1719 #elif defined(MACOS_X)
1720 #define ALDRIVER_DEFAULT "/System/Library/Frameworks/OpenAL.framework/OpenAL"
1721 #else
1722 #define ALDRIVER_DEFAULT "libopenal.so"
1723 #endif
1724 
1725 /*
1726 =================
1727 S_AL_StopAllSounds
1728 =================
1729 */
1730 static
S_AL_StopAllSounds(void)1731 void S_AL_StopAllSounds( void )
1732 {
1733 	int i;
1734 	S_AL_SrcShutup();
1735 	S_AL_StopBackgroundTrack();
1736 	for (i = 0; i < MAX_RAW_STREAMS; i++)
1737 		S_AL_StreamDie(i);
1738 }
1739 
1740 /*
1741 =================
1742 S_AL_Respatialize
1743 =================
1744 */
1745 static
S_AL_Respatialize(int entityNum,const vec3_t origin,vec3_t axis[3],int inwater)1746 void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater )
1747 {
1748 	float		velocity[3] = {0.0f, 0.0f, 0.0f};
1749 	float		orientation[6];
1750 	vec3_t	sorigin;
1751 
1752 	VectorCopy( origin, sorigin );
1753 	S_AL_SanitiseVector( sorigin );
1754 
1755 	S_AL_SanitiseVector( axis[ 0 ] );
1756 	S_AL_SanitiseVector( axis[ 1 ] );
1757 	S_AL_SanitiseVector( axis[ 2 ] );
1758 
1759 	orientation[0] = axis[0][0]; orientation[1] = axis[0][1]; orientation[2] = axis[0][2];
1760 	orientation[3] = axis[2][0]; orientation[4] = axis[2][1]; orientation[5] = axis[2][2];
1761 
1762 	VectorCopy( sorigin, lastListenerOrigin );
1763 
1764 	// Set OpenAL listener paramaters
1765 	qalListenerfv(AL_POSITION, (ALfloat *)sorigin);
1766 	qalListenerfv(AL_VELOCITY, velocity);
1767 	qalListenerfv(AL_ORIENTATION, orientation);
1768 }
1769 
1770 /*
1771 =================
1772 S_AL_Update
1773 =================
1774 */
1775 static
S_AL_Update(void)1776 void S_AL_Update( void )
1777 {
1778 	int i;
1779 
1780 	// Update SFX channels
1781 	S_AL_SrcUpdate();
1782 
1783 	// Update streams
1784 	for (i = 0; i < MAX_RAW_STREAMS; i++)
1785 		S_AL_StreamUpdate(i);
1786 	S_AL_MusicUpdate();
1787 
1788 	// Doppler
1789 	if(s_doppler->modified)
1790 	{
1791 		s_alDopplerFactor->modified = qtrue;
1792 		s_doppler->modified = qfalse;
1793 	}
1794 
1795 	// Doppler parameters
1796 	if(s_alDopplerFactor->modified)
1797 	{
1798 		if(s_doppler->integer)
1799 			qalDopplerFactor(s_alDopplerFactor->value);
1800 		else
1801 			qalDopplerFactor(0.0f);
1802 		s_alDopplerFactor->modified = qfalse;
1803 	}
1804 	if(s_alDopplerSpeed->modified)
1805 	{
1806 		qalDopplerVelocity(s_alDopplerSpeed->value);
1807 		s_alDopplerSpeed->modified = qfalse;
1808 	}
1809 
1810 	// Clear the modified flags on the other cvars
1811 	s_alGain->modified = qfalse;
1812 	s_volume->modified = qfalse;
1813 	s_musicVolume->modified = qfalse;
1814 	s_alMinDistance->modified = qfalse;
1815 	s_alRolloff->modified = qfalse;
1816 }
1817 
1818 /*
1819 =================
1820 S_AL_DisableSounds
1821 =================
1822 */
1823 static
S_AL_DisableSounds(void)1824 void S_AL_DisableSounds( void )
1825 {
1826 	S_AL_StopAllSounds();
1827 }
1828 
1829 /*
1830 =================
1831 S_AL_BeginRegistration
1832 =================
1833 */
1834 static
S_AL_BeginRegistration(void)1835 void S_AL_BeginRegistration( void )
1836 {
1837 }
1838 
1839 /*
1840 =================
1841 S_AL_ClearSoundBuffer
1842 =================
1843 */
1844 static
S_AL_ClearSoundBuffer(void)1845 void S_AL_ClearSoundBuffer( void )
1846 {
1847 }
1848 
1849 /*
1850 =================
1851 S_AL_SoundList
1852 =================
1853 */
1854 static
S_AL_SoundList(void)1855 void S_AL_SoundList( void )
1856 {
1857 }
1858 
1859 #ifdef USE_VOIP
1860 static
S_AL_StartCapture(void)1861 void S_AL_StartCapture( void )
1862 {
1863 	if (alCaptureDevice != NULL)
1864 		qalcCaptureStart(alCaptureDevice);
1865 }
1866 
1867 static
S_AL_AvailableCaptureSamples(void)1868 int S_AL_AvailableCaptureSamples( void )
1869 {
1870 	int retval = 0;
1871 	if (alCaptureDevice != NULL)
1872 	{
1873 		ALint samples = 0;
1874 		qalcGetIntegerv(alCaptureDevice, ALC_CAPTURE_SAMPLES, sizeof (samples), &samples);
1875 		retval = (int) samples;
1876 	}
1877 	return retval;
1878 }
1879 
1880 static
S_AL_Capture(int samples,byte * data)1881 void S_AL_Capture( int samples, byte *data )
1882 {
1883 	if (alCaptureDevice != NULL)
1884 		qalcCaptureSamples(alCaptureDevice, data, samples);
1885 }
1886 
S_AL_StopCapture(void)1887 void S_AL_StopCapture( void )
1888 {
1889 	if (alCaptureDevice != NULL)
1890 		qalcCaptureStop(alCaptureDevice);
1891 }
1892 
S_AL_MasterGain(float gain)1893 void S_AL_MasterGain( float gain )
1894 {
1895 	qalListenerf(AL_GAIN, gain);
1896 }
1897 #endif
1898 
1899 
1900 /*
1901 =================
1902 S_AL_SoundInfo
1903 =================
1904 */
1905 static
S_AL_SoundInfo(void)1906 void S_AL_SoundInfo( void )
1907 {
1908 	Com_Printf( "OpenAL info:\n" );
1909 	Com_Printf( "  Vendor:     %s\n", qalGetString( AL_VENDOR ) );
1910 	Com_Printf( "  Version:    %s\n", qalGetString( AL_VERSION ) );
1911 	Com_Printf( "  Renderer:   %s\n", qalGetString( AL_RENDERER ) );
1912 	Com_Printf( "  AL Extensions: %s\n", qalGetString( AL_EXTENSIONS ) );
1913 	Com_Printf( "  ALC Extensions: %s\n", qalcGetString( alDevice, ALC_EXTENSIONS ) );
1914 	if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
1915 	{
1916 		Com_Printf("  Device:     %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER));
1917 		Com_Printf("Available Devices:\n%s", s_alAvailableDevices->string);
1918 	}
1919 }
1920 
1921 /*
1922 =================
1923 S_AL_Shutdown
1924 =================
1925 */
1926 static
S_AL_Shutdown(void)1927 void S_AL_Shutdown( void )
1928 {
1929 	// Shut down everything
1930 	int i;
1931 	for (i = 0; i < MAX_RAW_STREAMS; i++)
1932 		S_AL_StreamDie(i);
1933 	S_AL_StopBackgroundTrack( );
1934 	S_AL_SrcShutdown( );
1935 	S_AL_BufferShutdown( );
1936 
1937 	qalcDestroyContext(alContext);
1938 	qalcCloseDevice(alDevice);
1939 
1940 #ifdef USE_VOIP
1941 	if (alCaptureDevice != NULL) {
1942 		qalcCaptureStop(alCaptureDevice);
1943 		qalcCaptureCloseDevice(alCaptureDevice);
1944 		alCaptureDevice = NULL;
1945 		Com_Printf( "OpenAL capture device closed.\n" );
1946 	}
1947 #endif
1948 
1949 	for (i = 0; i < MAX_RAW_STREAMS; i++) {
1950 		streamSourceHandles[i] = -1;
1951 		streamPlaying[i] = qfalse;
1952 		streamSources[i] = 0;
1953 	}
1954 
1955 	QAL_Shutdown();
1956 }
1957 
1958 #endif
1959 
1960 /*
1961 =================
1962 S_AL_Init
1963 =================
1964 */
S_AL_Init(soundInterface_t * si)1965 qboolean S_AL_Init( soundInterface_t *si )
1966 {
1967 #ifdef USE_OPENAL
1968 
1969 	qboolean enumsupport, founddev = qfalse;
1970 	int i;
1971 
1972 	if( !si ) {
1973 		return qfalse;
1974 	}
1975 
1976 	for (i = 0; i < MAX_RAW_STREAMS; i++) {
1977 		streamSourceHandles[i] = -1;
1978 		streamPlaying[i] = qfalse;
1979 		streamSources[i] = 0;
1980 	}
1981 
1982 	// New console variables
1983 	s_alPrecache = Cvar_Get( "s_alPrecache", "1", CVAR_ARCHIVE );
1984 	s_alGain = Cvar_Get( "s_alGain", "1.0", CVAR_ARCHIVE );
1985 	s_alSources = Cvar_Get( "s_alSources", "96", CVAR_ARCHIVE );
1986 	s_alDopplerFactor = Cvar_Get( "s_alDopplerFactor", "1.0", CVAR_ARCHIVE );
1987 	s_alDopplerSpeed = Cvar_Get( "s_alDopplerSpeed", "2200", CVAR_ARCHIVE );
1988 	s_alMinDistance = Cvar_Get( "s_alMinDistance", "120", CVAR_CHEAT );
1989 	s_alMaxDistance = Cvar_Get("s_alMaxDistance", "1024", CVAR_CHEAT);
1990 	s_alRolloff = Cvar_Get( "s_alRolloff", "2", CVAR_CHEAT);
1991 	s_alGraceDistance = Cvar_Get("s_alGraceDistance", "512", CVAR_CHEAT);
1992 
1993 	s_alDriver = Cvar_Get( "s_alDriver", ALDRIVER_DEFAULT, CVAR_ARCHIVE );
1994 
1995 	// Load QAL
1996 	if( !QAL_Init( s_alDriver->string ) )
1997 	{
1998 		Com_Printf( "Failed to load library: \"%s\".\n", s_alDriver->string );
1999 		return qfalse;
2000 	}
2001 
2002 	// Device enumeration support (extension is implemented reasonably only on Windows right now).
2003 	if((enumsupport = qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")))
2004 	{
2005 		char devicenames[1024] = "";
2006 		const char *devicelist;
2007 		const char *defaultdevice;
2008 		int curlen;
2009 
2010 		// get all available devices + the default device name.
2011 		devicelist = qalcGetString(NULL, ALC_DEVICE_SPECIFIER);
2012 		defaultdevice = qalcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
2013 
2014 #ifdef _WIN32
2015 		// check whether the default device is generic hardware. If it is, change to
2016 		// Generic Software as that one works more reliably with various sound systems.
2017 		// If it's not, use OpenAL's default selection as we don't want to ignore
2018 		// native hardware acceleration.
2019 		if(!strcmp(defaultdevice, "Generic Hardware"))
2020 			s_alDevice = Cvar_Get("s_alDevice", ALDEVICE_DEFAULT, CVAR_ARCHIVE | CVAR_LATCH);
2021 		else
2022 #endif
2023 			s_alDevice = Cvar_Get("s_alDevice", defaultdevice, CVAR_ARCHIVE | CVAR_LATCH);
2024 
2025 		// dump a list of available devices to a cvar for the user to see.
2026 		while((curlen = strlen(devicelist)))
2027 		{
2028 			Q_strcat(devicenames, sizeof(devicenames), devicelist);
2029 			Q_strcat(devicenames, sizeof(devicenames), "\n");
2030 
2031 			// check whether the device we want to load is available at all.
2032 			if(!strcmp(s_alDevice->string, devicelist))
2033 				founddev = qtrue;
2034 
2035 			devicelist += curlen + 1;
2036 		}
2037 
2038 		s_alAvailableDevices = Cvar_Get("s_alAvailableDevices", devicenames, CVAR_ROM | CVAR_NORESTART);
2039 
2040 		if(!founddev)
2041 		{
2042 			Cvar_ForceReset("s_alDevice");
2043 			founddev = 1;
2044 		}
2045 	}
2046 
2047 	if(founddev)
2048 		alDevice = qalcOpenDevice(s_alDevice->string);
2049 	else
2050 		alDevice = qalcOpenDevice(NULL);
2051 
2052 	if( !alDevice )
2053 	{
2054 		QAL_Shutdown( );
2055 		Com_Printf( "Failed to open OpenAL device.\n" );
2056 		return qfalse;
2057 	}
2058 
2059 	if(enumsupport)
2060 		Cvar_Set("s_alDevice", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER));
2061 
2062 	// Create OpenAL context
2063 	alContext = qalcCreateContext( alDevice, NULL );
2064 	if( !alContext )
2065 	{
2066 		QAL_Shutdown( );
2067 		qalcCloseDevice( alDevice );
2068 		Com_Printf( "Failed to create OpenAL context.\n" );
2069 		return qfalse;
2070 	}
2071 	qalcMakeContextCurrent( alContext );
2072 
2073 	// Initialize sources, buffers, music
2074 	S_AL_BufferInit( );
2075 	S_AL_SrcInit( );
2076 
2077 	// Set up OpenAL parameters (doppler, etc)
2078 	qalDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
2079 	qalDopplerFactor( s_alDopplerFactor->value );
2080 	qalDopplerVelocity( s_alDopplerSpeed->value );
2081 
2082 #ifdef USE_VOIP
2083 	// !!! FIXME: some of these alcCaptureOpenDevice() values should be cvars.
2084 	// !!! FIXME: add support for capture device enumeration.
2085 	// !!! FIXME: add some better error reporting.
2086 	s_alCapture = Cvar_Get( "s_alCapture", "1", CVAR_ARCHIVE | CVAR_LATCH );
2087 	if (!s_alCapture->integer)
2088 	{
2089 		Com_Printf("OpenAL capture support disabled by user ('+set s_alCapture 1' to enable)\n");
2090 	}
2091 #if USE_MUMBLE
2092 	else if (cl_useMumble->integer)
2093 	{
2094 		Com_Printf("OpenAL capture support disabled for Mumble support\n");
2095 	}
2096 #endif
2097 	else
2098 	{
2099 #ifdef MACOS_X
2100 		// !!! FIXME: Apple has a 1.1-compliant OpenAL, which includes
2101 		// !!! FIXME:  capture support, but they don't list it in the
2102 		// !!! FIXME:  extension string. We need to check the version string,
2103 		// !!! FIXME:  then the extension string, but that's too much trouble,
2104 		// !!! FIXME:  so we'll just check the function pointer for now.
2105 		if (qalcCaptureOpenDevice == NULL)
2106 #else
2107 		if (!qalcIsExtensionPresent(NULL, "ALC_EXT_capture"))
2108 #endif
2109 		{
2110 			Com_Printf("No ALC_EXT_capture support, can't record audio.\n");
2111 		}
2112 		else
2113 		{
2114 			// !!! FIXME: 8000Hz is what Speex narrowband mode needs, but we
2115 			// !!! FIXME:  should probably open the capture device after
2116 			// !!! FIXME:  initializing Speex so we can change to wideband
2117 			// !!! FIXME:  if we like.
2118 			Com_Printf("OpenAL default capture device is '%s'\n",
2119 			           qalcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
2120 			alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096);
2121 			Com_Printf( "OpenAL capture device %s.\n",
2122 			            (alCaptureDevice == NULL) ? "failed to open" : "opened");
2123 		}
2124 	}
2125 #endif
2126 
2127 	si->Shutdown = S_AL_Shutdown;
2128 	si->StartSound = S_AL_StartSound;
2129 	si->StartLocalSound = S_AL_StartLocalSound;
2130 	si->StartBackgroundTrack = S_AL_StartBackgroundTrack;
2131 	si->StopBackgroundTrack = S_AL_StopBackgroundTrack;
2132 	si->RawSamples = S_AL_RawSamples;
2133 	si->StopAllSounds = S_AL_StopAllSounds;
2134 	si->ClearLoopingSounds = S_AL_ClearLoopingSounds;
2135 	si->AddLoopingSound = S_AL_AddLoopingSound;
2136 	si->AddRealLoopingSound = S_AL_AddRealLoopingSound;
2137 	si->StopLoopingSound = S_AL_StopLoopingSound;
2138 	si->Respatialize = S_AL_Respatialize;
2139 	si->UpdateEntityPosition = S_AL_UpdateEntityPosition;
2140 	si->Update = S_AL_Update;
2141 	si->DisableSounds = S_AL_DisableSounds;
2142 	si->BeginRegistration = S_AL_BeginRegistration;
2143 	si->RegisterSound = S_AL_RegisterSound;
2144 	si->ClearSoundBuffer = S_AL_ClearSoundBuffer;
2145 	si->SoundInfo = S_AL_SoundInfo;
2146 	si->SoundList = S_AL_SoundList;
2147 
2148 #ifdef USE_VOIP
2149 	si->StartCapture = S_AL_StartCapture;
2150 	si->AvailableCaptureSamples = S_AL_AvailableCaptureSamples;
2151 	si->Capture = S_AL_Capture;
2152 	si->StopCapture = S_AL_StopCapture;
2153 	si->MasterGain = S_AL_MasterGain;
2154 #endif
2155 
2156 	return qtrue;
2157 #else
2158 	return qfalse;
2159 #endif
2160 }
2161 
2162