From c01edec021194bacb86dc9a3f5a3cc655a8cb15f Mon Sep 17 00:00:00 2001
From: Adam Moss <moss@lindenlab.com>
Date: Tue, 22 Apr 2008 16:17:22 +0000
Subject: QAR-468 "Babble Lipsync MergeMe"

svn merge -c85508
svn+ssh://svn.lindenlab.com/svn/linden/branches/babble-merge1-r85500
---
 indra/newview/app_settings/settings.xml |  66 ++++++++++
 indra/newview/llviewercontrol.cpp       |   2 +
 indra/newview/llvoavatar.cpp            | 218 +++++++++++++++++++-------------
 indra/newview/llvoavatar.h              |  11 ++
 indra/newview/llvoiceclient.cpp         |  19 +++
 indra/newview/llvoiceclient.h           |   4 +
 indra/newview/llvoicevisualizer.cpp     | 175 ++++++++++++++++++++++++-
 indra/newview/llvoicevisualizer.h       |  23 +++-
 8 files changed, 430 insertions(+), 88 deletions(-)

(limited to 'indra/newview')

diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 1c421b343a..f6bedc3339 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -3918,6 +3918,72 @@
         <key>Value</key>
             <integer>1</integer>
         </map>
+    <key>LipSyncAah</key>
+        <map>
+        <key>Comment</key>
+            <string>Aah (jaw opening) babble loop</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>String</string>
+        <key>Value</key>
+            <string>257998776531013446642343</string>
+        </map>
+    <key>LipSyncAahPowerTransfer</key>
+        <map>
+        <key>Comment</key>
+            <string>Transfer curve for Voice Interface power to aah lip sync amplitude</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>String</string>
+        <key>Value</key>
+            <string>0000123456789</string>
+        </map>
+    <key>LipSyncEnabled</key>
+        <map>
+        <key>Comment</key>
+            <string>0 disable lip-sync, 1 enable babble loop</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>U32</string>
+        <key>Value</key>
+            <integer>1</integer>
+        </map>
+    <key>LipSyncOoh</key>
+        <map>
+        <key>Comment</key>
+            <string>Ooh (mouth width) babble loop</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>String</string>
+        <key>Value</key>
+            <string>1247898743223344444443200000</string>
+        </map>
+    <key>LipSyncOohAahRate</key>
+        <map>
+        <key>Comment</key>
+            <string>Rate to babble Ooh and Aah (/sec)</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>F32</string>
+        <key>Value</key>
+            <real>24.0</real>
+        </map>
+    <key>LipSyncOohPowerTransfer</key>
+        <map>
+        <key>Comment</key>
+            <string>Transfer curve for Voice Interface power to ooh lip sync amplitude</string>
+        <key>Persist</key>
+            <integer>1</integer>
+        <key>Type</key>
+            <string>String</string>
+        <key>Value</key>
+            <string>0012345566778899</string>
+        </map>
     <key>LocalCacheVersion</key>
         <map>
         <key>Comment</key>
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 69e611fd99..b834eb7e2e 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -426,6 +426,7 @@ bool handleVoiceClientPrefsChanged(const LLSD& newvalue)
 		gVoiceClient->setCaptureDevice(inputDevice);
 		std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
 		gVoiceClient->setRenderDevice(outputDevice);
+		gVoiceClient->setLipSyncEnabled(gSavedSettings.getU32("LipSyncEnabled"));
 	}
 	return true;
 }
@@ -555,5 +556,6 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("VivoxDebugServerName")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
 	gSavedSettings.getControl("VoiceInputAudioDevice")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
 	gSavedSettings.getControl("VoiceOutputAudioDevice")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
+	gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));	
 }
 
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
index f3384c4a7e..0783468bb7 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -95,7 +95,6 @@
 #include "llstatusbar.h"
 #include "lltargetingmotion.h"
 #include "lltexlayer.h"
-#include "lltoolbar.h"
 #include "lltoolgrab.h"		// for needsRenderBeam
 #include "lltoolmgr.h"		// for needsRenderBeam
 #include "lltoolmorph.h"
