Browse Source

Fixed several memory management bugs, and added streaming control

Josh Yelon 17 years ago
parent
commit
4a474bfb13

+ 57 - 30
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) {
+should_load_audio(MovieAudioCursor *source, int mode) {
+  if (mode == SM_stream) {
+    // If the user asked for streaming, give him streaming.
+    return false;
+  }
   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)||(source->ready() != 0x40000000)) {
+  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) {

+ 3 - 3
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:

+ 1 - 1
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();

+ 7 - 5
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;

+ 6 - 1
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.