24 #define openal_audio_debug(x) \
25 audio_debug("OpenALAudioSound \""<<get_name() \
28#define openal_audio_debug(x) ((void)0)
50 _positional(positional),
52 _max_dist(1000000000.0f),
53 _drop_off_factor(1.0f),
59 _basename(movie->get_filename().get_basename()),
60 _active(manager->get_active()),
72 if (!require_sound_data()) {
77 _length = _sd->_length;
79 if (_sd->_channels != 1) {
80 audio_warning(
"stereo sound " << movie->
get_filename() <<
" will not be spatialized");
83 release_sound_data(
false);
99void OpenALAudioSound::
108 if (has_sound_data()) {
109 release_sound_data(
true);
111 _manager->release_sound(
this);
122 if (!is_valid())
return;
124 PN_stdfloat px,py,pz,vx,vy,vz;
133 if (!require_sound_data()) {
138 _manager->starting_sound(
this);
143 _manager->make_current();
149 alSourcei(_source,AL_SOURCE_RELATIVE,_positional?AL_FALSE:AL_TRUE);
150 al_audio_errcheck(
"alSourcei(_source,AL_SOURCE_RELATIVE)");
162 _playing_loops = _loop_count;
163 if (_playing_loops == 0) {
164 _playing_loops = 1000000000;
166 _loops_completed = 0;
168 double play_rate = _play_rate * _manager->get_play_rate();
169 audio_debug(
"playing. Rate=" << play_rate);
170 alSourcef(_source, AL_PITCH, play_rate);
171 _playing_rate = play_rate;
174 push_fresh_buffers();
175 alSourcef(_source, AL_SEC_OFFSET, _start_time);
176 _stream_queued[0]._time_offset = _start_time;
177 restart_stalled_audio();
179 audio_debug(
"Play: stream tell = " << _sd->_stream->tell() <<
" seeking " << _start_time);
180 if (_sd->_stream->tell() != _start_time) {
181 _sd->_stream->seek(_start_time);
183 push_fresh_buffers();
184 restart_stalled_audio();
187 set_calibrated_clock(rtc, _start_time, 1.0);
188 _current_time = _start_time;
199 if (!is_valid())
return;
202 _manager->make_current();
204 nassertv(has_sound_data());
207 alSourceStop(_source);
208 al_audio_errcheck(
"stopping a source");
209 alSourcei(_source, AL_BUFFER, 0);
210 al_audio_errcheck(
"clear source buffers");
211 for (
int i=0; i<((int)(_stream_queued.size())); i++) {
212 ALuint buffer = _stream_queued[i]._buffer;
213 if (buffer != _sd->_sample) {
214 _manager->delete_buffer(buffer);
217 _stream_queued.resize(0);
222 _manager->stopping_sound(
this);
223 release_sound_data(
false);
229void OpenALAudioSound::
233 if (!is_valid())
return;
236 _current_time = _length;
237 if (!_finished_event.empty()) {
238 throw_event(_finished_event);
248 set_loop_count((loop)?0:1);
256 return (_loop_count == 0);
262void OpenALAudioSound::
263set_loop_count(
unsigned long loop_count) {
266 if (!is_valid())
return;
268 if (loop_count >= 1000000000) {
271 _loop_count=loop_count;
289void OpenALAudioSound::
290restart_stalled_audio() {
294 if (!is_valid())
return;
295 nassertv(is_playing());
297 if (_stream_queued.size() == 0) {
302 alGetSourcei(_source, AL_SOURCE_STATE, &
status);
303 if (
status != AL_PLAYING) {
304 alSourcePlay(_source);
311void OpenALAudioSound::
312queue_buffer(ALuint buffer,
int samples,
int loop_index,
double time_offset) {
315 nassertv(is_playing());
319 alSourceQueueBuffers(_source,1,&buffer);
320 ALenum err = alGetError();
321 if (err != AL_NO_ERROR) {
322 audio_error(
"could not load sample buffer into the queue");
327 buf._buffer = buffer;
328 buf._samples = samples;
329 buf._loop_index = loop_index;
330 buf._time_offset = time_offset;
331 _stream_queued.push_back(buf);
337ALuint OpenALAudioSound::
338make_buffer(
int samples,
int channels,
int rate,
unsigned char *data) {
341 nassertr(is_playing(), 0);
346 alGenBuffers(1, &buffer);
347 if (alGetError() != AL_NO_ERROR) {
348 audio_error(
"could not allocate an OpenAL buffer object");
355 (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
356 data, samples * channels * 2, rate);
357 int err = alGetError();
358 if (err != AL_NO_ERROR) {
359 audio_error(
"could not fill OpenAL buffer object with data");
371int OpenALAudioSound::
372read_stream_data(
int bytelen,
unsigned char *buffer) {
375 nassertr(has_sound_data(), 0);
378 double length = cursor->length();
379 int channels = cursor->audio_channels();
380 int rate = cursor->audio_rate();
381 int space = bytelen / (channels * 2);
384 while (space && (_loops_completed < _playing_loops)) {
385 double t = cursor->tell();
386 double remain =
length - t;
390 int samples = (int)(remain * rate);
392 _loops_completed += 1;
396 if (_sd->_stream->ready() == 0) {
397 if (_sd->_stream->aborted()) {
398 _loops_completed = _playing_loops;
402 if (samples > space) {
405 if (samples > _sd->_stream->ready()) {
406 samples = _sd->_stream->ready();
410 audio_debug(
"Streaming " << cursor->get_source()->get_name() <<
" at " << t <<
" hash " << hval);
413 buffer += (samples * channels * 2);
424void OpenALAudioSound::
425correct_calibrated_clock(
double rtc,
double t) {
428 nassertv(is_playing());
430 double cc = (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
432 _calibrated_clock_decavg = (_calibrated_clock_decavg * 0.95) + (diff * 0.05);
434 set_calibrated_clock(rtc, t, 1.0);
435 _calibrated_clock_decavg = 0.0;
438 if ((_calibrated_clock_decavg > 0.01) && (diff > 0.01)) {
441 if ((_calibrated_clock_decavg < -0.01) && (diff < -0.01)) {
444 if ((_calibrated_clock_decavg < -0.05) && (diff < -0.05)) {
447 if ((_calibrated_clock_decavg < -0.15) && (diff < -0.15)) {
450 set_calibrated_clock(rtc, cc, scale);
452 cc = (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
458void OpenALAudioSound::
462 if (!is_valid())
return;
463 nassertv(is_playing());
464 nassertv(has_sound_data());
466 while (_stream_queued.size()) {
468 ALint num_buffers = 0;
469 alGetSourcei(_source, AL_BUFFERS_PROCESSED, &num_buffers);
470 if (num_buffers <= 0) {
473 alSourceUnqueueBuffers(_source, 1, &buffer);
474 int err = alGetError();
475 if (err == AL_NO_ERROR) {
476 if (_stream_queued[0]._buffer != buffer) {
481 bool found_culprit =
false;
482 for (
auto it = _stream_queued.begin(); it != _stream_queued.end(); ++it) {
483 if (it->_buffer == buffer) {
485 _stream_queued.erase(it);
486 found_culprit =
true;
490 if (!found_culprit) {
491 audio_error(
"corruption in stream queue");
496 _stream_queued.pop_front();
497 if (_stream_queued.size()) {
498 double al = _stream_queued[0]._time_offset + _stream_queued[0]._loop_index * _length;
500 correct_calibrated_clock(rtc, al);
502 if (buffer != _sd->_sample) {
503 _manager->delete_buffer(buffer);
516void OpenALAudioSound::
517push_fresh_buffers() {
519 static unsigned char data[65536];
521 if (!is_valid())
return;
522 nassertv(is_playing());
523 nassertv(has_sound_data());
526 while ((_loops_completed < _playing_loops) &&
527 (_stream_queued.size() < 100)) {
528 queue_buffer(_sd->_sample, 0,_loops_completed, 0.0);
529 _loops_completed += 1;
533 int channels = cursor->audio_channels();
534 int rate = cursor->audio_rate();
537 for (
size_t i = 0; i < _stream_queued.size(); i++) {
538 fill += _stream_queued[i]._samples;
541 while ((_loops_completed < _playing_loops) &&
542 (fill < (
int)(audio_buffering_seconds * rate * channels))) {
543 int loop_index = _loops_completed;
544 double time_offset = cursor->tell();
545 int samples = read_stream_data(65536, data);
549 ALuint buffer = make_buffer(samples, channels, rate, data);
550 if (!is_valid() || !buffer)
return;
551 queue_buffer(buffer, samples, loop_index, time_offset);
552 if (!is_valid())
return;
577 return _current_time;
583void OpenALAudioSound::
584cache_time(
double rtc) {
587 nassertv(is_playing());
589 double t=get_calibrated_clock(rtc);
590 double max = _length * _playing_loops;
592 _current_time = _length;
594 _current_time = fmod(t, _length);
607 volume*=_manager->get_volume();
608 _manager->make_current();
610 alSourcef(_source,AL_GAIN,volume);
611 al_audio_errcheck(
"alSourcef(_source,AL_GAIN)");
628 audio_debug(
"OpenALAudioSound::set_balance() not implemented");
637 audio_debug(
"OpenALAudioSound::get_balance() not implemented");
649 _play_rate = play_rate;
651 alSourcef(_source, AL_PITCH, play_rate);
658PN_stdfloat OpenALAudioSound::
659get_play_rate()
const {
682set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz) {
693 _manager->make_current();
696 alSourcefv(_source,AL_POSITION,_location);
697 al_audio_errcheck(
"alSourcefv(_source,AL_POSITION)");
698 alSourcefv(_source,AL_VELOCITY,_velocity);
699 al_audio_errcheck(
"alSourcefv(_source,AL_VELOCITY)");
708get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz) {
729 _manager->make_current();
732 alSourcef(_source,AL_REFERENCE_DISTANCE,_min_dist);
733 al_audio_errcheck(
"alSourcefv(_source,AL_REFERENCE_DISTANCE)");
754 _manager->make_current();
757 alSourcef(_source,AL_MAX_DISTANCE,_max_dist);
758 al_audio_errcheck(
"alSourcefv(_source,AL_MAX_DISTANCE)");
776 _drop_off_factor = factor;
779 _manager->make_current();
782 alSourcef(_source,AL_ROLLOFF_FACTOR,_drop_off_factor*_manager->audio_3d_get_drop_off_factor());
783 al_audio_errcheck(
"alSourcefv(_source,AL_ROLLOFF_FACTOR)");
792 return _drop_off_factor;
804 if (!is_valid())
return;
806 if (_active!=active) {
810 if (_paused && _loop_count==0) {
819 if (_loop_count==0) {
840void OpenALAudioSound::
841set_finished_event(
const std::string& event) {
842 _finished_event = event;
848const std::string& OpenALAudioSound::
849get_finished_event()
const {
850 return _finished_event;
870 return AudioSound::READY;
872 if ((_loops_completed >= _playing_loops)&&(_stream_queued.size()==0)) {
873 return AudioSound::READY;
875 return AudioSound::PLAYING;
static size_t add_hash(size_t start, const uint32_t *words, size_t num_words)
Adds a linear sequence of uint32 words to the hash.
A MovieAudio is actually any source that provides a sequence of audio samples.
virtual void seek(double offset)
Skips to the specified offset within the file.
void read_samples(int n, Datagram *dg)
Read audio samples from the stream into a Datagram.
A MovieAudio is actually any source that provides a sequence of audio samples.
get_filename
Returns the movie's filename.
AudioSound::SoundStatus status() const
Get status of the sound.
void set_loop(bool loop=true)
Turns looping on and off.
void get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz)
Get position and velocity of this sound Currently unimplemented.
void set_3d_min_distance(PN_stdfloat dist)
Set the distance that this sound begins to fall off.
bool get_loop() const
Returns whether looping is on or off.
void set_3d_max_distance(PN_stdfloat dist)
Set the distance that this sound stops falling off.
PN_stdfloat get_time() const
Gets the play position within the sound.
void set_active(bool active=true)
Sets whether the sound is marked "active".
void play()
Plays a sound.
const std::string & get_name() const
Get name of sound file.
PN_stdfloat get_3d_drop_off_factor() const
Control the effect distance has on audability.
bool get_active() const
Returns whether the sound has been marked "active".
PN_stdfloat get_volume() const
Gets the current volume of a sound.
void set_play_rate(PN_stdfloat play_rate=1.0f)
Sets the speed at which a sound plays back.
PN_stdfloat length() const
Get length.
void set_time(PN_stdfloat time=0.0)
The next time you call play, the sound will start from the specified offset.
PN_stdfloat get_3d_max_distance() const
Get the distance that this sound stops falling off.
void set_3d_drop_off_factor(PN_stdfloat factor)
Control the effect distance has on audability.
PN_stdfloat get_balance() const
-1.0 to 1.0 scale -1 should be all the way left.
void set_volume(PN_stdfloat volume=1.0)
0.0 to 1.0 scale of volume converted to Fmod's internal 0.0 to 255.0 scale.
void set_balance(PN_stdfloat balance_right=0.0)
-1.0 to 1.0 scale
PN_stdfloat get_3d_min_distance() const
Get the distance that this sound begins to fall off.
void set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz)
Set position and velocity of this sound.
unsigned long get_loop_count() const
Return how many times a sound will loop.
Similar to MutexHolder, but for a reentrant mutex.
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
TypeHandle is the identifier used to differentiate C++ class types.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.