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) { LL_DEBUGS("AudioEngine") << "Replacing source in channel due to priority!" << llendl; llassert(max_sourcep->getChannel() == NULL); llassert(channelp->mCurrentBufferp == NULL); channelp->setSource(max_sourcep); llassert(max_sourcep == channelp->getSource()); llassert(channelp->mCurrentBufferp == max_sourcep->getCurrentBuffer()); 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(); bool is_stopped = channelp && channelp->isPlaying(); if (is_stopped || (sourcep->isLoop() && channelp->mLoopedThisFrame)) { // This sound isn't playing, so we just process move the queue sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; if(is_stopped) { // 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(); channelp->play(); } continue; } } // 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) { LL_DEBUGS("AudioEngine") << "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(); }