diff --git a/autobuild.xml b/autobuild.xml index 4468a1cf724bf560c9c5e21b3a3ee9cf003ddcfd..9583220c88520023a18c00d1fc2fb10047e325e5 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -476,11 +476,11 @@ <key>archive</key> <map> <key>hash</key> - <string>026311237049cebc083b35ccf92e9d301adea46cb707833e283b6ee4f83e4ca662c2b5e9a00b14673aaa4f2ce4051cf38877c0c27c8361d3604225dbc1db4c49</string> + <string>3a2e26f01656a6634023d83f2a53a36a373c61b6af1d00517543050a09826113a2f99121ece20c2c0b581aab9020aeb7cdfe972fa331ea94f3862556a70eae19</string> <key>hash_algorithm</key> <string>blake2b</string> <key>url</key> - <string>https://git.alchemyviewer.org/api/v4/projects/103/packages/generic/dullahan/1.14.0_119.3.1_gf768881_chromium-119.0.6045.124.2234/dullahan-1.14.0_119.3.1_gf768881_chromium-119.0.6045.124-darwin64-2234.tar.zst</string> + <string>https://git.alchemyviewer.org/api/v4/projects/103/packages/generic/dullahan/1.12.4_118.4.1_g3dd6078_chromium-118.0.5993.54.2178/dullahan-1.12.4_118.4.1_g3dd6078_chromium-118.0.5993.54-darwin64-2178.tar.zst</string> </map> <key>name</key> <string>darwin64</string> @@ -490,11 +490,11 @@ <key>archive</key> <map> <key>hash</key> - <string>47b7cf28add3b59f7fe6aa9e7eb52ce65af6f656e47190cdf33703c65c84cc8ea494e38ab9b844aa764b87c9fb61cb344e430e0c7b8f1e171f0f284c32672397</string> + <string>ab9d290a61fb038e6c0ba9ce4efa5f431238e1ab9d4de2c28da1ad0114926af9b716db90cb216816ec8a1de3680d358913851e316d8df6b5eea5302989d9f3bc</string> <key>hash_algorithm</key> <string>blake2b</string> <key>url</key> - <string>https://git.alchemyviewer.org/api/v4/projects/103/packages/generic/dullahan/1.14.0_119.3.1_gf768881_chromium-119.0.6045.124.2234/dullahan-1.14.0_119.3.1_gf768881_chromium-119.0.6045.124-linux64-2234.tar.zst</string> + <string>https://git.alchemyviewer.org/api/v4/projects/103/packages/generic/dullahan/1.12.4_118.4.1_g3dd6078_chromium-118.0.5993.54.2178/dullahan-1.12.4_118.4.1_g3dd6078_chromium-118.0.5993.54-linux64-2178.tar.zst</string> </map> <key>name</key> <string>linux64</string> @@ -504,11 +504,11 @@ <key>archive</key> <map> <key>hash</key> - <string>b9167b9a0fdd95aedd300a6f010986e16a9f43c46092be7a9b2d5e930df8c300d34b17e80d9108315ac4348ea02014624bbf123da9b7851785c0c393051c6940</string> + <string>e88cd99c9d71b2dd1297007bcfdac224d5401cdfadd7aeb069195392274559f7f63ee312061d3441ba7505a4e649ccea67f9d8f3f3c56c9cce638b35c29b6eb5</string> <key>hash_algorithm</key> <string>blake2b</string> <key>url</key> - <string>https://git.alchemyviewer.org/api/v4/projects/103/packages/generic/dullahan/1.14.0_119.3.1_gf768881_chromium-119.0.6045.124.2234/dullahan-1.14.0_119.3.1_gf768881_chromium-119.0.6045.124-windows64-2234.tar.zst</string> + <string>https://git.alchemyviewer.org/api/v4/projects/103/packages/generic/dullahan/1.12.4_118.4.1_g3dd6078_chromium-118.0.5993.54.2178/dullahan-1.12.4_118.4.1_g3dd6078_chromium-118.0.5993.54-windows64-2178.tar.zst</string> </map> <key>name</key> <string>windows64</string> @@ -521,7 +521,7 @@ <key>copyright</key> <string>Copyright (c) 2017, Linden Research, Inc.</string> <key>version</key> - <string>1.14.0_119.3.1_gf768881_chromium-119.0.6045.124</string> + <string>1.12.4_118.4.1_g3dd6078_chromium-118.0.5993.54</string> <key>name</key> <string>dullahan</string> <key>description</key> diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h index e20725579853b7245d81517a8ea8c01b62270b72..812e065aa57387e91aa82a84f5a0eab298c9f916 100644 --- a/indra/llappearance/llavatarappearance.h +++ b/indra/llappearance/llavatarappearance.h @@ -142,7 +142,7 @@ class LLAvatarAppearance : public LLCharacter LLVector3 mHeadOffset; // current head position LLAvatarJoint *mRoot; - typedef boost::unordered_flat_map<std::string, LLJoint*> joint_map_t; + typedef boost::unordered_flat_map<std::string, LLJoint*, al::string_hash, std::equal_to<>> joint_map_t; joint_map_t mJointMap; typedef std::map<std::string, LLVector3> joint_state_map_t; diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index 906f19203cfc6caa824586a2ffa604dc549431e5..5282900f9fc024d6cb6fba509b1570311983da6c 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -533,6 +533,9 @@ void LLVorbisDecodeState::flushBadFile() { LL_WARNS("AudioEngine") << "Flushing bad vorbis file from cache for " << mUUID << LL_ENDL; mInFilep->remove(); + + delete mInFilep; + mInFilep = NULL; } } @@ -623,6 +626,9 @@ void LLAudioDecodeMgr::Impl::startMoreDecodes() if (!decode_state) { + if (gAudiop) + gAudiop->markSoundCorrupt(decode_id); + // Audio decode has errored return decode_state; } @@ -789,6 +795,8 @@ void LLAudioDecodeMgr::processQueue() BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) { + if (gAudiop && gAudiop->isCorruptSound(uuid)) + return FALSE; if (gAudiop && gAudiop->hasDecodedFile(uuid)) { // Already have a decoded version, don't need to decode it. diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 0fa3de28008d002055f74575949b566fdca6c793..7d647d54e415b08e3528bd417ec8819783f7890f 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -642,6 +642,9 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid) { LL_DEBUGS("AudioEngine")<<"( "<<uuid<<" )"<<LL_ENDL; + if (isCorruptSound(uuid)) + return false; + getAudioData(uuid); // We don't care about the return value, this is just to make sure // that we have an entry, which will mean that the audio engine knows about this @@ -1925,3 +1928,23 @@ bool LLAudioData::load() mBufferp->mAudioDatap = this; return true; } + +const U32 MAX_SOUND_RETRIES = 25; + +void LLAudioEngine::markSoundCorrupt(const LLUUID& sound_id) +{ + auto itr = mCorruptData.find(sound_id); + if (mCorruptData.end() == itr) + mCorruptData[sound_id] = 1; + else if (itr->second != MAX_SOUND_RETRIES) + itr->second += 1; +} + +bool LLAudioEngine::isCorruptSound(const LLUUID& sound_id) const +{ + auto itr = mCorruptData.find(sound_id); + if (mCorruptData.end() == itr) + return false; + + return itr->second == MAX_SOUND_RETRIES; +} diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index d1c3dab9b3f1afc34f4b40281a8de26b9a634777..1171b63f150146e2a1c5dfc33846fd674e8d7087 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -43,6 +43,7 @@ #include "lllistener.h" #include "boost/unordered/unordered_flat_map.hpp" +#include "boost/unordered_map.hpp" const F32 LL_WIND_UPDATE_INTERVAL = 0.1f; const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f; @@ -51,7 +52,7 @@ const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f; const F32 DEFAULT_MIN_DISTANCE = 2.0f; #define LL_MAX_AUDIO_CHANNELS 30 -#define LL_MAX_AUDIO_BUFFERS 40 // Some extra for preloading, maybe? +#define LL_MAX_AUDIO_BUFFERS 60 // Some extra for preloading, maybe? class LLAudioSource; class LLAudioData; @@ -258,6 +259,13 @@ class LLAudioEngine private: void setDefaults(); LLStreamingAudioInterface *mStreamingAudioImpl; + + boost::unordered_map<LLUUID,U32> mCorruptData; + +public: + void markSoundCorrupt(LLUUID const&); + bool isCorruptSound(LLUUID const&) const; + }; diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index 4798ae05c0d96adfc7f30aad585a872f6330ad4f..075282051b04e4e2eb915d2d31dbd67bb38bb1a4 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -44,7 +44,7 @@ #include "sound_ids.h" -const U32 EXTRA_SOUND_CHANNELS = 100; +const U32 EXTRA_SOUND_CHANNELS = 10; FMOD_RESULT F_CALLBACK windDSPCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels); diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp index 45ef7910f702b5a3357037b67b365fbc39058634..817ddc509ba1a2c0a35705990d7ce7466cc2fcd2 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -36,33 +36,33 @@ inline bool Check_FMOD_Stream_Error(FMOD_RESULT result, const char *string) { - if (result == FMOD_OK) - return false; - LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL; - return true; + if (result == FMOD_OK) + return false; + LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL; + return true; } class LLAudioStreamManagerFMODSTUDIO { public: - LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url); - FMOD::Channel* startStream(); - bool stopStream(); // Returns true if the stream was successfully stopped. + LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url); + FMOD::Channel* startStream(); + bool stopStream(); // Returns true if the stream was successfully stopped. - const std::string& getURL() { return mInternetStreamURL; } + const std::string& getURL() { return mInternetStreamURL; } - FMOD_RESULT getOpenState(FMOD_OPENSTATE& openstate, unsigned int* percentbuffered = nullptr, bool* starving = nullptr, bool* diskbusy = nullptr); + FMOD_RESULT getOpenState(FMOD_OPENSTATE& openstate, unsigned int* percentbuffered = nullptr, bool* starving = nullptr, bool* diskbusy = nullptr); protected: - FMOD::System* mSystem; - FMOD::ChannelGroup* mChannelGroup; - FMOD::Channel* mStreamChannel; - FMOD::Sound* mInternetStream; - bool mReady; + FMOD::System* mSystem; + FMOD::ChannelGroup* mChannelGroup; + FMOD::Channel* mStreamChannel; + FMOD::Sound* mInternetStream; + bool mReady; - std::string mInternetStreamURL; + std::string mInternetStreamURL; }; -LLMutex gWaveDataMutex; //Just to be extra strict. +LLMutex gWaveDataMutex; //Just to be extra strict. const U32 WAVE_BUFFER_SIZE = 1024; U32 gWaveBufferMinSize = 0; F32 gWaveDataBuffer[WAVE_BUFFER_SIZE] = { 0.f }; @@ -70,392 +70,415 @@ U32 gWaveDataBufferSize = 0; FMOD_RESULT F_CALLBACK waveDataCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels) { - if (!length || !inchannels) - return FMOD_OK; - memcpy(outbuffer, inbuffer, length * inchannels * sizeof(float)); - - static std::vector<F32> local_buf; - if (local_buf.size() < length) - local_buf.resize(length, 0.f); - - for (U32 i = 0; i < length; ++i) - { - F32 total = 0.f; - for (S32 j = 0; j < inchannels; ++j) - { - total += inbuffer[i*inchannels + j]; - } - local_buf[i] = total / inchannels; - } - - { - LLMutexLock lock(&gWaveDataMutex); - - for (U32 i = length; i > 0; --i) - { - if (++gWaveDataBufferSize > WAVE_BUFFER_SIZE) - { - if (gWaveBufferMinSize) - memcpy(gWaveDataBuffer + WAVE_BUFFER_SIZE - gWaveBufferMinSize, gWaveDataBuffer, gWaveBufferMinSize * sizeof(float)); - gWaveDataBufferSize = 1 + gWaveBufferMinSize; - } - gWaveDataBuffer[WAVE_BUFFER_SIZE - gWaveDataBufferSize] = local_buf[i - 1]; - } - } - - return FMOD_OK; + if (!length || !inchannels) + return FMOD_OK; + memcpy(outbuffer, inbuffer, length * inchannels * sizeof(float)); + + static std::vector<F32> local_buf; + if (local_buf.size() < length) + local_buf.resize(length, 0.f); + + for (U32 i = 0; i < length; ++i) + { + F32 total = 0.f; + for (S32 j = 0; j < inchannels; ++j) + { + total += inbuffer[i*inchannels + j]; + } + local_buf[i] = total / inchannels; + } + + { + LLMutexLock lock(&gWaveDataMutex); + + for (U32 i = length; i > 0; --i) + { + if (++gWaveDataBufferSize > WAVE_BUFFER_SIZE) + { + if (gWaveBufferMinSize) + memcpy(gWaveDataBuffer + WAVE_BUFFER_SIZE - gWaveBufferMinSize, gWaveDataBuffer, gWaveBufferMinSize * sizeof(float)); + gWaveDataBufferSize = 1 + gWaveBufferMinSize; + } + gWaveDataBuffer[WAVE_BUFFER_SIZE - gWaveDataBufferSize] = local_buf[i - 1]; + } + } + + return FMOD_OK; } //--------------------------------------------------------------------------- // Internet Streaming //--------------------------------------------------------------------------- LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) : - mSystem(system), - mCurrentInternetStreamp(nullptr), - mStreamDSP(nullptr), - mStreamGroup(nullptr), - mFMODInternetStreamChannelp(nullptr), - mGain(1.0f), - mMetaData(nullptr), - mNewMetadata(true) + mSystem(system), + mCurrentInternetStreamp(nullptr), + mStreamDSP(nullptr), + mStreamGroup(nullptr), + mFMODInternetStreamChannelp(nullptr), + mGain(1.0f), + mWasAlreadyPlaying(false), + mMetaData(nullptr), + mNewMetadata(true) { - FMOD_RESULT result; + // Number of milliseconds of audio to buffer for the audio card. + // Must be larger than the usual Second Life frame stutter time. + const U32 buffer_seconds = 10; //sec + const U32 estimated_bitrate = 128; //kbit/sec + Check_FMOD_Stream_Error(mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize"); - // Number of milliseconds of audio to buffer for the audio card. - // Must be larger than the usual Second Life frame stutter time. - const U32 buffer_seconds = 10; //sec - const U32 estimated_bitrate = 128; //kbit/sec - result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); - Check_FMOD_Stream_Error(result, "FMOD::System::setStreamBufferSize"); + Check_FMOD_Stream_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup"); - Check_FMOD_Stream_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup"); + FMOD_DSP_DESCRIPTION dspdesc = { }; + dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION; + strncpy(dspdesc.name, "Waveform", sizeof(dspdesc.name)); + dspdesc.numoutputbuffers = 1; + dspdesc.read = &waveDataCallback; //Assign callback. - FMOD_DSP_DESCRIPTION dspdesc = { }; - dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION; - strncpy(dspdesc.name, "Waveform", sizeof(dspdesc.name)); - dspdesc.numoutputbuffers = 1; - dspdesc.read = &waveDataCallback; //Assign callback. - - Check_FMOD_Stream_Error(system->createDSP(&dspdesc, &mStreamDSP), "FMOD::System::createDSP"); + Check_FMOD_Stream_Error(system->createDSP(&dspdesc, &mStreamDSP), "FMOD::System::createDSP"); } LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO() { - stop(); - for (U32 i = 0; i < 100; ++i) - { - if (releaseDeadStreams()) - break; - ms_sleep(10); - } - - cleanupWaveData(); + stop(); + for (U32 i = 0; i < 100; ++i) + { + if (releaseDeadStreams()) + break; + ms_sleep(10); + } + + cleanupWaveData(); } void LLStreamingAudio_FMODSTUDIO::start(const std::string& url) { - //if (!mInited) - //{ - // LL_WARNS() << "startInternetStream before audio initialized" << LL_ENDL; - // return; - //} - - // "stop" stream but don't clear url, etc. in case url == mInternetStreamURL - stop(); - - if (!url.empty()) - { - if(mDeadStreams.empty()) - { - LL_INFOS() << "Starting internet stream: " << url << LL_ENDL; - mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, url); - mURL = url; - mMetaData = new LLSD; - } - else - { - LL_INFOS() << "Deferring stream load until buffer release: " << url << LL_ENDL; - mPendingURL = url; - } - } - else - { - LL_INFOS() << "Set internet stream to null" << LL_ENDL; - mURL.clear(); - } + //if (!mInited) + //{ + // LL_WARNS() << "startInternetStream before audio initialized" << LL_ENDL; + // return; + //} + + // "stop" stream but don't clear url, etc. in case url == mInternetStreamURL + stop(); + + if (!url.empty()) + { + if (mDeadStreams.empty()) + { + LL_INFOS() << "Starting internet stream: " << url << LL_ENDL; + mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, url); + mURL = url; + mMetaData = new LLSD; + } + else + { + LL_INFOS() << "Deferring stream load until buffer release: " << url << LL_ENDL; + mPendingURL = url; + } + } + else + { + LL_INFOS() << "Set internet stream to null" << LL_ENDL; + mURL.clear(); + } } enum utf_endian_type_t { - UTF16LE, - UTF16BE, - UTF16 + UTF16LE, + UTF16BE, + UTF16 }; std::string utf16input_to_utf8(unsigned char* input, U32 len, utf_endian_type_t type) { - if (type == UTF16) - { - type = UTF16BE; //Default - if (len > 2) - { - //Parse and strip BOM. - if ((input[0] == 0xFE && input[1] == 0xFF) || - (input[0] == 0xFF && input[1] == 0xFE)) - { - input += 2; - len -= 2; - type = input[0] == 0xFE ? UTF16BE : UTF16LE; - } - } - } - llutf16string out_16((llutf16string::value_type*)input, len / 2); - if (len % 2) - { - out_16.push_back((input)[len - 1] << 8); - } - if (type == UTF16BE) - { - for (llutf16string::iterator i = out_16.begin(); i < out_16.end(); ++i) - { - llutf16string::value_type v = *i; - *i = ((v & 0x00FF) << 8) | ((v & 0xFF00) >> 8); - } - } - return utf16str_to_utf8str(out_16); + if (type == UTF16) + { + type = UTF16BE; //Default + if (len > 2) + { + //Parse and strip BOM. + if ((input[0] == 0xFE && input[1] == 0xFF) || + (input[0] == 0xFF && input[1] == 0xFE)) + { + input += 2; + len -= 2; + type = input[0] == 0xFE ? UTF16BE : UTF16LE; + } + } + } + llutf16string out_16((llutf16string::value_type*)input, len / 2); + if (len % 2) + { + out_16.push_back((input)[len - 1] << 8); + } + if (type == UTF16BE) + { + for (llutf16string::iterator i = out_16.begin(); i < out_16.end(); ++i) + { + llutf16string::value_type v = *i; + *i = ((v & 0x00FF) << 8) | ((v & 0xFF00) >> 8); + } + } + return utf16str_to_utf8str(out_16); } void LLStreamingAudio_FMODSTUDIO::update() { - if (!releaseDeadStreams()) - { - llassert_always(mCurrentInternetStreamp == NULL); - return; - } - - if(!mPendingURL.empty()) - { - llassert_always(mCurrentInternetStreamp == NULL); - LL_INFOS() << "Starting internet stream: " << mPendingURL << LL_ENDL; - mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, mPendingURL); - mURL = mPendingURL; - mMetaData = new LLSD; - mPendingURL.clear(); - } - - // Don't do anything if there are no streams playing - if (!mCurrentInternetStreamp) - { - return; - } - - unsigned int progress; - bool starving; - bool diskbusy; - FMOD_OPENSTATE open_state; - FMOD_RESULT result = mCurrentInternetStreamp->getOpenState(open_state, &progress, &starving, &diskbusy); - - if (result != FMOD_OK || open_state == FMOD_OPENSTATE_ERROR) - { - stop(); - return; - } - else if (open_state == FMOD_OPENSTATE_READY) - { - // Stream is live - - // start the stream if it's ready - if (!mFMODInternetStreamChannelp && - (mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream())) - { - // Reset volume to previously set volume - setGain(getGain()); - if (mStreamDSP) - { - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->addDSP(FMOD_CHANNELCONTROL_DSP_TAIL, mStreamDSP), "FMOD::Channel::addDSP"); - } - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused"); - } - } - - - if(mFMODInternetStreamChannelp) - { - if(!mMetaData) - mMetaData = new LLSD; - - FMOD::Sound *sound = nullptr; - - if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound) - { - FMOD_TAG tag; - S32 tagcount, dirtytagcount; - if(sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount) - { - mMetaData->clear(); - mNewMetadata = true; - - for(S32 i = 0; i < tagcount; ++i) - { - if(sound->getTag(nullptr, i, &tag)!=FMOD_OK) - continue; - - std::string name = tag.name; - switch(tag.type) //Crappy tag translate table. - { - case(FMOD_TAGTYPE_ID3V2): - if (!LLStringUtil::compareInsensitive(name, "TIT2")) name = "TITLE"; - else if(name == "TPE1") name = "ARTIST"; - break; - case(FMOD_TAGTYPE_ASF): - if (!LLStringUtil::compareInsensitive(name, "Title")) name = "TITLE"; - else if (!LLStringUtil::compareInsensitive(name, "WM/AlbumArtist")) name = "ARTIST"; - break; - case(FMOD_TAGTYPE_FMOD): - if (!LLStringUtil::compareInsensitive(name, "Sample Rate Change")) - { - LL_INFOS() << "Stream forced changing sample rate to " << *((float *)tag.data) << LL_ENDL; - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)), "FMOD::Channel::setFrequency"); - } - continue; - default: - if (!LLStringUtil::compareInsensitive(name, "TITLE") || - !LLStringUtil::compareInsensitive(name, "ARTIST")) - LLStringUtil::toUpper(name); - break; - } - - switch(tag.datatype) - { - case(FMOD_TAGDATATYPE_INT): - (*mMetaData)[name]=*(LLSD::Integer*)(tag.data); - LL_INFOS() << tag.name << ": " << *(int*)(tag.data) << LL_ENDL; - break; - case(FMOD_TAGDATATYPE_FLOAT): - (*mMetaData)[name]=*(LLSD::Real*)(tag.data); - LL_INFOS() << tag.name << ": " << *(float*)(tag.data) << LL_ENDL; - break; - case(FMOD_TAGDATATYPE_STRING): - { - std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen)); - if (out.length() && out[out.size() - 1] == 0) - out.erase(out.size() - 1); - (*mMetaData)[name]=out; - LL_INFOS() << tag.name << "(RAW): " << out << LL_ENDL; - } - break; - case(FMOD_TAGDATATYPE_STRING_UTF8) : - { - U8 offs = 0; - if (tag.datalen > 3 && ((unsigned char*)tag.data)[0] == 0xEF && ((unsigned char*)tag.data)[1] == 0xBB && ((unsigned char*)tag.data)[2] == 0xBF) - offs = 3; - std::string out((char*)tag.data + offs, tag.datalen - offs); - if (out.length() && out[out.size() - 1] == 0) - out.erase(out.size() - 1); - (*mMetaData)[name] = out; - LL_INFOS() << tag.name << "(UTF8): " << out << LL_ENDL; - } - break; - case(FMOD_TAGDATATYPE_STRING_UTF16): - { - std::string out = utf16input_to_utf8((unsigned char*)tag.data, tag.datalen, UTF16); - if (out.length() && out[out.size() - 1] == 0) - out.erase(out.size() - 1); - (*mMetaData)[name] = out; - LL_INFOS() << tag.name << "(UTF16): " << out << LL_ENDL; - } - break; - case(FMOD_TAGDATATYPE_STRING_UTF16BE): - { - std::string out = utf16input_to_utf8((unsigned char*)tag.data, tag.datalen, UTF16BE); - if (out.length() && out[out.size() - 1] == 0) - out.erase(out.size() - 1); - (*mMetaData)[name] = out; - LL_INFOS() << tag.name << "(UTF16BE): " << out << LL_ENDL; - } - default: - break; - } - } - } - static bool was_starved = false; - if(starving) - { - bool paused = false; - if (mFMODInternetStreamChannelp->getPaused(&paused) == FMOD_OK && !paused) - { - LL_INFOS() << "Stream starvation detected! Pausing stream until buffer nearly full." << LL_ENDL; - LL_INFOS() << " (diskbusy="<<diskbusy<<")" << LL_ENDL; - LL_INFOS() << " (progress="<<progress<<")" << LL_ENDL; - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused"); - } - was_starved = true; - } - else if(progress > 80 && was_starved) - { - was_starved = false; - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused"); - } - } - } + if (!releaseDeadStreams()) + { + llassert_always(mCurrentInternetStreamp == NULL); + return; + } + + if (!mPendingURL.empty()) + { + llassert_always(mCurrentInternetStreamp == NULL); + LL_INFOS() << "Starting internet stream: " << mPendingURL << LL_ENDL; + mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, mPendingURL); + mURL = mPendingURL; + mMetaData = new LLSD; + mPendingURL.clear(); + } + + // Don't do anything if there are no streams playing + if (!mCurrentInternetStreamp) + { + return; + } + + unsigned int progress; + bool starving; + bool diskbusy; + FMOD_OPENSTATE open_state; + + if (Check_FMOD_Stream_Error(mCurrentInternetStreamp->getOpenState(open_state, &progress, &starving, &diskbusy), "FMOD::Sound::getOpenState") || open_state == FMOD_OPENSTATE_ERROR) + { + LL_WARNS() << "Internet stream openstate error: open_state = " << open_state << " - progress = " << progress << " - starving = " << starving << " - diskbusy = " << diskbusy << LL_ENDL; + bool was_playing = mWasAlreadyPlaying; + stop(); + // Try to restart previously playing stream on socket error + if (open_state == FMOD_OPENSTATE_ERROR && was_playing) + { + LL_WARNS() << "Stream was playing before - trying to restart" << LL_ENDL; + start(mURL); + } + return; + } + else if (open_state == FMOD_OPENSTATE_READY) + { + // Stream is live + + // start the stream if it's ready + if (!mFMODInternetStreamChannelp && + (mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream())) + { + // Reset volume to previously set volume + setGain(getGain()); + if (mStreamDSP) + { + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->addDSP(FMOD_CHANNELCONTROL_DSP_TAIL, mStreamDSP), "FMOD::Channel::addDSP"); + } + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused"); + mWasAlreadyPlaying = true; + } + } + else if (open_state == FMOD_OPENSTATE_PLAYING) + { + if (!mWasAlreadyPlaying) + { + mWasAlreadyPlaying = true; + } + } + + + if (mFMODInternetStreamChannelp) + { + if(!mMetaData) + mMetaData = new LLSD; + + FMOD::Sound *sound = nullptr; + + if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound) + { + FMOD_TAG tag; + S32 tagcount, dirtytagcount; + if(sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount) + { + mMetaData->clear(); + mNewMetadata = true; + + for(S32 i = 0; i < tagcount; ++i) + { + if(sound->getTag(nullptr, i, &tag)!=FMOD_OK) + continue; + + std::string name = tag.name; + switch (tag.type) + { + case FMOD_TAGTYPE_ID3V2: + { + if (!LLStringUtil::compareInsensitive(name, "TIT2")) name = "TITLE"; + else if(!LLStringUtil::compareInsensitive(name, "TPE1")) name = "ARTIST"; + break; + } + case FMOD_TAGTYPE_ASF: + { + if (!LLStringUtil::compareInsensitive(name, "Title")) name = "TITLE"; + else if (!LLStringUtil::compareInsensitive(name, "WM/AlbumArtist")) name = "ARTIST"; + break; + } + case FMOD_TAGTYPE_VORBISCOMMENT: + { + if (!LLStringUtil::compareInsensitive(name, "title")) name = "TITLE"; + else if (!LLStringUtil::compareInsensitive(name, "artist")) name = "ARTIST"; + break; + } + case FMOD_TAGTYPE_FMOD: + { + if (!LLStringUtil::compareInsensitive(name, "Sample Rate Change")) + { + LL_INFOS() << "Stream forced changing sample rate to " << *((float *)tag.data) << LL_ENDL; + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)), "FMOD::Channel::setFrequency"); + } + continue; + } + default: + if (!LLStringUtil::compareInsensitive(name, "TITLE") || + !LLStringUtil::compareInsensitive(name, "ARTIST")) + LLStringUtil::toUpper(name); + break; + } + switch (tag.datatype) + { + case(FMOD_TAGDATATYPE_INT): + (*mMetaData)[name]=*(LLSD::Integer*)(tag.data); + LL_INFOS() << tag.name << ": " << *(int*)(tag.data) << LL_ENDL; + break; + case(FMOD_TAGDATATYPE_FLOAT): + (*mMetaData)[name]=*(LLSD::Real*)(tag.data); + LL_INFOS() << tag.name << ": " << *(float*)(tag.data) << LL_ENDL; + break; + case(FMOD_TAGDATATYPE_STRING): + { + std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen)); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); + (*mMetaData)[name]=out; + LL_INFOS() << tag.name << "(RAW): " << out << LL_ENDL; + break; + } + case(FMOD_TAGDATATYPE_STRING_UTF8) : + { + U8 offs = 0; + if (tag.datalen > 3 && ((unsigned char*)tag.data)[0] == 0xEF && ((unsigned char*)tag.data)[1] == 0xBB && ((unsigned char*)tag.data)[2] == 0xBF) + offs = 3; + std::string out((char*)tag.data + offs, tag.datalen - offs); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); + (*mMetaData)[name] = out; + LL_INFOS() << tag.name << "(UTF8): " << out << LL_ENDL; + break; + } + case(FMOD_TAGDATATYPE_STRING_UTF16): + { + std::string out = utf16input_to_utf8((unsigned char*)tag.data, tag.datalen, UTF16); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); + (*mMetaData)[name] = out; + LL_INFOS() << tag.name << "(UTF16): " << out << LL_ENDL; + break; + } + case(FMOD_TAGDATATYPE_STRING_UTF16BE): + { + std::string out = utf16input_to_utf8((unsigned char*)tag.data, tag.datalen, UTF16BE); + if (out.length() && out[out.size() - 1] == 0) + out.erase(out.size() - 1); + (*mMetaData)[name] = out; + LL_INFOS() << tag.name << "(UTF16BE): " << out << LL_ENDL; + break; + } + default: + break; + } + } + } + + if (starving) + { + bool paused = false; + if (!Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->getPaused(&paused), "FMOD:Channel::getPaused") && !paused) + { + LL_INFOS() << "Stream starvation detected! Pausing stream until buffer nearly full." << LL_ENDL; + LL_INFOS() << " (diskbusy=" << diskbusy << ")" << LL_ENDL; + LL_INFOS() << " (progress=" << progress << ")" << LL_ENDL; + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused"); + } + } + else if(progress > 80) + { + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused"); + } + } + } } void LLStreamingAudio_FMODSTUDIO::stop() { - mPendingURL.clear(); - - if(mMetaData) - { - delete mMetaData; - mMetaData = nullptr; - } - - if (mFMODInternetStreamChannelp) - { - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused"); - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPriority(0), "FMOD::Channel::setPriority"); - if (mStreamDSP) - { - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->removeDSP(mStreamDSP), "FMOD::Channel::removeDSP"); - } - mFMODInternetStreamChannelp = nullptr; - } - - if (mCurrentInternetStreamp) - { + mPendingURL.clear(); + mWasAlreadyPlaying = false; + + if(mMetaData) + { + delete mMetaData; + mMetaData = nullptr; + } + + if (mFMODInternetStreamChannelp) + { + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused"); + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPriority(0), "FMOD::Channel::setPriority"); + if (mStreamDSP) + { + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->removeDSP(mStreamDSP), "FMOD::Channel::removeDSP"); + } + mFMODInternetStreamChannelp = nullptr; + } + + if (mCurrentInternetStreamp) + { LL_INFOS("FMOD") << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << LL_ENDL; - if (mCurrentInternetStreamp->stopStream()) - { - delete mCurrentInternetStreamp; - } - else - { + if (mCurrentInternetStreamp->stopStream()) + { + delete mCurrentInternetStreamp; + } + else + { LL_WARNS("FMOD") << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << LL_ENDL; - mDeadStreams.push_back(mCurrentInternetStreamp); - } - mCurrentInternetStreamp = nullptr; - } + mDeadStreams.push_back(mCurrentInternetStreamp); + } + mCurrentInternetStreamp = nullptr; + } } void LLStreamingAudio_FMODSTUDIO::pause(S32 pauseopt) { - if (pauseopt < 0) - { - pauseopt = mCurrentInternetStreamp ? 1 : 0; - } - - if (pauseopt) - { - if (mCurrentInternetStreamp) - { - LL_INFOS("FMOD") << "Pausing internet stream" << LL_ENDL; - stop(); - } - } - else - { - start(getURL()); - } + if (pauseopt < 0) + { + pauseopt = mCurrentInternetStreamp ? 1 : 0; + } + + if (pauseopt) + { + if (mCurrentInternetStreamp) + { + stop(); + } + } + else + { + start(getURL()); + } } @@ -463,212 +486,212 @@ void LLStreamingAudio_FMODSTUDIO::pause(S32 pauseopt) // doesn't necessarily mean audio is coming out of the speakers. int LLStreamingAudio_FMODSTUDIO::isPlaying() { - if (mCurrentInternetStreamp) - { - return 1; // Active and playing - } - else if (!mURL.empty() || !mPendingURL.empty()) - { - return 2; // "Paused" - } - else - { - return 0; - } + if (mCurrentInternetStreamp) + { + return 1; // Active and playing + } + else if (!mURL.empty() || !mPendingURL.empty()) + { + return 2; // "Paused" + } + else + { + return 0; + } } F32 LLStreamingAudio_FMODSTUDIO::getGain() { - return mGain; + return mGain; } std::string LLStreamingAudio_FMODSTUDIO::getURL() { - return mURL; + return mURL; } void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol) { - mGain = vol; + mGain = vol; - if (mFMODInternetStreamChannelp) - { - vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here? + if (mFMODInternetStreamChannelp) + { + vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here? - Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setVolume(vol), "FMOD::Channel::setVolume"); - } + Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setVolume(vol), "FMOD::Channel::setVolume"); + } } bool LLStreamingAudio_FMODSTUDIO::hasNewMetaData() { - if (mCurrentInternetStreamp && mNewMetadata) - { - mNewMetadata = false; - return true; - } - else - { - return false; - } + if (mCurrentInternetStreamp && mNewMetadata) + { + mNewMetadata = false; + return true; + } + else + { + return false; + } } /* virtual */ bool LLStreamingAudio_FMODSTUDIO::getWaveData(float* arr, S32 count, S32 stride/*=1*/) { - if (count > (WAVE_BUFFER_SIZE / 2)) - LL_ERRS("AudioImpl") << "Count=" << count << " exceeds WAVE_BUFFER_SIZE/2=" << WAVE_BUFFER_SIZE << LL_ENDL; - - if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp) - return false; - - bool muted = false; - FMOD_RESULT res = mFMODInternetStreamChannelp->getMute(&muted); - if(res != FMOD_OK || muted) - return false; - { - U32 buff_size; - { - LLMutexLock lock(&gWaveDataMutex); - gWaveBufferMinSize = count; - buff_size = gWaveDataBufferSize; - if (!buff_size) - return false; - memcpy(arr, gWaveDataBuffer + WAVE_BUFFER_SIZE - buff_size, llmin(U32(count), buff_size) * sizeof(float)); - } - if (buff_size < U32(count)) - memset(arr + buff_size, 0, (count - buff_size) * sizeof(float)); - } - return true; + if (count > (WAVE_BUFFER_SIZE / 2)) + LL_ERRS("AudioImpl") << "Count=" << count << " exceeds WAVE_BUFFER_SIZE/2=" << WAVE_BUFFER_SIZE << LL_ENDL; + + if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp) + return false; + + bool muted = false; + FMOD_RESULT res = mFMODInternetStreamChannelp->getMute(&muted); + if(res != FMOD_OK || muted) + return false; + { + U32 buff_size; + { + LLMutexLock lock(&gWaveDataMutex); + gWaveBufferMinSize = count; + buff_size = gWaveDataBufferSize; + if (!buff_size) + return false; + memcpy(arr, gWaveDataBuffer + WAVE_BUFFER_SIZE - buff_size, llmin(U32(count), buff_size) * sizeof(float)); + } + if (buff_size < U32(count)) + memset(arr + buff_size, 0, (count - buff_size) * sizeof(float)); + } + return true; } /////////////////////////////////////////////////////// // manager of possibly-multiple internet audio streams LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url) : - mSystem(system), - mChannelGroup(group), - mStreamChannel(nullptr), - mInternetStream(nullptr), - mReady(false), - mInternetStreamURL(url) + mSystem(system), + mChannelGroup(group), + mStreamChannel(nullptr), + mInternetStream(nullptr), + mReady(false), + mInternetStreamURL(url) { - FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, nullptr, &mInternetStream); + FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, nullptr, &mInternetStream); - if (result!= FMOD_OK) - { + if (result != FMOD_OK) + { LL_WARNS("FMOD") << "Couldn't open fmod stream, error " - << FMOD_ErrorString(result) - << LL_ENDL; - mReady = false; - return; - } + << FMOD_ErrorString(result) + << LL_ENDL; + mReady = false; + return; + } - mReady = true; + mReady = true; } FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream() { - // We need a live and opened stream before we try and play it. - FMOD_OPENSTATE open_state; - if (!mInternetStream || (getOpenState(open_state) != FMOD_OK || open_state != FMOD_OPENSTATE_READY)) - { + // We need a live and opened stream before we try and play it. + FMOD_OPENSTATE open_state; + if (!mInternetStream || Check_FMOD_Stream_Error(getOpenState(open_state), "FMOD::Sound::getOpenState") || open_state != FMOD_OPENSTATE_READY) + { LL_WARNS("FMOD") << "No internet stream to start playing!" << LL_ENDL; - return nullptr; - } + return nullptr; + } - if(mStreamChannel) - return mStreamChannel; //Already have a channel for this stream. + if (mStreamChannel) + return mStreamChannel; //Already have a channel for this stream. - Check_FMOD_Stream_Error(mSystem->playSound(mInternetStream, mChannelGroup, true, &mStreamChannel), "FMOD::System::playSound"); - return mStreamChannel; + Check_FMOD_Stream_Error(mSystem->playSound(mInternetStream, mChannelGroup, true, &mStreamChannel), "FMOD::System::playSound"); + return mStreamChannel; } bool LLAudioStreamManagerFMODSTUDIO::stopStream() { - if (mInternetStream) - { - bool close = true; - FMOD_OPENSTATE open_state; - if (getOpenState(open_state) == FMOD_OK) - { - switch (open_state) - { - case FMOD_OPENSTATE_CONNECTING: - close = false; - break; - default: - close = true; - } - } - - if (close && mInternetStream->release() == FMOD_OK) - { - mStreamChannel = nullptr; - mInternetStream = nullptr; - return true; - } - else - { - return false; - } - } - else - { - return true; - } + if (mInternetStream) + { + bool close = true; + FMOD_OPENSTATE open_state; + if (getOpenState(open_state) == FMOD_OK) + { + switch (open_state) + { + case FMOD_OPENSTATE_CONNECTING: + close = false; + break; + default: + close = true; + } + } + + if (close && mInternetStream->release() == FMOD_OK) + { + mStreamChannel = nullptr; + mInternetStream = nullptr; + return true; + } + else + { + return false; + } + } + else + { + return true; + } } FMOD_RESULT LLAudioStreamManagerFMODSTUDIO::getOpenState(FMOD_OPENSTATE& state, unsigned int* percentbuffered, bool* starving, bool* diskbusy) { - if (!mInternetStream) - return FMOD_ERR_INVALID_HANDLE; - FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); - Check_FMOD_Stream_Error(result, "FMOD::Sound::getOpenState"); - return result; + if (!mInternetStream) + return FMOD_ERR_INVALID_HANDLE; + FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + Check_FMOD_Stream_Error(result, "FMOD::Sound::getOpenState"); + return result; } void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) { - Check_FMOD_Stream_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize"); - FMOD_ADVANCEDSETTINGS settings = { }; - settings.cbSize=sizeof(settings); - settings.defaultDecodeBufferSize = decodebuffertime;//ms - Check_FMOD_Stream_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings"); + Check_FMOD_Stream_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize"); + FMOD_ADVANCEDSETTINGS settings = { }; + settings.cbSize = sizeof(settings); + settings.defaultDecodeBufferSize = decodebuffertime;//ms + Check_FMOD_Stream_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings"); } bool LLStreamingAudio_FMODSTUDIO::releaseDeadStreams() { - // Kill dead internet streams, if possible - for (auto iter = mDeadStreams.begin(); iter != mDeadStreams.end();) - { - LLAudioStreamManagerFMODSTUDIO *streamp = *iter; - if (streamp->stopStream()) - { - LL_INFOS() << "Closed dead stream" << LL_ENDL; - delete streamp; - mDeadStreams.erase(iter++); - } - else - { - ++iter; - } - } - - return mDeadStreams.empty(); + // Kill dead internet streams, if possible + for (auto iter = mDeadStreams.begin(); iter != mDeadStreams.end();) + { + LLAudioStreamManagerFMODSTUDIO *streamp = *iter; + if (streamp->stopStream()) + { + LL_INFOS() << "Closed dead stream" << LL_ENDL; + delete streamp; + iter = mDeadStreams.erase(iter); + } + else + { + ++iter; + } + } + + return mDeadStreams.empty(); } void LLStreamingAudio_FMODSTUDIO::cleanupWaveData() { - if (mStreamGroup) - { - Check_FMOD_Stream_Error(mStreamGroup->release(), "FMOD::ChannelGroup::release"); - mStreamGroup = nullptr; - } - - if(mStreamDSP) - Check_FMOD_Stream_Error(mStreamDSP->release(), "FMOD::DSP::release"); - mStreamDSP = nullptr; + if (mStreamGroup) + { + Check_FMOD_Stream_Error(mStreamGroup->release(), "FMOD::ChannelGroup::release"); + mStreamGroup = nullptr; + } + + if(mStreamDSP) + Check_FMOD_Stream_Error(mStreamDSP->release(), "FMOD::DSP::release"); + mStreamDSP = nullptr; } diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.h b/indra/llaudio/llstreamingaudio_fmodstudio.h index ea1b5c81a8357b5bfaf7699283b47fbb5d11b724..ccfc16961f6de1c243b624da334819b65fea6fd6 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.h +++ b/indra/llaudio/llstreamingaudio_fmodstudio.h @@ -82,6 +82,8 @@ class LLStreamingAudio_FMODSTUDIO final : public LLStreamingAudioInterface std::string mPendingURL; F32 mGain; + bool mWasAlreadyPlaying; + LLSD *mMetaData; bool mNewMetadata; }; diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 636083787d86c1d3e2bc368ff52ce554befa9d0a..1b2427c633406545fd03f500c5722333e973253f 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -499,7 +499,7 @@ namespace } - typedef boost::unordered_flat_map<std::string, LLError::ELevel> LevelMap; + typedef boost::unordered_flat_map<std::string, LLError::ELevel, al::string_hash, std::equal_to<>> LevelMap; typedef std::vector<LLError::RecorderPtr> Recorders; typedef std::vector<LLError::CallSite*> CallSiteVector; diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index f219dce9fcd0e1134a04194825d076ef981e1466..25320fb683a95c58a1472a6db824e8bbfd1290ab 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -265,7 +265,7 @@ namespace LLInitParam private: struct Inaccessable{}; public: - typedef boost::unordered_flat_map<std::string, T> value_name_map_t; + typedef boost::unordered_flat_map<std::string, T, al::string_hash, std::equal_to<>> value_name_map_t; typedef Inaccessable name_t; typedef TypeValues<T> type_value_t; typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; diff --git a/indra/llinventory/llsettingsbase.h b/indra/llinventory/llsettingsbase.h index e420e4a8107874d258f1278507a7ec77c4dbca5b..49a73d3b074d9eb6040832f1fb87a9cbe489cf05 100644 --- a/indra/llinventory/llsettingsbase.h +++ b/indra/llinventory/llsettingsbase.h @@ -95,7 +95,7 @@ class LLSettingsBase : }; // Contains settings' names (map key), related shader id-key and default // value for revert in case we need to reset shader (no need to search each time) - typedef boost::unordered_flat_map<std::string, DefaultParam> parammapping_t; + typedef boost::unordered_flat_map<std::string, DefaultParam, al::string_hash, std::equal_to<>> parammapping_t; typedef PTR_NAMESPACE::shared_ptr<LLSettingsBase> ptr_t; @@ -329,7 +329,7 @@ class LLSettingsBase : LLSettingsBase(); LLSettingsBase(const LLSD setting); - typedef boost::unordered_flat_set<std::string> stringset_t; + typedef boost::unordered_flat_set<std::string, al::string_hash, std::equal_to<>> stringset_t; // combining settings objects. Customize for specific setting types virtual void lerpSettings(const LLSettingsBase &other, BlendFactor mix); diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index 366ce79381411c0b3e1331a39345ed6c066b4142..1c142502abc2a54a2d82a70602b23beab57311fc 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -185,7 +185,7 @@ typedef std::set<LLUUID> AskQueue; typedef std::list<PendingReply*> ReplyQueue; typedef std::map<LLUUID,U32> PendingQueue; typedef boost::unordered_flat_map<LLUUID, LLCacheNameEntry*> Cache; -typedef boost::unordered_flat_map<std::string, LLUUID> ReverseCache; +typedef boost::unordered_flat_map<std::string, LLUUID, al::string_hash, std::equal_to<>> ReverseCache; class LLCacheName::Impl { diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h index bca5960eb17d90e53a167c382b7e90c4a54d7c62..e58b11af7b42f8bc5c1e2d1a964462fcc63dce3e 100644 --- a/indra/llprimitive/lltextureentry.h +++ b/indra/llprimitive/lltextureentry.h @@ -84,10 +84,8 @@ class LLTextureEntry final LLTextureEntry(); LLTextureEntry(const LLUUID& tex_id); LLTextureEntry(const LLTextureEntry &rhs); - LLTextureEntry(LLTextureEntry&& rhs) noexcept; LLTextureEntry& operator=(const LLTextureEntry &rhs); - LLTextureEntry& operator=(LLTextureEntry&& rhs) noexcept; ~LLTextureEntry(); bool operator==(const LLTextureEntry &rhs) const; diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index c45be78039d2798bb98311e8f287f5ab30952eec..32f900105439a49465b50dea4b226e4936cd0fac 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -17569,7 +17569,7 @@ <key>Type</key> <string>U32</string> <key>Value</key> - <integer>4000</integer> + <integer>7000</integer> </map> <key>DisablePrecacheDelayAfterTeleporting</key> <map> diff --git a/indra/newview/llfloatermessagelog.h b/indra/newview/llfloatermessagelog.h index a1f6edcf96b3fbd6d54059787aca85f30e33b7c6..ece7665ded2ca5c45a55b7b16e5dfdf5dfc463da 100644 --- a/indra/newview/llfloatermessagelog.h +++ b/indra/newview/llfloatermessagelog.h @@ -37,7 +37,7 @@ class LLFloaterMessageLog; typedef boost::circular_buffer<LogPayload> LogPayloadList; typedef std::shared_ptr<LLEasyMessageLogEntry> FloaterMessageItem; typedef std::vector<FloaterMessageItem> FloaterMessageList; -typedef boost::container::flat_map<U64, FloaterMessageItem> HTTPConvoMap; +typedef boost::unordered_map<U64, FloaterMessageItem> HTTPConvoMap; class LLMessageLogFilter { @@ -51,8 +51,8 @@ class LLMessageLogFilter std::string asString() const { return mInputString; } //these should probably be unordered_sets - boost::container::flat_set<std::string> mPositiveNames; - boost::container::flat_set<std::string> mNegativeNames; + boost::unordered_flat_set<std::string, al::string_hash, std::equal_to<>> mPositiveNames; + boost::unordered_flat_set<std::string, al::string_hash, std::equal_to<>> mNegativeNames; protected: std::string mInputString; diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index 1792af64f978a3d4e958e354d9c3ec91cfd43732..ca9f210f67734f61ddadd6aaafb6db6692f56567 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -250,8 +250,12 @@ BOOL LLGroupMgrGroupData::getRoleData(const LLUUID& role_id, LLRoleData& role_da role_list_t::const_iterator rit = mRoles.find(role_id); if (rit != mRoles.end()) { - role_data = (*rit).second->getRoleData(); - return TRUE; + auto& role_datap = rit->second; + if (role_datap) + { + role_data = role_datap->getRoleData(); + return TRUE; + } } // This role must not exist. @@ -1451,11 +1455,11 @@ LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) { LLGroupMgrGroupData* group_datap = NULL; - group_map_t::iterator existing_group = LLGroupMgr::getInstance()->mGroups.find(id); - if (existing_group == LLGroupMgr::getInstance()->mGroups.end()) + group_map_t::iterator existing_group = mGroups.find(id); + if (existing_group == mGroups.end()) { group_datap = new LLGroupMgrGroupData(id); - LLGroupMgr::getInstance()->addGroup(group_datap); + addGroup(group_datap); } else { @@ -1472,8 +1476,8 @@ LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id) bool LLGroupMgr::hasPendingPropertyRequest(const LLUUID & id) { - properties_request_map_t::iterator existing_req = LLGroupMgr::getInstance()->mPropRequests.find(id); - if (existing_req != LLGroupMgr::getInstance()->mPropRequests.end()) + properties_request_map_t::iterator existing_req = mPropRequests.find(id); + if (existing_req != mPropRequests.end()) { if (gFrameTime - existing_req->second < MIN_GROUP_PROPERTY_REQUEST_FREQ) { @@ -1481,7 +1485,7 @@ bool LLGroupMgr::hasPendingPropertyRequest(const LLUUID & id) } else { - LLGroupMgr::getInstance()->mPropRequests.erase(existing_req); + mPropRequests.erase(existing_req); } } return false; @@ -1489,7 +1493,7 @@ bool LLGroupMgr::hasPendingPropertyRequest(const LLUUID & id) void LLGroupMgr::addPendingPropertyRequest(const LLUUID& id) { - LLGroupMgr::getInstance()->mPropRequests.insert_or_assign(id, gFrameTime); + mPropRequests.insert_or_assign(id, gFrameTime); } void LLGroupMgr::notifyObservers(LLGroupChange gc) @@ -1579,12 +1583,12 @@ void LLGroupMgr::sendGroupPropertiesRequest(const LLUUID& group_id) // This will happen when we get the reply //LLGroupMgrGroupData* group_datap = createGroupData(group_id); - if (LLGroupMgr::getInstance()->hasPendingPropertyRequest(group_id)) + if (hasPendingPropertyRequest(group_id)) { LL_DEBUGS("GrpMgr") << "LLGroupMgr::sendGroupPropertiesRequest suppressed repeat for " << group_id << LL_ENDL; return; } - LLGroupMgr::getInstance()->addPendingPropertyRequest(group_id); + addPendingPropertyRequest(group_id); LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_GroupProfileRequest); @@ -1926,7 +1930,7 @@ void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id, for (LLGroupMemberData::role_list_t::iterator rit = member_data->roleBegin(); rit != member_data->roleEnd(); ++rit) { - if ((*rit).first.notNull() && (*rit).second != 0) + if ((*rit).first.notNull() && (*rit).second != nullptr) { (*rit).second->removeMember(ejected_member_id); } @@ -2213,7 +2217,7 @@ void LLGroupMgr::processCapGroupMembersRequest(const LLSD& content) // Set mMemberDataComplete for correct handling of empty responses. See MAINT-5237 group_datap->mMemberDataComplete = true; group_datap->mChanged = TRUE; - LLGroupMgr::getInstance()->notifyObservers(GC_MEMBER_DATA); + notifyObservers(GC_MEMBER_DATA); return; } diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h index 2fac45ece7d29791cfad98d85f24a471e47a2744..4fc56a9b4dabd59d1078aae35d63e4c914b08050 100644 --- a/indra/newview/llgroupmgr.h +++ b/indra/newview/llgroupmgr.h @@ -77,7 +77,7 @@ class LLGroupMemberData friend class LLGroupMgrGroupData; public: - typedef boost::unordered_flat_map<LLUUID, LLGroupRoleData*> role_list_t; + typedef boost::unordered_map<LLUUID,LLGroupRoleData*> role_list_t; LLGroupMemberData(const LLUUID& id, S32 contribution, @@ -102,8 +102,6 @@ friend class LLGroupMgrGroupData; BOOL isInRole(const LLUUID& role_id) { return (mRolesList.find(role_id) != mRolesList.end()); } - const role_list_t& getRoles() { return mRolesList; } - LLUUID mID; S32 mContribution; U64 mAgentPowers; @@ -117,6 +115,36 @@ struct LLRoleData { LLRoleData() : mRolePowers(0), mChangeType(RC_UPDATE_NONE) { } + LLRoleData(const LLRoleData& rd) + { + *this = rd; + } + + LLRoleData(LLRoleData&& rd) + { + *this = std::move(rd); + } + + LLRoleData& operator=(const LLRoleData& rd) + { + mRoleName = rd.mRoleName; + mRoleTitle = rd.mRoleTitle; + mRoleDescription = rd.mRoleDescription; + mRolePowers = rd.mRolePowers; + mChangeType = rd.mChangeType; + return *this; + }; + + LLRoleData& operator=(LLRoleData&& rd) noexcept + { + mRoleName = std::move(rd.mRoleName); + mRoleTitle = std::move(rd.mRoleTitle); + mRoleDescription = std::move(rd.mRoleDescription); + mRolePowers = rd.mRolePowers; + mChangeType = rd.mChangeType; + return *this; + }; + std::string mRoleName; std::string mRoleTitle; std::string mRoleDescription; diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index 044affe4f5cb632e0967f0dc10f2ed1e0a33760c..c3721ddfa0c85f71a95b4fff59915943f04fbeb7 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -423,6 +423,10 @@ void LLPanelGroupRoles::setGroupID(const LLUUID& id) if ( button ) button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE)); + button = getChild<LLButton>("export_list"); + if (button) + button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_VISIBLE_IN_DIR)); + if(mSubTabContainer) mSubTabContainer->selectTab(1); group_roles_tab->mFirstOpen = TRUE; @@ -1089,7 +1093,7 @@ void LLPanelGroupMembersSubTab::handleMemberSelect() else { // This could happen if changes are not synced right on sub-panel change. - LL_WARNS() << "No group role data for " << iter->second << LL_ENDL; + LL_WARNS() << "No group role data for " << iter->first << LL_ENDL; } } mAssignedRolesList->setEnabled(TRUE); @@ -1815,6 +1819,15 @@ void LLPanelGroupMembersSubTab::updateMembers() mMembersList->deleteAllItems(); } + for (avatar_name_cache_connection_map_t::iterator it = mAvatarNameCacheConnections.begin(); it != mAvatarNameCacheConnections.end(); ++it) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + } + mAvatarNameCacheConnections.clear(); + LLGroupMgrGroupData::member_list_t::iterator end = gdatap->mMembers.end(); LLTimer update_time; @@ -2403,7 +2416,7 @@ void LLPanelGroupRolesSubTab::handleRoleSelect() mSelectedRole = item->getUUID(); buildMembersList(); - mCopyRoleButton->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CREATE)); + mCopyRoleButton->setEnabled((gdatap->mRoles.size() < (U32)MAX_ROLES) && gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CREATE)); can_delete = can_delete && gAgent.hasPowerInGroup(mGroupID, GP_ROLE_DELETE); mDeleteRoleButton->setEnabled(can_delete); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index e7fbbf16888d0edd790c3a7c6eda2884d83279c5..49a8c52cd7aa70d07581a118991d6115044103f8 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -164,7 +164,7 @@ using namespace LLAvatarAppearanceDefines; typedef LLPointer<LLViewerObject> LLViewerObjectPtr; -static boost::unordered_flat_map<std::string, LLStringExplicit> sDefaultItemLabels; +static boost::unordered_flat_map<std::string, LLStringExplicit, al::string_hash, std::equal_to<>> sDefaultItemLabels; BOOL enable_land_build(void*); BOOL enable_object_build(void*); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index e3339d215b9b308871eb8a5fdde8f1d131fbf31e..564c7efbb435179efc541e45b521f2e5eea84c0b 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -4202,6 +4202,10 @@ void process_sound_trigger(LLMessageSystem *msg, void **) msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_SoundID, sound_id); msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_OwnerID, owner_id); msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ObjectID, object_id); + + if (gAudiop->isCorruptSound(sound_id)) + return; + msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ParentID, parent_id); msg->getU64Fast(_PREHASH_SoundData, _PREHASH_Handle, region_handle); msg->getVector3Fast(_PREHASH_SoundData, _PREHASH_Position, pos_local); @@ -4273,6 +4277,9 @@ void process_preload_sound(LLMessageSystem *msg, void **user_data) msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id); msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id); + if (gAudiop->isCorruptSound(sound_id)) + return; + LLViewerObject *objectp = gObjectList.findObject(object_id); if (!objectp) return; diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index da7c3fbe6b548ac06b91b87a74420c206be99248..7f575e993c3f6a6505ec106adfa4a91e7cdb2776 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -696,7 +696,7 @@ class LLViewerRegion final : public LLCapabilityProvider // implements this inte std::vector<LLPointer<LLReflectionMap> > mReflectionMaps; mutable tex_matrix_t mWorldMapTiles; - boost::unordered_flat_set<std::string> mGodNames; + boost::unordered_flat_set<std::string, al::string_hash, std::equal_to<>> mGodNames; LLEasyMessageSender mMessageSender; using url_mapping_t = boost::unordered_multimap<std::string, std::string>; diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index a05d045d8ceda894b3bc6a0f2c231a12cb8e98d8..c03c45551e1001e721fc9a149abdac13ba0222b6 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -70,7 +70,7 @@ max_chars="128" combo_editor.commit_on_focus_lost="false" combo_editor.prevalidate_callback="ascii" - tool_tip="The account name you chose when you registered, like bobsmith420 or Steller Sunshine" + tool_tip="The account name you chose when you registered, like bobsmith12 or Steller Sunshine" top_pad="0" name="username_combo" width="178"> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 7a69c997c9ceaf4f02bcb97413d35a08d16c33cf..281c0141f2edc27596f9cbd76be7bb4a97f92f39 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -160,7 +160,7 @@ If you feel this is an error, please contact support.</string> <string name="LoginFailedAlreadyLoggedIn">This agent is already logged in.</string> <string name="LoginFailedAuthenticationFailed">Sorry! We couldn't log you in. Please check to make sure you entered the right - * Username (like yiffmaster69 or steller.sunshine) + * Username (like bobsmith12 or steller.sunshine) * Password * Second Factor Token (if enabled) Also, please make sure your Caps Lock key is off.</string> diff --git a/indra/newview/skins/heretic/colors.xml b/indra/newview/skins/heretic/colors.xml index ab7159606bf220d6e20de1b6176afd1933a394a5..b05ea6934456c7201a9da4ecc52925ec2d9e522e 100644 --- a/indra/newview/skins/heretic/colors.xml +++ b/indra/newview/skins/heretic/colors.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <colors> - <color name="ScriptDialog" value="0.25 0.25 0.25 1" /> diff --git a/indra/newview/skins/heretic/xui/en/panel_login.xml b/indra/newview/skins/heretic/xui/en/panel_login.xml index 26be5e536deab70a212613be31ae2d4278899946..b854c5de8cefaacda3c13436d9de3d31d24df2e0 100644 --- a/indra/newview/skins/heretic/xui/en/panel_login.xml +++ b/indra/newview/skins/heretic/xui/en/panel_login.xml @@ -71,7 +71,7 @@ max_chars="128" combo_editor.commit_on_focus_lost="false" combo_editor.prevalidate_callback="ascii" - tool_tip="The account name you chose when you registered, like yiffmaster420 or Steller Sunshine" + tool_tip="The account name you chose when you registered, like bobsmith12 or Steller Sunshine" top_pad="0" name="username_combo" width="178"> diff --git a/indra/newview/skins/heretic/xui/en/widgets/floater.xml b/indra/newview/skins/heretic/xui/en/widgets/floater.xml index a8c63dca7f54f7d78edc998301d1a005aee2e635..e53ee2521d1c384c4d036ad4eac818dc148ba470 100644 --- a/indra/newview/skins/heretic/xui/en/widgets/floater.xml +++ b/indra/newview/skins/heretic/xui/en/widgets/floater.xml @@ -12,16 +12,16 @@ background_opaque="false" header_height="20" close_image="Icon_Close_Foreground" - snooze_image="Icon_Snooze_Foreground" restore_image="Icon_Restore_Foreground" minimize_image="Icon_Minimize_Foreground" + collapse_image="Icon_Collapse_Foreground" tear_off_image="tearoffbox.tga" dock_image="Icon_Dock_Foreground" help_image="Icon_Help_Foreground" close_pressed_image="Icon_Close_Press" - snooze_pressed_image="Icon_Snooze_Press" restore_pressed_image="Icon_Restore_Press" minimize_pressed_image="Icon_Minimize_Press" + collapse_pressed_image="Icon_Collapse_Press" tear_off_pressed_image="tearoff_pressed.tga" dock_pressed_image="Icon_Dock_Press" help_pressed_image="Icon_Help_Press" diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp index ba4e7f77ec4533b26d491696d55ab77ff4229549..122a2a3016b121125f4499fcc391d73a146c264e 100644 --- a/indra/newview/tests/llviewerassetstats_test.cpp +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -217,7 +217,7 @@ get_region(const LLSD & sd, U64 region_handle1) U32 grid_x(0), grid_y(0); grid_from_region_handle(region_handle1, &grid_x, &grid_y); - for (const LLSD& llsd_val : sd["regions"].array()) + for (const LLSD& llsd_val : sd["regions"].asArray()) { if (llsd_val.has("grid_x") && llsd_val.has("grid_y") &&