@@ -117,20 +116,13 @@
 #include "llspatialpartition.h"
 #include "llglslshader.h"
 #include "llappviewer.h"
-#include "lscript_byteformat.h"
+#include "llsky.h"
 
 //#include "vtune/vtuneapi.h"
 
-//Ventrella
-#include "llgesturemgr.h" //needed to trigger the voice gestculations
+#include "llgesturemgr.h" //needed to trigger the voice gesticulations
 #include "llvoicevisualizer.h" 
 #include "llvoiceclient.h"
-//end Ventrella
-
-// Direct imports, evil
-extern LLSky gSky;
-extern void set_avatar_character(void* charNameArg);
-extern BOOL gRenderForSelect;
 
 LLXmlTree LLVOAvatar::sXMLTree;
 LLXmlTree LLVOAvatar::sSkeletonXMLTree;
@@ -766,6 +758,10 @@ LLVOAvatar::LLVOAvatar(
 	mStepOnLand = TRUE;
 	mStepMaterial = 0;
 
+	mLipSyncActive = false;
+	mOohMorph      = NULL;
+	mAahMorph      = NULL;
+
 	//-------------------------------------------------------------------------
 	// initialize joint, mesh and shape members
 	//-------------------------------------------------------------------------
@@ -950,10 +946,8 @@ LLVOAvatar::LLVOAvatar(
 
 	//VTPause();  // VTune
 	
-	//Ventrella
 	mVoiceVisualizer->setVoiceEnabled( gVoiceClient->getVoiceEnabled( mID ) );
 	mCurrentGesticulationLevel = 0;		
-	//END Ventrella
 }
 
 //------------------------------------------------------------------------
@@ -1886,6 +1880,26 @@ void LLVOAvatar::buildCharacter()
 	//-------------------------------------------------------------------------
 	updateHeadOffset();
 
+	//-------------------------------------------------------------------------
+	// initialize lip sync morph pointers
+	//-------------------------------------------------------------------------
+	mOohMorph     = getVisualParam( "Lipsync_Ooh" );
+	mAahMorph     = getVisualParam( "Lipsync_Aah" );
+
+	// If we don't have the Ooh morph, use the Kiss morph
+	if (!mOohMorph)
+	{
+		llwarns << "Missing 'Ooh' morph for lipsync, using fallback." << llendl;
+		mOohMorph = getVisualParam( "Express_Kiss" );
+	}
+
+	// If we don't have the Aah morph, use the Open Mouth morph
+	if (!mAahMorph)
+	{
+		llwarns << "Missing 'Aah' morph for lipsync, using fallback." << llendl;
+		mAahMorph = getVisualParam( "Express_Open_Mouth" );
+	}
+
 	//-------------------------------------------------------------------------
 	// start default motions
 	//-------------------------------------------------------------------------
@@ -2449,88 +2463,93 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 	// animate the character
 	// store off last frame's root position to be consistent with camera position
 	LLVector3 root_pos_last = mRoot.getWorldPosition();
-
 	BOOL detailed_update = updateCharacter(agent);
+	bool voiceEnabled = gVoiceClient->getVoiceEnabled( mID ) && gVoiceClient->inProximalChannel();
 
-	{
-		//Ventrella
-		bool voiceEnabled = gVoiceClient->getVoiceEnabled( mID ) && gVoiceClient->inProximalChannel();
-		// disable voice visualizer when in mouselook
-		mVoiceVisualizer->setVoiceEnabled( voiceEnabled && !(mIsSelf && gAgent.cameraMouselook()) );
-		if ( voiceEnabled )
-		{		
-			//----------------------------------------------------------------
-			// Only do gesture triggering for your own avatar, and only when you're in a proximal channel.
-			//----------------------------------------------------------------
-			if( mIsSelf )
+	// disable voice visualizer when in mouselook
+	mVoiceVisualizer->setVoiceEnabled( voiceEnabled && !(mIsSelf && gAgent.cameraMouselook()) );
+	if ( voiceEnabled )
+	{		
+		//----------------------------------------------------------------
+		// Only do gesture triggering for your own avatar, and only when you're in a proximal channel.
+		//----------------------------------------------------------------
+		if( mIsSelf )
+		{
+			//----------------------------------------------------------------------------------------
+			// The following takes the voice signal and uses that to trigger gesticulations. 
+			//----------------------------------------------------------------------------------------
+			int lastGesticulationLevel = mCurrentGesticulationLevel;
+			mCurrentGesticulationLevel = mVoiceVisualizer->getCurrentGesticulationLevel();
+			
+			//---------------------------------------------------------------------------------------------------
+			// If "current gesticulation level" changes, we catch this, and trigger the new gesture
+			//---------------------------------------------------------------------------------------------------
+			if ( lastGesticulationLevel != mCurrentGesticulationLevel )
 			{
-				//----------------------------------------------------------------------------------------
-				// The following takes the voice signal and uses that to trigger gesticulations. 
-				//----------------------------------------------------------------------------------------
-				int lastGesticulationLevel = mCurrentGesticulationLevel;
-				mCurrentGesticulationLevel = mVoiceVisualizer->getCurrentGesticulationLevel();
-				
-				//---------------------------------------------------------------------------------------------------
-				// If "current gesticulation level" changes, we catch this, and trigger the new gesture
-				//---------------------------------------------------------------------------------------------------
-				if ( lastGesticulationLevel != mCurrentGesticulationLevel )
+				if ( mCurrentGesticulationLevel != VOICE_GESTICULATION_LEVEL_OFF )
 				{
-					if ( mCurrentGesticulationLevel != VOICE_GESTICULATION_LEVEL_OFF )
-					{
-						LLString gestureString = "unInitialized";
-								if ( mCurrentGesticulationLevel == 0 )	{ gestureString = "/voicelevel1";	}
-						else	if ( mCurrentGesticulationLevel == 1 )	{ gestureString = "/voicelevel2";	}
-						else	if ( mCurrentGesticulationLevel == 2 )	{ gestureString = "/voicelevel3";	}
-						else	{ llinfos << "oops - CurrentGesticulationLevel can be only 0, 1, or 2"  << llendl; }
-						
-						// this is the call that Karl S. created for triggering gestures from within the code.
-						gGestureManager.triggerAndReviseString( gestureString );
-					}
-				}
-				
-			} //if( mIsSelf )
-
-			//-----------------------------------------------------------------------------------------------------------------
-			// If the avatar is speaking, then the voice amplitude signal is passed to the voice visualizer.
-			// Also, here we trigger voice visualizer start and stop speaking, so it can animate the voice symbol.
-			//
-			// Notice the calls to "gAwayTimer.reset()". This resets the timer that determines how long the avatar has been
-			// "away", so that the avatar doesn't lapse into away-mode (and slump over) while the user is still talking. 
-			//-----------------------------------------------------------------------------------------------------------------
-			if ( gVoiceClient->getIsSpeaking( mID ) )
-			{
-				if ( ! mVoiceVisualizer->getCurrentlySpeaking() )
-				{
-					mVoiceVisualizer->setStartSpeaking();
+					LLString gestureString = "unInitialized";
+					if ( mCurrentGesticulationLevel == 0 )	{ gestureString = "/voicelevel1";	}
+					else	if ( mCurrentGesticulationLevel == 1 )	{ gestureString = "/voicelevel2";	}
+					else	if ( mCurrentGesticulationLevel == 2 )	{ gestureString = "/voicelevel3";	}
+					else	{ llinfos << "oops - CurrentGesticulationLevel can be only 0, 1, or 2"  << llendl; }
 					
-					//printf( "gAwayTimer.reset();\n" );
-				}
-
-				mVoiceVisualizer->setSpeakingAmplitude( gVoiceClient->getCurrentPower( mID ) );
-
-				if( mIsSelf )
-				{
-					gAgent.clearAFK();
+					// this is the call that Karl S. created for triggering gestures from within the code.
+					gGestureManager.triggerAndReviseString( gestureString );
 				}
 			}
-			else
+			
+		} //if( mIsSelf )
+		
+		//-----------------------------------------------------------------------------------------------------------------
+		// If the avatar is speaking, then the voice amplitude signal is passed to the voice visualizer.
+		// Also, here we trigger voice visualizer start and stop speaking, so it can animate the voice symbol.
+		//
+		// Notice the calls to "gAwayTimer.reset()". This resets the timer that determines how long the avatar has been
+		// "away", so that the avatar doesn't lapse into away-mode (and slump over) while the user is still talking. 
+		//-----------------------------------------------------------------------------------------------------------------
+		if ( gVoiceClient->getIsSpeaking( mID ) )
+		{		
+			if ( ! mVoiceVisualizer->getCurrentlySpeaking() )
 			{
-				if ( mVoiceVisualizer->getCurrentlySpeaking() )
+				mVoiceVisualizer->setStartSpeaking();
+				
+				//printf( "gAwayTimer.reset();\n" );
+			}
+			
+			mVoiceVisualizer->setSpeakingAmplitude( gVoiceClient->getCurrentPower( mID ) );
+			
+			if( mIsSelf )
+			{
+				gAgent.clearAFK();
+			}
+		}
+		else
+		{
+			if ( mVoiceVisualizer->getCurrentlySpeaking() )
+			{
+				mVoiceVisualizer->setStopSpeaking();
+				
+				if ( mLipSyncActive )
 				{
-					mVoiceVisualizer->setStopSpeaking();
+					if( mOohMorph ) mOohMorph->setWeight(mOohMorph->getMinWeight(), FALSE);
+					if( mAahMorph ) mAahMorph->setWeight(mAahMorph->getMinWeight(), FALSE);
+					
+					mLipSyncActive = false;
+					LLCharacter::updateVisualParams();
+					dirtyMesh();
 				}
 			}
-
-			//--------------------------------------------------------------------------------------------
-			// here we get the approximate head position and set as sound source for the voice symbol
-			// (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing)
-			//--------------------------------------------------------------------------------------------
-			LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] );
-			mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot.getWorldPosition() + headOffset );
-
-		}//if ( voiceEnabled )
-	}
-	//End  Ventrella
+		}
+		
+		//--------------------------------------------------------------------------------------------
+		// here we get the approximate head position and set as sound source for the voice symbol
+		// (the following version uses a tweak of "mHeadOffset" which handle sitting vs. standing)
+		//--------------------------------------------------------------------------------------------
+		LLVector3 headOffset = LLVector3( 0.0f, 0.0f, mHeadOffset.mV[2] );
+		mVoiceVisualizer->setVoiceSourceWorldPosition( mRoot.getWorldPosition() + headOffset );
+		
+	}//if ( voiceEnabled )
 		
 	if (LLVOAvatar::sJointDebug)
 	{
@@ -2704,6 +2723,35 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
 		dirtyMesh();
 	}
 
