Panda3D

Loading...
Searching...
No Matches
openalAudioManager.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file openalAudioManager.cxx
10 * @author Ben Buchwald <bb2@alumni.cmu.edu>
11 */
12
13#include "pandabase.h"
14
15// Panda headers.
16#include "config_audio.h"
17#include "config_putil.h"
18#include "config_express.h"
19#include "config_openalAudio.h"
20#include "openalAudioManager.h"
21#include "openalAudioSound.h"
22#include "virtualFileSystem.h"
23#include "movieAudio.h"
24#include "reMutexHolder.h"
25
26#include <algorithm>
27
28#ifndef ALC_DEFAULT_ALL_DEVICES_SPECIFIER
29#define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012
30#endif
31
32#ifndef ALC_ALL_DEVICES_SPECIFIER
33#define ALC_ALL_DEVICES_SPECIFIER 0x1013
34#endif
35
36 using std::endl;
37 using std::string;
38
39 TypeHandle OpenALAudioManager::_type_handle;
40
41 ReMutex OpenALAudioManager::_lock;
42 int OpenALAudioManager::_active_managers = 0;
43 bool OpenALAudioManager::_openal_active = false;
44ALCdevice* OpenALAudioManager::_device = nullptr;
45ALCcontext* OpenALAudioManager::_context = nullptr;
46
47// This is the list of all OpenALAudioManager objects in the world. It must
48// be a pointer rather than a concrete object, so it won't be destructed at
49// exit time before we're done removing things from it.
50OpenALAudioManager::Managers *OpenALAudioManager::_managers = nullptr;
51
52OpenALAudioManager::SourceCache *OpenALAudioManager::_al_sources = nullptr;
53
54
55// Central dispatcher for audio errors.
56 void al_audio_errcheck(const char *context) {
57 ALenum result = alGetError();
58 if (result != AL_NO_ERROR) {
59 audio_error(context << ": " << alGetString(result) );
60 }
61}
62
63 void 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) );
67 }
68}
69
70/**
71 * Factory Function
72 */
73 AudioManager *Create_OpenALAudioManager() {
74 audio_debug("Create_OpenALAudioManager()");
75 return new OpenALAudioManager;
76}
77
78
79/**
80 *
81 */
82OpenALAudioManager::
83OpenALAudioManager() {
84 ReMutexHolder holder(_lock);
85 if (_managers == nullptr) {
86 _managers = new Managers;
87 _al_sources = new SourceCache;
88 }
89
90 _managers->insert(this);
91
92 _cleanup_required = true;
93 _active = audio_active;
94 _volume = audio_volume;
95 _play_rate = 1.0f;
96
97 _cache_limit = audio_cache_limit;
98
99 _concurrent_sound_limit = 0;
100 _is_valid = true;
101
102 // Init 3D attributes
103 _distance_factor = 1;
104 _drop_off_factor = 1;
105
106 _position[0] = 0;
107 _position[1] = 0;
108 _position[2] = 0;
109
110 _velocity[0] = 0;
111 _velocity[1] = 0;
112 _velocity[2] = 0;
113
114 _forward_up[0] = 0;
115 _forward_up[1] = 0;
116 _forward_up[2] = 0;
117 _forward_up[3] = 0;
118 _forward_up[4] = 0;
119 _forward_up[5] = 0;
120
121 // Initialization
122 audio_cat.init();
123 if (_active_managers == 0 || !_openal_active) {
124 _device = nullptr;
125 string dev_name = select_audio_device();
126
127 if (!dev_name.empty()) {
128 // Open a specific device by name.
129 audio_cat.info() << "Using OpenAL device " << dev_name << "\n";
130 _device = alcOpenDevice(dev_name.c_str());
131
132 if (_device == nullptr) {
133 audio_cat.error()
134 << "Couldn't open OpenAL device \"" << dev_name << "\", falling back to default device\n";
135 }
136 } else {
137 audio_cat.info() << "Using default OpenAL device\n";
138 }
139
140 if (_device == nullptr) {
141 // Open the default device.
142 _device = alcOpenDevice(nullptr);
143
144 if (_device == nullptr && dev_name != "OpenAL Soft") {
145 // Try the OpenAL Soft driver instead, which is fairly reliable.
146 _device = alcOpenDevice("OpenAL Soft");
147
148 if (_device == nullptr) {
149 audio_cat.error()
150 << "Couldn't open default OpenAL device\n";
151 }
152 }
153 }
154
155 if (_device != nullptr) {
156 // We managed to get a device open.
157 alcGetError(_device); // clear errors
158 _context = alcCreateContext(_device, nullptr);
159 alc_audio_errcheck("alcCreateContext(_device, NULL)", _device);
160 if (_context != nullptr) {
161 _openal_active = true;
162 }
163 }
164 }
165
166 // We increment _active_managers regardless of possible errors above. The
167 // shutdown call will do the right thing when it's called, either way.
168 ++_active_managers;
169 nassertv(_active_managers>0);
170
171 if (!_device || !_context) {
172 audio_error("OpenALAudioManager: No open device or context");
173 _is_valid = false;
174 } else {
175 alcGetError(_device); // clear errors
176 alcMakeContextCurrent(_context);
177 alc_audio_errcheck("alcMakeContextCurrent(_context)", _device);
178
179 // set 3D sound characteristics as they are given in the configrc
180 audio_3d_set_doppler_factor(audio_doppler_factor);
181 audio_3d_set_distance_factor(audio_distance_factor);
182 audio_3d_set_drop_off_factor(audio_drop_off_factor);
183
184 if (audio_cat.is_debug()) {
185 audio_cat.debug()
186 << "ALC_DEVICE_SPECIFIER:" << alcGetString(_device, ALC_DEVICE_SPECIFIER) << endl;
187 }
188 }
189
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;
194 }
195}
196
197/**
198 *
199 */
200OpenALAudioManager::
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);
207 cleanup();
208}
209
210/**
211 * Call this at exit time to shut down the audio system. This will invalidate
212 * all currently-active AudioManagers and AudioSounds in the system. If you
213 * change your mind and want to play sounds again, you will have to recreate
214 * all of these objects.
215 */
216 void OpenALAudioManager::
217 shutdown() {
218 ReMutexHolder holder(_lock);
219 if (_managers != nullptr) {
220 Managers::iterator mi;
221 for (mi = _managers->begin(); mi != _managers->end(); ++mi) {
222 (*mi)->cleanup();
223 }
224 }
225
226 nassertv(_active_managers == 0);
227}
228
229
230/**
231 * This is mostly for debugging, but it it could be used to detect errors in a
232 * release build if you don't mind the cpu cost.
233 */
234 bool OpenALAudioManager::
235 is_valid() {
236 return _is_valid;
237}
238
239/**
240 * Enumerate the audio devices, selecting the one that is most appropriate or
241 * has been selected by the user.
242 */
243 string OpenALAudioManager::
244select_audio_device() {
245 string selected_device = openal_device;
246
247 const char *devices = nullptr;
248
249 // This extension gives us all audio paths on all drivers.
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);
253
254 if (devices) {
255 if (audio_cat.is_debug()) {
256 audio_cat.debug() << "All OpenAL devices:\n";
257 }
258
259 while (*devices) {
260 string device(devices);
261 devices += device.size() + 1;
262
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";
268 } else {
269 audio_cat.debug() << " " << device << "\n";
270 }
271 }
272 }
273 }
274 } else {
275 audio_cat.debug() << "ALC_ENUMERATE_ALL_EXT not supported\n";
276 }
277
278 // This extension just gives us generic driver names, like "OpenAL Soft" and
279 // "Generic Software", rather than individual outputs.
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);
283
284 if (devices) {
285 if (audio_cat.is_debug()) {
286 audio_cat.debug() << "OpenAL drivers:\n";
287 }
288
289 while (*devices) {
290 string device(devices);
291 devices += device.size() + 1;
292
293 if (selected_device.empty() && device == "OpenAL Soft" &&
294 default_device == "Generic Software") {
295 // Prefer OpenAL Soft over the buggy Generic Software driver.
296 selected_device = "OpenAL Soft";
297 }
298
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";
304 } else {
305 audio_cat.debug() << " " << device << "\n";
306 }
307 }
308 }
309 }
310 } else {
311 audio_cat.debug() << "ALC_ENUMERATION_EXT not supported\n";
312 }
313
314 return selected_device;
315}
316
317/**
318 * This makes this manager's OpenAL context the current context. Needed
319 * before any parameter sets.
320 */
321 void OpenALAudioManager::
322make_current() const {
323 // Since we only use one context, this is now a no-op.
324}
325
326/**
327 * Returns true if the specified MovieAudioCursor can be used by this
328 * AudioManager. Mostly, this involves checking whether or not the format is
329 * implemented/supported.
330 */
331 bool OpenALAudioManager::
332can_use_audio(MovieAudioCursor *source) {
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.");
337 return false;
338 }
339 return true;
340}
341
342/**
343 * Returns true if the specified MovieAudio should be cached into RAM. A lot
344 * of conditions have to be met in order to allow caching - if any are not
345 * met, the file will be streamed.
346 */
347 bool OpenALAudioManager::
348should_load_audio(MovieAudioCursor *source, int mode) {
349 ReMutexHolder holder(_lock);
350 if (mode == SM_stream) {
351 // If the user asked for streaming, give him streaming.
352 return false;
353 }
354 if (source->get_source()->get_filename().empty()) {
355 // Non-files cannot be preloaded.
356 return false;
357 }
358 if (source->ready() != 0x40000000) {
359 // Streaming sources cannot be preloaded.
360 return false;
361 }
362 if (source->length() > 3600.0) {
363 // Anything longer than an hour cannot be preloaded.
364 return false;
365 }
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)) {
370 // In heuristic mode, if file is long, stream it.
371 return false;
372 }
373 return true;
374}
375
376/**
377 * Obtains a SoundData for the specified sound.
378 *
379 * When you are done with the SoundData, you need to decrement the client
380 * count.
381 */
382OpenALAudioManager::SoundData *OpenALAudioManager::
383get_sound_data(MovieAudio *movie, int mode) {
384 ReMutexHolder holder(_lock);
385 const Filename &path = movie->get_filename();
386
387 // Search for an already-cached sample or an already-opened stream.
388 if (!path.empty()) {
389
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);
395 return sd;
396 }
397 }
398
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);
405 return sd;
406 }
407 }
408 }
409 }
410
411 PT(MovieAudioCursor) stream = movie->open();
412 if (stream == nullptr) {
413 audio_error("Cannot open file: "<<path);
414 return nullptr;
415 }
416
417 if (!can_use_audio(stream)) {
418 audio_error("File is not in usable format: "<<path);
419 return nullptr;
420 }
421
422 SoundData *sd = new SoundData();
423 sd->_client_count = 1;
424 sd->_manager = this;
425 sd->_movie = movie;
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);
433
434 if (should_load_audio(stream, mode)) {
435 audio_debug(path.get_basename() << ": loading as sample");
436 make_current();
437 alGetError(); // clear errors
438 sd->_sample = 0;
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");
443 delete sd;
444 return nullptr;
445 }
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());
453 delete[] data;
454 int err = alGetError();
455 if (err != AL_NO_ERROR) {
456 audio_error("could not fill OpenAL buffer object with data");
457 delete sd;
458 return nullptr;
459 }
460 _sample_cache.insert(SampleCache::value_type(path, sd));
461 } else {
462 audio_debug(path.get_basename() << ": loading as stream");
463 sd->_stream = stream;
464 }
465
466 return sd;
467}
468
469/**
470 * This is what creates a sound instance.
471 */
472PT(AudioSound) OpenALAudioManager::
473get_sound(MovieAudio *sound, bool positional, int mode) {
474 ReMutexHolder holder(_lock);
475 if(!is_valid()) {
476 return get_null_sound();
477 }
478 PT(OpenALAudioSound) oas =
479 new OpenALAudioSound(this, sound, positional, mode);
480
481 if(!oas->_manager) {
482 // The sound cleaned itself up immediately. It pretty clearly didn't like
483 // something, so we should just return a null sound instead.
484 return get_null_sound();
485 }
486
487 _all_sounds.insert(oas);
488 PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
489 return res;
490}
491
492/**
493 * This is what creates a sound instance.
494 */
495PT(AudioSound) OpenALAudioManager::
496get_sound(const Filename &file_name, bool positional, int mode) {
497 ReMutexHolder holder(_lock);
498 if(!is_valid()) {
499 return get_null_sound();
500 }
501
502 Filename path = file_name;
503 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
504 vfs->resolve_filename(path, get_model_path());
505
506 if (path.empty()) {
507 audio_error("get_sound - invalid filename");
508 return nullptr;
509 }
510
511 PT(MovieAudio) mva = MovieAudio::get(path);
512
513 PT(OpenALAudioSound) oas =
514 new OpenALAudioSound(this, mva, positional, mode);
515
516 if(!oas->_manager) {
517 // The sound cleaned itself up immediately. It pretty clearly didn't like
518 // something, so we should just return a null sound instead.
519 return get_null_sound();
520 }
521
522 _all_sounds.insert(oas);
523 PT(AudioSound) res = (AudioSound*)(OpenALAudioSound*)oas;
524 return res;
525}
526
527/**
528 * Deletes a sample from the expiration queues. If the sound is actively in
529 * use, then the sound cannot be deleted, and this function has no effect.
530 */
531 void OpenALAudioManager::
532 uncache_sound(const Filename &file_name) {
533 ReMutexHolder holder(_lock);
534 nassertv(is_valid());
535 Filename path = file_name;
536
537 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
538 vfs->resolve_filename(path, get_model_path());
539
540 SampleCache::iterator sci = _sample_cache.find(path);
541 if (sci == _sample_cache.end()) {
542 sci = _sample_cache.find(file_name);
543 }
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);
549 delete sd;
550 }
551 }
552
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);
560 delete sd;
561 continue;
562 }
563 }
564 ++exqi;
565 }
566}
567
568/**
569 * Clear out the sound cache.
570 */
571 void OpenALAudioManager::
572 clear_cache() {
573 ReMutexHolder holder(_lock);
574 discard_excess_cache(0);
575}
576
577/**
578 * Set the number of sounds that the cache can hold.
579 */
580 void OpenALAudioManager::
581 set_cache_limit(unsigned int count) {
582 ReMutexHolder holder(_lock);
583 _cache_limit=count;
584 discard_excess_cache(count);
585}
586
587/**
588 *
589 */
590 unsigned int OpenALAudioManager::
591get_cache_limit() const {
592 return _cache_limit;
593}
594
595/**
596 *
597 */
598 void OpenALAudioManager::
599release_sound(OpenALAudioSound* audioSound) {
600 ReMutexHolder holder(_lock);
601 AllSounds::iterator ai = _all_sounds.find(audioSound);
602 if (ai != _all_sounds.end()) {
603 _all_sounds.erase(ai);
604 }
605}
606
607/**
608 *
609 * Sets listener gain
610 */
611 void OpenALAudioManager::set_volume(PN_stdfloat volume) {
612 ReMutexHolder holder(_lock);
613 if (_volume!=volume) {
614 _volume = volume;
615
616 // Tell our AudioSounds to adjust:
617 AllSounds::iterator i=_all_sounds.begin();
618 for (; i!=_all_sounds.end(); ++i) {
619 (**i).set_volume((**i).get_volume());
620 }
621
622 /*
623 // this was neat alternative to the above look when we had a seperate
624 // context for each manager
625 make_current();
626
627 alGetError(); // clear errors
628 alListenerf(AL_GAIN,(ALfloat)_volume);
629 al_audio_errcheck("alListerf(AL_GAIN)");*/
630 }
631}
632
633/**
634 *
635 * Gets listener gain
636 */
637PN_stdfloat OpenALAudioManager::
638 get_volume() const {
639 return _volume;
640}
641
642/**
643 * set the overall play rate
644 */
645 void OpenALAudioManager::
646 set_play_rate(PN_stdfloat play_rate) {
647 ReMutexHolder holder(_lock);
648 if (_play_rate!=play_rate) {
649 _play_rate = play_rate;
650 // Tell our AudioSounds to adjust:
651 AllSounds::iterator i=_all_sounds.begin();
652 for (; i!=_all_sounds.end(); ++i) {
653 (**i).set_play_rate((**i).get_play_rate());
654 }
655 }
656}
657
658/**
659 * get the overall speed/pitch/play rate
660 */
661PN_stdfloat OpenALAudioManager::
662 get_play_rate() const {
663 return _play_rate;
664}
665
666/**
667 * Turn on/off Warning: not implemented.
668 */
669 void OpenALAudioManager::
670 set_active(bool active) {
671 ReMutexHolder holder(_lock);
672 if (_active!=active) {
673 _active=active;
674 // Tell our AudioSounds to adjust:
675 AllSounds::iterator i=_all_sounds.begin();
676 for (; i!=_all_sounds.end(); ++i) {
677 (**i).set_active(_active);
678 }
679 }
680}
681
682/**
683 *
684 */
685 bool OpenALAudioManager::
686get_active() const {
687 return _active;
688}
689
690/**
691 * Set position of the "ear" that picks up 3d sounds NOW LISTEN UP!!! THIS IS
692 * IMPORTANT! Both Panda3D and OpenAL use a right handed coordinate system.
693 * But there is a major difference! In Panda3D the Y-Axis is going into the
694 * Screen and the Z-Axis is going up. In OpenAL the Y-Axis is going up and
695 * the Z-Axis is coming out of the screen. The solution is simple, we just
696 * flip the Y and Z axis and negate the Z, as we move coordinates from Panda
697 * to OpenAL and back. What does did mean to average Panda user? Nothing,
698 * they shouldn't notice anyway. But if you decide to do any 3D audio work in
699 * here you have to keep it in mind. I told you, so you can't say I didn't.
700 */
701 void OpenALAudioManager::
702 audio_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) {
703 ReMutexHolder holder(_lock);
704 CoordinateSystem cs = get_default_coordinate_system();
705
706 switch (cs) {
707 case CS_yup_right:
708 _position[0] = px;
709 _position[1] = py;
710 _position[2] = pz;
711
712 _velocity[0] = vx;
713 _velocity[1] = vy;
714 _velocity[2] = vz;
715
716 _forward_up[0] = fx;
717 _forward_up[1] = fy;
718 _forward_up[2] = fz;
719
720 _forward_up[3] = ux;
721 _forward_up[4] = uy;
722 _forward_up[5] = uz;
723 break;
724
725 case CS_zup_right:
726 _position[0] = px;
727 _position[1] = pz;
728 _position[2] = -py;
729
730 _velocity[0] = vx;
731 _velocity[1] = vz;
732 _velocity[2] = -vy;
733
734 _forward_up[0] = fx;
735 _forward_up[1] = fz;
736 _forward_up[2] = -fy;
737
738 _forward_up[3] = ux;
739 _forward_up[4] = uz;
740 _forward_up[5] = -uy;
741 break;
742
743 case CS_yup_left:
744 _position[0] = px;
745 _position[1] = py;
746 _position[2] = -pz;
747
748 _velocity[0] = vx;
749 _velocity[1] = vy;
750 _velocity[2] = -vz;
751
752 _forward_up[0] = fx;
753 _forward_up[1] = fy;
754 _forward_up[2] = -fz;
755
756 _forward_up[3] = ux;
757 _forward_up[4] = uy;
758 _forward_up[5] = -uz;
759 break;
760
761 case CS_zup_left:
762 _position[0] = px;
763 _position[1] = pz;
764 _position[2] = py;
765
766 _velocity[0] = vx;
767 _velocity[1] = vz;
768 _velocity[2] = vy;
769
770 _forward_up[0] = fx;
771 _forward_up[1] = fz;
772 _forward_up[2] = fy;
773
774 _forward_up[3] = ux;
775 _forward_up[4] = uz;
776 _forward_up[5] = uy;
777 break;
778
779 default:
780 nassert_raise("invalid coordinate system");
781 return;
782 }
783
784
785 make_current();
786
787 alGetError(); // clear errors
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)");
794}
795
796/**
797 * Get position of the "ear" that picks up 3d sounds
798 */
799 void OpenALAudioManager::
800 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) {
801 ReMutexHolder holder(_lock);
802 CoordinateSystem cs = get_default_coordinate_system();
803
804 switch (cs) {
805 case CS_yup_right:
806 *px = _position[0];
807 *py = _position[1];
808 *pz = _position[2];
809
810 *vx = _velocity[0];
811 *vy = _velocity[1];
812 *vz = _velocity[2];
813
814 *fx = _forward_up[0];
815 *fy = _forward_up[1];
816 *fz = _forward_up[2];
817
818 *ux = _forward_up[3];
819 *uy = _forward_up[4];
820 *uz = _forward_up[5];
821 break;
822
823 case CS_zup_right:
824 *px = _position[0];
825 *py = -_position[2];
826 *pz = _position[1];
827
828 *vx = _velocity[0];
829 *vy = -_velocity[2];
830 *vz = _velocity[1];
831
832 *fx = _forward_up[0];
833 *fy = -_forward_up[2];
834 *fz = _forward_up[1];
835
836 *ux = _forward_up[3];
837 *uy = -_forward_up[5];
838 *uz = _forward_up[4];
839 break;
840
841 case CS_yup_left:
842 *px = _position[0];
843 *py = _position[1];
844 *pz = -_position[2];
845
846 *vx = _velocity[0];
847 *vy = _velocity[1];
848 *vz = -_velocity[2];
849
850 *fx = _forward_up[0];
851 *fy = _forward_up[1];
852 *fz = -_forward_up[2];
853
854 *ux = _forward_up[3];
855 *uy = _forward_up[4];
856 *uz = -_forward_up[5];
857 break;
858
859 case CS_zup_left:
860 *px = _position[0];
861 *py = _position[2];
862 *pz = _position[1];
863
864 *vx = _velocity[0];
865 *vy = _velocity[2];
866 *vz = _velocity[1];
867
868 *fx = _forward_up[0];
869 *fy = _forward_up[2];
870 *fz = _forward_up[1];
871
872 *ux = _forward_up[3];
873 *uy = _forward_up[5];
874 *uz = _forward_up[4];
875 break;
876
877 default:
878 nassert_raise("invalid coordinate system");
879 return;
880 }
881}
882
883/**
884 * Set value in units per meter
885 * WARNING: OpenAL has no distance factor but we use this as a scale
886 * on the min/max distances of sounds to preserve FMOD compatibility.
887 * Also adjusts the speed of sound to compensate for unit difference.
888 */
889 void OpenALAudioManager::
890 audio_3d_set_distance_factor(PN_stdfloat factor) {
891 ReMutexHolder holder(_lock);
892 _distance_factor = factor;
893
894 make_current();
895
896 alGetError(); // clear errors
897
898 if (_distance_factor>0) {
899 alSpeedOfSound(343.3*_distance_factor);
900 al_audio_errcheck("alSpeedOfSound()");
901 // resets the doppler factor to the correct setting in case it was set to
902 // 0.0 by a distance_factor<=0.0
903 alDopplerFactor(_doppler_factor);
904 al_audio_errcheck("alDopplerFactor()");
905 } else {
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()");
909 }
910
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());
915 }
916}
917
918/**
919 * Get value in units per meter
920 */
921PN_stdfloat OpenALAudioManager::
922 audio_3d_get_distance_factor() const {
923 return _distance_factor;
924}
925
926/**
927 * Exaggerates or diminishes the Doppler effect. Defaults to 1.0
928 */
929 void OpenALAudioManager::
930 audio_3d_set_doppler_factor(PN_stdfloat factor) {
931 ReMutexHolder holder(_lock);
932 _doppler_factor = factor;
933
934 make_current();
935
936 alGetError(); // clear errors
937 alDopplerFactor(_doppler_factor);
938 al_audio_errcheck("alDopplerFactor()");
939}
940
941/**
942 *
943 */
944PN_stdfloat OpenALAudioManager::
945audio_3d_get_doppler_factor() const {
946 return _doppler_factor;
947}
948
949/**
950 * Control the effect distance has on audability. Defaults to 1.0
951 */
952 void OpenALAudioManager::
953 audio_3d_set_drop_off_factor(PN_stdfloat factor) {
954 ReMutexHolder holder(_lock);
955 _drop_off_factor = factor;
956
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());
960 }
961}
962
963/**
964 *
965 */
966PN_stdfloat OpenALAudioManager::
967audio_3d_get_drop_off_factor() const {
968 return _drop_off_factor;
969}
970
971/**
972 * Inform the manager that a sound is about to play. The manager will add
973 * this sound to the table of sounds that are playing, and will allocate a
974 * source to this sound.
975 */
976 void OpenALAudioManager::
977starting_sound(OpenALAudioSound* audio) {
978 ReMutexHolder holder(_lock);
979 ALuint source=0;
980
981 // If the sound already has a source, we don't need to do anything.
982 if (audio->_source) {
983 return;
984 }
985
986 // first give all sounds that have finished a chance to stop, so that these
987 // get stopped first
988 update();
989
990 if (_concurrent_sound_limit) {
991 reduce_sounds_playing_to(_concurrent_sound_limit-1); // because we're about to add one
992 }
993
994 // get a source from the source pool or create a new source
995 if (_al_sources->empty()) {
996 make_current();
997 alGetError(); // clear errors
998 alGenSources(1,&source);
999 ALenum result = alGetError();
1000 if (result!=AL_NO_ERROR) {
1001 audio_error("alGenSources(): " << alGetString(result) );
1002 // if we can't create any more sources, set stop a sound to free a
1003 // source
1004 reduce_sounds_playing_to(_sounds_playing.size()-1);
1005 source = 0;
1006 }
1007 }
1008 // get a source from the source bool if we didn't just allocate one
1009 if (!source && !_al_sources->empty()) {
1010 source = *(_al_sources->begin());
1011 _al_sources->erase(source);
1012 }
1013
1014 audio->_source = source;
1015
1016 if (source)
1017 _sounds_playing.insert(audio);
1018}
1019
1020/**
1021 * Inform the manager that a sound is finished or someone called stop on the
1022 * sound (this should not be called if a sound is only paused).
1023 */
1024 void OpenALAudioManager::
1025stopping_sound(OpenALAudioSound* audio) {
1026 ReMutexHolder holder(_lock);
1027 if (audio->_source) {
1028 _al_sources->insert(audio->_source);
1029 audio->_source = 0;
1030 }
1031 _sounds_playing.erase(audio); // This could cause the sound to destruct.
1032}
1033
1034/**
1035 *
1036 */
1037 void 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);
1042}
1043
1044/**
1045 *
1046 */
1047 unsigned int OpenALAudioManager::
1048get_concurrent_sound_limit() const {
1049 return _concurrent_sound_limit;
1050}
1051
1052/**
1053 *
1054 */
1055 void OpenALAudioManager::
1056reduce_sounds_playing_to(unsigned int count) {
1057 ReMutexHolder holder(_lock);
1058 // first give all sounds that have finished a chance to stop, so that these
1059 // get stopped first
1060 update();
1061
1062 int limit = _sounds_playing.size() - count;
1063 while (limit-- > 0) {
1064 SoundsPlaying::iterator sound = _sounds_playing.begin();
1065 nassertv(sound != _sounds_playing.end());
1066 // When the user stops a sound, there is still a PT in the user's hand.
1067 // When we stop a sound here, however, this can remove the last PT. This
1068 // can cause an ugly recursion where stop calls the destructor, and the
1069 // destructor calls stop. To avoid this, we create a temporary PT, stop
1070 // the sound, and then release the PT.
1071 PT(OpenALAudioSound) s = (*sound);
1072 s->stop();
1073 }
1074}
1075
1076/**
1077 * Stop playback on all sounds managed by this manager.
1078 */
1079 void OpenALAudioManager::
1080 stop_all_sounds() {
1081 ReMutexHolder holder(_lock);
1082 reduce_sounds_playing_to(0);
1083}
1084
1085/**
1086 * Perform all per-frame update functions.
1087 */
1088 void OpenALAudioManager::
1089 update() {
1090 ReMutexHolder holder(_lock);
1091
1092 // See if any of our playing sounds have ended we must first collect a
1093 // seperate list of finished sounds and then iterated over those again
1094 // calling their finished method. We can't call finished() within a loop
1095 // iterating over _sounds_playing since finished() modifies _sounds_playing
1096 SoundsPlaying sounds_finished;
1097
1098 double rtc = TrueClock::get_global_ptr()->get_short_time();
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));
1110 }
1111 }
1112
1113 for (OpenALAudioSound *sound : sounds_finished) {
1114 sound->finished();
1115 }
1116}
1117
1118
1119/**
1120 * Shuts down the audio manager and releases any resources associated with it.
1121 * Also cleans up all AudioSounds created via the manager.
1122 */
1123 void OpenALAudioManager::
1124cleanup() {
1125 ReMutexHolder holder(_lock);
1126 if (!_cleanup_required) {
1127 return;
1128 }
1129
1130 stop_all_sounds();
1131
1132 AllSounds sounds(_all_sounds);
1133 AllSounds::iterator ai;
1134 for (ai = sounds.begin(); ai != sounds.end(); ++ai) {
1135 (*ai)->cleanup();
1136 }
1137
1138 clear_cache();
1139
1140 nassertv(_active_managers > 0);
1141 --_active_managers;
1142
1143 if (_active_managers == 0) {
1144 if (_openal_active) {
1145 // empty the source cache
1146 int i=0;
1147 ALuint *sources;
1148 sources = new ALuint[_al_sources->size()];
1149 for (SourceCache::iterator si = _al_sources->begin(); si!=_al_sources->end(); ++si) {
1150 sources[i++]=*si;
1151 }
1152 make_current();
1153 alGetError(); // clear errors
1154 alDeleteSources(_al_sources->size(),sources);
1155 al_audio_errcheck("alDeleteSources()");
1156 delete [] sources;
1157 _al_sources->clear();
1158
1159 // make sure that the context is not current when it is destroyed
1160 alcGetError(_device); // clear errors
1161 alcMakeContextCurrent(nullptr);
1162 alc_audio_errcheck("alcMakeContextCurrent(NULL)",_device);
1163
1164 alcDestroyContext(_context);
1165 alc_audio_errcheck("alcDestroyContext(_context)",_device);
1166 _context = nullptr;
1167
1168 if (_device) {
1169 audio_debug("Going to try to close openAL");
1170 alcCloseDevice(_device);
1171 // alc_audio_errcheck("alcCloseDevice(_device)",_device);
1172 _device = nullptr;
1173 audio_debug("openAL Closed");
1174 }
1175
1176 _openal_active = false;
1177 }
1178 }
1179 _cleanup_required = false;
1180}
1181
1182/**
1183 *
1184 */
1185OpenALAudioManager::SoundData::
1186SoundData() :
1187 _manager(nullptr),
1188 _sample(0),
1189 _stream(nullptr),
1190 _length(0.0),
1191 _rate(0),
1192 _channels(0),
1193 _client_count(0)
1194{
1195}
1196
1197/**
1198 *
1199 */
1200OpenALAudioManager::SoundData::
1201~SoundData() {
1202 ReMutexHolder holder(OpenALAudioManager::_lock);
1203 if (_sample != 0) {
1204 if (_manager->_is_valid) {
1205 _manager->make_current();
1206 _manager->delete_buffer(_sample);
1207 }
1208 _sample = 0;
1209 }
1210}
1211
1212/**
1213 * Increments the SoundData's client count. Any SoundData that is actively in
1214 * use (ie, has a client) is removed entirely from the expiration queue.
1215 */
1216 void 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) {
1222 if (sd->_sample) {
1223 _expiring_samples.erase(sd->_expire);
1224 } else {
1225 _expiring_streams.erase(sd->_expire);
1226 }
1227 }
1228}
1229
1230/**
1231 * Decrements the SoundData's client count. Sounds that are no longer in use
1232 * (ie, have no clients) go into the expiration queue. When the expiration
1233 * queue reaches the cache limit, the first item on the queue is freed.
1234 */
1235 void 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) {
1241 if (sd->_sample) {
1242 _expiring_samples.push_back(sd);
1243 sd->_expire = _expiring_samples.end();
1244 sd->_expire--;
1245 } else {
1246 _expiring_streams.push_back(sd);
1247 sd->_expire = _expiring_streams.end();
1248 sd->_expire--;
1249 }
1250 discard_excess_cache(_cache_limit);
1251 }
1252}
1253
1254/**
1255 * Discards sounds from the sound cache until the number of sounds remaining
1256 * is under the limit.
1257 */
1258 void OpenALAudioManager::
1259discard_excess_cache(int sample_limit) {
1260 ReMutexHolder holder(_lock);
1261 int stream_limit = 5;
1262
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());
1270 delete sd;
1271 }
1272
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());
1279 delete sd;
1280 }
1281}
1282
1283/**
1284 * Deletes an OpenAL buffer. This is a special function because some
1285 * implementations of OpenAL (e.g. Apple's) don't unlock the buffers
1286 * immediately, due to needing to coordinate with another thread. If this is
1287 * the case, the alDeleteBuffers call will error back with AL_INVALID_OPERATION
1288 * as if trying to delete an actively-used buffer, which will tell us to wait a
1289 * bit and try again.
1290 */
1291 void OpenALAudioManager::
1292delete_buffer(ALuint buffer) {
1293 ReMutexHolder holder(_lock);
1294 int tries = 0;
1295 ALuint error;
1296
1297 // Keep trying until we succeed (or give up).
1298 while (true) {
1299 alDeleteBuffers(1, &buffer);
1300 error = alGetError();
1301
1302 if (error == AL_NO_ERROR) {
1303 // Success! This will happen right away 99% of the time.
1304 return;
1305 } else if (error != AL_INVALID_OPERATION) {
1306 // We weren't expecting that. This should be reported.
1307 break;
1308 } else if (tries >= openal_buffer_delete_retries.get_value()) {
1309 // We ran out of retries. Give up.
1310 break;
1311 } else {
1312 // Make another try after (delay * 2^n) seconds.
1313 Thread::sleep(openal_buffer_delete_delay.get_value() * (1 << tries));
1314 tries++;
1315 }
1316 }
1317
1318 // If we got here, one of the breaks above happened, indicating an error.
1319 audio_error("failed to delete a buffer: " << alGetString(error) );
1320}
AudioManager
Definition audioManager.h:28
AudioSound
Definition audioSound.h:25
ConfigVariableDouble::get_value
get_value
Returns the variable's value.
Definition configVariableDouble.h:43
ConfigVariableInt::get_value
get_value
Returns the variable's value.
Definition configVariableInt.h:43
Filename
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
Filename::get_basename
std::string get_basename() const
Returns the basename part of the filename.
Definition filename.I:367
MovieAudioCursor
A MovieAudio is actually any source that provides a sequence of audio samples.
Definition movieAudioCursor.h:34
MovieAudioCursor::ready
virtual int ready() const
Returns the number of audio samples that are ready to read.
Definition movieAudioCursor.cxx:161
MovieAudio
A MovieAudio is actually any source that provides a sequence of audio samples.
Definition movieAudio.h:44
MovieAudio::get_filename
get_filename
Returns the movie's filename.
Definition movieAudio.h:52
OpenALAudioManager
Definition openalAudioManager.h:39
OpenALAudioManager::set_active
virtual void set_active(bool)
Turn on/off Warning: not implemented.
Definition openalAudioManager.cxx:670
OpenALAudioManager::audio_3d_get_listener_attributes
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.
Definition openalAudioManager.cxx:800
OpenALAudioManager::set_cache_limit
virtual void set_cache_limit(unsigned int count)
Set the number of sounds that the cache can hold.
Definition openalAudioManager.cxx:581
OpenALAudioManager::uncache_sound
virtual void uncache_sound(const Filename &)
Deletes a sample from the expiration queues.
Definition openalAudioManager.cxx:532
OpenALAudioManager::stop_all_sounds
virtual void stop_all_sounds()
Stop playback on all sounds managed by this manager.
Definition openalAudioManager.cxx:1080
OpenALAudioManager::is_valid
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'...
Definition openalAudioManager.cxx:235
OpenALAudioManager::shutdown
virtual void shutdown()
Call this at exit time to shut down the audio system.
Definition openalAudioManager.cxx:217
OpenALAudioManager::audio_3d_get_distance_factor
virtual PN_stdfloat audio_3d_get_distance_factor() const
Get value in units per meter.
Definition openalAudioManager.cxx:922
OpenALAudioManager::clear_cache
virtual void clear_cache()
Clear out the sound cache.
Definition openalAudioManager.cxx:572
OpenALAudioManager::get_volume
virtual PN_stdfloat get_volume() const
Gets listener gain.
Definition openalAudioManager.cxx:638
OpenALAudioManager::set_play_rate
void set_play_rate(PN_stdfloat play_rate)
set the overall play rate
Definition openalAudioManager.cxx:646
OpenALAudioManager::audio_3d_set_doppler_factor
virtual void audio_3d_set_doppler_factor(PN_stdfloat factor)
Exaggerates or diminishes the Doppler effect.
Definition openalAudioManager.cxx:930
OpenALAudioManager::audio_3d_set_distance_factor
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...
Definition openalAudioManager.cxx:890
OpenALAudioManager::get_play_rate
PN_stdfloat get_play_rate() const
get the overall speed/pitch/play rate
Definition openalAudioManager.cxx:662
OpenALAudioManager::audio_3d_set_drop_off_factor
virtual void audio_3d_set_drop_off_factor(PN_stdfloat factor)
Control the effect distance has on audability.
Definition openalAudioManager.cxx:953
OpenALAudioManager::audio_3d_set_listener_attributes
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!
Definition openalAudioManager.cxx:702
OpenALAudioManager::update
virtual void update()
Perform all per-frame update functions.
Definition openalAudioManager.cxx:1089
OpenALAudioManager::set_volume
virtual void set_volume(PN_stdfloat)
Sets listener gain.
Definition openalAudioManager.cxx:611
OpenALAudioSound
Definition openalAudioSound.h:32
ReMutexHolder
Similar to MutexHolder, but for a reentrant mutex.
Definition reMutexHolder.h:25
ReMutex
A reentrant mutex.
Definition reMutex.h:34
Thread::sleep
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
Definition thread.I:192
TrueClock::get_global_ptr
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition trueClock.I:68
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition virtualFileSystem.h:40
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition virtualFileSystem.cxx:640
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition virtualFileSystem.cxx:741
config_audio.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_express.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_openalAudio.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_putil.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
movieAudio.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Create_OpenALAudioManager
AudioManager * Create_OpenALAudioManager()
Factory Function.
Definition openalAudioManager.cxx:73
openalAudioManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
openalAudioSound.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pandabase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
reMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.