From f22866dc67fc1d6a76945bc87c0a6f4700d0e600 Mon Sep 17 00:00:00 2001 From: Philip Saltzman Date: Wed, 27 Apr 2005 22:09:47 +0000 Subject: [PATCH] improvemnts to fmod positional audio handling --- direct/src/showbase/Audio3DManager.py | 351 ++++++++++++++++----- panda/src/audio/audioManager.h | 4 +- panda/src/audio/audioSound.cxx | 22 ++ panda/src/audio/audioSound.h | 15 + panda/src/audio/nullAudioSound.cxx | 18 ++ panda/src/audio/nullAudioSound.h | 4 + panda/src/audiotraits/fmodAudioManager.cxx | 27 +- panda/src/audiotraits/fmodAudioSound.cxx | 61 ++++ panda/src/audiotraits/fmodAudioSound.h | 6 + 9 files changed, 413 insertions(+), 95 deletions(-) diff --git a/direct/src/showbase/Audio3DManager.py b/direct/src/showbase/Audio3DManager.py index 610bde233a..5db2dc356a 100644 --- a/direct/src/showbase/Audio3DManager.py +++ b/direct/src/showbase/Audio3DManager.py @@ -1,80 +1,271 @@ -import pandac.AudioSound -import pandac.AudioManager - -class Audio3DManager: - - def __init__(self, audio_manager, listener_target = None): - self.audio_manager = audio_manager - self.listener_target = listener_target - - self.sound_dict = {} - - - def attachSoundToObject(self, sound, object): - # sound is an AudioSound - # object is any Panda object with coordinates - for known_object in self.sound_dict.keys(): - if self.sound_dict[known_object].count(sound): - # This sound is already attached to something - return 0 - - if not self.sound_dict.has_key(object): - self.sound_dict[object] = [] - - self.sound_dict[object].append(sound) - return 1 - - - def detachSound(self, sound): - for known_object in self.sound_dict.keys(): - if self.sound_dict[known_object].count(sound): - self.sound_dict[known_object].remove(sound) - if len(self.sound_dict[known_object]) == 0: - # if there are no other sounds, don't track - # the object any more - del self.sound_dict[known_object] - return 1 - return 0 - - - def getSoundsOnObject(self, object): - if not self.sound_dict.has_key(object): - return [] - sound_list = [] - sound_list.extend(self.sound_dict[object]) - return sound_list - - - def attachListener(self, object): - self.listener_target = object - return 1 - - - def detachListener(self): - self.listener_target = None - return 1 - - - def update(self): - # Update the positions of all sounds based on the objects - # to which they are attached - for known_object in self.sound_dict.keys(): - tracked_sound = 0 - while tracked_sound < len(self.sound_dict[known_object]): - sound = self.sound_dict[known_object][tracked_sound] - if self.listener_target: - pos = known_object.getPos(self.listener_target) - else: - pos = known_object.getPos() - sound.set3dAttributes(pos[0], pos[1], pos[2], 0,0,0) - tracked_sound += 1 - - # Update the position of the listener based on the object - # to which it is attached - if self.listener_target: - pos = self.listener_target.getPos() - self.audio_manager.audio3dSetListenerAttributes(pos[0], pos[1], pos[2], 0,0,0, 0,1,0, 0,0,1) - else: - self.audio_manager.audio3dSetListenerAttributes(0,0,0, 0,0,0, 0,1,0, 0,0,1) - self.audio_manager.audio3dUpdate() - return 1 +from pandac.VBase3 import VBase3 +from direct.task import Task + +class Audio3DManager: + + def __init__(self, audio_manager, listener_target = None, root = None, + taskPriority = 51): + self.audio_manager = audio_manager + self.listener_target = listener_target + + if (root==None): + self.root = render + else: + self.root = root + + self.sound_dict = {} + self.vel_dict = {} + self.listener_vel = VBase3(0,0,0) + + taskMgr.add(self.update, "Audio3DManager-updateTask", taskPriority) + + def loadSfx(self, name): + """ + Use Audio3DManager.loadSfx to load a sound with 3D positioning enabled + """ + sound = None + if (name): + sound=self.audio_manager.getSound(name,1) + return sound + + def setDistanceFactor(self,factor): + """ + Control the scale that sets the distance units for 3D spacialized audio. + Default is 1.0 which is adjust in panda to be feet. + """ + self.audio_manager.audio3dSetDistanceFactor(factor) + + def getDistanceFactor(self): + """ + Control the scale that sets the distance units for 3D spacialized audio. + Default is 1.0 which is adjust in panda to be feet. + """ + return self.audio_manager.audio3dGetDistanceFactor() + + def setDopplerFactor(self,factor): + """ + Control the presence of the Doppler effect. Default is 1.0 + Exaggerated Doppler, use >1.0 + Diminshed Doppler, use <1.0 + """ + self.audio_manager.audio3dSetDopplerFactor(factor) + + def getDopplerFactor(self): + """ + Control the presence of the Doppler effect. Default is 1.0 + Exaggerated Doppler, use >1.0 + Diminshed Doppler, use <1.0 + """ + return self.audio_manager.audio3dGetDopplerFactor() + + def setDropOffFactor(self,factor): + """ + Exaggerate or diminish the effect of distance on sound. Default is 1.0 + Valid range is 0 to 10 + Faster drop off, use >1.0 + Slower drop off, use <1.0 + """ + self.audio_manager.audio3dSetDropOffFactor(factor) + + def getDropOffFactor(self): + """ + Exaggerate or diminish the effect of distance on sound. Default is 1.0 + Valid range is 0 to 10 + Faster drop off, use >1.0 + Slower drop off, use <1.0 + """ + return self.audio_manager.audio3dGetDropOffFactor() + + def setSoundMinDistance(self,sound,dist): + """ + 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 + """ + sound.set3dMinDistance(dist) + + def getSoundMinDistance(self,sound): + """ + 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 + """ + return sound.get3dMinDistance() + + def setSoundMaxDistance(self,sound,dist): + """ + Controls the maximum distance (in units) that this sound stops falling off. + The sound does not stop at that point, it just doesn't get any quieter. + You should rarely need to adjust this. + Default is 1000000000.0 + """ + sound.set3dMaxDistance(dist) + + def getSoundMaxDistance(self,sound): + """ + Controls the maximum distance (in units) that this sound stops falling off. + The sound does not stop at that point, it just doesn't get any quieter. + You should rarely need to adjust this. + Default is 1000000000.0 + """ + return sound.get3dMaxDistance() + + def setSoundVelocity(self,sound,velocity): + """ + Set the velocity vector (in units/sec) of the sound, for calculating doppler shift. + This is relative to the sound root (probably render). + Default: VBase3(0,0,0) + """ + if not isinstance(velocity, VBase3): + raise TypeError, "Invalid argument 1, expected " + self.vel_dict[sound]=velocity + + def setSoundVelocityAuto(self,sound): + """ + If velocity is set to auto, the velocity will be determined by the + previous position of the object the sound is attached to and the frame dt. + Make sure if you use this method that you remember to clear the previous + transformation between frames. + """ + self.vel_dict[sound]=None + + def getSoundVelocity(self,sound): + """ + Get the velocity of the sound. + """ + if (self.vel_dict.has_key(sound)): + vel = self.vel_dict[sound] + if (vel!=None): + return vel + else: + for known_object in self.sound_dict.keys(): + if self.sound_dict[known_object].count(sound): + return known_object.getPosDelta(self.root)/globalClock.getDt() + return VBase3(0,0,0) + + def setListenerVelocity(self,velocity): + """ + Set the velocity vector (in units/sec) of the listener, for calculating doppler shift. + This is relative to the sound root (probably render). + Default: VBase3(0,0,0) + """ + if not isinstance(velocity, VBase3): + raise TypeError, "Invalid argument 0, expected " + self.listener_vel=velocity + + def setListenerVelocityAuto(self): + """ + If velocity is set to auto, the velocity will be determined by the + previous position of the object the listener is attached to and the frame dt. + Make sure if you use this method that you remember to clear the previous + transformation between frames. + """ + self.listener_vel = None + + def getListenerVelocity(self): + """ + Get the velocity of the listener. + """ + if (self.listener_vel!=None): + return self.listener_vel + elif (self.listener_target!=None): + return self.listener_target.getPosDelta(self.root)/globalClock.getDt() + else: + return VBase3(0,0,0) + + def attachSoundToObject(self, sound, object): + """ + Sound will come from the location of the object it is attached to + """ + # sound is an AudioSound + # object is any Panda object with coordinates + for known_object in self.sound_dict.keys(): + if self.sound_dict[known_object].count(sound): + # This sound is already attached to something + #return 0 + # detach sound + self.sound_dict[known_object].remove(sound) + if len(self.sound_dict[known_object]) == 0: + # if there are no other sounds, don't track + # the object any more + del self.sound_dict[known_object] + + if not self.sound_dict.has_key(object): + self.sound_dict[object] = [] + + self.sound_dict[object].append(sound) + return 1 + + + def detachSound(self, sound): + """ + sound will no longer have it's 3D position updated + """ + for known_object in self.sound_dict.keys(): + if self.sound_dict[known_object].count(sound): + self.sound_dict[known_object].remove(sound) + if len(self.sound_dict[known_object]) == 0: + # if there are no other sounds, don't track + # the object any more + del self.sound_dict[known_object] + return 1 + return 0 + + + def getSoundsOnObject(self, object): + """ + returns a list of sounds attached to an object + """ + if not self.sound_dict.has_key(object): + return [] + sound_list = [] + sound_list.extend(self.sound_dict[object]) + return sound_list + + + def attachListener(self, object): + """ + Sounds will be heard relative to this object. Should probably be the camera. + """ + self.listener_target = object + return 1 + + + def detachListener(self): + """ + Sounds will be heard relative to the root, probably render. + """ + self.listener_target = None + return 1 + + + def update(self,task=None): + """ + Updates position of sounds in the 3D audio system. Will be called automatically + in a task. + """ + # Update the positions of all sounds based on the objects + # to which they are attached + for known_object in self.sound_dict.keys(): + tracked_sound = 0 + while tracked_sound < len(self.sound_dict[known_object]): + sound = self.sound_dict[known_object][tracked_sound] + pos = known_object.getPos(self.root) + vel = self.getSoundVelocity(sound) + sound.set3dAttributes(pos[0], pos[1], pos[2], vel[0],vel[1],vel[2]) + tracked_sound += 1 + + # Update the position of the listener based on the object + # to which it is attached + if self.listener_target: + pos = self.listener_target.getPos(self.root) + 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) + else: + self.audio_manager.audio3dSetListenerAttributes(0,0,0, 0,0,0, 0,1,0, 0,0,1) + self.audio_manager.audio3dUpdate() + return Task.cont diff --git a/panda/src/audio/audioManager.h b/panda/src/audio/audioManager.h index 805a8dc52e..40c5f51f23 100644 --- a/panda/src/audio/audioManager.h +++ b/panda/src/audio/audioManager.h @@ -128,9 +128,10 @@ PUBLISHED: float *fx, float *fy, float *fz, float *ux, float *uy, float *uz); - // Control the "relative distance factor" for 3D spacialized audio. Default is 1.0 + // Control the "relative scale that sets the distance factor" units for 3D spacialized audio. Default is 1.0 // Fmod uses meters internally, so give a float in Units-per meter // Don't know what Miles uses. + // Default is 1.0 which is adjust in panda to be feet. virtual void audio_3d_set_distance_factor(float factor); virtual float audio_3d_get_distance_factor() const; @@ -141,6 +142,7 @@ PUBLISHED: virtual float audio_3d_get_doppler_factor() const; // Exaggerate or diminish the effect of distance on sound. Default is 1.0 + // Valid range is 0 to 10 // Faster drop off, use >1.0 // Slower drop off, use <1.0 virtual void audio_3d_set_drop_off_factor(float factor); diff --git a/panda/src/audio/audioSound.cxx b/panda/src/audio/audioSound.cxx index 0448d36474..87bf29a4f1 100644 --- a/panda/src/audio/audioSound.cxx +++ b/panda/src/audio/audioSound.cxx @@ -50,3 +50,25 @@ void AudioSound:: get_3d_attributes(float *px, float *py, float *pz, float *vx, float *vy, float *vz) { // Intentionally blank. } + +void AudioSound:: +set_3d_min_distance(float dist) { + // Intentionally blank. +} + +float AudioSound:: +get_3d_min_distance() const { + // Intentionally blank. + return 0.0f; +} + +void AudioSound:: +set_3d_max_distance(float dist) { + // Intentionally blank. +} + +float AudioSound:: +get_3d_max_distance() const { + // Intentionally blank. + return 0.0f; +} \ No newline at end of file diff --git a/panda/src/audio/audioSound.h b/panda/src/audio/audioSound.h index 5e74864bc2..b49a3338a2 100644 --- a/panda/src/audio/audioSound.h +++ b/panda/src/audio/audioSound.h @@ -104,6 +104,21 @@ PUBLISHED: float *vx, float *vy, float *vz); + // 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 + virtual void set_3d_min_distance(float dist); + virtual float get_3d_min_distance() const; + + // Controls the maximum distance (in units) that this sound stops falling off. + // The sound does not stop at that point, it just doesn't get any quieter. + // You should rarely need to adjust this. + // Default is 1000000000.0 + virtual void set_3d_max_distance(float dist); + virtual float get_3d_max_distance() const; + enum SoundStatus { BAD, READY, PLAYING }; virtual SoundStatus status() const = 0; diff --git a/panda/src/audio/nullAudioSound.cxx b/panda/src/audio/nullAudioSound.cxx index 30935988c1..4bbbd85c5b 100644 --- a/panda/src/audio/nullAudioSound.cxx +++ b/panda/src/audio/nullAudioSound.cxx @@ -118,6 +118,24 @@ void NullAudioSound::get_3d_attributes(float *px, float *py, float *pz, float *v // Intentionally blank. } +void NullAudioSound::set_3d_min_distance(float dist) { + // Intentionally blank. +} + +float NullAudioSound::get_3d_min_distance() const { + // Intentionally blank. + return 0.0f; +} + +void NullAudioSound::set_3d_max_distance(float dist) { + // Intentionally blank. +} + +float NullAudioSound::get_3d_max_distance() const { + // Intentionally blank. + return 0.0f; +} + AudioSound::SoundStatus NullAudioSound::status() const { return AudioSound::READY; } diff --git a/panda/src/audio/nullAudioSound.h b/panda/src/audio/nullAudioSound.h index 2676a65d23..56ea9a7fef 100644 --- a/panda/src/audio/nullAudioSound.h +++ b/panda/src/audio/nullAudioSound.h @@ -64,6 +64,10 @@ public: 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; AudioSound::SoundStatus status() const; diff --git a/panda/src/audiotraits/fmodAudioManager.cxx b/panda/src/audiotraits/fmodAudioManager.cxx index 933222da58..246f70c5a9 100644 --- a/panda/src/audiotraits/fmodAudioManager.cxx +++ b/panda/src/audiotraits/fmodAudioManager.cxx @@ -59,9 +59,9 @@ FmodAudioManager() { _listener_vel[0] = 0.0f; _listener_vel[1] = 0.0f; _listener_vel[2] = 0.0f; _listener_forward[0] = 0.0f; _listener_forward[1] = 1.0f; _listener_forward[2] = 0.0f; _listener_up[0] = 0.0f; _listener_up[1] = 0.0f; _listener_up[2] = 1.0f; - _distance_factor = audio_distance_factor; - _doppler_factor = audio_doppler_factor; - _drop_off_factor = audio_drop_off_factor; + _distance_factor = .3048f; + _doppler_factor = 1.0f; + _drop_off_factor = 1.0f; _cache_limit = audio_cache_limit; _concurrent_sound_limit = 0; @@ -642,15 +642,6 @@ stop_all_sounds() { void FmodAudioManager:: audio_3d_update() { audio_debug("FmodAudioManager::audio_3d_update()"); - //convert panda coordinates to fmod coordinates - float fmod_pos [] = {_listener_pos[0], _listener_pos[2], _listener_pos[1]}; - float fmod_vel [] = {_listener_vel[0], _listener_vel[2], _listener_vel[1]}; - float fmod_forward [] = {_listener_forward[0], _listener_forward[2], _listener_forward[1]}; - float fmod_up [] = {_listener_up[0], _listener_up[2], _listener_up[1]}; - - FSOUND_3D_Listener_SetAttributes(fmod_pos, fmod_vel, - fmod_forward[0], fmod_forward[1], fmod_forward[2], - fmod_up[0], fmod_up[1], fmod_up[2]); FSOUND_Update(); } @@ -668,7 +659,15 @@ audio_3d_set_listener_attributes(float px, float py, float pz, float vx, float v _listener_forward[0] = fx; _listener_forward[1] = fy; _listener_forward[2] = fz; _listener_up[0] = ux; _listener_up[1] = uy; _listener_up[2] = uz; - //FSOUND_3D_Listener_SetAttributes(_listener_pos, _listener_vel, fx, fz, fy, ux, uz, uy); + //convert panda coordinates to fmod coordinates + float fmod_pos [] = {_listener_pos[0], _listener_pos[2], _listener_pos[1]}; + float fmod_vel [] = {_listener_vel[0], _listener_vel[2], _listener_vel[1]}; + float fmod_forward [] = {_listener_forward[0], _listener_forward[2], _listener_forward[1]}; + float fmod_up [] = {_listener_up[0], _listener_up[2], _listener_up[1]}; + + FSOUND_3D_Listener_SetAttributes(fmod_pos, fmod_vel, + fmod_forward[0], fmod_forward[1], fmod_forward[2], + fmod_up[0], fmod_up[1], fmod_up[2]); } //////////////////////////////////////////////////////////////////// @@ -715,7 +714,7 @@ audio_3d_set_distance_factor(float factor) { } if (_distance_factor != factor){ _distance_factor = factor; - FSOUND_3D_SetDistanceFactor(_distance_factor); + FSOUND_3D_SetDistanceFactor(_distance_factor*3.28f); // convert from feet to meters } } diff --git a/panda/src/audiotraits/fmodAudioSound.cxx b/panda/src/audiotraits/fmodAudioSound.cxx index 875b38a95c..4e16ec57c2 100644 --- a/panda/src/audiotraits/fmodAudioSound.cxx +++ b/panda/src/audiotraits/fmodAudioSound.cxx @@ -79,6 +79,7 @@ FmodAudioSound(FmodAudioManager* manager, FSOUND_STREAM *audio_data, _active(true), _paused(false), _bExclusive(false),_channel(-1) { _pos[0] = 0.0f; _pos[1] = 0.0f; _pos[2] = 0.0f; _vel[0] = 0.0f; _vel[1] = 0.0f; _vel[2] = 0.0f; + _min_dist = 1.0f; _max_dist = 1000000000.0f; nassertv(!file_name.empty()); nassertv(audio_data != NULL); @@ -143,6 +144,12 @@ if (_bExclusive) { if(!FSOUND_3D_SetAttributes(_channel, fmod_pos, fmod_vel)) { audio_error("Unable to set 3d attributes for "<<_file_name<<"!"); } + + if(!FSOUND_3D_SetMinMaxDistance(_channel, _min_dist, _max_dist)) { + //Seems like the return value is documented incorrectly, so this error gets + //needlessly spammed + //audio_error("Unable to set 3d min/max distance for "<<_file_name<<"!"); + } } // Set looping -- unimplemented @@ -424,6 +431,13 @@ set_3d_attributes(float px, float py, float pz, float vx, float vy, float vz) { fmod_audio_debug("Set 3d position and velocity (px="<