+	// Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync
+	if ( voiceEnabled && (gVoiceClient->lipSyncEnabled() > 0) && gVoiceClient->getIsSpeaking( mID ) )
+	{
+		F32 ooh_morph_amount = 0.0f;
+		F32 aah_morph_amount = 0.0f;
+
+		mVoiceVisualizer->lipSyncOohAah( ooh_morph_amount, aah_morph_amount );
+
+		if( mOohMorph )
+		{
+			F32 ooh_weight = mOohMorph->getMinWeight()
+				+ ooh_morph_amount * (mOohMorph->getMaxWeight() - mOohMorph->getMinWeight());
+
+			mOohMorph->setWeight( ooh_weight, FALSE );
+		}
+
+		if( mAahMorph )
+		{
+			F32 aah_weight = mAahMorph->getMinWeight()
+				+ aah_morph_amount * (mAahMorph->getMaxWeight() - mAahMorph->getMinWeight());
+
+			mAahMorph->setWeight( aah_weight, FALSE );
+		}
+
+		mLipSyncActive = true;
+		LLCharacter::updateVisualParams();
+		dirtyMesh();
+	}
+
 	// update wind effect
 	if ((LLShaderMgr::getVertexShaderLevel(LLShaderMgr::SHADER_AVATAR) >= LLDrawPoolAvatar::SHADER_LEVEL_CLOTH))
 	{
@@ -3883,7 +3931,7 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
 	if (mDirtyMesh || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY))
 	{	//LOD changed or new mesh created, allocate new vertex buffer if needed
 		updateMeshData();
-        mDirtyMesh = FALSE;
+		mDirtyMesh = FALSE;
 		mNeedsSkin = TRUE;
 		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
 	}
