From 6d42e795f3c8f21734d45e8227dd80e77cbbbfa1 Mon Sep 17 00:00:00 2001
From: Rye Mutt <rye@alchemyviewer.org>
Date: Mon, 28 Sep 2020 18:52:50 -0400
Subject: [PATCH] Convert LLDictionary to string_view for lookup

---
 indra/llcommon/llassettype.cpp                |  37 ++-
 indra/llcommon/llassettype.h                  |   6 +-
 indra/llcommon/lldictionary.h                 |   2 +-
 indra/llinventory/llfoldertype.cpp            |   2 +-
 indra/llinventory/llfoldertype.h              |   2 +-
 indra/llinventory/llinventorytype.cpp         |   2 +-
 indra/llinventory/llinventorytype.h           |   2 +-
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/alpanelmusicticker.cpp          | 248 ++++++++++++++++++
 indra/newview/alpanelmusicticker.h            |  72 +++++
 indra/newview/llmeshrepository.cpp            |   4 +-
 indra/newview/llviewerfloaterreg.cpp          |   1 +
 .../skins/default/textures/textures.xml       |   2 +
 .../textures/ticker_visualizer_grid.tga       | Bin 0 -> 7436 bytes
 .../default/xui/en/floater_music_ticker.xml   |  27 ++
 .../default/xui/en/panel_music_ticker.xml     | 113 ++++++++
 16 files changed, 492 insertions(+), 30 deletions(-)
 create mode 100644 indra/newview/alpanelmusicticker.cpp
 create mode 100644 indra/newview/alpanelmusicticker.h
 create mode 100644 indra/newview/skins/default/textures/ticker_visualizer_grid.tga
 create mode 100644 indra/newview/skins/default/xui/en/floater_music_ticker.xml
 create mode 100644 indra/newview/skins/default/xui/en/panel_music_ticker.xml

diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index 09578608c8f..a8f586b734d 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -104,11 +104,10 @@ LLAssetDictionary::LLAssetDictionary()
 const std::string LLAssetType::BADLOOKUP("llassettype_bad_lookup");
 
 // static
-LLAssetType::EType LLAssetType::getType(const std::string& desc_name)
+LLAssetType::EType LLAssetType::getType(std::string desc_name)
 {
-	std::string s = desc_name;
-	LLStringUtil::toUpper(s);
-	return LLAssetDictionary::getInstance()->lookup(s);
+	LLStringUtil::toUpper(desc_name);
+	return LLAssetDictionary::getInstance()->lookup(desc_name);
 }
 
 // static
@@ -143,21 +142,21 @@ const char *LLAssetType::lookup(LLAssetType::EType asset_type)
 // static
 LLAssetType::EType LLAssetType::lookup(const char* name)
 {
-	return lookup(ll_safe_string(name));
+	return lookup(absl::NullSafeStringView(name));
 }
 
 // static
