From 694873dc37ea7a58f4abc25038ac6f1b54c230e7 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 1 Nov 2006 23:42:15 +0000 Subject: [PATCH] fix fmod for OSX; fix reference count leaks; use model-path as well as sound-path --- panda/src/audio/config_audio.cxx | 8 + panda/src/audio/config_audio.h | 1 + panda/src/audiotraits/Sources.pp | 2 +- panda/src/audiotraits/fmodAudioDSP.cxx | 30 +-- panda/src/audiotraits/fmodAudioManager.cxx | 245 +++++++++++++++----- panda/src/audiotraits/fmodAudioManager.h | 105 +++++---- panda/src/audiotraits/fmodAudioSound.cxx | 137 ++++++----- panda/src/audiotraits/fmodAudioSound.h | 84 ++++--- panda/src/audiotraits/milesAudioManager.cxx | 12 +- 9 files changed, 417 insertions(+), 207 deletions(-) diff --git a/panda/src/audio/config_audio.cxx b/panda/src/audio/config_audio.cxx index 86ec631f89..7e60402bfb 100644 --- a/panda/src/audio/config_audio.cxx +++ b/panda/src/audio/config_audio.cxx @@ -41,6 +41,14 @@ 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 Fmod: //I should note this somewhere. The actual number of sound one could diff --git a/panda/src/audio/config_audio.h b/panda/src/audio/config_audio.h index 2f91bdb29e..220d4b9efb 100644 --- a/panda/src/audio/config_audio.h +++ b/panda/src/audio/config_audio.h @@ -36,6 +36,7 @@ NotifyCategoryDecl(audio, EXPCL_PANDA, EXPTP_PANDA); extern EXPCL_PANDA ConfigVariableBool audio_active; extern EXPCL_PANDA ConfigVariableInt audio_cache_limit; extern EXPCL_PANDA ConfigVariableDouble audio_volume; +extern EXPCL_PANDA ConfigVariableFilename audio_dls_file; //We Need This one. extern EXPCL_PANDA ConfigVariableString audio_library_name; diff --git a/panda/src/audiotraits/Sources.pp b/panda/src/audiotraits/Sources.pp index 8eb7429574..69141ae653 100644 --- a/panda/src/audiotraits/Sources.pp +++ b/panda/src/audiotraits/Sources.pp @@ -26,7 +26,7 @@ #begin lib_target #define TARGET fmod_audio #define BUILD_TARGET $[HAVE_FMODEX] - #define USE_PACKAGES fmod + #define USE_PACKAGES fmodex #define BUILDING_DLL BUILDING_FMOD_AUDIO #define LOCAL_LIBS audio event #define WIN_SYS_LIBS $[WIN_SYS_LIBS] user32.lib advapi32.lib winmm.lib diff --git a/panda/src/audiotraits/fmodAudioDSP.cxx b/panda/src/audiotraits/fmodAudioDSP.cxx index dee21ef7fb..0424b174b5 100644 --- a/panda/src/audiotraits/fmodAudioDSP.cxx +++ b/panda/src/audiotraits/fmodAudioDSP.cxx @@ -51,7 +51,7 @@ FmodAudioDSP(AudioManager *manager, AudioManager::DSP_category cat) { FMOD_DSP_TYPE dsptype = (FMOD_DSP_TYPE)cat; result = _manager->_system->createDSPByType( dsptype, &_dsp); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_system->createDSPByType()", result); set_in_chain(false); @@ -74,10 +74,10 @@ FmodAudioDSP:: FMOD_RESULT result; result = _dsp->remove(); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->remove()", result); result = _dsp->release(); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->release()", result); audio_debug("DSP GONE"); } @@ -98,7 +98,7 @@ reset() { FMOD_RESULT result; result = _dsp->reset(); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->reset()", result); audio_debug("DSP Reset."); } @@ -118,7 +118,7 @@ remove() { FMOD_RESULT result; result = _dsp->remove(); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->remove()", result); audio_debug("DSP Removed from relative effects chain."); } @@ -138,7 +138,7 @@ set_bypass(bool bypass) { FMOD_RESULT result; result = _dsp->setBypass(bypass); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->setBypass()", result); audio_debug("DSP Bypass set to:" << bypass ); } @@ -159,7 +159,7 @@ get_bypass() { bool bypass; result = _dsp->getBypass(&bypass); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->getBypass()", result); return bypass; } @@ -183,7 +183,7 @@ set_parameter(const string &name, float value) { FMOD_RESULT result; result = _dsp->setParameter(parameterIndex, value); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->setParameter()", result); } @@ -203,7 +203,7 @@ get_num_parameters() { int numOfParameters; result = _dsp->getNumParameters(&numOfParameters); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->getNumParameters()", result); return numOfParameters; } @@ -231,7 +231,7 @@ get_parameter_name(int parameterIndex) { float parameterMax; result = _dsp->getParameterInfo(parameterIndex, parameterName, parameterLabel, parameterDescription, parameterDescriptionLength, ¶meterMin, ¶meterMax); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->getParameterInfo()", result); string returnInfo = (parameterName); @@ -275,7 +275,7 @@ get_parameter_description(int parameterIndex) { float parameterMax; result = _dsp->getParameterInfo(parameterIndex, parameterName, parameterLabel, parameterDescription, parameterDescriptionLength, ¶meterMin, ¶meterMax); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->getParameterInfo()", result); return parameterLabel; @@ -304,7 +304,7 @@ get_parameter_min(int parameterIndex) { float parameterMax; result = _dsp->getParameterInfo(parameterIndex, parameterName, parameterLabel, parameterDescription, parameterDescriptionLength, ¶meterMin, ¶meterMax); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->getParameterInfo()", result); return parameterMin; } @@ -332,7 +332,7 @@ get_parameter_max(int parameterIndex) { float parameterMax; result = _dsp->getParameterInfo(parameterIndex, parameterName, parameterLabel, parameterDescription, parameterDescriptionLength, ¶meterMin, ¶meterMax); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->getParameterInfo()", result); return parameterMax; } @@ -361,7 +361,7 @@ get_parameter_value(const string &name) { result = _dsp->getParameter(parameterIndex, ¶meterValue, valuestr, valuestrlen); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->getParameter()", result); return parameterValue; } @@ -404,7 +404,7 @@ get_dsp_name() { int configheight; result = _dsp->getInfo(name, &version, &channels, &configwidth, &configheight); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_dsp->getInfo()", result); string returnInfo = (name); //returnInfo.append(" Version: "); diff --git a/panda/src/audiotraits/fmodAudioManager.cxx b/panda/src/audiotraits/fmodAudioManager.cxx index 840de0e9ce..4b675ae973 100644 --- a/panda/src/audiotraits/fmodAudioManager.cxx +++ b/panda/src/audiotraits/fmodAudioManager.cxx @@ -43,15 +43,31 @@ TypeHandle FmodAudioManager::_type_handle; +FMOD::System *FmodAudioManager::_system; pset FmodAudioManager::_all_managers; +bool FmodAudioManager::_system_is_valid = false; + + +// This sets the distance factor for 3D audio to use feet. +// FMOD uses meters by default. +// Since Panda use feet we need to compensate for that with a factor of 3.28 +// +// This can be overwritten. You just need to call +// audio_3d_set_distance_factor(float factor) and set your new factor. + +float FmodAudioManager::_doppler_factor = 1; +float FmodAudioManager::_distance_factor = 3.28; +float FmodAudioManager::_drop_off_factor = 1; + + //////////////////////////////////////////////////////////////////// // Central dispatcher for audio errors. //////////////////////////////////////////////////////////////////// -void fmod_audio_errcheck(FMOD_RESULT result) { +void fmod_audio_errcheck(const char *context, FMOD_RESULT result) { if (result != 0) { - audio_error("FMOD Error: "<< FMOD_ErrorString(result) ); + audio_error(context << ": " << FMOD_ErrorString(result) ); } } @@ -95,48 +111,60 @@ FmodAudioManager() { _up.x = 0; _up.y = 0; _up.z = 0; + + if (_system == (FMOD::System *)NULL) { + // Create the global FMOD System object. This one object must be + // shared by all FmodAudioManagers (this is particularly true on + // OSX, but the FMOD documentation is unclear as to whether this + // is the intended design on all systems). + + result = FMOD::System_Create(&_system); + fmod_audio_errcheck("FMOD::System_Create()", result); + + // Let check the Version of FMOD to make sure the Headers and Libraries are correct. + result = _system->getVersion(&version); + fmod_audio_errcheck("_system->getVersion()", result); + + if (version < FMOD_VERSION){ + audio_error("You are using an old version of FMOD. This program requires:" << FMOD_VERSION); + } + + //Stick Surround Sound 5.1 thing Here. + if (fmod_use_surround_sound) { + audio_debug("Setting FMOD to use 5.1 Surround Sound."); + result = _system->setSpeakerMode( FMOD_SPEAKERMODE_5POINT1 ); + fmod_audio_errcheck("_system->setSpeakerMode()", result); + } + + //Now we Initialize the System. + result = _system->init(fmod_number_of_sound_channels, FMOD_INIT_NORMAL, 0); + fmod_audio_errcheck("_system->init()", result); + + _system_is_valid = (result == FMOD_OK); + + if (_system_is_valid) { + result = _system->set3DSettings( _doppler_factor, _distance_factor, _drop_off_factor); + fmod_audio_errcheck("_system->set3DSettings()", result); + + result = _system->setFileSystem(open_callback, close_callback, read_callback, seek_callback, -1); + fmod_audio_errcheck("_system->setFileSystem()", result); + } + } + + _is_valid = _system_is_valid; + + memset(&_midi_info, 0, sizeof(_midi_info)); + _midi_info.cbsize = sizeof(_midi_info); + + Filename dls_filename = audio_dls_file; + if (!dls_filename.empty()) { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + vfs->resolve_filename(dls_filename, get_sound_path()) || + vfs->resolve_filename(dls_filename, get_model_path()); - result = FMOD::System_Create(&_system); - fmod_audio_errcheck(result); - - // Let check the Version of FMOD to make sure the Headers and Libraries are correct. - result = _system->getVersion(&version); - fmod_audio_errcheck(result); - - if (version < FMOD_VERSION){ - audio_error("You are using an old version of FMOD. This program requires:" << FMOD_VERSION); + _dlsname = dls_filename.to_os_specific(); + _midi_info.dlsname = _dlsname.c_str(); } - - //Stick Surround Sound 5.1 thing Here. - if (fmod_use_surround_sound) { - audio_debug("Setting FMOD to use 5.1 Surround Sound."); - result = _system->setSpeakerMode( FMOD_SPEAKERMODE_5POINT1 ); - fmod_audio_errcheck(result); - } - - //Now we Initialize the System. - result = _system->init(fmod_number_of_sound_channels, FMOD_INIT_NORMAL, 0); - fmod_audio_errcheck(result); - - if (result == FMOD_OK){ - _is_valid = true; - } else { - _is_valid = false; - } - - // This sets the distance factor for 3D audio to use feet. - // FMOD uses meters by default. - // Since Panda use feet we need to compensate for that with a factor of 3.28 - // - // This can be over written. You just need to call - // audio_3d_set_distance_factor(float factor) and set you new factor. - - _doppler_factor = 1; - _distance_factor = 3.28; - _drop_off_factor = 1; - - result = _system->set3DSettings( _doppler_factor, _distance_factor, _drop_off_factor); - fmod_audio_errcheck( result ); } //////////////////////////////////////////////////////////////////// @@ -158,8 +186,12 @@ FmodAudioManager:: // Remove me from the managers list. _all_managers.erase(this); + if (_all_managers.empty()) { result = _system->release(); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_system->release()", result); + _system = NULL; + _system_is_valid = false; + } } //////////////////////////////////////////////////////////////////// @@ -186,7 +218,8 @@ get_sound(const string &file_name, bool positional) { Filename path = file_name; VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - vfs->resolve_filename(path, get_sound_path()); + vfs->resolve_filename(path, get_sound_path()) || + vfs->resolve_filename(path, get_model_path()); // Build a new AudioSound from the audio data. PT(AudioSound) audioSound = 0; @@ -233,7 +266,7 @@ add_dsp( PT(AudioDSP) x) { return false; } else { result = _system->addDSP( fdsp->_dsp ); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_system->addDSP()", result); _system_dsp.insert(fdsp); fdsp->set_in_chain(true); return true; @@ -256,7 +289,7 @@ remove_dsp(PT(AudioDSP) x) { if ( fdsp->get_in_chain() ) { result = fdsp->_dsp->remove(); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_dsp->remove()", result); _system_dsp.erase(fdsp); @@ -281,7 +314,7 @@ getSpeakerSetup() { int returnMode; result = _system->getSpeakerMode( &speakerMode ); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_system->getSpeakerMode()", result); switch (speakerMode) { case FMOD_SPEAKERMODE_RAW: @@ -343,7 +376,7 @@ setSpeakerSetup(AudioManager::SpeakerModeCategory cat) { FMOD_RESULT result; FMOD_SPEAKERMODE speakerModeType = (FMOD_SPEAKERMODE)cat; result = _system->setSpeakerMode( speakerModeType); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_system->setSpeakerMode()", result); } //////////////////////////////////////////////////////////////////// @@ -378,7 +411,16 @@ get_volume() const { //////////////////////////////////////////////////////////////////// void FmodAudioManager:: set_active(bool active) { - _active = active; + if (_active != active) { + _active = active; + + // Tell our AudioSounds to adjust: + for (SoundSet::iterator i = _all_sounds.begin(); + i != _all_sounds.end(); + ++i) { + (*i)->set_active(_active); + } + } } //////////////////////////////////////////////////////////////////// @@ -398,8 +440,17 @@ get_active() const { //////////////////////////////////////////////////////////////////// void FmodAudioManager:: stop_all_sounds() { - for (SoundSet::iterator i = _all_sounds.begin(); i != _all_sounds.end(); ++i) { + // We have to walk through this list with some care, since stopping + // a sound may also remove it from the set (if there are no other + // references to the sound). + SoundSet::iterator i; + i = _all_sounds.begin(); + while (i != _all_sounds.end()) { + SoundSet::iterator next = i; + ++next; + (*i)->stop(); + i = next; } } @@ -451,7 +502,7 @@ audio_3d_set_listener_attributes(float px, float py, float pz, float vx, float v _up.z = uy; result = _system->set3DListenerAttributes( 0, &_position, &_velocity, &_forward, &_up); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_system->set3DListenerAttributes()", result); } @@ -482,7 +533,7 @@ audio_3d_set_distance_factor(float factor) { _distance_factor = factor; result = _system->set3DSettings( _doppler_factor, _distance_factor, _drop_off_factor); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_system->set3DSettings()", result); } @@ -515,7 +566,7 @@ audio_3d_set_doppler_factor(float factor) { _doppler_factor = factor; result = _system->set3DSettings( _doppler_factor, _distance_factor, _drop_off_factor); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_system->set3DSettings()", result); } @@ -546,7 +597,7 @@ audio_3d_set_drop_off_factor(float factor) { _drop_off_factor = factor; result = _system->set3DSettings( _doppler_factor, _distance_factor, _drop_off_factor); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_system->set3DSettings()", result); } @@ -646,6 +697,92 @@ get_cache_limit() const { return 0; } +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioManager::open_callback +// Access: Private, Static +// Description: A hook into Panda's virtual file system. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioManager:: +open_callback(const char *name, int, unsigned int *file_size, + void **handle, void **user_data) { + if (name == (const char *)NULL || name[0] == '\0') { + // An invalid attempt to open an unnamed file. + return FMOD_ERR_FILE_NOTFOUND; + } + + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + PT(VirtualFile) file = vfs->get_file(Filename(name)); + if (file == (VirtualFile *)NULL) { + return FMOD_ERR_FILE_NOTFOUND; + } + istream *str = file->open_read_file(true); + + (*file_size) = file->get_file_size(str); + (*handle) = (void *)str; + (*user_data) = NULL; + + return FMOD_OK; +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioManager::close_callback +// Access: Private, Static +// Description: A hook into Panda's virtual file system. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioManager:: +close_callback(void *handle, void *user_data) { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + istream *str = (istream *)handle; + vfs->close_read_file(str); + + return FMOD_OK; +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioManager::read_callback +// Access: Private, Static +// Description: A hook into Panda's virtual file system. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioManager:: +read_callback(void *handle, void *buffer, unsigned int size_bytes, + unsigned int *bytes_read, void *user_data) { + istream *str = (istream *)handle; + str->read((char *)buffer, size_bytes); + (*bytes_read) = str->gcount(); + + if (str->eof()) { + if ((*bytes_read) == 0) { + return FMOD_ERR_FILE_EOF; + } else { + // Report the EOF next time. + return FMOD_OK; + } + } if (str->fail()) { + return FMOD_ERR_FILE_BAD; + } else { + return FMOD_OK; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: FmodAudioManager::seek_callback +// Access: Private, Static +// Description: A hook into Panda's virtual file system. +//////////////////////////////////////////////////////////////////// +FMOD_RESULT F_CALLBACK FmodAudioManager:: +seek_callback(void *handle, unsigned int pos, void *user_data) { + istream *str = (istream *)handle; + str->clear(); + str->seekg(pos); + + if (str->fail() && !str->eof()) { + return FMOD_ERR_FILE_COULDNOTSEEK; + } else { + return FMOD_OK; + } +} #endif //] diff --git a/panda/src/audiotraits/fmodAudioManager.h b/panda/src/audiotraits/fmodAudioManager.h index 0ee7522bda..9dc0894eee 100644 --- a/panda/src/audiotraits/fmodAudioManager.h +++ b/panda/src/audiotraits/fmodAudioManager.h @@ -23,43 +23,52 @@ //////////////////////////////////////////////////////////////////// // -//Hello, all future Panda audio code people! This is my errata documentation to -//help any future programmer maintain FMOD and PANDA. +//Hello, all future Panda audio code people! This is my errata +//documentation to help any future programmer maintain FMOD and PANDA. // -//This documentation more then that is needed, but I wanted to go all out, with -//the documentation. Because I was a totally newbie at programming [especially -//with C/C++] this semester I want to make sure future code maintainers have that -//insight I did not when starting on the PANDA project here at the ETC/CMU. +//This documentation more then that is needed, but I wanted to go all +//out, with the documentation. Because I was a totally newbie at +//programming [especially with C/C++] this semester I want to make +//sure future code maintainers have that insight I did not when +//starting on the PANDA project here at the ETC/CMU. // -//As of Spring 2006, Panda's FMOD audio support has been pretty much completely -//rewritten. This has been done so PANDA can use FMOD-EX [or AKA FMOD 4] and some -//of its new features. +//As of Spring 2006, Panda's FMOD audio support has been pretty much +//completely rewritten. This has been done so PANDA can use FMOD-EX +//[or AKA FMOD 4] and some of its new features. // -//First, the FMOD-EX API itself has been completely rewritten compared to previous -//versions. FMOD now handles any type of audio files, wave audio [WAV, AIF, MP3, -//OGG, etc...] or musical file [MID, TRACKERS] as the same type of an object. The -//API has also been structured more like a sound studio, with 'sounds' and -//'channels'. This will be covered more in the FmodAudioSound.h/.cxx sources. +//First, the FMOD-EX API itself has been completely rewritten compared +//to previous versions. FMOD now handles any type of audio files, wave +//audio [WAV, AIF, MP3, OGG, etc...] or musical file [MID, TRACKERS] +//as the same type of an object. The API has also been structured more +//like a sound studio, with 'sounds' and 'channels'. This will be +//covered more in the FmodAudioSound.h/.cxx sources. // -//Second, FMOD now offers virtually unlimited sounds to be played at once via -//their virtual channels system. Actually the theoretical limit is around 4000, -//but that is still a lot. What you need to know about this, is that even thought -//you might only hear 32 sound being played at once, FMOD will keep playing any -//additional sounds, and swap those on virtual channels in and out with those on real channels -//depending on priority, or distance [if you are dealing with 3D audio]. +//Second, FMOD now offers virtually unlimited sounds to be played at +//once via their virtual channels system. Actually the theoretical +//limit is around 4000, but that is still a lot. What you need to know +//about this, is that even thought you might only hear 32 sound being +//played at once, FMOD will keep playing any additional sounds, and +//swap those on virtual channels in and out with those on real +//channels depending on priority, or distance [if you are dealing with +//3D audio]. // -//Third, FMOD's DSP support has been added. So you can now add GLOBAL or SOUND -//specific DSP effects. Right not you can only use FMOD's built in DSP effects. -//But adding support for FMOD's support of VST effects shouldn't be that hard. +//Third, FMOD's DSP support has been added. So you can now add GLOBAL +//or SOUND specific DSP effects. Right not you can only use FMOD's +//built in DSP effects. But adding support for FMOD's support of VST +//effects shouldn't be that hard. // -//As for the FmodManager itself, it is pretty straight forward, I hope. As a -//manager class, it will create the FMOD system with the ‘_system’ variable which -//is an instance of FMOD::SYSTEM. This class is also the one responsible for -//creation of Sounds, DSP, and maintaining the GLOBAL DSP chains [The GLOBAL DSP -//chain is the DSP Chain which affects ALL the sounds]. +//As for the FmodManager itself, it is pretty straight forward, I +//hope. As a manager class, it will create the FMOD system with the +//"_system" variable which is an instance of FMOD::SYSTEM. (Actually, +//we create only one global _system variable now, and share it with +//all outstanding FmodManager objects--this appears to be the way FMOD +//wants to work.) The FmodManager class is also the one responsible +//for creation of Sounds, DSP, and maintaining the GLOBAL DSP chains +//[The GLOBAL DSP chain is the DSP Chain which affects ALL the +//sounds]. // -//Any way that is it for an intro, lets move on to looking at the rest of the -//code. +//Any way that is it for an intro, lets move on to looking at the rest +//of the code. // //////////////////////////////////////////////////////////////////// @@ -82,7 +91,7 @@ class FmodAudioSound; class FmodAudioDSP; -extern void fmod_audio_errcheck(FMOD_RESULT n); +extern void fmod_audio_errcheck(const char *context, FMOD_RESULT n); class EXPCL_FMOD_AUDIO FmodAudioManager : public AudioManager { friend class FmodAudioSound; @@ -163,24 +172,44 @@ class EXPCL_FMOD_AUDIO FmodAudioManager : public AudioManager { virtual unsigned int get_cache_limit() const; //////////////////////////////////////////////////////////////////// +private: + static FMOD_RESULT F_CALLBACK + open_callback(const char *name, int unicode, unsigned int *file_size, + void **handle, void **user_data); + + static FMOD_RESULT F_CALLBACK + close_callback(void *handle, void *user_data); + + static FMOD_RESULT F_CALLBACK + read_callback(void *handle, void *buffer, unsigned int size_bytes, + unsigned int *bytes_read, void *user_data); + + static FMOD_RESULT F_CALLBACK + seek_callback(void *handle, unsigned int pos, void *user_data); + private: - FMOD::System *_system; - + static FMOD::System *_system; static pset _all_managers; + static bool _system_is_valid; + + static float _distance_factor; + static float _doppler_factor; + static float _drop_off_factor; + FMOD_VECTOR _position; FMOD_VECTOR _velocity; FMOD_VECTOR _forward; FMOD_VECTOR _up; + // DLS info for MIDI files + string _dlsname; + FMOD_CREATESOUNDEXINFO _midi_info; + bool _is_valid; bool _active; - float _distance_factor; - float _doppler_factor; - float _drop_off_factor; - // The set of all sounds. Needed only to implement stop_all_sounds. typedef pset SoundSet; SoundSet _all_sounds; @@ -189,8 +218,6 @@ class EXPCL_FMOD_AUDIO FmodAudioManager : public AudioManager { typedef pset DSPSet; DSPSet _system_dsp; - friend class FmodAudioSound; - //////////////////////////////////////////////////////////// //These are needed for Panda's Pointer System. DO NOT ERASE! //////////////////////////////////////////////////////////// diff --git a/panda/src/audiotraits/fmodAudioSound.cxx b/panda/src/audiotraits/fmodAudioSound.cxx index a41e1c49e5..4951e0ce4e 100644 --- a/panda/src/audiotraits/fmodAudioSound.cxx +++ b/panda/src/audiotraits/fmodAudioSound.cxx @@ -45,6 +45,8 @@ FmodAudioSound:: FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { audio_debug("FmodAudioSound::FmodAudioSound() Creating new sound, filename: " << file_name ); + _active = manager->get_active(); + //Local Variables that are needed. FMOD_RESULT result; @@ -73,9 +75,11 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { _channel = 0; _file_name = file_name; + FMOD_CREATESOUNDEXINFO *sound_info = NULL; + //Get the Speaker Mode [Important for later on.] result = _manager->_system->getSpeakerMode( &_speakermode ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_system->getSpeakerMode()", result); // Calculate the approximate uncompressed size of the sound. int size = file_name.get_file_size(); @@ -84,12 +88,20 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { int flag = positional ? FMOD_3D : FMOD_2D; int streamflag = (size > 250000) ? FMOD_CREATESTREAM : FMOD_CREATESAMPLE; - - result = _manager->_system->createSound( file_name.to_os_specific().c_str(), FMOD_SOFTWARE | streamflag | flag , 0, &_sound); + if (ext == "mid") { + streamflag = FMOD_CREATESTREAM; + sound_info = &_manager->_midi_info; + } + + result = _manager->_system->createSound( file_name.c_str(), FMOD_SOFTWARE | streamflag | flag , + sound_info, &_sound); if (result != FMOD_OK) { + audio_error("createSound(" << file_name << "): " << FMOD_ErrorString(result)); + + // We couldn't load the sound file. Create a blank sound record + // instead. char blank_data[100]; FMOD_CREATESOUNDEXINFO exinfo; - audio_error("CreateSound " << file_name << ": " << FMOD_ErrorString(result)); memset(&exinfo, 0, sizeof(exinfo)); memset(blank_data, 0, sizeof(blank_data)); exinfo.cbsize = sizeof(exinfo); @@ -98,7 +110,7 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { exinfo.defaultfrequency = 8000; exinfo.format = FMOD_SOUND_FORMAT_PCM16; result = _manager->_system->createSound( blank_data, FMOD_SOFTWARE | flag | FMOD_OPENMEMORY | FMOD_OPENRAW, &exinfo, &_sound); - fmod_audio_errcheck(result); + fmod_audio_errcheck("createSound (blank)", result); } //This is just to collect the defaults of the sound, so we don't @@ -107,7 +119,7 @@ FmodAudioSound(AudioManager *manager, Filename file_name, bool positional) { //'set_play_rate()' and 'get_play_rate()' methods later; result = _sound->getDefaults( &_sampleFrequency, &_volume , &_balance, &_priority); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->getDefaults()", result); } @@ -128,7 +140,7 @@ FmodAudioSound:: //The Release Sound result = _sound->release(); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->release()", result); } @@ -154,7 +166,7 @@ FMOD_RESULT F_CALLBACK sound_end_callback(FMOD_CHANNEL * channel, unsigned int commanddata1, unsigned int commanddata2) { FmodAudioSound *fsound = (FmodAudioSound*)command; - fsound->unref(); + fsound->_self_ref = fsound; return FMOD_OK; } @@ -170,9 +182,9 @@ stop() { if (_channel != 0) { result =_channel->stop(); if (result == FMOD_OK) { - unref(); + _self_ref.clear(); } - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->stop()", result); } } @@ -187,11 +199,10 @@ set_loop(bool loop) { FMOD_RESULT result; if ( loop ) { result = _sound->setMode(FMOD_LOOP_NORMAL); - fmod_audio_errcheck(result); } else { result = _sound->setMode(FMOD_LOOP_OFF); - fmod_audio_errcheck(result); } + fmod_audio_errcheck("_sound->setMode()", result); } //////////////////////////////////////////////////////////////////// @@ -210,7 +221,7 @@ get_loop() const { bool loopState; result = _sound->getMode( &loopMode ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->getMode()", result); if ( (loopMode & FMOD_LOOP_NORMAL) != 0 ) { loopState = true; @@ -244,11 +255,10 @@ set_loop_count(unsigned long loop_count) { if (numberOfLoops == 0) { result = _sound->setLoopCount( -1 ); - fmod_audio_errcheck(result); } else { result = _sound->setLoopCount( numberOfLoops ); - fmod_audio_errcheck(result); } + fmod_audio_errcheck("_sound->setLoopCount()", result); audio_debug("FmodAudioSound::set_loop_count() Sound's loop count should be set to: " << loop_count); } @@ -268,7 +278,7 @@ get_loop_count() const { unsigned long returnedNumber; result = _sound->getLoopCount( &loop_count ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->getLoopCount()", result); audio_debug("FmodAudioSound::get_loop_count() returning "<< loop_count); @@ -285,34 +295,36 @@ get_loop_count() const { void FmodAudioSound:: set_time(float start_time) { FMOD_RESULT result; - bool playing; int startTime = (int)(start_time * 1000); if (_channel != 0) { // try backing up current sound. result = _channel->setPosition( startTime , FMOD_TIMEUNIT_MS ); - if (result == FMOD_ERR_INVALID_HANDLE) { + if (result == FMOD_ERR_INVALID_HANDLE || + result == FMOD_ERR_CHANNEL_STOLEN) { _channel = 0; + } else { - fmod_audio_errcheck(result); - } - result = _channel->isPlaying(&playing); - if ((result == FMOD_ERR_INVALID_HANDLE) || (playing == false)) { - _channel = 0; - } else { - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->setPosition()", result); + + bool playing; + result = _channel->isPlaying(&playing); + fmod_audio_errcheck("_channel->isPlaying()", result); + if (result != FMOD_OK || !playing) { + _channel = 0; + } } } if (_channel == 0) { result = _manager->_system->playSound(FMOD_CHANNEL_FREE, _sound, true, &_channel); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_system->playSound()", result); result = _channel->setCallback(FMOD_CHANNEL_CALLBACKTYPE_END, sound_end_callback, (int)this); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->setCallback()", result); result = _channel->setPosition( startTime , FMOD_TIMEUNIT_MS ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->setPosition()", result); set_volume_on_channel(); set_play_rate_on_channel(); @@ -320,13 +332,13 @@ set_time(float start_time) { add_dsp_on_channel(); set_3d_attributes_on_channel(); - result = _channel->setPaused(false); - fmod_audio_errcheck(result); + if (_active) { + result = _channel->setPaused(false); + fmod_audio_errcheck("_channel->setPaused()", result); + } - this->ref(); + _self_ref = this; } - - fmod_audio_errcheck(result); } //////////////////////////////////////////////////////////////////// @@ -347,7 +359,7 @@ get_time() const { if (result == FMOD_ERR_INVALID_HANDLE) { return 0.0f; } - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->getPosition()", result); current_time = current_time / 1000; @@ -390,7 +402,7 @@ set_volume_on_channel() { if (result == FMOD_ERR_INVALID_HANDLE) { _channel = 0; } else { - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->setVolume()", result); } } } @@ -459,7 +471,7 @@ set_play_rate_on_channel() { if (result == FMOD_ERR_INVALID_HANDLE) { _channel = 0; } else { - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->setFrequency()", result); } } } @@ -486,7 +498,7 @@ length() const { unsigned int length; result = _sound->getLength( &length, FMOD_TIMEUNIT_MS ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->getLength()", result); length = length / 1000; @@ -532,14 +544,14 @@ set_3d_attributes_on_channel() { FMOD_MODE soundMode; result = _sound->getMode(&soundMode); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->getMode()", result); if ((_channel != 0) && (soundMode & FMOD_3D)) { result = _channel->set3DAttributes( &_location, &_velocity ); if (result == FMOD_ERR_INVALID_HANDLE) { _channel = 0; } else { - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->set3DAttributes()", result); } } } @@ -568,7 +580,7 @@ set_3d_min_distance(float dist) { _min_dist = dist; result = _sound->set3DMinMaxDistance( dist, _max_dist ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->set3DMinMaxDistance()", result); } //////////////////////////////////////////////////////////////////// @@ -593,7 +605,7 @@ set_3d_max_distance(float dist) { _max_dist = dist; result = _sound->set3DMinMaxDistance( _min_dist, dist ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->set3DMinMaxDistance()", result); } //////////////////////////////////////////////////////////////////// @@ -636,10 +648,10 @@ add_dsp( PT(AudioDSP) x) { if ( _channel != 0 ) { result = _channel->isPlaying( &playingState ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->isPlaying()", result); if ( playingState ) { result = _channel->addDSP( fdsp->_dsp ); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_channel->addDSP()", result); } } @@ -665,7 +677,7 @@ add_dsp_on_channel() { if (result == FMOD_ERR_INVALID_HANDLE) { _channel = 0; } else { - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_channel->addDSP()", result); } } } @@ -689,7 +701,7 @@ remove_dsp(PT(AudioDSP) x) { if ( fdsp->get_in_chain() ) { result = fdsp->_dsp->remove(); - fmod_audio_errcheck( result ); + fmod_audio_errcheck("_dsp->remove()", result); _sound_dsp.erase(fdsp); @@ -736,7 +748,7 @@ get_speaker_mix(AudioManager::SpeakerId speaker) { float sideright; result = _channel->getSpeakerMix( &frontleft, &frontright, ¢er, &sub, &backleft, &backright, &sideleft, &sideright ); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->getSpeakerMix()", result); switch(speaker) { case AudioManager::SPK_frontleft: return frontleft; @@ -798,7 +810,7 @@ set_speaker_mix_or_balance_on_channel() { FMOD_MODE soundMode; result = _sound->getMode(&soundMode); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->getMode()", result); if ((_channel != 0) && (( soundMode & FMOD_3D ) == 0)) { if ( _speakermode == FMOD_SPEAKERMODE_STEREO ) { @@ -817,7 +829,7 @@ set_speaker_mix_or_balance_on_channel() { if (result == FMOD_ERR_INVALID_HANDLE) { _channel = 0; } else { - fmod_audio_errcheck(result); + fmod_audio_errcheck("_channel->setSpeakerMix()/setPan()", result); } } } @@ -852,7 +864,7 @@ set_priority(int priority) { _priority = priority; result = _sound->setDefaults( _sampleFrequency, _volume , _balance, _priority); - fmod_audio_errcheck(result); + fmod_audio_errcheck("_sound->setDefaults()", result); } //////////////////////////////////////////////////////////////////// @@ -880,31 +892,32 @@ status() const { //////////////////////////////////////////////////////////////////// // Function: FmodAudioSound::set_active // Access: public -// Description: NOT USED ANYMORE!!! -// -// +// 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 FmodAudioSound:: set_active(bool active) { - audio_debug("NOT USED ANYMORE in FMOD-EX version of PANDA."); + _active = active; + + if (status() == PLAYING) { + // If the sound is (or should be) playing, then pause or unpause + // it in the system. + FMOD_RESULT result = _channel->setPaused(!_active); + fmod_audio_errcheck("_channel->setPaused()", result); + } } //////////////////////////////////////////////////////////////////// // Function: FmodAudioSound::get_active // Access: public -// Description: NOT 'REALLY' USED ANYMORE!!! -// This is actually an layover from the old version of the FMOD code. -// The old version used a weird cache system to keep track of the sounds' states. -// I just converted this method, to return if a sound is 'Playing' or not. -// This is not exactly how the orignal use of this function btu I figured I might -// as well get some use out of it. +// Description: Returns whether the sound has been marked "active". //////////////////////////////////////////////////////////////////// bool FmodAudioSound:: get_active() const { - audio_debug("FmodAudioSound::get_active() Going to get a sound's activity."); - audio_debug("NOT USED ANYMORE in FMOD-EX version of PANDA."); - return false; + return _active; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/audiotraits/fmodAudioSound.h b/panda/src/audiotraits/fmodAudioSound.h index 4ec6f6e1ce..6778267eaf 100644 --- a/panda/src/audiotraits/fmodAudioSound.h +++ b/panda/src/audiotraits/fmodAudioSound.h @@ -23,44 +23,50 @@ // //////////////////////////////////////////////////////////////////// // -//[FIRST READ FmodAudioManager for an Introduction if you haven't already]. +//[FIRST READ FmodAudioManager for an Introduction if you haven't +//already]. // -//Hello, all future Panda audio code people! This is my errata documentation to -//Help any future programmer maintain FMOD and PANDA. +//Hello, all future Panda audio code people! This is my errata +//documentation to Help any future programmer maintain FMOD and PANDA. // -//Well, if you reading this you probably want to know how PANDA deals with sounds -//directly using FMOD-EX. Well I am going to tell you. +//Well, if you reading this you probably want to know how PANDA deals +//with sounds directly using FMOD-EX. Well I am going to tell you. // -//The first thing, you as the programmer have to understand, especially if you -//never have done sound programming before, is how the FMOD-EX API works. +//The first thing, you as the programmer have to understand, +//especially if you never have done sound programming before, is how +//the FMOD-EX API works. // -//With FMOD-EX the guys at Firelight, adopted a model of managing sounds with FMOD -//similar to how a Sound Designer creates sound in a sound studio using SOUNDS -//and CHANNELS. Although this may seem strange at first, if you are not familiar -//with sound programming, there is a very good metaphor you are probably already -//familiar with to explain how FMOD-EX works. +//With FMOD-EX the guys at Firelight, adopted a model of managing +//sounds with FMOD similar to how a Sound Designer creates sound in a +//sound studio using SOUNDS and CHANNELS. Although this may seem +//strange at first, if you are not familiar with sound programming, +//there is a very good metaphor you are probably already familiar with +//to explain how FMOD-EX works. // -//Think of you standard GUI API. Usually a GUI API is made up of two things: -//Windows and Widgets. These correspond to CHANNELS and SOUNDS, where a -//Channel is a Window and a Sound is Widget. Sounds are played within channels, -//and channels don’t exist unless they have something to display. +//Think of you standard GUI API. Usually a GUI API is made up of two +//things: Windows and Widgets. These correspond to CHANNELS and +//SOUNDS, where a Channel is a Window and a Sound is Widget. Sounds +//are played within channels, and channels don't exist unless they +//have something to display. // -//Now why am I explaining all of this? When PANDA was created they set up the -//basic audio classes to handle only the idea of a SOUND. The idea of a -//Channel really wasn't prevalent as in more modern Audio APIs. With this rewrite -//of PANDA to use the FMOD-EX API, the PANDA FmodAudioSound Class, now has to -//handle two different parts of the FMOD-EX API in order to play a sound. +//Now why am I explaining all of this? When PANDA was created they set +//up the basic audio classes to handle only the idea of a SOUND. The +//idea of a Channel really wasn't prevalent as in more modern Audio +//APIs. With this rewrite of PANDA to use the FMOD-EX API, the PANDA +//FmodAudioSound Class, now has to handle two different parts of the +//FMOD-EX API in order to play a sound. // -//SOUND: The object the handles the audio data in form of WAV, AIF, OGG, MID, IT, -//MP3, etc... And CHANNEL: The object that actually plays the sound and -//manipulates it in real time. +//SOUND: The object the handles the audio data in form of WAV, AIF, +//OGG, MID, IT, MP3, etc... And CHANNEL: The object that actually +//plays the sound and manipulates it in real time. // -//Ultimately this isn’t a problem expect for a couple situations when you go to -//play a sound, which I will explain in more detail in that part of the code. All -//that you have to know right now is that Channels in FMOD do not exist -//unless they are playing a sound. And in the PANDA FmodAudioSound API class there -//is only ONE dedicated channel per sound. -//Otherwise there is really nothing to worry about. +//Ultimately this isn't a problem expect for a couple situations when +//you go to play a sound, which I will explain in more detail in that +//part of the code. All that you have to know right now is that +//Channels in FMOD do not exist unless they are playing a sound. And +//in the PANDA FmodAudioSound API class there is only ONE dedicated +//channel per sound. Otherwise there is really nothing to worry +//about. // //////////////////////////////////////////////////////////////////// @@ -126,7 +132,7 @@ class EXPCL_FMOD_AUDIO FmodAudioSound : public AudioSound { void set_play_rate(float play_rate=1.0f); float get_play_rate() const; - const string& get_name() const; + const string &get_name() const; // return: playing time in seconds. float length() const; @@ -151,13 +157,13 @@ class EXPCL_FMOD_AUDIO FmodAudioSound : public AudioSound { virtual float get_speaker_mix(AudioManager::SpeakerId speaker); virtual void set_speaker_mix(float frontleft, float frontright, float center, float sub, float backleft, float backright, float sideleft, float sideright); + void set_active(bool active=true); + bool get_active() const; + //THESE ARE NOT USED ANYMORE. //THEY ARE ONLY HERE BECAUSE THEY are still needed by Miles. //THESE are stubs in FMOD-EX version //////////////////////////////////////////////////////////////////// - void set_active(bool active=true); - bool get_active() const; - void finished(); void set_finished_event(const string& event); const string& get_finished_event() const; @@ -204,6 +210,16 @@ class EXPCL_FMOD_AUDIO FmodAudioSound : public AudioSound { typedef pset DSPSet; DSPSet _sound_dsp; + bool _active; + + // This reference-counting pointer is set to this while the sound is + // playing, and cleared when we get an indication that the sound has + // stopped. This prevents a sound from destructing while it is + // playing. We use a PT instead of managing the reference counts by + // hand to help guard against accidental reference count leaks or + // other mismanagement. + PT(FmodAudioSound) _self_ref; + //THESE AREN'T USED ANYMORE. //THEY ARE ONLY HERE BECAUSE THEY are still need by Miles. //THESE are stubs in FMOD-EX version diff --git a/panda/src/audiotraits/milesAudioManager.cxx b/panda/src/audiotraits/milesAudioManager.cxx index 898dd7da89..2f8227181f 100644 --- a/panda/src/audiotraits/milesAudioManager.cxx +++ b/panda/src/audiotraits/milesAudioManager.cxx @@ -333,7 +333,8 @@ get_sound(const string& file_name, bool) { Filename path = file_name; VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); - vfs->resolve_filename(path, get_sound_path()); + vfs->resolve_filename(path, get_sound_path()) || + vfs->resolve_filename(path, get_model_path()); audio_debug(" resolved file_name is '"<resolve_filename(path, get_sound_path()); + vfs->resolve_filename(path, get_sound_path()) || + vfs->resolve_filename(path, get_model_path()); audio_debug(" path=\""<