@@ -9507,7 +9555,7 @@ BOOL LLVOAvatar::updateLOD()
 	if (mDirtyMesh || mDrawable->isState(LLDrawable::REBUILD_GEOMETRY))
 	{	//LOD changed or new mesh created, allocate new vertex buffer if needed
 		updateMeshData();
-        mDirtyMesh = FALSE;
+		mDirtyMesh = FALSE;
 		mNeedsSkin = TRUE;
 		mDrawable->clearState(LLDrawable::REBUILD_GEOMETRY);
 	}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
index b4a35d8e7e..8b742f153c 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -831,6 +831,17 @@ public:
 	F32				mLastAppearanceBlendTime;
 	BOOL			mAppearanceAnimating;
 
+	//--------------------------------------------------------------------
+	// we're morphing for lip sync
+	//--------------------------------------------------------------------
+	bool					mLipSyncActive;
+
+	//--------------------------------------------------------------------
+	// cached pointers morphs for lip sync
+	//--------------------------------------------------------------------
+	LLVisualParam		   *mOohMorph;
+	LLVisualParam		   *mAahMorph;
+
 	//--------------------------------------------------------------------
 	// static members
 	//--------------------------------------------------------------------
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 10dea0e715..bba9fe1ccf 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -833,6 +833,7 @@ LLVoiceClient::LLVoiceClient()
 	setCaptureDevice(captureDevice);
 	std::string renderDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
 	setRenderDevice(renderDevice);
