// <FS:Ansariel> Asset blacklisting void LLAudioEngine::removeAudioData(const LLUUID& audio_uuid) { if (audio_uuid.isNull()) { return; } data_map::iterator iter = mAllData.find(audio_uuid); if (iter != mAllData.end()) { uuid_vec_t delete_list; source_map::iterator iter2; for (iter2 = mAllSources.begin(); iter2 != mAllSources.end(); ++iter2) { LLAudioSource* sourcep = iter2->second; if (sourcep && sourcep->getCurrentData() && sourcep->getCurrentData()->getID() == audio_uuid) { delete_list.push_back(iter2->first); } } uuid_vec_t::iterator delete_list_end = delete_list.end(); for (uuid_vec_t::iterator del_it = delete_list.begin(); del_it != delete_list_end; ++del_it) { LLUUID source_id = *del_it; LLAudioSource* sourcep = mAllSources[source_id]; LLAudioChannel* chan = sourcep->getChannel(); delete sourcep; mAllSources.erase(source_id); if (chan) { chan->cleanup(); } } LLAudioData* data = iter->second; if (data) { LLAudioBuffer* buf = data->getBuffer(); if (buf) { S32 i; for (i = 0; i < MAX_BUFFERS; ++i) { if (mBuffers[i] == buf) { mBuffers[i] = NULL; } } delete buf; } delete data; } mAllData.erase(audio_uuid); } }
LLAudioBuffer * LLAudioEngine::getFreeBuffer() { //checkStates(); //Fails S32 i; for (i = 0; i < MAX_BUFFERS; i++) { if (!mBuffers[i]) { mBuffers[i] = createBuffer(); return mBuffers[i]; } } //checkStates(); // Fails // Grab the oldest unused buffer F32 max_age = -1.f; S32 buffer_id = -1; for (i = 0; i < MAX_BUFFERS; i++) { if (mBuffers[i]) { if (!mBuffers[i]->mInUse) { if (mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > max_age) { max_age = mBuffers[i]->mLastUseTimer.getElapsedTimeF32(); buffer_id = i; } } } } //checkStates(); //Fails if (buffer_id >= 0) { LL_DEBUGS("AudioEngine") << "Taking over unused buffer! max_age=" << max_age << LL_ENDL; mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL; for (U32 i = 0; i < MAX_CHANNELS; i++) { LLAudioChannel* channelp = mChannels[i]; if(channelp && channelp->mCurrentBufferp == mBuffers[buffer_id]) { channelp->cleanup(); llassert(channelp->mCurrentBufferp == NULL); } } delete mBuffers[buffer_id]; mBuffers[buffer_id] = createBuffer(); return mBuffers[buffer_id]; } //checkStates(); //Fails return NULL; }
LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) { S32 i; for (i = 0; i < mNumChannels; i++) { if (!mChannels[i]) { // No channel allocated here, use it. mChannels[i] = createChannel(); return mChannels[i]; } else { // Channel is allocated but not playing right now, use it. if (!mChannels[i]->isPlaying() && !mChannels[i]->isWaiting()) { LL_DEBUGS("AudioEngine") << "Replacing unused channel" << llendl; mChannels[i]->cleanup(); if (mChannels[i]->getSource()) { mChannels[i]->getSource()->setChannel(NULL); } return mChannels[i]; } } } // All channels used, check priorities. // Find channel with lowest priority and see if we want to replace it. F32 min_priority = 10000.f; LLAudioChannel *min_channelp = NULL; for (i = 0; i < mNumChannels; i++) { LLAudioChannel *channelp = mChannels[i]; LLAudioSource *sourcep = channelp->getSource(); if (sourcep->getPriority() < min_priority) { min_channelp = channelp; min_priority = sourcep->getPriority(); } } if (min_priority > priority || !min_channelp) { // All playing channels have higher priority, return. return NULL; } LL_DEBUGS("AudioEngine") << "Flushing min channel" << llendl; // Flush the minimum priority channel, and return it. min_channelp->cleanup(); return min_channelp; }
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; } } F32 max_priority = -1.f; LLAudioSource *max_sourcep = NULL; // Maximum priority source without a channel source_map::iterator iter; for (iter = mAllSources.begin(); iter != mAllSources.end();) { LLAudioSource *sourcep = iter->second; // Update this source sourcep->update(); sourcep->updatePriority(); if (sourcep->isDone()) { // The source is done playing, clean it up. delete sourcep; mAllSources.erase(iter++); continue; } if (sourcep->isMuted()) { ++iter; continue; } if (!sourcep->getChannel() && sourcep->getCurrentBuffer()) { // We could potentially play this sound if its priority is high enough. if (sourcep->getPriority() > max_priority) { max_priority = sourcep->getPriority(); max_sourcep = sourcep; } } // Move on to the next source iter++; } // Now, do priority-based organization of audio sources. // All channels used, check priorities. // Find channel with lowest priority if (max_sourcep) { LLAudioChannel *channelp = getFreeChannel(max_priority); if (channelp) { //llinfos << "Replacing source in channel due to priority!" << llendl; max_sourcep->setChannel(channelp); channelp->setSource(max_sourcep); if (max_sourcep->isSyncSlave()) { // A sync slave, it doesn't start playing until it's synced up with the master. // Flag this channel as waiting for sync, and return true. channelp->setWaiting(true); } else { channelp->setWaiting(false); channelp->play(); } } } // 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(); // Update queued sounds (switch to next queued data if the current has finished playing) for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) { // This is lame, instead of this I could actually iterate through all the sources // attached to each channel, since only those with active channels // can have anything interesting happen with their queue? (Maybe not true) LLAudioSource *sourcep = iter->second; if (!sourcep->mQueuedDatap || sourcep->isMuted()) { // Muted, or nothing queued, so we don't care. continue; } LLAudioChannel *channelp = sourcep->getChannel(); if (!channelp) { // This sound isn't playing, so we just process move the queue sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; // Reset the timer so the source doesn't die. sourcep->mAgeTimer.reset(); // Make sure we have the buffer set up if we just decoded the data if (sourcep->mCurrentDatap) { updateBufferForData(sourcep->mCurrentDatap); } // Actually play the associated data. sourcep->setupChannel(); channelp = sourcep->getChannel(); if (channelp) { channelp->updateBuffer(); sourcep->getChannel()->play(); } continue; } else { // Check to see if the current sound is done playing, or looped. if (!channelp->isPlaying()) { sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; // Reset the timer so the source doesn't die. sourcep->mAgeTimer.reset(); // Make sure we have the buffer set up if we just decoded the data if (sourcep->mCurrentDatap) { updateBufferForData(sourcep->mCurrentDatap); } // Actually play the associated data. sourcep->setupChannel(); channelp->updateBuffer(); sourcep->getChannel()->play(); } else if (sourcep->isLoop()) { // It's a loop, we need to check and see if we're done with it. if (channelp->mLoopedThisFrame) { sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; // Actually, should do a time sync so if we're a loop master/slave // we don't drift away. sourcep->setupChannel(); sourcep->getChannel()->play(); } } } } // Lame, update the channels AGAIN. // Update the channels to sync up with any changes that the source made, // such as changing what sound was playing. updateChannels(); // Hack! For now, just use a global sync master; LLAudioSource *sync_masterp = NULL; LLAudioChannel *master_channelp = NULL; F32 max_sm_priority = -1.f; for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) { LLAudioSource *sourcep = iter->second; if (sourcep->isMuted()) { continue; } if (sourcep->isSyncMaster()) { if (sourcep->getPriority() > max_sm_priority) { sync_masterp = sourcep; master_channelp = sync_masterp->getChannel(); max_sm_priority = sourcep->getPriority(); } } } if (master_channelp && master_channelp->mLoopedThisFrame) { // Synchronize loop slaves with their masters // Update queued sounds (switch to next queued data if the current has finished playing) for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) { LLAudioSource *sourcep = iter->second; if (!sourcep->isSyncSlave()) { // Not a loop slave, we don't need to do anything continue; } LLAudioChannel *channelp = sourcep->getChannel(); if (!channelp) { // Not playing, don't need to bother. continue; } if (!channelp->isPlaying()) { // Now we need to check if our loop master has just looped, and // start playback if that's the case. if (sync_masterp->getChannel()) { channelp->playSynced(master_channelp); channelp->setWaiting(false); } } } } // 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) { //llinfos << "Flushing unused buffer!" << llendl; 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); // Call this every frame, just in case we somehow // missed picking it up in all the places that can add // or request new data. startNextTransfer(); updateInternetStream(); }
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(); }