Merge branch 'master' of git://github.com/Shyotl/SingularityViewer

This commit is contained in:
Latif Khalifa
2013-10-24 01:37:19 +02:00
5 changed files with 147 additions and 136 deletions

View File

@@ -611,8 +611,10 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl; llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl;
mCurrentDecodep->flushBadFile(); mCurrentDecodep->flushBadFile();
LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID()); LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
adp->setHasValidData(false); if(adp)
adp->setHasCompletedDecode(true); {
adp->setLoadState(LLAudioData::STATE_LOAD_ERROR);
}
mCurrentDecodep = NULL; mCurrentDecodep = NULL;
done = TRUE; done = TRUE;
} }
@@ -634,10 +636,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
} }
else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone()) else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
{ {
adp->setHasCompletedDecode(true); adp->setLoadState(LLAudioData::STATE_LOAD_READY);
adp->setHasDecodedData(true);
adp->setHasValidData(true);
// At this point, we could see if anyone needs this sound immediately, but // 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 // I'm not sure that there's a reason to - we need to poll all of the playing
// sounds anyway. // sounds anyway.
@@ -645,7 +644,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
} }
else else
{ {
adp->setHasCompletedDecode(true); adp->setLoadState(LLAudioData::STATE_LOAD_ERROR);
llinfos << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << llendl; llinfos << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << llendl;
} }
mCurrentDecodep = NULL; mCurrentDecodep = NULL;
@@ -688,8 +687,7 @@ void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs)
LLAudioData *adp = gAudiop->getAudioData(uuid); LLAudioData *adp = gAudiop->getAudioData(uuid);
if(adp) if(adp)
{ {
adp->setHasValidData(false); adp->setLoadState(LLAudioData::STATE_LOAD_ERROR);
adp->setHasCompletedDecode(true);
} }
mCurrentDecodep = NULL; mCurrentDecodep = NULL;
} }
@@ -715,23 +713,13 @@ void LLAudioDecodeMgr::processQueue(const F32 num_secs)
mImpl->processQueue(num_secs); mImpl->processQueue(num_secs);
} }
BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) bool LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
{ {
if (gAudiop->hasDecodedFile(uuid)) if(!uuid.notNull())
{ return false;
// Already have a decoded version, don't need to decode it. else if (!gAssetStorage || !gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
//llinfos << "addDecodeRequest for " << uuid << " has decoded file already" << llendl; return false;
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); mImpl->mDecodeQueue.push(uuid);
return TRUE; return true;
}
//llinfos << "addDecodeRequest for " << uuid << " no file available" << llendl;
return FALSE;
} }

View File

@@ -44,7 +44,7 @@ public:
~LLAudioDecodeMgr(); ~LLAudioDecodeMgr();
void processQueue(const F32 num_secs = 0.005); void processQueue(const F32 num_secs = 0.005);
BOOL addDecodeRequest(const LLUUID &uuid); bool addDecodeRequest(const LLUUID &uuid);
void addAudioRequest(const LLUUID &uuid); void addAudioRequest(const LLUUID &uuid);
protected: protected:

View File

