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 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1400 		return;
1401 
1402 	if(streamSourceHandles[stream] == -1)
1403 		return;
1404 
1405 	streamPlaying[stream] = qfalse;
1406 	qalSourceStop(streamSources[stream]);
1407 	S_AL_FreeStreamChannel(stream);
1408 }
1409 
1410 
1411 //===========================================================================
1412 
1413 
1414 #define NUM_MUSIC_BUFFERS	4
1415 #define	MUSIC_BUFFER_SIZE 4096
1416 
1417 static qboolean musicPlaying = qfalse;
1418 static srcHandle_t musicSourceHandle = -1;
1419 static ALuint musicSource;
1420 static ALuint musicBuffers[NUM_MUSIC_BUFFERS];
1421 
1422 static snd_stream_t *mus_stream;
1423 static snd_stream_t *intro_stream;
1424 static char s_backgroundLoop[MAX_QPATH];
1425 
1426 static byte decode_buffer[MUSIC_BUFFER_SIZE];
1427 
1428 /*
1429 =================
1430 S_AL_MusicSourceGet
1431 =================
1432 */
S_AL_MusicSourceGet(void)1433 static void S_AL_MusicSourceGet( void )
1434 {
1435 	// Allocate a musicSource at high priority
1436 	musicSourceHandle = S_AL_SrcAlloc(SRCPRI_STREAM, -2, 0);
1437 	if(musicSourceHandle == -1)
1438 		return;
1439 
1440 	// Lock the musicSource so nobody else can use it, and get the raw musicSource
1441 	S_AL_SrcLock(musicSourceHandle);
1442 	musicSource = S_AL_SrcGet(musicSourceHandle);
1443 
1444 	// Set some musicSource parameters
1445 	qalSource3f(musicSource, AL_POSITION,        0.0, 0.0, 0.0);
1446 	qalSource3f(musicSource, AL_VELOCITY,        0.0, 0.0, 0.0);
1447 	qalSource3f(musicSource, AL_DIRECTION,       0.0, 0.0, 0.0);
1448 	qalSourcef (musicSource, AL_ROLLOFF_FACTOR,  0.0          );
1449 	qalSourcei (musicSource, AL_SOURCE_RELATIVE, AL_TRUE      );
1450 }
1451 
1452 /*
1453 =================
1454 S_AL_MusicSourceFree
1455 =================
1456 */
S_AL_MusicSourceFree(void)1457 static void S_AL_MusicSourceFree( void )
1458 {
1459 	// Release the output musicSource
1460 	S_AL_SrcUnlock(musicSourceHandle);
1461 	musicSource = 0;
1462 	musicSourceHandle = -1;
1463 }
1464 
1465 /*
1466 =================
1467 S_AL_CloseMusicFiles
1468 =================
1469 */
S_AL_CloseMusicFiles(void)1470 static void S_AL_CloseMusicFiles(void)
1471 {
1472 	if(intro_stream)
1473 	{
1474 		S_CodecCloseStream(intro_stream);
1475 		intro_stream = NULL;
1476 	}
1477 
1478 	if(mus_stream)
1479 	{
1480 		S_CodecCloseStream(mus_stream);
1481 		mus_stream = NULL;
1482 	}
1483 }
1484 
1485 /*
1486 =================
1487 S_AL_StopBackgroundTrack
1488 =================
1489 */
1490 static
S_AL_StopBackgroundTrack(void)1491 void S_AL_StopBackgroundTrack( void )
1492 {
1493 	if(!musicPlaying)
1494 		return;
1495 
1496 	// Stop playing
1497 	qalSourceStop(musicSource);
1498 
1499 	// De-queue the musicBuffers
1500 	qalSourceUnqueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
1501 
1502 	// Destroy the musicBuffers
1503 	qalDeleteBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
1504 
1505 	// Free the musicSource
1506 	S_AL_MusicSourceFree();
1507 
1508 	// Unload the stream
1509 	S_AL_CloseMusicFiles();
1510 
1511 	musicPlaying = qfalse;
1512 }
1513 
1514 /*
1515 =================
1516 S_AL_MusicProcess
1517 =================
1518 */
1519 static
S_AL_MusicProcess(ALuint b)1520 void S_AL_MusicProcess(ALuint b)
1521 {
1522 	ALenum error;
1523 	int l;
1524 	ALuint format;
1525 	snd_stream_t *curstream;
1526 
1527 	if(intro_stream)
1528 		curstream = intro_stream;
1529 	else
1530 		curstream = mus_stream;
1531 
1532 	if(!curstream)
1533 		return;
1534 
1535 	l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer);
1536 
1537 	// Run out data to read, start at the beginning again
1538 	if(l == 0)
1539 	{
1540 		S_CodecCloseStream(curstream);
1541 
1542 		// the intro stream just finished playing so we don't need to reopen
1543 		// the music stream.
1544 		if(intro_stream)
1545 			intro_stream = NULL;
1546 		else
1547 			mus_stream = S_CodecOpenStream(s_backgroundLoop);
1548 
1549 		curstream = mus_stream;
1550 
1551 		if(!curstream)
1552 		{
1553 			S_AL_StopBackgroundTrack();
1554 			return;
1555 		}
1556 
1557 		l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer);
1558 	}
1559 
1560 	format = S_AL_Format(curstream->info.width, curstream->info.channels);
1561 
1562 	if( l == 0 )
1563 	{
1564 		// We have no data to buffer, so buffer silence
1565 		byte dummyData[ 2 ] = { 0 };
1566 
1567 		qalBufferData( b, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050 );
1568 	}
1569 	else
1570 		qalBufferData(b, format, decode_buffer, l, curstream->info.rate);
1571 
1572 	if( ( error = qalGetError( ) ) != AL_NO_ERROR )
1573 	{
1574 		S_AL_StopBackgroundTrack( );
1575 		Com_Printf( S_COLOR_RED "ERROR: while buffering data for music stream - %s\n",
1576 				S_AL_ErrorMsg( error ) );
1577 		return;
1578 	}
1579 }
1580 
1581 /*
1582 =================
1583 S_AL_StartBackgroundTrack
1584 =================
1585 */
1586 static
S_AL_StartBackgroundTrack(const char * intro,const char * loop)1587 void S_AL_StartBackgroundTrack( const char *intro, const char *loop )
1588 {
1589 	int i;
1590 	qboolean issame;
1591 
1592 	// Stop any existing music that might be playing
1593 	S_AL_StopBackgroundTrack();
1594 
1595 	if((!intro || !*intro) && (!loop || !*loop))
1596 		return;
1597 
1598 	// Allocate a musicSource
1599 	S_AL_MusicSourceGet();
1600 	if(musicSourceHandle == -1)
1601 		return;
1602 
1603 	if (!loop || !*loop)
1604 	{
1605 		loop = intro;
1606 		issame = qtrue;
1607 	}
1608 	else if(intro && *intro && !strcmp(intro, loop))
1609 		issame = qtrue;
1610 	else
1611 		issame = qfalse;
1612 
1613 	// Copy the loop over
1614 	strncpy( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
1615 
1616 	if(!issame)
1617 	{
1618 		// Open the intro and don't mind whether it succeeds.
1619 		// The important part is the loop.
1620 		intro_stream = S_CodecOpenStream(intro);
1621 	}
1622 	else
1623 		intro_stream = NULL;
1624 
1625 	mus_stream = S_CodecOpenStream(s_backgroundLoop);
1626 	if(!mus_stream)
1627 	{
1628 		S_AL_CloseMusicFiles();
1629 		S_AL_MusicSourceFree();
1630 		return;
1631 	}
1632 
1633 	// Generate the musicBuffers
1634 	qalGenBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
1635 
1636 	// Queue the musicBuffers up
1637 	for(i = 0; i < NUM_MUSIC_BUFFERS; i++)
1638 	{
1639 		S_AL_MusicProcess(musicBuffers[i]);
1640 	}
1641 
1642 	qalSourceQueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
1643 
1644 	// Set the initial gain property
1645 	qalSourcef(musicSource, AL_GAIN, s_alGain->value * s_musicVolume->value);
1646 
1647 	// Start playing
1648 	qalSourcePlay(musicSource);
1649 
1650 	musicPlaying = qtrue;
1651 }
1652 
1653 /*
1654 =================
1655 S_AL_MusicUpdate
1656 =================
1657 */
1658 static
S_AL_MusicUpdate(void)1659 void S_AL_MusicUpdate( void )
1660 {
1661 	int		numBuffers;
1662 	ALint	state;
1663 
1664 	if(!musicPlaying)
1665 		return;
1666 
1667 	qalGetSourcei( musicSource, AL_BUFFERS_PROCESSED, &numBuffers );
1668 	while( numBuffers-- )
1669 	{
1670 		ALuint b;
1671 		qalSourceUnqueueBuffers(musicSource, 1, &b);
1672 		S_AL_MusicProcess(b);
1673 		qalSourceQueueBuffers(musicSource, 1, &b);
1674 	}
1675 
1676 	// Hitches can cause OpenAL to be starved of buffers when streaming.
1677 	// If this happens, it will stop playback. This restarts the source if
1678 	// it is no longer playing, and if there are buffers available
1679 	qalGetSourcei( musicSource, AL_SOURCE_STATE, &state );
1680 	qalGetSourcei( musicSource, AL_BUFFERS_QUEUED, &numBuffers );
1681 	if( state == AL_STOPPED && numBuffers )
1682 	{
1683 		Com_DPrintf( S_COLOR_YELLOW "Restarted OpenAL music\n" );
1684 		qalSourcePlay(musicSource);
1685 	}
1686 
1687 	// Set the gain property
1688 	qalSourcef(musicSource, AL_GAIN, s_alGain->value * s_musicVolume->value);
1689 }
1690 
1691 
1692 //===========================================================================
1693 
1694 
1695 // Local state variables
1696 static ALCdevice *alDevice;
1697 static ALCcontext *alContext;
1698 
1699 #if USE_VOIP
1700 static ALCdevice *alCaptureDevice;
1701 static cvar_t *s_alCapture;
1702 #endif
1703 
1704 #ifdef _WIN32
1705 #define ALDRIVER_DEFAULT "OpenAL32.dll"
1706 #define ALDEVICE_DEFAULT "Generic Software"
1707 #elif defined(MACOS_X)
1708 #define ALDRIVER_DEFAULT "/System/Library/Frameworks/OpenAL.framework/OpenAL"
1709 #else
1710 #define ALDRIVER_DEFAULT "libopenal.so"
1711 #endif
1712 
1713 /*
1714 =================
1715 S_AL_StopAllSounds
1716 =================
1717 */
1718 static
S_AL_StopAllSounds(void)1719 void S_AL_StopAllSounds( void )
1720 {
1721 	int i;
1722 	S_AL_SrcShutup();
1723 	S_AL_StopBackgroundTrack();
1724 	for (i = 0; i < MAX_RAW_STREAMS; i++)
1725 		S_AL_StreamDie(i);
1726 }
1727 
1728 /*
1729 =================
1730 S_AL_Respatialize
1731 =================
1732 */
1733 static
S_AL_Respatialize(int entityNum,const vec3_t origin,vec3_t axis[3],int inwater)1734 void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater )
1735 {
1736 	float		velocity[3] = {0.0f, 0.0f, 0.0f};
1737 	float		orientation[6];
1738 	vec3_t	sorigin;
1739 
1740 	VectorCopy( origin, sorigin );
1741 	S_AL_SanitiseVector( sorigin );
1742 
1743 	S_AL_SanitiseVector( axis[ 0 ] );
1744 	S_AL_SanitiseVector( axis[ 1 ] );
1745 	S_AL_SanitiseVector( axis[ 2 ] );
1746 
1747 	orientation[0] = axis[0][0]; orientation[1] = axis[0][1]; orientation[2] = axis[0][2];
1748 	orientation[3] = axis[2][0]; orientation[4] = axis[2][1]; orientation[5] = axis[2][2];
1749 
1750 	VectorCopy( sorigin, lastListenerOrigin );
1751 
1752 	// Set OpenAL listener paramaters
1753 	qalListenerfv(AL_POSITION, (ALfloat *)sorigin);
1754 	qalListenerfv(AL_VELOCITY, velocity);
1755 	qalListenerfv(AL_ORIENTATION, orientation);
1756 }
1757 
1758 /*
1759 =================
1760 S_AL_Update
1761 =================
1762 */
1763 static
S_AL_Update(void)1764 void S_AL_Update( void )
1765 {
1766 	int i;
1767 
1768 	// Update SFX channels
1769 	S_AL_SrcUpdate();
1770 
1771 	// Update streams
1772 	for (i = 0; i < MAX_RAW_STREAMS; i++)
1773 		S_AL_StreamUpdate(i);
1774 	S_AL_MusicUpdate();
1775 
1776 	// Doppler
1777 	if(s_doppler->modified)
1778 	{
1779 		s_alDopplerFactor->modified = qtrue;
1780 		s_doppler->modified = qfalse;
1781 	}
1782 
1783 	// Doppler parameters
1784 	if(s_alDopplerFactor->modified)
1785 	{
1786 		if(s_doppler->integer)
1787 			qalDopplerFactor(s_alDopplerFactor->value);
1788 		else
1789 			qalDopplerFactor(0.0f);
1790 		s_alDopplerFactor->modified = qfalse;
1791 	}
1792 	if(s_alDopplerSpeed->modified)
1793 	{
1794 		qalDopplerVelocity(s_alDopplerSpeed->value);
1795 		s_alDopplerSpeed->modified = qfalse;
1796 	}
1797 
1798 	// Clear the modified flags on the other cvars
1799 	s_alGain->modified = qfalse;
1800 	s_volume->modified = qfalse;
1801 	s_musicVolume->modified = qfalse;
1802 	s_alMinDistance->modified = qfalse;
1803 	s_alRolloff->modified = qfalse;
1804 }
1805 
1806 /*
1807 =================
1808 S_AL_DisableSounds
1809 =================
1810 */
1811 static
S_AL_DisableSounds(void)1812 void S_AL_DisableSounds( void )
1813 {
1814 	S_AL_StopAllSounds();
1815 }
1816 
1817 /*
1818 =================
1819 S_AL_BeginRegistration
1820 =================
1821 */
1822 static
S_AL_BeginRegistration(void)1823 void S_AL_BeginRegistration( void )
1824 {
1825 }
1826 
1827 /*
1828 =================
1829 S_AL_ClearSoundBuffer
1830 =================
1831 */
1832 static
S_AL_ClearSoundBuffer(void)1833 void S_AL_ClearSoundBuffer( void )
1834 {
1835 }
1836 
1837 /*
1838 =================
1839 S_AL_SoundList
1840 =================
1841 */
1842 static
S_AL_SoundList(void)1843 void S_AL_SoundList( void )
1844 {
1845 }
1846 
1847 #if USE_VOIP
1848 static
S_AL_StartCapture(void)1849 void S_AL_StartCapture( void )
1850 {
1851 	if (alCaptureDevice != NULL)
1852 		qalcCaptureStart(alCaptureDevice);
1853 }
1854 
1855 static
S_AL_AvailableCaptureSamples(void)1856 int S_AL_AvailableCaptureSamples( void )
1857 {
1858 	int retval = 0;
1859 	if (alCaptureDevice != NULL)
1860 	{
1861 		ALint samples = 0;
1862 		qalcGetIntegerv(alCaptureDevice, ALC_CAPTURE_SAMPLES, sizeof (samples), &samples);
1863 		retval = (int) samples;
1864 	}
1865 	return retval;
1866 }
1867 
1868 static
S_AL_Capture(int samples,byte * data)1869 void S_AL_Capture( int samples, byte *data )
1870 {
1871 	if (alCaptureDevice != NULL)
1872 		qalcCaptureSamples(alCaptureDevice, data, samples);
1873 }
1874 
S_AL_StopCapture(void)1875 void S_AL_StopCapture( void )
1876 {
1877 	if (alCaptureDevice != NULL)
1878 		qalcCaptureStop(alCaptureDevice);
1879 }
1880 
S_AL_MasterGain(float gain)1881 void S_AL_MasterGain( float gain )
1882 {
1883 	qalListenerf(AL_GAIN, gain);
1884 }
1885 #endif
1886 
1887 
1888 /*
1889 =================
1890 S_AL_SoundInfo
1891 =================
1892 */
1893 static
S_AL_SoundInfo(void)1894 void S_AL_SoundInfo( void )
1895 {
1896 	Com_Printf( "OpenAL info:\n" );
1897 	Com_Printf( "  Vendor:     %s\n", qalGetString( AL_VENDOR ) );
1898 	Com_Printf( "  Version:    %s\n", qalGetString( AL_VERSION ) );
1899 	Com_Printf( "  Renderer:   %s\n", qalGetString( AL_RENDERER ) );
1900 	Com_Printf( "  AL Extensions: %s\n", qalGetString( AL_EXTENSIONS ) );
1901 	Com_Printf( "  ALC Extensions: %s\n", qalcGetString( NULL, ALC_EXTENSIONS ) );
1902 	if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
1903 	{
1904 		Com_Printf("  Device:     %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER));
1905 		Com_Printf("Available Devices:\n%s", s_alAvailableDevices->string);
1906 	}
1907 }
1908 
1909 /*
1910 =================
1911 S_AL_Shutdown
1912 =================
1913 */
1914 static
S_AL_Shutdown(void)1915 void S_AL_Shutdown( void )
1916 {
1917 	// Shut down everything
1918 	int i;
1919 	for (i = 0; i < MAX_RAW_STREAMS; i++)
1920 		S_AL_StreamDie(i);
1921 	S_AL_StopBackgroundTrack( );
1922 	S_AL_SrcShutdown( );
1923 	S_AL_BufferShutdown( );
1924 
1925 	qalcDestroyContext(alContext);
1926 	qalcCloseDevice(alDevice);
1927 
1928 #if USE_VOIP
1929 	if (alCaptureDevice != NULL) {
1930 		qalcCaptureStop(alCaptureDevice);
1931 		qalcCaptureCloseDevice(alCaptureDevice);
1932 		alCaptureDevice = NULL;
1933 		Com_Printf( "OpenAL capture device closed.\n" );
1934 	}
1935 #endif
1936 
1937 	for (i = 0; i < MAX_RAW_STREAMS; i++) {
1938 		streamSourceHandles[i] = -1;
1939 		streamPlaying[i] = qfalse;
1940 		streamSources[i] = 0;
1941 	}
1942 
1943 	QAL_Shutdown();
1944 }
1945 
1946 #endif
1947 
1948 /*
1949 =================
1950 S_AL_Init
1951 =================
1952 */
S_AL_Init(soundInterface_t * si)1953 qboolean S_AL_Init( soundInterface_t *si )
1954 {
1955 #ifdef USE_OPENAL
1956 
1957 	qboolean enumsupport, founddev = qfalse;
1958 	int i;
1959 
1960 	if( !si ) {
1961 		return qfalse;
1962 	}
1963 
1964 	for (i = 0; i < MAX_RAW_STREAMS; i++) {
1965 		streamSourceHandles[i] = -1;
1966 		streamPlaying[i] = qfalse;
1967 		streamSources[i] = 0;
1968 	}
1969 
1970 	// New console variables
1971 	s_alPrecache = Cvar_Get( "s_alPrecache", "1", CVAR_ARCHIVE );
1972 	s_alGain = Cvar_Get( "s_alGain", "0.4", CVAR_ARCHIVE );
1973 	s_alSources = Cvar_Get( "s_alSources", "96", CVAR_ARCHIVE );
1974 	s_alDopplerFactor = Cvar_Get( "s_alDopplerFactor", "1.0", CVAR_ARCHIVE );
1975 	s_alDopplerSpeed = Cvar_Get( "s_alDopplerSpeed", "2200", CVAR_ARCHIVE );
1976 	s_alMinDistance = Cvar_Get( "s_alMinDistance", "120", CVAR_CHEAT );
1977 	s_alMaxDistance = Cvar_Get("s_alMaxDistance", "1024", CVAR_CHEAT);
1978 	s_alRolloff = Cvar_Get( "s_alRolloff", "2", CVAR_CHEAT);
1979 	s_alGraceDistance = Cvar_Get("s_alGraceDistance", "512", CVAR_CHEAT);
1980 
1981 	s_alDriver = Cvar_Get( "s_alDriver", ALDRIVER_DEFAULT, CVAR_ARCHIVE );
1982 
1983 	// Load QAL
1984 	if( !QAL_Init( s_alDriver->string ) )
1985 	{
1986 		Com_Printf( "Failed to load library: \"%s\".\n", s_alDriver->string );
1987 		return qfalse;
1988 	}
1989 
1990 	// Device enumeration support (extension is implemented reasonably only on Windows right now).
1991 	if((enumsupport = qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")))
1992 	{
1993 		char devicenames[1024] = "";
1994 		const char *devicelist;
1995 		const char *defaultdevice;
1996 		int curlen;
1997 
1998 		// get all available devices + the default device name.
1999 		devicelist = qalcGetString(NULL, ALC_DEVICE_SPECIFIER);
2000 		defaultdevice = qalcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
2001 
2002 #ifdef _WIN32
2003 		// check whether the default device is generic hardware. If it is, change to
2004 		// Generic Software as that one works more reliably with various sound systems.
2005 		// If it's not, use OpenAL's default selection as we don't want to ignore
2006 		// native hardware acceleration.
2007 		if(!strcmp(defaultdevice, "Generic Hardware"))
2008 			s_alDevice = Cvar_Get("s_alDevice", ALDEVICE_DEFAULT, CVAR_ARCHIVE | CVAR_LATCH);
2009 		else
2010 #endif
2011 			s_alDevice = Cvar_Get("s_alDevice", defaultdevice, CVAR_ARCHIVE | CVAR_LATCH);
2012 
2013 		// dump a list of available devices to a cvar for the user to see.
2014 		while((curlen = strlen(devicelist)))
2015 		{
2016 			Q_strcat(devicenames, sizeof(devicenames), devicelist);
2017 			Q_strcat(devicenames, sizeof(devicenames), "\n");
2018 
2019 			// check whether the device we want to load is available at all.
2020 			if(!strcmp(s_alDevice->string, devicelist))
2021 				founddev = qtrue;
2022 
2023 			devicelist += curlen + 1;
2024 		}
2025 
2026 		s_alAvailableDevices = Cvar_Get("s_alAvailableDevices", devicenames, CVAR_ROM | CVAR_NORESTART);
2027 
2028 		if(!founddev)
2029 		{
2030 			Cvar_ForceReset("s_alDevice");
2031 			founddev = 1;
2032 		}
2033 	}
2034 
2035 	if(founddev)
2036 		alDevice = qalcOpenDevice(s_alDevice->string);
2037 	else
2038 		alDevice = qalcOpenDevice(NULL);
2039 
2040 	if( !alDevice )
2041 	{
2042 		QAL_Shutdown( );
2043 		Com_Printf( "Failed to open OpenAL device.\n" );
2044 		return qfalse;
2045 	}
2046 
2047 	if(enumsupport)
2048 		Cvar_Set("s_alDevice", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER));
2049 
2050 	// Create OpenAL context
2051 	alContext = qalcCreateContext( alDevice, NULL );
2052 	if( !alContext )
2053 	{
2054 		QAL_Shutdown( );
2055 		qalcCloseDevice( alDevice );
2056 		Com_Printf( "Failed to create OpenAL context.\n" );
2057 		return qfalse;
2058 	}
2059 	qalcMakeContextCurrent( alContext );
2060 
2061 	// Initialize sources, buffers, music
2062 	S_AL_BufferInit( );
2063 	S_AL_SrcInit( );
2064 
2065 	// Set up OpenAL parameters (doppler, etc)
2066 	qalDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
2067 	qalDopplerFactor( s_alDopplerFactor->value );
2068 	qalDopplerVelocity( s_alDopplerSpeed->value );
2069 
2070 #if USE_VOIP
2071 	// !!! FIXME: some of these alcCaptureOpenDevice() values should be cvars.
2072 	// !!! FIXME: add support for capture device enumeration.
2073 	// !!! FIXME: add some better error reporting.
2074 	s_alCapture = Cvar_Get( "s_alCapture", "1", CVAR_ARCHIVE | CVAR_LATCH );
2075 	if (!s_alCapture->integer) {
2076 		Com_Printf("OpenAL capture support disabled by user ('+set s_alCapture 1' to enable)\n");
2077 #if USE_MUMBLE
2078 	} else if (cl_useMumble->integer) {
2079 		Com_Printf("OpenAL capture support disabled for Mumble support\n");
2080 #endif
2081 	} else {
2082 		// !!! FIXME: Apple has a 1.1-compliant OpenAL, which includes
2083 		// !!! FIXME:  capture support, but they don't list it in the
2084 		// !!! FIXME:  extension string. We need to check the version string,
2085 		// !!! FIXME:  then the extension string, but that's too much trouble,
2086 		// !!! FIXME:  so we'll just check the function pointer for now.
2087 		//if (qalcIsExtensionPresent(NULL, "ALC_EXT_capture")) {
2088 		if (qalcCaptureOpenDevice == NULL) {
2089 			Com_Printf("No ALC_EXT_capture support, can't record audio.\n");
2090 		} else {
2091 			Com_Printf("OpenAL default capture device is '%s'\n",
2092 			           qalcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
2093 			alCaptureDevice = qalcCaptureOpenDevice(NULL, 8000, AL_FORMAT_MONO16, 4096);
2094 			Com_Printf( "OpenAL capture device %s.\n",
2095 			            (alCaptureDevice == NULL) ? "failed to open" : "opened");
2096 		}
2097 	}
2098 #endif
2099 
2100 	si->Shutdown = S_AL_Shutdown;
2101 	si->StartSound = S_AL_StartSound;
2102 	si->StartLocalSound = S_AL_StartLocalSound;
2103 	si->StartBackgroundTrack = S_AL_StartBackgroundTrack;
2104 	si->StopBackgroundTrack = S_AL_StopBackgroundTrack;
2105 	si->RawSamples = S_AL_RawSamples;
2106 	si->StopAllSounds = S_AL_StopAllSounds;
2107 	si->ClearLoopingSounds = S_AL_ClearLoopingSounds;
2108 	si->AddLoopingSound = S_AL_AddLoopingSound;
2109 	si->AddRealLoopingSound = S_AL_AddRealLoopingSound;
2110 	si->StopLoopingSound = S_AL_StopLoopingSound;
2111 	si->Respatialize = S_AL_Respatialize;
2112 	si->UpdateEntityPosition = S_AL_UpdateEntityPosition;
2113 	si->Update = S_AL_Update;
2114 	si->DisableSounds = S_AL_DisableSounds;
2115 	si->BeginRegistration = S_AL_BeginRegistration;
2116 	si->RegisterSound = S_AL_RegisterSound;
2117 	si->ClearSoundBuffer = S_AL_ClearSoundBuffer;
2118 	si->SoundInfo = S_AL_SoundInfo;
2119 	si->SoundList = S_AL_SoundList;
2120 
2121 #if USE_VOIP
2122 	si->StartCapture = S_AL_StartCapture;
2123 	si->AvailableCaptureSamples = S_AL_AvailableCaptureSamples;
2124 	si->Capture = S_AL_Capture;
2125 	si->StopCapture = S_AL_StopCapture;
2126 	si->MasterGain = S_AL_MasterGain;
2127 #endif
2128 
2129 	return qtrue;
2130 #else
2131 	return qfalse;
2132 #endif
2133 }
2134 
2135