00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "pandabase.h"
00021 #ifdef HAVE_FMOD //[
00022
00023 #include "config_audio.h"
00024 #include "config_util.h"
00025 #include "config_express.h"
00026 #include "filename.h"
00027 #include "fmodAudioManager.h"
00028 #include "fmodAudioSound.h"
00029 #include "nullAudioSound.h"
00030 #include "virtualFileSystem.h"
00031 #include "string_utils.h"
00032
00033 #include <algorithm>
00034 #include <cctype>
00035 #include <fmod.h>
00036
00037 PT(AudioManager) Create_AudioManager() {
00038 audio_debug("Create_AudioManager() Fmod.");
00039 return new FmodAudioManager;
00040 }
00041
00042 int FmodAudioManager::_active_managers = 0;
00043
00044
00045
00046
00047
00048
00049 FmodAudioManager::
00050 FmodAudioManager() {
00051 audio_debug("FmodAudioManager::FmodAudioManager()");
00052 audio_debug(" audio_active="<<audio_active);
00053 audio_debug(" audio_volume="<<audio_volume);
00054
00055 _active = audio_active;
00056 _cache_limit = audio_cache_limit;
00057
00058
00059 _is_valid = true;
00060 if (_active_managers == 0) {
00061 do {
00062 audio_debug("Initializing FMOD for real.");
00063 float fmod_dll_version = FSOUND_GetVersion();
00064 if (fmod_dll_version < FMOD_VERSION) {
00065 audio_error("Wrong FMOD DLL version. You have "<<fmod_dll_version
00066 <<". You need "<<FMOD_VERSION);
00067 _is_valid = false;
00068 break;
00069 }
00070
00071 if (FSOUND_Init(44100, 32, 0) == 0) {
00072 audio_error("Fmod initialization failure.");
00073 _is_valid = false;
00074 break;
00075 }
00076
00077 }
00078 while(0);
00079 }
00080
00081
00082
00083 ++_active_managers;
00084 audio_debug(" _active_managers="<<_active_managers);
00085
00086 if (_is_valid) {
00087 assert(is_valid());
00088 }
00089 }
00090
00091
00092
00093
00094
00095
00096 FmodAudioManager::
00097 ~FmodAudioManager() {
00098
00099 nassertv(_soundsOnLoan.empty());
00100 clear_cache();
00101 --_active_managers;
00102 audio_debug("~FmodAudioManager(): "<<_active_managers<<" still active");
00103 if (_active_managers == 0) {
00104 audio_debug("Shutting down FMOD");
00105 FSOUND_Close();
00106 }
00107 }
00108
00109
00110
00111
00112
00113
00114 bool FmodAudioManager::
00115 is_valid() {
00116 bool check=true;
00117 if (_sounds.size() != _lru.size()) {
00118 audio_debug("--sizes--");
00119 check=false;
00120 } else {
00121 LRU::const_iterator i=_lru.begin();
00122 for (; i != _lru.end(); ++i) {
00123 SoundMap::const_iterator smi=_sounds.find(*i);
00124 if (smi == _sounds.end()) {
00125 audio_debug("--"<<*i<<"--");
00126 check=false;
00127 break;
00128 }
00129 }
00130 }
00131 return _is_valid && check;
00132 }
00133
00134
00135
00136
00137
00138
00139 PT(AudioSound) FmodAudioManager::
00140 get_sound(const string &file_name) {
00141 audio_debug("FmodAudioManager::get_sound(file_name=\""<<file_name<<"\")");
00142
00143 if(!is_valid()) {
00144 audio_debug("invalid FmodAudioManager returning NullSound");
00145 return get_null_sound();
00146 }
00147
00148 assert(is_valid());
00149 Filename path = file_name;
00150
00151 if (use_vfs) {
00152 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00153 vfs->resolve_filename(path, get_sound_path());
00154 } else {
00155 path.resolve_filename(get_sound_path());
00156 }
00157
00158 audio_debug(" resolved file_name is '"<<path<<"'");
00159
00160
00161 SoundMap::iterator si = _sounds.find(path);
00162 SoundCacheEntry *entry = NULL;
00163 if (si != _sounds.end()) {
00164
00165 entry = &(*si).second;
00166 audio_debug("Sound file '"<<path<<"' found in cache.");
00167 } else {
00168
00169 SoundCacheEntry new_entry;
00170 new_entry.data = load(path, new_entry.size);
00171 if (!new_entry.data) {
00172 audio_error("FmodAudioManager::load failed.");
00173 return get_null_sound();
00174 }
00175 new_entry.refcount = 0;
00176 new_entry.stale = true;
00177
00178
00179 while (_sounds.size() >= (unsigned int)_cache_limit) {
00180 uncache_a_sound();
00181 }
00182
00183 si = _sounds.insert(SoundMap::value_type(path, new_entry)).first;
00184
00185
00186
00187
00188
00189 entry = &(*si).second;
00190 }
00191 assert(entry != NULL);
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201 FSOUND_STREAM *stream = NULL;
00202 int flags = FSOUND_LOADMEMORY | FSOUND_MPEGACCURATE;
00203 string os_path = path.to_os_specific();
00204 string suffix = downcase(path.get_extension());
00205
00206 if (suffix == "mid" || suffix == "rmi" || suffix == "midi") {
00207 stream = FSOUND_Stream_OpenFile(os_path.c_str(), 0, 0);
00208 } else {
00209 stream = FSOUND_Stream_OpenFile((const char*)(entry->data),
00210 flags, entry->size);
00211 }
00212 if (stream == NULL) {
00213 audio_error("FmodAudioManager::get_sound failed.");
00214 return get_null_sound();
00215 }
00216 inc_refcount(path);
00217 most_recently_used(path);
00218
00219
00220 float length = (float)FSOUND_Stream_GetLengthMs(stream) * 0.001f;
00221
00222
00223 PT(AudioSound) audioSound = 0;
00224 PT(FmodAudioSound) fmodAudioSound = new FmodAudioSound(this, stream, path,
00225 length);
00226 fmodAudioSound->set_active(_active);
00227 _soundsOnLoan.insert(fmodAudioSound);
00228 audioSound = fmodAudioSound;
00229
00230 audio_debug(" returning 0x" << (void*)audioSound);
00231 assert(is_valid());
00232 return audioSound;
00233 }
00234
00235
00236
00237
00238
00239
00240 void FmodAudioManager::
00241 uncache_sound(const string& file_name) {
00242 audio_debug("FmodAudioManager::uncache_sound(\""<<file_name<<"\")");
00243 assert(is_valid());
00244 Filename path = file_name;
00245
00246 if (use_vfs) {
00247 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00248 vfs->resolve_filename(path, get_sound_path());
00249 } else {
00250 path.resolve_filename(get_sound_path());
00251 }
00252 audio_debug(" path=\""<<path<<"\"");
00253 SoundMap::iterator itor = _sounds.find(path);
00254 if (itor == _sounds.end()) {
00255 audio_error("FmodAudioManager::uncache_sound: no such entry "<<file_name);
00256 return;
00257 }
00258
00259
00260
00261 SoundCacheEntry *entry = &(*itor).second;
00262 if (entry->refcount == 0) {
00263
00264
00265 audio_debug("FmodAudioManager::uncache_sound: purging "<<path
00266 << " from the cache.");
00267 delete [] entry->data;
00268
00269
00270 assert(_lru.size()>0);
00271 LRU::iterator lru_i=find(_lru.begin(), _lru.end(), itor->first);
00272 assert(lru_i != _lru.end());
00273 _lru.erase(lru_i);
00274 _sounds.erase(itor);
00275 } else {
00276 entry->stale = true;
00277 }
00278
00279 assert(is_valid());
00280 }
00281
00282
00283
00284
00285
00286
00287 void FmodAudioManager::
00288 uncache_a_sound() {
00289 audio_debug("FmodAudioManager::uncache_a_sound()");
00290 assert(is_valid());
00291
00292 assert(_lru.size()>0);
00293 LRU::reference path=_lru.front();
00294 SoundMap::iterator i = _sounds.find(path);
00295 assert(i != _sounds.end());
00296 _lru.pop_front();
00297
00298 if (i != _sounds.end()) {
00299 audio_debug(" uncaching \""<<i->first<<"\"");
00300 uncache_sound(path);
00301 }
00302 assert(is_valid());
00303 }
00304
00305
00306
00307
00308
00309
00310 void FmodAudioManager::
00311 most_recently_used(const string& path) {
00312 audio_debug("FmodAudioManager::most_recently_used(path=\""
00313 <<path<<"\")");
00314 LRU::iterator i=find(_lru.begin(), _lru.end(), path);
00315 if (i != _lru.end()) {
00316 _lru.erase(i);
00317 }
00318
00319 assert(find(_lru.begin(), _lru.end(), path) == _lru.end());
00320 _lru.push_back(path);
00321 assert(is_valid());
00322 }
00323
00324
00325
00326
00327
00328
00329 void FmodAudioManager::
00330 clear_cache() {
00331 audio_debug("FmodAudioManager::clear_cache()");
00332
00333
00334
00335 SoundMap::iterator itor = _sounds.begin();
00336
00337
00338
00339 while (itor != _sounds.end()) {
00340 SoundCacheEntry *entry = &(*itor).second;
00341 if (entry->refcount == 0) {
00342 audio_debug("FmodAudioManager::clear_cache: purging "<< (*itor).first
00343 << " from the cache.");
00344 delete [] entry->data;
00345
00346
00347 assert(_lru.size()>0);
00348 LRU::iterator lru_i=find(_lru.begin(), _lru.end(), itor->first);
00349 assert(lru_i != _lru.end());
00350 _lru.erase(lru_i);
00351 _sounds.erase(itor);
00352
00353 itor = _sounds.begin();
00354 } else {
00355 entry->stale = true;
00356 ++itor;
00357 }
00358 }
00359 }
00360
00361
00362
00363
00364
00365
00366 void FmodAudioManager::
00367 set_cache_limit(int count) {
00368 audio_debug("FmodAudioManager::set_cache_limit(count="
00369 <<count<<")");
00370 assert(is_valid());
00371
00372 while (_lru.size() > (unsigned int) count) {
00373 uncache_a_sound();
00374 }
00375
00376 _cache_limit = count;
00377 assert(is_valid());
00378 }
00379
00380
00381
00382
00383
00384
00385 int FmodAudioManager::
00386 get_cache_limit() {
00387 audio_debug("FmodAudioManager::get_cache_limit() returning "
00388 <<_cache_limit);
00389 return _cache_limit;
00390 }
00391
00392
00393
00394
00395
00396
00397 void FmodAudioManager::
00398 release_sound(FmodAudioSound* audioSound) {
00399 audio_debug("FmodAudioManager::release_sound(audioSound=\""
00400 <<audioSound->get_name()<<"\")");
00401 dec_refcount(audioSound->get_name());
00402 _soundsOnLoan.erase(audioSound);
00403 }
00404
00405
00406
00407
00408
00409
00410 void FmodAudioManager::
00411 set_volume(float) {
00412
00413 }
00414
00415
00416
00417
00418
00419
00420 float FmodAudioManager::
00421 get_volume() {
00422 return 0;
00423 }
00424
00425
00426
00427
00428
00429
00430 void FmodAudioManager::
00431 set_active(bool active) {
00432 audio_debug("FmodAudioManager::set_active(flag="<<active<<")");
00433 if (_active!=active) {
00434 _active=active;
00435
00436 AudioSet::iterator i=_soundsOnLoan.begin();
00437 for (; i!=_soundsOnLoan.end(); ++i) {
00438 (**i).set_active(_active);
00439 }
00440 }
00441 _active = active;
00442 }
00443
00444
00445
00446
00447
00448
00449 bool FmodAudioManager::
00450 get_active() {
00451 return _active;
00452 }
00453
00454
00455
00456
00457
00458
00459 void FmodAudioManager::
00460 stop_all_sounds(void) {
00461 audio_debug("FmodAudioManager::stop_all_sounds()");
00462 AudioSet::iterator i=_soundsOnLoan.begin();
00463 for (; i!=_soundsOnLoan.end(); ++i) {
00464 if((**i).status()==AudioSound::PLAYING)
00465 (**i).stop();
00466 }
00467 }
00468
00469
00470
00471
00472
00473
00474 void FmodAudioManager::
00475 inc_refcount(const string& file_name) {
00476 Filename path = file_name;
00477 SoundMap::iterator itor = _sounds.find(path);
00478 if (itor == _sounds.end()) {
00479 audio_debug("FmodAudioManager::inc_refcount: no such file "<<path);
00480 return;
00481 }
00482
00483 SoundCacheEntry *entry = &(*itor).second;
00484 entry->refcount++;
00485 entry->stale = false;
00486 audio_debug("FmodAudioManager: "<<path<<" has a refcount of "
00487 << entry->refcount);
00488 }
00489
00490
00491
00492
00493
00494
00495
00496
00497 void FmodAudioManager::
00498 dec_refcount(const string& file_name) {
00499 Filename path = file_name;
00500 SoundMap::iterator itor = _sounds.find(path);
00501 if (itor != _sounds.end()) {
00502 SoundCacheEntry *entry = &(*itor).second;
00503 entry->refcount--;
00504 audio_debug("FmodAudioManager: "<<path<<" has a refcount of "
00505 << entry->refcount);
00506 if (entry->refcount == 0 && entry->stale) {
00507 audio_debug("FmodAudioManager::dec_refcount: purging "<<path<< " from the cache.");
00508 delete [] entry->data;
00509
00510
00511 assert(_lru.size()>0);
00512 LRU::iterator lru_i=find(_lru.begin(), _lru.end(), itor->first);
00513 assert(lru_i != _lru.end());
00514 _lru.erase(lru_i);
00515 _sounds.erase(itor);
00516 }
00517 } else {
00518 audio_debug("FmodAudioManager::dec_refcount: no such file "<<path);
00519 }
00520 }
00521
00522
00523
00524
00525
00526
00527
00528
00529 char* FmodAudioManager::
00530 load(const Filename& filename, size_t &size) const {
00531
00532 string suffix = downcase(filename.get_extension());
00533 bool bSupported = false;
00534 if (suffix == "wav" || suffix == "mp3" || suffix == "mid"
00535 || suffix == "rmi" || suffix == "midi") {
00536 bSupported = true;
00537 }
00538 if (!bSupported) {
00539 audio_error("FmodAudioManager::load: "<<filename
00540 <<" is not a supported file format.");
00541 audio_error("Supported formats are: WAV, MP3, MIDI");
00542 return NULL;
00543 }
00544
00545
00546 istream *audioFile = NULL;
00547
00548 Filename binary_filename = Filename::binary_filename(filename);
00549 if (use_vfs) {
00550 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00551
00552 if (!vfs->exists(filename)) {
00553 audio_error("File " << filename << " does not exist.");
00554 return NULL;
00555 }
00556
00557 audioFile = vfs->open_read_file(binary_filename);
00558
00559 } else {
00560 if (!filename.exists()) {
00561 audio_error("File " << filename << " does not exist.");
00562 return NULL;
00563 }
00564
00565 audioFile = new ifstream;
00566 if (!binary_filename.open_read(*(ifstream *)audioFile)) {
00567 delete audioFile;
00568 audioFile = NULL;
00569 }
00570 }
00571
00572 if (audioFile == (istream *)NULL) {
00573
00574 audio_error("Unable to read " << filename << ".");
00575 return NULL;
00576 }
00577
00578
00579 audioFile->seekg(0, ios::end);
00580 size = (size_t)audioFile->tellg();
00581 audioFile->seekg(0, ios::beg);
00582
00583
00584 char *buffer = new char[size];
00585 if (buffer == NULL) {
00586 audio_error("out-of-memory error while loading "<<filename);
00587 delete audioFile;
00588 return NULL;
00589 }
00590 audioFile->read(buffer, size);
00591 if (!(*audioFile)) {
00592 audio_error("Read error while loading "<<filename);
00593 delete audioFile;
00594 delete [] buffer;
00595 return NULL;
00596 }
00597
00598 delete audioFile;
00599 return buffer;
00600 }
00601
00602 #endif //]