-LLAssetType::EType LLAssetType::lookup(const std::string& type_name)
+LLAssetType::EType LLAssetType::lookup(const std::string_view type_name)
 {
-	const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
-	for (LLAssetDictionary::const_iterator iter = dict->begin();
-		 iter != dict->end();
-		 iter++)
+	if(type_name.empty()) return AT_UNKNOWN;
+
+	const LLAssetDictionary& dict = LLAssetDictionary::instance();
+	for (const auto& dict_pair : dict)
 	{
-		const AssetEntry *entry = iter->second;
+		const AssetEntry *entry = dict_pair.second;
 		if (type_name == entry->mTypeName)
 		{
-			return iter->first;
+			return dict_pair.first;
 		}
 	}
 	return AT_UNKNOWN;
@@ -181,21 +180,19 @@ const char *LLAssetType::lookupHumanReadable(LLAssetType::EType asset_type)
 // static
 LLAssetType::EType LLAssetType::lookupHumanReadable(const char* name)
 {
-	return lookupHumanReadable(ll_safe_string(name));
+	return lookupHumanReadable(absl::NullSafeStringView(name));
 }
 
 // static
-LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string& readable_name)
+LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string_view readable_name)
 {
-	const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
-	for (LLAssetDictionary::const_iterator iter = dict->begin();
-		 iter != dict->end();
-		 iter++)
+	const LLAssetDictionary& dict = LLAssetDictionary::instance();
+	for (const auto& dict_pair : dict)
 	{
-		const AssetEntry *entry = iter->second;
+		const AssetEntry *entry = dict_pair.second;
 		if (entry->mHumanName && (readable_name == entry->mHumanName))
 		{
-			return iter->first;
+			return dict_pair.first;
 		}
 	}
 	return AT_NONE;
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index e1b524ce687..13a454a9eb3 100644
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -145,15 +145,15 @@ class LL_COMMON_API LLAssetType
 
 	// machine transation between type and strings
 	static EType 				lookup(const char* name); // safe conversion to std::string, *TODO: deprecate
-	static EType 				lookup(const std::string& type_name);
+	static EType 				lookup(const std::string_view type_name);
 	static const char*			lookup(EType asset_type);
 
 	// translation from a type to a human readable form.
 	static EType 				lookupHumanReadable(const char* desc_name); // safe conversion to std::string, *TODO: deprecate
-	static EType 				lookupHumanReadable(const std::string& readable_name);
+	static EType 				lookupHumanReadable(const std::string_view readable_name);
 	static const char*			lookupHumanReadable(EType asset_type);
 
-	static EType 				getType(const std::string& desc_name);
+	static EType 				getType(std::string desc_name);
 	static const std::string&	getDesc(EType asset_type);
 
 	static bool 				lookupCanLink(EType asset_type);
diff --git a/indra/llcommon/lldictionary.h b/indra/llcommon/lldictionary.h
index d56865ab077..a4dbe43f417 100644
--- a/indra/llcommon/lldictionary.h
+++ b/indra/llcommon/lldictionary.h
@@ -61,7 +61,7 @@ class LLDictionary : public std::map<Index, Entry *>
 		if (dictionary_iter == map_t::end()) return NULL;
 		return dictionary_iter->second;
 	}
-	const Index lookup(const std::string &name) const 
+	const Index lookup(const std::string_view name) const 
 	{
 		for (const_iterator_t dictionary_iter = map_t::begin();
 			 dictionary_iter != map_t::end();
diff --git a/indra/llinventory/llfoldertype.cpp b/indra/llinventory/llfoldertype.cpp
index a41e5394554..fd3027c2965 100644
--- a/indra/llinventory/llfoldertype.cpp
+++ b/indra/llinventory/llfoldertype.cpp
@@ -109,7 +109,7 @@ LLFolderDictionary::LLFolderDictionary()
 };
 
 // static
-LLFolderType::EType LLFolderType::lookup(const std::string& name)
+LLFolderType::EType LLFolderType::lookup(const std::string_view name)
 {
 	return LLFolderDictionary::getInstance()->lookup(name);
 }
diff --git a/indra/llinventory/llfoldertype.h b/indra/llinventory/llfoldertype.h
index 7addc18bead..9a5af86e074 100644
--- a/indra/llinventory/llfoldertype.h
+++ b/indra/llinventory/llfoldertype.h
@@ -103,7 +103,7 @@ class LLFolderType
 		FT_NONE = -1
 	};
 
-	static EType 				lookup(const std::string& type_name);
+	static EType 				lookup(const std::string_view type_name);
 	static const std::string&	lookup(EType folder_type);
 
 	static bool 				lookupIsProtectedType(EType folder_type);
diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp
index d641a3f30a9..87030a16f84 100644
--- a/indra/llinventory/llinventorytype.cpp
+++ b/indra/llinventory/llinventorytype.cpp
@@ -166,7 +166,7 @@ const std::string &LLInventoryType::lookup(EType type)
 }
 
 // static
