|
@@ -1,1121 +0,0 @@
|
|
|
-/**
|
|
|
|
|
- * PANDA 3D SOFTWARE
|
|
|
|
|
- * Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
|
|
|
- *
|
|
|
|
|
- * All use of this software is subject to the terms of the revised BSD
|
|
|
|
|
- * license. You should have received a copy of this license along
|
|
|
|
|
- * with this source code in a file named "LICENSE."
|
|
|
|
|
- *
|
|
|
|
|
- * @file milesAudioManager.cxx
|
|
|
|
|
- * @author skyler
|
|
|
|
|
- * @date 2001-06-06
|
|
|
|
|
- * Prior system by: cary
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
-#include "milesAudioManager.h"
|
|
|
|
|
-
|
|
|
|
|
-#ifdef HAVE_RAD_MSS //[
|
|
|
|
|
-
|
|
|
|
|
-#include "milesAudioSound.h"
|
|
|
|
|
-#include "milesAudioSample.h"
|
|
|
|
|
-#include "milesAudioStream.h"
|
|
|
|
|
-#include "globalMilesManager.h"
|
|
|
|
|
-#include "config_audio.h"
|
|
|
|
|
-#include "config_putil.h"
|
|
|
|
|
-#include "config_express.h"
|
|
|
|
|
-#include "virtualFileSystem.h"
|
|
|
|
|
-#include "nullAudioSound.h"
|
|
|
|
|
-#include "string_utils.h"
|
|
|
|
|
-#include "mutexHolder.h"
|
|
|
|
|
-#include "lightReMutexHolder.h"
|
|
|
|
|
-
|
|
|
|
|
-#include <algorithm>
|
|
|
|
|
-
|
|
|
|
|
-using std::string;
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-TypeHandle MilesAudioManager::_type_handle;
|
|
|
|
|
-
|
|
|
|
|
-AudioManager *Create_MilesAudioManager() {
|
|
|
|
|
- audio_debug("Create_MilesAudioManager()");
|
|
|
|
|
- return new MilesAudioManager();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Create an audio manager. This may open the Miles sound system if there
|
|
|
|
|
- * were no other MilesAudioManager instances. Subsequent managers may use the
|
|
|
|
|
- * same Miles resources.
|
|
|
|
|
- */
|
|
|
|
|
-MilesAudioManager::
|
|
|
|
|
-MilesAudioManager() :
|
|
|
|
|
- _lock("MilesAudioManager::_lock"),
|
|
|
|
|
- _streams_lock("MilesAudioManager::_streams_lock"),
|
|
|
|
|
- _streams_cvar(_streams_lock)
|
|
|
|
|
-{
|
|
|
|
|
- audio_debug("MilesAudioManager::MilesAudioManager(), this = "
|
|
|
|
|
- << (void *)this);
|
|
|
|
|
- GlobalMilesManager::get_global_ptr()->add_manager(this);
|
|
|
|
|
- audio_debug(" audio_active="<<audio_active);
|
|
|
|
|
- audio_debug(" audio_volume="<<audio_volume);
|
|
|
|
|
- _cleanup_required = true;
|
|
|
|
|
- _active = audio_active;
|
|
|
|
|
- _volume = audio_volume;
|
|
|
|
|
- _play_rate = 1.0f;
|
|
|
|
|
- _cache_limit = audio_cache_limit;
|
|
|
|
|
- _concurrent_sound_limit = 0;
|
|
|
|
|
- _is_valid = true;
|
|
|
|
|
- _hasMidiSounds = false;
|
|
|
|
|
- _sounds_finished = false;
|
|
|
|
|
-
|
|
|
|
|
- // We used to hang a call to a force-shutdown function on atexit(), so that
|
|
|
|
|
- // any running sounds (particularly MIDI sounds) would be silenced on exit,
|
|
|
|
|
- // especially a sudden exit triggered by a Python exception. But that
|
|
|
|
|
- // causes problems because Miles itself also hangs a force-shutdown function
|
|
|
|
|
- // on atexit(), and you can't call AIL_cleanup() twice--that results in a
|
|
|
|
|
- // crash.
|
|
|
|
|
-
|
|
|
|
|
- // Nowadays, we provide the AudioManager::shutdown() method instead, which
|
|
|
|
|
- // allows the app to force all sounds to stop cleanly before we get to the
|
|
|
|
|
- // atexit() stack. In Python, we guarantee that this method will be called
|
|
|
|
|
- // in the sys.exitfunc chain.
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Clean up this audio manager and possibly release the Miles resources that
|
|
|
|
|
- * are reserved by the application (the later happens if this is the last
|
|
|
|
|
- * active manager).
|
|
|
|
|
- */
|
|
|
|
|
-MilesAudioManager::
|
|
|
|
|
-~MilesAudioManager() {
|
|
|
|
|
- audio_debug("MilesAudioManager::~MilesAudioManager(), this = "
|
|
|
|
|
- << (void *)this);
|
|
|
|
|
- cleanup();
|
|
|
|
|
- GlobalMilesManager::get_global_ptr()->remove_manager(this);
|
|
|
|
|
-
|
|
|
|
|
- audio_debug("MilesAudioManager::~MilesAudioManager() finished");
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Call this at exit time to shut down the audio system. This will invalidate
|
|
|
|
|
- * all currently-active AudioManagers and AudioSounds in the system. If you
|
|
|
|
|
- * change your mind and want to play sounds again, you will have to recreate
|
|
|
|
|
- * all of these objects.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-shutdown() {
|
|
|
|
|
- audio_debug("shutdown() started");
|
|
|
|
|
- GlobalMilesManager::get_global_ptr()->cleanup();
|
|
|
|
|
- audio_debug("shutdown() finished");
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * This is mostly for debugging, but it it could be used to detect errors in a
|
|
|
|
|
- * release build if you don't mind the cpu cost.
|
|
|
|
|
- */
|
|
|
|
|
-bool MilesAudioManager::
|
|
|
|
|
-is_valid() {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- return do_is_valid();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-PT(AudioSound) MilesAudioManager::
|
|
|
|
|
-get_sound(const Filename &file_name, bool, int) {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- audio_debug("MilesAudioManager::get_sound(file_name=\""<<file_name<<"\")");
|
|
|
|
|
-
|
|
|
|
|
- if (!do_is_valid()) {
|
|
|
|
|
- audio_debug("invalid MilesAudioManager returning NullSound");
|
|
|
|
|
- return get_null_sound();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- Filename path = file_name;
|
|
|
|
|
-
|
|
|
|
|
- VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
|
|
|
- vfs->resolve_filename(path, get_model_path());
|
|
|
|
|
- audio_debug("Reading "<<path);
|
|
|
|
|
- audio_debug(" resolved file_name is '"<<path<<"'");
|
|
|
|
|
-
|
|
|
|
|
- PT(SoundData) sd;
|
|
|
|
|
- // Get the sound, either from the cache or from a loader:
|
|
|
|
|
- SoundMap::const_iterator si=_sounds.find(path);
|
|
|
|
|
- if (si != _sounds.end()) {
|
|
|
|
|
- // ...found the sound in the cache.
|
|
|
|
|
- sd = (*si).second;
|
|
|
|
|
- audio_debug(" sound found in pool 0x" << (void*)sd);
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- // ...the sound was not found in the cachepool.
|
|
|
|
|
- sd = load(path);
|
|
|
|
|
- if (sd != nullptr) {
|
|
|
|
|
- while (_sounds.size() >= (unsigned int)_cache_limit) {
|
|
|
|
|
- uncache_a_sound();
|
|
|
|
|
- }
|
|
|
|
|
- // Put it in the pool: The following is roughly like: _sounds[path] =
|
|
|
|
|
- // sd; But, it gives us an iterator into the map.
|
|
|
|
|
- std::pair<SoundMap::const_iterator, bool> ib
|
|
|
|
|
- = _sounds.insert(SoundMap::value_type(path, sd));
|
|
|
|
|
- if (!ib.second) {
|
|
|
|
|
- // The insert failed.
|
|
|
|
|
- audio_debug(" failed map insert of "<<path);
|
|
|
|
|
- nassertr(do_is_valid(), nullptr);
|
|
|
|
|
- return get_null_sound();
|
|
|
|
|
- }
|
|
|
|
|
- // Set si, so that we can get a reference to the path for the
|
|
|
|
|
- // MilesAudioSound.
|
|
|
|
|
- si=ib.first;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // Create an AudioSound from the sound:
|
|
|
|
|
- PT(AudioSound) audioSound;
|
|
|
|
|
-
|
|
|
|
|
- if (sd != nullptr) {
|
|
|
|
|
- most_recently_used((*si).first);
|
|
|
|
|
- if (sd->_file_type == AILFILETYPE_MIDI ||
|
|
|
|
|
- sd->_file_type == AILFILETYPE_XMIDI ||
|
|
|
|
|
- sd->_file_type == AILFILETYPE_XMIDI_DLS ||
|
|
|
|
|
- sd->_file_type == AILFILETYPE_XMIDI_MLS) {
|
|
|
|
|
- // MIDI file.
|
|
|
|
|
- audioSound = new MilesAudioSequence(this, sd, file_name);
|
|
|
|
|
-
|
|
|
|
|
- } else if (!sd->_raw_data.empty()) {
|
|
|
|
|
- // WAV or MP3 file preloaded into memory.
|
|
|
|
|
- audioSound = new MilesAudioSample(this, sd, file_name);
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- // WAV or MP3 file streamed from disk.
|
|
|
|
|
- audioSound = new MilesAudioStream(this, file_name, path);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- audioSound->set_active(_active);
|
|
|
|
|
-
|
|
|
|
|
- bool inserted = _sounds_on_loan.insert((MilesAudioSound *)audioSound.p()).second;
|
|
|
|
|
- nassertr(inserted, audioSound);
|
|
|
|
|
-
|
|
|
|
|
- _hasMidiSounds |= (file_name.find(".mid")!=string::npos);
|
|
|
|
|
- } else {
|
|
|
|
|
- // Couldn't load the file; just return a NullAudioSound.
|
|
|
|
|
- audioSound = new NullAudioSound;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- audio_debug(" returning 0x" << (void*)audioSound);
|
|
|
|
|
- nassertr(do_is_valid(), nullptr);
|
|
|
|
|
- return audioSound;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-PT(AudioSound) MilesAudioManager::
|
|
|
|
|
-get_sound(MovieAudio *sound, bool, int) {
|
|
|
|
|
- nassert_raise("Miles audio manager does not support MovieAudio sources.");
|
|
|
|
|
- return nullptr;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-uncache_sound(const Filename &file_name) {
|
|
|
|
|
- audio_debug("MilesAudioManager::uncache_sound(file_name=\""
|
|
|
|
|
- <<file_name<<"\")");
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- nassertv(do_is_valid());
|
|
|
|
|
- Filename path = file_name;
|
|
|
|
|
-
|
|
|
|
|
- VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
|
|
|
- vfs->resolve_filename(path, get_model_path());
|
|
|
|
|
-
|
|
|
|
|
- audio_debug(" path=\""<<path<<"\"");
|
|
|
|
|
- SoundMap::iterator i = _sounds.find(path);
|
|
|
|
|
- if (i != _sounds.end()) {
|
|
|
|
|
- nassertv(_lru.size() > 0);
|
|
|
|
|
- LRU::iterator lru_i = find(_lru.begin(), _lru.end(), &(i->first));
|
|
|
|
|
- nassertv(lru_i != _lru.end());
|
|
|
|
|
- _lru.erase(lru_i);
|
|
|
|
|
- _sounds.erase(i);
|
|
|
|
|
- }
|
|
|
|
|
- nassertv(do_is_valid());
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Clear out the sound cache.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-clear_cache() {
|
|
|
|
|
- audio_debug("MilesAudioManager::clear_cache()");
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- do_clear_cache();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Set the number of sounds that the cache can hold.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-set_cache_limit(unsigned int count) {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
-
|
|
|
|
|
- audio_debug("MilesAudioManager::set_cache_limit(count="<<count<<")");
|
|
|
|
|
- nassertv(do_is_valid());
|
|
|
|
|
- while (_lru.size() > count) {
|
|
|
|
|
- uncache_a_sound();
|
|
|
|
|
- }
|
|
|
|
|
- _cache_limit=count;
|
|
|
|
|
- nassertv(do_is_valid());
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-unsigned int MilesAudioManager::
|
|
|
|
|
-get_cache_limit() const {
|
|
|
|
|
- return _cache_limit;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * set the overall volume
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-set_volume(PN_stdfloat volume) {
|
|
|
|
|
- audio_debug("MilesAudioManager::set_volume(volume="<<volume<<")");
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- if (_volume != volume) {
|
|
|
|
|
- _volume = volume;
|
|
|
|
|
- // Tell our AudioSounds to adjust:
|
|
|
|
|
- AudioSet::iterator i = _sounds_on_loan.begin();
|
|
|
|
|
- for (; i!=_sounds_on_loan.end(); ++i) {
|
|
|
|
|
- (*i)->set_volume((*i)->get_volume());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * get the overall volume
|
|
|
|
|
- */
|
|
|
|
|
-PN_stdfloat MilesAudioManager::
|
|
|
|
|
-get_volume() const {
|
|
|
|
|
- return _volume;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * set the overall play rate
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-set_play_rate(PN_stdfloat play_rate) {
|
|
|
|
|
- audio_debug("MilesAudioManager::set_play_rate(play_rate="<<play_rate<<")");
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- if (_play_rate != play_rate) {
|
|
|
|
|
- _play_rate = play_rate;
|
|
|
|
|
- // Tell our AudioSounds to adjust:
|
|
|
|
|
- AudioSet::iterator i = _sounds_on_loan.begin();
|
|
|
|
|
- for (; i != _sounds_on_loan.end(); ++i) {
|
|
|
|
|
- (*i)->set_play_rate((*i)->get_play_rate());
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * get the overall speed/pitch/play rate
|
|
|
|
|
- */
|
|
|
|
|
-PN_stdfloat MilesAudioManager::
|
|
|
|
|
-get_play_rate() const {
|
|
|
|
|
- return _play_rate;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * turn on/off
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-set_active(bool active) {
|
|
|
|
|
- audio_debug("MilesAudioManager::set_active(flag="<<active<<")");
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- if (_active != active) {
|
|
|
|
|
- _active=active;
|
|
|
|
|
- // Tell our AudioSounds to adjust:
|
|
|
|
|
- AudioSet::iterator i = _sounds_on_loan.begin();
|
|
|
|
|
- for (; i != _sounds_on_loan.end(); ++i) {
|
|
|
|
|
- (*i)->set_active(_active);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if ((!_active) && _hasMidiSounds) {
|
|
|
|
|
- GlobalMilesManager::get_global_ptr()->force_midi_reset();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-bool MilesAudioManager::
|
|
|
|
|
-get_active() const {
|
|
|
|
|
- return _active;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-set_concurrent_sound_limit(unsigned int limit) {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- _concurrent_sound_limit = limit;
|
|
|
|
|
- do_reduce_sounds_playing_to(_concurrent_sound_limit);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-unsigned int MilesAudioManager::
|
|
|
|
|
-get_concurrent_sound_limit() const {
|
|
|
|
|
- return _concurrent_sound_limit;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-reduce_sounds_playing_to(unsigned int count) {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- do_reduce_sounds_playing_to(count);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Stop playback on all sounds managed by this manager.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-stop_all_sounds() {
|
|
|
|
|
- audio_debug("MilesAudioManager::stop_all_sounds()");
|
|
|
|
|
- reduce_sounds_playing_to(0);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Set spatial attributes of the listener for 3D sounds. Note that Y and Z
|
|
|
|
|
- * are switched to translate into Miles's coordinate system.
|
|
|
|
|
- */
|
|
|
|
|
-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) {
|
|
|
|
|
- audio_debug("MilesAudioManager::audio_3d_set_listener_attributes()");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
- AIL_set_listener_3D_position(mgr->_digital_driver, px, pz, py);
|
|
|
|
|
- AIL_set_listener_3D_velocity_vector(mgr->_digital_driver, vx, vz, vy);
|
|
|
|
|
- AIL_set_listener_3D_orientation(mgr->_digital_driver, fx, fz, fy, ux, uz, uy);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Get spatial attributes of the listener for 3D sounds. Note that Y and Z
|
|
|
|
|
- * are switched to translate from Miles's coordinate system.
|
|
|
|
|
- */
|
|
|
|
|
-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) {
|
|
|
|
|
- audio_debug("MilesAudioManager::audio_3d_get_listener_attributes()");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
- float lpx, lpy, lpz, lvx, lvy, lvz, lfx, lfy, lfz, lux, luy, luz;
|
|
|
|
|
- AIL_listener_3D_position(mgr->_digital_driver, &lpx, &lpz, &lpy);
|
|
|
|
|
- AIL_listener_3D_velocity(mgr->_digital_driver, &lvx, &lvz, &lvy);
|
|
|
|
|
- AIL_listener_3D_orientation(mgr->_digital_driver, &lfx, &lfz, &lfy, &lux, &luz, &luy);
|
|
|
|
|
-
|
|
|
|
|
- *px = lpx;
|
|
|
|
|
- *py = lpy;
|
|
|
|
|
- *pz = lpz;
|
|
|
|
|
- *vx = lvx;
|
|
|
|
|
- *vy = lvy;
|
|
|
|
|
- *vz = lvz;
|
|
|
|
|
- *fx = lfx;
|
|
|
|
|
- *fy = lfy;
|
|
|
|
|
- *fz = lfz;
|
|
|
|
|
- *ux = lux;
|
|
|
|
|
- *uy = luy;
|
|
|
|
|
- *uz = luz;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Set factor to allow user to easily work in a different scale. 1.0
|
|
|
|
|
- * represents meters.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::audio_3d_set_distance_factor(PN_stdfloat factor) {
|
|
|
|
|
- audio_debug("MilesAudioManager::audio_3d_set_distance_factor( factor= " << factor << ")");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
- AIL_set_3D_distance_factor(mgr->_digital_driver, factor);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Get factor controlling working units.
|
|
|
|
|
- */
|
|
|
|
|
-PN_stdfloat MilesAudioManager::audio_3d_get_distance_factor() const {
|
|
|
|
|
- audio_debug("MilesAudioManager::audio_3d_get_distance_factor()");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
- return AIL_3D_distance_factor(mgr->_digital_driver);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Exaggerates or diminishes the Doppler effect. Defaults to 1.0
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::audio_3d_set_doppler_factor(PN_stdfloat factor) {
|
|
|
|
|
- audio_debug("MilesAudioManager::audio_3d_set_doppler_factor(factor="<<factor<<")");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
- AIL_set_3D_doppler_factor(mgr->_digital_driver, factor);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Get the factor controlling the Doppler effect.
|
|
|
|
|
- */
|
|
|
|
|
-PN_stdfloat MilesAudioManager::audio_3d_get_doppler_factor() const {
|
|
|
|
|
- audio_debug("MilesAudioManager::audio_3d_get_doppler_factor()");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
- return AIL_3D_doppler_factor(mgr->_digital_driver);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Control the effect distance has on audability. Defaults to 1.0
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::audio_3d_set_drop_off_factor(PN_stdfloat factor) {
|
|
|
|
|
- audio_debug("MilesAudioManager::audio_3d_set_drop_off_factor("<<factor<<")");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
- AIL_set_3D_rolloff_factor(mgr->_digital_driver, factor);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Get the factor controlling how quickly sound falls off with distance.
|
|
|
|
|
- */
|
|
|
|
|
-PN_stdfloat MilesAudioManager::audio_3d_get_drop_off_factor() const {
|
|
|
|
|
- audio_debug("MilesAudioManager::audio_3d_get_drop_off_factor()");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
- return AIL_3D_rolloff_factor(mgr->_digital_driver);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Works similarly to MilesAudioSound::set_speaker_levels, but specifies the
|
|
|
|
|
- * 3D positions of the speakers in space.
|
|
|
|
|
- *
|
|
|
|
|
- * Once a NULL value is found for a speaker position, no more speaker
|
|
|
|
|
- * positions will be used.
|
|
|
|
|
- *
|
|
|
|
|
- * Note that Y and Z are switched to translate from Miles's coordinate system.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-set_speaker_configuration(LVecBase3 *speaker1, LVecBase3 *speaker2, LVecBase3 *speaker3, LVecBase3 *speaker4, LVecBase3 *speaker5, LVecBase3 *speaker6, LVecBase3 *speaker7, LVecBase3 *speaker8, LVecBase3 *speaker9) {
|
|
|
|
|
- audio_debug("MilesAudioManager::set_speaker_configuration()");
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
-
|
|
|
|
|
- MSSVECTOR3D speakers[9];
|
|
|
|
|
-
|
|
|
|
|
- if(speaker1 != nullptr) {
|
|
|
|
|
- speakers[0].x = speaker1->get_x();
|
|
|
|
|
- speakers[0].y = speaker1->get_z();
|
|
|
|
|
- speakers[0].z = speaker1->get_y();
|
|
|
|
|
- }
|
|
|
|
|
- if(speaker2 != nullptr) {
|
|
|
|
|
- speakers[1].x = speaker2->get_x();
|
|
|
|
|
- speakers[1].y = speaker2->get_z();
|
|
|
|
|
- speakers[1].z = speaker2->get_y();
|
|
|
|
|
- }
|
|
|
|
|
- if(speaker3 != nullptr) {
|
|
|
|
|
- speakers[2].x = speaker3->get_x();
|
|
|
|
|
- speakers[2].y = speaker3->get_z();
|
|
|
|
|
- speakers[2].z = speaker3->get_y();
|
|
|
|
|
- }
|
|
|
|
|
- if(speaker4 != nullptr) {
|
|
|
|
|
- speakers[3].x = speaker4->get_x();
|
|
|
|
|
- speakers[3].y = speaker4->get_z();
|
|
|
|
|
- speakers[3].z = speaker4->get_y();
|
|
|
|
|
- }
|
|
|
|
|
- if(speaker5 != nullptr) {
|
|
|
|
|
- speakers[4].x = speaker5->get_x();
|
|
|
|
|
- speakers[4].y = speaker5->get_z();
|
|
|
|
|
- speakers[4].z = speaker5->get_y();
|
|
|
|
|
- }
|
|
|
|
|
- if(speaker6 != nullptr) {
|
|
|
|
|
- speakers[5].x = speaker6->get_x();
|
|
|
|
|
- speakers[5].y = speaker6->get_z();
|
|
|
|
|
- speakers[5].z = speaker6->get_y();
|
|
|
|
|
- }
|
|
|
|
|
- if(speaker7 != nullptr) {
|
|
|
|
|
- speakers[6].x = speaker7->get_x();
|
|
|
|
|
- speakers[6].y = speaker7->get_z();
|
|
|
|
|
- speakers[6].z = speaker7->get_y();
|
|
|
|
|
- }
|
|
|
|
|
- if(speaker8 != nullptr) {
|
|
|
|
|
- speakers[7].x = speaker8->get_x();
|
|
|
|
|
- speakers[7].y = speaker8->get_z();
|
|
|
|
|
- speakers[7].z = speaker8->get_y();
|
|
|
|
|
- }
|
|
|
|
|
- if(speaker9 != nullptr) {
|
|
|
|
|
- speakers[8].x = speaker9->get_x();
|
|
|
|
|
- speakers[8].y = speaker9->get_z();
|
|
|
|
|
- speakers[8].z = speaker9->get_y();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if(speaker1 == nullptr) {
|
|
|
|
|
- audio_error("No valid speaker positions specified in MilesAudioManager::set_speaker_configuration().");
|
|
|
|
|
- } else if(speaker2 == nullptr) {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 1, 1.0);
|
|
|
|
|
- } else if(speaker3 == nullptr) {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 2, 1.0);
|
|
|
|
|
- } else if(speaker4 == nullptr) {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 3, 1.0);
|
|
|
|
|
- } else if(speaker5 == nullptr) {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 4, 1.0);
|
|
|
|
|
- } else if(speaker6 == nullptr) {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 5, 1.0);
|
|
|
|
|
- } else if(speaker7 == nullptr) {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 6, 1.0);
|
|
|
|
|
- } else if(speaker8 == nullptr) {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 7, 1.0);
|
|
|
|
|
- } else if(speaker9 == nullptr) {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 8, 1.0);
|
|
|
|
|
- } else {
|
|
|
|
|
- AIL_set_speaker_configuration(mgr->_digital_driver, speakers, 9, 1.0);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Must be called every frame. Failure to call this every frame could cause
|
|
|
|
|
- * problems for some audio managers.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-update() {
|
|
|
|
|
- {
|
|
|
|
|
- MutexHolder holder(_streams_lock);
|
|
|
|
|
- if (_stream_thread.is_null() && !_streams.empty()) {
|
|
|
|
|
- // If we don't have a sub-thread, we have to service the streams in the
|
|
|
|
|
- // main thread.
|
|
|
|
|
- do_service_streams();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (_sounds_finished) {
|
|
|
|
|
- _sounds_finished = false;
|
|
|
|
|
-
|
|
|
|
|
- // If the _sounds_finished flag was set, we should scan our list of
|
|
|
|
|
- // playing sounds and see if any of them have finished recently. We don't
|
|
|
|
|
- // do this in the finished callback, because that might have been called
|
|
|
|
|
- // in a sub-thread (and we may not have threading supported--and mutex
|
|
|
|
|
- // protection--compiled in).
|
|
|
|
|
-
|
|
|
|
|
- SoundsPlaying::iterator si = _sounds_playing.begin();
|
|
|
|
|
- while (si != _sounds_playing.end()) {
|
|
|
|
|
- MilesAudioSound *sound = (*si);
|
|
|
|
|
- ++si;
|
|
|
|
|
-
|
|
|
|
|
- if (sound->status() == AudioSound::READY) {
|
|
|
|
|
- sound->stop();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-release_sound(MilesAudioSound *audioSound) {
|
|
|
|
|
- audio_debug("MilesAudioManager::release_sound(audioSound=\""
|
|
|
|
|
- <<audioSound->get_name()<<"\"), this = " << (void *)this);
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- AudioSet::iterator ai = _sounds_on_loan.find(audioSound);
|
|
|
|
|
- if (ai != _sounds_on_loan.end()) {
|
|
|
|
|
- _sounds_on_loan.erase(ai);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- audio_debug("MilesAudioManager::release_sound() finished");
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Shuts down the audio manager and releases any resources associated with it.
|
|
|
|
|
- * Also cleans up all AudioSounds created via the manager.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-cleanup() {
|
|
|
|
|
- audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this
|
|
|
|
|
- << ", _cleanup_required = " << _cleanup_required);
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- if (!_cleanup_required) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Be sure to cleanup associated sounds before cleaning up the manager:
|
|
|
|
|
- AudioSet orig_sounds;
|
|
|
|
|
- orig_sounds.swap(_sounds_on_loan);
|
|
|
|
|
- AudioSet::iterator ai;
|
|
|
|
|
- for (ai = orig_sounds.begin(); ai != orig_sounds.end(); ++ai) {
|
|
|
|
|
- (*ai)->cleanup();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- do_clear_cache();
|
|
|
|
|
-
|
|
|
|
|
- // Now stop the thread, if it has been started.
|
|
|
|
|
- if (!_stream_thread.is_null()) {
|
|
|
|
|
- milesAudio_cat.info()
|
|
|
|
|
- << "Stopping audio streaming thread.\n";
|
|
|
|
|
- PT(StreamThread) old_thread;
|
|
|
|
|
- {
|
|
|
|
|
- MutexHolder holder(_streams_lock);
|
|
|
|
|
- nassertv(!_stream_thread.is_null());
|
|
|
|
|
- _stream_thread->_keep_running = false;
|
|
|
|
|
- _streams_cvar.notify();
|
|
|
|
|
- old_thread = _stream_thread;
|
|
|
|
|
- _stream_thread.clear();
|
|
|
|
|
- }
|
|
|
|
|
- old_thread->join();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _cleanup_required = false;
|
|
|
|
|
- audio_debug("MilesAudioManager::cleanup() finished");
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-output(std::ostream &out) const {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- out << get_type() << ": " << _sounds_playing.size()
|
|
|
|
|
- << " / " << _sounds_on_loan.size() << " sounds playing / total";
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-write(std::ostream &out) const {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
-
|
|
|
|
|
- out << (*this) << "\n";
|
|
|
|
|
- AudioSet::const_iterator ai;
|
|
|
|
|
- for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) {
|
|
|
|
|
- MilesAudioSound *sound = (*ai);
|
|
|
|
|
- out << " " << *sound << "\n";
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- size_t total_preload = 0;
|
|
|
|
|
- size_t num_preloaded = 0;
|
|
|
|
|
- SoundMap::const_iterator si;
|
|
|
|
|
- for (si = _sounds.begin(); si != _sounds.end(); ++si) {
|
|
|
|
|
- if (!(*si).second->_raw_data.empty()) {
|
|
|
|
|
- ++num_preloaded;
|
|
|
|
|
- total_preload += (*si).second->_raw_data.size();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- out << num_preloaded << " of " << _sounds.size() << " sounds preloaded, size used is " << (total_preload + 1023) / 1024 << "K\n";
|
|
|
|
|
-
|
|
|
|
|
- {
|
|
|
|
|
- MutexHolder holder(_streams_lock);
|
|
|
|
|
- out << _streams.size() << " streams opened.\n";
|
|
|
|
|
- if (!_stream_thread.is_null()) {
|
|
|
|
|
- out << "(Audio streaming thread has been started.)\n";
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
|
|
|
|
|
-
|
|
|
|
|
- int num_samples = mgr->get_num_samples();
|
|
|
|
|
- out << num_samples << " sample handles allocated globally.\n";
|
|
|
|
|
-
|
|
|
|
|
- int num_sequences = mgr->get_num_sequences();
|
|
|
|
|
- out << num_sequences << " sequence handles allocated globally.\n";
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Implementation of is_valid(). Assumes the lock is already held.
|
|
|
|
|
- */
|
|
|
|
|
-bool MilesAudioManager::
|
|
|
|
|
-do_is_valid() {
|
|
|
|
|
- bool check = true;
|
|
|
|
|
- if (_sounds.size() != _lru.size()) {
|
|
|
|
|
- audio_debug("-- Error _sounds.size() != _lru.size() --");
|
|
|
|
|
- check = false;
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- LRU::const_iterator i = _lru.begin();
|
|
|
|
|
- for (; i != _lru.end(); ++i) {
|
|
|
|
|
- SoundMap::const_iterator smi = _sounds.find(**i);
|
|
|
|
|
- if (smi == _sounds.end()) {
|
|
|
|
|
- audio_debug("-- "<<**i<<" in _lru and not in _sounds --");
|
|
|
|
|
- check = false;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return _is_valid && check;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Assumes the lock is already held.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-do_reduce_sounds_playing_to(unsigned int count) {
|
|
|
|
|
- int limit = _sounds_playing.size() - count;
|
|
|
|
|
- while (limit-- > 0) {
|
|
|
|
|
- SoundsPlaying::iterator sound = _sounds_playing.begin();
|
|
|
|
|
- assert(sound != _sounds_playing.end());
|
|
|
|
|
- (**sound).stop();
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Assumes the lock is already held.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-do_clear_cache() {
|
|
|
|
|
- if (_is_valid) { nassertv(do_is_valid()); }
|
|
|
|
|
- _sounds.clear();
|
|
|
|
|
- _lru.clear();
|
|
|
|
|
- if (_is_valid) { nassertv(do_is_valid()); }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Adds the indicated stream to the list of streams to be serviced by a Panda
|
|
|
|
|
- * sub-thread. This is in lieu of Miles' auto-service-stream mechanism.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-start_service_stream(HSTREAM stream) {
|
|
|
|
|
- MutexHolder holder(_streams_lock);
|
|
|
|
|
- nassertv(find(_streams.begin(), _streams.end(), stream) == _streams.end());
|
|
|
|
|
- _streams.push_back(stream);
|
|
|
|
|
- _streams_cvar.notify();
|
|
|
|
|
-
|
|
|
|
|
- if (_stream_thread.is_null() && Thread::is_threading_supported()) {
|
|
|
|
|
- milesAudio_cat.info()
|
|
|
|
|
- << "Starting audio streaming thread.\n";
|
|
|
|
|
- _stream_thread = new StreamThread(this);
|
|
|
|
|
- _stream_thread->start(TP_low, true);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Removes the indicated stream from the list of streams to be serviced by a
|
|
|
|
|
- * Panda sub-thread.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-stop_service_stream(HSTREAM stream) {
|
|
|
|
|
- MutexHolder holder(_streams_lock);
|
|
|
|
|
- Streams::iterator si = find(_streams.begin(), _streams.end(), stream);
|
|
|
|
|
- if (si != _streams.end()) {
|
|
|
|
|
- _streams.erase(si);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Assumes the lock is already held.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-most_recently_used(const string &path) {
|
|
|
|
|
- audio_debug("MilesAudioManager::most_recently_used(path=\""
|
|
|
|
|
- <<path<<"\")");
|
|
|
|
|
- LRU::iterator i=find(_lru.begin(), _lru.end(), &path);
|
|
|
|
|
- if (i != _lru.end()) {
|
|
|
|
|
- _lru.erase(i);
|
|
|
|
|
- }
|
|
|
|
|
- // At this point, path should not exist in the _lru:
|
|
|
|
|
- assert(find(_lru.begin(), _lru.end(), &path) == _lru.end());
|
|
|
|
|
- _lru.push_back(&path);
|
|
|
|
|
- nassertv(do_is_valid());
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Assumes the lock is already held.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-uncache_a_sound() {
|
|
|
|
|
- audio_debug("MilesAudioManager::uncache_a_sound()");
|
|
|
|
|
- nassertv(do_is_valid());
|
|
|
|
|
- // uncache least recently used:
|
|
|
|
|
- assert(_lru.size()>0);
|
|
|
|
|
- LRU::reference path=_lru.front();
|
|
|
|
|
- SoundMap::iterator i = _sounds.find(*path);
|
|
|
|
|
- assert(i != _sounds.end());
|
|
|
|
|
- _lru.pop_front();
|
|
|
|
|
-
|
|
|
|
|
- if (i != _sounds.end()) {
|
|
|
|
|
- audio_debug(" uncaching \""<<i->first<<"\"");
|
|
|
|
|
- _sounds.erase(i);
|
|
|
|
|
- }
|
|
|
|
|
- nassertv(do_is_valid());
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Inform the manager that a sound is about to play.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-starting_sound(MilesAudioSound *audio) {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- if (_concurrent_sound_limit) {
|
|
|
|
|
- do_reduce_sounds_playing_to(_concurrent_sound_limit);
|
|
|
|
|
- }
|
|
|
|
|
- _sounds_playing.insert(audio);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Inform the manager that a sound is finished or someone called stop on the
|
|
|
|
|
- * sound (this should not be called if a sound is only paused).
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-stopping_sound(MilesAudioSound *audio) {
|
|
|
|
|
- LightReMutexHolder holder(_lock);
|
|
|
|
|
- _sounds_playing.erase(audio);
|
|
|
|
|
- if (_hasMidiSounds && _sounds_playing.size() == 0) {
|
|
|
|
|
- GlobalMilesManager::get_global_ptr()->force_midi_reset();
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Reads a sound file and allocates a SoundData pointer for it. Returns NULL
|
|
|
|
|
- * if the sound file cannot be loaded.
|
|
|
|
|
- *
|
|
|
|
|
- * Assumes the lock is already held.
|
|
|
|
|
- */
|
|
|
|
|
-PT(MilesAudioManager::SoundData) MilesAudioManager::
|
|
|
|
|
-load(const Filename &file_name) {
|
|
|
|
|
- PT(SoundData) sd = new SoundData;
|
|
|
|
|
-
|
|
|
|
|
- VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
|
|
|
|
- PT(VirtualFile) file = vfs->get_file(file_name);
|
|
|
|
|
- if (file == nullptr) {
|
|
|
|
|
- milesAudio_cat.warning()
|
|
|
|
|
- << "No such file: " << file_name << "\n";
|
|
|
|
|
- return nullptr;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (file->get_file_size() == 0) {
|
|
|
|
|
- milesAudio_cat.warning()
|
|
|
|
|
- << "File " << file_name << " is empty\n";
|
|
|
|
|
- return nullptr;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- sd->_basename = file_name.get_basename();
|
|
|
|
|
-
|
|
|
|
|
- string extension = sd->_basename.get_extension();
|
|
|
|
|
- if (extension == "pz" || extension == "gz") {
|
|
|
|
|
- extension = Filename(sd->_basename.get_basename_wo_extension()).get_extension();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- bool is_midi_file = (downcase(extension) == "mid");
|
|
|
|
|
-
|
|
|
|
|
- if ((miles_audio_preload_threshold == -1 || file->get_file_size() < (std::streamsize)miles_audio_preload_threshold) ||
|
|
|
|
|
- is_midi_file) {
|
|
|
|
|
- // If the file is sufficiently small, we'll preload it into memory. MIDI
|
|
|
|
|
- // files cannot be streamed, so we always preload them, regardless of
|
|
|
|
|
- // size.
|
|
|
|
|
-
|
|
|
|
|
- if (!file->read_file(sd->_raw_data, true)) {
|
|
|
|
|
- milesAudio_cat.warning()
|
|
|
|
|
- << "Unable to read " << file_name << "\n";
|
|
|
|
|
- return nullptr;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- sd->_file_type =
|
|
|
|
|
- AIL_file_type(&sd->_raw_data[0], sd->_raw_data.size());
|
|
|
|
|
-
|
|
|
|
|
- if (sd->_file_type == AILFILETYPE_MIDI) {
|
|
|
|
|
- // A standard MIDI file. We have to convert this to XMIDI for Miles.
|
|
|
|
|
- void *xmi;
|
|
|
|
|
- U32 xmi_size;
|
|
|
|
|
- if (AIL_MIDI_to_XMI(&sd->_raw_data[0], sd->_raw_data.size(),
|
|
|
|
|
- &xmi, &xmi_size, 0)) {
|
|
|
|
|
- audio_debug("converted " << sd->_basename << " from standard MIDI ("
|
|
|
|
|
- << sd->_raw_data.size() << " bytes) to XMIDI ("
|
|
|
|
|
- << xmi_size << " bytes)");
|
|
|
|
|
-
|
|
|
|
|
- // Copy the data to our own buffer and free the Miles-allocated data.
|
|
|
|
|
- sd->_raw_data.clear();
|
|
|
|
|
- sd->_raw_data.insert(sd->_raw_data.end(),
|
|
|
|
|
- (unsigned char *)xmi, (unsigned char *)xmi + xmi_size);
|
|
|
|
|
- AIL_mem_free_lock(xmi);
|
|
|
|
|
- sd->_file_type = AILFILETYPE_XMIDI;
|
|
|
|
|
- } else {
|
|
|
|
|
- milesAudio_cat.warning()
|
|
|
|
|
- << "Could not convert " << sd->_basename << " to XMIDI.\n";
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- bool expand_to_wav = false;
|
|
|
|
|
-
|
|
|
|
|
- if (sd->_file_type != AILFILETYPE_MPEG_L3_AUDIO) {
|
|
|
|
|
- audio_debug(sd->_basename << " is not an mp3 file.");
|
|
|
|
|
- } else if ((int)sd->_raw_data.size() >= miles_audio_expand_mp3_threshold) {
|
|
|
|
|
- audio_debug(sd->_basename << " is too large to expand in-memory.");
|
|
|
|
|
- } else {
|
|
|
|
|
- audio_debug(sd->_basename << " will be expanded in-memory.");
|
|
|
|
|
- expand_to_wav = true;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (expand_to_wav) {
|
|
|
|
|
- // Now convert the file to WAV format in-memory. This is useful to work
|
|
|
|
|
- // around seek and length problems associated with variable bit-rate MP3
|
|
|
|
|
- // encoding.
|
|
|
|
|
- void *wav_data;
|
|
|
|
|
- U32 wav_data_size;
|
|
|
|
|
- if (AIL_decompress_ASI(&sd->_raw_data[0], sd->_raw_data.size(),
|
|
|
|
|
- sd->_basename.c_str(), &wav_data, &wav_data_size,
|
|
|
|
|
- nullptr)) {
|
|
|
|
|
- audio_debug("expanded " << sd->_basename << " from " << sd->_raw_data.size()
|
|
|
|
|
- << " bytes to " << wav_data_size << " bytes.");
|
|
|
|
|
-
|
|
|
|
|
- if (wav_data_size != 0) {
|
|
|
|
|
- // Now copy the memory into our own buffers, and free the Miles-
|
|
|
|
|
- // allocated memory.
|
|
|
|
|
- sd->_raw_data.clear();
|
|
|
|
|
- sd->_raw_data.insert(sd->_raw_data.end(),
|
|
|
|
|
- (unsigned char *)wav_data, (unsigned char *)wav_data + wav_data_size);
|
|
|
|
|
- sd->_file_type = AILFILETYPE_PCM_WAV;
|
|
|
|
|
- sd->_basename.set_extension("wav");
|
|
|
|
|
- }
|
|
|
|
|
- AIL_mem_free_lock(wav_data);
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- audio_debug("unable to expand " << sd->_basename);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- } else {
|
|
|
|
|
- // If the file is large, we'll stream it from disk instead of preloading
|
|
|
|
|
- // it. This means we don't need to load any data at this point.
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return sd;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Called to service the streaming audio channels currently playing on the
|
|
|
|
|
- * audio manager.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-thread_main(volatile bool &keep_running) {
|
|
|
|
|
- MutexHolder holder(_streams_lock);
|
|
|
|
|
-
|
|
|
|
|
- while (keep_running) {
|
|
|
|
|
- if (_streams.empty()) {
|
|
|
|
|
- // If there are no streams to service, block on the condition variable.
|
|
|
|
|
- _streams_cvar.wait();
|
|
|
|
|
- } else {
|
|
|
|
|
- do_service_streams();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Now yield to be polite to the main application.
|
|
|
|
|
- _streams_lock.release();
|
|
|
|
|
- Thread::force_yield();
|
|
|
|
|
- _streams_lock.acquire();
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Internal function to service all the streams. Assumes _streams_lock is
|
|
|
|
|
- * already held.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::
|
|
|
|
|
-do_service_streams() {
|
|
|
|
|
- size_t i = 0;
|
|
|
|
|
- while (i < _streams.size()) {
|
|
|
|
|
- HSTREAM stream = _streams[i];
|
|
|
|
|
-
|
|
|
|
|
- // We must release the lock while we are servicing stream i.
|
|
|
|
|
- _streams_lock.release();
|
|
|
|
|
- AIL_service_stream(stream, 0);
|
|
|
|
|
- Thread::consider_yield();
|
|
|
|
|
- _streams_lock.acquire();
|
|
|
|
|
-
|
|
|
|
|
- ++i;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-MilesAudioManager::StreamThread::
|
|
|
|
|
-StreamThread(MilesAudioManager *mgr) :
|
|
|
|
|
- Thread("StreamThread", "StreamThread"),
|
|
|
|
|
- _mgr(mgr)
|
|
|
|
|
-{
|
|
|
|
|
- _keep_running = true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::StreamThread::
|
|
|
|
|
-thread_main() {
|
|
|
|
|
- _mgr->thread_main(_keep_running);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-MilesAudioManager::SoundData::
|
|
|
|
|
-SoundData() :
|
|
|
|
|
- _raw_data(MilesAudioManager::get_class_type()),
|
|
|
|
|
- _has_length(false),
|
|
|
|
|
- _length(0.0f)
|
|
|
|
|
-{
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-MilesAudioManager::SoundData::
|
|
|
|
|
-~SoundData() {
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- *
|
|
|
|
|
- */
|
|
|
|
|
-PN_stdfloat MilesAudioManager::SoundData::
|
|
|
|
|
-get_length() {
|
|
|
|
|
- if (!_has_length) {
|
|
|
|
|
- // Time to determine the length of the file.
|
|
|
|
|
-
|
|
|
|
|
- if (_raw_data.empty()) {
|
|
|
|
|
- _length = 0.0f;
|
|
|
|
|
- _has_length = true;
|
|
|
|
|
-
|
|
|
|
|
- } else if (_file_type == AILFILETYPE_MPEG_L3_AUDIO) {
|
|
|
|
|
- // If it's an mp3 file, we have to calculate its length by walking
|
|
|
|
|
- // through all of its frames.
|
|
|
|
|
- audio_debug("Computing length of mp3 file " << _basename);
|
|
|
|
|
-
|
|
|
|
|
- MP3_INFO info;
|
|
|
|
|
- AIL_inspect_MP3(&info, &_raw_data[0], _raw_data.size());
|
|
|
|
|
- _length = 0.0f;
|
|
|
|
|
- while (AIL_enumerate_MP3_frames(&info)) {
|
|
|
|
|
- _length += info.data_size * 8.0f / info.bit_rate;
|
|
|
|
|
- }
|
|
|
|
|
- _has_length = true;
|
|
|
|
|
-
|
|
|
|
|
- } else if (_file_type == AILFILETYPE_PCM_WAV ||
|
|
|
|
|
- _file_type == AILFILETYPE_ADPCM_WAV ||
|
|
|
|
|
- _file_type == AILFILETYPE_OTHER_ASI_WAV) {
|
|
|
|
|
- audio_debug("Getting length of wav file " << _basename);
|
|
|
|
|
-
|
|
|
|
|
- AILSOUNDINFO info;
|
|
|
|
|
- if (AIL_WAV_info(&_raw_data[0], &info)) {
|
|
|
|
|
- _length = (PN_stdfloat)info.samples / (PN_stdfloat)info.rate;
|
|
|
|
|
- audio_debug(info.samples << " samples at " << info.rate
|
|
|
|
|
- << "; length is " << _length << " seconds.");
|
|
|
|
|
- _has_length = true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- nassertr(_has_length, 0.0f);
|
|
|
|
|
- return _length;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * Records the sample length, as determined externally.
|
|
|
|
|
- */
|
|
|
|
|
-void MilesAudioManager::SoundData::
|
|
|
|
|
-set_length(PN_stdfloat length) {
|
|
|
|
|
- _length = length;
|
|
|
|
|
- _has_length = true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-#endif //]
|
|
|