Browse Source

cleanup miles better at program exit

David Rose 21 years ago
parent
commit
b905967e2d

+ 6 - 0
direct/src/showbase/ShowBase.py

@@ -282,6 +282,12 @@ class ShowBase(DirectObject.DirectObject):
         the desktop and keyboard functionality, etc.
         the desktop and keyboard functionality, etc.
         """
         """
         self.graphicsEngine.removeAllWindows()
         self.graphicsEngine.removeAllWindows()
+
+        if self.musicManager:
+            # Temporary condition for old Pandas.
+            if hasattr(self.musicManager, 'shutdown'):
+                self.musicManager.shutdown()
+
         del self.win
         del self.win
         del self.winList
         del self.winList
         del self.pipe
         del self.pipe

+ 22 - 0
panda/src/audio/audioManager.cxx

@@ -86,6 +86,28 @@ create_AudioManager() {
   return am;
   return am;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::Destructor
+//       Access: Published, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+AudioManager::
+~AudioManager() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AudioManager::shutdown
+//       Access: Published, Virtual
+//  Description: Call this at exit time to shut down the audio system.
+//               This will invalidate all currently-active
+//               AudioManagers and AudioSounds in the system.  If you
+//               change your mind and want to play sounds again, you
+//               will have to recreate all of these objects.
+////////////////////////////////////////////////////////////////////
+void AudioManager::
+shutdown() {
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: AudioManager::get_null_sound
 //     Function: AudioManager::get_null_sound
 //       Access: Public
 //       Access: Public

+ 3 - 1
panda/src/audio/audioManager.h

@@ -37,7 +37,9 @@ PUBLISHED:
   //   my_music = MyMusicManager.get_sound("introTheme.mid");
   //   my_music = MyMusicManager.get_sound("introTheme.mid");
 
 
   static PT(AudioManager) create_AudioManager();
   static PT(AudioManager) create_AudioManager();
-  virtual ~AudioManager() {}
+  virtual ~AudioManager();
+
+  virtual void shutdown();
   
   
   // If you're interested in knowing whether this audio manager
   // If you're interested in knowing whether this audio manager
   // is valid, here's the call to do it.  It is not necessary
   // is valid, here's the call to do it.  It is not necessary

+ 166 - 123
panda/src/audiotraits/milesAudioManager.cxx

@@ -34,56 +34,19 @@
 TypeHandle MilesAudioManager::_type_handle;
 TypeHandle MilesAudioManager::_type_handle;
 
 
 int MilesAudioManager::_active_managers = 0;
 int MilesAudioManager::_active_managers = 0;
+bool MilesAudioManager::_miles_active = false;
 HDLSFILEID MilesAudioManager::_dls_field = NULL;
 HDLSFILEID MilesAudioManager::_dls_field = NULL;
 
 
-namespace {
-  bool miles_shutdown_called = false;
-}
+// This is the list of all MilesAudioManager objects in the world.  It
+// must be a pointer rather than a concrete object, so it won't be
+// destructed at exit time before we're done removing things from it.
+MilesAudioManager::Managers *MilesAudioManager::_managers;
 
 
 PT(AudioManager) Create_AudioManager() {
 PT(AudioManager) Create_AudioManager() {
   audio_debug("Create_AudioManager() Miles.");
   audio_debug("Create_AudioManager() Miles.");
   return new MilesAudioManager();
   return new MilesAudioManager();
 }
 }
 
 
-void CustomMilesShutdown() {
-  if (miles_shutdown_called) {
-    return;
-  }
-  miles_shutdown_called = true;
-
-  if (MilesAudioManager::_dls_field!=NULL) {
-    HDLSDEVICE dls= NULL;
-    AIL_quick_handles(0, 0, &dls);
-    if (dls!=NULL) {
-      AIL_DLS_unload(dls,MilesAudioManager::_dls_field);
-    }
-    #ifndef NDEBUG //[
-      // Clear _dls_field in debug version (for assert in ctor):
-      MilesAudioManager::_dls_field = NULL;  
-    #endif //]
-  }
-
-  #define SHUTDOWN_HACK
-  // if python crashes, the midi notes are left on,
-  // so we need to turn them off.  Unfortunately
-  // in Miles 6.5, AIL_quick_shutdown() crashes
-  // when it's called from atexit after a python crash, probably
-  // because mss32.dll has already been unloaded
-  // workaround: use these internal values in the miles struct
-  //             to reset the win32 midi ourselves
-  #ifdef SHUTDOWN_HACK
-  HMDIDRIVER hMid=NULL;
-  AIL_quick_handles(0, &hMid, 0);
-  if ((hMid!=NULL) && (hMid->deviceid != MIDI_NULL_DRIVER) && (hMid->hMidiOut != NULL)) {
-    midiOutReset(hMid->hMidiOut);
-    midiOutClose(hMid->hMidiOut);
-  }
-  #else
-  audio_debug("  AIL_quick_shutdown()");
-  AIL_quick_shutdown();
-  #endif
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MilesAudioManager::MilesAudioManager
 //     Function: MilesAudioManager::MilesAudioManager
 //       Access: Public
 //       Access: Public
@@ -94,16 +57,23 @@ void CustomMilesShutdown() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MilesAudioManager::
 MilesAudioManager::
 MilesAudioManager() {
 MilesAudioManager() {
-  audio_debug("MilesAudioManager::MilesAudioManager()");
+  audio_debug("MilesAudioManager::MilesAudioManager(), this = " 
+              << (void *)this);
+  if (_managers == (Managers *)NULL) {
+    _managers = new Managers;
+  }
+  _managers->insert(this);
+
   audio_debug("  audio_active="<<audio_active);
   audio_debug("  audio_active="<<audio_active);
   audio_debug("  audio_volume="<<audio_volume);
   audio_debug("  audio_volume="<<audio_volume);
+  _cleanup_required = true;
   _active = audio_active;
   _active = audio_active;
   _volume = audio_volume;
   _volume = audio_volume;
   _cache_limit = audio_cache_limit;
   _cache_limit = audio_cache_limit;
   _concurrent_sound_limit = 0;
   _concurrent_sound_limit = 0;
   _is_valid = true;
   _is_valid = true;
   _hasMidiSounds = false;
   _hasMidiSounds = false;
-  if (_active_managers==0) {
+  if (_active_managers==0 || !_miles_active) {
     S32 use_digital=(audio_play_wave || audio_play_mp3)?1:0;
     S32 use_digital=(audio_play_wave || audio_play_mp3)?1:0;
     S32 use_MIDI=(audio_play_midi)?1:0;
     S32 use_MIDI=(audio_play_midi)?1:0;
     if (audio_play_midi && audio_software_midi) {
     if (audio_play_midi && audio_software_midi) {
@@ -123,33 +93,38 @@ MilesAudioManager() {
     if (AIL_quick_startup(use_digital,
     if (AIL_quick_startup(use_digital,
         use_MIDI, audio_output_rate,
         use_MIDI, audio_output_rate,
         audio_output_bits, audio_output_channels)) {
         audio_output_bits, audio_output_channels)) {
+      _miles_active = true;
       if (audio_software_midi) {
       if (audio_software_midi) {
         // Load the downloadable sounds file:
         // Load the downloadable sounds file:
 
 
-        HDLSDEVICE dls;
-        AIL_quick_handles(0, 0, &dls);
-        nassertv(!_dls_field);
-        string dls_file = Filename(audio_dls_file).to_os_specific();
-        if (dls_file.empty()) {
-          get_gm_file_path(dls_file);
-          // we need more dbg info in logs, so bumping the msgs from 'debug' status to 'info' status
-          audio_info("  using default dls_file: "<< dls_file );
-        }
-
-        audio_debug("  dls_file=\""<<dls_file<<"\"");
-
-        // note: if AIL_DLS_load_file is not done, midi fails to play on some machines.
-        _dls_field=AIL_DLS_load_file(dls, dls_file.c_str(), 0);
-        if (!_dls_field) {
-          audio_error("  AIL_DLS_load_file() failed, \""<<AIL_last_error() <<"\" Switching to hardware midi");
-          AIL_quick_shutdown();
-          if (!AIL_quick_startup(use_digital, 1, audio_output_rate,
-              audio_output_bits, audio_output_channels)) {
-            audio_error("  midi hardware startup failed, "<<AIL_last_error());
-            _is_valid = false;
+        if (_dls_field == NULL) {
+          HDLSDEVICE dls;
+          AIL_quick_handles(0, 0, &dls);
+          nassertv(dls != NULL);
+          string dls_file = Filename(audio_dls_file).to_os_specific();
+          if (dls_file.empty()) {
+            get_gm_file_path(dls_file);
+            // we need more dbg info in logs, so bumping the msgs from 'debug' status to 'info' status
+            audio_info("  using default dls_file: "<< dls_file );
+          }
+          
+          audio_debug("  dls_file=\""<<dls_file<<"\"");
+          
+          // note: if AIL_DLS_load_file is not done, midi fails to play on some machines.
+          nassertv(_dls_field == NULL);
+          audio_debug("  AIL_DLS_load_file(dls, " << dls_file << ", 0)");
+          _dls_field = AIL_DLS_load_file(dls, dls_file.c_str(), 0);
+          if (!_dls_field) {
+            audio_error("  AIL_DLS_load_file() failed, \""<<AIL_last_error() <<"\" Switching to hardware midi");
+            AIL_quick_shutdown();
+            if (!AIL_quick_startup(use_digital, 1, audio_output_rate,
+                                   audio_output_bits, audio_output_channels)) {
+              audio_error("  midi hardware startup failed, "<<AIL_last_error());
+              _is_valid = false;
+            }
+          } else {
+            audio_info("  using Miles software midi");
           }
           }
-        } else {
-          audio_info("  using Miles software midi");
         }
         }
       } else {
       } else {
         audio_info("  using Miles hardware midi");
         audio_info("  using Miles hardware midi");
@@ -166,15 +141,17 @@ MilesAudioManager() {
   audio_debug("  _active_managers="<<_active_managers);
   audio_debug("  _active_managers="<<_active_managers);
   nassertv(_active_managers>0);
   nassertv(_active_managers>0);
 
 
-  if (_is_valid)  {
-    assert(is_valid());
-
-    static bool atexit_registered = false;
-    if(!atexit_registered) {
-       atexit_registered = true;
-       atexit(CustomMilesShutdown);
-    }
-  }
+  // We used to hang a call to a force-shutdown function on atexit(),
+  // so that any running sounds (particularly MIDI sounds) would be
+  // silenced on exit, especially a sudden exit triggered by a Python
+  // exception.  But that causes problems because Miles itself also
+  // hangs a force-shutdown function on atexit(), and you can't call
+  // AIL_cleanup() twice--that results in a crash.
+
+  // Nowadays, we provide the AudioManager::shutdown() method instead,
+  // which allows the app to force all sounds to stop cleanly before
+  // we get to the atexit() stack.  In Python, we guarantee that this
+  // method will be called in the sys.exitfunc chain.
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -187,27 +164,38 @@ MilesAudioManager() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MilesAudioManager::
 MilesAudioManager::
 ~MilesAudioManager() {
 ~MilesAudioManager() {
-  audio_debug("MilesAudioManager::~MilesAudioManager()");
-  // Be sure to delete associated sounds before deleting the manager:
-  nassertv(_sounds_on_loan.empty());
-  clear_cache();
-  nassertv(_active_managers>0);
-  --_active_managers;
-  audio_debug("  _active_managers="<<_active_managers);
-  if (_active_managers==0) {
-    if (audio_software_midi) {
-      HDLSDEVICE dls;
-      AIL_quick_handles(0, 0, &dls);
-      AIL_DLS_unload(dls, _dls_field);
-      #ifndef NDEBUG //[
-        // Clear _dls_field in debug version (for assert in ctor):
-        _dls_field=0;
-      #endif //]
+  audio_debug("MilesAudioManager::~MilesAudioManager(), this = " 
+              << (void *)this);
+  nassertv(_managers != (Managers *)NULL);
+  Managers::iterator mi = _managers->find(this);
+  nassertv(mi != _managers->end());
+  _managers->erase(mi);
+
+  cleanup();
+  audio_debug("MilesAudioManager::~MilesAudioManager() finished");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MilesAudioManager::shutdown
+//       Access: Published, Virtual
+//  Description: Call this at exit time to shut down the audio system.
+//               This will invalidate all currently-active
+//               AudioManagers and AudioSounds in the system.  If you
+//               change your mind and want to play sounds again, you
+//               will have to recreate all of these objects.
+////////////////////////////////////////////////////////////////////
+void MilesAudioManager::
+shutdown() {
+  audio_debug("shutdown(), _miles_active = " << _miles_active);
+  if (_managers != (Managers *)NULL) {
+    Managers::iterator mi;
+    for (mi = _managers->begin(); mi != _managers->end(); ++mi) {
+      (*mi)->cleanup();
     }
     }
-    audio_debug("  AIL_quick_shutdown()");
-    AIL_quick_shutdown();
-    miles_shutdown_called = true;
   }
   }
+
+  nassertv(_active_managers == 0);
+  audio_debug("shutdown() finished");
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -305,7 +293,7 @@ load(Filename file_name) {
   }
   }
 
 
   sd->_audio = AIL_quick_load_mem(sd->_raw_data.data(), sd->_raw_data.size());
   sd->_audio = AIL_quick_load_mem(sd->_raw_data.data(), sd->_raw_data.size());
-   
+
   if (!sd->_audio) {
   if (!sd->_audio) {
     audio_error("  MilesAudioManager::load failed "<< AIL_last_error());
     audio_error("  MilesAudioManager::load failed "<< AIL_last_error());
     return NULL;
     return NULL;
@@ -376,7 +364,8 @@ get_sound(const string& file_name, bool) {
         =new MilesAudioSound(this, sd, (*si).first);
         =new MilesAudioSound(this, sd, (*si).first);
     nassertr(milesAudioSound, 0);
     nassertr(milesAudioSound, 0);
     milesAudioSound->set_active(_active);
     milesAudioSound->set_active(_active);
-    _sounds_on_loan.insert(milesAudioSound);
+    bool inserted = _sounds_on_loan.insert(milesAudioSound).second;
+    nassertr(inserted, milesAudioSound.p());
     audioSound=milesAudioSound;
     audioSound=milesAudioSound;
   }
   }
 
 
@@ -505,8 +494,12 @@ get_cache_limit() const {
 void MilesAudioManager::
 void MilesAudioManager::
 release_sound(MilesAudioSound* audioSound) {
 release_sound(MilesAudioSound* audioSound) {
   audio_debug("MilesAudioManager::release_sound(audioSound=\""
   audio_debug("MilesAudioManager::release_sound(audioSound=\""
-      <<audioSound->get_name()<<"\")");
-  _sounds_on_loan.erase(audioSound);
+              <<audioSound->get_name()<<"\"), this = " << (void *)this);
+  AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
+  nassertv(ai != _sounds_on_loan.end());
+  _sounds_on_loan.erase(ai);
+
+  audio_debug("MilesAudioManager::release_sound() finished");
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -527,29 +520,6 @@ set_volume(float volume) {
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: MilesAudioManager::force_midi_reset
-//       Access: Public
-//  Description: ?????.
-////////////////////////////////////////////////////////////////////
-void MilesAudioManager::
-force_midi_reset() {
-  if (!miles_audio_force_midi_reset) {
-    audio_debug("MilesAudioManager::skipping force_midi_reset");  
-    return;
-  }
-  audio_debug("MilesAudioManager::force_midi_reset");
-
-  // sometimes Miles seems to leave midi notes hanging, even after stop is called,
-  // so perform an explicit reset using winMM.dll calls, just to ensure silence.
-  HMDIDRIVER hMid=NULL;
-  AIL_quick_handles(0, &hMid, 0);
-  if ((hMid!=NULL) && (hMid->deviceid != MIDI_NULL_DRIVER) && (hMid->hMidiOut != NULL)) {
-    audio_debug("MilesAudioManager::calling midiOutReset");
-    midiOutReset(hMid->hMidiOut);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MilesAudioManager::get_volume
 //     Function: MilesAudioManager::get_volume
 //       Access: Public
 //       Access: Public
@@ -732,6 +702,76 @@ get_gm_file_path(string& result) {
   audio_debug("MilesAudioManager::get_gm_file_path() result out=\""<<result<<"\"");
   audio_debug("MilesAudioManager::get_gm_file_path() result out=\""<<result<<"\"");
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MilesAudioManager::force_midi_reset
+//       Access: Private
+//  Description: ?????.
+////////////////////////////////////////////////////////////////////
+void MilesAudioManager::
+force_midi_reset() {
+  if (!miles_audio_force_midi_reset) {
+    audio_debug("MilesAudioManager::skipping force_midi_reset");  
+    return;
+  }
+  audio_debug("MilesAudioManager::force_midi_reset");
+
+  // sometimes Miles seems to leave midi notes hanging, even after
+  // stop is called, so perform an explicit reset using winMM.dll
+  // calls, just to ensure silence.
+
+  HMDIDRIVER hMid=NULL;
+  AIL_quick_handles(0, &hMid, 0);
+  if ((hMid!=NULL) && (hMid->deviceid != MIDI_NULL_DRIVER) && (hMid->hMidiOut != NULL)) {
+    audio_debug("MilesAudioManager::calling midiOutReset");
+    midiOutReset(hMid->hMidiOut);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MilesAudioManager::cleanup
+//       Access: Private
+//  Description: Shuts down the audio manager and releases any
+//               resources associated with it.  Also cleans up all
+//               AudioSounds created via the manager.
+////////////////////////////////////////////////////////////////////
+void MilesAudioManager::
+cleanup() {
+  audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this
+              << ", _cleanup_required = " << _cleanup_required);
+  if (!_cleanup_required) {
+    return;
+  }
+
+  // Be sure to cleanup associated sounds before cleaning up the manager:
+  AudioSet::iterator ai;
+  for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) {
+    (*ai)->cleanup();
+  }
+
+  clear_cache();
+  nassertv(_active_managers > 0);
+  --_active_managers;
+  audio_debug("  _active_managers="<<_active_managers);
+
+  if (_active_managers == 0) {
+    if (_dls_field != NULL) {
+      HDLSDEVICE dls;
+      AIL_quick_handles(0, 0, &dls);
+      audio_debug("  AIL_DLS_unload()");
+      AIL_DLS_unload(dls, _dls_field);
+      _dls_field = NULL;
+    }
+
+    if (_miles_active) {
+      audio_debug("  AIL_quick_shutdown()");
+      AIL_quick_shutdown();
+      _miles_active = false;
+    }
+  }
+  _cleanup_required = false;
+  audio_debug("MilesAudioManager::cleanup() finished");
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MilesAudioManager::SoundData::Constructor
 //     Function: MilesAudioManager::SoundData::Constructor
 //       Access: Public
 //       Access: Public
@@ -752,7 +792,10 @@ SoundData() :
 MilesAudioManager::SoundData::
 MilesAudioManager::SoundData::
 ~SoundData() {
 ~SoundData() {
   if (_audio != 0) {
   if (_audio != 0) {
-    AIL_quick_unload(_audio);
+    if (_miles_active) {
+      AIL_quick_unload(_audio);
+    }
+    _audio = 0;
   }
   }
 }
 }
 
 

+ 11 - 4
panda/src/audiotraits/milesAudioManager.h

@@ -38,6 +38,8 @@ public:
   MilesAudioManager();
   MilesAudioManager();
   ~MilesAudioManager();
   ~MilesAudioManager();
 
 
+  virtual void shutdown();
+
   bool is_valid();
   bool is_valid();
   
   
   virtual PT(AudioSound) get_sound(const string& file_name, bool positional = false);
   virtual PT(AudioSound) get_sound(const string& file_name, bool positional = false);
@@ -59,10 +61,6 @@ public:
 
 
   void stop_all_sounds();
   void stop_all_sounds();
 
 
-  // Optional Downloadable Sound field for software midi:
-  // made public so C atexit fn can access it
-  static HDLSFILEID _dls_field;
-
 private:
 private:
   // The sound cache:
   // The sound cache:
   class SoundData : public ReferenceCount {
   class SoundData : public ReferenceCount {
@@ -96,12 +94,20 @@ private:
   float _volume;
   float _volume;
   bool _active;
   bool _active;
   int _cache_limit;
   int _cache_limit;
+  bool _cleanup_required;
   // keep a count for startup and shutdown:
   // keep a count for startup and shutdown:
   static int _active_managers;
   static int _active_managers;
+  static bool _miles_active;
   unsigned int _concurrent_sound_limit;
   unsigned int _concurrent_sound_limit;
   
   
   bool _is_valid;
   bool _is_valid;
   bool _hasMidiSounds;
   bool _hasMidiSounds;
+
+  // Optional Downloadable Sound field for software midi
+  static HDLSFILEID _dls_field;
+
+  typedef pset<MilesAudioManager *> Managers;
+  static Managers *_managers;
   
   
   PT(SoundData) load(Filename file_name);
   PT(SoundData) load(Filename file_name);
   // Tell the manager that the sound dtor was called.
   // Tell the manager that the sound dtor was called.
@@ -122,6 +128,7 @@ private:
   void get_gm_file_path(string& result);
   void get_gm_file_path(string& result);
 
 
   void force_midi_reset();
   void force_midi_reset();
+  void cleanup();
 
 
   friend class MilesAudioSound;
   friend class MilesAudioSound;
 
 

+ 24 - 2
panda/src/audiotraits/milesAudioSound.cxx

@@ -181,9 +181,9 @@ MilesAudioSound(MilesAudioManager* manager,
 MilesAudioSound::
 MilesAudioSound::
 ~MilesAudioSound() {
 ~MilesAudioSound() {
   miles_audio_debug("~MilesAudioSound()");
   miles_audio_debug("~MilesAudioSound()");
-  stop();
+  cleanup();
   _manager->release_sound(this);
   _manager->release_sound(this);
-  AIL_quick_unload(_audio);
+  miles_audio_debug("~MilesAudioSound() done");
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -204,6 +204,7 @@ play() {
     if (status() == AudioSound::PLAYING) {
     if (status() == AudioSound::PLAYING) {
       stop();
       stop();
     }
     }
+    nassertv(_audio);
     _manager->starting_sound(this);
     _manager->starting_sound(this);
     // Start playing:
     // Start playing:
     if (AIL_quick_play(_audio, _loop_count)) {
     if (AIL_quick_play(_audio, _loop_count)) {
@@ -235,6 +236,7 @@ stop() {
   // make this symmetrical with play().  set_active() is the 'owner' of
   // make this symmetrical with play().  set_active() is the 'owner' of
   // _paused.  play() accesses _paused to help in the situation where
   // _paused.  play() accesses _paused to help in the situation where
   // someone calls play on an inactive sound().
   // someone calls play on an inactive sound().
+  nassertv(_audio);
   AIL_quick_halt(_audio);
   AIL_quick_halt(_audio);
 }
 }
 
 
@@ -319,6 +321,7 @@ void MilesAudioSound::
 set_time(float time) {
 set_time(float time) {
   miles_audio_debug("set_time(time="<<time<<")");
   miles_audio_debug("set_time(time="<<time<<")");
 
 
+  nassertv(_audio);
   // Ensure we don't inadvertently run off the end of the sound.
   // Ensure we don't inadvertently run off the end of the sound.
   float max_time = length();
   float max_time = length();
   if (time > max_time) {
   if (time > max_time) {
@@ -339,6 +342,7 @@ set_time(float time) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float MilesAudioSound::
 float MilesAudioSound::
 get_time() const {
 get_time() const {
+  nassertr(_audio, 0.0f);
   S32 millisecond_time=AIL_quick_ms_position(_audio);
   S32 millisecond_time=AIL_quick_ms_position(_audio);
   float time=float(millisecond_time*.001);
   float time=float(millisecond_time*.001);
   miles_audio_debug("get_time() returning "<<time);
   miles_audio_debug("get_time() returning "<<time);
@@ -353,6 +357,7 @@ get_time() const {
 void MilesAudioSound::
 void MilesAudioSound::
 set_volume(float volume) {
 set_volume(float volume) {
   miles_audio_debug("set_volume(volume="<<volume<<")");
   miles_audio_debug("set_volume(volume="<<volume<<")");
+  nassertv(_audio);
   // *Set the volume even if our volume is not changing, because the
   // *Set the volume even if our volume is not changing, because the
   // MilesAudioManager will call set_volume when *its* volume changes.
   // MilesAudioManager will call set_volume when *its* volume changes.
   // Set the volume:
   // Set the volume:
@@ -539,5 +544,22 @@ status() const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MilesAudioSound::cleanup
+//       Access: Private
+//  Description: Called to release any resources associated with the
+//               sound.
+////////////////////////////////////////////////////////////////////
+void MilesAudioSound::
+cleanup() {
+  if (_audio) {
+    stop();
+    if (MilesAudioManager::_miles_active) {
+      AIL_quick_unload(_audio);
+    }
+    _audio = 0;
+  }
+}
+
 
 
 #endif //]
 #endif //]

+ 3 - 2
panda/src/audiotraits/milesAudioSound.h

@@ -142,8 +142,9 @@ private:
   // itwas set inactive.
   // itwas set inactive.
   bool _paused;
   bool _paused;
 
 
-  MilesAudioSound(MilesAudioManager* manager, 
-      MilesAudioManager::SoundData *sd, string file_name, float length=0.0f);
+  MilesAudioSound(MilesAudioManager* manager, MilesAudioManager::SoundData *sd,
+                  string file_name, float length=0.0f);
+  void cleanup();
 
 
   friend class MilesAudioManager;
   friend class MilesAudioManager;