Panda3D
|
00001 // Filename: globalMilesManager.cxx 00002 // Created by: drose (26Jul07) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "globalMilesManager.h" 00016 00017 #ifdef HAVE_RAD_MSS //[ 00018 00019 #include "lightMutexHolder.h" 00020 #include "milesAudioManager.h" 00021 #include "milesAudioSample.h" 00022 #include "milesAudioSequence.h" 00023 00024 #ifdef WIN32 00025 // For midiOutReset() 00026 #include <windows.h> 00027 #include <mmsystem.h> 00028 #endif 00029 00030 GlobalMilesManager *GlobalMilesManager::_global_ptr; 00031 00032 //////////////////////////////////////////////////////////////////// 00033 // Function: GlobalMilesManager::Constructor 00034 // Access: Private 00035 // Description: 00036 //////////////////////////////////////////////////////////////////// 00037 GlobalMilesManager:: 00038 GlobalMilesManager() : 00039 _managers_lock("GlobalMilesManager::_managers_lock"), 00040 _samples_lock("GlobalMilesManager::_samples_lock"), 00041 _sequences_lock("GlobalMilesManager::_sequences_lock") 00042 { 00043 _digital_driver = 0; 00044 _midi_driver = 0; 00045 _dls_device = 0; 00046 _dls_file = 0; 00047 _is_open = false; 00048 } 00049 00050 //////////////////////////////////////////////////////////////////// 00051 // Function: GlobalMilesManager::add_manager 00052 // Access: Public 00053 // Description: Records a new MilesAudioManager in the world. This 00054 // will open the Miles API when the first audio manager 00055 // is added. 00056 //////////////////////////////////////////////////////////////////// 00057 void GlobalMilesManager:: 00058 add_manager(MilesAudioManager *manager) { 00059 LightMutexHolder holder(_managers_lock); 00060 _managers.insert(manager); 00061 if (!_is_open) { 00062 open_api(); 00063 } 00064 } 00065 00066 //////////////////////////////////////////////////////////////////// 00067 // Function: GlobalMilesManager::remove_manager 00068 // Access: Public 00069 // Description: Records that a MilesAudioManager is destructing. 00070 // This will clsoe the Miles API when the last audio 00071 // manager is removed. 00072 //////////////////////////////////////////////////////////////////// 00073 void GlobalMilesManager:: 00074 remove_manager(MilesAudioManager *manager) { 00075 LightMutexHolder holder(_managers_lock); 00076 _managers.erase(manager); 00077 if (_managers.empty() && _is_open) { 00078 close_api(); 00079 } 00080 } 00081 00082 //////////////////////////////////////////////////////////////////// 00083 // Function: GlobalMilesManager::cleanup 00084 // Access: Public 00085 // Description: Calls cleanup() on all MilesAudioManagers, to cause a 00086 // clean shutdown. 00087 //////////////////////////////////////////////////////////////////// 00088 void GlobalMilesManager:: 00089 cleanup() { 00090 LightMutexHolder holder(_managers_lock); 00091 Managers::iterator mi; 00092 for (mi = _managers.begin(); mi != _managers.end(); ++mi) { 00093 (*mi)->cleanup(); 00094 } 00095 } 00096 00097 //////////////////////////////////////////////////////////////////// 00098 // Function: GlobalMilesManager::get_sample 00099 // Access: Public 00100 // Description: Gets a sample handle from the global pool for the 00101 // digital output device, to be used with the indicated 00102 // AudioSound. 00103 // 00104 // If successful, sets the sample handle and the index 00105 // (which should later be used to release the sample) 00106 // and returns true. If unsuccessful (because there are 00107 // no more available handles), returns false. 00108 // 00109 // This is a very limited resource; you should only get 00110 // a sample just before playing a sound. 00111 //////////////////////////////////////////////////////////////////// 00112 bool GlobalMilesManager:: 00113 get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound) { 00114 LightMutexHolder holder(_samples_lock); 00115 00116 for (size_t i = 0; i < _samples.size(); ++i) { 00117 SampleData &smp = _samples[i]; 00118 if (AIL_sample_status(smp._sample) == SMP_DONE) { 00119 if (smp._sound != NULL) { 00120 // Tell the last sound that was using this sample that it's 00121 // done now. 00122 smp._sound->internal_stop(); 00123 } 00124 smp._sound = sound; 00125 sample = smp._sample; 00126 index = i; 00127 return true; 00128 } 00129 } 00130 00131 // No more already-allocated samples; get a new one from the system. 00132 sample = AIL_allocate_sample_handle(_digital_driver); 00133 if (sample == 0) { 00134 return false; 00135 } 00136 00137 AIL_init_sample(sample, DIG_F_STEREO_16, 0); 00138 index = _samples.size(); 00139 00140 SampleData smp; 00141 smp._sound = sound; 00142 smp._sample = sample; 00143 _samples.push_back(smp); 00144 return true; 00145 } 00146 00147 //////////////////////////////////////////////////////////////////// 00148 // Function: GlobalMilesManager::release_sample 00149 // Access: Public 00150 // Description: Indicates that the indicated AudioSound no longer 00151 // needs this sample. 00152 //////////////////////////////////////////////////////////////////// 00153 void GlobalMilesManager:: 00154 release_sample(size_t index, MilesAudioSample *sound) { 00155 LightMutexHolder holder(_samples_lock); 00156 nassertv(index < _samples.size()); 00157 00158 SampleData &smp = _samples[index]; 00159 if (smp._sound == sound) { 00160 smp._sound = NULL; 00161 } 00162 } 00163 00164 //////////////////////////////////////////////////////////////////// 00165 // Function: GlobalMilesManager::get_sequence 00166 // Access: Public 00167 // Description: Gets a sequence handle from the global pool for the 00168 // digital output device, to be used with the indicated 00169 // AudioSound. 00170 // 00171 // If successful, sets the sequence handle and the index 00172 // (which should later be used to release the sequence) 00173 // and returns true. If unsuccessful (because there are 00174 // no more available handles), returns false. 00175 // 00176 // This is a very limited resource; you should only get 00177 // a sequence just before playing a sound. 00178 //////////////////////////////////////////////////////////////////// 00179 bool GlobalMilesManager:: 00180 get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound) { 00181 LightMutexHolder holder(_sequences_lock); 00182 00183 for (size_t i = 0; i < _sequences.size(); ++i) { 00184 SequenceData &seq = _sequences[i]; 00185 if (AIL_sequence_status(seq._sequence) == SEQ_DONE) { 00186 if (seq._sound != NULL) { 00187 // Tell the last sound that was using this sequence that it's 00188 // done now. 00189 seq._sound->internal_stop(); 00190 } 00191 seq._sound = sound; 00192 sequence = seq._sequence; 00193 index = i; 00194 return true; 00195 } 00196 } 00197 00198 // No more already-allocated sequences; get a new one from the system. 00199 sequence = AIL_allocate_sequence_handle(_midi_driver); 00200 if (sequence == 0) { 00201 return false; 00202 } 00203 00204 index = _sequences.size(); 00205 00206 SequenceData seq; 00207 seq._sound = sound; 00208 seq._sequence = sequence; 00209 _sequences.push_back(seq); 00210 return true; 00211 } 00212 00213 //////////////////////////////////////////////////////////////////// 00214 // Function: GlobalMilesManager::release_sequence 00215 // Access: Public 00216 // Description: Indicates that the indicated AudioSound no longer 00217 // needs this sequence. 00218 //////////////////////////////////////////////////////////////////// 00219 void GlobalMilesManager:: 00220 release_sequence(size_t index, MilesAudioSequence *sound) { 00221 LightMutexHolder holder(_sequences_lock); 00222 nassertv(index < _sequences.size()); 00223 00224 SequenceData &seq = _sequences[index]; 00225 if (seq._sound == sound) { 00226 seq._sound = NULL; 00227 } 00228 } 00229 00230 //////////////////////////////////////////////////////////////////// 00231 // Function: GlobalMilesManager::force_midi_reset 00232 // Access: Public 00233 // Description: Sometimes Miles seems to leave midi notes hanging, 00234 // even after stop is called, so call this method to 00235 // perform an explicit reset using winMM.dll calls, just 00236 // to ensure silence. 00237 //////////////////////////////////////////////////////////////////// 00238 void GlobalMilesManager:: 00239 force_midi_reset() { 00240 if (!miles_audio_force_midi_reset) { 00241 audio_debug("MilesAudioManager::skipping force_midi_reset"); 00242 return; 00243 } 00244 audio_debug("MilesAudioManager::force_midi_reset"); 00245 00246 #ifdef WIN32 00247 if ((_midi_driver!=NULL) && (_midi_driver->deviceid != MIDI_NULL_DRIVER) && (_midi_driver->hMidiOut != NULL)) { 00248 audio_debug("MilesAudioManager::calling midiOutReset"); 00249 midiOutReset(_midi_driver->hMidiOut); 00250 } 00251 #endif 00252 } 00253 00254 //////////////////////////////////////////////////////////////////// 00255 // Function: GlobalMilesManager::get_global_ptr 00256 // Access: Public, Static 00257 // Description: Returns the pointer to the one GlobalMilesManager 00258 // object. 00259 //////////////////////////////////////////////////////////////////// 00260 GlobalMilesManager *GlobalMilesManager:: 00261 get_global_ptr() { 00262 if (_global_ptr == NULL) { 00263 _global_ptr = new GlobalMilesManager; 00264 } 00265 return _global_ptr; 00266 } 00267 00268 //////////////////////////////////////////////////////////////////// 00269 // Function: GlobalMilesManager::open_api 00270 // Access: Private 00271 // Description: Called internally to initialize the Miles API. 00272 //////////////////////////////////////////////////////////////////// 00273 void GlobalMilesManager:: 00274 open_api() { 00275 audio_debug("GlobalMilesManager::open_api()") 00276 nassertv(!_is_open); 00277 00278 bool use_digital = (audio_play_wave || audio_play_mp3); 00279 if (audio_play_midi && audio_software_midi) { 00280 use_digital = true; 00281 } 00282 00283 #ifdef IS_OSX 00284 audio_software_midi = true; 00285 #endif 00286 00287 audio_debug(" use_digital="<<use_digital); 00288 audio_debug(" audio_play_midi="<<audio_play_midi); 00289 audio_debug(" audio_software_midi="<<audio_software_midi); 00290 audio_debug(" audio_output_rate="<<audio_output_rate); 00291 audio_debug(" audio_output_bits="<<audio_output_bits); 00292 audio_debug(" audio_output_channels="<<audio_output_channels); 00293 audio_debug(" audio_software_midi="<<audio_software_midi); 00294 00295 #if !defined(NDEBUG) && defined(AIL_MSS_version) //[ 00296 char version[8]; 00297 AIL_MSS_version(version, 8); 00298 audio_debug(" Mss32.dll Version: "<<version); 00299 #endif //] 00300 00301 if (!AIL_startup()) { 00302 milesAudio_cat.warning() 00303 << "Miles Sound System already initialized!\n"; 00304 } 00305 00306 AIL_set_file_callbacks(open_callback, close_callback, 00307 seek_callback, read_callback); 00308 00309 if (use_digital) { 00310 _digital_driver = 00311 AIL_open_digital_driver(audio_output_rate, audio_output_bits, 00312 audio_output_channels, 0); 00313 } 00314 00315 if (audio_play_midi) { 00316 if (audio_software_midi) { 00317 _midi_driver = AIL_open_XMIDI_driver(AIL_OPEN_XMIDI_NULL_DRIVER); 00318 00319 // Load the downloadable sounds file: 00320 _dls_device = AIL_DLS_open(_midi_driver, _digital_driver, NULL, 0, 00321 audio_output_rate, audio_output_bits, 00322 audio_output_channels); 00323 00324 Filename dls_pathname = AudioManager::get_dls_pathname(); 00325 00326 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00327 vfs->resolve_filename(dls_pathname, get_model_path()); 00328 00329 _dls_data.clear(); 00330 PT(VirtualFile) file = vfs->get_file(dls_pathname); 00331 if (file == (VirtualFile *)NULL) { 00332 milesAudio_cat.warning() 00333 << "DLS file does not exist: " << dls_pathname << "\n"; 00334 00335 } else if (!file->read_file(_dls_data, true)) { 00336 milesAudio_cat.warning() 00337 << "Could not read DLS file: " << dls_pathname << "\n"; 00338 00339 } else if (_dls_data.empty()) { 00340 milesAudio_cat.warning() 00341 << "DLS file is empty: " << dls_pathname << "\n"; 00342 00343 } else { 00344 _dls_file = AIL_DLS_load_memory(_dls_device, &_dls_data[0], 0); 00345 } 00346 00347 if (_dls_file == 0) { 00348 audio_error(" Could not get DLS file, switching to hardware MIDI."); 00349 AIL_DLS_close(_dls_device, 0); 00350 _dls_device = 0; 00351 AIL_close_XMIDI_driver(_midi_driver); 00352 _midi_driver = AIL_open_XMIDI_driver(0); 00353 00354 } else { 00355 audio_info(" using Miles software midi"); 00356 } 00357 } else { 00358 _midi_driver = AIL_open_XMIDI_driver(0); 00359 audio_info(" using Miles hardware midi"); 00360 } 00361 } 00362 00363 _is_open = true; 00364 } 00365 00366 //////////////////////////////////////////////////////////////////// 00367 // Function: GlobalMilesManager::close_api 00368 // Access: Private 00369 // Description: Called internally to shut down the Miles API. 00370 //////////////////////////////////////////////////////////////////// 00371 void GlobalMilesManager:: 00372 close_api() { 00373 audio_debug("GlobalMilesManager::close_api()") 00374 nassertv(_is_open); 00375 00376 Samples::iterator si; 00377 for (si = _samples.begin(); si != _samples.end(); ++si) { 00378 SampleData &smp = (*si); 00379 AIL_release_sample_handle(smp._sample); 00380 } 00381 _samples.clear(); 00382 00383 Sequences::iterator qi; 00384 for (qi = _sequences.begin(); qi != _sequences.end(); ++qi) { 00385 SequenceData &smp = (*qi); 00386 AIL_release_sequence_handle(smp._sequence); 00387 } 00388 _sequences.clear(); 00389 00390 if (_dls_file != 0) { 00391 AIL_DLS_unload(_dls_device, _dls_file); 00392 _dls_file = 0; 00393 } 00394 00395 if (_dls_device != 0) { 00396 AIL_DLS_close(_dls_device, 0); 00397 _dls_device = 0; 00398 } 00399 00400 if (_midi_driver != 0) { 00401 AIL_close_XMIDI_driver(_midi_driver); 00402 _midi_driver = 0; 00403 } 00404 00405 if (_digital_driver != 0) { 00406 AIL_close_digital_driver(_digital_driver); 00407 _digital_driver = 0; 00408 } 00409 00410 AIL_shutdown(); 00411 00412 _is_open = false; 00413 } 00414 00415 //////////////////////////////////////////////////////////////////// 00416 // Function: GlobalMilesManager::open_callback 00417 // Access: Private, Static 00418 // Description: This callback function is given to Miles to handle 00419 // file I/O via the Panda VFS. It's only used to 00420 // implemented streaming audio files, since in all other 00421 // cases we open files directly. 00422 //////////////////////////////////////////////////////////////////// 00423 U32 AILCALLBACK GlobalMilesManager:: 00424 open_callback(char const *filename, UINTa *file_handle) { 00425 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00426 istream *strm = vfs->open_read_file(Filename::binary_filename(string(filename)), true); 00427 if (strm == NULL) { 00428 // Failure. 00429 return 0; 00430 } 00431 // Success. 00432 (*file_handle) = (UINTa)strm; 00433 return 1; 00434 } 00435 00436 //////////////////////////////////////////////////////////////////// 00437 // Function: GlobalMilesManager::close_callback 00438 // Access: Private, Static 00439 // Description: This callback function is given to Miles to handle 00440 // file I/O via the Panda VFS. 00441 //////////////////////////////////////////////////////////////////// 00442 void AILCALLBACK GlobalMilesManager:: 00443 close_callback(UINTa file_handle) { 00444 istream *strm = (istream *)file_handle; 00445 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00446 vfs->close_read_file(strm); 00447 } 00448 00449 //////////////////////////////////////////////////////////////////// 00450 // Function: GlobalMilesManager::seek_callback 00451 // Access: Private, Static 00452 // Description: This callback function is given to Miles to handle 00453 // file I/O via the Panda VFS. 00454 //////////////////////////////////////////////////////////////////// 00455 S32 AILCALLBACK GlobalMilesManager:: 00456 seek_callback(UINTa file_handle, S32 offset, U32 type) { 00457 istream *strm = (istream *)file_handle; 00458 strm->clear(); 00459 switch (type) { 00460 case AIL_FILE_SEEK_BEGIN: 00461 strm->seekg(offset, ios::beg); 00462 break; 00463 00464 case AIL_FILE_SEEK_CURRENT: 00465 strm->seekg(offset, ios::cur); 00466 break; 00467 00468 case AIL_FILE_SEEK_END: 00469 strm->seekg(offset, ios::end); 00470 break; 00471 } 00472 00473 return strm->tellg(); 00474 } 00475 00476 //////////////////////////////////////////////////////////////////// 00477 // Function: GlobalMilesManager::read_callback 00478 // Access: Private, Static 00479 // Description: This callback function is given to Miles to handle 00480 // file I/O via the Panda VFS. 00481 //////////////////////////////////////////////////////////////////// 00482 U32 AILCALLBACK GlobalMilesManager:: 00483 read_callback(UINTa file_handle, void *buffer, U32 bytes) { 00484 istream *strm = (istream *)file_handle; 00485 strm->read((char *)buffer, bytes); 00486 return strm->gcount(); 00487 } 00488 00489 #endif //] 00490