Panda3D

milesAudioManager.cxx

00001 // Filename: milesAudioManager.cxx
00002 // Created by:  skyler (June 6, 2001)
00003 // Prior system by: cary
00004 //
00005 ////////////////////////////////////////////////////////////////////
00006 //
00007 // PANDA 3D SOFTWARE
00008 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00009 //
00010 // All use of this software is subject to the terms of the revised BSD
00011 // license.  You should have received a copy of this license along
00012 // with this source code in a file named "LICENSE."
00013 //
00014 ////////////////////////////////////////////////////////////////////
00015 
00016 #include "milesAudioManager.h"
00017 
00018 #ifdef HAVE_RAD_MSS //[
00019 
00020 #include "milesAudioSound.h"
00021 #include "milesAudioSample.h"
00022 #include "milesAudioStream.h"
00023 #include "globalMilesManager.h"
00024 #include "config_audio.h"
00025 #include "config_util.h"
00026 #include "config_express.h"
00027 #include "virtualFileSystem.h"
00028 #include "nullAudioSound.h"
00029 #include "string_utils.h"
00030 #include "mutexHolder.h"
00031 #include "lightReMutexHolder.h"
00032 
00033 #include <algorithm>
00034 
00035 
00036 TypeHandle MilesAudioManager::_type_handle;
00037 
00038 AudioManager *Create_MilesAudioManager() {
00039   audio_debug("Create_MilesAudioManager()");
00040   return new MilesAudioManager();
00041 }
00042 
00043 ////////////////////////////////////////////////////////////////////
00044 //     Function: MilesAudioManager::Constructor
00045 //       Access: Public
00046 //  Description: Create an audio manager.  This may open the Miles
00047 //               sound system if there were no other MilesAudioManager
00048 //               instances.  Subsequent managers may use the same
00049 //               Miles resources.
00050 ////////////////////////////////////////////////////////////////////
00051 MilesAudioManager::
00052 MilesAudioManager() : 
00053   _lock("MilesAudioManager::_lock"),
00054   _streams_lock("MilesAudioManager::_streams_lock"),
00055   _streams_cvar(_streams_lock)
00056 {
00057   audio_debug("MilesAudioManager::MilesAudioManager(), this = " 
00058               << (void *)this);
00059   GlobalMilesManager::get_global_ptr()->add_manager(this);
00060   audio_debug("  audio_active="<<audio_active);
00061   audio_debug("  audio_volume="<<audio_volume);
00062   _cleanup_required = true;
00063   _active = audio_active;
00064   _volume = audio_volume;
00065   _play_rate = 1.0f;
00066   _cache_limit = audio_cache_limit;
00067   _concurrent_sound_limit = 0;
00068   _is_valid = true;
00069   _hasMidiSounds = false;
00070   _sounds_finished = false;
00071 
00072   // We used to hang a call to a force-shutdown function on atexit(),
00073   // so that any running sounds (particularly MIDI sounds) would be
00074   // silenced on exit, especially a sudden exit triggered by a Python
00075   // exception.  But that causes problems because Miles itself also
00076   // hangs a force-shutdown function on atexit(), and you can't call
00077   // AIL_cleanup() twice--that results in a crash.
00078 
00079   // Nowadays, we provide the AudioManager::shutdown() method instead,
00080   // which allows the app to force all sounds to stop cleanly before
00081   // we get to the atexit() stack.  In Python, we guarantee that this
00082   // method will be called in the sys.exitfunc chain.
00083 }
00084 
00085 ////////////////////////////////////////////////////////////////////
00086 //     Function: MilesAudioManager::Destructor
00087 //       Access: Public, Virtual
00088 //  Description: Clean up this audio manager and possibly release
00089 //               the Miles resources that are reserved by the 
00090 //               application (the later happens if this is the last
00091 //               active manager).
00092 ////////////////////////////////////////////////////////////////////
00093 MilesAudioManager::
00094 ~MilesAudioManager() {
00095   audio_debug("MilesAudioManager::~MilesAudioManager(), this = " 
00096               << (void *)this);
00097   cleanup();
00098   GlobalMilesManager::get_global_ptr()->remove_manager(this);
00099 
00100   audio_debug("MilesAudioManager::~MilesAudioManager() finished");
00101 }
00102 
00103 ////////////////////////////////////////////////////////////////////
00104 //     Function: MilesAudioManager::shutdown
00105 //       Access: Public, Virtual
00106 //  Description: Call this at exit time to shut down the audio system.
00107 //               This will invalidate all currently-active
00108 //               AudioManagers and AudioSounds in the system.  If you
00109 //               change your mind and want to play sounds again, you
00110 //               will have to recreate all of these objects.
00111 ////////////////////////////////////////////////////////////////////
00112 void MilesAudioManager::
00113 shutdown() {
00114   audio_debug("shutdown() started");
00115   GlobalMilesManager::get_global_ptr()->cleanup();
00116   audio_debug("shutdown() finished");
00117 }
00118 
00119 ////////////////////////////////////////////////////////////////////
00120 //     Function: MilesAudioManager::is_valid
00121 //       Access: Public, Virtual
00122 //  Description: This is mostly for debugging, but it it could be
00123 //               used to detect errors in a release build if you
00124 //               don't mind the cpu cost.
00125 ////////////////////////////////////////////////////////////////////
00126 bool MilesAudioManager::
00127 is_valid() {
00128   LightReMutexHolder holder(_lock);
00129   return do_is_valid();
00130 }
00131 
00132 ////////////////////////////////////////////////////////////////////
00133 //     Function: MilesAudioManager::get_sound
00134 //       Access: Public, Virtual
00135 //  Description:
00136 ////////////////////////////////////////////////////////////////////
00137 PT(AudioSound) MilesAudioManager::
00138 get_sound(const string &file_name, bool, int) {
00139   LightReMutexHolder holder(_lock);
00140   audio_debug("MilesAudioManager::get_sound(file_name=\""<<file_name<<"\")");
00141 
00142   if (!do_is_valid()) {
00143      audio_debug("invalid MilesAudioManager returning NullSound");
00144      return get_null_sound();
00145   }
00146 
00147   Filename path = file_name;
00148 
00149   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00150   vfs->resolve_filename(path, get_model_path());
00151   audio_debug("Reading "<<path);
00152   audio_debug("  resolved file_name is '"<<path<<"'");
00153 
00154   PT(SoundData) sd;
00155   // Get the sound, either from the cache or from a loader:
00156   SoundMap::const_iterator si=_sounds.find(path);
00157   if (si != _sounds.end()) {
00158     // ...found the sound in the cache.
00159     sd = (*si).second;
00160     audio_debug("  sound found in pool 0x" << (void*)sd);
00161 
00162   } else {
00163     // ...the sound was not found in the cache/pool.
00164     sd = load(path);
00165     if (sd != (SoundData *)NULL) {
00166       while (_sounds.size() >= (unsigned int)_cache_limit) {
00167         uncache_a_sound();
00168       }
00169       // Put it in the pool:
00170       // The following is roughly like: _sounds[path] = sd;
00171       // But, it gives us an iterator into the map.
00172       pair<SoundMap::const_iterator, bool> ib
00173           = _sounds.insert(SoundMap::value_type(path, sd));
00174       if (!ib.second) {
00175         // The insert failed.
00176         audio_debug("  failed map insert of "<<path);
00177         nassertr(do_is_valid(), NULL);
00178         return get_null_sound();
00179       }
00180       // Set si, so that we can get a reference to the path
00181       // for the MilesAudioSound.
00182       si=ib.first;
00183     }
00184   }
00185   // Create an AudioSound from the sound:
00186   PT(AudioSound) audioSound;
00187 
00188   if (sd != (SoundData *)NULL) {
00189     most_recently_used((*si).first);
00190     if (sd->_file_type == AILFILETYPE_MIDI ||
00191         sd->_file_type == AILFILETYPE_XMIDI ||
00192         sd->_file_type == AILFILETYPE_XMIDI_DLS ||
00193         sd->_file_type == AILFILETYPE_XMIDI_MLS) {
00194       // MIDI file.
00195       audioSound = new MilesAudioSequence(this, sd, file_name);
00196 
00197     } else if (!sd->_raw_data.empty()) {
00198       // WAV or MP3 file preloaded into memory.
00199       audioSound = new MilesAudioSample(this, sd, file_name);
00200 
00201     } else {
00202       // WAV or MP3 file streamed from disk.
00203       audioSound = new MilesAudioStream(this, file_name, path);
00204     }
00205 
00206     audioSound->set_active(_active);
00207 
00208     bool inserted = _sounds_on_loan.insert((MilesAudioSound *)audioSound.p()).second;
00209     nassertr(inserted, audioSound);
00210 
00211     _hasMidiSounds |= (file_name.find(".mid")!=string::npos);
00212   } else {
00213     // Couldn't load the file; just return a NullAudioSound.
00214     audioSound = new NullAudioSound;
00215   }
00216 
00217   audio_debug("  returning 0x" << (void*)audioSound);
00218   nassertr(do_is_valid(), NULL);
00219   return audioSound;
00220 }
00221 
00222 ////////////////////////////////////////////////////////////////////
00223 //     Function: MilesAudioManager::get_sound
00224 //       Access: Public, Virtual
00225 //  Description:
00226 ////////////////////////////////////////////////////////////////////
00227 PT(AudioSound) MilesAudioManager::
00228 get_sound(MovieAudio *sound, bool, int) {
00229   nassert_raise("Miles audio manager does not support MovieAudio sources.");
00230   return NULL;
00231 }
00232 
00233 ////////////////////////////////////////////////////////////////////
00234 //     Function: MilesAudioManager::uncache_sound
00235 //       Access: Public, Virtual
00236 //  Description:
00237 ////////////////////////////////////////////////////////////////////
00238 void MilesAudioManager::
00239 uncache_sound(const string &file_name) {
00240   audio_debug("MilesAudioManager::uncache_sound(file_name=\""
00241       <<file_name<<"\")");
00242   LightReMutexHolder holder(_lock);
00243   nassertv(do_is_valid());
00244   Filename path = file_name;
00245 
00246   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00247   vfs->resolve_filename(path, get_model_path());
00248 
00249   audio_debug("  path=\""<<path<<"\"");
00250   SoundMap::iterator i = _sounds.find(path);
00251   if (i != _sounds.end()) {
00252     nassertv(_lru.size() > 0);
00253     LRU::iterator lru_i = find(_lru.begin(), _lru.end(), &(i->first));
00254     nassertv(lru_i != _lru.end());
00255     _lru.erase(lru_i);
00256     _sounds.erase(i);
00257   }
00258   nassertv(do_is_valid());
00259 }
00260 
00261 ////////////////////////////////////////////////////////////////////
00262 //     Function: MilesAudioManager::clear_cache
00263 //       Access: Public, Virtual
00264 //  Description: Clear out the sound cache.
00265 ////////////////////////////////////////////////////////////////////
00266 void MilesAudioManager::
00267 clear_cache() {
00268   audio_debug("MilesAudioManager::clear_cache()");
00269   LightReMutexHolder holder(_lock);
00270   do_clear_cache();
00271 }
00272 
00273 ////////////////////////////////////////////////////////////////////
00274 //     Function: MilesAudioManager::set_cache_limit
00275 //       Access: Public, Virtual
00276 //  Description: Set the number of sounds that the cache can hold.
00277 ////////////////////////////////////////////////////////////////////
00278 void MilesAudioManager::
00279 set_cache_limit(unsigned int count) {
00280   LightReMutexHolder holder(_lock);
00281 
00282   audio_debug("MilesAudioManager::set_cache_limit(count="<<count<<")");
00283   nassertv(do_is_valid());
00284   while (_lru.size() > count) {
00285     uncache_a_sound();
00286   }
00287   _cache_limit=count;
00288   nassertv(do_is_valid());
00289 }
00290 
00291 ////////////////////////////////////////////////////////////////////
00292 //     Function: MilesAudioManager::get_cache_limit
00293 //       Access: Public, Virtual
00294 //  Description:
00295 ////////////////////////////////////////////////////////////////////
00296 unsigned int MilesAudioManager::
00297 get_cache_limit() const {
00298   return _cache_limit;
00299 }
00300 
00301 ////////////////////////////////////////////////////////////////////
00302 //     Function: MilesAudioManager::set_volume
00303 //       Access: Public, Virtual
00304 //  Description: set the overall volume
00305 ////////////////////////////////////////////////////////////////////
00306 void MilesAudioManager::
00307 set_volume(float volume) {
00308   audio_debug("MilesAudioManager::set_volume(volume="<<volume<<")");
00309   LightReMutexHolder holder(_lock);
00310   if (_volume != volume) {
00311     _volume = volume;
00312     // Tell our AudioSounds to adjust:
00313     AudioSet::iterator i = _sounds_on_loan.begin();
00314     for (; i!=_sounds_on_loan.end(); ++i) {
00315       (*i)->set_volume((*i)->get_volume());
00316     }
00317   }
00318 }
00319 
00320 ////////////////////////////////////////////////////////////////////
00321 //     Function: MilesAudioManager::get_volume
00322 //       Access: Public, Virtual
00323 //  Description: get the overall volume
00324 ////////////////////////////////////////////////////////////////////
00325 float MilesAudioManager::
00326 get_volume() const {
00327   return _volume;
00328 }
00329 
00330 ////////////////////////////////////////////////////////////////////
00331 //     Function: MilesAudioManager::set_play_rate
00332 //       Access: Public
00333 //  Description: set the overall play rate
00334 ////////////////////////////////////////////////////////////////////
00335 void MilesAudioManager::
00336 set_play_rate(float play_rate) {
00337   audio_debug("MilesAudioManager::set_play_rate(play_rate="<<play_rate<<")");
00338   LightReMutexHolder holder(_lock);
00339   if (_play_rate != play_rate) {
00340     _play_rate = play_rate;
00341     // Tell our AudioSounds to adjust:
00342     AudioSet::iterator i = _sounds_on_loan.begin();
00343     for (; i != _sounds_on_loan.end(); ++i) {
00344       (*i)->set_play_rate((*i)->get_play_rate());
00345     }
00346   }
00347 }
00348 
00349 ////////////////////////////////////////////////////////////////////
00350 //     Function: MilesAudioManager::get_play_rate
00351 //       Access: Public
00352 //  Description: get the overall speed/pitch/play rate
00353 ////////////////////////////////////////////////////////////////////
00354 float MilesAudioManager::
00355 get_play_rate() const {
00356   return _play_rate;
00357 }
00358 
00359 ////////////////////////////////////////////////////////////////////
00360 //     Function: MilesAudioManager::set_active
00361 //       Access: Public, Virtual
00362 //  Description: turn on/off
00363 ////////////////////////////////////////////////////////////////////
00364 void MilesAudioManager::
00365 set_active(bool active) {
00366   audio_debug("MilesAudioManager::set_active(flag="<<active<<")");
00367   LightReMutexHolder holder(_lock);
00368   if (_active != active) {
00369     _active=active;
00370     // Tell our AudioSounds to adjust:
00371     AudioSet::iterator i = _sounds_on_loan.begin();
00372     for (; i != _sounds_on_loan.end(); ++i) {
00373       (*i)->set_active(_active);
00374     }
00375 
00376     if ((!_active) && _hasMidiSounds) {
00377       GlobalMilesManager::get_global_ptr()->force_midi_reset();
00378     }
00379   }
00380 }
00381 
00382 ////////////////////////////////////////////////////////////////////
00383 //     Function: MilesAudioManager::get_active
00384 //       Access: Public, Virtual
00385 //  Description:
00386 ////////////////////////////////////////////////////////////////////
00387 bool MilesAudioManager::
00388 get_active() const {
00389   return _active;
00390 }
00391 
00392 ////////////////////////////////////////////////////////////////////
00393 //     Function: MilesAudioManager::set_concurrent_sound_limit
00394 //       Access: Public, Virtual
00395 //  Description: 
00396 ////////////////////////////////////////////////////////////////////
00397 void MilesAudioManager::
00398 set_concurrent_sound_limit(unsigned int limit) {
00399   LightReMutexHolder holder(_lock);
00400   _concurrent_sound_limit = limit;
00401   do_reduce_sounds_playing_to(_concurrent_sound_limit);
00402 }
00403 
00404 ////////////////////////////////////////////////////////////////////
00405 //     Function: MilesAudioManager::get_concurrent_sound_limit
00406 //       Access: Public, Virtual
00407 //  Description: 
00408 ////////////////////////////////////////////////////////////////////
00409 unsigned int MilesAudioManager::
00410 get_concurrent_sound_limit() const {
00411   return _concurrent_sound_limit;
00412 }
00413 
00414 ////////////////////////////////////////////////////////////////////
00415 //     Function: MilesAudioManager::reduce_sounds_playing_to
00416 //       Access: Public, Virtual
00417 //  Description: 
00418 ////////////////////////////////////////////////////////////////////
00419 void MilesAudioManager::
00420 reduce_sounds_playing_to(unsigned int count) {
00421   LightReMutexHolder holder(_lock);
00422   do_reduce_sounds_playing_to(count);
00423 }
00424 
00425 ////////////////////////////////////////////////////////////////////
00426 //     Function: MilesAudioManager::stop_all_sounds
00427 //       Access: Public, Virtual
00428 //  Description: Stop playback on all sounds managed by this manager.
00429 ////////////////////////////////////////////////////////////////////
00430 void MilesAudioManager::
00431 stop_all_sounds() {
00432   audio_debug("MilesAudioManager::stop_all_sounds()");
00433   reduce_sounds_playing_to(0);
00434 }
00435 
00436 ////////////////////////////////////////////////////////////////////
00437 //     Function: MilesAudioManager::audio_3d_set_listener_attributes
00438 //       Access: Public
00439 //  Description: Set spatial attributes of the listener for 3D
00440 //               sounds.  Note that Y and Z are switched to
00441 //               translate into Miles's coordinate system.
00442 ////////////////////////////////////////////////////////////////////
00443 void MilesAudioManager::audio_3d_set_listener_attributes(float px, float py, float pz, float vx, float vy, float vz, float fx, float fy, float fz, float ux, float uy, float uz) {
00444   audio_debug("MilesAudioManager::audio_3d_set_listener_attributes()");
00445 
00446   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00447   AIL_set_listener_3D_position(mgr->_digital_driver, px, pz, py);
00448   AIL_set_listener_3D_velocity_vector(mgr->_digital_driver, vx, vz, vy);
00449   AIL_set_listener_3D_orientation(mgr->_digital_driver, fx, fz, fy, ux, uz, uy);
00450 }
00451 
00452 ////////////////////////////////////////////////////////////////////
00453 //     Function: MilesAudioManager::audio_3d_get_listener_attributes
00454 //       Access: Public
00455 //  Description: Get spatial attributes of the listener for 3D
00456 //               sounds.  Note that Y and Z are switched to
00457 //               translate from Miles's coordinate system.
00458 ////////////////////////////////////////////////////////////////////
00459 void MilesAudioManager::audio_3d_get_listener_attributes(float *px, float *py, float *pz, float *vx, float *vy, float *vz, float *fx, float *fy, float *fz, float *ux, float *uy, float *uz) {
00460   audio_debug("MilesAudioManager::audio_3d_get_listener_attributes()");
00461 
00462   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00463   AIL_listener_3D_position(mgr->_digital_driver, px, pz, py);
00464   AIL_listener_3D_velocity(mgr->_digital_driver, vx, vz, vy);
00465   AIL_listener_3D_orientation(mgr->_digital_driver, fx, fz, fy, ux, uz, uy);
00466 }
00467 
00468 ////////////////////////////////////////////////////////////////////
00469 //     Function: MilesAudioManager::audio_3d_set_distance_factor
00470 //       Access: Public
00471 //  Description: Set factor to allow user to easily work in a
00472 //               different scale.  1.0 represents meters.
00473 ////////////////////////////////////////////////////////////////////
00474 void MilesAudioManager::audio_3d_set_distance_factor(float factor) {
00475   audio_debug("MilesAudioManager::audio_3d_set_distance_factor( factor= " << factor << ")");
00476 
00477   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00478   AIL_set_3D_distance_factor(mgr->_digital_driver, factor);
00479 }
00480 
00481 ////////////////////////////////////////////////////////////////////
00482 //     Function: MilesAudioManager::audio_3d_get_distance_factor
00483 //       Access: Public
00484 //  Description: Get factor controlling working units.
00485 ////////////////////////////////////////////////////////////////////
00486 float MilesAudioManager::audio_3d_get_distance_factor() const {
00487   audio_debug("MilesAudioManager::audio_3d_get_distance_factor()");
00488 
00489   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00490   return AIL_3D_distance_factor(mgr->_digital_driver);
00491 }
00492 
00493 ////////////////////////////////////////////////////////////////////
00494 //     Function: MilesAudioManager::audio_3d_set_doppler_factor
00495 //       Access: Public
00496 //  Description: Exaggerates or diminishes the Doppler effect. 
00497 //               Defaults to 1.0
00498 ////////////////////////////////////////////////////////////////////
00499 void MilesAudioManager::audio_3d_set_doppler_factor(float factor) {
00500   audio_debug("MilesAudioManager::audio_3d_set_doppler_factor(factor="<<factor<<")");
00501 
00502   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00503   AIL_set_3D_doppler_factor(mgr->_digital_driver, factor);
00504 }
00505 
00506 ////////////////////////////////////////////////////////////////////
00507 //     Function: MilesAudioManager::audio_3d_get_doppler_factor
00508 //       Access: Public
00509 //  Description: Get the factor controlling the Doppler effect.
00510 ////////////////////////////////////////////////////////////////////
00511 float MilesAudioManager::audio_3d_get_doppler_factor() const {
00512   audio_debug("MilesAudioManager::audio_3d_get_doppler_factor()");
00513 
00514   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00515   return AIL_3D_doppler_factor(mgr->_digital_driver);
00516 }
00517 
00518 ////////////////////////////////////////////////////////////////////
00519 //     Function: MilesAudioManager::audio_3d_set_drop_off_factor
00520 //       Access: Public
00521 //  Description: Control the effect distance has on audability.
00522 //               Defaults to 1.0
00523 ////////////////////////////////////////////////////////////////////
00524 void MilesAudioManager::audio_3d_set_drop_off_factor(float factor) {
00525   audio_debug("MilesAudioManager::audio_3d_set_drop_off_factor("<<factor<<")");
00526 
00527   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00528   AIL_set_3D_rolloff_factor(mgr->_digital_driver, factor);
00529 }
00530 
00531 ////////////////////////////////////////////////////////////////////
00532 //     Function: MilesAudioManager::audio_3d_get_drop_off_factor
00533 //       Access: Public
00534 //  Description: Get the factor controlling how quickly sound falls
00535 //               off with distance.
00536 ////////////////////////////////////////////////////////////////////
00537 float MilesAudioManager::audio_3d_get_drop_off_factor() const {
00538   audio_debug("MilesAudioManager::audio_3d_get_drop_off_factor()");
00539 
00540   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00541   return AIL_3D_rolloff_factor(mgr->_digital_driver);
00542 }
00543 
00544 ////////////////////////////////////////////////////////////////////
00545 //     Function: MilesAudioManager::set_speaker_configuration
00546 //       Access: Published
00547 //  Description: Works similarly to MilesAudioSound::set_speaker_levels,
00548 //               but specifies the 3D positions of the speakers in space.
00549 //
00550 //               Once a NULL value is found for a speaker position,
00551 //               no more speaker positions will be used.
00552 //
00553 //               Note that Y and Z are switched to translate from Miles's
00554 //               coordinate system.
00555 ////////////////////////////////////////////////////////////////////
00556 void MilesAudioManager::
00557 set_speaker_configuration(LVecBase3f *speaker1, LVecBase3f *speaker2, LVecBase3f *speaker3, LVecBase3f *speaker4, LVecBase3f *speaker5, LVecBase3f *speaker6, LVecBase3f *speaker7, LVecBase3f *speaker8, LVecBase3f *speaker9) {
00558   audio_debug("MilesAudioManager::set_speaker_configuration()");
00559 
00560   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00561 
00562   MSSVECTOR3D speakers[9];
00563 
00564   if(speaker1 != NULL) {
00565     speakers[0].x = speaker1->get_x();
00566     speakers[0].y = speaker1->get_z();
00567     speakers[0].z = speaker1->get_y();
00568   }
00569   if(speaker2 != NULL) {
00570     speakers[1].x = speaker2->get_x();
00571     speakers[1].y = speaker2->get_z();
00572     speakers[1].z = speaker2->get_y();
00573   }
00574   if(speaker3 != NULL) {
00575     speakers[2].x = speaker3->get_x();
00576     speakers[2].y = speaker3->get_z();
00577     speakers[2].z = speaker3->get_y();
00578   }
00579   if(speaker4 != NULL) {
00580     speakers[3].x = speaker4->get_x();
00581     speakers[3].y = speaker4->get_z();
00582     speakers[3].z = speaker4->get_y();
00583   }
00584   if(speaker5 != NULL) {
00585     speakers[4].x = speaker5->get_x();
00586     speakers[4].y = speaker5->get_z();
00587     speakers[4].z = speaker5->get_y();
00588   }
00589   if(speaker6 != NULL) {
00590     speakers[5].x = speaker6->get_x();
00591     speakers[5].y = speaker6->get_z();
00592     speakers[5].z = speaker6->get_y();
00593   }
00594   if(speaker7 != NULL) {
00595     speakers[6].x = speaker7->get_x();
00596     speakers[6].y = speaker7->get_z();
00597     speakers[6].z = speaker7->get_y();
00598   }
00599   if(speaker8 != NULL) {
00600     speakers[7].x = speaker8->get_x();
00601     speakers[7].y = speaker8->get_z();
00602     speakers[7].z = speaker8->get_y();
00603   }
00604   if(speaker9 != NULL) {
00605     speakers[8].x = speaker9->get_x();
00606     speakers[8].y = speaker9->get_z();
00607     speakers[8].z = speaker9->get_y();
00608   }
00609 
00610   if(speaker1 == NULL) {
00611     audio_error("No valid speaker positions specified in MilesAudioManager::set_speaker_configuration().");
00612   } else if(speaker2 == NULL) {
00613     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 1, 1.0);
00614   } else if(speaker3 == NULL) {
00615     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 2, 1.0);
00616   } else if(speaker4 == NULL) {
00617     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 3, 1.0);
00618   } else if(speaker5 == NULL) {
00619     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 4, 1.0);
00620   } else if(speaker6 == NULL) {
00621     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 5, 1.0);
00622   } else if(speaker7 == NULL) {
00623     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 6, 1.0);
00624   } else if(speaker8 == NULL) {
00625     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 7, 1.0);
00626   } else if(speaker9 == NULL) {
00627     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 8, 1.0);
00628   } else {
00629     AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 9, 1.0);
00630   }
00631 }
00632 
00633 ////////////////////////////////////////////////////////////////////
00634 //     Function: MilesAudioManager::update()
00635 //       Access: Public, Virtual
00636 //  Description: Must be called every frame.  Failure to call this
00637 //               every frame could cause problems for some audio
00638 //               managers.
00639 ////////////////////////////////////////////////////////////////////
00640 void MilesAudioManager::
00641 update() {
00642   {
00643     MutexHolder holder(_streams_lock);
00644     if (_stream_thread.is_null() && !_streams.empty()) {
00645       // If we don't have a sub-thread, we have to service the streams
00646       // in the main thread.
00647       do_service_streams();
00648     }
00649   }
00650 
00651   if (_sounds_finished) {
00652     _sounds_finished = false;
00653     
00654     // If the _sounds_finished flag was set, we should scan our list
00655     // of playing sounds and see if any of them have finished
00656     // recently.  We don't do this in the finished callback, because
00657     // that might have been called in a sub-thread (and we may not
00658     // have threading supported--and mutex protection--compiled in).
00659 
00660     SoundsPlaying::iterator si = _sounds_playing.begin();
00661     while (si != _sounds_playing.end()) {
00662       MilesAudioSound *sound = (*si);
00663       ++si;
00664 
00665       if (sound->status() == AudioSound::READY) {
00666         sound->stop();
00667       }
00668     }
00669   }
00670 }
00671 
00672 ////////////////////////////////////////////////////////////////////
00673 //     Function: MilesAudioManager::release_sound
00674 //       Access: Public
00675 //  Description:
00676 ////////////////////////////////////////////////////////////////////
00677 void MilesAudioManager::
00678 release_sound(MilesAudioSound *audioSound) {
00679   audio_debug("MilesAudioManager::release_sound(audioSound=\""
00680               <<audioSound->get_name()<<"\"), this = " << (void *)this);
00681   LightReMutexHolder holder(_lock);
00682   AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
00683   if (ai != _sounds_on_loan.end()) {
00684     _sounds_on_loan.erase(ai);
00685   }
00686 
00687   audio_debug("MilesAudioManager::release_sound() finished");
00688 }
00689 
00690 ////////////////////////////////////////////////////////////////////
00691 //     Function: MilesAudioManager::cleanup
00692 //       Access: Public
00693 //  Description: Shuts down the audio manager and releases any
00694 //               resources associated with it.  Also cleans up all
00695 //               AudioSounds created via the manager.
00696 ////////////////////////////////////////////////////////////////////
00697 void MilesAudioManager::
00698 cleanup() {
00699   audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this
00700               << ", _cleanup_required = " << _cleanup_required);
00701   LightReMutexHolder holder(_lock);
00702   if (!_cleanup_required) {
00703     return;
00704   }
00705 
00706   // Be sure to cleanup associated sounds before cleaning up the manager:
00707   AudioSet orig_sounds;
00708   orig_sounds.swap(_sounds_on_loan);
00709   AudioSet::iterator ai;
00710   for (ai = orig_sounds.begin(); ai != orig_sounds.end(); ++ai) {
00711     (*ai)->cleanup();
00712   }
00713 
00714   do_clear_cache();
00715 
00716   // Now stop the thread, if it has been started.
00717   if (!_stream_thread.is_null()) {
00718     milesAudio_cat.info()
00719       << "Stopping audio streaming thread.\n";
00720     PT(StreamThread) old_thread;
00721     {
00722       MutexHolder holder(_streams_lock);
00723       nassertv(!_stream_thread.is_null());
00724       _stream_thread->_keep_running = false;
00725       _streams_cvar.notify();
00726       old_thread = _stream_thread;
00727       _stream_thread.clear();
00728     }
00729     old_thread->join();
00730   }
00731 
00732   _cleanup_required = false;
00733   audio_debug("MilesAudioManager::cleanup() finished");
00734 }
00735 
00736 ////////////////////////////////////////////////////////////////////
00737 //     Function: MilesAudioManager::output
00738 //       Access: Public, Virtual
00739 //  Description: 
00740 ////////////////////////////////////////////////////////////////////
00741 void MilesAudioManager::
00742 output(ostream &out) const {
00743   LightReMutexHolder holder(_lock);
00744   out << get_type() << ": " << _sounds_playing.size()
00745       << " / " << _sounds_on_loan.size() << " sounds playing / total"; 
00746 }
00747 
00748 ////////////////////////////////////////////////////////////////////
00749 //     Function: MilesAudioManager::write
00750 //       Access: Public, Virtual
00751 //  Description: 
00752 ////////////////////////////////////////////////////////////////////
00753 void MilesAudioManager::
00754 write(ostream &out) const {
00755   LightReMutexHolder holder(_lock);
00756 
00757   out << (*this) << "\n";
00758   AudioSet::const_iterator ai;
00759   for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) {
00760     MilesAudioSound *sound = (*ai);
00761     out << "  " << *sound << "\n";
00762   }
00763 
00764   size_t total_preload = 0;
00765   size_t num_preloaded = 0;
00766   SoundMap::const_iterator si;
00767   for (si = _sounds.begin(); si != _sounds.end(); ++si) {
00768     if (!(*si).second->_raw_data.empty()) {
00769       ++num_preloaded;
00770       total_preload += (*si).second->_raw_data.size();
00771     }
00772   }
00773   out << num_preloaded << " of " << _sounds.size() << " sounds preloaded, size used is " << (total_preload + 1023) / 1024 << "K\n";
00774 
00775   {
00776     MutexHolder holder(_streams_lock);
00777     out << _streams.size() << " streams opened.\n";
00778     if (!_stream_thread.is_null()) {
00779       out << "(Audio streaming thread has been started.)\n";
00780     }
00781   }
00782 
00783   GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
00784  
00785   int num_samples = mgr->get_num_samples();
00786   out << num_samples << " sample handles allocated globally.\n";
00787 
00788   int num_sequences = mgr->get_num_sequences();
00789   out << num_sequences << " sequence handles allocated globally.\n";
00790 }
00791 
00792 
00793 ////////////////////////////////////////////////////////////////////
00794 //     Function: MilesAudioManager::do_is_valid
00795 //       Access: Private
00796 //  Description: Implementation of is_valid().  Assumes the lock is
00797 //               already held.
00798 ////////////////////////////////////////////////////////////////////
00799 bool MilesAudioManager::
00800 do_is_valid() {
00801   bool check = true;
00802   if (_sounds.size() != _lru.size()) {
00803     audio_debug("-- Error _sounds.size() != _lru.size() --");
00804     check = false;
00805 
00806   } else {
00807     LRU::const_iterator i = _lru.begin();
00808     for (; i != _lru.end(); ++i) {
00809       SoundMap::const_iterator smi = _sounds.find(**i);
00810       if (smi == _sounds.end()) {
00811         audio_debug("-- "<<**i<<" in _lru and not in _sounds --");
00812         check = false;
00813         break;
00814       }
00815     }
00816   }
00817   return _is_valid && check;
00818 }
00819 
00820 ////////////////////////////////////////////////////////////////////
00821 //     Function: MilesAudioManager::do_reduce_sounds_playing_to
00822 //       Access: Private
00823 //  Description: Assumes the lock is already held.
00824 ////////////////////////////////////////////////////////////////////
00825 void MilesAudioManager::
00826 do_reduce_sounds_playing_to(unsigned int count) {
00827   int limit = _sounds_playing.size() - count;
00828   while (limit-- > 0) {
00829     SoundsPlaying::iterator sound = _sounds_playing.begin();
00830     assert(sound != _sounds_playing.end());
00831     (**sound).stop();
00832   }
00833 }
00834 
00835 ////////////////////////////////////////////////////////////////////
00836 //     Function: MilesAudioManager::do_clear_cache
00837 //       Access: Private
00838 //  Description: Assumes the lock is already held.
00839 ////////////////////////////////////////////////////////////////////
00840 void MilesAudioManager::
00841 do_clear_cache() {
00842   if (_is_valid) { nassertv(do_is_valid()); }
00843   _sounds.clear();
00844   _lru.clear();
00845   if (_is_valid) { nassertv(do_is_valid()); }
00846 }
00847 
00848 ////////////////////////////////////////////////////////////////////
00849 //     Function: MilesAudioManager::start_service_stream
00850 //       Access: Private
00851 //  Description: Adds the indicated stream to the list of streams to
00852 //               be serviced by a Panda sub-thread.  This is in lieu
00853 //               of Miles' auto-service-stream mechanism.
00854 ////////////////////////////////////////////////////////////////////
00855 void MilesAudioManager::
00856 start_service_stream(HSTREAM stream) {
00857   MutexHolder holder(_streams_lock);
00858   nassertv(find(_streams.begin(), _streams.end(), stream) == _streams.end());
00859   _streams.push_back(stream);
00860   _streams_cvar.notify();
00861 
00862   if (_stream_thread.is_null() && Thread::is_threading_supported()) {
00863     milesAudio_cat.info()
00864       << "Starting audio streaming thread.\n";
00865     _stream_thread = new StreamThread(this);
00866     _stream_thread->start(TP_low, true);
00867   }
00868 }
00869 
00870 ////////////////////////////////////////////////////////////////////
00871 //     Function: MilesAudioManager::stop_service_stream
00872 //       Access: Private
00873 //  Description: Removes the indicated stream from the list of streams
00874 //               to be serviced by a Panda sub-thread.
00875 ////////////////////////////////////////////////////////////////////
00876 void MilesAudioManager::
00877 stop_service_stream(HSTREAM stream) {
00878   MutexHolder holder(_streams_lock);
00879   Streams::iterator si = find(_streams.begin(), _streams.end(), stream);
00880   if (si != _streams.end()) {
00881     _streams.erase(si);
00882   }
00883 }
00884   
00885 
00886 ////////////////////////////////////////////////////////////////////
00887 //     Function: MilesAudioManager::most_recently_used
00888 //       Access: Private
00889 //  Description: Assumes the lock is already held.
00890 ////////////////////////////////////////////////////////////////////
00891 void MilesAudioManager::
00892 most_recently_used(const string &path) {
00893   audio_debug("MilesAudioManager::most_recently_used(path=\""
00894       <<path<<"\")");
00895   LRU::iterator i=find(_lru.begin(), _lru.end(), &path);
00896   if (i != _lru.end()) {
00897     _lru.erase(i);
00898   }
00899   // At this point, path should not exist in the _lru:
00900   assert(find(_lru.begin(), _lru.end(), &path) == _lru.end());
00901   _lru.push_back(&path);
00902   nassertv(do_is_valid());
00903 }
00904 
00905 ////////////////////////////////////////////////////////////////////
00906 //     Function: MilesAudioManager::uncache_a_sound
00907 //       Access: Private
00908 //  Description: Assumes the lock is already held.
00909 ////////////////////////////////////////////////////////////////////
00910 void MilesAudioManager::
00911 uncache_a_sound() {
00912   audio_debug("MilesAudioManager::uncache_a_sound()");
00913   nassertv(do_is_valid());
00914   // uncache least recently used:
00915   assert(_lru.size()>0);
00916   LRU::reference path=_lru.front();
00917   SoundMap::iterator i = _sounds.find(*path);
00918   assert(i != _sounds.end());
00919   _lru.pop_front();
00920 
00921   if (i != _sounds.end()) {
00922     audio_debug("  uncaching \""<<i->first<<"\"");
00923     _sounds.erase(i);
00924   }
00925   nassertv(do_is_valid());
00926 }
00927 
00928 ////////////////////////////////////////////////////////////////////
00929 //     Function: MilesAudioManager::starting_sound
00930 //       Access: Private
00931 //  Description: Inform the manager that a sound is about to play.
00932 ////////////////////////////////////////////////////////////////////
00933 void MilesAudioManager::
00934 starting_sound(MilesAudioSound *audio) {
00935   LightReMutexHolder holder(_lock);
00936   if (_concurrent_sound_limit) {
00937     do_reduce_sounds_playing_to(_concurrent_sound_limit);
00938   }
00939   _sounds_playing.insert(audio);
00940 }
00941 
00942 ////////////////////////////////////////////////////////////////////
00943 //     Function: MilesAudioManager::stopping_sound
00944 //       Access: Private
00945 //  Description: Inform the manager that a sound is finished or 
00946 //               someone called stop on the sound (this should not
00947 //               be called if a sound is only paused).
00948 ////////////////////////////////////////////////////////////////////
00949 void MilesAudioManager::
00950 stopping_sound(MilesAudioSound *audio) {
00951   LightReMutexHolder holder(_lock);
00952   _sounds_playing.erase(audio);
00953   if (_hasMidiSounds && _sounds_playing.size() == 0) {
00954     GlobalMilesManager::get_global_ptr()->force_midi_reset();
00955   }
00956 }
00957 
00958 ////////////////////////////////////////////////////////////////////
00959 //     Function: MilesAudioManager::load
00960 //       Access: Private
00961 //  Description: Reads a sound file and allocates a SoundData pointer
00962 //               for it.  Returns NULL if the sound file cannot be
00963 //               loaded.
00964 //
00965 //               Assumes the lock is already held.
00966 ////////////////////////////////////////////////////////////////////
00967 PT(MilesAudioManager::SoundData) MilesAudioManager::
00968 load(const Filename &file_name) {
00969   PT(SoundData) sd = new SoundData;
00970 
00971   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00972   PT(VirtualFile) file = vfs->get_file(file_name);
00973   if (file == (VirtualFile *)NULL) {
00974     milesAudio_cat.warning()
00975       << "No such file: " << file_name << "\n";
00976     return NULL;
00977   }
00978 
00979   if (file->get_file_size() == 0) {
00980     milesAudio_cat.warning()
00981       << "File " << file_name << " is empty\n";
00982     return NULL;
00983   }
00984 
00985   sd->_basename = file_name.get_basename();
00986 
00987   string extension = sd->_basename.get_extension();
00988   if (extension == "pz") {
00989     extension = Filename(sd->_basename.get_basename_wo_extension()).get_extension();
00990   }
00991 
00992   bool is_midi_file = (downcase(extension) == "mid");
00993 
00994   if ((miles_audio_preload_threshold == -1 || file->get_file_size() < (off_t)miles_audio_preload_threshold) ||
00995       is_midi_file) {
00996     // If the file is sufficiently small, we'll preload it into
00997     // memory.  MIDI files cannot be streamed, so we always preload
00998     // them, regardless of size.
00999 
01000     if (!file->read_file(sd->_raw_data, true)) {
01001       milesAudio_cat.warning()
01002         << "Unable to read " << file_name << "\n";
01003       return NULL;
01004     }
01005 
01006     sd->_file_type = 
01007       AIL_file_type(&sd->_raw_data[0], sd->_raw_data.size());
01008 
01009     if (sd->_file_type == AILFILETYPE_MIDI) {
01010       // A standard MIDI file.  We have to convert this to XMIDI for
01011       // Miles.
01012       void *xmi;
01013       U32 xmi_size;
01014       if (AIL_MIDI_to_XMI(&sd->_raw_data[0], sd->_raw_data.size(),
01015                           &xmi, &xmi_size, 0)) {
01016         audio_debug("converted " << sd->_basename << " from standard MIDI ("
01017                     << sd->_raw_data.size() << " bytes) to XMIDI ("
01018                     << xmi_size << " bytes)");
01019 
01020         // Copy the data to our own buffer and free the
01021         // Miles-allocated data.
01022         sd->_raw_data.clear();
01023         sd->_raw_data.insert(sd->_raw_data.end(), 
01024                              (unsigned char *)xmi, (unsigned char *)xmi + xmi_size);
01025         AIL_mem_free_lock(xmi);
01026         sd->_file_type = AILFILETYPE_XMIDI;
01027       } else {
01028         milesAudio_cat.warning()
01029           << "Could not convert " << sd->_basename << " to XMIDI.\n";
01030       }
01031     }
01032     
01033     bool expand_to_wav = false;
01034 
01035     if (sd->_file_type != AILFILETYPE_MPEG_L3_AUDIO) {
01036       audio_debug(sd->_basename << " is not an mp3 file.");
01037     } else if ((int)sd->_raw_data.size() >= miles_audio_expand_mp3_threshold) {
01038       audio_debug(sd->_basename << " is too large to expand in-memory.");
01039     } else {
01040       audio_debug(sd->_basename << " will be expanded in-memory.");
01041       expand_to_wav = true;
01042     }
01043     
01044     if (expand_to_wav) {
01045       // Now convert the file to WAV format in-memory.  This is useful
01046       // to work around seek and length problems associated with
01047       // variable bit-rate MP3 encoding.
01048       void *wav_data;
01049       U32 wav_data_size;
01050       if (AIL_decompress_ASI(&sd->_raw_data[0], sd->_raw_data.size(),
01051                              sd->_basename.c_str(), &wav_data, &wav_data_size,
01052                              NULL)) {
01053         audio_debug("expanded " << sd->_basename << " from " << sd->_raw_data.size()
01054                     << " bytes to " << wav_data_size << " bytes.");
01055         
01056         if (wav_data_size != 0) {
01057           // Now copy the memory into our own buffers, and free the
01058           // Miles-allocated memory.
01059           sd->_raw_data.clear();
01060           sd->_raw_data.insert(sd->_raw_data.end(),
01061                                (unsigned char *)wav_data, (unsigned char *)wav_data + wav_data_size);
01062           sd->_file_type = AILFILETYPE_PCM_WAV;
01063           sd->_basename.set_extension("wav");
01064         }
01065         AIL_mem_free_lock(wav_data);
01066         
01067       } else {
01068         audio_debug("unable to expand " << sd->_basename);
01069       }
01070     }
01071 
01072   } else {
01073     // If the file is large, we'll stream it from disk instead of
01074     // preloading it.  This means we don't need to load any data at
01075     // this point.
01076   }
01077 
01078   return sd;
01079 }
01080 
01081 ////////////////////////////////////////////////////////////////////
01082 //     Function: MilesAudioManager::thread_main
01083 //       Access: Private
01084 //  Description: Called to service the streaming audio channels
01085 //               currently playing on the audio manager.
01086 ////////////////////////////////////////////////////////////////////
01087 void MilesAudioManager::
01088 thread_main(volatile bool &keep_running) {
01089   MutexHolder holder(_streams_lock);
01090 
01091   while (keep_running) {
01092     if (_streams.empty()) {
01093       // If there are no streams to service, block on the condition
01094       // variable.
01095       _streams_cvar.wait();
01096     } else {
01097       do_service_streams();
01098     }
01099 
01100     // Now yield to be polite to the main application.
01101     _streams_lock.release();
01102     Thread::force_yield();
01103     _streams_lock.acquire();
01104   }
01105 }
01106 
01107 ////////////////////////////////////////////////////////////////////
01108 //     Function: MilesAudioManager::do_service_streams
01109 //       Access: Private
01110 //  Description: Internal function to service all the streams.
01111 //               Assumes _streams_lock is already held.
01112 ////////////////////////////////////////////////////////////////////
01113 void MilesAudioManager::
01114 do_service_streams() {
01115   size_t i = 0;
01116   while (i < _streams.size()) {
01117     HSTREAM stream = _streams[i];
01118     
01119     // We must release the lock while we are servicing stream i.
01120     _streams_lock.release();
01121     AIL_service_stream(stream, 0);
01122     Thread::consider_yield();
01123     _streams_lock.acquire();
01124     
01125     ++i;
01126   }
01127 }
01128 
01129 ////////////////////////////////////////////////////////////////////
01130 //     Function: MilesAudioManager::StreamThread::Constructor
01131 //       Access: Public
01132 //  Description: 
01133 ////////////////////////////////////////////////////////////////////
01134 MilesAudioManager::StreamThread::
01135 StreamThread(MilesAudioManager *mgr) : 
01136   Thread("StreamThread", "StreamThread"),
01137   _mgr(mgr) 
01138 {
01139   _keep_running = true;
01140 }
01141 
01142 ////////////////////////////////////////////////////////////////////
01143 //     Function: MilesAudioManager::StreamThread::thread_main
01144 //       Access: Public, Virtual
01145 //  Description: 
01146 ////////////////////////////////////////////////////////////////////
01147 void MilesAudioManager::StreamThread::
01148 thread_main() {
01149   _mgr->thread_main(_keep_running);
01150 }
01151 
01152 ////////////////////////////////////////////////////////////////////
01153 //     Function: MilesAudioManager::SoundData::Constructor
01154 //       Access: Public
01155 //  Description: 
01156 ////////////////////////////////////////////////////////////////////
01157 MilesAudioManager::SoundData::
01158 SoundData() :
01159   _raw_data(MilesAudioManager::get_class_type()),
01160   _has_length(false),
01161   _length(0.0f)
01162 {
01163 }
01164 
01165 ////////////////////////////////////////////////////////////////////
01166 //     Function: MilesAudioManager::SoundData::Destructor
01167 //       Access: Public
01168 //  Description: 
01169 ////////////////////////////////////////////////////////////////////
01170 MilesAudioManager::SoundData::
01171 ~SoundData() {
01172 }
01173 
01174 ////////////////////////////////////////////////////////////////////
01175 //     Function: MilesAudioManager::SoundData::get_length
01176 //       Access: Public
01177 //  Description: 
01178 ////////////////////////////////////////////////////////////////////
01179 float MilesAudioManager::SoundData::
01180 get_length() {
01181   if (!_has_length) {
01182     // Time to determine the length of the file.
01183     
01184     if (_raw_data.empty()) {
01185       _length = 0.0f;
01186       _has_length = true;
01187 
01188     } else if (_file_type == AILFILETYPE_MPEG_L3_AUDIO) {
01189       // If it's an mp3 file, we have to calculate its length by
01190       // walking through all of its frames.
01191       audio_debug("Computing length of mp3 file " << _basename);
01192       
01193       MP3_INFO info;
01194       AIL_inspect_MP3(&info, &_raw_data[0], _raw_data.size());
01195       _length = 0.0f;
01196       while (AIL_enumerate_MP3_frames(&info)) {
01197         _length += info.data_size * 8.0f / info.bit_rate;
01198       }
01199       _has_length = true;
01200 
01201     } else if (_file_type == AILFILETYPE_PCM_WAV ||
01202                _file_type == AILFILETYPE_ADPCM_WAV ||
01203                _file_type == AILFILETYPE_OTHER_ASI_WAV) {
01204       audio_debug("Getting length of wav file " << _basename);
01205 
01206       AILSOUNDINFO info;
01207       if (AIL_WAV_info(&_raw_data[0], &info)) {
01208         _length = (float)info.samples / (float)info.rate;
01209         audio_debug(info.samples << " samples at " << info.rate
01210                     << "; length is " << _length << " seconds.");
01211         _has_length = true;
01212       }
01213     }
01214   }
01215 
01216   nassertr(_has_length, 0.0f);
01217   return _length;
01218 }
01219 
01220 ////////////////////////////////////////////////////////////////////
01221 //     Function: MilesAudioManager::SoundData::set_length
01222 //       Access: Public
01223 //  Description: Records the sample length, as determined externally.
01224 ////////////////////////////////////////////////////////////////////
01225 void MilesAudioManager::SoundData::
01226 set_length(float length) {
01227   _length = length;
01228   _has_length = true;
01229 }
01230 
01231 #endif //]
 All Classes Functions Variables Enumerations