Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/audiotraits/fmodAudioManager.cxx

Go to the documentation of this file.
00001 // Filename: fmodAudioManager.cxx
00002 // Created by:  cort (January 22, 2003)
00003 // Prior system by: cary
00004 //
00005 ////////////////////////////////////////////////////////////////////
00006 //
00007 // PANDA 3D SOFTWARE
00008 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00009 //
00010 // All use of this software is subject to the terms of the Panda 3d
00011 // Software license.  You should have received a copy of this license
00012 // along with this source code; you will also find a current copy of
00013 // the license at http://www.panda3d.org/license.txt .
00014 //
00015 // To contact the maintainers of this program write to
00016 // panda3d@yahoogroups.com .
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 //     Function: FmodAudioManager::FmodAudioManager
00046 //       Access: 
00047 //  Description: 
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   // Initialize FMOD, if this is the first manager created.
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   // increment regardless of whether an error has occured -- the
00082   // destructor will do the right thing.
00083   ++_active_managers;
00084   audio_debug("  _active_managers="<<_active_managers);
00085 
00086   if (_is_valid)  {
00087     assert(is_valid());    
00088   }
00089 }
00090 
00091 ////////////////////////////////////////////////////////////////////
00092 //     Function: FmodAudioManager::~FmodAudioManager
00093 //       Access: Public
00094 //  Description: 
00095 ////////////////////////////////////////////////////////////////////
00096 FmodAudioManager::
00097 ~FmodAudioManager() {
00098   // Be sure to delete associated sounds before deleting the manager!
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 //     Function: FmodAudioManager::is_valid
00111 //       Access: Public
00112 //  Description: 
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 //     Function: FmodAudioManager::get_sound
00136 //       Access: Public
00137 //  Description: 
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   // Get the sound, either from the cache or from disk.
00161   SoundMap::iterator si = _sounds.find(path);
00162   SoundCacheEntry *entry = NULL;
00163   if (si != _sounds.end()) {
00164     // The sound was found in the cache.
00165     entry = &(*si).second;
00166     audio_debug("Sound file '"<<path<<"' found in cache.");
00167   } else {
00168     // The sound was not found in the cache.  Load it from disk.
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     // Add to the cache
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     // It's important that we assign entry to the address of the entry
00186     // we just added to the map, and not to the address of the
00187     // temporary variable new_entry, which we just defined locally and
00188     // is about to go out of scope.
00189     entry = &(*si).second;
00190   }
00191   assert(entry != NULL);
00192   
00193   // Create an FMOD object from the memory-mapped file.  Here remains
00194   // one last vestige of special-case MIDI code: apparently, FMOD
00195   // doesn't like creating streams from memory-mapped MIDI files.
00196   // They must therefore be streamed from disk every time.  This
00197   // causes strange things to happen when the same MIDI file is loaded
00198   // twice, and played simultaneously...so, *don't do that then*.  all
00199   // I can say is that MIDI support will be significantly improved in
00200   // FMOD v4.0!
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   // determine length of sound
00220   float length = (float)FSOUND_Stream_GetLengthMs(stream) * 0.001f;
00221 
00222   // Build a new AudioSound from the audio data.
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 //     Function: FmodAudioManager::uncache_sound
00237 //       Access: Public
00238 //  Description: 
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   // Mark the entry as stale -- when its refcount reaches zero, it will
00260   // be removed from the cache.
00261   SoundCacheEntry *entry = &(*itor).second;
00262   if (entry->refcount == 0) {
00263     // If the refcount is already zero, it can be
00264     // purged right now!
00265     audio_debug("FmodAudioManager::uncache_sound: purging "<<path
00266         << " from the cache.");
00267     delete [] entry->data;
00268 
00269     // Erase the sound from the LRU list as well.
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 //     Function: FmodAudioManager::uncache_a_sound
00284 //       Access: Public
00285 //  Description: Uncaches the least recently used sound.
00286 ////////////////////////////////////////////////////////////////////
00287 void FmodAudioManager::
00288 uncache_a_sound() {
00289   audio_debug("FmodAudioManager::uncache_a_sound()");
00290   assert(is_valid());
00291   // uncache least recently used:
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 //     Function: FmodAudioManager::most_recently_used
00307 //       Access: Public
00308 //  Description: Indicates that the given sound was the most recently used.
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   // At this point, path should not exist in the _lru:
00319   assert(find(_lru.begin(), _lru.end(), path) == _lru.end());
00320   _lru.push_back(path);
00321   assert(is_valid());
00322 }
00323 
00324 ////////////////////////////////////////////////////////////////////
00325 //     Function: FmodAudioManager::clear_cache
00326 //       Access: Public
00327 //  Description: 
00328 ////////////////////////////////////////////////////////////////////
00329 void FmodAudioManager::
00330 clear_cache() {
00331   audio_debug("FmodAudioManager::clear_cache()");
00332   // Mark all cache entries as stale.  Delete those which already have 
00333   // refcounts of zero.
00334 
00335   SoundMap::iterator itor = _sounds.begin();
00336 
00337   // Have to use a while loop, not a for loop, since we don't want to
00338   // increment itor in the case in which we delete an entry.
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       // Erase the sound from the LRU list as well.
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 //     Function: FmodAudioManager::set_cache_limit
00363 //       Access: Public
00364 //  Description: 
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 //     Function: FmodAudioManager::get_cache_limit
00382 //       Access: Public
00383 //  Description: 
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 //     Function: FmodAudioManager::release_sound
00394 //       Access: Private
00395 //  Description:
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 //     Function: FmodAudioManager::set_volume
00407 //       Access: Public
00408 //  Description: 
00409 ////////////////////////////////////////////////////////////////////
00410 void FmodAudioManager::
00411 set_volume(float) {
00412   // intentionally blank.
00413 }
00414 
00415 ////////////////////////////////////////////////////////////////////
00416 //     Function: FmodAudioManager::get_volume
00417 //       Access: Public
00418 //  Description: 
00419 ////////////////////////////////////////////////////////////////////
00420 float FmodAudioManager::
00421 get_volume() {
00422   return 0;
00423 }
00424 
00425 ////////////////////////////////////////////////////////////////////
00426 //     Function: FmodAudioManager::set_active
00427 //       Access: Public
00428 //  Description: 
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     // Tell our AudioSounds to adjust:
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 //     Function: FmodAudioManager::get_active
00446 //       Access: Public
00447 //  Description: 
00448 ////////////////////////////////////////////////////////////////////
00449 bool FmodAudioManager::
00450 get_active() {
00451   return _active;
00452 }
00453 
00454 ////////////////////////////////////////////////////////////////////
00455 //     Function: FmodAudioManager::stop_all_sounds
00456 //       Access: Public
00457 //  Description: Stop playback on all sounds managed by this manager.
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 //     Function: FmodAudioManager::inc_refcount
00471 //       Access: Protected
00472 //  Description: Increments the refcount of a file's cache entry.
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; // definitely not stale!
00486   audio_debug("FmodAudioManager: "<<path<<" has a refcount of "
00487           << entry->refcount);
00488 }
00489 
00490 ////////////////////////////////////////////////////////////////////
00491 //     Function: FmodAudioManager::dec_refcount
00492 //       Access: Protected
00493 //  Description: Decrements the refcount of a file's cache entry. If
00494 //               the refcount reaches zero and the entry is stale, it
00495 //               will be removed from the cache.
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       // Erase the sound from the LRU list as well.
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 //     Function: FmodAudioManager::load
00524 //       Access: Private
00525 //  Description: Loads the specified file into memory.  Returns a
00526 //               newly-allocated buffer, and stores the size of the
00527 //               buffer in size.  Returns NULL if an error occurs.
00528 ////////////////////////////////////////////////////////////////////
00529 char* FmodAudioManager::
00530 load(const Filename& filename, size_t &size) const {
00531   // Check file type (based on filename suffix
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   // open the file.
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     // Unable to open.
00574     audio_error("Unable to read " << filename << ".");
00575     return NULL;
00576   }
00577 
00578   // Determine the file size.
00579   audioFile->seekg(0, ios::end);
00580   size = (size_t)audioFile->tellg();
00581   audioFile->seekg(0, ios::beg);
00582   
00583   // Read the entire file into memory.
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 //]

Generated on Fri May 2 00:34:30 2003 for Panda by doxygen1.3