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 /** \file
21  *  Sound library-specific functions
22  */
23 
24 // this has to be first
25 #include "lib/framework/frame.h"
26 #include "lib/framework/math_ext.h"
27 #include "lib/framework/frameresource.h"
28 #include "lib/exceptionhandler/dumpinfo.h"
29 
30 #include <AL/al.h>
31 #include <AL/alc.h>
32 #if defined(HAVE_OPENAL_ALEXT_H)
33 # include <AL/alext.h>
34 #endif
35 
36 #include <physfs.h>
37 #include "lib/framework/physfs_ext.h"
38 #include <string.h>
39 #include <math.h>
40 #include <limits>
41 
42 #include "tracklib.h"
43 #include "audio.h"
44 #include "cdaudio.h"
45 #include "oggvorbis.h"
46 #include "openal_error.h"
47 #include "mixer.h"
48 #include "openal_info.h"
49 
50 static ALuint current_queue_sample = -1;
51 
52 static bool openal_initialized = false;
53 
54 struct AUDIO_STREAM
55 {
56 	ALuint                  source = -1;        // OpenAL name of the sound source
57 	struct OggVorbisDecoderState *decoder = nullptr;
58 	PHYSFS_file *fileHandle = nullptr;
59 	float                   volume = 0.f;
60 
61 	// Callbacks
62 	std::function<void (const void *)> onFinished;
63 	const void              *user_data = nullptr;
64 
65 	size_t                  bufferSize = 0;
66 
67 	// Linked list pointer
68 	AUDIO_STREAM           *next = nullptr;
69 };
70 
71 struct SAMPLE_LIST
72 {
73 	AUDIO_SAMPLE   *curr;
74 	SAMPLE_LIST    *next;
75 };
76 
77 static SAMPLE_LIST *active_samples = nullptr;
78 
79 static AUDIO_STREAM *active_streams = nullptr;
80 
81 static ALfloat		sfx_volume = 1.0;
82 static ALfloat		sfx3d_volume = 1.0;
83 
84 static ALCdevice *device = nullptr;
85 static ALCcontext *context = nullptr;
86 
87 #if defined(ALC_SOFT_HRTF)
88 static LPALCGETSTRINGISOFT alcGetStringiSOFT = nullptr;
89 static LPALCRESETDEVICESOFT alcResetDeviceSOFT = nullptr;
90 #endif
91 
92 
93 /** Removes the given sample from the "active_samples" linked list
94  *  \param previous either NULL (if \c to_remove is the first item in the
95  *                  list) or the item occurring just before \c to_remove in
96  *                  the list
97  *  \param to_remove the item to actually remove from the list
98  */
sound_RemoveSample(SAMPLE_LIST * previous,SAMPLE_LIST * to_remove)99 static void sound_RemoveSample(SAMPLE_LIST *previous, SAMPLE_LIST *to_remove)
100 {
101 	if (previous != nullptr && previous != to_remove)
102 	{
103 		// Verify that the given two samples actually follow eachother in the list
104 		ASSERT(previous->next == to_remove, "Sound samples don't follow eachother in the list, we're probably removing the wrong item.");
105 
106 		// Remove the item to remove from the linked list by skipping
107 		// it in the pointer sequence.
108 		previous->next = to_remove->next;
109 	}
110 	else
111 	{
112 		// Apparently we're removing the first item from the list. So
113 		// make the next one the list's head.
114 		active_samples = to_remove->next;
115 	}
116 }
117 
HRTFModeToALCint(HRTFMode mode)118 ALCint HRTFModeToALCint(HRTFMode mode)
119 {
120 #if defined(ALC_SOFT_HRTF)
121 	switch (mode)
122 	{
123 	case HRTFMode::Unsupported:
124 		// should never be called with unsupported, log it and fall through to disabled
125 		debug(LOG_ERROR, "HRTFModeToALCint called with HRTFMode::Unsupported");
126 		// fallthrough
127 	case HRTFMode::Disabled:
128 		return ALC_FALSE;
129 	case HRTFMode::Enabled:
130 		return ALC_TRUE;
131 	case HRTFMode::Auto:
132 		return ALC_DONT_CARE_SOFT;
133 	}
134 #endif
135 	return ALC_FALSE;
136 }
137 
138 //*
139 // =======================================================================================================================
140 // =======================================================================================================================
141 //
sound_InitLibrary(HRTFMode hrtf)142 bool sound_InitLibrary(HRTFMode hrtf)
143 {
144 	int err;
145 	const ALfloat listenerVel[3] = { 0.0, 0.0, 0.0 };
146 	const ALfloat listenerOri[6] = { 0.0, 0.0, 1.0, 0.0, 1.0, 0.0 };
147 	char buf[512];
148 	const ALCchar *deviceName;
149 
150 #if 0
151 	// This code is disabled because enumerating devices apparently crashes PulseAudio on Fedora12
152 
153 	/* Get the available devices and print them.
154 	 * Devices are separated by NUL chars ('\0') and the list of devices is
155 	 * terminated by two NUL chars.
156 	 */
157 	deviceName = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
158 	while (deviceName != NULL && *deviceName != '\0')
159 	{
160 		debug(LOG_SOUND, "available OpenAL device(s) are: %s", deviceName);
161 		deviceName += strlen(deviceName) + 1;
162 	}
163 #endif
164 
165 	if (enabled_debug[LOG_SOUND])
166 	{
167 		OpenALInfo::Output_PlaybackDevices([](const std::string &output) {
168 			addDumpInfo(output.c_str());
169 			if (enabled_debug[LOG_SOUND])
170 			{
171 				_debug_multiline(0, LOG_SOUND, "sound", output);
172 			}
173 		});
174 	}
175 
176 	// Open default device
177 	device = alcOpenDevice(nullptr);
178 
179 	if (!device)
180 	{
181 		debug(LOG_ERROR, "Couldn't open audio device.");
182 		return false;
183 	}
184 
185 	if (enabled_debug[LOG_SOUND])
186 	{
187 		OpenALInfo::Output_ALCInfo(device, [](const std::string &output) {
188 			// addDumpInfo(output.c_str());
189 			//if (enabled_debug[LOG_SOUND])
190 			//{
191 				_debug_multiline(0, LOG_SOUND, "sound", output);
192 			//}
193 		});
194 	}
195 
196 #if defined(ALC_SOFT_HRTF)
197 	// Load some extensions from OpenAL-Soft (if available)
198 	alcGetStringiSOFT = (LPALCGETSTRINGISOFT)alcGetProcAddress(device, "alcGetStringiSOFT");
199 	alcResetDeviceSOFT = (LPALCRESETDEVICESOFT)alcGetProcAddress(device, "alcResetDeviceSOFT");
200 #endif
201 
202 
203 	// Print current device name and add it to dump info
204 	deviceName = alcGetString(device, ALC_DEVICE_SPECIFIER);
205 	debug(LOG_SOUND, "Current audio device: %s", deviceName);
206 	ssprintf(buf, "OpenAL Device Name: %s", deviceName);
207 	addDumpInfo(buf);
208 
209 	context = alcCreateContext(device, nullptr);		//NULL was contextAttributes
210 	if (!context)
211 	{
212 		debug(LOG_ERROR, "Couldn't open audio context.");
213 		return false;
214 	}
215 
216 	alcMakeContextCurrent(context);
217 
218 	err = sound_GetContextError(device);
219 	if (err != ALC_NO_ERROR)
220 	{
221 		debug(LOG_ERROR, "Couldn't initialize audio context: %s", alcGetString(device, err));
222 		return false;
223 	}
224 
225 	// Dump Open AL device info (depends on context)
226 	// to the crash handler for the dump file and debug log
227 	ssprintf(buf, "OpenAL Vendor: %s", alGetString(AL_VENDOR));
228 	addDumpInfo(buf);
229 	debug(LOG_SOUND, "%s", buf);
230 
231 	ssprintf(buf, "OpenAL Version: %s", alGetString(AL_VERSION));
232 	addDumpInfo(buf);
233 	debug(LOG_SOUND, "%s", buf);
234 
235 	ssprintf(buf, "OpenAL Renderer: %s", alGetString(AL_RENDERER));
236 	addDumpInfo(buf);
237 	debug(LOG_SOUND, "%s", buf);
238 
239 	ssprintf(buf, "OpenAL Extensions: %s", alGetString(AL_EXTENSIONS));
240 	addDumpInfo(buf);
241 	debug(LOG_SOUND, "%s", buf);
242 
243 	openal_initialized = true;
244 
245 #if defined(ALC_SOFT_HRTF)
246 	if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
247 	{
248 		// Set desired HRTF mode
249 		sound_SetHRTFMode(hrtf);
250 
251 		// Get current HRTF status
252 		ALCint hrtfStatus;
253 		alcGetIntegerv(device, ALC_HRTF_STATUS_SOFT, 1, &hrtfStatus);
254 
255 		const char *hrtfStatusString = nullptr;
256 		switch (hrtfStatus)
257 		{
258 		case ALC_HRTF_DISABLED_SOFT:
259 			hrtfStatusString = "ALC_HRTF_DISABLED_SOFT: HRTF is disabled";
260 			break;
261 		case ALC_HRTF_ENABLED_SOFT:
262 			hrtfStatusString = "ALC_HRTF_ENABLED_SOFT: HRTF is enabled";
263 			break;
264 		case ALC_HRTF_DENIED_SOFT:
265 			// This may be caused by invalid resource permissions, or other user configuration that disallows HRTF.
266 			hrtfStatusString = "ALC_HRTF_DENIED_SOFT: HRTF is disabled because it's not allowed on the device.";
267 			break;
268 		case ALC_HRTF_REQUIRED_SOFT:
269 			// This may be caused by a device that can only use HRTF, or other user configuration that forces HRTF to be used.
270 			hrtfStatusString = "ALC_HRTF_REQUIRED_SOFT: HRTF is enabled because it must be used on the device.";
271 			break;
272 		case ALC_HRTF_HEADPHONES_DETECTED_SOFT:
273 			hrtfStatusString = "ALC_HRTF_HEADPHONES_DETECTED_SOFT: HRTF is enabled automatically because the device reported itself as headphones.";
274 			break;
275 		case ALC_HRTF_UNSUPPORTED_FORMAT_SOFT:
276 			// HRTF is disabled because the device does not support it with the current format.
277 			// Typically this is caused by non-stereo output or an incompatible output frequency.
278 			hrtfStatusString = "ALC_HRTF_UNSUPPORTED_FORMAT_SOFT: HRTF is disabled because the device does not support it with the current format.";
279 			break;
280 		default:
281 			hrtfStatusString = nullptr;
282 			break;
283 		}
284 
285 		if (hrtfStatusString)
286 		{
287 			debug(LOG_SOUND, "%s", hrtfStatusString);
288 		}
289 		else
290 		{
291 			debug(LOG_SOUND, "OpenAL-Soft returned an unknown ALC_HRTF_STATUS_SOFT result: %d", hrtfStatus);
292 		}
293 	}
294 	else
295 	{
296 		debug(LOG_SOUND, "alcIsExtensionPresent(..., \"ALC_SOFT_HRTF\") returned false");
297 	}
298 #else
299 	debug(LOG_SOUND, "ALC_SOFT_HRTF not defined");
300 #endif
301 
302 	// Clear Error Codes
303 	alGetError();
304 	alcGetError(device);
305 
306 	alListener3f(AL_POSITION, 0.f, 0.f, 0.f);
307 	alListenerfv(AL_VELOCITY, listenerVel);
308 	alListenerfv(AL_ORIENTATION, listenerOri);
309 	alDistanceModel(AL_NONE);
310 	sound_GetError();
311 
312 	return true;
313 }
314 
sound_GetHRTFMode()315 HRTFMode sound_GetHRTFMode()
316 {
317 #if defined(ALC_SOFT_HRTF)
318 	if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
319 	{
320 		ALCint hrtfStatus;
321 		alcGetIntegerv(device, ALC_HRTF_SOFT, 1, &hrtfStatus);
322 		switch (hrtfStatus)
323 		{
324 		case ALC_TRUE:
325 			return HRTFMode::Enabled;
326 		case ALC_FALSE:
327 			return HRTFMode::Disabled;
328 		default:
329 			debug(LOG_SOUND, "OpenAL-Soft returned an unexpected ALC_HRTF_SOFT result: %d", hrtfStatus);
330 		}
331 	}
332 #endif
333 	return HRTFMode::Unsupported;
334 }
335 
sound_SetHRTFMode(HRTFMode mode)336 bool sound_SetHRTFMode(HRTFMode mode)
337 {
338 #if defined(ALC_SOFT_HRTF)
339 	if(alcIsExtensionPresent(device, "ALC_SOFT_HRTF"))
340 	{
341 		ALCint hrtfSetting = HRTFModeToALCint(mode);
342 
343 		ALCint attrs[] = {
344 			ALC_HRTF_SOFT, hrtfSetting, /* configure HRTF */
345 			0 /* end of list */
346 		};
347 		if (alcResetDeviceSOFT)
348 		{
349 			ASSERT(device, "device is null");
350 			if (!alcResetDeviceSOFT(device, attrs))
351 			{
352 				debug(LOG_ERROR, "Failed to reset device: %s\n", alcGetString(device, alcGetError(device)));
353 				return false;
354 			}
355 			return true;
356 		}
357 		else
358 		{
359 			debug(LOG_ERROR, "ALC_SOFT_HRTF extension is available, but alcResetDeviceSOFT is null");
360 		}
361 	}
362 #endif
363 	return false;
364 }
365 
366 static void sound_UpdateStreams(void);
367 
sound_ShutdownLibrary(void)368 void sound_ShutdownLibrary(void)
369 {
370 	AUDIO_STREAM *stream;
371 	SAMPLE_LIST *aSample = active_samples, * tmpSample = nullptr;
372 
373 	if (!openal_initialized)
374 	{
375 		return;
376 	}
377 	debug(LOG_SOUND, "starting shutdown");
378 
379 	// Stop all streams, sound_UpdateStreams() will deallocate all stopped streams
380 	for (stream = active_streams; stream != nullptr; stream = stream->next)
381 	{
382 		sound_StopStream(stream);
383 	}
384 	sound_UpdateStreams();
385 
386 	alcGetError(device);	// clear error codes
387 
388 	/* On Linux since this caused some versions of OpenAL to hang on exit. - Per */
389 	debug(LOG_SOUND, "make default context NULL");
390 	alcMakeContextCurrent(nullptr);
391 	sound_GetContextError(device);
392 
393 	debug(LOG_SOUND, "destroy previous context");
394 	alcDestroyContext(context); // this gives a long delay on some impl.
395 	sound_GetContextError(device);
396 
397 	debug(LOG_SOUND, "close device");
398 	if (alcCloseDevice(device) == ALC_FALSE)
399 	{
400 		debug(LOG_SOUND, "OpenAl could not close the audio device.");
401 	}
402 	device = nullptr;
403 
404 	while (aSample)
405 	{
406 		tmpSample = aSample->next;
407 		free(aSample);
408 		aSample = tmpSample;
409 	}
410 	active_samples = nullptr;
411 }
412 
413 /** Deletes the given sample and updates the \c previous and \c current iterators
414  *  \param previous iterator to the previous sample in the list
415  *  \param sample iterator to the current sample in the list which you want to delete
416  */
sound_DestroyIteratedSample(SAMPLE_LIST ** previous,SAMPLE_LIST ** sample)417 static void sound_DestroyIteratedSample(SAMPLE_LIST **previous, SAMPLE_LIST **sample)
418 {
419 	// If an OpenAL source is associated with this sample, release it
420 	if ((*sample)->curr->iSample != (ALuint)AL_INVALID)
421 	{
422 		alDeleteSources(1, &(*sample)->curr->iSample);
423 		sound_GetError();
424 	}
425 
426 	// Do the cleanup of this sample
427 	sound_FinishedCallback((*sample)->curr);
428 
429 	// Remove the sample from the list
430 	sound_RemoveSample(*previous, *sample);
431 	// Free it
432 	free(*sample);
433 
434 	// Get a pointer to the next node, the previous pointer doesn't change
435 	*sample = (*previous != nullptr) ? (*previous)->next : active_samples;
436 }
437 
438 /** Counts the number of samples in active_samples
439  *  \return the number of actively playing sound samples
440  */
sound_GetActiveSamplesCount()441 unsigned int sound_GetActiveSamplesCount()
442 {
443 	unsigned int  num = 0;
444 	SAMPLE_LIST *node = active_samples;
445 
446 	while (node)
447 	{
448 		num++;
449 		node = node->next;
450 	}
451 	return num;
452 }
453 
sound_Update()454 void sound_Update()
455 {
456 	SAMPLE_LIST *node = active_samples;
457 	SAMPLE_LIST *previous = nullptr;
458 	ALfloat gain;
459 
460 	if (!openal_initialized)
461 	{
462 		return;
463 	}
464 
465 	// Update all streaming audio
466 	sound_UpdateStreams();
467 
468 	while (node != nullptr)
469 	{
470 		ALenum state, err;
471 
472 		// query what the gain is for this sample
473 		alGetSourcef(node->curr->iSample, AL_GAIN, &gain);
474 		err = sound_GetError();
475 
476 		// if gain is 0, then we can't hear it, so we kill it.
477 		if (gain == 0.0f)
478 		{
479 			sound_DestroyIteratedSample(&previous, &node);
480 			continue;
481 		}
482 
483 		//ASSERT(alIsSource(node->curr->iSample), "Not a valid source!");
484 		alGetSourcei(node->curr->iSample, AL_SOURCE_STATE, &state);
485 
486 		// Check whether an error occurred while retrieving the state.
487 		// If one did, the state returned is useless. So instead of
488 		// using it continue with the next sample.
489 		err = sound_GetError();
490 		if (err != AL_NO_ERROR)
491 		{
492 			// Make sure to invoke the "finished" callback
493 			sound_FinishedCallback(node->curr);
494 
495 			// Destroy this object and move to the next object
496 			sound_DestroyIteratedSample(&previous, &node);
497 			continue;
498 		}
499 
500 		switch (state)
501 		{
502 		case AL_PLAYING:
503 		case AL_PAUSED:
504 			// If we haven't finished playing yet, just
505 			// continue with the next item in the list.
506 
507 			// sound_SetObjectPosition(i->curr->iSample, i->curr->x, i->curr->y, i->curr->z);
508 
509 			// Move to the next object
510 			previous = node;
511 			node = node->next;
512 			break;
513 
514 		// NOTE: if it isn't playing | paused, then it is most likely either done
515 		// or a error.  In either case, we want to kill the sample in question.
516 
517 		default:
518 			sound_DestroyIteratedSample(&previous, &node);
519 			break;
520 		}
521 	}
522 
523 	// Reset the current error state
524 	alcGetError(device);
525 
526 	alcProcessContext(context);
527 
528 	ALCenum err = sound_GetContextError(device);
529 	if (err != ALC_NO_ERROR)
530 	{
531 		debug(LOG_ERROR, "Error while processing audio context: %s", alGetString(err));
532 	}
533 }
534 
535 //*
536 // =======================================================================================================================
537 // =======================================================================================================================
538 //
sound_QueueSamplePlaying(void)539 bool sound_QueueSamplePlaying(void)
540 {
541 	ALenum	state;
542 
543 	if (!openal_initialized)
544 	{
545 		return false;
546 	}
547 	if (current_queue_sample == (ALuint)AL_INVALID)
548 	{
549 		return false;
550 	}
551 
552 	alGetSourcei(current_queue_sample, AL_SOURCE_STATE, &state);
553 
554 	// Check whether an error occurred while retrieving the state.
555 	// If one did, the state returned is useless. So instead of
556 	// using it return false.
557 	if (sound_GetError() != AL_NO_ERROR)
558 	{
559 		return false;
560 	}
561 
562 	if (state == AL_PLAYING)
563 	{
564 		return true;
565 	}
566 
567 	if (current_queue_sample != (ALuint)AL_INVALID)
568 	{
569 		SAMPLE_LIST *node = active_samples;
570 		SAMPLE_LIST *previous = nullptr;
571 
572 		// We need to remove it from the queue of actively played samples
573 		while (node != nullptr)
574 		{
575 			if (node->curr->iSample == current_queue_sample)
576 			{
577 				sound_DestroyIteratedSample(&previous, &node);
578 				current_queue_sample = AL_INVALID;
579 				return false;
580 			}
581 			previous = node;
582 			if (node)
583 			{
584 				node = node->next;
585 			}
586 		}
587 		debug(LOG_ERROR, "Sample %u not deleted because it wasn't in the active queue!", current_queue_sample);
588 		current_queue_sample = AL_INVALID;
589 	}
590 	return false;
591 }
592 
593 /** Decodes an opened OggVorbis file into an OpenAL buffer
594  *  \param psTrack pointer to object which will contain the final buffer
595  *  \param PHYSFS_fileHandle file handle given by PhysicsFS to the opened file
596  *  \return on success the psTrack pointer, otherwise it will be free'd and a NULL pointer is returned instead
597  */
sound_DecodeOggVorbisTrack(TRACK * psTrack,PHYSFS_file * PHYSFS_fileHandle)598 static inline TRACK *sound_DecodeOggVorbisTrack(TRACK *psTrack, PHYSFS_file *PHYSFS_fileHandle)
599 {
600 	ALenum		format;
601 	ALuint		buffer;
602 	struct OggVorbisDecoderState *decoder;
603 	soundDataBuffer	*soundBuffer;
604 
605 	if (!openal_initialized)
606 	{
607 		return nullptr;
608 	}
609 
610 	decoder = sound_CreateOggVorbisDecoder(PHYSFS_fileHandle, true);
611 	if (decoder == nullptr)
612 	{
613 		debug(LOG_WARNING, "Failed to open audio file for decoding");
614 		free(psTrack);
615 		return nullptr;
616 	}
617 
618 	soundBuffer = sound_DecodeOggVorbis(decoder, 0);
619 	sound_DestroyOggVorbisDecoder(decoder);
620 
621 	if (soundBuffer == nullptr)
622 	{
623 		free(psTrack);
624 		return nullptr;
625 	}
626 
627 	if (soundBuffer->size == 0)
628 	{
629 		debug(LOG_WARNING, "sound_DecodeOggVorbisTrack: OggVorbis track is entirely empty after decoding");
630 // NOTE: I'm not entirely sure if a track that's empty after decoding should be
631 //       considered an error condition. Therefore I'll only error out on DEBUG
632 //       builds. (Returning NULL here __will__ result in a program termination.)
633 #ifdef DEBUG
634 		free(soundBuffer);
635 		free(psTrack);
636 		return NULL;
637 #endif
638 	}
639 
640 	// Determine PCM data format
641 	format = (soundBuffer->channelCount == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
642 
643 	// Create an OpenAL buffer and fill it with the decoded data
644 	alGenBuffers(1, &buffer);
645 	sound_GetError();
646 	ASSERT(soundBuffer->size <= static_cast<size_t>(std::numeric_limits<ALsizei>::max()), "soundBuffer->size (%zu) exceeds ALsizei::max", soundBuffer->size);
647 	alBufferData(buffer, format, soundBuffer->data, static_cast<ALsizei>(soundBuffer->size), soundBuffer->frequency);
648 	sound_GetError();
649 
650 	free(soundBuffer);
651 
652 	// save buffer name in track
653 	psTrack->iBufferName = buffer;
654 
655 	return psTrack;
656 }
657 
658 //*
659 // =======================================================================================================================
660 // =======================================================================================================================
661 //
sound_LoadTrackFromFile(const char * fileName)662 TRACK *sound_LoadTrackFromFile(const char *fileName)
663 {
664 	TRACK *pTrack;
665 	PHYSFS_file *fileHandle;
666 	size_t filename_size;
667 	char *track_name;
668 
669 	// Use PhysicsFS to open the file
670 	fileHandle = PHYSFS_openRead(fileName);
671 	debug(LOG_NEVER, "Reading...[directory: %s] %s", WZ_PHYSFS_getRealDir_String(fileName).c_str(), fileName);
672 	if (fileHandle == nullptr)
673 	{
674 		debug(LOG_ERROR, "sound_LoadTrackFromFile: PHYSFS_openRead(\"%s\") failed with error: %s\n", fileName, WZ_PHYSFS_getLastError());
675 		return nullptr;
676 	}
677 
678 	if (GetLastResourceFilename() == nullptr)
679 	{
680 		// This is a non fatal error.  We just can't find filename for some reason.
681 		debug(LOG_WARNING, "sound_LoadTrackFromFile: missing resource filename?");
682 		filename_size = 0;
683 	}
684 	else
685 	{
686 		filename_size = strlen(GetLastResourceFilename()) + 1;
687 	}
688 
689 	// allocate track, plus the memory required to contain the filename
690 	// one malloc call ensures only one free call is required
691 	pTrack = (TRACK *)malloc(sizeof(TRACK) + filename_size);
692 	if (pTrack == nullptr)
693 	{
694 		debug(LOG_FATAL, "sound_ConstructTrack: couldn't allocate memory\n");
695 		abort();
696 		return nullptr;
697 	}
698 
699 	// Initialize everything (except for the filename) to zero
700 	memset(pTrack, 0, sizeof(TRACK));
701 
702 	// Set filename pointer; if the filename (as returned by
703 	// GetLastResourceFilename()) is a NULL pointer, then this will be a
704 	// NULL pointer as well.
705 	track_name = filename_size ? (char *)(pTrack + 1) : nullptr;
706 
707 	// Copy the filename into the struct, if we don't have a NULL pointer
708 	if (filename_size != 0)
709 	{
710 		strcpy(track_name, GetLastResourceFilename());
711 	}
712 	pTrack->fileName = track_name;
713 
714 	// Now use sound_ReadTrackFromBuffer to decode the file's contents
715 	pTrack = sound_DecodeOggVorbisTrack(pTrack, fileHandle);
716 
717 	PHYSFS_close(fileHandle);
718 	return pTrack;
719 }
720 
sound_FreeTrack(TRACK * psTrack)721 void sound_FreeTrack(TRACK *psTrack)
722 {
723 	alDeleteBuffers(1, &psTrack->iBufferName);
724 	sound_GetError();
725 }
726 
sound_AddActiveSample(AUDIO_SAMPLE * psSample)727 static void sound_AddActiveSample(AUDIO_SAMPLE *psSample)
728 {
729 	SAMPLE_LIST *tmp = (SAMPLE_LIST *) malloc(sizeof(SAMPLE_LIST));
730 
731 	// Prepend the given sample to our list of active samples
732 	tmp->curr = psSample;
733 	tmp->next = active_samples;
734 	active_samples = tmp;
735 }
736 
737 /** Routine gets rid of the psObj's sound sample and reference in active_samples.
738  */
sound_RemoveActiveSample(AUDIO_SAMPLE * psSample)739 void sound_RemoveActiveSample(AUDIO_SAMPLE *psSample)
740 {
741 	SAMPLE_LIST *node = active_samples;
742 	SAMPLE_LIST *previous = nullptr;
743 
744 	while (node != nullptr)
745 	{
746 		if (node->curr->psObj == psSample->psObj)
747 		{
748 			debug(LOG_MEMORY, "Removing object 0x%p from active_samples list 0x%p\n", static_cast<void *>(psSample->psObj), static_cast<void *>(node));
749 
750 			// Buginator: should we wait for it to finish, or just stop it?
751 			sound_StopSample(node->curr);
752 
753 			sound_FinishedCallback(node->curr);	//tell the callback it is finished.
754 
755 			sound_DestroyIteratedSample(&previous, &node);
756 		}
757 		else
758 		{
759 			// Move to the next sample object
760 			previous = node;
761 			node = node->next;
762 		}
763 	}
764 }
765 
sound_SetupChannel(AUDIO_SAMPLE * psSample)766 static bool sound_SetupChannel(AUDIO_SAMPLE *psSample)
767 {
768 	sound_AddActiveSample(psSample);
769 
770 	return sound_TrackLooped(psSample->iTrack);
771 }
772 
773 //*
774 // =======================================================================================================================
775 // =======================================================================================================================
776 //
sound_Play2DSample(TRACK * psTrack,AUDIO_SAMPLE * psSample,bool bQueued)777 bool sound_Play2DSample(TRACK *psTrack, AUDIO_SAMPLE *psSample, bool bQueued)
778 {
779 	ALfloat zero[3] = { 0.0, 0.0, 0.0 };
780 	ALfloat volume;
781 	ALint error;
782 
783 	if (sfx_volume == 0.0)
784 	{
785 		return false;
786 	}
787 	volume = ((float)psTrack->iVol / 100.0f);		// each object can have OWN volume!
788 	psSample->fVol = volume;						// save computed volume
789 	volume *= sfx_volume;							// and now take into account the Users sound Prefs.
790 
791 	// We can't hear it, so don't bother creating it.
792 	if (volume == 0.0f)
793 	{
794 		return false;
795 	}
796 
797 	// Clear error codes
798 	alGetError();
799 
800 	alGenSources(1, &(psSample->iSample));
801 
802 	error = sound_GetError();
803 	if (error != AL_NO_ERROR)
804 	{
805 		/* FIXME: We run out of OpenAL sources very quickly, so we
806 		 * should handle the case where we've ran out of them.
807 		 * Currently we don't do this, causing some unpleasant side
808 		 * effects, e.g. crashing...
809 		 */
810 	}
811 
812 	alSourcef(psSample->iSample, AL_PITCH, 1.0f);
813 	alSourcef(psSample->iSample, AL_GAIN, volume);
814 	alSourcefv(psSample->iSample, AL_POSITION, zero);
815 	alSourcefv(psSample->iSample, AL_VELOCITY, zero);
816 	alSourcei(psSample->iSample, AL_BUFFER, psTrack->iBufferName);
817 	alSourcei(psSample->iSample, AL_SOURCE_RELATIVE, AL_TRUE);
818 	alSourcei(psSample->iSample, AL_LOOPING, (sound_SetupChannel(psSample)) ? AL_TRUE : AL_FALSE);
819 
820 	// NOTE: this is only useful for debugging.
821 #ifdef DEBUG
822 	psSample->is3d = false;
823 	psSample->isLooping = sound_TrackLooped(psSample->iTrack) ? AL_TRUE : AL_FALSE;
824 	memcpy(psSample->filename, psTrack->fileName, strlen(psTrack->fileName));
825 	psSample->filename[strlen(psTrack->fileName)] = '\0';
826 #endif
827 	// Clear error codes
828 	alGetError();
829 
830 	alSourcePlay(psSample->iSample);
831 	sound_GetError();
832 
833 	if (bQueued)
834 	{
835 		current_queue_sample = psSample->iSample;
836 	}
837 	else if (current_queue_sample == psSample->iSample)
838 	{
839 		current_queue_sample = -1;
840 	}
841 
842 	return true;
843 }
844 
845 //*
846 // =======================================================================================================================
847 // =======================================================================================================================
848 //
sound_Play3DSample(TRACK * psTrack,AUDIO_SAMPLE * psSample)849 bool sound_Play3DSample(TRACK *psTrack, AUDIO_SAMPLE *psSample)
850 {
851 	ALfloat zero[3] = { 0.0, 0.0, 0.0 };
852 	ALfloat volume;
853 	ALint error;
854 
855 	if (sfx3d_volume == 0.0)
856 	{
857 		return false;
858 	}
859 
860 	volume = ((float)psTrack->iVol / 100.f);		// max range is 0-100
861 	psSample->fVol = volume;						// store results for later
862 
863 	// If we can't hear it, then don't bother playing it.
864 	if (volume == 0.0f)
865 	{
866 		return false;
867 	}
868 	// Clear error codes
869 	alGetError();
870 
871 	alGenSources(1, &(psSample->iSample));
872 
873 	error = sound_GetError();
874 	if (error != AL_NO_ERROR)
875 	{
876 		/* FIXME: We run out of OpenAL sources very quickly, so we
877 		 * should handle the case where we've ran out of them.
878 		 * Currently we don't do this, causing some unpleasant side
879 		 * effects, e.g. crashing...
880 		 */
881 	}
882 
883 #if defined(WZ_OS_UNIX) && !defined(WZ_OS_MAC)
884 	// HACK: this is a workaround for a bug in the 64bit implementation of OpenAL on GNU/Linux
885 	// The AL_PITCH value really should be 1.0.
886 	alSourcef(psSample->iSample, AL_PITCH, 1.001f);
887 #else
888 	alSourcef(psSample->iSample, AL_PITCH, 1.0f);
889 #endif
890 
891 	sound_SetObjectPosition(psSample);
892 	alSourcefv(psSample->iSample, AL_VELOCITY, zero);
893 	alSourcei(psSample->iSample, AL_BUFFER, psTrack->iBufferName);
894 	alSourcei(psSample->iSample, AL_LOOPING, (sound_SetupChannel(psSample)) ? AL_TRUE : AL_FALSE);
895 
896 	// NOTE: this is only useful for debugging.
897 #ifdef DEBUG
898 	psSample->is3d = true;
899 	psSample->isLooping = sound_TrackLooped(psSample->iTrack) ? AL_TRUE : AL_FALSE;
900 	memcpy(psSample->filename, psTrack->fileName, strlen(psTrack->fileName));
901 	psSample->filename[strlen(psTrack->fileName)] = '\0';
902 #endif
903 
904 	// Clear error codes
905 	alGetError();
906 
907 	alSourcePlay(psSample->iSample);
908 	sound_GetError();
909 
910 	return true;
911 }
912 
913 /** Plays the audio data from the given file
914  *  \param fileHandle PhysicsFS file handle to stream the audio from
915  *  \param volume the volume to play the audio at (in a range of 0.0 to 1.0)
916  *  \param onFinished callback to invoke when we're finished playing
917  *  \param user_data user-data pointer to pass to the \c onFinished callback
918  *  \return a pointer to the currently playing stream when playing started
919  *          successfully, NULL otherwise.
920  *  \post When a non-NULL pointer is returned the audio stream system will
921  *        close the PhysicsFS file handle. Otherwise (when false is returned)
922  *        this is left to the user.
923  *  \note The returned pointer will become invalid/dangling immediately after
924  *        the \c onFinished callback is invoked.
925  *  \note You must _never_ manually free() the memory used by the returned
926  *        pointer.
927  */
sound_PlayStream(PHYSFS_file * fileHandle,float volume,void (* onFinished)(const void *),const void * user_data)928 AUDIO_STREAM *sound_PlayStream(PHYSFS_file *fileHandle, float volume, void (*onFinished)(const void *), const void *user_data)
929 {
930 	// Default buffer size
931 	static const size_t streamBufferSize = 16 * 1024;
932 	// Default buffer count
933 	static const unsigned int buffer_count = 2;
934 
935 	return sound_PlayStreamWithBuf(fileHandle, volume, onFinished, user_data, streamBufferSize, buffer_count);
936 }
937 
938 /** Plays the audio data from the given file
939  *  \param fileHandle,volume,onFinished,user_data see sound_PlayStream()
940  *  \param streamBufferSize the size to use for the decoded audio buffers
941  *  \param buffer_count the amount of audio buffers to use
942  *  \see sound_PlayStream() for details about the rest of the function
943  *       parameters and other details.
944  */
sound_PlayStreamWithBuf(PHYSFS_file * fileHandle,float volume,const std::function<void (const void *)> & onFinished,const void * user_data,size_t streamBufferSize,unsigned int buffer_count,bool allowSeeking)945 AUDIO_STREAM *sound_PlayStreamWithBuf(PHYSFS_file *fileHandle, float volume, const std::function<void (const void *)>& onFinished, const void *user_data, size_t streamBufferSize, unsigned int buffer_count, bool allowSeeking)
946 {
947 	AUDIO_STREAM *stream;
948 	ALuint       *buffers = nullptr;
949 	bool freeBuffers = false;
950 	ALint error;
951 	unsigned int i;
952 
953 	if (!openal_initialized)
954 	{
955 		debug(LOG_WARNING, "OpenAL isn't initialized, not creating an audio stream");
956 		return nullptr;
957 	}
958 
959 	stream = new AUDIO_STREAM();
960 	if (stream == nullptr)
961 	{
962 		debug(LOG_FATAL, "sound_PlayStream: Out of memory");
963 		abort();
964 		return nullptr;
965 	}
966 
967 	// Clear error codes
968 	alGetError();
969 
970 	// Retrieve an OpenAL sound source
971 	alGenSources(1, &(stream->source));
972 
973 	error = sound_GetError();
974 	if (error != AL_NO_ERROR)
975 	{
976 		// Failed to create OpenAL sound source, so bail out...
977 		debug(LOG_SOUND, "alGenSources failed, most likely out of sound sources");
978 		delete stream;
979 		return nullptr;
980 	}
981 
982 	stream->fileHandle = fileHandle;
983 
984 	stream->decoder = sound_CreateOggVorbisDecoder(stream->fileHandle, allowSeeking);
985 	if (stream->decoder == nullptr)
986 	{
987 		debug(LOG_ERROR, "sound_PlayStream: Failed to open audio file for decoding");
988 		delete stream;
989 		return nullptr;
990 	}
991 
992 	stream->volume = volume;
993 	stream->bufferSize = streamBufferSize;
994 
995 	alSourcef(stream->source, AL_GAIN, stream->volume);
996 
997 #if defined(WZ_OS_UNIX) && !defined(WZ_OS_MAC)
998 	// HACK: this is a workaround for a bug in the 64bit implementation of OpenAL on GNU/Linux
999 	// The AL_PITCH value really should be 1.0.
1000 	alSourcef(stream->source, AL_PITCH, 1.001f);
1001 #else
1002 	alSourcef(stream->source, AL_PITCH, 1.0f);
1003 #endif
1004 
1005 	// Create some OpenAL buffers to store the decoded data in
1006 	if (buffer_count <= (1024 / sizeof(ALuint))) // See CMakeLists.txt for value of -Walloca-larger-than=<N>
1007 	{
1008 		buffers = (ALuint *)alloca(buffer_count * sizeof(ALuint));
1009 	}
1010 	else
1011 	{
1012 		// Too many buffers - don't allocate on the stack!
1013 		buffers = (ALuint *)malloc(buffer_count * sizeof(ALuint));
1014 		freeBuffers = true;
1015 	}
1016 	alGenBuffers(buffer_count, buffers);
1017 	sound_GetError();
1018 
1019 	// Fill some buffers with audio data
1020 	for (i = 0; i < buffer_count; ++i)
1021 	{
1022 		// Decode some audio data
1023 		soundDataBuffer *soundBuffer = sound_DecodeOggVorbis(stream->decoder, stream->bufferSize);
1024 
1025 		// If we actually decoded some data
1026 		if (soundBuffer && soundBuffer->size > 0)
1027 		{
1028 			// Determine PCM data format
1029 			ALenum format = (soundBuffer->channelCount == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
1030 
1031 			// Copy the audio data into one of OpenAL's own buffers
1032 			ASSERT(soundBuffer->size <= static_cast<size_t>(std::numeric_limits<ALsizei>::max()), "soundBuffer->size (%zu) exceeds ALsizei::max", soundBuffer->size);
1033 			alBufferData(buffers[i], format, soundBuffer->data, static_cast<ALint>(soundBuffer->size), soundBuffer->frequency);
1034 			sound_GetError();
1035 
1036 			// Clean up our memory
1037 			free(soundBuffer);
1038 		}
1039 		else
1040 		{
1041 			// If no data has been decoded we're probably at the end of our
1042 			// stream. So cleanup the excess stuff here.
1043 
1044 			// First remove the data buffer itself
1045 			free(soundBuffer);
1046 
1047 			// Then remove OpenAL's buffers
1048 			alDeleteBuffers(buffer_count - i, &buffers[i]);
1049 			sound_GetError();
1050 
1051 			break;
1052 		}
1053 	}
1054 
1055 	// Bail out if we didn't fill any buffers
1056 	if (i == 0)
1057 	{
1058 		debug(LOG_ERROR, "Failed to fill buffers with decoded audio data!");
1059 
1060 		// Destroy the decoder
1061 		sound_DestroyOggVorbisDecoder(stream->decoder);
1062 
1063 		// Destroy the OpenAL source
1064 		alDeleteSources(1, &stream->source);
1065 
1066 		// Free allocated memory
1067 		delete stream;
1068 
1069 		if(freeBuffers)
1070 		{
1071 			free(buffers);
1072 		}
1073 		buffers = nullptr;
1074 
1075 		return nullptr;
1076 	}
1077 
1078 	// Attach the OpenAL buffers to our OpenAL source
1079 	// (i = the amount of buffers we worked on in the above for-loop)
1080 	alSourceQueueBuffers(stream->source, i, buffers);
1081 	sound_GetError();
1082 
1083 	// Start playing the source
1084 	alSourcePlay(stream->source);
1085 
1086 	sound_GetError();
1087 
1088 	// Set callback info
1089 	stream->onFinished = onFinished;
1090 	stream->user_data = user_data;
1091 
1092 	// Prepend this stream to the linked list
1093 	stream->next = active_streams;
1094 	active_streams = stream;
1095 
1096 	if(freeBuffers)
1097 	{
1098 		free(buffers);
1099 	}
1100 	buffers = nullptr;
1101 
1102 	return stream;
1103 }
1104 
1105 /** Checks if the stream is playing.
1106  *  \param stream the stream to check
1107  *  \post true if playing, false otherwise.
1108  *
1109  */
sound_isStreamPlaying(AUDIO_STREAM * stream)1110 bool sound_isStreamPlaying(AUDIO_STREAM *stream)
1111 {
1112 	ALint state;
1113 
1114 	if (stream)
1115 	{
1116 		alGetSourcei(stream->source, AL_SOURCE_STATE, &state);
1117 		sound_GetError();
1118 		if (state == AL_PLAYING)
1119 		{
1120 			return true;
1121 		}
1122 	}
1123 	return false;
1124 }
1125 
1126 /** Stops the current stream from playing.
1127  *  \param stream the stream to stop
1128  *  \post The stopped stream will be destroyed on the next invocation of
1129  *        sound_UpdateStreams(). So calling this function will result in the
1130  *        \c onFinished callback being called and the \c stream pointer becoming
1131  *        invalid.
1132  */
sound_StopStream(AUDIO_STREAM * stream)1133 void sound_StopStream(AUDIO_STREAM *stream)
1134 {
1135 	assert(stream != nullptr);
1136 
1137 	alGetError();	// clear error codes
1138 	// Tell OpenAL to stop playing on the given source
1139 	alSourceStop(stream->source);
1140 	sound_GetError();
1141 }
1142 
1143 /** Pauses playing of this stream until playing is resumed with
1144  *  sound_ResumeStream() or completely stopped with sound_StopStream().
1145  *  \param stream the stream to pause playing for
1146  */
sound_PauseStream(AUDIO_STREAM * stream)1147 void sound_PauseStream(AUDIO_STREAM *stream)
1148 {
1149 	ALint state;
1150 
1151 	// To be sure we won't go mutilating this OpenAL source, check whether
1152 	// it's playing first.
1153 	alGetSourcei(stream->source, AL_SOURCE_STATE, &state);
1154 	sound_GetError();
1155 
1156 	if (state != AL_PLAYING)
1157 	{
1158 		return;
1159 	}
1160 
1161 	// Pause playing of this OpenAL source
1162 	alSourcePause(stream->source);
1163 	sound_GetError();
1164 }
1165 
1166 /** Resumes playing of a stream that's paused by means of sound_PauseStream().
1167  *  \param stream the stream to resume playing for
1168  */
sound_ResumeStream(AUDIO_STREAM * stream)1169 void sound_ResumeStream(AUDIO_STREAM *stream)
1170 {
1171 	ALint state;
1172 
1173 	// To be sure we won't go mutilating this OpenAL source, check whether
1174 	// it's paused first.
1175 	alGetSourcei(stream->source, AL_SOURCE_STATE, &state);
1176 	sound_GetError();
1177 
1178 	if (state != AL_PAUSED)
1179 	{
1180 		return;
1181 	}
1182 
1183 	// Resume playing of this OpenAL source
1184 	alSourcePlay(stream->source);
1185 	sound_GetError();
1186 }
1187 
1188 /** Retrieve the playing volume of the given stream.
1189  *
1190  *  @param stream the stream to retrieve the volume for.
1191  *
1192  *  @return a floating point value between 0.f and 1.f, representing this
1193  *          stream's volume.
1194  */
sound_GetStreamVolume(const AUDIO_STREAM * stream)1195 float sound_GetStreamVolume(const AUDIO_STREAM *stream)
1196 {
1197 	ALfloat volume;
1198 	alGetSourcef(stream->source, AL_GAIN, &volume);
1199 	sound_GetError();
1200 
1201 	return volume;
1202 }
1203 
1204 /** Set the playing volume of the given stream.
1205  *
1206  *  @param stream the stream to change the volume for.
1207  *  @param volume a floating point value between 0.f and 1.f, to use as this
1208  *                @c stream's volume.
1209  */
sound_SetStreamVolume(AUDIO_STREAM * stream,float volume)1210 void sound_SetStreamVolume(AUDIO_STREAM *stream, float volume)
1211 {
1212 	stream->volume = volume;
1213 	alSourcef(stream->source, AL_GAIN, stream->volume);
1214 	sound_GetError();
1215 }
1216 
sound_GetStreamTotalTime(AUDIO_STREAM * stream)1217 double sound_GetStreamTotalTime(AUDIO_STREAM *stream)
1218 {
1219 	return sound_GetOggVorbisTotalTime(stream->decoder);
1220 }
1221 
1222 /** Update the given stream by making sure its buffers remain full
1223  *  \param stream the stream to update
1224  *  \return true when the stream is still playing, false when it has stopped
1225  */
sound_UpdateStream(AUDIO_STREAM * stream)1226 static bool sound_UpdateStream(AUDIO_STREAM *stream)
1227 {
1228 	ALint state, buffer_count;
1229 
1230 	alGetSourcei(stream->source, AL_SOURCE_STATE, &state);
1231 	sound_GetError();
1232 
1233 	if (state != AL_PLAYING && state != AL_PAUSED)
1234 	{
1235 		return false;
1236 	}
1237 
1238 	// Retrieve the amount of buffers which were processed and need refilling
1239 	alGetSourcei(stream->source, AL_BUFFERS_PROCESSED, &buffer_count);
1240 	sound_GetError();
1241 
1242 	// Refill and reattach all buffers
1243 	for (; buffer_count != 0; --buffer_count)
1244 	{
1245 		soundDataBuffer *soundBuffer;
1246 		ALuint buffer;
1247 
1248 		// Retrieve the buffer to work on
1249 		alSourceUnqueueBuffers(stream->source, 1, &buffer);
1250 		sound_GetError();
1251 
1252 		// Decode some data to stuff in our buffer
1253 		soundBuffer = sound_DecodeOggVorbis(stream->decoder, stream->bufferSize);
1254 
1255 		// If we actually decoded some data
1256 		if (soundBuffer && soundBuffer->size > 0)
1257 		{
1258 			// Determine PCM data format
1259 			ALenum format = (soundBuffer->channelCount == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
1260 
1261 			// Insert the data into the buffer
1262 			ASSERT(soundBuffer->size <= static_cast<size_t>(std::numeric_limits<ALsizei>::max()), "soundBuffer->size (%zu) exceeds ALsizei::max", soundBuffer->size);
1263 			alBufferData(buffer, format, soundBuffer->data, static_cast<ALsizei>(soundBuffer->size), soundBuffer->frequency);
1264 			sound_GetError();
1265 
1266 			// Reattach the buffer to the source
1267 			alSourceQueueBuffers(stream->source, 1, &buffer);
1268 			sound_GetError();
1269 		}
1270 		else
1271 		{
1272 			// If no data has been decoded we're probably at the end of our
1273 			// stream. So cleanup this buffer.
1274 
1275 			// Then remove OpenAL's buffer
1276 			alDeleteBuffers(1, &buffer);
1277 			sound_GetError();
1278 		}
1279 
1280 		// Now remove the data buffer itself
1281 		free(soundBuffer);
1282 	}
1283 
1284 	return true;
1285 }
1286 
1287 /** Destroy the given stream and release its associated resources. This function
1288  *  also handles calling of the \c onFinished callback function and closing of
1289  *  the PhysicsFS file handle.
1290  *  \param stream the stream to destroy
1291  */
sound_DestroyStream(AUDIO_STREAM * stream)1292 static void sound_DestroyStream(AUDIO_STREAM *stream)
1293 {
1294 	ALint buffer_count;
1295 	ALuint *buffers;
1296 	bool freeBuffers = false;
1297 	ALint error;
1298 
1299 	// Stop the OpenAL source from playing
1300 	alSourceStop(stream->source);
1301 	error = sound_GetError();
1302 
1303 	if (error != AL_NO_ERROR)
1304 	{
1305 		// FIXME: We should really handle these errors.
1306 	}
1307 
1308 	// Retrieve the amount of buffers which were processed
1309 	alGetSourcei(stream->source, AL_BUFFERS_PROCESSED, &buffer_count);
1310 	error = sound_GetError();
1311 	if (error != AL_NO_ERROR)
1312 	{
1313 		/* FIXME: We're leaking memory and resources here when bailing
1314 		 * out. But not doing so could cause stack overflows as a
1315 		 * result of the below alloca() call (due to buffer_count not
1316 		 * being properly initialised.
1317 		 */
1318 		debug(LOG_SOUND, "alGetSourcei(AL_BUFFERS_PROCESSED) failed; bailing out...");
1319 		return;
1320 	}
1321 
1322 	// Detach all buffers and retrieve their ID numbers
1323 	if (buffer_count > 0)
1324 	{
1325 		if (buffer_count <= (1024 / sizeof(ALuint))) // See CMakeLists.txt for value of -Walloca-larger-than=<N>
1326 		{
1327 			buffers = (ALuint *)alloca(buffer_count * sizeof(ALuint));
1328 		}
1329 		else
1330 		{
1331 			// Too many buffers - don't allocate on the stack!
1332 			buffers = (ALuint *)malloc(buffer_count * sizeof(ALuint));
1333 			freeBuffers = true;
1334 		}
1335 		alSourceUnqueueBuffers(stream->source, buffer_count, buffers);
1336 		sound_GetError();
1337 
1338 		// Destroy all of these buffers
1339 		alDeleteBuffers(buffer_count, buffers);
1340 		sound_GetError();
1341 		if(freeBuffers)
1342 		{
1343 			free(buffers);
1344 		}
1345 		buffers = nullptr;
1346 	}
1347 	else
1348 	{
1349 		// alGetSourcei(AL_BUFFERS_PROCESSED) returned a count <= 0?
1350 		debug(LOG_SOUND, "alGetSourcei(AL_BUFFERS_PROCESSED) returned count: %d", buffer_count);
1351 	}
1352 
1353 	// Destroy the OpenAL source
1354 	alDeleteSources(1, &stream->source);
1355 	sound_GetError();
1356 
1357 	// Destroy the sound decoder
1358 	sound_DestroyOggVorbisDecoder(stream->decoder);
1359 
1360 	// Now close the file
1361 	PHYSFS_close(stream->fileHandle);
1362 
1363 	// Now call the finished callback
1364 	if (stream->onFinished)
1365 	{
1366 		stream->onFinished(stream->user_data);
1367 	}
1368 
1369 	// Free the memory used by this stream
1370 	delete stream;
1371 }
1372 
1373 /** Update all currently running streams and destroy them when they're finished.
1374  */
sound_UpdateStreams()1375 static void sound_UpdateStreams()
1376 {
1377 	AUDIO_STREAM *stream = active_streams, *previous = nullptr, *next = nullptr;
1378 
1379 	while (stream != nullptr)
1380 	{
1381 		next = stream->next;
1382 
1383 		// Attempt to update the current stream, if we find that impossible,
1384 		// destroy it instead.
1385 		if (!sound_UpdateStream(stream))
1386 		{
1387 			// First remove our current stream from the linked list
1388 			if (previous)
1389 			{
1390 				// Make the previous item skip over the current to the next item
1391 				previous->next = next;
1392 			}
1393 			else
1394 			{
1395 				// Apparently this is the first item in the list, so make the
1396 				// next item the list-head.
1397 				active_streams = next;
1398 			}
1399 
1400 			// Now actually destroy the current stream
1401 			sound_DestroyStream(stream);
1402 
1403 			// Make sure the current stream pointer is intact again
1404 			stream = next;
1405 
1406 			// Skip regular style iterator incrementing
1407 			continue;
1408 		}
1409 
1410 		// Increment our iterator pair
1411 		previous = stream;
1412 		stream = stream->next;
1413 	}
1414 }
1415 
1416 //*
1417 // =======================================================================================================================
1418 // =======================================================================================================================
1419 //
sound_StopSample(AUDIO_SAMPLE * psSample)1420 void sound_StopSample(AUDIO_SAMPLE *psSample)
1421 {
1422 	if (psSample->iSample == (ALuint)SAMPLE_NOT_ALLOCATED)
1423 	{
1424 		debug(LOG_SOUND, "sound_StopSample: sample number (%u) out of range, we probably have run out of available OpenAL sources", psSample->iSample);
1425 		return;
1426 	}
1427 	alGetError();	// clear error codes
1428 	// Tell OpenAL to stop playing the given sample
1429 	alSourceStop(psSample->iSample);
1430 	sound_GetError();
1431 }
1432 
sound_SetPlayerPos(Vector3f pos)1433 void sound_SetPlayerPos(Vector3f pos)
1434 {
1435 	alListener3f(AL_POSITION, pos.x, pos.y, pos.z);
1436 	sound_GetError();
1437 }
1438 
1439 /**
1440  * Sets the player's orientation to use for sound
1441  * \param angle the angle in radians
1442  @NOTE the up vector is swapped because of qsound idiosyncrasies
1443  @FIXME we don't use qsound, but it still is in qsound 'format'...
1444 */
sound_SetPlayerOrientation(float angle)1445 void sound_SetPlayerOrientation(float angle)
1446 {
1447 	const ALfloat ori[6] =
1448 	{
1449 		-sinf(angle), cosf(angle), 0.0f,	// forward (at) vector
1450 		0.0f, 0.0f, 1.0f,					// up vector
1451 	};
1452 	alListenerfv(AL_ORIENTATION, ori);
1453 	sound_GetError();
1454 }
1455 
1456 /**
1457  * Sets the player's orientation to use for sound
1458  * \param forward forward pointing vector
1459  * \param up      upward pointing vector
1460  */
sound_SetPlayerOrientationVector(Vector3f forward,Vector3f up)1461 void sound_SetPlayerOrientationVector(Vector3f forward, Vector3f up)
1462 {
1463 	const ALfloat ori[6] =
1464 	{
1465 		forward.x, forward.y, forward.z,
1466 		up.x,      up.y,      up.z,
1467 	};
1468 
1469 	alListenerfv(AL_ORIENTATION, ori);
1470 	sound_GetError();
1471 }
1472 
1473 //*
1474 // =======================================================================================================================
1475 // Compute the sample's volume relative to AL_POSITION.
1476 // =======================================================================================================================
1477 //
sound_SetObjectPosition(AUDIO_SAMPLE * psSample)1478 void sound_SetObjectPosition(AUDIO_SAMPLE *psSample)
1479 {
1480 	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1481 	// coordinates
1482 	float	listenerX, listenerY, listenerZ, dX, dY, dZ;
1483 
1484 	// calculation results
1485 	float	distance, gain;
1486 	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1487 
1488 	// only set it when we have a valid sample
1489 	if (!psSample)
1490 	{
1491 		return;
1492 	}
1493 
1494 	// compute distance
1495 	alGetListener3f(AL_POSITION, &listenerX, &listenerY, &listenerZ);
1496 	sound_GetError();
1497 	dX = psSample->x  - listenerX; // distances on all axis
1498 	dY = psSample->y - listenerY;
1499 	dZ = psSample->z - listenerZ;
1500 	distance = sqrtf(dX * dX + dY * dY + dZ * dZ); // Pythagorean theorem
1501 
1502 	// compute gain
1503 	gain = (1.0f - (distance * ATTENUATION_FACTOR)) * psSample->fVol * sfx3d_volume;
1504 	// max volume
1505 	if (gain > 1.0f)
1506 	{
1507 		gain = 1.0f;
1508 	}
1509 	if (gain < 0.0f)
1510 	{
1511 		// this sample can't be heard right now
1512 		gain = 0.0f;
1513 	}
1514 	alSourcef(psSample->iSample, AL_GAIN, gain);
1515 
1516 	// the alSource3i variant would be better, if it wouldn't provide linker errors however
1517 	alSource3f(psSample->iSample, AL_POSITION, (float)psSample->x, (float)psSample->y, (float)psSample->z);
1518 	sound_GetError();
1519 
1520 	return;
1521 }
1522 
1523 //*
1524 // =======================================================================================================================
1525 // =======================================================================================================================
1526 //
sound_PauseSample(AUDIO_SAMPLE * psSample)1527 void sound_PauseSample(AUDIO_SAMPLE *psSample)
1528 {
1529 	alSourcePause(psSample->iSample);
1530 	sound_GetError();
1531 }
1532 
1533 //*
1534 // =======================================================================================================================
1535 // =======================================================================================================================
1536 //
sound_ResumeSample(AUDIO_SAMPLE * psSample)1537 void sound_ResumeSample(AUDIO_SAMPLE *psSample)
1538 {
1539 	alSourcePlay(psSample->iSample);
1540 	sound_GetError();
1541 }
1542 
1543 //*
1544 // =======================================================================================================================
1545 // =======================================================================================================================
1546 //
sound_PauseAll(void)1547 void sound_PauseAll(void)
1548 {
1549 }
1550 
1551 //*
1552 // =======================================================================================================================
1553 // =======================================================================================================================
1554 //
sound_ResumeAll(void)1555 void sound_ResumeAll(void)
1556 {
1557 }
1558 
1559 //*
1560 // =======================================================================================================================
1561 // =======================================================================================================================
1562 //
sound_StopAll(void)1563 void sound_StopAll(void)
1564 {
1565 }
1566 
1567 //*
1568 // =======================================================================================================================
1569 // =======================================================================================================================
1570 //
sound_SampleIsFinished(AUDIO_SAMPLE * psSample)1571 bool sound_SampleIsFinished(AUDIO_SAMPLE *psSample)
1572 {
1573 	ALenum	state;
1574 
1575 	alGetSourcei(psSample->iSample, AL_SOURCE_STATE, &state);
1576 	sound_GetError(); // check for an error and clear the error state for later on in this function
1577 	if (state == AL_PLAYING || state == AL_PAUSED)
1578 	{
1579 		return false;
1580 	}
1581 
1582 	if (psSample->iSample != (ALuint)AL_INVALID)
1583 	{
1584 		alDeleteSources(1, &(psSample->iSample));
1585 		sound_GetError();
1586 		psSample->iSample = AL_INVALID;
1587 	}
1588 
1589 	return true;
1590 }
1591 
1592 //*
1593 // =======================================================================================================================
1594 // =======================================================================================================================
1595 //
1596 
sound_GetUIVolume()1597 float sound_GetUIVolume()
1598 {
1599 	return sfx_volume;
1600 }
1601 
sound_SetUIVolume(float volume)1602 void sound_SetUIVolume(float volume)
1603 {
1604 	sfx_volume = volume;
1605 
1606 	// Keep volume in the range of 0.0 - 1.0
1607 	if (sfx_volume < 0.0)
1608 	{
1609 		sfx_volume = 0.0;
1610 	}
1611 	else if (sfx_volume > 1.0)
1612 	{
1613 		sfx_volume = 1.0;
1614 	}
1615 }
1616 
sound_GetEffectsVolume()1617 float sound_GetEffectsVolume()
1618 {
1619 	return sfx3d_volume;
1620 }
1621 
sound_SetEffectsVolume(float volume)1622 void sound_SetEffectsVolume(float volume)
1623 {
1624 	sfx3d_volume = volume;
1625 
1626 	// Keep volume in the range of 0.0 - 1.0
1627 	if (sfx3d_volume < 0.0)
1628 	{
1629 		sfx3d_volume = 0.0;
1630 	}
1631 	else if (sfx3d_volume > 1.0)
1632 	{
1633 		sfx3d_volume = 1.0;
1634 	}
1635 }
1636