diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 720bf22044..bacc080e71 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -716,7 +716,8 @@ class ShowBase(DirectObject.DirectObject): self.musicManagerIsValid=self.musicManager!=None \ and self.musicManager.isValid() if self.musicManagerIsValid: - self.musicManager.setMutuallyExclusive(1) # ensure only 1 midi song is playing at a time + # ensure only 1 midi song is playing at a time: + self.musicManager.setConcurrentSoundLimit(1) self.musicManager.setActive(self.musicActive) # enableMusic/enableSoundEffects are meant to be called in response to a user request diff --git a/panda/src/audio/audioManager.cxx b/panda/src/audio/audioManager.cxx index ec053efd65..f47bb4e0c3 100644 --- a/panda/src/audio/audioManager.cxx +++ b/panda/src/audio/audioManager.cxx @@ -79,7 +79,6 @@ create_AudioManager() { if (!am->is_valid()) { am = create_NullAudioManger(); } - am->_bExclusive = false; return am; } @@ -99,7 +98,3 @@ get_null_sound() { return _null_sound; } -void AudioManager:: -set_mutually_exclusive(bool bExclusive) { - _bExclusive = bExclusive; -} diff --git a/panda/src/audio/audioManager.h b/panda/src/audio/audioManager.h index 05cd6d1f1e..011da0f972 100644 --- a/panda/src/audio/audioManager.h +++ b/panda/src/audio/audioManager.h @@ -38,6 +38,14 @@ PUBLISHED: static PT(AudioManager) create_AudioManager(); virtual ~AudioManager() {} + + // If you're interested in knowing whether this audio manager + // is valid, here's the call to do it. It is not necessary + // to check whether the audio manager is valid before making other + // calls. You are free to use an invalid sound manager, you + // may get silent sounds from it though. The sound manager and + // the sounds it creates should not crash the application even + // when the objects are not valid. virtual bool is_valid() = 0; // Get a sound: @@ -51,12 +59,8 @@ PUBLISHED: // in its pool/cache. virtual void uncache_sound(const string& file_name) = 0; virtual void clear_cache() = 0; - virtual void set_cache_limit(int count) = 0; - virtual int get_cache_limit() = 0; - - // if set, turn off any currently-playing sounds before playing - // a new one (useful for midi songs) - void set_mutually_exclusive(bool bExclusive); + virtual void set_cache_limit(unsigned int count) = 0; + virtual unsigned int get_cache_limit() const = 0; // Control volume: // FYI: @@ -65,7 +69,7 @@ PUBLISHED: // 0 = minimum; 1.0 = maximum. // inits to 1.0. virtual void set_volume(float volume) = 0; - virtual float get_volume() = 0; + virtual float get_volume() const = 0; // Turn the manager on or off. // If you play a sound while the manager is inactive, it won't start. @@ -76,14 +80,42 @@ PUBLISHED: // they will start playing from the begining of their loop. // inits to true. virtual void set_active(bool flag) = 0; - virtual bool get_active() = 0; + virtual bool get_active() const = 0; + + // This controls the number of sounds that you allow at once. This + // is more of a user choice -- it avoids talk over and the creation + // of a cacophony. + // It can also be used to help performance. + // 0 == unlimited. + // 1 == mutually exclusive (one sound at a time). Which is an example of: + // n == allow n sounds to be playing at the same time. + virtual void set_concurrent_sound_limit(unsigned int limit = 0) = 0; + virtual unsigned int get_concurrent_sound_limit() const = 0; + + // This is likely to be a utility function for the concurrent_sound_limit + // options. It is exposed as an API, because it's reasonable that it + // may be useful to be here. It reduces the number of concurrently + // playing sounds to count by some implementation specific means. + // If the number of sounds currently playing is at or below count then + // there is no effect. + virtual void reduce_sounds_playing_to(unsigned int count) = 0; + + // Stop playback on all sounds managed by this manager. + // This is effectively the same as reduce_sounds_playing_to(0), but + // this call may be for efficient on some implementations. + virtual void stop_all_sounds() = 0; public: static void register_AudioManager_creator(Create_AudioManager_proc* proc); protected: + friend class AudioSound; + + // Avoid adding data members (instance variables) to this mostly abstract + // base class. This allows implementors of various sound systems the + // best flexibility. + static Create_AudioManager_proc* _create_AudioManager; - bool _bExclusive; PT(AudioSound) _null_sound; AudioManager() { diff --git a/panda/src/audio/audioSound.h b/panda/src/audio/audioSound.h index a2530b2f01..c94a4714eb 100644 --- a/panda/src/audio/audioSound.h +++ b/panda/src/audio/audioSound.h @@ -84,6 +84,11 @@ PUBLISHED: // inits to manager's state. virtual void set_active(bool flag=true) = 0; virtual bool get_active() const = 0; + + // Set (or clear) the event that will be thrown when the sound + // finishes playing. To clear the event, pass an empty string. + virtual void set_finished_event(const string& event) = 0; + virtual const string& get_finished_event() const = 0; // There is no set_name(), this is intentional. virtual const string& get_name() const = 0; diff --git a/panda/src/audio/nullAudioManager.cxx b/panda/src/audio/nullAudioManager.cxx index 264bf83cdc..76d30dd091 100644 --- a/panda/src/audio/nullAudioManager.cxx +++ b/panda/src/audio/nullAudioManager.cxx @@ -86,7 +86,7 @@ clear_cache() { // Description: //////////////////////////////////////////////////////////////////// void NullAudioManager:: -set_cache_limit(int) { +set_cache_limit(unsigned int) { // intentionally blank. } @@ -95,8 +95,8 @@ set_cache_limit(int) { // Access: Public // Description: //////////////////////////////////////////////////////////////////// -int NullAudioManager:: -get_cache_limit() { +unsigned int NullAudioManager:: +get_cache_limit() const { // intentionally blank. return 0; } @@ -117,7 +117,7 @@ set_volume(float) { // Description: //////////////////////////////////////////////////////////////////// float NullAudioManager:: -get_volume() { +get_volume() const { return 0; } @@ -137,6 +137,46 @@ set_active(bool) { // Description: //////////////////////////////////////////////////////////////////// bool NullAudioManager:: -get_active() { +get_active() const { return 0; } + +//////////////////////////////////////////////////////////////////// +// Function: NullAudioManager::set_concurrent_sound_limit +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NullAudioManager:: +set_concurrent_sound_limit(unsigned int) { + // intentionally blank. +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAudioManager::get_concurrent_sound_limit +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +unsigned int NullAudioManager:: +get_concurrent_sound_limit() const { + return 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAudioManager::reduce_sounds_playing_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NullAudioManager:: +reduce_sounds_playing_to(unsigned int) { + // intentionally blank. +} + +//////////////////////////////////////////////////////////////////// +// Function: NullAudioManager::stop_all_sounds +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void NullAudioManager:: +stop_all_sounds() { + // intentionally blank. +} diff --git a/panda/src/audio/nullAudioManager.h b/panda/src/audio/nullAudioManager.h index ff57f05c7c..dca8b06e56 100644 --- a/panda/src/audio/nullAudioManager.h +++ b/panda/src/audio/nullAudioManager.h @@ -37,14 +37,21 @@ public: virtual PT(AudioSound) get_sound(const string&); virtual void uncache_sound(const string&); virtual void clear_cache(); - virtual void set_cache_limit(int); - virtual int get_cache_limit(); + virtual void set_cache_limit(unsigned int); + virtual unsigned int get_cache_limit() const; virtual void set_volume(float); - virtual float get_volume(); + virtual float get_volume() const; virtual void set_active(bool); - virtual bool get_active(); + virtual bool get_active() const; + + virtual void set_concurrent_sound_limit(unsigned int limit); + virtual unsigned int get_concurrent_sound_limit() const; + + virtual void reduce_sounds_playing_to(unsigned int count); + + virtual void stop_all_sounds(); }; #endif /* __NULL_AUDIO_MANAGER_H__ */ diff --git a/panda/src/audio/nullAudioSound.cxx b/panda/src/audio/nullAudioSound.cxx index 2263a4100e..cf13391f02 100644 --- a/panda/src/audio/nullAudioSound.cxx +++ b/panda/src/audio/nullAudioSound.cxx @@ -20,6 +20,10 @@ #include "nullAudioSound.h" +namespace { + static const string blank=""; +} + NullAudioSound::NullAudioSound() { // Intentionally blank. } @@ -84,8 +88,15 @@ bool NullAudioSound::get_active() const { return false; } +void NullAudioSound::set_finished_event(const string& event) { + // Intentionally blank. +} + +const string& NullAudioSound::get_finished_event() const { + return blank; +} + const string& NullAudioSound::get_name() const { - static const string blank=""; return blank; } diff --git a/panda/src/audio/nullAudioSound.h b/panda/src/audio/nullAudioSound.h index 4eebb60e38..694cf0296b 100644 --- a/panda/src/audio/nullAudioSound.h +++ b/panda/src/audio/nullAudioSound.h @@ -54,6 +54,9 @@ public: void set_active(bool); bool get_active() const; + + void set_finished_event(const string& event); + const string& get_finished_event() const; const string& get_name() const; diff --git a/panda/src/audiotraits/fmodAudioManager.cxx b/panda/src/audiotraits/fmodAudioManager.cxx index d32db2a9ec..398170cbea 100644 --- a/panda/src/audiotraits/fmodAudioManager.cxx +++ b/panda/src/audiotraits/fmodAudioManager.cxx @@ -54,6 +54,7 @@ FmodAudioManager() { _active = audio_active; _cache_limit = audio_cache_limit; + _concurrent_sound_limit = 0; // Initialize FMOD, if this is the first manager created. _is_valid = true; @@ -62,20 +63,19 @@ FmodAudioManager() { audio_debug("Initializing FMOD for real."); float fmod_dll_version = FSOUND_GetVersion(); if (fmod_dll_version < FMOD_VERSION) { - audio_error("Wrong FMOD DLL version. You have "<set_active(_active); _soundsOnLoan.insert(fmodAudioSound); audioSound = fmodAudioSound; @@ -263,7 +263,7 @@ uncache_sound(const string& file_name) { // If the refcount is already zero, it can be // purged right now! audio_debug("FmodAudioManager::uncache_sound: purging "<data; // Erase the sound from the LRU list as well. @@ -324,7 +324,7 @@ most_recently_used(const string& path) { //////////////////////////////////////////////////////////////////// // Function: FmodAudioManager::clear_cache // Access: Public -// Description: +// Description: Clear out the sound cache. //////////////////////////////////////////////////////////////////// void FmodAudioManager:: clear_cache() { @@ -340,7 +340,7 @@ clear_cache() { SoundCacheEntry *entry = &(*itor).second; if (entry->refcount == 0) { audio_debug("FmodAudioManager::clear_cache: purging "<< (*itor).first - << " from the cache."); + << " from the cache."); delete [] entry->data; // Erase the sound from the LRU list as well. @@ -361,18 +361,15 @@ clear_cache() { //////////////////////////////////////////////////////////////////// // Function: FmodAudioManager::set_cache_limit // Access: Public -// Description: +// Description: Set the number of sounds that the cache can hold. //////////////////////////////////////////////////////////////////// void FmodAudioManager:: -set_cache_limit(int count) { - audio_debug("FmodAudioManager::set_cache_limit(count=" - < (unsigned int) count) { + while (_lru.size() > count) { uncache_a_sound(); } - _cache_limit = count; assert(is_valid()); } @@ -385,7 +382,7 @@ set_cache_limit(int count) { int FmodAudioManager:: get_cache_limit() { audio_debug("FmodAudioManager::get_cache_limit() returning " - <<_cache_limit); + <<_cache_limit); return _cache_limit; } @@ -451,18 +448,56 @@ get_active() { return _active; } +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioManager::set_concurrent_sound_limit +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void FmodAudioManager:: +set_concurrent_sound_limit(unsigned int limit) { + _concurrent_sound_limit = limit; + reduce_sounds_playing_to(_concurrent_sound_limit); +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioManager::get_concurrent_sound_limit +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +unsigned int FmodAudioManager:: +get_concurrent_sound_limit() const { + return _concurrent_sound_limit; +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioManager::reduce_sounds_playing_to +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void FmodAudioManager:: +reduce_sounds_playing_to(unsigned int count) { + // This is an example from Miles audio, this should be done for fmod: + //int limit = _sounds_playing.size() - count; + //while (limit-- > 0) { + // SoundsPlaying::iterator sound = _sounds_playing.begin(); + // assert(sound != _sounds_playing.end()); + // (**sound).stop(); + //} +} + //////////////////////////////////////////////////////////////////// // Function: FmodAudioManager::stop_all_sounds // Access: Public // Description: Stop playback on all sounds managed by this manager. //////////////////////////////////////////////////////////////////// void FmodAudioManager:: -stop_all_sounds(void) { +stop_all_sounds() { audio_debug("FmodAudioManager::stop_all_sounds()"); AudioSet::iterator i=_soundsOnLoan.begin(); for (; i!=_soundsOnLoan.end(); ++i) { - if((**i).status()==AudioSound::PLAYING) + if ((**i).status()==AudioSound::PLAYING) { (**i).stop(); + } } } @@ -484,7 +519,7 @@ inc_refcount(const string& file_name) { entry->refcount++; entry->stale = false; // definitely not stale! audio_debug("FmodAudioManager: "<refcount); + << entry->refcount); } //////////////////////////////////////////////////////////////////// @@ -502,7 +537,7 @@ dec_refcount(const string& file_name) { SoundCacheEntry *entry = &(*itor).second; entry->refcount--; audio_debug("FmodAudioManager: "<refcount); + << entry->refcount); if (entry->refcount == 0 && entry->stale) { audio_debug("FmodAudioManager::dec_refcount: purging "<data; @@ -537,7 +572,7 @@ load(const Filename& filename, size_t &size) const { } if (!bSupported) { audio_error("FmodAudioManager::load: "< +#include "pandabase.h" #ifdef HAVE_FMOD //[ #include "audioManager.h" @@ -30,7 +30,7 @@ class FmodAudioSound; #include "pmap.h" #include "pset.h" -#include +#include // Is fmod.h really a system file? I think maybe this should be "fmod.h". class EXPCL_FMOD_AUDIO FmodAudioManager : public AudioManager { // All of these methods are stubbed out to some degree. @@ -46,7 +46,7 @@ public: virtual PT(AudioSound) get_sound(const string&); virtual void uncache_sound(const string&); virtual void clear_cache(); - virtual void set_cache_limit(int); + virtual void set_cache_limit(unsigned int count); virtual int get_cache_limit(); // Indicates that the given sound was the most recently used. @@ -60,7 +60,13 @@ public: virtual void set_active(bool); virtual bool get_active(); - void stop_all_sounds(); + + virtual void set_concurrent_sound_limit(unsigned int limit = 0); + virtual unsigned int get_concurrent_sound_limit() const; + + virtual void reduce_sounds_playing_to(unsigned int count); + + virtual void stop_all_sounds(); protected: // increment or decrement the refcount of the given file's cache entry. @@ -80,6 +86,7 @@ private: typedef pset AudioSet; // The offspring of this manager: AudioSet _soundsOnLoan; + unsigned int _concurrent_sound_limit; // The Least Recently Used mechanism: typedef pdeque LRU; diff --git a/panda/src/audiotraits/fmodAudioSound.cxx b/panda/src/audiotraits/fmodAudioSound.cxx index dcc3f230f2..a6879e3f0e 100644 --- a/panda/src/audiotraits/fmodAudioSound.cxx +++ b/panda/src/audiotraits/fmodAudioSound.cxx @@ -34,14 +34,14 @@ FmodAudioSound:: FmodAudioSound(FmodAudioManager* manager, FSOUND_STREAM *audio_data, - string file_name, float length) + string file_name, float length) : _manager(manager), _audio(audio_data), _file_name(file_name), _volume(1.0f), _balance(0), _loop_count(1), _length(length), _active(true), _paused(false), _channel(-1) { nassertv(!file_name.empty()); nassertv(audio_data != NULL); audio_debug("FmodAudioSound(manager=0x"<<(void*)&manager - <<", file_name="<_bExclusive) { - // stop any other sound that parent mgr is playing - _manager->stop_all_sounds(); - } - // If the sound is already playing, stop it. if (this->status() == AudioSound::PLAYING) { this->stop(); } + if (_manager->stop_a_sound()) { + // stop another sound that parent mgr is playing + _manager->stop_all_sounds(); + } // Play the stream, but start it paused so we can set the volume and // panning first. @@ -92,7 +90,9 @@ play() { // Delay until the channel is actually playing. This shouldn't // result in a noticeable delay, and allows you to immediately test // the status of the sound after initiating playback. - while (!FSOUND_IsPlaying(_channel)); + while (!FSOUND_IsPlaying(_channel)) { + // intentinaly empty loop. + } } void FmodAudioSound::stop() { diff --git a/panda/src/audiotraits/fmodAudioSound.h b/panda/src/audiotraits/fmodAudioSound.h index ae401a1fba..9beb9fc3c0 100644 --- a/panda/src/audiotraits/fmodAudioSound.h +++ b/panda/src/audiotraits/fmodAudioSound.h @@ -69,6 +69,9 @@ public: // inits to manager's state. void set_active(bool active=true); bool get_active() const; + + void set_finished_event(const string& event); + const string& get_finished_event() const; const string& get_name() const; @@ -92,7 +95,7 @@ private: int _channel; FmodAudioSound(FmodAudioManager* manager, FSOUND_STREAM *audio_data, - string file_name, float length=0.0f); + string file_name, float length=0.0f); // forbidden functions! FmodAudioSound(const FmodAudioSound& rhs) {} diff --git a/panda/src/audiotraits/milesAudioManager.cxx b/panda/src/audiotraits/milesAudioManager.cxx index 9a6479f077..f84bd3eeff 100644 --- a/panda/src/audiotraits/milesAudioManager.cxx +++ b/panda/src/audiotraits/milesAudioManager.cxx @@ -32,54 +32,49 @@ int MilesAudioManager::_active_managers = 0; HDLSFILEID MilesAudioManager::_dls_field = NULL; bool bMilesShutdownCalled = false; -bool bMilesShutdownAtExitRegistered = false; PT(AudioManager) Create_AudioManager() { audio_debug("Create_AudioManager() Miles."); return new MilesAudioManager(); } -void fMilesShutdown(void) { - if(bMilesShutdownCalled) - return; +void CustomMilesShutdown() { + if (bMilesShutdownCalled) { + return; + } + bMilesShutdownCalled = true; - bMilesShutdownCalled = 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 //] + 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 + #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 } //////////////////////////////////////////////////////////////////// @@ -95,8 +90,9 @@ MilesAudioManager() { _active = audio_active; _volume = audio_volume; _cache_limit = audio_cache_limit; + _concurrent_sound_limit = 0; _is_valid = true; - _bHasMidiSounds = false; + _hasMidiSounds = false; if (_active_managers==0) { S32 use_digital=(audio_play_wave || audio_play_mp3)?1:0; S32 use_MIDI=(audio_play_midi)?1:0; @@ -146,7 +142,7 @@ MilesAudioManager() { audio_info(" using Miles software midi"); } } else { - audio_info(" using Miles hardware midi"); + audio_info(" using Miles hardware midi"); } if (use_vfs) { @@ -169,9 +165,10 @@ MilesAudioManager() { if (_is_valid) { assert(is_valid()); - if(!bMilesShutdownAtExitRegistered) { - bMilesShutdownAtExitRegistered = true; - atexit(fMilesShutdown); + static bool atexit_registered = false; + if(!atexit_registered) { + atexit_registered = true; + atexit(CustomMilesShutdown); } } } @@ -185,7 +182,7 @@ MilesAudioManager:: ~MilesAudioManager() { audio_debug("MilesAudioManager::~MilesAudioManager()"); // Be sure to delete associated sounds before deleting the manager: - nassertv(_soundsOnLoan.empty()); + nassertv(_sounds_on_loan.empty()); clear_cache(); --_active_managers; audio_debug(" _active_managers="<<_active_managers); @@ -238,10 +235,8 @@ is_valid() { HAUDIO MilesAudioManager:: load(Filename file_name) { HAUDIO audio; - if (use_vfs) { audio = AIL_quick_load(file_name.c_str()); - } else { string stmp = file_name.to_os_specific(); audio_debug(" \"" << stmp << "\""); @@ -318,11 +313,11 @@ get_sound(const string& file_name) { =new MilesAudioSound(this, audio, (*si).first); nassertr(milesAudioSound, 0); milesAudioSound->set_active(_active); - _soundsOnLoan.insert(milesAudioSound); + _sounds_on_loan.insert(milesAudioSound); audioSound=milesAudioSound; } - _bHasMidiSounds |= (file_name.find(".mid")!=string::npos); + _hasMidiSounds |= (file_name.find(".mid")!=string::npos); audio_debug(" returning 0x" << (void*)audioSound); assert(is_valid()); return audioSound; @@ -406,7 +401,7 @@ most_recently_used(const string& path) { //////////////////////////////////////////////////////////////////// // Function: MilesAudioManager::clear_cache // Access: Public -// Description: +// Description: Clear out the sound cache. //////////////////////////////////////////////////////////////////// void MilesAudioManager:: clear_cache() { @@ -424,14 +419,13 @@ clear_cache() { //////////////////////////////////////////////////////////////////// // Function: MilesAudioManager::set_cache_limit // Access: Public -// Description: +// Description: Set the number of sounds that the cache can hold. //////////////////////////////////////////////////////////////////// void MilesAudioManager:: -set_cache_limit(int count) { - audio_debug("MilesAudioManager::set_cache_limit(count=" - < (unsigned int) count) { + while (_lru.size() > count) { uncache_a_sound(); } _cache_limit=count; @@ -443,8 +437,8 @@ set_cache_limit(int count) { // Access: Public // Description: //////////////////////////////////////////////////////////////////// -int MilesAudioManager:: -get_cache_limit() { +unsigned int MilesAudioManager:: +get_cache_limit() const { audio_debug("MilesAudioManager::get_cache_limit() returning " <<_cache_limit); return _cache_limit; @@ -459,7 +453,7 @@ void MilesAudioManager:: release_sound(MilesAudioSound* audioSound) { audio_debug("MilesAudioManager::release_sound(audioSound=\"" <get_name()<<"\")"); - _soundsOnLoan.erase(audioSound); + _sounds_on_loan.erase(audioSound); } //////////////////////////////////////////////////////////////////// @@ -473,53 +467,43 @@ set_volume(float volume) { if (_volume!=volume) { _volume = volume; // Tell our AudioSounds to adjust: - AudioSet::iterator i=_soundsOnLoan.begin(); - for (; i!=_soundsOnLoan.end(); ++i) { + AudioSet::iterator i=_sounds_on_loan.begin(); + for (; i!=_sounds_on_loan.end(); ++i) { (**i).set_volume((**i).get_volume()); } } } +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::force_midi_reset +// Access: Public +// Description: ?????. +//////////////////////////////////////////////////////////////////// void MilesAudioManager:: -stop_all_sounds(void) { - audio_debug("MilesAudioManager::stop_all_sounds()"); - AudioSet::iterator i=_soundsOnLoan.begin(); - for (; i!=_soundsOnLoan.end(); ++i) { - if((**i).status()==AudioSound::PLAYING) - (**i).stop(); +force_midi_reset() { + if (!miles_audio_force_midi_reset) { + audio_debug("MilesAudioManager::skipping force_midi_reset"); + return; } + audio_debug("MilesAudioManager::force_midi_reset"); - if(_bHasMidiSounds) { - forceMidiReset(); + // 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); } } -void MilesAudioManager:: -forceMidiReset(void) { - if(!miles_audio_force_midi_reset) { - audio_debug("MilesAudioManager::skipping forceMidiReset"); - return; - } - - audio_debug("MilesAudioManager::ForceMidiReset"); - - // 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 // Description: get the overall volume //////////////////////////////////////////////////////////////////// float MilesAudioManager:: -get_volume() { +get_volume() const { audio_debug("MilesAudioManager::get_volume() returning "<<_volume); return _volume; } @@ -535,13 +519,13 @@ set_active(bool active) { if (_active!=active) { _active=active; // Tell our AudioSounds to adjust: - AudioSet::iterator i=_soundsOnLoan.begin(); - for (; i!=_soundsOnLoan.end(); ++i) { + AudioSet::iterator i=_sounds_on_loan.begin(); + for (; i!=_sounds_on_loan.end(); ++i) { (**i).set_active(_active); } - if((!_active) && _bHasMidiSounds) { - forceMidiReset(); + if((!_active) && _hasMidiSounds) { + force_midi_reset(); } } } @@ -552,11 +536,86 @@ set_active(bool active) { // Description: //////////////////////////////////////////////////////////////////// bool MilesAudioManager:: -get_active() { +get_active() const { audio_debug("MilesAudioManager::get_active() returning "<<_active); return _active; } +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::starting_sound +// Access: +// Description: Inform the manager that a sound is about to play. +//////////////////////////////////////////////////////////////////// +void MilesAudioManager:: +starting_sound(MilesAudioSound* audio) { + if (_concurrent_sound_limit) { + reduce_sounds_playing_to(_concurrent_sound_limit); + } + _sounds_playing.insert(audio); +} + +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::stoping_sound +// Access: +// Description: Inform the manager that a sound is finished or +// someone called stop on the sound (this should not +// be called if a sound is only paused). +//////////////////////////////////////////////////////////////////// +void MilesAudioManager:: +stoping_sound(MilesAudioSound* audio) { + _sounds_playing.erase(audio); + if (_hasMidiSounds && _sounds_playing.size() == 0) { + force_midi_reset(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::set_concurrent_sound_limit +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void MilesAudioManager:: +set_concurrent_sound_limit(unsigned int limit) { + _concurrent_sound_limit = limit; + reduce_sounds_playing_to(_concurrent_sound_limit); +} + +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::get_concurrent_sound_limit +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +unsigned int MilesAudioManager:: +get_concurrent_sound_limit() const { + return _concurrent_sound_limit; +} + +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::reduce_sounds_playing_to +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void MilesAudioManager:: +reduce_sounds_playing_to(unsigned int count) { + int limit = _sounds_playing.size() - count; + while (limit-- > 0) { + SoundsPlaying::iterator sound = _sounds_playing.begin(); + assert(sound != _sounds_playing.end()); + (**sound).stop(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::stop_all_sounds +// Access: Public +// Description: Stop playback on all sounds managed by this manager. +//////////////////////////////////////////////////////////////////// +void MilesAudioManager:: +stop_all_sounds() { + audio_debug("MilesAudioManager::stop_all_sounds()"); + reduce_sounds_playing_to(0); +} + //////////////////////////////////////////////////////////////////// // Function: MilesAudioManager::get_registry_entry // Access: private diff --git a/panda/src/audiotraits/milesAudioManager.h b/panda/src/audiotraits/milesAudioManager.h index 640d85d985..0e3705afab 100644 --- a/panda/src/audiotraits/milesAudioManager.h +++ b/panda/src/audiotraits/milesAudioManager.h @@ -20,7 +20,7 @@ #ifndef __MILES_AUDIO_MANAGER_H__ //[ #define __MILES_AUDIO_MANAGER_H__ -#include +#include "pandabase.h" #ifdef HAVE_RAD_MSS //[ #include "audioManager.h" @@ -43,16 +43,21 @@ public: PT(AudioSound) get_sound(const string& file_name); void uncache_sound(const string& file_name); void clear_cache(); - void set_cache_limit(int count); - int get_cache_limit(); + void set_cache_limit(unsigned int count); + unsigned int get_cache_limit() const; void set_volume(float volume); - float get_volume(); + float get_volume() const; void set_active(bool active); - bool get_active(); + bool get_active() const; + + void set_concurrent_sound_limit(unsigned int limit = 0); + unsigned int get_concurrent_sound_limit() const; + + void reduce_sounds_playing_to(unsigned int count); + void stop_all_sounds(); - void forceMidiReset(); // Optional Downloadable Sound field for software midi: // made public so C atexit fn can access it @@ -65,7 +70,11 @@ private: typedef pset AudioSet; // The offspring of this manager: - AudioSet _soundsOnLoan; + AudioSet _sounds_on_loan; + + typedef pset SoundsPlaying; + // The sounds from this manager that are currently playing: + SoundsPlaying _sounds_playing; // The Least Recently Used mechanism: typedef pdeque LRU; @@ -76,9 +85,10 @@ private: int _cache_limit; // keep a count for startup and shutdown: static int _active_managers; + unsigned int _concurrent_sound_limit; bool _is_valid; - bool _bHasMidiSounds; + bool _hasMidiSounds; HAUDIO load(Filename file_name); // Tell the manager that the sound dtor was called. @@ -87,6 +97,9 @@ private: void most_recently_used(const string& path); void uncache_a_sound(); + void starting_sound(MilesAudioSound* audio); + void stoping_sound(MilesAudioSound* audio); + // utility function that should be moved to another class: bool get_registry_entry(HKEY base, const char* subKeyName, @@ -95,6 +108,8 @@ private: // get the default dls file path: void get_gm_file_path(string& result); + void force_midi_reset(); + // These are "callback" functions that implement vfs-style I/O for // Miles. static U32 AILCALLBACK vfs_open_callback(const char *filename, U32 *file_handle); @@ -102,7 +117,7 @@ private: static S32 AILCALLBACK vfs_seek_callback(U32 file_handle, S32 offset, U32 type); static void AILCALLBACK vfs_close_callback(U32 file_handle); - friend MilesAudioSound; + friend class MilesAudioSound; }; EXPCL_MILES_AUDIO PT(AudioManager) Create_AudioManager(); diff --git a/panda/src/audiotraits/milesAudioSound.cxx b/panda/src/audiotraits/milesAudioSound.cxx index 9c0f54c841..58258a4ac7 100644 --- a/panda/src/audiotraits/milesAudioSound.cxx +++ b/panda/src/audiotraits/milesAudioSound.cxx @@ -16,9 +16,10 @@ // //////////////////////////////////////////////////////////////////// -#include +#include "pandabase.h" #ifdef HAVE_RAD_MSS //[ +#include "throw_event.h" #include "milesAudioSound.h" #include "milesAudioManager.h" @@ -45,6 +46,72 @@ return 'x'; // bad. } } + + AILSEQUENCECB sequence_callback = 0; + AILSAMPLECB sample_callback = 0; + const user_data_index = 7; + + void AILCALLBACK + pandaAudioAilCallback_Sequence(HSEQUENCE S) { + assert(S); + audio_debug("pandaAudioAilCallback_Sequence(HSEQUENCE="<<((void*)S)<<")"); + MilesAudioSound* sound = (MilesAudioSound*)AIL_sequence_user_data( + S, user_data_index); + assert(sound); + sound->finished(); + if (sequence_callback) { + sequence_callback(S); + } + } + + void AILCALLBACK + pandaAudioAilCallback_Sample(HSAMPLE S) { + assert(S); + audio_debug("pandaAudioAilCallback_Sample(HSAMPLE="<<((void*)S)<<")"); + MilesAudioSound* sound = (MilesAudioSound*)AIL_sample_user_data( + S, user_data_index); + assert(sound); + sound->finished(); + if (sample_callback) { + sample_callback(S); + } + } + + void + panda_AIL_quick_set_finished_callback(HAUDIO audio, MilesAudioSound* sound) { + audio_debug("panda_AIL_quick_set_finished_callback(audio="<<((void*)audio) + <<", sound="<<((void*)sound)<<")"); + if (!audio || !sound) { + return; + } + AIL_lock(); + if (audio->handle != NULL) { + switch (audio->type) { + case AIL_QUICK_XMIDI_TYPE: + audio_debug(" AIL_register_sequence_callback"); + AIL_set_sequence_user_data( + (HSEQUENCE)audio->handle, user_data_index, (long)sound); + sequence_callback=AIL_register_sequence_callback( + (HSEQUENCE)audio->handle, pandaAudioAilCallback_Sequence); + audio_debug( "AILCALLBACK "<<((void*)sequence_callback)); + break; + case AIL_QUICK_DIGITAL_TYPE: + case AIL_QUICK_MPEG_DIGITAL_TYPE: + audio_debug(" AIL_register_EOS_callback"); + AIL_set_sample_user_data( + (HSAMPLE)audio->handle, user_data_index, (long)sound); + sample_callback=AIL_register_EOS_callback( + (HSAMPLE)audio->handle, pandaAudioAilCallback_Sample); + audio_debug(" AILCALLBACK "<<((void*)sample_callback)); + break; + default: + audio_debug(" unknown audio type"); + break; + } + } + AIL_unlock(); + } + } #define miles_audio_debug(x) \ @@ -72,6 +139,7 @@ MilesAudioSound(MilesAudioManager* manager, MilesAudioSound:: ~MilesAudioSound() { miles_audio_debug("~MilesAudioSound()"); + stop(); _manager->release_sound(this); AIL_quick_unload(_audio); } @@ -86,12 +154,14 @@ play() { miles_audio_debug("play()"); if (_active) { - if(_manager->_bExclusive) { - // stop any other sound that parent mgr is playing - _manager->stop_all_sounds(); + if (status() == AudioSound::PLAYING) { + stop(); } + _manager->starting_sound(this); // Start playing: if (AIL_quick_play(_audio, _loop_count)) { + panda_AIL_quick_set_finished_callback(_audio, this); + assert(status()==PLAYING); audio_debug(" started sound " << _file_name ); } else { audio_debug(" sound " << _file_name<<" failed to start, err: " <stoping_sound(this); + // The _paused flag should not be cleared here. _paused is not like + // the Pause button on a cd/dvd player. It is used as a flag to say + // that it was looping when it was set inactive. There is no need to + // make this symetrical 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(). + // removing --> _paused=false; AIL_quick_halt(_audio); } void MilesAudioSound:: -halt() { - #if 0 - if(_file_name.find(".mid")!=string::npos) { - miles_audio_debug("halt() midi"); - int i=1; +finished() { + miles_audio_debug("finished()"); + _manager->stoping_sound(this); + if (!_finished_event.empty()) { + throw_event(_finished_event); } - #endif - - miles_audio_debug("halt()"); - AIL_quick_halt(_audio); } void MilesAudioSound:: @@ -154,7 +222,7 @@ set_loop_count(unsigned long loop_count) { // need to stop and start the sound, feel free. Or, maybe I'll spend // time on it in the future. Please set the loop option before starting // the sound. - halt(); + stop(); play(); } } @@ -234,43 +302,6 @@ get_volume() const { return _volume; } -void MilesAudioSound:: -set_active(bool active) { - miles_audio_debug("set_active(active="< -#include -#include -#include -#include -#include -#include -#include -#include +#include "bamReader.h" +#include "bamWriter.h" +#include "compose_matrix.h" +#include "datagram.h" +#include "datagramIterator.h" +#include "event.h" +#include "fftCompressor.h" +#include "indent.h" +#include "throw_event.h" #include "animBundle.h" #include "animBundleNode.h" diff --git a/panda/src/event/throw_event.h b/panda/src/event/throw_event.h index 807bb122ee..0a1ae6917a 100644 --- a/panda/src/event/throw_event.h +++ b/panda/src/event/throw_event.h @@ -19,7 +19,7 @@ #ifndef THROW_EVENT_H #define THROW_EVENT_H -#include +#include "pandabase.h" #include "eventQueue.h" #include "pt_Event.h" diff --git a/panda/src/lerp/lerp.cxx b/panda/src/lerp/lerp.cxx index a76ee4d604..3477e4c6aa 100644 --- a/panda/src/lerp/lerp.cxx +++ b/panda/src/lerp/lerp.cxx @@ -19,8 +19,8 @@ #include "lerp.h" -#include -#include +#include "clockObject.h" +#include "throw_event.h" TypeHandle Lerp::_type_handle; TypeHandle AutonomousLerp::_type_handle; diff --git a/panda/src/physics/physicsCollisionHandler.cxx b/panda/src/physics/physicsCollisionHandler.cxx index fc6592db8c..f98774897d 100755 --- a/panda/src/physics/physicsCollisionHandler.cxx +++ b/panda/src/physics/physicsCollisionHandler.cxx @@ -73,8 +73,8 @@ apply_linear_force(ColliderDef &def, const LVector3f &force) { LVector3f adjustment=force; physics_debug(" adjustment set "<get_mat(); physics_debug(" adjustment trn "<=0.0f && friction<=1.0f); + if (friction<0.0f && friction>1.0f) { + cerr<<"\n\nfriction error "<get_dt(); vel *= (1.0f-friction) * dt * dt;