16 #include "pandabase.h" 19 #include "config_audio.h" 20 #include "config_util.h" 21 #include "config_express.h" 22 #include "config_openalAudio.h" 23 #include "openalAudioManager.h" 24 #include "openalAudioSound.h" 25 #include "virtualFileSystem.h" 26 #include "movieAudio.h" 27 #include "reMutexHolder.h" 31 #ifndef ALC_DEFAULT_ALL_DEVICES_SPECIFIER 32 #define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012 35 #ifndef ALC_ALL_DEVICES_SPECIFIER 36 #define ALC_ALL_DEVICES_SPECIFIER 0x1013 41 ReMutex OpenALAudioManager::_lock;
42 int OpenALAudioManager::_active_managers = 0;
43 bool OpenALAudioManager::_openal_active =
false;
44 ALCdevice* OpenALAudioManager::_device = NULL;
45 ALCcontext* OpenALAudioManager::_context = NULL;
58 void al_audio_errcheck(
const char *context) {
59 ALenum result = alGetError();
60 if (result != AL_NO_ERROR) {
61 audio_error(context <<
": " << alGetString(result) );
65 void alc_audio_errcheck(
const char *context,ALCdevice* device) {
66 ALCenum result = alcGetError(device);
67 if (result != ALC_NO_ERROR) {
68 audio_error(context <<
": " << alcGetString(device,result) );
78 audio_debug(
"Create_OpenALAudioManager()");
89 OpenALAudioManager() {
91 if (_managers == (Managers *)NULL) {
92 _managers =
new Managers;
93 _al_sources =
new SourceCache;
96 _managers->insert(
this);
98 _cleanup_required =
true;
99 _active = audio_active;
100 _volume = audio_volume;
103 _cache_limit = audio_cache_limit;
105 _concurrent_sound_limit = 0;
109 _distance_factor = 3.28;
110 _drop_off_factor = 1;
129 if (_active_managers == 0 || !_openal_active) {
131 string dev_name = select_audio_device();
133 if (!dev_name.empty()) {
135 audio_cat.info() <<
"Using OpenAL device " << dev_name <<
"\n";
136 _device = alcOpenDevice(dev_name.c_str());
138 if (_device == NULL) {
140 <<
"Couldn't open OpenAL device \"" << dev_name <<
"\", falling back to default device\n";
143 audio_cat.info() <<
"Using default OpenAL device\n";
146 if (_device == NULL) {
148 _device = alcOpenDevice(NULL);
150 if (_device == NULL && dev_name !=
"OpenAL Soft") {
152 _device = alcOpenDevice(
"OpenAL Soft");
154 if (_device == NULL) {
156 <<
"Couldn't open default OpenAL device\n";
161 if (_device != NULL) {
163 alcGetError(_device);
164 _context = alcCreateContext(_device, NULL);
165 alc_audio_errcheck(
"alcCreateContext(_device, NULL)", _device);
166 if (_context != NULL) {
167 _openal_active =
true;
176 nassertv(_active_managers>0);
178 if (!_device || !_context) {
179 audio_error(
"OpenALAudioManager: No open device or context");
182 alcGetError(_device);
183 alcMakeContextCurrent(_context);
184 alc_audio_errcheck(
"alcMakeContextCurrent(_context)", _device);
191 if (audio_cat.is_debug()) {
193 <<
"ALC_DEVICE_SPECIFIER:" << alcGetString(_device, ALC_DEVICE_SPECIFIER) << endl;
197 if (audio_cat.is_debug()) {
198 audio_cat.debug() <<
"AL_RENDERER:" << alGetString(AL_RENDERER) << endl;
199 audio_cat.debug() <<
"AL_VENDOR:" << alGetString(AL_VENDOR) << endl;
200 audio_cat.debug() <<
"AL_VERSION:" << alGetString(AL_VERSION) << endl;
210 ~OpenALAudioManager() {
212 nassertv(_managers != (Managers *)NULL);
213 Managers::iterator mi = _managers->find(
this);
214 nassertv(mi != _managers->end());
215 _managers->erase(mi);
231 if (_managers != (
Managers *)NULL) {
232 Managers::iterator mi;
233 for (mi = _managers->begin(); mi != _managers->end(); ++mi) {
238 nassertv(_active_managers == 0);
260 string OpenALAudioManager::
261 select_audio_device() {
262 string selected_device = openal_device;
264 const char *devices = NULL;
267 if (alcIsExtensionPresent(NULL,
"ALC_ENUMERATE_ALL_EXT") == AL_TRUE) {
268 string default_device = alcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
269 devices = (
const char *)alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
272 audio_cat.debug() <<
"All OpenAL devices:\n";
275 string device(devices);
276 devices += device.size() + 1;
278 if (audio_cat.is_debug()) {
279 if (device == selected_device) {
280 audio_cat.debug() <<
" " << device <<
" [selected]\n";
281 }
else if (device == default_device) {
282 audio_cat.debug() <<
" " << device <<
" [default]\n";
284 audio_cat.debug() <<
" " << device <<
"\n";
290 audio_cat.debug() <<
"ALC_ENUMERATE_ALL_EXT not supported\n";
295 if (alcIsExtensionPresent(NULL,
"ALC_ENUMERATION_EXT") == AL_TRUE) {
296 string default_device = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
297 devices = (
const char *)alcGetString(NULL, ALC_DEVICE_SPECIFIER);
300 audio_cat.debug() <<
"OpenAL drivers:\n";
303 string device(devices);
304 devices += device.size() + 1;
306 if (selected_device.empty() && device ==
"OpenAL Soft" &&
307 default_device ==
"Generic Software") {
309 selected_device =
"OpenAL Soft";
312 if (audio_cat.is_debug()) {
313 if (device == selected_device) {
314 audio_cat.debug() <<
" " << device <<
" [selected]\n";
315 }
else if (device == default_device) {
316 audio_cat.debug() <<
" " << device <<
" [default]\n";
318 audio_cat.debug() <<
" " << device <<
"\n";
324 audio_cat.debug() <<
"ALC_ENUMERATION_EXT not supported\n";
327 return selected_device;
336 void OpenALAudioManager::
337 make_current()
const {
349 bool OpenALAudioManager::
352 int channels = source->audio_channels();
353 if ((channels != 1)&&(channels != 2)) {
354 audio_error(
"Currently, only mono and stereo are supported.");
368 bool OpenALAudioManager::
371 if (mode == SM_stream) {
375 if (source->get_source()->get_filename().empty()) {
379 if (source->
ready() != 0x40000000) {
383 if (source->length() > 3600.0) {
387 int channels = source->audio_channels();
388 int samples = (int)(source->length() * source->audio_rate());
389 int bytes = samples * channels * 2;
390 if ((mode == SM_heuristic)&&(bytes > audio_preload_threshold)) {
405 OpenALAudioManager::SoundData *OpenALAudioManager::
413 if (mode != SM_stream) {
414 SampleCache::iterator lsmi=_sample_cache.find(path);
415 if (lsmi != _sample_cache.end()) {
416 SoundData *sd = (*lsmi).second;
417 increment_client_count(sd);
422 if (mode != SM_sample) {
423 ExpirationQueue::iterator exqi;
424 for (exqi=_expiring_streams.begin(); exqi!=_expiring_streams.end(); exqi++) {
425 SoundData *sd = (SoundData*)(*exqi);
426 if (sd->_movie->get_filename() == path) {
427 increment_client_count(sd);
436 audio_error(
"Cannot open file: "<<path);
440 if (!can_use_audio(stream)) {
441 audio_error(
"File is not in usable format: "<<path);
445 SoundData *sd =
new SoundData();
446 sd->_client_count = 1;
449 sd->_rate = stream->audio_rate();
450 sd->_channels = stream->audio_channels();
451 sd->_length = stream->length();
452 audio_debug(
"Creating: " << sd->_movie->get_filename().get_basename());
453 audio_debug(
" - Rate: " << sd->_rate);
454 audio_debug(
" - Channels: " << sd->_channels);
455 audio_debug(
" - Length: " << sd->_length);
457 if (should_load_audio(stream, mode)) {
458 audio_debug(path.
get_basename() <<
": loading as sample");
462 alGenBuffers(1, &sd->_sample);
463 al_audio_errcheck(
"alGenBuffers");
464 if (sd->_sample == 0) {
465 audio_error(
"Could not create an OpenAL buffer object");
469 int channels = stream->audio_channels();
470 int samples = (int)(stream->length() * stream->audio_rate());
471 PN_int16 *data =
new PN_int16[samples * channels];
472 stream->read_samples(samples, data);
473 alBufferData(sd->_sample,
474 (channels>1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
475 data, samples * channels * 2, stream->audio_rate());
476 int err = alGetError();
477 if (err != AL_NO_ERROR) {
478 audio_error(
"could not fill OpenAL buffer object with data");
482 _sample_cache.insert(SampleCache::value_type(path, sd));
484 audio_debug(path.
get_basename() <<
": loading as stream");
485 sd->_stream = stream;
497 get_sound(
MovieAudio *sound,
bool positional,
int mode) {
500 return get_null_sound();
505 _all_sounds.insert(oas);
516 get_sound(
const string &file_name,
bool positional,
int mode) {
519 return get_null_sound();
527 audio_error(
"get_sound - invalid filename");
536 _all_sounds.insert(oas);
557 SampleCache::iterator sci = _sample_cache.find(path);
558 if (sci != _sample_cache.end()) {
559 SoundData *sd = (*sci).second;
560 if (sd->_client_count == 0) {
561 _expiring_samples.erase(sd->_expire);
562 _sample_cache.erase(sci);
576 discard_excess_cache(0);
588 discard_excess_cache(count);
596 unsigned int OpenALAudioManager::
597 get_cache_limit()
const {
606 void OpenALAudioManager::
609 AllSounds::iterator ai = _all_sounds.find(audioSound);
610 if (ai != _all_sounds.end()) {
611 _all_sounds.erase(ai);
623 if (_volume!=volume) {
627 AllSounds::iterator i=_all_sounds.begin();
628 for (; i!=_all_sounds.end(); ++i) {
629 (**i).set_volume((**i).get_volume());
662 if (_play_rate!=play_rate) {
663 _play_rate = play_rate;
665 AllSounds::iterator i=_all_sounds.begin();
666 for (; i!=_all_sounds.end(); ++i) {
667 (**i).set_play_rate((**i).get_play_rate());
691 if (_active!=active) {
694 AllSounds::iterator i=_all_sounds.begin();
695 for (; i!=_all_sounds.end(); ++i) {
696 (**i).set_active(_active);
706 bool OpenALAudioManager::
727 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) {
739 _forward_up[2] = -fy;
743 _forward_up[5] = -uy;
749 alListenerfv(AL_POSITION,_position);
750 al_audio_errcheck(
"alListerfv(AL_POSITION)");
751 alListenerfv(AL_VELOCITY,_velocity);
752 al_audio_errcheck(
"alListerfv(AL_VELOCITY)");
753 alListenerfv(AL_ORIENTATION,_forward_up);
754 al_audio_errcheck(
"alListerfv(AL_ORIENTATION)");
763 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) {
773 *fx = _forward_up[0];
774 *fy = -_forward_up[2];
775 *fz = _forward_up[1];
777 *ux = _forward_up[3];
778 *uy = -_forward_up[5];
779 *uz = _forward_up[4];
795 _distance_factor = factor;
801 if (_distance_factor>0) {
802 alSpeedOfSound(1126.3*_distance_factor);
803 al_audio_errcheck(
"alSpeedOfSound()");
805 alDopplerFactor(_doppler_factor);
806 al_audio_errcheck(
"alDopplerFactor()");
808 audio_debug(
"can't set speed of sound if distance_factor <=0.0, setting doppler factor to 0.0 instead");
809 alDopplerFactor(0.0);
810 al_audio_errcheck(
"alDopplerFactor()");
813 AllSounds::iterator i=_all_sounds.begin();
814 for (; i!=_all_sounds.end(); ++i) {
815 (**i).set_3d_min_distance((**i).get_3d_min_distance());
816 (**i).set_3d_max_distance((**i).get_3d_max_distance());
827 return _distance_factor;
839 _doppler_factor = factor;
844 alDopplerFactor(_doppler_factor);
845 al_audio_errcheck(
"alDopplerFactor()");
853 PN_stdfloat OpenALAudioManager::
854 audio_3d_get_doppler_factor()
const {
855 return _doppler_factor;
867 _drop_off_factor = factor;
869 AllSounds::iterator i=_all_sounds.begin();
870 for (; i!=_all_sounds.end(); ++i) {
871 (**i).set_3d_drop_off_factor((**i).get_3d_drop_off_factor());
880 PN_stdfloat OpenALAudioManager::
881 audio_3d_get_drop_off_factor()
const {
882 return _drop_off_factor;
893 void OpenALAudioManager::
899 if (audio->_source) {
906 if (_concurrent_sound_limit) {
907 reduce_sounds_playing_to(_concurrent_sound_limit-1);
911 if (_al_sources->empty()) {
914 alGenSources(1,&source);
915 ALenum result = alGetError();
916 if (result!=AL_NO_ERROR) {
917 audio_error(
"alGenSources(): " << alGetString(result) );
919 reduce_sounds_playing_to(_sounds_playing.size()-1);
924 if (!source && !_al_sources->empty()) {
925 source = *(_al_sources->begin());
926 _al_sources->erase(source);
929 audio->_source = source;
932 _sounds_playing.insert(audio);
942 void OpenALAudioManager::
945 if (audio->_source) {
946 _al_sources->insert(audio->_source);
949 _sounds_playing.erase(audio);
957 void OpenALAudioManager::
958 set_concurrent_sound_limit(
unsigned int limit) {
960 _concurrent_sound_limit = limit;
961 reduce_sounds_playing_to(_concurrent_sound_limit);
969 unsigned int OpenALAudioManager::
970 get_concurrent_sound_limit()
const {
971 return _concurrent_sound_limit;
979 void OpenALAudioManager::
980 reduce_sounds_playing_to(
unsigned int count) {
985 int limit = _sounds_playing.size() - count;
986 while (limit-- > 0) {
987 SoundsPlaying::iterator sound = _sounds_playing.begin();
988 assert(sound != _sounds_playing.end());
1008 reduce_sounds_playing_to(0);
1025 SoundsPlaying sounds_finished;
1028 SoundsPlaying::iterator i=_sounds_playing.begin();
1029 for (; i!=_sounds_playing.end(); ++i) {
1031 sound->pull_used_buffers();
1032 sound->push_fresh_buffers();
1033 sound->restart_stalled_audio();
1034 sound->cache_time(rtc);
1035 if ((sound->_source == 0)||
1036 ((sound->_stream_queued.size() == 0)&&
1037 (sound->_loops_completed >= sound->_playing_loops))) {
1038 sounds_finished.insert(*i);
1042 i=sounds_finished.begin();
1043 for (; i!=sounds_finished.end(); ++i) {
1056 void OpenALAudioManager::
1059 if (!_cleanup_required) {
1065 AllSounds sounds(_all_sounds);
1066 AllSounds::iterator ai;
1067 for (ai = sounds.begin(); ai != sounds.end(); ++ai) {
1073 nassertv(_active_managers > 0);
1076 if (_active_managers == 0) {
1077 if (_openal_active) {
1081 sources =
new ALuint[_al_sources->size()];
1082 for (SourceCache::iterator si = _al_sources->begin(); si!=_al_sources->end(); ++si) {
1087 alDeleteSources(_al_sources->size(),sources);
1088 al_audio_errcheck(
"alDeleteSources()");
1090 _al_sources->clear();
1093 alcGetError(_device);
1094 alcMakeContextCurrent(NULL);
1095 alc_audio_errcheck(
"alcMakeContextCurrent(NULL)",_device);
1097 alcDestroyContext(_context);
1098 alc_audio_errcheck(
"alcDestroyContext(_context)",_device);
1102 audio_debug(
"Going to try to close openAL");
1103 alcCloseDevice(_device);
1106 audio_debug(
"openAL Closed");
1109 _openal_active =
false;
1112 _cleanup_required =
false;
1120 OpenALAudioManager::SoundData::
1137 OpenALAudioManager::SoundData::
1141 if (_manager->_is_valid) {
1142 _manager->make_current();
1143 alDeleteBuffers(1,&_sample);
1156 void OpenALAudioManager::
1157 increment_client_count(SoundData *sd) {
1159 sd->_client_count += 1;
1160 audio_debug(
"Incrementing: " << sd->_movie->get_filename().get_basename() <<
" " << sd->_client_count);
1161 if (sd->_client_count == 1) {
1163 _expiring_samples.erase(sd->_expire);
1165 _expiring_streams.erase(sd->_expire);
1179 void OpenALAudioManager::
1180 decrement_client_count(SoundData *sd) {
1182 sd->_client_count -= 1;
1183 audio_debug(
"Decrementing: " << sd->_movie->get_filename().get_basename() <<
" " << sd->_client_count);
1184 if (sd->_client_count == 0) {
1186 _expiring_samples.push_back(sd);
1187 sd->_expire = _expiring_samples.end();
1190 _expiring_streams.push_back(sd);
1191 sd->_expire = _expiring_streams.end();
1194 discard_excess_cache(_cache_limit);
1204 void OpenALAudioManager::
1205 discard_excess_cache(
int sample_limit) {
1207 int stream_limit = 5;
1209 while (((
int)_expiring_samples.size()) > sample_limit) {
1210 SoundData *sd = (SoundData*)(_expiring_samples.front());
1211 assert(sd->_client_count == 0);
1212 assert(sd->_expire == _expiring_samples.begin());
1213 _expiring_samples.pop_front();
1214 _sample_cache.erase(_sample_cache.find(sd->_movie->get_filename()));
1215 audio_debug(
"Expiring: " << sd->_movie->get_filename().get_basename());
1219 while (((
int)_expiring_streams.size()) > stream_limit) {
1220 SoundData *sd = (SoundData*)(_expiring_streams.front());
1221 assert(sd->_client_count == 0);
1222 assert(sd->_expire == _expiring_streams.begin());
1223 _expiring_streams.pop_front();
1224 audio_debug(
"Expiring: " << sd->_movie->get_filename().get_basename());
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.
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
string get_basename() const
Returns the basename part of the filename.
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const string &default_extension=string()) const
Searches the given search path for the filename.
virtual void set_cache_limit(unsigned int count)
Set the number of sounds that the cache can hold.
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS's file system.
virtual void set_volume(PN_stdfloat)
Sets listener gain.
PN_stdfloat get_play_rate() const
get the overall speed/pitch/play rate
virtual void clear_cache()
Clear out the sound cache.
virtual PN_stdfloat get_volume() const
Gets listener gain.
virtual void stop_all_sounds()
Stop playback on all sounds managed by this manager.
virtual void audio_3d_set_doppler_factor(PN_stdfloat factor)
Exaggerates or diminishes the Doppler effect.
virtual void audio_3d_set_drop_off_factor(PN_stdfloat factor)
Control the effect distance has on audability.
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 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! Both Panda3D an...
The name of a file, such as a texture file or an Egg file.
void set_play_rate(PN_stdfloat play_rate)
set the overall play rate
A MovieAudio is actually any source that provides a sequence of audio samples.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
virtual void uncache_sound(const string &)
Deletes a sample from the expiration queues.
Similar to MutexHolder, but for a reentrant mutex.
const Filename & get_filename() const
Returns the movie's filename.
virtual PN_stdfloat audio_3d_get_distance_factor() const
Sets units per foot.
virtual void audio_3d_set_distance_factor(PN_stdfloat factor)
Set units per foot WARNING: OpenAL has no distance factor but we use this as a scale on the min/max d...
virtual void update()
Perform all per-frame update functions.
virtual void set_active(bool)
Turn on/off Warning: not implemented.
This is our own Panda specialization on the default STL set.
TypeHandle is the identifier used to differentiate C++ class types.
A MovieAudio is actually any source that provides a sequence of audio samples.
virtual void shutdown()
Call this at exit time to shut down the audio system.
virtual int ready() const
Returns the number of audio samples that are ready to read.