diff --git a/panda/src/audiotraits/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx index 065cc1a154..f4499a9bc9 100644 --- a/panda/src/audiotraits/openalAudioManager.cxx +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -123,14 +123,14 @@ OpenALAudioManager() { if (!_device) { // this is a unique kind of error audio_error("OpenALAudioManager: alcOpenDevice(NULL): ALC couldn't open device"); - } else { + } else { alcGetError(_device); // clear errors _context=alcCreateContext(_device,NULL); alc_audio_errcheck("alcCreateContext(_device,NULL)",_device); if (_context!=NULL) { _openal_active = true; } - } + } } // We increment _active_managers regardless of possible errors above. // The shutdown call will do the right thing when it's called, @@ -151,7 +151,6 @@ OpenALAudioManager() { audio_3d_set_distance_factor(audio_distance_factor); audio_3d_set_drop_off_factor(audio_drop_off_factor); } - } //////////////////////////////////////////////////////////////////// @@ -232,25 +231,36 @@ can_use_audio(MovieAudioCursor *source) { } //////////////////////////////////////////////////////////////////// -// Function: OpenALAudioManager::can_load_audio +// Function: OpenALAudioManager::should_load_audio // Access: Private -// Description: Returns true if the specified MovieAudio can be -// cached into RAM. To be cached, the data must have -// a filename (otherwise, we have no cache key), and it -// must not take too many bytes. +// Description: Returns true if the specified MovieAudio should be +// cached into RAM. A lot of conditions have to be met +// in order to allow caching - if any are not met, +// the file will be streamed. //////////////////////////////////////////////////////////////////// bool OpenALAudioManager:: -can_load_audio(MovieAudioCursor *source) { - if (source->get_source()->get_filename().empty()) { +should_load_audio(MovieAudioCursor *source, int mode) { + if (mode == SM_stream) { + // If the user asked for streaming, give him streaming. return false; } - if ((source->length() > 3600.0)||(source->ready() != 0x40000000)) { + if (source->get_source()->get_filename().empty()) { + // Non-files cannot be preloaded. + return false; + } + if (source->ready() != 0x40000000) { + // Streaming sources cannot be preloaded. + return false; + } + if (source->length() > 3600.0) { + // Anything longer than an hour cannot be preloaded. return false; } int channels = source->audio_channels(); int samples = (int)(source->length() * source->audio_rate()); int bytes = samples * channels * 2; - if (bytes > audio_preload_threshold) { + if ((mode == SM_heuristic)&&(bytes > audio_preload_threshold)) { + // In heuristic mode, if file is long, stream it. return false; } return true; @@ -265,27 +275,31 @@ can_load_audio(MovieAudioCursor *source) { // to decrement the client count. //////////////////////////////////////////////////////////////////// OpenALAudioManager::SoundData *OpenALAudioManager:: -get_sound_data(MovieAudio *movie) { +get_sound_data(MovieAudio *movie, int mode) { const Filename &path = movie->get_filename(); // Search for an already-cached sample or an already-opened stream. if (!path.empty()) { - SampleCache::iterator lsmi=_sample_cache.find(path); - if (lsmi != _sample_cache.end()) { - SoundData *sd = (*lsmi).second; - increment_client_count(sd); - return sd; - } - - ExpirationQueue::iterator exqi; - for (exqi=_expiring_streams.begin(); exqi!=_expiring_streams.end(); exqi++) { - SoundData *sd = (SoundData*)(*exqi); - if (sd->_movie->get_filename() == path) { + if (mode != SM_stream) { + SampleCache::iterator lsmi=_sample_cache.find(path); + if (lsmi != _sample_cache.end()) { + SoundData *sd = (*lsmi).second; increment_client_count(sd); return sd; } } + + if (mode != SM_sample) { + ExpirationQueue::iterator exqi; + for (exqi=_expiring_streams.begin(); exqi!=_expiring_streams.end(); exqi++) { + SoundData *sd = (SoundData*)(*exqi); + if (sd->_movie->get_filename() == path) { + increment_client_count(sd); + return sd; + } + } + } } PT(MovieAudioCursor) stream = movie->open(); @@ -306,10 +320,12 @@ get_sound_data(MovieAudio *movie) { sd->_rate = stream->audio_rate(); sd->_channels = stream->audio_channels(); sd->_length = stream->length(); - audio_debug("Creating: " << sd->_movie->get_filename().get_basename()); + audio_debug(" - Rate: " << sd->_rate); + audio_debug(" - Channels: " << sd->_channels); + audio_debug(" - Length: " << sd->_length); - if (can_load_audio(stream)) { + if (should_load_audio(stream, mode)) { audio_debug(path.get_basename() << ": loading as sample"); make_current(); alGetError(); // clear errors @@ -354,7 +370,7 @@ get_sound(MovieAudio *sound, bool positional, int mode) { return get_null_sound(); } PT(OpenALAudioSound) oas = - new OpenALAudioSound(this, sound, positional); + new OpenALAudioSound(this, sound, positional, mode); _all_sounds.insert(oas); PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas; @@ -385,7 +401,7 @@ get_sound(const string &file_name, bool positional, int mode) { PT(MovieAudio) mva = MovieAudio::get(path); PT(OpenALAudioSound) oas = - new OpenALAudioSound(this, mva, positional); + new OpenALAudioSound(this, mva, positional, mode); _all_sounds.insert(oas); PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas; @@ -783,11 +799,11 @@ starting_sound(OpenALAudioSound* audio) { //////////////////////////////////////////////////////////////////// void OpenALAudioManager:: stopping_sound(OpenALAudioSound* audio) { - _sounds_playing.erase(audio); if (audio->_source) { _al_sources->insert(audio->_source); audio->_source = 0; } + _sounds_playing.erase(audio); // This could cause the sound to destruct. } //////////////////////////////////////////////////////////////////// @@ -825,7 +841,14 @@ reduce_sounds_playing_to(unsigned int count) { while (limit-- > 0) { SoundsPlaying::iterator sound = _sounds_playing.begin(); assert(sound != _sounds_playing.end()); - (**sound).stop(); + // When the user stops a sound, there is still a PT in the + // user's hand. When we stop a sound here, however, + // this can remove the last PT. This can cause an ugly + // recursion where stop calls the destructor, and the + // destructor calls stop. To avoid this, we create + // a temporary PT, stop the sound, and then release the PT. + PT(OpenALAudioSound) s = (*sound); + s->stop(); } } @@ -862,7 +885,9 @@ update() { sound->push_fresh_buffers(); sound->restart_stalled_audio(); sound->cache_time(rtc); - if ((sound->_source == 0)||(sound->_loops_completed >= sound->_playing_loops)) { + if ((sound->_source == 0)|| + ((sound->_stream_queued.size() == 0)&& + (sound->_loops_completed >= sound->_playing_loops))) { sounds_finished.insert(*i); } } @@ -887,6 +912,8 @@ cleanup() { return; } + stop_all_sounds(); + AllSounds sounds(_all_sounds); AllSounds::iterator ai; for (ai = sounds.begin(); ai != sounds.end(); ++ai) { diff --git a/panda/src/audiotraits/openalAudioManager.h b/panda/src/audiotraits/openalAudioManager.h index af28a14464..3824364ae8 100644 --- a/panda/src/audiotraits/openalAudioManager.h +++ b/panda/src/audiotraits/openalAudioManager.h @@ -117,9 +117,9 @@ private: void make_current() const; bool can_use_audio(MovieAudioCursor *source); - bool can_load_audio(MovieAudioCursor *source); + bool should_load_audio(MovieAudioCursor *source, int mode); - SoundData *get_sound_data(MovieAudio *source); + SoundData *get_sound_data(MovieAudio *source, int mode); // Tell the manager that the sound dtor was called. void release_sound(OpenALAudioSound* audioSound); @@ -129,7 +129,7 @@ private: void starting_sound(OpenALAudioSound* audio); void stopping_sound(OpenALAudioSound* audio); - + void cleanup(); private: diff --git a/panda/src/audiotraits/openalAudioSound.I b/panda/src/audiotraits/openalAudioSound.I index ab80d2239a..59f61479e1 100644 --- a/panda/src/audiotraits/openalAudioSound.I +++ b/panda/src/audiotraits/openalAudioSound.I @@ -57,7 +57,7 @@ get_calibrated_clock(double rtc) const { void OpenALAudioSound:: require_sound_data() { if (_sd==0) { - _sd = _manager->get_sound_data(_movie); + _sd = _manager->get_sound_data(_movie, _desired_mode); if (_sd==0) { audio_error("Could not open audio " << _movie->get_filename()); cleanup(); diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx index aff8b49d5b..ebd319ee85 100644 --- a/panda/src/audiotraits/openalAudioSound.cxx +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -46,7 +46,8 @@ TypeHandle OpenALAudioSound::_type_handle; OpenALAudioSound:: OpenALAudioSound(OpenALAudioManager* manager, MovieAudio *movie, - bool positional) : + bool positional, + int mode) : _movie(movie), _sd(NULL), _loops_completed(0), @@ -63,7 +64,8 @@ OpenALAudioSound(OpenALAudioManager* manager, _play_rate(1.0), _current_time(0.0), _active(true), - _paused(false) + _paused(false), + _desired_mode(mode) { _location[0] = 0; _location[1] = 0; @@ -143,7 +145,7 @@ play() { require_sound_data(); if (_manager == 0) return; _manager->starting_sound(this); - + if (!_source) { return; } @@ -408,7 +410,7 @@ read_stream_data(int bytelen, unsigned char *buffer) { } cursor->read_samples(samples, (PN_int16 *)buffer); size_t hval = AddHash::add_hash(0, (PN_uint8*)buffer, samples*channels*2); - audio_debug("Streaming " << cursor->get_source()->get_filename().get_basename() << " at " << t << " hash " << hval); + audio_debug("Streaming " << cursor->get_source()->get_name() << " at " << t << " hash " << hval); fill += samples; space -= samples; buffer += (samples * channels * 2); @@ -881,7 +883,7 @@ status() const { if (_source==0) { return AudioSound::READY; } - if (_loops_completed >= _playing_loops) { + if ((_loops_completed >= _playing_loops)&&(_stream_queued.size()==0)) { return AudioSound::READY; } else { return AudioSound::PLAYING; diff --git a/panda/src/audiotraits/openalAudioSound.h b/panda/src/audiotraits/openalAudioSound.h index eb180e193c..fd680cf5af 100644 --- a/panda/src/audiotraits/openalAudioSound.h +++ b/panda/src/audiotraits/openalAudioSound.h @@ -116,7 +116,8 @@ public: private: OpenALAudioSound(OpenALAudioManager* manager, MovieAudio *movie, - bool positional); + bool positional, + int mode); INLINE void set_calibrated_clock(double rtc, double t, double playrate); INLINE double get_calibrated_clock(double rtc) const; void correct_calibrated_clock(double rtc, double t); @@ -134,6 +135,8 @@ private: private: + void do_stop(); + PT(MovieAudio) _movie; OpenALAudioManager::SoundData *_sd; @@ -168,6 +171,8 @@ private: double _length; int _loop_count; + int _desired_mode; + // The calibrated clock is initialized when the // sound starts playing, and is periodically corrected // thereafter.