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