Panda3D
 All Classes Functions Variables Enumerations
globalMilesManager.cxx
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 
 All Classes Functions Variables Enumerations