Panda3D
milesAudioManager.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 milesAudioManager.cxx
10  * @author skyler
11  * @date 2001-06-06
12  * Prior system by: cary
13  */
14 
15 #include "milesAudioManager.h"
16 
17 #ifdef HAVE_RAD_MSS //[
18 
19 #include "milesAudioSound.h"
20 #include "milesAudioSample.h"
21 #include "milesAudioStream.h"
22 #include "globalMilesManager.h"
23 #include "config_audio.h"
24 #include "config_putil.h"
25 #include "config_express.h"
26 #include "virtualFileSystem.h"
27 #include "nullAudioSound.h"
28 #include "string_utils.h"
29 #include "mutexHolder.h"
30 #include "lightReMutexHolder.h"
31 
32 #include <algorithm>
33 
34 using std::string;
35 
36 
37 TypeHandle MilesAudioManager::_type_handle;
38 
39 AudioManager *Create_MilesAudioManager() {
40  audio_debug("Create_MilesAudioManager()");
41  return new MilesAudioManager();
42 }
43 
44 /**
45  * Create an audio manager. This may open the Miles sound system if there
46  * were no other MilesAudioManager instances. Subsequent managers may use the
47  * same Miles resources.
48  */
49 MilesAudioManager::
50 MilesAudioManager() :
51  _lock("MilesAudioManager::_lock"),
52  _streams_lock("MilesAudioManager::_streams_lock"),
53  _streams_cvar(_streams_lock)
54 {
55  audio_debug("MilesAudioManager::MilesAudioManager(), this = "
56  << (void *)this);
57  GlobalMilesManager::get_global_ptr()->add_manager(this);
58  audio_debug(" audio_active="<<audio_active);
59  audio_debug(" audio_volume="<<audio_volume);
60  _cleanup_required = true;
61  _active = audio_active;
62  _volume = audio_volume;
63  _play_rate = 1.0f;
64  _cache_limit = audio_cache_limit;
65  _concurrent_sound_limit = 0;
66  _is_valid = true;
67  _hasMidiSounds = false;
68  _sounds_finished = false;
69 
70  // We used to hang a call to a force-shutdown function on atexit(), so that
71  // any running sounds (particularly MIDI sounds) would be silenced on exit,
72  // especially a sudden exit triggered by a Python exception. But that
73  // causes problems because Miles itself also hangs a force-shutdown function
74  // on atexit(), and you can't call AIL_cleanup() twice--that results in a
75  // crash.
76 
77  // Nowadays, we provide the AudioManager::shutdown() method instead, which
78  // allows the app to force all sounds to stop cleanly before we get to the
79  // atexit() stack. In Python, we guarantee that this method will be called
80  // in the sys.exitfunc chain.
81 }
82 
83 /**
84  * Clean up this audio manager and possibly release the Miles resources that
85  * are reserved by the application (the later happens if this is the last
86  * active manager).
87  */
88 MilesAudioManager::
89 ~MilesAudioManager() {
90  audio_debug("MilesAudioManager::~MilesAudioManager(), this = "
91  << (void *)this);
92  cleanup();
93  GlobalMilesManager::get_global_ptr()->remove_manager(this);
94 
95  audio_debug("MilesAudioManager::~MilesAudioManager() finished");
96 }
97 
98 /**
99  * Call this at exit time to shut down the audio system. This will invalidate
100  * all currently-active AudioManagers and AudioSounds in the system. If you
101  * change your mind and want to play sounds again, you will have to recreate
102  * all of these objects.
103  */
104 void MilesAudioManager::
105 shutdown() {
106  audio_debug("shutdown() started");
107  GlobalMilesManager::get_global_ptr()->cleanup();
108  audio_debug("shutdown() finished");
109 }
110 
111 /**
112  * This is mostly for debugging, but it it could be used to detect errors in a
113  * release build if you don't mind the cpu cost.
114  */
115 bool MilesAudioManager::
116 is_valid() {
117  LightReMutexHolder holder(_lock);
118  return do_is_valid();
119 }
120 
121 /**
122  *
123  */
124 PT(AudioSound) MilesAudioManager::
125 get_sound(const Filename &file_name, bool, int) {
126  LightReMutexHolder holder(_lock);
127  audio_debug("MilesAudioManager::get_sound(file_name=\""<<file_name<<"\")");
128 
129  if (!do_is_valid()) {
130  audio_debug("invalid MilesAudioManager returning NullSound");
131  return get_null_sound();
132  }
133 
134  Filename path = file_name;
135 
137  vfs->resolve_filename(path, get_model_path());
138  audio_debug("Reading "<<path);
139  audio_debug(" resolved file_name is '"<<path<<"'");
140 
141  PT(SoundData) sd;
142  // Get the sound, either from the cache or from a loader:
143  SoundMap::const_iterator si=_sounds.find(path);
144  if (si != _sounds.end()) {
145  // ...found the sound in the cache.
146  sd = (*si).second;
147  audio_debug(" sound found in pool 0x" << (void*)sd);
148 
149  } else {
150  // ...the sound was not found in the cachepool.
151  sd = load(path);
152  if (sd != nullptr) {
153  while (_sounds.size() >= (unsigned int)_cache_limit) {
154  uncache_a_sound();
155  }
156  // Put it in the pool: The following is roughly like: _sounds[path] =
157  // sd; But, it gives us an iterator into the map.
158  std::pair<SoundMap::const_iterator, bool> ib
159  = _sounds.insert(SoundMap::value_type(path, sd));
160  if (!ib.second) {
161  // The insert failed.
162  audio_debug(" failed map insert of "<<path);
163  nassertr(do_is_valid(), nullptr);
164  return get_null_sound();
165  }
166  // Set si, so that we can get a reference to the path for the
167  // MilesAudioSound.
168  si=ib.first;
169  }
170  }
171  // Create an AudioSound from the sound:
172  PT(AudioSound) audioSound;
173 
174  if (sd != nullptr) {
175  most_recently_used((*si).first);
176  if (sd->_file_type == AILFILETYPE_MIDI ||
177  sd->_file_type == AILFILETYPE_XMIDI ||
178  sd->_file_type == AILFILETYPE_XMIDI_DLS ||
179  sd->_file_type == AILFILETYPE_XMIDI_MLS) {
180  // MIDI file.
181  audioSound = new MilesAudioSequence(this, sd, file_name);
182 
183  } else if (!sd->_raw_data.empty()) {
184  // WAV or MP3 file preloaded into memory.
185  audioSound = new MilesAudioSample(this, sd, file_name);
186 
187  } else {
188  // WAV or MP3 file streamed from disk.
189  audioSound = new MilesAudioStream(this, file_name, path);
190  }
191 
192  audioSound->set_active(_active);
193 
194  bool inserted = _sounds_on_loan.insert((MilesAudioSound *)audioSound.p()).second;
195  nassertr(inserted, audioSound);
196 
197  _hasMidiSounds |= (file_name.find(".mid")!=string::npos);
198  } else {
199  // Couldn't load the file; just return a NullAudioSound.
200  audioSound = new NullAudioSound;
201  }
202 
203  audio_debug(" returning 0x" << (void*)audioSound);
204  nassertr(do_is_valid(), nullptr);
205  return audioSound;
206 }
207 
208 /**
209  *
210  */
211 PT(AudioSound) MilesAudioManager::
212 get_sound(MovieAudio *sound, bool, int) {
213  nassert_raise("Miles audio manager does not support MovieAudio sources.");
214  return nullptr;
215 }
216 
217 /**
218  *
219  */
220 void MilesAudioManager::
221 uncache_sound(const Filename &file_name) {
222  audio_debug("MilesAudioManager::uncache_sound(file_name=\""
223  <<file_name<<"\")");
224  LightReMutexHolder holder(_lock);
225  nassertv(do_is_valid());
226  Filename path = file_name;
227 
229  vfs->resolve_filename(path, get_model_path());
230 
231  audio_debug(" path=\""<<path<<"\"");
232  SoundMap::iterator i = _sounds.find(path);
233  if (i != _sounds.end()) {
234  nassertv(_lru.size() > 0);
235  LRU::iterator lru_i = find(_lru.begin(), _lru.end(), &(i->first));
236  nassertv(lru_i != _lru.end());
237  _lru.erase(lru_i);
238  _sounds.erase(i);
239  }
240  nassertv(do_is_valid());
241 }
242 
243 /**
244  * Clear out the sound cache.
245  */
246 void MilesAudioManager::
247 clear_cache() {
248  audio_debug("MilesAudioManager::clear_cache()");
249  LightReMutexHolder holder(_lock);
250  do_clear_cache();
251 }
252 
253 /**
254  * Set the number of sounds that the cache can hold.
255  */
256 void MilesAudioManager::
257 set_cache_limit(unsigned int count) {
258  LightReMutexHolder holder(_lock);
259 
260  audio_debug("MilesAudioManager::set_cache_limit(count="<<count<<")");
261  nassertv(do_is_valid());
262  while (_lru.size() > count) {
263  uncache_a_sound();
264  }
265  _cache_limit=count;
266  nassertv(do_is_valid());
267 }
268 
269 /**
270  *
271  */
272 unsigned int MilesAudioManager::
273 get_cache_limit() const {
274  return _cache_limit;
275 }
276 
277 /**
278  * set the overall volume
279  */
280 void MilesAudioManager::
281 set_volume(PN_stdfloat volume) {
282  audio_debug("MilesAudioManager::set_volume(volume="<<volume<<")");
283  LightReMutexHolder holder(_lock);
284  if (_volume != volume) {
285  _volume = volume;
286  // Tell our AudioSounds to adjust:
287  AudioSet::iterator i = _sounds_on_loan.begin();
288  for (; i!=_sounds_on_loan.end(); ++i) {
289  (*i)->set_volume((*i)->get_volume());
290  }
291  }
292 }
293 
294 /**
295  * get the overall volume
296  */
297 PN_stdfloat MilesAudioManager::
298 get_volume() const {
299  return _volume;
300 }
301 
302 /**
303  * set the overall play rate
304  */
305 void MilesAudioManager::
306 set_play_rate(PN_stdfloat play_rate) {
307  audio_debug("MilesAudioManager::set_play_rate(play_rate="<<play_rate<<")");
308  LightReMutexHolder holder(_lock);
309  if (_play_rate != play_rate) {
310  _play_rate = play_rate;
311  // Tell our AudioSounds to adjust:
312  AudioSet::iterator i = _sounds_on_loan.begin();
313  for (; i != _sounds_on_loan.end(); ++i) {
314  (*i)->set_play_rate((*i)->get_play_rate());
315  }
316  }
317 }
318 
319 /**
320  * get the overall speed/pitch/play rate
321  */
322 PN_stdfloat MilesAudioManager::
323 get_play_rate() const {
324  return _play_rate;
325 }
326 
327 /**
328  * turn on/off
329  */
330 void MilesAudioManager::
331 set_active(bool active) {
332  audio_debug("MilesAudioManager::set_active(flag="<<active<<")");
333  LightReMutexHolder holder(_lock);
334  if (_active != active) {
335  _active=active;
336  // Tell our AudioSounds to adjust:
337  AudioSet::iterator i = _sounds_on_loan.begin();
338  for (; i != _sounds_on_loan.end(); ++i) {
339  (*i)->set_active(_active);
340  }
341 
342  if ((!_active) && _hasMidiSounds) {
343  GlobalMilesManager::get_global_ptr()->force_midi_reset();
344  }
345  }
346 }
347 
348 /**
349  *
350  */
351 bool MilesAudioManager::
352 get_active() const {
353  return _active;
354 }
355 
356 /**
357  *
358  */
359 void MilesAudioManager::
360 set_concurrent_sound_limit(unsigned int limit) {
361  LightReMutexHolder holder(_lock);
362  _concurrent_sound_limit = limit;
363  do_reduce_sounds_playing_to(_concurrent_sound_limit);
364 }
365 
366 /**
367  *
368  */
369 unsigned int MilesAudioManager::
370 get_concurrent_sound_limit() const {
371  return _concurrent_sound_limit;
372 }
373 
374 /**
375  *
376  */
377 void MilesAudioManager::
378 reduce_sounds_playing_to(unsigned int count) {
379  LightReMutexHolder holder(_lock);
380  do_reduce_sounds_playing_to(count);
381 }
382 
383 /**
384  * Stop playback on all sounds managed by this manager.
385  */
386 void MilesAudioManager::
387 stop_all_sounds() {
388  audio_debug("MilesAudioManager::stop_all_sounds()");
389  reduce_sounds_playing_to(0);
390 }
391 
392 /**
393  * Set spatial attributes of the listener for 3D sounds. Note that Y and Z
394  * are switched to translate into Miles's coordinate system.
395  */
396 void MilesAudioManager::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) {
397  audio_debug("MilesAudioManager::audio_3d_set_listener_attributes()");
398 
399  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
400  AIL_set_listener_3D_position(mgr->_digital_driver, px, pz, py);
401  AIL_set_listener_3D_velocity_vector(mgr->_digital_driver, vx, vz, vy);
402  AIL_set_listener_3D_orientation(mgr->_digital_driver, fx, fz, fy, ux, uz, uy);
403 }
404 
405 /**
406  * Get spatial attributes of the listener for 3D sounds. Note that Y and Z
407  * are switched to translate from Miles's coordinate system.
408  */
409 void MilesAudioManager::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) {
410  audio_debug("MilesAudioManager::audio_3d_get_listener_attributes()");
411 
412  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
413  float lpx, lpy, lpz, lvx, lvy, lvz, lfx, lfy, lfz, lux, luy, luz;
414  AIL_listener_3D_position(mgr->_digital_driver, &lpx, &lpz, &lpy);
415  AIL_listener_3D_velocity(mgr->_digital_driver, &lvx, &lvz, &lvy);
416  AIL_listener_3D_orientation(mgr->_digital_driver, &lfx, &lfz, &lfy, &lux, &luz, &luy);
417 
418  *px = lpx;
419  *py = lpy;
420  *pz = lpz;
421  *vx = lvx;
422  *vy = lvy;
423  *vz = lvz;
424  *fx = lfx;
425  *fy = lfy;
426  *fz = lfz;
427  *ux = lux;
428  *uy = luy;
429  *uz = luz;
430 }
431 
432 /**
433  * Set factor to allow user to easily work in a different scale. 1.0
434  * represents meters.
435  */
436 void MilesAudioManager::audio_3d_set_distance_factor(PN_stdfloat factor) {
437  audio_debug("MilesAudioManager::audio_3d_set_distance_factor( factor= " << factor << ")");
438 
439  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
440  AIL_set_3D_distance_factor(mgr->_digital_driver, factor);
441 }
442 
443 /**
444  * Get factor controlling working units.
445  */
446 PN_stdfloat MilesAudioManager::audio_3d_get_distance_factor() const {
447  audio_debug("MilesAudioManager::audio_3d_get_distance_factor()");
448 
449  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
450  return AIL_3D_distance_factor(mgr->_digital_driver);
451 }
452 
453 /**
454  * Exaggerates or diminishes the Doppler effect. Defaults to 1.0
455  */
456 void MilesAudioManager::audio_3d_set_doppler_factor(PN_stdfloat factor) {
457  audio_debug("MilesAudioManager::audio_3d_set_doppler_factor(factor="<<factor<<")");
458 
459  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
460  AIL_set_3D_doppler_factor(mgr->_digital_driver, factor);
461 }
462 
463 /**
464  * Get the factor controlling the Doppler effect.
465  */
466 PN_stdfloat MilesAudioManager::audio_3d_get_doppler_factor() const {
467  audio_debug("MilesAudioManager::audio_3d_get_doppler_factor()");
468 
469  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
470  return AIL_3D_doppler_factor(mgr->_digital_driver);
471 }
472 
473 /**
474  * Control the effect distance has on audability. Defaults to 1.0
475  */
476 void MilesAudioManager::audio_3d_set_drop_off_factor(PN_stdfloat factor) {
477  audio_debug("MilesAudioManager::audio_3d_set_drop_off_factor("<<factor<<")");
478 
479  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
480  AIL_set_3D_rolloff_factor(mgr->_digital_driver, factor);
481 }
482 
483 /**
484  * Get the factor controlling how quickly sound falls off with distance.
485  */
486 PN_stdfloat MilesAudioManager::audio_3d_get_drop_off_factor() const {
487  audio_debug("MilesAudioManager::audio_3d_get_drop_off_factor()");
488 
489  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
490  return AIL_3D_rolloff_factor(mgr->_digital_driver);
491 }
492 
493 /**
494  * Works similarly to MilesAudioSound::set_speaker_levels, but specifies the
495  * 3D positions of the speakers in space.
496  *
497  * Once a NULL value is found for a speaker position, no more speaker
498  * positions will be used.
499  *
500  * Note that Y and Z are switched to translate from Miles's coordinate system.
501  */
502 void MilesAudioManager::
503 set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) {
504  audio_debug("MilesAudioManager::set_speaker_configuration()");
505 
506  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
507 
508  MSSVECTOR3D speakers[9];
509 
510  if(speaker1 != nullptr) {
511  speakers[0].x = speaker1->get_x();
512  speakers[0].y = speaker1->get_z();
513  speakers[0].z = speaker1->get_y();
514  }
515  if(speaker2 != nullptr) {
516  speakers[1].x = speaker2->get_x();
517  speakers[1].y = speaker2->get_z();
518  speakers[1].z = speaker2->get_y();
519  }
520  if(speaker3 != nullptr) {
521  speakers[2].x = speaker3->get_x();
522  speakers[2].y = speaker3->get_z();
523  speakers[2].z = speaker3->get_y();
524  }
525  if(speaker4 != nullptr) {
526  speakers[3].x = speaker4->get_x();
527  speakers[3].y = speaker4->get_z();
528  speakers[3].z = speaker4->get_y();
529  }
530  if(speaker5 != nullptr) {
531  speakers[4].x = speaker5->get_x();
532  speakers[4].y = speaker5->get_z();
533  speakers[4].z = speaker5->get_y();
534  }
535  if(speaker6 != nullptr) {
536  speakers[5].x = speaker6->get_x();
537  speakers[5].y = speaker6->get_z();
538  speakers[5].z = speaker6->get_y();
539  }
540  if(speaker7 != nullptr) {
541  speakers[6].x = speaker7->get_x();
542  speakers[6].y = speaker7->get_z();
543  speakers[6].z = speaker7->get_y();
544  }
545  if(speaker8 != nullptr) {
546  speakers[7].x = speaker8->get_x();
547  speakers[7].y = speaker8->get_z();
548  speakers[7].z = speaker8->get_y();
549  }
550  if(speaker9 != nullptr) {
551  speakers[8].x = speaker9->get_x();
552  speakers[8].y = speaker9->get_z();
553  speakers[8].z = speaker9->get_y();
554  }
555 
556  if(speaker1 == nullptr) {
557  audio_error("No valid speaker positions specified in MilesAudioManager::set_speaker_configuration().");
558  } else if(speaker2 == nullptr) {
559  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 1, 1.0);
560  } else if(speaker3 == nullptr) {
561  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 2, 1.0);
562  } else if(speaker4 == nullptr) {
563  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 3, 1.0);
564  } else if(speaker5 == nullptr) {
565  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 4, 1.0);
566  } else if(speaker6 == nullptr) {
567  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 5, 1.0);
568  } else if(speaker7 == nullptr) {
569  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 6, 1.0);
570  } else if(speaker8 == nullptr) {
571  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 7, 1.0);
572  } else if(speaker9 == nullptr) {
573  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 8, 1.0);
574  } else {
575  AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 9, 1.0);
576  }
577 }
578 
579 /**
580  * Must be called every frame. Failure to call this every frame could cause
581  * problems for some audio managers.
582  */
583 void MilesAudioManager::
584 update() {
585  {
586  MutexHolder holder(_streams_lock);
587  if (_stream_thread.is_null() && !_streams.empty()) {
588  // If we don't have a sub-thread, we have to service the streams in the
589  // main thread.
590  do_service_streams();
591  }
592  }
593 
594  if (_sounds_finished) {
595  _sounds_finished = false;
596 
597  // If the _sounds_finished flag was set, we should scan our list of
598  // playing sounds and see if any of them have finished recently. We don't
599  // do this in the finished callback, because that might have been called
600  // in a sub-thread (and we may not have threading supported--and mutex
601  // protection--compiled in).
602 
603  SoundsPlaying::iterator si = _sounds_playing.begin();
604  while (si != _sounds_playing.end()) {
605  MilesAudioSound *sound = (*si);
606  ++si;
607 
608  if (sound->status() == AudioSound::READY) {
609  sound->stop();
610  }
611  }
612  }
613 }
614 
615 /**
616  *
617  */
618 void MilesAudioManager::
619 release_sound(MilesAudioSound *audioSound) {
620  audio_debug("MilesAudioManager::release_sound(audioSound=\""
621  <<audioSound->get_name()<<"\"), this = " << (void *)this);
622  LightReMutexHolder holder(_lock);
623  AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
624  if (ai != _sounds_on_loan.end()) {
625  _sounds_on_loan.erase(ai);
626  }
627 
628  audio_debug("MilesAudioManager::release_sound() finished");
629 }
630 
631 /**
632  * Shuts down the audio manager and releases any resources associated with it.
633  * Also cleans up all AudioSounds created via the manager.
634  */
635 void MilesAudioManager::
636 cleanup() {
637  audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this
638  << ", _cleanup_required = " << _cleanup_required);
639  LightReMutexHolder holder(_lock);
640  if (!_cleanup_required) {
641  return;
642  }
643 
644  // Be sure to cleanup associated sounds before cleaning up the manager:
645  AudioSet orig_sounds;
646  orig_sounds.swap(_sounds_on_loan);
647  AudioSet::iterator ai;
648  for (ai = orig_sounds.begin(); ai != orig_sounds.end(); ++ai) {
649  (*ai)->cleanup();
650  }
651 
652  do_clear_cache();
653 
654  // Now stop the thread, if it has been started.
655  if (!_stream_thread.is_null()) {
656  milesAudio_cat.info()
657  << "Stopping audio streaming thread.\n";
658  PT(StreamThread) old_thread;
659  {
660  MutexHolder holder(_streams_lock);
661  nassertv(!_stream_thread.is_null());
662  _stream_thread->_keep_running = false;
663  _streams_cvar.notify();
664  old_thread = _stream_thread;
665  _stream_thread.clear();
666  }
667  old_thread->join();
668  }
669 
670  _cleanup_required = false;
671  audio_debug("MilesAudioManager::cleanup() finished");
672 }
673 
674 /**
675  *
676  */
677 void MilesAudioManager::
678 output(std::ostream &out) const {
679  LightReMutexHolder holder(_lock);
680  out << get_type() << ": " << _sounds_playing.size()
681  << " / " << _sounds_on_loan.size() << " sounds playing / total";
682 }
683 
684 /**
685  *
686  */
687 void MilesAudioManager::
688 write(std::ostream &out) const {
689  LightReMutexHolder holder(_lock);
690 
691  out << (*this) << "\n";
692  AudioSet::const_iterator ai;
693  for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) {
694  MilesAudioSound *sound = (*ai);
695  out << " " << *sound << "\n";
696  }
697 
698  size_t total_preload = 0;
699  size_t num_preloaded = 0;
700  SoundMap::const_iterator si;
701  for (si = _sounds.begin(); si != _sounds.end(); ++si) {
702  if (!(*si).second->_raw_data.empty()) {
703  ++num_preloaded;
704  total_preload += (*si).second->_raw_data.size();
705  }
706  }
707  out << num_preloaded << " of " << _sounds.size() << " sounds preloaded, size used is " << (total_preload + 1023) / 1024 << "K\n";
708 
709  {
710  MutexHolder holder(_streams_lock);
711  out << _streams.size() << " streams opened.\n";
712  if (!_stream_thread.is_null()) {
713  out << "(Audio streaming thread has been started.)\n";
714  }
715  }
716 
717  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
718 
719  int num_samples = mgr->get_num_samples();
720  out << num_samples << " sample handles allocated globally.\n";
721 
722  int num_sequences = mgr->get_num_sequences();
723  out << num_sequences << " sequence handles allocated globally.\n";
724 }
725 
726 
727 /**
728  * Implementation of is_valid(). Assumes the lock is already held.
729  */
730 bool MilesAudioManager::
731 do_is_valid() {
732  bool check = true;
733  if (_sounds.size() != _lru.size()) {
734  audio_debug("-- Error _sounds.size() != _lru.size() --");
735  check = false;
736 
737  } else {
738  LRU::const_iterator i = _lru.begin();
739  for (; i != _lru.end(); ++i) {
740  SoundMap::const_iterator smi = _sounds.find(**i);
741  if (smi == _sounds.end()) {
742  audio_debug("-- "<<**i<<" in _lru and not in _sounds --");
743  check = false;
744  break;
745  }
746  }
747  }
748  return _is_valid && check;
749 }
750 
751 /**
752  * Assumes the lock is already held.
753  */
754 void MilesAudioManager::
755 do_reduce_sounds_playing_to(unsigned int count) {
756  int limit = _sounds_playing.size() - count;
757  while (limit-- > 0) {
758  SoundsPlaying::iterator sound = _sounds_playing.begin();
759  assert(sound != _sounds_playing.end());
760  (**sound).stop();
761  }
762 }
763 
764 /**
765  * Assumes the lock is already held.
766  */
767 void MilesAudioManager::
768 do_clear_cache() {
769  if (_is_valid) { nassertv(do_is_valid()); }
770  _sounds.clear();
771  _lru.clear();
772  if (_is_valid) { nassertv(do_is_valid()); }
773 }
774 
775 /**
776  * Adds the indicated stream to the list of streams to be serviced by a Panda
777  * sub-thread. This is in lieu of Miles' auto-service-stream mechanism.
778  */
779 void MilesAudioManager::
780 start_service_stream(HSTREAM stream) {
781  MutexHolder holder(_streams_lock);
782  nassertv(find(_streams.begin(), _streams.end(), stream) == _streams.end());
783  _streams.push_back(stream);
784  _streams_cvar.notify();
785 
786  if (_stream_thread.is_null() && Thread::is_threading_supported()) {
787  milesAudio_cat.info()
788  << "Starting audio streaming thread.\n";
789  _stream_thread = new StreamThread(this);
790  _stream_thread->start(TP_low, true);
791  }
792 }
793 
794 /**
795  * Removes the indicated stream from the list of streams to be serviced by a
796  * Panda sub-thread.
797  */
798 void MilesAudioManager::
799 stop_service_stream(HSTREAM stream) {
800  MutexHolder holder(_streams_lock);
801  Streams::iterator si = find(_streams.begin(), _streams.end(), stream);
802  if (si != _streams.end()) {
803  _streams.erase(si);
804  }
805 }
806 
807 
808 /**
809  * Assumes the lock is already held.
810  */
811 void MilesAudioManager::
812 most_recently_used(const string &path) {
813  audio_debug("MilesAudioManager::most_recently_used(path=\""
814  <<path<<"\")");
815  LRU::iterator i=find(_lru.begin(), _lru.end(), &path);
816  if (i != _lru.end()) {
817  _lru.erase(i);
818  }
819  // At this point, path should not exist in the _lru:
820  assert(find(_lru.begin(), _lru.end(), &path) == _lru.end());
821  _lru.push_back(&path);
822  nassertv(do_is_valid());
823 }
824 
825 /**
826  * Assumes the lock is already held.
827  */
828 void MilesAudioManager::
829 uncache_a_sound() {
830  audio_debug("MilesAudioManager::uncache_a_sound()");
831  nassertv(do_is_valid());
832  // uncache least recently used:
833  assert(_lru.size()>0);
834  LRU::reference path=_lru.front();
835  SoundMap::iterator i = _sounds.find(*path);
836  assert(i != _sounds.end());
837  _lru.pop_front();
838 
839  if (i != _sounds.end()) {
840  audio_debug(" uncaching \""<<i->first<<"\"");
841  _sounds.erase(i);
842  }
843  nassertv(do_is_valid());
844 }
845 
846 /**
847  * Inform the manager that a sound is about to play.
848  */
849 void MilesAudioManager::
850 starting_sound(MilesAudioSound *audio) {
851  LightReMutexHolder holder(_lock);
852  if (_concurrent_sound_limit) {
853  do_reduce_sounds_playing_to(_concurrent_sound_limit);
854  }
855  _sounds_playing.insert(audio);
856 }
857 
858 /**
859  * Inform the manager that a sound is finished or someone called stop on the
860  * sound (this should not be called if a sound is only paused).
861  */
862 void MilesAudioManager::
863 stopping_sound(MilesAudioSound *audio) {
864  LightReMutexHolder holder(_lock);
865  _sounds_playing.erase(audio);
866  if (_hasMidiSounds && _sounds_playing.size() == 0) {
867  GlobalMilesManager::get_global_ptr()->force_midi_reset();
868  }
869 }
870 
871 /**
872  * Reads a sound file and allocates a SoundData pointer for it. Returns NULL
873  * if the sound file cannot be loaded.
874  *
875  * Assumes the lock is already held.
876  */
877 PT(MilesAudioManager::SoundData) MilesAudioManager::
878 load(const Filename &file_name) {
879  PT(SoundData) sd = new SoundData;
880 
882  PT(VirtualFile) file = vfs->get_file(file_name);
883  if (file == nullptr) {
884  milesAudio_cat.warning()
885  << "No such file: " << file_name << "\n";
886  return nullptr;
887  }
888 
889  if (file->get_file_size() == 0) {
890  milesAudio_cat.warning()
891  << "File " << file_name << " is empty\n";
892  return nullptr;
893  }
894 
895  sd->_basename = file_name.get_basename();
896 
897  string extension = sd->_basename.get_extension();
898  if (extension == "pz" || extension == "gz") {
899  extension = Filename(sd->_basename.get_basename_wo_extension()).get_extension();
900  }
901 
902  bool is_midi_file = (downcase(extension) == "mid");
903 
904  if ((miles_audio_preload_threshold == -1 || file->get_file_size() < (std::streamsize)miles_audio_preload_threshold) ||
905  is_midi_file) {
906  // If the file is sufficiently small, we'll preload it into memory. MIDI
907  // files cannot be streamed, so we always preload them, regardless of
908  // size.
909 
910  if (!file->read_file(sd->_raw_data, true)) {
911  milesAudio_cat.warning()
912  << "Unable to read " << file_name << "\n";
913  return nullptr;
914  }
915 
916  sd->_file_type =
917  AIL_file_type(&sd->_raw_data[0], sd->_raw_data.size());
918 
919  if (sd->_file_type == AILFILETYPE_MIDI) {
920  // A standard MIDI file. We have to convert this to XMIDI for Miles.
921  void *xmi;
922  U32 xmi_size;
923  if (AIL_MIDI_to_XMI(&sd->_raw_data[0], sd->_raw_data.size(),
924  &xmi, &xmi_size, 0)) {
925  audio_debug("converted " << sd->_basename << " from standard MIDI ("
926  << sd->_raw_data.size() << " bytes) to XMIDI ("
927  << xmi_size << " bytes)");
928 
929  // Copy the data to our own buffer and free the Miles-allocated data.
930  sd->_raw_data.clear();
931  sd->_raw_data.insert(sd->_raw_data.end(),
932  (unsigned char *)xmi, (unsigned char *)xmi + xmi_size);
933  AIL_mem_free_lock(xmi);
934  sd->_file_type = AILFILETYPE_XMIDI;
935  } else {
936  milesAudio_cat.warning()
937  << "Could not convert " << sd->_basename << " to XMIDI.\n";
938  }
939  }
940 
941  bool expand_to_wav = false;
942 
943  if (sd->_file_type != AILFILETYPE_MPEG_L3_AUDIO) {
944  audio_debug(sd->_basename << " is not an mp3 file.");
945  } else if ((int)sd->_raw_data.size() >= miles_audio_expand_mp3_threshold) {
946  audio_debug(sd->_basename << " is too large to expand in-memory.");
947  } else {
948  audio_debug(sd->_basename << " will be expanded in-memory.");
949  expand_to_wav = true;
950  }
951 
952  if (expand_to_wav) {
953  // Now convert the file to WAV format in-memory. This is useful to work
954  // around seek and length problems associated with variable bit-rate MP3
955  // encoding.
956  void *wav_data;
957  U32 wav_data_size;
958  if (AIL_decompress_ASI(&sd->_raw_data[0], sd->_raw_data.size(),
959  sd->_basename.c_str(), &wav_data, &wav_data_size,
960  nullptr)) {
961  audio_debug("expanded " << sd->_basename << " from " << sd->_raw_data.size()
962  << " bytes to " << wav_data_size << " bytes.");
963 
964  if (wav_data_size != 0) {
965  // Now copy the memory into our own buffers, and free the Miles-
966  // allocated memory.
967  sd->_raw_data.clear();
968  sd->_raw_data.insert(sd->_raw_data.end(),
969  (unsigned char *)wav_data, (unsigned char *)wav_data + wav_data_size);
970  sd->_file_type = AILFILETYPE_PCM_WAV;
971  sd->_basename.set_extension("wav");
972  }
973  AIL_mem_free_lock(wav_data);
974 
975  } else {
976  audio_debug("unable to expand " << sd->_basename);
977  }
978  }
979 
980  } else {
981  // If the file is large, we'll stream it from disk instead of preloading
982  // it. This means we don't need to load any data at this point.
983  }
984 
985  return sd;
986 }
987 
988 /**
989  * Called to service the streaming audio channels currently playing on the
990  * audio manager.
991  */
992 void MilesAudioManager::
993 thread_main(volatile bool &keep_running) {
994  MutexHolder holder(_streams_lock);
995 
996  while (keep_running) {
997  if (_streams.empty()) {
998  // If there are no streams to service, block on the condition variable.
999  _streams_cvar.wait();
1000  } else {
1001  do_service_streams();
1002  }
1003 
1004  // Now yield to be polite to the main application.
1005  _streams_lock.release();
1007  _streams_lock.acquire();
1008  }
1009 }
1010 
1011 /**
1012  * Internal function to service all the streams. Assumes _streams_lock is
1013  * already held.
1014  */
1015 void MilesAudioManager::
1016 do_service_streams() {
1017  size_t i = 0;
1018  while (i < _streams.size()) {
1019  HSTREAM stream = _streams[i];
1020 
1021  // We must release the lock while we are servicing stream i.
1022  _streams_lock.release();
1023  AIL_service_stream(stream, 0);
1025  _streams_lock.acquire();
1026 
1027  ++i;
1028  }
1029 }
1030 
1031 /**
1032  *
1033  */
1034 MilesAudioManager::StreamThread::
1035 StreamThread(MilesAudioManager *mgr) :
1036  Thread("StreamThread", "StreamThread"),
1037  _mgr(mgr)
1038 {
1039  _keep_running = true;
1040 }
1041 
1042 /**
1043  *
1044  */
1045 void MilesAudioManager::StreamThread::
1046 thread_main() {
1047  _mgr->thread_main(_keep_running);
1048 }
1049 
1050 /**
1051  *
1052  */
1053 MilesAudioManager::SoundData::
1054 SoundData() :
1055  _raw_data(MilesAudioManager::get_class_type()),
1056  _has_length(false),
1057  _length(0.0f)
1058 {
1059 }
1060 
1061 /**
1062  *
1063  */
1064 MilesAudioManager::SoundData::
1065 ~SoundData() {
1066 }
1067 
1068 /**
1069  *
1070  */
1071 PN_stdfloat MilesAudioManager::SoundData::
1072 get_length() {
1073  if (!_has_length) {
1074  // Time to determine the length of the file.
1075 
1076  if (_raw_data.empty()) {
1077  _length = 0.0f;
1078  _has_length = true;
1079 
1080  } else if (_file_type == AILFILETYPE_MPEG_L3_AUDIO) {
1081  // If it's an mp3 file, we have to calculate its length by walking
1082  // through all of its frames.
1083  audio_debug("Computing length of mp3 file " << _basename);
1084 
1085  MP3_INFO info;
1086  AIL_inspect_MP3(&info, &_raw_data[0], _raw_data.size());
1087  _length = 0.0f;
1088  while (AIL_enumerate_MP3_frames(&info)) {
1089  _length += info.data_size * 8.0f / info.bit_rate;
1090  }
1091  _has_length = true;
1092 
1093  } else if (_file_type == AILFILETYPE_PCM_WAV ||
1094  _file_type == AILFILETYPE_ADPCM_WAV ||
1095  _file_type == AILFILETYPE_OTHER_ASI_WAV) {
1096  audio_debug("Getting length of wav file " << _basename);
1097 
1098  AILSOUNDINFO info;
1099  if (AIL_WAV_info(&_raw_data[0], &info)) {
1100  _length = (PN_stdfloat)info.samples / (PN_stdfloat)info.rate;
1101  audio_debug(info.samples << " samples at " << info.rate
1102  << "; length is " << _length << " seconds.");
1103  _has_length = true;
1104  }
1105  }
1106  }
1107 
1108  nassertr(_has_length, 0.0f);
1109  return _length;
1110 }
1111 
1112 /**
1113  * Records the sample length, as determined externally.
1114  */
1115 void MilesAudioManager::SoundData::
1116 set_length(PN_stdfloat length) {
1117  _length = length;
1118  _has_length = true;
1119 }
1120 
1121 #endif //]
MutexHolder
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
config_putil.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LightReMutexHolder
Similar to MutexHolder, but for a light reentrant mutex.
Definition: lightReMutexHolder.h:25
VirtualFileSystem::get_file
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
Definition: virtualFileSystem.cxx:515
Thread::consider_yield
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:212
nullAudioSound.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
downcase
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
Definition: string_utils.cxx:71
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
NullAudioSound
Definition: nullAudioSound.h:23
Thread::is_threading_supported
is_threading_supported
Returns true if threading support has been compiled in and enabled, or false if no threading is avail...
Definition: thread.h:112
milesAudioSample.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
globalMilesManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
milesAudioStream.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_audio.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MovieAudio
A MovieAudio is actually any source that provides a sequence of audio samples.
Definition: movieAudio.h:44
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:741
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
mutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
milesAudioSound.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
AudioSound
Definition: audioSound.h:25
VirtualFile
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
AudioManager
Definition: audioManager.h:28
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition: virtualFileSystem.cxx:640
config_express.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Thread
A thread; that is, a lightweight process.
Definition: thread.h:46
Filename::get_extension
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
Filename::get_basename
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
Thread::force_yield
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:201
milesAudioManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
lightReMutexHolder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.