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) { if (!_device) {
// this is a unique kind of error // this is a unique kind of error
audio_error("OpenALAudioManager: alcOpenDevice(NULL): ALC couldn't open device"); audio_error("OpenALAudioManager: alcOpenDevice(NULL): ALC couldn't open device");
} else { } else {
alcGetError(_device); // clear errors alcGetError(_device); // clear errors
_context=alcCreateContext(_device,NULL); _context=alcCreateContext(_device,NULL);
alc_audio_errcheck("alcCreateContext(_device,NULL)",_device); alc_audio_errcheck("alcCreateContext(_device,NULL)",_device);
if (_context!=NULL) { if (_context!=NULL) {
_openal_active = true; _openal_active = true;
} }
} }
} }
// We increment _active_managers regardless of possible errors above. // We increment _active_managers regardless of possible errors above.
// The shutdown call will do the right thing when it's called, // 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_distance_factor(audio_distance_factor);
audio_3d_set_drop_off_factor(audio_drop_off_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 // Access: Private
// Description: Returns true if the specified MovieAudio can be // Description: Returns true if the specified MovieAudio should be
// cached into RAM. To be cached, the data must have // cached into RAM. A lot of conditions have to be met
// a filename (otherwise, we have no cache key), and it // in order to allow caching - if any are not met,
// must not take too many bytes. // the file will be streamed.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool OpenALAudioManager:: bool OpenALAudioManager::
can_load_audio(MovieAudioCursor *source) { should_load_audio(MovieAudioCursor *source, int mode) {
if (source->get_source()->get_filename().empty()) { if (mode == SM_stream) {
// If the user asked for streaming, give him streaming.
return false; 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; return false;
} }
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 > audio_preload_threshold) { if ((mode == SM_heuristic)&&(bytes > audio_preload_threshold)) {
// In heuristic mode, if file is long, stream it.
return false; return false;
} }
return true; return true;
@ -265,27 +275,31 @@ can_load_audio(MovieAudioCursor *source) {
// to decrement the client count. // to decrement the client count.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
OpenALAudioManager::SoundData *OpenALAudioManager:: OpenALAudioManager::SoundData *OpenALAudioManager::
get_sound_data(MovieAudio *movie) { get_sound_data(MovieAudio *movie, int mode) {
const Filename &path = movie->get_filename(); const Filename &path = movie->get_filename();
// Search for an already-cached sample or an already-opened stream. // Search for an already-cached sample or an already-opened stream.
if (!path.empty()) { if (!path.empty()) {
SampleCache::iterator lsmi=_sample_cache.find(path); if (mode != SM_stream) {
if (lsmi != _sample_cache.end()) { SampleCache::iterator lsmi=_sample_cache.find(path);
SoundData *sd = (*lsmi).second; if (lsmi != _sample_cache.end()) {
increment_client_count(sd); SoundData *sd = (*lsmi).second;
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); increment_client_count(sd);
return 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(); PT(MovieAudioCursor) stream = movie->open();
@ -306,10 +320,12 @@ get_sound_data(MovieAudio *movie) {
sd->_rate = stream->audio_rate(); sd->_rate = stream->audio_rate();
sd->_channels = stream->audio_channels(); sd->_channels = stream->audio_channels();
sd->_length = stream->length(); sd->_length = stream->length();
audio_debug("Creating: " << sd->_movie->get_filename().get_basename()); 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"); audio_debug(path.get_basename() << ": loading as sample");
make_current(); make_current();
alGetError(); // clear errors alGetError(); // clear errors
@ -354,7 +370,7 @@ get_sound(MovieAudio *sound, bool positional, int mode) {
return get_null_sound(); return get_null_sound();
} }
PT(OpenALAudioSound) oas = PT(OpenALAudioSound) oas =
new OpenALAudioSound(this, sound, positional); new OpenALAudioSound(this, sound, positional, mode);
_all_sounds.insert(oas); _all_sounds.insert(oas);
PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)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(MovieAudio) mva = MovieAudio::get(path);
PT(OpenALAudioSound) oas = PT(OpenALAudioSound) oas =
new OpenALAudioSound(this, mva, positional); new OpenALAudioSound(this, mva, positional, mode);
_all_sounds.insert(oas); _all_sounds.insert(oas);
PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas; PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
@ -783,11 +799,11 @@ starting_sound(OpenALAudioSound* audio) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void OpenALAudioManager:: void OpenALAudioManager::
stopping_sound(OpenALAudioSound* audio) { stopping_sound(OpenALAudioSound* audio) {
_sounds_playing.erase(audio);
if (audio->_source) { if (audio->_source) {
_al_sources->insert(audio->_source); _al_sources->insert(audio->_source);
audio->_source = 0; 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) { while (limit-- > 0) {
SoundsPlaying::iterator sound = _sounds_playing.begin(); SoundsPlaying::iterator sound = _sounds_playing.begin();
assert(sound != _sounds_playing.end()); 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->push_fresh_buffers();
sound->restart_stalled_audio(); sound->restart_stalled_audio();
sound->cache_time(rtc); 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); sounds_finished.insert(*i);
} }
} }
@ -887,6 +912,8 @@ cleanup() {
return; return;
} }
stop_all_sounds();
AllSounds sounds(_all_sounds); AllSounds sounds(_all_sounds);
AllSounds::iterator ai; AllSounds::iterator ai;
for (ai = sounds.begin(); ai != sounds.end(); ++ai) { for (ai = sounds.begin(); ai != sounds.end(); ++ai) {

View File

@ -117,9 +117,9 @@ 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 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. // Tell the manager that the sound dtor was called.
void release_sound(OpenALAudioSound* audioSound); void release_sound(OpenALAudioSound* audioSound);
@ -129,7 +129,7 @@ private:
void starting_sound(OpenALAudioSound* audio); void starting_sound(OpenALAudioSound* audio);
void stopping_sound(OpenALAudioSound* audio); void stopping_sound(OpenALAudioSound* audio);
void cleanup(); void cleanup();
private: private:

View File

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

View File

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

View File

@ -116,7 +116,8 @@ public:
private: private:
OpenALAudioSound(OpenALAudioManager* manager, OpenALAudioSound(OpenALAudioManager* manager,
MovieAudio *movie, MovieAudio *movie,
bool positional); bool positional,
int mode);
INLINE void set_calibrated_clock(double rtc, double t, double playrate); INLINE void set_calibrated_clock(double rtc, double t, double playrate);
INLINE double get_calibrated_clock(double rtc) const; INLINE double get_calibrated_clock(double rtc) const;
void correct_calibrated_clock(double rtc, double t); void correct_calibrated_clock(double rtc, double t);
@ -134,6 +135,8 @@ private:
private: private:
void do_stop();
PT(MovieAudio) _movie; PT(MovieAudio) _movie;
OpenALAudioManager::SoundData *_sd; OpenALAudioManager::SoundData *_sd;
@ -168,6 +171,8 @@ private:
double _length; double _length;
int _loop_count; int _loop_count;
int _desired_mode;
// The calibrated clock is initialized when the // The calibrated clock is initialized when the
// sound starts playing, and is periodically corrected // sound starts playing, and is periodically corrected
// thereafter. // thereafter.