Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "ISoundManager.h"
21 : #include "SoundManager.h"
22 : #include "data/SoundData.h"
23 : #include "items/CBufferItem.h"
24 : #include "items/CSoundItem.h"
25 : #include "items/CStreamItem.h"
26 :
27 : #include "lib/external_libraries/libsdl.h"
28 : #include "ps/CLogger.h"
29 : #include "ps/CStr.h"
30 : #include "ps/ConfigDB.h"
31 : #include "ps/Filesystem.h"
32 : #include "ps/Profiler2.h"
33 : #include "ps/Threading.h"
34 : #include "ps/XML/Xeromyces.h"
35 :
36 : #include <thread>
37 :
38 : ISoundManager* g_SoundManager = NULL;
39 :
40 : #define SOURCE_NUM 64
41 :
42 : #if CONFIG2_AUDIO
43 :
44 : class CSoundManagerWorker
45 : {
46 : NONCOPYABLE(CSoundManagerWorker);
47 :
48 : public:
49 0 : CSoundManagerWorker()
50 0 : {
51 0 : m_Items = new ItemsList;
52 0 : m_DeadItems = new ItemsList;
53 0 : m_Shutdown = false;
54 :
55 0 : m_WorkerThread = std::thread(Threading::HandleExceptions<RunThread>::Wrapper, this);
56 0 : }
57 :
58 0 : ~CSoundManagerWorker()
59 0 : {
60 0 : delete m_Items;
61 0 : CleanupItems();
62 0 : delete m_DeadItems;
63 0 : }
64 :
65 0 : bool Shutdown()
66 : {
67 : {
68 0 : std::lock_guard<std::mutex> lock(m_WorkerMutex);
69 :
70 0 : m_Shutdown = true;
71 :
72 0 : ItemsList::iterator lstr = m_Items->begin();
73 0 : while (lstr != m_Items->end())
74 : {
75 0 : delete *lstr;
76 0 : ++lstr;
77 : }
78 :
79 : }
80 :
81 0 : m_WorkerThread.join();
82 :
83 0 : return true;
84 : }
85 :
86 0 : void addItem(ISoundItem* anItem)
87 : {
88 0 : std::lock_guard<std::mutex> lock(m_WorkerMutex);
89 0 : m_Items->push_back(anItem);
90 0 : }
91 :
92 0 : void CleanupItems()
93 : {
94 0 : std::lock_guard<std::mutex> lock(m_DeadItemsMutex);
95 0 : AL_CHECK;
96 0 : ItemsList::iterator deadItems = m_DeadItems->begin();
97 0 : while (deadItems != m_DeadItems->end())
98 : {
99 0 : delete *deadItems;
100 0 : ++deadItems;
101 :
102 0 : AL_CHECK;
103 : }
104 0 : m_DeadItems->clear();
105 0 : }
106 :
107 : private:
108 0 : static void RunThread(CSoundManagerWorker* data)
109 : {
110 0 : debug_SetThreadName("CSoundManagerWorker");
111 0 : g_Profiler2.RegisterCurrentThread("soundmanager");
112 :
113 0 : data->Run();
114 0 : }
115 :
116 0 : void Run()
117 : {
118 : while (true)
119 : {
120 : // Handle shutdown requests as soon as possible
121 0 : if (GetShutdown())
122 0 : return;
123 :
124 0 : int pauseTime = 500;
125 0 : if (g_SoundManager->InDistress())
126 0 : pauseTime = 50;
127 :
128 : {
129 0 : std::lock_guard<std::mutex> workerLock(m_WorkerMutex);
130 :
131 0 : ItemsList::iterator lstr = m_Items->begin();
132 0 : ItemsList* nextItemList = new ItemsList;
133 :
134 0 : while (lstr != m_Items->end())
135 : {
136 0 : AL_CHECK;
137 0 : if ((*lstr)->IdleTask())
138 : {
139 0 : if ((pauseTime == 500) && (*lstr)->IsFading())
140 0 : pauseTime = 100;
141 :
142 0 : nextItemList->push_back(*lstr);
143 : }
144 : else
145 : {
146 0 : std::lock_guard<std::mutex> deadItemsLock(m_DeadItemsMutex);
147 0 : m_DeadItems->push_back(*lstr);
148 : }
149 0 : ++lstr;
150 :
151 0 : AL_CHECK;
152 : }
153 :
154 0 : delete m_Items;
155 0 : m_Items = nextItemList;
156 :
157 0 : AL_CHECK;
158 : }
159 0 : SDL_Delay(pauseTime);
160 0 : }
161 : }
162 :
163 0 : bool GetShutdown()
164 : {
165 0 : std::lock_guard<std::mutex> lock(m_WorkerMutex);
166 0 : return m_Shutdown;
167 : }
168 :
169 : private:
170 : // Thread-related members:
171 : std::thread m_WorkerThread;
172 : std::mutex m_WorkerMutex;
173 : std::mutex m_DeadItemsMutex;
174 :
175 : // Shared by main thread and worker thread:
176 : // These variables are all protected by a mutexes
177 : ItemsList* m_Items;
178 : ItemsList* m_DeadItems;
179 :
180 : bool m_Shutdown;
181 :
182 : CSoundManagerWorker(ISoundManager* UNUSED(other)){};
183 : };
184 :
185 0 : void ISoundManager::CreateSoundManager()
186 : {
187 0 : if (g_SoundManager)
188 0 : return;
189 :
190 0 : ALCdevice* device = alcOpenDevice(nullptr);
191 0 : if (!device)
192 : {
193 0 : LOGWARNING("No audio device was found.");
194 0 : return;
195 : }
196 :
197 0 : g_SoundManager = new CSoundManager(device);
198 0 : g_SoundManager->StartWorker();
199 : }
200 :
201 0 : void ISoundManager::SetEnabled(bool doEnable)
202 : {
203 0 : if (g_SoundManager && !doEnable)
204 0 : SAFE_DELETE(g_SoundManager);
205 0 : else if (!g_SoundManager && doEnable)
206 0 : ISoundManager::CreateSoundManager();
207 0 : }
208 0 : void ISoundManager::CloseGame()
209 : {
210 0 : if (CSoundManager* aSndMgr = (CSoundManager*)g_SoundManager)
211 0 : aSndMgr->SetAmbientItem(NULL);
212 0 : }
213 :
214 0 : void CSoundManager::al_ReportError(ALenum err, const char* caller, int line)
215 : {
216 0 : LOGERROR("OpenAL error: %s; called from %s (line %d)\n", alGetString(err), caller, line);
217 0 : }
218 :
219 0 : void CSoundManager::al_check(const char* caller, int line)
220 : {
221 0 : ALenum err = alGetError();
222 0 : if (err != AL_NO_ERROR)
223 0 : al_ReportError(err, caller, line);
224 0 : }
225 :
226 0 : Status CSoundManager::ReloadChangedFiles(const VfsPath& UNUSED(path))
227 : {
228 : // TODO implement sound file hotloading
229 0 : return INFO::OK;
230 : }
231 :
232 0 : /*static*/ Status CSoundManager::ReloadChangedFileCB(void* param, const VfsPath& path)
233 : {
234 0 : return static_cast<CSoundManager*>(param)->ReloadChangedFiles(path);
235 : }
236 :
237 0 : CSoundManager::CSoundManager(ALCdevice* device)
238 : : m_Context(nullptr), m_Device(device), m_ALSourceBuffer(nullptr),
239 : m_CurrentTune(nullptr), m_CurrentEnvirons(nullptr),
240 : m_Worker(nullptr), m_DistressMutex(), m_PlayListItems(nullptr), m_SoundGroups(),
241 : m_Gain(.5f), m_MusicGain(.5f), m_AmbientGain(.5f), m_ActionGain(.5f), m_UIGain(.5f),
242 : m_Enabled(false), m_BufferSize(98304), m_BufferCount(50),
243 : m_SoundEnabled(true), m_MusicEnabled(true), m_MusicPaused(false),
244 : m_AmbientPaused(false), m_ActionPaused(false),
245 : m_RunningPlaylist(false), m_PlayingPlaylist(false), m_LoopingPlaylist(false),
246 0 : m_PlaylistGap(0), m_DistressErrCount(0), m_DistressTime(0)
247 : {
248 0 : CFG_GET_VAL("sound.mastergain", m_Gain);
249 0 : CFG_GET_VAL("sound.musicgain", m_MusicGain);
250 0 : CFG_GET_VAL("sound.ambientgain", m_AmbientGain);
251 0 : CFG_GET_VAL("sound.actiongain", m_ActionGain);
252 0 : CFG_GET_VAL("sound.uigain", m_UIGain);
253 :
254 0 : AlcInit();
255 :
256 0 : if (m_Enabled)
257 : {
258 0 : SetMasterGain(m_Gain);
259 0 : InitListener();
260 :
261 0 : m_PlayListItems = new PlayList;
262 : }
263 :
264 0 : if (!CXeromyces::AddValidator(g_VFS, "sound_group", "audio/sound_group.rng"))
265 0 : LOGERROR("CSoundManager: failed to load grammar file 'audio/sound_group.rng'");
266 :
267 0 : RegisterFileReloadFunc(ReloadChangedFileCB, this);
268 :
269 0 : RunHardwareDetection();
270 0 : }
271 :
272 0 : CSoundManager::~CSoundManager()
273 : {
274 0 : UnregisterFileReloadFunc(ReloadChangedFileCB, this);
275 :
276 0 : if (m_Worker)
277 : {
278 0 : AL_CHECK;
279 0 : m_Worker->Shutdown();
280 0 : AL_CHECK;
281 0 : m_Worker->CleanupItems();
282 0 : AL_CHECK;
283 :
284 0 : delete m_Worker;
285 : }
286 0 : AL_CHECK;
287 :
288 0 : for (const std::pair<const std::wstring, CSoundGroup*>& p : m_SoundGroups)
289 0 : delete p.second;
290 0 : m_SoundGroups.clear();
291 :
292 0 : if (m_PlayListItems)
293 0 : delete m_PlayListItems;
294 :
295 0 : if (m_ALSourceBuffer != NULL)
296 0 : delete[] m_ALSourceBuffer;
297 :
298 0 : if (m_Context)
299 0 : alcDestroyContext(m_Context);
300 :
301 0 : if (m_Device)
302 0 : alcCloseDevice(m_Device);
303 0 : }
304 :
305 0 : void CSoundManager::StartWorker()
306 : {
307 0 : if (m_Enabled)
308 0 : m_Worker = new CSoundManagerWorker();
309 0 : }
310 :
311 0 : Status CSoundManager::AlcInit()
312 : {
313 0 : Status ret = INFO::OK;
314 :
315 0 : if(!m_Device)
316 0 : m_Device = alcOpenDevice(nullptr);
317 :
318 0 : if (m_Device)
319 : {
320 0 : ALCint attribs[] = {ALC_STEREO_SOURCES, 16, 0};
321 0 : m_Context = alcCreateContext(m_Device, &attribs[0]);
322 :
323 0 : if (m_Context)
324 : {
325 0 : alcMakeContextCurrent(m_Context);
326 0 : m_ALSourceBuffer = new ALSourceHolder[SOURCE_NUM];
327 0 : ALuint* sourceList = new ALuint[SOURCE_NUM];
328 :
329 0 : alGenSources(SOURCE_NUM, sourceList);
330 0 : ALCenum err = alcGetError(m_Device);
331 :
332 0 : if (err == ALC_NO_ERROR)
333 : {
334 0 : for (int x = 0; x < SOURCE_NUM; x++)
335 : {
336 0 : m_ALSourceBuffer[x].ALSource = sourceList[x];
337 0 : m_ALSourceBuffer[x].SourceItem = NULL;
338 : }
339 0 : m_Enabled = true;
340 : }
341 : else
342 : {
343 0 : LOGERROR("error in gensource = %d", err);
344 : }
345 0 : delete[] sourceList;
346 : }
347 : }
348 :
349 : // check if init succeeded.
350 : // some OpenAL implementations don't indicate failure here correctly;
351 : // we need to check if the device and context pointers are actually valid.
352 0 : ALCenum err = alcGetError(m_Device);
353 0 : const char* dev_name = (const char*)alcGetString(m_Device, ALC_DEVICE_SPECIFIER);
354 :
355 0 : if (err == ALC_NO_ERROR && m_Device && m_Context)
356 0 : debug_printf("Sound: AlcInit success, using %s\n", dev_name);
357 : else
358 : {
359 0 : LOGERROR("Sound: AlcInit failed, m_Device=%p m_Context=%p dev_name=%s err=%x\n", (void *)m_Device, (void *)m_Context, dev_name, err);
360 :
361 : // FIXME Hack to get around exclusive access to the sound device
362 : #if OS_UNIX
363 0 : ret = INFO::OK;
364 : #else
365 : ret = ERR::FAIL;
366 : #endif // !OS_UNIX
367 : }
368 :
369 0 : return ret;
370 : }
371 :
372 0 : bool CSoundManager::InDistress()
373 : {
374 0 : std::lock_guard<std::mutex> lock(m_DistressMutex);
375 :
376 0 : if (m_DistressTime == 0)
377 0 : return false;
378 0 : else if ((timer_Time() - m_DistressTime) > 10)
379 : {
380 0 : m_DistressTime = 0;
381 : // Coming out of distress mode
382 0 : m_DistressErrCount = 0;
383 0 : return false;
384 : }
385 :
386 0 : return true;
387 : }
388 :
389 0 : void CSoundManager::SetDistressThroughShortage()
390 : {
391 0 : std::lock_guard<std::mutex> lock(m_DistressMutex);
392 :
393 : // Going into distress for normal reasons
394 :
395 0 : m_DistressTime = timer_Time();
396 0 : }
397 :
398 0 : void CSoundManager::SetDistressThroughError()
399 : {
400 0 : std::lock_guard<std::mutex> lock(m_DistressMutex);
401 :
402 : // Going into distress due to unknown error
403 :
404 0 : m_DistressTime = timer_Time();
405 0 : m_DistressErrCount++;
406 0 : }
407 :
408 :
409 :
410 0 : ALuint CSoundManager::GetALSource(ISoundItem* anItem)
411 : {
412 0 : for (int x = 0; x < SOURCE_NUM; x++)
413 : {
414 0 : if (!m_ALSourceBuffer[x].SourceItem)
415 : {
416 0 : m_ALSourceBuffer[x].SourceItem = anItem;
417 0 : return m_ALSourceBuffer[x].ALSource;
418 : }
419 : }
420 0 : SetDistressThroughShortage();
421 0 : return 0;
422 : }
423 :
424 0 : void CSoundManager::ReleaseALSource(ALuint theSource)
425 : {
426 0 : for (int x = 0; x < SOURCE_NUM; x++)
427 : {
428 0 : if (m_ALSourceBuffer[x].ALSource == theSource)
429 : {
430 0 : m_ALSourceBuffer[x].SourceItem = NULL;
431 0 : return;
432 : }
433 : }
434 : }
435 :
436 0 : long CSoundManager::GetBufferCount()
437 : {
438 0 : return m_BufferCount;
439 : }
440 0 : long CSoundManager::GetBufferSize()
441 : {
442 0 : return m_BufferSize;
443 : }
444 :
445 0 : void CSoundManager::AddPlayListItem(const VfsPath& itemPath)
446 : {
447 0 : if (m_Enabled)
448 0 : m_PlayListItems->push_back(itemPath);
449 0 : }
450 :
451 0 : void CSoundManager::ClearPlayListItems()
452 : {
453 0 : if (m_Enabled)
454 : {
455 0 : if (m_PlayingPlaylist)
456 0 : SetMusicItem(NULL);
457 :
458 0 : m_PlayingPlaylist = false;
459 0 : m_LoopingPlaylist = false;
460 0 : m_RunningPlaylist = false;
461 :
462 0 : m_PlayListItems->clear();
463 : }
464 0 : }
465 :
466 0 : void CSoundManager::StartPlayList(bool doLoop)
467 : {
468 0 : if (m_Enabled && m_MusicEnabled)
469 : {
470 0 : if (m_PlayListItems->size() > 0)
471 : {
472 0 : m_PlayingPlaylist = true;
473 0 : m_LoopingPlaylist = doLoop;
474 0 : m_RunningPlaylist = false;
475 :
476 0 : ISoundItem* aSnd = LoadItem((m_PlayListItems->at(0)));
477 0 : if (aSnd)
478 0 : SetMusicItem(aSnd);
479 : else
480 0 : SetMusicItem(NULL);
481 : }
482 : }
483 0 : }
484 :
485 0 : void CSoundManager::SetMasterGain(float gain)
486 : {
487 0 : if (m_Enabled)
488 : {
489 0 : m_Gain = gain;
490 0 : alListenerf(AL_GAIN, m_Gain);
491 0 : AL_CHECK;
492 : }
493 0 : }
494 :
495 0 : void CSoundManager::SetMusicGain(float gain)
496 : {
497 0 : m_MusicGain = gain;
498 :
499 0 : if (m_CurrentTune)
500 0 : m_CurrentTune->SetGain(m_MusicGain);
501 0 : }
502 0 : void CSoundManager::SetAmbientGain(float gain)
503 : {
504 0 : m_AmbientGain = gain;
505 0 : }
506 0 : void CSoundManager::SetActionGain(float gain)
507 : {
508 0 : m_ActionGain = gain;
509 0 : }
510 0 : void CSoundManager::SetUIGain(float gain)
511 : {
512 0 : m_UIGain = gain;
513 0 : }
514 :
515 :
516 0 : ISoundItem* CSoundManager::LoadItem(const VfsPath& itemPath)
517 : {
518 0 : AL_CHECK;
519 :
520 0 : if (m_Enabled)
521 : {
522 0 : CSoundData* itemData = CSoundData::SoundDataFromFile(itemPath);
523 :
524 0 : AL_CHECK;
525 0 : if (itemData)
526 0 : return CSoundManager::ItemForData(itemData);
527 : }
528 :
529 0 : return NULL;
530 : }
531 :
532 0 : ISoundItem* CSoundManager::ItemForData(CSoundData* itemData)
533 : {
534 0 : AL_CHECK;
535 0 : ISoundItem* answer = NULL;
536 :
537 0 : AL_CHECK;
538 :
539 0 : if (m_Enabled && (itemData != NULL))
540 : {
541 0 : if (itemData->IsOneShot())
542 : {
543 0 : if (itemData->GetBufferCount() == 1)
544 0 : answer = new CSoundItem(itemData);
545 : else
546 0 : answer = new CBufferItem(itemData);
547 : }
548 : else
549 : {
550 0 : answer = new CStreamItem(itemData);
551 : }
552 :
553 0 : if (answer && m_Worker)
554 0 : m_Worker->addItem(answer);
555 : }
556 :
557 0 : return answer;
558 : }
559 :
560 0 : void CSoundManager::IdleTask()
561 : {
562 0 : if (m_Enabled)
563 : {
564 0 : if (m_CurrentTune)
565 : {
566 0 : m_CurrentTune->EnsurePlay();
567 0 : if (m_PlayingPlaylist && m_RunningPlaylist)
568 : {
569 0 : if (m_CurrentTune->Finished())
570 : {
571 0 : if (m_PlaylistGap == 0)
572 : {
573 0 : m_PlaylistGap = timer_Time() + 15;
574 : }
575 0 : else if (m_PlaylistGap < timer_Time())
576 : {
577 0 : m_PlaylistGap = 0;
578 0 : PlayList::iterator it = find(m_PlayListItems->begin(), m_PlayListItems->end(), m_CurrentTune->GetName());
579 0 : if (it != m_PlayListItems->end())
580 : {
581 0 : ++it;
582 :
583 0 : Path nextPath;
584 0 : if (it == m_PlayListItems->end())
585 0 : nextPath = m_PlayListItems->at(0);
586 : else
587 0 : nextPath = *it;
588 :
589 0 : ISoundItem* aSnd = LoadItem(nextPath);
590 0 : if (aSnd)
591 0 : SetMusicItem(aSnd);
592 : }
593 : }
594 : }
595 : }
596 : }
597 :
598 0 : if (m_CurrentEnvirons)
599 0 : m_CurrentEnvirons->EnsurePlay();
600 :
601 0 : if (m_Worker)
602 0 : m_Worker->CleanupItems();
603 : }
604 0 : }
605 :
606 0 : ISoundItem* CSoundManager::ItemForEntity(entity_id_t UNUSED(source), CSoundData* sndData)
607 : {
608 0 : ISoundItem* currentItem = NULL;
609 :
610 0 : if (m_Enabled)
611 0 : currentItem = ItemForData(sndData);
612 :
613 0 : return currentItem;
614 : }
615 :
616 :
617 0 : void CSoundManager::InitListener()
618 : {
619 0 : ALfloat listenerPos[] = {0.0, 0.0, 0.0};
620 0 : ALfloat listenerVel[] = {0.0, 0.0, 0.0};
621 0 : ALfloat listenerOri[] = {0.0, 0.0, -1.0, 0.0, 1.0, 0.0};
622 :
623 0 : alListenerfv(AL_POSITION, listenerPos);
624 0 : alListenerfv(AL_VELOCITY, listenerVel);
625 0 : alListenerfv(AL_ORIENTATION, listenerOri);
626 :
627 0 : alDistanceModel(AL_LINEAR_DISTANCE);
628 0 : }
629 :
630 0 : void CSoundManager::PlayGroupItem(ISoundItem* anItem, ALfloat groupGain)
631 : {
632 0 : if (anItem)
633 : {
634 0 : if (m_Enabled && (m_ActionGain > 0))
635 : {
636 0 : anItem->SetGain(m_ActionGain * groupGain);
637 0 : anItem->PlayAndDelete();
638 0 : AL_CHECK;
639 : }
640 : }
641 0 : }
642 :
643 0 : void CSoundManager::SetMusicEnabled(bool isEnabled)
644 : {
645 0 : if (m_CurrentTune && !isEnabled)
646 : {
647 0 : m_CurrentTune->FadeAndDelete(1.00);
648 0 : m_CurrentTune = NULL;
649 : }
650 0 : m_MusicEnabled = isEnabled;
651 0 : }
652 :
653 0 : void CSoundManager::PlayAsGroup(const VfsPath& groupPath, const CVector3D& sourcePos, entity_id_t source, bool ownedSound)
654 : {
655 : // Make sure the sound group is loaded
656 : CSoundGroup* group;
657 0 : if (m_SoundGroups.find(groupPath.string()) == m_SoundGroups.end())
658 : {
659 0 : group = new CSoundGroup();
660 0 : if (!group->LoadSoundGroup(L"audio/" + groupPath.string()))
661 : {
662 0 : LOGERROR("Failed to load sound group '%s'", groupPath.string8());
663 0 : delete group;
664 0 : group = NULL;
665 : }
666 : // Cache the sound group (or the null, if it failed)
667 0 : m_SoundGroups[groupPath.string()] = group;
668 : }
669 : else
670 : {
671 0 : group = m_SoundGroups[groupPath.string()];
672 : }
673 :
674 : // Failed to load group -> do nothing
675 0 : if (group && (ownedSound || !group->TestFlag(eOwnerOnly)))
676 0 : group->PlayNext(sourcePos, source);
677 0 : }
678 :
679 0 : void CSoundManager::PlayAsMusic(const VfsPath& itemPath, bool looping)
680 : {
681 0 : if (m_Enabled)
682 : {
683 : UNUSED2(looping);
684 :
685 0 : ISoundItem* aSnd = LoadItem(itemPath);
686 0 : if (aSnd != NULL)
687 0 : SetMusicItem(aSnd);
688 : }
689 0 : }
690 :
691 0 : void CSoundManager::PlayAsAmbient(const VfsPath& itemPath, bool looping)
692 : {
693 0 : if (m_Enabled)
694 : {
695 : UNUSED2(looping);
696 0 : ISoundItem* aSnd = LoadItem(itemPath);
697 0 : if (aSnd != NULL)
698 0 : SetAmbientItem(aSnd);
699 : }
700 0 : }
701 :
702 :
703 0 : void CSoundManager::PlayAsUI(const VfsPath& itemPath, bool looping)
704 : {
705 0 : if (m_Enabled)
706 : {
707 0 : IdleTask();
708 :
709 0 : if (ISoundItem* anItem = LoadItem(itemPath))
710 : {
711 0 : if (m_UIGain > 0)
712 : {
713 0 : anItem->SetGain(m_UIGain);
714 0 : anItem->SetLooping(looping);
715 0 : anItem->PlayAndDelete();
716 : }
717 : }
718 0 : AL_CHECK;
719 : }
720 0 : }
721 :
722 0 : void CSoundManager::Pause(bool pauseIt)
723 : {
724 0 : PauseMusic(pauseIt);
725 0 : PauseAmbient(pauseIt);
726 0 : PauseAction(pauseIt);
727 0 : }
728 :
729 0 : void CSoundManager::PauseMusic(bool pauseIt)
730 : {
731 0 : if (m_CurrentTune && pauseIt && !m_MusicPaused)
732 : {
733 0 : m_CurrentTune->FadeAndPause(1.0);
734 : }
735 0 : else if (m_CurrentTune && m_MusicPaused && !pauseIt && m_MusicEnabled)
736 : {
737 0 : m_CurrentTune->SetGain(0);
738 0 : m_CurrentTune->Resume();
739 0 : m_CurrentTune->FadeToIn(m_MusicGain, 1.0);
740 : }
741 0 : m_MusicPaused = pauseIt;
742 0 : }
743 :
744 0 : void CSoundManager::PauseAmbient(bool pauseIt)
745 : {
746 0 : if (m_CurrentEnvirons && pauseIt)
747 0 : m_CurrentEnvirons->Pause();
748 0 : else if (m_CurrentEnvirons)
749 0 : m_CurrentEnvirons->Resume();
750 :
751 0 : m_AmbientPaused = pauseIt;
752 0 : }
753 :
754 0 : void CSoundManager::PauseAction(bool pauseIt)
755 : {
756 0 : m_ActionPaused = pauseIt;
757 0 : }
758 :
759 0 : void CSoundManager::SetMusicItem(ISoundItem* anItem)
760 : {
761 0 : if (m_Enabled)
762 : {
763 0 : AL_CHECK;
764 0 : if (m_CurrentTune)
765 : {
766 0 : m_CurrentTune->FadeAndDelete(2.00);
767 0 : m_CurrentTune = NULL;
768 : }
769 :
770 0 : IdleTask();
771 :
772 0 : if (anItem)
773 : {
774 0 : if (m_MusicEnabled)
775 : {
776 0 : m_CurrentTune = anItem;
777 0 : m_CurrentTune->SetGain(0);
778 :
779 0 : if (m_PlayingPlaylist)
780 : {
781 0 : m_RunningPlaylist = true;
782 0 : m_CurrentTune->Play();
783 : }
784 : else
785 0 : m_CurrentTune->PlayLoop();
786 :
787 0 : m_MusicPaused = false;
788 0 : m_CurrentTune->FadeToIn(m_MusicGain, 1.00);
789 : }
790 : else
791 : {
792 0 : anItem->StopAndDelete();
793 : }
794 : }
795 0 : AL_CHECK;
796 : }
797 0 : }
798 :
799 0 : void CSoundManager::SetAmbientItem(ISoundItem* anItem)
800 : {
801 0 : if (m_Enabled)
802 : {
803 0 : if (m_CurrentEnvirons)
804 : {
805 0 : m_CurrentEnvirons->FadeAndDelete(3.00);
806 0 : m_CurrentEnvirons = NULL;
807 : }
808 0 : IdleTask();
809 :
810 0 : if (anItem)
811 : {
812 0 : if (m_AmbientGain > 0)
813 : {
814 0 : m_CurrentEnvirons = anItem;
815 0 : m_CurrentEnvirons->SetGain(0);
816 0 : m_CurrentEnvirons->PlayLoop();
817 0 : m_CurrentEnvirons->FadeToIn(m_AmbientGain, 2.00);
818 : }
819 : }
820 0 : AL_CHECK;
821 : }
822 0 : }
823 :
824 0 : void CSoundManager::RunHardwareDetection()
825 : {
826 : // OpenAL alGetString might not return anything interesting on certain platforms
827 : // (see https://stackoverflow.com/questions/28960638 for an example).
828 : // However our previous code supported only Windows, and alGetString does work on
829 : // Windows, so this is an improvement.
830 :
831 : // Sound cards
832 :
833 0 : const ALCchar* devices = nullptr;
834 0 : if (alcIsExtensionPresent(nullptr, "ALC_enumeration_EXT") == AL_TRUE)
835 : {
836 0 : if (alcIsExtensionPresent(nullptr, "ALC_enumerate_all_EXT") == AL_TRUE)
837 0 : devices = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
838 : else
839 0 : devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
840 : }
841 0 : WARN_IF_FALSE(devices);
842 :
843 0 : m_SoundCardNames.clear();
844 0 : do
845 : {
846 0 : m_SoundCardNames += devices;
847 0 : devices += strlen(devices) + 1;
848 0 : m_SoundCardNames += "; ";
849 0 : } while (*devices);
850 :
851 : // Driver version
852 0 : const ALCchar* al_version = alGetString(AL_VERSION);
853 0 : if (al_version)
854 0 : m_OpenALVersion = al_version;
855 0 : }
856 :
857 0 : CStr8 CSoundManager::GetOpenALVersion() const
858 : {
859 0 : return m_OpenALVersion;
860 : }
861 :
862 0 : CStr8 CSoundManager::GetSoundCardNames() const
863 : {
864 0 : return m_SoundCardNames;
865 3 : }
866 :
867 : #else // CONFIG2_AUDIO
868 :
869 : void ISoundManager::CreateSoundManager(){}
870 : void ISoundManager::SetEnabled(bool UNUSED(doEnable)){}
871 : void ISoundManager::CloseGame(){}
872 : void ISoundManager::RunHardwareDetection() {}
873 : CStr8 ISoundManager::GetSoundCardNames() const { return CStr8(); };
874 : CStr8 ISoundManager::GetOpenALVersion() const { return CStr8(); };
875 : #endif // CONFIG2_AUDIO
876 :
|