More improvements and bugfixes

This commit is contained in:
Josh Yelon 2007-08-28 16:28:41 +00:00
parent 11ee08072f
commit a89589033b
8 changed files with 726 additions and 403 deletions

View File

@ -341,7 +341,7 @@ make_dsp(const FilterProperties::FilterConfig &conf) {
void FmodAudioManager:: void FmodAudioManager::
update_dsp_chain(FMOD::DSP *head, FilterProperties *config) { update_dsp_chain(FMOD::DSP *head, FilterProperties *config) {
const FilterProperties::ConfigVector &conf = config->get_config(); const FilterProperties::ConfigVector &conf = config->get_config();
FMOD_RESULT res1,res2,res3,res4,res5,res6; FMOD_RESULT res1,res2,res3,res4,res5;
while (1) { while (1) {
int numinputs; int numinputs;
res1 = head->getNumInputs(&numinputs); res1 = head->getNumInputs(&numinputs);

View File

@ -282,14 +282,19 @@ get_loop_count() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: FmodAudioSound::set_time // Function: FmodAudioSound::set_time
// Access: public // Access: public
// Description: Sets the play position within the sound // Description: Starts playing from the specified location.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void FmodAudioSound:: void FmodAudioSound::
set_time(float start_time) { set_time(float start_time) {
FMOD_RESULT result; FMOD_RESULT result;
if (!_active) {
_paused = true;
return;
}
int startTime = (int)(start_time * 1000); int startTime = (int)(start_time * 1000);
if (_channel != 0) { if (_channel != 0) {
// try backing up current sound. // try backing up current sound.
result = _channel->setPosition( startTime , FMOD_TIMEUNIT_MS ); result = _channel->setPosition( startTime , FMOD_TIMEUNIT_MS );
@ -324,10 +329,8 @@ set_time(float start_time) {
// add_dsp_on_channel(); // add_dsp_on_channel();
set_3d_attributes_on_channel(); set_3d_attributes_on_channel();
if (_active) { result = _channel->setPaused(false);
result = _channel->setPaused(false); fmod_audio_errcheck("_channel->setPaused()", result);
fmod_audio_errcheck("_channel->setPaused()", result);
}
_self_ref = this; _self_ref = this;
} }
@ -785,13 +788,26 @@ status() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void FmodAudioSound:: void FmodAudioSound::
set_active(bool active) { set_active(bool active) {
_active = active; if (_active != active) {
_active = active;
if (_active) {
// ...activate the sound.
if (_paused && get_loop_count()==0) {
// ...this sound was looping when it was paused.
_paused = false;
play();
}
if (status() == PLAYING) { } else {
// If the sound is (or should be) playing, then pause or unpause // ...deactivate the sound.
// it in the system. if (status() == PLAYING) {
FMOD_RESULT result = _channel->setPaused(!_active); if (get_loop_count() == 0) {
fmod_audio_errcheck("_channel->setPaused()", result); // ...we're pausing a looping sound.
_paused = true;
}
stop();
}
}
} }
} }
@ -809,14 +825,11 @@ get_active() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: FmodAudioSound::finished // Function: FmodAudioSound::finished
// Access: public // Access: public
// Description: NOT USED ANYMORE!!! // Description: Not implemented.
// Called by finishedCallback function when a sound
// terminates (but doesn't loop).
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void FmodAudioSound:: void FmodAudioSound::
finished() { finished() {
audio_debug("FmodAudioSound::finished()"); audio_error("finished: not implemented under FMOD-EX");
audio_debug("NOT USED ANYMORE in FMOD-EX version of PANDA.");
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -829,8 +842,7 @@ finished() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void FmodAudioSound:: void FmodAudioSound::
set_finished_event(const string& event) { set_finished_event(const string& event) {
audio_debug("FmodAudioSound::set_finished_event(event="<<event<<")"); audio_error("set_finished_event: not implemented under FMOD-EX");
audio_debug("NOT USED ANYMORE in FMOD-EX version of PANDA.");
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -843,8 +855,7 @@ set_finished_event(const string& event) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
const string& FmodAudioSound:: const string& FmodAudioSound::
get_finished_event() const { get_finished_event() const {
audio_debug("FmodAudioSound::get_finished_event() returning " << _finished_event ); audio_error("get_finished_event: not implemented under FMOD-EX");
audio_debug("NOT USED ANYMORE in FMOD-EX version of PANDA.");
return _finished_event; return _finished_event;
} }

View File

@ -154,14 +154,9 @@ class EXPCL_FMOD_AUDIO FmodAudioSound : public AudioSound {
void set_active(bool active=true); void set_active(bool active=true);
bool get_active() const; 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 finished(); void finished();
void set_finished_event(const string& event); void set_finished_event(const string& event);
const string& get_finished_event() const; const string& get_finished_event() const;
////////////////////////////////////////////////////////////////////
private: private:
PT(FmodAudioManager) _manager; PT(FmodAudioManager) _manager;
@ -199,7 +194,10 @@ class EXPCL_FMOD_AUDIO FmodAudioSound : public AudioSound {
virtual void set_priority(int priority); virtual void set_priority(int priority);
bool _active; bool _active;
bool _paused;
string _finished_event;
// This reference-counting pointer is set to this while the sound is // This reference-counting pointer is set to this while the sound is
// playing, and cleared when we get an indication that the sound has // playing, and cleared when we get an indication that the sound has
// stopped. This prevents a sound from destructing while it is // stopped. This prevents a sound from destructing while it is
@ -208,11 +206,6 @@ class EXPCL_FMOD_AUDIO FmodAudioSound : public AudioSound {
// other mismanagement. // other mismanagement.
PT(FmodAudioSound) _self_ref; 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
string _finished_event;
friend FMOD_RESULT F_CALLBACK sound_end_callback(FMOD_CHANNEL * channel, friend FMOD_RESULT F_CALLBACK sound_end_callback(FMOD_CHANNEL * channel,
FMOD_CHANNEL_CALLBACKTYPE type, FMOD_CHANNEL_CALLBACKTYPE type,
int command, int command,

View File

@ -74,7 +74,6 @@ void alc_audio_errcheck(const char *context,ALCdevice* device) {
// Description: Factory Function // Description: Factory Function
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
PT(AudioManager) Create_AudioManager() { PT(AudioManager) Create_AudioManager() {
audio_debug("Create_AudioManager() OpenAL.");
return new OpenALAudioManager; return new OpenALAudioManager;
} }
@ -86,8 +85,6 @@ PT(AudioManager) Create_AudioManager() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
OpenALAudioManager:: OpenALAudioManager::
OpenALAudioManager() { OpenALAudioManager() {
audio_debug("OpenALAudioManager::OpenALAudioManager(), this = "
<< (void *)this);
if (_managers == (Managers *)NULL) { if (_managers == (Managers *)NULL) {
_managers = new Managers; _managers = new Managers;
_al_sources = new SourceCache; _al_sources = new SourceCache;
@ -95,9 +92,6 @@ OpenALAudioManager() {
_managers->insert(this); _managers->insert(this);
audio_debug(" audio_active="<<audio_active);
audio_debug(" audio_volume="<<audio_volume);
_cleanup_required = true; _cleanup_required = true;
_active = audio_active; _active = audio_active;
_volume = audio_volume; _volume = audio_volume;
@ -146,7 +140,6 @@ OpenALAudioManager() {
// The shutdown call will do the right thing when it's called, // The shutdown call will do the right thing when it's called,
// either way. // either way.
++_active_managers; ++_active_managers;
audio_debug(" _active_managers="<<_active_managers);
nassertv(_active_managers>0); nassertv(_active_managers>0);
if (!_device || !_context) { if (!_device || !_context) {
@ -170,15 +163,11 @@ OpenALAudioManager() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
OpenALAudioManager:: OpenALAudioManager::
~OpenALAudioManager() { ~OpenALAudioManager() {
audio_debug("OpenALAudioManager::~OpenALAudioManager(), this = "
<< (void *)this);
nassertv(_managers != (Managers *)NULL); nassertv(_managers != (Managers *)NULL);
Managers::iterator mi = _managers->find(this); Managers::iterator mi = _managers->find(this);
nassertv(mi != _managers->end()); nassertv(mi != _managers->end());
_managers->erase(mi); _managers->erase(mi);
cleanup(); cleanup();
audio_debug("OpenALAudioManager::~OpenALAudioManager() finished");
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -192,7 +181,6 @@ OpenALAudioManager::
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
shutdown() { shutdown() {
audio_debug("shutdown(), _openal_active = " << _openal_active);
if (_managers != (Managers *)NULL) { if (_managers != (Managers *)NULL) {
Managers::iterator mi; Managers::iterator mi;
for (mi = _managers->begin(); mi != _managers->end(); ++mi) { for (mi = _managers->begin(); mi != _managers->end(); ++mi) {
@ -201,7 +189,6 @@ shutdown() {
} }
nassertv(_active_managers == 0); nassertv(_active_managers == 0);
audio_debug("shutdown() finished");
} }
@ -267,77 +254,97 @@ can_load_audio(MovieAudioCursor *source) {
int channels = source->audio_channels(); int channels = source->audio_channels();
int samples = (int)(source->length() * source->audio_rate()); int samples = (int)(source->length() * source->audio_rate());
int bytes = samples * channels * 2; int bytes = samples * channels * 2;
if (bytes > 200000000) { if (bytes > 200000) {
return false; return false;
} }
return true; return true;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::load_sound_data // Function: OpenALAudioManager::get_sound_data
// Access: Private // Access: Private
// Description: Reads a sound file and creates a SoundData. // Description: Obtains a SoundData for the specified sound.
// 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);
al_audio_errcheck("alGenBuffers");
if (sd->_buffer == 0) {
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);
alBufferData(sd->_buffer,
(channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
data, samples * channels * 2, source->audio_rate());
int err = alGetError();
if (err != AL_NO_ERROR) {
audio_error("alBufferData: " << alGetString(err));
alDeleteBuffers(1, &sd->_buffer);
return NULL;
}
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 // When you are done with the SoundData, you need
// to decrement the client count. // to decrement the client count.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
OpenALAudioManager::SoundData *OpenALAudioManager:: OpenALAudioManager::SoundData *OpenALAudioManager::
cached_sound_data(const Filename &path) { get_sound_data(MovieAudio *movie) {
SoundDataSet::const_iterator si=_all_sound_data.find(path); const Filename &path = movie->get_filename();
if (si != _all_sound_data.end()) {
SoundData *sd = (*si).second; // Search for an already-cached sample or an already-opened stream.
increment_client_count(sd); if (!path.empty()) {
return sd;
SampleCache::iterator lsmi=_sample_cache.find(path);
if (lsmi != _sample_cache.end()) {
SoundData *sd = (*lsmi).second;
increment_client_count(sd);
return sd;
}
ExpirationQueue::iterator exqi;
for (exqi=_expiring_streams.begin(); exqi!=_expiring_streams.end(); exqi++) {
SoundData *sd = (SoundData*)(*exqi);
if (sd->_movie->get_filename() == path) {
increment_client_count(sd);
return sd;
}
}
} }
return NULL;
PT(MovieAudioCursor) stream = movie->open();
if (stream == 0) {
audio_error("Cannot open file: "<<path);
return NULL;
}
if (!can_use_audio(stream)) {
audio_error("File is not in usable format: "<<path);
return NULL;
}
SoundData *sd = new SoundData();
sd->_client_count = 1;
sd->_manager = this;
sd->_movie = movie;
sd->_rate = stream->audio_rate();
sd->_channels = stream->audio_channels();
sd->_length = stream->length();
audio_debug("Creating: " << sd->_movie->get_filename().get_basename());
if (can_load_audio(stream)) {
audio_debug(path.get_basename() << ": loading as sample");
make_current();
alGetError(); // clear errors
sd->_sample = 0;
alGenBuffers(1, &sd->_sample);
al_audio_errcheck("alGenBuffers");
if (sd->_sample == 0) {
audio_error("Could not create an OpenAL buffer object");
delete sd;
return NULL;
}
int channels = stream->audio_channels();
int samples = (int)(stream->length() * stream->audio_rate());
PN_int16 *data = new PN_int16[samples * channels];
stream->read_samples(samples, data);
alBufferData(sd->_sample,
(channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
data, samples * channels * 2, stream->audio_rate());
int err = alGetError();
if (err != AL_NO_ERROR) {
audio_error("could not fill OpenAL buffer object with data");
delete sd;
return NULL;
}
_sample_cache.insert(SampleCache::value_type(path, sd));
} else {
audio_debug(path.get_basename() << ": loading as stream");
sd->_stream = stream;
}
return sd;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -361,39 +368,20 @@ get_sound(const string &file_name, bool positional) {
return NULL; return NULL;
} }
PT(MovieAudioCursor) cursor; PT(MovieAudio) mva = MovieAudio::get(path);
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;
}
}
PT(OpenALAudioSound) oas = PT(OpenALAudioSound) oas =
new OpenALAudioSound(this, path, cursor, sd, positional); new OpenALAudioSound(this, mva, positional);
if (oas->get_active()) { _all_sounds.insert(oas);
_all_audio_sounds.insert(oas); PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas; return res;
return res;
}
return NULL;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::uncache_sound // Function: OpenALAudioManager::uncache_sound
// Access: Public // Access: Public
// Description: Deletes a sound from the expiration queue. // Description: Deletes a sample from the expiration queues.
// If the sound is actively in use, then the sound // If the sound is actively in use, then the sound
// cannot be deleted, and this function has no effect. // cannot be deleted, and this function has no effect.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -405,15 +393,13 @@ uncache_sound(const string& file_name) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 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()); vfs->resolve_filename(path, get_model_path());
SoundDataSet::iterator sdi=_all_sound_data.find(path); SampleCache::iterator sci = _sample_cache.find(path);
if (sdi != _all_sound_data.end()) { if (sci != _sample_cache.end()) {
SoundData *sd = (*sdi).second; SoundData *sd = (*sci).second;
ExpirationQueue::iterator exi = if (sd->_client_count == 0) {
find(_expiration_queue.begin(), _expiration_queue.end(), sd); _expiring_samples.erase(sd->_expire);
if (exi != _expiration_queue.end()) { _sample_cache.erase(sci);
_expiration_queue.erase(exi);
_all_sound_data.erase(sdi);
delete sd; delete sd;
} }
} }
@ -457,9 +443,9 @@ get_cache_limit() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
release_sound(OpenALAudioSound* audioSound) { release_sound(OpenALAudioSound* audioSound) {
AudioSoundSet::iterator ai = _all_audio_sounds.find(audioSound); AllSounds::iterator ai = _all_sounds.find(audioSound);
if (ai != _all_audio_sounds.end()) { if (ai != _all_sounds.end()) {
_all_audio_sounds.erase(ai); _all_sounds.erase(ai);
} }
} }
@ -470,13 +456,12 @@ release_sound(OpenALAudioSound* audioSound) {
// Sets listener gain // Sets listener gain
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager::set_volume(float volume) { void OpenALAudioManager::set_volume(float volume) {
audio_debug("OpenALAudioManager::set_volume(volume="<<volume<<")");
if (_volume!=volume) { if (_volume!=volume) {
_volume = volume; _volume = volume;
// Tell our AudioSounds to adjust: // Tell our AudioSounds to adjust:
AudioSoundSet::iterator i=_all_audio_sounds.begin(); AllSounds::iterator i=_all_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) { for (; i!=_all_sounds.end(); ++i) {
(**i).set_volume((**i).get_volume()); (**i).set_volume((**i).get_volume());
} }
@ -499,7 +484,6 @@ void OpenALAudioManager::set_volume(float volume) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
float OpenALAudioManager:: float OpenALAudioManager::
get_volume() const { get_volume() const {
audio_debug("OpenALAudioManager::get_volume() returning "<<_volume);
return _volume; return _volume;
} }
@ -510,12 +494,11 @@ get_volume() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
set_play_rate(float play_rate) { set_play_rate(float play_rate) {
audio_debug("OpenALAudioManager::set_play_rate(play_rate="<<play_rate<<")");
if (_play_rate!=play_rate) { if (_play_rate!=play_rate) {
_play_rate = play_rate; _play_rate = play_rate;
// Tell our AudioSounds to adjust: // Tell our AudioSounds to adjust:
AudioSoundSet::iterator i=_all_audio_sounds.begin(); AllSounds::iterator i=_all_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) { for (; i!=_all_sounds.end(); ++i) {
(**i).set_play_rate((**i).get_play_rate()); (**i).set_play_rate((**i).get_play_rate());
} }
} }
@ -528,7 +511,6 @@ set_play_rate(float play_rate) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
float OpenALAudioManager:: float OpenALAudioManager::
get_play_rate() const { get_play_rate() const {
audio_debug("OpenALAudioManager::get_play_rate() returning "<<_play_rate);
return _play_rate; return _play_rate;
} }
@ -540,12 +522,11 @@ get_play_rate() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
set_active(bool active) { set_active(bool active) {
audio_debug("OpenALAudioManager::set_active(flag="<<active<<")");
if (_active!=active) { if (_active!=active) {
_active=active; _active=active;
// Tell our AudioSounds to adjust: // Tell our AudioSounds to adjust:
AudioSoundSet::iterator i=_all_audio_sounds.begin(); AllSounds::iterator i=_all_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) { for (; i!=_all_sounds.end(); ++i) {
(**i).set_active(_active); (**i).set_active(_active);
} }
} }
@ -558,7 +539,6 @@ set_active(bool active) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool OpenALAudioManager:: bool OpenALAudioManager::
get_active() const { get_active() const {
audio_debug("OpenALAudioManager::get_active() returning "<<_active);
return _active; return _active;
} }
@ -661,8 +641,8 @@ audio_3d_set_distance_factor(float factor) {
al_audio_errcheck("alDopplerFactor()"); al_audio_errcheck("alDopplerFactor()");
} }
AudioSoundSet::iterator i=_all_audio_sounds.begin(); AllSounds::iterator i=_all_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) { for (; i!=_all_sounds.end(); ++i) {
(**i).set_3d_min_distance((**i).get_3d_min_distance()); (**i).set_3d_min_distance((**i).get_3d_min_distance());
(**i).set_3d_max_distance((**i).get_3d_max_distance()); (**i).set_3d_max_distance((**i).get_3d_max_distance());
} }
@ -715,8 +695,8 @@ void OpenALAudioManager::
audio_3d_set_drop_off_factor(float factor) { audio_3d_set_drop_off_factor(float factor) {
_drop_off_factor = factor; _drop_off_factor = factor;
AudioSoundSet::iterator i=_all_audio_sounds.begin(); AllSounds::iterator i=_all_sounds.begin();
for (; i!=_all_audio_sounds.end(); ++i) { for (; i!=_all_sounds.end(); ++i) {
(**i).set_3d_drop_off_factor((**i).get_3d_drop_off_factor()); (**i).set_3d_drop_off_factor((**i).get_3d_drop_off_factor());
} }
} }
@ -842,7 +822,6 @@ reduce_sounds_playing_to(unsigned int count) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
stop_all_sounds() { stop_all_sounds() {
audio_debug("OpenALAudioManager::stop_all_sounds()");
reduce_sounds_playing_to(0); reduce_sounds_playing_to(0);
} }
@ -853,7 +832,6 @@ stop_all_sounds() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
update() { update() {
//audio_debug("OpenALAudioManager::update()");
// See if any of our playing sounds have ended // See if any of our playing sounds have ended
// we must first collect a seperate list of finished sounds and then // we must first collect a seperate list of finished sounds and then
@ -862,13 +840,19 @@ update() {
// since finished() modifies _sounds_playing // since finished() modifies _sounds_playing
SoundsPlaying sounds_finished; SoundsPlaying sounds_finished;
double rtc = TrueClock::get_global_ptr()->get_short_time();
SoundsPlaying::iterator i=_sounds_playing.begin(); SoundsPlaying::iterator i=_sounds_playing.begin();
for (; i!=_sounds_playing.end(); ++i) { for (; i!=_sounds_playing.end(); ++i) {
if ((**i).status()!=AudioSound::PLAYING) { OpenALAudioSound *sound = (*i);
sound->pull_used_buffers();
sound->push_fresh_buffers();
sound->restart_stalled_audio();
sound->cache_time(rtc);
if (sound->status()!=AudioSound::PLAYING) {
sounds_finished.insert(*i); sounds_finished.insert(*i);
} }
} }
i=sounds_finished.begin(); i=sounds_finished.begin();
for (; i!=sounds_finished.end(); ++i) { for (; i!=sounds_finished.end(); ++i) {
(**i).finished(); (**i).finished();
@ -885,19 +869,16 @@ update() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
cleanup() { cleanup() {
audio_debug("OpenALAudioManager::cleanup(), this = " << (void *)this
<< ", _cleanup_required = " << _cleanup_required);
if (!_cleanup_required) { if (!_cleanup_required) {
return; return;
} }
AudioSoundSet sounds(_all_audio_sounds); AllSounds sounds(_all_sounds);
AudioSoundSet::iterator ai; AllSounds::iterator ai;
for (ai = sounds.begin(); ai != sounds.end(); ++ai) { for (ai = sounds.begin(); ai != sounds.end(); ++ai) {
(*ai)->cleanup(); (*ai)->cleanup();
} }
assert(_all_sound_data.size() == _expiration_queue.size());
clear_cache(); clear_cache();
nassertv(_active_managers > 0); nassertv(_active_managers > 0);
@ -938,7 +919,6 @@ cleanup() {
} }
} }
_cleanup_required = false; _cleanup_required = false;
audio_debug("OpenALAudioManager::cleanup() finished");
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -947,12 +927,15 @@ cleanup() {
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
OpenALAudioManager::SoundData:: OpenALAudioManager::SoundData::
SoundData(OpenALAudioManager* manager, const Filename &path) : SoundData() :
_manager(manager), _manager(0),
_path(path), _movie(0),
_buffer(0), _sample(0),
_client_count(1), _stream(NULL),
_length(0.0) _length(0.0),
_rate(0),
_channels(0),
_client_count(0)
{ {
} }
@ -963,12 +946,12 @@ SoundData(OpenALAudioManager* manager, const Filename &path) :
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
OpenALAudioManager::SoundData:: OpenALAudioManager::SoundData::
~SoundData() { ~SoundData() {
if (_buffer != 0) { if (_sample != 0) {
if (_manager->_is_valid) { if (_manager->_is_valid) {
_manager->make_current(); _manager->make_current();
alDeleteBuffers(1,&_buffer); alDeleteBuffers(1,&_sample);
} }
_buffer = 0; _sample = 0;
} }
} }
@ -981,13 +964,14 @@ OpenALAudioManager::SoundData::
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
increment_client_count(SoundData *sd) { increment_client_count(SoundData *sd) {
audio_debug("Incrementing client count: " << sd->_path);
sd->_client_count += 1; sd->_client_count += 1;
audio_debug("Incrementing: " << sd->_movie->get_filename().get_basename() << " " << sd->_client_count);
if (sd->_client_count == 1) { if (sd->_client_count == 1) {
audio_debug("Removing from expiration queue: " << sd->_path); if (sd->_sample) {
ExpirationQueue::iterator p=find(_expiration_queue.begin(), _expiration_queue.end(), sd); _expiring_samples.erase(sd->_expire);
assert(p != _expiration_queue.end()); } else {
_expiration_queue.erase(p); _expiring_streams.erase(sd->_expire);
}
} }
} }
@ -1002,11 +986,18 @@ increment_client_count(SoundData *sd) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
decrement_client_count(SoundData *sd) { decrement_client_count(SoundData *sd) {
audio_debug("Decrementing client count: " << sd->_path);
sd->_client_count -= 1; sd->_client_count -= 1;
audio_debug("Decrementing: " << sd->_movie->get_filename().get_basename() << " " << sd->_client_count);
if (sd->_client_count == 0) { if (sd->_client_count == 0) {
audio_debug("Adding to expiration queue: " << sd->_path); if (sd->_sample) {
_expiration_queue.push_back(sd); _expiring_samples.push_back(sd);
sd->_expire = _expiring_samples.end();
sd->_expire--;
} else {
_expiring_streams.push_back(sd);
sd->_expire = _expiring_streams.end();
sd->_expire--;
}
discard_excess_cache(_cache_limit); discard_excess_cache(_cache_limit);
} }
} }
@ -1018,13 +1009,25 @@ decrement_client_count(SoundData *sd) {
// number of sounds remaining is under the limit. // number of sounds remaining is under the limit.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
discard_excess_cache(int limit) { discard_excess_cache(int sample_limit) {
while (((int)_expiration_queue.size()) > limit) { int stream_limit = 5;
SoundData *sd = _expiration_queue.front();
audio_debug("Deleting head of sound cache: " << sd->_path); while (((int)_expiring_samples.size()) > sample_limit) {
SoundData *sd = (SoundData*)(_expiring_samples.front());
assert(sd->_client_count == 0); assert(sd->_client_count == 0);
_expiration_queue.pop_front(); assert(sd->_expire == _expiring_samples.begin());
_all_sound_data.erase(sd->_path); _expiring_samples.pop_front();
_sample_cache.erase(_sample_cache.find(sd->_movie->get_filename()));
audio_debug("Expiring: " << sd->_movie->get_filename().get_basename());
delete sd;
}
while (((int)_expiring_streams.size()) > stream_limit) {
SoundData *sd = (SoundData*)(_expiring_streams.front());
assert(sd->_client_count == 0);
assert(sd->_expire == _expiring_streams.begin());
_expiring_streams.pop_front();
audio_debug("Expiring: " << sd->_movie->get_filename().get_basename());
delete sd; delete sd;
} }
} }

View File

@ -25,9 +25,9 @@
#ifdef HAVE_OPENAL //[ #ifdef HAVE_OPENAL //[
#include "audioManager.h" #include "audioManager.h"
#include "pset.h" #include "plist.h"
#include "pmap.h" #include "pmap.h"
#include "pdeque.h" #include "pset.h"
#include "movieAudioCursor.h" #include "movieAudioCursor.h"
//The Includes needed for OpenAL //The Includes needed for OpenAL
@ -115,12 +115,11 @@ class EXPCL_OPENAL_AUDIO OpenALAudioManager : public AudioManager {
private: private:
void make_current() const; void make_current() const;
bool can_use_audio(MovieAudioCursor *source); bool can_use_audio(MovieAudioCursor *source);
bool can_load_audio(MovieAudioCursor *source); bool can_load_audio(MovieAudioCursor *source);
SoundData *cached_sound_data(const Filename &file_name); SoundData *get_sound_data(MovieAudio *source);
SoundData *load_sound_data(MovieAudioCursor *source);
// Tell the manager that the sound dtor was called. // Tell the manager that the sound dtor was called.
void release_sound(OpenALAudioSound* audioSound); void release_sound(OpenALAudioSound* audioSound);
@ -135,6 +134,16 @@ private:
private: private:
// An expiration queue is a list of SoundData
// that are no longer being used. They are kept
// around for a little while, since it is common to
// stop using a sound for a brief moment and then
// quickly resume.
typedef plist<void *> ExpirationQueue;
ExpirationQueue _expiring_samples;
ExpirationQueue _expiring_streams;
// An AudioSound that uses a SoundData is called a "client" // An AudioSound that uses a SoundData is called a "client"
// of the SoundData. The SoundData keeps track of how // of the SoundData. The SoundData keeps track of how
// many clients are using it. When the number of clients // many clients are using it. When the number of clients
@ -148,27 +157,28 @@ private:
class SoundData { class SoundData {
public: public:
SoundData(OpenALAudioManager* manager, const Filename &path); SoundData();
~SoundData(); ~SoundData();
OpenALAudioManager* _manager; OpenALAudioManager* _manager;
Filename _path; PT(MovieAudio) _movie;
ALuint _buffer; ALuint _sample;
double _length; PT(MovieAudioCursor) _stream;
int _rate; double _length;
int _channels; int _rate;
int _client_count; int _channels;
int _client_count;
ExpirationQueue::iterator _expire;
}; };
typedef pmap<string, SoundData *> SoundDataSet;
SoundDataSet _all_sound_data;
typedef pset<OpenALAudioSound *> AudioSoundSet; typedef phash_map<string, SoundData *> SampleCache;
AudioSoundSet _all_audio_sounds; SampleCache _sample_cache;
typedef pdeque<SoundData *> ExpirationQueue; typedef phash_set<OpenALAudioSound *> SoundsPlaying;
ExpirationQueue _expiration_queue;
typedef pset<OpenALAudioSound *> SoundsPlaying;
SoundsPlaying _sounds_playing; SoundsPlaying _sounds_playing;
typedef phash_set<OpenALAudioSound *> AllSounds;
AllSounds _all_sounds;
// State: // State:
int _cache_limit; int _cache_limit;

View File

@ -16,8 +16,65 @@
// //
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_calibrated_clock
// Access: public
// Description: Sets the sound's calibrated clock.
//
// OpenAL is not very accurate at reporting how much
// time has elapsed within a buffer. However, it does
// accurately report when it has finished playing a
// buffer. So we use a hybrid clock algorithm.
// When OpenAL is in the middle of a buffer,
// we use a real-time-clock to estimate how far the
// sound has gotten. Each time OpenAL reaches the end
// of a buffer (which it does every 1/4 second or so),
// we calibrate our real-time-clock by speeding it up
// or slowing it down.
////////////////////////////////////////////////////////////////////
INLINE void OpenALAudioSound::
set_calibrated_clock(double rtc, double t, double accel) {
_calibrated_clock_scale = _playing_rate * accel;
_calibrated_clock_base = rtc - (t / _calibrated_clock_scale);
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::get_calibrated_clock
// Access: public
// Description: Returns the value of the calibrated clock.
////////////////////////////////////////////////////////////////////
INLINE double OpenALAudioSound::
get_calibrated_clock(double rtc) const {
return (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::require_sound_data
// Access: Private
// Description: Makes sure the sound data record is present,
// and if not, obtains it.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
require_sound_data() {
if (_sd==0) {
_sd = _manager->get_sound_data(_movie);
if (_sd==0) {
audio_error("Could not open audio " << _movie->get_filename());
cleanup();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::release_sound_data
// Access: Private
// Description: Checks if the sound data record is present and
// releasable, and if so, releases it.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
release_sound_data() {
if ((_sd!=0) && (!_movie->get_filename().empty())) {
_manager->decrement_client_count(_sd);
_sd = 0;
}
}

View File

@ -45,42 +45,48 @@ TypeHandle OpenALAudioSound::_type_handle;
OpenALAudioSound:: OpenALAudioSound::
OpenALAudioSound(OpenALAudioManager* manager, OpenALAudioSound(OpenALAudioManager* manager,
const Filename &path, MovieAudio *movie,
PT(MovieAudioCursor) stream,
OpenALAudioManager::SoundData *sample,
bool positional) : bool positional) :
_sample(sample), _movie(movie),
_stream(stream), _sd(NULL),
_loops_completed(0),
_playing_rate(0.0),
_playing_loops(0),
_source(0), _source(0),
_manager(manager), _manager(manager),
_path(path), _basename(movie->get_filename().get_basename()),
_volume(1.0f), _volume(1.0f),
_balance(0), _balance(0),
_play_rate(1.0),
_loop_count(1), _loop_count(1),
_length(0.0),
_start_time(0.0),
_play_rate(1.0),
_current_time(0.0),
_active(true), _active(true),
_paused(false) _paused(false)
{ {
//Inits 3D Attributes
_location[0] = 0; _location[0] = 0;
_location[1] = 0; _location[1] = 0;
_location[2] = 0; _location[2] = 0;
_velocity[0] = 0; _velocity[0] = 0;
_velocity[1] = 0; _velocity[1] = 0;
_velocity[2] = 0; _velocity[2] = 0;
_min_dist = 3.28f; _max_dist = 1000000000.0f; _min_dist = 3.28f; _max_dist = 1000000000.0f;
_drop_off_factor = 1.0f; _drop_off_factor = 1.0f;
_positional = positional; _positional = positional;
if (_positional) {
if ((_sample && (_sample->_channels != 1)) || require_sound_data();
(_stream && (_stream->audio_channels() != 1))) { if (_manager == 0) return;
audio_warning("Stereo sounds won't be spacialized: "<<path); _length = _sd->_length;
if (positional) {
if (_sd->_channels != 1) {
audio_warning("stereo sound " << movie->get_filename() << " will not be spatialized");
} }
} }
release_sound_data();
} }
@ -108,13 +114,12 @@ cleanup() {
if (_source) { if (_source) {
stop(); stop();
} }
if (_sample) { if (_sd) {
_manager->decrement_client_count(_sample); _manager->decrement_client_count(_sd);
_sample = 0; _sd = 0;
} }
_manager->release_sound(this); _manager->release_sound(this);
_manager = 0; _manager = 0;
_active = false;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -124,9 +129,72 @@ cleanup() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
play() { play() {
set_time(0.0); if (_manager == 0) return;
}
float px,py,pz,vx,vy,vz;
if (!_active) {
_paused = true;
return;
}
stop();
require_sound_data();
if (_manager == 0) return;
_manager->starting_sound(this);
if (!_source) {
return;
}
_manager->make_current();
alGetError(); // clear errors
// 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_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);
_playing_loops = _loop_count;
if (_playing_loops == 0) {
_playing_loops = 1000000000;
}
_loops_completed = 0;
double play_rate = _play_rate * _manager->get_play_rate();
audio_debug("playing. Rate=" << play_rate);
alSourcef(_source, AL_PITCH, play_rate);
_playing_rate = play_rate;
if (_sd->_sample) {
push_fresh_buffers();
alSourcef(_source, AL_SEC_OFFSET, _start_time);
_stream_queued[0]._time_offset = _start_time;
restart_stalled_audio();
} else {
audio_debug("Play: stream tell = " << _sd->_stream->tell() << " seeking " << _start_time);
if (_sd->_stream->tell() != _start_time) {
_sd->_stream->seek(_start_time);
}
push_fresh_buffers();
restart_stalled_audio();
}
double rtc = TrueClock::get_global_ptr()->get_short_time();
set_calibrated_clock(rtc, _start_time, 1.0);
_current_time = _start_time;
_start_time = 0.0;
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::stop // Function: OpenALAudioSound::stop
@ -135,24 +203,28 @@ play() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
stop() { stop() {
openal_audio_debug("stop()"); if (_manager==0) return;
//nassertv(_source);
if (_source) { if (_source) {
_manager->make_current(); _manager->make_current();
alGetError(); // clear errors alGetError(); // clear errors
alSourceStop(_source); alSourceStop(_source);
al_audio_errcheck("alSourceStop(_source)"); al_audio_errcheck("stopping a source");
alSourcei(_source, AL_BUFFER, 0);
al_audio_errcheck("clear source buffers");
for (int i=0; i<((int)(_stream_queued.size())); i++) {
ALuint buffer = _stream_queued[i]._buffer;
if (buffer != _sd->_sample) {
alDeleteBuffers(1, &buffer);
al_audio_errcheck("deleting a buffer");
}
}
_stream_queued.resize(0);
} }
_manager->stopping_sound(this); _manager->stopping_sound(this);
// The _paused flag should not be cleared here. _paused is not like release_sound_data();
// the Pause button on a cd/dvd player. It is used as a flag to say
// that it was looping when it was set inactive. There is no need to
// make this symmetrical with play(). set_active() is the 'owner' of
// _paused. play() accesses _paused to help in the situation where
// someone calls play on an inactive sound().
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -162,8 +234,8 @@ stop() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
finished() { finished() {
openal_audio_debug("finished()"); stop();
_manager->stopping_sound(this); _current_time = _length;
if (!_finished_event.empty()) { if (!_finished_event.empty()) {
throw_event(_finished_event); throw_event(_finished_event);
} }
@ -186,7 +258,7 @@ set_loop(bool loop) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool OpenALAudioSound:: bool OpenALAudioSound::
get_loop() const { get_loop() const {
return (_loop_count != 1); return (_loop_count == 0);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -196,27 +268,12 @@ get_loop() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
set_loop_count(unsigned long loop_count) { set_loop_count(unsigned long loop_count) {
openal_audio_debug("set_loop_count(loop_count="<<loop_count<<")"); if (_manager==0) return;
if (loop_count>1) {
audio_error("OpenALAudioSound::set_loop_count() doesn't support looping a finite number of times, 0 (infinite) or 1 only");
loop_count = 1;
}
if (_loop_count==loop_count) { if (loop_count >= 1000000000) {
return; loop_count = 0;
} }
_loop_count=loop_count; _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)");
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -229,70 +286,250 @@ get_loop_count() const {
return _loop_count; return _loop_count;
} }
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::restart_stalled_audio
// Access: public
// Description: When streaming audio, the computer is supposed to
// keep OpenAL's queue full. However, there are times
// when the computer is running slow and the queue
// empties prematurely. In that case, OpenAL will stop.
// When the computer finally gets around to refilling
// the queue, it is necessary to tell OpenAL to resume
// playing.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
restart_stalled_audio() {
ALenum status;
if (_stream_queued.size() == 0) {
return;
}
alGetError();
alGetSourcei(_source, AL_SOURCE_STATE, &status);
if (status != AL_PLAYING) {
alSourcePlay(_source);
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::queue_buffer
// Access: public
// Description: Pushes a buffer into the source queue.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
queue_buffer(ALuint buffer, int loop_index, double time_offset) {
// Now push the buffer into the stream queue.
alGetError();
alSourceQueueBuffers(_source,1,&buffer);
ALenum err = alGetError();
if (err != AL_NO_ERROR) {
audio_error("could not load sample buffer into the queue");
cleanup();
return;
}
QueuedBuffer buf;
buf._buffer = buffer;
buf._loop_index = loop_index;
buf._time_offset = time_offset;
_stream_queued.push_back(buf);
// audio_debug("Buffer queued " << loop_index << " " << time_offset);
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::make_buffer
// Access: public
// Description: Creates an OpenAL buffer object.
////////////////////////////////////////////////////////////////////
ALuint OpenALAudioSound::
make_buffer(int samples, int channels, int rate, unsigned char *data) {
// Allocate a buffer to hold the data.
alGetError();
ALuint buffer;
alGenBuffers(1, &buffer);
if (alGetError() != AL_NO_ERROR) {
audio_error("could not allocate an OpenAL buffer object");
cleanup();
return 0;
}
// Now fill the buffer with the data provided.
alBufferData(buffer,
(channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
data, samples * channels * 2, rate);
int err = alGetError();
if (err != AL_NO_ERROR) {
audio_error("could not fill OpenAL buffer object with data");
cleanup();
return 0;
}
return buffer;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::read_stream_data
// Access: public
// Description: Fills a buffer with data from the stream.
// Returns the number of samples stored in the buffer.
////////////////////////////////////////////////////////////////////
int OpenALAudioSound::
read_stream_data(int bytelen, unsigned char *buffer) {
MovieAudioCursor *cursor = _sd->_stream;
double length = cursor->length();
int channels = cursor->audio_channels();
int rate = cursor->audio_rate();
int space = bytelen / (channels * 2);
int fill = 0;
while (space && (_loops_completed < _playing_loops)) {
double t = cursor->tell();
double remain = length - t;
if (remain > 60.0) {
remain = 60.0;
}
int samples = (int)(remain * rate);
if (samples <= 0) {
_loops_completed += 1;
cursor->seek(0.0);
continue;
}
if (samples > space) {
samples = space;
}
cursor->read_samples(samples, (PN_int16 *)buffer);
size_t hval = AddHash::add_hash(0, (PN_uint8*)buffer, samples*channels*2);
audio_debug("Streaming " << cursor->get_source()->get_filename().get_basename() << " at " << t << " hash " << hval);
fill += samples;
space -= samples;
buffer += (samples * channels * 2);
}
return fill;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::correct_calibrated_clock
// Access: public
// Description: Compares the specified time to the value of the
// calibrated clock, and adjusts the calibrated
// clock speed to make it closer to the target value.
// This routine is quite careful to make sure that
// the calibrated clock moves in a smooth, monotonic
// way.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
correct_calibrated_clock(double rtc, double t) {
double cc = (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
double diff = cc-t;
_calibrated_clock_decavg = (_calibrated_clock_decavg * 0.95) + (diff * 0.05);
if (diff > 0.5) {
set_calibrated_clock(rtc, t, 1.0);
_calibrated_clock_decavg = 0.0;
} else {
double scale = 1.0;
if ((_calibrated_clock_decavg > 0.01) && (diff > 0.01)) {
scale = 0.98;
}
if ((_calibrated_clock_decavg < -0.01) && (diff < -0.01)) {
scale = 1.03;
}
if ((_calibrated_clock_decavg < -0.05) && (diff < -0.05)) {
scale = 1.2;
}
if ((_calibrated_clock_decavg < -0.15) && (diff < -0.15)) {
scale = 1.5;
}
set_calibrated_clock(rtc, cc, scale);
}
cc = (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::pull_used_buffers
// Access: public
// Description: Pulls any used buffers out of OpenAL's queue.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
pull_used_buffers() {
while (_stream_queued.size()) {
ALuint buffer = 0;
alGetError();
alSourceUnqueueBuffers(_source, 1, &buffer);
int err = alGetError();
if (err == AL_NO_ERROR) {
if (_stream_queued[0]._buffer != buffer) {
audio_error("corruption in stream queue");
cleanup();
return;
}
_stream_queued.pop_front();
if (_stream_queued.size()) {
double al = _stream_queued[0]._time_offset + _stream_queued[0]._loop_index * _length;
double rtc = TrueClock::get_global_ptr()->get_short_time();
correct_calibrated_clock(rtc, al);
}
if (buffer != _sd->_sample) {
alDeleteBuffers(1,&buffer);
}
} else {
break;
}
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::push_fresh_buffers
// Access: public
// Description: Pushes fresh buffers into OpenAL's queue until
// the queue is "full" (ie, has plenty of data).
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
push_fresh_buffers() {
static unsigned char data[65536];
if (_sd->_sample) {
while ((_loops_completed < _playing_loops) &&
(_stream_queued.size() < 100)) {
queue_buffer(_sd->_sample, _loops_completed, 0.0);
_loops_completed += 1;
}
} else {
MovieAudioCursor *cursor = _sd->_stream;
int channels = cursor->audio_channels();
int rate = cursor->audio_rate();
double space = 65536 / (channels * 2);
// Calculate how many buffers to keep in the queue.
int fill_to = (int)((audio_buffering_seconds * rate) / space) + 1;
if (fill_to < 3) {
fill_to = 3;
}
while ((_loops_completed < _playing_loops) &&
(((int)(_stream_queued.size())) < fill_to)) {
int loop_index = _loops_completed;
double time_offset = cursor->tell();
int samples = read_stream_data(65536, data);
if (samples == 0) {
break;
}
ALuint buffer = make_buffer(samples, channels, rate, data);
if (_manager == 0) return;
queue_buffer(buffer, loop_index, time_offset);
if (_manager == 0) return;
}
}
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_time // Function: OpenALAudioSound::set_time
// Access: public // Access: public
// Description: Sets the play position within the sound // Description: The next time you call play, the sound will
// start from the specified offset.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
set_time(float time) { set_time(float time) {
float px,py,pz,vx,vy,vz; _start_time = time;
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<<")");
alSourcef(_source,AL_SEC_OFFSET,time);
al_audio_errcheck("alSourcef(_source,AL_SEC_OFFSET)");
alSourcePlay(_source);
al_audio_errcheck("alSourcePlay(_source)");
audio_debug(" started sound " << _path );
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -302,18 +539,27 @@ set_time(float time) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
float OpenALAudioSound:: float OpenALAudioSound::
get_time() const { get_time() const {
float time; if (_manager == 0) {
if (_source) {
_manager->make_current();
alGetError(); // clear errors
alGetSourcef(_source,AL_SEC_OFFSET,&time);
al_audio_errcheck("alGetSourcef(_source,AL_SEC_OFFSET)");
} else {
return 0.0; return 0.0;
} }
return _current_time;
return time; }
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::cache_time
// Access: Private
// Description: Updates the current_time field of a playing sound.
////////////////////////////////////////////////////////////////////
void OpenALAudioSound::
cache_time(double rtc) {
assert(_source != 0);
double t=get_calibrated_clock(rtc);
double max = _length * _playing_loops;
if (t >= max) {
_current_time = _length;
} else {
_current_time = fmod(t, _length);
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -324,17 +570,11 @@ get_time() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
set_volume(float volume) { set_volume(float volume) {
openal_audio_debug("set_volume(volume="<<volume<<")");
//nassertv(_source);
_volume=volume; _volume=volume;
if (_source) { if (_source) {
volume*=_manager->get_volume(); volume*=_manager->get_volume();
_manager->make_current(); _manager->make_current();
alGetError(); // clear errors alGetError(); // clear errors
alSourcef(_source,AL_GAIN,volume); alSourcef(_source,AL_GAIN,volume);
al_audio_errcheck("alSourcef(_source,AL_GAIN)"); al_audio_errcheck("alSourcef(_source,AL_GAIN)");
@ -348,7 +588,6 @@ set_volume(float volume) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
float OpenALAudioSound:: float OpenALAudioSound::
get_volume() const { get_volume() const {
openal_audio_debug("get_volume() returning "<<_volume);
return _volume; return _volume;
} }
@ -384,21 +623,7 @@ get_balance() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
set_play_rate(float play_rate) { set_play_rate(float play_rate) {
openal_audio_debug("set_play_rate(play_rate="<<play_rate<<")"); _play_rate = play_rate;
//nassertv(_source);
_play_rate=play_rate;
if (_source) {
play_rate*=_manager->get_play_rate();
_manager->make_current();
alGetError(); // clear errors
alSourcef(_source,AL_PITCH,play_rate);
al_audio_errcheck("alSourcef(_source,AL_PITCH)");
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -408,7 +633,6 @@ set_play_rate(float play_rate) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
float OpenALAudioSound:: float OpenALAudioSound::
get_play_rate() const { get_play_rate() const {
openal_audio_debug("get_play_rate() returning "<<_play_rate);
return _play_rate; return _play_rate;
} }
@ -419,23 +643,24 @@ get_play_rate() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
float OpenALAudioSound:: float OpenALAudioSound::
length() const { length() const {
return _sample->_length; return _length;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::set_3d_attributes // Function: OpenALAudioSound::set_3d_attributes
// Access: public // Access: public
// Description: Set position and velocity of this sound // Description: Set position and velocity of this sound
// NOW LISTEN UP!!! THIS IS IMPORTANT! //
// Both Panda3D and OpenAL use a right handed coordinate system. // Both Panda3D and OpenAL use a right handed
// But there is a major difference! // coordinate system. However, in Panda3D the
// In Panda3D the Y-Axis is going into the Screen and the Z-Axis is going up. // Y-Axis is going into the Screen and the
// In OpenAL the Y-Axis is going up and the Z-Axis is coming out of the screen. // Z-Axis is going up. In OpenAL the Y-Axis is
// The solution is simple, we just flip the Y and Z axis and negate the Z, as we move coordinates // going up and the Z-Axis is coming out of
// from Panda to OpenAL and back. // the screen.
// What does did mean to average Panda user? Nothing, they shouldn't notice anyway. //
// But if you decide to do any 3D audio work in here you have to keep it in mind. // The solution is simple, we just flip the Y
// I told you, so you can't say I didn't. // and Z axis and negate the Z, as we move
// coordinates from Panda to OpenAL and back.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
set_3d_attributes(float px, float py, float pz, float vx, float vy, float vz) { set_3d_attributes(float px, float py, float pz, float vx, float vy, float vz) {
@ -572,14 +797,11 @@ get_3d_drop_off_factor() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
set_active(bool active) { set_active(bool active) {
openal_audio_debug("set_active(active="<<active<<")");
if (_active!=active) { if (_active!=active) {
_active=active; _active=active;
if (_active) { if (_active) {
// ...activate the sound. // ...activate the sound.
if (_paused if (_paused && _loop_count==0) {
&&
_loop_count==0) {
// ...this sound was looping when it was paused. // ...this sound was looping when it was paused.
_paused=false; _paused=false;
play(); play();
@ -605,7 +827,6 @@ set_active(bool active) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool OpenALAudioSound:: bool OpenALAudioSound::
get_active() const { get_active() const {
openal_audio_debug("get_active() returning "<<_active);
return _active; return _active;
} }
@ -616,7 +837,6 @@ get_active() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioSound:: void OpenALAudioSound::
set_finished_event(const string& event) { set_finished_event(const string& event) {
openal_audio_debug("set_finished_event(event="<<event<<")");
_finished_event = event; _finished_event = event;
} }
@ -627,7 +847,6 @@ set_finished_event(const string& event) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
const string& OpenALAudioSound:: const string& OpenALAudioSound::
get_finished_event() const { get_finished_event() const {
openal_audio_debug("get_finished_event() returning "<<_finished_event);
return _finished_event; return _finished_event;
} }
@ -638,33 +857,29 @@ get_finished_event() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
const string& OpenALAudioSound:: const string& OpenALAudioSound::
get_name() const { get_name() const {
return _path; return _basename;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: OpenALAudioSound::status // Function: OpenALAudioSound::status
// Access: public // Access: public
// Description: Get status of the sound. // Description: Get status of the sound.
//
// This returns the status as of the
// last AudioManager::update.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
AudioSound::SoundStatus OpenALAudioSound:: AudioSound::SoundStatus OpenALAudioSound::
status() const { status() const {
ALenum status;
if (_source==0) { if (_source==0) {
//return AudioSound::BAD;
return AudioSound::READY; return AudioSound::READY;
} }
_manager->make_current(); _manager->make_current();
alGetError(); // clear errors if (_stream_queued.size() == 0) {
alGetSourcei(_source,AL_SOURCE_STATE,&status);
al_audio_errcheck("alGetSourcei(_source,AL_SOURCE_STATE)");
if (status == AL_PLAYING/* || status == AL_PAUSED*/) {
return AudioSound::PLAYING;
} else {
return AudioSound::READY; return AudioSound::READY;
} else {
return AudioSound::PLAYING;
} }
} }

View File

@ -28,6 +28,7 @@
#include "audioSound.h" #include "audioSound.h"
#include "movieAudioCursor.h" #include "movieAudioCursor.h"
#include "trueClock.h"
#include <al.h> #include <al.h>
#include <alc.h> #include <alc.h>
@ -89,7 +90,7 @@ public:
const string& get_finished_event() const; const string& get_finished_event() const;
const string &get_name() const; const string &get_name() const;
// return: playing time in seconds. // return: playing time in seconds.
float length() const; float length() const;
@ -114,18 +115,39 @@ public:
private: private:
OpenALAudioSound(OpenALAudioManager* manager, OpenALAudioSound(OpenALAudioManager* manager,
const Filename &path, MovieAudio *movie,
PT(MovieAudioCursor) cursor,
OpenALAudioManager::SoundData *sd,
bool positional); bool positional);
INLINE void set_calibrated_clock(double rtc, double t, double playrate);
INLINE double get_calibrated_clock(double rtc) const;
void correct_calibrated_clock(double rtc, double t);
void cache_time(double rtc);
void cleanup(); void cleanup();
void restart_stalled_audio();
void delete_queued_buffers();
ALuint make_buffer(int samples, int channels, int rate, unsigned char *data);
void queue_buffer(ALuint buffer, int loop_index, double time_offset);
int read_stream_data(int bytelen, unsigned char *data);
void pull_used_buffers();
void push_fresh_buffers();
INLINE void require_sound_data();
INLINE void release_sound_data();
private: private:
PT(MovieAudio) _movie;
OpenALAudioManager::SoundData *_sd;
// A Sound can have a sample or a stream, but not both. struct QueuedBuffer {
OpenALAudioManager::SoundData *_sample; ALuint _buffer;
PT(MovieAudioCursor) _stream; int _loop_index;
ALuint _stream_buffers[3]; double _time_offset;
};
int _playing_loops;
float _playing_rate;
pdeque<QueuedBuffer> _stream_queued;
int _loops_completed;
ALuint _source; ALuint _source;
PT(OpenALAudioManager) _manager; PT(OpenALAudioManager) _manager;
@ -133,7 +155,7 @@ private:
float _volume; // 0..1.0 float _volume; // 0..1.0
float _balance; // -1..1 float _balance; // -1..1
float _play_rate; // 0..1.0 float _play_rate; // 0..1.0
bool _positional; bool _positional;
ALfloat _location[3]; ALfloat _location[3];
ALfloat _velocity[3]; ALfloat _velocity[3];
@ -141,28 +163,40 @@ private:
float _min_dist; float _min_dist;
float _max_dist; float _max_dist;
float _drop_off_factor; float _drop_off_factor;
double _length;
int _loop_count;
mutable float _length; // in seconds. // The calibrated clock is initialized when the
unsigned long _loop_count; // sound starts playing, and is periodically corrected
// thereafter.
double _calibrated_clock_base;
double _calibrated_clock_scale;
double _calibrated_clock_decavg;
// The start_time field affects the next call to play.
double _start_time;
// The current_time field is updated every frame
// during the AudioManager update. Updates need
// to be atomic, because get_time can be called
// in the cull thread.
float _current_time;
// This is the string that throw_event() will throw // This is the string that throw_event() will throw
// when the sound finishes playing. It is not triggered // when the sound finishes playing. It is not triggered
// when the sound is stopped with stop(). // when the sound is stopped with stop().
string _finished_event; string _finished_event;
Filename _path; Filename _basename;
// _active is for things like a 'turn off sound effects' in // _active is for things like a 'turn off sound effects' in
// a preferences pannel. // a preferences pannel.
// _active is not about whether a sound is currently playing. // _active is not about whether a sound is currently playing.
// Use status() for info on whether the sound is playing. // Use status() for info on whether the sound is playing.
bool _active; bool _active;
// _paused is not like the Pause button on a cd/dvd player.
// It is used as a flag to say that the sound was looping when
// itwas set inactive.
bool _paused; bool _paused;
public: public:
static TypeHandle get_class_type() { static TypeHandle get_class_type() {
return _type_handle; return _type_handle;