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