diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index c0939ec716..fceae5f2cf 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -282,6 +282,12 @@ class ShowBase(DirectObject.DirectObject): the desktop and keyboard functionality, etc. """ self.graphicsEngine.removeAllWindows() + + if self.musicManager: + # Temporary condition for old Pandas. + if hasattr(self.musicManager, 'shutdown'): + self.musicManager.shutdown() + del self.win del self.winList del self.pipe diff --git a/panda/src/audio/audioManager.cxx b/panda/src/audio/audioManager.cxx index 9b5aea2c4d..ac93610d50 100644 --- a/panda/src/audio/audioManager.cxx +++ b/panda/src/audio/audioManager.cxx @@ -86,6 +86,28 @@ create_AudioManager() { 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 // Access: Public diff --git a/panda/src/audio/audioManager.h b/panda/src/audio/audioManager.h index 39d9bf1065..805a8dc52e 100644 --- a/panda/src/audio/audioManager.h +++ b/panda/src/audio/audioManager.h @@ -37,7 +37,9 @@ PUBLISHED: // my_music = MyMusicManager.get_sound("introTheme.mid"); static PT(AudioManager) create_AudioManager(); - virtual ~AudioManager() {} + virtual ~AudioManager(); + + virtual void shutdown(); // If you're interested in knowing whether this audio manager // is valid, here's the call to do it. It is not necessary diff --git a/panda/src/audiotraits/milesAudioManager.cxx b/panda/src/audiotraits/milesAudioManager.cxx index 4d7d57a518..478e7fe429 100644 --- a/panda/src/audiotraits/milesAudioManager.cxx +++ b/panda/src/audiotraits/milesAudioManager.cxx @@ -34,56 +34,19 @@ TypeHandle MilesAudioManager::_type_handle; int MilesAudioManager::_active_managers = 0; +bool MilesAudioManager::_miles_active = false; 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() { audio_debug("Create_AudioManager() Miles."); 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 // Access: Public @@ -94,16 +57,23 @@ void CustomMilesShutdown() { //////////////////////////////////////////////////////////////////// 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="<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()); - + if (!sd->_audio) { audio_error(" MilesAudioManager::load failed "<< AIL_last_error()); return NULL; @@ -376,7 +364,8 @@ get_sound(const string& file_name, bool) { =new MilesAudioSound(this, sd, (*si).first); nassertr(milesAudioSound, 0); milesAudioSound->set_active(_active); - _sounds_on_loan.insert(milesAudioSound); + bool inserted = _sounds_on_loan.insert(milesAudioSound).second; + nassertr(inserted, milesAudioSound.p()); audioSound=milesAudioSound; } @@ -505,8 +494,12 @@ get_cache_limit() const { void MilesAudioManager:: release_sound(MilesAudioSound* audioSound) { audio_debug("MilesAudioManager::release_sound(audioSound=\"" - <get_name()<<"\")"); - _sounds_on_loan.erase(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 // Access: Public @@ -732,6 +702,76 @@ get_gm_file_path(string& result) { audio_debug("MilesAudioManager::get_gm_file_path() result out=\""<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 // Access: Public @@ -752,7 +792,10 @@ SoundData() : MilesAudioManager::SoundData:: ~SoundData() { if (_audio != 0) { - AIL_quick_unload(_audio); + if (_miles_active) { + AIL_quick_unload(_audio); + } + _audio = 0; } } diff --git a/panda/src/audiotraits/milesAudioManager.h b/panda/src/audiotraits/milesAudioManager.h index 1ac2735a7d..875e1ac020 100644 --- a/panda/src/audiotraits/milesAudioManager.h +++ b/panda/src/audiotraits/milesAudioManager.h @@ -38,6 +38,8 @@ public: MilesAudioManager(); ~MilesAudioManager(); + virtual void shutdown(); + bool is_valid(); virtual PT(AudioSound) get_sound(const string& file_name, bool positional = false); @@ -59,10 +61,6 @@ public: 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: // The sound cache: class SoundData : public ReferenceCount { @@ -96,12 +94,20 @@ private: float _volume; bool _active; int _cache_limit; + bool _cleanup_required; // keep a count for startup and shutdown: static int _active_managers; + static bool _miles_active; unsigned int _concurrent_sound_limit; bool _is_valid; bool _hasMidiSounds; + + // Optional Downloadable Sound field for software midi + static HDLSFILEID _dls_field; + + typedef pset Managers; + static Managers *_managers; PT(SoundData) load(Filename file_name); // Tell the manager that the sound dtor was called. @@ -122,6 +128,7 @@ private: void get_gm_file_path(string& result); void force_midi_reset(); + void cleanup(); friend class MilesAudioSound; diff --git a/panda/src/audiotraits/milesAudioSound.cxx b/panda/src/audiotraits/milesAudioSound.cxx index eb2c64a2f6..56c4cef794 100644 --- a/panda/src/audiotraits/milesAudioSound.cxx +++ b/panda/src/audiotraits/milesAudioSound.cxx @@ -181,9 +181,9 @@ MilesAudioSound(MilesAudioManager* manager, MilesAudioSound:: ~MilesAudioSound() { miles_audio_debug("~MilesAudioSound()"); - stop(); + cleanup(); _manager->release_sound(this); - AIL_quick_unload(_audio); + miles_audio_debug("~MilesAudioSound() done"); } //////////////////////////////////////////////////////////////////// @@ -204,6 +204,7 @@ play() { if (status() == AudioSound::PLAYING) { stop(); } + nassertv(_audio); _manager->starting_sound(this); // Start playing: if (AIL_quick_play(_audio, _loop_count)) { @@ -235,6 +236,7 @@ stop() { // make this symmetrical with play(). set_active() is the 'owner' of // _paused. play() accesses _paused to help in the situation where // someone calls play on an inactive sound(). + nassertv(_audio); AIL_quick_halt(_audio); } @@ -319,6 +321,7 @@ void MilesAudioSound:: set_time(float time) { miles_audio_debug("set_time(time="< max_time) { @@ -339,6 +342,7 @@ set_time(float time) { //////////////////////////////////////////////////////////////////// float MilesAudioSound:: get_time() const { + nassertr(_audio, 0.0f); S32 millisecond_time=AIL_quick_ms_position(_audio); float time=float(millisecond_time*.001); miles_audio_debug("get_time() returning "<