Panda3D
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
36using std::endl;
37using std::string;
38
39TypeHandle OpenALAudioManager::_type_handle;
40
41ReMutex OpenALAudioManager::_lock;
42int OpenALAudioManager::_active_managers = 0;
43bool 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.
56void 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
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) );
67 }
68}
69
70/**
71 * Factory Function
72 */
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 */
217shutdown() {
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 */
235is_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 */
243string 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 */
321void 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 */
331bool 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 */
347bool 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;
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 */
532uncache_sound(const Filename &file_name) {
533 ReMutexHolder holder(_lock);
534 nassertv(is_valid());
535 Filename path = file_name;
536
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 */
572clear_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 */
581set_cache_limit(unsigned int count) {
582 ReMutexHolder holder(_lock);
583 _cache_limit=count;
584 discard_excess_cache(count);
585}
586
587/**
588 *
589 */
590unsigned int OpenALAudioManager::
591get_cache_limit() const {
592 return _cache_limit;
593}
594
595/**
596 *
597 */
598void 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 */
611void 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 */
638get_volume() const {
639 return _volume;
640}
641
642/**
643 * set the overall play rate
644 */
646set_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 */
662get_play_rate() const {
663 return _play_rate;
664}
665
666/**
667 * Turn on/off Warning: not implemented.
668 */
670set_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 */
685bool 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 */
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) {
703 ReMutexHolder holder(_lock);
704 _position[0] = px;
705 _position[1] = pz;
706 _position[2] = -py;
707
708 _velocity[0] = vx;
709 _velocity[1] = vz;
710 _velocity[2] = -vy;
711
712 _forward_up[0] = fx;
713 _forward_up[1] = fz;
714 _forward_up[2] = -fy;
715
716 _forward_up[3] = ux;
717 _forward_up[4] = uz;
718 _forward_up[5] = -uy;
719
720
721 make_current();
722
723 alGetError(); // clear errors
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)");
730}
731
732/**
733 * Get position of the "ear" that picks up 3d sounds
734 */
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) {
737 ReMutexHolder holder(_lock);
738 *px = _position[0];
739 *py = -_position[2];
740 *pz = _position[1];
741
742 *vx = _velocity[0];
743 *vy = -_velocity[2];
744 *vz = _velocity[1];
745
746 *fx = _forward_up[0];
747 *fy = -_forward_up[2];
748 *fz = _forward_up[1];
749
750 *ux = _forward_up[3];
751 *uy = -_forward_up[5];
752 *uz = _forward_up[4];
753}
754
755/**
756 * Set value in units per meter
757 * WARNING: OpenAL has no distance factor but we use this as a scale
758 * on the min/max distances of sounds to preserve FMOD compatibility.
759 * Also adjusts the speed of sound to compensate for unit difference.
760 */
762audio_3d_set_distance_factor(PN_stdfloat factor) {
763 ReMutexHolder holder(_lock);
764 _distance_factor = factor;
765
766 make_current();
767
768 alGetError(); // clear errors
769
770 if (_distance_factor>0) {
771 alSpeedOfSound(343.3*_distance_factor);
772 al_audio_errcheck("alSpeedOfSound()");
773 // resets the doppler factor to the correct setting in case it was set to
774 // 0.0 by a distance_factor<=0.0
775 alDopplerFactor(_doppler_factor);
776 al_audio_errcheck("alDopplerFactor()");
777 } else {
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()");
781 }
782
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());
787 }
788}
789
790/**
791 * Get value in units per meter
792 */
795 return _distance_factor;
796}
797
798/**
799 * Exaggerates or diminishes the Doppler effect. Defaults to 1.0
800 */
802audio_3d_set_doppler_factor(PN_stdfloat factor) {
803 ReMutexHolder holder(_lock);
804 _doppler_factor = factor;
805
806 make_current();
807
808 alGetError(); // clear errors
809 alDopplerFactor(_doppler_factor);
810 al_audio_errcheck("alDopplerFactor()");
811}
812
813/**
814 *
815 */
816PN_stdfloat OpenALAudioManager::
817audio_3d_get_doppler_factor() const {
818 return _doppler_factor;
819}
820
821/**
822 * Control the effect distance has on audability. Defaults to 1.0
823 */
825audio_3d_set_drop_off_factor(PN_stdfloat factor) {
826 ReMutexHolder holder(_lock);
827 _drop_off_factor = factor;
828
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());
832 }
833}
834
835/**
836 *
837 */
838PN_stdfloat OpenALAudioManager::
839audio_3d_get_drop_off_factor() const {
840 return _drop_off_factor;
841}
842
843/**
844 * Inform the manager that a sound is about to play. The manager will add
845 * this sound to the table of sounds that are playing, and will allocate a
846 * source to this sound.
847 */
848void OpenALAudioManager::
849starting_sound(OpenALAudioSound* audio) {
850 ReMutexHolder holder(_lock);
851 ALuint source=0;
852
853 // If the sound already has a source, we don't need to do anything.
854 if (audio->_source) {
855 return;
856 }
857
858 // first give all sounds that have finished a chance to stop, so that these
859 // get stopped first
860 update();
861
862 if (_concurrent_sound_limit) {
863 reduce_sounds_playing_to(_concurrent_sound_limit-1); // because we're about to add one
864 }
865
866 // get a source from the source pool or create a new source
867 if (_al_sources->empty()) {
868 make_current();
869 alGetError(); // clear errors
870 alGenSources(1,&source);
871 ALenum result = alGetError();
872 if (result!=AL_NO_ERROR) {
873 audio_error("alGenSources(): " << alGetString(result) );
874 // if we can't create any more sources, set stop a sound to free a
875 // source
876 reduce_sounds_playing_to(_sounds_playing.size()-1);
877 source = 0;
878 }
879 }
880 // get a source from the source bool if we didn't just allocate one
881 if (!source && !_al_sources->empty()) {
882 source = *(_al_sources->begin());
883 _al_sources->erase(source);
884 }
885
886 audio->_source = source;
887
888 if (source)
889 _sounds_playing.insert(audio);
890}
891
892/**
893 * Inform the manager that a sound is finished or someone called stop on the
894 * sound (this should not be called if a sound is only paused).
895 */
896void OpenALAudioManager::
897stopping_sound(OpenALAudioSound* audio) {
898 ReMutexHolder holder(_lock);
899 if (audio->_source) {
900 _al_sources->insert(audio->_source);
901 audio->_source = 0;
902 }
903 _sounds_playing.erase(audio); // This could cause the sound to destruct.
904}
905
906/**
907 *
908 */
909void OpenALAudioManager::
910set_concurrent_sound_limit(unsigned int limit) {
911 ReMutexHolder holder(_lock);
912 _concurrent_sound_limit = limit;
913 reduce_sounds_playing_to(_concurrent_sound_limit);
914}
915
916/**
917 *
918 */
919unsigned int OpenALAudioManager::
920get_concurrent_sound_limit() const {
921 return _concurrent_sound_limit;
922}
923
924/**
925 *
926 */
927void OpenALAudioManager::
928reduce_sounds_playing_to(unsigned int count) {
929 ReMutexHolder holder(_lock);
930 // first give all sounds that have finished a chance to stop, so that these
931 // get stopped first
932 update();
933
934 int limit = _sounds_playing.size() - count;
935 while (limit-- > 0) {
936 SoundsPlaying::iterator sound = _sounds_playing.begin();
937 nassertv(sound != _sounds_playing.end());
938 // When the user stops a sound, there is still a PT in the user's hand.
939 // When we stop a sound here, however, this can remove the last PT. This
940 // can cause an ugly recursion where stop calls the destructor, and the
941 // destructor calls stop. To avoid this, we create a temporary PT, stop
942 // the sound, and then release the PT.
943 PT(OpenALAudioSound) s = (*sound);
944 s->stop();
945 }
946}
947
948/**
949 * Stop playback on all sounds managed by this manager.
950 */
953 ReMutexHolder holder(_lock);
954 reduce_sounds_playing_to(0);
955}
956
957/**
958 * Perform all per-frame update functions.
959 */
961update() {
962 ReMutexHolder holder(_lock);
963
964 // See if any of our playing sounds have ended we must first collect a
965 // seperate list of finished sounds and then iterated over those again
966 // calling their finished method. We can't call finished() within a loop
967 // iterating over _sounds_playing since finished() modifies _sounds_playing
968 SoundsPlaying sounds_finished;
969
970 double rtc = TrueClock::get_global_ptr()->get_short_time();
971 SoundsPlaying::iterator i=_sounds_playing.begin();
972 for (; i!=_sounds_playing.end(); ++i) {
973 OpenALAudioSound *sound = (*i);
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.size() == 0)&&
980 (sound->_loops_completed >= sound->_playing_loops))) {
981 sounds_finished.insert(*i);
982 }
983 }
984
985 i=sounds_finished.begin();
986 for (; i!=sounds_finished.end(); ++i) {
987 (**i).finished();
988 }
989}
990
991
992/**
993 * Shuts down the audio manager and releases any resources associated with it.
994 * Also cleans up all AudioSounds created via the manager.
995 */
996void OpenALAudioManager::
997cleanup() {
998 ReMutexHolder holder(_lock);
999 if (!_cleanup_required) {
1000 return;
1001 }
1002
1004
1005 AllSounds sounds(_all_sounds);
1006 AllSounds::iterator ai;
1007 for (ai = sounds.begin(); ai != sounds.end(); ++ai) {
1008 (*ai)->cleanup();
1009 }
1010
1011 clear_cache();
1012
1013 nassertv(_active_managers > 0);
1014 --_active_managers;
1015
1016 if (_active_managers == 0) {
1017 if (_openal_active) {
1018 // empty the source cache
1019 int i=0;
1020 ALuint *sources;
1021 sources = new ALuint[_al_sources->size()];
1022 for (SourceCache::iterator si = _al_sources->begin(); si!=_al_sources->end(); ++si) {
1023 sources[i++]=*si;
1024 }
1025 make_current();
1026 alGetError(); // clear errors
1027 alDeleteSources(_al_sources->size(),sources);
1028 al_audio_errcheck("alDeleteSources()");
1029 delete [] sources;
1030 _al_sources->clear();
1031
1032 // make sure that the context is not current when it is destroyed
1033 alcGetError(_device); // clear errors
1034 alcMakeContextCurrent(nullptr);
1035 alc_audio_errcheck("alcMakeContextCurrent(NULL)",_device);
1036
1037 alcDestroyContext(_context);
1038 alc_audio_errcheck("alcDestroyContext(_context)",_device);
1039 _context = nullptr;
1040
1041 if (_device) {
1042 audio_debug("Going to try to close openAL");
1043 alcCloseDevice(_device);
1044 // alc_audio_errcheck("alcCloseDevice(_device)",_device);
1045 _device = nullptr;
1046 audio_debug("openAL Closed");
1047 }
1048
1049 _openal_active = false;
1050 }
1051 }
1052 _cleanup_required = false;
1053}
1054
1055/**
1056 *
1057 */
1058OpenALAudioManager::SoundData::
1059SoundData() :
1060 _manager(nullptr),
1061 _sample(0),
1062 _stream(nullptr),
1063 _length(0.0),
1064 _rate(0),
1065 _channels(0),
1066 _client_count(0)
1067{
1068}
1069
1070/**
1071 *
1072 */
1073OpenALAudioManager::SoundData::
1074~SoundData() {
1075 ReMutexHolder holder(OpenALAudioManager::_lock);
1076 if (_sample != 0) {
1077 if (_manager->_is_valid) {
1078 _manager->make_current();
1079 _manager->delete_buffer(_sample);
1080 }
1081 _sample = 0;
1082 }
1083}
1084
1085/**
1086 * Increments the SoundData's client count. Any SoundData that is actively in
1087 * use (ie, has a client) is removed entirely from the expiration queue.
1088 */
1089void OpenALAudioManager::
1090increment_client_count(SoundData *sd) {
1091 ReMutexHolder holder(_lock);
1092 sd->_client_count += 1;
1093 audio_debug("Incrementing: " << sd->_movie->get_filename().get_basename() << " " << sd->_client_count);
1094 if (sd->_client_count == 1) {
1095 if (sd->_sample) {
1096 _expiring_samples.erase(sd->_expire);
1097 } else {
1098 _expiring_streams.erase(sd->_expire);
1099 }
1100 }
1101}
1102
1103/**
1104 * Decrements the SoundData's client count. Sounds that are no longer in use
1105 * (ie, have no clients) go into the expiration queue. When the expiration
1106 * queue reaches the cache limit, the first item on the queue is freed.
1107 */
1108void OpenALAudioManager::
1109decrement_client_count(SoundData *sd) {
1110 ReMutexHolder holder(_lock);
1111 sd->_client_count -= 1;
1112 audio_debug("Decrementing: " << sd->_movie->get_filename().get_basename() << " " << sd->_client_count);
1113 if (sd->_client_count == 0) {
1114 if (sd->_sample) {
1115 _expiring_samples.push_back(sd);
1116 sd->_expire = _expiring_samples.end();
1117 sd->_expire--;
1118 } else {
1119 _expiring_streams.push_back(sd);
1120 sd->_expire = _expiring_streams.end();
1121 sd->_expire--;
1122 }
1123 discard_excess_cache(_cache_limit);
1124 }
1125}
1126
1127/**
1128 * Discards sounds from the sound cache until the number of sounds remaining
1129 * is under the limit.
1130 */
1131void OpenALAudioManager::
1132discard_excess_cache(int sample_limit) {
1133 ReMutexHolder holder(_lock);
1134 int stream_limit = 5;
1135
1136 while (((int)_expiring_samples.size()) > sample_limit) {
1137 SoundData *sd = (SoundData*)(_expiring_samples.front());
1138 nassertv(sd->_client_count == 0);
1139 nassertv(sd->_expire == _expiring_samples.begin());
1140 _expiring_samples.pop_front();
1141 _sample_cache.erase(_sample_cache.find(sd->_movie->get_filename()));
1142 audio_debug("Expiring: " << sd->_movie->get_filename().get_basename());
1143 delete sd;
1144 }
1145
1146 while (((int)_expiring_streams.size()) > stream_limit) {
1147 SoundData *sd = (SoundData*)(_expiring_streams.front());
1148 nassertv(sd->_client_count == 0);
1149 nassertv(sd->_expire == _expiring_streams.begin());
1150 _expiring_streams.pop_front();
1151 audio_debug("Expiring: " << sd->_movie->get_filename().get_basename());
1152 delete sd;
1153 }
1154}
1155
1156/**
1157 * Deletes an OpenAL buffer. This is a special function because some
1158 * implementations of OpenAL (e.g. Apple's) don't unlock the buffers
1159 * immediately, due to needing to coordinate with another thread. If this is
1160 * the case, the alDeleteBuffers call will error back with AL_INVALID_OPERATION
1161 * as if trying to delete an actively-used buffer, which will tell us to wait a
1162 * bit and try again.
1163 */
1164void OpenALAudioManager::
1165delete_buffer(ALuint buffer) {
1166 ReMutexHolder holder(_lock);
1167 int tries = 0;
1168 ALuint error;
1169
1170 // Keep trying until we succeed (or give up).
1171 while (true) {
1172 alDeleteBuffers(1, &buffer);
1173 error = alGetError();
1174
1175 if (error == AL_NO_ERROR) {
1176 // Success! This will happen right away 99% of the time.
1177 return;
1178 } else if (error != AL_INVALID_OPERATION) {
1179 // We weren't expecting that. This should be reported.
1180 break;
1181 } else if (tries >= openal_buffer_delete_retries.get_value()) {
1182 // We ran out of retries. Give up.
1183 break;
1184 } else {
1185 // Make another try after (delay * 2^n) seconds.
1186 Thread::sleep(openal_buffer_delete_delay.get_value() * (1 << tries));
1187 tries++;
1188 }
1189 }
1190
1191 // If we got here, one of the breaks above happened, indicating an error.
1192 audio_error("failed to delete a buffer: " << alGetString(error) );
1193}
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.
Definition: filename.h:39
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
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.
Definition: movieAudio.h:44
get_filename
Returns the movie's filename.
Definition: movieAudio.h:52
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.
Definition: reMutexHolder.h:25
A reentrant mutex.
Definition: reMutex.h:34
static void sleep(double seconds)
Suspends the current thread for at least the indicated amount of time.
Definition: thread.I:192
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition: trueClock.I:68
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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.
Definition: pset.h:49
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.
PT(AudioSound) OpenALAudioManager
This is what creates a sound instance.
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.