1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2011 by The Allacrost Project
3 // Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 // All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software
7 // and you may modify it and/or redistribute it under the terms of this license.
8 // See http://www.gnu.org/copyleft/gpl.html for details.
9 ///////////////////////////////////////////////////////////////////////////////
10
11 /** ****************************************************************************
12 *** \file audio.cpp
13 *** \author Tyler Olsen - roots@allacrost.org
14 *** \author Mois�s Ferrer Serra - byaku@allacrost.org
15 *** \author Aaron Smith - etherstar@allacrost.org
16 *** \author Yohann Ferreira, yohann ferreira orange fr
17 *** \brief Implementation of the audio engine singleton.
18 ***
19 *** The code included here implements the interface of the audio singleton.
20 ***
21 *** \note This code uses the OpenAL audio library. See http://www.openal.com/
22 *** ***************************************************************************/
23
24 #include "engine/audio/audio.h"
25
26 #include "engine/system.h"
27 #include "engine/mode_manager.h"
28
29 #include "utils/utils_strings.h"
30 #include "utils/utils_files.h"
31
32 using namespace vt_utils;
33 using namespace vt_system;
34 using namespace vt_audio::private_audio;
35
36 namespace vt_audio
37 {
38
39 AudioEngine *AudioManager = nullptr;
40 bool AUDIO_DEBUG = false;
41 bool AUDIO_ENABLE = true;
42
AudioEngine()43 AudioEngine::AudioEngine() :
44 _sound_volume(1.0f),
45 _music_volume(1.0f),
46 _device(0),
47 _context(0),
48 _max_sources(MAX_DEFAULT_AUDIO_SOURCES),
49 _active_music(nullptr)
50 {}
51
SingletonInitialize()52 bool AudioEngine::SingletonInitialize()
53 {
54 if(!AUDIO_ENABLE)
55 return true;
56
57 const ALCchar *best_device = 0; // Will store the name of the 'best' device for audio playback
58 ALCint highest_version = 0; // The highest version number found
59 CheckALError(); // Clears errors
60 CheckALCError(); // Clears errors
61
62 // Find the highest-version device available, if the extension for device enumeration is present
63 if(alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") == AL_TRUE) {
64 const ALCchar *device_list = 0;
65 device_list = alcGetString(0, ALC_DEVICE_SPECIFIER); // Get list of all devices (terminated with two '0')
66 if(CheckALCError()) {
67 IF_PRINT_WARNING(AUDIO_DEBUG) << "failed to retrieve the list of available audio devices: " << CreateALCErrorString() << std::endl;
68 }
69
70
71 while(*device_list != 0) { // Check all the detected devices
72 ALCint major_v = 0, minor_v = 0;
73
74 // Open a temporary device for reading in its version number
75 ALCdevice *temp_device = alcOpenDevice(device_list);
76 if(CheckALCError() || temp_device == nullptr) { // If we couldn't open the device, just move on to the next
77 IF_PRINT_WARNING(AUDIO_DEBUG) << "couldn't open device for version checking: " << device_list << std::endl;
78 device_list += strlen(device_list) + 1;
79 continue;
80 }
81
82 // Create a temporary context for the device
83 ALCcontext *temp_context = alcCreateContext(temp_device, 0);
84 if(CheckALCError() || temp_context == nullptr) { // If we couldn't create the context, move on to the next device
85 IF_PRINT_WARNING(AUDIO_DEBUG) << "couldn't create a temporary context for device: " << device_list << std::endl;
86 alcCloseDevice(temp_device);
87 device_list += strlen(device_list) + 1;
88 continue;
89 }
90
91 // Retrieve the version number for the device
92 alcMakeContextCurrent(temp_context);
93
94 alcGetIntegerv(temp_device, ALC_MAJOR_VERSION, sizeof(ALCint), &major_v);
95 alcGetIntegerv(temp_device, ALC_MINOR_VERSION, sizeof(ALCint), &minor_v);
96 alcMakeContextCurrent(0); // Disable the temporary context
97 alcDestroyContext(temp_context); // Destroy the temporary context
98 alcCloseDevice(temp_device); // Close the temporary device
99
100 // Check if a higher version device was found
101 if(highest_version < (major_v * 10 + minor_v)) {
102 highest_version = (major_v * 10 + minor_v);
103 best_device = device_list;
104 }
105 device_list += strlen(device_list) + 1; // Go to the next device name in the list
106 } // while (*device_name != 0)
107 } // if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") == AL_TRUE)
108
109 // Open the 'best' device we found above. If no devices were previously found,
110 // it will try opening the default device (= 0)
111 _device = alcOpenDevice(best_device);
112 if(CheckALCError() || _device == nullptr) {
113 PRINT_ERROR << "failed to open an OpenAL audio device: " << CreateALCErrorString() << std::endl;
114 return false;
115 }
116
117 // Create an OpenAL context
118 _context = alcCreateContext(_device, nullptr);
119 if(CheckALCError() || _context == nullptr) {
120 PRINT_ERROR << "failed to create an OpenAL context: " << CreateALCErrorString() << std::endl;
121 alcCloseDevice(_device);
122 return false;
123 }
124
125 alcMakeContextCurrent(_context);
126 CheckALError(); // Clear errors
127 CheckALCError(); // Clear errors
128
129 // Create as many sources as possible (we fix an upper bound of MAX_DEFAULT_AUDIO_SOURCES)
130 ALuint source;
131 for(uint16_t i = 0; i < _max_sources; ++i) {
132 alGenSources(1, &source);
133 if(CheckALError()) {
134 _max_sources = i;
135 break;
136 }
137 _audio_sources.push_back(new private_audio::AudioSource(source));
138 }
139
140 if(_max_sources == 0) {
141 PRINT_ERROR << "failed to create at least one OpenAL audio source" << std::endl;
142 return false;
143 }
144
145 return true;
146 } // bool AudioEngine::SingletonInitialize()
147
~AudioEngine()148 AudioEngine::~AudioEngine()
149 {
150 if(!AUDIO_ENABLE)
151 return;
152
153 // Delete all entries in the sound cache
154 for(std::map<std::string, private_audio::AudioCacheElement>::iterator i = _audio_cache.begin(); i != _audio_cache.end(); ++i) {
155 delete i->second.audio;
156 }
157 _audio_cache.clear();
158
159 // Delete all audio sources
160 for(std::vector<AudioSource *>::iterator i = _audio_sources.begin(); i != _audio_sources.end(); ++i) {
161 delete(*i);
162 }
163 _audio_sources.clear();
164
165 // We shouldn't have any descriptors registered left,
166 // except when some scripts have created its own descriptors and didn't free them.
167 // So, let's do that now.
168 if(!_registered_sounds.empty()) {
169 PRINT_WARNING << _registered_sounds.size() << " SoundDescriptor objects were still "
170 "registered when the destructor was invoked, "
171 "the objects will be freed now." << std::endl;
172
173 for(std::vector<SoundDescriptor *>::iterator it = _registered_sounds.begin();
174 it != _registered_sounds.end();) {
175 std::string filename = (*it)->GetFilename();
176 if(!filename.empty()) {
177 PRINT_WARNING << "This sound file was never unloaded: "
178 << filename << std::endl;
179 }
180
181 delete *it;
182 it = _registered_sounds.erase(it);
183 }
184 }
185 if(!_registered_music.empty()) {
186 PRINT_WARNING << _registered_music.size() << " MusicDescriptor objects were still "
187 "registered when the destructor was invoked, "
188 "the objects will be freed now." << std::endl;
189 for(std::vector<MusicDescriptor *>::iterator it = _registered_music.begin();
190 it != _registered_music.end();) {
191 std::string filename = (*it)->GetFilename();
192 if(!filename.empty()) {
193 PRINT_WARNING << "This music file was never unloaded: "
194 << filename << std::endl;
195 }
196
197 delete *it;
198 it = _registered_music.erase(it);
199 }
200 }
201
202 alcMakeContextCurrent(0);
203 alcDestroyContext(_context);
204 alcCloseDevice(_device);
205 }
206
Update()207 void AudioEngine::Update()
208 {
209 if(!AUDIO_ENABLE)
210 return;
211
212 for(std::vector<AudioSource *>::iterator i = _audio_sources.begin(); i != _audio_sources.end(); ++i) {
213 if((*i)->owner) {
214 (*i)->owner->_Update();
215 }
216 }
217 }
218
SetSoundVolume(float volume)219 void AudioEngine::SetSoundVolume(float volume)
220 {
221 if(volume < 0.0f) {
222 IF_PRINT_WARNING(AUDIO_DEBUG) << "tried to set sound volume less than 0.0f" << std::endl;
223 _sound_volume = 0.0f;
224 } else if(volume > 1.0f) {
225 IF_PRINT_WARNING(AUDIO_DEBUG) << "tried to set sound volume greater than 1.0f" << std::endl;
226 _sound_volume = 1.0f;
227 } else {
228 _sound_volume = volume;
229 }
230
231 for(std::vector<SoundDescriptor *>::iterator i = _registered_sounds.begin(); i != _registered_sounds.end(); ++i) {
232 if((*i)->_source != nullptr) {
233 alSourcef((*i)->_source->source, AL_GAIN, _sound_volume * (*i)->GetVolume());
234 }
235 }
236 }
237
SetMusicVolume(float volume)238 void AudioEngine::SetMusicVolume(float volume)
239 {
240 if(volume < 0.0f) {
241 IF_PRINT_WARNING(AUDIO_DEBUG) << "tried to set music volume less than 0.0f: " << volume << std::endl;
242 _music_volume = 0.0f;
243 } else if(volume > 1.0f) {
244 IF_PRINT_WARNING(AUDIO_DEBUG) << "tried to set music volume greater than 1.0f: " << volume << std::endl;
245 _music_volume = 1.0f;
246 } else {
247 _music_volume = volume;
248 }
249
250 for(std::vector<MusicDescriptor *>::iterator i = _registered_music.begin(); i != _registered_music.end(); ++i) {
251 if((*i)->_source != nullptr) {
252 alSourcef((*i)->_source->source, AL_GAIN, _music_volume * (*i)->GetVolume());
253 }
254 }
255 }
256
PauseAllSounds()257 void AudioEngine::PauseAllSounds()
258 {
259 for(std::vector<SoundDescriptor *>::iterator i = _registered_sounds.begin();
260 i != _registered_sounds.end(); ++i) {
261 (*i)->Pause();
262 }
263 }
264
ResumeAllSounds()265 void AudioEngine::ResumeAllSounds()
266 {
267 for(std::vector<SoundDescriptor *>::iterator i = _registered_sounds.begin();
268 i != _registered_sounds.end(); ++i) {
269 (*i)->Resume();
270 }
271 }
272
StopAllSounds()273 void AudioEngine::StopAllSounds()
274 {
275 for(std::vector<SoundDescriptor *>::iterator i = _registered_sounds.begin();
276 i != _registered_sounds.end(); ++i) {
277 (*i)->Stop();
278 }
279 }
280
RewindAllSounds()281 void AudioEngine::RewindAllSounds()
282 {
283 for(std::vector<SoundDescriptor *>::iterator i = _registered_sounds.begin();
284 i != _registered_sounds.end(); ++i) {
285 (*i)->Rewind();
286 }
287 }
288
PauseActiveMusic()289 void AudioEngine::PauseActiveMusic()
290 {
291 MusicDescriptor* music = GetActiveMusic();
292 if (music)
293 music->Pause();
294 }
295
ResumeActiveMusic()296 void AudioEngine::ResumeActiveMusic()
297 {
298 MusicDescriptor* music = GetActiveMusic();
299 if (music)
300 music->Resume();
301 }
302
StopActiveMusic()303 void AudioEngine::StopActiveMusic()
304 {
305 MusicDescriptor* music = GetActiveMusic();
306 if (music)
307 music->Stop();
308 }
309
RewindActiveMusic()310 void AudioEngine::RewindActiveMusic()
311 {
312 MusicDescriptor* music = GetActiveMusic();
313 if (music)
314 music->Rewind();
315 }
316
FadeOutActiveMusic(float time)317 void AudioEngine::FadeOutActiveMusic(float time)
318 {
319 MusicDescriptor* music = GetActiveMusic();
320 if (music)
321 music->FadeOut(time);
322 }
323
FadeInActiveMusic(float time)324 void AudioEngine::FadeInActiveMusic(float time)
325 {
326 MusicDescriptor* music = GetActiveMusic();
327 if (music)
328 music->FadeIn(time);
329 }
330
FadeOutAllSounds(float time)331 void AudioEngine::FadeOutAllSounds(float time)
332 {
333 for(std::vector<SoundDescriptor *>::iterator it = _registered_sounds.begin();
334 it != _registered_sounds.end(); ++it) {
335 if(*it)
336 (*it)->FadeOut(time);
337 }
338 }
339
SetListenerPosition(const float position[3])340 void AudioEngine::SetListenerPosition(const float position[3])
341 {
342 alListenerfv(AL_POSITION, position);
343 memcpy(_listener_position, position, sizeof(float) * 3);
344 }
345
SetListenerVelocity(const float velocity[3])346 void AudioEngine::SetListenerVelocity(const float velocity[3])
347 {
348 alListenerfv(AL_VELOCITY, velocity);
349 memcpy(_listener_velocity, velocity, sizeof(float) * 3);
350 }
351
SetListenerOrientation(const float orientation[3])352 void AudioEngine::SetListenerOrientation(const float orientation[3])
353 {
354 alListenerfv(AL_ORIENTATION, orientation);
355 memcpy(_listener_orientation, orientation, sizeof(float) * 3);
356 }
357
GetListenerPosition(float position[3]) const358 void AudioEngine::GetListenerPosition(float position[3]) const
359 {
360 memcpy(position, _listener_position, sizeof(float) * 3);
361 }
362
GetListenerVelocity(float velocity[3]) const363 void AudioEngine::GetListenerVelocity(float velocity[3]) const
364 {
365 memcpy(velocity, _listener_velocity, sizeof(float) * 3);
366 }
367
GetListenerOrientation(float orientation[3]) const368 void AudioEngine::GetListenerOrientation(float orientation[3]) const
369 {
370 memcpy(orientation, _listener_orientation, sizeof(float) * 3);
371 }
372
LoadSound(const std::string & filename,vt_mode_manager::GameMode * gm)373 bool AudioEngine::LoadSound(const std::string &filename, vt_mode_manager::GameMode *gm)
374 {
375 return _LoadAudio(filename, false, gm);
376 }
377
LoadMusic(const std::string & filename,vt_mode_manager::GameMode * gm)378 bool AudioEngine::LoadMusic(const std::string &filename, vt_mode_manager::GameMode *gm)
379 {
380 return _LoadAudio(filename, true, gm);
381 }
382
PlaySound(const std::string & filename)383 void AudioEngine::PlaySound(const std::string &filename)
384 {
385 std::map<std::string, AudioCacheElement>::iterator element = _audio_cache.find(filename);
386
387 if(element == _audio_cache.end()) {
388 // Don't check the current game mode to prevent the sound unloading in certain cases.
389 // We'll let the audio cache handle it all atm.
390 if(!LoadSound(filename)) {
391 IF_PRINT_WARNING(AUDIO_DEBUG)
392 << "could not play sound from cache because "
393 "the sound could not be loaded" << std::endl;
394 return;
395 } else {
396 element = _audio_cache.find(filename);
397 }
398 }
399
400 element->second.audio->Play();
401 element->second.last_update_time = SDL_GetTicks();
402 }
403
PlayMusic(const std::string & filename)404 void AudioEngine::PlayMusic(const std::string &filename)
405 {
406 std::map<std::string, AudioCacheElement>::iterator element = _audio_cache.find(filename);
407
408 if(element == _audio_cache.end()) {
409 // Get the current game mode, so that the loading/freeing micro management
410 // is handled the most possible.
411 vt_mode_manager::GameMode *gm = vt_mode_manager::ModeManager->GetTop();
412 if(!LoadMusic(filename, gm)) {
413 IF_PRINT_WARNING(AUDIO_DEBUG)
414 << "could not play music from cache because "
415 "the music could not be loaded" << std::endl;
416 return;
417 } else {
418 element = _audio_cache.find(filename);
419 }
420 }
421
422 // Special case: the music descriptor object must be taken back:
423 MusicDescriptor *music_audio = reinterpret_cast<MusicDescriptor *>(element->second.audio);
424 if(music_audio) {
425 music_audio->Play();
426 element->second.last_update_time = SDL_GetTicks();
427 }
428 }
429
StopSound(const std::string & filename)430 void AudioEngine::StopSound(const std::string &filename)
431 {
432 std::map<std::string, AudioCacheElement>::iterator element = _audio_cache.find(filename);
433
434 if(element == _audio_cache.end()) {
435 IF_PRINT_WARNING(AUDIO_DEBUG) << "could not stop audio because it was not contained in the cache: " << filename << std::endl;
436 return;
437 }
438
439 element->second.audio->Stop();
440 element->second.last_update_time = SDL_GetTicks();
441 }
442
PauseSound(const std::string & filename)443 void AudioEngine::PauseSound(const std::string &filename)
444 {
445 std::map<std::string, AudioCacheElement>::iterator element = _audio_cache.find(filename);
446
447 if(element == _audio_cache.end()) {
448 IF_PRINT_WARNING(AUDIO_DEBUG) << "could not pause audio because it was not contained in the cache: " << filename << std::endl;
449 return;
450 }
451
452 element->second.audio->Pause();
453 element->second.last_update_time = SDL_GetTicks();
454 }
455
ResumeSound(const std::string & filename)456 void AudioEngine::ResumeSound(const std::string &filename)
457 {
458 std::map<std::string, AudioCacheElement>::iterator element = _audio_cache.find(filename);
459
460 if(element == _audio_cache.end()) {
461 IF_PRINT_WARNING(AUDIO_DEBUG) << "could not resume audio because it was not contained in the cache: " << filename << std::endl;
462 return;
463 }
464
465 element->second.audio->Resume();
466 element->second.last_update_time = SDL_GetTicks();
467 }
468
RetrieveSound(const std::string & filename)469 SoundDescriptor *AudioEngine::RetrieveSound(const std::string &filename)
470 {
471 std::map<std::string, AudioCacheElement>::iterator element = _audio_cache.find(filename);
472
473 if(element == _audio_cache.end()) {
474 return nullptr;
475 } else if(element->second.audio->IsSound() == false) {
476 IF_PRINT_WARNING(AUDIO_DEBUG) << "incorrectly requested to retrieve a sound for a music filename: " << filename << std::endl;
477 return nullptr;
478 } else {
479 return dynamic_cast<SoundDescriptor *>(element->second.audio);
480 }
481 }
482
RetrieveMusic(const std::string & filename)483 MusicDescriptor *AudioEngine::RetrieveMusic(const std::string &filename)
484 {
485 std::map<std::string, AudioCacheElement>::iterator element = _audio_cache.find(filename);
486
487 if(element == _audio_cache.end()) {
488 return nullptr;
489 } else if(element->second.audio->IsSound()) {
490 IF_PRINT_WARNING(AUDIO_DEBUG) << "incorrectly requested to retrieve music for a sound filename: " << filename << std::endl;
491 return nullptr;
492 } else {
493 return dynamic_cast<MusicDescriptor *>(element->second.audio);
494 }
495 }
496
RemoveGameModeOwner(vt_mode_manager::GameMode * gm)497 void AudioEngine::RemoveGameModeOwner(vt_mode_manager::GameMode* gm)
498 {
499 if(!gm)
500 return;
501
502 // Tells all audio descriptor the owner can be removed.
503 std::map<std::string, AudioCacheElement>::iterator it = _audio_cache.begin();
504 for(; it != _audio_cache.end();) {
505 // If the audio buffers are erased, we can remove the descriptor from the cache.
506 if(it->second.audio->RemoveGameModeOwner(gm)) {
507 delete it->second.audio;
508 // Make sure the iterator doesn't get flawed after erase.
509 _audio_cache.erase(it++);
510 } else {
511 ++it;
512 }
513 }
514 }
515
CreateALErrorString()516 const std::string AudioEngine::CreateALErrorString()
517 {
518 switch(_al_error_code) {
519 case AL_NO_ERROR:
520 return "AL_NO_ERROR";
521 case AL_INVALID_NAME:
522 return "AL_INVALID_NAME";
523 case AL_INVALID_ENUM:
524 return "AL_INVALID_ENUM";
525 case AL_INVALID_VALUE:
526 return "AL_INVALID_VALUE";
527 case AL_INVALID_OPERATION:
528 return "AL_INVALID_OPERATION";
529 case AL_OUT_OF_MEMORY:
530 return "AL_OUT_OF_MEMORY";
531 default:
532 return ("Unknown AL error code: " + NumberToString(_al_error_code));
533 }
534 }
535
CreateALCErrorString()536 const std::string AudioEngine::CreateALCErrorString()
537 {
538 switch(_alc_error_code) {
539 case ALC_NO_ERROR:
540 return "ALC_NO_ERROR";
541 case ALC_INVALID_DEVICE:
542 return "ALC_INVALID_DEVICE";
543 case ALC_INVALID_CONTEXT:
544 return "ALC_INVALID_CONTEXT";
545 case ALC_INVALID_ENUM:
546 return "ALC_INVALID_ENUM";
547 case ALC_INVALID_VALUE:
548 return "ALC_INVALID_VALUE";
549 case ALC_OUT_OF_MEMORY:
550 return "ALC_OUT_OF_MEMORY";
551 default:
552 return ("Unknown ALC error code: " + NumberToString(_alc_error_code));
553 }
554 }
555
DEBUG_PrintInfo()556 void AudioEngine::DEBUG_PrintInfo()
557 {
558 const ALCchar *c;
559
560 PRINT_WARNING << "*** Audio Information ***" << std::endl;
561
562 PRINT_WARNING << "Maximum number of sources: " << _max_sources << std::endl;
563 PRINT_WARNING << "Default audio device: " << alcGetString(_device, ALC_DEFAULT_DEVICE_SPECIFIER) << std::endl;
564 PRINT_WARNING << "OpenAL Version: " << alGetString(AL_VERSION) << std::endl;
565 PRINT_WARNING << "OpenAL Renderer: " << alGetString(AL_RENDERER) << std::endl;
566 PRINT_WARNING << "OpenAL Vendor: " << alGetString(AL_VENDOR) << std::endl;
567
568 CheckALError();
569
570 PRINT_WARNING << "Available OpenAL Extensions:" << std::endl;
571 c = alGetString(AL_EXTENSIONS);
572 bool new_extension = true;
573 while(c[0]) {
574 if(new_extension) {
575 PRINT_WARNING << " - ";
576 new_extension = false;
577 continue;
578 } else if(c[0] == ' ') {
579 PRINT_WARNING << std::endl;
580 new_extension = true;
581 c++;
582 continue;
583 }
584
585 PRINT_WARNING << c[0];
586 c++;
587 }
588 }
589
_AcquireAudioSource()590 private_audio::AudioSource* AudioEngine::_AcquireAudioSource()
591 {
592 // Find and return the first source that does not have an owner
593 for(std::vector<AudioSource *>::iterator i = _audio_sources.begin(); i != _audio_sources.end(); ++i) {
594 AudioDescriptor* descriptor = (*i)->owner;
595 if(descriptor == nullptr) {
596 return *i;
597 }
598 else if (descriptor->GetState() == AUDIO_STATE_UNLOADED) {
599 // Test whether the sound is unloaded, meaning the source is available
600 return *i;
601 }
602 }
603
604 // Return nullptr in the (extremely rare) case that all sources are owned and actively playing or paused
605 return nullptr;
606 }
607
608
609
_LoadAudio(const std::string & filename,bool is_music,vt_mode_manager::GameMode * gm)610 bool AudioEngine::_LoadAudio(const std::string &filename, bool is_music, vt_mode_manager::GameMode *gm)
611 {
612 if(!DoesFileExist(filename))
613 return false;
614
615 std::map<std::string, private_audio::AudioCacheElement>::iterator it = _audio_cache.find(filename);
616 if(it != _audio_cache.end()) {
617
618 if (gm)
619 it->second.audio->AddGameModeOwner(gm);
620
621 // Return a success since basically everything will keep on working as expected.
622 return true;
623 }
624
625 // Creates the new audio object and adds its potential game mode owner.
626 AudioDescriptor* audio = nullptr;
627 if (is_music)
628 audio = new MusicDescriptor();
629 else
630 audio = new SoundDescriptor();
631
632 if (gm)
633 audio->AddGameModeOwner(gm);
634
635 // Try loading the audio and adding it in.
636 if (!audio->LoadAudio(filename)) {
637 IF_PRINT_WARNING(AUDIO_DEBUG) << "Could not add new audio file into cache because load operation failed: " << filename << std::endl;
638 delete audio;
639 return false;
640 }
641
642 _audio_cache.insert(std::make_pair(filename, AudioCacheElement(SDL_GetTicks(), audio)));
643 return true;
644 }
645
646 } // namespace vt_audio
647