From 821fd1f7d67577614f7aa1f0457ffb0bf31f46ab Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 4 Jun 2013 03:46:26 -0500 Subject: [PATCH] Handle fmodex internal channel release by registering channel callbacks. Harden channel<->source associations. Assign console output for audio system "AudioEngine" and "AudioImpl" tags. Added base LLAudioChannel::cleanup() vfunc to handle non-impl-related variable cleanup. Killed some redundant code. --- indra/llaudio/llaudioengine.cpp | 125 +++++++++---------------- indra/llaudio/llaudioengine.h | 2 +- indra/llaudio/llaudioengine_fmod.cpp | 2 +- indra/llaudio/llaudioengine_fmodex.cpp | 61 +++++++----- indra/llaudio/llaudioengine_fmodex.h | 2 +- indra/llaudio/llaudioengine_openal.cpp | 3 +- 6 files changed, 89 insertions(+), 106 deletions(-) diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 558c929e1..2c678346a 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -126,7 +126,7 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata) // Initialize the decode manager gAudioDecodeMgrp = new LLAudioDecodeMgr; - llinfos << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl; + LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl; return true; } @@ -311,8 +311,7 @@ void LLAudioEngine::idle(F32 max_decode_time) LLAudioChannel *channelp = getFreeChannel(max_priority); if (channelp) { - //llinfos << "Replacing source in channel due to priority!" << llendl; - max_sourcep->setChannel(channelp); + LL_DEBUGS("AudioEngine") << "Replacing source in channel due to priority!" << llendl; channelp->setSource(max_sourcep); if (max_sourcep->isSyncSlave()) { @@ -348,18 +347,22 @@ void LLAudioEngine::idle(F32 max_decode_time) } LLAudioChannel *channelp = sourcep->getChannel(); - if (!channelp) + 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; - // 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) + if(is_stopped) { - updateBufferForData(sourcep->mCurrentDatap); + // 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. @@ -372,52 +375,6 @@ void LLAudioEngine::idle(F32 max_decode_time) } 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 = sourcep->getChannel(); - if (channelp) - { - channelp->updateBuffer(); - channelp->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(); - channelp = sourcep->getChannel(); - if (channelp) - { - channelp->updateBuffer(); - channelp->play(); - } - } - } - } } // Lame, update the channels AGAIN. @@ -491,7 +448,7 @@ void LLAudioEngine::idle(F32 max_decode_time) { if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f) { - //llinfos << "Flushing unused buffer!" << llendl; + LL_DEBUGS("AudioEngine") << "Flushing unused buffer!" << llendl; mBuffers[i]->mAudioDatap->mBufferp = NULL; delete mBuffers[i]; mBuffers[i] = NULL; @@ -603,8 +560,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() if (buffer_id >= 0) { - lldebugs << "Taking over unused buffer " << buffer_id << llendl; - //llinfos << "Flushing unused buffer!" << llendl; + LL_DEBUGS("AudioEngine") << "Taking over unused buffer! max_age=" << max_age << llendl; mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL; for (U32 i = 0; i < MAX_CHANNELS; i++) { @@ -638,6 +594,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) // 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()) { @@ -670,9 +627,9 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) return NULL; } + LL_DEBUGS("AudioEngine") << "Flushing min channel" << llendl; // Flush the minimum priority channel, and return it. min_channelp->cleanup(); - min_channelp->getSource()->setChannel(NULL); return min_channelp; } @@ -1006,10 +963,13 @@ LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid) if( sourcep && sourcep->getCurrentData() && sourcep->getCurrentData()->getID() == audio_uuid ) { LLAudioChannel* chan=sourcep->getChannel(); - delete sourcep; if(chan) + { + LL_DEBUGS("AudioEngine") << "removeAudioData" << llendl; chan->cleanup(); - mAllSources.erase(iter2++); + } + delete sourcep; + mAllSources.erase(iter2++); } else ++iter2; @@ -1043,7 +1003,7 @@ void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp) iter = mAllSources.find(asp->getID()); if (iter == mAllSources.end()) { - llwarns << "Cleaning up unknown audio source!" << llendl; + LL_WARNS("AudioEngine") << "Cleaning up unknown audio source!" << llendl; return; } delete asp; @@ -1264,7 +1224,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.notNull()) { - llinfos << "Getting asset data for: " << asset_id << llendl; + LL_DEBUGS("AudioEngine") << "Getting asset data for: " << asset_id << llendl; gAudiop->mCurrentTransfer = asset_id; gAudiop->mCurrentTransferTimer.reset(); gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND, @@ -1282,7 +1242,7 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E { if (result_code) { - llinfos << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl; + LL_INFOS("AudioEngine") << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl; // Need to mark data as bad to avoid constant rerequests. LLAudioData *adp = gAudiop->getAudioData(uuid); if (adp) @@ -1299,7 +1259,7 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E if (!adp) { // Should never happen - llwarns << "Got asset callback without audio data for " << uuid << llendl; + LL_WARNS("AudioEngine") << "Got asset callback without audio data for " << uuid << llendl; } else { @@ -1360,8 +1320,7 @@ LLAudioSource::~LLAudioSource() if (mChannelp) { // Stop playback of this sound - mChannelp->setSource(NULL); - setChannel(NULL); + mChannelp->cleanup(); } } @@ -1404,7 +1363,7 @@ void LLAudioSource::update() } else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done { - llwarns << "Marking LLAudioSource corrupted for " << adp->getID() << llendl; + LL_WARNS("AudioEngine") << "Marking LLAudioSource corrupted for " << adp->getID() << llendl; mCorrupted = true ; } } @@ -1475,7 +1434,6 @@ bool LLAudioSource::play(const LLUUID &audio_uuid) if (getChannel()) { getChannel()->setSource(NULL); - setChannel(NULL); if (!isMuted()) { mCurrentDatap = NULL; @@ -1729,11 +1687,16 @@ LLAudioChannel::~LLAudioChannel() { // Need to disconnect any sources which are using this channel. //llinfos << "Cleaning up audio channel" << llendl; - if (mCurrentSourcep) - { + cleanup(); +} + +void LLAudioChannel::cleanup() +{ + if(mCurrentSourcep) mCurrentSourcep->setChannel(NULL); - } mCurrentBufferp = NULL; + mCurrentSourcep = NULL; + mWaiting = false; } @@ -1744,22 +1707,21 @@ void LLAudioChannel::setSource(LLAudioSource *sourcep) if (!sourcep) { // Clearing the source for this channel, don't need to do anything. - //llinfos << "Clearing source for channel" << llendl; + LL_DEBUGS("AudioEngine") << "Clearing source for channel" << llendl; cleanup(); - mCurrentSourcep = NULL; - mWaiting = false; return; } if (sourcep == mCurrentSourcep) { // Don't reallocate the channel, this will make FMOD goofy. - //llinfos << "Calling setSource with same source!" << llendl; + LL_DEBUGS("AudioEngine") << "Calling setSource with same source!" << llendl; } + cleanup(); mCurrentSourcep = sourcep; - - + mCurrentSourcep->setChannel(this); + updateBuffer(); update3DPosition(); } @@ -1796,7 +1758,10 @@ bool LLAudioChannel::updateBuffer() // The source changed what buffer it's playing. We need to clean up // the existing channel // + LLAudioSource* source = mCurrentSourcep; cleanup(); + mCurrentSourcep = source; + mCurrentSourcep->setChannel(this); mCurrentBufferp = bufferp; if (bufferp) @@ -1856,7 +1821,7 @@ bool LLAudioData::load() if (mBufferp) { // We already have this sound in a buffer, don't do anything. - llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl; + LL_INFOS("AudioEngine") << "Already have a buffer for this sound, don't bother loading!" << llendl; return true; } @@ -1864,7 +1829,7 @@ bool LLAudioData::load() if (!mBufferp) { // No free buffers, abort. - lldebugs << "Not able to allocate a new audio buffer, aborting." << llendl; + LL_DEBUGS("AudioEngine") << "Not able to allocate a new audio buffer, aborting." << llendl; return false; } diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index 230168ee9..d58758709 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -447,7 +447,7 @@ public: protected: virtual void play() = 0; virtual void playSynced(LLAudioChannel *channelp) = 0; - virtual void cleanup() = 0; + virtual void cleanup(); void setWaiting(bool waiting) { mWaiting = waiting; } public: diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp index f47a6cac0..4c4f4f48d 100644 --- a/indra/llaudio/llaudioengine_fmod.cpp +++ b/indra/llaudio/llaudioengine_fmod.cpp @@ -576,6 +576,7 @@ void LLAudioChannelFMOD::updateLoop() void LLAudioChannelFMOD::cleanup() { + LLAudioChannel::cleanup(); if (!mChannelID) { //llinfos << "Aborting cleanup with no channelID." << llendl; @@ -588,7 +589,6 @@ void LLAudioChannelFMOD::cleanup() LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; } - mCurrentBufferp = NULL; mChannelID = 0; } diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 56997a888..adcdf2b7f 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -93,7 +93,7 @@ inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) { if(result == FMOD_OK) return false; - llwarns << string << " Error: " << FMOD_ErrorString(result) << llendl; + LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << llendl; return true; } @@ -101,11 +101,11 @@ void* F_STDCALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const cha { if(type & FMOD_MEMORY_STREAM_DECODE) { - llinfos << "Decode buffer size: " << size << llendl; + LL_INFOS("AudioImpl") << "Decode buffer size: " << size << llendl; } else if(type & FMOD_MEMORY_STREAM_FILE) { - llinfos << "Strean buffer size: " << size << llendl; + LL_INFOS("AudioImpl") << "Stream buffer size: " << size << llendl; } return new char[size]; } @@ -344,7 +344,7 @@ void LLAudioEngine_FMODEX::allocateListener(void) mListenerp = (LLListener *) new LLListener_FMODEX(mSystem); if (!mListenerp) { - llwarns << "Listener creation failed" << llendl; + LL_WARNS("AudioImpl") << "Listener creation failed" << llendl; } } @@ -353,13 +353,13 @@ void LLAudioEngine_FMODEX::shutdown() { stopInternetStream(); - llinfos << "About to LLAudioEngine::shutdown()" << llendl; + LL_INFOS("AudioImpl") << "About to LLAudioEngine::shutdown()" << llendl; LLAudioEngine::shutdown(); - llinfos << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl; + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl; mSystem->close(); mSystem->release(); - llinfos << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl; + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl; delete mListenerp; mListenerp = NULL; @@ -493,6 +493,27 @@ LLAudioChannelFMODEX::~LLAudioChannelFMODEX() cleanup(); } +static FMOD_RESULT F_CALLBACK channel_callback(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2) +{ + if(type == FMOD_CHANNEL_CALLBACKTYPE_END) + { + FMOD::Channel* chan = reinterpret_cast(channel); + LLAudioChannelFMODEX* audio_channel = NULL; + chan->getUserData((void**)&audio_channel); + if(audio_channel) + { + audio_channel->onRelease(); + } + } + return FMOD_OK; +} + +void LLAudioChannelFMODEX::onRelease() +{ + mChannelp = NULL; //Null out channel here so cleanup doesn't try to redundantly stop it. + cleanup(); +} + bool LLAudioChannelFMODEX::updateBuffer() { if (LLAudioChannel::updateBuffer()) @@ -508,7 +529,7 @@ bool LLAudioChannelFMODEX::updateBuffer() { // This is bad, there should ALWAYS be a sound associated with a legit // buffer. - llerrs << "No FMOD sound!" << llendl; + LL_ERRS("AudioImpl") << "No FMOD sound!" << llendl; return false; } @@ -519,9 +540,13 @@ bool LLAudioChannelFMODEX::updateBuffer() { FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp); Check_FMOD_Error(result, "FMOD::System::playSound"); - } + if(result == FMOD_OK) + { - //llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl; + mChannelp->setCallback(&channel_callback); + mChannelp->setUserData(this); + } + } } // If we have a source for the channel, we need to update its gain. @@ -535,13 +560,6 @@ bool LLAudioChannelFMODEX::updateBuffer() Check_FMOD_Error(result, "FMOD::Channel::setVolume"); result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); Check_FMOD_Error(result, "FMOD::Channel::setMode"); - /*if(Check_FMOD_Error(result, "FMOD::Channel::setMode")) - { - S32 index; - mChannelp->getIndex(&index); - llwarns << "Channel " << index << "Source ID: " << mCurrentSourcep->getID() - << " at " << mCurrentSourcep->getPositionGlobal() << llendl; - }*/ } return true; @@ -608,16 +626,17 @@ void LLAudioChannelFMODEX::updateLoop() void LLAudioChannelFMODEX::cleanup() { + LLAudioChannel::cleanup(); + if (!mChannelp) { //llinfos << "Aborting cleanup with no channel handle." << llendl; return; } - //llinfos << "Cleaning up channel: " << mChannelID << llendl; + mChannelp->setCallback(NULL); Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); - mCurrentBufferp = NULL; mChannelp = NULL; } @@ -626,7 +645,7 @@ void LLAudioChannelFMODEX::play() { if (!mChannelp) { - llwarns << "Playing without a channel handle, aborting" << llendl; + LL_WARNS("AudioImpl") << "Playing without a channel handle, aborting" << llendl; return; } @@ -734,7 +753,7 @@ bool LLAudioBufferFMODEX::loadWAV(const std::string& filename) if (result != FMOD_OK) { // We failed to load the file for some reason. - llwarns << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl; + LL_WARNS("AudioImpl") << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl; // // If we EVER want to load wav files provided by end users, we need diff --git a/indra/llaudio/llaudioengine_fmodex.h b/indra/llaudio/llaudioengine_fmodex.h index 6b780128f..7f9a28b04 100644 --- a/indra/llaudio/llaudioengine_fmodex.h +++ b/indra/llaudio/llaudioengine_fmodex.h @@ -95,7 +95,7 @@ class LLAudioChannelFMODEX : public LLAudioChannel public: LLAudioChannelFMODEX(FMOD::System *audioengine); virtual ~LLAudioChannelFMODEX(); - + void onRelease(); protected: /*virtual*/ void play(); /*virtual*/ void playSynced(LLAudioChannel *channelp); diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index 1b2c8c6ee..10ce5e6ed 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -186,10 +186,9 @@ LLAudioChannelOpenAL::~LLAudioChannelOpenAL() void LLAudioChannelOpenAL::cleanup() { + LLAudioChannel::cleanup(); alSourceStop(mALSource); alSourcei(mALSource, AL_BUFFER, AL_NONE); - - mCurrentBufferp = NULL; } void LLAudioChannelOpenAL::play()