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
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 it = _sounds_playing.begin();
972 while (it != _sounds_playing.end()) {
973 PT(OpenALAudioSound) sound = *(it++);
974 sound->pull_used_buffers();
975 sound->push_fresh_buffers();
976 sound->restart_stalled_audio();
977 sound->cache_time(rtc);
978 if (sound->_source == 0 ||
979 (sound->_stream_queued.empty() &&
980 (sound->_loops_completed >= sound->_playing_loops))) {
981 sounds_finished.insert(std::move(sound));
982 }
983 }
984
985 for (OpenALAudioSound *sound : sounds_finished) {
986 sound->finished();
987 }
988}
989
990
991/**
992 * Shuts down the audio manager and releases any resources associated with it.
993 * Also cleans up all AudioSounds created via the manager.
994 */
995void OpenALAudioManager::
996cleanup() {
997 ReMutexHolder holder(_lock);
998 if (!_cleanup_required) {
999 return;
1000 }
1001
1003
1004 AllSounds sounds(_all_sounds);
1005 AllSounds::iterator ai;
1006 for (ai = sounds.begin(); ai != sounds.end(); ++ai) {
1007 (*ai)->cleanup();
1008 }
1009
1010 clear_cache();
1011
1012 nassertv(_active_managers > 0);
1013 --_active_managers;
1014
1015 if (_active_managers == 0) {
1016 if (_openal_active) {
1017 // empty the source cache
1018 int i=0;
1019 ALuint *sources;
1020 sources = new ALuint[_al_sources->size()];
1021 for (SourceCache::iterator si = _al_sources->begin(); si!=_al_sources->end(); ++si) {
1022 sources[i++]=*si;
1023 }
1024 make_current();
1025 alGetError(); // clear errors
1026 alDeleteSources(_al_sources->size(),sources);
1027 al_audio_errcheck("alDeleteSources()");
1028 delete [] sources;
1029 _al_sources->clear();
1030
1031 // make sure that the context is not current when it is destroyed
1032 alcGetError(_device); // clear errors
1033 alcMakeContextCurrent(nullptr);
1034 alc_audio_errcheck("alcMakeContextCurrent(NULL)",_device);
1035
1036 alcDestroyContext(_context);
1037 alc_audio_errcheck("alcDestroyContext(_context)",_device);
1038 _context = nullptr;
1039
1040 if (_device) {
1041 audio_debug("Going to try to close openAL");
1042 alcCloseDevice(_device);
1043 // alc_audio_errcheck("alcCloseDevice(_device)",_device);
1044 _device = nullptr;
1045 audio_debug("openAL Closed");
1046 }
1047
1048 _openal_active = false;
1049 }
1050 }
1051 _cleanup_required = false;
1052}
1053
1054/**
1055 *
1056 */
1057OpenALAudioManager::SoundData::
1058SoundData() :
1059 _manager(nullptr),
1060 _sample(0),
1061 _stream(nullptr),
1062 _length(0.0),
1063 _rate(0),
1064 _channels(0),
1065 _client_count(0)
1066{
1067}
1068
1069/**
1070 *
1071 */
1072OpenALAudioManager::SoundData::
1073~SoundData() {
1074 ReMutexHolder holder(OpenALAudioManager::_lock);
1075 if (_sample != 0) {
1076 if (_manager->_is_valid) {
1077 _manager->make_current();
1078 _manager->delete_buffer(_sample);
1079 }
1080 _sample = 0;
1081 }
1082}
1083
1084/**
1085 * Increments the SoundData's client count. Any SoundData that is actively in
1086 * use (ie, has a client) is removed entirely from the expiration queue.
1087 */
1088void OpenALAudioManager::
1089increment_client_count(SoundData *sd) {
1090 ReMutexHolder holder(_lock);
1091 sd->_client_count += 1;
1092 audio_debug("Incrementing: " << sd->_movie->get_filename().get_basename() << " " << sd->_client_count);
1093 if (sd->_client_count == 1) {
1094 if (sd->_sample) {
1095 _expiring_samples.erase(sd->_expire);
1096 } else {
1097 _expiring_streams.erase(sd->_expire);
1098 }
1099 }
1100}
1101
1102/**
1103 * Decrements the SoundData's client count. Sounds that are no longer in use
1104 * (ie, have no clients) go into the expiration queue. When the expiration
1105 * queue reaches the cache limit, the first item on the queue is freed.
1106 */
1107void OpenALAudioManager::
1108decrement_client_count(SoundData *sd) {
1109 ReMutexHolder holder(_lock);
1110 sd->_client_count -= 1;
1111 audio_debug("Decrementing: " << sd->_movie->get_filename().get_basename() << " " << sd->_client_count);
1112 if (sd->_client_count == 0) {
1113 if (sd->_sample) {
1114 _expiring_samples.push_back(sd);
1115 sd->_expire = _expiring_samples.end();
1116 sd->_expire--;
1117 } else {
1118 _expiring_streams.push_back(sd);
1119 sd->_expire = _expiring_streams.end();
1120 sd->_expire--;
1121 }
1122 discard_excess_cache(_cache_limit);
1123 }
1124}
1125
1126/**
1127 * Discards sounds from the sound cache until the number of sounds remaining
1128 * is under the limit.
1129 */
1130void OpenALAudioManager::
1131discard_excess_cache(int sample_limit) {
1132 ReMutexHolder holder(_lock);
1133 int stream_limit = 5;
1134
1135 while (((int)_expiring_samples.size()) > sample_limit) {
1136 SoundData *sd = (SoundData*)(_expiring_samples.front());
1137 nassertv(sd->_client_count == 0);
1138 nassertv(sd->_expire == _expiring_samples.begin());
1139 _expiring_samples.pop_front();
1140 _sample_cache.erase(_sample_cache.find(sd->_movie->get_filename()));
1141 audio_debug("Expiring: " << sd->_movie->get_filename().get_basename());
1142 delete sd;
1143 }
1144
1145 while (((int)_expiring_streams.size()) > stream_limit) {
1146 SoundData *sd = (SoundData*)(_expiring_streams.front());
1147 nassertv(sd->_client_count == 0);
1148 nassertv(sd->_expire == _expiring_streams.begin());
1149 _expiring_streams.pop_front();
1150 audio_debug("Expiring: " << sd->_movie->get_filename().get_basename());
1151 delete sd;
1152 }
1153}
1154
1155/**
1156 * Deletes an OpenAL buffer. This is a special function because some
1157 * implementations of OpenAL (e.g. Apple's) don't unlock the buffers
1158 * immediately, due to needing to coordinate with another thread. If this is
1159 * the case, the alDeleteBuffers call will error back with AL_INVALID_OPERATION
1160 * as if trying to delete an actively-used buffer, which will tell us to wait a
1161 * bit and try again.
1162 */
1163void OpenALAudioManager::
1164delete_buffer(ALuint buffer) {
1165 ReMutexHolder holder(_lock);
1166 int tries = 0;
1167 ALuint error;
1168
1169 // Keep trying until we succeed (or give up).
1170 while (true) {
1171 alDeleteBuffers(1, &buffer);
1172 error = alGetError();
1173
1174 if (error == AL_NO_ERROR) {
1175 // Success! This will happen right away 99% of the time.
1176 return;
1177 } else if (error != AL_INVALID_OPERATION) {
1178 // We weren't expecting that. This should be reported.
1179 break;
1180 } else if (tries >= openal_buffer_delete_retries.get_value()) {
1181 // We ran out of retries. Give up.
1182 break;
1183 } else {
1184 // Make another try after (delay * 2^n) seconds.
1185 Thread::sleep(openal_buffer_delete_delay.get_value() * (1 << tries));
1186 tries++;
1187 }
1188 }
1189
1190 // If we got here, one of the breaks above happened, indicating an error.
1191 audio_error("failed to delete a buffer: " << alGetString(error) );
1192}
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:44
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.
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.
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.