28#ifndef ALC_DEFAULT_ALL_DEVICES_SPECIFIER
29#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012
32#ifndef ALC_ALL_DEVICES_SPECIFIER
33#define ALC_ALL_DEVICES_SPECIFIER 0x1013
41ReMutex OpenALAudioManager::_lock;
42int OpenALAudioManager::_active_managers = 0;
43bool OpenALAudioManager::_openal_active =
false;
44ALCdevice* OpenALAudioManager::_device =
nullptr;
45ALCcontext* OpenALAudioManager::_context =
nullptr;
56void al_audio_errcheck(
const char *context) {
57 ALenum result = alGetError();
58 if (result != AL_NO_ERROR) {
59 audio_error(context <<
": " << alGetString(result) );
63void alc_audio_errcheck(
const char *context,ALCdevice* device) {
64 ALCenum result = alcGetError(device);
65 if (result != ALC_NO_ERROR) {
66 audio_error(context <<
": " << alcGetString(device,result) );
74 audio_debug(
"Create_OpenALAudioManager()");
85 if (_managers ==
nullptr) {
86 _managers =
new Managers;
87 _al_sources =
new SourceCache;
90 _managers->insert(
this);
92 _cleanup_required =
true;
93 _active = audio_active;
94 _volume = audio_volume;
97 _cache_limit = audio_cache_limit;
99 _concurrent_sound_limit = 0;
103 _distance_factor = 1;
104 _drop_off_factor = 1;
123 if (_active_managers == 0 || !_openal_active) {
125 string dev_name = select_audio_device();
127 if (!dev_name.empty()) {
129 audio_cat.info() <<
"Using OpenAL device " << dev_name <<
"\n";
130 _device = alcOpenDevice(dev_name.c_str());
132 if (_device ==
nullptr) {
134 <<
"Couldn't open OpenAL device \"" << dev_name <<
"\", falling back to default device\n";
137 audio_cat.info() <<
"Using default OpenAL device\n";
140 if (_device ==
nullptr) {
142 _device = alcOpenDevice(
nullptr);
144 if (_device ==
nullptr && dev_name !=
"OpenAL Soft") {
146 _device = alcOpenDevice(
"OpenAL Soft");
148 if (_device ==
nullptr) {
150 <<
"Couldn't open default OpenAL device\n";
155 if (_device !=
nullptr) {
157 alcGetError(_device);
158 _context = alcCreateContext(_device,
nullptr);
159 alc_audio_errcheck(
"alcCreateContext(_device, NULL)", _device);
160 if (_context !=
nullptr) {
161 _openal_active =
true;
169 nassertv(_active_managers>0);
171 if (!_device || !_context) {
172 audio_error(
"OpenALAudioManager: No open device or context");
175 alcGetError(_device);
176 alcMakeContextCurrent(_context);
177 alc_audio_errcheck(
"alcMakeContextCurrent(_context)", _device);
184 if (audio_cat.is_debug()) {
186 <<
"ALC_DEVICE_SPECIFIER:" << alcGetString(_device, ALC_DEVICE_SPECIFIER) << endl;
190 if (audio_cat.is_debug()) {
191 audio_cat.debug() <<
"AL_RENDERER:" << alGetString(AL_RENDERER) << endl;
192 audio_cat.debug() <<
"AL_VENDOR:" << alGetString(AL_VENDOR) << endl;
193 audio_cat.debug() <<
"AL_VERSION:" << alGetString(AL_VERSION) << endl;
201~OpenALAudioManager() {
203 nassertv(_managers !=
nullptr);
204 Managers::iterator mi = _managers->find(
this);
205 nassertv(mi != _managers->end());
206 _managers->erase(mi);
219 if (_managers !=
nullptr) {
220 Managers::iterator mi;
221 for (mi = _managers->begin(); mi != _managers->end(); ++mi) {
226 nassertv(_active_managers == 0);
243string OpenALAudioManager::
244select_audio_device() {
245 string selected_device = openal_device;
247 const char *devices =
nullptr;
250 if (alcIsExtensionPresent(
nullptr,
"ALC_ENUMERATE_ALL_EXT") == AL_TRUE) {
251 string default_device = alcGetString(
nullptr, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
252 devices = (
const char *)alcGetString(
nullptr, ALC_ALL_DEVICES_SPECIFIER);
255 if (audio_cat.is_debug()) {
256 audio_cat.debug() <<
"All OpenAL devices:\n";
260 string device(devices);
261 devices += device.size() + 1;
263 if (audio_cat.is_debug()) {
264 if (device == selected_device) {
265 audio_cat.debug() <<
" " << device <<
" [selected]\n";
266 }
else if (device == default_device) {
267 audio_cat.debug() <<
" " << device <<
" [default]\n";
269 audio_cat.debug() <<
" " << device <<
"\n";
275 audio_cat.debug() <<
"ALC_ENUMERATE_ALL_EXT not supported\n";
280 if (alcIsExtensionPresent(
nullptr,
"ALC_ENUMERATION_EXT") == AL_TRUE) {
281 string default_device = alcGetString(
nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
282 devices = (
const char *)alcGetString(
nullptr, ALC_DEVICE_SPECIFIER);
285 if (audio_cat.is_debug()) {
286 audio_cat.debug() <<
"OpenAL drivers:\n";
290 string device(devices);
291 devices += device.size() + 1;
293 if (selected_device.empty() && device ==
"OpenAL Soft" &&
294 default_device ==
"Generic Software") {
296 selected_device =
"OpenAL Soft";
299 if (audio_cat.is_debug()) {
300 if (device == selected_device) {
301 audio_cat.debug() <<
" " << device <<
" [selected]\n";
302 }
else if (device == default_device) {
303 audio_cat.debug() <<
" " << device <<
" [default]\n";
305 audio_cat.debug() <<
" " << device <<
"\n";
311 audio_cat.debug() <<
"ALC_ENUMERATION_EXT not supported\n";
314 return selected_device;
321void OpenALAudioManager::
322make_current()
const {
331bool OpenALAudioManager::
334 int channels = source->audio_channels();
335 if ((channels != 1)&&(channels != 2)) {
336 audio_error(
"Currently, only mono and stereo are supported.");
347bool OpenALAudioManager::
350 if (mode == SM_stream) {
354 if (source->get_source()->get_filename().empty()) {
358 if (source->
ready() != 0x40000000) {
362 if (source->length() > 3600.0) {
366 int channels = source->audio_channels();
367 int samples = (int)(source->length() * source->audio_rate());
368 int bytes = samples * channels * 2;
369 if ((mode == SM_heuristic)&&(bytes > audio_preload_threshold)) {
382OpenALAudioManager::SoundData *OpenALAudioManager::
390 if (mode != SM_stream) {
391 SampleCache::iterator lsmi=_sample_cache.find(path);
392 if (lsmi != _sample_cache.end()) {
393 SoundData *sd = (*lsmi).second;
394 increment_client_count(sd);
399 if (mode != SM_sample) {
400 ExpirationQueue::iterator exqi;
401 for (exqi=_expiring_streams.begin(); exqi!=_expiring_streams.end(); exqi++) {
402 SoundData *sd = (SoundData*)(*exqi);
403 if (sd->_movie->get_filename() == path) {
404 increment_client_count(sd);
412 if (stream ==
nullptr) {
413 audio_error(
"Cannot open file: "<<path);
417 if (!can_use_audio(stream)) {
418 audio_error(
"File is not in usable format: "<<path);
422 SoundData *sd =
new SoundData();
423 sd->_client_count = 1;
426 sd->_rate = stream->audio_rate();
427 sd->_channels = stream->audio_channels();
428 sd->_length = stream->length();
429 audio_debug(
"Creating: " << sd->_movie->get_filename().get_basename());
430 audio_debug(
" - Rate: " << sd->_rate);
431 audio_debug(
" - Channels: " << sd->_channels);
432 audio_debug(
" - Length: " << sd->_length);
434 if (should_load_audio(stream, mode)) {
435 audio_debug(path.
get_basename() <<
": loading as sample");
439 alGenBuffers(1, &sd->_sample);
440 al_audio_errcheck(
"alGenBuffers");
441 if (sd->_sample == 0) {
442 audio_error(
"Could not create an OpenAL buffer object");
446 int channels = stream->audio_channels();
447 int samples = (int)(stream->length() * stream->audio_rate());
448 int16_t *data =
new int16_t[samples * channels];
449 stream->read_samples(samples, data);
450 alBufferData(sd->_sample,
451 (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
452 data, samples * channels * 2, stream->audio_rate());
454 int err = alGetError();
455 if (err != AL_NO_ERROR) {
456 audio_error(
"could not fill OpenAL buffer object with data");
460 _sample_cache.insert(SampleCache::value_type(path, sd));
462 audio_debug(path.
get_basename() <<
": loading as stream");
463 sd->_stream = stream;
473get_sound(
MovieAudio *sound,
bool positional,
int mode) {
476 return get_null_sound();
484 return get_null_sound();
487 _all_sounds.insert(oas);
496get_sound(
const Filename &file_name,
bool positional,
int mode) {
499 return get_null_sound();
507 audio_error(
"get_sound - invalid filename");
519 return get_null_sound();
522 _all_sounds.insert(oas);
540 SampleCache::iterator sci = _sample_cache.find(path);
541 if (sci == _sample_cache.end()) {
542 sci = _sample_cache.find(file_name);
544 if (sci != _sample_cache.end()) {
545 SoundData *sd = (*sci).second;
546 if (sd->_client_count == 0) {
547 _expiring_samples.erase(sd->_expire);
548 _sample_cache.erase(sci);
553 ExpirationQueue::iterator exqi;
554 for (exqi = _expiring_streams.begin(); exqi != _expiring_streams.end();) {
555 SoundData *sd = (SoundData *)(*exqi);
556 if (sd->_client_count == 0) {
557 if (sd->_movie->get_filename() == path ||
558 sd->_movie->get_filename() == file_name) {
559 exqi = _expiring_streams.erase(exqi);
574 discard_excess_cache(0);
584 discard_excess_cache(count);
590unsigned int OpenALAudioManager::
591get_cache_limit()
const {
598void OpenALAudioManager::
601 AllSounds::iterator ai = _all_sounds.find(audioSound);
602 if (ai != _all_sounds.end()) {
603 _all_sounds.erase(ai);
613 if (_volume!=volume) {
617 AllSounds::iterator i=_all_sounds.begin();
618 for (; i!=_all_sounds.end(); ++i) {
619 (**i).set_volume((**i).get_volume());
648 if (_play_rate!=play_rate) {
649 _play_rate = play_rate;
651 AllSounds::iterator i=_all_sounds.begin();
652 for (; i!=_all_sounds.end(); ++i) {
653 (**i).set_play_rate((**i).get_play_rate());
672 if (_active!=active) {
675 AllSounds::iterator i=_all_sounds.begin();
676 for (; i!=_all_sounds.end(); ++i) {
677 (**i).set_active(_active);
685bool OpenALAudioManager::
702audio_3d_set_listener_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat uz) {
714 _forward_up[2] = -fy;
718 _forward_up[5] = -uy;
724 alListenerfv(AL_POSITION,_position);
725 al_audio_errcheck(
"alListerfv(AL_POSITION)");
726 alListenerfv(AL_VELOCITY,_velocity);
727 al_audio_errcheck(
"alListerfv(AL_VELOCITY)");
728 alListenerfv(AL_ORIENTATION,_forward_up);
729 al_audio_errcheck(
"alListerfv(AL_ORIENTATION)");
736audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz) {
746 *fx = _forward_up[0];
747 *fy = -_forward_up[2];
748 *fz = _forward_up[1];
750 *ux = _forward_up[3];
751 *uy = -_forward_up[5];
752 *uz = _forward_up[4];
764 _distance_factor = factor;
770 if (_distance_factor>0) {
771 alSpeedOfSound(343.3*_distance_factor);
772 al_audio_errcheck(
"alSpeedOfSound()");
775 alDopplerFactor(_doppler_factor);
776 al_audio_errcheck(
"alDopplerFactor()");
778 audio_debug(
"can't set speed of sound if distance_factor <=0.0, setting doppler factor to 0.0 instead");
779 alDopplerFactor(0.0);
780 al_audio_errcheck(
"alDopplerFactor()");
783 AllSounds::iterator i=_all_sounds.begin();
784 for (; i!=_all_sounds.end(); ++i) {
785 (**i).set_3d_min_distance((**i).get_3d_min_distance());
786 (**i).set_3d_max_distance((**i).get_3d_max_distance());
795 return _distance_factor;
804 _doppler_factor = factor;
809 alDopplerFactor(_doppler_factor);
810 al_audio_errcheck(
"alDopplerFactor()");
816PN_stdfloat OpenALAudioManager::
817audio_3d_get_doppler_factor()
const {
818 return _doppler_factor;
827 _drop_off_factor = factor;
829 AllSounds::iterator i=_all_sounds.begin();
830 for (; i!=_all_sounds.end(); ++i) {
831 (**i).set_3d_drop_off_factor((**i).get_3d_drop_off_factor());
838PN_stdfloat OpenALAudioManager::
839audio_3d_get_drop_off_factor()
const {
840 return _drop_off_factor;
848void OpenALAudioManager::
854 if (audio->_source) {
862 if (_concurrent_sound_limit) {
863 reduce_sounds_playing_to(_concurrent_sound_limit-1);
867 if (_al_sources->empty()) {
870 alGenSources(1,&source);
871 ALenum result = alGetError();
872 if (result!=AL_NO_ERROR) {
873 audio_error(
"alGenSources(): " << alGetString(result) );
876 reduce_sounds_playing_to(_sounds_playing.size()-1);
881 if (!source && !_al_sources->empty()) {
882 source = *(_al_sources->begin());
883 _al_sources->erase(source);
886 audio->_source = source;
889 _sounds_playing.insert(audio);
896void OpenALAudioManager::
899 if (audio->_source) {
900 _al_sources->insert(audio->_source);
903 _sounds_playing.erase(audio);
909void OpenALAudioManager::
910set_concurrent_sound_limit(
unsigned int limit) {
912 _concurrent_sound_limit = limit;
913 reduce_sounds_playing_to(_concurrent_sound_limit);
919unsigned int OpenALAudioManager::
920get_concurrent_sound_limit()
const {
921 return _concurrent_sound_limit;
927void OpenALAudioManager::
928reduce_sounds_playing_to(
unsigned int count) {
934 int limit = _sounds_playing.size() - count;
935 while (limit-- > 0) {
936 SoundsPlaying::iterator sound = _sounds_playing.begin();
937 nassertv(sound != _sounds_playing.end());
954 reduce_sounds_playing_to(0);
968 SoundsPlaying sounds_finished;
971 SoundsPlaying::iterator it = _sounds_playing.begin();
972 while (it != _sounds_playing.end()) {
974 sound->pull_used_buffers();
975 sound->push_fresh_buffers();
976 sound->restart_stalled_audio();
977 sound->cache_time(rtc);
978 if (sound->_source == 0 ||
979 (sound->_stream_queued.empty() &&
980 (sound->_loops_completed >= sound->_playing_loops))) {
981 sounds_finished.insert(std::move(sound));
995void OpenALAudioManager::
998 if (!_cleanup_required) {
1004 AllSounds sounds(_all_sounds);
1005 AllSounds::iterator ai;
1006 for (ai = sounds.begin(); ai != sounds.end(); ++ai) {
1012 nassertv(_active_managers > 0);
1015 if (_active_managers == 0) {
1016 if (_openal_active) {
1020 sources =
new ALuint[_al_sources->size()];
1021 for (SourceCache::iterator si = _al_sources->begin(); si!=_al_sources->end(); ++si) {
1026 alDeleteSources(_al_sources->size(),sources);
1027 al_audio_errcheck(
"alDeleteSources()");
1029 _al_sources->clear();
1032 alcGetError(_device);
1033 alcMakeContextCurrent(
nullptr);
1034 alc_audio_errcheck(
"alcMakeContextCurrent(NULL)",_device);
1036 alcDestroyContext(_context);
1037 alc_audio_errcheck(
"alcDestroyContext(_context)",_device);
1041 audio_debug(
"Going to try to close openAL");
1042 alcCloseDevice(_device);
1045 audio_debug(
"openAL Closed");
1048 _openal_active =
false;
1051 _cleanup_required =
false;
1057OpenALAudioManager::SoundData::
1072OpenALAudioManager::SoundData::
1076 if (_manager->_is_valid) {
1077 _manager->make_current();
1078 _manager->delete_buffer(_sample);
1088void OpenALAudioManager::
1089increment_client_count(SoundData *sd) {
1091 sd->_client_count += 1;
1092 audio_debug(
"Incrementing: " << sd->_movie->get_filename().get_basename() <<
" " << sd->_client_count);
1093 if (sd->_client_count == 1) {
1095 _expiring_samples.erase(sd->_expire);
1097 _expiring_streams.erase(sd->_expire);
1107void OpenALAudioManager::
1108decrement_client_count(SoundData *sd) {
1110 sd->_client_count -= 1;
1111 audio_debug(
"Decrementing: " << sd->_movie->get_filename().get_basename() <<
" " << sd->_client_count);
1112 if (sd->_client_count == 0) {
1114 _expiring_samples.push_back(sd);
1115 sd->_expire = _expiring_samples.end();
1118 _expiring_streams.push_back(sd);
1119 sd->_expire = _expiring_streams.end();
1122 discard_excess_cache(_cache_limit);
1130void OpenALAudioManager::
1131discard_excess_cache(
int sample_limit) {
1133 int stream_limit = 5;
1135 while (((
int)_expiring_samples.size()) > sample_limit) {
1136 SoundData *sd = (SoundData*)(_expiring_samples.front());
1137 nassertv(sd->_client_count == 0);
1138 nassertv(sd->_expire == _expiring_samples.begin());
1139 _expiring_samples.pop_front();
1140 _sample_cache.erase(_sample_cache.find(sd->_movie->get_filename()));
1141 audio_debug(
"Expiring: " << sd->_movie->get_filename().get_basename());
1145 while (((
int)_expiring_streams.size()) > stream_limit) {
1146 SoundData *sd = (SoundData*)(_expiring_streams.front());
1147 nassertv(sd->_client_count == 0);
1148 nassertv(sd->_expire == _expiring_streams.begin());
1149 _expiring_streams.pop_front();
1150 audio_debug(
"Expiring: " << sd->_movie->get_filename().get_basename());
1163void OpenALAudioManager::
1164delete_buffer(ALuint buffer) {
1171 alDeleteBuffers(1, &buffer);
1172 error = alGetError();
1174 if (error == AL_NO_ERROR) {
1177 }
else if (error != AL_INVALID_OPERATION) {
1180 }
else if (tries >= openal_buffer_delete_retries.
get_value()) {
1191 audio_error(
"failed to delete a buffer: " << alGetString(error) );
get_value
Returns the variable's value.
get_value
Returns the variable's value.
The name of a file, such as a texture file or an Egg file.
std::string get_basename() const
Returns the basename part of the filename.
A MovieAudio is actually any source that provides a sequence of audio samples.
virtual int ready() const
Returns the number of audio samples that are ready to read.
A MovieAudio is actually any source that provides a sequence of audio samples.
get_filename
Returns the movie's filename.
virtual void set_active(bool)
Turn on/off Warning: not implemented.
virtual void audio_3d_get_listener_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz, PN_stdfloat *fx, PN_stdfloat *fy, PN_stdfloat *fz, PN_stdfloat *ux, PN_stdfloat *uy, PN_stdfloat *uz)
Get position of the "ear" that picks up 3d sounds.
virtual void set_cache_limit(unsigned int count)
Set the number of sounds that the cache can hold.
virtual void uncache_sound(const Filename &)
Deletes a sample from the expiration queues.
virtual void stop_all_sounds()
Stop playback on all sounds managed by this manager.
virtual bool is_valid()
This is mostly for debugging, but it it could be used to detect errors in a release build if you don'...
virtual void shutdown()
Call this at exit time to shut down the audio system.
virtual PN_stdfloat audio_3d_get_distance_factor() const
Get value in units per meter.
virtual void clear_cache()
Clear out the sound cache.
virtual PN_stdfloat get_volume() const
Gets listener gain.
void set_play_rate(PN_stdfloat play_rate)
set the overall play rate
virtual void audio_3d_set_doppler_factor(PN_stdfloat factor)
Exaggerates or diminishes the Doppler effect.
virtual void audio_3d_set_distance_factor(PN_stdfloat factor)
Set value in units per meter WARNING: OpenAL has no distance factor but we use this as a scale on the...
PN_stdfloat get_play_rate() const
get the overall speed/pitch/play rate
virtual void audio_3d_set_drop_off_factor(PN_stdfloat factor)
Control the effect distance has on audability.
virtual void audio_3d_set_listener_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat xy, PN_stdfloat xz, PN_stdfloat fx, PN_stdfloat fy, PN_stdfloat fz, PN_stdfloat ux, PN_stdfloat uy, PN_stdfloat uz)
Set position of the "ear" that picks up 3d sounds NOW LISTEN UP!!! THIS IS IMPORTANT!...
virtual void update()
Perform all per-frame update functions.
virtual void set_volume(PN_stdfloat)
Sets listener gain.
Similar to MutexHolder, but for a reentrant mutex.
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
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.
A hierarchy of directories and files that appears to be one continuous file system,...
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This is our own Panda specialization on the default STL set.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
AudioManager * Create_OpenALAudioManager()
Factory Function.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.