Browse Source

Major refactoring of movie code.

Josh Yelon 18 years ago
parent
commit
1e02c06517
54 changed files with 4699 additions and 1151 deletions
  1. 7 7
      direct/src/showbase/Audio3DManager.py
  2. 1 1
      doc/makepanda/config.in
  3. 24 1
      doc/makepanda/makepanda.py
  4. 0 1
      dtool/src/parser-inc/avio.h
  5. 27 14
      panda/src/audio/config_audio.cxx
  6. 6 0
      panda/src/audio/config_audio.h
  7. 20 0
      panda/src/audiotraits/Sources.pp
  8. 64 0
      panda/src/audiotraits/config_openalAudio.cxx
  9. 35 0
      panda/src/audiotraits/config_openalAudio.h
  10. 3 7
      panda/src/audiotraits/fmodAudioSound.cxx
  11. 1049 0
      panda/src/audiotraits/openalAudioManager.cxx
  12. 227 0
      panda/src/audiotraits/openalAudioManager.h
  13. 23 0
      panda/src/audiotraits/openalAudioSound.I
  14. 730 0
      panda/src/audiotraits/openalAudioSound.cxx
  15. 194 0
      panda/src/audiotraits/openalAudioSound.h
  16. 1 0
      panda/src/audiotraits/openal_audio_composite.cxx
  17. 5 0
      panda/src/audiotraits/openal_audio_composite1.cxx
  18. 15 15
      panda/src/grutil/movieTexture.cxx
  19. 3 3
      panda/src/grutil/movieTexture.h
  20. 15 0
      panda/src/movies/Sources.pp
  21. 5 0
      panda/src/movies/config_movies.cxx
  22. 10 0
      panda/src/movies/config_movies.h
  23. 7 32
      panda/src/movies/ffmpegAudio.cxx
  24. 7 10
      panda/src/movies/ffmpegAudio.h
  25. 18 0
      panda/src/movies/ffmpegAudioCursor.I
  26. 264 0
      panda/src/movies/ffmpegAudioCursor.cxx
  27. 88 0
      panda/src/movies/ffmpegAudioCursor.h
  28. 7 259
      panda/src/movies/ffmpegVideo.cxx
  29. 6 33
      panda/src/movies/ffmpegVideo.h
  30. 18 0
      panda/src/movies/ffmpegVideoCursor.I
  31. 306 0
      panda/src/movies/ffmpegVideoCursor.cxx
  32. 86 0
      panda/src/movies/ffmpegVideoCursor.h
  33. 17 0
      panda/src/movies/ffmpegVirtualFileCursor.I
  34. 155 0
      panda/src/movies/ffmpegVirtualFileCursor.cxx
  35. 46 0
      panda/src/movies/ffmpegVirtualFileCursor.h
  36. 10 119
      panda/src/movies/inkblotVideo.cxx
  37. 10 13
      panda/src/movies/inkblotVideo.h
  38. 18 0
      panda/src/movies/inkblotVideoCursor.I
  39. 151 0
      panda/src/movies/inkblotVideoCursor.cxx
  40. 67 0
      panda/src/movies/inkblotVideoCursor.h
  41. 0 120
      panda/src/movies/movieAudio.I
  42. 11 66
      panda/src/movies/movieAudio.cxx
  43. 10 29
      panda/src/movies/movieAudio.h
  44. 147 0
      panda/src/movies/movieAudioCursor.I
  45. 104 0
      panda/src/movies/movieAudioCursor.cxx
  46. 93 0
      panda/src/movies/movieAudioCursor.h
  47. 0 136
      panda/src/movies/movieVideo.I
  48. 10 246
      panda/src/movies/movieVideo.cxx
  49. 11 39
      panda/src/movies/movieVideo.h
  50. 164 0
      panda/src/movies/movieVideoCursor.I
  51. 281 0
      panda/src/movies/movieVideoCursor.cxx
  52. 101 0
      panda/src/movies/movieVideoCursor.h
  53. 11 0
      panda/src/movies/movies_composite1.cxx
  54. 11 0
      panda/src/pandabase/pandasymbols.h

+ 7 - 7
direct/src/showbase/Audio3DManager.py

@@ -36,6 +36,7 @@ class Audio3DManager:
         """
         """
         Control the scale that sets the distance units for 3D spacialized audio.
         Control the scale that sets the distance units for 3D spacialized audio.
         Default is 1.0 which is adjust in panda to be feet.
         Default is 1.0 which is adjust in panda to be feet.
+        When you change this, don't forget that this effects the scale of setSoundMinDistance
         """
         """
         self.audio_manager.audio3dSetDistanceFactor(factor)
         self.audio_manager.audio3dSetDistanceFactor(factor)
 
 
@@ -84,9 +85,8 @@ class Audio3DManager:
         """
         """
         Controls the distance (in units) that this sound begins to fall off.
         Controls the distance (in units) that this sound begins to fall off.
         Also affects the rate it falls off.
         Also affects the rate it falls off.
-        Default is 1.0
-        Closer/Faster, <1.0
-        Farther/Slower, >1.0
+        Default is 3.28 (in feet, this is 1 meter)
+        Don't forget to change this when you change the DistanceFactor
         """
         """
         sound.set3dMinDistance(dist)
         sound.set3dMinDistance(dist)
 
 
@@ -94,9 +94,7 @@ class Audio3DManager:
         """
         """
         Controls the distance (in units) that this sound begins to fall off.
         Controls the distance (in units) that this sound begins to fall off.
         Also affects the rate it falls off.
         Also affects the rate it falls off.
-        Default is 1.0
-        Closer/Faster, <1.0
-        Farther/Slower, >1.0
+        Default is 3.28 (in feet, this is 1 meter)
         """
         """
         return sound.get3dMinDistance()
         return sound.get3dMinDistance()
 
 
@@ -267,8 +265,10 @@ class Audio3DManager:
         # to which it is attached
         # to which it is attached
         if self.listener_target:
         if self.listener_target:
             pos = self.listener_target.getPos(self.root)
             pos = self.listener_target.getPos(self.root)
+            forward = self.listener_target.getRelativeVector(self.root, VBase3(0,1,0))
+            up = self.listener_target.getRelativeVector(self.root, VBase3(0,0,1))
             vel = self.getListenerVelocity()
             vel = self.getListenerVelocity()
-            self.audio_manager.audio3dSetListenerAttributes(pos[0], pos[1], pos[2], vel[0], vel[1], vel[2], 0, 1, 0, 0, 0, 1)
+            self.audio_manager.audio3dSetListenerAttributes(pos[0], pos[1], pos[2], vel[0], vel[1], vel[2], forward[0], forward[1], forward[2], up[0], up[1], up[2]) 
         else:
         else:
             self.audio_manager.audio3dSetListenerAttributes(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1)
             self.audio_manager.audio3dSetListenerAttributes(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1)
         return Task.cont
         return Task.cont

+ 1 - 1
doc/makepanda/config.in

@@ -69,7 +69,7 @@ want-tk           #f
 want-pstats            #f
 want-pstats            #f
 show-frame-rate-meter  #f
 show-frame-rate-meter  #f
 
 
-# Enable audio using the FMod audio library by default:
+# Enable audio using the FmodEx audio library by default:
 
 
 audio-library-name p3fmod_audio
 audio-library-name p3fmod_audio
 
 

+ 24 - 1
doc/makepanda/makepanda.py

@@ -35,7 +35,7 @@ GENMAN=0
 VERSION=0
 VERSION=0
 VERBOSE=1
 VERBOSE=1
 COMPRESSOR="zlib"
 COMPRESSOR="zlib"
-PACKAGES=["PYTHON","ZLIB","PNG","JPEG","TIFF","VRPN","FMODEX","NVIDIACG",
+PACKAGES=["PYTHON","ZLIB","PNG","JPEG","TIFF","VRPN","FMOD","FMODEX","OPENAL","NVIDIACG",
           "OPENSSL","FREETYPE","FFTW","MILES",
           "OPENSSL","FREETYPE","FFTW","MILES",
           "MAYA6","MAYA65","MAYA7","MAYA8","MAYA85","MAX6","MAX7","MAX8","MAX9",
           "MAYA6","MAYA65","MAYA7","MAYA8","MAYA85","MAX6","MAX7","MAX8","MAX9",
           "FFMPEG","PANDATOOL","PANDAAPP","DX8","DX9"]
           "FFMPEG","PANDATOOL","PANDAAPP","DX8","DX9"]
@@ -1077,7 +1077,9 @@ def CompileCxx(obj,src,ipath,opts):
         if (OMIT.count("PYTHON")==0): cmd = cmd + ' -I"' + PYTHONSDK + '"'
         if (OMIT.count("PYTHON")==0): cmd = cmd + ' -I"' + PYTHONSDK + '"'
         if (PkgSelected(opts,"VRPN")):     cmd = cmd + ' -I' + THIRDPARTYLIBS + 'vrpn/include'
         if (PkgSelected(opts,"VRPN")):     cmd = cmd + ' -I' + THIRDPARTYLIBS + 'vrpn/include'
         if (PkgSelected(opts,"FFTW")):     cmd = cmd + ' -I' + THIRDPARTYLIBS + 'fftw/include'
         if (PkgSelected(opts,"FFTW")):     cmd = cmd + ' -I' + THIRDPARTYLIBS + 'fftw/include'
+        if (PkgSelected(opts,"FMOD")):     cmd = cmd + ' -I' + THIRDPARTYLIBS + 'fmod/include'
         if (PkgSelected(opts,"FMODEX")):   cmd = cmd + ' -I' + THIRDPARTYLIBS + 'fmodex/include'
         if (PkgSelected(opts,"FMODEX")):   cmd = cmd + ' -I' + THIRDPARTYLIBS + 'fmodex/include'
+        if (PkgSelected(opts,"OPENAL")):   cmd = cmd + ' -I' + THIRDPARTYLIBS + 'openal/include'
         if (PkgSelected(opts,"NVIDIACG")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'nvidiacg/include'
         if (PkgSelected(opts,"NVIDIACG")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'nvidiacg/include'
         if (PkgSelected(opts,"FFMPEG")):   cmd = cmd + ' -I' + THIRDPARTYLIBS + 'ffmpeg/include'
         if (PkgSelected(opts,"FFMPEG")):   cmd = cmd + ' -I' + THIRDPARTYLIBS + 'ffmpeg/include'
         if (PkgSelected(opts,"FREETYPE")): cmd = cmd + ' -I/usr/include/freetype2'
         if (PkgSelected(opts,"FREETYPE")): cmd = cmd + ' -I/usr/include/freetype2'
@@ -1354,8 +1356,13 @@ def CompileLink(dll, obj, opts, ldef):
         if (PkgSelected(opts,"VRPN")):
         if (PkgSelected(opts,"VRPN")):
             cmd = cmd + ' ' + THIRDPARTYLIBS + 'vrpn/lib/vrpn.lib'
             cmd = cmd + ' ' + THIRDPARTYLIBS + 'vrpn/lib/vrpn.lib'
             cmd = cmd + ' ' + THIRDPARTYLIBS + 'vrpn/lib/quat.lib'
             cmd = cmd + ' ' + THIRDPARTYLIBS + 'vrpn/lib/quat.lib'
+        if (PkgSelected(opts,"FMOD")):
+            cmd = cmd + ' ' + THIRDPARTYLIBS + 'fmod/lib/fmod.lib'
         if (PkgSelected(opts,"FMODEX")):
         if (PkgSelected(opts,"FMODEX")):
             cmd = cmd + ' ' + THIRDPARTYLIBS + 'fmodex/lib/fmodex_vc.lib'
             cmd = cmd + ' ' + THIRDPARTYLIBS + 'fmodex/lib/fmodex_vc.lib'
+        if (PkgSelected(opts,"OPENAL")):
+            cmd = cmd + ' ' + THIRDPARTYLIBS + 'openal/lib/OpenAL32.lib'
+            cmd = cmd + ' ' + THIRDPARTYLIBS + 'openal/lib/alut.lib'
         if (PkgSelected(opts,"MILES")):
         if (PkgSelected(opts,"MILES")):
             cmd = cmd + ' ' + THIRDPARTYLIBS + 'miles/lib/mss32.lib'
             cmd = cmd + ' ' + THIRDPARTYLIBS + 'miles/lib/mss32.lib'
         if (PkgSelected(opts,"NVIDIACG")):
         if (PkgSelected(opts,"NVIDIACG")):
@@ -1406,7 +1413,9 @@ def CompileLink(dll, obj, opts, ldef):
             elif (suffix==".dll"): cmd = cmd + ' -l' + x[3:-4]
             elif (suffix==".dll"): cmd = cmd + ' -l' + x[3:-4]
             elif (suffix==".lib"): cmd = cmd + ' built/lib/' + x[:-4] + '.a'
             elif (suffix==".lib"): cmd = cmd + ' built/lib/' + x[:-4] + '.a'
             elif (suffix==".ilb"): cmd = cmd + ' built/tmp/' + x[:-4] + '.a'
             elif (suffix==".ilb"): cmd = cmd + ' built/tmp/' + x[:-4] + '.a'
+        #if (PkgSelected(opts,"FMOD")):     cmd = cmd + ' -L' + THIRDPARTYLIBS + 'fmod/lib -lfmod'
         if (PkgSelected(opts,"FMODEX")):   cmd = cmd + ' -L' + THIRDPARTYLIBS + 'fmodex/lib -lfmodex'
         if (PkgSelected(opts,"FMODEX")):   cmd = cmd + ' -L' + THIRDPARTYLIBS + 'fmodex/lib -lfmodex'
+        #if (PkgSelected(opts,"OPENAL")):   cmd = cmd + ' -L' + THIRDPARTYLIBS + 'openal/lib -lopenal -lalut'
         if (PkgSelected(opts,"NVIDIACG")):
         if (PkgSelected(opts,"NVIDIACG")):
             cmd = cmd + ' -Lthirdparty/nvidiacg/lib '
             cmd = cmd + ' -Lthirdparty/nvidiacg/lib '
             if (opts.count("CGGL")):  cmd = cmd + " -lCgGL"
             if (opts.count("CGGL")):  cmd = cmd + " -lCgGL"
@@ -1659,7 +1668,9 @@ DTOOL_CONFIG=[
     ("HAVE_JPEG",                      'UNDEF',                  'UNDEF'),
     ("HAVE_JPEG",                      'UNDEF',                  'UNDEF'),
     ("HAVE_TIFF",                      'UNDEF',                  'UNDEF'),
     ("HAVE_TIFF",                      'UNDEF',                  'UNDEF'),
     ("HAVE_VRPN",                      'UNDEF',                  'UNDEF'),
     ("HAVE_VRPN",                      'UNDEF',                  'UNDEF'),
+    ("HAVE_FMOD",                      'UNDEF',                  'UNDEF'),
     ("HAVE_FMODEX",                    'UNDEF',                  'UNDEF'),
     ("HAVE_FMODEX",                    'UNDEF',                  'UNDEF'),
+    ("HAVE_OPENAL",                    'UNDEF',                  'UNDEF'),
     ("HAVE_NVIDIACG",                  'UNDEF',                  'UNDEF'),
     ("HAVE_NVIDIACG",                  'UNDEF',                  'UNDEF'),
     ("HAVE_FREETYPE",                  'UNDEF',                  'UNDEF'),
     ("HAVE_FREETYPE",                  'UNDEF',                  'UNDEF'),
     ("HAVE_FFTW",                      'UNDEF',                  'UNDEF'),
     ("HAVE_FFTW",                      'UNDEF',                  'UNDEF'),
@@ -2744,6 +2755,18 @@ if OMIT.count("FMODEX") == 0:
                'libp3dtool.dll',
                'libp3dtool.dll',
   ])
   ])
 
 
+if OMIT.count("OPENAL") == 0:
+  IPATH=['panda/src/audiotraits']
+  OPTS=['BUILDING_OPENAL_AUDIO',  'OPENAL']
+  EnqueueCxx(ipath=IPATH, opts=OPTS, src='openal_audio_composite.cxx', obj='openal_audio_openal_audio_composite.obj')
+  EnqueueLink(opts=['ADVAPI', 'WINUSER', 'WINMM', 'OPENAL'], dll='libp3openal_audio.dll', obj=[
+               'openal_audio_openal_audio_composite.obj',
+               'libpanda.dll',
+               'libpandaexpress.dll',
+               'libp3dtoolconfig.dll',
+               'libp3dtool.dll',
+  ])
+
 if OMIT.count("MILES") == 0:
 if OMIT.count("MILES") == 0:
   IPATH=['panda/src/audiotraits']
   IPATH=['panda/src/audiotraits']
   OPTS=['BUILDING_MILES_AUDIO',  'MILES']
   OPTS=['BUILDING_MILES_AUDIO',  'MILES']

+ 0 - 1
dtool/src/parser-inc/avio.h

@@ -1,4 +1,3 @@
 #ifndef AVIO_H
 #ifndef AVIO_H
 #define AVIO_H
 #define AVIO_H
 #endif
 #endif
-

+ 27 - 14
panda/src/audio/config_audio.cxx

@@ -40,7 +40,11 @@ ConfigVariableString audio_library_name
 #if defined(HAVE_RAD_MSS)
 #if defined(HAVE_RAD_MSS)
  "miles_audio"
  "miles_audio"
 #elif defined(HAVE_FMODEX)
 #elif defined(HAVE_FMODEX)
+ "fmodex_audio"
+#elif defined(HAVE_FMOD)
  "fmod_audio"
  "fmod_audio"
+#elif defined(HAVE_OPENAL)
+ "openal_audio"
 #else
 #else
  ""
  ""
 #endif
 #endif
@@ -49,23 +53,24 @@ ConfigVariableString audio_library_name
 ConfigVariableDouble audio_volume 
 ConfigVariableDouble audio_volume 
 ("audio-volume", 1.0f);
 ("audio-volume", 1.0f);
 
 
-ConfigVariableFilename audio_dls_file 
-("audio-dls-file", Filename(),
- PRC_DESC("Specifies a DLS file that defines an instrument set to load "
-          "for MIDI file playback.  If this is not specified, the sound "
-          "interface will try to use the system default DLS file, if "
-          "one is available; the likely success of this depends on the "
-          "operating system."));
+// Config variables for Fmod3:
+
+ConfigVariableDouble audio_doppler_factor 	 
+("audio-doppler-factor", 1.0f); 	 
+	  	 
+ConfigVariableDouble audio_distance_factor 	 
+("audio-distance-factor", 1.0f); 	 
+	  	 
+ConfigVariableDouble audio_drop_off_factor 	 
+("audio-drop-off-factor", 1.0f); 	 
+	  	 
+ConfigVariableInt audio_min_hw_channels 	 
+("audio-min-hw-channels", 15, 	 
+PRC_DESC("Guarantee this many channels on the local sound card, or just " 	 
+         "play EVERYTHING in software."));
 
 
 // Config variables for Fmod:
 // Config variables for Fmod:
 
 
-// At one time, the actual number of sound channels was twice this,
-// because Panda creates two AudioManagers, and each AudioManager was
-// initializing the sound hardware separately.  But it turns out you
-// can't do that reliably on all platforms, so now we only initialize
-// the sound hardware once, and this is once again the overall limit.
-// But with FMOD Ex there's not much reason to make this a small
-// number.
 ConfigVariableInt fmod_number_of_sound_channels
 ConfigVariableInt fmod_number_of_sound_channels
 ("fmod-number-of-sound-channels", 128,
 ("fmod-number-of-sound-channels", 128,
  PRC_DESC("Guarantee this many channels you will have with FMOD.  AKA the max number of sounds you can play at one time.") );
  PRC_DESC("Guarantee this many channels you will have with FMOD.  AKA the max number of sounds you can play at one time.") );
@@ -80,6 +85,14 @@ ConfigVariableBool fmod_use_surround_sound
 ConfigVariableBool audio_software_midi 
 ConfigVariableBool audio_software_midi 
 ("audio-software-midi", true);
 ("audio-software-midi", true);
 
 
+ConfigVariableFilename audio_dls_file 
+("audio-dls-file", Filename(),
+ PRC_DESC("Specifies a DLS file that defines an instrument set to load "
+          "for MIDI file playback.  If this is not specified, the sound "
+          "interface will try to use the system default DLS file, if "
+          "one is available; the likely success of this depends on the "
+          "operating system."));
+
 ConfigVariableBool audio_play_midi 
 ConfigVariableBool audio_play_midi 
 ("audio-play-midi", true);
 ("audio-play-midi", true);
 
 

+ 6 - 0
panda/src/audio/config_audio.h

@@ -46,6 +46,12 @@ extern EXPCL_PANDA_AUDIO ConfigVariableString audio_library_name;
 extern EXPCL_PANDA_AUDIO ConfigVariableInt fmod_number_of_sound_channels;
 extern EXPCL_PANDA_AUDIO ConfigVariableInt fmod_number_of_sound_channels;
 extern EXPCL_PANDA_AUDIO ConfigVariableBool fmod_use_surround_sound;
 extern EXPCL_PANDA_AUDIO ConfigVariableBool fmod_use_surround_sound;
 
 
+// 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; 	 
+
 // Config vars for Miles:
 // Config vars for Miles:
 
 
 extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_software_midi;
 extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_software_midi;

+ 20 - 0
panda/src/audiotraits/Sources.pp

@@ -49,6 +49,26 @@
 
 
 #end lib_target
 #end lib_target
 
 
+//#begin lib_target
+//  #define TARGET openal_audio
+//  #define BUILD_TARGET $[HAVE_OPENAL]
+//  #define USE_PACKAGES openal
+//  #define BUILDING_DLL BUILDING_OPENAL_AUDIO
+//  #define LOCAL_LIBS audio event
+//  #define WIN_SYS_LIBS $[WIN_SYS_LIBS] user32.lib advapi32.lib winmm.lib
+//
+//  #define COMBINED_SOURCES $[TARGET]_composite1.cxx
+//
+//  #define SOURCES \
+//      config_openalAudio.h \
+//      openalAudioManager.h \
+//      openalAudioSound.I openalAudioSound.h
+//
+//  #define INCLUDED_SOURCES \
+//      config_openalAudio.cxx openalAudioManager.cxx openalAudioSound.cxx
+//
+//#end lib_target
+
 //#begin lib_target
 //#begin lib_target
 //  #define TARGET audio_linux
 //  #define TARGET audio_linux
 //  #define BUILDING_DLL BUILDING_MISC
 //  #define BUILDING_DLL BUILDING_MISC

+ 64 - 0
panda/src/audiotraits/config_openalAudio.cxx

@@ -0,0 +1,64 @@
+// Filename: config_openalAudio.cxx
+// Created by:  cort
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+#ifdef HAVE_OPENAL //[
+
+
+#include "config_openalAudio.h"
+#include "openalAudioManager.h"
+#include "openalAudioSound.h"
+#include "pandaSystem.h"
+#include "dconfig.h"
+
+ConfigureDef(config_openalAudio);
+NotifyCategoryDef(openalAudio, ":audio");
+
+ConfigureFn(config_openalAudio) {
+  init_libOpenALAudio();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libOpenALAudio
+//  Description: Initializes the library.  This must be called at
+//               least once before any of the functions or classes in
+//               this library can be used.  Normally it will be
+//               called by the static initializers and need not be
+//               called explicitly, but special cases exist.
+////////////////////////////////////////////////////////////////////
+void
+init_libOpenALAudio() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  
+  initialized = true;
+
+  AudioManager::register_AudioManager_creator(Create_AudioManager);
+
+  OpenALAudioManager::init_type();
+  OpenALAudioSound::init_type();
+
+  PandaSystem *ps = PandaSystem::get_global_ptr();
+  ps->add_system("OpenAL");
+  ps->add_system("audio");
+  ps->set_system_tag("audio", "implementation", "OpenAL");
+}
+
+#endif //]

+ 35 - 0
panda/src/audiotraits/config_openalAudio.h

