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(PN_stdfloat 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 PN_stdfloat 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(PN_stdfloat 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 PN_stdfloat 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(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat 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(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz) { 00460 audio_debug("MilesAudioManager::audio_3d_get_listener_attributes()"); 00461 00462 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00463 float lpx, lpy, lpz, lvx, lvy, lvz, lfx, lfy, lfz, lux, luy, luz; 00464 AIL_listener_3D_position(mgr->_digital_driver, &lpx, &lpz, &lpy); 00465 AIL_listener_3D_velocity(mgr->_digital_driver, &lvx, &lvz, &lvy); 00466 AIL_listener_3D_orientation(mgr->_digital_driver, &lfx, &lfz, &lfy, &lux, &luz, &luy); 00467 00468 *px = lpx; 00469 *py = lpy; 00470 *pz = lpz; 00471 *vx = lvx; 00472 *vy = lvy; 00473 *vz = lvz; 00474 *fx = lfx; 00475 *fy = lfy; 00476 *fz = lfz; 00477 *ux = lux; 00478 *uy = luy; 00479 *uz = luz; 00480 } 00481 00482 //////////////////////////////////////////////////////////////////// 00483 // Function: MilesAudioManager::audio_3d_set_distance_factor 00484 // Access: Public 00485 // Description: Set factor to allow user to easily work in a 00486 // different scale. 1.0 represents meters. 00487 //////////////////////////////////////////////////////////////////// 00488 void MilesAudioManager::audio_3d_set_distance_factor(PN_stdfloat factor) { 00489 audio_debug("MilesAudioManager::audio_3d_set_distance_factor( factor= " << factor << ")"); 00490 00491 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00492 AIL_set_3D_distance_factor(mgr->_digital_driver, factor); 00493 } 00494 00495 //////////////////////////////////////////////////////////////////// 00496 // Function: MilesAudioManager::audio_3d_get_distance_factor 00497 // Access: Public 00498 // Description: Get factor controlling working units. 00499 //////////////////////////////////////////////////////////////////// 00500 PN_stdfloat MilesAudioManager::audio_3d_get_distance_factor() const { 00501 audio_debug("MilesAudioManager::audio_3d_get_distance_factor()"); 00502 00503 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00504 return AIL_3D_distance_factor(mgr->_digital_driver); 00505 } 00506 00507 //////////////////////////////////////////////////////////////////// 00508 // Function: MilesAudioManager::audio_3d_set_doppler_factor 00509 // Access: Public 00510 // Description: Exaggerates or diminishes the Doppler effect. 00511 // Defaults to 1.0 00512 //////////////////////////////////////////////////////////////////// 00513 void MilesAudioManager::audio_3d_set_doppler_factor(PN_stdfloat factor) { 00514 audio_debug("MilesAudioManager::audio_3d_set_doppler_factor(factor="<<factor<<")"); 00515 00516 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00517 AIL_set_3D_doppler_factor(mgr->_digital_driver, factor); 00518 } 00519 00520 //////////////////////////////////////////////////////////////////// 00521 // Function: MilesAudioManager::audio_3d_get_doppler_factor 00522 // Access: Public 00523 // Description: Get the factor controlling the Doppler effect. 00524 //////////////////////////////////////////////////////////////////// 00525 PN_stdfloat MilesAudioManager::audio_3d_get_doppler_factor() const { 00526 audio_debug("MilesAudioManager::audio_3d_get_doppler_factor()"); 00527 00528 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00529 return AIL_3D_doppler_factor(mgr->_digital_driver); 00530 } 00531 00532 //////////////////////////////////////////////////////////////////// 00533 // Function: MilesAudioManager::audio_3d_set_drop_off_factor 00534 // Access: Public 00535 // Description: Control the effect distance has on audability. 00536 // Defaults to 1.0 00537 //////////////////////////////////////////////////////////////////// 00538 void MilesAudioManager::audio_3d_set_drop_off_factor(PN_stdfloat factor) { 00539 audio_debug("MilesAudioManager::audio_3d_set_drop_off_factor("<<factor<<")"); 00540 00541 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00542 AIL_set_3D_rolloff_factor(mgr->_digital_driver, factor); 00543 } 00544 00545 //////////////////////////////////////////////////////////////////// 00546 // Function: MilesAudioManager::audio_3d_get_drop_off_factor 00547 // Access: Public 00548 // Description: Get the factor controlling how quickly sound falls 00549 // off with distance. 00550 //////////////////////////////////////////////////////////////////// 00551 PN_stdfloat MilesAudioManager::audio_3d_get_drop_off_factor() const { 00552 audio_debug("MilesAudioManager::audio_3d_get_drop_off_factor()"); 00553 00554 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00555 return AIL_3D_rolloff_factor(mgr->_digital_driver); 00556 } 00557 00558 //////////////////////////////////////////////////////////////////// 00559 // Function: MilesAudioManager::set_speaker_configuration 00560 // Access: Published 00561 // Description: Works similarly to MilesAudioSound::set_speaker_levels, 00562 // but specifies the 3D positions of the speakers in space. 00563 // 00564 // Once a NULL value is found for a speaker position, 00565 // no more speaker positions will be used. 00566 // 00567 // Note that Y and Z are switched to translate from Miles's 00568 // coordinate system. 00569 //////////////////////////////////////////////////////////////////// 00570 void MilesAudioManager:: 00571 set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) { 00572 audio_debug("MilesAudioManager::set_speaker_configuration()"); 00573 00574 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00575 00576 MSSVECTOR3D speakers[9]; 00577 00578 if(speaker1 != NULL) { 00579 speakers[0].x = speaker1->get_x(); 00580 speakers[0].y = speaker1->get_z(); 00581 speakers[0].z = speaker1->get_y(); 00582 } 00583 if(speaker2 != NULL) { 00584 speakers[1].x = speaker2->get_x(); 00585 speakers[1].y = speaker2->get_z(); 00586 speakers[1].z = speaker2->get_y(); 00587 } 00588 if(speaker3 != NULL) { 00589 speakers[2].x = speaker3->get_x(); 00590 speakers[2].y = speaker3->get_z(); 00591 speakers[2].z = speaker3->get_y(); 00592 } 00593 if(speaker4 != NULL) { 00594 speakers[3].x = speaker4->get_x(); 00595 speakers[3].y = speaker4->get_z(); 00596 speakers[3].z = speaker4->get_y(); 00597 } 00598 if(speaker5 != NULL) { 00599 speakers[4].x = speaker5->get_x(); 00600 speakers[4].y = speaker5->get_z(); 00601 speakers[4].z = speaker5->get_y(); 00602 } 00603 if(speaker6 != NULL) { 00604 speakers[5].x = speaker6->get_x(); 00605 speakers[5].y = speaker6->get_z(); 00606 speakers[5].z = speaker6->get_y(); 00607 } 00608 if(speaker7 != NULL) { 00609 speakers[6].x = speaker7->get_x(); 00610 speakers[6].y = speaker7->get_z(); 00611 speakers[6].z = speaker7->get_y(); 00612 } 00613 if(speaker8 != NULL) { 00614 speakers[7].x = speaker8->get_x(); 00615 speakers[7].y = speaker8->get_z(); 00616 speakers[7].z = speaker8->get_y(); 00617 } 00618 if(speaker9 != NULL) { 00619 speakers[8].x = speaker9->get_x(); 00620 speakers[8].y = speaker9->get_z(); 00621 speakers[8].z = speaker9->get_y(); 00622 } 00623 00624 if(speaker1 == NULL) { 00625 audio_error("No valid speaker positions specified in MilesAudioManager::set_speaker_configuration()."); 00626 } else if(speaker2 == NULL) { 00627 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 1, 1.0); 00628 } else if(speaker3 == NULL) { 00629 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 2, 1.0); 00630 } else if(speaker4 == NULL) { 00631 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 3, 1.0); 00632 } else if(speaker5 == NULL) { 00633 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 4, 1.0); 00634 } else if(speaker6 == NULL) { 00635 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 5, 1.0); 00636 } else if(speaker7 == NULL) { 00637 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 6, 1.0); 00638 } else if(speaker8 == NULL) { 00639 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 7, 1.0); 00640 } else if(speaker9 == NULL) { 00641 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 8, 1.0); 00642 } else { 00643 AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 9, 1.0); 00644 } 00645 } 00646 00647 //////////////////////////////////////////////////////////////////// 00648 // Function: MilesAudioManager::update() 00649 // Access: Public, Virtual 00650 // Description: Must be called every frame. Failure to call this 00651 // every frame could cause problems for some audio 00652 // managers. 00653 //////////////////////////////////////////////////////////////////// 00654 void MilesAudioManager:: 00655 update() { 00656 { 00657 MutexHolder holder(_streams_lock); 00658 if (_stream_thread.is_null() && !_streams.empty()) { 00659 // If we don't have a sub-thread, we have to service the streams 00660 // in the main thread. 00661 do_service_streams(); 00662 } 00663 } 00664 00665 if (_sounds_finished) { 00666 _sounds_finished = false; 00667 00668 // If the _sounds_finished flag was set, we should scan our list 00669 // of playing sounds and see if any of them have finished 00670 // recently. We don't do this in the finished callback, because 00671 // that might have been called in a sub-thread (and we may not 00672 // have threading supported--and mutex protection--compiled in). 00673 00674 SoundsPlaying::iterator si = _sounds_playing.begin(); 00675 while (si != _sounds_playing.end()) { 00676 MilesAudioSound *sound = (*si); 00677 ++si; 00678 00679 if (sound->status() == AudioSound::READY) { 00680 sound->stop(); 00681 } 00682 } 00683 } 00684 } 00685 00686 //////////////////////////////////////////////////////////////////// 00687 // Function: MilesAudioManager::release_sound 00688 // Access: Public 00689 // Description: 00690 //////////////////////////////////////////////////////////////////// 00691 void MilesAudioManager:: 00692 release_sound(MilesAudioSound *audioSound) { 00693 audio_debug("MilesAudioManager::release_sound(audioSound=\"" 00694 <<audioSound->get_name()<<"\"), this = " << (void *)this); 00695 LightReMutexHolder holder(_lock); 00696 AudioSet::iterator ai = _sounds_on_loan.find(audioSound); 00697 if (ai != _sounds_on_loan.end()) { 00698 _sounds_on_loan.erase(ai); 00699 } 00700 00701 audio_debug("MilesAudioManager::release_sound() finished"); 00702 } 00703 00704 //////////////////////////////////////////////////////////////////// 00705 // Function: MilesAudioManager::cleanup 00706 // Access: Public 00707 // Description: Shuts down the audio manager and releases any 00708 // resources associated with it. Also cleans up all 00709 // AudioSounds created via the manager. 00710 //////////////////////////////////////////////////////////////////// 00711 void MilesAudioManager:: 00712 cleanup() { 00713 audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this 00714 << ", _cleanup_required = " << _cleanup_required); 00715 LightReMutexHolder holder(_lock); 00716 if (!_cleanup_required) { 00717 return; 00718 } 00719 00720 // Be sure to cleanup associated sounds before cleaning up the manager: 00721 AudioSet orig_sounds; 00722 orig_sounds.swap(_sounds_on_loan); 00723 AudioSet::iterator ai; 00724 for (ai = orig_sounds.begin(); ai != orig_sounds.end(); ++ai) { 00725 (*ai)->cleanup(); 00726 } 00727 00728 do_clear_cache(); 00729 00730 // Now stop the thread, if it has been started. 00731 if (!_stream_thread.is_null()) { 00732 milesAudio_cat.info() 00733 << "Stopping audio streaming thread.\n"; 00734 PT(StreamThread) old_thread; 00735 { 00736 MutexHolder holder(_streams_lock); 00737 nassertv(!_stream_thread.is_null()); 00738 _stream_thread->_keep_running = false; 00739 _streams_cvar.notify(); 00740 old_thread = _stream_thread; 00741 _stream_thread.clear(); 00742 } 00743 old_thread->join(); 00744 } 00745 00746 _cleanup_required = false; 00747 audio_debug("MilesAudioManager::cleanup() finished"); 00748 } 00749 00750 //////////////////////////////////////////////////////////////////// 00751 // Function: MilesAudioManager::output 00752 // Access: Public, Virtual 00753 // Description: 00754 //////////////////////////////////////////////////////////////////// 00755 void MilesAudioManager:: 00756 output(ostream &out) const { 00757 LightReMutexHolder holder(_lock); 00758 out << get_type() << ": " << _sounds_playing.size() 00759 << " / " << _sounds_on_loan.size() << " sounds playing / total"; 00760 } 00761 00762 //////////////////////////////////////////////////////////////////// 00763 // Function: MilesAudioManager::write 00764 // Access: Public, Virtual 00765 // Description: 00766 //////////////////////////////////////////////////////////////////// 00767 void MilesAudioManager:: 00768 write(ostream &out) const { 00769 LightReMutexHolder holder(_lock); 00770 00771 out << (*this) << "\n"; 00772 AudioSet::const_iterator ai; 00773 for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) { 00774 MilesAudioSound *sound = (*ai); 00775 out << " " << *sound << "\n"; 00776 } 00777 00778 size_t total_preload = 0; 00779 size_t num_preloaded = 0; 00780 SoundMap::const_iterator si; 00781 for (si = _sounds.begin(); si != _sounds.end(); ++si) { 00782 if (!(*si).second->_raw_data.empty()) { 00783 ++num_preloaded; 00784 total_preload += (*si).second->_raw_data.size(); 00785 } 00786 } 00787 out << num_preloaded << " of " << _sounds.size() << " sounds preloaded, size used is " << (total_preload + 1023) / 1024 << "K\n"; 00788 00789 { 00790 MutexHolder holder(_streams_lock); 00791 out << _streams.size() << " streams opened.\n"; 00792 if (!_stream_thread.is_null()) { 00793 out << "(Audio streaming thread has been started.)\n"; 00794 } 00795 } 00796 00797 GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr(); 00798 00799 int num_samples = mgr->get_num_samples(); 00800 out << num_samples << " sample handles allocated globally.\n"; 00801 00802 int num_sequences = mgr->get_num_sequences(); 00803 out << num_sequences << " sequence handles allocated globally.\n"; 00804 } 00805 00806 00807 //////////////////////////////////////////////////////////////////// 00808 // Function: MilesAudioManager::do_is_valid 00809 // Access: Private 00810 // Description: Implementation of is_valid(). Assumes the lock is 00811 // already held. 00812 //////////////////////////////////////////////////////////////////// 00813 bool MilesAudioManager:: 00814 do_is_valid() { 00815 bool check = true; 00816 if (_sounds.size() != _lru.size()) { 00817 audio_debug("-- Error _sounds.size() != _lru.size() --"); 00818 check = false; 00819 00820 } else { 00821 LRU::const_iterator i = _lru.begin(); 00822 for (; i != _lru.end(); ++i) { 00823 SoundMap::const_iterator smi = _sounds.find(**i); 00824 if (smi == _sounds.end()) { 00825 audio_debug("-- "<<**i<<" in _lru and not in _sounds --"); 00826 check = false; 00827 break; 00828 } 00829 } 00830 } 00831 return _is_valid && check; 00832 } 00833 00834 //////////////////////////////////////////////////////////////////// 00835 // Function: MilesAudioManager::do_reduce_sounds_playing_to 00836 // Access: Private 00837 // Description: Assumes the lock is already held. 00838 //////////////////////////////////////////////////////////////////// 00839 void MilesAudioManager:: 00840 do_reduce_sounds_playing_to(unsigned int count) { 00841 int limit = _sounds_playing.size() - count; 00842 while (limit-- > 0) { 00843 SoundsPlaying::iterator sound = _sounds_playing.begin(); 00844 assert(sound != _sounds_playing.end()); 00845 (**sound).stop(); 00846 } 00847 } 00848 00849 //////////////////////////////////////////////////////////////////// 00850 // Function: MilesAudioManager::do_clear_cache 00851 // Access: Private 00852 // Description: Assumes the lock is already held. 00853 //////////////////////////////////////////////////////////////////// 00854 void MilesAudioManager:: 00855 do_clear_cache() { 00856 if (_is_valid) { nassertv(do_is_valid()); } 00857 _sounds.clear(); 00858 _lru.clear(); 00859 if (_is_valid) { nassertv(do_is_valid()); } 00860 } 00861 00862 //////////////////////////////////////////////////////////////////// 00863 // Function: MilesAudioManager::start_service_stream 00864 // Access: Private 00865 // Description: Adds the indicated stream to the list of streams to 00866 // be serviced by a Panda sub-thread. This is in lieu 00867 // of Miles' auto-service-stream mechanism. 00868 //////////////////////////////////////////////////////////////////// 00869 void MilesAudioManager:: 00870 start_service_stream(HSTREAM stream) { 00871 MutexHolder holder(_streams_lock); 00872 nassertv(find(_streams.begin(), _streams.end(), stream) == _streams.end()); 00873 _streams.push_back(stream); 00874 _streams_cvar.notify(); 00875 00876 if (_stream_thread.is_null() && Thread::is_threading_supported()) { 00877 milesAudio_cat.info() 00878 << "Starting audio streaming thread.\n"; 00879 _stream_thread = new StreamThread(this); 00880 _stream_thread->start(TP_low, true); 00881 } 00882 } 00883 00884 //////////////////////////////////////////////////////////////////// 00885 // Function: MilesAudioManager::stop_service_stream 00886 // Access: Private 00887 // Description: Removes the indicated stream from the list of streams 00888 // to be serviced by a Panda sub-thread. 00889 //////////////////////////////////////////////////////////////////// 00890 void MilesAudioManager:: 00891 stop_service_stream(HSTREAM stream) { 00892 MutexHolder holder(_streams_lock); 00893 Streams::iterator si = find(_streams.begin(), _streams.end(), stream); 00894 if (si != _streams.end()) { 00895 _streams.erase(si); 00896 } 00897 } 00898 00899 00900 //////////////////////////////////////////////////////////////////// 00901 // Function: MilesAudioManager::most_recently_used 00902 // Access: Private 00903 // Description: Assumes the lock is already held. 00904 //////////////////////////////////////////////////////////////////// 00905 void MilesAudioManager:: 00906 most_recently_used(const string &path) { 00907 audio_debug("MilesAudioManager::most_recently_used(path=\"" 00908 <<path<<"\")"); 00909 LRU::iterator i=find(_lru.begin(), _lru.end(), &path); 00910 if (i != _lru.end()) { 00911 _lru.erase(i); 00912 } 00913 // At this point, path should not exist in the _lru: 00914 assert(find(_lru.begin(), _lru.end(), &path) == _lru.end()); 00915 _lru.push_back(&path); 00916 nassertv(do_is_valid()); 00917 } 00918 00919 //////////////////////////////////////////////////////////////////// 00920 // Function: MilesAudioManager::uncache_a_sound 00921 // Access: Private 00922 // Description: Assumes the lock is already held. 00923 //////////////////////////////////////////////////////////////////// 00924 void MilesAudioManager:: 00925 uncache_a_sound() { 00926 audio_debug("MilesAudioManager::uncache_a_sound()"); 00927 nassertv(do_is_valid()); 00928 // uncache least recently used: 00929 assert(_lru.size()>0); 00930 LRU::reference path=_lru.front(); 00931 SoundMap::iterator i = _sounds.find(*path); 00932 assert(i != _sounds.end()); 00933 _lru.pop_front(); 00934 00935 if (i != _sounds.end()) { 00936 audio_debug(" uncaching \""<<i->first<<"\""); 00937 _sounds.erase(i); 00938 } 00939 nassertv(do_is_valid()); 00940 } 00941 00942 //////////////////////////////////////////////////////////////////// 00943 // Function: MilesAudioManager::starting_sound 00944 // Access: Private 00945 // Description: Inform the manager that a sound is about to play. 00946 //////////////////////////////////////////////////////////////////// 00947 void MilesAudioManager:: 00948 starting_sound(MilesAudioSound *audio) { 00949 LightReMutexHolder holder(_lock); 00950 if (_concurrent_sound_limit) { 00951 do_reduce_sounds_playing_to(_concurrent_sound_limit); 00952 } 00953 _sounds_playing.insert(audio); 00954 } 00955 00956 //////////////////////////////////////////////////////////////////// 00957 // Function: MilesAudioManager::stopping_sound 00958 // Access: Private 00959 // Description: Inform the manager that a sound is finished or 00960 // someone called stop on the sound (this should not 00961 // be called if a sound is only paused). 00962 //////////////////////////////////////////////////////////////////// 00963 void MilesAudioManager:: 00964 stopping_sound(MilesAudioSound *audio) { 00965 LightReMutexHolder holder(_lock); 00966 _sounds_playing.erase(audio); 00967 if (_hasMidiSounds && _sounds_playing.size() == 0) { 00968 GlobalMilesManager::get_global_ptr()->force_midi_reset(); 00969 } 00970 } 00971 00972 //////////////////////////////////////////////////////////////////// 00973 // Function: MilesAudioManager::load 00974 // Access: Private 00975 // Description: Reads a sound file and allocates a SoundData pointer 00976 // for it. Returns NULL if the sound file cannot be 00977 // loaded. 00978 // 00979 // Assumes the lock is already held. 00980 //////////////////////////////////////////////////////////////////// 00981 PT(MilesAudioManager::SoundData) MilesAudioManager:: 00982 load(const Filename &file_name) { 00983 PT(SoundData) sd = new SoundData; 00984 00985 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00986 PT(VirtualFile) file = vfs->get_file(file_name); 00987 if (file == (VirtualFile *)NULL) { 00988 milesAudio_cat.warning() 00989 << "No such file: " << file_name << "\n"; 00990 return NULL; 00991 } 00992 00993 if (file->get_file_size() == 0) { 00994 milesAudio_cat.warning() 00995 << "File " << file_name << " is empty\n"; 00996 return NULL; 00997 } 00998 00999 sd->_basename = file_name.get_basename(); 01000 01001 string extension = sd->_basename.get_extension(); 01002 if (extension == "pz") { 01003 extension = Filename(sd->_basename.get_basename_wo_extension()).get_extension(); 01004 } 01005 01006 bool is_midi_file = (downcase(extension) == "mid"); 01007 01008 if ((miles_audio_preload_threshold == -1 || file->get_file_size() < (off_t)miles_audio_preload_threshold) || 01009 is_midi_file) { 01010 // If the file is sufficiently small, we'll preload it into 01011 // memory. MIDI files cannot be streamed, so we always preload 01012 // them, regardless of size. 01013 01014 if (!file->read_file(sd->_raw_data, true)) { 01015 milesAudio_cat.warning() 01016 << "Unable to read " << file_name << "\n"; 01017 return NULL; 01018 } 01019 01020 sd->_file_type = 01021 AIL_file_type(&sd->_raw_data[0], sd->_raw_data.size()); 01022 01023 if (sd->_file_type == AILFILETYPE_MIDI) { 01024 // A standard MIDI file. We have to convert this to XMIDI for 01025 // Miles. 01026 void *xmi; 01027 U32 xmi_size; 01028 if (AIL_MIDI_to_XMI(&sd->_raw_data[0], sd->_raw_data.size(), 01029 &xmi, &xmi_size, 0)) { 01030 audio_debug("converted " << sd->_basename << " from standard MIDI (" 01031 << sd->_raw_data.size() << " bytes) to XMIDI (" 01032 << xmi_size << " bytes)"); 01033 01034 // Copy the data to our own buffer and free the 01035 // Miles-allocated data. 01036 sd->_raw_data.clear(); 01037 sd->_raw_data.insert(sd->_raw_data.end(), 01038 (unsigned char *)xmi, (unsigned char *)xmi + xmi_size); 01039 AIL_mem_free_lock(xmi); 01040 sd->_file_type = AILFILETYPE_XMIDI; 01041 } else { 01042 milesAudio_cat.warning() 01043 << "Could not convert " << sd->_basename << " to XMIDI.\n"; 01044 } 01045 } 01046 01047 bool expand_to_wav = false; 01048 01049 if (sd->_file_type != AILFILETYPE_MPEG_L3_AUDIO) { 01050 audio_debug(sd->_basename << " is not an mp3 file."); 01051 } else if ((int)sd->_raw_data.size() >= miles_audio_expand_mp3_threshold) { 01052 audio_debug(sd->_basename << " is too large to expand in-memory."); 01053 } else { 01054 audio_debug(sd->_basename << " will be expanded in-memory."); 01055 expand_to_wav = true; 01056 } 01057 01058 if (expand_to_wav) { 01059 // Now convert the file to WAV format in-memory. This is useful 01060 // to work around seek and length problems associated with 01061 // variable bit-rate MP3 encoding. 01062 void *wav_data; 01063 U32 wav_data_size; 01064 if (AIL_decompress_ASI(&sd->_raw_data[0], sd->_raw_data.size(), 01065 sd->_basename.c_str(), &wav_data, &wav_data_size, 01066 NULL)) { 01067 audio_debug("expanded " << sd->_basename << " from " << sd->_raw_data.size() 01068 << " bytes to " << wav_data_size << " bytes."); 01069 01070 if (wav_data_size != 0) { 01071 // Now copy the memory into our own buffers, and free the 01072 // Miles-allocated memory. 01073 sd->_raw_data.clear(); 01074 sd->_raw_data.insert(sd->_raw_data.end(), 01075 (unsigned char *)wav_data, (unsigned char *)wav_data + wav_data_size); 01076 sd->_file_type = AILFILETYPE_PCM_WAV; 01077 sd->_basename.set_extension("wav"); 01078 } 01079 AIL_mem_free_lock(wav_data); 01080 01081 } else { 01082 audio_debug("unable to expand " << sd->_basename); 01083 } 01084 } 01085 01086 } else { 01087 // If the file is large, we'll stream it from disk instead of 01088 // preloading it. This means we don't need to load any data at 01089 // this point. 01090 } 01091 01092 return sd; 01093 } 01094 01095 //////////////////////////////////////////////////////////////////// 01096 // Function: MilesAudioManager::thread_main 01097 // Access: Private 01098 // Description: Called to service the streaming audio channels 01099 // currently playing on the audio manager. 01100 //////////////////////////////////////////////////////////////////// 01101 void MilesAudioManager:: 01102 thread_main(volatile bool &keep_running) { 01103 MutexHolder holder(_streams_lock); 01104 01105 while (keep_running) { 01106 if (_streams.empty()) { 01107 // If there are no streams to service, block on the condition 01108 // variable. 01109 _streams_cvar.wait(); 01110 } else { 01111 do_service_streams(); 01112 } 01113 01114 // Now yield to be polite to the main application. 01115 _streams_lock.release(); 01116 Thread::force_yield(); 01117 _streams_lock.acquire(); 01118 } 01119 } 01120 01121 //////////////////////////////////////////////////////////////////// 01122 // Function: MilesAudioManager::do_service_streams 01123 // Access: Private 01124 // Description: Internal function to service all the streams. 01125 // Assumes _streams_lock is already held. 01126 //////////////////////////////////////////////////////////////////// 01127 void MilesAudioManager:: 01128 do_service_streams() { 01129 size_t i = 0; 01130 while (i < _streams.size()) { 01131 HSTREAM stream = _streams[i]; 01132 01133 // We must release the lock while we are servicing stream i. 01134 _streams_lock.release(); 01135 AIL_service_stream(stream, 0); 01136 Thread::consider_yield(); 01137 _streams_lock.acquire(); 01138 01139 ++i; 01140 } 01141 } 01142 01143 //////////////////////////////////////////////////////////////////// 01144 // Function: MilesAudioManager::StreamThread::Constructor 01145 // Access: Public 01146 // Description: 01147 //////////////////////////////////////////////////////////////////// 01148 MilesAudioManager::StreamThread:: 01149 StreamThread(MilesAudioManager *mgr) : 01150 Thread("StreamThread", "StreamThread"), 01151 _mgr(mgr) 01152 { 01153 _keep_running = true; 01154 } 01155 01156 //////////////////////////////////////////////////////////////////// 01157 // Function: MilesAudioManager::StreamThread::thread_main 01158 // Access: Public, Virtual 01159 // Description: 01160 //////////////////////////////////////////////////////////////////// 01161 void MilesAudioManager::StreamThread:: 01162 thread_main() { 01163 _mgr->thread_main(_keep_running); 01164 } 01165 01166 //////////////////////////////////////////////////////////////////// 01167 // Function: MilesAudioManager::SoundData::Constructor 01168 // Access: Public 01169 // Description: 01170 //////////////////////////////////////////////////////////////////// 01171 MilesAudioManager::SoundData:: 01172 SoundData() : 01173 _raw_data(MilesAudioManager::get_class_type()), 01174 _has_length(false), 01175 _length(0.0f) 01176 { 01177 } 01178 01179 //////////////////////////////////////////////////////////////////// 01180 // Function: MilesAudioManager::SoundData::Destructor 01181 // Access: Public 01182 // Description: 01183 //////////////////////////////////////////////////////////////////// 01184 MilesAudioManager::SoundData:: 01185 ~SoundData() { 01186 } 01187 01188 //////////////////////////////////////////////////////////////////// 01189 // Function: MilesAudioManager::SoundData::get_length 01190 // Access: Public 01191 // Description: 01192 //////////////////////////////////////////////////////////////////// 01193 PN_stdfloat MilesAudioManager::SoundData:: 01194 get_length() { 01195 if (!_has_length) { 01196 // Time to determine the length of the file. 01197 01198 if (_raw_data.empty()) { 01199 _length = 0.0f; 01200 _has_length = true; 01201 01202 } else if (_file_type == AILFILETYPE_MPEG_L3_AUDIO) { 01203 // If it's an mp3 file, we have to calculate its length by 01204 // walking through all of its frames. 01205 audio_debug("Computing length of mp3 file " << _basename); 01206 01207 MP3_INFO info; 01208 AIL_inspect_MP3(&info, &_raw_data[0], _raw_data.size()); 01209 _length = 0.0f; 01210 while (AIL_enumerate_MP3_frames(&info)) { 01211 _length += info.data_size * 8.0f / info.bit_rate; 01212 } 01213 _has_length = true; 01214 01215 } else if (_file_type == AILFILETYPE_PCM_WAV || 01216 _file_type == AILFILETYPE_ADPCM_WAV || 01217 _file_type == AILFILETYPE_OTHER_ASI_WAV) { 01218 audio_debug("Getting length of wav file " << _basename); 01219 01220 AILSOUNDINFO info; 01221 if (AIL_WAV_info(&_raw_data[0], &info)) { 01222 _length = (PN_stdfloat)info.samples / (PN_stdfloat)info.rate; 01223 audio_debug(info.samples << " samples at " << info.rate 01224 << "; length is " << _length << " seconds."); 01225 _has_length = true; 01226 } 01227 } 01228 } 01229 01230 nassertr(_has_length, 0.0f); 01231 return _length; 01232 } 01233 01234 //////////////////////////////////////////////////////////////////// 01235 // Function: MilesAudioManager::SoundData::set_length 01236 // Access: Public 01237 // Description: Records the sample length, as determined externally. 01238 //////////////////////////////////////////////////////////////////// 01239 void MilesAudioManager::SoundData:: 01240 set_length(PN_stdfloat length) { 01241 _length = length; 01242 _has_length = true; 01243 } 01244 01245 #endif //]