From a3f7399d7d785cd3dbbc923a11227809524fd4a1 Mon Sep 17 00:00:00 2001 From: Shyotl Date: Tue, 3 Jan 2012 20:27:56 -0600 Subject: [PATCH] Added media ticker to Singularity menu. Will be greyed out for anything not FMOD based. Consider implementing in other sound libraries as a to-do. FMOD Ex also displays an oscillator (making fmod3 do this is too dirty, so that's not going to be backported). --- indra/llaudio/llstreamingaudio.h | 6 +- indra/llaudio/llstreamingaudio_fmod.h | 5 +- indra/llaudio/llstreamingaudio_fmodex.cpp | 21 ++ indra/llaudio/llstreamingaudio_fmodex.h | 5 +- indra/newview/CMakeLists.txt | 2 + indra/newview/app_settings/settings_sh.xml | 29 ++ indra/newview/llstartup.cpp | 7 + indra/newview/llviewergesture.cpp | 2 +- indra/newview/llviewermedia_streamingaudio.h | 6 +- indra/newview/llviewermenu.cpp | 3 + indra/newview/shfloatermediaticker.cpp | 262 ++++++++++++++++++ indra/newview/shfloatermediaticker.h | 55 ++++ .../textures/ticker_background_small.tga | Bin 0 -> 12332 bytes .../xui/en-us/sh_floater_media_ticker.xml | 39 +++ 14 files changed, 437 insertions(+), 5 deletions(-) create mode 100644 indra/newview/shfloatermediaticker.cpp create mode 100644 indra/newview/shfloatermediaticker.h create mode 100644 indra/newview/skins/default/textures/ticker_background_small.tga create mode 100644 indra/newview/skins/default/xui/en-us/sh_floater_media_ticker.xml diff --git a/indra/llaudio/llstreamingaudio.h b/indra/llaudio/llstreamingaudio.h index 009327c7c..0e81cc530 100644 --- a/indra/llaudio/llstreamingaudio.h +++ b/indra/llaudio/llstreamingaudio.h @@ -53,7 +53,11 @@ class LLStreamingAudioInterface virtual void setGain(F32 vol) = 0; virtual F32 getGain() = 0; virtual std::string getURL() = 0; - virtual const LLSD *getMetaData() = 0; //return NULL if not supported. + + virtual bool supportsMetaData() = 0; + virtual const LLSD *getMetaData() = 0; + virtual bool supportsWaveData() = 0; + virtual bool getWaveData(float* arr, S32 count, S32 stride = 1) = 0; }; #endif // LL_STREAMINGAUDIO_H diff --git a/indra/llaudio/llstreamingaudio_fmod.h b/indra/llaudio/llstreamingaudio_fmod.h index 5c4597fae..c8a940eed 100644 --- a/indra/llaudio/llstreamingaudio_fmod.h +++ b/indra/llaudio/llstreamingaudio_fmod.h @@ -54,8 +54,11 @@ class LLStreamingAudio_FMOD : public LLStreamingAudioInterface /*virtual*/ void setGain(F32 vol); /*virtual*/ F32 getGain(); /*virtual*/ std::string getURL(); - /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not supported. + /*virtual*/ bool supportsMetaData(){return true;} + /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing. + /*virtual*/ bool supportsWaveData(){return false;} + /*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1){return false}; private: LLAudioStreamManagerFMOD *mCurrentInternetStreamp; int mFMODInternetStreamChannel; diff --git a/indra/llaudio/llstreamingaudio_fmodex.cpp b/indra/llaudio/llstreamingaudio_fmodex.cpp index ce46e0162..741af65d1 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.cpp +++ b/indra/llaudio/llstreamingaudio_fmodex.cpp @@ -334,6 +334,27 @@ void LLStreamingAudio_FMODEX::setGain(F32 vol) } } +/*virtual*/ bool LLStreamingAudio_FMODEX::getWaveData(float* arr, S32 count, S32 stride/*=1*/) +{ + if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp) + return false; + + static std::vector local_array(count); //Have to have an extra buffer to mix channels. Bleh. + if(count > (S32)local_array.size()) //Expand the array if needed. Try to minimize allocation calls, so don't ever shrink. + local_array.resize(count); + + if( mFMODInternetStreamChannelp->getWaveData(&local_array[0],count,0) == FMOD_OK && + mFMODInternetStreamChannelp->getWaveData(&arr[0],count,1) == FMOD_OK ) + { + for(S32 i = count;i>=0;i-=stride) + { + arr[i] += local_array[i]; + arr[i] *= .5f; + } + return true; + } + return false; +} /////////////////////////////////////////////////////// // manager of possibly-multiple internet audio streams diff --git a/indra/llaudio/llstreamingaudio_fmodex.h b/indra/llaudio/llstreamingaudio_fmodex.h index 0bb88fc8a..064b266e6 100644 --- a/indra/llaudio/llstreamingaudio_fmodex.h +++ b/indra/llaudio/llstreamingaudio_fmodex.h @@ -61,8 +61,11 @@ class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface /*virtual*/ void setGain(F32 vol); /*virtual*/ F32 getGain(); /*virtual*/ std::string getURL(); - /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not supported. + /*virtual*/ bool supportsMetaData(){return true;} + /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing. + /*virtual*/ bool supportsWaveData(){return true;} + /*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1); private: FMOD::System *mSystem; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index bd1e5a28f..39b74df92 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -537,6 +537,7 @@ set(viewer_SOURCE_FILES rlvfloaterbehaviour.cpp rlvviewer2.cpp shcommandhandler.cpp + shfloatermediaticker.cpp ) # This gets renamed in the packaging step @@ -1019,6 +1020,7 @@ set(viewer_HEADER_FILES rlvfloaterbehaviour.h rlvviewer2.h shcommandhandler.h + shfloatermediaticker.h ) source_group("CMake Rules" FILES ViewerInstall.cmake) diff --git a/indra/newview/app_settings/settings_sh.xml b/indra/newview/app_settings/settings_sh.xml index d1c5f640b..db1e8490f 100644 --- a/indra/newview/app_settings/settings_sh.xml +++ b/indra/newview/app_settings/settings_sh.xml @@ -1,6 +1,35 @@ + + SHShowMediaTicker + + Comment + Enable media ticker tool for supported audio libraries + Persist + 1 + Type + Boolean + Value + 0 + + SHMediaTickerRect + + Comment + Rectangle for media ticker + Persist + 1 + Type + Rect + Value + + 200 + 82 + 456 + 50 + + + SHEnableFMODExProfiler Comment diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index a23c3ddd1..191b8c918 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -214,6 +214,7 @@ #include "ascentdaycyclemanager.h" #include "llfloaterblacklist.h" #include "scriptcounter.h" +#include "shfloatermediaticker.h" // #include "llavatarnamecache.h" @@ -2053,6 +2054,10 @@ bool idle_startup() { LLFloaterAvatarList::createInstance(false); } + if (gSavedSettings.getBOOL("SHShowMediaTicker")) + { + SHFloaterMediaTicker::showInstance(); + } // if (gSavedSettings.getBOOL("ShowCameraControls")) { @@ -2073,6 +2078,8 @@ bool idle_startup() LLFloaterBeacons::showInstance(); } + + if (!gNoRender) { //Set up cloud rendertypes. Passed argument is unused. diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index 2c0a7739a..a8129875a 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -51,6 +51,7 @@ #include "chatbar_as_cmdline.h" #if SHY_MOD //Command handler +#include "llvoavatarself.h" #include "shcommandhandler.h" #endif //shy_mod @@ -109,7 +110,6 @@ BOOL LLViewerGesture::trigger(const std::string &trigger_string) } } - // private void LLViewerGesture::doTrigger( BOOL send_chat ) { diff --git a/indra/newview/llviewermedia_streamingaudio.h b/indra/newview/llviewermedia_streamingaudio.h index e5093f974..cba667861 100644 --- a/indra/newview/llviewermedia_streamingaudio.h +++ b/indra/newview/llviewermedia_streamingaudio.h @@ -55,7 +55,11 @@ class LLStreamingAudio_MediaPlugins : public LLStreamingAudioInterface /*virtual*/ void setGain(F32 vol); /*virtual*/ F32 getGain(); /*virtual*/ std::string getURL(); - /*virtual*/ LLSD *getMetaData(){return NULL;} //return NULL if not supported. + + /*virtual*/ bool supportsMetaData(){return false;} + /*virtual*/ LLSD *getMetaData(){return NULL;} //return NULL if not playing. + /*virtual*/ virtual bool supportsWaveData(){return false;} + /*virtual*/ virtual bool getWaveData(float* arr, S32 count, S32 stride = 1){return false;} private: LLPluginClassMedia* initializeMedia(const std::string& media_type); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 9b3186ba2..ffc19fda5 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -247,6 +247,7 @@ #include "llfloatermessagelog.h" #include "llfloatervfs.h" #include "llfloatervfsexplorer.h" +#include "shfloatermediaticker.h" // #include "scriptcounter.h" @@ -825,6 +826,8 @@ void init_menus() &handle_sounds_explorer, NULL)); menu->append(new LLMenuItemCallGL( "Asset Blacklist", &handle_blacklist, NULL)); + menu->append(new LLMenuItemCheckGL( "Streaming Audio Display", + &handle_ticker_toggle, &handle_ticker_enabled, &handle_ticker_check, NULL )); diff --git a/indra/newview/shfloatermediaticker.cpp b/indra/newview/shfloatermediaticker.cpp new file mode 100644 index 000000000..92f2df399 --- /dev/null +++ b/indra/newview/shfloatermediaticker.cpp @@ -0,0 +1,262 @@ + +#include "llviewerprecompiledheaders.h" + +#include "shfloatermediaticker.h" + +// Library includes +#include "llaudioengine.h" +#include "lliconctrl.h" +#include "llstreamingaudio.h" +#include "lluictrlfactory.h" + +// Viewer includes +#include "llviewercontrol.h" + + +SHFloaterMediaTicker::SHFloaterMediaTicker() : LLFloater()/*, LLSingleton()*/, + mPlayState(STATE_PAUSED), + mArtistScrollChars(0), + mTitleScrollChars(0), + mCurScrollChar(0), + mTickerBackground(NULL), + mArtistText(NULL), + mTitleText(NULL), + mVisualizer(NULL) +{ + setIsChrome(TRUE); + LLUICtrlFactory::getInstance()->buildFloater(this, "sh_floater_media_ticker.xml"); +} + +/*virtual*/ BOOL SHFloaterMediaTicker::postBuild() +{ + mTickerBackground = getChild("ticker_background"); + mArtistText = getChild("artist_text",true,false); + mTitleText = getChild("title_text",true,false); + mVisualizer = getChild("visualizer_box",true,false); + mszLoading = getString("loading"); + mszPaused = getString("paused"); + + if(mArtistText) mArtistText->setText(mszPaused); + if(mTitleText) mTitleText->setText(mszPaused); + + if(!gAudiop->getStreamingAudioImpl()->supportsWaveData()) //Can't visualize. Extend textboxes. + { + if(mArtistText) + { + LLRect text_rect = mArtistText->getRect(); + text_rect.mRight = llmax(mTickerBackground->getRect().mRight-2,text_rect.mRight); + mArtistText->setRect(text_rect); + } + if(mTitleText) + { + LLRect text_rect = mTitleText->getRect(); + text_rect.mRight = llmax(mTickerBackground->getRect().mRight-2,text_rect.mRight); + mArtistText->setRect(text_rect); + } + } + + return LLFloater::postBuild(); +} + +/*virtual*/ void SHFloaterMediaTicker::draw() +{ + updateTickerText(); + LLFloater::draw(); + drawOscilloscope(); +} + +/*virtual*/ void SHFloaterMediaTicker::onOpen() +{ + LLFloater::onOpen(); + + gSavedSettings.setBOOL("SHShowMediaTicker", TRUE); +} +/*virtual*/ void SHFloaterMediaTicker::onClose(bool app_quitting) +{ + LLFloater::onClose(app_quitting); + + if (!app_quitting) + { + gSavedSettings.setBOOL("SHShowMediaTicker", FALSE); + delete this; + } +} + +void SHFloaterMediaTicker::updateTickerText() //called via draw. +{ + bool stream_paused = gAudiop->getStreamingAudioImpl()->isPlaying() != 1; //will return 1 if playing. + + bool dirty = setPaused(stream_paused); + if(!stream_paused) + { + const LLSD* metadata = gAudiop->getStreamingAudioImpl()->getMetaData(); + LLSD artist = metadata ? metadata->get("ARTIST") : LLSD(); + LLSD title = metadata ? metadata->get("TITLE") : LLSD(); + + dirty |= setArtist(artist.isDefined() ? artist.asString() : mszLoading); + dirty |= setTitle(title.isDefined() ? title.asString() : mszLoading); + + if(dirty) + resetTicker(); + else iterateTickerOffset(); + } +} + +void SHFloaterMediaTicker::drawOscilloscope() //called via draw. +{ + if(!mVisualizer || !gAudiop->getStreamingAudioImpl()->supportsWaveData()) + return; + + static const S32 NUM_LINE_STRIPS = 64; //How many lines to draw. 64 is more than enough. + static const S32 WAVE_DATA_STEP_SIZE = 4; //Increase to provide more history at expense of cpu/memory. + + static const S32 NUM_WAVE_DATA_VALUES = NUM_LINE_STRIPS*WAVE_DATA_STEP_SIZE; //Actual buffer size. Don't toy with this. Change above vars to tweak. + static F32 buf[NUM_WAVE_DATA_VALUES]; + + if(!gAudiop->getStreamingAudioImpl()->getWaveData(&buf[0],NUM_WAVE_DATA_VALUES,WAVE_DATA_STEP_SIZE)) + return; + + LLRect root_rect = mVisualizer->getRect(); + + F32 height = root_rect.getHeight(); + F32 height_scale = height / 2.f; //WaveData ranges from 1 to -1, so height_scale = height / 2 + F32 width_scale = root_rect.getWidth() / (F32)NUM_WAVE_DATA_VALUES; + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.color4f(0.f,0.f,0.f,.75f); + gGL.pushMatrix(); + gGL.translatef((F32)root_rect.mLeft, (F32)root_rect.mBottom + height*.5f, 0.f); + gGL.begin( LLRender::LINE_STRIP ); + if((mPlayState != STATE_PAUSED)) + { + for(S32 i = NUM_WAVE_DATA_VALUES; i>=0;i-=WAVE_DATA_STEP_SIZE) + gGL.vertex2f((F32)i * width_scale, buf[i]*height_scale); + } + else + { + gGL.vertex2f(0.f, 0.f); + gGL.vertex2f(root_rect.getWidth(), 0.f); + } + gGL.end(); + gGL.popMatrix(); + gGL.flush(); +} + +bool SHFloaterMediaTicker::setPaused(bool pause) +{ + if(pause == (mPlayState == STATE_PAUSED)) + return false; + mPlayState = pause ? STATE_PAUSED : STATE_PLAYING; + if(pause) + { + if(mArtistText) mArtistText->setText(mszPaused); + if(mTitleText) mTitleText->setText(mszPaused); + } + return true; +} + +void SHFloaterMediaTicker::resetTicker() +{ + mScrollTimer.reset(); + mCurScrollChar=0; + if(mArtistText) mArtistText->setText(LLStringExplicit(mszArtist.substr(0,mszArtist.length()-mArtistScrollChars))); + if(mTitleText) mTitleText->setText(LLStringExplicit(mszTitle.substr(0,mszTitle.length()-mTitleScrollChars))); +} + +bool SHFloaterMediaTicker::setArtist(const std::string &artist) +{ + if(!mArtistText || mszArtist == artist) + return false; + mszArtist = artist; + mArtistText->setText(mszArtist); + mArtistScrollChars = countExtraChars(mArtistText,mszArtist); + return true; +} + +bool SHFloaterMediaTicker::setTitle(const std::string &title) +{ + if(!mTitleText || mszTitle == title) + return false; + mszTitle=title; + mTitleText->setText(mszTitle); + mTitleScrollChars = countExtraChars(mTitleText,mszTitle); + return true; +} + +S32 SHFloaterMediaTicker::countExtraChars(LLTextBox *texbox, const std::string &text) +{ + S32 text_width = texbox->getTextPixelWidth(); + S32 box_width = texbox->getRect().getWidth(); + if(text_width > box_width) + { + const LLFontGL* font = texbox->getFont(); + for(S32 count = 1;count<(S32)text.length();count++) + { + //This isn't very efficient... + const std::string substr = text.substr(0,text.length()-count); + if(font->getWidth(substr) <= box_width) + return count; + } + } + return 0; +} + +void SHFloaterMediaTicker::iterateTickerOffset() +{ + if( (mPlayState != STATE_PAUSED) && + (mArtistScrollChars || mTitleScrollChars) && + ((!mCurScrollChar && mScrollTimer.getElapsedTimeF32() >= 5.f) || + ( mCurScrollChar && mScrollTimer.getElapsedTimeF32() >= .5f))) + { + if(++mCurScrollChar > llmax(mArtistScrollChars, mTitleScrollChars)) + { + if(mScrollTimer.getElapsedTimeF32() >= 2.f) //pause for a bit when it reaches beyond last character. + resetTicker(); + } + else + { + mScrollTimer.reset(); + if(mArtistText && mCurScrollChar <= mArtistScrollChars) + { + mArtistText->setText(LLStringExplicit(mszArtist.substr(mCurScrollChar,mszArtist.length()-mArtistScrollChars+mCurScrollChar))); + } + if(mTitleText && mCurScrollChar <= mTitleScrollChars) + { + mTitleText->setText(LLStringExplicit(mszTitle.substr(mCurScrollChar,mszTitle.length()-mTitleScrollChars+mCurScrollChar))); + } + } + } +} + +/*static*/ +void SHFloaterMediaTicker::showInstance() +{ + if(!handle_ticker_enabled(NULL)) + return; + if(!SHFloaterMediaTicker::instanceExists()) + { + SHFloaterMediaTicker::getInstance(); + } +} + +BOOL handle_ticker_enabled(void *) +{ + return gAudiop && gAudiop->getStreamingAudioImpl() && gAudiop->getStreamingAudioImpl()->supportsMetaData(); +} +BOOL handle_ticker_check(void *) +{ + return SHFloaterMediaTicker::instanceExists(); +} +void handle_ticker_toggle(void *) +{ + if(!handle_ticker_enabled(NULL)) + return; + if(!SHFloaterMediaTicker::instanceExists()) + { + SHFloaterMediaTicker::getInstance(); + } + else + { + SHFloaterMediaTicker::getInstance()->close(); + } +} \ No newline at end of file diff --git a/indra/newview/shfloatermediaticker.h b/indra/newview/shfloatermediaticker.h new file mode 100644 index 000000000..cfc0475e7 --- /dev/null +++ b/indra/newview/shfloatermediaticker.h @@ -0,0 +1,55 @@ +#include "llfloater.h" + +class LLIconCtrl; + +class SHFloaterMediaTicker : public LLFloater, public LLSingleton +{ + friend class LLSingleton; +public: + SHFloaterMediaTicker(); //ctor + + virtual ~SHFloaterMediaTicker() {} + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void onOpen(); + /*virtual*/ void onClose(bool app_quitting); + + static void showInstance(); //use to create. +private: + void updateTickerText(); //called via draw. + void drawOscilloscope(); //called via draw. + bool setPaused(bool pause); //returns true on state change. + void resetTicker(); //Resets tickers to their innitial values (no offset). + bool setArtist(const std::string &artist); //returns true on change + bool setTitle(const std::string &title); //returns true on change + S32 countExtraChars(LLTextBox *texbox, const std::string &text); //calculates how many characters are truncated by bounds. + void iterateTickerOffset(); //Logic that actually shuffles the text to the left. + + enum ePlayState + { + STATE_PAUSED, + STATE_PLAYING + }; + + ePlayState mPlayState; + std::string mszLoading; + std::string mszPaused; + std::string mszArtist; + std::string mszTitle; + LLTimer mScrollTimer; + S32 mArtistScrollChars; + S32 mTitleScrollChars; + S32 mCurScrollChar; + + //UI elements + LLIconCtrl* mTickerBackground; + LLTextBox* mArtistText; + LLTextBox* mTitleText; + LLUICtrl* mVisualizer; +}; + +//Menu callbacks. +BOOL handle_ticker_enabled(void *); +BOOL handle_ticker_check(void *); +void handle_ticker_toggle(void *); + diff --git a/indra/newview/skins/default/textures/ticker_background_small.tga b/indra/newview/skins/default/textures/ticker_background_small.tga new file mode 100644 index 0000000000000000000000000000000000000000..ce3bb3fcd64c4f142b61831e7c8380e16218dfa6 GIT binary patch literal 12332 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 CKfw3^ literal 0 HcmV?d00001 diff --git a/indra/newview/skins/default/xui/en-us/sh_floater_media_ticker.xml b/indra/newview/skins/default/xui/en-us/sh_floater_media_ticker.xml new file mode 100644 index 000000000..f95f2d09f --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/sh_floater_media_ticker.xml @@ -0,0 +1,39 @@ + + + + + Artist: + + + + + Title: + + + + + + + + (not playing) + + + (loading...) + +