@@ -0,0 +1,35 @@
+// Filename: config_openalAudio.h
+// Created by:  Ben Buchwald <[email protected]>
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 CONFIG_OPENALAUDIO_H
+#define CONFIG_OPENALAUDIO_H
+
+#include "pandabase.h"
+
+#ifdef HAVE_OPENAL //[
+#include "notifyCategoryProxy.h"
+#include "dconfig.h"
+
+ConfigureDecl(config_openalAudio, EXPCL_OPENAL_AUDIO, EXPTP_OPENAL_AUDIO);
+NotifyCategoryDecl(openalAudio, EXPCL_OPENAL_AUDIO, EXPTP_OPENAL_AUDIO);
+
+extern EXPCL_OPENAL_AUDIO void init_libOpenALAudio();
+
+#endif //]
+
+#endif // CONFIG_OPENALAUDIO_H

+ 3 - 7
panda/src/audiotraits/fmodAudioSound.cxx

@@ -356,10 +356,8 @@ get_time() const {
     return 0.0f;
     return 0.0f;
   }
   }
   fmod_audio_errcheck("_channel->getPosition()", result);
   fmod_audio_errcheck("_channel->getPosition()", result);
-
-  current_time = current_time / 1000;
-
-  return current_time;
+  
+  return ((double)current_time) / 1000.0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -496,9 +494,7 @@ length() const {
   result = _sound->getLength( &length, FMOD_TIMEUNIT_MS );
   result = _sound->getLength( &length, FMOD_TIMEUNIT_MS );
   fmod_audio_errcheck("_sound->getLength()", result);
   fmod_audio_errcheck("_sound->getLength()", result);
 
 
-  length = length / 1000;
-
-  return length;
+  return ((double)length) / 1000.0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1049 - 0
panda/src/audiotraits/openalAudioManager.cxx

@@ -0,0 +1,1049 @@
+// Filename: openalAudioManager.cxx
+// Created by:  Ben Buchwald <[email protected]>
+//
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+
+#ifdef HAVE_OPENAL //[
+
+//Panda headers.
+#include "config_audio.h"
+#include "config_util.h"
+#include "config_express.h"
+#include "openalAudioManager.h"
+#include "openalAudioSound.h"
+#include "virtualFileSystem.h"
+#include "movieAudio.h"
+
+#include <algorithm>
+
+//OpenAL Headers.
+#include <al.h>
+#include <alc.h>
+
+TypeHandle OpenALAudioManager::_type_handle;
+
+int OpenALAudioManager::_active_managers = 0;
+bool OpenALAudioManager::_openal_active = false;
+ALCdevice* OpenALAudioManager::_device = NULL;
+ALCcontext* OpenALAudioManager::_context = NULL;
+
+// This is the list of all OpenALAudioManager objects in the world.  It
+// must be a pointer rather than a concrete object, so it won't be
+// destructed at exit time before we're done removing things from it.
+OpenALAudioManager::Managers *OpenALAudioManager::_managers = NULL;
+
+OpenALAudioManager::SourceCache *OpenALAudioManager::_al_sources = NULL;
+
+
+////////////////////////////////////////////////////////////////////
+// Central dispatcher for audio errors.
+////////////////////////////////////////////////////////////////////
+void al_audio_errcheck(const char *context) {
+  ALenum result = alGetError();
+  if (result != AL_NO_ERROR) {
+    audio_error(context << ": " << alGetString(result) );
+  }
+}
+
+void alc_audio_errcheck(const char *context,ALCdevice* device) {
+  ALCenum result = alcGetError(device);
+  if (result != ALC_NO_ERROR) {
+    audio_error(context << ": " << alcGetString(device,result) );
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Create_AudioManager
+//       Access: Private
+//  Description: Factory Function
+////////////////////////////////////////////////////////////////////
+PT(AudioManager) Create_AudioManager() {
+  audio_debug("Create_AudioManager() OpenAL.");
+  return new OpenALAudioManager;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::OpenALAudioManager()
+//       Access: Public
+//  Description: Constructor
+////////////////////////////////////////////////////////////////////
+OpenALAudioManager::
+OpenALAudioManager() {
+  audio_debug("OpenALAudioManager::OpenALAudioManager(), this = " 
+              << (void *)this);
+  if (_managers == (Managers *)NULL) {
+    _managers = new Managers;
+    _al_sources = new SourceCache;
+  }
+
+  _managers->insert(this);
+
+  audio_debug("  audio_active="<<audio_active);
+  audio_debug("  audio_volume="<<audio_volume);
+
+  _cleanup_required = true;
+  _active = audio_active;
+  _volume = audio_volume;
+  _play_rate = 1.0f;
+  
+  _cache_limit = audio_cache_limit;
+
+  _concurrent_sound_limit = 0;
+  _is_valid = true;
+
+  //Init 3D attributes
+  _distance_factor = 3.28;
+  _drop_off_factor = 1;
+
+  _position[0] = 0;
+  _position[1] = 0;
+  _position[2] = 0;
+
+  _velocity[0] = 0;
+  _velocity[1] = 0;
+  _velocity[2] = 0;
+
+  _forward_up[0] = 0;
+  _forward_up[1] = 0;
+  _forward_up[2] = 0;
+  _forward_up[3] = 0;
+  _forward_up[4] = 0;
+  _forward_up[5] = 0;
+
+  // Initialization
+  if (_active_managers==0 || !_openal_active) {
+    _device = alcOpenDevice(NULL); // select the "preferred device"
+    if (!_device) {
+      // this is a unique kind of error
+      audio_error("OpenALAudioManager: alcOpenDevice(NULL): ALC couldn't open device");
+    } 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,
+  // either way.
+  ++_active_managers;
+  audio_debug("  _active_managers="<<_active_managers);
+  nassertv(_active_managers>0);
+
+  if (!_device || !_context) {
+    audio_error("OpenALAudioManager: No open device or context");
+    _is_valid = false;
+  } else {
+    make_current();	
+
+    // set 3D sound characteristics as they are given in the configrc
+    audio_3d_set_doppler_factor(audio_doppler_factor);
+    audio_3d_set_distance_factor(audio_distance_factor);
+    audio_3d_set_drop_off_factor(audio_drop_off_factor);
+  }
+  
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::~OpenALAudioManager
+//       Access: Public
+//  Description: DESTRCUTOR !!!
+////////////////////////////////////////////////////////////////////
+OpenALAudioManager::
+~OpenALAudioManager() {
+  audio_debug("OpenALAudioManager::~OpenALAudioManager(), this = " 
+              << (void *)this);
+  nassertv(_managers != (Managers *)NULL);
+  Managers::iterator mi = _managers->find(this);
+  nassertv(mi != _managers->end());
+  _managers->erase(mi);
+
+  cleanup();
+  audio_debug("OpenALAudioManager::~OpenALAudioManager() finished");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::shutdown
+//       Access: Published, Virtual
+//  Description: Call this at exit time to shut down the audio system.
+//               This will invalidate all currently-active
+//               AudioManagers and AudioSounds in the system.  If you
+//               change your mind and want to play sounds again, you
+//               will have to recreate all of these objects.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+shutdown() {
+  audio_debug("shutdown(), _openal_active = " << _openal_active);
+  if (_managers != (Managers *)NULL) {
+    Managers::iterator mi;
+    for (mi = _managers->begin(); mi != _managers->end(); ++mi) {
+      (*mi)->cleanup();
+    }
+  }
+
+  nassertv(_active_managers == 0);
+  audio_debug("shutdown() finished");
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::is_valid
+//       Access:
+//  Description: This is mostly for debugging, but it it could be
+//               used to detect errors in a release build if you
+//               don't mind the cpu cost.
+////////////////////////////////////////////////////////////////////
+bool OpenALAudioManager::
+is_valid() {
+  bool check=true;
+  if (_sounds.size() != _lru.size()) {
+    audio_debug("-- Error _sounds.size() != _lru.size() --");
+    check=false;
+  } else {
+    LRU::const_iterator i=_lru.begin();
+    for (; i != _lru.end(); ++i) {
+      SoundMap::const_iterator smi=_sounds.find(**i);
+      if (smi == _sounds.end()) {
+        audio_debug("-- "<<**i<<" in _lru and not in _sounds --");
+        check=false;
+        break;
+      }
+    }
+  }
+  return _is_valid && check;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::make_current
+//       Access: Private
+//  Description: This makes this manager's OpenAL context the 
+//         current context. Needed before any parameter sets.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+make_current() const {
+  alcGetError(_device); // clear errors
+  alcMakeContextCurrent(_context);
+  alc_audio_errcheck("alcMakeContextCurrent(_context)",_device);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::load
+//       Access: Private
+//  Description: Reads a sound file and allocates a SoundData pointer
+//               for it.  Returns NULL if the sound file cannot be
+//               loaded.
+////////////////////////////////////////////////////////////////////
+PT(OpenALAudioManager::SoundData) OpenALAudioManager::
+load(Filename file_name) {
+  string raw_data;
+  PT(SoundData) sd = new SoundData(this);
+
+  sd->_basename = file_name.get_basename();
+
+  make_current();
+  
+  PT(MovieAudioCursor) source = MovieAudio::get(file_name)->open();
+  if (source == 0) {
+    audio_error("Could not load audio file "<<file_name);
+    return NULL;
+  }
+
+  int channels = source->audio_channels();
+  if ((channels != 1)&&(channels != 2)) {
+    audio_error("Currently, only mono and stereo are supported.");
+    return NULL;
+  }    
+  int samples = (int)(source->length() * source->audio_rate());
+  if (samples > 10000000) {
+    audio_error("Sound is too long for loading into RAM.");
+    return NULL;
+  }
+
+  alGetError(); // clear errors
+  sd->_buffer = 0;
+  alGenBuffers(1, &sd->_buffer);
+  al_audio_errcheck("alGenBuffers");
+  if (sd->_buffer == 0) {
+    audio_error("Could not create an OpenAL buffer object");
+    return NULL;
+  }
+  
+  PN_int16 *data = new PN_int16[samples * channels];
+  source->read_samples(samples, data);
+  alBufferData(sd->_buffer,
+               (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
+               data, samples * channels * 2, source->audio_rate());
+  int err = alGetError();
+  if (err != AL_NO_ERROR) {
+    audio_error("alBufferData: " << alGetString(err));
+    alDeleteBuffers(1, &sd->_buffer);
+    return NULL;
+  }
+  audio_debug("Loaded "<<file_name<<" Src Len "<<source->length());
+  double len = sd->get_length();
+  return sd;
+}
+
+////////////////////////////////////////////////////////////////////
+//     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) {
+  audio_debug("OpenALAudioManager::get_sound(file_name=\""<<file_name<<"\")");
+
+  if(!is_valid()) {
+     audio_debug("invalid OpenALAudioManager returning NullSound");
+     return get_null_sound();
+  }
+
+  assert(is_valid());
+  Filename path = file_name;
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  vfs->resolve_filename(path, get_sound_path()) ||
+    vfs->resolve_filename(path, get_model_path());
+  audio_debug("  resolved file_name is '"<<path<<"'");
+
+  PT(SoundData) sd;
+  // Get the sound, either from the cache or from a loader:
+  SoundMap::const_iterator si=_sounds.find(path);
+  if (si != _sounds.end()) {
+    // ...found the sound in the cache.
+    sd = (*si).second;
+    audio_debug("  sound found in pool 0x" << (void*)sd);
+  } else {
+    // ...the sound was not found in the cache/pool.
+    sd = load(path);
+    if (sd != (SoundData *)NULL) {
+      while (_sounds.size() >= (unsigned int)_cache_limit) {
+        uncache_a_sound();
+      }
+      // Put it in the pool:
+      // The following is roughly like: _sounds[path] = sd;
+      // But, it gives us an iterator into the map.
+      pair<SoundMap::const_iterator, bool> ib
+          =_sounds.insert(SoundMap::value_type(path, sd));
+      if (!ib.second) {
+        // The insert failed.
+        audio_debug("  failed map insert of "<<path);
+        assert(is_valid());
+        return get_null_sound();
+      }
+      // Set si, so that we can get a reference to the path
+      // for the OpenALAudioSound.
+      si=ib.first;
+    }
+  }
+  // Create an AudioSound from the sound:
+  PT(AudioSound) audioSound = 0;
+  if (sd != (SoundData *)NULL) {
+    most_recently_used((*si).first);
+    PT(OpenALAudioSound) openalAudioSound
+        =new OpenALAudioSound(this, sd, (*si).first, positional);
+    nassertr(openalAudioSound, 0);
+    openalAudioSound->set_active(_active);
+    bool inserted = _sounds_on_loan.insert(openalAudioSound).second;
+    nassertr(inserted, openalAudioSound.p());
+    audioSound=openalAudioSound;
+  }
+
+  audio_debug("  returning 0x" << (void*)audioSound);
+  assert(is_valid());
+  return audioSound;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::uncache_sound
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+uncache_sound(const string& file_name) {
+  audio_debug("OpenALAudioManager::uncache_sound(file_name=\""
+      <<file_name<<"\")");
+  assert(is_valid());
+  Filename path = file_name;
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  vfs->resolve_filename(path, get_sound_path()) ||
+    vfs->resolve_filename(path, get_model_path());
+
+  audio_debug("  path=\""<<path<<"\"");
+  SoundMap::iterator i=_sounds.find(path);
+  if (i != _sounds.end()) {
+    assert(_lru.size()>0);
+    LRU::iterator lru_i=find(_lru.begin(), _lru.end(), &(i->first));
+    assert(lru_i != _lru.end());
+    _lru.erase(lru_i);
+    _sounds.erase(i);
+  }
+  assert(is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::uncache_a_sound
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+uncache_a_sound() {
+  audio_debug("OpenALAudioManager::uncache_a_sound()");
+  assert(is_valid());
+  // uncache least recently used:
+  assert(_lru.size()>0);
+  LRU::reference path=_lru.front();
+  SoundMap::iterator i = _sounds.find(*path);
+  assert(i != _sounds.end());
+  _lru.pop_front();
+
+  if (i != _sounds.end()) {
+    audio_debug("  uncaching \""<<i->first<<"\"");
+    _sounds.erase(i);
+  }
+  assert(is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::most_recently_used
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+most_recently_used(const string& path) {
+  audio_debug("OpenALAudioManager::most_recently_used(path=\""
+      <<path<<"\")");
+  LRU::iterator i=find(_lru.begin(), _lru.end(), &path);
+  if (i != _lru.end()) {
+    _lru.erase(i);
+  }
+  // At this point, path should not exist in the _lru:
+  assert(find(_lru.begin(), _lru.end(), &path) == _lru.end());
+  _lru.push_back(&path);
+  assert(is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::clear_cache
+//       Access: Public
+//  Description: Clear out the sound cache.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+clear_cache() {
+  audio_debug("OpenALAudioManager::clear_cache()");
+  if (_is_valid) { assert(is_valid()); }
+  _sounds.clear();
+  _lru.clear();
+  if (_is_valid) { assert(is_valid()); }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_cache_limit
+//       Access: Public
+//  Description: Set the number of sounds that the cache can hold.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+set_cache_limit(unsigned int count) {
+  audio_debug("OpenALAudioManager::set_cache_limit(count="<<count<<")");
+  assert(is_valid());
+  while (_lru.size() > count) {
+    uncache_a_sound();
+  }
+  _cache_limit=count;
+  assert(is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_cache_limit
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+unsigned int OpenALAudioManager::
+get_cache_limit() const {
+  audio_debug("OpenALAudioManager::get_cache_limit() returning "
+      <<_cache_limit);
+  return _cache_limit;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::release_sound
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+release_sound(OpenALAudioSound* audioSound) {
+  audio_debug("OpenALAudioManager::release_sound(audioSound=\""
+              <<audioSound->get_name()<<"\"), this = " << (void *)this);
+  AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
+  nassertv(ai != _sounds_on_loan.end());
+  _sounds_on_loan.erase(ai);
+
+  audio_debug("OpenALAudioManager::release_sound() finished");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_volume(float volume)
+//       Access: Public
+//  Description: 
+//        Sets listener gain
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::set_volume(float volume) {
+  audio_debug("OpenALAudioManager::set_volume(volume="<<volume<<")");
+  if (_volume!=volume) {
+    _volume = volume;
+
+    // Tell our AudioSounds to adjust:
+    AudioSet::iterator i=_sounds_on_loan.begin();
+    for (; i!=_sounds_on_loan.end(); ++i) {
+      (**i).set_volume((**i).get_volume());
+    }
+
+    /* 
+    // this was neat alternative to the above look
+    // when we had a seperate context for each manager
+    make_current();
+
+    alGetError(); // clear errors
+    alListenerf(AL_GAIN,(ALfloat)_volume);
+    al_audio_errcheck("alListerf(AL_GAIN)");*/
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_volume()
+//       Access: Public
+//  Description: 
+//        Gets listener gain
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+get_volume() const {
+  audio_debug("OpenALAudioManager::get_volume() returning "<<_volume);
+  return _volume;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_play_rate
+//       Access: Public
+//  Description: set the overall play rate
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+set_play_rate(float play_rate) {
+  audio_debug("OpenALAudioManager::set_play_rate(play_rate="<<play_rate<<")");
+  if (_play_rate!=play_rate) {
+    _play_rate = play_rate;
+    // Tell our AudioSounds to adjust:
+    AudioSet::iterator i=_sounds_on_loan.begin();
+    for (; i!=_sounds_on_loan.end(); ++i) {
+      (**i).set_play_rate((**i).get_play_rate());
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_play_rate
+//       Access: Public
+//  Description: get the overall speed/pitch/play rate
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+get_play_rate() const {
+  audio_debug("OpenALAudioManager::get_play_rate() returning "<<_play_rate);
+  return _play_rate;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_active(bool active)
+//       Access: Public
+//  Description: Turn on/off
+//               Warning: not implemented.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+set_active(bool active) {
+  audio_debug("OpenALAudioManager::set_active(flag="<<active<<")");
+  if (_active!=active) {
+    _active=active;
+    // Tell our AudioSounds to adjust:
+    AudioSet::iterator i=_sounds_on_loan.begin();
+    for (; i!=_sounds_on_loan.end(); ++i) {
+      (**i).set_active(_active);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_active()
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool OpenALAudioManager::
+get_active() const {
+  audio_debug("OpenALAudioManager::get_active() returning "<<_active);
+  return _active;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_set_listener_attributes
+//       Access: Public
+//  Description: Set position of the "ear" that picks up 3d sounds
+//        NOW LISTEN UP!!! THIS IS IMPORTANT!
+//        Both Panda3D and OpenAL use a right handed coordinate system.
+//        But there is a major difference!
+//        In Panda3D the Y-Axis is going into the Screen and the Z-Axis is going up.
+//        In OpenAL the Y-Axis is going up and the Z-Axis is coming out of the screen.
+//        The solution is simple, we just flip the Y and Z axis and negate the Z, as we move coordinates
+//        from Panda to OpenAL and back.
+//        What does did mean to average Panda user?  Nothing, they shouldn't notice anyway.
+//        But if you decide to do any 3D audio work in here you have to keep it in mind.
+//        I told you, so you can't say I didn't.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+audio_3d_set_listener_attributes(float px, float py, float pz, float vx, float vy, float vz, float fx, float fy, float fz, float ux, float uy, float uz) {
+  _position[0] = px;
+  _position[1] = pz;
+  _position[2] = -py; 
+
+  _velocity[0] = vx;
+  _velocity[1] = vz;
+  _velocity[2] = -vy;
+
+  _forward_up[0] = fx;
+  _forward_up[1] = fz;
+  _forward_up[2] = -fy;
+
+  _forward_up[3] = ux;
+  _forward_up[4] = uz;
+  _forward_up[5] = -uy;
+    
+  
+  make_current();
+
+  alGetError(); // clear errors
+  alListenerfv(AL_POSITION,_position);
+  al_audio_errcheck("alListerfv(AL_POSITION)");
+  alListenerfv(AL_VELOCITY,_velocity);
+  al_audio_errcheck("alListerfv(AL_VELOCITY)");
+  alListenerfv(AL_ORIENTATION,_forward_up);
+  al_audio_errcheck("alListerfv(AL_ORIENTATION)");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_get_listener_attributes
+//       Access: Public
+//  Description: Get position of the "ear" that picks up 3d sounds
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+audio_3d_get_listener_attributes(float *px, float *py, float *pz, float *vx, float *vy, float *vz, float *fx, float *fy, float *fz, float *ux, float *uy, float *uz) {
+  *px = _position[0];
+  *py = -_position[2];
+  *pz = _position[1];
+
+  *vx = _velocity[0];
+  *vy = -_velocity[2];
+  *vz = _velocity[1];
+
+  *fx = _forward_up[0];
+  *fy = -_forward_up[2];
+  *fz = _forward_up[1];
+  
+  *ux = _forward_up[3];
+  *uy = -_forward_up[5];
+  *uz = _forward_up[4];
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_set_distance_factor
+//       Access: Public
+//  Description: Set units per foot
+//               WARNING: OpenAL has no distance factor but we use this as a scale
+//                        on the min/max distances of sounds to preserve FMOD compatibility.
+//                        Also, adjusts the speed of sound to compensate for unit difference.
+//                        OpenAL's default speed of sound is 343.3 m/s == 1126.3 ft/s
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+audio_3d_set_distance_factor(float factor) {
+  _distance_factor = factor;
+
+  make_current();
+
+  alGetError(); // clear errors
+
+  if (_distance_factor>0) {
+    alSpeedOfSound(1126.3*_distance_factor);
+    al_audio_errcheck("alSpeedOfSound()");
+    // resets the doppler factor to the correct setting in case it was set to 0.0 by a distance_factor<=0.0
+    alDopplerFactor(_doppler_factor);
+    al_audio_errcheck("alDopplerFactor()");
+  } else {
+    audio_debug("can't set speed of sound if distance_factor <=0.0, setting doppler factor to 0.0 instead");
+    alDopplerFactor(0.0);
+    al_audio_errcheck("alDopplerFactor()");
+  }
+
+  AudioSet::iterator i=_sounds_on_loan.begin();
+  for (; i!=_sounds_on_loan.end(); ++i) {
+    (**i).set_3d_min_distance((**i).get_3d_min_distance());
+    (**i).set_3d_max_distance((**i).get_3d_max_distance());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_get_distance_factor
+//       Access: Public
+//  Description: Sets units per foot
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+audio_3d_get_distance_factor() const {
+  return _distance_factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_set_doppler_factor
+//       Access: Public
+//  Description: Exaggerates or diminishes the Doppler effect. 
+//               Defaults to 1.0
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+audio_3d_set_doppler_factor(float factor) {
+  _doppler_factor = factor;
+
+  make_current();
+  
+  alGetError(); // clear errors
+  alDopplerFactor(_doppler_factor);
+  al_audio_errcheck("alDopplerFactor()");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_get_doppler_factor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+audio_3d_get_doppler_factor() const {
+  return _doppler_factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_set_drop_off_factor
+//       Access: Public
+//  Description: Control the effect distance has on audability.
+//               Defaults to 1.0
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+audio_3d_set_drop_off_factor(float factor) {
+  _drop_off_factor = factor;
+
+  AudioSet::iterator i=_sounds_on_loan.begin();
+  for (; i!=_sounds_on_loan.end(); ++i) {
+    (**i).set_3d_drop_off_factor((**i).get_3d_drop_off_factor());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::audio_3d_get_drop_off_factor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::
+audio_3d_get_drop_off_factor() const {
+  return _drop_off_factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::starting_sound
+//       Access: 
+//  Description: Inform the manager that a sound is about to play.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+starting_sound(OpenALAudioSound* audio) {
+  ALuint source=0;
+
+  // first give all sounds that have finished a chance to stop, so that these get stopped first
+  update();
+
+  if (_concurrent_sound_limit) {
+    reduce_sounds_playing_to(_concurrent_sound_limit-1); // because we're about to add one
+  }
+  
+  // get a source from the source pool or create a new source
+  if (_al_sources->empty()) {
+    make_current();
+    alGetError(); // clear errors
+    alGenSources(1,&source);
+    ALenum result = alGetError();
+    if (result!=AL_NO_ERROR) {
+      audio_error("alGenSources(): " << alGetString(result) );
+      // if we can't create any more sources, set stop a sound to free a source
+      reduce_sounds_playing_to(_sounds_playing.size()-1);
+      source = 0;
+    }
+  }
+  // get a source from the source bool if we didn't just allocate one
+  if (!source && !_al_sources->empty()) {
+    source = *(_al_sources->begin());
+    _al_sources->erase(source);
+  }
+
+  assert(!audio->_source);
+  audio->_source = source;
+
+  if (source)
+    _sounds_playing.insert(audio);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::stopping_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 OpenALAudioManager::
+stopping_sound(OpenALAudioSound* audio) {
+  _sounds_playing.erase(audio);
+  if (audio->_source) {
+    _al_sources->insert(audio->_source);
+    audio->_source = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::set_concurrent_sound_limit
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+set_concurrent_sound_limit(unsigned int limit) {
+  _concurrent_sound_limit = limit;
+  reduce_sounds_playing_to(_concurrent_sound_limit);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::get_concurrent_sound_limit
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+unsigned int OpenALAudioManager::
+get_concurrent_sound_limit() const {
+  return _concurrent_sound_limit;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::reduce_sounds_playing_to
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+reduce_sounds_playing_to(unsigned int count) {
+  // first give all sounds that have finished a chance to stop, so that these get stopped first
+  update();
+
+  int limit = _sounds_playing.size() - count;
+  while (limit-- > 0) {
+    SoundsPlaying::iterator sound = _sounds_playing.begin();
+    assert(sound != _sounds_playing.end());
+    (**sound).stop();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::stop_all_sounds()
+//       Access: Public
+//  Description: Stop playback on all sounds managed by this manager.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+stop_all_sounds() {
+  audio_debug("OpenALAudioManager::stop_all_sounds()");
+  reduce_sounds_playing_to(0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::update
+//       Access: Public
+//  Description: Perform all per-frame update functions.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+update() {
+  //audio_debug("OpenALAudioManager::update()");
+
+  // See if any of our playing sounds have ended
+  // we must first collect a seperate list of finished sounds and then
+  // iterated over those again calling their finished method. We 
+  // can't call finished() within a loop iterating over _sounds_playing
+  // since finished() modifies _sounds_playing
+  SoundsPlaying sounds_finished;
+
+  SoundsPlaying::iterator i=_sounds_playing.begin();
+  for (; i!=_sounds_playing.end(); ++i) {
+    if ((**i).status()!=AudioSound::PLAYING) {
+      sounds_finished.insert(*i);
+    }
+  }
+
+  i=sounds_finished.begin();
+  for (; i!=sounds_finished.end(); ++i) {
+    (**i).finished();
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::cleanup
+//       Access: Private
+//  Description: Shuts down the audio manager and releases any
+//               resources associated with it.  Also cleans up all
+//               AudioSounds created via the manager.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioManager::
+cleanup() {
+  audio_debug("OpenALAudioManager::cleanup(), this = " << (void *)this
+              << ", _cleanup_required = " << _cleanup_required);
+  if (!_cleanup_required) {
+    return;
+  }
+
+  // Be sure to cleanup associated sounds before cleaning up the manager:
+  AudioSet::iterator ai;
+  for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) {
+    (*ai)->cleanup();
+  }
+
+  clear_cache();
+
+  nassertv(_active_managers > 0);
+  --_active_managers;
+  audio_debug("  _active_managers="<<_active_managers);
+
+  if (_active_managers == 0) {
+    if (_openal_active) {
+      // empty the source cache
+      int i=0;
+      ALuint *sources;
+      sources = new ALuint[_al_sources->size()];
+      for (SourceCache::iterator si = _al_sources->begin(); si!=_al_sources->end(); ++si) {
+        sources[i++]=*si;
+      }
+      make_current();
+      alGetError(); // clear errors
+      alDeleteSources(_al_sources->size(),sources);
+      al_audio_errcheck("alDeleteSources()");
+      delete [] sources;
+      _al_sources->clear();
+
+      // make sure that the context is not current when it is destroyed
+      alcGetError(_device); // clear errors
+      alcMakeContextCurrent(NULL);
+      alc_audio_errcheck("alcMakeContextCurrent(NULL)",_device);
+      
+      alcDestroyContext(_context);
+      alc_audio_errcheck("alcDestroyContext(_context)",_device);
+      _context = NULL;
+
+      if (_device) {
+        alcCloseDevice(_device);
+        //alc_audio_errcheck("alcCloseDevice(_device)",_device);
+        _device = NULL;
+      }
+
+      _openal_active = false;
+    }
+    /*if (_managers) {
+      delete _managers;
+      _managers = NULL;
+      delete _al_sources;
+      _al_sources = NULL;
+    }*/
+  }
+  _cleanup_required = false;
+  audio_debug("OpenALAudioManager::cleanup() finished");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::SoundData::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+OpenALAudioManager::SoundData::
+SoundData(OpenALAudioManager* manager) :
+  _manager(manager),
+  _buffer(0),
+  _has_length(false)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::SoundData::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+OpenALAudioManager::SoundData::
+~SoundData() {
+  if (_buffer != 0) {
+    if (_manager->_is_valid) {
+      _manager->make_current();
+      alDeleteBuffers(1,&_buffer);
+    }
+    _buffer = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioManager::SoundData::get_length
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float OpenALAudioManager::SoundData::
+get_length() {
+  int freq,bits,channels,size;
+
+  if (!_has_length) {
+    // Time to determine the length of the file.
+
+    audio_debug("Computing length of " << _basename);
+
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alGetBufferi(_buffer,AL_FREQUENCY,&freq);
+    audio_debug("Frequency = "<<freq);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_FREQUENCY)");
+    alGetBufferi(_buffer,AL_BITS,&bits);
+    audio_debug("Bits = "<<bits);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_BITS)");
+    alGetBufferi(_buffer,AL_CHANNELS,&channels);
+    audio_debug("Channels = "<<channels);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_CHANNELS)");
+    alGetBufferi(_buffer,AL_SIZE,&size);
+    audio_debug("Size = "<<size);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_SIZE)");
+  
+    _length = ((float)size)/channels/((float)bits/8)/freq;
+    audio_debug("Length = "<<_length);
+    _has_length = true;
+  }
+
+  return _length;
+}
+
+#endif //]

+ 227 - 0
panda/src/audiotraits/openalAudioManager.h

@@ -0,0 +1,227 @@
+// Filename: openalAudioManager.h
+// Created by:  Ben Buchwald <[email protected]>
+//
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 __OPENAL_AUDIO_MANAGER_H__
+#define __OPENAL_AUDIO_MANAGER_H__
+
+#include "pandabase.h"
+#ifdef HAVE_OPENAL //[
+
+#include "audioManager.h"
+#include "pset.h"
+#include "pmap.h"
+#include "pdeque.h"
+
+//The Includes needed for OpenAL
+#include <al.h>
+#include <alc.h>
+#include <alut.h>
+
+class OpenALAudioSound;
+
+extern void al_audio_errcheck(const char *context);
+extern void alc_audio_errcheck(const char *context,ALCdevice* device);
+extern void alut_audio_errcheck(const char *context);
+
+class EXPCL_OPENAL_AUDIO OpenALAudioManager : public AudioManager {
+  class SoundData;
+  
+  friend class OpenALAudioSound;
+  friend class OpenALSoundData;
+ public:
+
+  //Constructor and Destructor
+  OpenALAudioManager();
+  virtual ~OpenALAudioManager();
+
+  virtual void shutdown();
+
+  virtual bool is_valid();
+          
+  virtual PT(AudioSound) get_sound(const string&, bool positional = false);
+  
+  virtual void uncache_sound(const string&);
+  virtual void clear_cache();
+  virtual void set_cache_limit(unsigned int count);
+  virtual unsigned int get_cache_limit() const;
+    
+  virtual void set_volume(float);
+  virtual float get_volume() const;
+          
+  void set_play_rate(float play_rate);
+  float get_play_rate() const;
+
+  virtual void set_active(bool);
+  virtual bool get_active() const;
+
+  // This controls the "set of ears" that listens to 3D spacialized sound
+  // px, py, pz are position coordinates. Can be 0.0f to ignore.
+  // vx, vy, vz are a velocity vector in UNITS PER SECOND.
+  // fx, fy and fz are the respective components of a unit forward-vector
+  // ux, uy and uz are the respective components of a unit up-vector
+  // These changes will NOT be invoked until audio_3d_update() is called.
+  virtual void audio_3d_set_listener_attributes(float px, float py, float pz,
+                                                float vx, float xy, float xz, 
+                                                float fx, float fy, float fz,
+                                                float ux, float uy, float uz);
+
+  virtual void audio_3d_get_listener_attributes(float *px, float *py, float *pz,
+                                                float *vx, float *vy, float *vz,
+                                                float *fx, float *fy, float *fz,
+                                                float *ux, float *uy, float *uz);
+          
+  // Control the "relative distance factor" for 3D spacialized audio in units-per-foot. Default is 1.0
+  // OpenAL has no distance factor but we use this as a scale
+  // on the min/max distances of sounds to preserve FMOD compatibility.
+  // Also, adjusts the speed of sound to compensate for unit difference.
+  virtual void audio_3d_set_distance_factor(float factor);
+  virtual float audio_3d_get_distance_factor() const;
+
+  // Control the presence of the Doppler effect. Default is 1.0
+  // Exaggerated Doppler, use >1.0
+  // Diminshed Doppler, use <1.0
+  virtual void audio_3d_set_doppler_factor(float factor);
+  virtual float audio_3d_get_doppler_factor() const;
+
+  // Exaggerate or diminish the effect of distance on sound. Default is 1.0
+  // Faster drop off, use >1.0
+  // Slower drop off, use <1.0
+  virtual void audio_3d_set_drop_off_factor(float factor);
+  virtual float audio_3d_get_drop_off_factor() const;
+
+  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();
+
+  virtual void update();
+
+private:
+  void make_current() const;
+
+  PT(SoundData) load(Filename file_name);
+
+  // Tell the manager that the sound dtor was called.
+  void release_sound(OpenALAudioSound* audioSound);
+  
+  void most_recently_used(const string& path);
+  void uncache_a_sound();
+
+  void starting_sound(OpenALAudioSound* audio);
+  void stopping_sound(OpenALAudioSound* audio);
+
+  void cleanup();
+private:
+
+  // The sound cache:
+  class SoundData : public ReferenceCount {
+  public:
+    SoundData(OpenALAudioManager* manager);
+    ~SoundData();
+    float get_length();
+
+    OpenALAudioManager* _manager;
+    string _basename;
+    ALuint _buffer;
+    bool _has_length;
+    float _length;  // in seconds.
+  };
+  typedef pmap<string, PT(SoundData) > SoundMap;
+  SoundMap _sounds;
+  int _cache_limit;
+
+  typedef pset<OpenALAudioSound* > AudioSet;
+  // The offspring of this manager:
+  AudioSet _sounds_on_loan;
+
+  typedef pset<OpenALAudioSound* > SoundsPlaying;
+  // The sounds from this manager that are currently playing
+  SoundsPlaying _sounds_playing;
+
+  // The Least Recently Used mechanism:
+  typedef pdeque<const string* > LRU;
+  LRU _lru;
+
+  // State:
+  float _volume;
+  float _play_rate;
+  bool _active;
+  bool _cleanup_required;
+  // keep a count for startup and shutdown:
+  static int _active_managers;
+  static bool _openal_active;
+  unsigned int _concurrent_sound_limit;
+  
+  bool _is_valid;
+  
+  typedef pset<OpenALAudioManager *> Managers;
+  static Managers *_managers;
+
+  static ALCdevice* _device; 
+  static ALCcontext* _context;
+
+  // cache of openal sources, use only for playing sounds
+  typedef pset<ALuint > SourceCache;
+  static SourceCache *_al_sources;
+
+  float _distance_factor;
+  float _doppler_factor;
+  float _drop_off_factor;
+
+  ALfloat _position[3];
+  ALfloat _velocity[3];
+  ALfloat _forward_up[6];
+
+  ////////////////////////////////////////////////////////////
+  //These are needed for Panda's Pointer System. DO NOT ERASE!
+  ////////////////////////////////////////////////////////////
+
+ public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    AudioManager::init_type();
+    register_type(_type_handle, "OpenALAudioManager", AudioManager::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;
+
+  ////////////////////////////////////////////////////////////
+  //DONE
+  ////////////////////////////////////////////////////////////
+
+};
+
+EXPCL_OPENAL_AUDIO PT(AudioManager) Create_AudioManager();
+
+
+#endif //]
+
+#endif /* __OPENAL_AUDIO_MANAGER_H__ */

+ 23 - 0
panda/src/audiotraits/openalAudioSound.I

@@ -0,0 +1,23 @@
+// Filename: openalAudioSound.I
+// Created by:  Ben Buchwald <[email protected]>
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+
+
+
+

+ 730 - 0
panda/src/audiotraits/openalAudioSound.cxx

@@ -0,0 +1,730 @@
+// Filename: openalAudioSound.cxx
+// Created by:  Ben Buchwald <[email protected]>
+//
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+
+#ifdef HAVE_OPENAL //[
+
+//Panda Headers
+#include "throw_event.h"
+#include "openalAudioSound.h"
+#include "openalAudioManager.h"
+
+TypeHandle OpenALAudioSound::_type_handle;
+
+
+#ifndef NDEBUG //[
+  #define openal_audio_debug(x) \
+      audio_debug("OpenALAudioSound \""<<get_name() \
+      <<"\" "<< x )
+#else //][
+#define openal_audio_debug(x) ((void)0)
+#endif //]
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::OpenALAudioSound
+//       Access: public
+//  Description: Constructor
+//               All sound will DEFAULT load as a 2D sound unless
+//               otherwise specified.
+////////////////////////////////////////////////////////////////////
+
+OpenALAudioSound::
+OpenALAudioSound(OpenALAudioManager* manager,
+    OpenALAudioManager::SoundData *sd, string file_name, bool positional)
+    : _sd(sd), _source(0), _manager(manager), _file_name(file_name),
+    _volume(1.0f), _balance(0), _play_rate(1.0),
+    _loop_count(1), _pause_time(0.0),
+    _active(true), _paused(false) {
+  nassertv(sd != NULL);
+  nassertv(!file_name.empty());
+  audio_debug("OpenALAudioSound(manager=0x"<<(void*)&manager
+      <<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
+
+  //Inits 3D Attributes
+  _location[0] = 0;
+  _location[1] = 0;
+  _location[2] = 0;
+
+  _velocity[0] = 0;
+  _velocity[1] = 0;
+  _velocity[2] = 0;
+
+  _min_dist = 3.28f; _max_dist = 1000000000.0f;
+  _drop_off_factor = 1.0f;
+
+  _buffer = _sd->_buffer;
+  
+  // don't assign source until we play since sources are limited
+  /*
+  // Create a source to play the buffer
+  alGetError(); // clear errors
+  alGenSources(1,&_source);
+  al_audio_errcheck("alGenSources()");
+
+  // Assign the buffer to the source
+  alSourcei(_source,AL_BUFFER,_buffer);
+  al_audio_errcheck("alSourcei(_source,AL_BUFFER,_buffer)");
+  */
+
+  // nonpositional sources are made relative to the listener so they don't move
+  _positional = positional;
+  if (_positional) {
+    int nChannels=1;
+    alGetBufferi(_buffer,AL_CHANNELS,&nChannels);
+    al_audio_errcheck("alGetBufferi(_buffer,AL_CHANNELS)");
+    if (nChannels>1)
+      audio_warning("OpenALAudioSound: Stereo sounds won't be spacialized: "<<file_name);
+  } else {
+    /*alSourcei(_source,AL_SOURCE_RELATIVE,AL_TRUE);
+    al_audio_errcheck("alSourcei(_source,AL_SOURCE_RELATIVE,AL_TRUE)");*/
+  }
+
+  /*
+  // set initial values since they are manager-relative
+  set_volume(_volume);
+  //set_balance(_balance);
+  set_play_rate(_play_rate);
+  set_3d_min_distance(_min_dist);
+  set_3d_max_distance(_max_dist);
+  set_3d_drop_off_factor(_drop_off_factor);
+  */
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::~OpenALAudioSound
+//       Access: public
+//  Description: DESTRUCTOR!!!
+////////////////////////////////////////////////////////////////////
+OpenALAudioSound::
+~OpenALAudioSound() {
+  openal_audio_debug("~OpenALAudioSound()");
+  cleanup();
+  _manager->release_sound(this);
+  openal_audio_debug("~OpenALAudioSound() done");
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound:: play
+//       Access: public
+//  Description: Plays a sound.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+play() {
+  float px,py,pz,vx,vy,vz;
+      
+  openal_audio_debug("play()");
+  if (_active) {
+    if (status() == AudioSound::PLAYING) {
+      stop();
+    }
+    //nassertv(_source);
+    _manager->starting_sound(this);
+
+    if (_source) {
+      // Setup source
+      _manager->make_current();
+
+      alGetError(); // clear errors
+      
+      // Assign the buffer to the source
+      alSourcei(_source,AL_BUFFER,_buffer);
+      ALenum result = alGetError();
+      if (result!=AL_NO_ERROR) {
+        audio_error("alSourcei(_source,AL_BUFFER,_buffer): " << alGetString(result) );
+        stop();
+        return;
+      }
+
+      // nonpositional sources are made relative to the listener so they don't move
+      alSourcei(_source,AL_SOURCE_RELATIVE,_positional?AL_FALSE:AL_TRUE);
+      al_audio_errcheck("alSourcei(_source,AL_SOURCE_RELATIVE)");
+
+      // set source properties that we have stored
+      set_volume(_volume);
+      //set_balance(_balance);
+      set_play_rate(_play_rate);
+      set_3d_min_distance(_min_dist);
+      set_3d_max_distance(_max_dist);
+      set_3d_drop_off_factor(_drop_off_factor);
+      
+      get_3d_attributes(&px,&py,&pz,&vx,&vy,&vz);
+      set_3d_attributes(px, py, pz, vx, vy, vz);
+
+      set_loop_count(_loop_count);
+
+      if (_pause_time) {
+        set_time(_pause_time);
+        _pause_time = 0.0;
+      }
+
+      // Start playing:
+      alSourcePlay(_source);
+      al_audio_errcheck("alSourcePlay(_source)");
+
+      audio_debug("  started sound " << _file_name );
+    }
+  } else {
+    // In case _loop_count gets set to forever (zero):
+    audio_debug("  paused "<<_file_name );
+    _paused=true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::stop
+//       Access: public
+//  Description: Stop a sound
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+stop() {
+  openal_audio_debug("stop()");
+  //nassertv(_source);
+  
+  if (_source) {
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourceStop(_source);
+    al_audio_errcheck("alSourceStop(_source)");
+  }
+
+  _pause_time = 0.0;
+
+  _manager->stopping_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 symmetrical with play().  set_active() is the 'owner' of
+  // _paused.  play() accesses _paused to help in the situation where
+  // someone calls play on an inactive sound().
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::finished
+//       Access: 
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+finished() {
+  openal_audio_debug("finished()");
+  _manager->stopping_sound(this);
+  if (!_finished_event.empty()) {
+    throw_event(_finished_event);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_loop
+//       Access: public
+//  Description: Turns looping on and off
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_loop(bool loop) {
+  openal_audio_debug("set_loop(loop="<<loop<<")");
+  // loop count of 0 means always loop
+  set_loop_count((loop)?0:1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_loop
+//       Access: public
+//  Description: Returns whether looping is on or off
+////////////////////////////////////////////////////////////////////
+bool OpenALAudioSound::
+get_loop() const {
+  openal_audio_debug("get_loop() returning "<<(_loop_count==0));
+  return (_loop_count == 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_loop_count
+//       Access: public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_loop_count(unsigned long loop_count) {
+  openal_audio_debug("set_loop_count(loop_count="<<loop_count<<")");
+
+  if (loop_count>1) {
+    audio_error("OpenALAudioSound::set_loop_count() doesn't support looping a finite number of times, 0 (infinite) or 1 only");
+    loop_count = 1;
+  }
+
+  if (_loop_count!=loop_count) {
+    _loop_count=loop_count;
+  }
+
+  if (_source) {
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourcei(_source,AL_LOOPING,_loop_count==0?AL_TRUE:AL_FALSE);
+    al_audio_errcheck("alSourcei(_source,AL_LOOPING)");
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_loop_count
+//       Access: public
+//  Description: Return how many times a sound will loop.
+////////////////////////////////////////////////////////////////////
+unsigned long OpenALAudioSound::
+get_loop_count() const {
+  openal_audio_debug("get_loop_count() returning "<<_loop_count);
+  return _loop_count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_time
+//       Access: public
+//  Description: Sets the play position within the sound
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_time(float time) {
+  openal_audio_debug("set_time(time="<<time<<")");
+
+  //nassertv(_source);
+  
+  // Ensure we don't inadvertently run off the end of the sound.
+  float max_time = length();
+  if (time > max_time) {
+    openalAudio_cat.warning()
+      << "set_time(" << time << ") requested for sound of length " 
+      << max_time << "\n";
+    time = max_time;
+  }
+
+  if (_source) {
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourcef(_source,AL_SEC_OFFSET,time);
+    al_audio_errcheck("alSourcef(_source,AL_SEC_OFFSET)");
+  }
+
+  if (status()==PLAYING) {
+    _pause_time = 0.0;
+  } else {
+    _pause_time = time;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_time
+//       Access: public
+//  Description: Gets the play position within the sound
+////////////////////////////////////////////////////////////////////
+float OpenALAudioSound::
+get_time() const {
+  float time;
+
+  //nassertv(_source);
+
+  if (_source) {
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alGetSourcef(_source,AL_SEC_OFFSET,&time);
+    al_audio_errcheck("alGetSourcef(_source,AL_SEC_OFFSET)");
+  } else {
+    time = _pause_time;
+  }
+  openal_audio_debug("get_time() returning "<<time);
+  return time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_volume(float vol)
+//       Access: public
+//  Description: 0.0 to 1.0 scale of volume converted to Fmod's
+//               internal 0.0 to 255.0 scale.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_volume(float volume) {
+  openal_audio_debug("set_volume(volume="<<volume<<")");
+
+  //nassertv(_source);
+  
+  _volume=volume;
+
+  if (_source) {
+    volume*=_manager->get_volume();
+    
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourcef(_source,AL_GAIN,volume);
+    al_audio_errcheck("alSourcef(_source,AL_GAIN)");
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_volume
+//       Access: public
+//  Description: Gets the current volume of a sound.  1 is Max. O is Min.
+////////////////////////////////////////////////////////////////////
+float OpenALAudioSound::
+get_volume() const {
+  openal_audio_debug("get_volume() returning "<<_volume);
+  return _volume;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_balance(float bal)
+//       Access: public
+//  Description: -1.0 to 1.0 scale
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_balance(float balance_right) {
+  audio_debug("OpenALAudioSound::set_balance() not implemented");
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_balance
+//       Access: public
+//  Description: -1.0 to 1.0 scale 
+//        -1 should be all the way left.
+//        1 is all the way to the right.
+////////////////////////////////////////////////////////////////////
+float OpenALAudioSound::
+get_balance() const {
+  audio_debug("OpenALAudioSound::get_balance() not implemented");
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_play_rate(float rate)
+//       Access: public
+//  Description: Sets the speed at which a sound plays back.
+//        The rate is a multiple of the sound, normal playback speed.
+//        IE 2 would play back 2 times fast, 3 would play 3 times, and so on.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_play_rate(float play_rate) {
+  openal_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
+
+  //nassertv(_source);
+  
+  _play_rate=play_rate;
+
+  if (_source) {
+    play_rate*=_manager->get_play_rate();
+    
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourcef(_source,AL_PITCH,play_rate);
+    al_audio_errcheck("alSourcef(_source,AL_PITCH)");
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_play_rate
+//       Access: public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+float OpenALAudioSound::
+get_play_rate() const {
+  openal_audio_debug("get_play_rate() returning "<<_play_rate);
+  return _play_rate;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::length
+//       Access: public
+//  Description: Get length
+////////////////////////////////////////////////////////////////////
+float OpenALAudioSound::
+length() const {
+  return _sd->get_length();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_3d_attributes
+//       Access: public
+//  Description: Set position and velocity of this sound
+//        NOW LISTEN UP!!! THIS IS IMPORTANT!
+//        Both Panda3D and OpenAL use a right handed coordinate system.
+//        But there is a major difference!
+//        In Panda3D the Y-Axis is going into the Screen and the Z-Axis is going up.
+//        In OpenAL the Y-Axis is going up and the Z-Axis is coming out of the screen.
+//        The solution is simple, we just flip the Y and Z axis and negate the Z, as we move coordinates
+//        from Panda to OpenAL and back.
+//        What does did mean to average Panda user?  Nothing, they shouldn't notice anyway.
+//        But if you decide to do any 3D audio work in here you have to keep it in mind.
+//        I told you, so you can't say I didn't.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_3d_attributes(float px, float py, float pz, float vx, float vy, float vz) {
+  _location[0] = px;
+  _location[1] = pz;
+  _location[2] = -py; 
+
+  _velocity[0] = vx;
+  _velocity[1] = vz;
+  _velocity[2] = -vy;
+
+  if (_source) {
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourcefv(_source,AL_POSITION,_location);
+    al_audio_errcheck("alSourcefv(_source,AL_POSITION)");
+    alSourcefv(_source,AL_VELOCITY,_velocity);
+    al_audio_errcheck("alSourcefv(_source,AL_VELOCITY)");
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_3d_attributes
+//       Access: public
+//  Description: Get position and velocity of this sound
+//         Currently unimplemented. Get the attributes of the attached object.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+get_3d_attributes(float *px, float *py, float *pz, float *vx, float *vy, float *vz) {
+  *px = _location[0];
+  *py = -_location[2];
+  *pz = _location[1];
+
+  *vx = _velocity[0];
+  *vy = -_velocity[2];
+  *vz = _velocity[1];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_3d_min_distance
+//       Access: public
+//  Description: Set the distance that this sound begins to fall off. Also
+//               affects the rate it falls off.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_3d_min_distance(float dist) {
+  _min_dist = dist;
+
+  if (_source) {
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourcef(_source,AL_REFERENCE_DISTANCE,_min_dist*_manager->audio_3d_get_distance_factor());
+    al_audio_errcheck("alSourcefv(_source,AL_REFERENCE_DISTANCE)");
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_3d_min_distance
+//       Access: public
+//  Description: Get the distance that this sound begins to fall off
+////////////////////////////////////////////////////////////////////
+float OpenALAudioSound::
+get_3d_min_distance() const {
+  return _min_dist;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_3d_max_distance
+//       Access: public
+//  Description: Set the distance that this sound stops falling off
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_3d_max_distance(float dist) {
+  _max_dist = dist;
+
+  if (_source) {
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourcef(_source,AL_MAX_DISTANCE,_max_dist*_manager->audio_3d_get_distance_factor());
+    al_audio_errcheck("alSourcefv(_source,AL_MAX_DISTANCE)");
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_3d_max_distance
+//       Access: public
+//  Description: Get the distance that this sound stops falling off
+////////////////////////////////////////////////////////////////////
+float OpenALAudioSound::
+get_3d_max_distance() const {
+  return _max_dist;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_3d_drop_off_factor
+//       Access: public
+//  Description: Control the effect distance has on audability.
+//               Defaults to 1.0
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_3d_drop_off_factor(float factor) {
+  _drop_off_factor = factor;
+
+  if (_source) {
+    _manager->make_current();
+
+    alGetError(); // clear errors
+    alSourcef(_source,AL_ROLLOFF_FACTOR,_drop_off_factor*_manager->audio_3d_get_drop_off_factor());
+    al_audio_errcheck("alSourcefv(_source,AL_ROLLOFF_FACTOR)");
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_3d_drop_off_factor
+//       Access: public
+//  Description: Control the effect distance has on audability.
+//               Defaults to 1.0
+////////////////////////////////////////////////////////////////////
+float OpenALAudioSound::
+get_3d_drop_off_factor() const {
+  return _drop_off_factor;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_active
+//       Access: public
+//  Description: Sets whether the sound is marked "active".  By
+//               default, the active flag true for all sounds.  If the
+//               active flag is set to false for any particular sound,
+//               the sound will not be heard.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_active(bool active) {
+  openal_audio_debug("set_active(active="<<active<<")");
+  if (_active!=active) {
+    _active=active;
+    if (_active) {
+      // ...activate the sound.
+      if (_paused
+          &&
+          _loop_count==0) {
+        // ...this sound was looping when it was paused.
+        _paused=false;
+        play();
+      }
+    } else {
+      // ...deactivate the sound.
+      if (status()==PLAYING) {
+        if (_loop_count==0) {
+          // ...we're pausing a looping sound.
+          _paused=true;
+        }
+        stop();
+      }
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_active 
+//       Access: public
+//  Description: Returns whether the sound has been marked "active".
+////////////////////////////////////////////////////////////////////
+bool OpenALAudioSound::
+get_active() const {
+  openal_audio_debug("get_active() returning "<<_active);
+  return _active;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::set_finished_event
+//       Access: 
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+set_finished_event(const string& event) {
+  openal_audio_debug("set_finished_event(event="<<event<<")");
+  _finished_event = event;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_finished_event
+//       Access: 
+//  Description: 
+////////////////////////////////////////////////////////////////////
+const string& OpenALAudioSound::
+get_finished_event() const {
+  openal_audio_debug("get_finished_event() returning "<<_finished_event);
+  return _finished_event;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::get_name
+//       Access: public
+//  Description: Get name of sound file
+////////////////////////////////////////////////////////////////////
+const string& OpenALAudioSound::
+get_name() const {
+  return _file_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::status
+//       Access: public
+//  Description: Get status of the sound.
+////////////////////////////////////////////////////////////////////
+AudioSound::SoundStatus OpenALAudioSound::
+status() const {
+  ALenum status;
+
+  if (_source==0) {
+    //return AudioSound::BAD;
+    return AudioSound::READY;
+  }
+
+  _manager->make_current();
+
+  alGetError(); // clear errors
+  alGetSourcei(_source,AL_SOURCE_STATE,&status);
+  al_audio_errcheck("alGetSourcei(_source,AL_SOURCE_STATE)");
+
+  if (status == AL_PLAYING/* || status == AL_PAUSED*/) {
+    return AudioSound::PLAYING;
+  } else {
+    return AudioSound::READY;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: OpenALAudioSound::cleanup
+//       Access: Private
+//  Description: Called to release any resources associated with the
+//               sound.
+////////////////////////////////////////////////////////////////////
+void OpenALAudioSound::
+cleanup() {
+  if (_source) {
+    stop();
+    /*
+    if (OpenALAudioManager::_openal_active) {
+      // delete the source
+      _manager->make_current();
+      alGetError(); // clear errors
+      alDeleteSources(1,&_source);
+      al_audio_errcheck("alDeleteSources()");
+    }
+    _source = 0;
+    */
+  }
+}
+
+#endif //]

+ 194 - 0
panda/src/audiotraits/openalAudioSound.h

@@ -0,0 +1,194 @@
+// Filename: openalAudioSound.h
+// Created by:  Ben Buchwald <[email protected]>
+//
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 __OPENAL_AUDIO_SOUND_H__
+#define __OPENAL_AUDIO_SOUND_H__
+
+#include "pandabase.h"
+
+#ifdef HAVE_OPENAL //[
+
+#include "audioSound.h"
+
+#include <al.h>
+#include <alc.h>
+#include <alut.h>
+
+class EXPCL_OPENAL_AUDIO OpenALAudioSound : public AudioSound {
+  friend class OpenALAudioManager;
+
+public:
+
+  ~OpenALAudioSound();
+            
+  // For best compatability, set the loop_count, start_time,
+  // volume, and balance, prior to calling play().  You may
+  // set them while they're playing, but it's implementation
+  // specific whether you get the results.
+  void play();
+  void stop();
+            
+  // loop: false = play once; true = play forever.
+  // inits to false.
+  void set_loop(bool loop=true);
+  bool get_loop() const;
+            
+  // loop_count: 0 = forever; 1 = play once; n = play n times.
+  // inits to 1.
+  void set_loop_count(unsigned long loop_count=1);
+  unsigned long get_loop_count() const;
+            
+  // 0 = begining; length() = end.
+  // inits to 0.0.
+  void set_time(float time=0.0);
+  float get_time() const;
+            
+  // 0 = minimum; 1.0 = maximum.
+  // inits to 1.0.
+  void set_volume(float volume=1.0);
+  float get_volume() const;
+            
+  // -1.0 is hard left
+  // 0.0 is centered
+  // 1.0 is hard right
+  // inits to 0.0.
+  void set_balance(float balance_right=0.0);
+  float get_balance() const;
+
+  // play_rate is any positive float value.
+  // inits to 1.0.
+  void set_play_rate(float play_rate=1.0f);
+  float get_play_rate() const;
+
+  // inits to manager's state.
+  void set_active(bool active=true);
+  bool get_active() const;
+
+  // This is the string that throw_event() will throw
+  // when the sound finishes playing.  It is not triggered
+  // when the sound is stopped with stop().
+  void set_finished_event(const string& event);
+  const string& get_finished_event() const;
+
+  const string &get_name() const;
+            
+  // return: playing time in seconds.
+  float length() const;
+
+  // Controls the position of this sound's emitter.
+  // pos is a pointer to an xyz triplet of the emitter's position.
+  // vel is a pointer to an xyz triplet of the emitter's velocity.
+  void set_3d_attributes(float px, float py, float pz, float vx, float vy, float vz);
+  void get_3d_attributes(float *px, float *py, float *pz, float *vx, float *vy, float *vz);
+
+  void set_3d_min_distance(float dist);
+  float get_3d_min_distance() const;
+
+  void set_3d_max_distance(float dist);
+  float get_3d_max_distance() const;
+            
+  void set_3d_drop_off_factor(float factor);
+  float get_3d_drop_off_factor() const;
+
+  AudioSound::SoundStatus status() const;
+
+  void finished();
+
+private:
+  OpenALAudioSound(OpenALAudioManager* manager, OpenALAudioManager::SoundData *sd,
+                  string file_name, bool positional);
+  void cleanup();
+
+private:
+  PT(OpenALAudioManager::SoundData) _sd;
+  ALuint _buffer;
+  ALuint _source;
+  PT(OpenALAudioManager) _manager;
+
+  float _volume; // 0..1.0
+  float _balance; // -1..1
+  float _play_rate; // 0..1.0
+
+  float _pause_time;
+
+  bool _positional;
+  ALfloat _location[3];
+  ALfloat _velocity[3];
+
+  float _min_dist;
+  float _max_dist;
+  float _drop_off_factor;
+
+  mutable float _length; // in seconds.
+  unsigned long _loop_count;
+
+  // This is the string that throw_event() will throw
+  // when the sound finishes playing.  It is not triggered
+  // when the sound is stopped with stop().
+  string _finished_event;
+
+  string _file_name;
+
+  // _active is for things like a 'turn off sound effects' in
+  // a preferences pannel.
+  // _active is not about whether a sound is currently playing.
+  // Use status() for info on whether the sound is playing.
+  bool _active;
+
+  // _paused is not like the Pause button on a cd/dvd player.
+  // It is used as a flag to say that the sound was looping when
+  // itwas set inactive.
+  bool _paused;
+
+ public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    AudioSound::init_type();
+    register_type(_type_handle, "OpenALAudioSound", AudioSound::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;
+
+  ////////////////////////////////////////////////////////////
+  //DONE
+  ////////////////////////////////////////////////////////////
+};
+
+#include "openalAudioSound.I"
+
+#endif //]
+
+#endif /* __OPENAL_AUDIO_SOUND_H__ */
+
+
+
+
+

+ 1 - 0
panda/src/audiotraits/openal_audio_composite.cxx

@@ -0,0 +1 @@
+#include "openal_audio_composite1.cxx"

+ 5 - 0
panda/src/audiotraits/openal_audio_composite1.cxx

@@ -0,0 +1,5 @@
+
+#include "config_openalAudio.cxx"
+#include "openalAudioManager.cxx"
+#include "openalAudioSound.cxx"
+      

+ 15 - 15
panda/src/grutil/movieTexture.cxx

@@ -19,6 +19,7 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "movieVideo.h"
 #include "movieVideo.h"
+#include "movieVideoCursor.h"
 #include "movieTexture.h"
 #include "movieTexture.h"
 #include "clockObject.h"
 #include "clockObject.h"
 #include "config_gobj.h"
 #include "config_gobj.h"
@@ -49,9 +50,7 @@ MovieTexture::
 MovieTexture(PT(MovieVideo) video) : 
 MovieTexture(PT(MovieVideo) video) : 
   Texture(video->get_name())
   Texture(video->get_name())
 {
 {
-  // It is necessary to copy the video, because
-  // the cull thread will be accessing it.
-  do_load_one(video->make_copy(), NULL, 0);
+  do_load_one(video->open(), NULL, 0);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -112,8 +111,8 @@ MovieTexture(const MovieTexture &copy) :
   // Since 'make_copy' can be a slow operation, 
   // Since 'make_copy' can be a slow operation, 
   // I release the read lock before calling make_copy.
   // I release the read lock before calling make_copy.
   
   
-  pvector<MovieVideo *> color;
-  pvector<MovieVideo *> alpha;
+  pvector<MovieVideoCursor *> color;
+  pvector<MovieVideoCursor *> alpha;
   {
   {
     CDReader copy_cdata(copy._cycler);
     CDReader copy_cdata(copy._cycler);
     color.resize(copy_cdata->_pages.size());
     color.resize(copy_cdata->_pages.size());
@@ -129,10 +128,10 @@ MovieTexture(const MovieTexture &copy) :
     cdata->_pages.resize(color.size());
     cdata->_pages.resize(color.size());
     for (int i=0; i<(int)(color.size()); i++) {
     for (int i=0; i<(int)(color.size()); i++) {
       if (color[i]) {
       if (color[i]) {
-        cdata->_pages[i]._color = color[i]->make_copy();
+        cdata->_pages[i]._color = color[i]->get_source()->open();
       }
       }
       if (alpha[i]) {
       if (alpha[i]) {
-        cdata->_pages[i]._alpha = color[i]->make_copy();
+        cdata->_pages[i]._alpha = alpha[i]->get_source()->open();
       }
       }
     }
     }
     recalculate_image_properties(cdata);
     recalculate_image_properties(cdata);
@@ -205,7 +204,7 @@ recalculate_image_properties(CDWriter &cdata) {
   double len = 0.0;
   double len = 0.0;
   
   
   for (int i=0; i<_z_size; i++) {
   for (int i=0; i<_z_size; i++) {
-    MovieVideo *t = cdata->_pages[i]._color;
+    MovieVideoCursor *t = cdata->_pages[i]._color;
     if (t) {
     if (t) {
       if (t->size_x() > x_max) x_max = t->size_x();
       if (t->size_x() > x_max) x_max = t->size_x();
       if (t->size_y() > y_max) y_max = t->size_y();
       if (t->size_y() > y_max) y_max = t->size_y();
@@ -259,15 +258,15 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
     record->add_dependent_file(fullpath);
     record->add_dependent_file(fullpath);
   }
   }
 
 
-  PT(MovieVideo) color;
-  PT(MovieVideo) alpha;
+  PT(MovieVideoCursor) color;
+  PT(MovieVideoCursor) alpha;
   
   
-  color = MovieVideo::load(fullpath);
+  color = MovieVideo::get(fullpath)->open();
   if (color == 0) {
   if (color == 0) {
     return false;
     return false;
   }
   }
   if (!alpha_fullpath.empty()) {
   if (!alpha_fullpath.empty()) {
-    alpha = MovieVideo::load(alpha_fullpath);
+    alpha = MovieVideo::get(alpha_fullpath)->open();
     if (alpha == 0) {
     if (alpha == 0) {
       return false;
       return false;
     }
     }
@@ -296,6 +295,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
   set_loaded_from_image();
   set_loaded_from_image();
   set_loop(true);
   set_loop(true);
   play();
   play();
+  return true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -304,7 +304,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
 //  Description: Loads movie objects into the texture.
 //  Description: Loads movie objects into the texture.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool MovieTexture::
 bool MovieTexture::
-do_load_one(PT(MovieVideo) color, PT(MovieVideo) alpha, int z) {
+do_load_one(PT(MovieVideoCursor) color, PT(MovieVideoCursor) alpha, int z) {
   
   
   {
   {
     CDWriter cdata(_cycler);
     CDWriter cdata(_cycler);
@@ -391,8 +391,8 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
   }
   }
   
   
   for (int i=0; i<((int)(cdata->_pages.size())); i++) {
   for (int i=0; i<((int)(cdata->_pages.size())); i++) {
-    MovieVideo *color = cdata->_pages[i]._color;
-    MovieVideo *alpha = cdata->_pages[i]._alpha;
+    MovieVideoCursor *color = cdata->_pages[i]._color;
+    MovieVideoCursor *alpha = cdata->_pages[i]._alpha;
     if (color && alpha) {
     if (color && alpha) {
       if ((offset >= color->next_start())||
       if ((offset >= color->next_start())||
           ((offset < color->last_start()) && (color->can_seek()))) {
           ((offset < color->last_start()) && (color->can_seek()))) {

+ 3 - 3
panda/src/grutil/movieTexture.h

@@ -68,13 +68,13 @@ protected:
                            bool header_only, BamCacheRecord *record);
                            bool header_only, BamCacheRecord *record);
   virtual bool do_load_one(const PNMImage &pnmimage, const string &name,
   virtual bool do_load_one(const PNMImage &pnmimage, const string &name,
                            int z, int n);
                            int z, int n);
-  bool do_load_one(PT(MovieVideo) color, PT(MovieVideo) alpha, int z);
+  bool do_load_one(PT(MovieVideoCursor) color, PT(MovieVideoCursor) alpha, int z);
 
 
   class VideoPage {
   class VideoPage {
   public:
   public:
     VideoPage();
     VideoPage();
-    PT(MovieVideo) _color;
-    PT(MovieVideo) _alpha;
+    PT(MovieVideoCursor) _color;
+    PT(MovieVideoCursor) _alpha;
   };
   };
   
   
   typedef pvector<VideoPage> Pages;
   typedef pvector<VideoPage> Pages;

+ 15 - 0
panda/src/movies/Sources.pp

@@ -12,28 +12,43 @@
 
 
   #define SOURCES \
   #define SOURCES \
 	movieAudio.h movieAudio.I \
 	movieAudio.h movieAudio.I \
+	movieAudioCursor.h movieAudioCursor.I \
 	movieVideo.h movieVideo.I \
 	movieVideo.h movieVideo.I \
+	movieVideoCursor.h movieVideoCursor.I \
         inkblotVideo.h inkblotVideo.I \
         inkblotVideo.h inkblotVideo.I \
+        inkblotVideoCursor.h inkblotVideoCursor.I \
         ffmpegVideo.h ffmpegVideo.I \
         ffmpegVideo.h ffmpegVideo.I \
+        ffmpegVideoCursor.h ffmpegVideoCursor.I \
         ffmpegAudio.h ffmpegAudio.I \
         ffmpegAudio.h ffmpegAudio.I \
+        ffmpegAudioCursor.h ffmpegAudioCursor.I \
         ffmpegVirtualFile.h ffmpegVirtualFile.I \
         ffmpegVirtualFile.h ffmpegVirtualFile.I \
 	config_movies.h
 	config_movies.h
     
     
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
         movieVideo.cxx \
         movieVideo.cxx \
+        movieVideoCursor.cxx \
         movieAudio.cxx \
         movieAudio.cxx \
+        movieAudioCursor.cxx \
         inkblotVideo.cxx \
         inkblotVideo.cxx \
+        inkblotVideoCursor.cxx \
         ffmpegVideo.cxx \
         ffmpegVideo.cxx \
+        ffmpegVideoCursor.cxx \
         ffmpegAudio.cxx \
         ffmpegAudio.cxx \
+        ffmpegAudioCursor.cxx \
         ffmpegVirtualFile.cxx \
         ffmpegVirtualFile.cxx \
         config_movies.cxx
         config_movies.cxx
     
     
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
 	movieAudio.h movieAudio.I \
 	movieAudio.h movieAudio.I \
+	movieAudioCursor.h movieAudioCursor.I \
 	movieVideo.h movieVideo.I \
 	movieVideo.h movieVideo.I \
+	movieVideoCursor.h movieVideoCursor.I \
         inkblotVideo.h inkblotVideo.I \
         inkblotVideo.h inkblotVideo.I \
+        inkblotVideoCursor.h inkblotVideoCursor.I \
         ffmpegVideo.h ffmpegVideo.I \
         ffmpegVideo.h ffmpegVideo.I \
+        ffmpegVideoCursor.h ffmpegVideoCursor.I \
         ffmpegAudio.h ffmpegAudio.I \
         ffmpegAudio.h ffmpegAudio.I \
+        ffmpegAudioCursor.h ffmpegAudioCursor.I \
         ffmpegVirtualFile.h ffmpegVirtualFile.I \
         ffmpegVirtualFile.h ffmpegVirtualFile.I \
 	config_movies.h
 	config_movies.h
 
 

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

@@ -47,11 +47,16 @@ init_libmovies() {
   initialized = true;
   initialized = true;
 
 
   MovieVideo::init_type();
   MovieVideo::init_type();
+  MovieVideoCursor::init_type();
   MovieAudio::init_type();
   MovieAudio::init_type();
+  MovieAudioCursor::init_type();
   InkblotVideo::init_type();
   InkblotVideo::init_type();
+  InkblotVideoCursor::init_type();
 #ifdef HAVE_FFMPEG
 #ifdef HAVE_FFMPEG
   FfmpegVideo::init_type();
   FfmpegVideo::init_type();
+  FfmpegVideoCursor::init_type();
   FfmpegAudio::init_type();
   FfmpegAudio::init_type();
+  FfmpegAudioCursor::init_type();
   av_register_all();
   av_register_all();
   FfmpegVirtualFile::register_protocol();
   FfmpegVirtualFile::register_protocol();
 #endif
 #endif

+ 10 - 0
panda/src/movies/config_movies.h

@@ -24,11 +24,21 @@
 #include "configVariableEnum.h"
 #include "configVariableEnum.h"
 #include "configVariableDouble.h"
 #include "configVariableDouble.h"
 #include "dconfig.h"
 #include "dconfig.h"
+
 #include "movieVideo.h"
 #include "movieVideo.h"
+#include "movieVideoCursor.h"
+
 #include "movieAudio.h"
 #include "movieAudio.h"
+#include "movieAudioCursor.h"
+
 #include "inkblotVideo.h"
 #include "inkblotVideo.h"
+#include "inkblotVideoCursor.h"
+
 #include "ffmpegVideo.h"
 #include "ffmpegVideo.h"
+#include "ffmpegVideoCursor.h"
+
 #include "ffmpegAudio.h"
 #include "ffmpegAudio.h"
+#include "ffmpegAudioCursor.h"
 
 
 ConfigureDecl(config_movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
 ConfigureDecl(config_movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
 NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);
 NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES);

+ 7 - 32
panda/src/movies/ffmpegAudio.cxx

@@ -19,8 +19,7 @@
 #ifdef HAVE_FFMPEG
 #ifdef HAVE_FFMPEG
 
 
 #include "ffmpegAudio.h"
 #include "ffmpegAudio.h"
-#include "avcodec.h"
-#include "avformat.h"
+#include "ffmpegAudioCursor.h"
 
 
 TypeHandle FfmpegAudio::_type_handle;
 TypeHandle FfmpegAudio::_type_handle;
 
 
@@ -32,7 +31,7 @@ TypeHandle FfmpegAudio::_type_handle;
 FfmpegAudio::
 FfmpegAudio::
 FfmpegAudio(const Filename &name) :
 FfmpegAudio(const Filename &name) :
   MovieAudio(name),
   MovieAudio(name),
-  _filename(name)
+  _specified_filename(name)
 {
 {
 }
 }
 
 
@@ -46,37 +45,13 @@ FfmpegAudio::
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: FfmpegAudio::read_samples
-//       Access: Public, Virtual
-//  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 FfmpegAudio::
-read_samples(int n, PN_int16 *data) {
-
-  // This is the null implementation, which generates pure silence.
-  // Normally, this method will be overridden by a subclass.
-
-  if (n <= 0) {
-    return;
-  }
-
-  for (int i=0; i<n; i++) {
-    data[i] = 0;
-  }
-  _samples_read += n;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FfmpegAudio::make_copy
+//     Function: FfmpegAudio::open
 //       Access: Published, Virtual
 //       Access: Published, Virtual
-//  Description: Make a copy of this MovieAudio with its own cursor.
+//  Description: Open this audio, returning a MovieAudioCursor
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(MovieAudio) FfmpegAudio::
-make_copy() const {
-  return new FfmpegAudio(_filename);
+PT(MovieAudioCursor) FfmpegAudio::
+open() {
+  return new FfmpegAudioCursor(this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 7 - 10
panda/src/movies/ffmpegAudio.h

@@ -20,10 +20,9 @@
 #define FFMPEGAUDIO_H
 #define FFMPEGAUDIO_H
 #ifdef HAVE_FFMPEG
 #ifdef HAVE_FFMPEG
 
 
-#include "pandabase.h"
-#include "namable.h"
-#include "texture.h"
-#include "pointerTo.h"
+#include "movieAudio.h"
+
+class FfmpegAudioCursor;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : FfmpegAudio
 //       Class : FfmpegAudio
@@ -34,13 +33,11 @@ class EXPCL_PANDA_MOVIES FfmpegAudio : public MovieAudio {
 PUBLISHED:
 PUBLISHED:
   FfmpegAudio(const Filename &name);
   FfmpegAudio(const Filename &name);
   virtual ~FfmpegAudio();
   virtual ~FfmpegAudio();
-  virtual PT(MovieAudio) make_copy() const;
+  virtual PT(MovieAudioCursor) open();
 
 
-public:
-  virtual void read_samples(int n, PN_int16 *data);
-  
-protected:
-  Filename _filename;
+ private:
+  Filename _specified_filename;
+  friend class FfmpegAudioCursor;
   
   
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

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

@@ -0,0 +1,18 @@
+// Filename: ffmpegAudioCursor.I
+// Created by: jyelon (01Aug2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 264 - 0
panda/src/movies/ffmpegAudioCursor.cxx

@@ -0,0 +1,264 @@
+// Filename: ffmpegAudioCursor.cxx
+// Created by: jyelon (01Aug2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] 
+//
+////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_FFMPEG
+
+#include "ffmpegAudioCursor.h"
+#include "avcodec.h"
+#include "avformat.h"
+
+TypeHandle FfmpegAudioCursor::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegAudioCursor::Constructor
+//       Access: Protected
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+FfmpegAudioCursor::
+FfmpegAudioCursor(PT(FfmpegAudio) src) :
+  MovieAudioCursor((MovieAudio*)(FfmpegAudio*)src),
+  _filename(src->_specified_filename)
+{
+  string url = "pandavfs:";
+  url += _filename;
+  if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
+    cleanup();
+    return;
+  }
+  
+  if (av_find_stream_info(_format_ctx)<0) {
+    cleanup();
+    return;
+  }
+  
+  // Find the audio stream
+  for(int i=0; i<_format_ctx->nb_streams; i++) {
+    if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) {
+      _audio_index = i;
+      _audio_ctx = _format_ctx->streams[i]->codec;
+      _audio_timebase = av_q2d(_format_ctx->streams[i]->time_base);
+      _audio_rate = _audio_ctx->sample_rate;
+      _audio_channels = _audio_ctx->channels;
+    }
+  }
+  
+  if (_audio_ctx == 0) {
+    cleanup();
+    return;
+  }
+  
+  AVCodec *pAudioCodec=avcodec_find_decoder(_audio_ctx->codec_id);
+  if(pAudioCodec == 0) {
+    cleanup();
+    return;
+  }
+  if(avcodec_open(_audio_ctx, pAudioCodec)<0) {
+    cleanup();
+    return;
+  }
+
+  _length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
+  _can_seek = true;
+  _can_seek_fast = true;
+
+  _packet = new AVPacket;
+  _buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE / 2;
+  _buffer_alloc = new PN_int16[_buffer_size + 128];
+  if ((_packet == 0)||(_buffer_alloc == 0)) {
+    cleanup();
+    return;
+  }
+  memset(_packet, 0, sizeof(AVPacket));
+  
+  // Align the buffer to a 16-byte boundary
+  // The ffmpeg codec likes this, because it uses SSE/SSE2.
+  _buffer = _buffer_alloc;
+  while (((int)_buffer) & 15) {
+    _buffer += 1;
+  }
+  
+  fetch_packet();
+  _initial_dts = _packet->dts;
+  _last_seek = 0;
+  _samples_read = 0;
+  _buffer_head = 0;
+  _buffer_tail = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegAudioCursor::Destructor
+//       Access: Protected, Virtual
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+FfmpegAudioCursor::
+~FfmpegAudioCursor() {
+  cleanup();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegAudioCursor::cleanup
+//       Access: Public
+//  Description: Reset to a standard inactive state.
+////////////////////////////////////////////////////////////////////
+void FfmpegAudioCursor::
+cleanup() {
+  if (_packet) {
+    if (_packet->data) {
+      av_free_packet(_packet);
+    }
+    delete _packet;
+    _packet = 0;
+  }
+  if (_buffer_alloc) {
+    delete[] _buffer_alloc;
+    _buffer_alloc = 0;
+    _buffer = 0;
+  }
+  if (_audio_ctx) {
+    avcodec_close(_audio_ctx);
+    _audio_ctx = 0;
+  }
+  if (_format_ctx) {
+    av_close_input_file(_format_ctx);
+    _format_ctx = 0;
+  }
+  _audio_ctx = 0;
+  _audio_index = -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegAudioCursor::fetch_packet
+//       Access: Protected
+//  Description: Fetches an audio packet and stores it in the 
+//               packet buffer.  Also sets packet_size and packet_data.
+////////////////////////////////////////////////////////////////////
+void FfmpegAudioCursor::
+fetch_packet() {
+  if (_packet->data) {
+    av_free_packet(_packet);
+  }
+  while (av_read_frame(_format_ctx, _packet) >= 0) {
+    if (_packet->stream_index == _audio_index) {
+      _packet_size = _packet->size;
+      _packet_data = _packet->data;
+      return;
+    }
+    av_free_packet(_packet);
+  }
+  _packet->data = 0;
+  _packet_size = 0;
+  _packet_data = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegAudioCursor::reload_buffer
+//       Access: Protected
+//  Description: Reloads the audio buffer by decoding audio packets
+//               until one of those audio packets finally yields
+//               some samples.  If we encounter the end of the
+//               stream, we synthesize silence.
+////////////////////////////////////////////////////////////////////
+void FfmpegAudioCursor::
+reload_buffer() {
+  while (_buffer_head == _buffer_tail) {
+    // If we're out of packets, generate silence.
+    if (_packet->data == 0) {
+      _buffer_head = 0;
+      _buffer_tail = _buffer_size;
+      memset(_buffer, 0, _buffer_size * 2);
+      return;
+    } else if (_packet_size > 0) {
+      int bufsize = _buffer_size * 2;
+      int len = avcodec_decode_audio(_audio_ctx, _buffer, &bufsize, 
+                                     _packet_data, _packet_size);
+      if (len < 0) {
+        break;
+      }
+      _packet_data += len;
+      _packet_size -= len;
+      if (bufsize > 0) {
+        _buffer_head = 0;
+        _buffer_tail = (bufsize/2);
+        return;
+      }
+    } else {
+      fetch_packet();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegAudioCursor::seek
+//       Access: Protected
+//  Description: Seeks to a target location.  Afterward, the
+//               packet_time is guaranteed to be less than or 
+//               equal to the specified time.
+////////////////////////////////////////////////////////////////////
+void FfmpegAudioCursor::
+seek(double t) {
+  movies_cat.error() << "Seek not implemented yet.\n";
+  //  PN_int64 target_ts = (PN_int64)(t / _audio_timebase);
+  //  if (target_ts < (PN_int64)(_initial_dts)) {
+  //    // Attempts to seek before the first packet will fail.
+  //    target_ts = _initial_dts;
+  //  }
+  //  if (av_seek_frame(_format_ctx, _audio_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
+  //    if (t >= _packet_time) {
+  //      return;
+  //    }
+  //    movies_cat.error() << "Seek failure. Shutting down movie.\n";
+  //    cleanup();
+  //    _packet_time = t;
+  //    return;
+  //  }
+  //  fetch_packet(t);
+  //  if (_packet_time > t) {
+  //    _packet_time = t;
+  //  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegAudioCursor::read_samples
+//       Access: Public, Virtual
+//  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 FfmpegAudioCursor::
+read_samples(int n, PN_int16 *data) {
+  int desired = n * _audio_channels;
+  while (desired) {
+    if (_buffer_head == _buffer_tail) {
+      reload_buffer();
+    }
+    int available = _buffer_tail - _buffer_head;
+    int ncopy = (desired > available) ? available : desired;
+    if (ncopy) {
+      memcpy(data, _buffer + _buffer_head, ncopy * 2);
+      data += ncopy;
+      desired -= ncopy;
+      _buffer_head += ncopy;
+    }
+  }
+  _samples_read += n;
+}
+
+////////////////////////////////////////////////////////////////////
+
+#endif // HAVE_FFMPEG

+ 88 - 0
panda/src/movies/ffmpegAudioCursor.h

@@ -0,0 +1,88 @@
+// Filename: ffmpegAudioCursor.h
+// Created by: jyelon (01Aug2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 FFMPEGAUDIOCURSOR_H
+#define FFMPEGAUDIOCURSOR_H
+#ifdef HAVE_FFMPEG
+
+#include "pandabase.h"
+#include "namable.h"
+#include "texture.h"
+#include "pointerTo.h"
+
+struct AVFormatContext;
+struct AVCodecContext;
+struct AVStream;
+struct AVPacket;
+
+////////////////////////////////////////////////////////////////////
+//       Class : FfmpegAudioCursor
+// Description : A stream that generates a sequence of audio samples.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES FfmpegAudioCursor : public MovieAudioCursor {
+
+PUBLISHED:
+  FfmpegAudioCursor(PT(FfmpegAudio) src);
+  virtual ~FfmpegAudioCursor();
+  virtual void seek(double offset);
+  
+public:
+  virtual void read_samples(int n, PN_int16 *data);
+  
+protected:
+  void fetch_packet();
+  void reload_buffer();
+  void cleanup();
+  Filename _filename;
+  int _initial_dts;
+  AVPacket *_packet;
+  int            _packet_size;
+  unsigned char *_packet_data;
+  AVFormatContext *_format_ctx;
+  AVCodecContext  *_audio_ctx;
+  int _audio_index;
+  double _audio_timebase;
+
+  PN_int16 *_buffer;
+  int       _buffer_size;
+  PN_int16 *_buffer_alloc;
+  int       _buffer_head;
+  int       _buffer_tail;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "FfmpegAudioCursor",
+                  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 "ffmpegAudioCursor.I"
+
+#endif // HAVE_FFMPEG
+#endif // FFMPEG_AUDIO.H

+ 7 - 259
panda/src/movies/ffmpegVideo.cxx

@@ -20,18 +20,6 @@
 
 
 #include "ffmpegVideo.h"
 #include "ffmpegVideo.h"
 #include "config_movies.h"
 #include "config_movies.h"
-#include "avcodec.h"
-#include "avformat.h"
-
-// Earlier versions of ffmpeg didn't define this symbol.
-#ifndef PIX_FMT_BGRA
-#ifdef WORDS_BIGENDIAN
-#define PIX_FMT_BGRA PIX_FMT_BGR32_1
-#else
-#define PIX_FMT_BGRA PIX_FMT_RGBA32
-#endif
-#endif  // PIX_FMT_BGRA
-
 
 
 TypeHandle FfmpegVideo::_type_handle;
 TypeHandle FfmpegVideo::_type_handle;
 
 
@@ -43,71 +31,8 @@ TypeHandle FfmpegVideo::_type_handle;
 FfmpegVideo::
 FfmpegVideo::
 FfmpegVideo(const Filename &name) :
 FfmpegVideo(const Filename &name) :
   MovieVideo(name),
   MovieVideo(name),
-  _filename(name),
-  _format_ctx(0),
-  _video_ctx(0),
-  _video_index(-1),
-  _frame(0),
-  _frame_out(0),
-  _min_fseek(3.0)
+  _specified_filename(name)
 {
 {
-  string url = "pandavfs:";
-  url += name;
-  if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
-    cleanup();
-    return;
-  }
-  
-  if (av_find_stream_info(_format_ctx)<0) {
-    cleanup();
-    return;
-  }
-  
-  // Find the video stream
-  for(int i=0; i<_format_ctx->nb_streams; i++) {
-    if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
-      _video_index = i;
-      _video_ctx = _format_ctx->streams[i]->codec;
-      _video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
-    }
-  }
-  
-  if (_video_ctx == 0) {
-    cleanup();
-    return;
-  }
-
-  AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
-  if(pVideoCodec == 0) {
-    cleanup();
-    return;
-  }
-  if(avcodec_open(_video_ctx, pVideoCodec)<0) {
-    cleanup();
-    return;
-  }
-
-  _size_x = _video_ctx->width;
-  _size_y = _video_ctx->height;
-  _num_components = 3; // Don't know how to implement RGBA movies yet.
-  _length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
-  _can_seek = true;
-  _can_seek_fast = true;
-
-  _packet = new AVPacket;
-  _frame = avcodec_alloc_frame();
-  _frame_out = avcodec_alloc_frame();
-  if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
-    cleanup();
-    return;
-  }
-  memset(_packet, 0, sizeof(AVPacket));
-  
-  fetch_packet(0.0);
-  _initial_dts = _packet->dts;
-  _packet_time = 0.0;
-  _last_start = -1.0;
-  _next_start = 0.0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -117,196 +42,19 @@ FfmpegVideo(const Filename &name) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 FfmpegVideo::
 FfmpegVideo::
 ~FfmpegVideo() {
 ~FfmpegVideo() {
-  cleanup();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FfmpegVideo::cleanup
-//       Access: Public
-//  Description: Reset to a standard inactive state.
-////////////////////////////////////////////////////////////////////
-void FfmpegVideo::
-cleanup() {
-  _frame_out->data[0] = 0;
-  if (_packet) {
-    if (_packet->data) {
-      av_free_packet(_packet);
-    }
-    delete _packet;
-    _packet = 0;
-  }
-  if (_format_ctx) {
-    av_close_input_file(_format_ctx);
-    _format_ctx = 0;
-  }
-  if (_frame) {
-    av_free(_frame);
-    _frame = 0;
-  }
-  if (_frame_out) {
-    av_free(_frame_out);
-    _frame_out = 0;
-  }
-  _video_ctx = 0;
-  _video_index = -1;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FfmpegVideo::export_frame
-//       Access: Public, Virtual
-//  Description: Exports the contents of the frame buffer into the
-//               user's target buffer.
-////////////////////////////////////////////////////////////////////
-void FfmpegVideo::
-export_frame(unsigned char *data, bool bgra) {
-  if (bgra) {
-    _frame_out->data[0] = data + ((_size_y - 1) * _size_x * 4);
-    _frame_out->linesize[0] = _size_x * -4;
-    img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA, 
-                (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
-  } else {
-    _frame_out->data[0] = data + ((_size_y - 1) * _size_x * 3);
-    _frame_out->linesize[0] = _size_x * -3;
-    img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24, 
-                (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
-  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: FfmpegVideo::fetch_packet
-//       Access: Protected
-//  Description: Fetches a video packet and stores it in the 
-//               packet buffer.  Sets packet_time to the packet's
-//               timestamp.  If a packet could not be read, the
-//               packet is cleared and the packet_time is set to
-//               the specified default value.
-////////////////////////////////////////////////////////////////////
-void FfmpegVideo::
-fetch_packet(double default_time) {
-  if (_packet->data) {
-    av_free_packet(_packet);
-  }
-  while (av_read_frame(_format_ctx, _packet) >= 0) {
-    if (_packet->stream_index == _video_index) {
-      _packet_time = _packet->dts * _video_timebase;
-      return;
-    }
-    av_free_packet(_packet);
-  }
-  _packet->data = 0;
-  _packet_time = default_time;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FfmpegVideo::fetch_frame
-//       Access: Protected
-//  Description: Fetches a frame from the stream and stores it in
-//               the frame buffer.  Sets last_start and next_start
-//               to indicate the extents of the frame.
-////////////////////////////////////////////////////////////////////
-void FfmpegVideo::
-fetch_frame() {
-  int finished = 0;
-  _last_start = _packet_time;
-  while (!finished && _packet->data) {
-    avcodec_decode_video(_video_ctx, _frame,
-                         &finished, _packet->data, _packet->size);
-    fetch_packet(_last_start + 1.0);
-  }
-  _next_start = _packet_time;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FfmpegVideo::seek
-//       Access: Protected
-//  Description: Seeks to a target location.  Afterward, the
-//               packet_time is guaranteed to be less than or 
-//               equal to the specified time.
-////////////////////////////////////////////////////////////////////
-void FfmpegVideo::
-seek(double t) {
-  long long target_ts = (long long)(t / _video_timebase);
-  if (target_ts < (long long)(_initial_dts)) {
-    // Attempts to seek before the first packet will fail.
-    target_ts = _initial_dts;
-  }
-  if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
-    if (t >= _packet_time) {
-      return;
-    }
-    movies_cat.error() << "Seek failure. Shutting down movie.\n";
-    cleanup();
-    _packet_time = t;
-    return;
-  }
-  fetch_packet(t);
-  if (_packet_time > t) {
-    _packet_time = t;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FfmpegVideo::fetch_into_buffer
-//       Access: Public, Virtual
-//  Description: See MovieVideo::fetch_into_buffer.
-////////////////////////////////////////////////////////////////////
-void FfmpegVideo::
-fetch_into_buffer(double time, unsigned char *data, bool bgra) {
-  
-  // If there was an error at any point, synthesize black.
-  if (_format_ctx==0) {
-    if (data) {
-      memset(data,0,size_x()*size_y()*(bgra?4:3));
-    }
-    _last_start = time;
-    _next_start = time + 1.0;
-    return;
-  }
-  
-  if (time < _last_start) {
-    // Time is in the past.
-    seek(time);
-    while (_packet_time <= time) {
-      fetch_frame();
-    }
-  } else if (time < _next_start) {
-    // Time is in the present: already have the frame.
-  } else if (time < _next_start + _min_fseek) {
-    // Time is in the near future.
-    while (_packet_time <= time) {
-      fetch_frame();
-    }
-  } else {
-    // Time is in the far future.  Seek forward, then read.
-    // There's a danger here: because keyframes are spaced
-    // unpredictably, trying to seek forward could actually
-    // move us backward in the stream!  This must be avoided.
-    // So the rule is, try the seek.  If it hurts us by moving
-    // us backward, we increase the minimum threshold distance
-    // for forward-seeking in the future.
-    
-    double base = _packet_time;
-    seek(time);
-    if (_packet_time < base) {
-      _min_fseek += (base - _packet_time);
-    }
-    while (_packet_time <= time) {
-      fetch_frame();
-    }
-  }
-  export_frame(data, bgra);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: FfmpegVideo::make_copy
+//     Function: FfmpegVideo::open
 //       Access: Published, Virtual
 //       Access: Published, Virtual
-//  Description: Make a copy of this MovieVideo with its own cursor.
+//  Description: Open this video, returning a MovieVideoCursor.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(MovieVideo) FfmpegVideo::
-make_copy() const {
-  return new FfmpegVideo(_filename);
+PT(MovieVideoCursor) FfmpegVideo::
+open() {
+  return new FfmpegVideoCursor(this);
 }
 }
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #endif // HAVE_FFMPEG
 #endif // HAVE_FFMPEG

+ 6 - 33
panda/src/movies/ffmpegVideo.h

@@ -20,15 +20,9 @@
 #define FFMPEGVIDEO_H
 #define FFMPEGVIDEO_H
 #ifdef HAVE_FFMPEG
 #ifdef HAVE_FFMPEG
 
 
-#include "pandabase.h"
-#include "texture.h"
-#include "pointerTo.h"
+#include "movieVideo.h"
 
 
-struct AVFormatContext;
-struct AVCodecContext;
-struct AVStream;
-struct AVPacket;
-struct AVFrame;
+class FfmpegVideoCursor;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : FfmpegVideo
 //       Class : FfmpegVideo
@@ -39,32 +33,11 @@ class EXPCL_PANDA_MOVIES FfmpegVideo : public MovieVideo {
  PUBLISHED:
  PUBLISHED:
   FfmpegVideo(const Filename &name);
   FfmpegVideo(const Filename &name);
   virtual ~FfmpegVideo();
   virtual ~FfmpegVideo();
-  virtual PT(MovieVideo) make_copy() const;
+  virtual PT(MovieVideoCursor) open();
   
   
- public:
-  virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
-
- protected:
-  INLINE double get_time_correction();
-  INLINE void update_time_correction(double diff);
-  void fetch_packet(double default_time);
-  void fetch_frame();
-  void seek(double t);
-  void export_frame(unsigned char *data, bool bgra);
-  void cleanup();
-  
-  Filename _filename;
-  AVPacket *_packet;
-  double _packet_time;
-  AVFormatContext *_format_ctx;
-  AVCodecContext *_video_ctx;
-  AVCodecContext *_audio_ctx;
-  int    _video_index;
-  double _video_timebase;
-  AVFrame *_frame;
-  AVFrame *_frame_out;
-  int _initial_dts;
-  double _min_fseek;
+ private:
+  Filename _specified_filename;
+  friend class FfmpegVideoCursor;
   
   
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

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

@@ -0,0 +1,18 @@
+// Filename: ffmpegVideoCursor.I
+// Created by: jyelon (01Aug2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 306 - 0
panda/src/movies/ffmpegVideoCursor.cxx

@@ -0,0 +1,306 @@
+// Filename: ffmpegVideoCursor.cxx
+// Created by: jyelon (01Aug2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_FFMPEG
+
+#include "ffmpegVideoCursor.h"
+#include "config_movies.h"
+#include "avcodec.h"
+#include "avformat.h"
+
+// Earlier versions of ffmpeg didn't define this symbol.
+#ifndef PIX_FMT_BGRA
+#ifdef WORDS_BIGENDIAN
+#define PIX_FMT_BGRA PIX_FMT_BGR32_1
+#else
+#define PIX_FMT_BGRA PIX_FMT_RGBA32
+#endif
+#endif  // PIX_FMT_BGRA
+
+
+TypeHandle FfmpegVideoCursor::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::Constructor
+//       Access: Public
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+FfmpegVideoCursor::
+FfmpegVideoCursor(PT(FfmpegVideo) src) :
+  MovieVideoCursor((MovieVideo*)(FfmpegVideo*)src),
+  _filename(src->_specified_filename),
+  _format_ctx(0),
+  _video_index(-1),
+  _video_ctx(0),
+  _frame(0),
+  _frame_out(0),
+  _min_fseek(3.0)
+{
+  string url = "pandavfs:";
+  url += _filename;
+  if (av_open_input_file(&_format_ctx, url.c_str(), NULL, 0, NULL)!=0) {
+    cleanup();
+    return;
+  }
+  
+  if (av_find_stream_info(_format_ctx)<0) {
+    cleanup();
+    return;
+  }
+  
+  // Find the video stream
+  for(int i=0; i<_format_ctx->nb_streams; i++) {
+    if(_format_ctx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
+      _video_index = i;
+      _video_ctx = _format_ctx->streams[i]->codec;
+      _video_timebase = av_q2d(_format_ctx->streams[i]->time_base);
+    }
+  }
+  
+  if (_video_ctx == 0) {
+    cleanup();
+    return;
+  }
+
+  AVCodec *pVideoCodec=avcodec_find_decoder(_video_ctx->codec_id);
+  if(pVideoCodec == 0) {
+    cleanup();
+    return;
+  }
+  if(avcodec_open(_video_ctx, pVideoCodec)<0) {
+    cleanup();
+    return;
+  }
+
+  _size_x = _video_ctx->width;
+  _size_y = _video_ctx->height;
+  _num_components = 3; // Don't know how to implement RGBA movies yet.
+  _length = (_format_ctx->duration * 1.0) / AV_TIME_BASE;
+  _can_seek = true;
+  _can_seek_fast = true;
+
+  _packet = new AVPacket;
+  _frame = avcodec_alloc_frame();
+  _frame_out = avcodec_alloc_frame();
+  if ((_packet == 0)||(_frame == 0)||(_frame_out == 0)) {
+    cleanup();
+    return;
+  }
+  memset(_packet, 0, sizeof(AVPacket));
+  
+  fetch_packet(0.0);
+  _initial_dts = _packet->dts;
+  _packet_time = 0.0;
+  _last_start = -1.0;
+  _next_start = 0.0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::Destructor
+//       Access: Public
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+FfmpegVideoCursor::
+~FfmpegVideoCursor() {
+  cleanup();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::cleanup
+//       Access: Public
+//  Description: Reset to a standard inactive state.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+cleanup() {
+  _frame_out->data[0] = 0;
+  if (_packet) {
+    if (_packet->data) {
+      av_free_packet(_packet);
+    }
+    delete _packet;
+    _packet = 0;
+  }
+  if (_video_ctx) {
+    avcodec_close(_video_ctx);
+    _video_ctx = 0;
+  }
+  if (_format_ctx) {
+    av_close_input_file(_format_ctx);
+    _format_ctx = 0;
+  }
+  if (_frame) {
+    av_free(_frame);
+    _frame = 0;
+  }
+  if (_frame_out) {
+    av_free(_frame_out);
+    _frame_out = 0;
+  }
+  _video_ctx = 0;
+  _video_index = -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::export_frame
+//       Access: Public, Virtual
+//  Description: Exports the contents of the frame buffer into the
+//               user's target buffer.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+export_frame(unsigned char *data, bool bgra) {
+  if (bgra) {
+    _frame_out->data[0] = data + ((_size_y - 1) * _size_x * 4);
+    _frame_out->linesize[0] = _size_x * -4;
+    img_convert((AVPicture *)_frame_out, PIX_FMT_BGRA, 
+                (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
+  } else {
+    _frame_out->data[0] = data + ((_size_y - 1) * _size_x * 3);
+    _frame_out->linesize[0] = _size_x * -3;
+    img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24, 
+                (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::fetch_packet
+//       Access: Protected
+//  Description: Fetches a video packet and stores it in the 
+//               packet buffer.  Sets packet_time to the packet's
+//               timestamp.  If a packet could not be read, the
+//               packet is cleared and the packet_time is set to
+//               the specified default value.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+fetch_packet(double default_time) {
+  if (_packet->data) {
+    av_free_packet(_packet);
+  }
+  while (av_read_frame(_format_ctx, _packet) >= 0) {
+    if (_packet->stream_index == _video_index) {
+      _packet_time = _packet->dts * _video_timebase;
+      return;
+    }
+    av_free_packet(_packet);
+  }
+  _packet->data = 0;
+  _packet_time = default_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::fetch_frame
+//       Access: Protected
+//  Description: Fetches a frame from the stream and stores it in
+//               the frame buffer.  Sets last_start and next_start
+//               to indicate the extents of the frame.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+fetch_frame() {
+  int finished = 0;
+  _last_start = _packet_time;
+  while (!finished && _packet->data) {
+    avcodec_decode_video(_video_ctx, _frame,
+                         &finished, _packet->data, _packet->size);
+    fetch_packet(_last_start + 1.0);
+  }
+  _next_start = _packet_time;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::seek
+//       Access: Protected
+//  Description: Seeks to a target location.  Afterward, the
+//               packet_time is guaranteed to be less than or 
+//               equal to the specified time.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+seek(double t) {
+  PN_int64 target_ts = (PN_int64)(t / _video_timebase);
+  if (target_ts < (PN_int64)(_initial_dts)) {
+    // Attempts to seek before the first packet will fail.
+    target_ts = _initial_dts;
+  }
+  if (av_seek_frame(_format_ctx, _video_index, target_ts, AVSEEK_FLAG_BACKWARD) < 0) {
+    if (t >= _packet_time) {
+      return;
+    }
+    movies_cat.error() << "Seek failure. Shutting down movie.\n";
+    cleanup();
+    _packet_time = t;
+    return;
+  }
+  fetch_packet(t);
+  if (_packet_time > t) {
+    _packet_time = t;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVideoCursor::fetch_into_buffer
+//       Access: Public, Virtual
+//  Description: See MovieVideoCursor::fetch_into_buffer.
+////////////////////////////////////////////////////////////////////
+void FfmpegVideoCursor::
+fetch_into_buffer(double time, unsigned char *data, bool bgra) {
+  
+  // If there was an error at any point, synthesize black.
+  if (_format_ctx==0) {
+    if (data) {
+      memset(data,0,size_x()*size_y()*(bgra?4:3));
+    }
+    _last_start = time;
+    _next_start = time + 1.0;
+    return;
+  }
+  
+  if (time < _last_start) {
+    // Time is in the past.
+    seek(time);
+    while (_packet_time <= time) {
+      fetch_frame();
+    }
+  } else if (time < _next_start) {
+    // Time is in the present: already have the frame.
+  } else if (time < _next_start + _min_fseek) {
+    // Time is in the near future.
+    while (_packet_time <= time) {
+      fetch_frame();
+    }
+  } else {
+    // Time is in the far future.  Seek forward, then read.
+    // There's a danger here: because keyframes are spaced
+    // unpredictably, trying to seek forward could actually
+    // move us backward in the stream!  This must be avoided.
+    // So the rule is, try the seek.  If it hurts us by moving
+    // us backward, we increase the minimum threshold distance
+    // for forward-seeking in the future.
+    
+    double base = _packet_time;
+    seek(time);
+    if (_packet_time < base) {
+      _min_fseek += (base - _packet_time);
+    }
+    while (_packet_time <= time) {
+      fetch_frame();
+    }
+  }
+  export_frame(data, bgra);
+}
+
+////////////////////////////////////////////////////////////////////
+
+#endif // HAVE_FFMPEG

+ 86 - 0
panda/src/movies/ffmpegVideoCursor.h

@@ -0,0 +1,86 @@
+// Filename: ffmpegVideoCursor.h
+// Created by: jyelon (01Aug2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 FFMPEGVIDEOCURSOR_H
+#define FFMPEGVIDEOCURSOR_H
+#ifdef HAVE_FFMPEG
+
+#include "pandabase.h"
+#include "texture.h"
+#include "pointerTo.h"
+
+struct AVFormatContext;
+struct AVCodecContext;
+struct AVStream;
+struct AVPacket;
+struct AVFrame;
+
+////////////////////////////////////////////////////////////////////
+//       Class : FfmpegVideoCursor
+// Description : 
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES FfmpegVideoCursor : public MovieVideoCursor {
+
+ PUBLISHED:
+  FfmpegVideoCursor(PT(FfmpegVideo) src);
+  virtual ~FfmpegVideoCursor();
+  
+ public:
+  virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
+
+ protected:
+  void fetch_packet(double default_time);
+  void fetch_frame();
+  void seek(double t);
+  void export_frame(unsigned char *data, bool bgra);
+  void cleanup();
+  
+  Filename _filename;
+  AVPacket *_packet;
+  double _packet_time;
+  AVFormatContext *_format_ctx;
+  AVCodecContext *_video_ctx;
+  int    _video_index;
+  double _video_timebase;
+  AVFrame *_frame;
+  AVFrame *_frame_out;
+  int _initial_dts;
+  double _min_fseek;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MovieVideoCursor::init_type();
+    register_type(_type_handle, "FfmpegVideoCursor",
+                  MovieVideoCursor::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 "ffmpegVideoCursor.I"
+
+#endif // HAVE_FFMPEG
+#endif // FFMPEGVIDEO_H

+ 17 - 0
panda/src/movies/ffmpegVirtualFileCursor.I

@@ -0,0 +1,17 @@
+// Filename: ffmpegVirtualFile.I
+// Created by: jyelon (01Aug2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////

+ 155 - 0
panda/src/movies/ffmpegVirtualFileCursor.cxx

@@ -0,0 +1,155 @@
+// Filename: ffmpegVirtualFile.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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_FFMPEG
+
+#include "pandabase.h"
+#include "config_movies.h"
+#include "ffmpegVirtualFile.h"
+#include "virtualFileSystem.h"
+#include "avio.h"
+
+////////////////////////////////////////////////////////////////////
+// These functions need to use C calling conventions.
+////////////////////////////////////////////////////////////////////
+extern "C" {
+  static int       pandavfs_open(URLContext *h, const char *filename, int flags);
+  static int       pandavfs_read(URLContext *h, unsigned char *buf, int size);
+  static int       pandavfs_write(URLContext *h, unsigned char *buf, int size);
+  static PN_int64  pandavfs_seek(URLContext *h, PN_int64 pos, int whence);
+  static int       pandavfs_close(URLContext *h);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: pandavfs_open
+//       Access: Static Function
+//  Description: A hook to open a panda VFS file.
+////////////////////////////////////////////////////////////////////
+static int
+pandavfs_open(URLContext *h, const char *filename, int flags) {
+  if (flags != 0) {
+    movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
+    return -1;
+  }
+  filename += 9; // Skip over "pandavfs:"
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  istream *s = vfs->open_read_file(filename, true);
+  if (s == 0) {
+    return -1;
+  }
+  // Test whether seek works.
+  s->seekg(1, ios::beg);
+  int tel1 = s->tellg();
+  s->seekg(0, ios::beg);
+  int tel2 = s->tellg();
+  if (s->fail() || (tel1!=1) || (tel2!=0)) {
+    movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
+    delete s;
+    return -1;
+  }
+  h->priv_data = s;
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: pandavfs_read
+//       Access: Static Function
+//  Description: A hook to read a panda VFS file.
+////////////////////////////////////////////////////////////////////
+static int
+pandavfs_read(URLContext *h, unsigned char *buf, int size) {
+  istream *s = (istream*)(h->priv_data);
+  s->read((char*)buf, size);
+  int gc = s->gcount();
+  s->clear();
+  return gc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: pandavfs_write
+//       Access: Static Function
+//  Description: A hook to write a panda VFS file.
+////////////////////////////////////////////////////////////////////
+static int
+pandavfs_write(URLContext *h, unsigned char *buf, int size) {
+  movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
+  return -1;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: pandavfs_seek
+//       Access: Static Function
+//  Description: A hook to seek a panda VFS file.
+////////////////////////////////////////////////////////////////////
+static PN_int64
+pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
+  istream *s = (istream*)(h->priv_data);
+  switch(whence) {
+  case 0: s->seekg(pos, ios::beg); break;
+  case 1: s->seekg(pos, ios::cur); break;
+  case 2: s->seekg(pos, ios::end); break;
+  default:
+    movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
+    return -1;
+  }
+  s->clear();
+  int tl = s->tellg();
+  return tl;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: pandavfs_close
+//       Access: Static Function
+//  Description: A hook to close a panda VFS file.
+////////////////////////////////////////////////////////////////////
+static int
+pandavfs_close(URLContext *h) {
+  istream *s = (istream*)(h->priv_data);
+  delete s;
+  h->priv_data = 0;
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FfmpegVirtualFile::register_protocol
+//       Access: Public, Static
+//  Description: Enables ffmpeg to access panda's VFS.
+//
+//               After calling this method, ffmpeg will be
+//               able to open "URLs" that look like this:
+//               
+//               pandavfs:/c/mygame/foo.avi
+//
+////////////////////////////////////////////////////////////////////
+void FfmpegVirtualFile::
+register_protocol() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  static URLProtocol protocol;
+  protocol.name = "pandavfs";
+  protocol.url_open  = pandavfs_open;
+  protocol.url_read  = pandavfs_read;
+  protocol.url_write = pandavfs_write;
+  protocol.url_seek  = pandavfs_seek;
+  protocol.url_close = pandavfs_close;
+  ::register_protocol(&protocol);
+}
+
+#endif // HAVE_FFMPEG

+ 46 - 0
panda/src/movies/ffmpegVirtualFileCursor.h

@@ -0,0 +1,46 @@
+// Filename: ffmpegVirtualFile.h
+// Created by: jyelon (01Aug2007)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef FFMPEGVIRTUALFILE_H
+#define FFMPEGVIRTUALFILE_H
+#ifdef HAVE_FFMPEG
+
+#include "config_movies.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : FfmpegVirtualFile
+// Description : Enables ffmpeg to access panda's VFS.
+//
+//               This class only has one public method,
+//               register_hooks.  Once the hooks are registered,
+//               ffmpeg will be able to open "URLs" that look
+//               like this:
+//               
+//               pandavfs:/c/mygame/foo.avi
+//
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES FfmpegVirtualFile {
+ public:
+  static void register_protocol();
+};
+
+#include "ffmpegVirtualFile.I"
+
+#endif // HAVE_FFMPEG
+#endif // FFMPEGVIRTUALFILE_H
+

+ 10 - 119
panda/src/movies/inkblotVideo.cxx

@@ -17,39 +17,10 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "inkblotVideo.h"
 #include "inkblotVideo.h"
-#include "config_movies.h"
+#include "inkblotVideoCursor.h"
 
 
 TypeHandle InkblotVideo::_type_handle;
 TypeHandle InkblotVideo::_type_handle;
 
 
-////////////////////////////////////////////////////////////////////
-//
-// The Color-Map
-//
-////////////////////////////////////////////////////////////////////
-struct color {
-  int r,g,b;
-};
-
-static color colormap[17] = {
-  { 255,0,0 },
-  { 255,255,0 },
-  { 0,255,0 },
-  { 0,255,255 },
-  { 0,0,255 },
-  { 0,0,0 },
-  { 255,0,0 },
-  { 255,255,0 },
-  { 0,255,0 },
-  { 0,255,255 },
-  { 0,0,255 },
-  { 0,0,0 },
-  { 255,0,0 },
-  { 255,255,0 },
-  { 0,255,0 },
-  { 0,255,255 },
-  { 0,0,255 },
-};  
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: InkblotVideo::Constructor
 //     Function: InkblotVideo::Constructor
 //       Access: Public
 //       Access: Public
@@ -57,24 +28,11 @@ static color colormap[17] = {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 InkblotVideo::
 InkblotVideo::
 InkblotVideo(int x, int y, int fps) :
 InkblotVideo(int x, int y, int fps) :
-  MovieVideo("inkblot")
+  MovieVideo("inkblot"),
+  _specified_x(x),
+  _specified_y(y),
+  _specified_fps(y)
 {
 {
-  if (x < 1) x=1;
-  if (y < 1) y=1;
-  if (fps < 1) fps=1;
-  _size_x = x;
-  _size_y = y;
-  _fps = fps;
-  
-  int padx = x + 2;
-  int pady = y + 2;
-  _cells = new unsigned char[padx * pady];
-  _cells2 = new unsigned char[padx * pady];
-  memset(_cells, 255, padx * pady);
-  memset(_cells2, 255, padx * pady);
-  _can_seek = true;
-  _can_seek_fast = false;
-  _frames_read = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -84,81 +42,14 @@ InkblotVideo(int x, int y, int fps) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 InkblotVideo::
 InkblotVideo::
 ~InkblotVideo() {
 ~InkblotVideo() {
-  delete[] _cells;
-  delete[] _cells2;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: InkblotVideo::fetch_into_buffer
-//       Access: Published, Virtual
-//  Description: See MovieVideo::fetch_into_buffer.
-////////////////////////////////////////////////////////////////////
-void InkblotVideo::
-fetch_into_buffer(double time, unsigned char *data, bool bgra) {
-
-  int padx = size_x() + 2;
-  int pady = size_y() + 2;
-  
-  if (time < _next_start) {
-    // Rewind to beginning.
-    memset(_cells, 255, padx * pady);
-    memset(_cells2, 255, padx * pady);
-    _last_start = -1.0;
-    _next_start = 0.0;
-    _frames_read = 0;
-  }
-  
-  nassertv(time >= _next_start);
-  
-  while (_next_start <= time) {
-    _last_start = (_frames_read * 1.0) / _fps;
-    _frames_read += 1;
-    _next_start = (_frames_read * 1.0) / _fps;
-    for (int y=1; y<pady-1; y++) {
-      for (int x=1; x<padx-1; x++) {
-        int tot =
-          _cells[(x+1)+(y+1)*padx] +
-          _cells[(x+1)+(y+0)*padx] +
-          _cells[(x+1)+(y-1)*padx] +
-          _cells[(x+0)+(y+1)*padx] +
-          _cells[(x+0)+(y+0)*padx] +
-          _cells[(x+0)+(y-1)*padx] +
-          _cells[(x-1)+(y+1)*padx] +
-          _cells[(x-1)+(y+0)*padx] +
-          _cells[(x-1)+(y-1)*padx];
-        _cells2[x + y*padx] = (tot/9)+3;
-      }
-    }
-    unsigned char *t = _cells;
-    _cells = _cells2;
-    _cells2 = t;
-  }
-
-  for (int y=1; y<pady - 1; y++) {
-    for (int x=1; x<padx - 1; x++) {
-      int val = _cells[x + y*padx];
-      color &c1 = colormap[(val>>4)+0];
-      color &c2 = colormap[(val>>4)+1];
-      int lerp = val & 15;
-      data[0] = (c1.b * (16-lerp) + c2.b * lerp) / 16;
-      data[1] = (c1.g * (16-lerp) + c2.g * lerp) / 16;
-      data[2] = (c1.r * (16-lerp) + c2.r * lerp) / 16;
-      if (bgra) {
-        data[3] = 255;
-        data += 4;
-      } else {
-        data += 3;
-      }
-    }
-  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: InkblotVideo::make_copy
+//     Function: InkblotVideo::open
 //       Access: Published, Virtual
 //       Access: Published, Virtual
-//  Description: Make a copy of this video with a separate cursor.
+//  Description: Open this video, returning a MovieVideoCursor.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(MovieVideo) InkblotVideo::
-make_copy() const {
-  return new InkblotVideo(_size_x, _size_y, _fps);
+PT(MovieVideoCursor) InkblotVideo::
+open() {
+  return new InkblotVideoCursor(this);
 }
 }

+ 10 - 13
panda/src/movies/inkblotVideo.h

@@ -19,9 +19,9 @@
 #ifndef INKBLOTVIDEO_H
 #ifndef INKBLOTVIDEO_H
 #define INKBLOTVIDEO_H
 #define INKBLOTVIDEO_H
 
 
-#include "pandabase.h"
-#include "texture.h"
-#include "pointerTo.h"
+#include "movieVideo.h"
+
+class InkblotVideoCursor;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : InkblotVideo
 //       Class : InkblotVideo
@@ -33,16 +33,13 @@ class EXPCL_PANDA_MOVIES InkblotVideo : public MovieVideo {
  PUBLISHED:
  PUBLISHED:
   InkblotVideo(int x, int y, int fps);
   InkblotVideo(int x, int y, int fps);
   virtual ~InkblotVideo();
   virtual ~InkblotVideo();
-  virtual PT(MovieVideo) make_copy() const;
-  
- public:
-  virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
-  
- protected:
-  unsigned char *_cells;
-  unsigned char *_cells2;
-  int _fps;
-  int _frames_read;
+  virtual PT(MovieVideoCursor) open();
+
+ private:
+  int _specified_x;
+  int _specified_y;
+  int _specified_fps;
+  friend class InkblotVideoCursor;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

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

@@ -0,0 +1,18 @@
+// Filename: inkblotVideoCursor.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] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 151 - 0
panda/src/movies/inkblotVideoCursor.cxx

@@ -0,0 +1,151 @@
+// Filename: inkblotVideoCursor.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 "inkblotVideoCursor.h"
+#include "config_movies.h"
+
+TypeHandle InkblotVideoCursor::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//
+// The Color-Map
+//
+////////////////////////////////////////////////////////////////////
+struct color {
+  int r,g,b;
+};
+
+static color colormap[17] = {
+  { 255,0,0 },
+  { 255,255,0 },
+  { 0,255,0 },
+  { 0,255,255 },
+  { 0,0,255 },
+  { 0,0,0 },
+  { 255,0,0 },
+  { 255,255,0 },
+  { 0,255,0 },
+  { 0,255,255 },
+  { 0,0,255 },
+  { 0,0,0 },
+  { 255,0,0 },
+  { 255,255,0 },
+  { 0,255,0 },
+  { 0,255,255 },
+  { 0,0,255 },
+};  
+
+////////////////////////////////////////////////////////////////////
+//     Function: InkblotVideoCursor::Constructor
+//       Access: Public
+//  Description: xxx
+////////////////////////////////////////////////////////////////////
+InkblotVideoCursor::
+InkblotVideoCursor(PT(InkblotVideo) src) :
+  MovieVideoCursor((MovieVideo*)(InkblotVideo*)src)
+{
+  _size_x = src->_specified_x;
+  _size_y = src->_specified_y;
+  _fps = src->_specified_fps;
+  int padx = _size_x + 2;
+  int pady = _size_y + 2;
+  _cells = new unsigned char[padx * pady];
+  _cells2 = new unsigned char[padx * pady];
+  memset(_cells, 255, padx * pady);
+  memset(_cells2, 255, padx * pady);
+  _can_seek = true;
+  _can_seek_fast = false;
+  _frames_read = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InkblotVideoCursor::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+InkblotVideoCursor::
+~InkblotVideoCursor() {
+  delete[] _cells;
+  delete[] _cells2;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: InkblotVideoCursor::fetch_into_buffer
+//       Access: Published, Virtual
+//  Description: See MovieVideoCursor::fetch_into_buffer.
+////////////////////////////////////////////////////////////////////
+void InkblotVideoCursor::
+fetch_into_buffer(double time, unsigned char *data, bool bgra) {
+
+  int padx = size_x() + 2;
+  int pady = size_y() + 2;
+  
+  if (time < _next_start) {
+    // Rewind to beginning.
+    memset(_cells, 255, padx * pady);
+    memset(_cells2, 255, padx * pady);
+    _last_start = -1.0;
+    _next_start = 0.0;
+    _frames_read = 0;
+  }
+  
+  nassertv(time >= _next_start);
+  
+  while (_next_start <= time) {
+    _last_start = (_frames_read * 1.0) / _fps;
+    _frames_read += 1;
+    _next_start = (_frames_read * 1.0) / _fps;
+    for (int y=1; y<pady-1; y++) {
+      for (int x=1; x<padx-1; x++) {
+        int tot =
+          _cells[(x+1)+(y+1)*padx] +
+          _cells[(x+1)+(y+0)*padx] +
+          _cells[(x+1)+(y-1)*padx] +
+          _cells[(x+0)+(y+1)*padx] +
+          _cells[(x+0)+(y+0)*padx] +
+          _cells[(x+0)+(y-1)*padx] +
+          _cells[(x-1)+(y+1)*padx] +
+          _cells[(x-1)+(y+0)*padx] +
+          _cells[(x-1)+(y-1)*padx];
+        _cells2[x + y*padx] = (tot/9)+3;
+      }
+    }
+    unsigned char *t = _cells;
+    _cells = _cells2;
+    _cells2 = t;
+  }
+
+  for (int y=1; y<pady - 1; y++) {
+    for (int x=1; x<padx - 1; x++) {
+      int val = _cells[x + y*padx];
+      color &c1 = colormap[(val>>4)+0];
+      color &c2 = colormap[(val>>4)+1];
+      int lerp = val & 15;
+      data[0] = (c1.b * (16-lerp) + c2.b * lerp) / 16;
+      data[1] = (c1.g * (16-lerp) + c2.g * lerp) / 16;
+      data[2] = (c1.r * (16-lerp) + c2.r * lerp) / 16;
+      if (bgra) {
+        data[3] = 255;
+        data += 4;
+      } else {
+        data += 3;
+      }
+    }
+  }
+}
+

+ 67 - 0
panda/src/movies/inkblotVideoCursor.h

@@ -0,0 +1,67 @@
+// Filename: inkblotVideoCursor.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 INKBLOTVIDEOCURSOR_H
+#define INKBLOTVIDEOCURSOR_H
+
+#include "pandabase.h"
+#include "texture.h"
+#include "pointerTo.h"
+#include "inkblotVideo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : InkblotVideoCursor
+// Description : A cellular automaton that generates an amusing
+//               pattern of swirling colors.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES InkblotVideoCursor : public MovieVideoCursor {
+
+ PUBLISHED:
+  InkblotVideoCursor(PT(InkblotVideo) src);
+  virtual ~InkblotVideoCursor();
+  
+ public:
+  virtual void fetch_into_buffer(double time, unsigned char *block, bool rgba);
+  
+ protected:
+  unsigned char *_cells;
+  unsigned char *_cells2;
+  int _fps;
+  int _frames_read;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    MovieVideoCursor::init_type();
+    register_type(_type_handle, "InkblotVideoCursor",
+                  MovieVideoCursor::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 "inkblotVideoCursor.I"
+
+#endif

+ 0 - 120
panda/src/movies/movieAudio.I

@@ -16,123 +16,3 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::audio_rate
-//       Access: Public
-//  Description: Returns the audio sample rate.
-////////////////////////////////////////////////////////////////////
-INLINE int MovieAudio::
-audio_rate() const {
-  return _audio_rate;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::audio_channels
-//       Access: Public
-//  Description: Returns the number of audio channels (ie, two for
-//               stereo, one for mono).
-////////////////////////////////////////////////////////////////////
-INLINE int MovieAudio::
-audio_channels() const {
-  return _audio_channels;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::length
-//       Access: Public
-//  Description: Returns the length of the movie.
-//
-//               Some kinds of Movie, such as internet TV station, 
-//               might not have a predictable length.  In that case,
-//               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
-//               into them - usually, they're a second or two long or
-//               short.  When playing such an AVI using the Movie class,
-//               you may see a slightly truncated video, or a slightly
-//               elongated video (padded with black frames).  There are
-//               utilities out there to fix the length values in AVI
-//               files.
-//
-////////////////////////////////////////////////////////////////////
-INLINE double MovieAudio::
-length() const {
-  return _length;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::can_seek
-//       Access: Public
-//  Description: Returns true if the movie can seek.  If this is
-//               true, seeking is still not guaranteed to be fast:
-//               for some movies, seeking is implemented by rewinding
-//               to the beginning and then fast-forwarding to the
-//               desired location.  Even if the movie cannot seek,
-//               the seek method can still advance to an arbitrary
-//               location by reading samples and discarding them.
-//               However, to move backward, can_seek must return true.
-////////////////////////////////////////////////////////////////////
-INLINE bool MovieAudio::
-can_seek() const {
-  return _can_seek;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::can_seek_fast
-//       Access: Public
-//  Description: Returns true if seek operations are constant time.
-////////////////////////////////////////////////////////////////////
-INLINE bool MovieAudio::
-can_seek_fast() const {
-  return _can_seek_fast;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::aborted
-//       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.
-////////////////////////////////////////////////////////////////////
-INLINE bool MovieAudio::
-aborted() const {
-  return _aborted;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::samples_read
-//       Access: Public
-//  Description: Returns the number of audio samples you have read
-//               from the audio stream so far.
-////////////////////////////////////////////////////////////////////
-INLINE int MovieAudio::
-samples_read() const {
-  return _samples_read;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::skip_samples
-//       Access: Published
-//  Description: Skip audio samples from the stream.  This is mostly
-//               for debugging purposes.
-////////////////////////////////////////////////////////////////////
-INLINE void MovieAudio::
-skip_samples(int n) {
-  read_samples(n, 0);
-}
- 

+ 11 - 66
panda/src/movies/movieAudio.cxx

@@ -17,6 +17,7 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "movieAudio.h"
 #include "movieAudio.h"
+#include "movieAudioCursor.h"
 
 
 TypeHandle MovieAudio::_type_handle;
 TypeHandle MovieAudio::_type_handle;
 
 
@@ -30,14 +31,7 @@ TypeHandle MovieAudio::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MovieAudio::
 MovieAudio::
 MovieAudio(const string &name) :
 MovieAudio(const string &name) :
-  Namable(name),
-  _audio_rate(8000),
-  _audio_channels(1),
-  _length(1.0E10),
-  _can_seek(true),
-  _can_seek_fast(true),
-  _aborted(false),
-  _samples_read(0)
+  Namable(name)
 {
 {
 }
 }
 
 
@@ -51,76 +45,27 @@ MovieAudio::
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::read_samples
-//       Access: Public, Virtual
-//  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 MovieAudio::
-read_samples(int n, PN_int16 *data) {
-
-  // This is the null implementation, which generates pure silence.
-  // Normally, this method will be overridden by a subclass.
-
-  if (n <= 0) {
-    return;
-  }
-
-  for (int i=0; i<n; i++) {
-    data[i] = 0;
-  }
-  _samples_read += n;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::seek
+//     Function: MovieAudio::open
 //       Access: Published, Virtual
 //       Access: Published, Virtual
-//  Description: Skips to the specified sample number. After
-//               calling seek, samples_read will be equal to the
-//               specified value.  If the movie reports that it
-//               cannot seek, then this method can still advance
-//               by reading samples and discarding them.  However,
-//               to move backward, can_seek must be true.
-//
-//               If the movie reports that it can_seek, it doesn't
-//               mean that it can do so quickly.  It may have to
-//               rewind the movie and then fast forward to the
-//               desired location.  Only if can_seek_fast returns
-//               true can seek operations be done in constant time.
-//
-//               Seeking may not be precise, because AVI files 
-//               often have inaccurate indices.  However, it is
-//               usually pretty close (ie, 0.05 seconds).
-////////////////////////////////////////////////////////////////////
-void MovieAudio::
-seek(int sr) {
-  _samples_read = sr;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::make_copy
-//       Access: Published, Virtual
-//  Description: Make a copy of this MovieAudio with its own cursor.
+//  Description: Open this audio, returning a MovieAudioCursor
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PT(MovieAudio) MovieAudio::
-make_copy() const {
-  return new MovieAudio();
+PT(MovieAudioCursor) MovieAudio::
+open() {
+  return new MovieAudioCursor(this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: MovieAudio::load
+//     Function: MovieAudio::get
 //       Access: Published, Static
 //       Access: Published, Static
-//  Description: Load a movie from a file.
+//  Description: Obtains a MovieAudio that references a file.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(MovieAudio) MovieAudio::
 PT(MovieAudio) MovieAudio::
-load(const Filename &name) {
+get(const Filename &name) {
 #ifdef HAVE_FFMPEG
 #ifdef HAVE_FFMPEG
   // Someday, I'll probably put a dispatcher here.
   // Someday, I'll probably put a dispatcher here.
   // But for now, just hardwire it to go to FFMPEG.
   // But for now, just hardwire it to go to FFMPEG.
   return new FfmpegAudio(name);
   return new FfmpegAudio(name);
 #else
 #else
-  return NULL;
+  return new MovieAudio("Load-Failure Stub");
 #endif
 #endif
 }
 }

+ 10 - 29
panda/src/movies/movieAudio.h

@@ -21,8 +21,10 @@
 
 
 #include "pandabase.h"
 #include "pandabase.h"
 #include "namable.h"
 #include "namable.h"
-#include "texture.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
+#include "typedWritableReferenceCount.h"
+class MovieAudio;
+#include "movieAudioCursor.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : MovieAudio
 //       Class : MovieAudio
@@ -30,40 +32,19 @@
 //               a sequence of audio samples.  That could include an
 //               a sequence of audio samples.  That could include an
 //               AVI file, a microphone, or an internet TV station.
 //               AVI file, a microphone, or an internet TV station.
 //
 //
-//               Thread safety: each individual MovieAudio
-//               must be owned and accessed by a single thread.
-//               It is OK for two different threads to open
-//               the same file at the same time, as long as they
-//               use separate MovieAudio objects.
+//               The difference between a MovieAudio and a
+//               MovieAudioCursor is like the difference between a
+//               filename and a file handle.  The MovieAudio just
+//               indicates a particular movie.  The MovieAudioCursor
+//               is what allows access.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public Namable {
 class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public Namable {
 
 
 PUBLISHED:
 PUBLISHED:
   MovieAudio(const string &name = "Blank Audio");
   MovieAudio(const string &name = "Blank Audio");
   virtual ~MovieAudio();
   virtual ~MovieAudio();
-  INLINE int audio_rate() const;
-  INLINE int audio_channels() const;
-  INLINE double length() const;
-  INLINE bool can_seek() const;
-  INLINE bool can_seek_fast() const;
-  INLINE bool aborted() const;
-  INLINE int samples_read() const;
-  INLINE void skip_samples(int n);
-  virtual void seek(int sr);
-  virtual PT(MovieAudio) make_copy() const;
-  static PT(MovieAudio) load(const Filename &name);
-
-public:
-  virtual void read_samples(int n, PN_int16 *data);
-  
-protected:
-  int _audio_rate;
-  int _audio_channels;
-  double _length;
-  bool _can_seek;
-  bool _can_seek_fast;
-  bool _aborted;
-  int _samples_read;
+  virtual PT(MovieAudioCursor) open();
+  static PT(MovieAudio) get(const Filename &name);
   
   
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 147 - 0
panda/src/movies/movieAudioCursor.I

@@ -0,0 +1,147 @@
+// Filename: movieAudioCursor.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] .
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::get_source
+//       Access: Public
+//  Description: Returns the MovieAudio which this cursor references.
+////////////////////////////////////////////////////////////////////
+INLINE PT(MovieAudio) MovieAudioCursor::
+get_source() const {
+  return _source;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::audio_rate
+//       Access: Public
+//  Description: Returns the audio sample rate.
+////////////////////////////////////////////////////////////////////
+INLINE int MovieAudioCursor::
+audio_rate() const {
+  return _audio_rate;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::audio_channels
+//       Access: Public
+//  Description: Returns the number of audio channels (ie, two for
+//               stereo, one for mono).
+////////////////////////////////////////////////////////////////////
+INLINE int MovieAudioCursor::
+audio_channels() const {
+  return _audio_channels;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::length
+//       Access: Public
+//  Description: Returns the length of the movie.
+//
+//               Some kinds of Movie, such as internet TV station, 
+//               might not have a predictable length.  In that case,
+//               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
+//               into them - usually, they're a second or two long or
+//               short.  When playing such an AVI using the Movie class,
+//               you may see a slightly truncated video, or a slightly
+//               elongated video (padded with black frames).  There are
+//               utilities out there to fix the length values in AVI
+//               files.
+//
+////////////////////////////////////////////////////////////////////
+INLINE double MovieAudioCursor::
+length() const {
+  return _length;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::can_seek
+//       Access: Public
+//  Description: Returns true if the movie can seek.  If this is
+//               true, seeking is still not guaranteed to be fast:
+//               for some movies, seeking is implemented by rewinding
+//               to the beginning and then fast-forwarding to the
+//               desired location.  Even if the movie cannot seek,
+//               the seek method can still advance to an arbitrary
+//               location by reading samples and discarding them.
+//               However, to move backward, can_seek must return true.
+////////////////////////////////////////////////////////////////////
+INLINE bool MovieAudioCursor::
+can_seek() const {
+  return _can_seek;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::can_seek_fast
+//       Access: Public
+//  Description: Returns true if seek operations are constant time.
+////////////////////////////////////////////////////////////////////
+INLINE bool MovieAudioCursor::
+can_seek_fast() const {
+  return _can_seek_fast;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::aborted
+//       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.
+////////////////////////////////////////////////////////////////////
+INLINE bool MovieAudioCursor::
+aborted() const {
+  return _aborted;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::tell
+//       Access: Public
+//  Description: Returns the current offset within the file.
+////////////////////////////////////////////////////////////////////
+INLINE double MovieAudioCursor::
+tell() const {
+  return _last_seek + ((_samples_read * 1.0) / _audio_rate);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::skip_samples
+//       Access: Published
+//  Description: Skip audio samples from the stream.  This is mostly
+//               for debugging purposes.
+////////////////////////////////////////////////////////////////////
+INLINE void MovieAudioCursor::
+skip_samples(int n) {
+  read_samples(n, 0);
+}
+ 

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

@@ -0,0 +1,104 @@
+// Filename: movieAudioCursor.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 "movieAudioCursor.h"
+
+TypeHandle MovieAudioCursor::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::Constructor
+//       Access: Public
+//  Description: This constructor returns a null audio stream --- a
+//               stream of total silence, at 8000 samples per second.
+//               To get more interesting audio, you need to construct
+//               a subclass of this class.
+////////////////////////////////////////////////////////////////////
+MovieAudioCursor::
+MovieAudioCursor(PT(MovieAudio) src) :
+  _source(src),
+  _audio_rate(8000),
+  _audio_channels(1),
+  _length(1.0E10),
+  _can_seek(true),
+  _can_seek_fast(true),
+  _aborted(false),
+  _samples_read(0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MovieAudioCursor::
+~MovieAudioCursor() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::read_samples
+//       Access: Public, Virtual
+//  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 MovieAudioCursor::
+read_samples(int n, PN_int16 *data) {
+
+  // This is the null implementation, which generates pure silence.
+  // Normally, this method will be overridden by a subclass.
+
+  if (n <= 0) {
+    return;
+  }
+
+  for (int i=0; i<n; i++) {
+    data[i] = 0;
+  }
+  _samples_read += n;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieAudioCursor::seek
+//       Access: Published, Virtual
+//  Description: Skips to the specified offset within the file.
+//
+//               If the movie reports that it cannot seek, then
+//               this method can still advance by reading samples
+//               and discarding them.  However, to move backward,
+//               can_seek must be true.
+//
+//               If the movie reports that it can_seek, it doesn't
+//               mean that it can do so quickly.  It may have to
+//               rewind the movie and then fast forward to the
+//               desired location.  Only if can_seek_fast returns
+//               true can seek operations be done in constant time.
+//
+//               Seeking may not be precise, because AVI files 
+//               often have inaccurate indices.  After
+//               seeking, tell will indicate that the cursor is
+//               at the target location. However, in truth, the data
+//               you read may come from a slightly offset location.
+////////////////////////////////////////////////////////////////////
+void MovieAudioCursor::
+seek(double offset) {
+  _last_seek = offset;
+  _samples_read = 0;
+}
+

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

@@ -0,0 +1,93 @@
+// Filename: movieAudioCursor.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 MOVIEAUDIOCURSOR_H
+#define MOVIEAUDIOCURSOR_H
+
+#include "pandabase.h"
+#include "namable.h"
+#include "texture.h"
+#include "pointerTo.h"
+class MovieAudioCursor;
+#include "movieAudio.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : MovieAudioCursor
+// Description : A MovieAudio is actually any source that provides
+//               a sequence of audio samples.  That could include an
+//               AVI file, a microphone, or an internet TV station.
+//               A MovieAudioCursor is a handle that lets you read
+//               data sequentially from a MovieAudio.
+//
+//               Thread safety: each individual MovieAudioCursor
+//               must be owned and accessed by a single thread.
+//               It is OK for two different threads to open
+//               the same file at the same time, as long as they
+//               use separate MovieAudioCursor objects.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES MovieAudioCursor : public TypedWritableReferenceCount {
+
+PUBLISHED:
+  MovieAudioCursor(PT(MovieAudio) src);
+  virtual ~MovieAudioCursor();
+  INLINE PT(MovieAudio) get_source() const;
+  INLINE int audio_rate() const;
+  INLINE int audio_channels() const;
+  INLINE double length() const;
+  INLINE bool can_seek() const;
+  INLINE bool can_seek_fast() const;
+  INLINE bool aborted() const;
+  INLINE double tell() const;
+  INLINE void skip_samples(int n);
+  virtual void seek(double offset);
+
+public:
+  virtual void read_samples(int n, PN_int16 *data);
+  
+protected:
+  PT(MovieAudio) _source;
+  int _audio_rate;
+  int _audio_channels;
+  double _length;
+  bool _can_seek;
+  bool _can_seek_fast;
+  bool _aborted;
+  double _last_seek;
+  PN_int64 _samples_read;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "MovieAudioCursor",
+                  TypedWritableReferenceCount::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 "movieAudioCursor.I"
+
+#endif

+ 0 - 136
panda/src/movies/movieVideo.I

@@ -16,139 +16,3 @@
 //
 //
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::size_x
-//       Access: Published
-//  Description: Get the horizontal size of the movie.
-////////////////////////////////////////////////////////////////////
-INLINE int MovieVideo::
-size_x() const {
-  return _size_x;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::size_y
-//       Access: Published
-//  Description: Get the vertical size of the movie.
-////////////////////////////////////////////////////////////////////
-INLINE int MovieVideo::
-size_y() const {
-  return _size_y;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::get_num_components
-//       Access: Published
-//  Description: Returns 4 if the movie has an alpha
-//               channel, 3 otherwise.
-////////////////////////////////////////////////////////////////////
-INLINE int MovieVideo::
-get_num_components() const {
-  return _num_components;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::length
-//       Access: Published
-//  Description: Returns the length of the movie.
-//
-//               Some kinds of Movie, such as internet TV station, 
-//               might not have a predictable length.  In that case,
-//               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
-//               into them - usually, they're a second or two long or
-//               short.  When playing such an AVI using the Movie class,
-//               you may see a slightly truncated video, or a slightly
-//               elongated video (padded with black frames).  There are
-//               utilities out there to fix the length values in AVI
-//               files.
-//
-////////////////////////////////////////////////////////////////////
-INLINE int MovieVideo::
-length() const {
-  return _length;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::can_seek
-//       Access: Published
-//  Description: Returns true if the movie can seek.  If this is
-//               true, seeking is still not guaranteed to be fast:
-//               for some movies, seeking is implemented by rewinding
-//               to the beginning and then fast-forwarding to the
-//               desired location.  Even if the movie cannot seek,
-//               the fetch methods can still advance to an arbitrary
-//               location by reading frames and discarding them.
-//               However, to move backward, can_seek must return true.
-////////////////////////////////////////////////////////////////////
-INLINE bool MovieVideo::
-can_seek() const {
-  return _can_seek;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::can_seek_fast
-//       Access: Published
-//  Description: Returns true if seek operations are constant time.
-////////////////////////////////////////////////////////////////////
-INLINE bool MovieVideo::
-can_seek_fast() const {
-  return _can_seek_fast;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::aborted
-//       Access: Published
-//  Description: Returns true if the video 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 video does not
-//               constitute an 'abort' condition.
-////////////////////////////////////////////////////////////////////
-INLINE bool MovieVideo::
-aborted() const {
-  return _aborted;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::last_start
-//       Access: Published
-//  Description: Returns the start time of the last frame you read.
-//
-//               MovieVideo streams have variable frame rates.  Each
-//               frame will specify how long it is to be displayed.
-//               These lengths may not be equal from frame to frame.
-////////////////////////////////////////////////////////////////////
-INLINE double MovieVideo::
-last_start() const {
-  return _last_start;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::next_start
-//       Access: Published
-//  Description: Returns the start time of the next frame you can read.
-//
-//               MovieVideo streams have variable frame rates.  Each
-//               frame will specify how long it is to be displayed.
-//               These lengths may not be equal from frame to frame.
-////////////////////////////////////////////////////////////////////
-INLINE double MovieVideo::
-next_start() const {
-  return _next_start;
-}
-

+ 10 - 246
panda/src/movies/movieVideo.cxx

@@ -17,7 +17,6 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "movieVideo.h"
 #include "movieVideo.h"
-#include "ffmpegVideo.h"
 #include "config_movies.h"
 #include "config_movies.h"
 
 
 TypeHandle MovieVideo::_type_handle;
 TypeHandle MovieVideo::_type_handle;
@@ -32,16 +31,7 @@ TypeHandle MovieVideo::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MovieVideo::
 MovieVideo::
 MovieVideo(const string &name) :
 MovieVideo(const string &name) :
-  Namable(name),
-  _size_x(1),
-  _size_y(1),
-  _num_components(3),
-  _length(1.0E10),
-  _can_seek(true),
-  _can_seek_fast(true),
-  _aborted(false),
-  _last_start(-1.0),
-  _next_start(0.0)
+  Namable(name)
 {
 {
 }
 }
 
 
@@ -52,257 +42,31 @@ MovieVideo(const string &name) :
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 MovieVideo::
 MovieVideo::
 ~MovieVideo() {
 ~MovieVideo() {
-  if (_conversion_buffer != 0) {
-    delete[] _conversion_buffer;
-  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::allocate_conversion_buffer
-//       Access: Private
-//  Description: The generic implementations of fetch_into_texture
-//               and fetch_into_alpha require the use of a conversion
-//               buffer.  This allocates the buffer.
-////////////////////////////////////////////////////////////////////
-void MovieVideo::
-allocate_conversion_buffer() {
-  if (_conversion_buffer == 0) {
-    _conversion_buffer = new unsigned char[size_x() * size_y() * 4];
-  }
-}
-  
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::fetch_into_bitbucket
-//       Access: Published, Virtual
-//  Description: Discards the next video frame.  Still sets
-//               last_start and next_start.
-//
-//               See fetch_into_buffer for more details.
-////////////////////////////////////////////////////////////////////
-void MovieVideo::
-fetch_into_bitbucket(double time) {
-
-  // This generic implementation is layered on fetch_into_buffer.
-  // It will work for any derived class, so it is never necessary to
-  // redefine this.  It is probably possible to make a faster
-  // implementation, but since this function is rarely used, it
-  // probably isn't worth the trouble.
-
-  allocate_conversion_buffer();
-  fetch_into_buffer(time, _conversion_buffer, false);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::fetch_into_texture
-//       Access: Published, Virtual
-//  Description: Reads the specified video frame into 
-//               the specified texture.
-//
-//               See fetch_into_buffer for more details.
-////////////////////////////////////////////////////////////////////
-void MovieVideo::
-fetch_into_texture(double time, Texture *t, int page) {
-
-  // This generic implementation is layered on fetch_into_buffer.
-  // It will work for any derived class, so it is never necessary to
-  // redefine this.  However, it may be possible to make a faster
-  // implementation that uses fewer intermediate copies, depending
-  // on the capabilities of the underlying codec software.
-
-  nassertv(t->get_x_size() >= size_x());
-  nassertv(t->get_y_size() >= size_y());
-  nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
-  nassertv(t->get_component_width() == 1);
-  nassertv(page < t->get_z_size());
-  
-  PTA_uchar img = t->modify_ram_image();
-  
-  unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
-
-  if (t->get_x_size() == size_x()) {
-    fetch_into_buffer(time, data, t->get_num_components() == 4);
-  } else {
-    allocate_conversion_buffer();
-    fetch_into_buffer(time, _conversion_buffer, t->get_num_components() == 4);
-    int src_stride = size_x() * t->get_num_components();
-    int dst_stride = t->get_x_size() * t->get_num_components();
-    unsigned char *p = _conversion_buffer;
-    for (int y=0; y<size_y(); y++) {
-      memcpy(data, p, src_stride);
-      data += dst_stride;
-      p += src_stride;
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::fetch_into_texture_alpha
+//     Function: MovieVideo::open
 //       Access: Published, Virtual
 //       Access: Published, Virtual
-//  Description: Reads the specified video frame into 
-//               the alpha channel of the supplied texture.  The
-//               RGB channels of the texture are not touched.
-//
-//               See fetch_into_buffer for more details.
+//  Description: Open this video, returning a MovieVideoCursor.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void MovieVideo::
-fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src) {
-
-  // This generic implementation is layered on fetch_into_buffer.
-  // It will work for any derived class, so it is never necessary to
-  // redefine this.  However, it may be possible to make a faster
-  // implementation that uses fewer intermediate copies, depending
-  // on the capabilities of the underlying codec software.
-
-  nassertv(t->get_x_size() >= size_x());
-  nassertv(t->get_y_size() >= size_y());
-  nassertv(t->get_num_components() == 4);
-  nassertv(t->get_component_width() == 1);
-  nassertv(page < t->get_z_size());
-  nassertv((alpha_src >= 0) && (alpha_src <= 4));
-
-  allocate_conversion_buffer();
-  
-  fetch_into_buffer(time, _conversion_buffer, true);
-  
-  PTA_uchar img = t->modify_ram_image();
-  
-  unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
-  
-  int src_stride = size_x() * 4;
-  int dst_stride = t->get_x_size() * 4;
-  if (alpha_src == 0) {
-    unsigned char *p = _conversion_buffer;
-    for (int y=0; y<size_y(); y++) {
-      for (int x=0; x<size_x(); x++) {
-        data[x*4+3] = (p[x*4+0] + p[x*4+1] + p[x*4+2]) / 3;
-      }
-      data += dst_stride;
-      p += src_stride;
-    }
-  } else {
-    alpha_src -= 1;
-    unsigned char *p = _conversion_buffer;
-    for (int y=0; y<size_y(); y++) {
-      for (int x=0; x<size_x(); x++) {
-        data[x*4+3] = p[x*4+alpha_src];
-      }
-      data += dst_stride;
-      p += src_stride;
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::fetch_into_texture_rgb
-//       Access: Published, Virtual
-//  Description: Reads the specified video frame into
-//               the RGB channels of the supplied texture.  The alpha
-//               channel of the texture is not touched.
-//
-//               See fetch_into_buffer for more details.
-////////////////////////////////////////////////////////////////////
-void MovieVideo::
-fetch_into_texture_rgb(double time, Texture *t, int page) {
-
-  // This generic implementation is layered on fetch_into_buffer.
-  // It will work for any derived class, so it is never necessary to
-  // redefine this.  However, it may be possible to make a faster
-  // implementation that uses fewer intermediate copies, depending
-  // on the capabilities of the underlying codec software.
-
-  nassertv(t->get_x_size() >= size_x());
-  nassertv(t->get_y_size() >= size_y());
-  nassertv(t->get_num_components() == 4);
-  nassertv(t->get_component_width() == 1);
-  nassertv(page < t->get_z_size());
-
-  allocate_conversion_buffer();
-  
-  fetch_into_buffer(time, _conversion_buffer, true);
-  
-  PTA_uchar img = t->modify_ram_image();
-  
-  unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
-  
-  int src_stride = size_x() * 4;
-  int dst_stride = t->get_x_size() * 4;
-  unsigned char *p = _conversion_buffer;
-  for (int y=0; y<size_y(); y++) {
-    for (int x=0; x<size_x(); x++) {
-      data[x*4+0] = p[x*4+0];
-      data[x*4+1] = p[x*4+1];
-      data[x*4+2] = p[x*4+2];
-    }
-    data += dst_stride;
-    p += src_stride;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::fetch_into_buffer
-//       Access: Published, Virtual
-//  Description: Reads the specified video frame into the supplied
-//               BGR or BGRA buffer.  The frame's begin and end
-//               times are stored in last_start and next_start.
-//
-//               If the movie reports that it can_seek, you may
-//               also specify a timestamp less than next_start.
-//               Otherwise, you may only specify a timestamp
-//               greater than or equal to next_start.
-//
-//               If the movie reports that it can_seek, it doesn't
-//               mean that it can do so quickly.  It may have to
-//               rewind the movie and then fast forward to the
-//               desired location.  Only if can_seek_fast returns
-//               true can it seek rapidly.
-////////////////////////////////////////////////////////////////////
-void MovieVideo::
-fetch_into_buffer(double time, unsigned char *data, bool bgra) {
-  
-  // The following is the implementation of the null video stream, ie,
-  // a stream of blinking red and blue frames.  This method must be
-  // overridden by the subclass.
-  
-  _last_start = floor(time);
-  _next_start = _last_start + 1;
-
-  if (((int)_last_start) & 1) {
-    data[0] = 255;
-    data[1] = 128;
-    data[2] = 128;
-  } else {
-    data[0] = 128;
-    data[1] = 128;
-    data[2] = 255;
-  }
-  if (bgra) {
-    data[3] = 255;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::make_copy
-//       Access: Published, Virtual
-//  Description: Make a copy of this video with a separate cursor.
-////////////////////////////////////////////////////////////////////
-PT(MovieVideo) MovieVideo::
-make_copy() const {
-  return new MovieVideo();
+PT(MovieVideoCursor) MovieVideo::
+open() {
+  return new MovieVideoCursor(this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: MovieVideo::load
+//     Function: MovieVideo::get
 //       Access: Published, Static
 //       Access: Published, Static
-//  Description: Load a movie from a file.
+//  Description: Obtains a MovieVideo that references a file.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(MovieVideo) MovieVideo::
 PT(MovieVideo) MovieVideo::
-load(const Filename &name) {
+get(const Filename &name) {
 #ifdef HAVE_FFMPEG
 #ifdef HAVE_FFMPEG
   // Someday, I'll probably put a dispatcher here.
   // Someday, I'll probably put a dispatcher here.
   // But for now, just hardwire it to go to FFMPEG.
   // But for now, just hardwire it to go to FFMPEG.
   return new FfmpegVideo(name);
   return new FfmpegVideo(name);
 #else
 #else
-  return NULL;
+  return new MovieVideo("Load-Failure Stub");
 #endif
 #endif
 }
 }
 
 

+ 11 - 39
panda/src/movies/movieVideo.h

@@ -20,8 +20,11 @@
 #define MOVIEVIDEO_H
 #define MOVIEVIDEO_H
 
 
 #include "pandabase.h"
 #include "pandabase.h"
-#include "texture.h"
+#include "namable.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
+#include "typedWritableReferenceCount.h"
+class MovieVideo;
+#include "movieVideoCursor.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : MovieVideo
 //       Class : MovieVideo
@@ -29,50 +32,19 @@
 //               a sequence of video frames.  That could include an
 //               a sequence of video frames.  That could include an
 //               AVI file, a digital camera, or an internet TV station.
 //               AVI file, a digital camera, or an internet TV station.
 //
 //
-//               Thread safety: each individual MovieVideo or
-//               must be owned and accessed by a single thread.
-//               It is OK for two different threads to open
-//               the same file at the same time, as long as they
-//               use separate MovieVideo objects.
+//               The difference between a MovieVideo and a
+//               MovieVideoCursor is like the difference between a
+//               filename and a file handle.  The MovieVideo just
+//               indicates a particular movie.  The MovieVideoCursor
+//               is what allows access.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA_MOVIES MovieVideo : public TypedWritableReferenceCount, public Namable {
 class EXPCL_PANDA_MOVIES MovieVideo : public TypedWritableReferenceCount, public Namable {
 
 
  PUBLISHED:
  PUBLISHED:
   MovieVideo(const string &name = "Blank Video");
   MovieVideo(const string &name = "Blank Video");
   virtual ~MovieVideo();
   virtual ~MovieVideo();
-  INLINE int size_x() const;
-  INLINE int size_y() const;
-  INLINE int get_num_components() const;
-  INLINE int length() const;
-  INLINE bool can_seek() const;
-  INLINE bool can_seek_fast() const;
-  INLINE bool aborted() const;
-  INLINE double last_start() const;
-  INLINE double next_start() const;
-  virtual void fetch_into_bitbucket(double time);
-  virtual void fetch_into_texture(double time, Texture *t, int page);
-  virtual void fetch_into_texture_rgb(double time, Texture *t, int page);
-  virtual void fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src);
-  virtual PT(MovieVideo) make_copy() const;
-  static PT(MovieVideo) load(const Filename &name);
-
- public:
-  virtual void fetch_into_buffer(double time, unsigned char *block, bool bgra);
-  
- private:
-  void allocate_conversion_buffer();
-  unsigned char *_conversion_buffer;
-  
- protected:
-  int _size_x;
-  int _size_y;
-  int _num_components;
-  double _length;
-  bool _can_seek;
-  bool _can_seek_fast;
-  bool _aborted;
-  double _last_start;
-  double _next_start;
+  virtual PT(MovieVideoCursor) open();
+  static PT(MovieVideo) get(const Filename &name);
   
   
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 164 - 0
panda/src/movies/movieVideoCursor.I

@@ -0,0 +1,164 @@
+// Filename: movieVideoCursor.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] .
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::get_source
+//       Access: Published
+//  Description: Get the MovieVideo which this cursor references.
+////////////////////////////////////////////////////////////////////
+INLINE PT(MovieVideo) MovieVideoCursor::
+get_source() const {
+  return _source;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::size_x
+//       Access: Published
+//  Description: Get the horizontal size of the movie.
+////////////////////////////////////////////////////////////////////
+INLINE int MovieVideoCursor::
+size_x() const {
+  return _size_x;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::size_y
+//       Access: Published
+//  Description: Get the vertical size of the movie.
+////////////////////////////////////////////////////////////////////
+INLINE int MovieVideoCursor::
+size_y() const {
+  return _size_y;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::get_num_components
+//       Access: Published
+//  Description: Returns 4 if the movie has an alpha
+//               channel, 3 otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE int MovieVideoCursor::
+get_num_components() const {
+  return _num_components;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::length
+//       Access: Published
+//  Description: Returns the length of the movie.
+//
+//               Some kinds of Movie, such as internet TV station, 
+//               might not have a predictable length.  In that case,
+//               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
+//               into them - usually, they're a second or two long or
+//               short.  When playing such an AVI using the Movie class,
+//               you may see a slightly truncated video, or a slightly
+//               elongated video (padded with black frames).  There are
+//               utilities out there to fix the length values in AVI
+//               files.
+//
+////////////////////////////////////////////////////////////////////
+INLINE double MovieVideoCursor::
+length() const {
+  return _length;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::can_seek
+//       Access: Published
+//  Description: Returns true if the movie can seek.  If this is
+//               true, seeking is still not guaranteed to be fast:
+//               for some movies, seeking is implemented by rewinding
+//               to the beginning and then fast-forwarding to the
+//               desired location.  Even if the movie cannot seek,
+//               the fetch methods can still advance to an arbitrary
+//               location by reading frames and discarding them.
+//               However, to move backward, can_seek must return true.
+////////////////////////////////////////////////////////////////////
+INLINE bool MovieVideoCursor::
+can_seek() const {
+  return _can_seek;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::can_seek_fast
+//       Access: Published
+//  Description: Returns true if seek operations are constant time.
+////////////////////////////////////////////////////////////////////
+INLINE bool MovieVideoCursor::
+can_seek_fast() const {
+  return _can_seek_fast;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::aborted
+//       Access: Published
+//  Description: Returns true if the video 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 video does not
+//               constitute an 'abort' condition.
+////////////////////////////////////////////////////////////////////
+INLINE bool MovieVideoCursor::
+aborted() const {
+  return _aborted;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::last_start
+//       Access: Published
+//  Description: Returns the start time of the last frame you read.
+//
+//               MovieVideoCursor streams have variable frame rates.  Each
+//               frame will specify how long it is to be displayed.
+//               These lengths may not be equal from frame to frame.
+////////////////////////////////////////////////////////////////////
+INLINE double MovieVideoCursor::
+last_start() const {
+  return _last_start;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::next_start
+//       Access: Published
+//  Description: Returns the start time of the next frame you can read.
+//
+//               MovieVideoCursor streams have variable frame rates.  Each
+//               frame will specify how long it is to be displayed.
+//               These lengths may not be equal from frame to frame.
+////////////////////////////////////////////////////////////////////
+INLINE double MovieVideoCursor::
+next_start() const {
+  return _next_start;
+}
+

+ 281 - 0
panda/src/movies/movieVideoCursor.cxx

@@ -0,0 +1,281 @@
+// Filename: movieVideo.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 "movieVideoCursor.h"
+#include "config_movies.h"
+
+TypeHandle MovieVideoCursor::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::Constructor
+//       Access: Public
+//  Description: This constructor returns a null video stream --- a
+//               stream of plain blue and white frames that last one
+//               second each. To get more interesting video, you need
+//               to construct a subclass of this class.
+////////////////////////////////////////////////////////////////////
+MovieVideoCursor::
+MovieVideoCursor(PT(MovieVideo) src) :
+  _source(src),
+  _size_x(1),
+  _size_y(1),
+  _num_components(3),
+  _length(1.0E10),
+  _can_seek(true),
+  _can_seek_fast(true),
+  _aborted(false),
+  _last_start(-1.0),
+  _next_start(0.0)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MovieVideoCursor::
+~MovieVideoCursor() {
+  if (_conversion_buffer != 0) {
+    delete[] _conversion_buffer;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::allocate_conversion_buffer
+//       Access: Private
+//  Description: The generic implementations of fetch_into_texture
+//               and fetch_into_alpha require the use of a conversion
+//               buffer.  This allocates the buffer.
+////////////////////////////////////////////////////////////////////
+void MovieVideoCursor::
+allocate_conversion_buffer() {
+  if (_conversion_buffer == 0) {
+    _conversion_buffer = new unsigned char[size_x() * size_y() * 4];
+  }
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::fetch_into_bitbucket
+//       Access: Published, Virtual
+//  Description: Discards the next video frame.  Still sets
+//               last_start and next_start.
+//
+//               See fetch_into_buffer for more details.
+////////////////////////////////////////////////////////////////////
+void MovieVideoCursor::
+fetch_into_bitbucket(double time) {
+
+  // This generic implementation is layered on fetch_into_buffer.
+  // It will work for any derived class, so it is never necessary to
+  // redefine this.  It is probably possible to make a faster
+  // implementation, but since this function is rarely used, it
+  // probably isn't worth the trouble.
+
+  allocate_conversion_buffer();
+  fetch_into_buffer(time, _conversion_buffer, false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::fetch_into_texture
+//       Access: Published, Virtual
+//  Description: Reads the specified video frame into 
+//               the specified texture.
+//
+//               See fetch_into_buffer for more details.
+////////////////////////////////////////////////////////////////////
+void MovieVideoCursor::
+fetch_into_texture(double time, Texture *t, int page) {
+
+  // This generic implementation is layered on fetch_into_buffer.
+  // It will work for any derived class, so it is never necessary to
+  // redefine this.  However, it may be possible to make a faster
+  // implementation that uses fewer intermediate copies, depending
+  // on the capabilities of the underlying codec software.
+
+  nassertv(t->get_x_size() >= size_x());
+  nassertv(t->get_y_size() >= size_y());
+  nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
+  nassertv(t->get_component_width() == 1);
+  nassertv(page < t->get_z_size());
+  
+  PTA_uchar img = t->modify_ram_image();
+  
+  unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
+
+  if (t->get_x_size() == size_x()) {
+    fetch_into_buffer(time, data, t->get_num_components() == 4);
+  } else {
+    allocate_conversion_buffer();
+    fetch_into_buffer(time, _conversion_buffer, t->get_num_components() == 4);
+    int src_stride = size_x() * t->get_num_components();
+    int dst_stride = t->get_x_size() * t->get_num_components();
+    unsigned char *p = _conversion_buffer;
+    for (int y=0; y<size_y(); y++) {
+      memcpy(data, p, src_stride);
+      data += dst_stride;
+      p += src_stride;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::fetch_into_texture_alpha
+//       Access: Published, Virtual
+//  Description: Reads the specified video frame into 
+//               the alpha channel of the supplied texture.  The
+//               RGB channels of the texture are not touched.
+//
+//               See fetch_into_buffer for more details.
+////////////////////////////////////////////////////////////////////
+void MovieVideoCursor::
+fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src) {
+
+  // This generic implementation is layered on fetch_into_buffer.
+  // It will work for any derived class, so it is never necessary to
+  // redefine this.  However, it may be possible to make a faster
+  // implementation that uses fewer intermediate copies, depending
+  // on the capabilities of the underlying codec software.
+
+  nassertv(t->get_x_size() >= size_x());
+  nassertv(t->get_y_size() >= size_y());
+  nassertv(t->get_num_components() == 4);
+  nassertv(t->get_component_width() == 1);
+  nassertv(page < t->get_z_size());
+  nassertv((alpha_src >= 0) && (alpha_src <= 4));
+
+  allocate_conversion_buffer();
+  
+  fetch_into_buffer(time, _conversion_buffer, true);
+  
+  PTA_uchar img = t->modify_ram_image();
+  
+  unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
+  
+  int src_stride = size_x() * 4;
+  int dst_stride = t->get_x_size() * 4;
+  if (alpha_src == 0) {
+    unsigned char *p = _conversion_buffer;
+    for (int y=0; y<size_y(); y++) {
+      for (int x=0; x<size_x(); x++) {
+        data[x*4+3] = (p[x*4+0] + p[x*4+1] + p[x*4+2]) / 3;
+      }
+      data += dst_stride;
+      p += src_stride;
+    }
+  } else {
+    alpha_src -= 1;
+    unsigned char *p = _conversion_buffer;
+    for (int y=0; y<size_y(); y++) {
+      for (int x=0; x<size_x(); x++) {
+        data[x*4+3] = p[x*4+alpha_src];
+      }
+      data += dst_stride;
+      p += src_stride;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::fetch_into_texture_rgb
+//       Access: Published, Virtual
+//  Description: Reads the specified video frame into
+//               the RGB channels of the supplied texture.  The alpha
+//               channel of the texture is not touched.
+//
+//               See fetch_into_buffer for more details.
+////////////////////////////////////////////////////////////////////
+void MovieVideoCursor::
+fetch_into_texture_rgb(double time, Texture *t, int page) {
+
+  // This generic implementation is layered on fetch_into_buffer.
+  // It will work for any derived class, so it is never necessary to
+  // redefine this.  However, it may be possible to make a faster
+  // implementation that uses fewer intermediate copies, depending
+  // on the capabilities of the underlying codec software.
+
+  nassertv(t->get_x_size() >= size_x());
+  nassertv(t->get_y_size() >= size_y());
+  nassertv(t->get_num_components() == 4);
+  nassertv(t->get_component_width() == 1);
+  nassertv(page < t->get_z_size());
+
+  allocate_conversion_buffer();
+  
+  fetch_into_buffer(time, _conversion_buffer, true);
+  
+  PTA_uchar img = t->modify_ram_image();
+  
+  unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
+  
+  int src_stride = size_x() * 4;
+  int dst_stride = t->get_x_size() * 4;
+  unsigned char *p = _conversion_buffer;
+  for (int y=0; y<size_y(); y++) {
+    for (int x=0; x<size_x(); x++) {
+      data[x*4+0] = p[x*4+0];
+      data[x*4+1] = p[x*4+1];
+      data[x*4+2] = p[x*4+2];
+    }
+    data += dst_stride;
+    p += src_stride;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MovieVideoCursor::fetch_into_buffer
+//       Access: Published, Virtual
+//  Description: Reads the specified video frame into the supplied
+//               BGR or BGRA buffer.  The frame's begin and end
+//               times are stored in last_start and next_start.
+//
+//               If the movie reports that it can_seek, you may
+//               also specify a timestamp less than next_start.
+//               Otherwise, you may only specify a timestamp
+//               greater than or equal to next_start.
+//
+//               If the movie reports that it can_seek, it doesn't
+//               mean that it can do so quickly.  It may have to
+//               rewind the movie and then fast forward to the
+//               desired location.  Only if can_seek_fast returns
+//               true can it seek rapidly.
+////////////////////////////////////////////////////////////////////
+void MovieVideoCursor::
+fetch_into_buffer(double time, unsigned char *data, bool bgra) {
+  
+  // The following is the implementation of the null video stream, ie,
+  // a stream of blinking red and blue frames.  This method must be
+  // overridden by the subclass.
+  
+  _last_start = floor(time);
+  _next_start = _last_start + 1;
+
+  if (((int)_last_start) & 1) {
+    data[0] = 255;
+    data[1] = 128;
+    data[2] = 128;
+  } else {
+    data[0] = 128;
+    data[1] = 128;
+    data[2] = 255;
+  }
+  if (bgra) {
+    data[3] = 255;
+  }
+}
+

+ 101 - 0
panda/src/movies/movieVideoCursor.h

@@ -0,0 +1,101 @@
+// Filename: movieVideoCursor.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 MOVIEVIDEOCURSOR_H
+#define MOVIEVIDEOCURSOR_H
+
+#include "pandabase.h"
+#include "texture.h"
+#include "pointerTo.h"
+class MovieVideoCursor;
+#include "movieVideo.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : MovieVideoCursor
+// Description : A MovieVideo is actually any source that provides
+//               a sequence of video frames.  That could include an
+//               AVI file, a digital camera, or an internet TV station.
+//               A MovieVideoCursor is a handle that lets you read
+//               data sequentially from a MovieVideo.
+//
+//               Thread safety: each individual MovieVideoCursor
+//               must be owned and accessed by a single thread.
+//               It is OK for two different threads to open
+//               the same file at the same time, as long as they
+//               use separate MovieVideoCursor objects.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_MOVIES MovieVideoCursor : public TypedWritableReferenceCount {
+
+ PUBLISHED:
+  MovieVideoCursor(PT(MovieVideo) src);
+  virtual ~MovieVideoCursor();
+  PT(MovieVideo) get_source() const;
+  INLINE int size_x() const;
+  INLINE int size_y() const;
+  INLINE int get_num_components() const;
+  INLINE double length() const;
+  INLINE bool can_seek() const;
+  INLINE bool can_seek_fast() const;
+  INLINE bool aborted() const;
+  INLINE double last_start() const;
+  INLINE double next_start() const;
+  virtual void fetch_into_bitbucket(double time);
+  virtual void fetch_into_texture(double time, Texture *t, int page);
+  virtual void fetch_into_texture_rgb(double time, Texture *t, int page);
+  virtual void fetch_into_texture_alpha(double time, Texture *t, int page, int alpha_src);
+
+ public:
+  virtual void fetch_into_buffer(double time, unsigned char *block, bool bgra);
+  
+ private:
+  void allocate_conversion_buffer();
+  unsigned char *_conversion_buffer;
+  
+ protected:
+  PT(MovieVideo) _source;
+  int _size_x;
+  int _size_y;
+  int _num_components;
+  double _length;
+  bool _can_seek;
+  bool _can_seek_fast;
+  bool _aborted;
+  double _last_start;
+  double _next_start;
+  
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWritableReferenceCount::init_type();
+    register_type(_type_handle, "MovieVideo",
+                  TypedWritableReferenceCount::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 "movieVideoCursor.I"
+
+#endif

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

@@ -1,7 +1,18 @@
 #include "movieVideo.cxx"
 #include "movieVideo.cxx"
+#include "movieVideoCursor.cxx"
+
 #include "movieAudio.cxx"
 #include "movieAudio.cxx"
+#include "movieAudioCursor.cxx"
+
 #include "inkblotVideo.cxx"
 #include "inkblotVideo.cxx"
+#include "inkblotVideoCursor.cxx"
+
 #include "ffmpegAudio.cxx"
 #include "ffmpegAudio.cxx"
+#include "ffmpegAudioCursor.cxx"
+
 #include "ffmpegVideo.cxx"
 #include "ffmpegVideo.cxx"
+#include "ffmpegVideoCursor.cxx"
+
 #include "ffmpegVirtualFile.cxx"
 #include "ffmpegVirtualFile.cxx"
+
 #include "config_movies.cxx"
 #include "config_movies.cxx"

+ 11 - 0
panda/src/pandabase/pandasymbols.h

@@ -60,6 +60,14 @@
   #define EXPTP_FMOD_AUDIO extern
   #define EXPTP_FMOD_AUDIO extern
 #endif
 #endif
 
 
+#ifdef BUILDING_OPENAL_AUDIO
+  #define EXPCL_OPENAL_AUDIO __declspec(dllexport)
+  #define EXPTP_OPENAL_AUDIO
+#else
+  #define EXPCL_OPENAL_AUDIO __declspec(dllimport)
+  #define EXPTP_OPENAL_AUDIO extern
+#endif
+
 #ifdef BUILDING_PANDA
 #ifdef BUILDING_PANDA
   #define EXPCL_PANDA __declspec(dllexport)
   #define EXPCL_PANDA __declspec(dllexport)
   #define EXPTP_PANDA
   #define EXPTP_PANDA
@@ -194,6 +202,9 @@
 #define EXPCL_FMOD_AUDIO
 #define EXPCL_FMOD_AUDIO
 #define EXPTP_FMOD_AUDIO
 #define EXPTP_FMOD_AUDIO
 
 
+#define EXPCL_OPENAL_AUDIO
+#define EXPTP_OPENAL_AUDIO
+
 #define EXPCL_PANDA
 #define EXPCL_PANDA
 #define EXPTP_PANDA
 #define EXPTP_PANDA