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