@@ -113,6 +113,8 @@ void LLAudioEngine::setDefaults()
for (U32 i = 0; i < LLAudioEngine::AUDIO_TYPE_COUNT; i++) for (U32 i = 0; i < LLAudioEngine::AUDIO_TYPE_COUNT; i++)
mSecondaryGain[i] = 1.0f; mSecondaryGain[i] = 1.0f;
mCurrentTransfer = NULL;
mAllowLargeSounds = false; 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. // Increment iter here (it is not used anymore), so we can use continue below to move on to the next source.
++iter; ++iter;
if(!sourcep->isLoop() && sourcep->mPlayedOnce && (!sourcep->mChannelp || !sourcep->mChannelp->isPlaying()))
{
continue;
}
LLAudioData *adp = sourcep->getCurrentData(); LLAudioData *adp = sourcep->getCurrentData();
//If there is no current data at all, or if it hasn't loaded, we must skip this source. //If there is no current data at all, or if it hasn't loaded, we must skip this source.
if (!adp || !adp->getBuffer()) if (!adp || !adp->getBuffer())
@@ -509,9 +516,8 @@ void LLAudioEngine::idle(F32 max_decode_time)
// Decode audio files // Decode audio files
gAudioDecodeMgrp->processQueue(max_decode_time); gAudioDecodeMgrp->processQueue(max_decode_time);
// Call this every frame, just in case we somehow // Just call here every frame. It makes little sense to call elsewhere,
// missed picking it up in all the places that can add // as it's throttled to one active preloading loading sound at a time anyhow
// or request new data.
startNextTransfer(); startNextTransfer();
updateInternetStream(); updateInternetStream();
@@ -660,10 +666,7 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid)
if(uuid.isNull()) if(uuid.isNull())
return false; return false;
gAudiop->getAudioData(uuid); // We don't care about the return value, this is just to make sure if(getAudioData(uuid)->getLoadState() >= LLAudioData::STATE_LOAD_DECODING)
// that we have an entry, which will mean that the audio engine knows about this
if (gAudioDecodeMgrp->addDecodeRequest(uuid))
{ {
// This means that we do have a local copy, and we're working on decoding it. // This means that we do have a local copy, and we're working on decoding it.
return true; 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() void LLAudioEngine::startNextTransfer()
{ {
//LL_INFOS("AudioEngine") << "LLAudioEngine::startNextTransfer()" << LL_ENDL; //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; 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. // Get the ID for the next asset that we want to transfer.
// Pick one in the following order: // Pick one in the following order:
LLUUID asset_id;
S32 i; S32 i;
LLAudioSource *asp = NULL; LLAudioSource *asp = NULL;
LLAudioData *adp = NULL; LLAudioData *adp = NULL;
LLAudioData *cur_adp = NULL;
data_map::iterator data_iter; data_map::iterator data_iter;
// Check all channels for currently playing sounds. // Check all channels for currently playing sounds.
@@ -1119,15 +1132,15 @@ void LLAudioEngine::startNextTransfer()
continue; continue;
} }
if (!adp->hasLocalData() && adp->hasValidData()) if (adp->isInPreload())
{ {
asset_id = adp->getID();
max_pri = asp->getPriority(); max_pri = asp->getPriority();
cur_adp = adp;
} }
} }
// Check all channels for currently queued sounds. // Check all channels for currently queued sounds.
if (asset_id.isNull()) if (!cur_adp)
{ {
max_pri = -1.f; max_pri = -1.f;
for (i = 0; i < MAX_CHANNELS; i++) for (i = 0; i < MAX_CHANNELS; i++)
@@ -1155,16 +1168,16 @@ void LLAudioEngine::startNextTransfer()
continue; continue;
} }
if (!adp->hasLocalData() && adp->hasValidData()) if (adp->isInPreload())
{ {
asset_id = adp->getID();
max_pri = asp->getPriority(); max_pri = asp->getPriority();
cur_adp = adp;
} }
} }
} }
// Check all live channels for other sounds (preloads). // Check all live channels for other sounds (preloads).
if (asset_id.isNull()) if (!cur_adp)
{ {
max_pri = -1.f; max_pri = -1.f;
for (i = 0; i < MAX_CHANNELS; i++) for (i = 0; i < MAX_CHANNELS; i++)
@@ -1195,17 +1208,17 @@ void LLAudioEngine::startNextTransfer()
continue; continue;
} }
if (!adp->hasLocalData() && adp->hasValidData()) if (adp->isInPreload())
{ {
asset_id = adp->getID();
max_pri = asp->getPriority(); max_pri = asp->getPriority();
cur_adp = adp;
} }
} }
} }
} }
// Check all sources // Check all sources
if (asset_id.isNull()) if (!cur_adp)
{ {
max_pri = -1.f; max_pri = -1.f;
source_map::iterator source_iter; source_map::iterator source_iter;
@@ -1223,18 +1236,18 @@ void LLAudioEngine::startNextTransfer()
} }
adp = asp->getCurrentData(); adp = asp->getCurrentData();
if (adp && !adp->hasLocalData() && adp->hasValidData()) if (adp && adp->isInPreload())
{ {
asset_id = adp->getID();
max_pri = asp->getPriority(); max_pri = asp->getPriority();
cur_adp = adp;
continue; continue;
} }
adp = asp->getQueuedData(); adp = asp->getQueuedData();
if (adp && !adp->hasLocalData() && adp->hasValidData()) if (adp && adp->isInPreload())
{ {
asset_id = adp->getID();
max_pri = asp->getPriority(); max_pri = asp->getPriority();
cur_adp = adp;
continue; continue;
} }
@@ -1246,35 +1259,41 @@ void LLAudioEngine::startNextTransfer()
continue; continue;
} }
if (!adp->hasLocalData() && adp->hasValidData()) if (adp->isInPreload())
{ {
asset_id = adp->getID();
max_pri = asp->getPriority(); max_pri = asp->getPriority();
cur_adp = adp;
break; break;
} }
} }
} }
} }
if (asset_id.isNull() && !mPreloadSystemList.empty()) if (!cur_adp)
{ {
asset_id = mPreloadSystemList.front(); while(!mPreloadSystemList.empty())
{
adp = getAudioData(mPreloadSystemList.front());
mPreloadSystemList.pop_front(); mPreloadSystemList.pop_front();
} if(adp->isInPreload())
else if(asset_id.notNull())
{ {
std::list<LLUUID>::iterator it = std::find(mPreloadSystemList.begin(),mPreloadSystemList.end(),asset_id); cur_adp = adp;
break;
}
}
}
else if(cur_adp)
{
std::list<LLUUID>::iterator it = std::find(mPreloadSystemList.begin(),mPreloadSystemList.end(),cur_adp->getID());
if(it != mPreloadSystemList.end()) if(it != mPreloadSystemList.end())
mPreloadSystemList.erase(it); mPreloadSystemList.erase(it);
} }
if (asset_id.notNull()) if (cur_adp)
{ {
LL_DEBUGS("AudioEngine") << "Getting asset data for: " << asset_id << LL_ENDL; mCurrentTransfer = cur_adp;
gAudiop->mCurrentTransfer = asset_id; mCurrentTransferTimer.reset();
gAudiop->mCurrentTransferTimer.reset(); mCurrentTransfer->updateLoadState();
gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND,
assetCallback, NULL);
} }
else else
{ {
@@ -1289,22 +1308,20 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E
if(!gAudiop) if(!gAudiop)
return; return;
LLAudioData *adp = gAudiop->getAudioData(uuid);
if (result_code) if (result_code)
{ {
LL_INFOS("AudioEngine") << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << LL_ENDL; 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. // Need to mark data as bad to avoid constant rerequests.
LLAudioData *adp = gAudiop->getAudioData(uuid);
if (adp) if (adp)
{ // Make sure everything is cleared {
adp->setHasValidData(false); adp->setLoadState(LLAudioData::STATE_LOAD_ERROR);
adp->setHasLocalData(false);
adp->setHasDecodedData(false);
adp->setHasCompletedDecode(true);
} }
} }
else else
{ {
LLAudioData *adp = gAudiop->getAudioData(uuid);
if (!adp) if (!adp)
{ {
// Should never happen // Should never happen
@@ -1313,13 +1330,11 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E
else else
{ {
// LL_INFOS("AudioEngine") << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL; // LL_INFOS("AudioEngine") << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL;
adp->setHasValidData(true); adp->setLoadState(LLAudioData::STATE_LOAD_REQ_DECODE);
adp->setHasLocalData(true); //Immediate decode.
gAudioDecodeMgrp->addDecodeRequest(uuid); 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. //Make sure this source looks like its brand new again to prevent removal.
mPlayedOnce = false; mPlayedOnce = false;
mAgeTimer.reset(); mAgeTimer.reset();
gAudiop->startNextTransfer();
} }
LLAudioData *adp = getCurrentData(); LLAudioData *adp = getCurrentData();
if (adp && !adp->getBuffer()) if (adp && !adp->getBuffer())
{
if(adp->getLoadState() == LLAudioData::STATE_LOAD_ERROR)
{
LL_WARNS("AudioEngine") << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL;
mCorrupted = true ;
}
else if(adp->getLoadState() == LLAudioData::STATE_LOAD_READY)
{ {
// Update the audio buffer first - load a sound if we have it. // Update the audio buffer first - load a sound if we have it.
// Note that this could potentially cause us to waste time updating buffers // Note that this could potentially cause us to waste time updating buffers
// for sounds that actually aren't playing, although this should be mitigated // 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 // by the fact that we limit the number of buffers, and we flush buffers based
// on priority. // on priority.
if (adp->hasDecodedData()) adp->load(); //If it fails, just try again next update.
{
if(!adp->load() && adp->hasCompletedDecode())
{
LL_WARNS("AudioEngine") << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL;
mCorrupted = true ;
} }
} else
else if (adp->hasLocalData() && adp->hasValidData())
{ {
if (adp->getID().notNull()) //The sound wasn't preloaded yet... so we must kick off the process.
{ adp->updateLoadState();
gAudioDecodeMgrp->addDecodeRequest(adp->getID());
}
} }
} }
} }
@@ -1524,9 +1536,6 @@ bool LLAudioSource::play(const LLUUID &audio_uuid)
mCurrentDatap = adp; mCurrentDatap = adp;
// Make sure the audio engine knows that we want to request this sound.
gAudiop->startNextTransfer();
return true; return true;
} }
@@ -1536,7 +1545,6 @@ bool LLAudioSource::isDone() const
static const F32 MAX_AGE = 60.f; static const F32 MAX_AGE = 60.f;
static const F32 MAX_UNPLAYED_AGE = 15.f; static const F32 MAX_UNPLAYED_AGE = 15.f;
static const F32 MAX_MUTED_AGE = 11.f; static const F32 MAX_MUTED_AGE = 11.f;
if(mCorrupted) if(mCorrupted)
{ {
// If we decode bad data then just kill this source entirely. // If we decode bad data then just kill this source entirely.
@@ -1570,7 +1578,7 @@ bool LLAudioSource::isDone() const
LLAudioData* adp = mCurrentDatap; LLAudioData* adp = mCurrentDatap;
//Still decoding. //Still decoding.
if(adp && !adp->hasDecodedData() && adp->hasValidData()) if(adp && adp->isInPreload())
return false; return false;
// We don't have a channel assigned, and it's been // 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. // Add it to the preload list.
mPreloadMap[audio_id] = gAudiop->getAudioData(audio_id); mPreloadMap[audio_id] = gAudiop->getAudioData(audio_id);
gAudiop->startNextTransfer();
} }
} }
@@ -1617,7 +1624,7 @@ bool LLAudioSource::hasPendingPreloads() const
{ {
continue; continue;
} }
if (!adp->hasDecodedData() && adp->hasValidData()) if (adp->isInPreload())
{ {
// This source is still waiting for a preload // This source is still waiting for a preload
return true; return true;
@@ -1734,10 +1741,7 @@ bool LLAudioChannel::updateBuffer()
LLAudioData::LLAudioData(const LLUUID &uuid) : LLAudioData::LLAudioData(const LLUUID &uuid) :
mID(uuid), mID(uuid),
mBufferp(NULL), mBufferp(NULL),
mHasLocalData(false), mLoadState(STATE_LOAD_ERROR)
mHasDecodedData(false),
mHasCompletedDecode(false),
mHasValidData(true)
{ {
if (uuid.isNull()) if (uuid.isNull())
{ {
@@ -1745,16 +1749,34 @@ LLAudioData::LLAudioData(const LLUUID &uuid) :
return; 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. if( gAudioDecodeMgrp->addDecodeRequest(getID()) )
setHasLocalData(true); {
setHasDecodedData(true); setLoadState(STATE_LOAD_DECODING);
setHasCompletedDecode(true); LL_INFOS("AudioEngine") << "Decoding asset data for: " << getID() << LL_ENDL;
} }
else if (gAssetStorage && gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND)) else
{ {
setHasLocalData(true); setLoadState(STATE_LOAD_ERROR);
}
}
else if(mLoadState == STATE_LOAD_REQ_FETCH && gAssetStorage && gAssetStorage->isUpstreamOK())
{
LL_INFOS("AudioEngine") << "Fetching asset data for: " << getID() << LL_ENDL;
setLoadState(STATE_LOAD_FETCHING);
gAssetStorage->getAssetData(getID(), LLAssetType::AT_SOUND, LLAudioEngine::assetCallback, NULL);
} }
} }

View File

@@ -177,7 +177,6 @@ public:
void cleanupBuffer(LLAudioBuffer *bufferp); void cleanupBuffer(LLAudioBuffer *bufferp);
bool hasDecodedFile(const LLUUID &uuid); bool hasDecodedFile(const LLUUID &uuid);
bool hasLocalFile(const LLUUID &uuid);
void setAllowLargeSounds(bool allow) { mAllowLargeSounds = allow ;} void setAllowLargeSounds(bool allow) { mAllowLargeSounds = allow ;}
bool getAllowLargeSounds() const {return mAllowLargeSounds;} bool getAllowLargeSounds() const {return mAllowLargeSounds;}
@@ -227,7 +226,7 @@ protected:
S32 mNumChannels; S32 mNumChannels;
bool mEnableWind; bool mEnableWind;
LLUUID mCurrentTransfer; // Audio file currently being transferred by the system LLAudioData* mCurrentTransfer; // Audio file currently being transferred by the system
LLFrameTimer mCurrentTransferTimer; LLFrameTimer mCurrentTransferTimer;
// A list of all audio sources that are known to the viewer at this time. // 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; } LLUUID getID() const { return mID; }
LLAudioBuffer *getBuffer() const { return mBufferp; } LLAudioBuffer *getBuffer() const { return mBufferp; }
bool hasLocalData() const { return mHasLocalData; } enum ELoadState
bool hasDecodedData() const { return mHasDecodedData; } {
bool hasCompletedDecode() const { return mHasCompletedDecode; } STATE_LOAD_ERROR,
bool hasValidData() const { return mHasValidData; } 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 updateLoadState();
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. friend class LLAudioEngine; // Severe laziness, bad.
protected: protected:
LLUUID mID; LLUUID mID;
LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. 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 ELoadState mLoadState;
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
}; };

View File

@@ -4863,7 +4863,7 @@ void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& ow
if (audio_uuid.isNull()) if (audio_uuid.isNull())
{ {
if (!mAudioSourcep) if (!mAudioSourcep || (flags & LL_SOUND_FLAG_STOP && !mAudioSourcep->isLoop()))
{ {
return; return;
} }