diff options
Diffstat (limited to 'indra/llaudio')
-rwxr-xr-x[-rw-r--r--] | indra/llaudio/llaudiodecodemgr.cpp | 346 | ||||
-rw-r--r-- | indra/llaudio/llaudiodecodemgr.h | 15 | ||||
-rw-r--r-- | indra/llaudio/llaudioengine.cpp | 114 | ||||
-rwxr-xr-x[-rw-r--r--] | indra/llaudio/llaudioengine.h | 75 | ||||
-rw-r--r-- | indra/llaudio/llaudioengine_fmodstudio.cpp | 18 | ||||
-rw-r--r-- | indra/llaudio/llaudioengine_fmodstudio.h | 2 | ||||
-rw-r--r-- | indra/llaudio/llaudioengine_openal.cpp | 6 | ||||
-rw-r--r-- | indra/llaudio/llaudioengine_openal.h | 2 | ||||
-rw-r--r-- | indra/llaudio/llstreamingaudio_fmodstudio.cpp | 33 |
9 files changed, 366 insertions, 245 deletions
diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index ff0aa6e76e..38a6b41afe 100644..100755 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -35,6 +35,8 @@ #include "llendianswizzle.h" #include "llassetstorage.h" #include "llrefcount.h" +#include "threadpool.h" +#include "workqueue.h" #include "llvorbisencode.h" @@ -45,15 +47,13 @@ extern LLAudioEngine *gAudiop; -LLAudioDecodeMgr *gAudioDecodeMgrp = NULL; - static const S32 WAV_HEADER_SIZE = 44; ////////////////////////////////////////////////////////////////////////////// -class LLVorbisDecodeState : public LLRefCount +class LLVorbisDecodeState : public LLThreadSafeRefCount { public: class WriteResponder : public LLLFSThread::Responder @@ -532,146 +532,254 @@ void LLVorbisDecodeState::flushBadFile() class LLAudioDecodeMgr::Impl { - friend class LLAudioDecodeMgr; -public: - Impl() {}; - ~Impl() {}; + friend class LLAudioDecodeMgr; + Impl(); + public: - void processQueue(const F32 num_secs = 0.005); + void processQueue(); -protected: - std::deque<LLUUID> mDecodeQueue; - LLPointer<LLVorbisDecodeState> mCurrentDecodep; + void startMoreDecodes(); + void enqueueFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState>& decode_state); + void checkDecodesFinished(); + + protected: + std::deque<LLUUID> mDecodeQueue; + std::map<LLUUID, LLPointer<LLVorbisDecodeState>> mDecodes; }; +LLAudioDecodeMgr::Impl::Impl() +{ +} + +// Returns the in-progress decode_state, which may be an empty LLPointer if +// there was an error and there is no more work to be done. +LLPointer<LLVorbisDecodeState> beginDecodingAndWritingAudio(const LLUUID &decode_id); -void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs) +// Return true if finished +bool tryFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState> decode_state); + +void LLAudioDecodeMgr::Impl::processQueue() { - LLUUID uuid; + // First, check if any audio from in-progress decodes are ready to play. If + // so, mark them ready for playback (or errored, in case of error). + checkDecodesFinished(); - LLTimer decode_timer; + // Second, start as many decodes from the queue as permitted + startMoreDecodes(); +} - BOOL done = FALSE; - while (!done) - { - if (mCurrentDecodep) - { - BOOL res; +void LLAudioDecodeMgr::Impl::startMoreDecodes() +{ + llassert_always(gAudiop); + + LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop"); + // *NOTE: main_queue->postTo casts this refcounted smart pointer to a weak + // pointer + LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General"); + const LL::ThreadPool::ptr_t general_thread_pool = LL::ThreadPool::getInstance("General"); + llassert_always(main_queue); + llassert_always(general_queue); + llassert_always(general_thread_pool); + // Set max decodes to double the thread count of the general work queue. + // This ensures the general work queue is full, but prevents theoretical + // buildup of buffers in memory due to disk writes once the + // LLVorbisDecodeState leaves the worker thread (see + // LLLFSThread::sLocal->write). This is probably as fast as we can get it + // without modifying/removing LLVorbisDecodeState, at which point we should + // consider decoding the audio during the asset download process. + // -Cosmic,2022-05-11 + const size_t max_decodes = general_thread_pool->getWidth() * 2; + + while (!mDecodeQueue.empty() && mDecodes.size() < max_decodes) + { + const LLUUID decode_id = mDecodeQueue.front(); + mDecodeQueue.pop_front(); + + // Don't decode the same file twice + if (mDecodes.find(decode_id) != mDecodes.end()) + { + continue; + } + if (gAudiop->hasDecodedFile(decode_id)) + { + continue; + } + + // Kick off a decode + mDecodes[decode_id] = LLPointer<LLVorbisDecodeState>(NULL); + try + { + main_queue->postTo( + general_queue, + [decode_id]() // Work done on general queue + { + LLPointer<LLVorbisDecodeState> decode_state = beginDecodingAndWritingAudio(decode_id); + + if (!decode_state) + { + // Audio decode has errored + return decode_state; + } + + // Disk write of decoded audio is now in progress off-thread + return decode_state; + }, + [decode_id, this](LLPointer<LLVorbisDecodeState> decode_state) // Callback to main thread + mutable { + if (!gAudiop) + { + // There is no LLAudioEngine anymore. This might happen if + // an audio decode is enqueued just before shutdown. + return; + } + + // At this point, we can be certain that the pointer to "this" + // is valid because the lifetime of "this" is dependent upon + // the lifetime of gAudiop. + + enqueueFinishAudio(decode_id, decode_state); + }); + } + catch (const LLThreadSafeQueueInterrupt&) + { + // Shutdown + // Consider making processQueue() do a cleanup instead + // of starting more decodes + LL_WARNS() << "Tried to start decoding on shutdown" << LL_ENDL; + } + } +} - // Decode in a loop until we're done or have run out of time. - while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs)) - { - // decodeSection does all of the work above - } +LLPointer<LLVorbisDecodeState> beginDecodingAndWritingAudio(const LLUUID &decode_id) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; + + LL_DEBUGS() << "Decoding " << decode_id << " from audio queue!" << LL_ENDL; + + std::string d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, decode_id.asString()) + ".dsf"; + LLPointer<LLVorbisDecodeState> decode_state = new LLVorbisDecodeState(decode_id, d_path); + + if (!decode_state->initDecode()) + { + return NULL; + } + + // Decode in a loop until we're done + while (!decode_state->decodeSection()) + { + // decodeSection does all of the work above + } + + if (!decode_state->isDone()) + { + // Decode stopped early, or something bad happened to the file + // during decoding. + LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data or decode has been canceled, aborting decode" << LL_ENDL; + decode_state->flushBadFile(); + return NULL; + } + + if (!decode_state->isValid()) + { + // We had an error when decoding, abort. + LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data, aborting decode" << LL_ENDL; + decode_state->flushBadFile(); + return NULL; + } + + // Kick off the writing of the decoded audio to the disk cache. + // The receiving thread can then cheaply call finishDecode() again to check + // if writing has finished. Someone has to hold on to the refcounted + // decode_state to prevent it from getting destroyed during write. + decode_state->finishDecode(); + + return decode_state; +} - if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid()) - { - // We had an error when decoding, abort. - LL_WARNS("AudioEngine") << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << LL_ENDL; - mCurrentDecodep->flushBadFile(); - - if (gAudiop) - { - LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID()); - adp->setHasValidData(false); - adp->setHasCompletedDecode(true); - } - - mCurrentDecodep = NULL; - done = TRUE; - } +void LLAudioDecodeMgr::Impl::enqueueFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState>& decode_state) +{ + // Assumed fast + if (tryFinishAudio(decode_id, decode_state)) + { + // Done early! + auto decode_iter = mDecodes.find(decode_id); + llassert(decode_iter != mDecodes.end()); + mDecodes.erase(decode_iter); + return; + } + + // Not done yet... enqueue it + mDecodes[decode_id] = decode_state; +} - if (!res) - { - // We've used up out time slice, bail... - done = TRUE; - } - else if (mCurrentDecodep) - { - if (gAudiop && mCurrentDecodep->finishDecode()) - { - // We finished! - LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID()); - if (!adp) - { - LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << mCurrentDecodep->getUUID() << LL_ENDL; - } - else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone()) - { - adp->setHasCompletedDecode(true); - adp->setHasDecodedData(true); - adp->setHasValidData(true); - - // At this point, we could see if anyone needs this sound immediately, but - // I'm not sure that there's a reason to - we need to poll all of the playing - // sounds anyway. - //LL_INFOS("AudioEngine") << "Finished the vorbis decode, now what?" << LL_ENDL; - } - else - { - adp->setHasCompletedDecode(true); - LL_INFOS("AudioEngine") << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << LL_ENDL; - } - mCurrentDecodep = NULL; - } - done = TRUE; // done for now - } - } +void LLAudioDecodeMgr::Impl::checkDecodesFinished() +{ + auto decode_iter = mDecodes.begin(); + while (decode_iter != mDecodes.end()) + { + const LLUUID& decode_id = decode_iter->first; + const LLPointer<LLVorbisDecodeState>& decode_state = decode_iter->second; + if (tryFinishAudio(decode_id, decode_state)) + { + decode_iter = mDecodes.erase(decode_iter); + } + else + { + ++decode_iter; + } + } +} - if (!done) - { - if (mDecodeQueue.empty()) - { - // Nothing else on the queue. - done = TRUE; - } - else - { - LLUUID uuid; - uuid = mDecodeQueue.front(); - mDecodeQueue.pop_front(); - if (!gAudiop || gAudiop->hasDecodedFile(uuid)) - { - // This file has already been decoded, don't decode it again. - continue; - } - - LL_DEBUGS() << "Decoding " << uuid << " from audio queue!" << LL_ENDL; - - std::string uuid_str; - std::string d_path; - - LLTimer timer; - timer.reset(); - - uuid.toString(uuid_str); - d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf"; - - mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path); - if (!mCurrentDecodep->initDecode()) - { - mCurrentDecodep = NULL; - } - } - } - } +bool tryFinishAudio(const LLUUID &decode_id, LLPointer<LLVorbisDecodeState> decode_state) +{ + // decode_state is a file write in progress unless finished is true + bool finished = decode_state && decode_state->finishDecode(); + if (!finished) + { + return false; + } + + llassert_always(gAudiop); + + LLAudioData *adp = gAudiop->getAudioData(decode_id); + if (!adp) + { + LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << decode_id << LL_ENDL; + return true; + } + + bool valid = decode_state && decode_state->isValid(); + // Mark current decode finished regardless of success or failure + adp->setHasCompletedDecode(true); + // Flip flags for decoded data + adp->setHasDecodeFailed(!valid); + adp->setHasDecodedData(valid); + // When finished decoding, there will also be a decoded wav file cached on + // disk with the .dsf extension + if (valid) + { + adp->setHasWAVLoadFailed(false); + } + + return true; } ////////////////////////////////////////////////////////////////////////////// LLAudioDecodeMgr::LLAudioDecodeMgr() { - mImpl = new Impl; + mImpl = new Impl(); } LLAudioDecodeMgr::~LLAudioDecodeMgr() { - delete mImpl; + delete mImpl; + mImpl = nullptr; } -void LLAudioDecodeMgr::processQueue(const F32 num_secs) +void LLAudioDecodeMgr::processQueue() { - mImpl->processQueue(num_secs); + mImpl->processQueue(); } BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) @@ -687,7 +795,7 @@ BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) { // Just put it on the decode queue. LL_DEBUGS("AudioEngine") << "addDecodeRequest for " << uuid << " has local asset file already" << LL_ENDL; - mImpl->mDecodeQueue.push_back(uuid); + mImpl->mDecodeQueue.push_back(uuid); return TRUE; } diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h index ceaff3f2d8..4c17b46156 100644 --- a/indra/llaudio/llaudiodecodemgr.h +++ b/indra/llaudio/llaudiodecodemgr.h @@ -32,24 +32,23 @@ #include "llassettype.h" #include "llframetimer.h" +#include "llsingleton.h" +template<class T> class LLPointer; class LLVorbisDecodeState; -class LLAudioDecodeMgr +class LLAudioDecodeMgr : public LLSingleton<LLAudioDecodeMgr> { + LLSINGLETON(LLAudioDecodeMgr); + ~LLAudioDecodeMgr(); public: - LLAudioDecodeMgr(); - ~LLAudioDecodeMgr(); - - void processQueue(const F32 num_secs = 0.005); + void processQueue(); BOOL addDecodeRequest(const LLUUID &uuid); void addAudioRequest(const LLUUID &uuid); protected: class Impl; - Impl* mImpl; + Impl* mImpl; }; -extern LLAudioDecodeMgr *gAudioDecodeMgrp; - #endif diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index e0ebbb76bd..008e1827c5 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -83,18 +83,10 @@ void LLAudioEngine::setDefaults() mLastStatus = 0; - mNumChannels = 0; mEnableWind = false; - S32 i; - for (i = 0; i < MAX_CHANNELS; i++) - { - mChannels[i] = NULL; - } - for (i = 0; i < MAX_BUFFERS; i++) - { - mBuffers[i] = NULL; - } + mChannels.fill(nullptr); + mBuffers.fill(nullptr); mMasterGain = 1.f; // Setting mInternalGain to an out of range value fixes the issue reported in STORM-830. @@ -111,18 +103,14 @@ void LLAudioEngine::setDefaults() } -bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine::init(void* userdata, const std::string &app_title) { setDefaults(); - mNumChannels = num_channels; mUserData = userdata; allocateListener(); - // Initialize the decode manager - gAudioDecodeMgrp = new LLAudioDecodeMgr; - LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << LL_ENDL; return true; @@ -131,10 +119,6 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::stri void LLAudioEngine::shutdown() { - // Clean up decode manager - delete gAudioDecodeMgrp; - gAudioDecodeMgrp = NULL; - // Clean up wind source cleanupWind(); @@ -156,14 +140,14 @@ void LLAudioEngine::shutdown() // Clean up channels S32 i; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { delete mChannels[i]; mChannels[i] = NULL; } // Clean up buffers - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { delete mBuffers[i]; mBuffers[i] = NULL; @@ -229,7 +213,7 @@ std::string LLAudioEngine::getInternetStreamURL() void LLAudioEngine::updateChannels() { S32 i; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (mChannels[i]) { @@ -240,20 +224,14 @@ void LLAudioEngine::updateChannels() } } -static const F32 default_max_decode_time = .002f; // 2 ms -void LLAudioEngine::idle(F32 max_decode_time) +void LLAudioEngine::idle() { - if (max_decode_time <= 0.f) - { - max_decode_time = default_max_decode_time; - } - // "Update" all of our audio sources, clean up dead ones. // Primarily does position updating, cleanup of unused audio sources. // Also does regeneration of the current priority of each audio source. S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -276,7 +254,7 @@ void LLAudioEngine::idle(F32 max_decode_time) { // The source is done playing, clean it up. delete sourcep; - mAllSources.erase(iter++); + iter = mAllSources.erase(iter); continue; } @@ -473,7 +451,7 @@ void LLAudioEngine::idle(F32 max_decode_time) commitDeferredChanges(); // Flush unused buffers that are stale enough - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -489,7 +467,7 @@ void LLAudioEngine::idle(F32 max_decode_time) // Clear all of the looped flags for the channels - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (mChannels[i]) { @@ -498,7 +476,7 @@ void LLAudioEngine::idle(F32 max_decode_time) } // Decode audio files - gAudioDecodeMgrp->processQueue(max_decode_time); + LLAudioDecodeMgr::getInstance()->processQueue(); // Call this every frame, just in case we somehow // missed picking it up in all the places that can add @@ -532,7 +510,7 @@ bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uu { if (audio_uuid.notNull()) { - gAudioDecodeMgrp->addDecodeRequest(audio_uuid); + LLAudioDecodeMgr::getInstance()->addDecodeRequest(audio_uuid); } } else @@ -561,7 +539,7 @@ void LLAudioEngine::enableWind(bool enable) LLAudioBuffer * LLAudioEngine::getFreeBuffer() { S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (!mBuffers[i]) { @@ -574,7 +552,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() // Grab the oldest unused buffer F32 max_age = -1.f; S32 buffer_id = -1; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -605,7 +583,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) { S32 i; - for (i = 0; i < mNumChannels; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -633,7 +611,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) F32 min_priority = 10000.f; LLAudioChannel *min_channelp = NULL; - for (i = 0; i < mNumChannels; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { LLAudioChannel *channelp = mChannels[i]; LLAudioSource *sourcep = channelp->getSource(); @@ -660,7 +638,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp) { S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i] == bufferp) { @@ -678,7 +656,7 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid) getAudioData(uuid); // We don't care about the return value, this is just to make sure // that we have an entry, which will mean that the audio engine knows about this - if (gAudioDecodeMgrp->addDecodeRequest(uuid)) + if (LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid)) { // This means that we do have a local copy, and we're working on decoding it. return true; @@ -827,7 +805,8 @@ void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_i addAudioSource(asp); if (pos_global.isExactlyZero()) { - asp->setAmbient(true); + // For sound preview and UI + asp->setForcedPriority(true); } else { @@ -953,6 +932,7 @@ LLAudioSource * LLAudioEngine::findAudioSource(const LLUUID &source_id) LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; data_map::iterator iter; iter = mAllData.find(audio_uuid); if (iter == mAllData.end()) @@ -1039,7 +1019,7 @@ void LLAudioEngine::startNextTransfer() // Check all channels for currently playing sounds. F32 max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1067,7 +1047,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1078,7 +1058,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.isNull()) { max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1103,7 +1083,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1115,7 +1095,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.isNull()) { max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1143,7 +1123,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1171,7 +1151,7 @@ void LLAudioEngine::startNextTransfer() } adp = asp->getCurrentData(); - if (adp && !adp->hasLocalData() && adp->hasValidData()) + if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1179,7 +1159,7 @@ void LLAudioEngine::startNextTransfer() } adp = asp->getQueuedData(); - if (adp && !adp->hasLocalData() && adp->hasValidData()) + if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1194,7 +1174,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1235,7 +1215,7 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v LLAudioData *adp = gAudiop->getAudioData(uuid); if (adp) { // Make sure everything is cleared - adp->setHasValidData(false); + adp->setHasDecodeFailed(true); adp->setHasLocalData(false); adp->setHasDecodedData(false); adp->setHasCompletedDecode(true); @@ -1252,9 +1232,9 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v else { // LL_INFOS() << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL; - adp->setHasValidData(true); + adp->setHasDecodeFailed(false); adp->setHasLocalData(true); - gAudioDecodeMgrp->addDecodeRequest(uuid); + LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid); } } gAudiop->mCurrentTransfer = LLUUID::null; @@ -1273,7 +1253,7 @@ LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 mPriority(0.f), mGain(gain), mSourceMuted(false), - mAmbient(false), + mForcedPriority(false), mLoop(false), mSyncMaster(false), mSyncSlave(false), @@ -1324,11 +1304,15 @@ void LLAudioSource::update() { // Hack - try and load the sound. Will do this as a callback // on decode later. - if (adp->load() && adp->getBuffer()) + if (adp->getBuffer()) { play(adp->getID()); } - else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done + else if (adp->hasDecodedData() && !adp->hasWAVLoadFailed()) + { + adp->load(); + } + else if (adp->hasCompletedDecode() && adp->hasDecodeFailed()) // Only mark corrupted after decode is done { LL_WARNS() << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL; mCorrupted = true ; @@ -1339,7 +1323,7 @@ void LLAudioSource::update() void LLAudioSource::updatePriority() { - if (isAmbient()) + if (isForcedPriority()) { mPriority = 1.f; } @@ -1624,12 +1608,12 @@ bool LLAudioSource::hasPendingPreloads() const { LLAudioData *adp = iter->second; // note: a bad UUID will forever be !hasDecodedData() - // but also !hasValidData(), hence the check for hasValidData() + // but also hasDecodeFailed(), hence the check for hasDecodeFailed() if (!adp) { continue; } - if (!adp->hasDecodedData() && adp->hasValidData()) + if (!adp->hasDecodedData() && !adp->hasDecodeFailed()) { // This source is still waiting for a preload return true; @@ -1786,7 +1770,8 @@ LLAudioData::LLAudioData(const LLUUID &uuid) : mHasLocalData(false), mHasDecodedData(false), mHasCompletedDecode(false), - mHasValidData(true) + mHasDecodeFailed(false), + mHasWAVLoadFailed(false) { if (uuid.isNull()) { @@ -1821,12 +1806,14 @@ bool LLAudioData::load() { // We already have this sound in a buffer, don't do anything. LL_INFOS() << "Already have a buffer for this sound, don't bother loading!" << LL_ENDL; + mHasWAVLoadFailed = false; return true; } if (!gAudiop) { LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; + mHasWAVLoadFailed = true; return false; } @@ -1835,6 +1822,8 @@ bool LLAudioData::load() { // No free buffers, abort. LL_INFOS() << "Not able to allocate a new audio buffer, aborting." << LL_ENDL; + // *TODO: Mark this failure differently so the audio engine could retry loading this buffer in the future + mHasWAVLoadFailed = true; return true; } @@ -1843,7 +1832,8 @@ bool LLAudioData::load() mID.toString(uuid_str); wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf"; - if (!mBufferp->loadWAV(wav_path)) + mHasWAVLoadFailed = !mBufferp->loadWAV(wav_path); + if (mHasWAVLoadFailed) { // Hrm. Right now, let's unset the buffer, since it's empty. gAudiop->cleanupBuffer(mBufferp); diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index b5fd4c27a1..0fe8b3d756 100644..100755 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -47,8 +47,8 @@ const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f; const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f; const F32 DEFAULT_MIN_DISTANCE = 2.0f; -#define MAX_CHANNELS 30 -#define MAX_BUFFERS 40 // Some extra for preloading, maybe? +#define LL_MAX_AUDIO_CHANNELS 30 +#define LL_MAX_AUDIO_BUFFERS 40 // Some extra for preloading, maybe? class LLAudioSource; class LLAudioData; @@ -88,7 +88,7 @@ public: virtual ~LLAudioEngine(); // initialization/startup/shutdown - virtual bool init(const S32 num_channels, void *userdata, const std::string &app_title); + virtual bool init(void *userdata, const std::string &app_title); virtual std::string getDriverName(bool verbose) = 0; virtual void shutdown(); @@ -96,7 +96,7 @@ public: //virtual void processQueue(const LLUUID &sound_guid); virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at); virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0; - virtual void idle(F32 max_decode_time = 0.f); + virtual void idle(); virtual void updateChannels(); // @@ -209,7 +209,6 @@ protected: S32 mLastStatus; - S32 mNumChannels; bool mEnableWind; LLUUID mCurrentTransfer; // Audio file currently being transferred by the system @@ -224,11 +223,11 @@ protected: source_map mAllSources; data_map mAllData; - LLAudioChannel *mChannels[MAX_CHANNELS]; + std::array<LLAudioChannel*, LL_MAX_AUDIO_CHANNELS> mChannels; // Buffers needs to change into a different data structure, as the number of buffers // that we have active should be limited by RAM usage, not count. - LLAudioBuffer *mBuffers[MAX_BUFFERS]; + std::array<LLAudioBuffer*, LL_MAX_AUDIO_BUFFERS> mBuffers; F32 mMasterGain; F32 mInternalGain; // Actual gain set; either mMasterGain or 0 when mMuted is true. @@ -266,8 +265,8 @@ public: void addAudioData(LLAudioData *adp, bool set_current = TRUE); - void setAmbient(const bool ambient) { mAmbient = ambient; } - bool isAmbient() const { return mAmbient; } + void setForcedPriority(const bool ambient) { mForcedPriority = ambient; } + bool isForcedPriority() const { return mForcedPriority; } void setLoop(const bool loop) { mLoop = loop; } bool isLoop() const { return mLoop; } @@ -326,7 +325,7 @@ protected: F32 mPriority; F32 mGain; bool mSourceMuted; - bool mAmbient; + bool mForcedPriority; // ignore mute, set high priority, researved for sound preview and UI bool mLoop; bool mSyncMaster; bool mSyncSlave; @@ -360,32 +359,36 @@ protected: class LLAudioData { -public: - LLAudioData(const LLUUID &uuid); - bool load(); - - LLUUID getID() const { return mID; } - LLAudioBuffer *getBuffer() const { return mBufferp; } - - bool hasLocalData() const { return mHasLocalData; } - bool hasDecodedData() const { return mHasDecodedData; } - bool hasCompletedDecode() const { return mHasCompletedDecode; } - bool hasValidData() const { return mHasValidData; } - - void setHasLocalData(const bool hld) { mHasLocalData = hld; } - void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; } - void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; } - void setHasValidData(const bool hvd) { mHasValidData = hvd; } - - friend class LLAudioEngine; // Severe laziness, bad. - -protected: - LLUUID mID; - LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. - bool mHasLocalData; // Set true if the sound asset file is available locally - bool mHasDecodedData; // Set true if the sound file has been decoded - bool mHasCompletedDecode; // Set true when the sound is decoded - bool mHasValidData; // Set false if decoding failed, meaning the sound asset is bad + public: + LLAudioData(const LLUUID &uuid); + bool load(); + + LLUUID getID() const { return mID; } + LLAudioBuffer *getBuffer() const { return mBufferp; } + + bool hasLocalData() const { return mHasLocalData; } + bool hasDecodedData() const { return mHasDecodedData; } + bool hasCompletedDecode() const { return mHasCompletedDecode; } + bool hasDecodeFailed() const { return mHasDecodeFailed; } + bool hasWAVLoadFailed() const { return mHasWAVLoadFailed; } + + void setHasLocalData(const bool hld) { mHasLocalData = hld; } + void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; } + void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; } + void setHasDecodeFailed(const bool hdf) { mHasDecodeFailed = hdf; } + void setHasWAVLoadFailed(const bool hwlf) { mHasWAVLoadFailed = hwlf; } + + friend class LLAudioEngine; // Severe laziness, bad. + + protected: + LLUUID mID; + LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. + bool mHasLocalData; // Set true if the encoded sound asset file is available locally + bool mHasDecodedData; // Set true if the decoded sound file is available on disk + bool mHasCompletedDecode; // Set true when the sound is decoded + bool mHasDecodeFailed; // Set true if decoding failed, meaning the sound asset is bad + bool mHasWAVLoadFailed; // Set true if loading the decoded WAV file failed, meaning the sound asset should be decoded instead if + // possible }; diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index b0c87b0208..ba743020b5 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -74,7 +74,7 @@ static inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) return true; } -bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title) { U32 version; FMOD_RESULT result; @@ -86,7 +86,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons return false; //will call LLAudioEngine_FMODSTUDIO::allocateListener, which needs a valid mSystem pointer. - LLAudioEngine::init(num_channels, userdata, app_title); + LLAudioEngine::init(userdata, app_title); result = mSystem->getVersion(&version); Check_FMOD_Error(result, "FMOD::System::getVersion"); @@ -98,7 +98,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons } // In this case, all sounds, PLUS wind and stream will be software. - result = mSystem->setSoftwareChannels(num_channels + 2); + result = mSystem->setSoftwareChannels(LL_MAX_AUDIO_CHANNELS + 2); Check_FMOD_Error(result, "FMOD::System::setSoftwareChannels"); FMOD_ADVANCEDSETTINGS settings; @@ -127,7 +127,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL; if (mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK && - (result = mSystem->init(num_channels + 2, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK) + (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK) { LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL; audio_ok = true; @@ -149,7 +149,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; if (mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK && - (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) + (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; audio_ok = true; @@ -190,7 +190,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons // initialize the FMOD engine // number of channel in this case looks to be identiacal to number of max simultaneously // playing objects and we can set practically any number - result = mSystem->init(num_channels + 2, fmod_flags, 0); + result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0); if (Check_FMOD_Error(result, "Error initializing FMOD Studio with default settins, retrying with other format")) { result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0/*- ignore*/); @@ -198,7 +198,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { return false; } - result = mSystem->init(num_channels + 2, fmod_flags, 0); + result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0); } if (Check_FMOD_Error(result, "Error initializing FMOD Studio")) { @@ -519,9 +519,9 @@ void LLAudioChannelFMODSTUDIO::update3DPosition() return; } - if (mCurrentSourcep->isAmbient()) + if (mCurrentSourcep->isForcedPriority()) { - // Ambient sound, don't need to do any positional updates. + // Prioritized UI and preview sounds don't need to do any positional updates. set3DMode(false); } else diff --git a/indra/llaudio/llaudioengine_fmodstudio.h b/indra/llaudio/llaudioengine_fmodstudio.h index f2361df1b6..d3d6d69685 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.h +++ b/indra/llaudio/llaudioengine_fmodstudio.h @@ -51,7 +51,7 @@ public: virtual ~LLAudioEngine_FMODSTUDIO(); // initialization/startup/shutdown - virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title); + virtual bool init(void *user_data, const std::string &app_title); virtual std::string getDriverName(bool verbose); virtual void allocateListener(); diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index 3bdd0302ee..0a79614424 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -52,10 +52,10 @@ LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL() } // virtual -bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine_OpenAL::init(void* userdata, const std::string &app_title) { mWindGen = NULL; - LLAudioEngine::init(num_channels, userdata, app_title); + LLAudioEngine::init(userdata, app_title); if(!alutInit(NULL, NULL)) { @@ -297,7 +297,7 @@ void LLAudioChannelOpenAL::update3DPosition() { return; } - if (mCurrentSourcep->isAmbient()) + if (mCurrentSourcep->isForcedPriority()) { alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0); alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0); diff --git a/indra/llaudio/llaudioengine_openal.h b/indra/llaudio/llaudioengine_openal.h index 366f9259e3..a3cab97cd2 100644 --- a/indra/llaudio/llaudioengine_openal.h +++ b/indra/llaudio/llaudioengine_openal.h @@ -40,7 +40,7 @@ class LLAudioEngine_OpenAL : public LLAudioEngine LLAudioEngine_OpenAL(); virtual ~LLAudioEngine_OpenAL(); - virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title); + virtual bool init(void *user_data, const std::string &app_title); virtual std::string getDriverName(bool verbose); virtual void allocateListener(); diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp index 1ad29a3f59..85577992a6 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -70,7 +70,11 @@ mRetryCount(0) // Must be larger than the usual Second Life frame stutter time. const U32 buffer_seconds = 10; //sec const U32 estimated_bitrate = 128; //kbit/sec - mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + FMOD_RESULT result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << "setStreamBufferSize error: " << FMOD_ErrorString(result) << LL_ENDL; + } // Here's where we set the size of the network buffer and some buffering // parameters. In this case we want a network buffer of 16k, we want it @@ -134,7 +138,7 @@ void LLStreamingAudio_FMODSTUDIO::killDeadStreams() { LL_INFOS("FMOD") << "Closed dead stream" << LL_ENDL; delete streamp; - mDeadStreams.erase(iter++); + iter = mDeadStreams.erase(iter); } else { @@ -404,7 +408,11 @@ FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream() if (mStreamChannel) return mStreamChannel; //Already have a channel for this stream. - mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel); + FMOD_RESULT result = mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << FMOD_ErrorString(result) << LL_ENDL; + } return mStreamChannel; } @@ -445,16 +453,29 @@ bool LLAudioStreamManagerFMODSTUDIO::stopStream() FMOD_OPENSTATE LLAudioStreamManagerFMODSTUDIO::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy) { FMOD_OPENSTATE state; - mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << FMOD_ErrorString(result) << LL_ENDL; + } return state; } void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) { - mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES); + FMOD_RESULT result = mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << "setStreamBufferSize error: " << FMOD_ErrorString(result) << LL_ENDL; + return; + } FMOD_ADVANCEDSETTINGS settings; memset(&settings, 0, sizeof(settings)); settings.cbSize = sizeof(settings); settings.defaultDecodeBufferSize = decodebuffertime;//ms - mSystem->setAdvancedSettings(&settings); + result = mSystem->setAdvancedSettings(&settings); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << "setAdvancedSettings error: " << FMOD_ErrorString(result) << LL_ENDL; + } } |