Fixed several memory management bugs, and added streaming control

This commit is contained in:
Josh Yelon 2008-04-24 01:59:04 +00:00
parent 16b6c060a8
commit 4a474bfb13
5 changed files with 75 additions and 41 deletions

View File

@ -123,14 +123,14 @@ OpenALAudioManager() {
if (!_device) {
// this is a unique kind of error
audio_error("OpenALAudioManager: alcOpenDevice(NULL): ALC couldn't open device");
} else {
} else {
alcGetError(_device); // clear errors
_context=alcCreateContext(_device,NULL);
alc_audio_errcheck("alcCreateContext(_device,NULL)",_device);
if (_context!=NULL) {
_openal_active = true;
}
}
}
}
// We increment _active_managers regardless of possible errors above.
// The shutdown call will do the right thing when it's called,
@ -151,7 +151,6 @@ OpenALAudioManager() {
audio_3d_set_distance_factor(audio_distance_factor);
audio_3d_set_drop_off_factor(audio_drop_off_factor);
}
}
////////////////////////////////////////////////////////////////////
@ -232,25 +231,36 @@ can_use_audio(MovieAudioCursor *source) {
}
////////////////////////////////////////////////////////////////////
// Function: OpenALAudioManager::can_load_audio
// Function: OpenALAudioManager::should_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.
// Description: Returns true if the specified MovieAudio should be
// cached into RAM. A lot of conditions have to be met
// in order to allow caching - if any are not met,
// the file will be streamed.
////////////////////////////////////////////////////////////////////
bool OpenALAudioManager::
can_load_audio(MovieAudioCursor *source) {
if (source->get_source()->get_filename().empty()) {
should_load_audio(MovieAudioCursor *source, int mode) {
if (mode == SM_stream) {
// If the user asked for streaming, give him streaming.
return false;
}
if ((source->length() > 3600.0)||(source->ready() != 0x40000000)) {
if (source->get_source()->get_filename().empty()) {
// Non-files cannot be preloaded.
return false;
}
if (source->ready() != 0x40000000) {
// Streaming sources cannot be preloaded.
return false;
}
if (source->length() > 3600.0) {
// Anything longer than an hour cannot be preloaded.
return false;
}
int channels = source->audio_channels();
int samples = (int)(source->length() * source->audio_rate());
int bytes = samples * channels * 2;
if (bytes > audio_preload_threshold) {
if ((mode == SM_heuristic)&&(bytes > audio_preload_threshold)) {
// In heuristic mode, if file is long, stream it.
return false;
}
return true;
@ -265,27 +275,31 @@ can_load_audio(MovieAudioCursor *source) {
// to decrement the client count.
////////////////////////////////////////////////////////////////////
OpenALAudioManager::SoundData *OpenALAudioManager::
get_sound_data(MovieAudio *movie) {
get_sound_data(MovieAudio *movie, int mode) {
const Filename &path = movie->get_filename();
// Search for an already-cached sample or an already-opened stream.
if (!path.empty()) {
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) {
if (mode != SM_stream) {
SampleCache::iterator lsmi=_sample_cache.find(path);
if (lsmi != _sample_cache.end()) {
SoundData *sd = (*lsmi).second;
increment_client_count(sd);
return sd;
}
}
if (mode != SM_sample) {
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;
}
}
}
}
PT(MovieAudioCursor) stream = movie->open();
@ -306,10 +320,12 @@ get_sound_data(MovieAudio *movie) {
sd->_rate = stream->audio_rate();
sd->_channels = stream->audio_channels();
sd->_length = stream->length();
audio_debug("Creating: " << sd->_movie->get_filename().get_basename());
audio_debug(" - Rate: " << sd->_rate);
audio_debug(" - Channels: " << sd->_channels);
audio_debug(" - Length: " << sd->_length);
if (can_load_audio(stream)) {
if (should_load_audio(stream, mode)) {
audio_debug(path.get_basename() << ": loading as sample");
make_current();
alGetError(); // clear errors
@ -354,7 +370,7 @@ get_sound(MovieAudio *sound, bool positional, int mode) {
return get_null_sound();
}
PT(OpenALAudioSound) oas =
new OpenALAudioSound(this, sound, positional);
new OpenALAudioSound(this, sound, positional, mode);
_all_sounds.insert(oas);
PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
@ -385,7 +401,7 @@ get_sound(const string &file_name, bool positional, int mode) {
PT(MovieAudio) mva = MovieAudio::get(path);
PT(OpenALAudioSound) oas =
new OpenALAudioSound(this, mva, positional);
new OpenALAudioSound(this, mva, positional, mode);
_all_sounds.insert(oas);
PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
@ -783,11 +799,11 @@ starting_sound(OpenALAudioSound* audio) {
////////////////////////////////////////////////////////////////////
void OpenALAudioManager::
stopping_sound(OpenALAudioSound* audio) {
_sounds_playing.erase(audio);
if (audio->_source) {
_al_sources->insert(audio->_source);
audio->_source = 0;
}
_sounds_playing.erase(audio); // This could cause the sound to destruct.
}
////////////////////////////////////////////////////////////////////
@ -825,7 +841,14 @@ reduce_sounds_playing_to(unsigned int count) {
while (limit-- > 0) {
SoundsPlaying::iterator sound = _sounds_playing.begin();
assert(sound != _sounds_playing.end());
(**sound).stop();
// When the user stops a sound, there is still a PT in the
// user's hand. When we stop a sound here, however,
// this can remove the last PT. This can cause an ugly
// recursion where stop calls the destructor, and the
// destructor calls stop. To avoid this, we create
// a temporary PT, stop the sound, and then release the PT.
PT(OpenALAudioSound) s = (*sound);
s->stop();
}
}
@ -862,7 +885,9 @@ update() {
sound->push_fresh_buffers();
sound->restart_stalled_audio();
sound->cache_time(rtc);
if ((sound->_source == 0)||(sound->_loops_completed >= sound->_playing_loops)) {
if ((sound->_source == 0)||
((sound->_stream_queued.size() == 0)&&
(sound->_loops_completed >= sound->_playing_loops))) {
sounds_finished.insert(*i);
}
}
@ -887,6 +912,8 @@ cleanup() {
return;
}
stop_all_sounds();
AllSounds sounds(_all_sounds);
AllSounds::iterator ai;
for (ai = sounds.begin(); ai != sounds.end(); ++ai) {

View File

@ -117,9 +117,9 @@ private:
void make_current() const;
bool can_use_audio(MovieAudioCursor *source);
bool can_load_audio(MovieAudioCursor *source);
bool should_load_audio(MovieAudioCursor *source, int mode);
SoundData *get_sound_data(MovieAudio *source);
SoundData *get_sound_data(MovieAudio *source, int mode);
// Tell the manager that the sound dtor was called.
void release_sound(OpenALAudioSound* audioSound);
@ -129,7 +129,7 @@ private:
void starting_sound(OpenALAudioSound* audio);
void stopping_sound(OpenALAudioSound* audio);
void cleanup();
private:

View File

@ -57,7 +57,7 @@ get_calibrated_clock(double rtc) const {
void OpenALAudioSound::
require_sound_data() {
if (_sd==0) {
_sd = _manager->get_sound_data(_movie);
_sd = _manager->get_sound_data(_movie, _desired_mode);
if (_sd==0) {
audio_error("Could not open audio " << _movie->get_filename());
cleanup();

View File

@ -46,7 +46,8 @@ TypeHandle OpenALAudioSound::_type_handle;
OpenALAudioSound::
OpenALAudioSound(OpenALAudioManager* manager,
MovieAudio *movie,
bool positional) :
bool positional,
int mode) :
_movie(movie),
_sd(NULL),
_loops_completed(0),
@ -63,7 +64,8 @@ OpenALAudioSound(OpenALAudioManager* manager,
_play_rate(1.0),
_current_time(0.0),
_active(true),
_paused(false)
_paused(false),
_desired_mode(mode)
{
_location[0] = 0;
_location[1] = 0;
@ -143,7 +145,7 @@ play() {
require_sound_data();
if (_manager == 0) return;
_manager->starting_sound(this);
if (!_source) {
return;
}
@ -408,7 +410,7 @@ read_stream_data(int bytelen, unsigned char *buffer) {
}
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);
audio_debug("Streaming " << cursor->get_source()->get_name() << " at " << t << " hash " << hval);
fill += samples;
space -= samples;
buffer += (samples * channels * 2);
@ -881,7 +883,7 @@ status() const {
if (_source==0) {
return AudioSound::READY;
}
if (_loops_completed >= _playing_loops) {
if ((_loops_completed >= _playing_loops)&&(_stream_queued.size()==0)) {
return AudioSound::READY;
} else {
return AudioSound::PLAYING;

View File

@ -116,7 +116,8 @@ public:
private:
OpenALAudioSound(OpenALAudioManager* manager,
MovieAudio *movie,
bool positional);
bool positional,
int mode);
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);
@ -134,6 +135,8 @@ private:
private:
void do_stop();
PT(MovieAudio) _movie;
OpenALAudioManager::SoundData *_sd;
@ -168,6 +171,8 @@ private:
double _length;
int _loop_count;
int _desired_mode;
// The calibrated clock is initialized when the
// sound starts playing, and is periodically corrected
// thereafter.