+	mLipSyncEnabled = gSavedSettings.getU32("LipSyncEnabled");
 	
 	mTuningMode = false;
 	mTuningEnergy = 0.0f;
@@ -3607,6 +3608,24 @@ bool LLVoiceClient::voiceEnabled()
 	return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice");
 }
 
+void LLVoiceClient::setLipSyncEnabled(U32 enabled)
+{
+	mLipSyncEnabled = enabled;
+}
+
+U32 LLVoiceClient::lipSyncEnabled()
+{
+	   
+	if ( mVoiceEnabled && stateDisabled != getState() )
+	{
+		return mLipSyncEnabled;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
 void LLVoiceClient::setUsePTT(bool usePTT)
 {
 	if(usePTT && !mUsePTT)
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index 5179bc099c..3ac5f0cdbf 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -210,6 +210,8 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient>
 		void setMicGain(F32 volume);
 		void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)
 		void setVivoxDebugServerName(std::string &serverName);
+		void setLipSyncEnabled(U32 enabled);
+		U32 lipSyncEnabled();
 
 		// PTT key triggering
 		void keyDown(KEY key, MASK mask);
@@ -516,6 +518,8 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient>
 		
 		LLTimer		mUpdateTimer;
 		
+		U32			mLipSyncEnabled;
+
 		typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
 		observer_set_t mObservers;
 
diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp
index a7e1915171..38d435ff74 100644
--- a/indra/newview/llvoicevisualizer.cpp
+++ b/indra/newview/llvoicevisualizer.cpp
@@ -79,6 +79,35 @@ const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE	= 1.0f;
 const F32 ONE_HALF = 1.0f; // to clarify intent and reduce magic numbers in the code. 
 const LLVector3 WORLD_UPWARD_DIRECTION = LLVector3( 0.0f, 0.0f, 1.0f ); // Z is up in SL
 
+
+//------------------------------------------------------------------
+// handles parameter updates
+//------------------------------------------------------------------
+static bool handleVoiceVisualizerPrefsChanged(const LLSD& newvalue)
+{
+	// Note: Ignore the specific event value, we look up the ones we want
+	LLVoiceVisualizer::setPreferences();
+	return true;
+}
+
+//------------------------------------------------------------------
+// Initialize the statics
+//------------------------------------------------------------------
+bool LLVoiceVisualizer::sPrefsInitialized	= false;
+U32	 LLVoiceVisualizer::sLipSyncEnabled		= 0;
+F32* LLVoiceVisualizer::sOoh				= NULL;
+F32* LLVoiceVisualizer::sAah				= NULL;
+U32	 LLVoiceVisualizer::sOohs				= 0;
+U32	 LLVoiceVisualizer::sAahs				= 0;
+F32	 LLVoiceVisualizer::sOohAahRate			= 0.0f;
+F32* LLVoiceVisualizer::sOohPowerTransfer	= NULL;
+U32	 LLVoiceVisualizer::sOohPowerTransfers	= 0;
+F32	 LLVoiceVisualizer::sOohPowerTransfersf = 0.0f;
+F32* LLVoiceVisualizer::sAahPowerTransfer	= NULL;
+U32	 LLVoiceVisualizer::sAahPowerTransfers	= 0;
+F32	 LLVoiceVisualizer::sAahPowerTransfersf = 0.0f;
+
+
 //-----------------------------------------------
 // constructor
 //-----------------------------------------------
@@ -87,6 +116,7 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type )
 {
 	mCurrentTime					= mTimer.getTotalSeconds();
 	mPreviousTime					= mCurrentTime;
+	mStartTime						= mCurrentTime;
 	mVoiceSourceWorldPosition		= LLVector3( 0.0f, 0.0f, 0.0f );
 	mSpeakingAmplitude				= 0.0f;
 	mCurrentlySpeaking				= false;
@@ -105,7 +135,7 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type )
 		"29de489d-0491-fb00-7dab-f9e686d31e83.j2c",
 		"29de489d-0491-fb00-7dab-f9e686d31e83.j2c",
 		"29de489d-0491-fb00-7dab-f9e686d31e83.j2c",
-		"29de489d-0491-fb00-7dab-f9e686d31e83.j2c",															
+		"29de489d-0491-fb00-7dab-f9e686d31e83.j2c",
 		"29de489d-0491-fb00-7dab-f9e686d31e83.j2c"
 	};
 
