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_alInputDevice;
45 cvar_t *s_alAvailableDevices;
46 cvar_t *s_alAvailableInputDevices;
47 
48 static qboolean enumeration_ext = qfalse;
49 static qboolean enumeration_all_ext = qfalse;
50 #ifdef USE_VOIP
51 static qboolean capture_ext = qfalse;
52 #endif
53 
54 /*
55 =================
56 S_AL_Format
57 =================
58 */
59 static
S_AL_Format(int width,int channels)60 ALuint S_AL_Format(int width, int channels)
61 {
62 	ALuint format = AL_FORMAT_MONO16;
63 
64 	// Work out format
65 	if(width == 1)
66 	{
67 		if(channels == 1)
68 			format = AL_FORMAT_MONO8;
69 		else if(channels == 2)
70 			format = AL_FORMAT_STEREO8;
71 	}
72 	else if(width == 2)
73 	{
74 		if(channels == 1)
75 			format = AL_FORMAT_MONO16;
76 		else if(channels == 2)
77 			format = AL_FORMAT_STEREO16;
78 	}
79 
80 	return format;
81 }
82 
83 /*
84 =================
85 S_AL_ErrorMsg
86 =================
87 */
S_AL_ErrorMsg(ALenum error)88 static const char *S_AL_ErrorMsg(ALenum error)
89 {
90 	switch(error)
91 	{
92 		case AL_NO_ERROR:
93 			return "No error";
94 		case AL_INVALID_NAME:
95 			return "Invalid name";
96 		case AL_INVALID_ENUM:
97 			return "Invalid enumerator";
98 		case AL_INVALID_VALUE:
99 			return "Invalid value";
100 		case AL_INVALID_OPERATION:
101 			return "Invalid operation";
102 		case AL_OUT_OF_MEMORY:
103 			return "Out of memory";
104 		default:
105 			return "Unknown error";
106 	}
107 }
108 
109 /*
110 =================
111 S_AL_ClearError
112 =================
113 */
S_AL_ClearError(qboolean quiet)114 static void S_AL_ClearError( qboolean quiet )
115 {
116 	int error = qalGetError();
117 
118 	if( quiet )
119 		return;
120 	if(error != AL_NO_ERROR)
121 	{
122 		Com_DPrintf(S_COLOR_YELLOW "WARNING: unhandled AL error: %s\n",
123 			S_AL_ErrorMsg(error));
124 	}
125 }
126 
127 
128 //===========================================================================
129 
130 
131 typedef struct alSfx_s
132 {
133 	char			filename[MAX_QPATH];
134 	ALuint		buffer;					// OpenAL buffer
135 	snd_info_t	info;					// information for this sound like rate, sample count..
136 
137 	qboolean	isDefault;				// Couldn't be loaded - use default FX
138 	qboolean	isDefaultChecked;		// Sound has been check if it isDefault
139 	qboolean	inMemory;				// Sound is stored in memory
140 	qboolean	isLocked;				// Sound is locked (can not be unloaded)
141 	int				lastUsedTime;		// Time last used
142 
143 	int				loopCnt;		// number of loops using this sfx
144 	int				loopActiveCnt;		// number of playing loops using this sfx
145 	int				masterLoopSrc;		// All other sources looping this buffer are synced to this master src
146 } alSfx_t;
147 
148 static qboolean alBuffersInitialised = qfalse;
149 
150 // Sound effect storage, data structures
151 #define MAX_SFX 4096
152 static alSfx_t knownSfx[MAX_SFX];
153 static sfxHandle_t numSfx = 0;
154 
155 static sfxHandle_t default_sfx;
156 
157 /*
158 =================
159 S_AL_BufferFindFree
160 
161 Find a free handle
162 =================
163 */
S_AL_BufferFindFree(void)164 static sfxHandle_t S_AL_BufferFindFree( void )
165 {
166 	int i;
167 
168 	for(i = 0; i < MAX_SFX; i++)
169 	{
170 		// Got one
171 		if(knownSfx[i].filename[0] == '\0')
172 		{
173 			if(i >= numSfx)
174 				numSfx = i + 1;
175 			return i;
176 		}
177 	}
178 
179 	// Shit...
180 	Com_Error(ERR_FATAL, "S_AL_BufferFindFree: No free sound handles");
181 	return -1;
182 }
183 
184 /*
185 =================
186 S_AL_BufferFind
187 
188 Find a sound effect if loaded, set up a handle otherwise
189 =================
190 */
S_AL_BufferFind(const char * filename)191 static sfxHandle_t S_AL_BufferFind(const char *filename)
192 {
193 	// Look it up in the table
194 	sfxHandle_t sfx = -1;
195 	int i;
196 
197 	if ( !filename ) {
198 		//Com_Error( ERR_FATAL, "Sound name is NULL" );
199 		filename = "*default*";
200 	}
201 
202 	if ( !filename[0] ) {
203 		//Com_Printf( S_COLOR_YELLOW "WARNING: Sound name is empty\n" );
204 		filename = "*default*";
205 	}
206 
207 	if ( strlen( filename ) >= MAX_QPATH ) {
208 		Com_Printf( S_COLOR_YELLOW "WARNING: Sound name is too long: %s\n", filename );
209 		return 0;
210 	}
211 
212 	for(i = 0; i < numSfx; i++)
213 	{
214 		if(!Q_stricmp(knownSfx[i].filename, filename))
215 		{
216 			sfx = i;
217 			break;
218 		}
219 	}
220 
221 	// Not found in table?
222 	if(sfx == -1)
223 	{
224 		alSfx_t *ptr;
225 
226 		sfx = S_AL_BufferFindFree();
227 
228 		// Clear and copy the filename over
229 		ptr = &knownSfx[sfx];
230 		memset(ptr, 0, sizeof(*ptr));
231 		ptr->masterLoopSrc = -1;
232 		strcpy(ptr->filename, filename);
233 	}
234 
235 	// Return the handle
236 	return sfx;
237 }
238 
239 /*
240 =================
241 S_AL_BufferUseDefault
242 =================
243 */
S_AL_BufferUseDefault(sfxHandle_t sfx)244 static void S_AL_BufferUseDefault(sfxHandle_t sfx)
245 {
246 	if(sfx == default_sfx)
247 		Com_Error(ERR_FATAL, "Can't load default sound effect %s", knownSfx[sfx].filename);
248 
249 //	Com_Printf( S_COLOR_YELLOW "WARNING: Using default sound for %s\n", knownSfx[sfx].filename);
250 	knownSfx[sfx].isDefault = qtrue;
251 	knownSfx[sfx].buffer = knownSfx[default_sfx].buffer;
252 }
253 
254 /*
255 =================
256 S_AL_BufferUnload
257 =================
258 */
S_AL_BufferUnload(sfxHandle_t sfx)259 static void S_AL_BufferUnload(sfxHandle_t sfx)
260 {
261 	if(knownSfx[sfx].filename[0] == '\0')
262 		return;
263 
264 	if(!knownSfx[sfx].inMemory)
265 		return;
266 
267 	// Delete it
268 	S_AL_ClearError( qfalse );
269 	qalDeleteBuffers(1, &knownSfx[sfx].buffer);
270 	if(qalGetError() != AL_NO_ERROR)
271 		Com_Printf( S_COLOR_RED "ERROR: Can't delete sound buffer for %s\n",
272 				knownSfx[sfx].filename);
273 
274 	knownSfx[sfx].inMemory = qfalse;
275 }
276 
277 /*
278 =================
279 S_AL_BufferEvict
280 =================
281 */
S_AL_BufferEvict(void)282 static qboolean S_AL_BufferEvict( void )
283 {
284 	int	i, oldestBuffer = -1;
285 	int	oldestTime = Sys_Milliseconds( );
286 
287 	for( i = 0; i < numSfx; i++ )
288 	{
289 		if( !knownSfx[ i ].filename[ 0 ] )
290 			continue;
291 
292 		if( !knownSfx[ i ].inMemory )
293 			continue;
294 
295 		if( knownSfx[ i ].lastUsedTime < oldestTime )
296 		{
297 			oldestTime = knownSfx[ i ].lastUsedTime;
298 			oldestBuffer = i;
299 		}
300 	}
301 
302 	if( oldestBuffer >= 0 )
303 	{
304 		S_AL_BufferUnload( oldestBuffer );
305 		return qtrue;
306 	}
307 	else
308 		return qfalse;
309 }
310 
311 /*
312 =================
313 S_AL_GenBuffers
314 =================
315 */
S_AL_GenBuffers(ALsizei numBuffers,ALuint * buffers,const char * name)316 static qboolean S_AL_GenBuffers(ALsizei numBuffers, ALuint *buffers, const char *name)
317 {
318 	ALenum error;
319 
320 	S_AL_ClearError( qfalse );
321 	qalGenBuffers( numBuffers, buffers );
322 	error = qalGetError();
323 
324 	// If we ran out of buffers, start evicting the least recently used sounds
325 	while( error == AL_INVALID_VALUE )
326 	{
327 		if( !S_AL_BufferEvict( ) )
328 		{
329 			Com_Printf( S_COLOR_RED "ERROR: Out of audio buffers\n");
330 			return qfalse;
331 		}
332 
333 		// Try again
334 		S_AL_ClearError( qfalse );
335 		qalGenBuffers( numBuffers, buffers );
336 		error = qalGetError();
337 	}
338 
339 	if( error != AL_NO_ERROR )
340 	{
341 		Com_Printf( S_COLOR_RED "ERROR: Can't create a sound buffer for %s - %s\n",
342 				    name, S_AL_ErrorMsg(error));
343 		return qfalse;
344 	}
345 
346 	return qtrue;
347 }
348 
349 /*
350 =================
351 S_AL_BufferLoad
352 =================
353 */
S_AL_BufferLoad(sfxHandle_t sfx,qboolean cache)354 static void S_AL_BufferLoad(sfxHandle_t sfx, qboolean cache)
355 {
356 	ALenum error;
357 	ALuint format;
358 
359 	void *data;
360 	snd_info_t info;
361 	alSfx_t *curSfx = &knownSfx[sfx];
362 
363 	// Nothing?
364 	if(curSfx->filename[0] == '\0')
365 		return;
366 
367 	// Player SFX
368 	if(curSfx->filename[0] == '*')
369 		return;
370 
371 	// Already done?
372 	if((curSfx->inMemory) || (curSfx->isDefault) || (!cache && curSfx->isDefaultChecked))
373 		return;
374 
375 	// Try to load
376 	data = S_CodecLoad(curSfx->filename, &info);
377 	if(!data)
378 	{
379 		S_AL_BufferUseDefault(sfx);
380 		return;
381 	}
382 
383 	curSfx->isDefaultChecked = qtrue;
384 
385 	if (!cache)
386 	{
387 		// Don't create AL cache
388 		Hunk_FreeTempMemory(data);
389 		return;
390 	}
391 
392 	format = S_AL_Format(info.width, info.channels);
393 
394 	// Create a buffer
395 	if (!S_AL_GenBuffers(1, &curSfx->buffer, curSfx->filename))
396 	{
397 		S_AL_BufferUseDefault(sfx);
398 		Hunk_FreeTempMemory(data);
399 		return;
400 	}
401 
402 	// Fill the buffer
403 	if( info.size == 0 )
404 	{
405 		// We have no data to buffer, so buffer silence
406 		byte dummyData[ 2 ] = { 0 };
407 
408 		qalBufferData(curSfx->buffer, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050);
409 	}
410 	else
411 		qalBufferData(curSfx->buffer, format, data, info.size, info.rate);
412 
413 	error = qalGetError();
414 
415 	// If we ran out of memory, start evicting the least recently used sounds
416 	while(error == AL_OUT_OF_MEMORY)
417 	{
418 		if( !S_AL_BufferEvict( ) )
419 		{
420 			qalDeleteBuffers(1, &curSfx->buffer);
421 			S_AL_BufferUseDefault(sfx);
422 			Hunk_FreeTempMemory(data);
423 			Com_Printf( S_COLOR_RED "ERROR: Out of memory loading %s\n", curSfx->filename);
424 			return;
425 		}
426 
427 		// Try load it again
428 		qalBufferData(curSfx->buffer, format, data, info.size, info.rate);
429 		error = qalGetError();
430 	}
431 
432 	// Some other error condition
433 	if(error != AL_NO_ERROR)
434 	{
435 		qalDeleteBuffers(1, &curSfx->buffer);
436 		S_AL_BufferUseDefault(sfx);
437 		Hunk_FreeTempMemory(data);
438 		Com_Printf( S_COLOR_RED "ERROR: Can't fill sound buffer for %s - %s\n",
439 				curSfx->filename, S_AL_ErrorMsg(error));
440 		return;
441 	}
442 
443 	curSfx->info = info;
444 
445 	// Free the memory
446 	Hunk_FreeTempMemory(data);
447 
448 	// Woo!
449 	curSfx->inMemory = qtrue;
450 }
451 
452 /*
453 =================
454 S_AL_BufferUse
455 =================
456 */
457 static
S_AL_BufferUse(sfxHandle_t sfx)458 void S_AL_BufferUse(sfxHandle_t sfx)
459 {
460 	if(knownSfx[sfx].filename[0] == '\0')
461 		return;
462 
463 	if((!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
464 		S_AL_BufferLoad(sfx, qtrue);
465 	knownSfx[sfx].lastUsedTime = Sys_Milliseconds();
466 }
467 
468 /*
469 =================
470 S_AL_BufferInit
471 =================
472 */
473 static
S_AL_BufferInit(void)474 qboolean S_AL_BufferInit( void )
475 {
476 	if(alBuffersInitialised)
477 		return qtrue;
478 
479 	// Clear the hash table, and SFX table
480 	memset(knownSfx, 0, sizeof(knownSfx));
481 	numSfx = 0;
482 
483 	// Load the default sound, and lock it
484 	default_sfx = S_AL_BufferFind( "***DEFAULT***" );
485 	S_AL_BufferUse(default_sfx);
486 	knownSfx[default_sfx].isLocked = qtrue;
487 
488 	// All done
489 	alBuffersInitialised = qtrue;
490 	return qtrue;
491 }
492 
493 /*
494 =================
495 S_AL_BufferShutdown
496 =================
497 */
498 static
S_AL_BufferShutdown(void)499 void S_AL_BufferShutdown( void )
500 {
501 	int i;
502 
503 	if(!alBuffersInitialised)
504 		return;
505 
506 	// Unlock the default sound effect
507 	knownSfx[default_sfx].isLocked = qfalse;
508 
509 	// Free all used effects
510 	for(i = 0; i < numSfx; i++)
511 		S_AL_BufferUnload(i);
512 
513 	// Clear the tables
514 	numSfx = 0;
515 
516 	// All undone
517 	alBuffersInitialised = qfalse;
518 }
519 
520 /*
521 =================
522 S_AL_RegisterSound
523 =================
524 */
525 static
S_AL_RegisterSound(const char * sample,qboolean compressed)526 sfxHandle_t S_AL_RegisterSound( const char *sample, qboolean compressed )
527 {
528 	sfxHandle_t sfx = S_AL_BufferFind(sample);
529 
530 	if((!knownSfx[sfx].inMemory) && (!knownSfx[sfx].isDefault))
531 		S_AL_BufferLoad(sfx, s_alPrecache->integer);
532 	knownSfx[sfx].lastUsedTime = Com_Milliseconds();
533 
534 	if (knownSfx[sfx].isDefault) {
535 		return 0;
536 	}
537 
538 	return sfx;
539 }
540 
541 /*
542 =================
543 S_AL_BufferGet
544 
545 Return's a sfx's buffer
546 =================
547 */
548 static
S_AL_BufferGet(sfxHandle_t sfx)549 ALuint S_AL_BufferGet(sfxHandle_t sfx)
550 {
551 	return knownSfx[sfx].buffer;
552 }
553 
554 
555 //===========================================================================
556 
557 
558 typedef struct src_s
559 {
560 	ALuint		alSource;		// OpenAL source object
561 	sfxHandle_t	sfx;			// Sound effect in use
562 
563 	int		lastUsedTime;		// Last time used
564 	alSrcPriority_t	priority;		// Priority
565 	int		entity;			// Owning entity (-1 if none)
566 	int		channel;		// Associated channel (-1 if none)
567 
568 	qboolean	isActive;		// Is this source currently in use?
569 	qboolean	isPlaying;		// Is this source currently playing, or stopped?
570 	qboolean	isLocked;		// This is locked (un-allocatable)
571 	qboolean	isLooping;		// Is this a looping effect (attached to an entity)
572 	qboolean	isTracking;		// Is this object tracking its owner
573 	qboolean	isStream;		// Is this source a stream
574 
575 	float		curGain;		// gain employed if source is within maxdistance.
576 	float		scaleGain;		// Last gain value for this source. 0 if muted.
577 
578 	float		lastTimePos;		// On stopped loops, the last position in the buffer
579 	int		lastSampleTime;		// Time when this was stopped
580 	vec3_t		loopSpeakerPos;		// Origin of the loop speaker
581 
582 	qboolean	local;			// Is this local (relative to the cam)
583 
584 	int			flags;			// flags from StartSoundEx
585 } src_t;
586 
587 #ifdef __APPLE__
588 	#define MAX_SRC 128
589 #else
590 	#define MAX_SRC 256
591 #endif
592 static src_t srcList[MAX_SRC];
593 static int srcCount = 0;
594 static int srcActiveCnt = 0;
595 static qboolean alSourcesInitialised = qfalse;
596 static int lastListenerNumber = -1;
597 static vec3_t lastListenerOrigin = { 0.0f, 0.0f, 0.0f };
598 
599 typedef struct sentity_s
600 {
601 	vec3_t					origin;
602 
603 	qboolean						srcAllocated; // If a src_t has been allocated to this entity
604 	int							srcIndex;
605 
606 	qboolean				loopAddedThisFrame;
607 	alSrcPriority_t	loopPriority;
608 	sfxHandle_t			loopSfx;
609 	qboolean				startLoopingSound;
610 } sentity_t;
611 
612 static sentity_t entityList[MAX_GENTITIES];
613 
614 /*
615 =================
616 S_AL_SanitiseVector
617 =================
618 */
619 #define S_AL_SanitiseVector(v) _S_AL_SanitiseVector(v,__LINE__)
_S_AL_SanitiseVector(vec3_t v,int line)620 static void _S_AL_SanitiseVector( vec3_t v, int line )
621 {
622 	if( Q_isnan( v[ 0 ] ) || Q_isnan( v[ 1 ] ) || Q_isnan( v[ 2 ] ) )
623 	{
624 		Com_DPrintf( S_COLOR_YELLOW "WARNING: vector with one or more NaN components "
625 				"being passed to OpenAL at %s:%d -- zeroing\n", __FILE__, line );
626 		VectorClear( v );
627 	}
628 }
629 
630 /*
631 =================
632 S_AL_Gain
633 Set gain to 0 if muted, otherwise set it to given value.
634 =================
635 */
636 
S_AL_Gain(ALuint source,float gainval)637 static void S_AL_Gain(ALuint source, float gainval)
638 {
639 	if(s_muted->integer)
640 		qalSourcef(source, AL_GAIN, 0.0f);
641 	else
642 		qalSourcef(source, AL_GAIN, gainval);
643 }
644 
645 /*
646 =================
647 S_AL_ScaleGain
648 Adapt the gain if necessary to get a quicker fadeout when the source is too far away.
649 =================
650 */
651 
S_AL_ScaleGain(src_t * chksrc,vec3_t origin)652 static void S_AL_ScaleGain(src_t *chksrc, vec3_t origin)
653 {
654 	float distance;
655 
656 	if(!chksrc->local)
657 		distance = Distance(origin, lastListenerOrigin);
658 
659 	// If we exceed a certain distance, scale the gain linearly until the sound
660 	// vanishes into nothingness.
661 	if(!chksrc->local && (distance -= s_alMaxDistance->value) > 0)
662 	{
663 		float scaleFactor;
664 
665 		if(distance >= s_alGraceDistance->value)
666 			scaleFactor = 0.0f;
667 		else
668 			scaleFactor = 1.0f - distance / s_alGraceDistance->value;
669 
670 		scaleFactor *= chksrc->curGain;
671 
672 		if(chksrc->scaleGain != scaleFactor)
673 		{
674 			chksrc->scaleGain = scaleFactor;
675 			S_AL_Gain(chksrc->alSource, chksrc->scaleGain);
676 		}
677 	}
678 	else if(chksrc->scaleGain != chksrc->curGain)
679 	{
680 		chksrc->scaleGain = chksrc->curGain;
681 		S_AL_Gain(chksrc->alSource, chksrc->scaleGain);
682 	}
683 }
684 
685 /*
686 =================
687 S_AL_HearingThroughEntity
688 
689 Also see S_Base_HearingThroughEntity
690 =================
691 */
S_AL_HearingThroughEntity(int entityNum)692 static qboolean S_AL_HearingThroughEntity( int entityNum )
693 {
694 	float	distanceSq;
695 
696 	if( lastListenerNumber == entityNum )
697 	{
698 		// This is an outrageous hack to detect
699 		// whether or not the player is rendering in third person or not. We can't
700 		// ask the renderer because the renderer has no notion of entities and we
701 		// can't ask cgame since that would involve changing the API and hence mod
702 		// compatibility. I don't think there is any way around this, but I'll leave
703 		// the FIXME just in case anyone has a bright idea.
704 		distanceSq = DistanceSquared(
705 				entityList[ entityNum ].origin,
706 				lastListenerOrigin );
707 
708 		if( distanceSq > THIRD_PERSON_THRESHOLD_SQ )
709 			return qfalse; //we're the player, but third person
710 		else
711 			return qtrue;  //we're the player
712 	}
713 	else
714 		return qfalse; //not the player
715 }
716 
717 /*
718 =================
719 S_AL_SrcInit
720 =================
721 */
722 static
S_AL_SrcInit(void)723 qboolean S_AL_SrcInit( void )
724 {
725 	int i;
726 	int limit;
727 
728 	// Clear the sources data structure
729 	memset(srcList, 0, sizeof(srcList));
730 	srcCount = 0;
731 	srcActiveCnt = 0;
732 
733 	// Cap s_alSources to MAX_SRC
734 	limit = s_alSources->integer;
735 	if(limit > MAX_SRC)
736 		limit = MAX_SRC;
737 	else if(limit < 16)
738 		limit = 16;
739 
740 	S_AL_ClearError( qfalse );
741 	// Allocate as many sources as possible
742 	for(i = 0; i < limit; i++)
743 	{
744 		qalGenSources(1, &srcList[i].alSource);
745 		if(qalGetError() != AL_NO_ERROR)
746 			break;
747 		srcCount++;
748 	}
749 
750 	alSourcesInitialised = qtrue;
751 
752 	return qtrue;
753 }
754 
755 /*
756 =================
757 S_AL_SrcShutdown
758 =================
759 */
760 static
S_AL_SrcShutdown(void)761 void S_AL_SrcShutdown( void )
762 {
763 	int i;
764 	src_t *curSource;
765 
766 	if(!alSourcesInitialised)
767 		return;
768 
769 	// Destroy all the sources
770 	for(i = 0; i < srcCount; i++)
771 	{
772 		curSource = &srcList[i];
773 
774 		if(curSource->isLocked)
775 		{
776 			srcList[i].isLocked = qfalse;
777 			Com_DPrintf( S_COLOR_YELLOW "WARNING: Source %d was locked\n", i);
778 		}
779 
780 		if(curSource->entity > 0)
781 			entityList[curSource->entity].srcAllocated = qfalse;
782 
783 		qalSourceStop(srcList[i].alSource);
784 		qalDeleteSources(1, &srcList[i].alSource);
785 	}
786 
787 	memset(srcList, 0, sizeof(srcList));
788 
789 	alSourcesInitialised = qfalse;
790 }
791 
792 /*
793 =================
794 S_AL_SrcSetup
795 =================
796 */
S_AL_SrcSetup(srcHandle_t src,sfxHandle_t sfx,alSrcPriority_t priority,int entity,int channel,int flags,qboolean local)797 static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t priority,
798 		int entity, int channel, int flags, qboolean local)
799 {
800 	src_t *curSource;
801 
802 	// Set up src struct
803 	curSource = &srcList[src];
804 
805 	curSource->lastUsedTime = Sys_Milliseconds();
806 	curSource->sfx = sfx;
807 	curSource->priority = priority;
808 	curSource->entity = entity;
809 	curSource->channel = channel;
810 	curSource->isPlaying = qfalse;
811 	curSource->isLocked = qfalse;
812 	curSource->isLooping = qfalse;
813 	curSource->isTracking = qfalse;
814 	curSource->isStream = qfalse;
815 	curSource->curGain = s_alGain->value * s_volume->value;
816 	curSource->scaleGain = curSource->curGain;
817 	curSource->local = local;
818 	curSource->flags = flags;
819 
820 	// Set up OpenAL source
821 	if(sfx >= 0)
822 	{
823         	// Mark the SFX as used, and grab the raw AL buffer
824         	S_AL_BufferUse(sfx);
825         	qalSourcei(curSource->alSource, AL_BUFFER, S_AL_BufferGet(sfx));
826 	}
827 
828 	qalSourcef(curSource->alSource, AL_PITCH, 1.0f);
829 	S_AL_Gain(curSource->alSource, curSource->curGain);
830 	qalSourcefv(curSource->alSource, AL_POSITION, vec3_origin);
831 	qalSourcefv(curSource->alSource, AL_VELOCITY, vec3_origin);
832 	qalSourcei(curSource->alSource, AL_LOOPING, AL_FALSE);
833 	qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value);
834 
835 	if(local)
836 	{
837 		qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_TRUE);
838 		qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, 0.0f);
839 	}
840 	else
841 	{
842 		qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE);
843 		qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value);
844 	}
845 }
846 
847 /*
848 =================
849 S_AL_SaveLoopPos
850 Remove given source as loop master if it is the master and hand off master status to another source in this case.
851 =================
852 */
853 
S_AL_SaveLoopPos(src_t * dest,ALuint alSource)854 static void S_AL_SaveLoopPos(src_t *dest, ALuint alSource)
855 {
856 	int error;
857 
858 	S_AL_ClearError( qfalse );
859 
860 	qalGetSourcef(alSource, AL_SEC_OFFSET, &dest->lastTimePos);
861 	if((error = qalGetError()) != AL_NO_ERROR)
862 	{
863 		// Old OpenAL implementations don't support AL_SEC_OFFSET
864 
865 		if(error != AL_INVALID_ENUM)
866 		{
867 			Com_Printf(S_COLOR_YELLOW "WARNING: Could not get time offset for alSource %d: %s\n",
868 				   alSource, S_AL_ErrorMsg(error));
869 		}
870 
871 		dest->lastTimePos = -1;
872 	}
873 	else
874 		dest->lastSampleTime = Sys_Milliseconds();
875 }
876 
877 /*
878 =================
879 S_AL_NewLoopMaster
880 Remove given source as loop master if it is the master and hand off master status to another source in this case.
881 =================
882 */
883 
S_AL_NewLoopMaster(src_t * rmSource,qboolean iskilled)884 static void S_AL_NewLoopMaster(src_t *rmSource, qboolean iskilled)
885 {
886 	int index;
887 	src_t *curSource = NULL;
888 	alSfx_t *curSfx;
889 
890 	curSfx = &knownSfx[rmSource->sfx];
891 
892 	if(rmSource->isPlaying)
893 		curSfx->loopActiveCnt--;
894 	if(iskilled)
895 		curSfx->loopCnt--;
896 
897 	if(curSfx->loopCnt)
898 	{
899 		if(rmSource->priority == SRCPRI_ENTITY)
900 		{
901 			if(!iskilled && rmSource->isPlaying)
902 			{
903 				// only sync ambient loops...
904 				// It makes more sense to have sounds for weapons/projectiles unsynced
905 				S_AL_SaveLoopPos(rmSource, rmSource->alSource);
906 			}
907 		}
908 		else if(curSfx->masterLoopSrc != -1 &&
909 		        rmSource == &srcList[curSfx->masterLoopSrc])
910 		{
911 			int firstInactive = -1;
912 
913 			// Only if rmSource was the master and if there are still playing loops for
914 			// this sound will we need to find a new master.
915 
916 			if(iskilled || curSfx->loopActiveCnt)
917 			{
918 				for(index = 0; index < srcCount; index++)
919 				{
920 					curSource = &srcList[index];
921 
922 					if(curSource->sfx == rmSource->sfx && curSource != rmSource &&
923 					   curSource->isActive && curSource->isLooping && curSource->priority == SRCPRI_AMBIENT)
924 					{
925 						if(curSource->isPlaying)
926 						{
927 							curSfx->masterLoopSrc = index;
928 							break;
929 						}
930 						else if(firstInactive < 0)
931 							firstInactive = index;
932 					}
933 				}
934 			}
935 
936 			if(!curSfx->loopActiveCnt)
937 			{
938 				if(firstInactive < 0)
939 				{
940 					if(iskilled)
941 					{
942 						curSfx->masterLoopSrc = -1;
943 						return;
944 					}
945 					else
946 						curSource = rmSource;
947 				}
948 				else
949 					curSource = &srcList[firstInactive];
950 
951 				if(rmSource->isPlaying)
952 				{
953 					// this was the last not stopped source, save last sample position + time
954 					S_AL_SaveLoopPos(curSource, rmSource->alSource);
955 				}
956 				else
957 				{
958 					// second case: all loops using this sound have stopped due to listener being of of range,
959 					// and now the inactive master gets deleted. Just move over the soundpos settings to the
960 					// new master.
961 					curSource->lastTimePos = rmSource->lastTimePos;
962 					curSource->lastSampleTime = rmSource->lastSampleTime;
963 				}
964 			}
965 		}
966 	}
967 	else
968 		curSfx->masterLoopSrc = -1;
969 }
970 
971 /*
972 =================
973 S_AL_SrcKill
974 =================
975 */
S_AL_SrcKill(srcHandle_t src)976 static void S_AL_SrcKill(srcHandle_t src)
977 {
978 	src_t *curSource = &srcList[src];
979 
980 	// I'm not touching it. Unlock it first.
981 	if(curSource->isLocked)
982 		return;
983 
984 	// Remove the entity association and loop master status
985 	if(curSource->isLooping)
986 	{
987 		curSource->isLooping = qfalse;
988 
989 		if(curSource->entity != -1)
990 		{
991 			sentity_t *curEnt = &entityList[curSource->entity];
992 
993 			curEnt->srcAllocated = qfalse;
994 			curEnt->srcIndex = -1;
995 			curEnt->loopAddedThisFrame = qfalse;
996 			curEnt->startLoopingSound = qfalse;
997 		}
998 
999 		S_AL_NewLoopMaster(curSource, qtrue);
1000 	}
1001 
1002 	// Stop it if it's playing
1003 	if(curSource->isPlaying)
1004 	{
1005 		qalSourceStop(curSource->alSource);
1006 		curSource->isPlaying = qfalse;
1007 	}
1008 
1009 	// Detach any buffers
1010 	qalSourcei(curSource->alSource, AL_BUFFER, 0);
1011 
1012 	curSource->sfx = 0;
1013 	curSource->lastUsedTime = 0;
1014 	curSource->priority = 0;
1015 	curSource->entity = -1;
1016 	curSource->channel = -1;
1017 	if(curSource->isActive)
1018 	{
1019 		curSource->isActive = qfalse;
1020 		srcActiveCnt--;
1021 	}
1022 	curSource->isLocked = qfalse;
1023 	curSource->isTracking = qfalse;
1024 	curSource->local = qfalse;
1025 }
1026 
1027 /*
1028 =================
1029 S_AL_SrcAlloc
1030 =================
1031 */
1032 static
S_AL_SrcAlloc(sfxHandle_t sfx,alSrcPriority_t priority,int entnum,int channel,int flags)1033 srcHandle_t S_AL_SrcAlloc( sfxHandle_t sfx, alSrcPriority_t priority, int entnum, int channel, int flags )
1034 {
1035 	int i;
1036 	int empty = -1;
1037 	int weakest = -1;
1038 	int weakest_time = Sys_Milliseconds();
1039 	int weakest_pri = 999;
1040 	float weakest_gain = 1000.0;
1041 	qboolean weakest_isplaying = qtrue;
1042 	int weakest_numloops = 0;
1043 	src_t *curSource;
1044 	qboolean cutDuplicateSound = qfalse;
1045 
1046 	for(i = 0; i < srcCount; i++)
1047 	{
1048 		curSource = &srcList[i];
1049 
1050 		// If it's locked, we aren't even going to look at it
1051 		if(curSource->isLocked)
1052 			continue;
1053 
1054 		// Is it empty or not?
1055 		if(!curSource->isActive)
1056 		{
1057 			if (empty == -1)
1058 				empty = i;
1059 			break;
1060 		}
1061 
1062 		if(curSource->isPlaying)
1063 		{
1064 			if(weakest_isplaying && curSource->priority < priority &&
1065 			   (curSource->priority < weakest_pri ||
1066 			   (!curSource->isLooping && (curSource->scaleGain < weakest_gain || curSource->lastUsedTime < weakest_time))))
1067 			{
1068 				// If it has lower priority, is fainter or older, flag it as weak
1069 				// the last two values are only compared if it's not a looping sound, because we want to prevent two
1070 				// loops (loops are added EVERY frame) fighting for a slot
1071 				weakest_pri = curSource->priority;
1072 				weakest_time = curSource->lastUsedTime;
1073 				weakest_gain = curSource->scaleGain;
1074 				weakest = i;
1075 			}
1076 		}
1077 		else
1078 		{
1079 			weakest_isplaying = qfalse;
1080 
1081 			if(weakest < 0 ||
1082 			   knownSfx[curSource->sfx].loopCnt > weakest_numloops ||
1083 			   curSource->priority < weakest_pri ||
1084 			   curSource->lastUsedTime < weakest_time)
1085 			{
1086 				// Sources currently not playing of course have lowest priority
1087 				// also try to always keep at least one loop master for every loop sound
1088 				weakest_pri = curSource->priority;
1089 				weakest_time = curSource->lastUsedTime;
1090 				weakest_numloops = knownSfx[curSource->sfx].loopCnt;
1091 				weakest = i;
1092 			}
1093 		}
1094 
1095 		// shut off other sounds on this channel if necessary
1096 		if((curSource->entity == entnum) && curSource->sfx > 0 && (curSource->channel == channel))
1097 		{
1098 			// currently apply only to non-looping sounds
1099 			if ( curSource->isLooping ) {
1100 				continue;
1101 			}
1102 
1103 			// cutoff all on channel
1104 			if ( flags & SND_CUTOFF_ALL ) {
1105 				S_AL_SrcKill(i);
1106 				if (empty == -1)
1107 					empty = i;
1108 				continue;
1109 			}
1110 
1111 			if ( curSource->flags & SND_NOCUT ) {
1112 				continue;
1113 			}
1114 
1115 			// cutoff sounds that expect to be overwritten
1116 			if ( curSource->flags & SND_OKTOCUT ) {
1117 				S_AL_SrcKill(i);
1118 				if (empty == -1)
1119 					empty = i;
1120 				continue;
1121 			}
1122 
1123 			// cutoff 'weak' sounds on channel
1124 			if ( flags & SND_CUTOFF ) {
1125 				if ( curSource->flags & SND_REQUESTCUT ) {
1126 					S_AL_SrcKill(i);
1127 					if (empty == -1)
1128 						empty = i;
1129 					continue;
1130 				}
1131 			}
1132 
1133 			// re-use channel if applicable
1134 			if ( curSource->channel != -1 && curSource->sfx == sfx && !cutDuplicateSound ) {
1135 				cutDuplicateSound = qtrue;
1136 				S_AL_SrcKill(i);
1137 				if (empty == -1)
1138 					empty = i;
1139 				continue;
1140 			}
1141 		}
1142 	}
1143 
1144 	if(empty == -1)
1145 		empty = weakest;
1146 
1147 	if(empty >= 0)
1148 	{
1149 		S_AL_SrcKill(empty);
1150 		srcList[empty].isActive = qtrue;
1151 		srcActiveCnt++;
1152 	}
1153 
1154 	return empty;
1155 }
1156 
1157 /*
1158 =================
1159 S_AL_SrcFind
1160 
1161 Finds an active source with matching entity and channel numbers
1162 Returns -1 if there isn't one
1163 =================
1164 */
1165 #if 0
1166 static
1167 srcHandle_t S_AL_SrcFind(int entnum, int channel)
1168 {
1169 	int i;
1170 	for(i = 0; i < srcCount; i++)
1171 	{
1172 		if(!srcList[i].isActive)
1173 			continue;
1174 		if((srcList[i].entity == entnum) && (srcList[i].channel == channel))
1175 			return i;
1176 	}
1177 	return -1;
1178 }
1179 #endif
1180 
1181 /*
1182 =================
1183 S_AL_SrcLock
1184 
1185 Locked sources will not be automatically reallocated or managed
1186 =================
1187 */
1188 static
S_AL_SrcLock(srcHandle_t src)1189 void S_AL_SrcLock(srcHandle_t src)
1190 {
1191 	srcList[src].isLocked = qtrue;
1192 }
1193 
1194 /*
1195 =================
1196 S_AL_SrcUnlock
1197 
1198 Once unlocked, the source may be reallocated again
1199 =================
1200 */
1201 static
S_AL_SrcUnlock(srcHandle_t src)1202 void S_AL_SrcUnlock(srcHandle_t src)
1203 {
1204 	srcList[src].isLocked = qfalse;
1205 }
1206 
1207 /*
1208 =================
1209 S_AL_UpdateEntityPosition
1210 =================
1211 */
1212 static
S_AL_UpdateEntityPosition(int entityNum,const vec3_t origin)1213 void S_AL_UpdateEntityPosition( int entityNum, const vec3_t origin )
1214 {
1215 	vec3_t sanOrigin;
1216 
1217 	VectorCopy( origin, sanOrigin );
1218 	S_AL_SanitiseVector( sanOrigin );
1219 	if ( entityNum < 0 || entityNum >= MAX_GENTITIES )
1220 		Com_Error( ERR_DROP, "S_UpdateEntityPosition: bad entitynum %i", entityNum );
1221 	VectorCopy( sanOrigin, entityList[entityNum].origin );
1222 }
1223 
1224 /*
1225 =================
1226 S_AL_CheckInput
1227 Check whether input values from mods are out of range.
1228 Necessary for i.g. Western Quake3 mod which is buggy.
1229 =================
1230 */
S_AL_CheckInput(int entityNum,sfxHandle_t sfx)1231 static qboolean S_AL_CheckInput(int entityNum, sfxHandle_t sfx)
1232 {
1233 	if (entityNum < 0 || entityNum >= MAX_GENTITIES)
1234 		Com_Error(ERR_DROP, "ERROR: S_AL_CheckInput: bad entitynum %i", entityNum);
1235 
1236 	if (sfx < 0 || sfx >= numSfx)
1237 	{
1238 		Com_Printf(S_COLOR_RED "ERROR: S_AL_CheckInput: handle %i out of range\n", sfx);
1239 		return qtrue;
1240 	}
1241 
1242 	return qfalse;
1243 }
1244 
1245 /*
1246 =================
1247 S_AL_StartLocalSound
1248 
1249 Play a local (non-spatialized) sound effect
1250 =================
1251 */
1252 static
S_AL_StartLocalSound(sfxHandle_t sfx,int channel)1253 void S_AL_StartLocalSound(sfxHandle_t sfx, int channel)
1254 {
1255 	srcHandle_t src;
1256 
1257 	if(S_AL_CheckInput(0, sfx))
1258 		return;
1259 
1260 	// Try to grab a source
1261 	src = S_AL_SrcAlloc(sfx, SRCPRI_LOCAL, -1, channel, 0);
1262 
1263 	if(src == -1)
1264 		return;
1265 
1266 	// Set up the effect
1267 	S_AL_SrcSetup(src, sfx, SRCPRI_LOCAL, -1, channel, 0, qtrue);
1268 
1269 	// Start it playing
1270 	srcList[src].isPlaying = qtrue;
1271 	qalSourcePlay(srcList[src].alSource);
1272 }
1273 
1274 /*
1275 =================
1276 S_AL_MainStartSound
1277 
1278 Play a one-shot sound effect
1279 =================
1280 */
S_AL_MainStartSound(vec3_t origin,int entnum,int entchannel,sfxHandle_t sfx,int flags)1281 static void S_AL_MainStartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx, int flags )
1282 {
1283 	vec3_t sorigin;
1284 	srcHandle_t src;
1285 	src_t *curSource;
1286 
1287 	if(origin)
1288 	{
1289 		if(S_AL_CheckInput(0, sfx))
1290 			return;
1291 
1292 		VectorCopy(origin, sorigin);
1293 	}
1294 	else
1295 	{
1296 		if(S_AL_CheckInput(entnum, sfx))
1297 			return;
1298 
1299 		if(S_AL_HearingThroughEntity(entnum))
1300 		{
1301 			S_AL_StartLocalSound(sfx, entchannel);
1302 			return;
1303 		}
1304 
1305 		VectorCopy(entityList[entnum].origin, sorigin);
1306 	}
1307 
1308 	S_AL_SanitiseVector(sorigin);
1309 
1310 	if((srcActiveCnt > 5 * srcCount / 3) &&
1311 		(DistanceSquared(sorigin, lastListenerOrigin) >=
1312 		(s_alMaxDistance->value + s_alGraceDistance->value) * (s_alMaxDistance->value + s_alGraceDistance->value)))
1313 	{
1314 		// We're getting tight on sources and source is not within hearing distance so don't add it
1315 		return;
1316 	}
1317 
1318 	// Try to grab a source
1319 	src = S_AL_SrcAlloc(sfx, SRCPRI_ONESHOT, entnum, entchannel, flags);
1320 	if(src == -1)
1321 		return;
1322 
1323 	S_AL_SrcSetup(src, sfx, SRCPRI_ONESHOT, entnum, entchannel, flags, qfalse);
1324 
1325 	curSource = &srcList[src];
1326 
1327 	if(!origin)
1328 		curSource->isTracking = qtrue;
1329 
1330 	qalSourcefv(curSource->alSource, AL_POSITION, sorigin );
1331 	S_AL_ScaleGain(curSource, sorigin);
1332 
1333 	// Start it playing
1334 	curSource->isPlaying = qtrue;
1335 	qalSourcePlay(curSource->alSource);
1336 }
1337 
1338 /*
1339 =================
1340 S_AL_StartSound
1341 =================
1342 */
S_AL_StartSound(vec3_t origin,int entnum,int entchannel,sfxHandle_t sfx)1343 static void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx )
1344 {
1345 	S_AL_MainStartSound( origin, entnum, entchannel, sfx, 0 );
1346 }
1347 
1348 /*
1349 =================
1350 S_AL_StartSoundEx
1351 =================
1352 */
S_AL_StartSoundEx(vec3_t origin,int entnum,int entchannel,sfxHandle_t sfx,int flags)1353 static void S_AL_StartSoundEx( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx, int flags )
1354 {
1355 	// RF, we have lots of NULL sounds using up valuable channels, so just ignore them
1356 	if ( !sfx && entchannel != CHAN_WEAPON ) {  // let null weapon sounds try to play.  they kill any weapon sounds playing when a guy dies
1357 		return;
1358 	}
1359 
1360 	// RF, make the call now, or else we could override following streaming sounds in the same frame, due to the delay
1361 	S_AL_MainStartSound( origin, entnum, entchannel, sfx, flags );
1362 }
1363 
1364 /*
1365 =================
1366 S_AL_ClearLoopingSounds
1367 =================
1368 */
1369 static
S_AL_ClearLoopingSounds(qboolean killall)1370 void S_AL_ClearLoopingSounds( qboolean killall )
1371 {
1372 	int i;
1373 	for(i = 0; i < srcCount; i++)
1374 	{
1375 		if((srcList[i].isLooping) && (srcList[i].entity != -1))
1376 			entityList[srcList[i].entity].loopAddedThisFrame = qfalse;
1377 	}
1378 }
1379 
1380 /*
1381 =================
1382 S_AL_SrcLoop
1383 =================
1384 */
S_AL_SrcLoop(alSrcPriority_t priority,sfxHandle_t sfx,const vec3_t origin,const vec3_t velocity,int entityNum,int volume)1385 static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx,
1386 		const vec3_t origin, const vec3_t velocity, int entityNum, int volume )
1387 {
1388 	int				src;
1389 	sentity_t	*sent = &entityList[ entityNum ];
1390 	src_t		*curSource;
1391 	vec3_t		sorigin, svelocity;
1392 
1393 	if( entityNum < 0 || entityNum >= MAX_GENTITIES )
1394 		return;
1395 
1396 	if(S_AL_CheckInput(entityNum, sfx))
1397 		return;
1398 
1399 	// Do we need to allocate a new source for this entity
1400 	if( !sent->srcAllocated )
1401 	{
1402 		// Try to get a channel
1403 		src = S_AL_SrcAlloc( sfx, priority, entityNum, -1, 0 );
1404 		if( src == -1 )
1405 		{
1406 			Com_DPrintf( S_COLOR_YELLOW "WARNING: Failed to allocate source "
1407 					"for loop sfx %d on entity %d\n", sfx, entityNum );
1408 			return;
1409 		}
1410 
1411 		curSource = &srcList[src];
1412 
1413 		sent->startLoopingSound = qtrue;
1414 
1415 		curSource->lastTimePos = -1.0;
1416 		curSource->lastSampleTime = Sys_Milliseconds();
1417 	}
1418 	else
1419 	{
1420 		src = sent->srcIndex;
1421 		curSource = &srcList[src];
1422 	}
1423 
1424 	sent->srcAllocated = qtrue;
1425 	sent->srcIndex = src;
1426 
1427 	sent->loopPriority = priority;
1428 	sent->loopSfx = sfx;
1429 
1430 	// If this is not set then the looping sound is stopped.
1431 	sent->loopAddedThisFrame = qtrue;
1432 
1433 	// UGH
1434 	// These lines should be called via S_AL_SrcSetup, but we
1435 	// can't call that yet as it buffers sfxes that may change
1436 	// with subsequent calls to S_AL_SrcLoop
1437 	curSource->entity = entityNum;
1438 	curSource->isLooping = qtrue;
1439 
1440 	if( S_AL_HearingThroughEntity( entityNum ) )
1441 	{
1442 		curSource->local = qtrue;
1443 
1444 		VectorClear(sorigin);
1445 
1446 		if ( volume > 255 ) {
1447 			volume = 255;
1448 		} else if ( volume < 0 ) {
1449 			volume = 0;
1450 		}
1451 
1452 		qalSourcefv(curSource->alSource, AL_POSITION, sorigin);
1453 		qalSourcefv(curSource->alSource, AL_VELOCITY, vec3_origin);
1454 		S_AL_Gain(curSource->alSource, volume / 255.0f);
1455 	}
1456 	else
1457 	{
1458 		curSource->local = qfalse;
1459 
1460 		if(origin)
1461 			VectorCopy(origin, sorigin);
1462 		else
1463 			VectorCopy(sent->origin, sorigin);
1464 
1465 		S_AL_SanitiseVector(sorigin);
1466 
1467 		VectorCopy(sorigin, curSource->loopSpeakerPos);
1468 
1469 		if(velocity)
1470 		{
1471 			VectorCopy(velocity, svelocity);
1472 			S_AL_SanitiseVector(svelocity);
1473 		}
1474 		else
1475 			VectorClear(svelocity);
1476 
1477 		qalSourcefv(curSource->alSource, AL_POSITION, (ALfloat *) sorigin);
1478 		qalSourcefv(curSource->alSource, AL_VELOCITY, (ALfloat *) svelocity);
1479 	}
1480 }
1481 
1482 /*
1483 =================
1484 S_AL_AddLoopingSound
1485 =================
1486 */
S_AL_AddLoopingSound(int entityNum,const vec3_t origin,const vec3_t velocity,const int range,sfxHandle_t sfx,int volume)1487 static void S_AL_AddLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, const int range, sfxHandle_t sfx, int volume)
1488 {
1489 	S_AL_SrcLoop(SRCPRI_ENTITY, sfx, origin, velocity, entityNum, volume);
1490 }
1491 
1492 /*
1493 =================
1494 S_AL_AddRealLoopingSound
1495 =================
1496 */
S_AL_AddRealLoopingSound(int entityNum,const vec3_t origin,const vec3_t velocity,const int range,sfxHandle_t sfx)1497 static void S_AL_AddRealLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, const int range, sfxHandle_t sfx)
1498 {
1499 	S_AL_SrcLoop(SRCPRI_AMBIENT, sfx, origin, velocity, entityNum, 255);
1500 }
1501 
1502 /*
1503 =================
1504 S_AL_StopLoopingSound
1505 =================
1506 */
1507 static
S_AL_StopLoopingSound(int entityNum)1508 void S_AL_StopLoopingSound(int entityNum )
1509 {
1510 	if(entityList[entityNum].srcAllocated)
1511 		S_AL_SrcKill(entityList[entityNum].srcIndex);
1512 }
1513 
1514 /*
1515 =================
1516 S_AL_SrcUpdate
1517 
1518 Update state (move things around, manage sources, and so on)
1519 =================
1520 */
1521 static
S_AL_SrcUpdate(void)1522 void S_AL_SrcUpdate( void )
1523 {
1524 	int i;
1525 	int entityNum;
1526 	ALint state;
1527 	src_t *curSource;
1528 
1529 	for(i = 0; i < srcCount; i++)
1530 	{
1531 		entityNum = srcList[i].entity;
1532 		curSource = &srcList[i];
1533 
1534 		if(curSource->isLocked)
1535 			continue;
1536 
1537 		if(!curSource->isActive)
1538 			continue;
1539 
1540 		// Update source parameters
1541 		if((s_alGain->modified) || (s_volume->modified))
1542 			curSource->curGain = s_alGain->value * s_volume->value;
1543 		if((s_alRolloff->modified) && (!curSource->local))
1544 			qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value);
1545 		if(s_alMinDistance->modified)
1546 			qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value);
1547 
1548 		if(curSource->isLooping)
1549 		{
1550 			sentity_t *sent = &entityList[ entityNum ];
1551 
1552 			// If a looping effect hasn't been touched this frame, pause or kill it
1553 			if(sent->loopAddedThisFrame)
1554 			{
1555 				alSfx_t *curSfx;
1556 
1557 				// The sound has changed without an intervening removal
1558 				if(curSource->isActive && !sent->startLoopingSound &&
1559 						curSource->sfx != sent->loopSfx)
1560 				{
1561 					S_AL_NewLoopMaster(curSource, qtrue);
1562 
1563 					curSource->isPlaying = qfalse;
1564 					qalSourceStop(curSource->alSource);
1565 					qalSourcei(curSource->alSource, AL_BUFFER, 0);
1566 					sent->startLoopingSound = qtrue;
1567 				}
1568 
1569 				// The sound hasn't been started yet
1570 				if(sent->startLoopingSound)
1571 				{
1572 					S_AL_SrcSetup(i, sent->loopSfx, sent->loopPriority,
1573 							entityNum, -1, 0, curSource->local);
1574 					curSource->isLooping = qtrue;
1575 
1576 					knownSfx[curSource->sfx].loopCnt++;
1577 					sent->startLoopingSound = qfalse;
1578 				}
1579 
1580 				curSfx = &knownSfx[curSource->sfx];
1581 
1582 				S_AL_ScaleGain(curSource, curSource->loopSpeakerPos);
1583 				if(!curSource->scaleGain)
1584 				{
1585 					if(curSource->isPlaying)
1586 					{
1587 						// Sound is mute, stop playback until we are in range again
1588 						S_AL_NewLoopMaster(curSource, qfalse);
1589 						qalSourceStop(curSource->alSource);
1590 						curSource->isPlaying = qfalse;
1591 					}
1592 					else if(!curSfx->loopActiveCnt && curSfx->masterLoopSrc < 0)
1593 						curSfx->masterLoopSrc = i;
1594 
1595 					continue;
1596 				}
1597 
1598 				if(!curSource->isPlaying)
1599 				{
1600 					qalSourcei(curSource->alSource, AL_LOOPING, AL_TRUE);
1601 					curSource->isPlaying = qtrue;
1602 					qalSourcePlay(curSource->alSource);
1603 
1604 					if(curSource->priority == SRCPRI_AMBIENT)
1605 					{
1606 						// If there are other ambient looping sources with the same sound,
1607 						// make sure the sound of these sources are in sync.
1608 
1609 						if(curSfx->loopActiveCnt)
1610 						{
1611 							int offset, error;
1612 
1613 							// we already have a master loop playing, get buffer position.
1614 							S_AL_ClearError( qfalse );
1615 							qalGetSourcei(srcList[curSfx->masterLoopSrc].alSource, AL_SAMPLE_OFFSET, &offset);
1616 							if((error = qalGetError()) != AL_NO_ERROR)
1617 							{
1618 								if(error != AL_INVALID_ENUM)
1619 								{
1620 									Com_Printf(S_COLOR_YELLOW "WARNING: Cannot get sample offset from source %d: "
1621 										   "%s\n", i, S_AL_ErrorMsg(error));
1622 								}
1623 							}
1624 							else
1625 								qalSourcei(curSource->alSource, AL_SAMPLE_OFFSET, offset);
1626 						}
1627 						else if(curSfx->loopCnt && curSfx->masterLoopSrc >= 0)
1628 						{
1629 							float secofs;
1630 
1631 							src_t *master = &srcList[curSfx->masterLoopSrc];
1632 							// This loop sound used to be played, but all sources are stopped. Use last sample position/time
1633 							// to calculate offset so the player thinks the sources continued playing while they were inaudible.
1634 
1635 							if(master->lastTimePos >= 0)
1636 							{
1637 								secofs = master->lastTimePos + (Sys_Milliseconds() - master->lastSampleTime) / 1000.0f;
1638 								secofs = fmodf(secofs, (float) curSfx->info.samples / curSfx->info.rate);
1639 
1640 								qalSourcef(curSource->alSource, AL_SEC_OFFSET, secofs);
1641 							}
1642 
1643 							// I be the master now
1644 							curSfx->masterLoopSrc = i;
1645 						}
1646 						else
1647 							curSfx->masterLoopSrc = i;
1648 					}
1649 					else if(curSource->lastTimePos >= 0)
1650 					{
1651 						float secofs;
1652 
1653 						// For unsynced loops (SRCPRI_ENTITY) just carry on playing as if the sound was never stopped
1654 
1655 						secofs = curSource->lastTimePos + (Sys_Milliseconds() - curSource->lastSampleTime) / 1000.0f;
1656 						secofs = fmodf(secofs, (float) curSfx->info.samples / curSfx->info.rate);
1657 						qalSourcef(curSource->alSource, AL_SEC_OFFSET, secofs);
1658 					}
1659 
1660 					curSfx->loopActiveCnt++;
1661 				}
1662 
1663 				// Update locality
1664 				if(curSource->local)
1665 				{
1666 					qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_TRUE);
1667 					qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, 0.0f);
1668 				}
1669 				else
1670 				{
1671 					qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE);
1672 					qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value);
1673 				}
1674 
1675 			}
1676 			else if(curSource->priority == SRCPRI_AMBIENT)
1677 			{
1678 				if(curSource->isPlaying)
1679 				{
1680 					S_AL_NewLoopMaster(curSource, qfalse);
1681 					qalSourceStop(curSource->alSource);
1682 					curSource->isPlaying = qfalse;
1683 				}
1684 			}
1685 			else
1686 				S_AL_SrcKill(i);
1687 
1688 			continue;
1689 		}
1690 
1691 		if(!curSource->isStream)
1692 		{
1693         		// Check if it's done, and flag it
1694 	        	qalGetSourcei(curSource->alSource, AL_SOURCE_STATE, &state);
1695 	        	if(state == AL_STOPPED)
1696         		{
1697 	        		curSource->isPlaying = qfalse;
1698 		        	S_AL_SrcKill(i);
1699 		        	continue;
1700         		}
1701                 }
1702 
1703 		// Query relativity of source, don't move if it's true
1704 		qalGetSourcei(curSource->alSource, AL_SOURCE_RELATIVE, &state);
1705 
1706 		// See if it needs to be moved
1707 		if(curSource->isTracking && !state)
1708 		{
1709 			qalSourcefv(curSource->alSource, AL_POSITION, entityList[entityNum].origin);
1710  			S_AL_ScaleGain(curSource, entityList[entityNum].origin);
1711 		}
1712 	}
1713 }
1714 
1715 /*
1716 =================
1717 S_AL_SrcShutup
1718 =================
1719 */
1720 static
S_AL_SrcShutup(void)1721 void S_AL_SrcShutup( void )
1722 {
1723 	int i;
1724 	for(i = 0; i < srcCount; i++)
1725 		S_AL_SrcKill(i);
1726 }
1727 
1728 /*
1729 =================
1730 S_AL_SrcGet
1731 =================
1732 */
1733 static
S_AL_SrcGet(srcHandle_t src)1734 ALuint S_AL_SrcGet(srcHandle_t src)
1735 {
1736 	return srcList[src].alSource;
1737 }
1738 
1739 
1740 //===========================================================================
1741 
1742 // Q3A cinematics use up to 12 buffers at once
1743 #define MAX_STREAM_BUFFERS 20
1744 
1745 static srcHandle_t streamSourceHandles[MAX_RAW_STREAMS];
1746 static qboolean streamPlaying[MAX_RAW_STREAMS];
1747 static ALuint streamSources[MAX_RAW_STREAMS];
1748 static ALuint streamBuffers[MAX_RAW_STREAMS][MAX_STREAM_BUFFERS];
1749 static int streamNumBuffers[MAX_RAW_STREAMS];
1750 static int streamBufIndex[MAX_RAW_STREAMS];
1751 
1752 /*
1753 =================
1754 S_AL_AllocateStreamChannel
1755 =================
1756 */
S_AL_AllocateStreamChannel(int stream,int entityNum)1757 static void S_AL_AllocateStreamChannel(int stream, int entityNum)
1758 {
1759         srcHandle_t cursrc;
1760         ALuint alsrc;
1761 
1762 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1763 		return;
1764 
1765         if(entityNum >= 0)
1766         {
1767                 // This is a stream that tracks an entity
1768         	// Allocate a streamSource at normal priority
1769         	cursrc = S_AL_SrcAlloc(-1, SRCPRI_ENTITY, entityNum, 0, 0);
1770         	if(cursrc < 0)
1771 	        	return;
1772 
1773         	S_AL_SrcSetup(cursrc, -1, SRCPRI_ENTITY, entityNum, 0, 0, qfalse);
1774         	alsrc = S_AL_SrcGet(cursrc);
1775         	srcList[cursrc].isTracking = qtrue;
1776         	srcList[cursrc].isStream = qtrue;
1777         }
1778         else
1779         {
1780                 // Unspatialized stream source
1781 
1782         	// Allocate a streamSource at high priority
1783         	cursrc = S_AL_SrcAlloc(-1, SRCPRI_STREAM, -2, 0, 0);
1784         	if(cursrc < 0)
1785 	        	return;
1786 
1787         	alsrc = S_AL_SrcGet(cursrc);
1788 
1789         	// Lock the streamSource so nobody else can use it, and get the raw streamSource
1790         	S_AL_SrcLock(cursrc);
1791 
1792         	// make sure that after unmuting the S_AL_Gain in S_Update() does not turn
1793         	// volume up prematurely for this source
1794         	srcList[cursrc].scaleGain = 0.0f;
1795 
1796         	// Set some streamSource parameters
1797         	qalSourcei (alsrc, AL_BUFFER,          0            );
1798         	qalSourcei (alsrc, AL_LOOPING,         AL_FALSE     );
1799         	qalSource3f(alsrc, AL_POSITION,        0.0, 0.0, 0.0);
1800         	qalSource3f(alsrc, AL_VELOCITY,        0.0, 0.0, 0.0);
1801         	qalSource3f(alsrc, AL_DIRECTION,       0.0, 0.0, 0.0);
1802         	qalSourcef (alsrc, AL_ROLLOFF_FACTOR,  0.0          );
1803         	qalSourcei (alsrc, AL_SOURCE_RELATIVE, AL_TRUE      );
1804         }
1805 
1806         streamSourceHandles[stream] = cursrc;
1807        	streamSources[stream] = alsrc;
1808 
1809 	streamNumBuffers[stream] = 0;
1810 	streamBufIndex[stream] = 0;
1811 }
1812 
1813 /*
1814 =================
1815 S_AL_FreeStreamChannel
1816 =================
1817 */
S_AL_FreeStreamChannel(int stream)1818 static void S_AL_FreeStreamChannel( int stream )
1819 {
1820 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1821 		return;
1822 
1823 	// Detach any buffers
1824 	qalSourcei(streamSources[stream], AL_BUFFER, 0);
1825 
1826 	// Delete the buffers
1827 	if (streamNumBuffers[stream] > 0) {
1828 		qalDeleteBuffers(streamNumBuffers[stream], streamBuffers[stream]);
1829 		streamNumBuffers[stream] = 0;
1830 	}
1831 
1832 	// Release the output streamSource
1833 	S_AL_SrcUnlock(streamSourceHandles[stream]);
1834 	S_AL_SrcKill(streamSourceHandles[stream]);
1835 	streamSources[stream] = 0;
1836 	streamSourceHandles[stream] = -1;
1837 }
1838 
1839 /*
1840 =================
1841 S_AL_RawSamples
1842 =================
1843 */
1844 static
S_AL_RawSamples(int stream,int samples,int rate,int width,int channels,const byte * data,float volume,int entityNum)1845 void S_AL_RawSamples(int stream, int samples, int rate, int width, int channels, const byte *data, float volume, int entityNum)
1846 {
1847 	int numBuffers;
1848 	ALuint buffer;
1849 	ALuint format;
1850 
1851 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1852 		return;
1853 
1854 	format = S_AL_Format( width, channels );
1855 
1856 	// Create the streamSource if necessary
1857 	if(streamSourceHandles[stream] == -1)
1858 	{
1859 		S_AL_AllocateStreamChannel(stream, entityNum);
1860 
1861 		// Failed?
1862 		if(streamSourceHandles[stream] == -1)
1863 		{
1864 			Com_Printf( S_COLOR_RED "ERROR: Can't allocate streaming streamSource\n");
1865 			return;
1866 		}
1867 	}
1868 
1869 	qalGetSourcei(streamSources[stream], AL_BUFFERS_QUEUED, &numBuffers);
1870 
1871 	if (numBuffers == MAX_STREAM_BUFFERS)
1872 	{
1873 		Com_DPrintf(S_COLOR_RED"WARNING: Steam dropping raw samples, reached MAX_STREAM_BUFFERS\n");
1874 		return;
1875 	}
1876 
1877 	// Allocate a new AL buffer if needed
1878 	if (numBuffers == streamNumBuffers[stream])
1879 	{
1880 		ALuint oldBuffers[MAX_STREAM_BUFFERS];
1881 		int	i;
1882 
1883 		if (!S_AL_GenBuffers(1, &buffer, "stream"))
1884 			return;
1885 
1886 		Com_Memcpy(oldBuffers, &streamBuffers[stream], sizeof (oldBuffers));
1887 
1888 		// Reorder buffer array in order of oldest to newest
1889 		for ( i = 0; i < streamNumBuffers[stream]; ++i )
1890 			streamBuffers[stream][i] = oldBuffers[(streamBufIndex[stream] + i) % streamNumBuffers[stream]];
1891 
1892 		// Add the new buffer to end
1893 		streamBuffers[stream][streamNumBuffers[stream]] = buffer;
1894 		streamBufIndex[stream] = streamNumBuffers[stream];
1895 		streamNumBuffers[stream]++;
1896 	}
1897 
1898 	// Select next buffer in loop
1899 	buffer = streamBuffers[stream][ streamBufIndex[stream] ];
1900 	streamBufIndex[stream] = (streamBufIndex[stream] + 1) % streamNumBuffers[stream];
1901 
1902 	// Fill buffer
1903 	qalBufferData(buffer, format, (ALvoid *)data, (samples * width * channels), rate);
1904 
1905 	// Shove the data onto the streamSource
1906 	qalSourceQueueBuffers(streamSources[stream], 1, &buffer);
1907 
1908 	if(entityNum < 0)
1909 	{
1910         	// Volume
1911         	S_AL_Gain (streamSources[stream], volume * s_volume->value * s_alGain->value);
1912         }
1913 
1914 	// Start stream
1915 	if(!streamPlaying[stream])
1916 	{
1917 		qalSourcePlay( streamSources[stream] );
1918 		streamPlaying[stream] = qtrue;
1919 	}
1920 }
1921 
1922 /*
1923 =================
1924 S_AL_StreamUpdate
1925 =================
1926 */
1927 static
S_AL_StreamUpdate(int stream)1928 void S_AL_StreamUpdate( int stream )
1929 {
1930 	int		numBuffers;
1931 	ALint	state;
1932 
1933 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1934 		return;
1935 
1936 	if(streamSourceHandles[stream] == -1)
1937 		return;
1938 
1939 	// Un-queue any buffers
1940 	qalGetSourcei( streamSources[stream], AL_BUFFERS_PROCESSED, &numBuffers );
1941 	while( numBuffers-- )
1942 	{
1943 		ALuint buffer;
1944 		qalSourceUnqueueBuffers(streamSources[stream], 1, &buffer);
1945 	}
1946 
1947 	// Start the streamSource playing if necessary
1948 	qalGetSourcei( streamSources[stream], AL_BUFFERS_QUEUED, &numBuffers );
1949 
1950 	qalGetSourcei(streamSources[stream], AL_SOURCE_STATE, &state);
1951 	if(state == AL_STOPPED)
1952 	{
1953 		streamPlaying[stream] = qfalse;
1954 
1955 		// If there are no buffers queued up, release the streamSource
1956 		if( !numBuffers )
1957 			S_AL_FreeStreamChannel( stream );
1958 	}
1959 
1960 	if( !streamPlaying[stream] && numBuffers )
1961 	{
1962 		qalSourcePlay( streamSources[stream] );
1963 		streamPlaying[stream] = qtrue;
1964 	}
1965 }
1966 
1967 /*
1968 =================
1969 S_AL_StreamDie
1970 =================
1971 */
1972 static
S_AL_StreamDie(int stream)1973 void S_AL_StreamDie( int stream )
1974 {
1975 	if ((stream < 0) || (stream >= MAX_RAW_STREAMS))
1976 		return;
1977 
1978 	if(streamSourceHandles[stream] == -1)
1979 		return;
1980 
1981 	streamPlaying[stream] = qfalse;
1982 	qalSourceStop(streamSources[stream]);
1983 
1984 	S_AL_FreeStreamChannel(stream);
1985 }
1986 
1987 
1988 //===========================================================================
1989 
1990 
1991 #define NUM_MUSIC_BUFFERS	4
1992 #define	MUSIC_BUFFER_SIZE 4096
1993 
1994 static qboolean musicPlaying = qfalse;
1995 static srcHandle_t musicSourceHandle = -1;
1996 static ALuint musicSource;
1997 static ALuint musicBuffers[NUM_MUSIC_BUFFERS];
1998 
1999 static snd_stream_t *mus_stream;
2000 static snd_stream_t *intro_stream;
2001 static char s_backgroundLoop[MAX_QPATH];
2002 
2003 static byte decode_buffer[MUSIC_BUFFER_SIZE];
2004 
2005 /*
2006 =================
2007 S_AL_MusicSourceGet
2008 =================
2009 */
S_AL_MusicSourceGet(void)2010 static void S_AL_MusicSourceGet( void )
2011 {
2012 	// Allocate a musicSource at high priority
2013 	musicSourceHandle = S_AL_SrcAlloc(-1, SRCPRI_STREAM, -2, 0, 0);
2014 	if(musicSourceHandle == -1)
2015 		return;
2016 
2017 	// Lock the musicSource so nobody else can use it, and get the raw musicSource
2018 	S_AL_SrcLock(musicSourceHandle);
2019 	musicSource = S_AL_SrcGet(musicSourceHandle);
2020 
2021 	// make sure that after unmuting the S_AL_Gain in S_Update() does not turn
2022 	// volume up prematurely for this source
2023 	srcList[musicSourceHandle].scaleGain = 0.0f;
2024 
2025 	// Set some musicSource parameters
2026 	qalSource3f(musicSource, AL_POSITION,        0.0, 0.0, 0.0);
2027 	qalSource3f(musicSource, AL_VELOCITY,        0.0, 0.0, 0.0);
2028 	qalSource3f(musicSource, AL_DIRECTION,       0.0, 0.0, 0.0);
2029 	qalSourcef (musicSource, AL_ROLLOFF_FACTOR,  0.0          );
2030 	qalSourcei (musicSource, AL_SOURCE_RELATIVE, AL_TRUE      );
2031 }
2032 
2033 /*
2034 =================
2035 S_AL_MusicSourceFree
2036 =================
2037 */
S_AL_MusicSourceFree(void)2038 static void S_AL_MusicSourceFree( void )
2039 {
2040 	// Release the output musicSource
2041 	S_AL_SrcUnlock(musicSourceHandle);
2042 	S_AL_SrcKill(musicSourceHandle);
2043 	musicSource = 0;
2044 	musicSourceHandle = -1;
2045 }
2046 
2047 /*
2048 =================
2049 S_AL_CloseMusicFiles
2050 =================
2051 */
S_AL_CloseMusicFiles(void)2052 static void S_AL_CloseMusicFiles(void)
2053 {
2054 	if(intro_stream)
2055 	{
2056 		S_CodecCloseStream(intro_stream);
2057 		intro_stream = NULL;
2058 	}
2059 
2060 	if(mus_stream)
2061 	{
2062 		S_CodecCloseStream(mus_stream);
2063 		mus_stream = NULL;
2064 	}
2065 }
2066 
2067 /*
2068 =================
2069 S_AL_StopBackgroundTrack
2070 =================
2071 */
2072 static
S_AL_StopBackgroundTrack(void)2073 void S_AL_StopBackgroundTrack( void )
2074 {
2075 	if(!musicPlaying)
2076 		return;
2077 
2078 	// Stop playing
2079 	qalSourceStop(musicSource);
2080 
2081 	// Detach any buffers
2082 	qalSourcei(musicSource, AL_BUFFER, 0);
2083 
2084 	// Delete the buffers
2085 	qalDeleteBuffers(NUM_MUSIC_BUFFERS, musicBuffers);
2086 
2087 	// Free the musicSource
2088 	S_AL_MusicSourceFree();
2089 
2090 	// Unload the stream
2091 	S_AL_CloseMusicFiles();
2092 
2093 	musicPlaying = qfalse;
2094 }
2095 
2096 /*
2097 =================
2098 S_AL_MusicProcess
2099 =================
2100 */
2101 static
S_AL_MusicProcess(ALuint b)2102 void S_AL_MusicProcess(ALuint b)
2103 {
2104 	ALenum error;
2105 	int l;
2106 	ALuint format;
2107 	snd_stream_t *curstream;
2108 
2109 	S_AL_ClearError( qfalse );
2110 
2111 	if(intro_stream)
2112 		curstream = intro_stream;
2113 	else
2114 		curstream = mus_stream;
2115 
2116 	if(!curstream)
2117 		return;
2118 
2119 	l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer);
2120 
2121 	// Run out data to read, start at the beginning again
2122 	if(l == 0)
2123 	{
2124 		S_CodecCloseStream(curstream);
2125 
2126 		// the intro stream just finished playing so we don't need to reopen
2127 		// the music stream.
2128 		if(intro_stream)
2129 			intro_stream = NULL;
2130 		else
2131 			mus_stream = S_CodecOpenStream(s_backgroundLoop);
2132 
2133 		curstream = mus_stream;
2134 
2135 		if(!curstream)
2136 		{
2137 			S_AL_StopBackgroundTrack();
2138 			return;
2139 		}
2140 
2141 		l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer);
2142 	}
2143 
2144 	format = S_AL_Format(curstream->info.width, curstream->info.channels);
2145 
2146 	if( l == 0 )
2147 	{
2148 		// We have no data to buffer, so buffer silence
2149 		byte dummyData[ 2 ] = { 0 };
2150 
2151 		qalBufferData( b, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050 );
2152 	}
2153 	else
2154 		qalBufferData(b, format, decode_buffer, l, curstream->info.rate);
2155 
2156 	if( ( error = qalGetError( ) ) != AL_NO_ERROR )
2157 	{
2158 		S_AL_StopBackgroundTrack( );
2159 		Com_Printf( S_COLOR_RED "ERROR: while buffering data for music stream - %s\n",
2160 				S_AL_ErrorMsg( error ) );
2161 		return;
2162 	}
2163 }
2164 
2165 /*
2166 =================
2167 S_AL_StartBackgroundTrack
2168 =================
2169 */
2170 static
S_AL_StartBackgroundTrack(const char * intro,const char * loop)2171 void S_AL_StartBackgroundTrack( const char *intro, const char *loop )
2172 {
2173 	int i;
2174 	qboolean issame;
2175 
2176 	Com_DPrintf( "S_AL_StartBackgroundTrack( %s, %s )\n", intro, loop );
2177 
2178 	// Stop any existing music that might be playing
2179 	S_AL_StopBackgroundTrack();
2180 
2181 	if((!intro || !*intro) && (!loop || !*loop))
2182 		return;
2183 
2184 	// Allocate a musicSource
2185 	S_AL_MusicSourceGet();
2186 	if(musicSourceHandle == -1)
2187 		return;
2188 
2189 	if (!loop || !*loop)
2190 	{
2191 		loop = intro;
2192 		issame = qtrue;
2193 	}
2194 	else if(intro && *intro && !strcmp(intro, loop))
2195 		issame = qtrue;
2196 	else
2197 		issame = qfalse;
2198 
2199 	// Copy the loop over
2200 	Q_strncpyz( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) );
2201 
2202 	if(!issame)
2203 	{
2204 		// Open the intro and don't mind whether it succeeds.
2205 		// The important part is the loop.
2206 		intro_stream = S_CodecOpenStream(intro);
2207 	}
2208 	else
2209 		intro_stream = NULL;
2210 
2211 	mus_stream = S_CodecOpenStream(s_backgroundLoop);
2212 	if(!mus_stream)
2213 	{
2214 		S_AL_CloseMusicFiles();
2215 		S_AL_MusicSourceFree();
2216 		return;
2217 	}
2218 
2219 	// Generate the musicBuffers
2220 	if (!S_AL_GenBuffers(NUM_MUSIC_BUFFERS, musicBuffers, "music"))
2221 		return;
2222 
2223 	// Queue the musicBuffers up
2224 	for(i = 0; i < NUM_MUSIC_BUFFERS; i++)
2225 	{
2226 		S_AL_MusicProcess(musicBuffers[i]);
2227 	}
2228 
2229 	qalSourceQueueBuffers(musicSource, NUM_MUSIC_BUFFERS, musicBuffers);
2230 
2231 	// Set the initial gain property
2232 	S_AL_Gain(musicSource, s_alGain->value * s_musicVolume->value);
2233 
2234 	// Start playing
2235 	qalSourcePlay(musicSource);
2236 
2237 	musicPlaying = qtrue;
2238 }
2239 
2240 /*
2241 =================
2242 S_AL_MusicUpdate
2243 =================
2244 */
2245 static
S_AL_MusicUpdate(void)2246 void S_AL_MusicUpdate( void )
2247 {
2248 	int		numBuffers;
2249 	ALint	state;
2250 
2251 	if(!musicPlaying)
2252 		return;
2253 
2254 	qalGetSourcei( musicSource, AL_BUFFERS_PROCESSED, &numBuffers );
2255 	while( numBuffers-- )
2256 	{
2257 		ALuint b;
2258 		qalSourceUnqueueBuffers(musicSource, 1, &b);
2259 		S_AL_MusicProcess(b);
2260 		qalSourceQueueBuffers(musicSource, 1, &b);
2261 	}
2262 
2263 	// Hitches can cause OpenAL to be starved of buffers when streaming.
2264 	// If this happens, it will stop playback. This restarts the source if
2265 	// it is no longer playing, and if there are buffers available
2266 	qalGetSourcei( musicSource, AL_SOURCE_STATE, &state );
2267 	qalGetSourcei( musicSource, AL_BUFFERS_QUEUED, &numBuffers );
2268 	if( state == AL_STOPPED && numBuffers )
2269 	{
2270 		Com_DPrintf( S_COLOR_YELLOW "Restarted OpenAL music\n" );
2271 		qalSourcePlay(musicSource);
2272 	}
2273 
2274 	// Set the gain property
2275 	S_AL_Gain(musicSource, s_alGain->value * s_musicVolume->value);
2276 }
2277 
2278 /*
2279 ======================
2280 S_StartStreamingSound
2281 ======================
2282 */
S_AL_StartStreamingSound(const char * intro,const char * loop,int entnum,int channel,int attenuation)2283 void S_AL_StartStreamingSound( const char *intro, const char *loop, int entnum, int channel, int attenuation ) {
2284 	// FIXME: Stub
2285 }
2286 
2287 /*
2288 ======================
2289 S_GetVoiceAmplitude
2290 ======================
2291 */
S_AL_GetVoiceAmplitude(int entityNum)2292 int S_AL_GetVoiceAmplitude( int entityNum ) {
2293 	// FIXME: Stub
2294 	return 0;
2295 }
2296 
2297 //===========================================================================
2298 
2299 
2300 // Local state variables
2301 static ALCdevice *alDevice;
2302 static ALCcontext *alContext;
2303 
2304 #ifdef USE_VOIP
2305 static ALCdevice *alCaptureDevice;
2306 static cvar_t *s_alCapture;
2307 #endif
2308 
2309 #if defined( _WIN32 ) && defined( _WIN64 )
2310 #define ALDRIVER_DEFAULT "OpenAL64.dll"
2311 #elif defined( _WIN32 )
2312 #define ALDRIVER_DEFAULT "OpenAL32.dll"
2313 #elif defined(__APPLE__)
2314 #define ALDRIVER_DEFAULT "libopenal.dylib"
2315 #elif defined(__OpenBSD__)
2316 #define ALDRIVER_DEFAULT "libopenal.so"
2317 #else
2318 #define ALDRIVER_DEFAULT "libopenal.so.1"
2319 #endif
2320 
2321 /*
2322 =================
2323 S_AL_ClearSoundBuffer
2324 =================
2325 */
2326 static
S_AL_ClearSoundBuffer(void)2327 void S_AL_ClearSoundBuffer( void )
2328 {
2329 	S_AL_SrcShutdown( );
2330 	S_AL_SrcInit( );
2331 }
2332 
2333 /*
2334 =================
2335 S_AL_StopAllSounds
2336 =================
2337 */
2338 static
S_AL_StopAllSounds(void)2339 void S_AL_StopAllSounds( void )
2340 {
2341 	int i;
2342 	S_AL_SrcShutup();
2343 	S_AL_StopBackgroundTrack();
2344 	for (i = 0; i < MAX_RAW_STREAMS; i++)
2345 		S_AL_StreamDie(i);
2346 	S_AL_ClearSoundBuffer();
2347 }
2348 
2349 /*
2350 =================
2351 S_AL_Respatialize
2352 =================
2353 */
2354 static
S_AL_Respatialize(int entityNum,const vec3_t origin,vec3_t axis[3],int inwater)2355 void S_AL_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], int inwater )
2356 {
2357 	float		orientation[6];
2358 	vec3_t	sorigin;
2359 
2360 	VectorCopy( origin, sorigin );
2361 	S_AL_SanitiseVector( sorigin );
2362 
2363 	S_AL_SanitiseVector( axis[ 0 ] );
2364 	S_AL_SanitiseVector( axis[ 1 ] );
2365 	S_AL_SanitiseVector( axis[ 2 ] );
2366 
2367 	orientation[0] = axis[0][0]; orientation[1] = axis[0][1]; orientation[2] = axis[0][2];
2368 	orientation[3] = axis[2][0]; orientation[4] = axis[2][1]; orientation[5] = axis[2][2];
2369 
2370 	lastListenerNumber = entityNum;
2371 	VectorCopy( sorigin, lastListenerOrigin );
2372 
2373 	// Set OpenAL listener paramaters
2374 	qalListenerfv(AL_POSITION, (ALfloat *)sorigin);
2375 	qalListenerfv(AL_VELOCITY, vec3_origin);
2376 	qalListenerfv(AL_ORIENTATION, orientation);
2377 }
2378 
2379 /*
2380 =================
2381 S_AL_Update
2382 =================
2383 */
2384 static
S_AL_Update(void)2385 void S_AL_Update( void )
2386 {
2387 	int i;
2388 
2389 	if(s_muted->modified)
2390 	{
2391 		// muted state changed. Let S_AL_Gain turn up all sources again.
2392 		for(i = 0; i < srcCount; i++)
2393 		{
2394 			if(srcList[i].isActive)
2395 				S_AL_Gain(srcList[i].alSource, srcList[i].scaleGain);
2396 		}
2397 
2398 		s_muted->modified = qfalse;
2399 	}
2400 
2401 	// Update SFX channels
2402 	S_AL_SrcUpdate();
2403 
2404 	// Update streams
2405 	for (i = 0; i < MAX_RAW_STREAMS; i++)
2406 		S_AL_StreamUpdate(i);
2407 	S_AL_MusicUpdate();
2408 
2409 	// Doppler
2410 	if(s_doppler->modified)
2411 	{
2412 		s_alDopplerFactor->modified = qtrue;
2413 		s_doppler->modified = qfalse;
2414 	}
2415 
2416 	// Doppler parameters
2417 	if(s_alDopplerFactor->modified)
2418 	{
2419 		if(s_doppler->integer)
2420 			qalDopplerFactor(s_alDopplerFactor->value);
2421 		else
2422 			qalDopplerFactor(0.0f);
2423 		s_alDopplerFactor->modified = qfalse;
2424 	}
2425 	if(s_alDopplerSpeed->modified)
2426 	{
2427 		qalSpeedOfSound(s_alDopplerSpeed->value);
2428 		s_alDopplerSpeed->modified = qfalse;
2429 	}
2430 
2431 	// Clear the modified flags on the other cvars
2432 	s_alGain->modified = qfalse;
2433 	s_volume->modified = qfalse;
2434 	s_musicVolume->modified = qfalse;
2435 	s_alMinDistance->modified = qfalse;
2436 	s_alRolloff->modified = qfalse;
2437 }
2438 
2439 /*
2440 =================
2441 S_AL_DisableSounds
2442 =================
2443 */
2444 static
S_AL_DisableSounds(void)2445 void S_AL_DisableSounds( void )
2446 {
2447 	S_AL_StopAllSounds();
2448 }
2449 
2450 /*
2451 =================
2452 S_AL_BeginRegistration
2453 =================
2454 */
2455 static
S_AL_BeginRegistration(void)2456 void S_AL_BeginRegistration( void )
2457 {
2458 	if(!numSfx)
2459 		S_AL_BufferInit();
2460 }
2461 
2462 /*
2463 =================
2464 S_AL_SoundList
2465 =================
2466 */
2467 static
S_AL_SoundList(void)2468 void S_AL_SoundList( void )
2469 {
2470 }
2471 
2472 #ifdef USE_VOIP
2473 static
S_AL_StartCapture(void)2474 void S_AL_StartCapture( void )
2475 {
2476 	if (alCaptureDevice != NULL)
2477 		qalcCaptureStart(alCaptureDevice);
2478 }
2479 
2480 static
S_AL_AvailableCaptureSamples(void)2481 int S_AL_AvailableCaptureSamples( void )
2482 {
2483 	int retval = 0;
2484 	if (alCaptureDevice != NULL)
2485 	{
2486 		ALint samples = 0;
2487 		qalcGetIntegerv(alCaptureDevice, ALC_CAPTURE_SAMPLES, sizeof (samples), &samples);
2488 		retval = (int) samples;
2489 	}
2490 	return retval;
2491 }
2492 
2493 static
S_AL_Capture(int samples,byte * data)2494 void S_AL_Capture( int samples, byte *data )
2495 {
2496 	if (alCaptureDevice != NULL)
2497 		qalcCaptureSamples(alCaptureDevice, data, samples);
2498 }
2499 
S_AL_StopCapture(void)2500 void S_AL_StopCapture( void )
2501 {
2502 	if (alCaptureDevice != NULL)
2503 		qalcCaptureStop(alCaptureDevice);
2504 }
2505 
S_AL_MasterGain(float gain)2506 void S_AL_MasterGain( float gain )
2507 {
2508 	qalListenerf(AL_GAIN, gain);
2509 }
2510 #endif
2511 
2512 
2513 /*
2514 =================
2515 S_AL_SoundInfo
2516 =================
2517 */
S_AL_SoundInfo(void)2518 static void S_AL_SoundInfo(void)
2519 {
2520 	Com_Printf( "OpenAL info:\n" );
2521 	Com_Printf( "  Vendor:         %s\n", qalGetString( AL_VENDOR ) );
2522 	Com_Printf( "  Version:        %s\n", qalGetString( AL_VERSION ) );
2523 	Com_Printf( "  Renderer:       %s\n", qalGetString( AL_RENDERER ) );
2524 	Com_Printf( "  AL Extensions:  %s\n", qalGetString( AL_EXTENSIONS ) );
2525 	Com_Printf( "  ALC Extensions: %s\n", qalcGetString( alDevice, ALC_EXTENSIONS ) );
2526 
2527 	if(enumeration_all_ext)
2528 		Com_Printf("  Device:         %s\n", qalcGetString(alDevice, ALC_ALL_DEVICES_SPECIFIER));
2529 	else if(enumeration_ext)
2530 		Com_Printf("  Device:         %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER));
2531 
2532 	if(enumeration_all_ext || enumeration_ext)
2533 		Com_Printf("  Available Devices:\n%s", s_alAvailableDevices->string);
2534 
2535 #ifdef USE_VOIP
2536 	if(capture_ext)
2537 	{
2538 #ifdef __APPLE__
2539 		Com_Printf("  Input Device:   %s\n", qalcGetString(alCaptureDevice, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
2540 #else
2541 		Com_Printf("  Input Device:   %s\n", qalcGetString(alCaptureDevice, ALC_CAPTURE_DEVICE_SPECIFIER));
2542 #endif
2543 		Com_Printf("  Available Input Devices:\n%s", s_alAvailableInputDevices->string);
2544 	}
2545 #endif
2546 }
2547 
2548 
2549 
2550 /*
2551 =================
2552 S_AL_Shutdown
2553 =================
2554 */
2555 static
S_AL_Shutdown(void)2556 void S_AL_Shutdown( void )
2557 {
2558 	// Shut down everything
2559 	int i;
2560 	for (i = 0; i < MAX_RAW_STREAMS; i++)
2561 		S_AL_StreamDie(i);
2562 	S_AL_StopBackgroundTrack( );
2563 	S_AL_SrcShutdown( );
2564 	S_AL_BufferShutdown( );
2565 
2566 	qalcDestroyContext(alContext);
2567 	qalcCloseDevice(alDevice);
2568 
2569 #ifdef USE_VOIP
2570 	if (alCaptureDevice != NULL) {
2571 		qalcCaptureStop(alCaptureDevice);
2572 		qalcCaptureCloseDevice(alCaptureDevice);
2573 		alCaptureDevice = NULL;
2574 		Com_Printf( "OpenAL capture device closed.\n" );
2575 	}
2576 #endif
2577 
2578 	for (i = 0; i < MAX_RAW_STREAMS; i++) {
2579 		streamSourceHandles[i] = -1;
2580 		streamPlaying[i] = qfalse;
2581 		streamSources[i] = 0;
2582 	}
2583 
2584 	QAL_Shutdown();
2585 }
2586 
2587 #endif
2588 
2589 /*
2590 =================
2591 S_AL_Init
2592 =================
2593 */
S_AL_Init(soundInterface_t * si)2594 qboolean S_AL_Init( soundInterface_t *si )
2595 {
2596 #ifdef USE_OPENAL
2597 	const char* device = NULL;
2598 	const char* inputdevice = NULL;
2599 	int i;
2600 
2601 	if( !si ) {
2602 		return qfalse;
2603 	}
2604 
2605 	for (i = 0; i < MAX_RAW_STREAMS; i++) {
2606 		streamSourceHandles[i] = -1;
2607 		streamPlaying[i] = qfalse;
2608 		streamSources[i] = 0;
2609 		streamNumBuffers[i] = 0;
2610 		streamBufIndex[i] = 0;
2611 	}
2612 
2613 	// New console variables
2614 	s_alPrecache = Cvar_Get( "s_alPrecache", "1", CVAR_ARCHIVE );
2615 	s_alGain = Cvar_Get( "s_alGain", "1.0", CVAR_ARCHIVE );
2616 	s_alSources = Cvar_Get( "s_alSources", "128", CVAR_ARCHIVE );
2617 	s_alDopplerFactor = Cvar_Get( "s_alDopplerFactor", "1.0", CVAR_ARCHIVE );
2618 	s_alDopplerSpeed = Cvar_Get( "s_alDopplerSpeed", "9000", CVAR_ARCHIVE );
2619 	s_alMinDistance = Cvar_Get( "s_alMinDistance", "120", CVAR_CHEAT );
2620 	s_alMaxDistance = Cvar_Get("s_alMaxDistance", "1024", CVAR_CHEAT);
2621 	s_alRolloff = Cvar_Get( "s_alRolloff", "2", CVAR_CHEAT);
2622 	s_alGraceDistance = Cvar_Get("s_alGraceDistance", "512", CVAR_CHEAT);
2623 
2624 	s_alDriver = Cvar_Get( "s_alDriver", ALDRIVER_DEFAULT, CVAR_ARCHIVE | CVAR_LATCH | CVAR_PROTECTED );
2625 
2626 	s_alInputDevice = Cvar_Get( "s_alInputDevice", "", CVAR_ARCHIVE | CVAR_LATCH );
2627 	s_alDevice = Cvar_Get("s_alDevice", "", CVAR_ARCHIVE | CVAR_LATCH);
2628 
2629 	// Load QAL
2630 	if( !QAL_Init( s_alDriver->string ) )
2631  	{
2632 #if defined( _WIN32 )
2633 		if( !Q_stricmp( s_alDriver->string, ALDRIVER_DEFAULT ) && !QAL_Init( "OpenAL64.dll" ) ) {
2634 #elif defined ( __APPLE__ )
2635 		if( !Q_stricmp( s_alDriver->string, ALDRIVER_DEFAULT ) && !QAL_Init( "/System/Library/Frameworks/OpenAL.framework/OpenAL" ) ) {
2636 #else
2637 		if( !Q_stricmp( s_alDriver->string, ALDRIVER_DEFAULT ) || !QAL_Init( ALDRIVER_DEFAULT ) ) {
2638 #endif
2639 			return qfalse;
2640 		}
2641  	}
2642 
2643 	device = s_alDevice->string;
2644 	if(device && !*device)
2645 		device = NULL;
2646 
2647 	inputdevice = s_alInputDevice->string;
2648 	if(inputdevice && !*inputdevice)
2649 		inputdevice = NULL;
2650 
2651 
2652 	// Device enumeration support
2653 	enumeration_all_ext = qalcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT");
2654 	enumeration_ext = qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT");
2655 
2656 	if(enumeration_ext || enumeration_all_ext)
2657 	{
2658 		char devicenames[16384] = "";
2659 		const char *devicelist;
2660 #ifdef _WIN32
2661 		const char *defaultdevice;
2662 #endif
2663 		int curlen;
2664 
2665 		// get all available devices + the default device name.
2666 		if(enumeration_all_ext)
2667 		{
2668 			devicelist = qalcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
2669 #ifdef _WIN32
2670 			defaultdevice = qalcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
2671 #endif
2672 		}
2673 		else
2674 		{
2675 			// We don't have ALC_ENUMERATE_ALL_EXT but normal enumeration.
2676 			devicelist = qalcGetString(NULL, ALC_DEVICE_SPECIFIER);
2677 #ifdef _WIN32
2678 			defaultdevice = qalcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
2679 #endif
2680 			enumeration_ext = qtrue;
2681 		}
2682 
2683 #ifdef _WIN32
2684 		// check whether the default device is generic hardware. If it is, change to
2685 		// Generic Software as that one works more reliably with various sound systems.
2686 		// If it's not, use OpenAL's default selection as we don't want to ignore
2687 		// native hardware acceleration.
2688 		if(!device && defaultdevice && !strcmp(defaultdevice, "Generic Hardware"))
2689 			device = "Generic Software";
2690 #endif
2691 
2692 		// dump a list of available devices to a cvar for the user to see.
2693 
2694 		if(devicelist)
2695 		{
2696 			while((curlen = strlen(devicelist)))
2697 			{
2698 				Q_strcat(devicenames, sizeof(devicenames), devicelist);
2699 				Q_strcat(devicenames, sizeof(devicenames), "\n");
2700 
2701 				devicelist += curlen + 1;
2702 			}
2703 		}
2704 
2705 		s_alAvailableDevices = Cvar_Get("s_alAvailableDevices", devicenames, CVAR_ROM | CVAR_NORESTART);
2706 	}
2707 
2708 	alDevice = qalcOpenDevice(device);
2709 	if( !alDevice && device )
2710 	{
2711 		Com_Printf( "Failed to open OpenAL device '%s', trying default.\n", device );
2712 		alDevice = qalcOpenDevice(NULL);
2713 	}
2714 
2715 	if( !alDevice )
2716 	{
2717 		QAL_Shutdown( );
2718 		Com_Printf( "Failed to open OpenAL device.\n" );
2719 		return qfalse;
2720 	}
2721 
2722 	// Create OpenAL context
2723 	alContext = qalcCreateContext( alDevice, NULL );
2724 	if( !alContext )
2725 	{
2726 		QAL_Shutdown( );
2727 		qalcCloseDevice( alDevice );
2728 		Com_Printf( "Failed to create OpenAL context.\n" );
2729 		return qfalse;
2730 	}
2731 	qalcMakeContextCurrent( alContext );
2732 
2733 	// Initialize sources, buffers, music
2734 	S_AL_BufferInit( );
2735 	S_AL_SrcInit( );
2736 
2737 	// Print this for informational purposes
2738 	Com_Printf( "Allocated %d sources.\n", srcCount);
2739 
2740 	// Set up OpenAL parameters (doppler, etc)
2741 	qalDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
2742 	qalDopplerFactor( s_alDopplerFactor->value );
2743 	qalSpeedOfSound( s_alDopplerSpeed->value );
2744 
2745 #ifdef USE_VOIP
2746 	// !!! FIXME: some of these alcCaptureOpenDevice() values should be cvars.
2747 	// !!! FIXME: add support for capture device enumeration.
2748 	// !!! FIXME: add some better error reporting.
2749 	s_alCapture = Cvar_Get( "s_alCapture", "1", CVAR_ARCHIVE | CVAR_LATCH );
2750 	if (!s_alCapture->integer)
2751 	{
2752 		Com_Printf("OpenAL capture support disabled by user ('+set s_alCapture 1' to enable)\n");
2753 	}
2754 #if USE_MUMBLE
2755 	else if (cl_useMumble->integer)
2756 	{
2757 		Com_Printf("OpenAL capture support disabled for Mumble support\n");
2758 	}
2759 #endif
2760 	else
2761 	{
2762 #ifdef __APPLE__
2763 		// !!! FIXME: Apple has a 1.1-compliant OpenAL, which includes
2764 		// !!! FIXME:  capture support, but they don't list it in the
2765 		// !!! FIXME:  extension string. We need to check the version string,
2766 		// !!! FIXME:  then the extension string, but that's too much trouble,
2767 		// !!! FIXME:  so we'll just check the function pointer for now.
2768 		if (qalcCaptureOpenDevice == NULL)
2769 #else
2770 		if (!qalcIsExtensionPresent(NULL, "ALC_EXT_capture"))
2771 #endif
2772 		{
2773 			Com_Printf("No ALC_EXT_capture support, can't record audio.\n");
2774 		}
2775 		else
2776 		{
2777 			char inputdevicenames[16384] = "";
2778 			const char *inputdevicelist;
2779 			const char *defaultinputdevice;
2780 			int curlen;
2781 
2782 			capture_ext = qtrue;
2783 
2784 			// get all available input devices + the default input device name.
2785 			inputdevicelist = qalcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
2786 			defaultinputdevice = qalcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
2787 
2788 			// dump a list of available devices to a cvar for the user to see.
2789 			if (inputdevicelist)
2790 			{
2791 				while((curlen = strlen(inputdevicelist)))
2792 				{
2793 					Q_strcat(inputdevicenames, sizeof(inputdevicenames), inputdevicelist);
2794 					Q_strcat(inputdevicenames, sizeof(inputdevicenames), "\n");
2795 					inputdevicelist += curlen + 1;
2796 				}
2797 			}
2798 
2799 			s_alAvailableInputDevices = Cvar_Get("s_alAvailableInputDevices", inputdevicenames, CVAR_ROM | CVAR_NORESTART);
2800 
2801 			Com_Printf("OpenAL default capture device is '%s'\n", defaultinputdevice ? defaultinputdevice : "none");
2802 			alCaptureDevice = qalcCaptureOpenDevice(inputdevice, 48000, AL_FORMAT_MONO16, VOIP_MAX_PACKET_SAMPLES*4);
2803 			if( !alCaptureDevice && inputdevice )
2804 			{
2805 				Com_Printf( "Failed to open OpenAL Input device '%s', trying default.\n", inputdevice );
2806 				alCaptureDevice = qalcCaptureOpenDevice(NULL, 48000, AL_FORMAT_MONO16, VOIP_MAX_PACKET_SAMPLES*4);
2807 			}
2808 			Com_Printf( "OpenAL capture device %s.\n",
2809 				    (alCaptureDevice == NULL) ? "failed to open" : "opened");
2810 		}
2811 	}
2812 #endif
2813 
2814 	si->Shutdown = S_AL_Shutdown;
2815 	si->StartSound = S_AL_StartSound;
2816 	si->StartSoundEx = S_AL_StartSoundEx;
2817 	si->StartLocalSound = S_AL_StartLocalSound;
2818 	si->StartBackgroundTrack = S_AL_StartBackgroundTrack;
2819 	si->StopBackgroundTrack = S_AL_StopBackgroundTrack;
2820 	si->StartStreamingSound = S_AL_StartStreamingSound;
2821 	si->GetVoiceAmplitude = S_AL_GetVoiceAmplitude;
2822 	si->RawSamples = S_AL_RawSamples;
2823 	si->StopAllSounds = S_AL_StopAllSounds;
2824 	si->ClearLoopingSounds = S_AL_ClearLoopingSounds;
2825 	si->AddLoopingSound = S_AL_AddLoopingSound;
2826 	si->AddRealLoopingSound = S_AL_AddRealLoopingSound;
2827 	si->StopLoopingSound = S_AL_StopLoopingSound;
2828 	si->Respatialize = S_AL_Respatialize;
2829 	si->UpdateEntityPosition = S_AL_UpdateEntityPosition;
2830 	si->Update = S_AL_Update;
2831 	si->DisableSounds = S_AL_DisableSounds;
2832 	si->BeginRegistration = S_AL_BeginRegistration;
2833 	si->RegisterSound = S_AL_RegisterSound;
2834 	si->ClearSoundBuffer = S_AL_ClearSoundBuffer;
2835 	si->SoundInfo = S_AL_SoundInfo;
2836 	si->SoundList = S_AL_SoundList;
2837 
2838 #ifdef USE_VOIP
2839 	si->StartCapture = S_AL_StartCapture;
2840 	si->AvailableCaptureSamples = S_AL_AvailableCaptureSamples;
2841 	si->Capture = S_AL_Capture;
2842 	si->StopCapture = S_AL_StopCapture;
2843 	si->MasterGain = S_AL_MasterGain;
2844 #endif
2845 
2846 	return qtrue;
2847 #else
2848 	return qfalse;
2849 #endif
2850 }
2851 
2852