diff --git a/direct/src/showbase/Audio3DManager.py b/direct/src/showbase/Audio3DManager.py index 0d5ffa310c..4b72066049 100644 --- a/direct/src/showbase/Audio3DManager.py +++ b/direct/src/showbase/Audio3DManager.py @@ -36,6 +36,7 @@ class Audio3DManager: """ Control the scale that sets the distance units for 3D spacialized audio. 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) @@ -84,9 +85,8 @@ class Audio3DManager: """ Controls the distance (in units) that this sound begins to fall 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) @@ -94,9 +94,7 @@ class Audio3DManager: """ Controls the distance (in units) that this sound begins to fall 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() @@ -267,8 +265,10 @@ class Audio3DManager: # to which it is attached if self.listener_target: 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() - 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: self.audio_manager.audio3dSetListenerAttributes(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1) return Task.cont diff --git a/doc/makepanda/config.in b/doc/makepanda/config.in index 26727c1ecc..6bee6cfc4e 100755 --- a/doc/makepanda/config.in +++ b/doc/makepanda/config.in @@ -69,7 +69,7 @@ want-tk #f want-pstats #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 diff --git a/doc/makepanda/makepanda.py b/doc/makepanda/makepanda.py index 225d11d3a4..7a23047312 100755 --- a/doc/makepanda/makepanda.py +++ b/doc/makepanda/makepanda.py @@ -35,7 +35,7 @@ GENMAN=0 VERSION=0 VERBOSE=1 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", "MAYA6","MAYA65","MAYA7","MAYA8","MAYA85","MAX6","MAX7","MAX8","MAX9", "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 (PkgSelected(opts,"VRPN")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'vrpn/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,"OPENAL")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'openal/include' if (PkgSelected(opts,"NVIDIACG")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'nvidiacg/include' if (PkgSelected(opts,"FFMPEG")): cmd = cmd + ' -I' + THIRDPARTYLIBS + 'ffmpeg/include' if (PkgSelected(opts,"FREETYPE")): cmd = cmd + ' -I/usr/include/freetype2' @@ -1354,8 +1356,13 @@ def CompileLink(dll, obj, opts, ldef): if (PkgSelected(opts,"VRPN")): cmd = cmd + ' ' + THIRDPARTYLIBS + 'vrpn/lib/vrpn.lib' cmd = cmd + ' ' + THIRDPARTYLIBS + 'vrpn/lib/quat.lib' + if (PkgSelected(opts,"FMOD")): + cmd = cmd + ' ' + THIRDPARTYLIBS + 'fmod/lib/fmod.lib' if (PkgSelected(opts,"FMODEX")): 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")): cmd = cmd + ' ' + THIRDPARTYLIBS + 'miles/lib/mss32.lib' 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==".lib"): cmd = cmd + ' built/lib/' + 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,"OPENAL")): cmd = cmd + ' -L' + THIRDPARTYLIBS + 'openal/lib -lopenal -lalut' if (PkgSelected(opts,"NVIDIACG")): cmd = cmd + ' -Lthirdparty/nvidiacg/lib ' if (opts.count("CGGL")): cmd = cmd + " -lCgGL" @@ -1659,7 +1668,9 @@ DTOOL_CONFIG=[ ("HAVE_JPEG", 'UNDEF', 'UNDEF'), ("HAVE_TIFF", 'UNDEF', 'UNDEF'), ("HAVE_VRPN", 'UNDEF', 'UNDEF'), + ("HAVE_FMOD", 'UNDEF', 'UNDEF'), ("HAVE_FMODEX", 'UNDEF', 'UNDEF'), + ("HAVE_OPENAL", 'UNDEF', 'UNDEF'), ("HAVE_NVIDIACG", 'UNDEF', 'UNDEF'), ("HAVE_FREETYPE", 'UNDEF', 'UNDEF'), ("HAVE_FFTW", 'UNDEF', 'UNDEF'), @@ -2744,6 +2755,18 @@ if OMIT.count("FMODEX") == 0: '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: IPATH=['panda/src/audiotraits'] OPTS=['BUILDING_MILES_AUDIO', 'MILES'] diff --git a/dtool/src/parser-inc/avio.h b/dtool/src/parser-inc/avio.h index aa1fe581c7..77bdb9ce81 100644 --- a/dtool/src/parser-inc/avio.h +++ b/dtool/src/parser-inc/avio.h @@ -1,4 +1,3 @@ #ifndef AVIO_H #define AVIO_H #endif - diff --git a/panda/src/audio/config_audio.cxx b/panda/src/audio/config_audio.cxx index c55693081c..ee96c953fe 100644 --- a/panda/src/audio/config_audio.cxx +++ b/panda/src/audio/config_audio.cxx @@ -40,7 +40,11 @@ ConfigVariableString audio_library_name #if defined(HAVE_RAD_MSS) "miles_audio" #elif defined(HAVE_FMODEX) + "fmodex_audio" +#elif defined(HAVE_FMOD) "fmod_audio" +#elif defined(HAVE_OPENAL) + "openal_audio" #else "" #endif @@ -49,23 +53,24 @@ ConfigVariableString audio_library_name ConfigVariableDouble audio_volume ("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: -// 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 ("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.") ); @@ -80,6 +85,14 @@ ConfigVariableBool fmod_use_surround_sound ConfigVariableBool audio_software_midi ("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 ("audio-play-midi", true); diff --git a/panda/src/audio/config_audio.h b/panda/src/audio/config_audio.h index 140cff63fa..865512dfe0 100644 --- a/panda/src/audio/config_audio.h +++ b/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 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: extern EXPCL_PANDA_AUDIO ConfigVariableBool audio_software_midi; diff --git a/panda/src/audiotraits/Sources.pp b/panda/src/audiotraits/Sources.pp index 7a84a138b7..5196e9622e 100644 --- a/panda/src/audiotraits/Sources.pp +++ b/panda/src/audiotraits/Sources.pp @@ -49,6 +49,26 @@ #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 // #define TARGET audio_linux // #define BUILDING_DLL BUILDING_MISC diff --git a/panda/src/audiotraits/config_openalAudio.cxx b/panda/src/audiotraits/config_openalAudio.cxx new file mode 100644 index 0000000000..5ae7c42257 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 //] diff --git a/panda/src/audiotraits/config_openalAudio.h b/panda/src/audiotraits/config_openalAudio.h new file mode 100644 index 0000000000..937b7be3e0 --- /dev/null +++ b/panda/src/audiotraits/config_openalAudio.h @@ -0,0 +1,35 @@ +// Filename: config_openalAudio.h +// Created by: Ben Buchwald +// +//////////////////////////////////////////////////////////////////// +// +// 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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/audiotraits/fmodAudioSound.cxx b/panda/src/audiotraits/fmodAudioSound.cxx index cfd1706ff6..3626c07f58 100644 --- a/panda/src/audiotraits/fmodAudioSound.cxx +++ b/panda/src/audiotraits/fmodAudioSound.cxx @@ -356,10 +356,8 @@ get_time() const { return 0.0f; } 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 ); fmod_audio_errcheck("_sound->getLength()", result); - length = length / 1000; - - return length; + return ((double)length) / 1000.0; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/audiotraits/openalAudioManager.cxx b/panda/src/audiotraits/openalAudioManager.cxx new file mode 100644 index 0000000000..57e4f7fe96 --- /dev/null +++ b/panda/src/audiotraits/openalAudioManager.cxx @@ -0,0 +1,1049 @@ +// Filename: openalAudioManager.cxx +// Created by: Ben Buchwald +// +// +//////////////////////////////////////////////////////////////////// +// +// 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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 + +//OpenAL Headers. +#include +#include + +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="<_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 "<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 "<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=\""<resolve_filename(path, get_sound_path()) || + vfs->resolve_filename(path, get_model_path()); + audio_debug(" resolved file_name is '"<= (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 ib + =_sounds.insert(SoundMap::value_type(path, sd)); + if (!ib.second) { + // The insert failed. + audio_debug(" failed map insert of "<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=\"" + <resolve_filename(path, get_sound_path()) || + vfs->resolve_filename(path, get_model_path()); + + audio_debug(" path=\""<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 \""<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=\"" + < 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=\"" + <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="<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 = "< +#include +#include + +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 SoundMap; + SoundMap _sounds; + int _cache_limit; + + typedef pset AudioSet; + // The offspring of this manager: + AudioSet _sounds_on_loan; + + typedef pset SoundsPlaying; + // The sounds from this manager that are currently playing + SoundsPlaying _sounds_playing; + + // The Least Recently Used mechanism: + typedef pdeque LRU; + 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 Managers; + static Managers *_managers; + + static ALCdevice* _device; + static ALCcontext* _context; + + // cache of openal sources, use only for playing sounds + typedef pset 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__ */ diff --git a/panda/src/audiotraits/openalAudioSound.I b/panda/src/audiotraits/openalAudioSound.I new file mode 100644 index 0000000000..743aab32be --- /dev/null +++ b/panda/src/audiotraits/openalAudioSound.I @@ -0,0 +1,23 @@ +// Filename: openalAudioSound.I +// Created by: Ben Buchwald +// +//////////////////////////////////////////////////////////////////// +// +// 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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + + + + + diff --git a/panda/src/audiotraits/openalAudioSound.cxx b/panda/src/audiotraits/openalAudioSound.cxx new file mode 100644 index 0000000000..ed459f25c6 --- /dev/null +++ b/panda/src/audiotraits/openalAudioSound.cxx @@ -0,0 +1,730 @@ +// Filename: openalAudioSound.cxx +// Created by: Ben Buchwald +// +// +//////////////////////////////////////////////////////////////////// +// +// 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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 \""<_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: "<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="<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="< 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 "<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="<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="<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 //] diff --git a/panda/src/audiotraits/openalAudioSound.h b/panda/src/audiotraits/openalAudioSound.h new file mode 100644 index 0000000000..664c900965 --- /dev/null +++ b/panda/src/audiotraits/openalAudioSound.h @@ -0,0 +1,194 @@ +// Filename: openalAudioSound.h +// Created by: Ben Buchwald +// +// +//////////////////////////////////////////////////////////////////// +// +// 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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + + +#ifndef __OPENAL_AUDIO_SOUND_H__ +#define __OPENAL_AUDIO_SOUND_H__ + +#include "pandabase.h" + +#ifdef HAVE_OPENAL //[ + +#include "audioSound.h" + +#include +#include +#include + +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__ */ + + + + + diff --git a/panda/src/audiotraits/openal_audio_composite.cxx b/panda/src/audiotraits/openal_audio_composite.cxx new file mode 100644 index 0000000000..398b748d6b --- /dev/null +++ b/panda/src/audiotraits/openal_audio_composite.cxx @@ -0,0 +1 @@ +#include "openal_audio_composite1.cxx" diff --git a/panda/src/audiotraits/openal_audio_composite1.cxx b/panda/src/audiotraits/openal_audio_composite1.cxx new file mode 100644 index 0000000000..fbb71156a7 --- /dev/null +++ b/panda/src/audiotraits/openal_audio_composite1.cxx @@ -0,0 +1,5 @@ + +#include "config_openalAudio.cxx" +#include "openalAudioManager.cxx" +#include "openalAudioSound.cxx" + diff --git a/panda/src/grutil/movieTexture.cxx b/panda/src/grutil/movieTexture.cxx index 3cc7223b15..09e3f65393 100644 --- a/panda/src/grutil/movieTexture.cxx +++ b/panda/src/grutil/movieTexture.cxx @@ -19,6 +19,7 @@ #include "pandabase.h" #include "movieVideo.h" +#include "movieVideoCursor.h" #include "movieTexture.h" #include "clockObject.h" #include "config_gobj.h" @@ -49,9 +50,7 @@ MovieTexture:: MovieTexture(PT(MovieVideo) video) : 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 ©) : // Since 'make_copy' can be a slow operation, // I release the read lock before calling make_copy. - pvector color; - pvector alpha; + pvector color; + pvector alpha; { CDReader copy_cdata(copy._cycler); color.resize(copy_cdata->_pages.size()); @@ -129,10 +128,10 @@ MovieTexture(const MovieTexture ©) : cdata->_pages.resize(color.size()); for (int i=0; i<(int)(color.size()); i++) { if (color[i]) { - cdata->_pages[i]._color = color[i]->make_copy(); + cdata->_pages[i]._color = color[i]->get_source()->open(); } if (alpha[i]) { - cdata->_pages[i]._alpha = color[i]->make_copy(); + cdata->_pages[i]._alpha = alpha[i]->get_source()->open(); } } recalculate_image_properties(cdata); @@ -205,7 +204,7 @@ recalculate_image_properties(CDWriter &cdata) { double len = 0.0; for (int i=0; i<_z_size; i++) { - MovieVideo *t = cdata->_pages[i]._color; + MovieVideoCursor *t = cdata->_pages[i]._color; if (t) { if (t->size_x() > x_max) x_max = t->size_x(); 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); } - PT(MovieVideo) color; - PT(MovieVideo) alpha; + PT(MovieVideoCursor) color; + PT(MovieVideoCursor) alpha; - color = MovieVideo::load(fullpath); + color = MovieVideo::get(fullpath)->open(); if (color == 0) { return false; } if (!alpha_fullpath.empty()) { - alpha = MovieVideo::load(alpha_fullpath); + alpha = MovieVideo::get(alpha_fullpath)->open(); if (alpha == 0) { return false; } @@ -296,6 +295,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath, set_loaded_from_image(); set_loop(true); play(); + return true; } //////////////////////////////////////////////////////////////////// @@ -304,7 +304,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath, // Description: Loads movie objects into the texture. //////////////////////////////////////////////////////////////////// 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); @@ -391,8 +391,8 @@ cull_callback(CullTraverser *, const CullTraverserData &) const { } 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 ((offset >= color->next_start())|| ((offset < color->last_start()) && (color->can_seek()))) { diff --git a/panda/src/grutil/movieTexture.h b/panda/src/grutil/movieTexture.h index f7bcd3a3ab..c2f5903ec9 100644 --- a/panda/src/grutil/movieTexture.h +++ b/panda/src/grutil/movieTexture.h @@ -68,13 +68,13 @@ protected: bool header_only, BamCacheRecord *record); virtual bool do_load_one(const PNMImage &pnmimage, const string &name, 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 { public: VideoPage(); - PT(MovieVideo) _color; - PT(MovieVideo) _alpha; + PT(MovieVideoCursor) _color; + PT(MovieVideoCursor) _alpha; }; typedef pvector Pages; diff --git a/panda/src/movies/Sources.pp b/panda/src/movies/Sources.pp index 7d43ee4863..f8343699e3 100644 --- a/panda/src/movies/Sources.pp +++ b/panda/src/movies/Sources.pp @@ -12,28 +12,43 @@ #define SOURCES \ movieAudio.h movieAudio.I \ + movieAudioCursor.h movieAudioCursor.I \ movieVideo.h movieVideo.I \ + movieVideoCursor.h movieVideoCursor.I \ inkblotVideo.h inkblotVideo.I \ + inkblotVideoCursor.h inkblotVideoCursor.I \ ffmpegVideo.h ffmpegVideo.I \ + ffmpegVideoCursor.h ffmpegVideoCursor.I \ ffmpegAudio.h ffmpegAudio.I \ + ffmpegAudioCursor.h ffmpegAudioCursor.I \ ffmpegVirtualFile.h ffmpegVirtualFile.I \ config_movies.h #define INCLUDED_SOURCES \ movieVideo.cxx \ + movieVideoCursor.cxx \ movieAudio.cxx \ + movieAudioCursor.cxx \ inkblotVideo.cxx \ + inkblotVideoCursor.cxx \ ffmpegVideo.cxx \ + ffmpegVideoCursor.cxx \ ffmpegAudio.cxx \ + ffmpegAudioCursor.cxx \ ffmpegVirtualFile.cxx \ config_movies.cxx #define INSTALL_HEADERS \ movieAudio.h movieAudio.I \ + movieAudioCursor.h movieAudioCursor.I \ movieVideo.h movieVideo.I \ + movieVideoCursor.h movieVideoCursor.I \ inkblotVideo.h inkblotVideo.I \ + inkblotVideoCursor.h inkblotVideoCursor.I \ ffmpegVideo.h ffmpegVideo.I \ + ffmpegVideoCursor.h ffmpegVideoCursor.I \ ffmpegAudio.h ffmpegAudio.I \ + ffmpegAudioCursor.h ffmpegAudioCursor.I \ ffmpegVirtualFile.h ffmpegVirtualFile.I \ config_movies.h diff --git a/panda/src/movies/config_movies.cxx b/panda/src/movies/config_movies.cxx index 4786ef3d27..b1043110f4 100644 --- a/panda/src/movies/config_movies.cxx +++ b/panda/src/movies/config_movies.cxx @@ -47,11 +47,16 @@ init_libmovies() { initialized = true; MovieVideo::init_type(); + MovieVideoCursor::init_type(); MovieAudio::init_type(); + MovieAudioCursor::init_type(); InkblotVideo::init_type(); + InkblotVideoCursor::init_type(); #ifdef HAVE_FFMPEG FfmpegVideo::init_type(); + FfmpegVideoCursor::init_type(); FfmpegAudio::init_type(); + FfmpegAudioCursor::init_type(); av_register_all(); FfmpegVirtualFile::register_protocol(); #endif diff --git a/panda/src/movies/config_movies.h b/panda/src/movies/config_movies.h index 4e5c2a9383..147204a9e6 100644 --- a/panda/src/movies/config_movies.h +++ b/panda/src/movies/config_movies.h @@ -24,11 +24,21 @@ #include "configVariableEnum.h" #include "configVariableDouble.h" #include "dconfig.h" + #include "movieVideo.h" +#include "movieVideoCursor.h" + #include "movieAudio.h" +#include "movieAudioCursor.h" + #include "inkblotVideo.h" +#include "inkblotVideoCursor.h" + #include "ffmpegVideo.h" +#include "ffmpegVideoCursor.h" + #include "ffmpegAudio.h" +#include "ffmpegAudioCursor.h" ConfigureDecl(config_movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); NotifyCategoryDecl(movies, EXPCL_PANDA_MOVIES, EXPTP_PANDA_MOVIES); diff --git a/panda/src/movies/ffmpegAudio.cxx b/panda/src/movies/ffmpegAudio.cxx index 7b31e8de38..175407e1c0 100644 --- a/panda/src/movies/ffmpegAudio.cxx +++ b/panda/src/movies/ffmpegAudio.cxx @@ -19,8 +19,7 @@ #ifdef HAVE_FFMPEG #include "ffmpegAudio.h" -#include "avcodec.h" -#include "avformat.h" +#include "ffmpegAudioCursor.h" TypeHandle FfmpegAudio::_type_handle; @@ -32,7 +31,7 @@ TypeHandle FfmpegAudio::_type_handle; FfmpegAudio:: FfmpegAudio(const Filename &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_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 diff --git a/panda/src/movies/ffmpegAudioCursor.h b/panda/src/movies/ffmpegAudioCursor.h new file mode 100644 index 0000000000..2f33b223e0 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/movies/ffmpegVideo.cxx b/panda/src/movies/ffmpegVideo.cxx index 46817028aa..992cc9bdbc 100644 --- a/panda/src/movies/ffmpegVideo.cxx +++ b/panda/src/movies/ffmpegVideo.cxx @@ -20,18 +20,6 @@ #include "ffmpegVideo.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; @@ -43,71 +31,8 @@ TypeHandle FfmpegVideo::_type_handle; FfmpegVideo:: FfmpegVideo(const Filename &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() { - 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 -// 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 diff --git a/panda/src/movies/ffmpegVideo.h b/panda/src/movies/ffmpegVideo.h index e3259843e8..feed81bfac 100644 --- a/panda/src/movies/ffmpegVideo.h +++ b/panda/src/movies/ffmpegVideo.h @@ -20,15 +20,9 @@ #define FFMPEGVIDEO_H #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 @@ -39,32 +33,11 @@ class EXPCL_PANDA_MOVIES FfmpegVideo : public MovieVideo { PUBLISHED: FfmpegVideo(const Filename &name); 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: static TypeHandle get_class_type() { diff --git a/panda/src/movies/ffmpegVideoCursor.I b/panda/src/movies/ffmpegVideoCursor.I new file mode 100644 index 0000000000..4b86d94b11 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/movies/ffmpegVideoCursor.cxx b/panda/src/movies/ffmpegVideoCursor.cxx new file mode 100644 index 0000000000..42ebfbbb12 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/movies/ffmpegVideoCursor.h b/panda/src/movies/ffmpegVideoCursor.h new file mode 100644 index 0000000000..47a11cd2aa --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/movies/ffmpegVirtualFileCursor.I b/panda/src/movies/ffmpegVirtualFileCursor.I new file mode 100644 index 0000000000..77f1b03103 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// diff --git a/panda/src/movies/ffmpegVirtualFileCursor.cxx b/panda/src/movies/ffmpegVirtualFileCursor.cxx new file mode 100644 index 0000000000..228350e41b --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/movies/ffmpegVirtualFileCursor.h b/panda/src/movies/ffmpegVirtualFileCursor.h new file mode 100644 index 0000000000..cd8b12fb73 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 + diff --git a/panda/src/movies/inkblotVideo.cxx b/panda/src/movies/inkblotVideo.cxx index 7b18d9592e..80171e293c 100644 --- a/panda/src/movies/inkblotVideo.cxx +++ b/panda/src/movies/inkblotVideo.cxx @@ -17,39 +17,10 @@ //////////////////////////////////////////////////////////////////// #include "inkblotVideo.h" -#include "config_movies.h" +#include "inkblotVideoCursor.h" 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 // Access: Public @@ -57,24 +28,11 @@ static color colormap[17] = { //////////////////////////////////////////////////////////////////// InkblotVideo:: 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() { - delete[] _cells; - delete[] _cells2; } //////////////////////////////////////////////////////////////////// -// Function: InkblotVideo::fetch_into_buffer +// Function: InkblotVideo::open // Access: Published, Virtual -// Description: See MovieVideo::fetch_into_buffer. +// Description: Open this video, returning a MovieVideoCursor. //////////////////////////////////////////////////////////////////// -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>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 -// Access: Published, Virtual -// Description: Make a copy of this video with a separate cursor. -//////////////////////////////////////////////////////////////////// -PT(MovieVideo) InkblotVideo:: -make_copy() const { - return new InkblotVideo(_size_x, _size_y, _fps); +PT(MovieVideoCursor) InkblotVideo:: +open() { + return new InkblotVideoCursor(this); } diff --git a/panda/src/movies/inkblotVideo.h b/panda/src/movies/inkblotVideo.h index c99a570c47..0587158ec1 100644 --- a/panda/src/movies/inkblotVideo.h +++ b/panda/src/movies/inkblotVideo.h @@ -19,9 +19,9 @@ #ifndef INKBLOTVIDEO_H #define INKBLOTVIDEO_H -#include "pandabase.h" -#include "texture.h" -#include "pointerTo.h" +#include "movieVideo.h" + +class InkblotVideoCursor; //////////////////////////////////////////////////////////////////// // Class : InkblotVideo @@ -33,16 +33,13 @@ class EXPCL_PANDA_MOVIES InkblotVideo : public MovieVideo { PUBLISHED: InkblotVideo(int x, int y, int fps); 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: static TypeHandle get_class_type() { diff --git a/panda/src/movies/inkblotVideoCursor.I b/panda/src/movies/inkblotVideoCursor.I new file mode 100644 index 0000000000..d9b3327abc --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/movies/inkblotVideoCursor.cxx b/panda/src/movies/inkblotVideoCursor.cxx new file mode 100644 index 0000000000..75c969d7b2 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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>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; + } + } + } +} + diff --git a/panda/src/movies/inkblotVideoCursor.h b/panda/src/movies/inkblotVideoCursor.h new file mode 100644 index 0000000000..b7420a01ea --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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 diff --git a/panda/src/movies/movieAudio.I b/panda/src/movies/movieAudio.I index 5d69d00751..91ecc0c5d1 100644 --- a/panda/src/movies/movieAudio.I +++ b/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); -} - diff --git a/panda/src/movies/movieAudio.cxx b/panda/src/movies/movieAudio.cxx index ff99acdb2c..7e826b7bb0 100644 --- a/panda/src/movies/movieAudio.cxx +++ b/panda/src/movies/movieAudio.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "movieAudio.h" +#include "movieAudioCursor.h" TypeHandle MovieAudio::_type_handle; @@ -30,14 +31,7 @@ TypeHandle MovieAudio::_type_handle; //////////////////////////////////////////////////////////////////// MovieAudio:: 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; iget_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; yget_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; yget_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; yget_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; yget_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; yget_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