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();
177#ifdef HAVE_OPENAL_FRAMEWORK
179 alGetSourcei(_source, AL_SOURCE_STATE, &
status);
180 if (
status != AL_PLAYING) {
181 alSourcePlay(_source);
184 alSourcef(_source, AL_SEC_OFFSET, _start_time);
185 _stream_queued[0]._time_offset = _start_time;
186#ifndef HAVE_OPENAL_FRAMEWORK
187 restart_stalled_audio();
190 audio_debug(
"Play: stream tell = " << _sd->_stream->tell() <<
" seeking " << _start_time);
191 if (_sd->_stream->tell() != _start_time) {
192 _sd->_stream->seek(_start_time);
194 push_fresh_buffers();
195 restart_stalled_audio();
198 set_calibrated_clock(rtc, _start_time, 1.0);
199 _current_time = _start_time;
210 if (!is_valid())
return;
213 _manager->make_current();
215 nassertv(has_sound_data());
218 alSourceStop(_source);
219 al_audio_errcheck(
"stopping a source");
220 alSourcei(_source, AL_BUFFER, 0);
221 al_audio_errcheck(
"clear source buffers");
222 for (
int i=0; i<((int)(_stream_queued.size())); i++) {
223 ALuint buffer = _stream_queued[i]._buffer;
224 if (buffer != _sd->_sample) {
225 _manager->delete_buffer(buffer);
228 _stream_queued.resize(0);
233 _manager->stopping_sound(
this);
234 release_sound_data(
false);
240void OpenALAudioSound::
244 if (!is_valid())
return;
247 _current_time = _length;
248 if (!_finished_event.empty()) {
249 throw_event(_finished_event);
259 set_loop_count((loop)?0:1);
267 return (_loop_count == 0);
273void OpenALAudioSound::
274set_loop_count(
unsigned long loop_count) {
277 if (!is_valid())
return;
279 if (loop_count >= 1000000000) {
282 _loop_count=loop_count;
300void OpenALAudioSound::
301restart_stalled_audio() {
305 if (!is_valid())
return;
306 nassertv(is_playing());
308 if (_stream_queued.size() == 0) {
313 alGetSourcei(_source, AL_SOURCE_STATE, &
status);
314 if (
status != AL_PLAYING) {
315 alSourcePlay(_source);
322void OpenALAudioSound::
323queue_buffer(ALuint buffer,
int samples,
int loop_index,
double time_offset) {
326 nassertv(is_playing());
330 alSourceQueueBuffers(_source,1,&buffer);
331 ALenum err = alGetError();
332 if (err != AL_NO_ERROR) {
333 audio_error(
"could not load sample buffer into the queue");
338 buf._buffer = buffer;
339 buf._samples = samples;
340 buf._loop_index = loop_index;
341 buf._time_offset = time_offset;
342 _stream_queued.push_back(buf);
348ALuint OpenALAudioSound::
349make_buffer(
int samples,
int channels,
int rate,
unsigned char *data) {
352 nassertr(is_playing(), 0);
357 alGenBuffers(1, &buffer);
358 if (alGetError() != AL_NO_ERROR) {
359 audio_error(
"could not allocate an OpenAL buffer object");
366 (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
367 data, samples * channels * 2, rate);
368 int err = alGetError();
369 if (err != AL_NO_ERROR) {
370 audio_error(
"could not fill OpenAL buffer object with data");
382int OpenALAudioSound::
383read_stream_data(
int bytelen,
unsigned char *buffer) {
386 nassertr(has_sound_data(), 0);
389 double length = cursor->length();
390 int channels = cursor->audio_channels();
391 int rate = cursor->audio_rate();
392 int space = bytelen / (channels * 2);
395 while (space && (_loops_completed < _playing_loops)) {
396 double t = cursor->tell();
397 double remain =
length - t;
401 int samples = (int)(remain * rate);
403 _loops_completed += 1;
407 if (_sd->_stream->ready() == 0) {
408 if (_sd->_stream->aborted()) {
409 _loops_completed = _playing_loops;
413 if (samples > space) {
416 if (samples > _sd->_stream->ready()) {
417 samples = _sd->_stream->ready();
421 audio_debug(
"Streaming " << cursor->get_source()->get_name() <<
" at " << t <<
" hash " << hval);
424 buffer += (samples * channels * 2);
435void OpenALAudioSound::
436correct_calibrated_clock(
double rtc,
double t) {
439 nassertv(is_playing());
441 double cc = (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
443 _calibrated_clock_decavg = (_calibrated_clock_decavg * 0.95) + (diff * 0.05);
445 set_calibrated_clock(rtc, t, 1.0);
446 _calibrated_clock_decavg = 0.0;
449 if ((_calibrated_clock_decavg > 0.01) && (diff > 0.01)) {
452 if ((_calibrated_clock_decavg < -0.01) && (diff < -0.01)) {
455 if ((_calibrated_clock_decavg < -0.05) && (diff < -0.05)) {
458 if ((_calibrated_clock_decavg < -0.15) && (diff < -0.15)) {
461 set_calibrated_clock(rtc, cc, scale);
463 cc = (rtc - _calibrated_clock_base) * _calibrated_clock_scale;
469void OpenALAudioSound::
473 if (!is_valid())
return;
474 nassertv(is_playing());
475 nassertv(has_sound_data());
477 while (_stream_queued.size()) {
479 ALint num_buffers = 0;
480 alGetSourcei(_source, AL_BUFFERS_PROCESSED, &num_buffers);
481 if (num_buffers <= 0) {
484 alSourceUnqueueBuffers(_source, 1, &buffer);
485 int err = alGetError();
486 if (err == AL_NO_ERROR) {
487 if (_stream_queued[0]._buffer != buffer) {
492 bool found_culprit =
false;
493 for (
auto it = _stream_queued.begin(); it != _stream_queued.end(); ++it) {
494 if (it->_buffer == buffer) {
496 _stream_queued.erase(it);
497 found_culprit =
true;
501 if (!found_culprit) {
502 audio_error(
"corruption in stream queue");
507 _stream_queued.pop_front();
508 if (_stream_queued.size()) {
509 double al = _stream_queued[0]._time_offset + _stream_queued[0]._loop_index * _length;
511 correct_calibrated_clock(rtc, al);
513 if (buffer != _sd->_sample) {
514 _manager->delete_buffer(buffer);
527void OpenALAudioSound::
528push_fresh_buffers() {
530 static unsigned char data[65536];
532 if (!is_valid())
return;
533 nassertv(is_playing());
534 nassertv(has_sound_data());
537 while ((_loops_completed < _playing_loops) &&
538 (_stream_queued.size() < 100)) {
539 queue_buffer(_sd->_sample, 0,_loops_completed, 0.0);
540 _loops_completed += 1;
544 int channels = cursor->audio_channels();
545 int rate = cursor->audio_rate();
548 for (
size_t i = 0; i < _stream_queued.size(); i++) {
549 fill += _stream_queued[i]._samples;
552 while ((_loops_completed < _playing_loops) &&
553 (fill < (
int)(audio_buffering_seconds * rate * channels))) {
554 int loop_index = _loops_completed;
555 double time_offset = cursor->tell();
556 int samples = read_stream_data(65536, data);
560 ALuint buffer = make_buffer(samples, channels, rate, data);
561 if (!is_valid() || !buffer)
return;
562 queue_buffer(buffer, samples, loop_index, time_offset);
563 if (!is_valid())
return;
588 return _current_time;
594void OpenALAudioSound::
595cache_time(
double rtc) {
598 nassertv(is_playing());
600 double t=get_calibrated_clock(rtc);
601 double max = _length * _playing_loops;
603 _current_time = _length;
605 _current_time = fmod(t, _length);
618 volume*=_manager->get_volume();
619 _manager->make_current();
621 alSourcef(_source,AL_GAIN,volume);
622 al_audio_errcheck(
"alSourcef(_source,AL_GAIN)");
639 audio_debug(
"OpenALAudioSound::set_balance() not implemented");
648 audio_debug(
"OpenALAudioSound::get_balance() not implemented");
660 _play_rate = play_rate;
662 alSourcef(_source, AL_PITCH, play_rate);
669PN_stdfloat OpenALAudioSound::
670get_play_rate()
const {
693set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz) {
704 _manager->make_current();
707 alSourcefv(_source,AL_POSITION,_location);
708 al_audio_errcheck(
"alSourcefv(_source,AL_POSITION)");
709 alSourcefv(_source,AL_VELOCITY,_velocity);
710 al_audio_errcheck(
"alSourcefv(_source,AL_VELOCITY)");
719get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz) {
740 _manager->make_current();
743 alSourcef(_source,AL_REFERENCE_DISTANCE,_min_dist);
744 al_audio_errcheck(
"alSourcefv(_source,AL_REFERENCE_DISTANCE)");
765 _manager->make_current();
768 alSourcef(_source,AL_MAX_DISTANCE,_max_dist);
769 al_audio_errcheck(
"alSourcefv(_source,AL_MAX_DISTANCE)");
787 _drop_off_factor = factor;
790 _manager->make_current();
793 alSourcef(_source,AL_ROLLOFF_FACTOR,_drop_off_factor*_manager->audio_3d_get_drop_off_factor());
794 al_audio_errcheck(
"alSourcefv(_source,AL_ROLLOFF_FACTOR)");
803 return _drop_off_factor;
815 if (!is_valid())
return;
817 if (_active!=active) {
821 if (_paused && _loop_count==0) {
830 if (_loop_count==0) {
851void OpenALAudioSound::
852set_finished_event(
const std::string& event) {
853 _finished_event = event;
859const std::string& OpenALAudioSound::
860get_finished_event()
const {
861 return _finished_event;
881 return AudioSound::READY;
883 if ((_loops_completed >= _playing_loops)&&(_stream_queued.size()==0)) {
884 return AudioSound::READY;
886 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.