1 /*
2 This file is part of Warzone 2100.
3 Copyright (C) 1999-2004 Eidos Interactive
4 Copyright (C) 2005-2020 Warzone 2100 Project
5
6 Warzone 2100 is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Warzone 2100 is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Warzone 2100; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #include "lib/framework/frame.h"
21 #include "lib/framework/frameresource.h"
22 #include "lib/framework/math_ext.h"
23 #include "lib/gamelib/gtime.h"
24 #include "lib/ivis_opengl/pietypes.h"
25 #include "lib/framework/physfs_ext.h"
26
27 #include "tracklib.h"
28 #include "aud.h"
29 #include "audio.h"
30 #include "audio_id.h"
31 #include "openal_error.h"
32 #include "mixer.h"
33 // defines
34 #define NO_SAMPLE - 2
35 #define MAX_SAME_SAMPLES 2
36
37 // global variables
38 static AUDIO_SAMPLE *g_psSampleList = nullptr;
39 static AUDIO_SAMPLE *g_psSampleQueue = nullptr;
40 static bool g_bAudioEnabled = false;
41 static bool g_bAudioPaused = false;
42 static AUDIO_SAMPLE g_sPreviousSample;
43 static int g_iPreviousSampleTime = 0;
44
45 /** Counts the number of samples in the SampleQueue
46 * \return the number of samples in the SampleQueue
47 */
audio_GetSampleQueueCount()48 unsigned int audio_GetSampleQueueCount()
49 {
50 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
51 AUDIO_SAMPLE *psSample = nullptr;
52 unsigned int count = 0;
53 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54
55 // loop through SampleQueue to count how many we have.
56 psSample = g_psSampleQueue;
57 while (psSample != nullptr)
58 {
59 count++;
60 psSample = psSample->psNext;
61 }
62
63 return count;
64 }
65
66 /** Counts the number of samples in the SampleList
67 * \return the number of samples in the SampleList
68 */
audio_GetSampleListCount()69 unsigned int audio_GetSampleListCount()
70 {
71 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72 AUDIO_SAMPLE *psSample = nullptr;
73 unsigned int count = 0;
74 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75
76 // loop through SampleList to count how many we have.
77 psSample = g_psSampleList;
78 while (psSample != nullptr)
79 {
80 ++count;
81 psSample = psSample->psNext;
82 }
83
84 return count;
85 }
86 //*
87 // =======================================================================================================================
88 // =======================================================================================================================
89 //
audio_Disabled(void)90 bool audio_Disabled(void)
91 {
92 return !g_bAudioEnabled;
93 }
94
95 //*
96 // =======================================================================================================================
97 // =======================================================================================================================
98 //
audio_Init(AUDIO_CALLBACK pStopTrackCallback,HRTFMode hrtf,bool really_enable)99 bool audio_Init(AUDIO_CALLBACK pStopTrackCallback, HRTFMode hrtf, bool really_enable)
100 {
101 // init audio system
102 g_sPreviousSample.iTrack = NO_SAMPLE;
103 g_sPreviousSample.x = SAMPLE_COORD_INVALID;
104 g_sPreviousSample.y = SAMPLE_COORD_INVALID;
105 g_sPreviousSample.z = SAMPLE_COORD_INVALID;
106 g_bAudioEnabled = really_enable;
107 if (g_bAudioEnabled)
108 {
109 g_bAudioEnabled = sound_Init(hrtf);
110 }
111 if (g_bAudioEnabled)
112 {
113 sound_SetStoppedCallback(pStopTrackCallback);
114 }
115 return g_bAudioEnabled;
116 }
117
118 //*
119 // =======================================================================================================================
120 // =======================================================================================================================
121 //
audio_Shutdown(void)122 bool audio_Shutdown(void)
123 {
124 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
125 AUDIO_SAMPLE *psSample = nullptr, *psSampleTemp = nullptr;
126 bool bOK;
127 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
128
129 // if audio not enabled return true to carry on game without audio
130 if (g_bAudioEnabled == false)
131 {
132 return true;
133 }
134
135 sound_StopAll();
136 bOK = sound_Shutdown();
137
138 // empty sample list
139 psSample = g_psSampleList;
140 while (psSample != nullptr)
141 {
142 psSampleTemp = psSample->psNext;
143 free(psSample);
144 psSample = psSampleTemp;
145 }
146
147 // empty sample queue
148 psSample = g_psSampleQueue;
149 while (psSample != nullptr)
150 {
151 psSampleTemp = psSample->psNext;
152 free(psSample);
153 psSample = psSampleTemp;
154 }
155
156 // free sample heap
157 g_psSampleList = nullptr;
158 g_psSampleQueue = nullptr;
159
160 return bOK;
161 }
162
163 //*
164 // =======================================================================================================================
165 // =======================================================================================================================
166 //
audio_GetPreviousQueueTrackPos(SDWORD * iX,SDWORD * iY,SDWORD * iZ)167 bool audio_GetPreviousQueueTrackPos(SDWORD *iX, SDWORD *iY, SDWORD *iZ)
168 {
169 if (g_sPreviousSample.x == SAMPLE_COORD_INVALID
170 || g_sPreviousSample.y == SAMPLE_COORD_INVALID
171 || g_sPreviousSample.z == SAMPLE_COORD_INVALID)
172 {
173 return false;
174 }
175
176 *iX = g_sPreviousSample.x;
177 *iY = g_sPreviousSample.y;
178 *iZ = g_sPreviousSample.z;
179
180 return true;
181 }
182
audio_GetPreviousQueueTrackRadarBlipPos(SDWORD * iX,SDWORD * iY)183 bool audio_GetPreviousQueueTrackRadarBlipPos(SDWORD *iX, SDWORD *iY)
184 {
185 if (g_sPreviousSample.x == SAMPLE_COORD_INVALID
186 || g_sPreviousSample.y == SAMPLE_COORD_INVALID)
187 {
188 return false;
189 }
190
191 if (g_sPreviousSample.iTrack != ID_SOUND_STRUCTURE_UNDER_ATTACK && g_sPreviousSample.iTrack != ID_SOUND_UNIT_UNDER_ATTACK &&
192 g_sPreviousSample.iTrack != ID_SOUND_LASER_SATELLITE_FIRING && g_sPreviousSample.iTrack != ID_SOUND_INCOMING_LASER_SAT_STRIKE)
193 {
194 return false;
195 }
196
197 if (realTime > g_iPreviousSampleTime + 5 * GAME_TICKS_PER_SEC)
198 {
199 return false;
200 }
201
202 *iX = g_sPreviousSample.x;
203 *iY = g_sPreviousSample.y;
204
205 return true;
206 }
207
208 //*
209 // =======================================================================================================================
210 // =======================================================================================================================
211 //
audio_AddSampleToHead(AUDIO_SAMPLE ** ppsSampleList,AUDIO_SAMPLE * psSample)212 static void audio_AddSampleToHead(AUDIO_SAMPLE **ppsSampleList, AUDIO_SAMPLE *psSample)
213 {
214 psSample->psNext = (*ppsSampleList);
215 psSample->psPrev = nullptr;
216 if ((*ppsSampleList) != nullptr)
217 {
218 (*ppsSampleList)->psPrev = psSample;
219 }
220 (*ppsSampleList) = psSample;
221 }
222
223 //*
224 // =======================================================================================================================
225 // =======================================================================================================================
226 //
audio_AddSampleToTail(AUDIO_SAMPLE ** ppsSampleList,AUDIO_SAMPLE * psSample)227 static void audio_AddSampleToTail(AUDIO_SAMPLE **ppsSampleList, AUDIO_SAMPLE *psSample)
228 {
229 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
230 AUDIO_SAMPLE *psSampleTail = nullptr;
231 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
232
233 if ((*ppsSampleList) == nullptr)
234 {
235 (*ppsSampleList) = psSample;
236 return;
237 }
238
239 psSampleTail = (*ppsSampleList);
240 while (psSampleTail->psNext != nullptr)
241 {
242 psSampleTail = psSampleTail->psNext;
243 }
244
245 psSampleTail->psNext = psSample;
246 psSample->psPrev = psSampleTail;
247 psSample->psNext = nullptr;
248 }
249
250 //*
251 //
252 // audio_RemoveSample Removes sample from list but doesn't free its memory
253
254 //*
255 // =======================================================================================================================
256 // =======================================================================================================================
257 //
audio_RemoveSample(AUDIO_SAMPLE ** ppsSampleList,AUDIO_SAMPLE * psSample)258 static void audio_RemoveSample(AUDIO_SAMPLE **ppsSampleList, AUDIO_SAMPLE *psSample)
259 {
260 if (psSample == nullptr)
261 {
262 return;
263 }
264
265 if (psSample == (*ppsSampleList))
266 {
267 // first sample in list
268 (*ppsSampleList) = psSample->psNext;
269 }
270
271 if (psSample->psPrev != nullptr)
272 {
273 psSample->psPrev->psNext = psSample->psNext;
274 }
275
276 if (psSample->psNext != nullptr)
277 {
278 psSample->psNext->psPrev = psSample->psPrev;
279 }
280
281 // set sample pointers NULL for safety
282 psSample->psPrev = nullptr;
283 psSample->psNext = nullptr;
284 }
285
286 //*
287 // =======================================================================================================================
288 // =======================================================================================================================
289 //
audio_CheckSameQueueTracksPlaying(SDWORD iTrack)290 static bool audio_CheckSameQueueTracksPlaying(SDWORD iTrack)
291 {
292 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
293 SDWORD iCount;
294 AUDIO_SAMPLE *psSample = nullptr;
295 bool bOK = true;
296 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
297
298 // return if audio not enabled
299 if (g_bAudioEnabled == false || g_bAudioPaused == true)
300 {
301 return true;
302 }
303
304 iCount = 0;
305
306 // loop through queue sounds and check whether too many already in it
307 psSample = g_psSampleQueue;
308 while (psSample != nullptr)
309 {
310 if (psSample->iTrack == iTrack)
311 {
312 iCount++;
313 }
314
315 if (iCount > MAX_SAME_SAMPLES)
316 {
317 bOK = false;
318 break;
319 }
320
321 psSample = psSample->psNext;
322 }
323
324 return bOK;
325 }
326
327 //*
328 // =======================================================================================================================
329 // =======================================================================================================================
330 //
audio_QueueSample(SDWORD iTrack)331 static AUDIO_SAMPLE *audio_QueueSample(SDWORD iTrack)
332 {
333 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
334 AUDIO_SAMPLE *psSample = nullptr;
335 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
336
337 // return if audio not enabled
338 if (g_bAudioEnabled == false || g_bAudioPaused == true)
339 {
340 return nullptr;
341 }
342
343 ASSERT(sound_CheckTrack(iTrack) == true, "audio_QueueSample: track %i outside limits\n", iTrack);
344
345 // reject track if too many of same ID already in queue
346 if (audio_CheckSameQueueTracksPlaying(iTrack) == false)
347 {
348 return nullptr;
349 }
350
351 psSample = (AUDIO_SAMPLE *)calloc(1, sizeof(AUDIO_SAMPLE));
352 if (psSample == nullptr)
353 {
354 debug(LOG_ERROR, "audio_QueueSample: Out of memory");
355 return nullptr;
356 }
357
358 psSample->iTrack = iTrack;
359 psSample->x = SAMPLE_COORD_INVALID;
360 psSample->y = SAMPLE_COORD_INVALID;
361 psSample->z = SAMPLE_COORD_INVALID;
362 psSample->bFinishedPlaying = false;
363
364 // add to queue
365 audio_AddSampleToTail(&g_psSampleQueue, psSample);
366
367 return psSample;
368 }
369
370 //*
371 // =======================================================================================================================
372 // =======================================================================================================================
373 //
audio_QueueTrack(SDWORD iTrack)374 void audio_QueueTrack(SDWORD iTrack)
375 {
376 // return if audio not enabled
377 if (g_bAudioEnabled == false || g_bAudioPaused == true)
378 {
379 return;
380 }
381
382 audio_QueueSample(iTrack);
383 return;
384 }
385
386 //*
387 //
388 //
389 // * audio_QueueTrackMinDelay Will only play track if iMinDelay has elapsed since
390 // * track last finished
391 //
392
393 //*
394 // =======================================================================================================================
395 // =======================================================================================================================
396 //
audio_QueueTrackMinDelay(SDWORD iTrack,UDWORD iMinDelay)397 void audio_QueueTrackMinDelay(SDWORD iTrack, UDWORD iMinDelay)
398 {
399 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
400 AUDIO_SAMPLE *psSample;
401 UDWORD iDelay;
402 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
403
404 // return if audio not enabled
405 if (g_bAudioEnabled == false || g_bAudioPaused == true)
406 {
407 return;
408 }
409
410 // Determine if at least iMinDelay time has passed since the last time this track was played
411 iDelay = sound_GetGameTime() - sound_GetTrackTimeLastFinished(iTrack);
412 if (!(iDelay > iMinDelay))
413 {
414 return;
415 }
416
417 // Construct an audio sample from requested track
418 psSample = audio_QueueSample(iTrack);
419 if (psSample == nullptr)
420 {
421 return;
422 }
423
424 // Set last finished tracktime to current time to prevent parallel playing of this track
425 sound_SetTrackTimeLastFinished(iTrack, sound_GetGameTime());
426 }
427
428 //*
429 // =======================================================================================================================
430 // =======================================================================================================================
431 //
audio_QueueTrackMinDelayPos(SDWORD iTrack,UDWORD iMinDelay,SDWORD iX,SDWORD iY,SDWORD iZ)432 void audio_QueueTrackMinDelayPos(SDWORD iTrack, UDWORD iMinDelay, SDWORD iX, SDWORD iY, SDWORD iZ)
433 {
434 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
435 AUDIO_SAMPLE *psSample;
436 UDWORD iDelay;
437 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
438
439 // return if audio not enabled
440 if (g_bAudioEnabled == false || g_bAudioPaused == true)
441 {
442 return;
443 }
444
445 // Determine if at least iMinDelay time has passed since the last time this track was played
446 iDelay = sound_GetGameTime() - sound_GetTrackTimeLastFinished(iTrack);
447 if (iDelay > iMinDelay)
448 {
449 return;
450 }
451
452 // Construct an audio sample from requested track
453 psSample = audio_QueueSample(iTrack);
454 if (psSample == nullptr)
455 {
456 return;
457 }
458
459 psSample->x = iX;
460 psSample->y = iY;
461 psSample->z = iZ;
462
463 // Set last finished tracktime to current time to prevent parallel playing of this track
464 sound_SetTrackTimeLastFinished(iTrack, sound_GetGameTime());
465 }
466
467 //*
468 // =======================================================================================================================
469 // =======================================================================================================================
470 //
audio_QueueTrackPos(SDWORD iTrack,SDWORD iX,SDWORD iY,SDWORD iZ)471 void audio_QueueTrackPos(SDWORD iTrack, SDWORD iX, SDWORD iY, SDWORD iZ)
472 {
473 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
474 AUDIO_SAMPLE *psSample;
475 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
476
477 // return if audio not enabled
478 if (g_bAudioEnabled == false || g_bAudioPaused == true)
479 {
480 return;
481 }
482
483 // Construct an audio sample from requested track
484 psSample = audio_QueueSample(iTrack);
485 if (psSample == nullptr)
486 {
487 return;
488 }
489
490 psSample->x = iX;
491 psSample->y = iY;
492 psSample->z = iZ;
493 }
494
495 //*
496 // =======================================================================================================================
497 // =======================================================================================================================
498 //
audio_UpdateQueue(void)499 static void audio_UpdateQueue(void)
500 {
501 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
502 AUDIO_SAMPLE *psSample;
503 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
504
505 // return if audio not enabled
506 if (g_bAudioEnabled == false || g_bAudioPaused == true)
507 {
508 return;
509 }
510
511 if (sound_QueueSamplePlaying() == true)
512 {
513 return;
514 }
515
516 // check queue for members
517 if (g_psSampleQueue == nullptr)
518 {
519 return;
520 }
521
522 // remove queue head
523 psSample = g_psSampleQueue;
524 audio_RemoveSample(&g_psSampleQueue, psSample);
525
526 // add sample to list if able to play
527 if (!sound_Play2DTrack(psSample, true))
528 {
529 debug(LOG_NEVER, "audio_UpdateQueue: couldn't play sample\n");
530 free(psSample);
531 return;
532 }
533
534 audio_AddSampleToHead(&g_psSampleList, psSample);
535
536 // update last queue sound coords
537 if (psSample->x != SAMPLE_COORD_INVALID && psSample->y != SAMPLE_COORD_INVALID
538 && psSample->z != SAMPLE_COORD_INVALID)
539 {
540 g_sPreviousSample.x = psSample->x;
541 g_sPreviousSample.y = psSample->y;
542 g_sPreviousSample.z = psSample->z;
543 g_sPreviousSample.iTrack = psSample->iTrack;
544 g_iPreviousSampleTime = realTime;
545 }
546 }
547
audio_Update()548 void audio_Update()
549 {
550 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
551 Vector3f playerPos;
552 float angle;
553 AUDIO_SAMPLE *psSample, *psSampleTemp;
554 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
555
556 // if audio not enabled return true to carry on game without audio
557 if (g_bAudioEnabled == false)
558 {
559 return;
560 }
561
562 alGetError(); // clear error codes
563 audio_UpdateQueue();
564 alGetError(); // clear error codes
565
566 // get player position
567 playerPos = audio_GetPlayerPos();
568 audio_Get3DPlayerRotAboutVerticalAxis(&angle);
569 sound_SetPlayerPos(playerPos);
570 sound_SetPlayerOrientation(angle);
571
572 // loop through 3D sounds and remove if finished or update position
573 psSample = g_psSampleList;
574 while (psSample != nullptr)
575 {
576 // remove finished samples from list
577 if (psSample->bFinishedPlaying == true)
578 {
579 psSampleTemp = psSample->psNext;
580 audio_RemoveSample(&g_psSampleList, psSample);
581 free(psSample);
582 psSample = psSampleTemp;
583 }
584
585 // check looping sound callbacks for finished condition
586 else
587 {
588 if (psSample->psObj != nullptr)
589 {
590 if (audio_ObjectDead(psSample->psObj)
591 || (psSample->pCallback != nullptr && psSample->pCallback(psSample->psObj) == false))
592 {
593 sound_StopTrack(psSample);
594 psSample->psObj = nullptr;
595 }
596 else // update sample position
597 {
598 audio_GetObjectPos(psSample->psObj, &psSample->x, &psSample->y, &psSample->z);
599 sound_SetObjectPosition(psSample);
600 }
601 }
602 // next sample
603 psSample = psSample->psNext;
604 }
605 }
606
607 sound_Update();
608 return;
609 }
610
611
612 /** Retrieves loaded audio files and retrieves a TRACK from them on which some values are set and returns their respective ID numbers
613 * \param fileName the filename of the track
614 * \param loop whether the track should be looped until explicitly stopped
615 * \param volume the volume this track should be played on (range is 0-100)
616 * \param audibleRadius the radius from the source of sound where it can be heard
617 * \return a non-zero value when successful or audio is disabled, zero when the file is not found or no more tracks can be loaded (i.e. the limit is reached)
618 */
audio_SetTrackVals(const char * fileName,bool loop,unsigned int volume,unsigned int audibleRadius)619 unsigned int audio_SetTrackVals(const char *fileName, bool loop, unsigned int volume, unsigned int audibleRadius)
620 {
621 // if audio not enabled return a random non-zero value to carry on game without audio
622 if (g_bAudioEnabled == false)
623 {
624 return 1;
625 }
626
627 return sound_SetTrackVals(fileName, loop, volume, audibleRadius);
628 }
629
630 //*
631 //
632 //
633 // * audio_CheckSame3DTracksPlaying Reject samples if too many already playing in
634 // * same area
635 //
636
637 //*
638 // =======================================================================================================================
639 // =======================================================================================================================
640 //
audio_CheckSame3DTracksPlaying(SDWORD iTrack,SDWORD iX,SDWORD iY,SDWORD iZ)641 static bool audio_CheckSame3DTracksPlaying(SDWORD iTrack, SDWORD iX, SDWORD iY, SDWORD iZ)
642 {
643 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
644 SDWORD iCount, iDx, iDy, iDz, iDistSq, iMaxDistSq, iRad;
645 AUDIO_SAMPLE *psSample = nullptr;
646 bool bOK = true;
647 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
648
649 // return if audio not enabled
650 if (g_bAudioEnabled == false || g_bAudioPaused == true)
651 {
652 return true;
653 }
654
655 iCount = 0;
656
657 // loop through 3D sounds and check whether too many already in earshot
658 psSample = g_psSampleList;
659 while (psSample != nullptr)
660 {
661 if (psSample->iTrack == iTrack)
662 {
663 iDx = iX - psSample->x;
664 iDy = iY - psSample->y;
665 iDz = iZ - psSample->z;
666 iDistSq = (iDx * iDx) + (iDy * iDy) + (iDz * iDz);
667 iRad = sound_GetTrackAudibleRadius(iTrack);
668 iMaxDistSq = iRad * iRad;
669 if (iDistSq < iMaxDistSq)
670 {
671 iCount++;
672 }
673
674 if (iCount > MAX_SAME_SAMPLES)
675 {
676 bOK = false;
677 break;
678 }
679 }
680
681 psSample = psSample->psNext;
682 }
683
684 return bOK;
685 }
686
687 //*
688 // =======================================================================================================================
689 // =======================================================================================================================
690 //
audio_Play3DTrack(SDWORD iX,SDWORD iY,SDWORD iZ,int iTrack,SIMPLE_OBJECT * psObj,AUDIO_CALLBACK pUserCallback)691 static bool audio_Play3DTrack(SDWORD iX, SDWORD iY, SDWORD iZ, int iTrack, SIMPLE_OBJECT *psObj, AUDIO_CALLBACK pUserCallback)
692 {
693 AUDIO_SAMPLE *psSample;
694 // coordinates
695 float listenerX = .0f, listenerY = .0f, listenerZ = .0f, dX, dY, dZ;
696 // calculation results
697 float distance, gain;
698 ALenum err;
699
700 // if audio not enabled return true to carry on game without audio
701 if (g_bAudioEnabled == false || g_bAudioPaused == true)
702 {
703 return false;
704 }
705
706 if (audio_CheckSame3DTracksPlaying(iTrack, iX, iY, iZ) == false)
707 {
708 return false;
709 }
710
711 // compute distance
712 // NOTE, if this call fails, expect garbage
713 alGetListener3f(AL_POSITION, &listenerX, &listenerY, &listenerZ);
714 err = sound_GetError();
715 if (err != AL_NO_ERROR)
716 {
717 return false;
718 }
719 dX = (float)iX - listenerX; // distances on all axis
720 dY = (float)iY - listenerY;
721 dZ = (float)iZ - listenerZ;
722 distance = sqrtf(dX * dX + dY * dY + dZ * dZ); // Pythagorean theorem
723
724 // compute gain
725 gain = (1.0 - (distance * ATTENUATION_FACTOR)) ;//* 1.0f * sfx3d_volume
726 if (gain > 1.0f)
727 {
728 gain = 1.0f;
729 }
730 if (gain < 0.0f)
731 {
732 gain = 0.0f;
733 }
734 // don't bother adding samples that we can't hear
735 if (gain == 0.0f)
736 {
737 return false;
738 }
739
740 psSample = (AUDIO_SAMPLE *)malloc(sizeof(AUDIO_SAMPLE));
741 if (psSample == nullptr)
742 {
743 debug(LOG_ERROR, "audio_Play3DTrack: Out of memory");
744 return false;
745 }
746
747 // setup sample
748 memset(psSample, 0, sizeof(AUDIO_SAMPLE)); // [check] -Q
749 psSample->iTrack = iTrack;
750 psSample->x = iX;
751 psSample->y = iY;
752 psSample->z = iZ;
753 psSample->bFinishedPlaying = false;
754 psSample->psObj = psObj;
755 psSample->pCallback = pUserCallback;
756
757 // add sample to list if able to play
758 if (!sound_Play3DTrack(psSample))
759 {
760 debug(LOG_NEVER, "audio_Play3DTrack: couldn't play sample\n");
761 free(psSample);
762 return false;
763 }
764
765 audio_AddSampleToHead(&g_psSampleList, psSample);
766 return true;
767 }
768
769 //*
770 // =======================================================================================================================
771 // =======================================================================================================================
772 //
audio_PlayStaticTrack(SDWORD iMapX,SDWORD iMapY,int iTrack)773 bool audio_PlayStaticTrack(SDWORD iMapX, SDWORD iMapY, int iTrack)
774 {
775 //~~~~~~~~~~~~~~~
776 SDWORD iX, iY, iZ;
777 //~~~~~~~~~~~~~~~
778
779 // if audio not enabled return true to carry on game without audio
780 if (g_bAudioEnabled == false)
781 {
782 return false;
783 }
784
785 audio_GetStaticPos(iMapX, iMapY, &iX, &iY, &iZ);
786 return audio_Play3DTrack(iX, iY, iZ, iTrack, nullptr, nullptr);
787 }
788
789 //*
790 // =======================================================================================================================
791 // =======================================================================================================================
792 //
audio_PlayObjStaticTrack(SIMPLE_OBJECT * psObj,int iTrack)793 bool audio_PlayObjStaticTrack(SIMPLE_OBJECT *psObj, int iTrack)
794 {
795 //~~~~~~~~~~~~~~~
796 SDWORD iX, iY, iZ;
797 //~~~~~~~~~~~~~~~
798
799 // if audio not enabled return true to carry on game without audio
800 if (g_bAudioEnabled == false)
801 {
802 return false;
803 }
804
805 audio_GetObjectPos(psObj, &iX, &iY, &iZ);
806 return audio_Play3DTrack(iX, iY, iZ, iTrack, psObj, nullptr);
807 }
808
809 //*
810 // =======================================================================================================================
811 // =======================================================================================================================
812 //
audio_PlayObjStaticTrackCallback(SIMPLE_OBJECT * psObj,int iTrack,AUDIO_CALLBACK pUserCallback)813 bool audio_PlayObjStaticTrackCallback(SIMPLE_OBJECT *psObj, int iTrack, AUDIO_CALLBACK pUserCallback)
814 {
815 //~~~~~~~~~~~~~~~
816 SDWORD iX, iY, iZ;
817 //~~~~~~~~~~~~~~~
818
819 // if audio not enabled return true to carry on game without audio
820 if (g_bAudioEnabled == false)
821 {
822 return false;
823 }
824
825 audio_GetObjectPos(psObj, &iX, &iY, &iZ);
826 return audio_Play3DTrack(iX, iY, iZ, iTrack, psObj, pUserCallback);
827 }
828
829 //*
830 // =======================================================================================================================
831 // =======================================================================================================================
832 //
audio_PlayObjDynamicTrack(SIMPLE_OBJECT * psObj,int iTrack,AUDIO_CALLBACK pUserCallback)833 bool audio_PlayObjDynamicTrack(SIMPLE_OBJECT *psObj, int iTrack, AUDIO_CALLBACK pUserCallback)
834 {
835 //~~~~~~~~~~~~~~~
836 SDWORD iX, iY, iZ;
837 //~~~~~~~~~~~~~~~
838
839 // if audio not enabled return true to carry on game without audio
840 if (g_bAudioEnabled == false)
841 {
842 return false;
843 }
844
845 audio_GetObjectPos(psObj, &iX, &iY, &iZ);
846 return audio_Play3DTrack(iX, iY, iZ, iTrack, psObj, pUserCallback);
847 }
848
849 /** Plays the given audio file as a stream and reports back when it has finished
850 * playing.
851 * \param fileName the (OggVorbis) file to play from
852 * \param volume the volume to use while playing this file (in the range of
853 * 0.0 - 1.0)
854 * \param onFinished a callback function to invoke when playing of this stream
855 * has been finished. You can use NULL to specifiy no callback function.
856 * \param user_data a pointer to contain some user data to pass along to the
857 * finished callback.
858 * \return a pointer to the currently playing stream when the stream is playing
859 * (and as such the callback will be invoked some time in the future),
860 * NULL when the stream didn't start playing (and the callback won't be
861 * invoked).
862 * \note The returned pointer will become invalid/dangling immediately after
863 * the \c onFinished callback is invoked.
864 * \note You must _never_ manually free() the memory used by the returned
865 * pointer.
866 */
audio_PlayStream(const char * fileName,float volume,void (* onFinished)(const void *),const void * user_data)867 AUDIO_STREAM *audio_PlayStream(const char *fileName, float volume, void (*onFinished)(const void *), const void *user_data)
868 {
869 PHYSFS_file *fileHandle;
870 AUDIO_STREAM *stream;
871
872 // If audio is not enabled return false to indicate that the given callback
873 // will not be invoked.
874 if (g_bAudioEnabled == false)
875 {
876 return nullptr;
877 }
878
879 // Open up the file
880 fileHandle = PHYSFS_openRead(fileName);
881 debug(LOG_WZ, "Reading...[directory: %s] %s", WZ_PHYSFS_getRealDir_String(fileName).c_str(), fileName);
882 if (fileHandle == nullptr)
883 {
884 debug(LOG_ERROR, "sound_LoadTrackFromFile: PHYSFS_openRead(\"%s\") failed with error: %s\n", fileName, WZ_PHYSFS_getLastError());
885 return nullptr;
886 }
887
888 stream = sound_PlayStream(fileHandle, volume, onFinished, user_data);
889 if (stream == nullptr)
890 {
891 PHYSFS_close(fileHandle);
892 return nullptr;
893 }
894
895 return stream;
896 }
897
898 //*
899 // =======================================================================================================================
900 // =======================================================================================================================
901 //
audio_StopObjTrack(SIMPLE_OBJECT * psObj,int iTrack)902 void audio_StopObjTrack(SIMPLE_OBJECT *psObj, int iTrack)
903 {
904 //~~~~~~~~~~~~~~~~~~~~~~
905 AUDIO_SAMPLE *psSample;
906 //~~~~~~~~~~~~~~~~~~~~~~
907
908 // return if audio not enabled
909 if (g_bAudioEnabled == false)
910 {
911 return;
912 }
913
914 // find sample
915 psSample = g_psSampleList;
916 while (psSample != nullptr)
917 {
918 // If track has been found stop it and return
919 if (psSample->psObj == psObj && psSample->iTrack == iTrack)
920 {
921 sound_StopTrack(psSample);
922 return;
923 }
924
925 // get next sample from linked list
926 psSample = psSample->psNext;
927 }
928 }
929
930 //*
931 //
932 // audio_PlayTrack Play immediate 2D FX track
933
934 //*
935 // =======================================================================================================================
936 // =======================================================================================================================
937 //
audio_PlayTrack(int iTrack)938 void audio_PlayTrack(int iTrack)
939 {
940 //~~~~~~~~~~~~~~~~~~~~~~
941 AUDIO_SAMPLE *psSample;
942 //~~~~~~~~~~~~~~~~~~~~~~
943
944 // return if audio not enabled
945 if (g_bAudioEnabled == false || g_bAudioPaused == true)
946 {
947 return;
948 }
949
950 // Allocate a sample
951 psSample = (AUDIO_SAMPLE *)malloc(sizeof(AUDIO_SAMPLE));
952 if (psSample == nullptr)
953 {
954 debug(LOG_ERROR, "audio_PlayTrack: Out of memory");
955 return;
956 }
957
958 // setup/initialize sample
959 psSample->iTrack = iTrack;
960 psSample->bFinishedPlaying = false;
961
962 // Zero callback stuff since we don't need/want it
963 psSample->pCallback = nullptr;
964 psSample->psObj = nullptr;
965
966 /* iSample, psPrev, and psNext will be initialized by the
967 * following functions, and x, y and z will be completely
968 * ignored so we don't need to bother about it
969 */
970
971 // add sample to list if able to play
972 if (!sound_Play2DTrack(psSample, false))
973 {
974 debug(LOG_NEVER, "audio_PlayTrack: couldn't play sample\n");
975 free(psSample);
976 return;
977 }
978
979 audio_AddSampleToHead(&g_psSampleList, psSample);
980 }
981
982 //*
983 // =======================================================================================================================
984 // =======================================================================================================================
985 //
audio_PauseAll(void)986 void audio_PauseAll(void)
987 {
988 // return if audio not enabled
989 if (g_bAudioEnabled == false)
990 {
991 return;
992 }
993
994 g_bAudioPaused = true;
995 sound_PauseAll();
996 }
997
998 //*
999 // =======================================================================================================================
1000 // =======================================================================================================================
1001 //
audio_ResumeAll(void)1002 void audio_ResumeAll(void)
1003 {
1004 // return if audio not enabled
1005 if (g_bAudioEnabled == false)
1006 {
1007 return;
1008 }
1009
1010 g_bAudioPaused = false;
1011 sound_ResumeAll();
1012 }
1013
1014 //*
1015 // =======================================================================================================================
1016 // =======================================================================================================================
1017 //
audio_StopAll(void)1018 void audio_StopAll(void)
1019 {
1020 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1021 AUDIO_SAMPLE *psSample;
1022 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1023
1024 // return if audio not enabled
1025 if (g_bAudioEnabled == false)
1026 {
1027 return;
1028 }
1029
1030 //
1031 // * empty list - audio_Update will free samples because callbacks have to come in first
1032 //
1033 for (psSample = g_psSampleList; psSample != nullptr; psSample = psSample->psNext)
1034 {
1035 // Stop this sound sample
1036 sound_StopTrack(psSample);
1037
1038 // HACK:
1039 // Make sure to set psObj to NULL, since sometimes it becomes
1040 // invalidated, i.e. dangling, before audio_Update() gets the
1041 // chance to clean it up.
1042 //
1043 // Meaning at this place in the code audio_ObjectDead(psObj)
1044 // would indicate that psObj is still valid and will remain
1045 // such. While in reality, before audio_Update() can get the
1046 // chance to check again, this pointer will have become
1047 // dangling.
1048 //
1049 // NOTE: This would always happen when audio_StopAll() had been
1050 // invoked by stageThreeShutDown().
1051 psSample->psObj = nullptr;
1052 }
1053
1054 // empty sample queue
1055 psSample = g_psSampleQueue;
1056 while (psSample != nullptr)
1057 {
1058 AUDIO_SAMPLE *psSampleTemp = psSample;
1059
1060 // Advance the sample iterator (before invalidating it)
1061 psSample = psSample->psNext;
1062
1063 // Destroy the sample
1064 free(psSampleTemp);
1065 }
1066
1067 g_psSampleQueue = nullptr;
1068 }
1069
1070 //*
1071 // =======================================================================================================================
1072 // =======================================================================================================================
1073 //
audio_GetTrackID(const char * fileName)1074 SDWORD audio_GetTrackID(const char *fileName)
1075 {
1076 //~~~~~~~~~~~~~
1077 TRACK *psTrack;
1078 //~~~~~~~~~~~~~
1079
1080 // return if audio not enabled
1081 if (g_bAudioEnabled == false)
1082 {
1083 return SAMPLE_NOT_FOUND;
1084 }
1085
1086 if (fileName == nullptr || strlen(fileName) == 0)
1087 {
1088 debug(LOG_WARNING, "fileName is %s", (fileName == nullptr) ? "a NULL pointer" : "empty");
1089 return SAMPLE_NOT_FOUND;
1090 }
1091
1092 psTrack = (TRACK *)resGetData("WAV", fileName);
1093 if (psTrack == nullptr)
1094 {
1095 return SAMPLE_NOT_FOUND;
1096 }
1097
1098 return sound_GetTrackID(psTrack);
1099 }
1100
1101 /** Loop through the list of playing and queued audio samples, and destroy any
1102 * of them that refer to the given object.
1103 * \param psObj pointer to the object for which we must destroy all of its
1104 * outstanding audio samples.
1105 */
audio_RemoveObj(SIMPLE_OBJECT const * psObj)1106 void audio_RemoveObj(SIMPLE_OBJECT const *psObj)
1107 {
1108 unsigned int count = 0;
1109
1110 // loop through queued sounds and check if a sample needs to be removed
1111 AUDIO_SAMPLE *psSample = g_psSampleQueue;
1112 while (psSample != nullptr)
1113 {
1114 if (psSample->psObj == psObj)
1115 {
1116 // The current audio sample seems to refer to an object
1117 // that is about to be destroyed. So destroy this
1118 // sample as well.
1119 AUDIO_SAMPLE *toRemove = psSample;
1120
1121 // Make sure to keep our linked list iterator valid
1122 psSample = psSample->psNext;
1123
1124 debug(LOG_MEMORY, "audio_RemoveObj: callback %p sample %d\n", reinterpret_cast<void *>(toRemove->pCallback), toRemove->iTrack);
1125 // Remove sound from global active list
1126 sound_RemoveActiveSample(toRemove); //remove from global active list.
1127
1128 // Perform the actual task of destroying this sample
1129 audio_RemoveSample(&g_psSampleQueue, toRemove);
1130 free(toRemove);
1131
1132 // Increment the deletion count
1133 ++count;
1134 }
1135 else
1136 {
1137 psSample = psSample->psNext;
1138 }
1139 }
1140
1141 if (count)
1142 {
1143 debug(LOG_MEMORY, "audio_RemoveObj: BASE_OBJECT* 0x%p was found %u times in the audio sample queue", static_cast<const void *>(psObj), count);
1144 }
1145
1146 // Reset the deletion count
1147 count = 0;
1148
1149 // loop through list of currently playing sounds and check if a sample needs to be removed
1150 psSample = g_psSampleList;
1151 while (psSample != nullptr)
1152 {
1153 if (psSample->psObj == psObj)
1154 {
1155 // The current audio sample seems to refer to an object
1156 // that is about to be destroyed. So destroy this
1157 // sample as well.
1158 AUDIO_SAMPLE *toRemove = psSample;
1159
1160 // Make sure to keep our linked list iterator valid
1161 psSample = psSample->psNext;
1162
1163 debug(LOG_MEMORY, "audio_RemoveObj: callback %p sample %d\n", reinterpret_cast<void *>(toRemove->pCallback), toRemove->iTrack);
1164 // Stop this sound sample
1165 sound_RemoveActiveSample(toRemove); //remove from global active list.
1166
1167 // Perform the actual task of destroying this sample
1168 audio_RemoveSample(&g_psSampleList, toRemove);
1169 free(toRemove);
1170
1171 // Increment the deletion count
1172 ++count;
1173 }
1174 else
1175 {
1176 psSample = psSample->psNext;
1177 }
1178 }
1179
1180 if (count)
1181 {
1182 debug(LOG_MEMORY, "audio_RemoveObj: ***Warning! psOBJ %p was found %u times in the list of playing audio samples", static_cast<const void *>(psObj), count);
1183 }
1184 }
1185