@@ -117,7 +147,23 @@ LLVoiceVisualizer::LLVoiceVisualizer( const U8 type )
 		mSoundSymbol.mWaveOpacity			[i] = 1.0f;
 		mSoundSymbol.mWaveExpansion			[i] = 1.0f;
 	}
-			
+
+	// The first instance loads the initial state from prefs.
+	if (!sPrefsInitialized)
+	{
+		setPreferences();
+       
+		// Set up our listener to get updates on all prefs values we care about.
+		gSavedSettings.getControl("LipSyncEnabled")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _1));
+		gSavedSettings.getControl("LipSyncOohAahRate")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _1));
+		gSavedSettings.getControl("LipSyncOoh")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _1));
+		gSavedSettings.getControl("LipSyncAah")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _1));
+		gSavedSettings.getControl("LipSyncOohPowerTransfer")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _1));
+		gSavedSettings.getControl("LipSyncAahPowerTransfer")->getSignal()->connect(boost::bind(&handleVoiceVisualizerPrefsChanged, _1));
+		
+		sPrefsInitialized = true;
+	}
+
 }//---------------------------------------------------
 
 //---------------------------------------------------
@@ -144,6 +190,7 @@ void LLVoiceVisualizer::setVoiceEnabled( bool v )
 //---------------------------------------------------
 void LLVoiceVisualizer::setStartSpeaking()
 {
+	mStartTime				= mTimer.getTotalSeconds();
 	mCurrentlySpeaking		= true;
 	mSoundSymbol.mActive	= true;
 		
@@ -175,6 +222,130 @@ void LLVoiceVisualizer::setSpeakingAmplitude( F32 a )
 }//---------------------------------------------------
 
 
