Explorar o código

Improvements to audio and creation of UserDataAudio

Josh Yelon %!s(int64=18) %!d(string=hai) anos
pai
achega
03715468ca

+ 10 - 2
panda/src/audio/audioManager.h

@@ -23,6 +23,7 @@
 #include "config_audio.h"
 #include "config_audio.h"
 #include "audioSound.h"
 #include "audioSound.h"
 #include "filterProperties.h"
 #include "filterProperties.h"
+#include "movieAudio.h"
 
 
 typedef PT(AudioManager) Create_AudioManager_proc();
 typedef PT(AudioManager) Create_AudioManager_proc();
 
 
@@ -58,6 +59,12 @@ PUBLISHED:
     SPK_sideright,
     SPK_sideright,
     SPK_COUNT,
     SPK_COUNT,
   };
   };
+
+  enum StreamMode {
+    SM_heuristic,
+    SM_sample,
+    SM_stream,
+  };
   
   
   virtual int getSpeakerSetup();
   virtual int getSpeakerSetup();
   virtual void setSpeakerSetup(SpeakerModeCategory cat);
   virtual void setSpeakerSetup(SpeakerModeCategory cat);
@@ -86,8 +93,9 @@ PUBLISHED:
   virtual bool is_valid() = 0;
   virtual bool is_valid() = 0;
   
   
   // Get a sound:
   // Get a sound:
-  virtual PT(AudioSound) get_sound(const string& file_name, bool positional = false) = 0;
-  
+  virtual PT(AudioSound) get_sound(const string& file_name, bool positional = false, int mode=SM_heuristic) = 0;
+  virtual PT(AudioSound) get_sound(MovieAudio *source, bool positional = false, int mode=SM_heuristic) = 0;
+
   PT(AudioSound) get_null_sound();
   PT(AudioSound) get_null_sound();
 
 
   // Tell the AudioManager there is no need to keep this one cached.
   // Tell the AudioManager there is no need to keep this one cached.

+ 8 - 0
panda/src/audio/config_audio.cxx

@@ -75,6 +75,14 @@ ConfigVariableDouble audio_buffering_seconds
           "correctness over efficiency, but for a commercial application "
           "correctness over efficiency, but for a commercial application "
           "you may wish to lower this."));
           "you may wish to lower this."));
 
 
+ConfigVariableInt audio_preload_threshold
+("audio-preload-threshold", 1000000,
+ PRC_DESC("If the decompressed size of a sound file exceeds this amount, "
+          "then Panda3D will not attempt to store that sound file in RAM. "
+          "Instead, it will stream the sound file from disk.  It is not "
+          "practical to stream multiple sound-files from disk at the same "
+          "time - the hard drive seek time makes it stutter."));
+
 // Unknown
 // Unknown
 
 
 ConfigVariableInt audio_min_hw_channels 	 
 ConfigVariableInt audio_min_hw_channels 	 

+ 5 - 4
panda/src/audio/config_audio.h

@@ -48,10 +48,11 @@ extern EXPCL_PANDA_AUDIO ConfigVariableBool fmod_use_surround_sound;
 
 
 // Config vars for OpenAL:
 // Config vars for OpenAL:
 
 
-extern EXPCL_PANDA ConfigVariableDouble audio_doppler_factor; 	 
-extern EXPCL_PANDA ConfigVariableDouble audio_distance_factor; 	 
-extern EXPCL_PANDA ConfigVariableDouble audio_drop_off_factor; 	 
-extern EXPCL_PANDA ConfigVariableDouble audio_buffering_seconds;
+extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_doppler_factor; 	 
+extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_distance_factor; 	 
+extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_drop_off_factor; 	 
+extern EXPCL_PANDA_AUDIO ConfigVariableDouble audio_buffering_seconds;
+extern EXPCL_PANDA_AUDIO ConfigVariableInt    audio_preload_threshold;
 
 
 // Config vars for Miles:
 // Config vars for Miles:
 
 

+ 11 - 1
panda/src/audio/nullAudioManager.cxx

