diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index eea39f0c1..92a0b27e9 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -611,8 +611,10 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs) llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl; mCurrentDecodep->flushBadFile(); LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID()); - adp->setHasValidData(false); - adp->setHasCompletedDecode(true); + if(adp) + { + adp->setLoadState(LLAudioData::STATE_LOAD_ERROR); + } mCurrentDecodep = NULL; done = TRUE; } @@ -634,10 +636,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs) } else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone()) { - adp->setHasCompletedDecode(true); - adp->setHasDecodedData(true); - adp->setHasValidData(true); - + adp->setLoadState(LLAudioData::STATE_LOAD_READY); // 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. @@ -645,7 +644,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs) } else { - adp->setHasCompletedDecode(true); + adp->setLoadState(LLAudioData::STATE_LOAD_ERROR); llinfos << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << llendl; } mCurrentDecodep = NULL; @@ -688,8 +687,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs) LLAudioData *adp = gAudiop->getAudioData(uuid); if(adp) { - adp->setHasValidData(false); - adp->setHasCompletedDecode(true); + adp->setLoadState(LLAudioData::STATE_LOAD_ERROR); } mCurrentDecodep = NULL; } @@ -715,23 +713,13 @@ void LLAudioDecodeMgr::processQueue(const F32 num_secs) mImpl->processQueue(num_secs); } -BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) +bool LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) { - if (gAudiop->hasDecodedFile(uuid)) - { - // Already have a decoded version, don't need to decode it. - //llinfos << "addDecodeRequest for " << uuid << " has decoded file already" << llendl; - return TRUE; - } - - if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND)) - { - // Just put it on the decode queue. - //llinfos << "addDecodeRequest for " << uuid << " has local asset file already" << llendl; - mImpl->mDecodeQueue.push(uuid); - return TRUE; - } - - //llinfos << "addDecodeRequest for " << uuid << " no file available" << llendl; - return FALSE; + if(!uuid.notNull()) + return false; + else if (!gAssetStorage || !gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND)) + return false; + + mImpl->mDecodeQueue.push(uuid); + return true; } diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h index e42fe8a40..917511daa 100644 --- a/indra/llaudio/llaudiodecodemgr.h +++ b/indra/llaudio/llaudiodecodemgr.h @@ -44,7 +44,7 @@ public: ~LLAudioDecodeMgr(); void processQueue(const F32 num_secs = 0.005); - BOOL addDecodeRequest(const LLUUID &uuid); + bool addDecodeRequest(const LLUUID &uuid); void addAudioRequest(const LLUUID &uuid); protected: diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index c1f362d32..6aed74daf 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -113,6 +113,8 @@ void LLAudioEngine::setDefaults() for (U32 i = 0; i < LLAudioEngine::AUDIO_TYPE_COUNT; i++) mSecondaryGain[i] = 1.0f; + mCurrentTransfer = NULL; + mAllowLargeSounds = false; } @@ -354,6 +356,11 @@ void LLAudioEngine::idle(F32 max_decode_time) // Increment iter here (it is not used anymore), so we can use continue below to move on to the next source. ++iter; + if(!sourcep->isLoop() && sourcep->mPlayedOnce && (!sourcep->mChannelp || !sourcep->mChannelp->isPlaying())) + { + continue; + } + LLAudioData *adp = sourcep->getCurrentData(); //If there is no current data at all, or if it hasn't loaded, we must skip this source. if (!adp || !adp->getBuffer()) @@ -509,9 +516,8 @@ void LLAudioEngine::idle(F32 max_decode_time) // Decode audio files gAudioDecodeMgrp->processQueue(max_decode_time); - // Call this every frame, just in case we somehow - // missed picking it up in all the places that can add - // or request new data. + // Just call here every frame. It makes little sense to call elsewhere, + // as it's throttled to one active preloading loading sound at a time anyhow startNextTransfer(); updateInternetStream(); @@ -660,10 +666,7 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid) if(uuid.isNull()) return false; - gAudiop->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(getAudioData(uuid)->getLoadState() >= LLAudioData::STATE_LOAD_DECODING) { // This means that we do have a local copy, and we're working on decoding it. return true; @@ -1064,29 +1067,39 @@ bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid) } } - -bool LLAudioEngine::hasLocalFile(const LLUUID &uuid) -{ - // See if it's in the VFS. - return gVFS->getExists(uuid, LLAssetType::AT_SOUND); -} - - void LLAudioEngine::startNextTransfer() { //LL_INFOS("AudioEngine") << "LLAudioEngine::startNextTransfer()" << LL_ENDL; - if (!gAssetStorage->isUpstreamOK() || mCurrentTransfer.notNull() || getMuted()) + if (getMuted()) { - //LL_INFOS("AudioEngine") << "Transfer in progress, aborting" << LL_ENDL; return; } + else if(mCurrentTransferTimer.getElapsedTimeF32() <= .1f) + { + return; + } + else if(mCurrentTransfer && mCurrentTransfer->isInPreload()) + { + //Keep updating until it either errors out or completes. + mCurrentTransfer->updateLoadState(); + return; + } + else + { + mCurrentTransfer = NULL; + } + + //Technically, mCurrentTransfer could end up pointing to an audiodata object that's already + //being transmitted/decoded if such was spawned via needing it for playback immediately. + //This will effectively block us from choosing a lower priority audiodata object until the + //immediate ones are done, but it's not a real problem. // Get the ID for the next asset that we want to transfer. // Pick one in the following order: - LLUUID asset_id; S32 i; LLAudioSource *asp = NULL; LLAudioData *adp = NULL; + LLAudioData *cur_adp = NULL; data_map::iterator data_iter; // Check all channels for currently playing sounds. @@ -1119,15 +1132,15 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (adp->isInPreload()) { - asset_id = adp->getID(); max_pri = asp->getPriority(); + cur_adp = adp; } } // Check all channels for currently queued sounds. - if (asset_id.isNull()) + if (!cur_adp) { max_pri = -1.f; for (i = 0; i < MAX_CHANNELS; i++) @@ -1155,16 +1168,16 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (adp->isInPreload()) { - asset_id = adp->getID(); max_pri = asp->getPriority(); + cur_adp = adp; } } } // Check all live channels for other sounds (preloads). - if (asset_id.isNull()) + if (!cur_adp) { max_pri = -1.f; for (i = 0; i < MAX_CHANNELS; i++) @@ -1195,17 +1208,17 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (adp->isInPreload()) { - asset_id = adp->getID(); max_pri = asp->getPriority(); + cur_adp = adp; } } } } // Check all sources - if (asset_id.isNull()) + if (!cur_adp) { max_pri = -1.f; source_map::iterator source_iter; @@ -1223,18 +1236,18 @@ void LLAudioEngine::startNextTransfer() } adp = asp->getCurrentData(); - if (adp && !adp->hasLocalData() && adp->hasValidData()) + if (adp && adp->isInPreload()) { - asset_id = adp->getID(); max_pri = asp->getPriority(); + cur_adp = adp; continue; } adp = asp->getQueuedData(); - if (adp && !adp->hasLocalData() && adp->hasValidData()) + if (adp && adp->isInPreload()) { - asset_id = adp->getID(); max_pri = asp->getPriority(); + cur_adp = adp; continue; } @@ -1246,35 +1259,41 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (adp->isInPreload()) { - asset_id = adp->getID(); max_pri = asp->getPriority(); + cur_adp = adp; break; } } } } - if (asset_id.isNull() && !mPreloadSystemList.empty()) + if (!cur_adp) { - asset_id = mPreloadSystemList.front(); - mPreloadSystemList.pop_front(); + while(!mPreloadSystemList.empty()) + { + adp = getAudioData(mPreloadSystemList.front()); + mPreloadSystemList.pop_front(); + if(adp->isInPreload()) + { + cur_adp = adp; + break; + } + } } - else if(asset_id.notNull()) + else if(cur_adp) { - std::list::iterator it = std::find(mPreloadSystemList.begin(),mPreloadSystemList.end(),asset_id); + std::list::iterator it = std::find(mPreloadSystemList.begin(),mPreloadSystemList.end(),cur_adp->getID()); if(it != mPreloadSystemList.end()) mPreloadSystemList.erase(it); } - if (asset_id.notNull()) + if (cur_adp) { - LL_DEBUGS("AudioEngine") << "Getting asset data for: " << asset_id << LL_ENDL; - gAudiop->mCurrentTransfer = asset_id; - gAudiop->mCurrentTransferTimer.reset(); - gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND, - assetCallback, NULL); + mCurrentTransfer = cur_adp; + mCurrentTransferTimer.reset(); + mCurrentTransfer->updateLoadState(); } else { @@ -1289,22 +1308,20 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E if(!gAudiop) return; + LLAudioData *adp = gAudiop->getAudioData(uuid); + if (result_code) { LL_INFOS("AudioEngine") << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << LL_ENDL; // Need to mark data as bad to avoid constant rerequests. - LLAudioData *adp = gAudiop->getAudioData(uuid); + if (adp) - { // Make sure everything is cleared - adp->setHasValidData(false); - adp->setHasLocalData(false); - adp->setHasDecodedData(false); - adp->setHasCompletedDecode(true); + { + adp->setLoadState(LLAudioData::STATE_LOAD_ERROR); } } else { - LLAudioData *adp = gAudiop->getAudioData(uuid); if (!adp) { // Should never happen @@ -1313,13 +1330,11 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E else { // LL_INFOS("AudioEngine") << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL; - adp->setHasValidData(true); - adp->setHasLocalData(true); - gAudioDecodeMgrp->addDecodeRequest(uuid); + adp->setLoadState(LLAudioData::STATE_LOAD_REQ_DECODE); + //Immediate decode. + adp->updateLoadState(); } } - gAudiop->mCurrentTransfer = LLUUID::null; - gAudiop->startNextTransfer(); } @@ -1411,32 +1426,29 @@ void LLAudioSource::update() //Make sure this source looks like its brand new again to prevent removal. mPlayedOnce = false; mAgeTimer.reset(); - - gAudiop->startNextTransfer(); } LLAudioData *adp = getCurrentData(); if (adp && !adp->getBuffer()) { - // Update the audio buffer first - load a sound if we have it. - // Note that this could potentially cause us to waste time updating buffers - // for sounds that actually aren't playing, although this should be mitigated - // by the fact that we limit the number of buffers, and we flush buffers based - // on priority. - if (adp->hasDecodedData()) + if(adp->getLoadState() == LLAudioData::STATE_LOAD_ERROR) { - if(!adp->load() && adp->hasCompletedDecode()) - { - LL_WARNS("AudioEngine") << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL; - mCorrupted = true ; - } + LL_WARNS("AudioEngine") << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL; + mCorrupted = true ; } - else if (adp->hasLocalData() && adp->hasValidData()) + else if(adp->getLoadState() == LLAudioData::STATE_LOAD_READY) { - if (adp->getID().notNull()) - { - gAudioDecodeMgrp->addDecodeRequest(adp->getID()); - } + // Update the audio buffer first - load a sound if we have it. + // Note that this could potentially cause us to waste time updating buffers + // for sounds that actually aren't playing, although this should be mitigated + // by the fact that we limit the number of buffers, and we flush buffers based + // on priority. + adp->load(); //If it fails, just try again next update. + } + else + { + //The sound wasn't preloaded yet... so we must kick off the process. + adp->updateLoadState(); } } } @@ -1524,9 +1536,6 @@ bool LLAudioSource::play(const LLUUID &audio_uuid) mCurrentDatap = adp; - // Make sure the audio engine knows that we want to request this sound. - gAudiop->startNextTransfer(); - return true; } @@ -1536,7 +1545,6 @@ bool LLAudioSource::isDone() const static const F32 MAX_AGE = 60.f; static const F32 MAX_UNPLAYED_AGE = 15.f; static const F32 MAX_MUTED_AGE = 11.f; - if(mCorrupted) { // If we decode bad data then just kill this source entirely. @@ -1570,7 +1578,7 @@ bool LLAudioSource::isDone() const LLAudioData* adp = mCurrentDatap; //Still decoding. - if(adp && !adp->hasDecodedData() && adp->hasValidData()) + if(adp && adp->isInPreload()) return false; // We don't have a channel assigned, and it's been @@ -1599,7 +1607,6 @@ void LLAudioSource::preload(const LLUUID &audio_id) { // Add it to the preload list. mPreloadMap[audio_id] = gAudiop->getAudioData(audio_id); - gAudiop->startNextTransfer(); } } @@ -1617,7 +1624,7 @@ bool LLAudioSource::hasPendingPreloads() const { continue; } - if (!adp->hasDecodedData() && adp->hasValidData()) + if (adp->isInPreload()) { // This source is still waiting for a preload return true; @@ -1734,27 +1741,42 @@ bool LLAudioChannel::updateBuffer() LLAudioData::LLAudioData(const LLUUID &uuid) : mID(uuid), mBufferp(NULL), - mHasLocalData(false), - mHasDecodedData(false), - mHasCompletedDecode(false), - mHasValidData(true) + mLoadState(STATE_LOAD_ERROR) { if (uuid.isNull()) { // This is a null sound. return; } - - if (gAudiop && gAudiop->hasDecodedFile(uuid)) + + if(gAudiop->hasDecodedFile(getID())) + mLoadState = STATE_LOAD_READY; + else if(gAssetStorage && gAssetStorage->hasLocalAsset(getID(), LLAssetType::AT_SOUND)) + mLoadState = STATE_LOAD_REQ_DECODE; + else + mLoadState = STATE_LOAD_REQ_FETCH; +} + +void LLAudioData::updateLoadState() +{ + if(mLoadState == STATE_LOAD_REQ_DECODE && gAudioDecodeMgrp) { - // Already have a decoded version, don't need to decode it. - setHasLocalData(true); - setHasDecodedData(true); - setHasCompletedDecode(true); + if( gAudioDecodeMgrp->addDecodeRequest(getID()) ) + { + setLoadState(STATE_LOAD_DECODING); + LL_INFOS("AudioEngine") << "Decoding asset data for: " << getID() << LL_ENDL; + } + else + { + setLoadState(STATE_LOAD_ERROR); + } } - else if (gAssetStorage && gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND)) + else if(mLoadState == STATE_LOAD_REQ_FETCH && gAssetStorage && gAssetStorage->isUpstreamOK()) { - setHasLocalData(true); + LL_INFOS("AudioEngine") << "Fetching asset data for: " << getID() << LL_ENDL; + setLoadState(STATE_LOAD_FETCHING); + + gAssetStorage->getAssetData(getID(), LLAssetType::AT_SOUND, LLAudioEngine::assetCallback, NULL); } } diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index 4067f23a7..717e688df 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -177,7 +177,6 @@ public: void cleanupBuffer(LLAudioBuffer *bufferp); bool hasDecodedFile(const LLUUID &uuid); - bool hasLocalFile(const LLUUID &uuid); void setAllowLargeSounds(bool allow) { mAllowLargeSounds = allow ;} bool getAllowLargeSounds() const {return mAllowLargeSounds;} @@ -227,7 +226,7 @@ protected: S32 mNumChannels; bool mEnableWind; - LLUUID mCurrentTransfer; // Audio file currently being transferred by the system + LLAudioData* mCurrentTransfer; // Audio file currently being transferred by the system LLFrameTimer mCurrentTransferTimer; // A list of all audio sources that are known to the viewer at this time. @@ -400,25 +399,27 @@ public: 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; } + enum ELoadState + { + STATE_LOAD_ERROR, + STATE_LOAD_REQ_FETCH, + STATE_LOAD_FETCHING, + STATE_LOAD_REQ_DECODE, + STATE_LOAD_DECODING, + STATE_LOAD_READY + }; + ELoadState getLoadState() const { return mLoadState; } + ELoadState setLoadState(ELoadState state) { return mLoadState = state; } + bool isInPreload() const { return mLoadState > STATE_LOAD_ERROR && mLoadState < STATE_LOAD_READY; } - 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; } + void updateLoadState(); 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 + ELoadState mLoadState; }; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 9a23cf56c..45d486784 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4863,7 +4863,7 @@ void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& ow if (audio_uuid.isNull()) { - if (!mAudioSourcep) + if (!mAudioSourcep || (flags & LL_SOUND_FLAG_STOP && !mAudioSourcep->isLoop())) { return; }