-LLInventoryType::EType LLInventoryType::lookup(const std::string& name)
+LLInventoryType::EType LLInventoryType::lookup(const std::string_view name)
 {
 	return LLInventoryDictionary::getInstance()->lookup(name);
 }
diff --git a/indra/llinventory/llinventorytype.h b/indra/llinventory/llinventorytype.h
index 200d2a9fffb..76f949da0e8 100644
--- a/indra/llinventory/llinventorytype.h
+++ b/indra/llinventory/llinventorytype.h
@@ -126,7 +126,7 @@ class LLInventoryType
 
 
 	// machine transation between type and strings
-	static EType lookup(const std::string& name);
+	static EType lookup(const std::string_view name);
 	static const std::string &lookup(EType type);
 	// translation from a type to a human readable form.
 	static const std::string &lookupHumanReadable(EType type);
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index ee8bd5ef352..72519ae6554 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -120,6 +120,7 @@ set(viewer_SOURCE_FILES
     alfloaterregiontracker.cpp
     alpanelaomini.cpp
     alpanelaopulldown.cpp
+    alpanelmusicticker.cpp
     alpanelquicksettings.cpp
     alpanelquicksettingspulldown.cpp
     altoolalign.cpp
@@ -775,6 +776,7 @@ set(viewer_HEADER_FILES
     alfloaterregiontracker.h
     alpanelaomini.h
     alpanelaopulldown.h
+    alpanelmusicticker.h
     alpanelquicksettings.h
     alpanelquicksettingspulldown.h
     altoolalign.h
diff --git a/indra/newview/alpanelmusicticker.cpp b/indra/newview/alpanelmusicticker.cpp
new file mode 100644
index 00000000000..9dd56825cdd
--- /dev/null
+++ b/indra/newview/alpanelmusicticker.cpp
@@ -0,0 +1,248 @@
+/**
+* @file alpanelmusicticker.cpp
+* @brief ALPanelMusicTicker implementation
+*
+* $LicenseInfo:firstyear=2015&license=viewerlgpl$
+* Copyright (C) Shyotl Kuhr
+* Copyright (C) 2015 Drake Arconis
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+* $/LicenseInfo$
+**/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "alpanelmusicticker.h"
+
+// Library includes
+#include "llaudioengine.h"
+#include "lliconctrl.h"
+#include "llstreamingaudio.h"
+#include "lltextbox.h"
+#include "lluicolortable.h"
+#include "lluictrl.h"
+
+// Viewer includes
+#include "llviewercontrol.h"
+
+static LLPanelInjector<ALPanelMusicTicker> t_music_ticker("music_ticker");
+
+ALPanelMusicTicker::ALPanelMusicTicker() : LLPanel(),
+	mPlayState(STATE_PLAYING), 
+	mArtistScrollChars(0), 
+	mTitleScrollChars(0), 
+	mCurScrollChar(0),
+	mArtistText(nullptr),
+	mTitleText(nullptr),
+	mVisualizer(nullptr)
+{
+}
+
+BOOL ALPanelMusicTicker::postBuild()
+{
+	mArtistText =	getChild<LLTextBox>("artist_text");
+	mTitleText	=	getChild<LLTextBox>("title_text");
+	mVisualizer =	getChild<LLUICtrl>("visualizer_box");
+	mszLoading	=	getString("loading");
+	mszPaused	=	getString("paused");
+	mOscillatorColor = LLUIColorTable::getInstance()->getColor("ALMediaTickerOscillatorColor");
+	
+	setPaused(true);
+
+	return LLPanel::postBuild();
+}
+
+void ALPanelMusicTicker::draw()
+{
+	updateTickerText();
+	drawOscilloscope();
+	LLPanel::draw();
+}
+
+void ALPanelMusicTicker::reshape(S32 width, S32 height, BOOL called_from_parent/*=TRUE*/)
+{
+	bool width_changed = (getRect().getWidth() != width);
+	LLPanel::reshape(width, height, called_from_parent);
+	if(width_changed)
+	{
+		if(mTitleText)
+			mTitleScrollChars = countExtraChars(mTitleText, mszTitle);
+		if(mArtistText)
+			mArtistScrollChars = countExtraChars(mArtistText, mszArtist);
+		resetTicker();
+	}
+}
+
+void ALPanelMusicTicker::updateTickerText() //called via draw.
+{
+	if(!gAudiop)
+		return;
+
+	bool stream_paused = gAudiop->getStreamingAudioImpl()->isPlaying() != 1;	//will return 1 if playing.
+
+	bool dirty = setPaused(stream_paused);
+	if(!stream_paused)
+	{
+		if (dirty || gAudiop->getStreamingAudioImpl()->hasNewMetaData())
+		{
+			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(artist.isDefined() && title.isDefined())
+				mLoadTimer.stop();
+			else if(dirty)
+				mLoadTimer.start();
+			else if(mLoadTimer.getStarted() && mLoadTimer.getElapsedTimeF64() > 10.f) //It has been 10 seconds.. give up.
+			{
+				if(!artist.isDefined())
+					dirty |= setArtist(LLStringUtil::null);
+				if(!title.isDefined())
+					dirty |= setTitle(LLStringUtil::null);
+				mLoadTimer.stop();
+			}
+		}
+	}
+	if(dirty)
+		resetTicker();
+	else 
+		iterateTickerOffset();
+}
+
+void ALPanelMusicTicker::drawOscilloscope() //called via draw.
+{
+	if(!gAudiop || !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];
+
+	const 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 = root_rect.getWidth();
+	F32 width_scale = width / (F32)NUM_WAVE_DATA_VALUES;
+
+	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
+	gGL.color4fv(mOscillatorColor.mV);
+	gGL.pushMatrix();
+		const auto& ui_scale = gGL.getUIScale();
+		F32 x = (F32) root_rect.mLeft * ui_scale[VX];
+		F32 y = (F32) (root_rect.mBottom + height * 0.5f) * ui_scale[VY];
+		gGL.translatef(x, y, 0.f);
+		gGL.begin( LLRender::LINE_STRIP );
+			if(mPlayState == STATE_PAUSED
+			   || !gAudiop->getStreamingAudioImpl()->getWaveData(&buf[0], NUM_WAVE_DATA_VALUES,WAVE_DATA_STEP_SIZE))
+			{
+				gGL.vertex2i(0, 0);
+				gGL.vertex2i((S32)width, 0);
+			}
+			else
+				for(S32 i = NUM_WAVE_DATA_VALUES - 1; i >= 0; i -= WAVE_DATA_STEP_SIZE)
+					gGL.vertex2f((F32)i * width_scale, buf[i] * height_scale);
+		gGL.end();
+	gGL.popMatrix();
+	gGL.flush();
+}
+
+bool ALPanelMusicTicker::setPaused(bool pause)
+{
+	if(pause == (mPlayState == STATE_PAUSED))
+		return false;
+	mPlayState = pause ? STATE_PAUSED : STATE_PLAYING;
+	if(pause)
+	{
+		setArtist(mszPaused);
+		setTitle(mszPaused);
+	}
+	return true;
+}
+
+void ALPanelMusicTicker::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 ALPanelMusicTicker::setArtist(const std::string &artist)
+{
+	if(!mArtistText || mszArtist == artist)
+		return false;
+	mszArtist = artist;
+	mArtistText->setText(mszArtist);
+	mArtistScrollChars = countExtraChars(mArtistText, mszArtist);
+	return true;
+}
+
+bool ALPanelMusicTicker::setTitle(const std::string &title)
+{
+	if(!mTitleText || mszTitle == title)
+		return false;
+	mszTitle = title;
+	mTitleText->setText(mszTitle);
+	mTitleScrollChars = countExtraChars(mTitleText, mszTitle);
+	return true;
+}
+
+S32 ALPanelMusicTicker::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 ALPanelMusicTicker::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)));
+			}
+		}
+	}
+}
diff --git a/indra/newview/alpanelmusicticker.h b/indra/newview/alpanelmusicticker.h
new file mode 100644
index 00000000000..6e3168330db
--- /dev/null
+++ b/indra/newview/alpanelmusicticker.h
@@ -0,0 +1,72 @@
+/**
+* @file alpanelmusicticker.h
+* @brief ALPanelMusicTicker declaration
+*
+* $LicenseInfo:firstyear=2015&license=viewerlgpl$
+* Copyright (C) Shyotl Kuhr
+* Copyright (C) 2015 Drake Arconis
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+* $/LicenseInfo$
+**/
+
+#ifndef AL_PANELMUSICTICKER_H
+#define AL_PANELMUSICTICKER_H
+
+#include "llpanel.h"
+
+class LLIconCtrl;
+class LLTextBox;
+
+class ALPanelMusicTicker final : public LLPanel
+{
+public:
+	ALPanelMusicTicker();	//ctor
+
+	BOOL postBuild() final override;
+	void draw() final override;
+	void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) final override;
+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;
+	LLTimer mLoadTimer;
+	S32 mArtistScrollChars;
+	S32 mTitleScrollChars;
+	S32 mCurScrollChar;
+
+	LLColor4 mOscillatorColor;
+
+	//UI elements
+	LLTextBox* mArtistText;
+	LLTextBox* mTitleText;
+	LLUICtrl* mVisualizer;
+};
+
+#endif // AL_PANELMUSICTICKER_H
diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp
index e6397532a9d..3c28396be0e 100644
--- a/indra/newview/llmeshrepository.cpp
+++ b/indra/newview/llmeshrepository.cpp
@@ -3805,8 +3805,8 @@ void LLMeshRepository::notifyLoadedMeshes()
 		{
 			inventory_data& data = mInventoryQ.front();
 
-			LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString());
-			LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString());
+			LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asStringRef());
+			LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asStringRef());
 
 			// Handle addition of texture, if any.
 			if ( data.mResponse.has("new_texture_folder_id") )
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index e6d2ff357df..f1b1c50a6c1 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -393,6 +393,7 @@ void LLViewerFloaterReg::registerFloaters()
 
 	LLFloaterReg::add("ao", "floater_ao.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterAO>);
 	LLFloaterReg::add("delete_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDeleteQueue>);
+	LLFloaterReg::add("music_ticker", "floater_music_ticker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
 	LLFloaterReg::add("particle_editor", "floater_particle_editor.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterParticleEditor>);
 	LLFloaterReg::add("quick_settings", "floater_quick_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
 	LLFloaterReg::add("region_tracker", "floater_region_tracker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterRegionTracker>);
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 836dabfe552..957d581629e 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -753,6 +753,8 @@ with the same filename but different name
   <texture name="cloud-particle.j2c" use_mips="true" />
   <texture name="transparent.j2c" use_mips="true" />
 
+  <texture name="Ticker_Grid" file_name="ticker_visualizer_grid.tga" preload="false" />
+
   <!--WARNING OLD ART BELOW *do not use*-->
   <texture name="icn_media_web.tga" preload="true" />
   <texture name="icn_media_movie.tga" preload="true" />
diff --git a/indra/newview/skins/default/textures/ticker_visualizer_grid.tga b/indra/newview/skins/default/textures/ticker_visualizer_grid.tga
new file mode 100644
index 0000000000000000000000000000000000000000..a7bb07b4e84feead52f5720b6873a1861cb1a4d4
GIT binary patch
literal 7436
zcmeI1F$#b%5Cjh^pP>)X+7Cnsh=Nh^{~u9<T%CG1dmuI`TxRD|Xx5m|WW9Cm^N)Qm
z)+Z-q&6ac3Fzo!457$xohs)0Qr~JcZDF1NT$v<p%&X3(e-4D1u&D~_@C;xC6*U(S-
whixc3Kjp*TpV)Slzh3>s?k79Ha)Yr?u=AtM=YKno{R~%3aa)Ibj!PKr1zP`bM*si-

literal 0
HcmV?d00001

diff --git a/indra/newview/skins/default/xui/en/floater_music_ticker.xml b/indra/newview/skins/default/xui/en/floater_music_ticker.xml
new file mode 100644
index 00000000000..99daa03b410
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_music_ticker.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ width="320"
+ height="39"
+ layout="topleft"
+ min_width="300"
+ min_height="39"
+ name="floater_music_ticker"
+ positioning="cascading"
+ title=""
+ can_minimize="false"
+ header_height="0"
+ can_resize="true"
+ can_resize_height="false"
+ show_title="false"
+ save_rect="true"
+ chrome="true">
+  <panel
+   class="music_ticker"
+   name="music_ticker"
+   filename="panel_music_ticker.xml"
+   layout="topleft"
+   follows="all"
+   width="296"
+   top="0"
+   mouse_opaque="false"/>
+</floater>
diff --git a/indra/newview/skins/default/xui/en/panel_music_ticker.xml b/indra/newview/skins/default/xui/en/panel_music_ticker.xml
new file mode 100644
index 00000000000..4c380ec932d
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/panel_music_ticker.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel
+ layout="topleft"
+ follows="top|right|left"
+ width="300"
+ height="38"
+ name="music_ticker">
+    <string name="paused">
+        (not playing)
+    </string>
+    <string name="loading">
+        (loading...)
+    </string>
+    <icon
+     top="19"
+     color="AlAshGrey"
+     follows="left|top|right" 
+     height="1"
+     layout="topleft"
+     image_name="white.tga"
+     left="0"
+     mouse_opaque="false"
+     name="ticker_background"
+     width="244" />
+    <locate
+     top="0"
+     bottom="-1"
+     left="244"
+     name="visualizer_box"
+     width="60"
+     follows="top|right"
+     use_bounding_rect="true"
+     layout="topleft"/>
+    <icon
+     top="0"
+     bottom="-1"
+     color="AlAshGrey"
+     follows="top|right"
+     layout="topleft"
+     image_name="Ticker_Grid"
+     left="244"
+     mouse_opaque="false"
+     name="visualizer_grid"
+     width="60" />
+    <text
+     text_color="AlBlue"
+     bg_visible="false"
+     border_visible="false"
+     layout="topleft"
+     top="0"
+     follows="top|left"
+     font="SansSerifBold"
+     h_pad="0"
+     halign="left"
+     height="16"
+     left="4"
+     mouse_opaque="false"
+     name="artist_label"
+     v_pad="0"
+     width="50">
+        Artist:
+    </text>
+    <text
+     bg_visible="false" 
+     border_visible="false"
+     layout="topleft"
+     top="0" 
+     follows="top|left|right"
+     font="SansSerifBold" 
+     h_pad="0" 
+     halign="left" 
+     height="16" 
+     left="50"
+     mouse_opaque="false" 
+     name="artist_text" 
+     v_pad="0" 
+     width="193">
+    </text>
+    <text 
+     text_color="AlBlue" 
+     bg_visible="false"
+     border_visible="false" 
+     layout="topleft"
+     top="19" 
+     follows="top|left"
+     font="SansSerifBold"
+     h_pad="0" 
+     halign="left" 
+     height="16" 
+     left="4"
+     mouse_opaque="false"
+     name="title_label" 
+     v_pad="0" 
+     width="50">
+        Title:
+    </text>
+    <text 
+     bg_visible="false" 
+     border_visible="false" 
+     layout="topleft"
+     top="19"
+     follows="top|left|right"
+     font="SansSerifBold"
+     h_pad="0"
+     halign="left"
+     height="16"
+     left="50"
+     mouse_opaque="false"
+     name="title_text"
+     v_pad="0"
+     width="193">
+    </text>
+</panel>
-- 
GitLab