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