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