void LLAudioEngine::startNextTransfer() { //LL_INFOS("AudioEngine") << "LLAudioEngine::startNextTransfer()" << LL_ENDL; if (getMuted()) { 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: S32 i; LLAudioSource *asp = NULL; LLAudioData *adp = NULL; LLAudioData *cur_adp = NULL; data_map::iterator data_iter; // Check all channels for currently playing sounds. F32 max_pri = -1.f; for (i = 0; i < MAX_CHANNELS; i++) { if (!mChannels[i]) { continue; } asp = mChannels[i]->getSource(); if (!asp) { continue; } if (asp->getPriority() <= max_pri) { continue; } if (asp->getPriority() <= max_pri) { continue; } adp = asp->getCurrentData(); if (!adp) { continue; } if (adp->isInPreload()) { max_pri = asp->getPriority(); cur_adp = adp; } } // Check all channels for currently queued sounds. if (!cur_adp) { max_pri = -1.f; for (i = 0; i < MAX_CHANNELS; i++) { if (!mChannels[i]) { continue; } LLAudioSource *asp; asp = mChannels[i]->getSource(); if (!asp) { continue; } if (asp->getPriority() <= max_pri) { continue; } adp = asp->getQueuedData(); if (!adp) { continue; } if (adp->isInPreload()) { max_pri = asp->getPriority(); cur_adp = adp; } } } // Check all live channels for other sounds (preloads). if (!cur_adp) { max_pri = -1.f; for (i = 0; i < MAX_CHANNELS; i++) { if (!mChannels[i]) { continue; } LLAudioSource *asp; asp = mChannels[i]->getSource(); if (!asp) { continue; } if (asp->getPriority() <= max_pri) { continue; } for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++) { LLAudioData *adp = data_iter->second; if (!adp) { continue; } if (adp->isInPreload()) { max_pri = asp->getPriority(); cur_adp = adp; } } } } // Check all sources if (!cur_adp) { max_pri = -1.f; source_map::iterator source_iter; for (source_iter = mAllSources.begin(); source_iter != mAllSources.end(); source_iter++) { asp = source_iter->second; if (!asp) { continue; } if (asp->getPriority() <= max_pri) { continue; } adp = asp->getCurrentData(); if (adp && adp->isInPreload()) { max_pri = asp->getPriority(); cur_adp = adp; continue; } adp = asp->getQueuedData(); if (adp && adp->isInPreload()) { max_pri = asp->getPriority(); cur_adp = adp; continue; } for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++) { LLAudioData *adp = data_iter->second; if (!adp) { continue; } if (adp->isInPreload()) { max_pri = asp->getPriority(); cur_adp = adp; break; } } } } if (!cur_adp) { while(!mPreloadSystemList.empty()) { adp = getAudioData(mPreloadSystemList.front()); mPreloadSystemList.pop_front(); if(adp->isInPreload()) { 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()) mPreloadSystemList.erase(it); } if (cur_adp) { mCurrentTransfer = cur_adp; mCurrentTransferTimer.reset(); mCurrentTransfer->updateLoadState(); } else { //LL_INFOS("AudioEngine") << "No pending transfers?" << LL_ENDL; } }
void LLAudioEngine::idle(F32 max_decode_time) { 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++) { if (mBuffers[i]) { mBuffers[i]->mInUse = false; } } //Iterate down all queued sources. Remove finished ones, sort active ones by priority. Find highest priority 'master' source if present. LLAudioSource *sync_masterp = NULL; //Highest priority syncmaster LLAudioSource *sync_slavep = NULL; //Highest priority syncslave //Priority queue that will be filled with soundsources that have a high likeleyhood of being able to start [re]playing this idle tick. //Sort order: Primary: priority. Secondary: syncmaster. Tertiary: syncslave std::priority_queue<LLAudioSource*,std::vector<LLAudioSource*,boost::pool_allocator<LLAudioSource*> >,SourcePriorityComparator> queue; //Have to put syncslaves into a temporary list until the syncmaster state is determined. //If the syncmaster might be started, or just looped, insert all pending/looping syncslaves into the priority queue. //If the there is no active syncmaster, then stop any currently looping syncslaves and add none to the priority queue. //This is necessary as any looping soundsources to be stopped, MUST be stopped before iterating down the priority queue. static std::vector<LLAudioSource*> slave_list; slave_list.clear(); //Iterate over all sources. Update their decode or 'done' state, as well as their priorities. //Also add sources that might be able to start playing to the priority queue. //Only sources without channels should be added to priority queue. //Syncslaves must put into the slave list instead of the priority queue. for (source_map::iterator iter = mAllSources.begin(); iter != mAllSources.end();) { LLAudioSource *sourcep = iter->second; //Load/decode/queue pop sourcep->update(); //Done after update, as failure to load might mark source as corrupt, which causes isDone to return true. if (sourcep->isDone()) { // The source is done playing, clean it up. delete sourcep; mAllSources.erase(iter++); continue; } // 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()) { continue; } sourcep->updatePriority(); //Calculates current priority. 1.f=ambient. 0.f=muted. Otherwise based off of distance. if (sourcep->getPriority() < F_APPROXIMATELY_ZERO) { // Muted, or nothing queued, or too far, so we don't care. continue; } else if(sourcep->isSyncMaster()) { if(!sync_masterp || sourcep->getPriority() > sync_masterp->getPriority()) { if(sync_masterp && !sync_masterp->getChannel()) queue.push(sync_masterp); //Add lower-priority soundmaster to the queue as a normal sound. sync_masterp = sourcep; //Don't add master to the queue yet. //Add it after highest-priority sound slave is found so we can outrank its priority. continue; } //Else fall through like a normal sound. } else if(sourcep->isSyncSlave()) { if(!sync_slavep || sourcep->getPriority() > sync_slavep->getPriority()) { sync_slavep = sourcep; } //Don't add to the priority queue quite yet. Best primary syncmaster candidate may not have been determined yet. slave_list.push_back(sourcep); continue; } if(sourcep->getChannel()) { //If this is just a regular sound and is currently playing then do nothing special. continue; } //Add to our queue. Highest priority will be at the front. queue.push(sourcep); } // Do this BEFORE we update the channels // Update the channels to sync up with any changes that the source made, // such as changing what sound was playing. updateChannels(); //Loop over all syncsaves. If no syncmaster, stop looping ones, else add ones that need [re]started to the priority queue. //Normally there are zero to one syncslaves, so this loop isn't typically expensive. for(std::vector<LLAudioSource*>::iterator iter=slave_list.begin();iter!=slave_list.end();++iter) { if(!sync_masterp) { //Stop any looping syncslaves. I'm not sure this behavior is correct, but letting looping syncslaves run //off on their own seems wrong. The lsl documentation is unclear. if((*iter)->getChannel() && (*iter)->isLoop()) { (*iter)->getChannel()->cleanup(); } } //If the syncmaster already has a channel and hasn't looped, then we can skip syncslaves this idle tick (there's nothing to do). else if((!sync_masterp->getChannel() || sync_masterp->getChannel()->mLoopedThisFrame)) { //Add syncslaves that we might want to [re]start. if(!(*iter)->getChannel() || (*iter)->isLoop()) queue.push((*iter)); } } if(sync_masterp) { //Syncmaster must have priority greater than or equal to priority of highest priority syncslave. //The case of slave priority equaling master priority is handled in the comparator (SourcePriorityComparator). if(sync_slavep) sync_masterp->setPriority(sync_slavep->getPriority()); if(!sync_masterp->getChannel()) { queue.push(sync_masterp); } } // Dispatch all soundsources. bool syncmaster_started = sync_masterp && sync_masterp->getChannel() && sync_masterp->getChannel()->mLoopedThisFrame; while(!queue.empty()) { LLAudioSource *sourcep = queue.top(); queue.pop(); //If the syncmaster hasn't just started playing, or hasn't just looped then skip this soundslave, //as there's nothing to do with it yet. if (sourcep->isSyncSlave() && !syncmaster_started) { continue; } LLAudioChannel *channelp = sourcep->getChannel(); if (!channelp) { //No more channels. We can break here, as in theory, any lower priority sounds should have had their //channel already stolen. There should be nothing playing, nor playable, when iterating beyond this point. if(!(channelp = getFreeChannel(sourcep->getPriority()))) { break; } //If this is the primary syncmaster that we just started, then [re]start syncslaves further down in the priority queue. //Due to sorting and priority fudgery, the syncmaster is ALWAYS before any syncslaves in this loop. syncmaster_started |= (sourcep == sync_masterp); //setSource calls updateBuffer and update3DPosition, and resets the source mAgeTimer channelp->setSource(sourcep); } if(sourcep->isSyncSlave()) channelp->playSynced(sync_masterp->getChannel()); else channelp->play(); } // Sync up everything that the audio engine needs done. commitDeferredChanges(); // Flush unused buffers that are stale enough for (i = 0; i < MAX_BUFFERS; i++) { if (mBuffers[i]) { if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f) { LL_DEBUGS("AudioEngine") << "Flushing unused buffer!" << LL_ENDL; mBuffers[i]->mAudioDatap->mBufferp = NULL; delete mBuffers[i]; mBuffers[i] = NULL; } } } // Clear all of the looped flags for the channels for (i = 0; i < MAX_CHANNELS; i++) { if (mChannels[i]) { mChannels[i]->mLoopedThisFrame = false; } } // Decode audio files gAudioDecodeMgrp->processQueue(max_decode_time); // 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(); }
void LLAudioEngine::startNextTransfer() { //llinfos << "LLAudioEngine::startNextTransfer()" << llendl; if (mCurrentTransfer.notNull() || getMuted()) { //llinfos << "Transfer in progress, aborting" << llendl; return; } // 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; data_map::iterator data_iter; // Check all channels for currently playing sounds. F32 max_pri = -1.f; for (i = 0; i < MAX_CHANNELS; i++) { if (!mChannels[i]) { continue; } asp = mChannels[i]->getSource(); if (!asp) { continue; } if (asp->getPriority() <= max_pri) { continue; } if (asp->getPriority() <= max_pri) { continue; } adp = asp->getCurrentData(); if (!adp) { continue; } if (!adp->hasLocalData() && adp->hasValidData()) { asset_id = adp->getID(); max_pri = asp->getPriority(); } } // Check all channels for currently queued sounds. if (asset_id.isNull()) { max_pri = -1.f; for (i = 0; i < MAX_CHANNELS; i++) { if (!mChannels[i]) { continue; } LLAudioSource *asp; asp = mChannels[i]->getSource(); if (!asp) { continue; } if (asp->getPriority() <= max_pri) { continue; } adp = asp->getQueuedData(); if (!adp) { continue; } if (!adp->hasLocalData() && adp->hasValidData()) { asset_id = adp->getID(); max_pri = asp->getPriority(); } } } // Check all live channels for other sounds (preloads). if (asset_id.isNull()) { max_pri = -1.f; for (i = 0; i < MAX_CHANNELS; i++) { if (!mChannels[i]) { continue; } LLAudioSource *asp; asp = mChannels[i]->getSource(); if (!asp) { continue; } if (asp->getPriority() <= max_pri) { continue; } for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++) { LLAudioData *adp = data_iter->second; if (!adp) { continue; } if (!adp->hasLocalData() && adp->hasValidData()) { asset_id = adp->getID(); max_pri = asp->getPriority(); } } } } // Check all sources if (asset_id.isNull()) { max_pri = -1.f; source_map::iterator source_iter; for (source_iter = mAllSources.begin(); source_iter != mAllSources.end(); source_iter++) { asp = source_iter->second; if (!asp) { continue; } if (asp->getPriority() <= max_pri) { continue; } adp = asp->getCurrentData(); if (adp && !adp->hasLocalData() && adp->hasValidData()) { asset_id = adp->getID(); max_pri = asp->getPriority(); continue; } adp = asp->getQueuedData(); if (adp && !adp->hasLocalData() && adp->hasValidData()) { asset_id = adp->getID(); max_pri = asp->getPriority(); continue; } for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++) { LLAudioData *adp = data_iter->second; if (!adp) { continue; } if (!adp->hasLocalData() && adp->hasValidData()) { asset_id = adp->getID(); max_pri = asp->getPriority(); break; } } } } if (asset_id.notNull()) { llinfos << "Getting asset data for: " << asset_id << llendl; gAudiop->mCurrentTransfer = asset_id; gAudiop->mCurrentTransferTimer.reset(); gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND, assetCallback, NULL); } else { //llinfos << "No pending transfers?" << llendl; } }