Пример #1
0
// <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);
	}
}
Пример #2
0
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();
}
Пример #5
0
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();
}