@@ -63,7 +63,17 @@ is_valid() {
 //  Description: 
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(AudioSound) NullAudioManager::
 PT(AudioSound) NullAudioManager::
-get_sound(const string&, bool positional) {
+get_sound(const string&, bool positional, int mode) {
+  return get_null_sound();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NullAudioManager::get_sound
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PT(AudioSound) NullAudioManager::
+get_sound(MovieAudio *sound, bool positional, int mode) {
   return get_null_sound();
   return get_null_sound();
 }
 }
 
 

+ 2 - 1
panda/src/audio/nullAudioManager.h

@@ -34,7 +34,8 @@ public:
   
   
   virtual bool is_valid();
   virtual bool is_valid();
   
   
-  virtual PT(AudioSound) get_sound(const string&, bool positional = false);
+  virtual PT(AudioSound) get_sound(const string&, bool positional = false, int mode=SM_heuristic);
+  virtual PT(AudioSound) get_sound(MovieAudio *sound, bool positional = false, int mode=SM_heuristic);
   virtual void uncache_sound(const string&);
   virtual void uncache_sound(const string&);
   virtual void clear_cache();
   virtual void clear_cache();
   virtual void set_cache_limit(unsigned int);
   virtual void set_cache_limit(unsigned int);

+ 12 - 2
panda/src/audiotraits/fmodAudioManager.cxx

@@ -402,12 +402,12 @@ configure_filters(FilterProperties *config) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: FmodAudioManager::get_sound()
+//     Function: FmodAudioManager::get_sound
 //       Access: Public
 //       Access: Public
 //  Description: This is what creates a sound instance.
 //  Description: This is what creates a sound instance.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(AudioSound) FmodAudioManager::
 PT(AudioSound) FmodAudioManager::
-get_sound(const string &file_name, bool positional) {
+get_sound(const string &file_name, bool positional, int) {
   //Needed so People use Panda's Generic UNIX Style Paths for Filename.
   //Needed so People use Panda's Generic UNIX Style Paths for Filename.
   //path.to_os_specific() converts it back to the proper OS version later on.
   //path.to_os_specific() converts it back to the proper OS version later on.
   
   
@@ -428,6 +428,16 @@ get_sound(const string &file_name, bool positional) {
   return audioSound;
   return audioSound;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: FmodAudioManager::get_sound
+//       Access: Public
+//  Description: This is what creates a sound instance.
+////////////////////////////////////////////////////////////////////
+PT(AudioSound) FmodAudioManager::
+get_sound(MovieAudio *source, bool positional, int) {
+  nassert_raise("FMOD audio manager does not support MovieAudio sources");
+  return NULL;
+}
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: FmodAudioManager::getSpeakerSetup()
 //     Function: FmodAudioManager::getSpeakerSetup()

+ 2 - 1
panda/src/audiotraits/fmodAudioManager.h

@@ -103,7 +103,8 @@ class EXPCL_FMOD_AUDIO FmodAudioManager : public AudioManager {
 
 
   virtual bool is_valid();
   virtual bool is_valid();
           
           
-  virtual PT(AudioSound) get_sound(const string&, bool positional = false);
+  virtual PT(AudioSound) get_sound(const string&, bool positional = false, int mode=SM_heuristic);
+  virtual PT(AudioSound) get_sound(MovieAudio *,  bool positional = false, int mode=SM_heuristic);
     
     
   virtual int getSpeakerSetup();
   virtual int getSpeakerSetup();
   virtual void setSpeakerSetup(SpeakerModeCategory cat);
   virtual void setSpeakerSetup(SpeakerModeCategory cat);

+ 12 - 1
panda/src/audiotraits/milesAudioManager.cxx

@@ -139,7 +139,7 @@ is_valid() {
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(AudioSound) MilesAudioManager::
 PT(AudioSound) MilesAudioManager::
-get_sound(const string &file_name, bool) {
+get_sound(const string &file_name, bool, int) {
   ReMutexHolder holder(_lock);
   ReMutexHolder holder(_lock);
   audio_debug("MilesAudioManager::get_sound(file_name=\""<<file_name<<"\")");
   audio_debug("MilesAudioManager::get_sound(file_name=\""<<file_name<<"\")");
 
 
@@ -223,6 +223,17 @@ get_sound(const string &file_name, bool) {
   return audioSound;
   return audioSound;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MilesAudioManager::get_sound
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+PT(AudioSound) MilesAudioManager::
+get_sound(MovieAudio *sound, bool, int) {
+  nassert_raise("Miles audio manager does not support MovieAudio sources.");
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MilesAudioManager::uncache_sound
 //     Function: MilesAudioManager::uncache_sound
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 2 - 1
panda/src/audiotraits/milesAudioManager.h

@@ -47,7 +47,8 @@ public:
 
 
   virtual bool is_valid();
   virtual 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, int mode=SM_heuristic);
+  virtual PT(AudioSound) get_sound(MovieAudio *sound, bool positional = false, int mode=SM_heuristic);
   virtual void uncache_sound(const string &file_name);
   virtual void uncache_sound(const string &file_name);
   virtual void clear_cache();
   virtual void clear_cache();
   virtual void set_cache_limit(unsigned int count);
   virtual void set_cache_limit(unsigned int count);

+ 24 - 6
panda/src/audiotraits/openalAudioManager.cxx

@@ -244,13 +244,13 @@ can_load_audio(MovieAudioCursor *source) {
   if (source->get_source()->get_filename().empty()) {
   if (source->get_source()->get_filename().empty()) {
     return false;
     return false;
   }
   }
-  if (source->length() > 60.0) {
+  if ((source->length() > 3600.0)||(source->ready() != 0x40000000)) {
     return false;
     return false;
   }
   }
   int channels = source->audio_channels();
   int channels = source->audio_channels();
   int samples = (int)(source->length() * source->audio_rate());
   int samples = (int)(source->length() * source->audio_rate());
   int bytes = samples * channels * 2;
   int bytes = samples * channels * 2;
-  if (bytes > 200000) {
+  if (bytes > audio_preload_threshold) {
     return false;
     return false;
   }
   }
   return true;
   return true;
@@ -344,12 +344,30 @@ get_sound_data(MovieAudio *movie) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: OpenALAudioManager::get_sound()
+//     Function: OpenALAudioManager::get_sound
 //       Access: Public
 //       Access: Public
 //  Description: This is what creates a sound instance.
 //  Description: This is what creates a sound instance.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(AudioSound) OpenALAudioManager::
 PT(AudioSound) OpenALAudioManager::
-get_sound(const string &file_name, bool positional) {
+get_sound(MovieAudio *sound, bool positional, int mode) {
+  if(!is_valid()) {
+    return get_null_sound();
+  }
+  PT(OpenALAudioSound) oas = 
+    new OpenALAudioSound(this, sound, positional);
+  
+  _all_sounds.insert(oas);
+  PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
+  return res;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_sound
+//       Access: Public
+//  Description: This is what creates a sound instance.
+////////////////////////////////////////////////////////////////////
+PT(AudioSound) OpenALAudioManager::
+get_sound(const string &file_name, bool positional, int mode) {
   if(!is_valid()) {
   if(!is_valid()) {
     return get_null_sound();
     return get_null_sound();
   }
   }
@@ -363,7 +381,7 @@ get_sound(const string &file_name, bool positional) {
     audio_error("get_sound - invalid filename");
     audio_error("get_sound - invalid filename");
     return NULL;
     return NULL;
   }
   }
-  
+
   PT(MovieAudio) mva = MovieAudio::get(path);
   PT(MovieAudio) mva = MovieAudio::get(path);
   
   
   PT(OpenALAudioSound) oas = 
   PT(OpenALAudioSound) oas = 
@@ -844,7 +862,7 @@ update() {
     sound->push_fresh_buffers();
     sound->push_fresh_buffers();
     sound->restart_stalled_audio();
     sound->restart_stalled_audio();
     sound->cache_time(rtc);
     sound->cache_time(rtc);
-    if (sound->status()!=AudioSound::PLAYING) {
+    if ((sound->_source == 0)||(sound->_loops_completed >= sound->_playing_loops)) {
       sounds_finished.insert(*i);
       sounds_finished.insert(*i);
     }
     }
   }
   }

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

@@ -53,7 +53,8 @@ class EXPCL_OPENAL_AUDIO OpenALAudioManager : public AudioManager {
 
 
   virtual bool is_valid();
   virtual bool is_valid();
           
           
-  virtual PT(AudioSound) get_sound(const string&, bool positional = false);
+  virtual PT(AudioSound) get_sound(const string&,     bool positional = false, int mode=SM_heuristic);
+  virtual PT(AudioSound) get_sound(MovieAudio *sound, bool positional = false, int mode=SM_heuristic);
   
   
   virtual void uncache_sound(const string&);
   virtual void uncache_sound(const string&);
   virtual void clear_cache();
   virtual void clear_cache();
@@ -173,7 +174,7 @@ private:
   typedef phash_map<string, SoundData *> SampleCache;
   typedef phash_map<string, SoundData *> SampleCache;
   SampleCache _sample_cache;
   SampleCache _sample_cache;
   
   
-  typedef phash_set<OpenALAudioSound *> SoundsPlaying;
+  typedef phash_set<PT(OpenALAudioSound)> SoundsPlaying;
   SoundsPlaying _sounds_playing;
   SoundsPlaying _sounds_playing;
 
 
   typedef phash_set<OpenALAudioSound *> AllSounds;
   typedef phash_set<OpenALAudioSound *> AllSounds;

+ 20 - 15
panda/src/audiotraits/openalAudioSound.cxx

@@ -316,7 +316,7 @@ restart_stalled_audio() {
 //  Description: Pushes a buffer into the source queue.
 //  Description: Pushes a buffer into the source queue.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void OpenALAudioSound::
 void OpenALAudioSound::
-queue_buffer(ALuint buffer, int loop_index, double time_offset) {
+queue_buffer(ALuint buffer, int samples, int loop_index, double time_offset) {
   // Now push the buffer into the stream queue.
   // Now push the buffer into the stream queue.
   alGetError();
   alGetError();
   alSourceQueueBuffers(_source,1,&buffer);
   alSourceQueueBuffers(_source,1,&buffer);
@@ -328,10 +328,10 @@ queue_buffer(ALuint buffer, int loop_index, double time_offset) {
   }
   }
   QueuedBuffer buf;
   QueuedBuffer buf;
   buf._buffer = buffer;
   buf._buffer = buffer;
+  buf._samples = samples;
   buf._loop_index = loop_index;
   buf._loop_index = loop_index;
   buf._time_offset = time_offset;
   buf._time_offset = time_offset;
   _stream_queued.push_back(buf);
   _stream_queued.push_back(buf);
-  //  audio_debug("Buffer queued " << loop_index << " " << time_offset);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -394,9 +394,18 @@ read_stream_data(int bytelen, unsigned char *buffer) {
       cursor->seek(0.0);
       cursor->seek(0.0);
       continue;
       continue;
     }
     }
+    if (_sd->_stream->ready() == 0) {
+      if (_sd->_stream->aborted()) {
+        _loops_completed = _playing_loops;
+      }
+      return fill;
+    }
     if (samples > space) {
     if (samples > space) {
       samples = space;
       samples = space;
     }
     }
+    if (samples > _sd->_stream->ready()) {
+      samples = _sd->_stream->ready();
+    }
     cursor->read_samples(samples, (PN_int16 *)buffer);
     cursor->read_samples(samples, (PN_int16 *)buffer);
     size_t hval = AddHash::add_hash(0, (PN_uint8*)buffer, samples*channels*2);
     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_filename().get_basename() << " at " << t << " hash " << hval);
@@ -490,23 +499,21 @@ push_fresh_buffers() {
   if (_sd->_sample) {
   if (_sd->_sample) {
     while ((_loops_completed < _playing_loops) &&
     while ((_loops_completed < _playing_loops) &&
            (_stream_queued.size() < 100)) {
            (_stream_queued.size() < 100)) {
-      queue_buffer(_sd->_sample, _loops_completed, 0.0);
+      queue_buffer(_sd->_sample, 0,_loops_completed, 0.0);
       _loops_completed += 1;
       _loops_completed += 1;
     }
     }
   } else {
   } else {
     MovieAudioCursor *cursor = _sd->_stream;
     MovieAudioCursor *cursor = _sd->_stream;
     int channels = cursor->audio_channels();
     int channels = cursor->audio_channels();
     int rate = cursor->audio_rate();
     int rate = cursor->audio_rate();
-    double space = 65536 / (channels * 2);
     
     
-    // Calculate how many buffers to keep in the queue.
-    int fill_to = (int)((audio_buffering_seconds * rate) / space) + 1;
-    if (fill_to < 3) {
-      fill_to = 3;
+    int fill = 0;
+    for (int i=0; i<_stream_queued.size(); i++) {
+      fill += _stream_queued[i]._samples;
     }
     }
     
     
     while ((_loops_completed < _playing_loops) &&
     while ((_loops_completed < _playing_loops) &&
-           (((int)(_stream_queued.size())) < fill_to)) {
+           (fill < (int)(audio_buffering_seconds * rate * channels))) {
       int loop_index = _loops_completed;
       int loop_index = _loops_completed;
       double time_offset = cursor->tell();
       double time_offset = cursor->tell();
       int samples = read_stream_data(65536, data);
       int samples = read_stream_data(65536, data);
@@ -515,8 +522,9 @@ push_fresh_buffers() {
       }
       }
       ALuint buffer = make_buffer(samples, channels, rate, data);
       ALuint buffer = make_buffer(samples, channels, rate, data);
       if (_manager == 0) return;
       if (_manager == 0) return;
-      queue_buffer(buffer, loop_index, time_offset);
+      queue_buffer(buffer, samples, loop_index, time_offset);
       if (_manager == 0) return;
       if (_manager == 0) return;
+      fill += samples;
     }
     }
   }
   }
 }
 }
@@ -866,17 +874,14 @@ get_name() const {
 //  Description: Get status of the sound.
 //  Description: Get status of the sound.
 //
 //
 //               This returns the status as of the
 //               This returns the status as of the
-//               last AudioManager::update.
+//               last push_fresh_buffers
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 AudioSound::SoundStatus OpenALAudioSound::
 AudioSound::SoundStatus OpenALAudioSound::
 status() const {
 status() const {
   if (_source==0) {
   if (_source==0) {
     return AudioSound::READY;
     return AudioSound::READY;
   }
   }
-  
-  _manager->make_current();
-  
-  if (_stream_queued.size() == 0) {
+  if (_loops_completed >= _playing_loops) {
     return AudioSound::READY;
     return AudioSound::READY;
   } else {
   } else {
     return AudioSound::PLAYING;
     return AudioSound::PLAYING;

+ 2 - 1
panda/src/audiotraits/openalAudioSound.h

@@ -125,7 +125,7 @@ private:
   void restart_stalled_audio();
   void restart_stalled_audio();
   void delete_queued_buffers();
   void delete_queued_buffers();
   ALuint make_buffer(int samples, int channels, int rate, unsigned char *data);
   ALuint make_buffer(int samples, int channels, int rate, unsigned char *data);
-  void queue_buffer(ALuint buffer, int loop_index, double time_offset);
+  void queue_buffer(ALuint buffer, int samples, int loop_index, double time_offset);
   int  read_stream_data(int bytelen, unsigned char *data);
   int  read_stream_data(int bytelen, unsigned char *data);
   void pull_used_buffers();
   void pull_used_buffers();
   void push_fresh_buffers();
   void push_fresh_buffers();
@@ -139,6 +139,7 @@ private:
 
 
   struct QueuedBuffer {
   struct QueuedBuffer {
     ALuint _buffer;
     ALuint _buffer;
+    int    _samples;
     int    _loop_index;
     int    _loop_index;
     double _time_offset;
     double _time_offset;
   };
   };

+ 2 - 0
panda/src/movies/config_movies.cxx

@@ -53,6 +53,8 @@ init_libmovies() {
   MovieAudioCursor::init_type();
   MovieAudioCursor::init_type();
   InkblotVideo::init_type();
   InkblotVideo::init_type();
   InkblotVideoCursor::init_type();
   InkblotVideoCursor::init_type();
+  UserDataAudio::init_type();
+  UserDataAudioCursor::init_type();
   WebcamVideo::init_type();
   WebcamVideo::init_type();
 #ifdef HAVE_FFMPEG
 #ifdef HAVE_FFMPEG
   FfmpegVideo::init_type();
   FfmpegVideo::init_type();

+ 43 - 20
panda/src/movies/movieAudioCursor.I

@@ -50,39 +50,64 @@ audio_channels() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieAudioCursor::length
 //     Function: MovieAudioCursor::length
 //       Access: Public
 //       Access: Public
-//  Description: Returns the length of the movie.
+//  Description: Returns the length of the movie.  Attempting to read
+//               audio samples beyond the specified length will produce 
+//               silent samples.
 //
 //
 //               Some kinds of Movie, such as internet TV station, 
 //               Some kinds of Movie, such as internet TV station, 
 //               might not have a predictable length.  In that case,
 //               might not have a predictable length.  In that case,
 //               the length will be set to a very large number: 1.0E10.
 //               the length will be set to a very large number: 1.0E10.
-//               If the internet TV station goes offline, the video
-//               or audio stream will set its abort flag.  Reaching the
-//               end of the movie (ie, the specified length) normally
-//               does not cause the abort flag to be set.
-//
-//               The video and audio streams produced by get_video and
-//               get_audio are always of unlimited duration - you can
-//               always read another video frame or another audio
-//               sample.  This is true even if the specified length
-//               is reached, or an abort is flagged. If either stream
-//               runs out of data, it will synthesize blank video
-//               frames and silent audio samples as necessary to
-//               satisfy read requests.
 //
 //
 //               Some AVI files have incorrect length values encoded
 //               Some AVI files have incorrect length values encoded
-//               into them - usually, they're a second or two long or
+//               into them - they may be a second or two long or
 //               short.  When playing such an AVI using the Movie class,
 //               short.  When playing such an AVI using the Movie class,
 //               you may see a slightly truncated video, or a slightly
 //               you may see a slightly truncated video, or a slightly
 //               elongated video (padded with black frames).  There are
 //               elongated video (padded with black frames).  There are
 //               utilities out there to fix the length values in AVI
 //               utilities out there to fix the length values in AVI
 //               files.
 //               files.
 //
 //
+//               An audio consumer needs to check the length, the
+//               ready status, and the aborted flag.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE double MovieAudioCursor::
 INLINE double MovieAudioCursor::
 length() const {
 length() const {
   return _length;
   return _length;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::ready
+//       Access: Public
+//  Description: Returns the number of audio samples that are ready
+//               to read.  This is primarily relevant for sources like
+//               microphones which produce samples at a fixed rate.
+//               If you try to read more samples than are ready, the
+//               result will be silent samples.  
+//
+//               Some audio streams do not have a limit on how fast 
+//               they can produce samples.  Such streams will always
+//               return 0x40000000 as the ready-count.  This may well
+//               exceed the length of the audio stream.  You therefore
+//               need to check length separately.
+//
+//               If the aborted flag is set, that means the ready count
+//               is no longer being replenished.  For example, a
+//               MovieAudioCursor might be reading from an internet
+//               radio station, and it might buffer data to avoid 
+//               underruns.  If it loses connection to the radio
+//               station, it will set the aborted flag to indicate that
+//               the buffer is no longer being replenished.  But it is
+//               still ok to read the samples that are in the buffer,
+//               at least until they run out.  Once those are gone,
+//               there will be no more.
+//
+//               An audio consumer needs to check the length, the
+//               ready status, and the aborted flag.
+////////////////////////////////////////////////////////////////////
+INLINE int MovieAudioCursor::
+ready() const {
+  return _ready;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieAudioCursor::can_seek
 //     Function: MovieAudioCursor::can_seek
 //       Access: Public
 //       Access: Public
@@ -113,11 +138,9 @@ can_seek_fast() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: MovieAudioCursor::aborted
 //     Function: MovieAudioCursor::aborted
 //       Access: Public
 //       Access: Public
-//  Description: Returns true if the audio has aborted prematurely.
-//               For example, this could occur if the Movie was actually
-//               an internet TV station, and the connection was lost.
-//               Reaching the normal end of the audio does not
-//               constitute an 'abort' condition.
+//  Description: If aborted is true, it means that the "ready" samples
+//               are not being replenished.  See the method "ready"
+//               for an explanation.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE bool MovieAudioCursor::
 INLINE bool MovieAudioCursor::
 aborted() const {
 aborted() const {

+ 1 - 0
panda/src/movies/movieAudioCursor.cxx

@@ -36,6 +36,7 @@ MovieAudioCursor(MovieAudio *src) :
   _length(1.0E10),
   _length(1.0E10),
   _can_seek(true),
   _can_seek(true),
   _can_seek_fast(true),
   _can_seek_fast(true),
+  _ready(0x40000000),
   _aborted(false),
   _aborted(false),
   _samples_read(0)
   _samples_read(0)
 {
 {

+ 2 - 0
panda/src/movies/movieAudioCursor.h

@@ -53,6 +53,7 @@ PUBLISHED:
   INLINE bool aborted() const;
   INLINE bool aborted() const;
   INLINE double tell() const;
   INLINE double tell() const;
   INLINE void skip_samples(int n);
   INLINE void skip_samples(int n);
+  INLINE int ready() const;
   virtual void seek(double offset);
   virtual void seek(double offset);
 
 
 public:
 public:
@@ -62,6 +63,7 @@ protected:
   PT(MovieAudio) _source;
   PT(MovieAudio) _source;
   int _audio_rate;
   int _audio_rate;
   int _audio_channels;
   int _audio_channels;
+  int _ready;
   double _length;
   double _length;
   bool _can_seek;
   bool _can_seek;
   bool _can_seek_fast;
   bool _can_seek_fast;

+ 3 - 0
panda/src/movies/movies_composite1.cxx

@@ -15,6 +15,9 @@
 
 
 #include "ffmpegVirtualFile.cxx"
 #include "ffmpegVirtualFile.cxx"
 
 
+#include "userDataAudio.cxx"
+#include "userDataAudioCursor.cxx"
+
 #include "webcamVideo.cxx"
 #include "webcamVideo.cxx"
 #include "webcamVideoDS.cxx"
 #include "webcamVideoDS.cxx"
 
 

+ 18 - 0
panda/src/movies/userDataAudio.I

@@ -0,0 +1,18 @@
+// Filename: userDataAudio.I
+// Created by: jyelon (02Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 144 - 0
panda/src/movies/userDataAudio.cxx

@@ -0,0 +1,144 @@
+// Filename: userDataAudio.cxx
+// Created by: jyelon (02Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2007, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] 
+//
+////////////////////////////////////////////////////////////////////
+
+#include "userDataAudio.h"
+#include "userDataAudioCursor.h"
+
+TypeHandle UserDataAudio::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudio::Constructor
+//       Access: Public
+//  Description: This constructor returns a UserDataAudio --- 
+//               a means to supply raw audio samples manually.
+////////////////////////////////////////////////////////////////////
+UserDataAudio::
+UserDataAudio(int rate, int channels) :
+  MovieAudio("User Data Audio"),
+  _desired_rate(rate),
+  _desired_channels(channels),
+  _aborted(false),
+  _cursor(NULL)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudio::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+UserDataAudio::
+~UserDataAudio() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudio::update_cursor
+//       Access: Private
+//  Description: Make sure that the UserDataAudioCursor's ready
+//               and aborted status flags are correct.
+////////////////////////////////////////////////////////////////////
+void UserDataAudio::
+update_cursor() {
+  if (_cursor == 0) return;
+  _cursor->_ready = _data.size();
+  _cursor->_aborted = _aborted;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudio::open
+//       Access: Published, Virtual
+//  Description: Open this audio, returning a UserDataAudioCursor.  A
+//               UserDataAudio can only be opened by one consumer
+//               at a time.
+////////////////////////////////////////////////////////////////////
+PT(MovieAudioCursor) UserDataAudio::
+open() {
+  if (_cursor) {
+    nassert_raise("A UserDataAudio can only be opened by one consumer at a time.");
+    return NULL;
+  }
+  _cursor = new UserDataAudioCursor(this);
+  update_cursor();
+  return _cursor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudio::read_samples
+//       Access: Private
+//  Description: Read audio samples from the stream.  N is the
+//               number of samples you wish to read.  Your buffer
+//               must be equal in size to N * channels.  
+//               Multiple-channel audio will be interleaved. 
+////////////////////////////////////////////////////////////////////
+void UserDataAudio::
+read_samples(int n, PN_int16 *data) {
+  int nread = n;
+  if (nread > (int)_data.size()) {
+    nread = _data.size();
+  }
+  for (int i=0; i<nread; i++) {
+    data[i] = _data[i];
+  }
+  for (int i=nread; i<n; i++) {
+    data[i] = 0;
+  }
+  for (int i=0; i<nread; i++) {
+    _data.pop_front();
+  }
+  update_cursor();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudio::append
+//       Access: Published
+//  Description: Appends audio samples to the buffer.
+////////////////////////////////////////////////////////////////////
+void UserDataAudio::
+append(PN_int16 *data, int len) {
+  nassertv(!_aborted);
+  for (int i=0; i<len; i++) {
+    _data.push_back(data[i]);
+  }
+  update_cursor();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudio::append
+//       Access: Published
+//  Description: Appends audio samples to the buffer.
+////////////////////////////////////////////////////////////////////
+void UserDataAudio::
+append(int val) {
+  nassertv(!_aborted);
+  PN_int16 truncated = (PN_int16)val;
+  nassertv(truncated == val);
+  _data.push_back(truncated);
+  update_cursor();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudio::done
+//       Access: Published
+//  Description: Promises not to append any more samples, ie, this
+//               marks the end of the audio stream.
+////////////////////////////////////////////////////////////////////
+void UserDataAudio::
+done() {
+  _aborted = true;
+  update_cursor();
+}

+ 72 - 0
panda/src/movies/userDataAudio.h

@@ -0,0 +1,72 @@
+// Filename: userDataAudio.h
+// Created by: jyelon (02Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef USERDATAAUDIO_H
+#define USERDATAAUDIO_H
+
+#include "movieAudio.h"
+class MovieAudioCursor;
+class UserDataAudioCursor;
+
+////////////////////////////////////////////////////////////////////
+//       Class : UserDataAudio
+// Description : A UserDataAudio is a way for the user to manually
+//               supply raw audio samples. 
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES UserDataAudio : public MovieAudio {
+
+ PUBLISHED:
+  UserDataAudio(int rate, int channels);
+  virtual ~UserDataAudio();
+  virtual PT(MovieAudioCursor) open();
+
+  void append(PN_int16 *data, int len);
+  void append(int value); // Not fast enough, but useful for debugging.
+  void done(); // A promise not to write any more samples.  
+
+ private:
+  void read_samples(int n, PN_int16 *data);
+  void update_cursor();
+  int _desired_rate;
+  int _desired_channels;
+  UserDataAudioCursor *_cursor;
+  pdeque<PN_int16> _data;
+  bool _aborted;
+  friend class UserDataAudioCursor;
+  
+ public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MovieAudio::init_type();
+    register_type(_type_handle, "UserDataAudio",
+                  MovieAudio::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+ private:
+  static TypeHandle _type_handle;
+};
+
+#include "userDataAudio.I"
+
+#endif

+ 18 - 0
panda/src/movies/userDataAudioCursor.I

@@ -0,0 +1,18 @@
+// Filename: userDataAudioCursor.I
+// Created by: jyelon (02Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 63 - 0
panda/src/movies/userDataAudioCursor.cxx

@@ -0,0 +1,63 @@
+// Filename: userDataAudioCursor.cxx
+// Created by: jyelon (02Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2007, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] 
+//
+////////////////////////////////////////////////////////////////////
+
+#include "userDataAudioCursor.h"
+
+TypeHandle UserDataAudioCursor::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudioCursor::Constructor
+//       Access: 
+//  Description: 
+////////////////////////////////////////////////////////////////////
+UserDataAudioCursor::
+UserDataAudioCursor(UserDataAudio *src) :
+  MovieAudioCursor(src)
+{
+  _audio_rate = src->_desired_rate;
+  _audio_channels = src->_desired_channels;
+  _can_seek = false;
+  _can_seek_fast = false;
+  _ready = 0;
+  _aborted = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudioCursor::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+UserDataAudioCursor::
+~UserDataAudioCursor() {
+  UserDataAudio *source = (UserDataAudio*)(MovieAudio*)_source;
+  source->_cursor = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: UserDataAudioCursor::read_samples
+//       Access: Private
+//  Description: Read audio samples from the stream.  N is the
+//               number of samples you wish to read.  Your buffer
+//               must be equal in size to N * channels.  
+//               Multiple-channel audio will be interleaved. 
+////////////////////////////////////////////////////////////////////
+void UserDataAudioCursor::
+read_samples(int n, PN_int16 *data) {
+  UserDataAudio *source = (UserDataAudio*)(MovieAudio*)_source;
+  source->read_samples(n, data);
+}

+ 64 - 0
panda/src/movies/userDataAudioCursor.h

@@ -0,0 +1,64 @@
+// Filename: userDataAudioCursor.h
+// Created by: jyelon (02Jul07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef USERDATAAUDIOCURSOR_H
+#define USERDATAAUDIOCURSOR_H
+
+#include "pandabase.h"
+#include "luse.h"
+#include "pointerTo.h"
+#include "pointerToArray.h"
+class UserDataAudio;
+
+////////////////////////////////////////////////////////////////////
+//       Class : UserDataAudioCursor
+// Description : A UserDataAudioCursor is a means to manually
+//               supply a sequence of raw audio samples.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES UserDataAudioCursor : public MovieAudioCursor {
+
+PUBLISHED:
+  UserDataAudioCursor(UserDataAudio *src);
+  virtual ~UserDataAudioCursor();
+  
+public:
+  virtual void read_samples(int n, PN_int16 *data);
+  friend class UserDataAudio;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MovieAudioCursor::init_type();
+    register_type(_type_handle, "UserDataAudioCursor",
+                  MovieAudioCursor::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "userDataAudioCursor.I"
+#include "userDataAudio.h"
+
+#endif