Panda3D
|
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 //]