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