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;
50OpenALAudioManager::Managers *OpenALAudioManager::_managers =
nullptr;
52OpenALAudioManager::SourceCache *OpenALAudioManager::_al_sources =
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()");
84 ReMutexHolder holder(_lock);
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() {
202 ReMutexHolder holder(_lock);
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::
333 ReMutexHolder holder(_lock);
334 int channels = source->audio_channels();
335 if ((channels != 1)&&(channels != 2)) {
336 audio_error(
"Currently, only mono and stereo are supported.");
347bool OpenALAudioManager::
349 ReMutexHolder holder(_lock);
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::
384 ReMutexHolder holder(_lock);
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);
411 PT(MovieAudioCursor) stream = movie->open();
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) {
497 ReMutexHolder holder(_lock);
499 return get_null_sound();
502 Filename path = file_name;
507 audio_error(
"get_sound - invalid filename");
511 PT(MovieAudio) mva = MovieAudio::get(path);
513 PT(OpenALAudioSound) oas =
514 new OpenALAudioSound(
this, mva, positional, mode);
519 return get_null_sound();
522 _all_sounds.insert(oas);
523 PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
538 vfs->resolve_filename(path, get_model_path());
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::
600 ReMutexHolder holder(_lock);
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) {
704 CoordinateSystem cs = get_default_coordinate_system();
736 _forward_up[2] = -fy;
740 _forward_up[5] = -uy;
754 _forward_up[2] = -fz;
758 _forward_up[5] = -uz;
780 nassert_raise(
"invalid coordinate system");
788 alListenerfv(AL_POSITION,_position);
789 al_audio_errcheck(
"alListerfv(AL_POSITION)");
790 alListenerfv(AL_VELOCITY,_velocity);
791 al_audio_errcheck(
"alListerfv(AL_VELOCITY)");
792 alListenerfv(AL_ORIENTATION,_forward_up);
793 al_audio_errcheck(
"alListerfv(AL_ORIENTATION)");
800audio_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) {
802 CoordinateSystem cs = get_default_coordinate_system();
814 *fx = _forward_up[0];
815 *fy = _forward_up[1];
816 *fz = _forward_up[2];
818 *ux = _forward_up[3];
819 *uy = _forward_up[4];
820 *uz = _forward_up[5];
832 *fx = _forward_up[0];
833 *fy = -_forward_up[2];
834 *fz = _forward_up[1];
836 *ux = _forward_up[3];
837 *uy = -_forward_up[5];
838 *uz = _forward_up[4];
850 *fx = _forward_up[0];
851 *fy = _forward_up[1];
852 *fz = -_forward_up[2];
854 *ux = _forward_up[3];
855 *uy = _forward_up[4];
856 *uz = -_forward_up[5];
868 *fx = _forward_up[0];
869 *fy = _forward_up[2];
870 *fz = _forward_up[1];
872 *ux = _forward_up[3];
873 *uy = _forward_up[5];
874 *uz = _forward_up[4];
878 nassert_raise(
"invalid coordinate system");
892 _distance_factor = factor;
898 if (_distance_factor>0) {
899 alSpeedOfSound(343.3*_distance_factor);
900 al_audio_errcheck(
"alSpeedOfSound()");
903 alDopplerFactor(_doppler_factor);
904 al_audio_errcheck(
"alDopplerFactor()");
906 audio_debug(
"can't set speed of sound if distance_factor <=0.0, setting doppler factor to 0.0 instead");
907 alDopplerFactor(0.0);
908 al_audio_errcheck(
"alDopplerFactor()");
911 AllSounds::iterator i=_all_sounds.begin();
912 for (; i!=_all_sounds.end(); ++i) {
913 (**i).set_3d_min_distance((**i).get_3d_min_distance());
914 (**i).set_3d_max_distance((**i).get_3d_max_distance());
923 return _distance_factor;
932 _doppler_factor = factor;
937 alDopplerFactor(_doppler_factor);
938 al_audio_errcheck(
"alDopplerFactor()");
944PN_stdfloat OpenALAudioManager::
945audio_3d_get_doppler_factor()
const {
946 return _doppler_factor;
955 _drop_off_factor = factor;
957 AllSounds::iterator i=_all_sounds.begin();
958 for (; i!=_all_sounds.end(); ++i) {
959 (**i).set_3d_drop_off_factor((**i).get_3d_drop_off_factor());
966PN_stdfloat OpenALAudioManager::
967audio_3d_get_drop_off_factor()
const {
968 return _drop_off_factor;
976void OpenALAudioManager::
978 ReMutexHolder holder(_lock);
982 if (audio->_source) {
990 if (_concurrent_sound_limit) {
991 reduce_sounds_playing_to(_concurrent_sound_limit-1);
995 if (_al_sources->empty()) {
998 alGenSources(1,&source);
999 ALenum result = alGetError();
1000 if (result!=AL_NO_ERROR) {
1001 audio_error(
"alGenSources(): " << alGetString(result) );
1004 reduce_sounds_playing_to(_sounds_playing.size()-1);
1009 if (!source && !_al_sources->empty()) {
1010 source = *(_al_sources->begin());
1011 _al_sources->erase(source);
1014 audio->_source = source;
1017 _sounds_playing.insert(audio);
1024void OpenALAudioManager::
1026 ReMutexHolder holder(_lock);
1027 if (audio->_source) {
1028 _al_sources->insert(audio->_source);
1031 _sounds_playing.erase(audio);
1037void OpenALAudioManager::
1038set_concurrent_sound_limit(
unsigned int limit) {
1039 ReMutexHolder holder(_lock);
1040 _concurrent_sound_limit = limit;
1041 reduce_sounds_playing_to(_concurrent_sound_limit);
1047unsigned int OpenALAudioManager::
1048get_concurrent_sound_limit()
const {
1049 return _concurrent_sound_limit;
1055void OpenALAudioManager::
1056reduce_sounds_playing_to(
unsigned int count) {
1057 ReMutexHolder holder(_lock);
1062 int limit = _sounds_playing.size() - count;
1063 while (limit-- > 0) {
1064 SoundsPlaying::iterator sound = _sounds_playing.begin();
1065 nassertv(sound != _sounds_playing.end());
1071 PT(OpenALAudioSound) s = (*sound);
1082 reduce_sounds_playing_to(0);
1096 SoundsPlaying sounds_finished;
1099 SoundsPlaying::iterator it = _sounds_playing.begin();
1100 while (it != _sounds_playing.end()) {
1101 PT(OpenALAudioSound) sound = *(it++);
1102 sound->pull_used_buffers();
1103 sound->push_fresh_buffers();
1104 sound->restart_stalled_audio();
1105 sound->cache_time(rtc);
1106 if (sound->_source == 0 ||
1107 (sound->_stream_queued.empty() &&
1108 (sound->_loops_completed >= sound->_playing_loops))) {
1109 sounds_finished.insert(std::move(sound));
1113 for (OpenALAudioSound *sound : sounds_finished) {
1123void OpenALAudioManager::
1126 if (!_cleanup_required) {
1132 AllSounds sounds(_all_sounds);
1133 AllSounds::iterator ai;
1134 for (ai = sounds.begin(); ai != sounds.end(); ++ai) {
1140 nassertv(_active_managers > 0);
1143 if (_active_managers == 0) {
1144 if (_openal_active) {
1148 sources =
new ALuint[_al_sources->size()];
1149 for (SourceCache::iterator si = _al_sources->begin(); si!=_al_sources->end(); ++si) {
1154 alDeleteSources(_al_sources->size(),sources);
1155 al_audio_errcheck(
"alDeleteSources()");
1157 _al_sources->clear();
1160 alcGetError(_device);
1161 alcMakeContextCurrent(
nullptr);
1162 alc_audio_errcheck(
"alcMakeContextCurrent(NULL)",_device);
1164 alcDestroyContext(_context);
1165 alc_audio_errcheck(
"alcDestroyContext(_context)",_device);
1169 audio_debug(
"Going to try to close openAL");
1170 alcCloseDevice(_device);
1173 audio_debug(
"openAL Closed");
1176 _openal_active =
false;
1179 _cleanup_required =
false;
1185OpenALAudioManager::SoundData::
1200OpenALAudioManager::SoundData::
1202 ReMutexHolder holder(OpenALAudioManager::_lock);
1204 if (_manager->_is_valid) {
1205 _manager->make_current();
1206 _manager->delete_buffer(_sample);
1216void OpenALAudioManager::
1217increment_client_count(SoundData *sd) {
1218 ReMutexHolder holder(_lock);
1219 sd->_client_count += 1;
1220 audio_debug(
"Incrementing: " << sd->_movie->get_filename().get_basename() <<
" " << sd->_client_count);
1221 if (sd->_client_count == 1) {
1223 _expiring_samples.erase(sd->_expire);
1225 _expiring_streams.erase(sd->_expire);
1235void OpenALAudioManager::
1236decrement_client_count(SoundData *sd) {
1237 ReMutexHolder holder(_lock);
1238 sd->_client_count -= 1;
1239 audio_debug(
"Decrementing: " << sd->_movie->get_filename().get_basename() <<
" " << sd->_client_count);
1240 if (sd->_client_count == 0) {
1242 _expiring_samples.push_back(sd);
1243 sd->_expire = _expiring_samples.end();
1246 _expiring_streams.push_back(sd);
1247 sd->_expire = _expiring_streams.end();
1250 discard_excess_cache(_cache_limit);
1258void OpenALAudioManager::
1259discard_excess_cache(
int sample_limit) {
1260 ReMutexHolder holder(_lock);
1261 int stream_limit = 5;
1263 while (((
int)_expiring_samples.size()) > sample_limit) {
1264 SoundData *sd = (SoundData*)(_expiring_samples.front());
1265 nassertv(sd->_client_count == 0);
1266 nassertv(sd->_expire == _expiring_samples.begin());
1267 _expiring_samples.pop_front();
1268 _sample_cache.erase(_sample_cache.find(sd->_movie->get_filename()));
1269 audio_debug(
"Expiring: " << sd->_movie->get_filename().get_basename());
1273 while (((
int)_expiring_streams.size()) > stream_limit) {
1274 SoundData *sd = (SoundData*)(_expiring_streams.front());
1275 nassertv(sd->_client_count == 0);
1276 nassertv(sd->_expire == _expiring_streams.begin());
1277 _expiring_streams.pop_front();
1278 audio_debug(
"Expiring: " << sd->_movie->get_filename().get_basename());
1291void OpenALAudioManager::
1292delete_buffer(ALuint buffer) {
1293 ReMutexHolder holder(_lock);
1299 alDeleteBuffers(1, &buffer);
1300 error = alGetError();
1302 if (error == AL_NO_ERROR) {
1305 }
else if (error != AL_INVALID_OPERATION) {
1308 }
else if (tries >= openal_buffer_delete_retries.
get_value()) {
1319 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!
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.
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.