+//---------------------------------------------------
+void LLVoiceVisualizer::setPreferences( )
+{
+	sLipSyncEnabled = gSavedSettings.getU32("LipSyncEnabled");
+	sOohAahRate		= gSavedSettings.getF32("LipSyncOohAahRate");
+
+	std::string oohString = gSavedSettings.getString("LipSyncOoh");
+	lipStringToF32s (oohString, sOoh, sOohs);
+
+	std::string aahString = gSavedSettings.getString("LipSyncAah");
+	lipStringToF32s (aahString, sAah, sAahs);
+
+	std::string oohPowerString = gSavedSettings.getString("LipSyncOohPowerTransfer");
+	lipStringToF32s (oohPowerString, sOohPowerTransfer, sOohPowerTransfers);
+	sOohPowerTransfersf = (F32) sOohPowerTransfers;
+
+	std::string aahPowerString = gSavedSettings.getString("LipSyncAahPowerTransfer");
+	lipStringToF32s (aahPowerString, sAahPowerTransfer, sAahPowerTransfers);
+	sAahPowerTransfersf = (F32) sAahPowerTransfers;
+
+}//---------------------------------------------------
+
+
+//---------------------------------------------------
+// convert a string of digits to an array of floats.
+// the result for each digit is the value of the
+// digit multiplied by 0.11
+//---------------------------------------------------
+void LLVoiceVisualizer::lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s )
+{
+	delete[] out_F32s;	// get rid of the current array
+
+	count_F32s = in_string.length();
+	if (count_F32s == 0)
+	{
+		// we don't like zero length arrays
+
+		count_F32s  = 1;
+		out_F32s	   = new F32[1];
+		out_F32s[0] = 0.0f;
+	}
+	else
+	{
+		out_F32s = new F32[count_F32s];
+
+		for (U32 i=0; i<count_F32s; i++)
+		{
+			// we convert the characters 0 to 9 to their numeric value
+			// anything else we take the low order four bits with a ceiling of 9
+
+		    U8 digit = in_string[i];
+			U8 four_bits = digit % 16;
+			if (four_bits > 9)
+			{
+				four_bits = 9;
+			}
+			out_F32s[i] = 0.11f * (F32) four_bits;
+		} 
+	}
+
+}//---------------------------------------------------
+
+
+//--------------------------------------------------------------------------
+// find the amount to blend the ooh and aah mouth morphs
+//--------------------------------------------------------------------------
+void LLVoiceVisualizer::lipSyncOohAah( F32& ooh, F32& aah )
+{
+	if( ( sLipSyncEnabled == 1 ) && mCurrentlySpeaking )
+	{
+		U32 transfer_index = (U32) (sOohPowerTransfersf * mSpeakingAmplitude);
+		if (transfer_index < 0)
+		{
+		   transfer_index = 0;
+		}
+		if (transfer_index >= sOohPowerTransfers)
+		{
+		   transfer_index = sOohPowerTransfers - 1;
+		}
+		F32 transfer_ooh = sOohPowerTransfer[transfer_index];
+
+		transfer_index = (U32) (sAahPowerTransfersf * mSpeakingAmplitude);
+		if (transfer_index < 0)
+		{
+		   transfer_index = 0;
+		}
+		if (transfer_index >= sAahPowerTransfers)
+		{
+		   transfer_index = sAahPowerTransfers - 1;
+		}
+		F32 transfer_aah = sAahPowerTransfer[transfer_index];
+
+		F64 current_time   = mTimer.getTotalSeconds();
+		F64 elapsed_time   = current_time - mStartTime;
+		U32 elapsed_frames = (U32) (elapsed_time * sOohAahRate);
+		U32 elapsed_oohs   = elapsed_frames % sOohs;
+		U32 elapsed_aahs   = elapsed_frames % sAahs;
+
+		ooh = transfer_ooh * sOoh[elapsed_oohs];
+		aah = transfer_aah * sAah[elapsed_aahs];
+
+		/*
+		llinfos << " elapsed frames " << elapsed_frames
+				<< " ooh "            << ooh
+				<< " aah "            << aah
+				<< " transfer ooh"    << transfer_ooh
+				<< " transfer aah"    << transfer_aah
+				<< " start time "     << mStartTime
+				<< " current time "   << current_time
+				<< " elapsed time "   << elapsed_time
+				<< " elapsed oohs "   << elapsed_oohs
+				<< " elapsed aahs "   << elapsed_aahs
+				<< llendl;
+		*/
+	}
+	else
+	{
+		ooh = 0.0f;
+		aah = 0.0f;
+	}
+	
+}//---------------------------------------------------
+
+
 //---------------------------------------------------
 // this method is inherited from HUD Effect
 //---------------------------------------------------
