More work on OpenAL

This commit is contained in:
Josh Yelon 2007-08-22 17:28:34 +00:00
parent 610422eac9
commit 40879d8def
8 changed files with 395 additions and 1094 deletions

View File

@ -23,7 +23,6 @@
#include "config_fmodAudio.h"
#include "fmodAudioManager.h"
#include "fmodAudioSound.h"
#include "fmodAudioDSP.h"
#include "pandaSystem.h"
#include "dconfig.h"
@ -55,7 +54,6 @@ init_libFmodAudio() {
FmodAudioManager::init_type();
FmodAudioSound::init_type();
FmodAudioDSP::init_type();
PandaSystem *ps = PandaSystem::get_global_ptr();
ps->add_system("FMOD");

View File

@ -1,25 +0,0 @@
// Filename: fmodAudioDSP.I
// Created by: Stan Rosenbaum "Staque" - Spring 2006
//
//
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////

View File

@ -1,448 +0,0 @@
// Filename: fmodAudioDSP.cxx
// Created by: Stan Rosenbaum "Staque" - Spring 2006
//
//
////////////////////////////////////////////////////////////////////
//
// 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"
#include "dcast.h"
#ifdef HAVE_FMODEX //[
//Panda Headers
#include "config_audio.h"
#include "fmodAudioDSP.h"
TypeHandle FmodAudioDSP::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::FmodAudioDSP
// Access: Protected
// Description: Constructor
// This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
FmodAudioDSP::
FmodAudioDSP(AudioManager *manager, AudioManager::DSP_category cat) {
// Intentionally blank.
audio_debug("FmodAudioDSP::FmodAudioDSP() Creating new DSP " );
//Local Variables that are needed.
FMOD_RESULT result;
//Assign the values we need
DCAST_INTO_V(_manager, manager);
FMOD_DSP_TYPE dsptype = (FMOD_DSP_TYPE)cat;
result = _manager->_system->createDSPByType( dsptype, &_dsp);
fmod_audio_errcheck("_system->createDSPByType()", result);
set_in_chain(false);
audio_debug("DSP Loaded");
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::Destructor
// Access: Published, Virtual
// Description: DESTRUCTOR!!!
////////////////////////////////////////////////////////////////////
FmodAudioDSP::
~FmodAudioDSP() {
audio_debug("FmodAudioSound::FmodAudioDSP() Destruction!!! " );
//Local Variables that are needed.
FMOD_RESULT result;
result = _dsp->remove();
fmod_audio_errcheck("_dsp->remove()", result);
result = _dsp->release();
fmod_audio_errcheck("_dsp->release()", result);
audio_debug("DSP GONE");
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::reset
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
// [This resets an FMOD DSP to its default values]
////////////////////////////////////////////////////////////////////
void FmodAudioDSP::
reset() {
audio_debug("FmodAudioSound::reset() Reset DSP to default settings." );
//Local Variables that are needed.
FMOD_RESULT result;
result = _dsp->reset();
fmod_audio_errcheck("_dsp->reset()", result);
audio_debug("DSP Reset.");
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::remove
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
// [This removes the DSP from an Effects Chain]
////////////////////////////////////////////////////////////////////
void FmodAudioDSP::
remove() {
audio_debug("FmodAudioSound::remove() Removes a DSP from and effect chain." );
//Local Variables that are needed.
FMOD_RESULT result;
result = _dsp->remove();
fmod_audio_errcheck("_dsp->remove()", result);
audio_debug("DSP Removed from relative effects chain.");
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::set_bypass
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
// [This turns the Bypass for an Effect on and off]/
////////////////////////////////////////////////////////////////////
void FmodAudioDSP::
set_bypass(bool bypass) {
audio_debug("FmodAudioSound::set_bypass() ." );
//Local Variables that are needed.
FMOD_RESULT result;
result = _dsp->setBypass(bypass);
fmod_audio_errcheck("_dsp->setBypass()", result);
audio_debug("DSP Bypass set to:" << bypass );
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_bypass
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
bool FmodAudioDSP::
get_bypass() {
audio_debug("FmodAudioSound::get_bypass() ." );
//Local Variables that are needed.
FMOD_RESULT result;
bool bypass;
result = _dsp->getBypass(&bypass);
fmod_audio_errcheck("_dsp->getBypass()", result);
return bypass;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::set_parameter
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
void FmodAudioDSP::
set_parameter(const string &name, float value) {
int parameterIndex = find_parameter(name);
if (parameterIndex < 0) {
return;
}
//Local Variables that are needed.
FMOD_RESULT result;
result = _dsp->setParameter(parameterIndex, value);
fmod_audio_errcheck("_dsp->setParameter()", result);
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_num_parameters
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
int FmodAudioDSP::
get_num_parameters() {
audio_debug("FmodAudioSound::get_num_parameters() ." );
//Local Variables that are needed.
FMOD_RESULT result;
int numOfParameters;
result = _dsp->getNumParameters(&numOfParameters);
fmod_audio_errcheck("_dsp->getNumParameters()", result);
return numOfParameters;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_parameter_name
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
string FmodAudioDSP::
get_parameter_name(int parameterIndex) {
audio_debug("FmodAudioSound::get_parameter_name()" );
//Local Variables that are needed.
FMOD_RESULT result;
//int parameterIndex;
char parameterName[32];
char parameterLabel[32];
char parameterDescription[32];
int parameterDescriptionLength = 0;
float parameterMin;
float parameterMax;
result = _dsp->getParameterInfo(parameterIndex, parameterName, parameterLabel, parameterDescription, parameterDescriptionLength, &parameterMin, &parameterMax);
fmod_audio_errcheck("_dsp->getParameterInfo()", result);
string returnInfo = (parameterName);
return returnInfo;
//return "";
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_parameter_description
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
// This Method actually returns FMOD's Parameter Label
// Information, and not Description.
// The reason is, that most of the FMOD's Description
// Properties seem to be empty.
// Also the Label sort of serves as as a description by
// return the type of unit the cooresponding parameter
// modifies for a DSP.
// IE. For the Echo. The first parameter is 'Delay'
// and the units for measuring the Delay is in Milliseconds.
// The Label returns Milliseconds letting you know that.
////////////////////////////////////////////////////////////////////
string FmodAudioDSP::
get_parameter_description(int parameterIndex) {
// intentionally blank
audio_debug("FmodAudioSound::get_parameter_description()." );
//Local Variables that are needed.
FMOD_RESULT result;
//int parameterIndex;
char parameterName[32];
char parameterLabel[32];
char parameterDescription[32];
int parameterDescriptionLength = 0;
float parameterMin;
float parameterMax;
result = _dsp->getParameterInfo(parameterIndex, parameterName, parameterLabel, parameterDescription, parameterDescriptionLength, &parameterMin, &parameterMax);
fmod_audio_errcheck("_dsp->getParameterInfo()", result);
return parameterLabel;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_parameter_min
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
float FmodAudioDSP::
get_parameter_min(int parameterIndex) {
audio_debug("FmodAudioSound::get_parameter_min()." );
//Local Variables that are needed.
FMOD_RESULT result;
//int parameterIndex;
char parameterName[32];
char parameterLabel[32];
char parameterDescription[32];
int parameterDescriptionLength = 0;
float parameterMin;
float parameterMax;
result = _dsp->getParameterInfo(parameterIndex, parameterName, parameterLabel, parameterDescription, parameterDescriptionLength, &parameterMin, &parameterMax);
fmod_audio_errcheck("_dsp->getParameterInfo()", result);
return parameterMin;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_parameter_max
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
float FmodAudioDSP::
get_parameter_max(int parameterIndex) {
audio_debug("FmodAudioSound::get_parameter_min()." );
//Local Variables that are needed.
FMOD_RESULT result;
//int parameterIndex;
char parameterName[32];
char parameterLabel[32];
char parameterDescription[32];
int parameterDescriptionLength = 0;
float parameterMin;
float parameterMax;
result = _dsp->getParameterInfo(parameterIndex, parameterName, parameterLabel, parameterDescription, parameterDescriptionLength, &parameterMin, &parameterMax);
fmod_audio_errcheck("_dsp->getParameterInfo()", result);
return parameterMax;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_parameter_value
// Access: Published, Virtual
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
float FmodAudioDSP::
get_parameter_value(const string &name) {
int parameterIndex = find_parameter(name);
if (parameterIndex < 0) {
return 0.0;
}
//Local Variables that are needed.
FMOD_RESULT result;
float parameterValue;
char valuestr[32];
int valuestrlen = 32;
result = _dsp->getParameter(parameterIndex, &parameterValue, valuestr, valuestrlen);
fmod_audio_errcheck("_dsp->getParameter()", result);
return parameterValue;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::find_parameter
// Access: Private
// Description: Convert a parameter name to an fmod parameter index.
////////////////////////////////////////////////////////////////////
int FmodAudioDSP::
find_parameter(const string &name) {
int np = get_num_parameters();
for (int i=0; i<np; i++) {
if ( name == get_parameter_name(i) ) {
audio_debug("FmodAudioSound::find_parameter() returning: " << get_parameter_name(i) << " " << i );
return i;
}
}
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_dsp_name()
// Access: Protected
// Description: This is a thin wrapper around FMOD-EX.
// See the FMOD-EX documentation.
////////////////////////////////////////////////////////////////////
string FmodAudioDSP::
get_dsp_name() {
audio_debug("FmodAudioSound::get_dsp_name()." );
//Local Variables that are needed.
FMOD_RESULT result;
char name[32];
unsigned int version;
int channels;
int configwidth;
int configheight;
result = _dsp->getInfo(name, &version, &channels, &configwidth, &configheight);
fmod_audio_errcheck("_dsp->getInfo()", result);
string returnInfo = (name);
//returnInfo.append(" Version: ");
//returnInfo.append(version);
//returnInfo.append("\n");
return returnInfo;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::get_in_chain()
// Access: Published, Virtual
// Description: This is a functiont to query if a DSP have been assigned
// to the GLOBAL or a SOUND's effect chain.
// This is to make sure you 'remove' an effect from a chain
// before you move it somewhere else or destroy it.
////////////////////////////////////////////////////////////////////
bool FmodAudioDSP::
get_in_chain() {
audio_debug("FmodAudioSound::get_in_chain()." );
return _in_chain;
}
////////////////////////////////////////////////////////////////////
// Function: FmodAudioDSP::set_in_chain()
// Access: Published, Virtual
// Description: This is a functiont to set if a DSP have been assigned
// to the GLOBAL or a SOUND's effect chain.
////////////////////////////////////////////////////////////////////
void FmodAudioDSP::
set_in_chain(bool chain_state) {
audio_debug("FmodAudioSound::set_in_chain()." );
_in_chain = chain_state;
}
#endif //]

View File

@ -1,164 +0,0 @@
// Filename: fmodAudioDSP.h
// Created by: Stan Rosenbaum "Staque" - Spring 2006
//
//
////////////////////////////////////////////////////////////////////
//
// 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 .
//
////////////////////////////////////////////////////////////////////
//[FIRST READ FmodAudioManager and then maybe FmodAudioSound 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.
//
//Finally lets talk about the DSP, or Digital Signal Processing. To the layman,
//these are filters and shaders for sound.
//
//Currently FmodAudioDSP give you access to all of FMODs built in DSP functions.
//
//The enumerated list for the types are found in the 'AudioManager.h, but I will
//repeat it here for good fortune.
//
// enum DSP_category {
// // These enumerants line up one-to-one
// // with the FMOD DSP enumerants.
// DSP_unknown,
// DSP_mixer,
// DSP_oscillator,
// DSP_lowpass,
// DSP_itlowpass,
// DSP_highpass,
// DSP_echo,
// DSP_flange,
// DSP_distortion,
// DSP_normalize,
// DSP_parameq,
// DSP_pitchshift,
// DSP_chorus,
// DSP_reverb,
// DSP_vstplugin,
// DSP_winampplugin,
// DSP_itecho,
// DSP_COUNT
// };
//
//Now, I want to point a couple things out. First, we have to place the above list
//in AudioManager.h because that was the only way to them to be 'PUBLISHED in
//Pandas Python bindings when you build Panda.
//
//Second, you only need to use the 'it#### named DSP effects if you are using
//mod/tracker files. [If you dont know what a mod/tracker file is you probably
//arent using them so dont worry about it.]
//
//I think that is everything, the DSP info was pretty short.
////////////////////////////////////////////////////////////////////
//
#ifndef __FMOD_AUDIO_DSP_H__
#define __FMOD_AUDIO_DSP_H__
#include <pandabase.h>
#ifdef HAVE_FMODEX //[
#include "audioManager.h"
#include "audioDSP.h"
#include <fmod.hpp>
#include <fmod_errors.h>
class EXPCL_FMOD_AUDIO FmodAudioDSP : public AudioDSP {
public:
FmodAudioDSP(AudioManager *mgr, AudioManager::DSP_category);
virtual void reset();
virtual void remove();
virtual void set_bypass(bool bypass);
virtual void set_parameter(const string &name, float value);
virtual float get_parameter_value(const string &name);
virtual bool get_bypass();
virtual int get_num_parameters();
virtual string get_parameter_name(int index);
virtual string get_parameter_description(int index);
virtual float get_parameter_min(int index);
virtual float get_parameter_max(int index);
bool get_in_chain();
void set_in_chain(bool chain_state);
virtual ~FmodAudioDSP();
protected:
virtual string get_dsp_name();
private:
int find_parameter(const string &pn);
bool _in_chain;
FmodAudioManager *_manager;
FMOD::DSP *_dsp;
friend class FmodAudioManager;
friend class FmodAudioSound;
////////////////////////////////////////////////////////////
//These are needed for Panda's Pointer System. DO NOT ERASE!
////////////////////////////////////////////////////////////
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
AudioDSP::init_type();
register_type(_type_handle, "FmodAudioDSP", AudioDSP::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 "fmodAudioDSP.I"
#endif //]
#endif /* __FMOD_AUDIO_DSP_H__ */

View File

@ -80,9 +80,9 @@ PT(AudioManager) Create_AudioManager() {
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::OpenALAudioManager()
// Function: OpenALAudioManager::Constructor
// Access: Public
// Description: Constructor
// Description:
////////////////////////////////////////////////////////////////////
OpenALAudioManager::
OpenALAudioManager() {
@ -164,9 +164,9 @@ OpenALAudioManager() {
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::~OpenALAudioManager
// Function: OpenALAudioManager::Destructor
// Access: Public
// Description: DESTRCUTOR !!!
// Description:
////////////////////////////////////////////////////////////////////
OpenALAudioManager::
~OpenALAudioManager() {
@ -214,22 +214,7 @@ shutdown() {
////////////////////////////////////////////////////////////////////
bool OpenALAudioManager::
is_valid() {
bool check=true;
if (_sounds.size() != _lru.size()) {
audio_debug("-- Error _sounds.size() != _lru.size() --");
check=false;
} else {
LRU::const_iterator i=_lru.begin();
for (; i != _lru.end(); ++i) {
SoundMap::const_iterator smi=_sounds.find(**i);
if (smi == _sounds.end()) {
audio_debug("-- "<<**i<<" in _lru and not in _sounds --");
check=false;
break;
}
}
}
return _is_valid && check;
return _is_valid;
}
////////////////////////////////////////////////////////////////////
@ -245,40 +230,66 @@ make_current() const {
alc_audio_errcheck("alcMakeContextCurrent(_context)",_device);
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::load
// Function: OpenALAudioManager::can_use_audio
// Access: Private
// Description: Reads a sound file and allocates a SoundData pointer
// for it. Returns NULL if the sound file cannot be
// loaded.
// Description: Returns true if the specified MovieAudioCursor
// can be used by this AudioManager. Mostly, this
// involves checking whether or not the format is
// implemented/supported.
////////////////////////////////////////////////////////////////////
PT(OpenALAudioManager::SoundData) OpenALAudioManager::
load(Filename file_name) {
string raw_data;
PT(SoundData) sd = new SoundData(this);
sd->_basename = file_name.get_basename();
make_current();
PT(MovieAudioCursor) source = MovieAudio::get(file_name)->open();
if (source == 0) {
audio_error("Could not load audio file "<<file_name);
return NULL;
}
bool OpenALAudioManager::
can_use_audio(MovieAudioCursor *source) {
int channels = source->audio_channels();
if ((channels != 1)&&(channels != 2)) {
audio_error("Currently, only mono and stereo are supported.");
return NULL;
}
int samples = (int)(source->length() * source->audio_rate());
if (samples > 10000000) {
audio_error("Sound is too long for loading into RAM.");
return NULL;
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::can_load_audio
// Access: Private
// Description: Returns true if the specified MovieAudio can be
// cached into RAM. To be cached, the data must have
// a filename (otherwise, we have no cache key), and it
// must not take too many bytes.
////////////////////////////////////////////////////////////////////
bool OpenALAudioManager::
can_load_audio(MovieAudioCursor *source) {
if (source->get_source()->get_filename().empty()) {
return false;
}
if (source->length() > 60.0) {
return false;
}
int channels = source->audio_channels();
int samples = (int)(source->length() * source->audio_rate());
int bytes = samples * channels * 2;
if (bytes > 200000000) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::load_sound_data
// Access: Private
// Description: Reads a sound file and creates a SoundData.
// When you are done with the SoundData, you need
// to decrement the client count.
////////////////////////////////////////////////////////////////////
OpenALAudioManager::SoundData *OpenALAudioManager::
load_sound_data(MovieAudioCursor *source) {
nassertr(can_use_audio(source), NULL);
nassertr(can_load_audio(source), NULL);
make_current();
Filename path = source->get_source()->get_filename();
SoundData *sd = new SoundData(this, path);
alGetError(); // clear errors
sd->_buffer = 0;
alGenBuffers(1, &sd->_buffer);
@ -287,6 +298,9 @@ load(Filename file_name) {
audio_error("Could not create an OpenAL buffer object");
return NULL;
}
int channels = source->audio_channels();
int samples = (int)(source->length() * source->audio_rate());
PN_int16 *data = new PN_int16[samples * channels];
source->read_samples(samples, data);
@ -299,11 +313,33 @@ load(Filename file_name) {
alDeleteBuffers(1, &sd->_buffer);
return NULL;
}
audio_debug("Loaded "<<file_name<<" Src Len "<<source->length());
double len = sd->get_length();
sd->_rate = source->audio_rate();
sd->_channels = source->audio_channels();
sd->_length = source->length();
_all_sound_data.insert(SoundDataSet::value_type(path, sd));
return sd;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::cached_sound_data
// Access: Private
// Description: Looks in the cache for the specified sound data,
// and returns it if present.
//
// When you are done with the SoundData, you need
// to decrement the client count.
////////////////////////////////////////////////////////////////////
OpenALAudioManager::SoundData *OpenALAudioManager::
cached_sound_data(const Filename &path) {
SoundDataSet::const_iterator si=_all_sound_data.find(path);
if (si != _all_sound_data.end()) {
SoundData *sd = (*si).second;
increment_client_count(sd);
return sd;
}
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::get_sound()
// Access: Public
@ -311,137 +347,76 @@ load(Filename file_name) {
////////////////////////////////////////////////////////////////////
PT(AudioSound) OpenALAudioManager::
get_sound(const string &file_name, bool positional) {
audio_debug("OpenALAudioManager::get_sound(file_name=\""<<file_name<<"\")");
if(!is_valid()) {
audio_debug("invalid OpenALAudioManager returning NullSound");
return get_null_sound();
return get_null_sound();
}
assert(is_valid());
Filename path = file_name;
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->resolve_filename(path, get_sound_path()) ||
vfs->resolve_filename(path, get_model_path());
audio_debug(" resolved file_name is '"<<path<<"'");
PT(SoundData) sd;
// Get the sound, either from the cache or from a loader:
SoundMap::const_iterator si=_sounds.find(path);
if (si != _sounds.end()) {
// ...found the sound in the cache.
sd = (*si).second;
audio_debug(" sound found in pool 0x" << (void*)sd);
} else {
// ...the sound was not found in the cache/pool.
sd = load(path);
if (sd != (SoundData *)NULL) {
while (_sounds.size() >= (unsigned int)_cache_limit) {
uncache_a_sound();
}
// Put it in the pool:
// The following is roughly like: _sounds[path] = sd;
// But, it gives us an iterator into the map.
pair<SoundMap::const_iterator, bool> ib
=_sounds.insert(SoundMap::value_type(path, sd));
if (!ib.second) {
// The insert failed.
audio_debug(" failed map insert of "<<path);
assert(is_valid());
return get_null_sound();
}
// Set si, so that we can get a reference to the path
// for the OpenALAudioSound.
si=ib.first;
if (path.empty()) {
audio_error("get_sound - invalid filename");
return NULL;
}
PT(MovieAudioCursor) cursor;
SoundData *sd = cached_sound_data(path);
if (sd == 0) {
cursor = MovieAudio::get(path)->open();
if (cursor == 0) {
audio_error("Cannot open file: "<<path);
return NULL;
}
if (!can_use_audio(cursor)) {
audio_error("Audio data not supported format: "<<path);
return NULL;
}
if (can_load_audio(cursor)) {
sd = load_sound_data(cursor);
cursor = 0;
}
}
// Create an AudioSound from the sound:
PT(AudioSound) audioSound = 0;
if (sd != (SoundData *)NULL) {
most_recently_used((*si).first);
PT(OpenALAudioSound) openalAudioSound
=new OpenALAudioSound(this, sd, (*si).first, positional);
nassertr(openalAudioSound, 0);
openalAudioSound->set_active(_active);
bool inserted = _sounds_on_loan.insert(openalAudioSound).second;
nassertr(inserted, openalAudioSound.p());
audioSound=openalAudioSound;
PT(OpenALAudioSound) oas =
new OpenALAudioSound(this, path, cursor, sd, positional);
if (oas->get_active()) {
_all_audio_sounds.insert(oas);
PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
return res;
}
audio_debug(" returning 0x" << (void*)audioSound);
assert(is_valid());
return audioSound;
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::uncache_sound
// Access: Public
// Description:
// Description: Deletes a sound from the expiration queue.
// If the sound is actively in use, then the sound
// cannot be deleted, and this function has no effect.
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
uncache_sound(const string& file_name) {
audio_debug("OpenALAudioManager::uncache_sound(file_name=\""
<<file_name<<"\")");
assert(is_valid());
Filename path = file_name;
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
vfs->resolve_filename(path, get_sound_path()) ||
vfs->resolve_filename(path, get_model_path());
audio_debug(" path=\""<<path<<"\"");
SoundMap::iterator i=_sounds.find(path);
if (i != _sounds.end()) {
assert(_lru.size()>0);
LRU::iterator lru_i=find(_lru.begin(), _lru.end(), &(i->first));
assert(lru_i != _lru.end());
_lru.erase(lru_i);
_sounds.erase(i);
SoundDataSet::iterator sdi=_all_sound_data.find(path);
if (sdi != _all_sound_data.end()) {
SoundData *sd = (*sdi).second;
ExpirationQueue::iterator exi =
find(_expiration_queue.begin(), _expiration_queue.end(), sd);
if (exi != _expiration_queue.end()) {
_expiration_queue.erase(exi);
_all_sound_data.erase(sdi);
delete sd;
}
}
assert(is_valid());
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::uncache_a_sound
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
uncache_a_sound() {
audio_debug("OpenALAudioManager::uncache_a_sound()");
assert(is_valid());
// uncache least recently used:
assert(_lru.size()>0);
LRU::reference path=_lru.front();
SoundMap::iterator i = _sounds.find(*path);
assert(i != _sounds.end());
_lru.pop_front();
if (i != _sounds.end()) {
audio_debug(" uncaching \""<<i->first<<"\"");
_sounds.erase(i);
}
assert(is_valid());
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::most_recently_used
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
most_recently_used(const string& path) {
audio_debug("OpenALAudioManager::most_recently_used(path=\""
<<path<<"\")");
LRU::iterator i=find(_lru.begin(), _lru.end(), &path);
if (i != _lru.end()) {
_lru.erase(i);
}
// At this point, path should not exist in the _lru:
assert(find(_lru.begin(), _lru.end(), &path) == _lru.end());
_lru.push_back(&path);
assert(is_valid());
}
////////////////////////////////////////////////////////////////////
@ -451,11 +426,7 @@ most_recently_used(const string& path) {
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
clear_cache() {
audio_debug("OpenALAudioManager::clear_cache()");
if (_is_valid) { assert(is_valid()); }
_sounds.clear();
_lru.clear();
if (_is_valid) { assert(is_valid()); }
discard_excess_cache(0);
}
////////////////////////////////////////////////////////////////////
@ -465,13 +436,8 @@ clear_cache() {
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
set_cache_limit(unsigned int count) {
audio_debug("OpenALAudioManager::set_cache_limit(count="<<count<<")");
assert(is_valid());
while (_lru.size() > count) {
uncache_a_sound();
}
_cache_limit=count;
assert(is_valid());
discard_excess_cache(count);
}
////////////////////////////////////////////////////////////////////
@ -481,8 +447,6 @@ set_cache_limit(unsigned int count) {
////////////////////////////////////////////////////////////////////
unsigned int OpenALAudioManager::
get_cache_limit() const {
audio_debug("OpenALAudioManager::get_cache_limit() returning "
<<_cache_limit);
return _cache_limit;
}
@ -493,13 +457,10 @@ get_cache_limit() const {
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
release_sound(OpenALAudioSound* audioSound) {
audio_debug("OpenALAudioManager::release_sound(audioSound=\""
<<audioSound->get_name()<<"\"), this = " << (void *)this);
AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
nassertv(ai != _sounds_on_loan.end());
_sounds_on_loan.erase(ai);
audio_debug("OpenALAudioManager::release_sound() finished");
AudioSoundSet::iterator ai = _all_audio_sounds.find(audioSound);
if (ai != _all_audio_sounds.end()) {
_all_audio_sounds.erase(ai);
}
}
////////////////////////////////////////////////////////////////////
@ -514,8 +475,8 @@ void OpenALAudioManager::set_volume(float volume) {
_volume = volume;
// Tell our AudioSounds to adjust:
AudioSet::iterator i=_sounds_on_loan.begin();
for (; i!=_sounds_on_loan.end(); ++i) {
AudioSoundSet::iterator i=_all_audio_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) {
(**i).set_volume((**i).get_volume());
}
@ -553,8 +514,8 @@ set_play_rate(float play_rate) {
if (_play_rate!=play_rate) {
_play_rate = play_rate;
// Tell our AudioSounds to adjust:
AudioSet::iterator i=_sounds_on_loan.begin();
for (; i!=_sounds_on_loan.end(); ++i) {
AudioSoundSet::iterator i=_all_audio_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) {
(**i).set_play_rate((**i).get_play_rate());
}
}
@ -583,8 +544,8 @@ set_active(bool active) {
if (_active!=active) {
_active=active;
// Tell our AudioSounds to adjust:
AudioSet::iterator i=_sounds_on_loan.begin();
for (; i!=_sounds_on_loan.end(); ++i) {
AudioSoundSet::iterator i=_all_audio_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) {
(**i).set_active(_active);
}
}
@ -700,8 +661,8 @@ audio_3d_set_distance_factor(float factor) {
al_audio_errcheck("alDopplerFactor()");
}
AudioSet::iterator i=_sounds_on_loan.begin();
for (; i!=_sounds_on_loan.end(); ++i) {
AudioSoundSet::iterator i=_all_audio_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) {
(**i).set_3d_min_distance((**i).get_3d_min_distance());
(**i).set_3d_max_distance((**i).get_3d_max_distance());
}
@ -754,8 +715,8 @@ 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) {
AudioSoundSet::iterator i=_all_audio_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) {
(**i).set_3d_drop_off_factor((**i).get_3d_drop_off_factor());
}
}
@ -774,10 +735,18 @@ audio_3d_get_drop_off_factor() const {
// Function: OpenALAudioManager::starting_sound
// Access:
// Description: Inform the manager that a sound is about to play.
// The manager will add this sound to the table of
// sounds that are playing, and will allocate a source
// to this sound.
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
starting_sound(OpenALAudioSound* audio) {
ALuint source=0;
// If the sound already has a source, we don't need to do anything.
if (audio->_source) {
return;
}
// first give all sounds that have finished a chance to stop, so that these get stopped first
update();
@ -805,9 +774,8 @@ starting_sound(OpenALAudioSound* audio) {
_al_sources->erase(source);
}
assert(!audio->_source);
audio->_source = source;
if (source)
_sounds_playing.insert(audio);
}
@ -923,17 +891,17 @@ cleanup() {
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) {
AudioSoundSet sounds(_all_audio_sounds);
AudioSoundSet::iterator ai;
for (ai = sounds.begin(); ai != sounds.end(); ++ai) {
(*ai)->cleanup();
}
assert(_all_sound_data.size() == _expiration_queue.size());
clear_cache();
nassertv(_active_managers > 0);
--_active_managers;
audio_debug(" _active_managers="<<_active_managers);
if (_active_managers == 0) {
if (_openal_active) {
@ -968,12 +936,6 @@ cleanup() {
_openal_active = false;
}
/*if (_managers) {
delete _managers;
_managers = NULL;
delete _al_sources;
_al_sources = NULL;
}*/
}
_cleanup_required = false;
audio_debug("OpenALAudioManager::cleanup() finished");
@ -985,10 +947,12 @@ cleanup() {
// Description:
////////////////////////////////////////////////////////////////////
OpenALAudioManager::SoundData::
SoundData(OpenALAudioManager* manager) :
SoundData(OpenALAudioManager* manager, const Filename &path) :
_manager(manager),
_path(path),
_buffer(0),
_has_length(false)
_client_count(1),
_length(0.0)
{
}
@ -1009,41 +973,60 @@ OpenALAudioManager::SoundData::
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::SoundData::get_length
// Function: OpenALAudioManager::increment_client_count
// Access: Public
// Description:
// Description: Increments the SoundData's client count. Any
// SoundData that is actively in use (ie, has a client)
// is removed entirely from the expiration queue.
////////////////////////////////////////////////////////////////////
float OpenALAudioManager::SoundData::
get_length() {
int freq,bits,channels,size;
if (!_has_length) {
// Time to determine the length of the file.
audio_debug("Computing length of " << _basename);
_manager->make_current();
alGetError(); // clear errors
alGetBufferi(_buffer,AL_FREQUENCY,&freq);
audio_debug("Frequency = "<<freq);
al_audio_errcheck("alGetBufferi(_buffer,AL_FREQUENCY)");
alGetBufferi(_buffer,AL_BITS,&bits);
audio_debug("Bits = "<<bits);
al_audio_errcheck("alGetBufferi(_buffer,AL_BITS)");
alGetBufferi(_buffer,AL_CHANNELS,&channels);
audio_debug("Channels = "<<channels);
al_audio_errcheck("alGetBufferi(_buffer,AL_CHANNELS)");
alGetBufferi(_buffer,AL_SIZE,&size);
audio_debug("Size = "<<size);
al_audio_errcheck("alGetBufferi(_buffer,AL_SIZE)");
_length = ((float)size)/channels/((float)bits/8)/freq;
audio_debug("Length = "<<_length);
_has_length = true;
void OpenALAudioManager::
increment_client_count(SoundData *sd) {
audio_debug("Incrementing client count: " << sd->_path);
sd->_client_count += 1;
if (sd->_client_count == 1) {
audio_debug("Removing from expiration queue: " << sd->_path);
ExpirationQueue::iterator p=find(_expiration_queue.begin(), _expiration_queue.end(), sd);
assert(p != _expiration_queue.end());
_expiration_queue.erase(p);
}
}
return _length;
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::decrement_client_count
// Access: Public
// Description: Decrements the SoundData's client count. Sounds
// that are no longer in use (ie, have no clients)
// go into the expiration queue. When the expiration
// queue reaches the cache limit, the first item on
// the queue is freed.
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
decrement_client_count(SoundData *sd) {
audio_debug("Decrementing client count: " << sd->_path);
sd->_client_count -= 1;
if (sd->_client_count == 0) {
audio_debug("Adding to expiration queue: " << sd->_path);
_expiration_queue.push_back(sd);
discard_excess_cache(_cache_limit);
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::discard_excess_cache
// Access: Public
// Description: Discards sounds from the sound cache until the
// number of sounds remaining is under the limit.
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
discard_excess_cache(int limit) {
while (((int)_expiration_queue.size()) > limit) {
SoundData *sd = _expiration_queue.front();
audio_debug("Deleting head of sound cache: " << sd->_path);
assert(sd->_client_count == 0);
_expiration_queue.pop_front();
_all_sound_data.erase(sd->_path);
delete sd;
}
}
#endif //]

View File

@ -28,17 +28,16 @@
#include "pset.h"
#include "pmap.h"
#include "pdeque.h"
#include "movieAudioCursor.h"
//The Includes needed for OpenAL
#include <al.h>
#include <alc.h>
#include <alut.h>
class OpenALAudioSound;
extern void al_audio_errcheck(const char *context);
extern void alc_audio_errcheck(const char *context,ALCdevice* device);
extern void alut_audio_errcheck(const char *context);
class EXPCL_OPENAL_AUDIO OpenALAudioManager : public AudioManager {
class SoundData;
@ -116,51 +115,63 @@ class EXPCL_OPENAL_AUDIO OpenALAudioManager : public AudioManager {
private:
void make_current() const;
PT(SoundData) load(Filename file_name);
bool can_use_audio(MovieAudioCursor *source);
bool can_load_audio(MovieAudioCursor *source);
SoundData *cached_sound_data(const Filename &file_name);
SoundData *load_sound_data(MovieAudioCursor *source);
// Tell the manager that the sound dtor was called.
void release_sound(OpenALAudioSound* audioSound);
void increment_client_count(SoundData *sd);
void decrement_client_count(SoundData *sd);
void discard_excess_cache(int limit);
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 {
// An AudioSound that uses a SoundData is called a "client"
// of the SoundData. The SoundData keeps track of how
// many clients are using it. When the number of clients
// drops to zero, the SoundData is no longer in use. The
// expiration queue is a list of all SoundData that aren't
// in use, in least-recently-used order. If a SoundData
// in the expiration queue gains a new client, it is removed
// from the expiration queue. When the number of sounds
// in the expiration queue exceeds the cache limit, the
// first sound in the expiration queue is purged.
class SoundData {
public:
SoundData(OpenALAudioManager* manager);
SoundData(OpenALAudioManager* manager, const Filename &path);
~SoundData();
float get_length();
OpenALAudioManager* _manager;
string _basename;
Filename _path;
ALuint _buffer;
bool _has_length;
float _length; // in seconds.
double _length;
int _rate;
int _channels;
int _client_count;
};
typedef pmap<string, PT(SoundData) > SoundMap;
SoundMap _sounds;
int _cache_limit;
typedef pset<OpenALAudioSound* > AudioSet;
// The offspring of this manager:
AudioSet _sounds_on_loan;
typedef pset<OpenALAudioSound* > SoundsPlaying;
// The sounds from this manager that are currently playing
typedef pmap<string, SoundData *> SoundDataSet;
SoundDataSet _all_sound_data;
typedef pset<OpenALAudioSound *> AudioSoundSet;
AudioSoundSet _all_audio_sounds;
typedef pdeque<SoundData *> ExpirationQueue;
ExpirationQueue _expiration_queue;
typedef pset<OpenALAudioSound *> SoundsPlaying;
SoundsPlaying _sounds_playing;
// The Least Recently Used mechanism:
typedef pdeque<const string* > LRU;
LRU _lru;
// State:
int _cache_limit;
float _volume;
float _play_rate;
bool _active;

View File

@ -38,26 +38,31 @@ TypeHandle OpenALAudioSound::_type_handle;
#endif //]
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::OpenALAudioSound
// Access: public
// Description: Constructor
// All sound will DEFAULT load as a 2D sound unless
// otherwise specified.
// Function: OpenALAudioSound::Constructor
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
OpenALAudioSound::
OpenALAudioSound(OpenALAudioManager* manager,
OpenALAudioManager::SoundData *sd, string file_name, bool positional)
: _sd(sd), _source(0), _manager(manager), _file_name(file_name),
_volume(1.0f), _balance(0), _play_rate(1.0),
_loop_count(1), _pause_time(0.0),
_active(true), _paused(false) {
nassertv(sd != NULL);
nassertv(!file_name.empty());
audio_debug("OpenALAudioSound(manager=0x"<<(void*)&manager
<<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
const Filename &path,
PT(MovieAudioCursor) stream,
OpenALAudioManager::SoundData *sample,
bool positional) :
_sample(sample),
_stream(stream),
_source(0),
_manager(manager),
_path(path),
_volume(1.0f),
_balance(0),
_play_rate(1.0),
_loop_count(1),
_active(true),
_paused(false)
{
//Inits 3D Attributes
_location[0] = 0;
_location[1] = 0;
_location[2] = 0;
@ -68,127 +73,61 @@ OpenALAudioSound(OpenALAudioManager* manager,
_min_dist = 3.28f; _max_dist = 1000000000.0f;
_drop_off_factor = 1.0f;
_buffer = _sd->_buffer;
// don't assign source until we play since sources are limited
/*
// Create a source to play the buffer
alGetError(); // clear errors
alGenSources(1,&_source);
al_audio_errcheck("alGenSources()");
// Assign the buffer to the source
alSourcei(_source,AL_BUFFER,_buffer);
al_audio_errcheck("alSourcei(_source,AL_BUFFER,_buffer)");
*/
// nonpositional sources are made relative to the listener so they don't move
_positional = positional;
if (_positional) {
int nChannels=1;
alGetBufferi(_buffer,AL_CHANNELS,&nChannels);
al_audio_errcheck("alGetBufferi(_buffer,AL_CHANNELS)");
if (nChannels>1)
audio_warning("OpenALAudioSound: Stereo sounds won't be spacialized: "<<file_name);
} else {
/*alSourcei(_source,AL_SOURCE_RELATIVE,AL_TRUE);
al_audio_errcheck("alSourcei(_source,AL_SOURCE_RELATIVE,AL_TRUE)");*/
if ((_sample && (_sample->_channels != 1)) ||
(_stream && (_stream->audio_channels() != 1))) {
audio_warning("Stereo sounds won't be spacialized: "<<path);
}
}
/*
// set initial values since they are manager-relative
set_volume(_volume);
//set_balance(_balance);
set_play_rate(_play_rate);
set_3d_min_distance(_min_dist);
set_3d_max_distance(_max_dist);
set_3d_drop_off_factor(_drop_off_factor);
*/
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::~OpenALAudioSound
// Function: OpenALAudioSound::Destructor
// Access: public
// Description: DESTRUCTOR!!!
// Description:
////////////////////////////////////////////////////////////////////
OpenALAudioSound::
~OpenALAudioSound() {
openal_audio_debug("~OpenALAudioSound()");
cleanup();
_manager->release_sound(this);
openal_audio_debug("~OpenALAudioSound() done");
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::cleanup
// Access: Private
// Description: Disables the sound forever. Releases resources and
// detaches the sound from its audio manager.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
cleanup() {
if (_manager == 0) {
return;
}
if (_source) {
stop();
}
if (_sample) {
_manager->decrement_client_count(_sample);
_sample = 0;
}
_manager->release_sound(this);
_manager = 0;
_active = false;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound:: play
// 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;
}
set_time(0.0);
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::stop
// Access: public
@ -207,8 +146,6 @@ stop() {
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
@ -239,8 +176,6 @@ finished() {
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_loop(bool loop) {
openal_audio_debug("set_loop(loop="<<loop<<")");
// loop count of 0 means always loop
set_loop_count((loop)?0:1);
}
@ -251,8 +186,7 @@ set_loop(bool loop) {
////////////////////////////////////////////////////////////////////
bool OpenALAudioSound::
get_loop() const {
openal_audio_debug("get_loop() returning "<<(_loop_count==0));
return (_loop_count == 0);
return (_loop_count != 1);
}
////////////////////////////////////////////////////////////////////
@ -268,14 +202,17 @@ set_loop_count(unsigned long loop_count) {
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 (_loop_count==loop_count) {
return;
}
_loop_count=loop_count;
if (_source) {
// I believe there is a race condition here.
_manager->make_current();
alGetError(); // clear errors
alSourcei(_source,AL_LOOPING,_loop_count==0?AL_TRUE:AL_FALSE);
al_audio_errcheck("alSourcei(_source,AL_LOOPING)");
@ -289,7 +226,6 @@ set_loop_count(unsigned long loop_count) {
////////////////////////////////////////////////////////////////////
unsigned long OpenALAudioSound::
get_loop_count() const {
openal_audio_debug("get_loop_count() returning "<<_loop_count);
return _loop_count;
}
@ -300,32 +236,63 @@ get_loop_count() const {
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
set_time(float time) {
float px,py,pz,vx,vy,vz;
openal_audio_debug("play()");
if (!_active) {
_paused=true;
return;
}
if (status() == AudioSound::PLAYING) {
stop();
}
_manager->starting_sound(this);
if (!_source) {
return;
}
// Setup source
_manager->make_current();
alGetError(); // clear errors
// Assign the buffer to the source
alSourcei(_source,AL_BUFFER,_sample->_buffer);
ALenum result = alGetError();
if (result!=AL_NO_ERROR) {
audio_error("alSourcei(_source,AL_BUFFER,_sample->_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);
openal_audio_debug("set_time(time="<<time<<")");
//nassertv(_source);
alSourcef(_source,AL_SEC_OFFSET,time);
al_audio_errcheck("alSourcef(_source,AL_SEC_OFFSET)");
alSourcePlay(_source);
al_audio_errcheck("alSourcePlay(_source)");
// Ensure we don't inadvertently run off the end of the sound.
float max_time = length();
if (time > max_time) {
openalAudio_cat.warning()
<< "set_time(" << time << ") requested for sound of length "
<< max_time << "\n";
time = max_time;
}
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alSourcef(_source,AL_SEC_OFFSET,time);
al_audio_errcheck("alSourcef(_source,AL_SEC_OFFSET)");
}
if (status()==PLAYING) {
_pause_time = 0.0;
} else {
_pause_time = time;
}
audio_debug(" started sound " << _path );
}
////////////////////////////////////////////////////////////////////
@ -336,19 +303,16 @@ set_time(float time) {
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;
return 0.0;
}
openal_audio_debug("get_time() returning "<<time);
return time;
}
@ -455,7 +419,7 @@ get_play_rate() const {
////////////////////////////////////////////////////////////////////
float OpenALAudioSound::
length() const {
return _sd->get_length();
return _sample->_length;
}
////////////////////////////////////////////////////////////////////
@ -674,7 +638,7 @@ get_finished_event() const {
////////////////////////////////////////////////////////////////////
const string& OpenALAudioSound::
get_name() const {
return _file_name;
return _path;
}
////////////////////////////////////////////////////////////////////
@ -685,18 +649,18 @@ get_name() const {
AudioSound::SoundStatus OpenALAudioSound::
status() const {
ALenum status;
if (_source==0) {
//return AudioSound::BAD;
return AudioSound::READY;
}
_manager->make_current();
alGetError(); // clear errors
alGetSourcei(_source,AL_SOURCE_STATE,&status);
al_audio_errcheck("alGetSourcei(_source,AL_SOURCE_STATE)");
if (status == AL_PLAYING/* || status == AL_PAUSED*/) {
return AudioSound::PLAYING;
} else {
@ -704,27 +668,4 @@ status() const {
}
}
////////////////////////////////////////////////////////////////////
// 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 //]

View File

@ -27,10 +27,10 @@
#ifdef HAVE_OPENAL //[
#include "audioSound.h"
#include "movieAudioCursor.h"
#include <al.h>
#include <alc.h>
#include <alut.h>
class EXPCL_OPENAL_AUDIO OpenALAudioSound : public AudioSound {
friend class OpenALAudioManager;
@ -113,22 +113,27 @@ public:
void finished();
private:
OpenALAudioSound(OpenALAudioManager* manager, OpenALAudioManager::SoundData *sd,
string file_name, bool positional);
OpenALAudioSound(OpenALAudioManager* manager,
const Filename &path,
PT(MovieAudioCursor) cursor,
OpenALAudioManager::SoundData *sd,
bool positional);
void cleanup();
private:
PT(OpenALAudioManager::SoundData) _sd;
ALuint _buffer;
// A Sound can have a sample or a stream, but not both.
OpenALAudioManager::SoundData *_sample;
PT(MovieAudioCursor) _stream;
ALuint _stream_buffers[3];
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];
@ -144,8 +149,8 @@ private:
// when the sound finishes playing. It is not triggered
// when the sound is stopped with stop().
string _finished_event;
string _file_name;
Filename _path;
// _active is for things like a 'turn off sound effects' in
// a preferences pannel.