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 //]
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
A hierarchy of directories and files that appears to be one continuous file system,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
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
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void force_yield()
Suspends the current thread for the rest of the current epoch.
Definition: thread.I:201
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
A thread; that is, a lightweight process.
Definition: thread.h:46
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
A MovieAudio is actually any source that provides a sequence of audio samples.
Definition: movieAudio.h:44
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.