diff --git a/indra/newview/llvoicevisualizer.h b/indra/newview/llvoicevisualizer.h
index 3543f254db..bb2ce55d4a 100644
--- a/indra/newview/llvoicevisualizer.h
+++ b/indra/newview/llvoicevisualizer.h
@@ -90,7 +90,9 @@ class LLVoiceVisualizer : public LLHUDEffect
 		void					setStopSpeaking();										// tell me when the av stops speaking
 		bool					getCurrentlySpeaking();									// the get for the above set
 		VoiceGesticulationLevel	getCurrentGesticulationLevel();							// based on voice amplitude, I'll give you the current "energy level" of avatar speech
-
+		static void				setPreferences( );
+		static void				lipStringToF32s ( std::string& in_string, F32*& out_F32s, U32& count_F32s ); // convert a string of digits to an array of floats
+		void					lipSyncOohAah( F32& ooh, F32& aah );
 		void					render();												// inherited from HUD Effect
 		void 					packData(LLMessageSystem *mesgsys);						// inherited from HUD Effect
 		void 					unpackData(LLMessageSystem *mesgsys, S32 blocknum);		// inherited from HUD Effect
@@ -124,6 +126,7 @@ class LLVoiceVisualizer : public LLHUDEffect
 		};
 
 		LLFrameTimer			mTimer;							// so I can ask the current time in seconds
+		F64						mStartTime;						// time in seconds when speaking started
 		F64						mCurrentTime;					// current time in seconds, captured every step
 		F64						mPreviousTime;					// copy of "current time" from last frame
 		SoundSymbol				mSoundSymbol;					// the sound symbol that appears over the avatar's head
@@ -134,6 +137,24 @@ class LLVoiceVisualizer : public LLHUDEffect
 		F32						mMaxGesticulationAmplitude;		// this is the upper-limit of the envelope of detectable gesticulation leves
 		F32						mMinGesticulationAmplitude;		// this is the lower-limit of the envelope of detectable gesticulation leves
 		
+	//---------------------------------------------------
+	// private static members 
+	//---------------------------------------------------
+
+		static U32	  sLipSyncEnabled;		 // 0 disabled, 1 babble loop
+		static bool	  sPrefsInitialized;	 // the first instance will initialize the static members
+		static F32*	  sOoh;					 // the babble loop of amplitudes for the ooh morph
+		static F32*	  sAah;					 // the babble loop of amplitudes for the ooh morph
+		static U32	  sOohs;				 // the number of entries in the ooh loop
+		static U32	  sAahs;				 // the number of entries in the aah loop
+		static F32	  sOohAahRate;			 // frames per second for the babble loop
+		static F32*	  sOohPowerTransfer;	 // the power transfer characteristics for the ooh amplitude
+		static U32	  sOohPowerTransfers;	 // the number of entries in the ooh transfer characteristics
+		static F32	  sOohPowerTransfersf;	 // the number of entries in the ooh transfer characteristics as a float
+		static F32*	  sAahPowerTransfer;	 // the power transfer characteristics for the aah amplitude
+		static U32	  sAahPowerTransfers;	 // the number of entries in the aah transfer characteristics
+		static F32	  sAahPowerTransfersf;	 // the number of entries in the aah transfer characteristics as a float
+
 };//-----------------------------------------------------------------
  //   end of LLVoiceVisualizer class
 //------------------------------------------------------------------
-- 
cgit v1.2.3