|
|
@@ -80,7 +80,7 @@ OpenALAudioSound(OpenALAudioManager* manager,
|
|
|
audio_warning("stereo sound " << movie->get_filename() << " will not be spatialized");
|
|
|
}
|
|
|
}
|
|
|
- release_sound_data();
|
|
|
+ release_sound_data(false);
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -99,15 +99,14 @@ OpenALAudioSound::
|
|
|
void OpenALAudioSound::
|
|
|
cleanup() {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
- if (_manager == 0) {
|
|
|
+ if (!is_valid()) {
|
|
|
return;
|
|
|
}
|
|
|
- if (_source) {
|
|
|
+ if (is_playing()) {
|
|
|
stop();
|
|
|
}
|
|
|
- if (_sd) {
|
|
|
- _manager->decrement_client_count(_sd);
|
|
|
- _sd = 0;
|
|
|
+ if (has_sound_data()) {
|
|
|
+ release_sound_data(true);
|
|
|
}
|
|
|
_manager->release_sound(this);
|
|
|
_manager = 0;
|
|
|
@@ -119,7 +118,8 @@ cleanup() {
|
|
|
void OpenALAudioSound::
|
|
|
play() {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
- if (_manager == 0) return;
|
|
|
+
|
|
|
+ if (!is_valid()) return;
|
|
|
|
|
|
PN_stdfloat px,py,pz,vx,vy,vz;
|
|
|
|
|
|
@@ -136,7 +136,7 @@ play() {
|
|
|
}
|
|
|
|
|
|
_manager->starting_sound(this);
|
|
|
- if (!_source) {
|
|
|
+ if (!is_playing()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -195,11 +195,14 @@ play() {
|
|
|
void OpenALAudioSound::
|
|
|
stop() {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
- if (_manager==0) return;
|
|
|
|
|
|
- if (_source) {
|
|
|
+ if (!is_valid()) return;
|
|
|
+
|
|
|
+ if (is_playing()) {
|
|
|
_manager->make_current();
|
|
|
|
|
|
+ nassertv(has_sound_data());
|
|
|
+
|
|
|
alGetError(); // clear errors
|
|
|
alSourceStop(_source);
|
|
|
al_audio_errcheck("stopping a source");
|
|
|
@@ -208,15 +211,14 @@ stop() {
|
|
|
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");
|
|
|
+ _manager->delete_buffer(buffer);
|
|
|
}
|
|
|
}
|
|
|
_stream_queued.resize(0);
|
|
|
}
|
|
|
|
|
|
_manager->stopping_sound(this);
|
|
|
- release_sound_data();
|
|
|
+ release_sound_data(false);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -225,6 +227,9 @@ stop() {
|
|
|
void OpenALAudioSound::
|
|
|
finished() {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
+
|
|
|
+ if (!is_valid()) return;
|
|
|
+
|
|
|
stop();
|
|
|
_current_time = _length;
|
|
|
if (!_finished_event.empty()) {
|
|
|
@@ -255,7 +260,8 @@ get_loop() const {
|
|
|
void OpenALAudioSound::
|
|
|
set_loop_count(unsigned long loop_count) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
- if (_manager==0) return;
|
|
|
+
|
|
|
+ if (!is_valid()) return;
|
|
|
|
|
|
if (loop_count >= 1000000000) {
|
|
|
loop_count = 0;
|
|
|
@@ -282,9 +288,14 @@ void OpenALAudioSound::
|
|
|
restart_stalled_audio() {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
ALenum status;
|
|
|
+
|
|
|
+ if (!is_valid()) return;
|
|
|
+ nassertv(is_playing());
|
|
|
+
|
|
|
if (_stream_queued.size() == 0) {
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
alGetError();
|
|
|
alGetSourcei(_source, AL_SOURCE_STATE, &status);
|
|
|
if (status != AL_PLAYING) {
|
|
|
@@ -298,6 +309,9 @@ restart_stalled_audio() {
|
|
|
void OpenALAudioSound::
|
|
|
queue_buffer(ALuint buffer, int samples, int loop_index, double time_offset) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
+
|
|
|
+ nassertv(is_playing());
|
|
|
+
|
|
|
// Now push the buffer into the stream queue.
|
|
|
alGetError();
|
|
|
alSourceQueueBuffers(_source,1,&buffer);
|
|
|
@@ -322,6 +336,8 @@ ALuint OpenALAudioSound::
|
|
|
make_buffer(int samples, int channels, int rate, unsigned char *data) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
|
|
|
+ nassertr(is_playing(), 0);
|
|
|
+
|
|
|
// Allocate a buffer to hold the data.
|
|
|
alGetError();
|
|
|
ALuint buffer;
|
|
|
@@ -354,6 +370,8 @@ int OpenALAudioSound::
|
|
|
read_stream_data(int bytelen, unsigned char *buffer) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
|
|
|
+ nassertr(has_sound_data(), 0);
|
|
|
+
|
|
|
MovieAudioCursor *cursor = _sd->_stream;
|
|
|
double length = cursor->length();
|
|
|
int channels = cursor->audio_channels();
|
|
|
@@ -404,6 +422,9 @@ read_stream_data(int bytelen, unsigned char *buffer) {
|
|
|
void OpenALAudioSound::
|
|
|
correct_calibrated_clock(double rtc, double t) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
+
|
|
|
+ nassertv(is_playing());
|
|
|
+
|
|
|
double cc = (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
|
|
|
double diff = cc-t;
|
|
|
_calibrated_clock_decavg = (_calibrated_clock_decavg * 0.95) + (diff * 0.05);
|
|
|
@@ -435,6 +456,11 @@ correct_calibrated_clock(double rtc, double t) {
|
|
|
void OpenALAudioSound::
|
|
|
pull_used_buffers() {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
+
|
|
|
+ if (!is_valid()) return;
|
|
|
+ nassertv(is_playing());
|
|
|
+ nassertv(has_sound_data());
|
|
|
+
|
|
|
while (_stream_queued.size()) {
|
|
|
ALuint buffer = 0;
|
|
|
ALint num_buffers = 0;
|
|
|
@@ -446,18 +472,34 @@ pull_used_buffers() {
|
|
|
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);
|
|
|
+ // This is certainly atypical: most implementations of OpenAL unqueue
|
|
|
+ // buffers in FIFO order. However, some (e.g. Apple's) can unqueue
|
|
|
+ // buffers out-of-order if playback is interrupted. So, we don't freak
|
|
|
+ // out unless `buffer` isn't in _stream_queued at all.
|
|
|
+ bool found_culprit = false;
|
|
|
+ for (auto it = _stream_queued.begin(); it != _stream_queued.end(); ++it) {
|
|
|
+ if (it->_buffer == buffer) {
|
|
|
+ // Phew. Found it. Just remove that.
|
|
|
+ _stream_queued.erase(it);
|
|
|
+ found_culprit = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!found_culprit) {
|
|
|
+ audio_error("corruption in stream queue");
|
|
|
+ cleanup();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ _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) {
|
|
|
+ _manager->delete_buffer(buffer);
|
|
|
+ }
|
|
|
}
|
|
|
} else {
|
|
|
break;
|
|
|
@@ -474,6 +516,10 @@ push_fresh_buffers() {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
static unsigned char data[65536];
|
|
|
|
|
|
+ if (!is_valid()) return;
|
|
|
+ nassertv(is_playing());
|
|
|
+ nassertv(has_sound_data());
|
|
|
+
|
|
|
if (_sd->_sample) {
|
|
|
while ((_loops_completed < _playing_loops) &&
|
|
|
(_stream_queued.size() < 100)) {
|
|
|
@@ -499,9 +545,9 @@ push_fresh_buffers() {
|
|
|
break;
|
|
|
}
|
|
|
ALuint buffer = make_buffer(samples, channels, rate, data);
|
|
|
- if (_manager == 0) return;
|
|
|
+ if (!is_valid() || !buffer) return;
|
|
|
queue_buffer(buffer, samples, loop_index, time_offset);
|
|
|
- if (_manager == 0) return;
|
|
|
+ if (!is_valid()) return;
|
|
|
fill += samples;
|
|
|
}
|
|
|
}
|
|
|
@@ -523,7 +569,7 @@ set_time(PN_stdfloat time) {
|
|
|
PN_stdfloat OpenALAudioSound::
|
|
|
get_time() const {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
- if (_manager == 0) {
|
|
|
+ if (!is_valid()) {
|
|
|
return 0.0;
|
|
|
}
|
|
|
return _current_time;
|
|
|
@@ -535,7 +581,9 @@ get_time() const {
|
|
|
void OpenALAudioSound::
|
|
|
cache_time(double rtc) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
- assert(_source != 0);
|
|
|
+
|
|
|
+ nassertv(is_playing());
|
|
|
+
|
|
|
double t=get_calibrated_clock(rtc);
|
|
|
double max = _length * _playing_loops;
|
|
|
if (t >= max) {
|
|
|
@@ -553,7 +601,7 @@ set_volume(PN_stdfloat volume) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
_volume=volume;
|
|
|
|
|
|
- if (_source) {
|
|
|
+ if (is_playing()) {
|
|
|
volume*=_manager->get_volume();
|
|
|
_manager->make_current();
|
|
|
alGetError(); // clear errors
|
|
|
@@ -597,7 +645,7 @@ void OpenALAudioSound::
|
|
|
set_play_rate(PN_stdfloat play_rate) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
_play_rate = play_rate;
|
|
|
- if (_source) {
|
|
|
+ if (is_playing()) {
|
|
|
alSourcef(_source, AL_PITCH, play_rate);
|
|
|
}
|
|
|
}
|
|
|
@@ -639,7 +687,7 @@ set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx
|
|
|
_velocity[1] = vz;
|
|
|
_velocity[2] = -vy;
|
|
|
|
|
|
- if (_source) {
|
|
|
+ if (is_playing()) {
|
|
|
_manager->make_current();
|
|
|
|
|
|
alGetError(); // clear errors
|
|
|
@@ -675,7 +723,7 @@ set_3d_min_distance(PN_stdfloat dist) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
_min_dist = dist;
|
|
|
|
|
|
- if (_source) {
|
|
|
+ if (is_playing()) {
|
|
|
_manager->make_current();
|
|
|
|
|
|
alGetError(); // clear errors
|
|
|
@@ -700,7 +748,7 @@ set_3d_max_distance(PN_stdfloat dist) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
_max_dist = dist;
|
|
|
|
|
|
- if (_source) {
|
|
|
+ if (is_playing()) {
|
|
|
_manager->make_current();
|
|
|
|
|
|
alGetError(); // clear errors
|
|
|
@@ -725,7 +773,7 @@ set_3d_drop_off_factor(PN_stdfloat factor) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
_drop_off_factor = factor;
|
|
|
|
|
|
- if (_source) {
|
|
|
+ if (is_playing()) {
|
|
|
_manager->make_current();
|
|
|
|
|
|
alGetError(); // clear errors
|
|
|
@@ -743,13 +791,16 @@ get_3d_drop_off_factor() const {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Sets whether the sound is marked "active". By default, the active flag
|
|
|
+ * Sets whether the sound is marked "active". By default, the active flag is
|
|
|
* true for all sounds. If the active flag is set to false for any particular
|
|
|
* sound, the sound will not be heard.
|
|
|
*/
|
|
|
void OpenALAudioSound::
|
|
|
set_active(bool active) {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
+
|
|
|
+ if (!is_valid()) return;
|
|
|
+
|
|
|
if (_active!=active) {
|
|
|
_active=active;
|
|
|
if (_active) {
|
|
|
@@ -813,7 +864,7 @@ get_name() const {
|
|
|
AudioSound::SoundStatus OpenALAudioSound::
|
|
|
status() const {
|
|
|
ReMutexHolder holder(OpenALAudioManager::_lock);
|
|
|
- if (_source==0) {
|
|
|
+ if (!is_playing()) {
|
|
|
return AudioSound::READY;
|
|
|
}
|
|
|
if ((_loops_completed >= _playing_loops)&&(_stream_queued.size()==0)) {
|