[SCM] A client for connecting to 3D metaverses such as Linden Labs Secondlife(tm) and OpenSim grids branch, upstream, updated. upstream/1.21.6-13-g1836a2b

Robin Cornelius robin.cornelius at gmail.com
Tue Apr 14 11:12:13 UTC 2009


The following commit has been merged in the upstream branch:
commit 62894817c251c1344b50b5620b93ad55cf3b6055
Author: Robin Cornelius <robin.cornelius at gmail.com>
Date:   Thu Feb 5 22:26:25 2009 +0000

    Imported Upstream version 1.22.8

diff --git a/doc/contributions.txt b/doc/contributions.txt
index 3608b52..5cc26c2 100644
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -224,11 +224,14 @@ McCabe Maxsted
 	VWR-8689
 Michelle2 Zenovka
 	VWR-2652
+	VWR-2662
 	VWR-2834
 	VWR-3749
 	VWR-4022
+	VWR-4331
 	VWR-4506
 	VWR-4981
+	VWR-5659
 	VWR-7831
 	VWR-8889
 	VWR-8310
@@ -348,6 +351,7 @@ Seg Baphomet
 	VWR-1525
 	VWR-1585
 	VWR-1586
+	VWR-2662
 	VWR-3206
 	VWR-2488
 SignpostMarv Martin
@@ -418,6 +422,7 @@ Whoops Babii
 	VWR-631
 	VWR-1640
 	VWR-3340
+	VWR-5659
 Zarkonnen Decosta
 	VWR-253
 Zi Ree
diff --git a/indra/cmake/CopyWinLibs.cmake b/indra/cmake/CopyWinLibs.cmake
index d4ddb23..92b5d71 100644
--- a/indra/cmake/CopyWinLibs.cmake
+++ b/indra/cmake/CopyWinLibs.cmake
@@ -8,12 +8,7 @@ include(CMakeCopyIfDifferent)
 
 set(vivox_src_dir "${CMAKE_SOURCE_DIR}/newview/vivox-runtime/i686-win32")
 set(vivox_files
-    tntk.dll
-    libeay32.dll
     SLVoice.exe
-    ssleay32.dll
-    SLVoiceAgent.exe
-    srtp.dll
     alut.dll
     vivoxsdk.dll
     ortp.dll
diff --git a/indra/cmake/LLAudio.cmake b/indra/cmake/LLAudio.cmake
index eff643d..ff479f6 100644
--- a/indra/cmake/LLAudio.cmake
+++ b/indra/cmake/LLAudio.cmake
@@ -6,4 +6,11 @@ set(LLAUDIO_INCLUDE_DIRS
     ${LIBS_OPEN_DIR}/llaudio
     )
 
-set(LLAUDIO_LIBRARIES llaudio)
+set(LLAUDIO_LIBRARIES
+    llaudio
+    ${VORBISENC_LIBRARIES}
+    ${VORBISFILE_LIBRARIES}
+    ${VORBIS_LIBRARIES}
+    ${OGG_LIBRARIES}
+    ${OPENAL_LIBRARIES}
+    )
diff --git a/indra/cmake/OPENAL.cmake b/indra/cmake/OPENAL.cmake
new file mode 100644
index 0000000..eb195a0
--- /dev/null
+++ b/indra/cmake/OPENAL.cmake
@@ -0,0 +1,28 @@
+# -*- cmake -*-
+include(Linking)
+include(Prebuilt)
+
+if (LINUX)
+  set(OPENAL ON CACHE BOOL "Enable OpenAL")
+else (LINUX)
+  set(OPENAL OFF CACHE BOOL "Enable OpenAL")
+endif (LINUX)
+
+if (OPENAL)
+  if (STANDALONE)
+    include(FindPkgConfig)
+    include(FindOpenAL)
+    pkg_check_modules(OPENAL_LIB REQUIRED openal)
+    pkg_check_modules(FREEALUT_LIB REQUIRED freealut)
+  else (STANDALONE)
+    use_prebuilt_binary(openal)
+  endif (STANDALONE)
+  set(OPENAL_LIBRARIES 
+    openal
+    alut
+    )
+endif (OPENAL)
+
+if (OPENAL)
+  message(STATUS "Building with OpenAL audio support")
+endif (OPENAL)
diff --git a/indra/lib/python/indra/__init__.pyc b/indra/lib/python/indra/__init__.pyc
new file mode 100644
index 0000000..ac7a555
Binary files /dev/null and b/indra/lib/python/indra/__init__.pyc differ
diff --git a/indra/lib/python/indra/ipc/__init__.pyc b/indra/lib/python/indra/ipc/__init__.pyc
new file mode 100644
index 0000000..c9b04ba
Binary files /dev/null and b/indra/lib/python/indra/ipc/__init__.pyc differ
diff --git a/indra/lib/python/indra/ipc/compatibility.pyc b/indra/lib/python/indra/ipc/compatibility.pyc
new file mode 100644
index 0000000..8eb8832
Binary files /dev/null and b/indra/lib/python/indra/ipc/compatibility.pyc differ
diff --git a/indra/lib/python/indra/ipc/llmessage.pyc b/indra/lib/python/indra/ipc/llmessage.pyc
new file mode 100644
index 0000000..3485e0f
Binary files /dev/null and b/indra/lib/python/indra/ipc/llmessage.pyc differ
diff --git a/indra/lib/python/indra/ipc/tokenstream.pyc b/indra/lib/python/indra/ipc/tokenstream.pyc
new file mode 100644
index 0000000..4411912
Binary files /dev/null and b/indra/lib/python/indra/ipc/tokenstream.pyc differ
diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py
index 59c84e5..12e33f2 100644
--- a/indra/lib/python/indra/util/llmanifest.py
+++ b/indra/lib/python/indra/util/llmanifest.py
@@ -452,7 +452,7 @@ class LLManifest(object):
             # *TODO is this gonna be useful?
             print "Cleaning up " + c
 
-    def process_file(self, src, dst):
+    def process_file(self, src, dst, strip=False):
         if self.includes(src, dst):
 #            print src, "=>", dst
             for action in self.actions:
@@ -460,7 +460,7 @@ class LLManifest(object):
                 method = getattr(self, methodname, None)
                 if method is not None:
                     method(src, dst)
-            self.file_list.append([src, dst])
+            self.file_list.append([src, dst, strip])
             return 1
         else:
             sys.stdout.write(" (excluding %r, %r)" % (src, dst))
@@ -607,7 +607,7 @@ class LLManifest(object):
             d = src_re.sub(d_template, s.replace('\\', '/'))
             yield os.path.normpath(s), os.path.normpath(d)
 
-    def path(self, src, dst=None):
+    def path(self, src, dst=None, strip=False):
         sys.stdout.write("Processing %s => %s ... " % (src, dst))
         sys.stdout.flush()
         if src == None:
@@ -622,7 +622,7 @@ class LLManifest(object):
             if self.wildcard_pattern.search(src):
                 for s,d in self.expand_globs(src, dst):
                     assert(s != d)
-                    count += self.process_file(s, d)
+                    count += self.process_file(s, d, strip)
             else:
                 # if we're specifying a single path (not a glob),
                 # we should error out if it doesn't exist
@@ -631,7 +631,7 @@ class LLManifest(object):
                 if os.path.isdir(src):
                     count += self.process_directory(src, dst)
                 else:
-                    count += self.process_file(src, dst)
+                    count += self.process_file(src, dst, strip)
             return count
         try:
             count = try_path(os.path.join(self.get_src_prefix(), src))
diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt
index ebedcab..235248e 100644
--- a/indra/llaudio/CMakeLists.txt
+++ b/indra/llaudio/CMakeLists.txt
@@ -5,10 +5,12 @@ project(llaudio)
 include(00-Common)
 include(Audio)
 include(FMOD)
+include(OPENAL)
 include(LLCommon)
 include(LLMath)
 include(LLMessage)
 include(LLVFS)
+include(LLMedia)
 
 include_directories(
     ${FMOD_INCLUDE_DIR}
@@ -20,6 +22,9 @@ include_directories(
     ${VORBISENC_INCLUDE_DIRS}
     ${VORBISFILE_INCLUDE_DIRS}
     ${VORBIS_INCLUDE_DIRS}
+    ${OPENAL_LIB_INCLUDE_DIRS}
+    ${FREEAULT_LIB_INCLUDE_DIRS}
+    ${LLMEDIA_INCLUDE_DIRS}
     )
 
 set(llaudio_SOURCE_FILES
@@ -38,6 +43,7 @@ set(llaudio_HEADER_FILES
     llaudiodecodemgr.h
     vorbisdecode.h
     vorbisencode.h
+    windgen.h
     )
 
 if (FMOD)
@@ -59,6 +65,18 @@ if (FMOD)
     endif (LINUX)
 endif (FMOD)
 
+if (OPENAL)
+  list(APPEND llaudio_SOURCE_FILES
+    audioengine_openal.cpp
+    listener_openal.cpp
+    )
+  
+  list(APPEND llaudio_HEADER_FILES
+    audioengine_openal.h
+    listener_openal.h
+    )
+endif (OPENAL)
+
 set_source_files_properties(${llaudio_HEADER_FILES}
                             PROPERTIES HEADER_FILE_ONLY TRUE)
 
diff --git a/indra/llaudio/audioengine.cpp b/indra/llaudio/audioengine.cpp
index 81536cf..c5bc367 100644
--- a/indra/llaudio/audioengine.cpp
+++ b/indra/llaudio/audioengine.cpp
@@ -44,14 +44,13 @@
 #include "llaudiodecodemgr.h"
 #include "llassetstorage.h"
 
+#include "llmediamanager.h"
+
 // necessary for grabbing sounds from sim (implemented in viewer)	
 extern void request_sound(const LLUUID &sound_guid);
 
 LLAudioEngine* gAudiop = NULL;
 
-// Maximum amount of time we wait for a transfer to complete before starting
-// off another one.
-const F32 MAX_CURRENT_TRANSFER_TIME = 60.f;
 
 //
 // LLAudioEngine implementation
@@ -75,13 +74,13 @@ void LLAudioEngine::setDefaults()
 
 	mListenerp = NULL;
 
-	mMuted = FALSE;
+	mMuted = false;
 	mUserData = NULL;
 
 	mLastStatus = 0;
 
 	mNumChannels = 0;
-	mEnableWind = FALSE;
+	mEnableWind = false;
 
 	S32 i;
 	for (i = 0; i < MAX_CHANNELS; i++)
@@ -91,7 +90,7 @@ void LLAudioEngine::setDefaults()
 	for (i = 0; i < MAX_BUFFERS; i++)
 	{
 		mBuffers[i] = NULL;
-	}	
+	}
 
 	mMasterGain = 1.f;
 	mInternetStreamGain = 0.125f;
@@ -99,10 +98,13 @@ void LLAudioEngine::setDefaults()
 
 	for (U32 i = 0; i < LLAudioEngine::AUDIO_TYPE_COUNT; i++)
 		mSecondaryGain[i] = 1.0f;
+
+	mInternetStreamMedia = NULL;
+	mInternetStreamURL.clear();
 }
 
 
-BOOL LLAudioEngine::init(const S32 num_channels, void* userdata)
+bool LLAudioEngine::init(const S32 num_channels, void* userdata)
 {
 	setDefaults();
 
@@ -114,7 +116,9 @@ BOOL LLAudioEngine::init(const S32 num_channels, void* userdata)
 	// Initialize the decode manager
 	gAudioDecodeMgrp = new LLAudioDecodeMgr;
 
-	return TRUE;
+	llinfos << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl;
+
+	return true;
 }
 
 
@@ -124,6 +128,9 @@ void LLAudioEngine::shutdown()
 	delete gAudioDecodeMgrp;
 	gAudioDecodeMgrp = NULL;
 
+	// Clean up wind source
+	cleanupWind();
+
 	// Clean up audio sources
 	source_map::iterator iter_src;
 	for (iter_src = mAllSources.begin(); iter_src != mAllSources.end(); iter_src++)
@@ -144,22 +151,145 @@ void LLAudioEngine::shutdown()
 	S32 i;
 	for (i = 0; i < MAX_CHANNELS; i++)
 	{
-		if (mChannels[i])
-		{
-			delete mChannels[i];
-			mChannels[i] = NULL;
-		}
+		delete mChannels[i];
+		mChannels[i] = NULL;
 	}
 
 	// Clean up buffers
 	for (i = 0; i < MAX_BUFFERS; i++)
 	{
-		if (mBuffers[i])
+		delete mBuffers[i];
+		mBuffers[i] = NULL;
+	}
+
+	delete mInternetStreamMedia;
+	mInternetStreamMedia = NULL;
+	mInternetStreamURL.clear();
+}
+
+
+// virtual
+void LLAudioEngine::startInternetStream(const std::string& url)
+{
+	llinfos << "entered startInternetStream()" << llendl;
+
+	if (!mInternetStreamMedia)
+	{
+		LLMediaManager* mgr = LLMediaManager::getInstance();
+		if (mgr)
 		{
-			delete mBuffers[i];
-			mBuffers[i] = NULL;
+			mInternetStreamMedia = mgr->createSourceFromMimeType(LLURI(url).scheme(), "audio/mpeg"); // assumes that whatever media implementation supports mp3 also supports vorbis.
+			llinfos << "mInternetStreamMedia is now " << mInternetStreamMedia << llendl;
 		}
 	}
+
+	if(!mInternetStreamMedia)
+		return;
+	
+	if (!url.empty()) {
+		llinfos << "Starting internet stream: " << url << llendl;
+		mInternetStreamURL = url;
+		mInternetStreamMedia->navigateTo ( url );
+		llinfos << "Playing....." << llendl;		
+		mInternetStreamMedia->addCommand(LLMediaBase::COMMAND_START);
+		mInternetStreamMedia->updateMedia();
+	} else {
+		llinfos << "setting stream to NULL"<< llendl;
+		mInternetStreamURL.clear();
+		mInternetStreamMedia->addCommand(LLMediaBase::COMMAND_STOP);
+		mInternetStreamMedia->updateMedia();
+	}
+}
+
+// virtual
+void LLAudioEngine::stopInternetStream()
+{
+	llinfos << "entered stopInternetStream()" << llendl;
+	
+        if(mInternetStreamMedia)
+	{
+		if( ! mInternetStreamMedia->addCommand(LLMediaBase::COMMAND_STOP)){
+			llinfos << "attempting to stop stream failed!" << llendl;
+		}
+		mInternetStreamMedia->updateMedia();
+	}
+
+	mInternetStreamURL.clear();
+}
+
+// virtual
+void LLAudioEngine::pauseInternetStream(int pause)
+{
+	llinfos << "entered pauseInternetStream()" << llendl;
+
+	if(!mInternetStreamMedia)
+		return;
+	
+	if(pause)
+	{
+		if(! mInternetStreamMedia->addCommand(LLMediaBase::COMMAND_PAUSE))
+		{
+			llinfos << "attempting to pause stream failed!" << llendl;
+		}
+	} else {
+		if(! mInternetStreamMedia->addCommand(LLMediaBase::COMMAND_START))
+		{
+			llinfos << "attempting to unpause stream failed!" << llendl;
+		}
+	}
+	mInternetStreamMedia->updateMedia();
+}
+
+// virtual
+void LLAudioEngine::updateInternetStream()
+{
+	if (mInternetStreamMedia)
+		mInternetStreamMedia->updateMedia();
+}
+
+// virtual
+int LLAudioEngine::isInternetStreamPlaying()
+{
+	if (!mInternetStreamMedia)
+		return 0;
+	
+	if (mInternetStreamMedia->getStatus() == LLMediaBase::STATUS_STARTED)
+	{
+		return 1; // Active and playing
+	}	
+
+	if (mInternetStreamMedia->getStatus() == LLMediaBase::STATUS_PAUSED)
+	{
+		return 2; // paused
+	}
+
+	return 0; // Stopped
+}
+
+// virtual
+void LLAudioEngine::getInternetStreamInfo(char* artist, char* title)
+{
+	artist[0] = 0;
+	title[0] = 0;
+}
+
+// virtual
+void LLAudioEngine::setInternetStreamGain(F32 vol)
+{
+	mInternetStreamGain = vol;
+
+	if(!mInternetStreamMedia)
+		return;
+
+	vol = llclamp(vol, 0.f, 1.f);
+	mInternetStreamMedia->setVolume(vol);
+	mInternetStreamMedia->updateMedia();
+}
+
+// virtual
+const std::string& LLAudioEngine::getInternetStreamURL()
+{
+	return mInternetStreamURL;
 }
 
 
@@ -203,7 +333,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
 	{
 		if (mBuffers[i])
 		{
-			mBuffers[i]->mInUse = FALSE;
+			mBuffers[i]->mInUse = false;
 		}
 	}
 
@@ -255,11 +385,11 @@ void LLAudioEngine::idle(F32 max_decode_time)
 			{
 				// A sync slave, it doesn't start playing until it's synced up with the master.
 				// Flag this channel as waiting for sync, and return true.
-				channelp->setWaiting(TRUE);
+				channelp->setWaiting(true);
 			}
 			else
 			{
-				channelp->setWaiting(FALSE);
+				channelp->setWaiting(false);
 				channelp->play();
 			}
 		}
@@ -399,7 +529,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
 				if (sync_masterp->getChannel())
 				{
 					channelp->playSynced(master_channelp);
-					channelp->setWaiting(FALSE);
+					channelp->setWaiting(false);
 				}
 			}
 		}
@@ -429,7 +559,7 @@ void LLAudioEngine::idle(F32 max_decode_time)
 	{
 		if (mChannels[i])
 		{
-			mChannels[i]->mLoopedThisFrame = FALSE;
+			mChannels[i]->mLoopedThisFrame = false;
 		}
 	}
 
@@ -440,13 +570,17 @@ void LLAudioEngine::idle(F32 max_decode_time)
 	// missed picking it up in all the places that can add
 	// or request new data.
 	startNextTransfer();
+
+	updateInternetStream();
 }
 
-BOOL LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid)
+
+
+bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid)
 {
 	if (!adp)
 	{
-		return FALSE;
+		return false;
 	}
 
 	// Update the audio buffer first - load a sound if we have it.
@@ -469,14 +603,14 @@ BOOL LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uu
 		}
 		else
 		{
-			return FALSE;
+			return false;
 		}
 	}
-	return TRUE;
+	return true;
 }
 
 
-void LLAudioEngine::enableWind(BOOL enable)
+void LLAudioEngine::enableWind(bool enable)
 {
 	if (enable && (!mEnableWind))
 	{
@@ -604,7 +738,7 @@ void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp)
 }
 
 
-BOOL LLAudioEngine::preloadSound(const LLUUID &uuid)
+bool LLAudioEngine::preloadSound(const LLUUID &uuid)
 {
 	gAudiop->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
@@ -612,23 +746,23 @@ BOOL LLAudioEngine::preloadSound(const LLUUID &uuid)
 	if (gAudioDecodeMgrp->addDecodeRequest(uuid))
 	{
 		// This means that we do have a local copy, and we're working on decoding it.
-		return TRUE;
+		return true;
 	}
 
 	// At some point we need to have the audio/asset system check the static VFS
 	// before it goes off and fetches stuff from the server.
 	//llwarns << "Used internal preload for non-local sound" << llendl;
-	return FALSE;
+	return false;
 }
 
 
-BOOL LLAudioEngine::isWindEnabled()
+bool LLAudioEngine::isWindEnabled()
 {
 	return mEnableWind;
 }
 
 
-void LLAudioEngine::setMuted(BOOL muted)
+void LLAudioEngine::setMuted(bool muted)
 {
 	mMuted = muted;
 	enableWind(!mMuted);
@@ -751,7 +885,7 @@ void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_i
 	gAudiop->addAudioSource(asp);
 	if (pos_global.isExactlyZero())
 	{
-		asp->setAmbient(TRUE);
+		asp->setAmbient(true);
 	}
 	else
 	{
@@ -930,7 +1064,7 @@ void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp)
 }
 
 
-BOOL LLAudioEngine::hasDecodedFile(const LLUUID &uuid)
+bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid)
 {
 	std::string uuid_str;
 	uuid.toString(uuid_str);
@@ -941,16 +1075,16 @@ BOOL LLAudioEngine::hasDecodedFile(const LLUUID &uuid)
 
 	if (gDirUtilp->fileExists(wav_path))
 	{
-		return TRUE;
+		return true;
 	}
 	else
 	{
-		return FALSE;
+		return false;
 	}
 }
 
 
-BOOL LLAudioEngine::hasLocalFile(const LLUUID &uuid)
+bool LLAudioEngine::hasLocalFile(const LLUUID &uuid)
 {
 	// See if it's in the VFS.
 	return gVFS->getExists(uuid, LLAssetType::AT_SOUND);
@@ -1166,9 +1300,9 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E
 		LLAudioData *adp = gAudiop->getAudioData(uuid);
 		if (adp)
         {
-			adp->setHasValidData(FALSE);
-			adp->setHasLocalData(FALSE);
-			adp->setHasDecodedData(FALSE);
+			adp->setHasValidData(false);
+			adp->setHasLocalData(false);
+			adp->setHasDecodedData(false);
 		}
 	}
 	else
@@ -1181,8 +1315,8 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E
         }
 		else
 		{
-			adp->setHasValidData(TRUE);
-		    adp->setHasLocalData(TRUE);
+			adp->setHasValidData(true);
+		    adp->setHasLocalData(true);
 		    gAudioDecodeMgrp->addDecodeRequest(uuid);
 		}
 	}
@@ -1202,12 +1336,12 @@ LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32
 	mPriority(0.f),
 	mGain(gain),
 	mType(type),
-	mAmbient(FALSE),
-	mLoop(FALSE),
-	mSyncMaster(FALSE),
-	mSyncSlave(FALSE),
-	mQueueSounds(FALSE),
-	mPlayedOnce(FALSE),
+	mAmbient(false),
+	mLoop(false),
+	mSyncMaster(false),
+	mSyncSlave(false),
+	mQueueSounds(false),
+	mPlayedOnce(false),
 	mChannelp(NULL),
 	mCurrentDatap(NULL),
 	mQueuedDatap(NULL)
@@ -1271,7 +1405,7 @@ void LLAudioSource::updatePriority()
 	}
 }
 
-BOOL LLAudioSource::setupChannel()
+bool LLAudioSource::setupChannel()
 {
 	LLAudioData *adp = getCurrentData();
 
@@ -1279,7 +1413,7 @@ BOOL LLAudioSource::setupChannel()
 	{
 		// We're not ready to play back the sound yet, so don't try and allocate a channel for it.
 		//llwarns << "Aborting, no buffer" << llendl;
-		return FALSE;
+		return false;
 	}
 
 
@@ -1297,15 +1431,15 @@ BOOL LLAudioSource::setupChannel()
 		// Now we have to reprioritize.
 		// For now, just don't play the sound.
 		//llwarns << "Aborting, no free channels" << llendl;
-		return FALSE;
+		return false;
 	}
 
 	mChannelp->setSource(this);
-	return TRUE;
+	return true;
 }
 
 
-BOOL LLAudioSource::play(const LLUUID &audio_uuid)
+bool LLAudioSource::play(const LLUUID &audio_uuid)
 {
 	if (audio_uuid.isNull())
 	{
@@ -1313,7 +1447,7 @@ BOOL LLAudioSource::play(const LLUUID &audio_uuid)
 		{
 			getChannel()->setSource(NULL);
 			setChannel(NULL);
-			addAudioData(NULL, TRUE);
+			addAudioData(NULL, true);
 		}
 	}
 	// Reset our age timeout if someone attempts to play the source.
@@ -1321,7 +1455,7 @@ BOOL LLAudioSource::play(const LLUUID &audio_uuid)
 
 	LLAudioData *adp = gAudiop->getAudioData(audio_uuid);
 
-	BOOL has_buffer = gAudiop->updateBufferForData(adp, audio_uuid);
+	bool has_buffer = gAudiop->updateBufferForData(adp, audio_uuid);
 
 
 	addAudioData(adp);
@@ -1329,47 +1463,48 @@ BOOL LLAudioSource::play(const LLUUID &audio_uuid)
 	if (!has_buffer)
 	{
 		// Don't bother trying to set up a channel or anything, we don't have an audio buffer.
-		return FALSE;
+		return false;
 	}
 
 	if (!setupChannel())
 	{
-		return FALSE;
+		return false;
 	}
 
 	if (isSyncSlave())
 	{
 		// A sync slave, it doesn't start playing until it's synced up with the master.
 		// Flag this channel as waiting for sync, and return true.
-		getChannel()->setWaiting(TRUE);
-		return TRUE;
+		getChannel()->setWaiting(true);
+		return true;
 	}
 
 	getChannel()->play();
-	return TRUE;
+	return true;
 }
 
 
-BOOL LLAudioSource::isDone()
+bool LLAudioSource::isDone()
 {
 	const F32 MAX_AGE = 60.f;
 	const F32 MAX_UNPLAYED_AGE = 15.f;
+
 	if (isLoop())
 	{
 		// Looped sources never die on their own.
-		return FALSE;
+		return false;
 	}
 
 
 	if (hasPendingPreloads())
 	{
-		return FALSE;
+		return false;
 	}
 
 	if (mQueuedDatap)
 	{
 		// Don't kill this sound if we've got something queued up to play.
-		return FALSE;
+		return false;
 	}
 
 	F32 elapsed = mAgeTimer.getElapsedTimeF32();
@@ -1382,11 +1517,11 @@ BOOL LLAudioSource::isDone()
 			// We don't have a channel assigned, and it's been
 			// over 5 seconds since we tried to play it.  Don't bother.
 			//llinfos << "No channel assigned, source is done" << llendl;
-			return TRUE;
+			return true;
 		}
 		else
 		{
-			return FALSE;
+			return false;
 		}
 	}
 
@@ -1394,27 +1529,27 @@ BOOL LLAudioSource::isDone()
 	{
 		if (elapsed > MAX_AGE)
 		{
-			// Arbitarily cut off non-looped sounds when they're 20 seconds old.
-			return TRUE;
+			// Arbitarily cut off non-looped sounds when they're old.
+			return true;
 		}
 		else
 		{
 			// Sound is still playing and we haven't timed out, don't kill it.
-			return FALSE;
+			return false;
 		}
 	}
 
 	if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce)
 	{
 		// The sound isn't playing back after 5 seconds or we're already done playing it, kill it.
-		return TRUE;
+		return true;
 	}
 
-	return FALSE;
+	return false;
 }
 
 
-void LLAudioSource::addAudioData(LLAudioData *adp, const BOOL set_current)
+void LLAudioSource::addAudioData(LLAudioData *adp, const bool set_current)
 {
 	// Only handle a single piece of audio data associated with a source right now,
 	// until I implement prefetch.
@@ -1482,7 +1617,7 @@ void LLAudioSource::addAudioData(LLAudioData *adp, const BOOL set_current)
 }
 
 
-BOOL LLAudioSource::hasPendingPreloads() const
+bool LLAudioSource::hasPendingPreloads() const
 {
 	// Check to see if we've got any preloads on deck for this source
 	data_map::const_iterator iter;
@@ -1492,11 +1627,11 @@ BOOL LLAudioSource::hasPendingPreloads() const
 		if (!adp->hasDecodedData())
 		{
 			// This source is still waiting for a preload
-			return TRUE;
+			return true;
 		}
 	}
 
-	return FALSE;
+	return false;
 }
 
 
@@ -1531,8 +1666,8 @@ LLAudioBuffer *LLAudioSource::getCurrentBuffer()
 LLAudioChannel::LLAudioChannel() :
 	mCurrentSourcep(NULL),
 	mCurrentBufferp(NULL),
-	mLoopedThisFrame(FALSE),
-	mWaiting(FALSE),
+	mLoopedThisFrame(false),
+	mWaiting(false),
 	mSecondaryGain(1.0f)
 {
 }
@@ -1560,7 +1695,7 @@ void LLAudioChannel::setSource(LLAudioSource *sourcep)
 		//llinfos << "Clearing source for channel" << llendl;
 		cleanup();
 		mCurrentSourcep = NULL;
-		mWaiting = FALSE;
+		mWaiting = false;
 		return;
 	}
 
@@ -1578,13 +1713,13 @@ void LLAudioChannel::setSource(LLAudioSource *sourcep)
 }
 
 
-BOOL LLAudioChannel::updateBuffer()
+bool LLAudioChannel::updateBuffer()
 {
 	if (!mCurrentSourcep)
 	{
 		// This channel isn't associated with any source, nothing
 		// to be updated
-		return FALSE;
+		return false;
 	}
 
 	// Initialize the channel's gain setting for this sound.
@@ -1600,14 +1735,14 @@ BOOL LLAudioChannel::updateBuffer()
 		{
 			// The source hasn't changed what buffer it's playing
 			bufferp->mLastUseTimer.reset();
-			bufferp->mInUse = TRUE;
+			bufferp->mInUse = true;
 		}
-		return FALSE;
+		return false;
 	}
 
 	//
-	// The source changed what buffer it's playing.  Whe need to clean up the
-	// existing fmod channel
+	// The source changed what buffer it's playing.  We need to clean up
+	// the existing channel
 	//
 	cleanup();
 
@@ -1615,16 +1750,16 @@ BOOL LLAudioChannel::updateBuffer()
 	if (bufferp)
 	{
 		bufferp->mLastUseTimer.reset();
-		bufferp->mInUse = TRUE;
+		bufferp->mInUse = true;
 	}
 
 	if (!mCurrentBufferp)
 	{
 		// There's no new buffer to be played, so we just abort.
-		return FALSE;
+		return false;
 	}
 
-	return TRUE;
+	return true;
 }
 
 
@@ -1638,9 +1773,9 @@ BOOL LLAudioChannel::updateBuffer()
 LLAudioData::LLAudioData(const LLUUID &uuid) :
 	mID(uuid),
 	mBufferp(NULL),
-	mHasLocalData(FALSE),
-	mHasDecodedData(FALSE),
-	mHasValidData(TRUE)
+	mHasLocalData(false),
+	mHasDecodedData(false),
+	mHasValidData(true)
 {
 	if (uuid.isNull())
 	{
@@ -1651,24 +1786,24 @@ LLAudioData::LLAudioData(const LLUUID &uuid) :
 	if (gAudiop && gAudiop->hasDecodedFile(uuid))
 	{
 		// Already have a decoded version, don't need to decode it.
-		mHasLocalData = TRUE;
-		mHasDecodedData = TRUE;
+		mHasLocalData = true;
+		mHasDecodedData = true;
 	}
 	else if (gAssetStorage && gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
 	{
-		mHasLocalData = TRUE;
+		mHasLocalData = true;
 	}
 }
 
 
-BOOL LLAudioData::load()
+bool LLAudioData::load()
 {
 	// For now, just assume we're going to use one buffer per audiodata.
 	if (mBufferp)
 	{
 		// We already have this sound in a buffer, don't do anything.
 		llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl;
-		return TRUE;
+		return true;
 	}
 	
 	mBufferp = gAudiop->getFreeBuffer();
@@ -1676,7 +1811,7 @@ BOOL LLAudioData::load()
 	{
 		// No free buffers, abort.
 		llinfos << "Not able to allocate a new audio buffer, aborting." << llendl;
-		return FALSE;
+		return false;
 	}
 
 	std::string uuid_str;
@@ -1690,10 +1825,10 @@ BOOL LLAudioData::load()
 		gAudiop->cleanupBuffer(mBufferp);
 		mBufferp = NULL;
 
-		return FALSE;
+		return false;
 	}
 	mBufferp->mAudioDatap = this;
-	return TRUE;
+	return true;
 }
 
 
diff --git a/indra/llaudio/audioengine.h b/indra/llaudio/audioengine.h
index 0bd6327..b582f14 100644
--- a/indra/llaudio/audioengine.h
+++ b/indra/llaudio/audioengine.h
@@ -45,6 +45,8 @@
 #include "llframetimer.h"
 #include "llassettype.h"
 
+class LLMediaBase;
+
 const F32 LL_WIND_UPDATE_INTERVAL = 0.1f;
 const F32 LL_ROLLOFF_MULTIPLIER_UNDER_WATER = 5.f;			//  How much sounds are weaker under water
 const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f;
@@ -67,6 +69,7 @@ class LLVFS;
 class LLAudioSource;
 class LLAudioData;
 class LLAudioChannel;
+class LLAudioChannelOpenAL;
 class LLAudioBuffer;
 
 
@@ -77,6 +80,8 @@ class LLAudioBuffer;
 
 class LLAudioEngine 
 {
+	friend class LLAudioChannelOpenAL; // bleh. channel needs some listener methods.
+	
 public:
 	enum LLAudioType
 	{
@@ -91,9 +96,8 @@ public:
 	virtual ~LLAudioEngine();
 
 	// initialization/startup/shutdown
-	//virtual BOOL init();
-
-	virtual BOOL init(const S32 num_channels, void *userdata);
+	virtual bool init(const S32 num_channels, void *userdata);
+	virtual std::string getDriverName(bool verbose) = 0;
 	virtual void shutdown();
 
 	// Used by the mechanics of the engine
@@ -106,14 +110,14 @@ public:
 	//
 	// "End user" functionality
 	//
-	virtual BOOL isWindEnabled();
-	virtual void enableWind(BOOL state_b);
+	virtual bool isWindEnabled();
+	virtual void enableWind(bool state_b);
 
 	// Use these for temporarily muting the audio system.
 	// Does not change buffers, initialization, etc. but
 	// stops playing new sounds.
-	virtual void setMuted(BOOL muted);
-	virtual BOOL getMuted() const { return mMuted; }
+	virtual void setMuted(bool muted);
+	virtual bool getMuted() const { return mMuted; }
 
 	F32 getMasterGain();
 	void setMasterGain(F32 gain);
@@ -137,7 +141,7 @@ public:
 	void triggerSound(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain,
 					  const S32 type = LLAudioEngine::AUDIO_TYPE_NONE,
 					  const LLVector3d &pos_global = LLVector3d::zero);
-	BOOL preloadSound(const LLUUID &id);
+	bool preloadSound(const LLUUID &id);
 
 	void addAudioSource(LLAudioSource *asp);
 	void cleanupAudioSource(LLAudioSource *asp);
@@ -146,14 +150,16 @@ public:
 	LLAudioData *getAudioData(const LLUUID &audio_uuid);
 
 
-	virtual void startInternetStream(const std::string& url) = 0;
-	virtual void stopInternetStream() = 0;
-	virtual void pauseInternetStream(int pause) = 0;
-	virtual int isInternetStreamPlaying() = 0;
-	virtual void getInternetStreamInfo(char* artist, char* title) { artist[0] = 0; title[0] = 0; }
+	// Internet stream methods
+	virtual void startInternetStream(const std::string& url);
+	virtual void stopInternetStream();
+	virtual void pauseInternetStream(int pause);
+	virtual void updateInternetStream();
+	virtual int isInternetStreamPlaying();
+	virtual void getInternetStreamInfo(char* artist, char* title);
 	// use a value from 0.0 to 1.0, inclusive
-	virtual void setInternetStreamGain(F32 vol) { mInternetStreamGain = vol; }
-	virtual const std::string& getInternetStreamURL() { return LLStringUtil::null; }
+	virtual void setInternetStreamGain(F32 vol);
+	virtual const std::string& getInternetStreamURL();
 
 	// For debugging usage
 	virtual LLVector3 getListenerPos();
@@ -162,17 +168,16 @@ public:
 	LLAudioChannel *getFreeChannel(const F32 priority); // Get a free channel or flush an existing one if your priority is higher
 	void cleanupBuffer(LLAudioBuffer *bufferp);
 
-	BOOL hasDecodedFile(const LLUUID &uuid);
-	BOOL hasLocalFile(const LLUUID &uuid);
+	bool hasDecodedFile(const LLUUID &uuid);
+	bool hasLocalFile(const LLUUID &uuid);
 
-	BOOL updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid = LLUUID::null);
+	bool updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid = LLUUID::null);
 
 
 	// Asset callback when we're retrieved a sound from the asset server.
 	void startNextTransfer();
 	static void assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status);
 
-
 	friend class LLPipeline; // For debugging
 public:
 	F32 mMaxWindGain; // Hack.  Public to set before fade in?
@@ -190,11 +195,6 @@ protected:
 	virtual void allocateListener() = 0;
 
 
-	// Internet stream methods
-	virtual void initInternetStream() {}
-	virtual void updateInternetStream() {}
-
-
 	// listener methods
 	virtual void setListenerPos(LLVector3 vec);
 	virtual void setListenerVelocity(LLVector3 vec);
@@ -209,13 +209,13 @@ protected:
 protected:
 	LLListener *mListenerp;
 
-	BOOL mMuted;
+	bool mMuted;
 	void* mUserData;
 
 	S32 mLastStatus;
 	
 	S32 mNumChannels;
-	BOOL mEnableWind;
+	bool mEnableWind;
 
 	LLUUID mCurrentTransfer; // Audio file currently being transferred by the system
 	LLFrameTimer mCurrentTransferTimer;
@@ -240,6 +240,7 @@ protected:
 
 	// Hack!  Internet streams are treated differently from other sources!
 	F32 mInternetStreamGain;
+	std::string mInternetStreamURL;
 
 	F32 mNextWindUpdate;
 
@@ -247,6 +248,7 @@ protected:
 
 private:
 	void setDefaults();
+	LLMediaBase *mInternetStreamMedia;
 };
 
 
@@ -270,24 +272,24 @@ public:
 
 	void preload(const LLUUID &audio_id); // Only used for preloading UI sounds, now.
 
-	void addAudioData(LLAudioData *adp, BOOL set_current = TRUE);
+	void addAudioData(LLAudioData *adp, bool set_current = TRUE);
 
-	void setAmbient(const BOOL ambient)						{ mAmbient = ambient; }
-	BOOL isAmbient() const									{ return mAmbient; }
+	void setAmbient(const bool ambient)						{ mAmbient = ambient; }
+	bool isAmbient() const									{ return mAmbient; }
 
-	void setLoop(const BOOL loop)							{ mLoop = loop; }
-	BOOL isLoop() const										{ return mLoop; }
+	void setLoop(const bool loop)							{ mLoop = loop; }
+	bool isLoop() const										{ return mLoop; }
 
-	void setSyncMaster(const BOOL master)					{ mSyncMaster = master; }
-	BOOL isSyncMaster() const								{ return mSyncMaster; }
+	void setSyncMaster(const bool master)					{ mSyncMaster = master; }
+	bool isSyncMaster() const								{ return mSyncMaster; }
 
-	void setSyncSlave(const BOOL slave)						{ mSyncSlave = slave; }
-	BOOL isSyncSlave() const								{ return mSyncSlave; }
+	void setSyncSlave(const bool slave)						{ mSyncSlave = slave; }
+	bool isSyncSlave() const								{ return mSyncSlave; }
 
-	void setQueueSounds(const BOOL queue)					{ mQueueSounds = queue; }
-	BOOL isQueueSounds() const								{ return mQueueSounds; }
+	void setQueueSounds(const bool queue)					{ mQueueSounds = queue; }
+	bool isQueueSounds() const								{ return mQueueSounds; }
 
-	void setPlayedOnce(const BOOL played_once)				{ mPlayedOnce = played_once; }
+	void setPlayedOnce(const bool played_once)				{ mPlayedOnce = played_once; }
 
 	void setType(S32 type)                                  { mType = type; }
 	S32 getType()                                           { return mType; }
@@ -302,16 +304,16 @@ public:
 	virtual void setGain(const F32 gain)							{ mGain = llclamp(gain, 0.f, 1.f); }
 
 	const LLUUID &getID() const		{ return mID; }
-	BOOL isDone();
+	bool isDone();
 
 	LLAudioData *getCurrentData();
 	LLAudioData *getQueuedData();
 	LLAudioBuffer *getCurrentBuffer();
 
-	BOOL setupChannel();
-	BOOL play(const LLUUID &audio_id);	// Start the audio source playing
+	bool setupChannel();
+	bool play(const LLUUID &audio_id);	// Start the audio source playing
 
-	BOOL hasPendingPreloads() const;	// Has preloads that haven't been done yet
+	bool hasPendingPreloads() const;	// Has preloads that haven't been done yet
 
 	friend class LLAudioEngine;
 	friend class LLAudioChannel;
@@ -324,12 +326,12 @@ protected:
 	LLUUID			mOwnerID;	// owner of the object playing the sound
 	F32				mPriority;
 	F32				mGain;
-	BOOL			mAmbient;
-	BOOL			mLoop;
-	BOOL			mSyncMaster;
-	BOOL			mSyncSlave;
-	BOOL			mQueueSounds;
-	BOOL			mPlayedOnce;
+	bool			mAmbient;
+	bool			mLoop;
+	bool			mSyncMaster;
+	bool			mSyncSlave;
+	bool			mQueueSounds;
+	bool			mPlayedOnce;
 	S32             mType;
 	LLVector3d		mPositionGlobal;
 	LLVector3		mVelocity;
@@ -359,27 +361,27 @@ class LLAudioData
 {
 public:
 	LLAudioData(const LLUUID &uuid);
-	BOOL load();
+	bool load();
 
 	LLUUID getID() const				{ return mID; }
 	LLAudioBuffer *getBuffer() const	{ return mBufferp; }
 
-	BOOL	hasLocalData() const		{ return mHasLocalData; }
-	BOOL	hasDecodedData() const		{ return mHasDecodedData; }
-	BOOL	hasValidData() const		{ return mHasValidData; }
+	bool	hasLocalData() const		{ return mHasLocalData; }
+	bool	hasDecodedData() const		{ return mHasDecodedData; }
+	bool	hasValidData() const		{ return mHasValidData; }
 
-	void	setHasLocalData(const BOOL hld)		{ mHasLocalData = hld; }
-	void	setHasDecodedData(const BOOL hdd)	{ mHasDecodedData = hdd; }
-	void	setHasValidData(const BOOL hvd)		{ mHasValidData = hvd; }
+	void	setHasLocalData(const bool hld)		{ mHasLocalData = hld; }
+	void	setHasDecodedData(const bool hdd)	{ mHasDecodedData = hdd; }
+	void	setHasValidData(const bool hvd)		{ mHasValidData = hvd; }
 
 	friend class LLAudioEngine; // Severe laziness, bad.
 
 protected:
 	LLUUID mID;
 	LLAudioBuffer *mBufferp;	// If this data is being used by the audio system, a pointer to the buffer will be set here.
-	BOOL mHasLocalData;
-	BOOL mHasDecodedData;
-	BOOL mHasValidData;
+	bool mHasLocalData;
+	bool mHasDecodedData;
+	bool mHasValidData;
 };
 
 
@@ -408,18 +410,18 @@ protected:
 	virtual void play() = 0;
 	virtual void playSynced(LLAudioChannel *channelp) = 0;
 	virtual void cleanup() = 0;
-	virtual BOOL isPlaying() = 0;
-	void setWaiting(const BOOL waiting)			{ mWaiting = waiting; }
-	BOOL isWaiting() const						{ return mWaiting; }
+	virtual bool isPlaying() = 0;
+	void setWaiting(const bool waiting)			{ mWaiting = waiting; }
+	bool isWaiting() const						{ return mWaiting; }
 
-	virtual BOOL updateBuffer(); // Check to see if the buffer associated with the source changed, and update if necessary.
+	virtual bool updateBuffer(); // Check to see if the buffer associated with the source changed, and update if necessary.
 	virtual void update3DPosition() = 0;
 	virtual void updateLoop() = 0; // Update your loop/completion status, for use by queueing/syncing.
 protected:
 	LLAudioSource	*mCurrentSourcep;
 	LLAudioBuffer	*mCurrentBufferp;
-	BOOL			mLoopedThisFrame;
-	BOOL			mWaiting;	// Waiting for sync.
+	bool			mLoopedThisFrame;
+	bool			mWaiting;	// Waiting for sync.
 	F32             mSecondaryGain;
 };
 
@@ -435,14 +437,14 @@ class LLAudioBuffer
 {
 public:
 	virtual ~LLAudioBuffer() {};
-	virtual BOOL loadWAV(const std::string& filename) = 0;
+	virtual bool loadWAV(const std::string& filename) = 0;
 	virtual U32 getLength() = 0;
 
 	friend class LLAudioEngine;
 	friend class LLAudioChannel;
 	friend class LLAudioData;
 protected:
-	BOOL mInUse;
+	bool mInUse;
 	LLAudioData *mAudioDatap;
 	LLFrameTimer mLastUseTimer;
 };
diff --git a/indra/llaudio/audioengine_fmod.cpp b/indra/llaudio/audioengine_fmod.cpp
index 666ecce..2197a45 100644
--- a/indra/llaudio/audioengine_fmod.cpp
+++ b/indra/llaudio/audioengine_fmod.cpp
@@ -1,7 +1,6 @@
 /** 
  * @file audioengine_fmod.cpp
- * @brief Implementation of LLAudioEngine class abstracting the audio
- * support as a FMOD 3D implementation
+ * @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation
  *
  * $LicenseInfo:firstyear=2002&license=viewergpl$
  * 
@@ -46,27 +45,12 @@
 
 #include "sound_ids.h"
 
+extern "C" {
+	void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata);
+}
 
-void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata);
 FSOUND_DSPUNIT *gWindDSP = NULL;
 
-// These globals for the wind filter.  Blech!
-F64 gbuf0 = 0.0;
-F64 gbuf1 = 0.0;
-F64 gbuf2 = 0.0;
-F64 gbuf3 = 0.0;
-F64 gbuf4 = 0.0;
-F64 gbuf5 = 0.0;
-F64 gY0 = 0.0;
-F64 gY1 = 0.0;
-
-F32 gTargetGain = 0.f;
-F32 gCurrentGain = 0.f;
-F32 gTargetFreq = 100.f;
-F32 gCurrentFreq = 100.f;
-F32 gTargetPanGainR = 0.5f;
-F32 gCurrentPanGainR = 0.5f;
-
 
 // Safe strcpy
 #if 0 //(unused)  //LL_WINDOWS || LL_LINUX
@@ -94,9 +78,10 @@ static size_t strlcpy( char* dest, const char* src, size_t dst_size )
 
 LLAudioEngine_FMOD::LLAudioEngine_FMOD()
 {
-	mInited = FALSE;
+	mInited = false;
 	mCurrentInternetStreamp = NULL;
 	mInternetStreamChannel = -1;
+	mWindGen = NULL;
 }
 
 
@@ -105,7 +90,7 @@ LLAudioEngine_FMOD::~LLAudioEngine_FMOD()
 }
 
 
-BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
+bool LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 {
 	mFadeIn = -10000;
 
@@ -124,7 +109,7 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 	{
 		LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version
 			<< ")!  You should be using FMOD " << FMOD_VERSION << LL_ENDL;
-		//return FALSE;
+		//return false;
 	}
 
 	U32 fmod_flags = 0x0;
@@ -139,7 +124,7 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 	{
 		LL_WARNS("AppInit") << "Error setting FMOD window: "
 			<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
-		return FALSE;
+		return false;
 	}
 	// Play audio when we don't have focus.
 	// (For example, IM client on top of us.)
@@ -167,10 +152,10 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 	// on top of ALSA is ironically more reliable than raw ALSA.
 	// Ack, and ESD has more reliable failure modes - but has worse
 	// latency - than all of them, so wins for now.
-	BOOL audio_ok = FALSE;
+	bool audio_ok = false;
 
 	if (!audio_ok)
-		if (NULL == getenv("LL_BAD_ESD")) /*Flawfinder: ignore*/
+		if (NULL == getenv("LL_BAD_FMOD_ESD")) /*Flawfinder: ignore*/
 		{
 			LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL;
 			if(FSOUND_SetOutput(FSOUND_OUTPUT_ESD) &&
@@ -178,7 +163,7 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 			{
 				LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY"
 					<< LL_ENDL;
-				audio_ok = TRUE;
+				audio_ok = true;
 			} else {
 				LL_WARNS("AppInit") << "ESD audio output FAILED to initialize: "
 					<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
@@ -188,14 +173,14 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 		}
 
 	if (!audio_ok)
-		if (NULL == getenv("LL_BAD_OSS")) 	 /*Flawfinder: ignore*/
+		if (NULL == getenv("LL_BAD_FMOD_OSS")) 	 /*Flawfinder: ignore*/
 		{
 			LL_DEBUGS("AppInit") << "Trying OSS audio output..."	<< LL_ENDL;
 			if(FSOUND_SetOutput(FSOUND_OUTPUT_OSS) &&
 			   FSOUND_Init(44100, num_channels, fmod_flags))
 			{
 				LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL;
-				audio_ok = TRUE;
+				audio_ok = true;
 			} else {
 				LL_WARNS("AppInit") << "OSS audio output FAILED to initialize: "
 					<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
@@ -205,14 +190,14 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 		}
 
 	if (!audio_ok)
-		if (NULL == getenv("LL_BAD_ALSA"))		/*Flawfinder: ignore*/
+		if (NULL == getenv("LL_BAD_FMOD_ALSA"))		/*Flawfinder: ignore*/
 		{
 			LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
 			if(FSOUND_SetOutput(FSOUND_OUTPUT_ALSA) &&
 			   FSOUND_Init(44100, num_channels, fmod_flags))
 			{
 				LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
-				audio_ok = TRUE;
+				audio_ok = true;
 			} else {
 				LL_WARNS("AppInit") << "ALSA audio output FAILED to initialize: "
 					<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
@@ -224,7 +209,7 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 	if (!audio_ok)
 	{
 		LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
-		return FALSE;
+		return false;
 	}
 
 	// On Linux, FMOD causes a SIGPIPE for some netstream error
@@ -250,7 +235,7 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 	{
 		LL_WARNS("AppInit") << "Error initializing FMOD: "
 			<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
-		return FALSE;
+		return false;
 	}
 	
 #endif
@@ -259,17 +244,23 @@ BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
 
 	LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() FMOD initialized correctly" << LL_ENDL;
 
-	mInited = TRUE;
+	mInited = true;
 
-	return TRUE;
+	return true;
 }
 
 
-void LLAudioEngine_FMOD::idle(F32 max_decode_time)
+std::string LLAudioEngine_FMOD::getDriverName(bool verbose)
 {
-	LLAudioEngine::idle(max_decode_time);
-
-	updateInternetStream();
+	if (verbose)
+	{
+		F32 version = FSOUND_GetVersion();
+		return llformat("FMOD version %f", version);
+	}
+	else
+	{
+		return "FMOD";
+	}
 }
 
 
@@ -287,7 +278,7 @@ void LLAudioEngine_FMOD::shutdown()
 {
 	if (gWindDSP)
 	{
-		FSOUND_DSP_SetActive(gWindDSP,FALSE);
+		FSOUND_DSP_SetActive(gWindDSP,false);
 		FSOUND_DSP_Free(gWindDSP);
 	}
 
@@ -318,13 +309,15 @@ LLAudioChannel *LLAudioEngine_FMOD::createChannel()
 
 void LLAudioEngine_FMOD::initWind()
 {
+	mWindGen = new LLWindGen<MIXBUFFERFORMAT>;
+
 	if (!gWindDSP)
 	{
-		gWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, NULL);
+		gWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, mWindGen);
 	}
 	if (gWindDSP)
 	{
-		FSOUND_DSP_SetActive(gWindDSP, TRUE);
+		FSOUND_DSP_SetActive(gWindDSP, true);
 	}
 	mNextWindUpdate = 0.0;
 }
@@ -334,10 +327,13 @@ void LLAudioEngine_FMOD::cleanupWind()
 {
 	if (gWindDSP)
 	{
-		FSOUND_DSP_SetActive(gWindDSP, FALSE);
+		FSOUND_DSP_SetActive(gWindDSP, false);
 		FSOUND_DSP_Free(gWindDSP);
 		gWindDSP = NULL;
 	}
+
+	delete mWindGen;
+	mWindGen = NULL;
 }
 
 
@@ -367,9 +363,9 @@ void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_
 		pitch = 1.0 + mapWindVecToPitch(wind_vec);
 		center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
 		
-		gTargetFreq = (F32)center_freq;
-		gTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
-		gTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
+		mWindGen->mTargetFreq = (F32)center_freq;
+		mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
+		mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
   	}
 }
 
@@ -457,11 +453,11 @@ LLAudioChannelFMOD::~LLAudioChannelFMOD()
 }
 
 
-BOOL LLAudioChannelFMOD::updateBuffer()
+bool LLAudioChannelFMOD::updateBuffer()
 {
 	if (LLAudioChannel::updateBuffer())
 	{
-		// Base class update returned TRUE, which means that we need to actually
+		// Base class update returned true, which means that we need to actually
 		// set up the channel for a different buffer.
 
 		LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentSourcep->getCurrentBuffer();
@@ -473,13 +469,13 @@ BOOL LLAudioChannelFMOD::updateBuffer()
 			// This is bad, there should ALWAYS be a sample associated with a legit
 			// buffer.
 			llerrs << "No FMOD sample!" << llendl;
-			return FALSE;
+			return false;
 		}
 
 
 		// Actually play the sound.  Start it off paused so we can do all the necessary
 		// setup.
-		mChannelID = FSOUND_PlaySoundEx(FSOUND_FREE, samplep, FSOUND_DSP_GetSFXUnit(), TRUE);
+		mChannelID = FSOUND_PlaySoundEx(FSOUND_FREE, samplep, FSOUND_DSP_GetSFXUnit(), true);
 
 		//llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl;
 	}
@@ -501,7 +497,7 @@ BOOL LLAudioChannelFMOD::updateBuffer()
 		}
 	}
 
-	return TRUE;
+	return true;
 }
 
 
@@ -524,12 +520,12 @@ void LLAudioChannelFMOD::update3DPosition()
 	if (mCurrentSourcep->isAmbient())
 	{
 		// Ambient sound, don't need to do any positional updates.
-		bufferp->set3DMode(FALSE);
+		bufferp->set3DMode(false);
 	}
 	else
 	{
 		// Localized sound.  Update the position and velocity of the sound.
-		bufferp->set3DMode(TRUE);
+		bufferp->set3DMode(true);
 
 		LLVector3 float_pos;
 		float_pos.setVec(mCurrentSourcep->getPositionGlobal());
@@ -550,13 +546,14 @@ void LLAudioChannelFMOD::updateLoop()
 	}
 
 	//
-	// Hack:  We keep track of whether we looped or not by seeing when the sign of the last sample
-	// flips.  This is pretty crappy.
+	// Hack:  We keep track of whether we looped or not by seeing when the
+	// sample position looks like it's going backwards.  Not reliable; may
+	// yield false negatives.
 	//
 	U32 cur_pos = FSOUND_GetCurrentPosition(mChannelID);
 	if (cur_pos < (U32)mLastSamplePos)
 	{
-		mLoopedThisFrame = TRUE;
+		mLoopedThisFrame = true;
 	}
 	mLastSamplePos = cur_pos;
 }
@@ -589,11 +586,11 @@ void LLAudioChannelFMOD::play()
 		return;
 	}
 
-	if (!FSOUND_SetPaused(mChannelID, FALSE))
+	if (!FSOUND_SetPaused(mChannelID, false))
 	{
 		llwarns << "LLAudioChannelFMOD::play error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
 	}
-	getSource()->setPlayedOnce(TRUE);
+	getSource()->setPlayedOnce(true);
 }
 
 
@@ -618,11 +615,11 @@ void LLAudioChannelFMOD::playSynced(LLAudioChannel *channelp)
 }
 
 
-BOOL LLAudioChannelFMOD::isPlaying()
+bool LLAudioChannelFMOD::isPlaying()
 {
 	if (!mChannelID)
 	{
-		return FALSE;
+		return false;
 	}
 
 	return FSOUND_IsPlaying(mChannelID) && (!FSOUND_GetPaused(mChannelID));
@@ -652,14 +649,14 @@ LLAudioBufferFMOD::~LLAudioBufferFMOD()
 }
 
 
-BOOL LLAudioBufferFMOD::loadWAV(const std::string& filename)
+bool LLAudioBufferFMOD::loadWAV(const std::string& filename)
 {
 	// Try to open a wav file from disk.  This will eventually go away, as we don't
 	// really want to block doing this.
 	if (filename.empty())
 	{
 		// invalid filename, abort.
-		return FALSE;
+		return false;
 	}
 
 	S32 file_size = 0;
@@ -667,7 +664,7 @@ BOOL LLAudioBufferFMOD::loadWAV(const std::string& filename)
 	if (!apr_file)
 	{
 		// File not found, abort.
-		return FALSE;
+		return false;
 	}
 	apr_file_close(apr_file);
 
@@ -717,11 +714,11 @@ BOOL LLAudioBufferFMOD::loadWAV(const std::string& filename)
 		//
 		// file is probably corrupt - remove it.
 		LLFile::remove(filename);
-		return FALSE;
+		return false;
 	}
 
-	// Everything went well, return TRUE
-	return TRUE;
+	// Everything went well, return true
+	return true;
 }
 
 
@@ -736,7 +733,7 @@ U32 LLAudioBufferFMOD::getLength()
 }
 
 
-void LLAudioBufferFMOD::set3DMode(BOOL use3d)
+void LLAudioBufferFMOD::set3DMode(bool use3d)
 {
 	U16 current_mode = FSOUND_Sample_GetMode(mSamplep);
 	
@@ -765,7 +762,7 @@ void LLAudioEngine_FMOD::initInternetStream()
 {
 	// Number of milliseconds of audio to buffer for the audio card.
 	// Must be larger than the usual Second Life frame stutter time.
-    FSOUND_Stream_SetBufferSize(200);
+	FSOUND_Stream_SetBufferSize(200);
 
 	// Here's where we set the size of the network buffer and some buffering 
 	// parameters.  In this case we want a network buffer of 16k, we want it 
@@ -810,19 +807,19 @@ signed char F_CALLBACKAPI LLAudioEngine_FMOD::callbackMetaData(char *name, char
     if (!strcmp("ARTIST", name))
     {
         strlcpy(self->mInternetStreamArtist, value, 256);
-        self->mInternetStreamNewMetaData = TRUE;
-        return TRUE;
+        self->mInternetStreamNewMetaData = true;
+        return true;
     }
 
     if (!strcmp("TITLE", name))
     {
         strlcpy(self->mInternetStreamTitle, value, 256);
-        self->mInternetStreamNewMetaData = TRUE;
-        return TRUE;
+        self->mInternetStreamNewMetaData = true;
+        return true;
     }
 	*/
 
-    return TRUE;
+    return true;
 }
 
 
@@ -867,7 +864,7 @@ void LLAudioEngine_FMOD::updateInternetStream()
 			{
 				// Reset volume to previously set volume
 				setInternetStreamGain(mInternetStreamGain);
-				FSOUND_SetPaused(mInternetStreamChannel, FALSE);
+				FSOUND_SetPaused(mInternetStreamChannel, false);
 				//FSOUND_Stream_Net_SetMetadataCallback(mInternetStream, callbackMetaData, this);
 			}
 		}
@@ -909,7 +906,7 @@ void LLAudioEngine_FMOD::stopInternetStream()
 {
 	if (mInternetStreamChannel != -1)
 	{
-		FSOUND_SetPaused(mInternetStreamChannel, TRUE);
+		FSOUND_SetPaused(mInternetStreamChannel, true);
 		FSOUND_SetPriority(mInternetStreamChannel, 0);
 		mInternetStreamChannel = -1;
 	}
@@ -971,16 +968,10 @@ int LLAudioEngine_FMOD::isInternetStreamPlaying()
 }
 
 
-void LLAudioEngine_FMOD::getInternetStreamInfo(char* artist_out, char* title_out)
-{
-	//strlcpy(artist_out, mInternetStreamArtist, 256);
-	//strlcpy(title_out, mInternetStreamTitle, 256);
-}
-
-
 void LLAudioEngine_FMOD::setInternetStreamGain(F32 vol)
 {
-	LLAudioEngine::setInternetStreamGain(vol);
+	mInternetStreamGain = vol;
+
 	if (mInternetStreamChannel != -1)
 	{
 		vol = llclamp(vol, 0.f, 1.f);
@@ -990,15 +981,9 @@ void LLAudioEngine_FMOD::setInternetStreamGain(F32 vol)
 }
 
 
-const std::string& LLAudioEngine_FMOD::getInternetStreamURL()
-{
-	return mInternetStreamURL;
-}
-
-
 LLAudioStreamFMOD::LLAudioStreamFMOD(const std::string& url) :
 	mInternetStream(NULL),
-	mReady(FALSE)
+	mReady(false)
 {
 	mInternetStreamURL = url;
 	mInternetStream = FSOUND_Stream_Open(url.c_str(), FSOUND_NORMAL | FSOUND_NONBLOCKING, 0, 0);
@@ -1007,11 +992,11 @@ LLAudioStreamFMOD::LLAudioStreamFMOD(const std::string& url) :
 		llwarns << "Couldn't open fmod stream, error "
 			<< FMOD_ErrorString(FSOUND_GetError())
 			<< llendl;
-		mReady = FALSE;
+		mReady = false;
 		return;
 	}
 
-	mReady = TRUE;
+	mReady = true;
 }
 
 int LLAudioStreamFMOD::startStream()
@@ -1026,10 +1011,10 @@ int LLAudioStreamFMOD::startStream()
 	// Make sure the stream is set to 2D mode.
 	FSOUND_Stream_SetMode(mInternetStream, FSOUND_2D);
 
-	return FSOUND_Stream_PlayEx(FSOUND_FREE, mInternetStream, NULL, TRUE);
+	return FSOUND_Stream_PlayEx(FSOUND_FREE, mInternetStream, NULL, true);
 }
 
-BOOL LLAudioStreamFMOD::stopStream()
+bool LLAudioStreamFMOD::stopStream()
 {
 	if (mInternetStream)
 	{
@@ -1039,34 +1024,34 @@ BOOL LLAudioStreamFMOD::stopStream()
 		unsigned int flags = 0x0;
 		FSOUND_Stream_Net_GetStatus(mInternetStream, &status, &read_percent, &bitrate, &flags);
 
-		BOOL close = TRUE;
+		bool close = true;
 		switch (status)
 		{
 		case FSOUND_STREAM_NET_CONNECTING:
-			close = FALSE;
+			close = false;
 			break;
 		case FSOUND_STREAM_NET_NOTCONNECTED:
 		case FSOUND_STREAM_NET_BUFFERING:
 		case FSOUND_STREAM_NET_READY:
 		case FSOUND_STREAM_NET_ERROR:
 		default:
-			close = TRUE;
+			close = true;
 		}
 
 		if (close)
 		{
 			FSOUND_Stream_Close(mInternetStream);
 			mInternetStream = NULL;
-			return TRUE;
+			return true;
 		}
 		else
 		{
-			return FALSE;
+			return false;
 		}
 	}
 	else
 	{
-		return TRUE;
+		return true;
 	}
 }
 
@@ -1076,94 +1061,35 @@ int LLAudioStreamFMOD::getOpenState()
 	return open_state;
 }
 
-/* This determines the format of the mixbuffer being passed in. change if you want to support int32 or float32 */
-#if LL_DARWIN
-	#define MIXBUFFERFORMAT S32
-#else
-	#define MIXBUFFERFORMAT S16
-#endif
-
-inline MIXBUFFERFORMAT clipSample(MIXBUFFERFORMAT sample, MIXBUFFERFORMAT min, MIXBUFFERFORMAT max)
-{
-	if (sample > max)
-		sample = max;
-	else if (sample < min)
-		sample = min;
-	
-	return sample;
-}
-
-void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void*)
+void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata)
 {
-// originalbuffer = fsounds original mixbuffer.
-// newbuffer = the buffer passed from the previous DSP unit.
-// length = length in samples at this mix time.
-// param = user parameter passed through in FSOUND_DSP_Create.
-//
-// modify the buffer in some fashion
+	// originalbuffer = fmod's original mixbuffer.
+	// newbuffer = the buffer passed from the previous DSP unit.
+	// length = length in samples at this mix time.
+	// param = user parameter passed through in FSOUND_DSP_Create.
+	//
+	// modify the buffer in some fashion
 
-	U8 *cursamplep = (U8*)newbuffer;
-	U8   wordsize = 2;
+	LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *windgen =
+		(LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *)userdata;
+	U8 stride;
 
 #if LL_DARWIN
-	wordsize = sizeof(MIXBUFFERFORMAT);
+	stride = sizeof(LLAudioEngine_FMOD::MIXBUFFERFORMAT);
 #else
-    int		mixertype = FSOUND_GetMixer();
-    if (mixertype == FSOUND_MIXER_BLENDMODE || mixertype == FSOUND_MIXER_QUALITY_FPU)
-    {
-		wordsize = 4;
-    }
-#endif
-
-    double bandwidth = 50;
-    double inputSamplingRate = 44100;
-	double a0,b1,b2;
- 
-	// calculate resonant filter coeffs
-    b2 = exp(-(F_TWO_PI) * (bandwidth / inputSamplingRate));
-
-	while (length--)
-	{
-		gCurrentFreq = (float)((0.999 * gCurrentFreq) + (0.001 * gTargetFreq));
-		gCurrentGain = (float)((0.999 * gCurrentGain) + (0.001 * gTargetGain));
-		gCurrentPanGainR = (float)((0.999 * gCurrentPanGainR) + (0.001 * gTargetPanGainR));
-		b1 = (-4.0 * b2) / (1.0 + b2) * cos(F_TWO_PI * (gCurrentFreq / inputSamplingRate));
-	    a0 = (1.0 - b2) * sqrt(1.0 - (b1 * b1) / (4.0 * b2));
-		double nextSample;
-
-	    // start with white noise
-		nextSample = ll_frand(2.0f) - 1.0f;
-									 
-#if 1 // LLAE_WIND_PINK apply pinking filter
-		gbuf0 = 0.997f * gbuf0 + 0.0126502f * nextSample; 
-        gbuf1 = 0.985f * gbuf1 + 0.0139083f * nextSample;
-        gbuf2 = 0.950f * gbuf2 + 0.0205439f * nextSample;
-        gbuf3 = 0.850f * gbuf3 + 0.0387225f * nextSample;
-        gbuf4 = 0.620f * gbuf4 + 0.0465932f * nextSample;
-        gbuf5 = 0.250f * gbuf5 + 0.1093477f * nextSample;
-			  
-        nextSample = gbuf0 + gbuf1 + gbuf2 + gbuf3 + gbuf4 + gbuf5;
-#endif
-			
-#if 1 //LLAE_WIND_RESONANT // do a resonant filter on the noise
-        nextSample = (double)( a0 * nextSample - b1 * gY0 - b2 * gY1 );
-
-        gY1 = gY0;
-        gY0 = nextSample;
+	int mixertype = FSOUND_GetMixer();
+	if (mixertype == FSOUND_MIXER_BLENDMODE ||
+	    mixertype == FSOUND_MIXER_QUALITY_FPU)
+	{
+		stride = 4;
+	}
+	else
+	{
+		stride = 2;
+	}
 #endif
 
-	    nextSample *= gCurrentGain;
-		
-		MIXBUFFERFORMAT	sample;
-
-		sample = llfloor(((F32)nextSample*32768.f*(1.0f - gCurrentPanGainR))+0.5f);
-		*(MIXBUFFERFORMAT*)cursamplep = clipSample((*(MIXBUFFERFORMAT*)cursamplep) + sample, -32768, 32767);
-		cursamplep += wordsize;
-
-		sample = llfloor(((F32)nextSample*32768.f*gCurrentPanGainR)+0.5f);
-		*(MIXBUFFERFORMAT*)cursamplep = clipSample((*(MIXBUFFERFORMAT*)cursamplep) + sample, -32768, 32767);
-		cursamplep += wordsize;
-	}
+	newbuffer = windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT *)newbuffer, length, stride);
 
 	return newbuffer;
 }
diff --git a/indra/llaudio/audioengine_fmod.h b/indra/llaudio/audioengine_fmod.h
index 444d754..4d2cbce 100644
--- a/indra/llaudio/audioengine_fmod.h
+++ b/indra/llaudio/audioengine_fmod.h
@@ -35,6 +35,7 @@
 
 #include "audioengine.h"
 #include "listener_fmod.h"
+#include "windgen.h"
 
 #include "fmod.h"
 
@@ -47,13 +48,12 @@ public:
 	virtual ~LLAudioEngine_FMOD();
 
 	// initialization/startup/shutdown
-	virtual BOOL init(const S32 num_channels, void *user_data);
+	virtual bool init(const S32 num_channels, void *user_data);
+       	virtual std::string getDriverName(bool verbose);
 	virtual void allocateListener();
 
 	virtual void shutdown();
 
-	virtual void idle(F32 max_decode_time = 0.f);
-
 	// Internet stream methods
 	virtual void initInternetStream();
 	virtual void startInternetStream(const std::string& url);
@@ -61,15 +61,19 @@ public:
 	virtual void stopInternetStream();
 	virtual void pauseInternetStream(int pause);
 	virtual int isInternetStreamPlaying();
-	virtual void getInternetStreamInfo(char* artist, char* title);
 	virtual void setInternetStreamGain(F32 vol);
-	virtual const std::string& getInternetStreamURL();
 
 	/*virtual*/ void initWind();
 	/*virtual*/ void cleanupWind();
 
 	/*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water);
 
+#if LL_DARWIN
+        typedef S32 MIXBUFFERFORMAT;
+#else
+        typedef S16 MIXBUFFERFORMAT;
+#endif
+
 protected:
 	/*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to.
 	/*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel.
@@ -79,7 +83,6 @@ protected:
 	static signed char F_CALLBACKAPI callbackMetaData(char* name, char* value, void* userdata);
 
 	LLAudioStreamFMOD *mCurrentInternetStreamp;
-	std::string mInternetStreamURL;
 	int mInternetStreamChannel;
 
 	std::list<LLAudioStreamFMOD *> mDeadStreams;
@@ -88,11 +91,12 @@ protected:
 	//F32 mMaxDistance[MAX_BUFFERS];
 
 	S32 mFadeIn;
-	BOOL mInited;
+	bool mInited;
 
 	// On Windows, userdata is the HWND of the application window.
 	void* mUserData;
 
+	LLWindGen<MIXBUFFERFORMAT> *mWindGen;
 };
 
 
@@ -106,9 +110,9 @@ protected:
 	/*virtual*/ void play();
 	/*virtual*/ void playSynced(LLAudioChannel *channelp);
 	/*virtual*/ void cleanup();
-	/*virtual*/ BOOL isPlaying();
+	/*virtual*/ bool isPlaying();
 
-	/*virtual*/ BOOL updateBuffer();
+	/*virtual*/ bool updateBuffer();
 	/*virtual*/ void update3DPosition();
 	/*virtual*/ void updateLoop();
 
@@ -124,11 +128,11 @@ public:
 	LLAudioBufferFMOD();
 	virtual ~LLAudioBufferFMOD();
 
-	/*virtual*/ BOOL loadWAV(const std::string& filename);
+	/*virtual*/ bool loadWAV(const std::string& filename);
 	/*virtual*/ U32 getLength();
 	friend class LLAudioChannelFMOD;
 
-	void set3DMode(BOOL use3d);
+	void set3DMode(bool use3d);
 protected:
 	FSOUND_SAMPLE *getSample()	{ return mSamplep; }
 protected:
@@ -140,15 +144,15 @@ class LLAudioStreamFMOD
 public:
 	LLAudioStreamFMOD(const std::string& url);
 	int	startStream();
-	BOOL stopStream(); // Returns true if the stream was successfully stopped.
-	BOOL ready();
+	bool stopStream(); // Returns true if the stream was successfully stopped.
+	bool ready();
 
 	const std::string& getURL() 	{ return mInternetStreamURL; }
 
 	int getOpenState();
 protected:
 	FSOUND_STREAM* mInternetStream;
-	BOOL mReady;
+	bool mReady;
 
 	std::string mInternetStreamURL;
 };
diff --git a/indra/llaudio/audioengine_openal.cpp b/indra/llaudio/audioengine_openal.cpp
new file mode 100644
index 0000000..b33e0b0
--- /dev/null
+++ b/indra/llaudio/audioengine_openal.cpp
@@ -0,0 +1,547 @@
+/**
+ * @file audioengine_openal.cpp
+ * @brief implementation of audio engine using OpenAL
+ * support as a OpenAL 3D implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ * 
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "lldir.h"
+
+#include "audioengine_openal.h"
+#include "listener_openal.h"
+
+
+LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
+	:
+	mWindGen(NULL),
+	mWindBuf(NULL),
+	mWindBufFreq(0),
+	mWindBufSamples(0),
+	mWindBufBytes(0),
+	mWindSource(AL_NONE),
+	mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS)
+{
+}
+
+// virtual
+LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
+{
+}
+
+// virtual
+bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata)
+{
+	mWindGen = NULL;
+	LLAudioEngine::init(num_channels, userdata);
+
+	if(!alutInit(NULL, NULL))
+	{
+		llwarns << "LLAudioEngine_OpenAL::init() ALUT initialization failed: " << alutGetErrorString (alutGetError ()) << llendl;
+		return false;
+	}
+
+	llinfos << "LLAudioEngine_OpenAL::init() OpenAL successfully initialized" << llendl;
+
+	llinfos << "OpenAL version: "
+		<< ll_safe_string(alGetString(AL_VERSION)) << llendl;
+	llinfos << "OpenAL vendor: "
+		<< ll_safe_string(alGetString(AL_VENDOR)) << llendl;
+	llinfos << "OpenAL renderer: "
+		<< ll_safe_string(alGetString(AL_RENDERER)) << llendl;
+
+	ALint major = alutGetMajorVersion ();
+	ALint minor = alutGetMinorVersion ();
+	llinfos << "ALUT version: " << major << "." << minor << llendl;
+
+	ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
+
+	alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major);
+	alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &minor);
+	llinfos << "ALC version: " << major << "." << minor << llendl;
+
+	llinfos << "ALC default device: "
+		<< ll_safe_string(alcGetString(device,
+					       ALC_DEFAULT_DEVICE_SPECIFIER))
+		<< llendl;
+
+	return true;
+}
+
+// virtual
+std::string LLAudioEngine_OpenAL::getDriverName(bool verbose)
+{
+	ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
+	std::ostringstream version;
+
+	version <<
+		"OpenAL";
+
+	if (verbose)
+	{
+		version <<
+			", version " <<
+			ll_safe_string(alGetString(AL_VERSION)) <<
+			" / " <<
+			ll_safe_string(alGetString(AL_VENDOR)) <<
+			" / " <<
+			ll_safe_string(alGetString(AL_RENDERER));
+		
+		if (device)
+			version <<
+				": " <<
+				ll_safe_string(alcGetString(device,
+				    ALC_DEFAULT_DEVICE_SPECIFIER));
+	}
+
+	return version.str();
+}
+
+// virtual
+void LLAudioEngine_OpenAL::allocateListener()
+{
+	mListenerp = (LLListener *) new LLListener_OpenAL();
+	if(!mListenerp)
+	{
+		llwarns << "LLAudioEngine_OpenAL::allocateListener() Listener creation failed" << llendl;
+	}
+}
+
+// virtual
+void LLAudioEngine_OpenAL::shutdown()
+{
+	llinfos << "About to LLAudioEngine::shutdown()" << llendl;
+	LLAudioEngine::shutdown();
+
+	llinfos << "About to alutExit()" << llendl;
+	if(!alutExit())
+	{
+		llwarns << "Nuts." << llendl;
+		llwarns << "LLAudioEngine_OpenAL::shutdown() ALUT shutdown failed: " << alutGetErrorString (alutGetError ()) << llendl;
+	}
+
+	llinfos << "LLAudioEngine_OpenAL::shutdown() OpenAL successfully shut down" << llendl;
+
+	delete mListenerp;
+	mListenerp = NULL;
+}
+
+LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer()
+{
+	return new LLAudioBufferOpenAL();
+}
+
+LLAudioChannel *LLAudioEngine_OpenAL::createChannel()
+{
+	return new LLAudioChannelOpenAL();
+}
+
+void LLAudioEngine_OpenAL::setInternalGain(F32 gain)
+{
+	//llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl;
+	alListenerf(AL_GAIN, gain);
+}
+
+LLAudioChannelOpenAL::LLAudioChannelOpenAL()
+	:
+	mALSource(AL_NONE),
+	mLastSamplePos(0)
+{
+	alGenSources(1, &mALSource);
+}
+
+LLAudioChannelOpenAL::~LLAudioChannelOpenAL()
+{
+	cleanup();
+	alDeleteSources(1, &mALSource);
+}
+
+void LLAudioChannelOpenAL::cleanup()
+{
+	alSourceStop(mALSource);
+	mCurrentBufferp = NULL;
+}
+
+void LLAudioChannelOpenAL::play()
+{
+	if (mALSource == AL_NONE)
+	{
+		llwarns << "Playing without a mALSource, aborting" << llendl;
+		return;
+	}
+
+	if(!isPlaying())
+	{
+		alSourcePlay(mALSource);
+		getSource()->setPlayedOnce(true);
+	}
+}
+
+void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp)
+{
+	if (channelp)
+	{
+		LLAudioChannelOpenAL *masterchannelp =
+			(LLAudioChannelOpenAL*)channelp;
+		if (mALSource != AL_NONE &&
+		    masterchannelp->mALSource != AL_NONE)
+		{
+			// we have channels allocated to master and slave
+			ALfloat master_offset;
+			alGetSourcef(masterchannelp->mALSource, AL_SEC_OFFSET,
+				     &master_offset);
+
+			llinfos << "Syncing with master at " << master_offset
+				<< "sec" << llendl;
+			// *TODO: detect when this fails, maybe use AL_SAMPLE_
+			alSourcef(mALSource, AL_SEC_OFFSET, master_offset);
+		}
+	}
+	play();
+}
+
+bool LLAudioChannelOpenAL::isPlaying()
+{
+	if (mALSource != AL_NONE)
+	{
+		ALint state;
+		alGetSourcei(mALSource, AL_SOURCE_STATE, &state);
+		if(state == AL_PLAYING)
+		{
+			return true;
+		}
+	}
+		
+	return false;
+}
+
+bool LLAudioChannelOpenAL::updateBuffer()
+{
+	if (LLAudioChannel::updateBuffer())
+	{
+		// Base class update returned true, which means that we need to actually
+		// set up the source for a different buffer.
+		LLAudioBufferOpenAL *bufferp = (LLAudioBufferOpenAL *)mCurrentSourcep->getCurrentBuffer();
+		ALuint buffer = bufferp->getBuffer();
+		alSourcei(mALSource, AL_BUFFER, buffer);
+		mLastSamplePos = 0;
+	}
+
+	if (mCurrentSourcep)
+	{
+		alSourcef(mALSource, AL_GAIN,
+			  mCurrentSourcep->getGain() * getSecondaryGain());
+		alSourcei(mALSource, AL_LOOPING,
+			  mCurrentSourcep->isLoop() ? AL_TRUE : AL_FALSE);
+		alSourcef(mALSource, AL_ROLLOFF_FACTOR,
+			  gAudiop->mListenerp->getRolloffFactor());
+		alSourcef(mALSource, AL_REFERENCE_DISTANCE,
+			  gAudiop->mListenerp->getDistanceFactor());
+	}
+
+	return true;
+}
+
+
+void LLAudioChannelOpenAL::updateLoop()
+{
+	if (mALSource == AL_NONE)
+	{
+		return;
+	}
+
+	// Hack:  We keep track of whether we looped or not by seeing when the
+	// sample position looks like it's going backwards.  Not reliable; may
+	// yield false negatives.
+	//
+	ALint cur_pos;
+	alGetSourcei(mALSource, AL_SAMPLE_OFFSET, &cur_pos);
+	if (cur_pos < mLastSamplePos)
+	{
+		mLoopedThisFrame = true;
+	}
+	mLastSamplePos = cur_pos;
+}
+
+
+void LLAudioChannelOpenAL::update3DPosition()
+{
+	if(!mCurrentSourcep)
+	{
+		return;
+	}
+	if (mCurrentSourcep->isAmbient())
+	{
+		alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0);
+		alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0);
+		alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_TRUE);
+	} else {
+		LLVector3 float_pos;
+		float_pos.setVec(mCurrentSourcep->getPositionGlobal());
+		alSourcefv(mALSource, AL_POSITION, float_pos.mV);
+		alSourcefv(mALSource, AL_VELOCITY, mCurrentSourcep->getVelocity().mV);
+		alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_FALSE);
+	}
+
+	alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain() * getSecondaryGain());
+}
+
+LLAudioBufferOpenAL::LLAudioBufferOpenAL()
+{
+	mALBuffer = AL_NONE;
+}
+
+LLAudioBufferOpenAL::~LLAudioBufferOpenAL()
+{
+	cleanup();
+}
+
+void LLAudioBufferOpenAL::cleanup()
+{
+	if(mALBuffer != AL_NONE)
+	{
+		alDeleteBuffers(1, &mALBuffer);
+		mALBuffer = AL_NONE;
+	}
+}
+
+bool LLAudioBufferOpenAL::loadWAV(const std::string& filename)
+{
+	cleanup();
+	mALBuffer = alutCreateBufferFromFile(filename.c_str());
+	if(mALBuffer == AL_NONE)
+	{
+		ALenum error = alutGetError(); 
+		if (gDirUtilp->fileExists(filename))
+		{
+			llwarns <<
+				"LLAudioBufferOpenAL::loadWAV() Error loading "
+				<< filename
+				<< " " << alutGetErrorString(error) << llendl;
+		}
+		else
+		{
+			// It's common for the file to not actually exist.
+			lldebugs <<
+				"LLAudioBufferOpenAL::loadWAV() Error loading "
+				 << filename
+				 << " " << alutGetErrorString(error) << llendl;
+		}
+		return false;
+	}
+
+	return true;
+}
+
+U32 LLAudioBufferOpenAL::getLength()
+{
+	if(mALBuffer == AL_NONE)
+	{
+		return 0;
+	}
+	ALint length;
+	alGetBufferi(mALBuffer, AL_SIZE, &length);
+	return length / 2; // convert size in bytes to size in (16-bit) samples
+}
+
+// ------------
+
+void LLAudioEngine_OpenAL::initWind()
+{
+	ALenum error;
+	llinfos << "LLAudioEngine_OpenAL::initWind() start" << llendl;
+
+	mNumEmptyWindALBuffers = MAX_NUM_WIND_BUFFERS;
+
+	alGetError(); /* clear error */
+	
+	alGenSources(1,&mWindSource);
+	
+	if((error=alGetError()) != AL_NO_ERROR)
+	{
+		llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind sources: "<<error<<llendl;
+	}
+
+	mWindGen = new LLWindGen<WIND_SAMPLE_T>;
+
+	mWindBufFreq = mWindGen->getInputSamplingRate();
+	mWindBufSamples = llceil(mWindBufFreq * WIND_BUFFER_SIZE_SEC);
+	mWindBufBytes = mWindBufSamples * 2 /*stereo*/ * sizeof(WIND_SAMPLE_T);
+
+	mWindBuf = new WIND_SAMPLE_T [mWindBufSamples * 2 /*stereo*/];
+
+	if(mWindBuf==NULL)
+	{
+		llerrs << "LLAudioEngine_OpenAL::initWind() Error creating wind memory buffer" << llendl;
+		mEnableWind=false;
+	}
+
+	llinfos << "LLAudioEngine_OpenAL::initWind() done" << llendl;
+}
+
+void LLAudioEngine_OpenAL::cleanupWind()
+{
+	llinfos << "LLAudioEngine_OpenAL::cleanupWind()" << llendl;
+
+	if (mWindSource != AL_NONE)
+	{
+		// detach and delete all outstanding buffers on the wind source
+		alSourceStop(mWindSource);
+		ALint processed;
+		alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
+		while (processed--)
+		{
+			ALuint buffer = AL_NONE;
+			alSourceUnqueueBuffers(mWindSource, 1, &buffer);
+			alDeleteBuffers(1, &buffer);
+		}
+
+		// delete the wind source itself
+		alDeleteSources(1, &mWindSource);
+
+		mWindSource = AL_NONE;
+	}
+	
+	delete[] mWindBuf;
+	mWindBuf = NULL;
+
+	delete mWindGen;
+	mWindGen = NULL;
+}
+
+void LLAudioEngine_OpenAL::updateWind(LLVector3 wind_vec, F32 camera_altitude)
+{
+	LLVector3 wind_pos;
+	F64 pitch;
+	F64 center_freq;
+	ALenum error;
+	
+	if (!mEnableWind)
+		return;
+	
+	if(!mWindBuf)
+		return;
+	
+	if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
+	{
+		
+		// wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
+		// need to convert this to the conventional orientation DS3D and OpenAL use
+		// where +X = right, +Y = up, +Z = backwards
+		
+		wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
+		
+		pitch = 1.0 + mapWindVecToPitch(wind_vec);
+		center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
+		
+		mWindGen->mTargetFreq = (F32)center_freq;
+		mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
+		mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
+		
+		alSourcei(mWindSource, AL_LOOPING, AL_FALSE);
+		alSource3f(mWindSource, AL_POSITION, 0.0, 0.0, 0.0);
+		alSource3f(mWindSource, AL_VELOCITY, 0.0, 0.0, 0.0);
+		alSourcef(mWindSource, AL_ROLLOFF_FACTOR, 0.0);
+		alSourcei(mWindSource, AL_SOURCE_RELATIVE, AL_TRUE);
+	}
+
+	// ok lets make a wind buffer now
+
+	ALint processed, queued, unprocessed;
+	alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
+	alGetSourcei(mWindSource, AL_BUFFERS_QUEUED, &queued);
+	unprocessed = queued - processed;
+
+	// ensure that there are always at least 3x as many filled buffers
+	// queued as we managed to empty since last time.
+	mNumEmptyWindALBuffers = llmin(mNumEmptyWindALBuffers + processed * 3 - unprocessed, MAX_NUM_WIND_BUFFERS-unprocessed);
+	mNumEmptyWindALBuffers = llmax(mNumEmptyWindALBuffers, 0);
+
+	//llinfos << "mNumEmptyWindALBuffers: " << mNumEmptyWindALBuffers	<<" (" << unprocessed << ":" << processed << ")" << llendl;
+
+	while(processed--) // unqueue old buffers
+	{
+		ALuint buffer;
+		ALenum error;
+		alGetError(); /* clear error */
+		alSourceUnqueueBuffers(mWindSource, 1, &buffer);
+		error = alGetError();
+		if(error != AL_NO_ERROR)
+		{
+			llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (unqueuing) buffers" << llendl;
+		}
+		else
+		{
+			alDeleteBuffers(1, &buffer);
+		}
+	}
+
+	unprocessed += mNumEmptyWindALBuffers;
+	while (mNumEmptyWindALBuffers > 0) // fill+queue new buffers
+	{
+		ALuint buffer;
+		alGetError(); /* clear error */
+		alGenBuffers(1,&buffer);
+		if((error=alGetError()) != AL_NO_ERROR)
+		{
+			llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind buffer: " << error << llendl;
+			break;
+		}
+
+		alBufferData(buffer,
+			     AL_FORMAT_STEREO16,
+			     mWindGen->windGenerate(mWindBuf,
+						    mWindBufSamples, 2),
+			     mWindBufBytes,
+			     mWindBufFreq);
+		error = alGetError();
+		if(error != AL_NO_ERROR)
+		{
+			llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (bufferdata) buffers" << llendl;
+		}
+		
+		alSourceQueueBuffers(mWindSource, 1, &buffer);
+		error = alGetError();
+		if(error != AL_NO_ERROR)
+		{
+			llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (queuing) buffers" << llendl;
+		}
+
+		--mNumEmptyWindALBuffers;
+	}
+
+	ALint playing;
+	alGetSourcei(mWindSource, AL_SOURCE_STATE, &playing);
+	if(playing != AL_PLAYING)
+	{
+		alSourcePlay(mWindSource);
+
+		lldebugs << "Wind had stopped - probably ran out of buffers - restarting: " << (unprocessed+mNumEmptyWindALBuffers) << " now queued." << llendl;
+	}
+}
+
diff --git a/indra/llaudio/audioengine_openal.h b/indra/llaudio/audioengine_openal.h
new file mode 100644
index 0000000..54b60e6
--- /dev/null
+++ b/indra/llaudio/audioengine_openal.h
@@ -0,0 +1,113 @@
+/**
+ * @file audioengine_openal.cpp
+ * @brief implementation of audio engine using OpenAL
+ * support as a OpenAL 3D implementation
+ *
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ * 
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+
+#ifndef LL_AUDIOENGINE_OPENAL_H
+#define LL_AUDIOENGINE_OPENAL_H
+
+#include "audioengine.h"
+#include "listener_openal.h"
+#include "windgen.h"
+
+class LLAudioEngine_OpenAL : public LLAudioEngine
+{
+	public:
+		LLAudioEngine_OpenAL();
+		virtual ~LLAudioEngine_OpenAL();
+
+		virtual bool init(const S32 num_channels, void *user_data);
+        	virtual std::string getDriverName(bool verbose);
+		virtual void allocateListener();
+
+		virtual void shutdown();
+
+		void setInternalGain(F32 gain);
+
+		LLAudioBuffer* createBuffer();
+		LLAudioChannel* createChannel();
+
+		/*virtual*/ void initWind();
+		/*virtual*/ void cleanupWind();
+		/*virtual*/ void updateWind(LLVector3 direction, F32 camera_altitude);
+
+	private:
+		void * windDSP(void *newbuffer, int length);
+	        typedef S16 WIND_SAMPLE_T;
+        	LLWindGen<WIND_SAMPLE_T> *mWindGen;
+        	S16 *mWindBuf;
+        	U32 mWindBufFreq;
+        	U32 mWindBufSamples;
+        	U32 mWindBufBytes;
+        	ALuint mWindSource;
+	        int mNumEmptyWindALBuffers;
+
+        	static const int MAX_NUM_WIND_BUFFERS = 80;
+        	static const float WIND_BUFFER_SIZE_SEC = 0.05f; // 1/20th sec
+};
+
+class LLAudioChannelOpenAL : public LLAudioChannel
+{
+	public:
+		LLAudioChannelOpenAL();
+		virtual ~LLAudioChannelOpenAL();
+	protected:
+		/*virtual*/ void play();
+		/*virtual*/ void playSynced(LLAudioChannel *channelp);
+		/*virtual*/ void cleanup();
+		/*virtual*/ bool isPlaying();
+
+		/*virtual*/ bool updateBuffer();
+		/*virtual*/ void update3DPosition();
+		/*virtual*/ void updateLoop();
+
+		ALuint mALSource;
+	        ALint mLastSamplePos;
+};
+
+class LLAudioBufferOpenAL : public LLAudioBuffer{
+	public:
+		LLAudioBufferOpenAL();
+		virtual ~LLAudioBufferOpenAL();
+
+		bool loadWAV(const std::string& filename);
+		U32 getLength();
+
+		friend class LLAudioChannelOpenAL;
+	protected:
+		void cleanup();
+		ALuint getBuffer() {return mALBuffer;}
+
+		ALuint mALBuffer;
+};
+
+#endif
diff --git a/indra/llaudio/listener_fmod.h b/indra/llaudio/listener_fmod.h
index 2c773ef..6ac8f20 100644
--- a/indra/llaudio/listener_fmod.h
+++ b/indra/llaudio/listener_fmod.h
@@ -37,11 +37,6 @@
 
 class LLListener_FMOD : public LLListener
 {
- protected:
-	 F32 mDopplerFactor;
-	 F32 mDistanceFactor;
-	 F32 mRolloffFactor;
-
  public:  
 	LLListener_FMOD();
 	virtual ~LLListener_FMOD();
@@ -59,6 +54,11 @@ class LLListener_FMOD : public LLListener
 	virtual F32 getDistanceFactor();
 	virtual void setRolloffFactor(F32 factor);
 	virtual F32 getRolloffFactor();
+
+ protected:
+	 F32 mDopplerFactor;
+	 F32 mDistanceFactor;
+	 F32 mRolloffFactor;
 };
 
 #endif
diff --git a/indra/llaudio/listener_openal.cpp b/indra/llaudio/listener_openal.cpp
new file mode 100644
index 0000000..718de63
--- /dev/null
+++ b/indra/llaudio/listener_openal.cpp
@@ -0,0 +1,125 @@
+/**
+ * @file audioengine_openal.cpp
+ * @brief implementation of audio engine using OpenAL
+ * support as a OpenAL 3D implementation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ * 
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "audioengine.h"
+
+#include "listener_openal.h"
+
+LLListener_OpenAL::LLListener_OpenAL()
+{
+	init();
+}
+
+LLListener_OpenAL::~LLListener_OpenAL()
+{
+}
+
+void LLListener_OpenAL::translate(LLVector3 offset)
+{
+	//llinfos << "LLListener_OpenAL::translate() : " << offset << llendl;
+	LLListener::translate(offset);
+}
+
+void LLListener_OpenAL::setPosition(LLVector3 pos)
+{
+	//llinfos << "LLListener_OpenAL::setPosition() : " << pos << llendl;
+	LLListener::setPosition(pos);
+}
+
+void LLListener_OpenAL::setVelocity(LLVector3 vel)
+{
+	LLListener::setVelocity(vel);
+}
+
+void LLListener_OpenAL::orient(LLVector3 up, LLVector3 at)
+{
+	//llinfos << "LLListener_OpenAL::orient() up: " << up << " at: " << at << llendl;
+	LLListener::orient(up, at);
+}
+
+void LLListener_OpenAL::commitDeferredChanges()
+{
+	ALfloat orientation[6];
+	orientation[0] = mListenAt.mV[0];
+	orientation[1] = mListenAt.mV[1];
+	orientation[2] = mListenAt.mV[2];
+	orientation[3] = mListenUp.mV[0];
+	orientation[4] = mListenUp.mV[1];
+	orientation[5] = mListenUp.mV[2];
+
+	ALfloat velocity[3];
+	velocity[0] = mVelocity.mV[0];
+	velocity[1] = mVelocity.mV[1];
+	velocity[2] = mVelocity.mV[2];
+
+	alListenerfv(AL_ORIENTATION, orientation);
+	alListenerfv(AL_POSITION, mPosition.mV);
+	alListenerfv(AL_VELOCITY, velocity);
+}
+
+void LLListener_OpenAL::setDopplerFactor(F32 factor)
+{
+	//llinfos << "LLListener_OpenAL::setDopplerFactor() : " << factor << llendl;
+	alDopplerFactor(factor);
+}
+
+F32 LLListener_OpenAL::getDopplerFactor()
+{
+	ALfloat factor;
+	factor = alGetFloat(AL_DOPPLER_FACTOR);
+	//llinfos << "LLListener_OpenAL::getDopplerFactor() : " << factor << llendl;
+	return factor;
+}
+
+
+void LLListener_OpenAL::setRolloffFactor(F32 factor)
+{
+	mRolloffFactor = factor;
+}
+
+F32 LLListener_OpenAL::getRolloffFactor()
+{
+	return mRolloffFactor;
+}
+
+
+void LLListener_OpenAL::setDistanceFactor(F32 factor)
+{
+	mDistanceFactor = factor;
+}
+
+F32 LLListener_OpenAL::getDistanceFactor()
+{
+	return mDistanceFactor;
+}
+
diff --git a/indra/llaudio/listener_openal.h b/indra/llaudio/listener_openal.h
index e6c9324..3e4353f 100644
--- a/indra/llaudio/listener_openal.h
+++ b/indra/llaudio/listener_openal.h
@@ -40,12 +40,6 @@
 
 class LLListener_OpenAL  : public LLListener
 {
- private:
- protected:
- public:
-
- private:
- protected:
  public:  
 	LLListener_OpenAL();
 	virtual ~LLListener_OpenAL();
@@ -54,6 +48,18 @@ class LLListener_OpenAL  : public LLListener
 	virtual void setPosition(LLVector3 pos);
 	virtual void setVelocity(LLVector3 vel);
 	virtual void orient(LLVector3 up, LLVector3 at);
+	virtual void commitDeferredChanges();
+
+	virtual void setDopplerFactor(F32 factor);
+	virtual F32 getDopplerFactor();
+	virtual void setDistanceFactor(F32 factor);
+	virtual F32 getDistanceFactor();
+	virtual void setRolloffFactor(F32 factor);
+	virtual F32 getRolloffFactor();
+
+ protected:
+	F32 mDistanceFactor;
+	F32 mRolloffFactor;
 };
 
 #endif
diff --git a/indra/llaudio/windgen.h b/indra/llaudio/windgen.h
new file mode 100644
index 0000000..faffd3c
--- /dev/null
+++ b/indra/llaudio/windgen.h
@@ -0,0 +1,135 @@
+/** 
+ * @file windgen.h
+ * @brief Templated wind noise generation
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ * 
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ * 
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab.  Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * 
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ * 
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#ifndef WINDGEN_H
+#define WINDGEN_H
+
+#include "llcommon.h"
+#include "llrand.h"
+
+template <class MIXBUFFERFORMAT_T>
+class LLWindGen
+{
+public:
+	LLWindGen() :
+		mTargetGain(0.f),
+		mTargetFreq(100.f),
+		mTargetPanGainR(0.5f),
+		mbuf0(0.0),
+		mbuf1(0.0),
+		mbuf2(0.0),
+		mbuf3(0.0),
+		mbuf4(0.0),
+		mbuf5(0.0),
+		mY0(0.0),
+		mY1(0.0),
+		mCurrentGain(0.f),
+		mCurrentFreq(100.f),
+		mCurrentPanGainR(0.5f) {};
+
+	static const U32 getInputSamplingRate() {return mInputSamplingRate;}
+
+	// newbuffer = the buffer passed from the previous DSP unit.
+	// numsamples = length in samples-per-channel at this mix time.
+	// stride = number of bytes between start of each sample.
+	// NOTE: generates L/R interleaved stereo
+	MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples, int stride)
+	{
+		U8 *cursamplep = (U8*)newbuffer;
+		
+		double bandwidth = 50.0F;
+		double a0,b1,b2;
+		
+		// calculate resonant filter coeffs
+		b2 = exp(-(F_TWO_PI) * (bandwidth / mInputSamplingRate));
+		
+		while (numsamples--)
+		{
+			mCurrentFreq = (float)((0.999 * mCurrentFreq) + (0.001 * mTargetFreq));
+			mCurrentGain = (float)((0.999 * mCurrentGain) + (0.001 * mTargetGain));
+			mCurrentPanGainR = (float)((0.999 * mCurrentPanGainR) + (0.001 * mTargetPanGainR));
+			b1 = (-4.0 * b2) / (1.0 + b2) * cos(F_TWO_PI * (mCurrentFreq / mInputSamplingRate));
+			a0 = (1.0 - b2) * sqrt(1.0 - (b1 * b1) / (4.0 * b2));
+			double nextSample;
+			
+			// start with white noise
+			nextSample = ll_frand(2.0f) - 1.0f;
+			
+			// apply pinking filter
+			mbuf0 = 0.997f * mbuf0 + 0.0126502f * nextSample; 
+			mbuf1 = 0.985f * mbuf1 + 0.0139083f * nextSample;
+			mbuf2 = 0.950f * mbuf2 + 0.0205439f * nextSample;
+			mbuf3 = 0.850f * mbuf3 + 0.0387225f * nextSample;
+			mbuf4 = 0.620f * mbuf4 + 0.0465932f * nextSample;
+			mbuf5 = 0.250f * mbuf5 + 0.1093477f * nextSample;
+			
+			nextSample = mbuf0 + mbuf1 + mbuf2 + mbuf3 + mbuf4 + mbuf5;
+			
+			// do a resonant filter on the noise
+			nextSample = (double)( a0 * nextSample - b1 * mY0 - b2 * mY1 );
+			mY1 = mY0;
+			mY0 = nextSample;
+			
+			nextSample *= mCurrentGain;
+			
+			MIXBUFFERFORMAT_T	sample;
+			
+			sample = llfloor(((F32)nextSample*32768.f*(1.0f - mCurrentPanGainR))+0.5f);
+			*(MIXBUFFERFORMAT_T*)cursamplep = llclamp(sample, (MIXBUFFERFORMAT_T)-32768, (MIXBUFFERFORMAT_T)32767);
+			cursamplep += stride;
+
+			sample = llfloor(((F32)nextSample*32768.f*mCurrentPanGainR)+0.5f);
+			*(MIXBUFFERFORMAT_T*)cursamplep = llclamp(sample, (MIXBUFFERFORMAT_T)-32768, (MIXBUFFERFORMAT_T)32767);
+			cursamplep += stride;
+		}
+		
+		return newbuffer;
+	}
+
+	F32 mTargetGain;
+	F32 mTargetFreq;
+	F32 mTargetPanGainR;
+
+private:
+	static const U32 mInputSamplingRate = 44100;
+	F64 mbuf0;
+	F64 mbuf1;
+	F64 mbuf2;
+	F64 mbuf3;
+	F64 mbuf4;
+	F64 mbuf5;
+	F64 mY0;
+	F64 mY1;
+	F32 mCurrentGain;
+	F32 mCurrentFreq;
+	F32 mCurrentPanGainR;
+};
+
+#endif
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 11401f2..ce29ab4 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -34,7 +34,7 @@
 
 const S32 LL_VERSION_MAJOR = 1;
 const S32 LL_VERSION_MINOR = 22;
-const S32 LL_VERSION_PATCH = 7;
+const S32 LL_VERSION_PATCH = 8;
 const S32 LL_VERSION_BUILD = 0;
 
 const char * const LL_CHANNEL = "Second Life Release";
diff --git a/indra/llmedia/llmediaimplgstreamer.cpp b/indra/llmedia/llmediaimplgstreamer.cpp
index 573c699..51614c5 100644
--- a/indra/llmedia/llmediaimplgstreamer.cpp
+++ b/indra/llmedia/llmediaimplgstreamer.cpp
@@ -80,6 +80,8 @@ LLMediaImplGStreamer () :
 {
 	DEBUGMSG("constructing media...");
 
+	mVolume = 0.1234567; // minor hack to force an initial volume update
+
 	setMediaDepth(4);
 
 	// Create a pumpable main-loop for this media
@@ -639,12 +641,19 @@ bool
 LLMediaImplGStreamer::
 setVolume(float volume)
 {
+	// we try to only update volume as conservatively as
+	// possible, as many gst-plugins-base versions up to at least
+	// November 2008 have critical race-conditions in setting volume - sigh
+	if (mVolume == volume)
+		return true; // nothing to do, everything's fine
+
 	mVolume = volume;
 	if (mPlaybin)
 	{
 		g_object_set(mPlaybin, "volume", mVolume, NULL);
 		return true;
 	}
+
 	return false;
 }
 
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 7c49f72..b95ba37 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -9,6 +9,7 @@ include(DBusGlib)
 include(DirectX)
 include(ELFIO)
 include(FMOD)
+include(OPENAL)
 include(FindOpenGL)
 include(LLAudio)
 include(LLCharacter)
@@ -1257,8 +1258,12 @@ if (WINDOWS)
   list(APPEND viewer_SOURCE_FILES ${viewer_INSTALLER_FILES})
 endif (WINDOWS)
 
+if (OPENAL)
+  set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_OPENAL")
+endif (OPENAL)
+
 if (FMOD)
-  set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS -DLL_FMOD)
+  set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMOD")
 
   if (NOT WINDOWS)
     set(fmodwrapper_SOURCE_FILES fmodwrapper.cpp)
@@ -1279,6 +1284,8 @@ if (FMOD)
   endif (NOT WINDOWS)
 endif (FMOD)
 
+set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}")
+
 list(APPEND viewer_SOURCE_FILES ${viewer_HEADER_FILES})
 
 set_source_files_properties(${viewer_HEADER_FILES}
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index d3519da..27eacfb 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -9607,16 +9607,27 @@
       <key>Value</key>
       <string>-1</string>
     </map>
-    <key>VivoxDebugServerName</key>
+    <key>VivoxDebugSIPURIHostName</key>
     <map>
       <key>Comment</key>
-      <string>Hostname of the vivox account server to use for voice when not connected to Agni.</string>
+      <string>Hostname portion of vivox SIP URIs (empty string for the default).</string>
       <key>Persist</key>
       <integer>1</integer>
       <key>Type</key>
       <string>String</string>
       <key>Value</key>
-      <string>bhd.vivox.com</string>
+      <string></string>
+    </map>
+    <key>VivoxDebugVoiceAccountServerURI</key>
+    <map>
+      <key>Comment</key>
+      <string>URI to the vivox account management server (empty string for the default).</string>
+      <key>Persist</key>
+      <integer>1</integer>
+      <key>Type</key>
+      <string>String</string>
+      <key>Value</key>
+      <string></string>
     </map>
     <key>VoiceCallsFriendsOnly</key>
     <map>
@@ -9759,7 +9770,7 @@
       <key>Type</key>
       <string>U32</string>
       <key>Value</key>
-      <integer>44124</integer>
+      <integer>44125</integer>
     </map>
     <key>WLSkyDetail</key>
     <map>
diff --git a/indra/newview/installers/darwin/dmg-cleanup.applescript b/indra/newview/installers/darwin/dmg-cleanup.applescript
new file mode 100644
index 0000000..f3d39ae
--- /dev/null
+++ b/indra/newview/installers/darwin/dmg-cleanup.applescript
@@ -0,0 +1,28 @@
+-- First, convert the disk image to "read-write" format with Disk Utility or hdiutil
+-- Mount the image, open the disk image window in the Finder and make it frontmost, then run this script from inside Script Editor
+-- After running the script, unmount the disk image, re-mount it, and copy the .DS_Store file off from the command line.
+
+tell application "Finder"
+	
+	set foo to every item in front window
+	repeat with i in foo
+		if the name of i is "Applications" then
+			set the position of i to {391, 165}
+		else if the name of i ends with ".app" then
+			set the position of i to {121, 166}
+		end if
+	end repeat
+	
+	-- There doesn't seem to be a way to set the background picture with applescript, but all the saved .DS_Store files should already have that set correctly.
+	
+	set foo to front window
+	set current view of foo to icon view
+	set toolbar visible of foo to false
+	set statusbar visible of foo to false
+	set the bounds of foo to {100, 100, 600, 399}
+	
+	-- set the position of front window to {100, 100}
+	-- get {name, position} of every item of front window
+	
+	get properties of front window
+end tell
diff --git a/indra/newview/installers/darwin/firstlook-dmg/Applications-alias.r b/indra/newview/installers/darwin/firstlookslim-dmg/Applications-alias.r
similarity index 100%
copy from indra/newview/installers/darwin/firstlook-dmg/Applications-alias.r
copy to indra/newview/installers/darwin/firstlookslim-dmg/Applications-alias.r
diff --git a/indra/newview/installers/darwin/firstlookslim-dmg/_DS_Store b/indra/newview/installers/darwin/firstlookslim-dmg/_DS_Store
new file mode 100644
index 0000000..e170ac7
Binary files /dev/null and b/indra/newview/installers/darwin/firstlookslim-dmg/_DS_Store differ
diff --git a/indra/newview/installers/darwin/firstlook-dmg/_VolumeIcon.icns b/indra/newview/installers/darwin/firstlookslim-dmg/_VolumeIcon.icns
similarity index 100%
copy from indra/newview/installers/darwin/firstlook-dmg/_VolumeIcon.icns
copy to indra/newview/installers/darwin/firstlookslim-dmg/_VolumeIcon.icns
diff --git a/indra/newview/installers/darwin/firstlook-dmg/background.jpg b/indra/newview/installers/darwin/firstlookslim-dmg/background.jpg
similarity index 100%
copy from indra/newview/installers/darwin/firstlook-dmg/background.jpg
copy to indra/newview/installers/darwin/firstlookslim-dmg/background.jpg
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
index 179cb9e..fc7994b 100644
--- a/indra/newview/linux_tools/client-readme.txt
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -15,7 +15,7 @@ Life itself - please see <http://www.secondlife.com/whatis/>.
    5.3. Blank window after minimizing it
    5.4. Audio
    5.5. 'Alt' key for camera controls doesn't work
-   5.6. In-world movie playback
+   5.6. In-world streaming movie/music playback
 6. Advanced Troubleshooting
    6.1. Audio
    6.2. OpenGL
@@ -169,11 +169,11 @@ SOLUTION:- Some window managers eat the Alt key for their own purposes; you
    example, the 'Windows' key!) which will allow the Alt key to function
    properly with mouse actions in Second Life and other applications.
 
-PROBLEM 6:- In-world movie playback doesn't work for me.
+PROBLEM 6:- In-world movie and/or music playback doesn't work for me.
 SOLUTION:- You need to have a working installation of GStreamer 0.10; this
    is usually an optional package for most versions of Linux.  If you have
-   installed GStreamer 0.10 and you can play some movies but not others then
-   you need to install a wider selection of GStreamer plugins, either
+   installed GStreamer 0.10 and you can play some music/movies but not others
+   then you need to install a wider selection of GStreamer plugins, either
    from your vendor or an appropriate third party.
 
 
@@ -183,11 +183,11 @@ SOLUTION:- You need to have a working installation of GStreamer 0.10; this
 The 'secondlife' script which launches Second Life contains some
 configuration options for advanced troubleshooters.
 
-* AUDIO - Edit the 'secondlife' script and you will see three audio
-  options: LL_BAD_ESD, LL_BAD_OSS, LL_BAD_ALSA.  Second Life tries to
-  use ESD, OSS, then ALSA audio drivers in this order; you may uncomment
-  the corresponding LL_BAD_* option to skip an audio driver which you
-  believe may be causing you trouble.
+* AUDIO - Edit the 'secondlife' script and you will see these audio
+  options: LL_BAD_OPENAL_DRIVER, LL_BAD_FMOD_ESD, LL_BAD_FMOD_OSS, and
+  LL_BAD_FMOD_ALSA.  Second Life tries to use OpenAL, ESD, OSS, then ALSA
+  audio drivers in this order; you may uncomment the corresponding LL_BAD_*
+  option to skip an audio driver which you believe may be causing you trouble.
 
 * OPENGL - For advanced troubleshooters, the LL_GL_BLACKLIST option lets
   you disable specific GL extensions, each of which is represented by a
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index 9d2e06b..d7b17ed 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -4,14 +4,17 @@
 ## These options are for self-assisted troubleshooting during this beta
 ## testing phase; you should not usually need to touch them.
 
-## - Avoids using the ESD audio driver.
-#export LL_BAD_ESD=x
-
-## - Avoids using the OSS audio driver.
-#export LL_BAD_OSS=x
-
-## - Avoids using the ALSA audio driver.
-#export LL_BAD_ALSA=x
+## - Avoids using any OpenAL audio driver.
+#export LL_BAD_OPENAL_DRIVER=x
+## - Avoids using any FMOD audio driver.
+#export LL_BAD_FMOD_DRIVER=x
+
+## - Avoids using the FMOD ESD audio driver.
+#export LL_BAD_FMOD_ESD=x
+## - Avoids using the FMOD OSS audio driver.
+#export LL_BAD_FMOD_OSS=x
+## - Avoids using the FMOD ALSA audio driver.
+#export LL_BAD_FMOD_ALSA=x
 
 ## - Avoids the optional OpenGL extensions which have proven most problematic
 ##   on some hardware.  Disabling this option may cause BETTER PERFORMANCE but
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index e1a0fe2..09d1141 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1156,16 +1156,24 @@ bool LLAppViewer::cleanup()
 	
 	llinfos << "Global stuff deleted" << llendflush;
 
-#if !LL_RELEASE_FOR_DOWNLOAD
 	if (gAudiop)
 	{
-		gAudiop->shutdown();
+		bool want_longname = false;
+		if (gAudiop->getDriverName(want_longname) == "FMOD")
+		{
+			// This hack exists because fmod likes to occasionally
+			// hang forever when shutting down, for no apparent
+			// reason.
+			llwarns << "Hack, skipping FMOD audio engine cleanup" << llendflush;
+		}
+		else
+		{
+			gAudiop->shutdown();
+		}
+
+		delete gAudiop;
+		gAudiop = NULL;
 	}
-#else
-	// This hack exists because fmod likes to occasionally hang forever
-	// when shutting down for no apparent reason.
-	llwarns << "Hack, skipping audio engine cleanup" << llendflush;
-#endif
 
 	// Note: this is where LLFeatureManager::getInstance()-> used to be deleted.
 
@@ -1176,9 +1184,6 @@ bool LLAppViewer::cleanup()
 	cleanupSavedSettings();
 	llinfos << "Settings patched up" << llendflush;
 
-	delete gAudiop;
-	gAudiop = NULL;
-
 	// delete some of the files left around in the cache.
 	removeCacheFiles("*.wav");
 	removeCacheFiles("*.tmp");
@@ -2301,6 +2306,7 @@ void LLAppViewer::handleViewerCrash()
 	gDebugInfo["CurrentPath"] = gDirUtilp->getCurPath();
 	gDebugInfo["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds());
 	gDebugInfo["StartupState"] = LLStartUp::getStartupStateString();
+	gDebugInfo["RAMInfo"]["Allocated"] = (LLSD::Integer) getCurrentRSS() >> 10;
 
 	if(gLogoutInProgress)
 	{
@@ -3206,6 +3212,8 @@ void LLAppViewer::idle()
 		return;
     }
 
+	gViewerWindow->handlePerFrameHover();
+
 	///////////////////////////////////////
 	// Agent and camera movement
 	//
diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp
index a5ad97f..19b7653 100644
--- a/indra/newview/llfloaterabout.cpp
+++ b/indra/newview/llfloaterabout.cpp
@@ -41,6 +41,7 @@
 
 #include "llcurl.h"
 #include "llimagej2c.h"
+#include "audioengine.h"
 
 #include "llviewertexteditor.h"
 #include "llviewercontrol.h"
@@ -197,6 +198,11 @@ LLFloaterAbout::LLFloaterAbout()
 	support.append( LLImageJ2C::getEngineInfo() );
 	support.append("\n");
 
+	support.append("Audio Driver Version: ");
+	bool want_fullname = true;
+	support.append( gAudiop ? gAudiop->getDriverName(want_fullname) : "(none)" );
+	support.append("\n");
+
 	LLMediaManager *mgr = LLMediaManager::getInstance();
 	if (mgr)
 	{
diff --git a/indra/newview/llfloateractivespeakers.cpp b/indra/newview/llfloateractivespeakers.cpp
index be0e88f..0a19fb2 100644
--- a/indra/newview/llfloateractivespeakers.cpp
+++ b/indra/newview/llfloateractivespeakers.cpp
@@ -547,7 +547,8 @@ void LLPanelActiveSpeakers::refreshSpeakers()
 									&& gVoiceClient->getVoiceEnabled(selected_id)
 									&& selected_id.notNull() 
 									&& selected_id != gAgent.getID() 
-									&& (selected_speakerp.notNull() && selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT));
+									&& (selected_speakerp.notNull() && (selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT || selected_speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL)));
+
 	}
 	if (mMuteTextCtrl)
 	{
@@ -555,6 +556,7 @@ void LLPanelActiveSpeakers::refreshSpeakers()
 		mMuteTextCtrl->setEnabled(selected_id.notNull() 
 								&& selected_id != gAgent.getID() 
 								&& selected_speakerp.notNull() 
+								&& selected_speakerp->mType != LLSpeaker::SPEAKER_EXTERNAL
 								&& !LLMuteList::getInstance()->isLinden(selected_speakerp->mDisplayName));
 	}
 	childSetValue("speaker_volume", gVoiceClient->getUserVolume(selected_id));
@@ -562,7 +564,7 @@ void LLPanelActiveSpeakers::refreshSpeakers()
 					&& gVoiceClient->getVoiceEnabled(selected_id)
 					&& selected_id.notNull() 
 					&& selected_id != gAgent.getID() 
-					&& (selected_speakerp.notNull() && selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT));
+					&& (selected_speakerp.notNull() && (selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT || selected_speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL)));
 
 	childSetEnabled(
 		"moderator_controls_label",
@@ -580,7 +582,7 @@ void LLPanelActiveSpeakers::refreshSpeakers()
 
 	if (mProfileBtn)
 	{
-		mProfileBtn->setEnabled(selected_id.notNull());
+		mProfileBtn->setEnabled(selected_id.notNull() && (selected_speakerp.notNull() && selected_speakerp->mType != LLSpeaker::SPEAKER_EXTERNAL) );
 	}
 
 	// show selected user name in large font
@@ -1034,9 +1036,17 @@ void LLSpeakerMgr::update(BOOL resort_ok)
 		// speaker no longer registered in voice channel, demote to text only
 		else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL)
 		{
-			speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY;
-			speakerp->mSpeechVolume = 0.f;
-			speakerp->mDotColor = ACTIVE_COLOR;
+			if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL)
+			{
+				// external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice)
+				speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
+			}
+			else
+			{
+				speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY;
+				speakerp->mSpeechVolume = 0.f;
+				speakerp->mDotColor = ACTIVE_COLOR;
+			}
 		}
 	}
 
@@ -1088,13 +1098,16 @@ void LLSpeakerMgr::updateSpeakerList()
 	if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
 	{
 		LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList();
-		LLVoiceClient::participantMap::iterator participant_it;
-
-		// add new participants to our list of known speakers
-		for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it)
+		if(participants)
 		{
-			LLVoiceClient::participantState* participantp = participant_it->second;
-			setSpeaker(participantp->mAvatarID, LLStringUtil::null, LLSpeaker::STATUS_VOICE_ACTIVE);
+			LLVoiceClient::participantMap::iterator participant_it;
+
+			// add new participants to our list of known speakers
+			for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it)
+			{
+				LLVoiceClient::participantState* participantp = participant_it->second;
+				setSpeaker(participantp->mAvatarID, participantp->mDisplayName, LLSpeaker::STATUS_VOICE_ACTIVE, (participantp->mAvatarIDValid?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL));
+			}
 		}
 	}
 }
@@ -1167,6 +1180,10 @@ void LLIMSpeakerMgr::updateSpeakerList()
 {
 	// don't do normal updates which are pulled from voice channel
 	// rely on user list reported by sim
+	
+	// We need to do this to allow PSTN callers into group chats to show in the list.
+	LLSpeakerMgr::updateSpeakerList();
+	
 	return;
 }
 
diff --git a/indra/newview/llfloateractivespeakers.h b/indra/newview/llfloateractivespeakers.h
index 9b6f119..0fb0267 100644
--- a/indra/newview/llfloateractivespeakers.h
+++ b/indra/newview/llfloateractivespeakers.h
@@ -53,7 +53,8 @@ public:
 	typedef enum e_speaker_type
 	{
 		SPEAKER_AGENT,
-		SPEAKER_OBJECT
+		SPEAKER_OBJECT,
+		SPEAKER_EXTERNAL	// Speaker that doesn't map to an avatar or object (i.e. PSTN caller in a group)
 	} ESpeakerType;
 
 	typedef enum e_speaker_status
diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp
index 5563705..1402b2c 100644
--- a/indra/newview/llfloaterfriends.cpp
+++ b/indra/newview/llfloaterfriends.cpp
@@ -57,6 +57,7 @@
 #include "llviewermessage.h"
 #include "lltimer.h"
 #include "lltextbox.h"
+#include "llvoiceclient.h"
 
 //Maximum number of people you can select to do an operation on at once.
 #define MAX_FRIEND_SELECT 20
@@ -64,6 +65,8 @@
 #define RIGHTS_CHANGE_TIMEOUT 5.0
 #define OBSERVER_TIMEOUT 0.5
 
+#define ONLINE_SIP_ICON_NAME "slim_icon_16_viewer.tga"
+
 // simple class to observe the calling cards.
 class LLLocalFriendsObserver : public LLFriendObserver, public LLEventTimer
 {
@@ -111,10 +114,14 @@ LLPanelFriends::LLPanelFriends() :
 	mEventTimer.stop();
 	mObserver = new LLLocalFriendsObserver(this);
 	LLAvatarTracker::instance().addObserver(mObserver);
+	// For notification when SIP online status changes.
+	LLVoiceClient::getInstance()->addObserver(mObserver);
 }
 
 LLPanelFriends::~LLPanelFriends()
 {
+	// For notification when SIP online status changes.
+	LLVoiceClient::getInstance()->removeObserver(mObserver);
 	LLAvatarTracker::instance().removeObserver(mObserver);
 	delete mObserver;
 }
@@ -212,7 +219,9 @@ BOOL LLPanelFriends::addFriend(const LLUUID& agent_id)
 	LLAvatarTracker& at = LLAvatarTracker::instance();
 	const LLRelationship* relationInfo = at.getBuddyInfo(agent_id);
 	if(!relationInfo) return FALSE;
-	BOOL online = relationInfo->isOnline();
+
+	bool isOnlineSIP = LLVoiceClient::getInstance()->isOnlineSIP(agent_id);
+	bool isOnline = relationInfo->isOnline();
 
 	std::string fullname;
 	BOOL have_name = gCacheName->getFullName(agent_id, fullname);
@@ -228,12 +237,17 @@ BOOL LLPanelFriends::addFriend(const LLUUID& agent_id)
 	LLSD& online_status_column = element["columns"][LIST_ONLINE_STATUS];
 	online_status_column["column"] = "icon_online_status";
 	online_status_column["type"] = "icon";
-
-	if (online)
+	
+	if (isOnline)
 	{
 		friend_column["font-style"] = "BOLD";	
 		online_status_column["value"] = "icon_avatar_online.tga";
 	}
+	else if(isOnlineSIP)
+	{
+		friend_column["font-style"] = "BOLD";	
+		online_status_column["value"] = ONLINE_SIP_ICON_NAME;
+	}
 
 	LLSD& online_column = element["columns"][LIST_VISIBLE_ONLINE];
 	online_column["column"] = "icon_visible_online";
@@ -271,14 +285,30 @@ BOOL LLPanelFriends::updateFriendItem(const LLUUID& agent_id, const LLRelationsh
 	if (!info) return FALSE;
 	LLScrollListItem* itemp = mFriendsList->getItem(agent_id);
 	if (!itemp) return FALSE;
+	
+	bool isOnlineSIP = LLVoiceClient::getInstance()->isOnlineSIP(itemp->getUUID());
+	bool isOnline = info->isOnline();
 
 	std::string fullname;
 	BOOL have_name = gCacheName->getFullName(agent_id, fullname);
+	
+	// Name of the status icon to use
+	std::string statusIcon;
+	
+	if(isOnline)
+	{
+		statusIcon = "icon_avatar_online.tga";
+	}
+	else if(isOnlineSIP)
+	{
+		statusIcon = ONLINE_SIP_ICON_NAME;
+	}
 
-	itemp->getColumn(LIST_ONLINE_STATUS)->setValue(info->isOnline() ? std::string("icon_avatar_online.tga") : LLStringUtil::null);
+	itemp->getColumn(LIST_ONLINE_STATUS)->setValue(statusIcon);
+	
 	itemp->getColumn(LIST_FRIEND_NAME)->setValue(fullname);
 	// render name of online friends in bold text
-	((LLScrollListText*)itemp->getColumn(LIST_FRIEND_NAME))->setFontStyle(info->isOnline() ? LLFontGL::BOLD : LLFontGL::NORMAL);	
+	((LLScrollListText*)itemp->getColumn(LIST_FRIEND_NAME))->setFontStyle((isOnline || isOnlineSIP) ? LLFontGL::BOLD : LLFontGL::NORMAL);	
 	itemp->getColumn(LIST_VISIBLE_ONLINE)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS));
 	itemp->getColumn(LIST_VISIBLE_MAP)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION));
 	itemp->getColumn(LIST_EDIT_MINE)->setValue(info->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS));
diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp
index 0c9333e..6d77354 100644
--- a/indra/newview/llimpanel.cpp
+++ b/indra/newview/llimpanel.cpp
@@ -357,7 +357,7 @@ LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& sess
 		llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl;
 	}
 
-	LLVoiceClient::getInstance()->addStatusObserver(this);
+	LLVoiceClient::getInstance()->addObserver(this);
 }
 
 LLVoiceChannel::~LLVoiceChannel()
@@ -365,7 +365,7 @@ LLVoiceChannel::~LLVoiceChannel()
 	// Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed.
 	if(gVoiceClient)
 	{
-		gVoiceClient->removeStatusObserver(this);
+		gVoiceClient->removeObserver(this);
 	}
 	
 	sVoiceChannelMap.erase(mSessionID);
@@ -983,7 +983,8 @@ void LLVoiceChannelP2P::activate()
 		// otherwise answering the call
 		else
 		{
-			LLVoiceClient::getInstance()->answerInvite(mSessionHandle, mOtherUserID);
+			LLVoiceClient::getInstance()->answerInvite(mSessionHandle);
+			
 			// using the session handle invalidates it.  Clear it out here so we can't reuse it by accident.
 			mSessionHandle.clear();
 		}
@@ -1000,7 +1001,7 @@ void LLVoiceChannelP2P::getChannelInfo()
 }
 
 // receiving session from other user who initiated call
-void LLVoiceChannelP2P::setSessionHandle(const std::string& handle)
+void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI)
 { 
 	BOOL needs_activate = FALSE;
 	if (callStarted())
@@ -1023,8 +1024,17 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle)
 	}
 
 	mSessionHandle = handle;
+
 	// The URI of a p2p session should always be the other end's SIP URI.
-	setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID));
+	if(!inURI.empty())
+	{
+		setURI(inURI);
+	}
+	else
+	{
+		setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID));
+	}
+	
 	mReceivedCall = TRUE;
 
 	if (needs_activate)
@@ -1207,7 +1217,23 @@ LLFloaterIMPanel::~LLFloaterIMPanel()
 {
 	delete mSpeakers;
 	mSpeakers = NULL;
-
+	
+	// End the text IM session if necessary
+	if(gVoiceClient && mOtherParticipantUUID.notNull())
+	{
+		switch(mDialog)
+		{
+			case IM_NOTHING_SPECIAL:
+			case IM_SESSION_P2P_INVITE:
+				gVoiceClient->endUserIMSession(mOtherParticipantUUID);
+			break;
+			
+			default:
+				// Appease the compiler
+			break;
+		}
+	}
+	
 	//kicks you out of the voice channel if it is currently active
 
 	// HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point).
@@ -1872,33 +1898,45 @@ void deliver_message(const std::string& utf8_text,
 					 EInstantMessage dialog)
 {
 	std::string name;
+	bool sent = false;
 	gAgent.buildFullname(name);
 
 	const LLRelationship* info = NULL;
 	info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id);
+	
 	U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
-
-	// default to IM_SESSION_SEND unless it's nothing special - in
-	// which case it's probably an IM to everyone.
-	U8 new_dialog = dialog;
-
-	if ( dialog != IM_NOTHING_SPECIAL )
+	
+	if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))
 	{
-		new_dialog = IM_SESSION_SEND;
+		// User is online through the OOW connector, but not with a regular viewer.  Try to send the message via SLVoice.
+		sent = gVoiceClient->sendTextMessage(other_participant_id, utf8_text);
 	}
+	
+	if(!sent)
+	{
+		// Send message normally.
 
-	pack_instant_message(
-		gMessageSystem,
-		gAgent.getID(),
-		FALSE,
-		gAgent.getSessionID(),
-		other_participant_id,
-		name,
-		utf8_text,
-		offline,
-		(EInstantMessage)new_dialog,
-		im_session_id);
-	gAgent.sendReliableMessage();
+		// default to IM_SESSION_SEND unless it's nothing special - in
+		// which case it's probably an IM to everyone.
+		U8 new_dialog = dialog;
+
+		if ( dialog != IM_NOTHING_SPECIAL )
+		{
+			new_dialog = IM_SESSION_SEND;
+		}
+		pack_instant_message(
+			gMessageSystem,
+			gAgent.getID(),
+			FALSE,
+			gAgent.getSessionID(),
+			other_participant_id,
+			name.c_str(),
+			utf8_text.c_str(),
+			offline,
+			(EInstantMessage)new_dialog,
+			im_session_id);
+		gAgent.sendReliableMessage();
+	}
 
 	// If there is a mute list and this is not a group chat...
 	if ( LLMuteList::getInstance() )
diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h
index bdddda4..3e22292 100644
--- a/indra/newview/llimpanel.h
+++ b/indra/newview/llimpanel.h
@@ -161,7 +161,7 @@ public:
     /*virtual*/ void activate();
 	/*virtual*/ void getChannelInfo();
 
-	void setSessionHandle(const std::string& handle);
+	void setSessionHandle(const std::string& handle, const std::string &inURI);
 
 protected:
 	virtual void setState(EState state);
@@ -294,8 +294,6 @@ private:
 
 	void sendTypingState(BOOL typing);
 	
-	static LLFloaterIMPanel* sInstance;
-
 private:
 	LLLineEditor* mInputEditor;
 	LLViewerTextEditor* mHistoryEditor;
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 7839146..10e8ff8 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -267,7 +267,8 @@ public:
 		EInstantMessage type,
 		EInvitationType inv_type,
 		const std::string& session_handle,
-		const std::string& notify_box) : 
+		const std::string& notify_box,
+		const std::string& session_uri) : 
 		mSessionID(session_id),
 		mSessionName(session_name),
 		mCallerID(caller_id),
@@ -275,7 +276,8 @@ public:
 		mType(type),
 		mInvType(inv_type),
 		mSessionHandle(session_handle),
-		mNotifyBox(notify_box)
+		mNotifyBox(notify_box),
+		mSessionURI(session_uri)
 	{};
 
 	LLUUID		mSessionID;
@@ -286,6 +288,7 @@ public:
 	EInvitationType mInvType;
 	std::string	mSessionHandle;
 	std::string	mNotifyBox;
+	std::string	mSessionURI;
 };
 
 
@@ -568,7 +571,8 @@ BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid)
 
 LLUUID LLIMMgr::addP2PSession(const std::string& name,
 							const LLUUID& other_participant_id,
-							const std::string& voice_session_handle)
+							const std::string& voice_session_handle,
+							const std::string& caller_uri)
 {
 	LLUUID session_id = addSession(name, IM_NOTHING_SPECIAL, other_participant_id);
 
@@ -576,7 +580,7 @@ LLUUID LLIMMgr::addP2PSession(const std::string& name,
 	if(floater)
 	{
 		LLVoiceChannelP2P* voice_channelp = (LLVoiceChannelP2P*)floater->getVoiceChannel();
-		voice_channelp->setSessionHandle(voice_session_handle);
+		voice_channelp->setSessionHandle(voice_session_handle, caller_uri);		
 	}
 
 	return session_id;
@@ -699,7 +703,8 @@ void LLIMMgr::inviteToSession(
 	const std::string& caller_name,
 	EInstantMessage type,
 	EInvitationType inv_type,
-	const std::string& session_handle)
+	const std::string& session_handle,
+	const std::string& session_uri)
 {
 	//ignore invites from muted residents
 	if (LLMuteList::getInstance()->isMuted(caller_id))
@@ -741,7 +746,8 @@ void LLIMMgr::inviteToSession(
 		type,
 		inv_type,
 		session_handle,
-		notify_box_type);
+		notify_box_type,
+		session_uri);
 	
 	LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(session_id);
 	if (channelp && channelp->callStarted())
@@ -916,7 +922,8 @@ void LLIMMgr::inviteUserResponse(S32 option, void* user_data)
 				invitep->mSessionID = gIMMgr->addP2PSession(
 					invitep->mSessionName,
 					invitep->mCallerID,
-					invitep->mSessionHandle);
+					invitep->mSessionHandle, 
+					invitep->mSessionURI );
 
 				LLFloaterIMPanel* im_floater =
 					gIMMgr->findFloaterBySession(
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
index d28d00a..6b82c2e 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -97,7 +97,8 @@ public:
 	// Creates a P2P session with the requisite handle for responding to voice calls
 	LLUUID addP2PSession(const std::string& name,
 					  const LLUUID& other_participant_id,
-					  const std::string& voice_session_handle);
+					  const std::string& voice_session_handle,
+					  const std::string& caller_uri = LLStringUtil::null);
 
 	// This removes the panel referenced by the uuid, and then
 	// restores internal consistency. The internal pointer is not
@@ -111,7 +112,8 @@ public:
 		const std::string& caller_name,
 		EInstantMessage type,
 		EInvitationType inv_type, 
-		const std::string& session_handle = LLStringUtil::null);
+		const std::string& session_handle = LLStringUtil::null,
+		const std::string& session_uri = LLStringUtil::null);
 
 	//Updates a given session's session IDs.  Does not open,
 	//create or do anything new.  If the old session doesn't
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index dd4b66c..5f25dc3 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -45,6 +45,10 @@
 # include "audioengine_fmod.h"
 #endif
 
+#ifdef LL_OPENAL
+#include "audioengine_openal.h"
+#endif
+
 #include "llares.h"
 #include "llcachename.h"
 #include "llviewercontrol.h"
@@ -581,10 +585,28 @@ bool idle_startup()
 
 		if (FALSE == gSavedSettings.getBOOL("NoAudio"))
 		{
-#ifdef LL_FMOD
-			gAudiop = (LLAudioEngine *) new LLAudioEngine_FMOD();
-#else
 			gAudiop = NULL;
+
+#ifdef LL_OPENAL
+			if (!gAudiop
+#if !LL_WINDOWS
+			    && NULL == getenv("LL_BAD_OPENAL_DRIVER")
+#endif // !LL_WINDOWS
+			    )
+			{
+				gAudiop = (LLAudioEngine *) new LLAudioEngine_OpenAL();
+			}
+#endif
+
+#ifdef LL_FMOD			
+			if (!gAudiop
+#if !LL_WINDOWS
+			    && NULL == getenv("LL_BAD_FMOD_DRIVER")
+#endif // !LL_WINDOWS
+			    )
+			{
+				gAudiop = (LLAudioEngine *) new LLAudioEngine_FMOD();
+			}
 #endif
 
 			if (gAudiop)
@@ -597,11 +619,16 @@ bool idle_startup()
 				void* window_handle = NULL;
 #endif
 				bool init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle);
-				if(!init)
+				if(init)
+				{
+					gAudiop->setMuted(TRUE);
+				}
+				else
 				{
 					LL_WARNS("AppInit") << "Unable to initialize audio engine" << LL_ENDL;
+					delete gAudiop;
+					gAudiop = NULL;
 				}
-				gAudiop->setMuted(TRUE);
 			}
 		}
 		
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
index 210fca4..df39ee7 100644
--- a/indra/newview/llviewercontrol.cpp
+++ b/indra/newview/llviewercontrol.cpp
@@ -552,7 +552,6 @@ void settings_setup_listeners()
 	gSavedSettings.getControl("PushToTalkButton")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
 	gSavedSettings.getControl("PushToTalkToggle")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
 	gSavedSettings.getControl("VoiceEarLocation")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
-	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("AudioLevelMic")->getSignal()->connect(boost::bind(&handleVoiceClientPrefsChanged, _1));
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index a4438a5..96591a2 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -253,8 +253,6 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot)
 		gViewerWindow->performPick();
 	}
 	
-	gViewerWindow->handlePerFrameHover();
-
 	LLAppViewer::instance()->pingMainloopTimeout("Display:CheckStates");
 	LLGLState::checkStates();
 	LLGLState::checkTextureChannels();
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 3bec16f..145132a 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -62,15 +62,20 @@
 #include "llviewerwindow.h"
 #include "llviewercamera.h"
 
+#include "llfloaterfriends.h"  //VIVOX, inorder to refresh communicate panel
+#include "llfloaterchat.h"		// for LLFloaterChat::addChat()
+
 // for base64 decoding
 #include "apr_base64.h"
 
 // for SHA1 hash
 #include "apr_sha1.h"
 
-// If we are connecting to agni AND the user's last name is "Linden", join this channel instead of looking up the sim name.
-// If we are connecting to agni and the user's last name is NOT "Linden", disable voice.
-#define AGNI_LINDENS_ONLY_CHANNEL "SL"
+// for MD5 hash
+#include "llmd5.h"
+
+#define USE_SESSION_GROUPS 0
+
 static bool sConnectingToAgni = false;
 F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
 
@@ -90,6 +95,44 @@ const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
 const F32 LOGIN_RETRY_SECONDS = 10.0f;
 const int MAX_LOGIN_RETRIES = 12;
 
+static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str)
+{
+	LLMD5 md5_uuid;
+	md5_uuid.update((const unsigned char*)str.data(), str.size());
+	md5_uuid.finalize();
+	md5_uuid.raw_digest(uuid.mData);
+}
+
+static int scale_mic_volume(float volume)
+{
+	// incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.
+	// Map it as follows: 0.0 -> 40, 1.0 -> 44, 2.0 -> 75
+
+	volume -= 1.0f;		// offset volume to the range [-1.0 ... 1.0], with 0 at the default.
+	int scaled_volume = 44;	// offset scaled_volume by its default level
+	if(volume < 0.0f)
+		scaled_volume += ((int)(volume * 4.0f));	// (44 - 40)
+	else
+		scaled_volume += ((int)(volume * 31.0f));	// (75 - 44)
+	
+	return scaled_volume;
+}
+
+static int scale_speaker_volume(float volume)
+{
+	// incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
+	// Map it as follows: 0.0 -> 0, 0.5 -> 62, 1.0 -> 75
+	
+	volume -= 0.5f;		// offset volume to the range [-0.5 ... 0.5], with 0 at the default.
+	int scaled_volume = 62;	// offset scaled_volume by its default level
+	if(volume < 0.0f)
+		scaled_volume += ((int)(volume * 124.0f));	// (62 - 0) * 2
+	else
+		scaled_volume += ((int)(volume * 26.0f));	// (75 - 62) * 2
+	
+	return scaled_volume;
+}
+
 class LLViewerVoiceAccountProvisionResponder :
 	public LLHTTPClient::Responder
 {
@@ -103,12 +146,13 @@ public:
 	{
 		if ( mRetries > 0 )
 		{
+			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying.  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
 			if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision(
 				mRetries - 1);
 		}
 		else
 		{
-			//TODO: throw an error message?
+			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up).  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL;
 			if ( gVoiceClient ) gVoiceClient->giveUp();
 		}
 	}
@@ -117,9 +161,23 @@ public:
 	{
 		if ( gVoiceClient )
 		{
+			std::string voice_sip_uri_hostname;
+			std::string voice_account_server_uri;
+			
+			LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
+			
+			if(content.has("voice_sip_uri_hostname"))
+				voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString();
+			
+			// this key is actually misnamed -- it will be an entire URI, not just a hostname.
+			if(content.has("voice_account_server_name"))
+				voice_account_server_uri = content["voice_account_server_name"].asString();
+			
 			gVoiceClient->login(
 				content["username"].asString(),
-				content["password"].asString());
+				content["password"].asString(),
+				voice_sip_uri_hostname,
+				voice_account_server_uri);
 		}
 	}
 
@@ -166,21 +224,27 @@ protected:
 	int				ignoreDepth;
 
 	// Members for processing responses. The values are transient and only valid within a call to processResponse().
+	bool			squelchDebugOutput;
 	int				returnCode;
 	int				statusCode;
 	std::string		statusString;
-	std::string		uuidString;
+	std::string		requestId;
 	std::string		actionString;
 	std::string		connectorHandle;
+	std::string		versionID;
 	std::string		accountHandle;
 	std::string		sessionHandle;
-	std::string		eventSessionHandle;
+	std::string		sessionGroupHandle;
+	std::string		alias;
+	std::string		applicationString;
 
 	// Members for processing events. The values are transient and only valid within a call to processResponse().
 	std::string		eventTypeString;
 	int				state;
 	std::string		uriString;
 	bool			isChannel;
+	bool			incoming;
+	bool			enabled;
 	std::string		nameString;
 	std::string		audioMediaString;
 	std::string		displayNameString;
@@ -190,6 +254,21 @@ protected:
 	bool			isSpeaking;
 	int				volume;
 	F32				energy;
+	std::string		messageHeader;
+	std::string		messageBody;
+	std::string		notificationType;
+	bool			hasText;
+	bool			hasAudio;
+	bool			hasVideo;
+	bool			terminated;
+	std::string		blockMask;
+	std::string		presenceOnly;
+	std::string		autoAcceptMask;
+	std::string		autoAddAsBuddy;
+	int				numberOfAliases;
+	std::string		subscriptionHandle;
+	std::string		subscriptionType;
+		
 
 	// Members for processing text between tags
 	std::string		textBuffer;
@@ -222,8 +301,6 @@ void LLVivoxProtocolParser::reset()
 	responseDepth = 0;
 	ignoringTags = false;
 	accumulateText = false;
-	textBuffer.clear();
-
 	energy = 0.f;
 	ignoreDepth = 0;
 	isChannel = false;
@@ -232,10 +309,15 @@ void LLVivoxProtocolParser::reset()
 	isModeratorMuted = false;
 	isSpeaking = false;
 	participantType = 0;
-	returnCode = 0;
+	squelchDebugOutput = false;
+	returnCode = -1;
 	state = 0;
 	statusCode = 0;
 	volume = 0;
+	textBuffer.clear();
+	alias.clear();
+	numberOfAliases = 0;
+	applicationString.clear();
 }
 
 //virtual 
@@ -262,33 +344,11 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
 		mInput.append(buf, istr.gcount());
 	}
 	
-	// MBW -- XXX -- This should no longer be necessary.  Or even possible.
-	// We've read all the data out of the buffer.  Make sure it doesn't accumulate.
-//	buffer->clear();
-	
 	// Look for input delimiter(s) in the input buffer.  If one is found, send the message to the xml parser.
 	int start = 0;
 	int delim;
 	while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
 	{	
-		// Turn this on to log incoming XML
-		if(0)
-		{
-			int foo = mInput.find("Set3DPosition", start);
-			int bar = mInput.find("ParticipantPropertiesEvent", start);
-			if(foo != std::string::npos && (foo < delim))
-			{
-				// This is a Set3DPosition response.  Don't print it, since these are way too spammy.
-			}
-			else if(bar != std::string::npos && (bar < delim))
-			{
-				// This is a ParticipantPropertiesEvent response.  Don't print it, since these are way too spammy.
-			}
-			else
-			{
-				LL_INFOS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
-			}
-		}
 		
 		// Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
 		reset();
@@ -299,13 +359,19 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
 		XML_SetUserData(parser, this);	
 		XML_Parse(parser, mInput.data() + start, delim - start, false);
 		
+		// If this message isn't set to be squelched, output the raw XML received.
+		if(!squelchDebugOutput)
+		{
+			LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
+		}
+		
 		start = delim + 3;
 	}
 	
 	if(start != 0)
 		mInput = mInput.substr(start);
 
-	LL_DEBUGS("Voice") << "at end, mInput is: " << mInput << LL_ENDL;
+	LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL;
 	
 	if(!gVoiceClient->mConnected)
 	{
@@ -360,9 +426,9 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
 	
 	if (responseDepth == 0)
 	{	
-		isEvent = strcmp("Event", tag) == 0;
+		isEvent = !stricmp("Event", tag);
 		
-		if (strcmp("Response", tag) == 0 || isEvent)
+		if (!stricmp("Response", tag) || isEvent)
 		{
 			// Grab the attributes
 			while (*attr)
@@ -370,49 +436,62 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
 				const char	*key = *attr++;
 				const char	*value = *attr++;
 				
-				if (strcmp("requestId", key) == 0)
+				if (!stricmp("requestId", key))
 				{
-					uuidString = value;
+					requestId = value;
 				}
-				else if (strcmp("action", key) == 0)
+				else if (!stricmp("action", key))
 				{
 					actionString = value;
 				}
-				else if (strcmp("type", key) == 0)
+				else if (!stricmp("type", key))
 				{
 					eventTypeString = value;
 				}
 			}
 		}
-		LL_DEBUGS("Voice") << tag << " (" << responseDepth << ")"  << LL_ENDL;
+		LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL;
 	}
 	else
 	{
 		if (ignoringTags)
 		{
-			LL_DEBUGS("Voice") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
+			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
 		}
 		else
 		{
-			LL_DEBUGS("Voice") << tag << " (" << responseDepth << ")"  << LL_ENDL;
+			LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL;
 	
 			// Ignore the InputXml stuff so we don't get confused
-			if (strcmp("InputXml", tag) == 0)
+			if (!stricmp("InputXml", tag))
 			{
 				ignoringTags = true;
 				ignoreDepth = responseDepth;
 				accumulateText = false;
 
-				LL_DEBUGS("Voice") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
+				LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
 			}
-			else if (strcmp("CaptureDevices", tag) == 0)
+			else if (!stricmp("CaptureDevices", tag))
 			{
 				gVoiceClient->clearCaptureDevices();
 			}
-			else if (strcmp("RenderDevices", tag) == 0)
+			else if (!stricmp("RenderDevices", tag))
 			{
 				gVoiceClient->clearRenderDevices();
 			}
+			else if (!stricmp("Buddies", tag))
+			{
+				gVoiceClient->deleteAllBuddies();
+			}
+			else if (!stricmp("BlockRules", tag))
+			{
+				gVoiceClient->deleteAllBlockRules();
+			}
+			else if (!stricmp("AutoAcceptRules", tag))
+			{
+				gVoiceClient->deleteAllAutoAcceptRules();
+			}
+			
 		}
 	}
 	responseDepth++;
@@ -431,90 +510,138 @@ void LLVivoxProtocolParser::EndTag(const char *tag)
 	{
 		if (ignoreDepth == responseDepth)
 		{
-			LL_DEBUGS("Voice") << "end of ignore" << LL_ENDL;
+			LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL;
 			ignoringTags = false;
 		}
 		else
 		{
-			LL_DEBUGS("Voice") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
+			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
 		}
 	}
 	
 	if (!ignoringTags)
 	{
-		LL_DEBUGS("Voice") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
+		LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
 
 		// Closing a tag. Finalize the text we've accumulated and reset
-		if (strcmp("ReturnCode", tag) == 0)
+		if (!stricmp("ReturnCode", tag))
 			returnCode = strtol(string.c_str(), NULL, 10);
-		else if (strcmp("StatusCode", tag) == 0)
+		else if (!stricmp("SessionHandle", tag))
+			sessionHandle = string;
+		else if (!stricmp("SessionGroupHandle", tag))
+			sessionGroupHandle = string;
+		else if (!stricmp("StatusCode", tag))
 			statusCode = strtol(string.c_str(), NULL, 10);
-		else if (strcmp("ConnectorHandle", tag) == 0)
+		else if (!stricmp("StatusString", tag))
+			statusString = string;
+		else if (!stricmp("ParticipantURI", tag))
+			uriString = string;
+		else if (!stricmp("Volume", tag))
+			volume = strtol(string.c_str(), NULL, 10);
+		else if (!stricmp("Energy", tag))
+			energy = (F32)strtod(string.c_str(), NULL);
+		else if (!stricmp("IsModeratorMuted", tag))
+			isModeratorMuted = !stricmp(string.c_str(), "true");
+		else if (!stricmp("IsSpeaking", tag))
+			isSpeaking = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Alias", tag))
+			alias = string;
+		else if (!stricmp("NumberOfAliases", tag))
+			numberOfAliases = strtol(string.c_str(), NULL, 10);
+		else if (!stricmp("Application", tag))
+			applicationString = string;
+		else if (!stricmp("ConnectorHandle", tag))
 			connectorHandle = string;
-		else if (strcmp("AccountHandle", tag) == 0)
+		else if (!stricmp("VersionID", tag))
+			versionID = string;
+		else if (!stricmp("AccountHandle", tag))
 			accountHandle = string;
-		else if (strcmp("SessionHandle", tag) == 0)
-		{
-			if (isEvent)
-				eventSessionHandle = string;
-			else
-				sessionHandle = string;
-		}
-		else if (strcmp("StatusString", tag) == 0)
-			statusString = string;
-		else if (strcmp("State", tag) == 0)
+		else if (!stricmp("State", tag))
 			state = strtol(string.c_str(), NULL, 10);
-		else if (strcmp("URI", tag) == 0)
+		else if (!stricmp("URI", tag))
 			uriString = string;
-		else if (strcmp("IsChannel", tag) == 0)
-			isChannel = string == "true" ? true : false;
-		else if (strcmp("Name", tag) == 0)
+		else if (!stricmp("IsChannel", tag))
+			isChannel = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Incoming", tag))
+			incoming = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Enabled", tag))
+			enabled = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Name", tag))
 			nameString = string;
-		else if (strcmp("AudioMedia", tag) == 0)
+		else if (!stricmp("AudioMedia", tag))
 			audioMediaString = string;
-		else if (strcmp("ChannelName", tag) == 0)
+		else if (!stricmp("ChannelName", tag))
 			nameString = string;
-		else if (strcmp("ParticipantURI", tag) == 0)
-			uriString = string;
-		else if (strcmp("DisplayName", tag) == 0)
+		else if (!stricmp("DisplayName", tag))
 			displayNameString = string;
-		else if (strcmp("AccountName", tag) == 0)
+		else if (!stricmp("AccountName", tag))
 			nameString = string;
-		else if (strcmp("ParticipantTyppe", tag) == 0)
+		else if (!stricmp("ParticipantType", tag))
 			participantType = strtol(string.c_str(), NULL, 10);
-		else if (strcmp("IsLocallyMuted", tag) == 0)
-			isLocallyMuted = string == "true" ? true : false;
-		else if (strcmp("IsModeratorMuted", tag) == 0)
-			isModeratorMuted = string == "true" ? true : false;
-		else if (strcmp("IsSpeaking", tag) == 0)
-			isSpeaking = string == "true" ? true : false;
-		else if (strcmp("Volume", tag) == 0)
-			volume = strtol(string.c_str(), NULL, 10);
-		else if (strcmp("Energy", tag) == 0)
-			energy = (F32)strtod(string.c_str(), NULL);
-		else if (strcmp("MicEnergy", tag) == 0)
+		else if (!stricmp("IsLocallyMuted", tag))
+			isLocallyMuted = !stricmp(string.c_str(), "true");
+		else if (!stricmp("MicEnergy", tag))
 			energy = (F32)strtod(string.c_str(), NULL);
-		else if (strcmp("ChannelName", tag) == 0)
+		else if (!stricmp("ChannelName", tag))
 			nameString = string;
-		else if (strcmp("ChannelURI", tag) == 0)
+		else if (!stricmp("ChannelURI", tag))
 			uriString = string;
-		else if (strcmp("ChannelListResult", tag) == 0)
-		{
-			gVoiceClient->addChannelMapEntry(nameString, uriString);
-		}
-		else if (strcmp("Device", tag) == 0)
+		else if (!stricmp("BuddyURI", tag))
+			uriString = string;
+		else if (!stricmp("Presence", tag))
+			statusString = string;
+		else if (!stricmp("Device", tag))
 		{
 			// This closing tag shouldn't clear the accumulated text.
 			clearbuffer = false;
 		}
-		else if (strcmp("CaptureDevice", tag) == 0)
+		else if (!stricmp("CaptureDevice", tag))
 		{
 			gVoiceClient->addCaptureDevice(textBuffer);
 		}
-		else if (strcmp("RenderDevice", tag) == 0)
+		else if (!stricmp("RenderDevice", tag))
 		{
 			gVoiceClient->addRenderDevice(textBuffer);
 		}
+		else if (!stricmp("Buddy", tag))
+		{
+			gVoiceClient->processBuddyListEntry(uriString, displayNameString);
+		}
+		else if (!stricmp("BlockRule", tag))
+		{
+			gVoiceClient->addBlockRule(blockMask, presenceOnly);
+		}
+		else if (!stricmp("BlockMask", tag))
+			blockMask = string;
+		else if (!stricmp("PresenceOnly", tag))
+			presenceOnly = string;
+		else if (!stricmp("AutoAcceptRule", tag))
+		{
+			gVoiceClient->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy);
+		}
+		else if (!stricmp("AutoAcceptMask", tag))
+			autoAcceptMask = string;
+		else if (!stricmp("AutoAddAsBuddy", tag))
+			autoAddAsBuddy = string;
+		else if (!stricmp("MessageHeader", tag))
+			messageHeader = string;
+		else if (!stricmp("MessageBody", tag))
+			messageBody = string;
+		else if (!stricmp("NotificationType", tag))
+			notificationType = string;
+		else if (!stricmp("HasText", tag))
+			hasText = !stricmp(string.c_str(), "true");
+		else if (!stricmp("HasAudio", tag))
+			hasAudio = !stricmp(string.c_str(), "true");
+		else if (!stricmp("HasVideo", tag))
+			hasVideo = !stricmp(string.c_str(), "true");
+		else if (!stricmp("Terminated", tag))
+			terminated = !stricmp(string.c_str(), "true");
+		else if (!stricmp("SubscriptionHandle", tag))
+			subscriptionHandle = string;
+		else if (!stricmp("SubscriptionType", tag))
+			subscriptionType = string;
+		
 
 		if(clearbuffer)
 		{
@@ -549,144 +676,296 @@ void LLVivoxProtocolParser::CharData(const char *buffer, int length)
 
 void LLVivoxProtocolParser::processResponse(std::string tag)
 {
-	LL_DEBUGS("Voice") << tag << LL_ENDL;
+	LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL;
 
+	// SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success.  This is a change vs. previous SDKs.
+	// According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned",
+	// so I believe this will give correct behavior.
+	
+	if(returnCode == 0)
+		statusCode = 0;
+		
 	if (isEvent)
 	{
-		if (eventTypeString == "LoginStateChangeEvent")
+		const char *eventTypeCstr = eventTypeString.c_str();
+		if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent"))
 		{
-			gVoiceClient->loginStateChangeEvent(accountHandle, statusCode, statusString, state);
+			gVoiceClient->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state);
 		}
-		else if (eventTypeString == "SessionNewEvent")
+		else if (!stricmp(eventTypeCstr, "SessionAddedEvent"))
 		{
-			gVoiceClient->sessionNewEvent(accountHandle, eventSessionHandle, state, nameString, uriString);
+			/*
+			<Event type="SessionAddedEvent">
+				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
+				<Uri>sip:confctl-1408789 at bhr.vivox.com</Uri>
+				<IsChannel>true</IsChannel>
+				<Incoming>false</Incoming>
+				<ChannelName />
+			</Event>
+			*/
+			gVoiceClient->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString);
 		}
-		else if (eventTypeString == "SessionStateChangeEvent")
+		else if (!stricmp(eventTypeCstr, "SessionRemovedEvent"))
 		{
-			gVoiceClient->sessionStateChangeEvent(uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString);
+			gVoiceClient->sessionRemovedEvent(sessionHandle, sessionGroupHandle);
 		}
-		else if (eventTypeString == "ParticipantStateChangeEvent")
+		else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent"))
 		{
-			gVoiceClient->participantStateChangeEvent(uriString, statusCode, statusString, state,  nameString, displayNameString, participantType);
-			
+			gVoiceClient->sessionGroupAddedEvent(sessionGroupHandle);
+		}
+		else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent"))
+		{
+			/*
+			<Event type="MediaStreamUpdatedEvent">
+				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
+				<StatusCode>200</StatusCode>
+				<StatusString>OK</StatusString>
+				<State>2</State>
+				<Incoming>false</Incoming>
+			</Event>
+			*/
+			gVoiceClient->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming);
+		}		
+		else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent"))
+		{
+			/*
+			<Event type="TextStreamUpdatedEvent">
+				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle>
+				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle>
+				<Enabled>true</Enabled>
+				<State>1</State>
+				<Incoming>true</Incoming>
+			</Event>
+			*/
+			gVoiceClient->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming);
+		}
+		else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent"))
+		{
+			/* 
+			<Event type="ParticipantAddedEvent">
+				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
+				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
+				<ParticipantUri>sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com</ParticipantUri>
+				<AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName>
+				<DisplayName />
+				<ParticipantType>0</ParticipantType>
+			</Event>
+			*/
+			gVoiceClient->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType);
 		}
-		else if (eventTypeString == "ParticipantPropertiesEvent")
+		else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent"))
 		{
-			gVoiceClient->participantPropertiesEvent(uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy);
+			/*
+			<Event type="ParticipantRemovedEvent">
+				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle>
+				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle>
+				<ParticipantUri>sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com</ParticipantUri>
+				<AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName>
+			</Event>
+			*/
+			gVoiceClient->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString);
+		}
+		else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent"))
+		{
+			/*
+			<Event type="ParticipantUpdatedEvent">
+				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
+				<ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri>
+				<IsModeratorMuted>false</IsModeratorMuted>
+				<IsSpeaking>true</IsSpeaking>
+				<Volume>44</Volume>
+				<Energy>0.0879437</Energy>
+			</Event>
+			*/
+			
+			// These happen so often that logging them is pretty useless.
+			squelchDebugOutput = true;
+			
+			gVoiceClient->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy);
 		}
-		else if (eventTypeString == "AuxAudioPropertiesEvent")
+		else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent"))
 		{
 			gVoiceClient->auxAudioPropertiesEvent(energy);
 		}
+		else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent"))
+		{
+			gVoiceClient->buddyPresenceEvent(uriString, alias, statusString, applicationString);
+		}
+		else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent"))
+		{
+			// The buddy list was updated during parsing.
+			// Need to recheck against the friends list.
+			gVoiceClient->buddyListChanged();
+		}
+		else if (!stricmp(eventTypeCstr, "BuddyChangedEvent"))
+		{
+			/*
+			<Event type="BuddyChangedEvent">
+				<AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle>
+				<BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI>
+				<DisplayName>Monroe Tester</DisplayName>
+				<BuddyData />
+				<GroupID>0</GroupID>
+				<ChangeType>Set</ChangeType>
+			</Event>
+			*/		
+			// TODO: Question: Do we need to process this at all?
+		}
+		else if (!stricmp(eventTypeCstr, "MessageEvent"))  
+		{
+			gVoiceClient->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString);
+		}
+		else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))  
+		{
+			gVoiceClient->sessionNotificationEvent(sessionHandle, uriString, notificationType);
+		}
+		else if (!stricmp(eventTypeCstr, "SubscriptionEvent"))  
+		{
+			gVoiceClient->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType);
+		}
+		else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))  
+		{
+			/*
+			<Event type="SessionUpdatedEvent">
+				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle>
+				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle>
+				<Uri>sip:confctl-9 at bhd.vivox.com</Uri>
+				<IsMuted>0</IsMuted>
+				<Volume>50</Volume>
+				<TransmitEnabled>1</TransmitEnabled>
+				<IsFocused>0</IsFocused>
+				<SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition>
+				<SessionFontID>0</SessionFontID>
+			</Event>
+			*/
+			// We don't need to process this, but we also shouldn't warn on it, since that confuses people.
+		}
+		else
+		{
+			LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL;
+		}
 	}
 	else
 	{
-		if (actionString == "Connector.Create.1")
+		const char *actionCstr = actionString.c_str();
+		if (!stricmp(actionCstr, "Connector.Create.1"))
 		{
-			gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle);
+			gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID);
 		}
-		else if (actionString == "Account.Login.1")
+		else if (!stricmp(actionCstr, "Account.Login.1"))
 		{
-			gVoiceClient->loginResponse(statusCode, statusString, accountHandle);
+			gVoiceClient->loginResponse(statusCode, statusString, accountHandle, numberOfAliases);
 		}
-		else if (actionString == "Session.Create.1")
+		else if (!stricmp(actionCstr, "Session.Create.1"))
 		{
-			gVoiceClient->sessionCreateResponse(statusCode, statusString, sessionHandle);			
+			gVoiceClient->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);			
 		}
-		else if (actionString == "Session.Connect.1")
+		else if (!stricmp(actionCstr, "SessionGroup.AddSession.1"))
 		{
-			gVoiceClient->sessionConnectResponse(statusCode, statusString);			
+			gVoiceClient->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);			
 		}
-		else if (actionString == "Session.Terminate.1")
+		else if (!stricmp(actionCstr, "Session.Connect.1"))
 		{
-			gVoiceClient->sessionTerminateResponse(statusCode, statusString);			
+			gVoiceClient->sessionConnectResponse(requestId, statusCode, statusString);			
 		}
-		else if (actionString == "Account.Logout.1")
+		else if (!stricmp(actionCstr, "Account.Logout.1"))
 		{
 			gVoiceClient->logoutResponse(statusCode, statusString);			
 		}
-		else if (actionString == "Connector.InitiateShutdown.1")
+		else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1"))
 		{
 			gVoiceClient->connectorShutdownResponse(statusCode, statusString);			
 		}
-		else if (actionString == "Account.ChannelGetList.1")
+		else if (!stricmp(actionCstr, "Account.ListBlockRules.1"))
 		{
-			gVoiceClient->channelGetListResponse(statusCode, statusString);
+			gVoiceClient->accountListBlockRulesResponse(statusCode, statusString);						
+		}
+		else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1"))
+		{
+			gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString);						
+		}
+		else if (!stricmp(actionCstr, "Session.Set3DPosition.1"))
+		{
+			// We don't need to process these, but they're so spammy we don't want to log them.
+			squelchDebugOutput = true;
 		}
 /*
-		else if (actionString == "Connector.AccountCreate.1")
+		else if (!stricmp(actionCstr, "Account.ChannelGetList.1"))
 		{
-			
+			gVoiceClient->channelGetListResponse(statusCode, statusString);
 		}
-		else if (actionString == "Connector.MuteLocalMic.1")
+		else if (!stricmp(actionCstr, "Connector.AccountCreate.1"))
 		{
 			
 		}
-		else if (actionString == "Connector.MuteLocalSpeaker.1")
+		else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1"))
 		{
 			
 		}
-		else if (actionString == "Connector.SetLocalMicVolume.1")
+		else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1"))
 		{
 			
 		}
-		else if (actionString == "Connector.SetLocalSpeakerVolume.1")
+		else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1"))
 		{
 			
 		}
-		else if (actionString == "Session.ListenerSetPosition.1")
+		else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1"))
 		{
 			
 		}
-		else if (actionString == "Session.SpeakerSetPosition.1")
+		else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1"))
 		{
 			
 		}
-		else if (actionString == "Session.Set3DPosition.1")
+		else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1"))
 		{
 			
 		}
-		else if (actionString == "Session.AudioSourceSetPosition.1")
+		else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1"))
 		{
 			
 		}
-		else if (actionString == "Session.GetChannelParticipants.1")
+		else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelCreate.1")
+		else if (!stricmp(actionCstr, "Account.ChannelCreate.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelUpdate.1")
+		else if (!stricmp(actionCstr, "Account.ChannelUpdate.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelDelete.1")
+		else if (!stricmp(actionCstr, "Account.ChannelDelete.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelCreateAndInvite.1")
+		else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelFolderCreate.1")
+		else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelFolderUpdate.1")
+		else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelFolderDelete.1")
+		else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelAddModerator.1")
+		else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1"))
 		{
 			
 		}
-		else if (actionString == "Account.ChannelDeleteModerator.1")
+		else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1"))
 		{
 			
 		}
@@ -700,9 +979,18 @@ class LLVoiceClientMuteListObserver : public LLMuteListObserver
 {
 	/* virtual */ void onChange()  { gVoiceClient->muteListChanged();}
 };
+
+class LLVoiceClientFriendsObserver : public LLFriendObserver
+{
+public:
+	/* virtual */ void changed(U32 mask) { gVoiceClient->updateFriends(mask);}
+};
+
 static LLVoiceClientMuteListObserver mutelist_listener;
 static bool sMuteListListener_listening = false;
 
+static LLVoiceClientFriendsObserver *friendslist_listener = NULL;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 class LLVoiceClientCapResponder : public LLHTTPClient::Responder
@@ -726,11 +1014,8 @@ void LLVoiceClientCapResponder::error(U32 status, const std::string& reason)
 void LLVoiceClientCapResponder::result(const LLSD& content)
 {
 	LLSD::map_const_iterator iter;
-	for(iter = content.beginMap(); iter != content.endMap(); ++iter)
-	{
-		LL_DEBUGS("Voice") << "LLVoiceClientCapResponder::result got " 
-			<< iter->first << LL_ENDL;
-	}
+	
+	LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL;
 
 	if ( content.has("voice_credentials") )
 	{
@@ -816,26 +1101,26 @@ LLVoiceClient::LLVoiceClient()
 	mUserPTTState = false;
 	mMuteMic = false;
 	mSessionTerminateRequested = false;
+	mRelogRequested = false;
 	mCommandCookie = 0;
-	mNonSpatialChannel = false;
-	mNextSessionSpatial = true;
-	mNextSessionNoReconnect = false;
-	mSessionP2P = false;
 	mCurrentParcelLocalID = 0;
 	mLoginRetryCount = 0;
-	mVivoxErrorStatusCode = 0;
 
-	mNextSessionResetOnClose = false;
-	mSessionResetOnClose = false;
 	mSpeakerVolume = 0;
 	mMicVolume = 0;
 
+	mAudioSession = NULL;
+	mAudioSessionChanged = false;
+
 	// Initial dirty state
 	mSpatialCoordsDirty = false;
 	mPTTDirty = true;
-	mVolumeDirty = true;
+	mFriendsListDirty = true;
 	mSpeakerVolumeDirty = true;
 	mMicVolumeDirty = true;
+	mBuddyListMapPopulated = false;
+	mBlockRulesListReceived = false;
+	mAutoAcceptRulesListReceived = false;
 	mCaptureDeviceDirty = false;
 	mRenderDeviceDirty = false;
 
@@ -856,14 +1141,12 @@ LLVoiceClient::LLVoiceClient()
 	//  gMuteListp isn't set up at this point, so we defer this until later.
 //	gMuteListp->addObserver(&mutelist_listener);
 	
-	mParticipantMapChanged = false;
-
 	// stash the pump for later use
 	// This now happens when init() is called instead.
 	mPump = NULL;
 	
 #if LL_DARWIN || LL_LINUX
-		// MBW -- XXX -- THIS DOES NOT BELONG HERE
+		// HACK: THIS DOES NOT BELONG HERE
 		// When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
 		// This should cause us to ignore SIGPIPE and handle the error through proper channels.
 		// This should really be set up elsewhere.  Where should it go?
@@ -899,8 +1182,10 @@ void LLVoiceClient::terminate()
 {
 	if(gVoiceClient)
 	{
-		gVoiceClient->sessionTerminateSendMessage();
+//		gVoiceClient->leaveAudioSession();
 		gVoiceClient->logout();
+		// As of SDK version 4885, this should no longer be necessary.  It will linger after the socket close if it needs to.
+		// ms_sleep(2000);
 		gVoiceClient->connectorShutdown();
 		gVoiceClient->closeSocket();		// Need to do this now -- bad things happen if the destructor does it later.
 		
@@ -925,8 +1210,6 @@ void LLVoiceClient::updateSettings()
 	setPTTKey(keyString);
 	setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle"));
 	setEarLocation(gSavedSettings.getS32("VoiceEarLocation"));
-	std::string serverName = gSavedSettings.getString("VivoxDebugServerName");
-	setVivoxDebugServerName(serverName);
 
 	std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice");
 	setCaptureDevice(inputDevice);
@@ -949,9 +1232,10 @@ bool LLVoiceClient::writeString(const std::string &str)
 		apr_size_t size = (apr_size_t)str.size();
 		apr_size_t written = size;
 	
-		LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
+		//MARK: Turn this on to log outgoing XML
+//		LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
 
-		// MBW -- XXX -- check return code - sockets will fail (broken, etc.)
+		// check return code - sockets will fail (broken, etc.)
 		err = apr_socket_send(
 				mSocket->getSocket(),
 				(const char*)str.data(),
@@ -962,7 +1246,7 @@ bool LLVoiceClient::writeString(const std::string &str)
 			// Success.
 			result = true;
 		}
-		// MBW -- XXX -- handle partial writes (written is number of bytes written)
+		// TODO: handle partial writes (written is number of bytes written)
 		// Need to set socket to non-blocking before this will work.
 //		else if(APR_STATUS_IS_EAGAIN(err))
 //		{
@@ -986,7 +1270,7 @@ bool LLVoiceClient::writeString(const std::string &str)
 void LLVoiceClient::connectorCreate()
 {
 	std::ostringstream stream;
-	std::string logpath;
+	std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
 	std::string loglevel = "0";
 	
 	// Transition to stateConnectorStarted when the connector handle comes back.
@@ -998,20 +1282,20 @@ void LLVoiceClient::connectorCreate()
 	{
 		LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL;
 		loglevel = "10";
-		logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
 	}
 	
 	stream 
 	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
 		<< "<ClientName>V2 SDK</ClientName>"
-		<< "<AccountManagementServer>" << mAccountServerURI << "</AccountManagementServer>"
+		<< "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>"
+		<< "<Mode>Normal</Mode>"
 		<< "<Logging>"
-			<< "<Enabled>false</Enabled>"
 			<< "<Folder>" << logpath << "</Folder>"
 			<< "<FileNamePrefix>Connector</FileNamePrefix>"
 			<< "<FileNameSuffix>.log</FileNameSuffix>"
 			<< "<LogLevel>" << loglevel << "</LogLevel>"
 		<< "</Logging>"
+		<< "<Application>SecondLifeViewer.1</Application>"
 	<< "</Request>\n\n\n";
 	
 	writeString(stream.str());
@@ -1049,20 +1333,6 @@ void LLVoiceClient::userAuthorized(const std::string& firstName, const std::stri
 
 	sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid();
 
-	// MBW -- XXX -- Enable this when the bhd.vivox.com server gets a real ssl cert.	
-	if(sConnectingToAgni)
-	{
-		// Use the release account server
-		mAccountServerName = "bhr.vivox.com";
-		mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
-	}
-	else
-	{
-		// Use the development account server
-		mAccountServerName = gSavedSettings.getString("VivoxDebugServerName");
-		mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
-	}
-
 	mAccountName = nameFromID(agentID);
 }
 
@@ -1084,24 +1354,66 @@ void LLVoiceClient::requestVoiceAccountProvision(S32 retries)
 }
 
 void LLVoiceClient::login(
-	const std::string& accountName,
-	const std::string &password)
+	const std::string& account_name,
+	const std::string& password,
+	const std::string& voice_sip_uri_hostname,
+	const std::string& voice_account_server_uri)
 {
+	mVoiceSIPURIHostName = voice_sip_uri_hostname;
+	mVoiceAccountServerURI = voice_account_server_uri;
+
 	if((getState() >= stateLoggingIn) && (getState() < stateLoggedOut))
 	{
 		// Already logged in.  This is an internal error.
 		LL_ERRS("Voice") << "Can't login again. Called from wrong state." << LL_ENDL;
 	}
-	else if ( accountName != mAccountName )
+	else if ( account_name != mAccountName )
 	{
 		//TODO: error?
-		LL_WARNS("Voice") << "Wrong account name! " << accountName
+		LL_WARNS("Voice") << "Wrong account name! " << account_name
 				<< " instead of " << mAccountName << LL_ENDL;
 	}
 	else
 	{
 		mAccountPassword = password;
 	}
+
+	std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName");
+	
+	if( !debugSIPURIHostName.empty() )
+	{
+		mVoiceSIPURIHostName = debugSIPURIHostName;
+	}
+	
+	if( mVoiceSIPURIHostName.empty() )
+	{
+		// we have an empty account server name
+		// so we fall back to hardcoded defaults
+
+		if(sConnectingToAgni)
+		{
+			// Use the release account server
+			mVoiceSIPURIHostName = "bhr.vivox.com";
+		}
+		else
+		{
+			// Use the development account server
+			mVoiceSIPURIHostName = "bhd.vivox.com";
+		}
+	}
+	
+	std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI");
+
+	if( !debugAccountServerURI.empty() )
+	{
+		mVoiceAccountServerURI = debugAccountServerURI;
+	}
+	
+	if( mVoiceAccountServerURI.empty() )
+	{
+		// If the account server URI isn't specified, construct it from the SIP URI hostname
+		mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";		
+	}
 }
 
 void LLVoiceClient::idle(void* user_data)
@@ -1124,6 +1436,7 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
 		CASE(stateDaemonLaunched);
 		CASE(stateConnecting);
 		CASE(stateIdle);
+		CASE(stateNeedsProvision);
 		CASE(stateConnectorStart);
 		CASE(stateConnectorStarting);
 		CASE(stateConnectorStarted);
@@ -1132,12 +1445,11 @@ std::string LLVoiceClient::state2string(LLVoiceClient::state inState)
 		CASE(stateNeedsLogin);
 		CASE(stateLoggingIn);
 		CASE(stateLoggedIn);
+		CASE(stateCreatingSessionGroup);
 		CASE(stateNoChannel);
 		CASE(stateMicTuningStart);
 		CASE(stateMicTuningRunning);
 		CASE(stateMicTuningStop);
-		CASE(stateSessionCreate);
-		CASE(stateSessionConnect);
 		CASE(stateJoiningSession);
 		CASE(stateSessionJoined);
 		CASE(stateRunning);
@@ -1176,6 +1488,7 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv
 		CASE(STATUS_JOINING);
 		CASE(STATUS_JOINED);
 		CASE(STATUS_LEFT_CHANNEL);
+		CASE(STATUS_VOICE_DISABLED);
 		CASE(BEGIN_ERROR_STATUS);
 		CASE(ERROR_CHANNEL_FULL);
 		CASE(ERROR_CHANNEL_LOCKED);
@@ -1221,12 +1534,15 @@ void LLVoiceClient::stateMachine()
 				killGateway();
 			}
 			
-			sessionTerminateSendMessage();
+//			leaveAudioSession();
 			logout();
+			// As of SDK version 4885, this should no longer be necessary.  It will linger after the socket close if it needs to.
+			// ms_sleep(2000);
 			connectorShutdown();
 			closeSocket();
-			removeAllParticipants();
-
+			deleteAllSessions();
+			deleteAllBuddies();
+			
 			setState(stateDisabled);
 		}
 	}
@@ -1242,7 +1558,7 @@ void LLVoiceClient::stateMachine()
 			std::string regionName = region->getName();
 			std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
 		
-			LL_DEBUGS("Voice") << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << LL_ENDL;
+//			LL_DEBUGS("Voice") << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << LL_ENDL;
 
 			// The region name starts out empty and gets filled in later.  
 			// Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
@@ -1263,6 +1579,7 @@ void LLVoiceClient::stateMachine()
 
 	switch(getState())
 	{
+		//MARK: stateDisabled
 		case stateDisabled:
 			if(mVoiceEnabled && (!mAccountName.empty() || mTuningMode))
 			{
@@ -1270,6 +1587,7 @@ void LLVoiceClient::stateMachine()
 			}
 		break;
 		
+		//MARK: stateStart
 		case stateStart:
 			if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
 			{
@@ -1300,7 +1618,9 @@ void LLVoiceClient::stateMachine()
 					if(!LLFile::stat(exe_path, &s))
 					{
 						// vivox executable exists.  Build the command line and launch the daemon.
-						std::string args = " -p tcp -h -c";
+						// SLIM SDK: these arguments are no longer necessary.
+//						std::string args = " -p tcp -h -c";
+						std::string args;
 						std::string cmd;
 						std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
 						
@@ -1385,14 +1705,15 @@ void LLVoiceClient::stateMachine()
 					}	
 					else
 					{
-						LL_INFOS("Voice") << exe_path << "not found." << LL_ENDL;
+						LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL;
 					}	
 				}
 				else
 				{		
+					// SLIM SDK: port changed from 44124 to 44125.
 					// We can connect to a client gateway running on another host.  This is useful for testing.
 					// To do this, launch the gateway on a nearby host like this:
-					//  vivox-gw.exe -p tcp -i 0.0.0.0:44124
+					//  vivox-gw.exe -p tcp -i 0.0.0.0:44125
 					// and put that host's IP address here.
 					mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort"));
 				}
@@ -1404,17 +1725,23 @@ void LLVoiceClient::stateMachine()
 				
 				// Dirty the states we'll need to sync with the daemon when it comes up.
 				mPTTDirty = true;
+				mMicVolumeDirty = true;
 				mSpeakerVolumeDirty = true;
+				mSpeakerMuteDirty = true;
 				// These only need to be set if they're not default (i.e. empty string).
 				mCaptureDeviceDirty = !mCaptureDevice.empty();
 				mRenderDeviceDirty = !mRenderDevice.empty();
+				
+				mMainSessionGroupHandle.clear();
 			}
 		break;
-		
+
+		//MARK: stateDaemonLaunched
 		case stateDaemonLaunched:
-			LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
 			if(mUpdateTimer.hasExpired())
 			{
+				LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
+
 				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
 
 				if(!mSocket)
@@ -1435,6 +1762,7 @@ void LLVoiceClient::stateMachine()
 			}
 		break;
 
+		//MARK: stateConnecting
 		case stateConnecting:
 		// Can't do this until we have the pump available.
 		if(mPump)
@@ -1457,6 +1785,7 @@ void LLVoiceClient::stateMachine()
 
 		break;
 		
+		//MARK: stateIdle
 		case stateIdle:
 			// Initial devices query
 			getCaptureDevicesSendMessage();
@@ -1464,17 +1793,48 @@ void LLVoiceClient::stateMachine()
 
 			mLoginRetryCount = 0;
 			
-			setState(stateConnectorStart);
+			setState(stateNeedsProvision);
 				
 		break;
 		
+		//MARK: stateNeedsProvision
+		case stateNeedsProvision:
+			if(!mVoiceEnabled)
+			{
+				// We were never logged in.  This will shut down the connector.
+				setState(stateLoggedOut);
+			}
+			else if(!mAccountName.empty())
+			{
+				LLViewerRegion *region = gAgent.getRegion();
+				
+				if(region)
+				{
+					if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
+					{
+						if ( mAccountPassword.empty() )
+						{
+							requestVoiceAccountProvision();
+						}
+						setState(stateConnectorStart);
+					}
+				}
+			}
+			else if(mTuningMode)
+			{
+				mTuningExitState = stateNeedsProvision;
+				setState(stateMicTuningStart);
+			}
+		break;
+		
+		//MARK: stateConnectorStart
 		case stateConnectorStart:
 			if(!mVoiceEnabled)
 			{
 				// We were never logged in.  This will shut down the connector.
 				setState(stateLoggedOut);
 			}
-			else if(!mAccountServerURI.empty())
+			else if(!mVoiceAccountServerURI.empty())
 			{
 				connectorCreate();
 			}
@@ -1485,34 +1845,26 @@ void LLVoiceClient::stateMachine()
 			}
 		break;
 		
+		//MARK: stateConnectorStarting
 		case stateConnectorStarting:	// waiting for connector handle
 			// connectorCreateResponse() will transition from here to stateConnectorStarted.
 		break;
 		
+		//MARK: stateConnectorStarted
 		case stateConnectorStarted:		// connector handle received
 			if(!mVoiceEnabled)
 			{
 				// We were never logged in.  This will shut down the connector.
 				setState(stateLoggedOut);
 			}
-			else if(!mAccountName.empty())
+			else
 			{
-				LLViewerRegion *region = gAgent.getRegion();
-				
-				if(region)
-				{
-					if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
-					{
-						if ( mAccountPassword.empty() )
-						{
-							requestVoiceAccountProvision();
-						}
-						setState(stateNeedsLogin);
-					}
-				}
+				// The connector is started.  Send a login message.
+				setState(stateNeedsLogin);
 			}
 		break;
 				
+		//MARK: stateMicTuningStart
 		case stateMicTuningStart:
 			if(mUpdateTimer.hasExpired())
 			{
@@ -1520,19 +1872,9 @@ void LLVoiceClient::stateMachine()
 				{
 					// These can't be changed while in tuning mode.  Set them before starting.
 					std::ostringstream stream;
-
-					if(mCaptureDeviceDirty)
-					{
-						buildSetCaptureDevice(stream);
-					}
-
-					if(mRenderDeviceDirty)
-					{
-						buildSetRenderDevice(stream);
-					}
-
-					mCaptureDeviceDirty = false;
-					mRenderDeviceDirty = false;
+					
+					buildSetCaptureDevice(stream);
+					buildSetRenderDevice(stream);
 
 					if(!stream.str().empty())
 					{
@@ -1554,6 +1896,7 @@ void LLVoiceClient::stateMachine()
 			
 		break;
 		
+		//MARK: stateMicTuningRunning
 		case stateMicTuningRunning:
 			if(!mTuningMode || !mVoiceEnabled || mSessionTerminateRequested || mCaptureDeviceDirty || mRenderDeviceDirty)
 			{
@@ -1595,6 +1938,7 @@ void LLVoiceClient::stateMachine()
 			}
 		break;
 		
+		//MARK: stateMicTuningStop
 		case stateMicTuningStop:
 		{
 			// transition out of mic tuning
@@ -1609,6 +1953,7 @@ void LLVoiceClient::stateMachine()
 		}
 		break;
 								
+		//MARK: stateLoginRetry
 		case stateLoginRetry:
 			if(mLoginRetryCount == 0)
 			{
@@ -1632,6 +1977,7 @@ void LLVoiceClient::stateMachine()
 			}
 		break;
 		
+		//MARK: stateLoginRetryWait
 		case stateLoginRetryWait:
 			if(mUpdateTimer.hasExpired())
 			{
@@ -1639,6 +1985,7 @@ void LLVoiceClient::stateMachine()
 			}
 		break;
 		
+		//MARK: stateNeedsLogin
 		case stateNeedsLogin:
 			if(!mAccountPassword.empty())
 			{
@@ -1647,16 +1994,22 @@ void LLVoiceClient::stateMachine()
 			}		
 		break;
 		
+		//MARK: stateLoggingIn
 		case stateLoggingIn:			// waiting for account handle
 			// loginResponse() will transition from here to stateLoggedIn.
 		break;
 		
+		//MARK: stateLoggedIn
 		case stateLoggedIn:				// account handle received
-			// Initial kick-off of channel lookup logic
-			parcelChanged();
 
 			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
 
+			// request the current set of block rules (we'll need them when updating the friends list)
+			accountListBlockRulesSendMessage();
+			
+			// request the current set of auto-accept rules
+			accountListAutoAcceptRulesSendMessage();
+			
 			// Set up the mute list observer if it hasn't been set up already.
 			if((!sMuteListListener_listening))
 			{
@@ -1664,13 +2017,67 @@ void LLVoiceClient::stateMachine()
 				sMuteListListener_listening = true;
 			}
 
+			// Set up the friends list observer if it hasn't been set up already.
+			if(friendslist_listener == NULL)
+			{
+				friendslist_listener = new LLVoiceClientFriendsObserver;
+				LLAvatarTracker::instance().addObserver(friendslist_listener);
+			}
+			
+			// Set the initial state of mic mute, local speaker volume, etc.
+			{
+				std::ostringstream stream;
+				
+				buildLocalAudioUpdates(stream);
+				
+				if(!stream.str().empty())
+				{
+					writeString(stream.str());
+				}
+			}
+			
+#if USE_SESSION_GROUPS			
+			// create the main session group
+			sessionGroupCreateSendMessage();
+			
+			setState(stateCreatingSessionGroup);
+#else
+			// Not using session groups -- skip the stateCreatingSessionGroup state.
 			setState(stateNoChannel);
+
+			// Initial kick-off of channel lookup logic
+			parcelChanged();		
+#endif
+		break;
+		
+		//MARK: stateCreatingSessionGroup
+		case stateCreatingSessionGroup:
+			if(mSessionTerminateRequested || !mVoiceEnabled)
+			{
+				// TODO: Question: is this the right way out of this state
+				setState(stateSessionTerminated);
+			}
+			else if(!mMainSessionGroupHandle.empty())
+			{
+				setState(stateNoChannel);
+				
+				// Start looped recording (needed for "panic button" anti-griefing tool)
+				recordingLoopStart();
+
+				// Initial kick-off of channel lookup logic
+				parcelChanged();		
+			}
 		break;
 					
+		//MARK: stateNoChannel
 		case stateNoChannel:
+			// Do this here as well as inside sendPositionalUpdate().  
+			// Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync.
+			sendFriendsListUpdates();
+			
 			if(mSessionTerminateRequested || !mVoiceEnabled)
 			{
-				// MBW -- XXX -- Is this the right way out of this state?
+				// TODO: Question: Is this the right way out of this state?
 				setState(stateSessionTerminated);
 			}
 			else if(mTuningMode)
@@ -1678,30 +2085,49 @@ void LLVoiceClient::stateMachine()
 				mTuningExitState = stateNoChannel;
 				setState(stateMicTuningStart);
 			}
-			else if(!mNextSessionHandle.empty())
+			else if(sessionNeedsRelog(mNextAudioSession))
 			{
-				setState(stateSessionConnect);
+				requestRelog();
+				setState(stateSessionTerminated);
+			}
+			else if(mNextAudioSession)
+			{				
+				sessionState *oldSession = mAudioSession;
+
+				mAudioSession = mNextAudioSession;
+				if(!mAudioSession->mReconnect)	
+				{
+					mNextAudioSession = NULL;
+				}
+				
+				// The old session may now need to be deleted.
+				reapSession(oldSession);
+				
+				if(!mAudioSession->mHandle.empty())
+				{
+					// Connect to a session by session handle
+
+					sessionMediaConnectSendMessage(mAudioSession);
+				}
+				else
+				{
+					// Connect to a session by URI
+					sessionCreateSendMessage(mAudioSession, true, false);
+				}
+
+				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
+				setState(stateJoiningSession);
 			}
-			else if(!mNextSessionURI.empty())
+			else if(!mSpatialSessionURI.empty())
 			{
-				setState(stateSessionCreate);
+				// If we're not headed elsewhere and have a spatial URI, return to spatial.
+				switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
 			}
 		break;
 
-		case stateSessionCreate:
-			sessionCreateSendMessage();
-			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
-			setState(stateJoiningSession);
-		break;
-		
-		case stateSessionConnect:
-			sessionConnectSendMessage();
-			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
-			setState(stateJoiningSession);
-		break;
-		
+		//MARK: stateJoiningSession
 		case stateJoiningSession:		// waiting for session handle
-			// sessionCreateResponse() will transition from here to stateSessionJoined.
+			// joinedAudioSession() will transition from here to stateSessionJoined.
 			if(!mVoiceEnabled)
 			{
 				// User bailed out during connect -- jump straight to teardown.
@@ -1709,30 +2135,27 @@ void LLVoiceClient::stateMachine()
 			}
 			else if(mSessionTerminateRequested)
 			{
-				if(!mSessionHandle.empty())
+				if(mAudioSession && !mAudioSession->mHandle.empty())
 				{
 					// Only allow direct exits from this state in p2p calls (for cancelling an invite).
 					// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
-					if(mSessionP2P)
+					if(mAudioSession->mIsP2P)
 					{
-						sessionTerminateSendMessage();
+						sessionMediaDisconnectSendMessage(mAudioSession);
 						setState(stateSessionTerminated);
 					}
 				}
 			}
 		break;
 		
+		//MARK: stateSessionJoined
 		case stateSessionJoined:		// session handle received
-			// MBW -- XXX -- It appears that I need to wait for BOTH the Session.Create response and the SessionStateChangeEvent with state 4
-			//  before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck.
-			// For now, the Session.Create response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
+			// It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4
+			// before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck.
+			// For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
 			// This is a cheap way to make sure both have happened before proceeding.
-			if(!mSessionHandle.empty())
+			if(mAudioSession && mAudioSession->mVoiceEnabled)
 			{
-				// Events that need to happen when a session is joined could go here.
-				// Maybe send initial spatial data?
-				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
-
 				// Dirty state that may need to be sync'ed with the daemon.
 				mPTTDirty = true;
 				mSpeakerVolumeDirty = true;
@@ -1743,6 +2166,11 @@ void LLVoiceClient::stateMachine()
 				// Start the throttle timer
 				mUpdateTimer.start();
 				mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
+
+				// Events that need to happen when a session is joined could go here.
+				// Maybe send initial spatial data?
+				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
+
 			}
 			else if(!mVoiceEnabled)
 			{
@@ -1753,21 +2181,20 @@ void LLVoiceClient::stateMachine()
 			{
 				// Only allow direct exits from this state in p2p calls (for cancelling an invite).
 				// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
-				if(mSessionP2P)
+				if(mAudioSession && mAudioSession->mIsP2P)
 				{
-					sessionTerminateSendMessage();
+					sessionMediaDisconnectSendMessage(mAudioSession);
 					setState(stateSessionTerminated);
 				}
 			}
 		break;
 		
+		//MARK: stateRunning
 		case stateRunning:				// steady state
-			// sessionTerminateSendMessage() will transition from here to stateLeavingSession
-			
 			// Disabling voice or disconnect requested.
 			if(!mVoiceEnabled || mSessionTerminateRequested)
 			{
-				sessionTerminateSendMessage();
+				leaveAudioSession();
 			}
 			else
 			{
@@ -1800,7 +2227,7 @@ void LLVoiceClient::stateMachine()
 					}
 				}
 				
-				if(mNonSpatialChannel)
+				if(!inSpatialChannel())
 				{
 					// When in a non-spatial channel, never send positional updates.
 					mSpatialCoordsDirty = false;
@@ -1813,7 +2240,7 @@ void LLVoiceClient::stateMachine()
 				
 				// Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
 				// or every 10hz, whichever is sooner.
-				if(mVolumeDirty || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
+				if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
 				{
 					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
 					sendPositionalUpdate();
@@ -1821,25 +2248,38 @@ void LLVoiceClient::stateMachine()
 			}
 		break;
 		
+		//MARK: stateLeavingSession
 		case stateLeavingSession:		// waiting for terminate session response
 			// The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
 		break;
 
+		//MARK: stateSessionTerminated
 		case stateSessionTerminated:
-			// Always reset the terminate request flag when we get here.
-			mSessionTerminateRequested = false;
 			
+			// Must do this first, since it uses mAudioSession.
 			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
+			
+			if(mAudioSession)
+			{
+				sessionState *oldSession = mAudioSession;
+
+				mAudioSession = NULL;
+				// We just notified status observers about this change.  Don't do it again.
+				mAudioSessionChanged = false;
 
-			if(mVoiceEnabled)
+				// The old session may now need to be deleted.
+				reapSession(oldSession);
+			}
+			else
 			{
-				// SPECIAL CASE: if going back to spatial but in a parcel with an empty URI, transfer the non-spatial flag now.
-				// This fixes the case where you come out of a group chat in a parcel with voice disabled, and get stuck unable to rejoin spatial chat thereafter.
-				if(mNextSessionSpatial && mNextSessionURI.empty())
-				{
-					mNonSpatialChannel = !mNextSessionSpatial;
-				}
-				
+				LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL;
+			}
+	
+			// Always reset the terminate request flag when we get here.
+			mSessionTerminateRequested = false;
+
+			if(mVoiceEnabled && !mRelogRequested)
+			{				
 				// Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
 				setState(stateNoChannel);
 			}
@@ -1847,49 +2287,71 @@ void LLVoiceClient::stateMachine()
 			{
 				// Shutting down voice, continue with disconnecting.
 				logout();
+				
+				// The state machine will take it from here
+				mRelogRequested = false;
 			}
 			
 		break;
 		
+		//MARK: stateLoggingOut
 		case stateLoggingOut:			// waiting for logout response
 			// The handler for the Account.Logout response will transition from here to stateLoggedOut.
 		break;
+		//MARK: stateLoggedOut
 		case stateLoggedOut:			// logout response received
 			// shut down the connector
 			connectorShutdown();
 		break;
 		
+		//MARK: stateConnectorStopping
 		case stateConnectorStopping:	// waiting for connector stop
 			// The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
 		break;
 
+		//MARK: stateConnectorStopped
 		case stateConnectorStopped:		// connector stop received
 			// Clean up and reset everything. 
 			closeSocket();
-			removeAllParticipants();
+			deleteAllSessions();
+			deleteAllBuddies();
 			setState(stateDisabled);
 		break;
 
+		//MARK: stateConnectorFailed
 		case stateConnectorFailed:
 			setState(stateConnectorFailedWaiting);
 		break;
+		//MARK: stateConnectorFailedWaiting
 		case stateConnectorFailedWaiting:
 		break;
 
+		//MARK: stateLoginFailed
 		case stateLoginFailed:
 			setState(stateLoginFailedWaiting);
 		break;
+		//MARK: stateLoginFailedWaiting
 		case stateLoginFailedWaiting:
 			// No way to recover from these.  Yet.
 		break;
 
+		//MARK: stateJoinSessionFailed
 		case stateJoinSessionFailed:
 			// Transition to error state.  Send out any notifications here.
-			LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mVivoxErrorStatusCode << "): " << mVivoxErrorStatusString << LL_ENDL;
+			if(mAudioSession)
+			{
+				LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL;
+			}
+			else
+			{
+				LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL;
+			}
+			
 			notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
 			setState(stateJoinSessionFailedWaiting);
 		break;
 		
+		//MARK: stateJoinSessionFailedWaiting
 		case stateJoinSessionFailedWaiting:
 			// Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
 			// Region crossings may leave this state and try the join again.
@@ -1899,22 +2361,29 @@ void LLVoiceClient::stateMachine()
 			}
 		break;
 		
+		//MARK: stateJail
 		case stateJail:
 			// We have given up.  Do nothing.
 		break;
 
-	        case stateMicTuningNoLogin:
-			// *TODO: Implement me.
-			LL_WARNS("Voice") << "stateMicTuningNoLogin not handled" << LL_ENDL;
+		//MARK: stateMicTuningNoLogin
+		case stateMicTuningNoLogin:
+		// *TODO: Implement me.
+		LL_WARNS("Voice") << "stateMicTuningNoLogin not handled" << LL_ENDL;
 		break;
 	}
-
-	if(mParticipantMapChanged)
+	
+	if(mAudioSession && mAudioSession->mParticipantsChanged)
 	{
-		mParticipantMapChanged = false;
-		notifyObservers();
+		mAudioSession->mParticipantsChanged = false;
+		mAudioSessionChanged = true;
+	}
+	
+	if(mAudioSessionChanged)
+	{
+		mAudioSessionChanged = false;
+		notifyParticipantObservers();
 	}
-
 }
 
 void LLVoiceClient::closeSocket(void)
@@ -1932,6 +2401,9 @@ void LLVoiceClient::loginSendMessage()
 		<< "<AccountName>" << mAccountName << "</AccountName>"
 		<< "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
 		<< "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
+		<< "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>"
+		<< "<BuddyManagementMode>Application</BuddyManagementMode>"
+		<< "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>"
 	<< "</Request>\n\n\n";
 	
 	writeString(stream.str());
@@ -1939,7 +2411,10 @@ void LLVoiceClient::loginSendMessage()
 
 void LLVoiceClient::logout()
 {
-	mAccountPassword = "";
+	// Ensure that we'll re-request provisioning before logging in again
+	mAccountPassword.clear();
+	mVoiceAccountServerURI.clear();
+	
 	setState(stateLoggingOut);
 	logoutSendMessage();
 }
@@ -1961,78 +2436,164 @@ void LLVoiceClient::logoutSendMessage()
 	}
 }
 
-void LLVoiceClient::channelGetListSendMessage()
+void LLVoiceClient::accountListBlockRulesSendMessage()
 {
-	std::ostringstream stream;
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ChannelGetList.1\">"
-		<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-	<< "</Request>\n\n\n";
+	if(!mAccountHandle.empty())
+	{		
+		std::ostringstream stream;
 
-	writeString(stream.str());
+		LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL;
+
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListBlockRules.1\">"
+			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+		<< "</Request>"
+		<< "\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVoiceClient::accountListAutoAcceptRulesSendMessage()
+{
+	if(!mAccountHandle.empty())
+	{		
+		std::ostringstream stream;
+
+		LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL;
+
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListAutoAcceptRules.1\">"
+			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+		<< "</Request>"
+		<< "\n\n\n";
+
+		writeString(stream.str());
+	}
 }
 
-void LLVoiceClient::sessionCreateSendMessage()
+void LLVoiceClient::sessionGroupCreateSendMessage()
 {
-	LL_DEBUGS("Voice") << "requesting join: " << mNextSessionURI << LL_ENDL;
+	if(!mAccountHandle.empty())
+	{		
+		std::ostringstream stream;
+
+		LL_DEBUGS("Voice") << "creating session group" << LL_ENDL;
 
-	mSessionURI = mNextSessionURI;
-	mNonSpatialChannel = !mNextSessionSpatial;
-	mSessionResetOnClose = mNextSessionResetOnClose;
-	mNextSessionResetOnClose = false;
-	if(mNextSessionNoReconnect)
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">"
+			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+			<< "<Type>Normal</Type>"
+		<< "</Request>"
+		<< "\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText)
+{
+	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
+	
+	session->mCreateInProgress = true;
+	if(startAudio)
 	{
-		// Clear the stashed URI so it can't reconnect
-		mNextSessionURI.clear();
+		session->mMediaConnectInProgress = true;
 	}
-	// Only p2p sessions are created with "no reconnect".
-	mSessionP2P = mNextSessionNoReconnect;
 
 	std::ostringstream stream;
 	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Create.1\">"
+	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">"
 		<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
-		<< "<URI>" << mSessionURI << "</URI>";
+		<< "<URI>" << session->mSIPURI << "</URI>";
 
 	static const std::string allowed_chars =
 				"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
 				"0123456789"
 				"-._~";
 
-	if(!mNextSessionHash.empty())
+	if(!session->mHash.empty())
 	{
 		stream
-			<< "<Password>" << LLURI::escape(mNextSessionHash, allowed_chars) << "</Password>"
+			<< "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>"
 			<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
 	}
 	
 	stream
+		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
+		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
 		<< "<Name>" << mChannelName << "</Name>"
 	<< "</Request>\n\n\n";
 	writeString(stream.str());
 }
 
-void LLVoiceClient::sessionConnectSendMessage()
+void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText)
 {
-	LL_DEBUGS("Voice") << "connecting to session handle: " << mNextSessionHandle << LL_ENDL;
+	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL;
 	
-	mSessionHandle = mNextSessionHandle;
-	mSessionURI = mNextP2PSessionURI;
-	mNextSessionHandle.clear();		// never want to re-use these.
-	mNextP2PSessionURI.clear();
-	mNonSpatialChannel = !mNextSessionSpatial;
-	mSessionResetOnClose = mNextSessionResetOnClose;
-	mNextSessionResetOnClose = false;
-	// Joining by session ID is only used to answer p2p invitations, so we know this is a p2p session.
-	mSessionP2P = true;
+	session->mCreateInProgress = true;
+	if(startAudio)
+	{
+		session->mMediaConnectInProgress = true;
+	}
+	
+	std::string password;
+	if(!session->mHash.empty())
+	{
+		static const std::string allowed_chars =
+					"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+					"0123456789"
+					"-._~"
+					;
+		password = LLURI::escape(session->mHash, allowed_chars);
+	}
+
+	std::ostringstream stream;
+	stream
+	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"SessionGroup.AddSession.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<URI>" << session->mSIPURI << "</URI>"
+		<< "<Name>" << mChannelName << "</Name>"
+		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>"
+		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>"
+		<< "<Password>" << password << "</Password>"
+		<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"
+	<< "</Request>\n\n\n"
+	;
+	
+	writeString(stream.str());
+}
+
+void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session)
+{
+	LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL;
+
+	session->mMediaConnectInProgress = true;
 	
 	std::ostringstream stream;
+
+	stream
+	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+		<< "<Media>Audio</Media>"
+	<< "</Request>\n\n\n";
+
+	writeString(stream.str());
+}
+
+void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session)
+{
+	LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL;
 	
+	std::ostringstream stream;
+
 	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Connect.1\">"
-		<< "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
-		<< "<AudioMedia>default</AudioMedia>"
+	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.TextConnect.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
 	<< "</Request>\n\n\n";
+
 	writeString(stream.str());
 }
 
@@ -2041,52 +2602,112 @@ void LLVoiceClient::sessionTerminate()
 	mSessionTerminateRequested = true;
 }
 
-void LLVoiceClient::sessionTerminateSendMessage()
+void LLVoiceClient::requestRelog()
 {
-	LL_DEBUGS("Voice") << "leaving session: " << mSessionURI << LL_ENDL;
+	mSessionTerminateRequested = true;
+	mRelogRequested = true;
+}
 
-	switch(getState())
+
+void LLVoiceClient::leaveAudioSession()
+{
+	if(mAudioSession)
 	{
-		case stateNoChannel:
-			// In this case, we want to pretend the join failed so our state machine doesn't get stuck.
-			// Skip the join failed transition state so we don't send out error notifications.
-			setState(stateJoinSessionFailedWaiting);
-		break;
-		case stateJoiningSession:
-		case stateSessionJoined:
-		case stateRunning:
-			if(!mSessionHandle.empty())
-			{
-				sessionTerminateByHandle(mSessionHandle);
-				setState(stateLeavingSession);
-			}
-			else
-			{
-				LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;	
+		LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL;
+
+		switch(getState())
+		{
+			case stateNoChannel:
+				// In this case, we want to pretend the join failed so our state machine doesn't get stuck.
+				// Skip the join failed transition state so we don't send out error notifications.
+				setState(stateJoinSessionFailedWaiting);
+			break;
+			case stateJoiningSession:
+			case stateSessionJoined:
+			case stateRunning:
+				if(!mAudioSession->mHandle.empty())
+				{
+
+#if RECORD_EVERYTHING
+					// HACK: for testing only
+					// Save looped recording
+					std::string savepath("/tmp/vivoxrecording");
+					{
+						time_t now = time(NULL);
+						const size_t BUF_SIZE = 64;
+						char time_str[BUF_SIZE];	/* Flawfinder: ignore */
+						
+						strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
+						savepath += time_str;
+					}
+					recordingLoopSave(savepath);
+#endif
+
+					sessionMediaDisconnectSendMessage(mAudioSession);
+					setState(stateLeavingSession);
+				}
+				else
+				{
+					LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;	
+					setState(stateSessionTerminated);
+				}
+			break;
+			case stateJoinSessionFailed:
+			case stateJoinSessionFailedWaiting:
 				setState(stateSessionTerminated);
-			}
-		break;
-		case stateJoinSessionFailed:
-		case stateJoinSessionFailedWaiting:
-			setState(stateSessionTerminated);
-		break;
-		
-		default:
-			LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
-		break;
+			break;
+			
+			default:
+				LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
+			break;
+		}
+	}
+	else
+	{
+		LL_WARNS("Voice") << "called with no active session" << LL_ENDL;
+		setState(stateSessionTerminated);
 	}
 }
 
-void LLVoiceClient::sessionTerminateByHandle(std::string &sessionHandle)
+void LLVoiceClient::sessionTerminateSendMessage(sessionState *session)
 {
-	LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << sessionHandle << LL_ENDL;	
-
 	std::ostringstream stream;
+	
+	LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;	
 	stream
 	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
-		<< "<SessionHandle>" << sessionHandle << "</SessionHandle>"
-	<< "</Request>"
-	<< "\n\n\n";
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+}
+
+void LLVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session)
+{
+	std::ostringstream stream;
+	
+	LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;	
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+		<< "<Media>Audio</Media>"
+	<< "</Request>\n\n\n";
+	
+	writeString(stream.str());
+	
+}
+
+void LLVoiceClient::sessionTextDisconnectSendMessage(sessionState *session)
+{
+	std::ostringstream stream;
+	
+	LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;	
+	stream
+	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">"
+		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>"
+		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+	<< "</Request>\n\n\n";
 	
 	writeString(stream.str());
 }
@@ -2113,14 +2734,12 @@ void LLVoiceClient::getRenderDevicesSendMessage()
 
 void LLVoiceClient::clearCaptureDevices()
 {
-	// MBW -- XXX -- do something here
 	LL_DEBUGS("Voice") << "called" << LL_ENDL;
 	mCaptureDevices.clear();
 }
 
 void LLVoiceClient::addCaptureDevice(const std::string& name)
 {
-	// MBW -- XXX -- do something here
 	LL_DEBUGS("Voice") << name << LL_ENDL;
 
 	mCaptureDevices.push_back(name);
@@ -2152,15 +2771,13 @@ void LLVoiceClient::setCaptureDevice(const std::string& name)
 }
 
 void LLVoiceClient::clearRenderDevices()
-{
-	// MBW -- XXX -- do something here
+{	
 	LL_DEBUGS("Voice") << "called" << LL_ENDL;
 	mRenderDevices.clear();
 }
 
 void LLVoiceClient::addRenderDevice(const std::string& name)
 {
-	// MBW -- XXX -- do something here
 	LL_DEBUGS("Voice") << name << LL_ENDL;
 	mRenderDevices.push_back(name);
 }
@@ -2272,29 +2889,22 @@ void LLVoiceClient::tuningCaptureStopSendMessage()
 
 void LLVoiceClient::tuningSetMicVolume(float volume)
 {
-	int scaledVolume = ((int)(volume * 100.0f)) - 100;
-	if(scaledVolume != mTuningMicVolume)
+	int scaled_volume = scale_mic_volume(volume);
+
+	if(scaled_volume != mTuningMicVolume)
 	{
-		mTuningMicVolume = scaledVolume;
+		mTuningMicVolume = scaled_volume;
 		mTuningMicVolumeDirty = true;
 	}
 }
 
 void LLVoiceClient::tuningSetSpeakerVolume(float volume)
 {
-	// incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
-	// Map it as follows: 0.0 -> -100, 0.5 -> 24, 1.0 -> 50
-	
-	volume -= 0.5f;		// offset volume to the range [-0.5 ... 0.5], with 0 at the default.
-	int scaledVolume = 24;	// offset scaledVolume by its default level
-	if(volume < 0.0f)
-		scaledVolume += ((int)(volume * 248.0f));	// (24 - (-100)) * 2
-	else
-		scaledVolume += ((int)(volume * 52.0f));	// (50 - 24) * 2
+	int scaled_volume = scale_speaker_volume(volume);	
 
-	if(scaledVolume != mTuningSpeakerVolume)
+	if(scaled_volume != mTuningSpeakerVolume)
 	{
-		mTuningSpeakerVolume = scaledVolume;
+		mTuningSpeakerVolume = scaled_volume;
 		mTuningSpeakerVolumeDirty = true;
 	}
 }
@@ -2334,7 +2944,8 @@ void LLVoiceClient::daemonDied()
 	LL_WARNS("Voice") << "Connection to vivox daemon lost.  Resetting state."<< LL_ENDL;
 
 	closeSocket();
-	removeAllParticipants();
+	deleteAllSessions();
+	deleteAllBuddies();
 	
 	// Try to relaunch the daemon
 	setState(stateDisabled);
@@ -2344,56 +2955,185 @@ void LLVoiceClient::giveUp()
 {
 	// All has failed.  Clean up and stop trying.
 	closeSocket();
-	removeAllParticipants();
+	deleteAllSessions();
+	deleteAllBuddies();
 	
 	setState(stateJail);
 }
 
+static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel)
+{
+	F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the  new position and velocity
+	F64 npos[3];
+	
+	// The original XML command was sent like this:
+	/*
+			<< "<Position>"
+				<< "<X>" << pos[VX] << "</X>"
+				<< "<Y>" << pos[VZ] << "</Y>"
+				<< "<Z>" << pos[VY] << "</Z>"
+			<< "</Position>"
+			<< "<Velocity>"
+				<< "<X>" << mAvatarVelocity[VX] << "</X>"
+				<< "<Y>" << mAvatarVelocity[VZ] << "</Y>"
+				<< "<Z>" << mAvatarVelocity[VY] << "</Z>"
+			<< "</Velocity>"
+			<< "<AtOrientation>"
+				<< "<X>" << l.mV[VX] << "</X>"
+				<< "<Y>" << u.mV[VX] << "</Y>"
+				<< "<Z>" << a.mV[VX] << "</Z>"
+			<< "</AtOrientation>"
+			<< "<UpOrientation>"
+				<< "<X>" << l.mV[VZ] << "</X>"
+				<< "<Y>" << u.mV[VY] << "</Y>"
+				<< "<Z>" << a.mV[VZ] << "</Z>"
+			<< "</UpOrientation>"
+			<< "<LeftOrientation>"
+				<< "<X>" << l.mV [VY] << "</X>"
+				<< "<Y>" << u.mV [VZ] << "</Y>"
+				<< "<Z>" << a.mV [VY] << "</Z>"
+			<< "</LeftOrientation>";
+	*/
+
+#if 1
+	// This was the original transform done when building the XML command
+	nat[0] = left.mV[VX];
+	nat[1] = up.mV[VX];
+	nat[2] = at.mV[VX];
+
+	nup[0] = left.mV[VZ];
+	nup[1] = up.mV[VY];
+	nup[2] = at.mV[VZ];
+
+	nl[0] = left.mV[VY];
+	nl[1] = up.mV[VZ];
+	nl[2] = at.mV[VY];
+
+	npos[0] = pos.mdV[VX];
+	npos[1] = pos.mdV[VZ];
+	npos[2] = pos.mdV[VY];
+
+	nvel[0] = vel.mV[VX];
+	nvel[1] = vel.mV[VZ];
+	nvel[2] = vel.mV[VY];
+
+	for(int i=0;i<3;++i) {
+		at.mV[i] = nat[i];
+		up.mV[i] = nup[i];
+		left.mV[i] = nl[i];
+		pos.mdV[i] = npos[i];
+	}
+	
+	// This was the original transform done in the SDK
+	nat[0] = at.mV[2];
+	nat[1] = 0; // y component of at vector is always 0, this was up[2]
+	nat[2] = -1 * left.mV[2];
+
+	// We override whatever the application gives us
+	nup[0] = 0; // x component of up vector is always 0
+	nup[1] = 1; // y component of up vector is always 1
+	nup[2] = 0; // z component of up vector is always 0
+
+	nl[0] = at.mV[0];
+	nl[1] = 0;  // y component of left vector is always zero, this was up[0]
+	nl[2] = -1 * left.mV[0];
+
+	npos[2] = pos.mdV[2] * -1.0;
+	npos[1] = pos.mdV[1];
+	npos[0] = pos.mdV[0];
+
+	for(int i=0;i<3;++i) {
+		at.mV[i] = nat[i];
+		up.mV[i] = nup[i];
+		left.mV[i] = nl[i];
+		pos.mdV[i] = npos[i];
+	}
+#else
+	// This is the compose of the two transforms (at least, that's what I'm trying for)
+	nat[0] = at.mV[VX];
+	nat[1] = 0; // y component of at vector is always 0, this was up[2]
+	nat[2] = -1 * up.mV[VZ];
+
+	// We override whatever the application gives us
+	nup[0] = 0; // x component of up vector is always 0
+	nup[1] = 1; // y component of up vector is always 1
+	nup[2] = 0; // z component of up vector is always 0
+
+	nl[0] = left.mV[VX];
+	nl[1] = 0;  // y component of left vector is always zero, this was up[0]
+	nl[2] = -1 * left.mV[VY];
+
+	npos[0] = pos.mdV[VX];
+	npos[1] = pos.mdV[VZ];
+	npos[2] = pos.mdV[VY] * -1.0;
+
+	nvel[0] = vel.mV[VX];
+	nvel[1] = vel.mV[VZ];
+	nvel[2] = vel.mV[VY];
+
+	for(int i=0;i<3;++i) {
+		at.mV[i] = nat[i];
+		up.mV[i] = nup[i];
+		left.mV[i] = nl[i];
+		pos.mdV[i] = npos[i];
+	}
+	
+#endif
+}
+
 void LLVoiceClient::sendPositionalUpdate(void)
 {	
 	std::ostringstream stream;
 	
 	if(mSpatialCoordsDirty)
 	{
-		LLVector3 l, u, a;
+		LLVector3 l, u, a, vel;
+		LLVector3d pos;
+
+		mSpatialCoordsDirty = false;
 		
 		// Always send both speaker and listener positions together.
 		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"		
-			<< "<SessionHandle>" << mSessionHandle << "</SessionHandle>";
+			<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>";
 		
 		stream << "<SpeakerPosition>";
 
+//		LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
 		l = mAvatarRot.getLeftRow();
 		u = mAvatarRot.getUpRow();
 		a = mAvatarRot.getFwdRow();
-			
-		LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
+		pos = mAvatarPosition;
+		vel = mAvatarVelocity;
 
+		// SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore.
+		// The old transform is replicated by this function.
+		oldSDKTransform(l, u, a, pos, vel);
+		
 		stream 
 			<< "<Position>"
-				<< "<X>" << mAvatarPosition[VX] << "</X>"
-				<< "<Y>" << mAvatarPosition[VZ] << "</Y>"
-				<< "<Z>" << mAvatarPosition[VY] << "</Z>"
+				<< "<X>" << pos.mdV[VX] << "</X>"
+				<< "<Y>" << pos.mdV[VY] << "</Y>"
+				<< "<Z>" << pos.mdV[VZ] << "</Z>"
 			<< "</Position>"
 			<< "<Velocity>"
-				<< "<X>" << mAvatarVelocity[VX] << "</X>"
-				<< "<Y>" << mAvatarVelocity[VZ] << "</Y>"
-				<< "<Z>" << mAvatarVelocity[VY] << "</Z>"
+				<< "<X>" << vel.mV[VX] << "</X>"
+				<< "<Y>" << vel.mV[VY] << "</Y>"
+				<< "<Z>" << vel.mV[VZ] << "</Z>"
 			<< "</Velocity>"
 			<< "<AtOrientation>"
-				<< "<X>" << l.mV[VX] << "</X>"
-				<< "<Y>" << u.mV[VX] << "</Y>"
-				<< "<Z>" << a.mV[VX] << "</Z>"
+				<< "<X>" << a.mV[VX] << "</X>"
+				<< "<Y>" << a.mV[VY] << "</Y>"
+				<< "<Z>" << a.mV[VZ] << "</Z>"
 			<< "</AtOrientation>"
 			<< "<UpOrientation>"
-				<< "<X>" << l.mV[VZ] << "</X>"
+				<< "<X>" << u.mV[VX] << "</X>"
 				<< "<Y>" << u.mV[VY] << "</Y>"
-				<< "<Z>" << a.mV[VZ] << "</Z>"
+				<< "<Z>" << u.mV[VZ] << "</Z>"
 			<< "</UpOrientation>"
 			<< "<LeftOrientation>"
-				<< "<X>" << l.mV [VY] << "</X>"
-				<< "<Y>" << u.mV [VZ] << "</Y>"
-				<< "<Z>" << a.mV [VY] << "</Z>"
+				<< "<X>" << l.mV [VX] << "</X>"
+				<< "<Y>" << l.mV [VY] << "</Y>"
+				<< "<Z>" << l.mV [VZ] << "</Z>"
 			<< "</LeftOrientation>";
 
 		stream << "</SpeakerPosition>";
@@ -2429,43 +3169,158 @@ void LLVoiceClient::sendPositionalUpdate(void)
 		l = earRot.getLeftRow();
 		u = earRot.getUpRow();
 		a = earRot.getFwdRow();
+		pos = earPosition;
+		vel = earVelocity;
 
-		LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL;
-
+//		LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL;
+		
+		oldSDKTransform(l, u, a, pos, vel);
+		
 		stream 
 			<< "<Position>"
-				<< "<X>" << earPosition[VX] << "</X>"
-				<< "<Y>" << earPosition[VZ] << "</Y>"
-				<< "<Z>" << earPosition[VY] << "</Z>"
+				<< "<X>" << pos.mdV[VX] << "</X>"
+				<< "<Y>" << pos.mdV[VY] << "</Y>"
+				<< "<Z>" << pos.mdV[VZ] << "</Z>"
 			<< "</Position>"
 			<< "<Velocity>"
-				<< "<X>" << earVelocity[VX] << "</X>"
-				<< "<Y>" << earVelocity[VZ] << "</Y>"
-				<< "<Z>" << earVelocity[VY] << "</Z>"
+				<< "<X>" << vel.mV[VX] << "</X>"
+				<< "<Y>" << vel.mV[VY] << "</Y>"
+				<< "<Z>" << vel.mV[VZ] << "</Z>"
 			<< "</Velocity>"
 			<< "<AtOrientation>"
-				<< "<X>" << l.mV[VX] << "</X>"
-				<< "<Y>" << u.mV[VX] << "</Y>"
-				<< "<Z>" << a.mV[VX] << "</Z>"
+				<< "<X>" << a.mV[VX] << "</X>"
+				<< "<Y>" << a.mV[VY] << "</Y>"
+				<< "<Z>" << a.mV[VZ] << "</Z>"
 			<< "</AtOrientation>"
 			<< "<UpOrientation>"
-				<< "<X>" << l.mV[VZ] << "</X>"
+				<< "<X>" << u.mV[VX] << "</X>"
 				<< "<Y>" << u.mV[VY] << "</Y>"
-				<< "<Z>" << a.mV[VZ] << "</Z>"
+				<< "<Z>" << u.mV[VZ] << "</Z>"
 			<< "</UpOrientation>"
 			<< "<LeftOrientation>"
-				<< "<X>" << l.mV [VY] << "</X>"
-				<< "<Y>" << u.mV [VZ] << "</Y>"
-				<< "<Z>" << a.mV [VY] << "</Z>"
+				<< "<X>" << l.mV [VX] << "</X>"
+				<< "<Y>" << l.mV [VY] << "</Y>"
+				<< "<Z>" << l.mV [VZ] << "</Z>"
 			<< "</LeftOrientation>";
 
+
 		stream << "</ListenerPosition>";
 
 		stream << "</Request>\n\n\n";
 	}	
+	
+	if(mAudioSession && mAudioSession->mVolumeDirty)
+	{
+		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
+
+		mAudioSession->mVolumeDirty = false;
+		
+		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
+		{
+			participantState *p = iter->second;
+			
+			if(p->mVolumeDirty)
+			{
+				// Can't set volume/mute for yourself
+				if(!p->mIsSelf)
+				{
+					int volume = p->mUserVolume;
+					bool mute = p->mOnMuteList;
+										
+					// SLIM SDK: scale volume from 0-400 (with 100 as "normal") to 0-100 (with 56 as "normal")
+					if(volume < 100)
+						volume = (volume * 56) / 100;
+					else
+						volume = (((volume - 100) * (100 - 56)) / 300) + 56;
+
+					if(mute)
+					{
+						// SetParticipantMuteForMe doesn't work in p2p sessions.
+						// If we want the user to be muted, set their volume to 0 as well.
+						// This isn't perfect, but it will at least reduce their volume to a minimum.
+						volume = 0;
+					}
+					
+					if(volume == 0)
+						mute = true;
+
+					LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
+					
+					// SLIM SDK: Send both volume and mute commands.
+					
+					// Send a "volume for me" command for the user.
+					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
+						<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
+						<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
+						<< "<Volume>" << volume << "</Volume>"
+						<< "</Request>\n\n\n";
+
+					// Send a "mute for me" command for the user
+					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">"
+						<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"
+						<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
+						<< "<Mute>" << (mute?"1":"0") << "</Mute>"
+						<< "</Request>\n\n\n";
+				}
+				
+				p->mVolumeDirty = false;
+			}
+		}
+	}
+			
+	buildLocalAudioUpdates(stream);
+	
+	if(!stream.str().empty())
+	{
+		writeString(stream.str());
+	}
+	
+	// Friends list updates can be huge, especially on the first voice login of an account with lots of friends.
+	// Batching them all together can choke SLVoice, so send them in separate writes.
+	sendFriendsListUpdates();
+}
+
+void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
+{
+	if(mCaptureDeviceDirty)
+	{
+		LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL;
+	
+		stream 
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
+			<< "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
+		<< "</Request>"
+		<< "\n\n\n";
+		
+		mCaptureDeviceDirty = false;
+	}
+}
+
+void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
+{
+	if(mRenderDeviceDirty)
+	{
+		LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL;
+
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
+			<< "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
+		<< "</Request>"
+		<< "\n\n\n";
+		mRenderDeviceDirty = false;
+	}
+}
+
+void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream)
+{
+	buildSetCaptureDevice(stream);
+
+	buildSetRenderDevice(stream);
 
 	if(mPTTDirty)
 	{
+		mPTTDirty = false;
+
 		// Send a local mute command.
 		// NOTE that the state of "PTT" is the inverse of "local mute".
 		//   (i.e. when PTT is true, we send a mute command with "false", and vice versa)
@@ -2476,119 +3331,337 @@ void LLVoiceClient::sendPositionalUpdate(void)
 			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
 			<< "<Value>" << (mPTT?"false":"true") << "</Value>"
 			<< "</Request>\n\n\n";
-
-	}
-	
-	if(mVolumeDirty)
-	{
-		participantMap::iterator iter = mParticipantMap.begin();
 		
-		for(; iter != mParticipantMap.end(); iter++)
-		{
-			participantState *p = iter->second;
-			
-			if(p->mVolumeDirty)
-			{
-				int volume = p->mOnMuteList?0:p->mUserVolume;
-				
-				LL_INFOS("Voice") << "Setting volume for avatar " << p->mAvatarID << " to " << volume << LL_ENDL;
-				
-				// Send a mute/unumte command for the user (actually "volume for me").
-				stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
-					<< "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
-					<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
-					<< "<Volume>" << volume << "</Volume>"
-					<< "</Request>\n\n\n";
-
-				p->mVolumeDirty = false;
-			}
-		}
 	}
-	
+
 	if(mSpeakerMuteDirty)
 	{
-		const char *muteval = ((mSpeakerVolume == -100)?"true":"false");
+		const char *muteval = ((mSpeakerVolume == 0)?"true":"false");
+
+		mSpeakerMuteDirty = false;
+
 		LL_INFOS("Voice") << "Setting speaker mute to " << muteval  << LL_ENDL;
 		
 		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
 			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
 			<< "<Value>" << muteval << "</Value>"
-			<< "</Request>\n\n\n";		
+			<< "</Request>\n\n\n";	
+		
 	}
 	
 	if(mSpeakerVolumeDirty)
 	{
+		mSpeakerVolumeDirty = false;
+
 		LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume  << LL_ENDL;
 
 		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
 			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
 			<< "<Value>" << mSpeakerVolume << "</Value>"
-			<< "</Request>\n\n\n";		
+			<< "</Request>\n\n\n";
+			
 	}
 	
 	if(mMicVolumeDirty)
 	{
+		mMicVolumeDirty = false;
+
 		LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume  << LL_ENDL;
 
 		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
 			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
 			<< "<Value>" << mMicVolume << "</Value>"
-			<< "</Request>\n\n\n";		
+			<< "</Request>\n\n\n";				
 	}
 
 	
-	// MBW -- XXX -- Maybe check to make sure the capture/render devices are in the current list here?
-	if(mCaptureDeviceDirty)
-	{
-		buildSetCaptureDevice(stream);
-	}
+}
 
-	if(mRenderDeviceDirty)
+void LLVoiceClient::checkFriend(const LLUUID& id)
+{
+	std::string name;
+	buddyListEntry *buddy = findBuddy(id);
+
+	// Make sure we don't add a name before it's been looked up.
+	if(gCacheName->getFullName(id, name))
 	{
-		buildSetRenderDevice(stream);
+
+		const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id);
+		bool canSeeMeOnline = false;
+		if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
+			canSeeMeOnline = true;
+		
+		// When we get here, mNeedsSend is true and mInSLFriends is false.  Change them as necessary.
+		
+		if(buddy)
+		{
+			// This buddy is already in both lists.
+
+			if(name != buddy->mDisplayName)
+			{
+				// The buddy is in the list with the wrong name.  Update it with the correct name.
+				LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL;
+				buddy->mDisplayName = name;
+				buddy->mNeedsNameUpdate = true;		// This will cause the buddy to be resent.
+			}
+		}
+		else
+		{
+			// This buddy was not in the vivox list, needs to be added.
+			buddy = addBuddy(sipURIFromID(id), name);
+			buddy->mUUID = id;
+		}
+		
+		// In all the above cases, the buddy is in the SL friends list (which is how we got here).
+		buddy->mInSLFriends = true;
+		buddy->mCanSeeMeOnline = canSeeMeOnline;
+		buddy->mNameResolved = true;
+		
 	}
-	
-	mSpatialCoordsDirty = false;
-	mPTTDirty = false;
-	mVolumeDirty = false;
-	mSpeakerVolumeDirty = false;
-	mMicVolumeDirty = false;
-	mSpeakerMuteDirty = false;
-	mCaptureDeviceDirty = false;
-	mRenderDeviceDirty = false;
-	
-	if(!stream.str().empty())
+	else
 	{
-		writeString(stream.str());
+		// This name hasn't been looked up yet.  Don't do anything with this buddy list entry until it has.
+		if(buddy)
+		{
+			buddy->mNameResolved = false;
+		}
+		
+		// Initiate a lookup.
+		// The "lookup completed" callback will ensure that the friends list is rechecked after it completes.
+		lookupName(id);
 	}
 }
 
-void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
+void LLVoiceClient::clearAllLists()
 {
-	LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL;
+	// FOR TESTING ONLY
 	
-	stream 
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
-		<< "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
-	<< "</Request>"
-	<< "\n\n\n";
+	// This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about.
+	buddyListMap::iterator buddy_it;
+	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
+	{
+		buddyListEntry *buddy = buddy_it->second;
+		buddy_it++;
+		
+		std::ostringstream stream;
+
+		if(buddy->mInVivoxBuddies)
+		{
+			// delete this entry from the vivox buddy list
+			buddy->mInVivoxBuddies = false;
+			LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
+			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
+				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
+				<< "</Request>\n\n\n";		
+		}
+
+		if(buddy->mHasBlockListEntry)
+		{
+			// Delete the associated block list entry (so the block list doesn't fill up with junk)
+			buddy->mHasBlockListEntry = false;
+			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
+				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+				<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
+				<< "</Request>\n\n\n";								
+		}
+		if(buddy->mHasAutoAcceptListEntry)
+		{
+			// Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk)
+			buddy->mHasAutoAcceptListEntry = false;
+			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
+				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+				<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
+				<< "</Request>\n\n\n";
+		}
+
+		writeString(stream.str());
+
+	}
 }
 
-void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
+void LLVoiceClient::sendFriendsListUpdates()
 {
-	LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL;
+	if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty)
+	{
+		mFriendsListDirty = false;
+		
+		if(0)
+		{
+			// FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries.
+			clearAllLists();
+			return;
+		}
+		
+		LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL;
+		
+		buddyListMap::iterator buddy_it;
+		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
+		{
+			// reset the temp flags in the local buddy list
+			buddy_it->second->mInSLFriends = false;
+		}
+		
+		// correlate with the friends list
+		{
+			LLCollectAllBuddies collect;
+			LLAvatarTracker::instance().applyFunctor(collect);
+			LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin();
+			LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end();
+			
+			for ( ; it != end; ++it)
+			{
+				checkFriend(it->second);
+			}
+			it = collect.mOffline.begin();
+			end = collect.mOffline.end();
+			for ( ; it != end; ++it)
+			{
+				checkFriend(it->second);
+			}
+		}
+				
+		LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL;
 
-	stream
-	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
-		<< "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
-	<< "</Request>"
-	<< "\n\n\n";
+		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();)
+		{
+			buddyListEntry *buddy = buddy_it->second;
+			buddy_it++;
+			
+			// Ignore entries that aren't resolved yet.
+			if(buddy->mNameResolved)
+			{
+				std::ostringstream stream;
+
+				if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate))
+				{					
+					if(mNumberOfAliases > 0)
+					{
+						// Add (or update) this entry in the vivox buddy list
+						buddy->mInVivoxBuddies = true;
+						buddy->mNeedsNameUpdate = false;
+						LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
+						stream 
+							<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddySet.1\">"
+								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+								<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
+								<< "<DisplayName>" << buddy->mDisplayName << "</DisplayName>"
+								<< "<BuddyData></BuddyData>"	// Without this, SLVoice doesn't seem to parse the command.
+								<< "<GroupID>0</GroupID>"
+							<< "</Request>\n\n\n";	
+					}
+				}
+				else if(!buddy->mInSLFriends)
+				{
+					// This entry no longer exists in your SL friends list.  Remove all traces of it from the Vivox buddy list.
+ 					if(buddy->mInVivoxBuddies)
+					{
+						// delete this entry from the vivox buddy list
+						buddy->mInVivoxBuddies = false;
+						LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL;
+						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">"
+							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+							<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
+							<< "</Request>\n\n\n";		
+					}
+
+					if(buddy->mHasBlockListEntry)
+					{
+						// Delete the associated block list entry, if any
+						buddy->mHasBlockListEntry = false;
+						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
+							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+							<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
+							<< "</Request>\n\n\n";								
+					}
+					if(buddy->mHasAutoAcceptListEntry)
+					{
+						// Delete the associated auto-accept list entry, if any
+						buddy->mHasAutoAcceptListEntry = false;
+						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
+							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+							<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
+							<< "</Request>\n\n\n";
+					}
+				}
+				
+				if(buddy->mInSLFriends)
+				{
+
+					if(buddy->mCanSeeMeOnline)
+					{
+						// Buddy should not be blocked.
+
+						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
+						
+						// If the buddy has a block list entry, delete it.
+						if(buddy->mHasBlockListEntry)
+						{
+							buddy->mHasBlockListEntry = false;
+							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">"
+								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+								<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
+								<< "</Request>\n\n\n";		
+							
+							
+							// If we just deleted a block list entry, add an auto-accept entry.
+							if(!buddy->mHasAutoAcceptListEntry)
+							{
+								buddy->mHasAutoAcceptListEntry = true;								
+								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateAutoAcceptRule.1\">"
+									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+									<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
+									<< "<AutoAddAsBuddy>0</AutoAddAsBuddy>"
+									<< "</Request>\n\n\n";
+							}
+						}
+					}
+					else
+					{
+						// Buddy should be blocked.
+						
+						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent.
+
+						// If this buddy has an autoaccept entry, delete it
+						if(buddy->mHasAutoAcceptListEntry)
+						{
+							buddy->mHasAutoAcceptListEntry = false;
+							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">"
+								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+								<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>"
+								<< "</Request>\n\n\n";
+						
+							// If we just deleted an auto-accept entry, add a block list entry.
+							if(!buddy->mHasBlockListEntry)
+							{
+								buddy->mHasBlockListEntry = true;
+								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateBlockRule.1\">"
+									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+									<< "<BlockMask>" << buddy->mURI << "</BlockMask>"
+									<< "<PresenceOnly>1</PresenceOnly>"
+									<< "</Request>\n\n\n";								
+							}
+						}
+					}
+
+					if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies)
+					{
+						// Delete this entry from the local buddy list.  This should NOT invalidate the iterator,
+						// since it has already been incremented to the next entry.
+						deleteBuddy(buddy->mURI);
+					}
+
+				}
+				writeString(stream.str());
+			}
+		}
+	}
 }
 
 /////////////////////////////
 // Response/Event handlers
 
-void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle)
+void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID)
 {	
 	if(statusCode != 0)
 	{
@@ -2598,6 +3671,7 @@ void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusS
 	else
 	{
 		// Connector created, move forward.
+		LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL;
 		mConnectorHandle = connectorHandle;
 		if(getState() == stateConnectorStarting)
 		{
@@ -2606,7 +3680,7 @@ void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusS
 	}
 }
 
-void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle)
+void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases)
 { 
 	LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
 	
@@ -2627,7 +3701,8 @@ void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std
 	{
 		// Login succeeded, move forward.
 		mAccountHandle = accountHandle;
-		// MBW -- XXX -- This needs to wait until the LoginStateChangeEvent is received.
+		mNumberOfAliases = numberOfAliases;
+		// This needs to wait until the AccountLoginStateChangeEvent is received.
 //		if(getState() == stateLoggingIn)
 //		{
 //			setState(stateLoggedIn);
@@ -2635,106 +3710,91 @@ void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std
 	}
 }
 
-void LLVoiceClient::channelGetListResponse(int statusCode, std::string &statusString)
-{
+void LLVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
+{	
+	sessionState *session = findSessionBeingCreatedByURI(requestId);
+	
+	if(session)
+	{
+		session->mCreateInProgress = false;
+	}
+	
 	if(statusCode != 0)
 	{
-		LL_WARNS("Voice") << "Account.ChannelGetList response failure: " << statusString << LL_ENDL;
-		switchChannel();
+		LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
+		if(session)
+		{
+			session->mErrorStatusCode = statusCode;		
+			session->mErrorStatusString = statusString;
+			if(session == mAudioSession)
+			{
+				setState(stateJoinSessionFailed);
+			}
+			else
+			{
+				reapSession(session);
+			}
+		}
 	}
 	else
 	{
-		// Got the channel list, try to do a lookup.
-		std::string uri = findChannelURI(mChannelName);
-		if(uri.empty())
-		{	
-			// Lookup failed, can't join a channel for this area.
-			LL_INFOS("Voice") << "failed to map channel name: " << mChannelName << LL_ENDL;
-		}
-		else
+		LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL;
+		if(session)
 		{
-			// We have a sip URL for this area.
-			LL_INFOS("Voice") << "mapped channel " << mChannelName << " to URI "<< uri << LL_ENDL;
+			setSessionHandle(session, sessionHandle);
 		}
-		
-		// switchChannel with an empty uri string will do the right thing (leave channel and not rejoin)
-		switchChannel(uri);
 	}
 }
 
-void LLVoiceClient::sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle)
+void LLVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle)
 {	
+	sessionState *session = findSessionBeingCreatedByURI(requestId);
+	
+	if(session)
+	{
+		session->mCreateInProgress = false;
+	}
+	
 	if(statusCode != 0)
 	{
-		LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
-//		if(statusCode == 1015)
-//		{
-//			if(getState() == stateJoiningSession)
-//			{
-//				// this happened during a real join.  Going to sessionTerminated should cause a retry in appropriate cases.
-//				LL_WARNS("Voice") << "session handle \"" << sessionHandle << "\", mSessionStateEventHandle \"" << mSessionStateEventHandle << "\""<< LL_ENDL;
-//				if(!sessionHandle.empty())
-//				{
-//					// This session is bad.  Terminate it.
-//					mSessionHandle = sessionHandle;
-//					sessionTerminateByHandle(sessionHandle);
-//					setState(stateLeavingSession);
-//				}
-//				else if(!mSessionStateEventHandle.empty())
-//				{
-//					mSessionHandle = mSessionStateEventHandle;
-//					sessionTerminateByHandle(mSessionStateEventHandle);
-//					setState(stateLeavingSession);
-//				}
-//				else
-//				{
-//					setState(stateSessionTerminated);
-//				}
-//			}
-//			else
-//			{
-//				// We didn't think we were in the middle of a join.  Don't change state.
-//				LL_WARNS("Voice") << "Not in stateJoiningSession, ignoring" << LL_ENDL;
-//			}
-//		}
-//		else
+		LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL;
+		if(session)
 		{
-			mVivoxErrorStatusCode = statusCode;		
-			mVivoxErrorStatusString = statusString;
-			setState(stateJoinSessionFailed);
+			session->mErrorStatusCode = statusCode;		
+			session->mErrorStatusString = statusString;
+			if(session == mAudioSession)
+			{
+				setState(stateJoinSessionFailed);
+			}
+			else
+			{
+				reapSession(session);
+			}
 		}
 	}
 	else
 	{
-		LL_DEBUGS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL;
-		if(getState() == stateJoiningSession)
+		LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL;
+		if(session)
 		{
-			// This is also grabbed in the SessionStateChangeEvent handler, but it might be useful to have it early...
-			mSessionHandle = sessionHandle;
-		}
-		else
-		{
-			// We should never get a session.create response in any state except stateJoiningSession.  Things are out of sync.  Kill this session.
-			sessionTerminateByHandle(sessionHandle);
+			setSessionHandle(session, sessionHandle);
 		}
 	}
 }
 
-void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusString)
+void LLVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString)
 {
+	sessionState *session = findSession(requestId);
 	if(statusCode != 0)
 	{
 		LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
-//		if(statusCode == 1015)
-//		{
-//			LL_WARNS("Voice") << "terminating existing session" << LL_ENDL;
-//			sessionTerminate();
-//		}
-//		else
+		if(session)
 		{
-			mVivoxErrorStatusCode = statusCode;		
-			mVivoxErrorStatusString = statusString;
-			setState(stateJoinSessionFailed);
+			session->mMediaConnectInProgress = false;
+			session->mErrorStatusCode = statusCode;		
+			session->mErrorStatusString = statusString;
+			if(session == mAudioSession)
+				setState(stateJoinSessionFailed);
 		}
 	}
 	else
@@ -2743,27 +3803,12 @@ void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusSt
 	}
 }
 
-void LLVoiceClient::sessionTerminateResponse(int statusCode, std::string &statusString)
-{	
-	if(statusCode != 0)
-	{
-		LL_WARNS("Voice") << "Session.Terminate response failure: (" << statusCode << "): " << statusString << LL_ENDL;
-		if(getState() == stateLeavingSession)
-		{
-			// This is probably "(404): Server reporting Failure. Not a member of this conference."
-			// Do this so we don't get stuck.
-			setState(stateSessionTerminated);
-		}
-	}
-	
-}
-
 void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString)
 {	
 	if(statusCode != 0)
 	{
 		LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
-		// MBW -- XXX -- Should this ever fail?  do we care if it does?
+		// Should this ever fail?  do we care if it does?
 	}
 	
 	if(getState() == stateLoggingOut)
@@ -2777,7 +3822,7 @@ void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statu
 	if(statusCode != 0)
 	{
 		LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL;
-		// MBW -- XXX -- Should this ever fail?  do we care if it does?
+		// Should this ever fail?  do we care if it does?
 	}
 	
 	mConnected = false;
@@ -2788,109 +3833,277 @@ void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statu
 	}
 }
 
-void LLVoiceClient::sessionStateChangeEvent(
+void LLVoiceClient::sessionAddedEvent(
 		std::string &uriString, 
-		int statusCode, 
-		std::string &statusString, 
-		std::string &sessionHandle,
-		int state,  
+		std::string &alias, 
+		std::string &sessionHandle, 
+		std::string &sessionGroupHandle, 
 		bool isChannel, 
-		std::string &nameString)
+		bool incoming,
+		std::string &nameString,
+		std::string &applicationString)
 {
-	switch(state)
-	{
-		case 4:	// I see this when joining the session
-			LL_INFOS("Voice") << "joined session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << LL_ENDL;
+	sessionState *session = NULL;
 
-			// Session create succeeded, move forward.
-			mSessionStateEventHandle = sessionHandle;
-			mSessionStateEventURI = uriString;
-			if(sessionHandle == mSessionHandle)
+	LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL;
+	
+	session = addSession(uriString, sessionHandle);
+	if(session)
+	{
+		session->mGroupHandle = sessionGroupHandle;
+		session->mIsChannel = isChannel;
+		session->mIncoming = incoming;
+		session->mAlias = alias;
+			
+		// Generate a caller UUID -- don't need to do this for channels
+		if(!session->mIsChannel)
+		{
+			if(IDFromName(session->mSIPURI, session->mCallerID))
 			{
-				// This is the session we're joining.
-				if(getState() == stateJoiningSession)
-				{
-					setState(stateSessionJoined);
-					//RN: the uriString being returned by vivox here is actually your account uri, not the channel
-					// you are attempting to join, so ignore it
-					//LL_DEBUGS("Voice") << "received URI " << uriString << "(previously " << mSessionURI << ")" << LL_ENDL;
-					//mSessionURI = uriString;
-				}
+				// Normal URI(base64-encoded UUID) 
 			}
-			else if(sessionHandle == mNextSessionHandle)
+			else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID))
 			{
-//				LL_DEBUGS("Voice") << "received URI " << uriString << ", name " << nameString << " for next session (handle " << mNextSessionHandle << ")" << LL_ENDL;
+				// Wrong URI, but an alias is available.  Stash the incoming URI as an alternate
+				session->mAlternateSIPURI = session->mSIPURI;
+				
+				// and generate a proper URI from the ID.
+				setSessionURI(session, sipURIFromID(session->mCallerID));
 			}
 			else
 			{
-				LL_WARNS("Voice") << "joining unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << LL_ENDL;
-				// MBW -- XXX -- Should we send a Session.Terminate here?
+				LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL;
+				setUUIDFromStringHash(session->mCallerID, session->mSIPURI);
+				session->mSynthesizedCallerID = true;
+				
+				// Can't look up the name in this case -- we have to extract it from the URI.
+				std::string namePortion = nameFromsipURI(session->mSIPURI);
+				if(namePortion.empty())
+				{
+					// Didn't seem to be a SIP URI, just use the whole provided name.
+					namePortion = nameString;
+				}
+				
+				// Some incoming names may be separated with an underscore instead of a space.  Fix this.
+				LLStringUtil::replaceChar(namePortion, '_', ' ');
+				
+				// Act like we just finished resolving the name (this stores it in all the right places)
+				avatarNameResolved(session->mCallerID, namePortion);
 			}
-			
-		break;
-		case 5:	// I see this when leaving the session
-			LL_INFOS("Voice") << "left session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << LL_ENDL;
+		
+			LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL;
+
+			if(!session->mSynthesizedCallerID)
+			{
+				// If we got here, we don't have a proper name.  Initiate a lookup.
+				lookupName(session->mCallerID);
+			}
+		}
+	}
+}
+
+void LLVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle)
+{
+	LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL;
+	
+#if USE_SESSION_GROUPS
+	if(mMainSessionGroupHandle.empty())
+	{
+		// This is the first (i.e. "main") session group.  Save its handle.
+		mMainSessionGroupHandle = sessionGroupHandle;
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL;
+	}
+#endif
+}
+
+void LLVoiceClient::joinedAudioSession(sessionState *session)
+{
+	if(mAudioSession != session)
+	{
+		sessionState *oldSession = mAudioSession;
+
+		mAudioSession = session;
+		mAudioSessionChanged = true;
+
+		// The old session may now need to be deleted.
+		reapSession(oldSession);
+	}
+	
+	// This is the session we're joining.
+	if(getState() == stateJoiningSession)
+	{
+		setState(stateSessionJoined);
+		
+		// SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now.
+		// Add the current user as a participant here.
+		participantState *participant = session->addParticipant(sipURIFromName(mAccountName));
+		if(participant)
+		{
+			participant->mIsSelf = true;
+			lookupName(participant->mAvatarID);
 
-			// Set the session handle to the empty string.  If we get back to stateJoiningSession, we'll want to wait for the new session handle.
-			if(sessionHandle == mSessionHandle)
+			LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName 
+					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
+		}
+		
+		if(!session->mIsChannel)
+		{
+			// this is a p2p session.  Make sure the other end is added as a participant.
+			participantState *participant = session->addParticipant(session->mSIPURI);
+			if(participant)
 			{
-				// MBW -- XXX -- I think this is no longer necessary, now that we've got mNextSessionURI/mNextSessionHandle
-				// mSessionURI.clear();
-				// clear the session handle here just for sanity.
-				mSessionHandle.clear();
-				if(mSessionResetOnClose)
+				if(participant->mAvatarIDValid)
 				{
-					mSessionResetOnClose = false;
-					mNonSpatialChannel = false;
-					mNextSessionSpatial = true;
-					parcelChanged();
+					lookupName(participant->mAvatarID);
+				}
+				else if(!session->mName.empty())
+				{
+					participant->mDisplayName = session->mName;
+					avatarNameResolved(participant->mAvatarID, session->mName);
 				}
-			
-				removeAllParticipants();
 				
-				switch(getState())
+				// TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here?
+				LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName 
+						<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
+			}
+		}
+	}
+}
+
+void LLVoiceClient::sessionRemovedEvent(
+	std::string &sessionHandle, 
+	std::string &sessionGroupHandle)
+{
+	LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL;
+	
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		leftAudioSession(session);
+
+		// This message invalidates the session's handle.  Set it to empty.
+		setSessionHandle(session);
+		
+		// Reset the media state (we now have no info)
+		session->mMediaStreamState = streamStateUnknown;
+		session->mTextStreamState = streamStateUnknown;
+		
+		// Conditionally delete the session
+		reapSession(session);
+	}
+	else
+	{
+		LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL;
+	}
+}
+
+void LLVoiceClient::reapSession(sessionState *session)
+{
+	if(session)
+	{
+		if(!session->mHandle.empty())
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL;
+		}
+		else if(session->mCreateInProgress)
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL;
+		}
+		else if(session->mMediaConnectInProgress)
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL;
+		}
+		else if(session == mAudioSession)
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL;
+		}
+		else if(session == mNextAudioSession)
+		{
+			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL;
+		}
+		else
+		{
+			// TODO: Question: Should we check for queued text messages here?
+			// We don't have a reason to keep tracking this session, so just delete it.
+			LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL;
+			deleteSession(session);
+			session = NULL;
+		}	
+	}
+	else
+	{
+//		LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL;
+	}
+}
+
+// Returns true if the session seems to indicate we've moved to a region on a different voice server
+bool LLVoiceClient::sessionNeedsRelog(sessionState *session)
+{
+	bool result = false;
+	
+	if(session != NULL)
+	{
+		// Only make this check for spatial channels (so it won't happen for group or p2p calls)
+		if(session->mIsSpatial)
+		{
+			std::string::size_type atsign;
+			
+			atsign = session->mSIPURI.find("@");
+			
+			if(atsign != std::string::npos)
+			{
+				std::string urihost = session->mSIPURI.substr(atsign + 1);
+				if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str()))
 				{
-					case stateJoiningSession:
-					case stateSessionJoined:
-					case stateRunning:
-					case stateLeavingSession:
-					case stateJoinSessionFailed:
-					case stateJoinSessionFailedWaiting:
-						// normal transition
-						LL_INFOS("Voice") << "left session " << sessionHandle << "in state " << state2string(getState()) << LL_ENDL;
-						setState(stateSessionTerminated);
-					break;
+					// The hostname in this URI is different from what we expect.  This probably means we need to relog.
 					
-					case stateSessionTerminated:
-						// this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
-						LL_WARNS("Voice") << "left session " << sessionHandle << "in state " << state2string(getState()) << LL_ENDL;
-					break;
+					// We could make a ProvisionVoiceAccountRequest and compare the result with the current values of
+					// mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator.
 					
-					default:
-						LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
-						setState(stateSessionTerminated);
-					break;
+					result = true;
 				}
-
-				// store status values for later notification of observers
-				mVivoxErrorStatusCode = statusCode;		
-				mVivoxErrorStatusString = statusString;
-			}
-			else
-			{
-				LL_INFOS("Voice") << "leaving unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << LL_ENDL;
 			}
+		}
+	}
+	
+	return result;
+}
 
-			mSessionStateEventHandle.clear();
-			mSessionStateEventURI.clear();
-		break;
-		default:
-			LL_WARNS("Voice") << "unknown state: " << state << LL_ENDL;
-		break;
+void LLVoiceClient::leftAudioSession(
+	sessionState *session)
+{
+	if(mAudioSession == session)
+	{
+		switch(getState())
+		{
+			case stateJoiningSession:
+			case stateSessionJoined:
+			case stateRunning:
+			case stateLeavingSession:
+			case stateJoinSessionFailed:
+			case stateJoinSessionFailedWaiting:
+				// normal transition
+				LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
+				setState(stateSessionTerminated);
+			break;
+			
+			case stateSessionTerminated:
+				// this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
+				LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL;
+			break;
+			
+			default:
+				LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
+				setState(stateSessionTerminated);
+			break;
+		}
 	}
 }
 
-void LLVoiceClient::loginStateChangeEvent(
+void LLVoiceClient::accountLoginStateChangeEvent(
 		std::string &accountHandle, 
 		int statusCode, 
 		std::string &statusString, 
@@ -2923,110 +4136,571 @@ void LLVoiceClient::loginStateChangeEvent(
 	}
 }
 
-void LLVoiceClient::sessionNewEvent(
-		std::string &accountHandle, 
-		std::string &eventSessionHandle, 
-		int state, 
-		std::string &nameString, 
-		std::string &uriString)
+void LLVoiceClient::mediaStreamUpdatedEvent(
+	std::string &sessionHandle, 
+	std::string &sessionGroupHandle, 
+	int statusCode, 
+	std::string &statusString, 
+	int state, 
+	bool incoming)
 {
-	LL_DEBUGS("Voice") << "state is " << state << LL_ENDL;
+	sessionState *session = findSession(sessionHandle);
 	
-	switch(state)
+	LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL;
+	
+	if(session)
 	{
-		case 0:
-			{
-				LL_DEBUGS("Voice") << "session handle = " << eventSessionHandle << ", name = " << nameString << ", uri = " << uriString << LL_ENDL;
+		// We know about this session
+		
+		// Save the state for later use
+		session->mMediaStreamState = state;
+		
+		switch(statusCode)
+		{
+			case 0:
+			case 200:
+				// generic success
+				// Don't change the saved error code (it may have been set elsewhere)
+			break;
+			default:
+				// save the status code for later
+				session->mErrorStatusCode = statusCode;
+			break;
+		}
+		
+		switch(state)
+		{
+			case streamStateIdle:
+				// Standard "left audio session"
+				session->mVoiceEnabled = false;
+				session->mMediaConnectInProgress = false;
+				leftAudioSession(session);
+			break;
+
+			case streamStateConnected:
+				session->mVoiceEnabled = true;
+				session->mMediaConnectInProgress = false;
+				joinedAudioSession(session);
+			break;
+			
+			case streamStateRinging:
+				if(incoming)
+				{
+					// Send the voice chat invite to the GUI layer
+					// TODO: Question: Should we correlate with the mute list here?
+					session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID);
+					session->mVoiceInvitePending = true;
+					if(session->mName.empty())
+					{
+						lookupName(session->mCallerID);
+					}
+					else
+					{
+						// Act like we just finished resolving the name
+						avatarNameResolved(session->mCallerID, session->mName);
+					}
+				}
+			break;
+			
+			default:
+				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
+			break;
+			
+		}
+		
+	}
+	else
+	{
+		LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL;
+	}
+}
 
-				LLUUID caller_id;
-				if(IDFromName(nameString, caller_id))
+void LLVoiceClient::textStreamUpdatedEvent(
+	std::string &sessionHandle, 
+	std::string &sessionGroupHandle, 
+	bool enabled,
+	int state, 
+	bool incoming)
+{
+	sessionState *session = findSession(sessionHandle);
+	
+	if(session)
+	{
+		// Save the state for later use
+		session->mTextStreamState = state;
+		
+		// We know about this session
+		switch(state)
+		{
+			case 0:	// We see this when the text stream closes
+				LL_DEBUGS("Voice") << "stream closed" << LL_ENDL;
+			break;
+			
+			case 1:	// We see this on an incoming call from the Connector
+				// Try to send any text messages queued for this session.
+				sendQueuedTextMessages(session);
+
+				// Send the text chat invite to the GUI layer
+				// TODO: Question: Should we correlate with the mute list here?
+				session->mTextInvitePending = true;
+				if(session->mName.empty())
 				{
-					gIMMgr->inviteToSession(
-						LLIMMgr::computeSessionID(
-							IM_SESSION_P2P_INVITE,
-							caller_id),
-						LLStringUtil::null,
-						caller_id, 
-						LLStringUtil::null, 
-						IM_SESSION_P2P_INVITE, 
-						LLIMMgr::INVITATION_TYPE_VOICE,
-						eventSessionHandle);
+					lookupName(session->mCallerID);
 				}
 				else
 				{
-					LL_WARNS("Voice") << "Could not generate caller id from uri " << uriString << LL_ENDL;
+					// Act like we just finished resolving the name
+					avatarNameResolved(session->mCallerID, session->mName);
 				}
-			}
-		break;
-		
-		default:
-			LL_WARNS("Voice") << "unknown state: " << state << LL_ENDL;
-		break;
+			break;
+
+			default:
+				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL;
+			break;
+			
+		}
 	}
 }
 
-void LLVoiceClient::participantStateChangeEvent(
+void LLVoiceClient::participantAddedEvent(
+		std::string &sessionHandle, 
+		std::string &sessionGroupHandle, 
 		std::string &uriString, 
-		int statusCode, 
-		std::string &statusString, 
-		int state,  
+		std::string &alias, 
 		std::string &nameString, 
 		std::string &displayNameString, 
 		int participantType)
 {
-	participantState *participant = NULL;
-	LL_DEBUGS("Voice") << "state is " << state << LL_ENDL;
-
-	switch(state)
+	sessionState *session = findSession(sessionHandle);
+	if(session)
 	{
-		case 7:	// I see this when a participant joins
-			participant = addParticipant(uriString);
-			if(participant)
+		participantState *participant = session->addParticipant(uriString);
+		if(participant)
+		{
+			participant->mAccountName = nameString;
+
+			LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName 
+					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
+
+			if(participant->mAvatarIDValid)
 			{
-				participant->mName = nameString;
-				LL_DEBUGS("Voice") << "added participant \"" << participant->mName 
-						<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
+				// Initiate a lookup
+				lookupName(participant->mAvatarID);
 			}
-		break;
-		case 9:	// I see this when a participant leaves
-			participant = findParticipant(uriString);
-			if(participant)
+			else
 			{
-				removeParticipant(participant);
+				// If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work.
+				std::string namePortion = nameFromsipURI(uriString);
+				if(namePortion.empty())
+				{
+					// Problem with the SIP URI, fall back to the display name
+					namePortion = displayNameString;
+				}
+				if(namePortion.empty())
+				{
+					// Problems with both of the above, fall back to the account name
+					namePortion = nameString;
+				}
+				
+				// Set the display name (which is a hint to the active speakers window not to do its own lookup)
+				participant->mDisplayName = namePortion;
+				avatarNameResolved(participant->mAvatarID, namePortion);
 			}
-		break;
-		default:
-			LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
-		break;
+		}
 	}
 }
 
-void LLVoiceClient::participantPropertiesEvent(
+void LLVoiceClient::participantRemovedEvent(
+		std::string &sessionHandle, 
+		std::string &sessionGroupHandle, 
 		std::string &uriString, 
-		int statusCode, 
-		std::string &statusString, 
-		bool isLocallyMuted, 
+		std::string &alias, 
+		std::string &nameString)
+{
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		participantState *participant = session->findParticipant(uriString);
+		if(participant)
+		{
+			session->removeParticipant(participant);
+		}
+		else
+		{
+			LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL;
+		}
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
+	}
+}
+
+
+void LLVoiceClient::participantUpdatedEvent(
+		std::string &sessionHandle, 
+		std::string &sessionGroupHandle, 
+		std::string &uriString, 
+		std::string &alias, 
 		bool isModeratorMuted, 
 		bool isSpeaking, 
 		int volume, 
 		F32 energy)
 {
-	participantState *participant = findParticipant(uriString);
-	if(participant)
+	sessionState *session = findSession(sessionHandle);
+	if(session)
 	{
-		participant->mPTT = !isLocallyMuted;
-		participant->mIsSpeaking = isSpeaking;
-		participant->mIsModeratorMuted = isModeratorMuted;
-		if (isSpeaking)
+		participantState *participant = session->findParticipant(uriString);
+		
+		if(participant)
+		{
+			participant->mIsSpeaking = isSpeaking;
+			participant->mIsModeratorMuted = isModeratorMuted;
+
+			// SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false
+			if (isSpeaking)
+			{
+				participant->mSpeakingTimeout.reset();
+				participant->mPower = energy;
+			}
+			else
+			{
+				participant->mPower = 0.0f;
+			}
+			participant->mVolume = volume;
+		}
+		else
 		{
-			participant->mSpeakingTimeout.reset();
+			LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL;
 		}
-		participant->mPower = energy;
-		participant->mVolume = volume;
 	}
 	else
 	{
-		LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL;
+		LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL;
+	}
+}
+
+void LLVoiceClient::buddyPresenceEvent(
+		std::string &uriString, 
+		std::string &alias, 
+		std::string &statusString,
+		std::string &applicationString)
+{
+	buddyListEntry *buddy = findBuddy(uriString);
+	
+	if(buddy)
+	{
+		LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL;
+		LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
+
+		if(applicationString.empty())
+		{
+			// This presence event is from a client that doesn't set up the Application string.  Do things the old-skool way.
+			// NOTE: this will be needed to support people who aren't on the 3010-class SDK yet.
+
+			if ( stricmp("Unknown", statusString.c_str())== 0) 
+			{
+				// User went offline with a non-SLim-enabled viewer.
+				buddy->mOnlineSL = false;
+			}
+			else if ( stricmp("Online", statusString.c_str())== 0) 
+			{
+				// User came online with a non-SLim-enabled viewer.
+				buddy->mOnlineSL = true;
+			}
+			else
+			{
+				// If the user is online through SLim, their status will be "Online-slc", "Away", or something else.
+				// NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string.
+				buddy->mOnlineSLim = true;
+			} 
+		}
+		else if(applicationString.find("SecondLifeViewer") != std::string::npos)
+		{
+			// This presence event is from a viewer that sets the application string
+			if ( stricmp("Unknown", statusString.c_str())== 0) 
+			{
+				// Viewer says they're offline
+				buddy->mOnlineSL = false;
+			}
+			else
+			{
+				// Viewer says they're online
+				buddy->mOnlineSL = true;
+			}
+		}
+		else
+		{
+			// This presence event is from something which is NOT the SL viewer (assume it's SLim).
+			if ( stricmp("Unknown", statusString.c_str())== 0) 
+			{
+				// SLim says they're offline
+				buddy->mOnlineSLim = false;
+			}
+			else
+			{
+				// SLim says they're online
+				buddy->mOnlineSLim = true;
+			}
+		} 
+
+		LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL;
+		
+		// HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change.
+		LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID));
+
+		notifyFriendObservers();
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL;
+	}	
+}
+
+void LLVoiceClient::messageEvent(
+		std::string &sessionHandle, 
+		std::string &uriString, 
+		std::string &alias, 
+		std::string &messageHeader, 
+		std::string &messageBody,
+		std::string &applicationString)
+{
+	LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL;
+//	LL_DEBUGS("Voice") << "    header " << messageHeader << ", body: \n" << messageBody << LL_ENDL;
+	
+	if(messageHeader.find("text/html") != std::string::npos)
+	{
+		std::string rawMessage;
+
+		{
+			const std::string startMarker = "<body";
+			const std::string startMarker2 = ">";
+			const std::string endMarker = "</body>";
+			const std::string startSpan = "<span";
+			const std::string endSpan = "</span>";
+			std::string::size_type start;
+			std::string::size_type end;
+			
+			// Default to displaying the raw string, so the message gets through.
+			rawMessage = messageBody;
+
+			// Find the actual message text within the XML fragment
+			start = messageBody.find(startMarker);
+			start = messageBody.find(startMarker2, start);
+			end = messageBody.find(endMarker);
+
+			if(start != std::string::npos)
+			{
+				start += startMarker2.size();
+				
+				if(end != std::string::npos)
+					end -= start;
+					
+				rawMessage.assign(messageBody, start, end);
+			}
+			else 
+			{
+				// Didn't find a <body>, try looking for a <span> instead.
+				start = messageBody.find(startSpan);
+				start = messageBody.find(startMarker2, start);
+				end = messageBody.find(endSpan);
+				
+				if(start != std::string::npos)
+				{
+					start += startMarker2.size();
+					
+					if(end != std::string::npos)
+						end -= start;
+					
+					rawMessage.assign(messageBody, start, end);
+				}			
+			}
+		}	
+		
+//		LL_DEBUGS("Voice") << "    raw message = \n" << rawMessage << LL_ENDL;
+
+		// strip formatting tags
+		{
+			std::string::size_type start;
+			std::string::size_type end;
+			
+			while((start = rawMessage.find('<')) != std::string::npos)
+			{
+				if((end = rawMessage.find('>', start + 1)) != std::string::npos)
+				{
+					// Strip out the tag
+					rawMessage.erase(start, (end + 1) - start);
+				}
+				else
+				{
+					// Avoid an infinite loop
+					break;
+				}
+			}
+		}
+		
+		// Decode ampersand-escaped chars
+		{
+			std::string::size_type mark = 0;
+
+			// The text may contain text encoded with &lt;, &gt;, and &amp;
+			mark = 0;
+			while((mark = rawMessage.find("&lt;", mark)) != std::string::npos)
+			{
+				rawMessage.replace(mark, 4, "<");
+				mark += 1;
+			}
+			
+			mark = 0;
+			while((mark = rawMessage.find("&gt;", mark)) != std::string::npos)
+			{
+				rawMessage.replace(mark, 4, ">");
+				mark += 1;
+			}
+			
+			mark = 0;
+			while((mark = rawMessage.find("&amp;", mark)) != std::string::npos)
+			{
+				rawMessage.replace(mark, 5, "&");
+				mark += 1;
+			}
+		}
+		
+		// strip leading/trailing whitespace (since we always seem to get a couple newlines)
+		LLStringUtil::trim(rawMessage);
+		
+//		LL_DEBUGS("Voice") << "    stripped message = \n" << rawMessage << LL_ENDL;
+		
+		sessionState *session = findSession(sessionHandle);
+		if(session)
+		{
+			bool is_busy = gAgent.getBusy();
+			bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat);
+			bool is_linden = LLMuteList::getInstance()->isLinden(session->mName);
+			bool quiet_chat = false;
+			LLChat chat;
+
+			chat.mMuted = is_muted && !is_linden;
+			
+			if(!chat.mMuted)
+			{
+				chat.mFromID = session->mCallerID;
+				chat.mFromName = session->mName;
+				chat.mSourceType = CHAT_SOURCE_AGENT;
+
+				if(is_busy && !is_linden)
+				{
+					quiet_chat = true;
+					// TODO: Question: Return busy mode response here?  Or maybe when session is started instead?
+				}
+				
+				std::string fullMessage = std::string(": ") + rawMessage;
+				
+				LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL;
+				gIMMgr->addMessage(session->mIMSessionID,
+						session->mCallerID,
+						session->mName.c_str(),
+						fullMessage.c_str(),
+						LLStringUtil::null,		// default arg
+						IM_NOTHING_SPECIAL,		// default arg
+						0,						// default arg
+						LLUUID::null,			// default arg
+						LLVector3::zero,		// default arg
+						true);					// prepend name and make it a link to the user's profile
+
+				chat.mText = std::string("IM: ") + session->mName + std::string(": ") + rawMessage;
+				// If the chat should come in quietly (i.e. we're in busy mode), pretend it's from a local agent.
+				LLFloaterChat::addChat( chat, TRUE, quiet_chat );
+			}
+		}		
+	}
+}
+
+void LLVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType)
+{
+	sessionState *session = findSession(sessionHandle);
+	
+	if(session)
+	{
+		participantState *participant = session->findParticipant(uriString);
+		if(participant)
+		{
+			if (!stricmp(notificationType.c_str(), "Typing"))
+			{
+				// Other end started typing
+				// TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart().
+				// It requires an LLIMInfo for the message, which we don't have here.
+			}
+			else if (!stricmp(notificationType.c_str(), "NotTyping"))
+			{
+				// Other end stopped typing
+				// TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop().
+				// It requires an LLIMInfo for the message, which we don't have here.
+			}
+			else
+			{
+				LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
+			}
+		}
+		else
+		{
+			LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL;
+		}
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL;
+	}
+}
+
+void LLVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType)
+{
+	buddyListEntry *buddy = findBuddy(buddyURI);
+	
+	if(!buddy)
+	{
+		// Couldn't find buddy by URI, try converting the alias...
+		if(!alias.empty())
+		{
+			LLUUID id;
+			if(IDFromName(alias, id))
+			{
+				buddy = findBuddy(id);
+			}
+		}
+	}
+	
+	if(buddy)
+	{
+		std::ostringstream stream;
+		
+		if(buddy->mCanSeeMeOnline)
+		{
+			// Sending the response will create an auto-accept rule
+			buddy->mHasAutoAcceptListEntry = true;
+		}
+		else
+		{
+			// Sending the response will create a block rule
+			buddy->mHasBlockListEntry = true;
+		}
+		
+		if(buddy->mInSLFriends)
+		{
+			buddy->mInVivoxBuddies = true;
+		}
+		
+		stream
+			<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.SendSubscriptionReply.1\">"
+				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
+				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>"
+				<< "<RuleType>" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "</RuleType>"
+				<< "<AutoAccept>"<< (buddy->mInSLFriends?"1":"0")<< "</AutoAccept>"
+				<< "<SubscriptionHandle>" << subscriptionHandle << "</SubscriptionHandle>"
+			<< "</Request>"
+			<< "\n\n\n";
+			
+		writeString(stream.str());
 	}
 }
 
@@ -3036,155 +4710,226 @@ void LLVoiceClient::auxAudioPropertiesEvent(F32 energy)
 	mTuningEnergy = energy;
 }
 
+void LLVoiceClient::buddyListChanged()
+{
+	// This is called after we receive a BuddyAndGroupListChangedEvent.
+	mBuddyListMapPopulated = true;
+	mFriendsListDirty = true;
+}
+
 void LLVoiceClient::muteListChanged()
 {
 	// The user's mute list has been updated.  Go through the current participant list and sync it with the mute list.
-
-	participantMap::iterator iter = mParticipantMap.begin();
-	
-	for(; iter != mParticipantMap.end(); iter++)
+	if(mAudioSession)
 	{
-		participantState *p = iter->second;
+		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
 		
-		// Check to see if this participant is on the mute list already
-		updateMuteState(p);
+		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
+		{
+			participantState *p = iter->second;
+			
+			// Check to see if this participant is on the mute list already
+			if(p->updateMuteState())
+				mAudioSession->mVolumeDirty = true;
+		}
+	}
+}
+
+void LLVoiceClient::updateFriends(U32 mask)
+{
+	if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS))
+	{
+		// Just resend the whole friend list to the daemon
+		mFriendsListDirty = true;
 	}
 }
 
 /////////////////////////////
 // Managing list of participants
 LLVoiceClient::participantState::participantState(const std::string &uri) : 
-	 mURI(uri), mPTT(false), mIsSpeaking(false), mIsModeratorMuted(false), mLastSpokeTimestamp(0.f), mPower(0.f), mVolume(0), mServiceType(serviceTypeUnknown),
-	 mOnMuteList(false), mUserVolume(100), mVolumeDirty(false), mAvatarIDValid(false)
+	 mURI(uri), 
+	 mPTT(false), 
+	 mIsSpeaking(false), 
+	 mIsModeratorMuted(false), 
+	 mLastSpokeTimestamp(0.f), 
+	 mPower(0.f), 
+	 mVolume(0), 
+	 mOnMuteList(false), 
+	 mUserVolume(100), 
+	 mVolumeDirty(false), 
+	 mAvatarIDValid(false),
+	 mIsSelf(false)
 {
 }
 
-LLVoiceClient::participantState *LLVoiceClient::addParticipant(const std::string &uri)
+LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(const std::string &uri)
 {
 	participantState *result = NULL;
-
-	participantMap::iterator iter = mParticipantMap.find(uri);
+	bool useAlternateURI = false;
 	
-	if(iter != mParticipantMap.end())
+	// Note: this is mostly the body of LLVoiceClient::sessionState::findParticipant(), but since we need to know if it
+	// matched the alternate SIP URI (so we can add it properly), we need to reproduce it here.
 	{
-		// Found a matching participant already in the map.
-		result = iter->second;
-	}
+		participantMap::iterator iter = mParticipantsByURI.find(&uri);
 
+		if(iter == mParticipantsByURI.end())
+		{
+			if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
+			{
+				// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
+				// Use mSIPURI instead, since it will be properly encoded.
+				iter = mParticipantsByURI.find(&(mSIPURI));
+				useAlternateURI = true;
+			}
+		}
+
+		if(iter != mParticipantsByURI.end())
+		{
+			result = iter->second;
+		}
+	}
+		
 	if(!result)
 	{
 		// participant isn't already in one list or the other.
-		result = new participantState(uri);
-		mParticipantMap.insert(participantMap::value_type(uri, result));
-		mParticipantMapChanged = true;
+		result = new participantState(useAlternateURI?mSIPURI:uri);
+		mParticipantsByURI.insert(participantMap::value_type(&(result->mURI), result));
+		mParticipantsChanged = true;
 		
 		// Try to do a reverse transform on the URI to get the GUID back.
 		{
 			LLUUID id;
-			if(IDFromName(uri, id))
+			if(IDFromName(result->mURI, id))
 			{
 				result->mAvatarIDValid = true;
 				result->mAvatarID = id;
 
-				updateMuteState(result);
+				if(result->updateMuteState())
+					mVolumeDirty = true;
+			}
+			else
+			{
+				// Create a UUID by hashing the URI, but do NOT set mAvatarIDValid.
+				// This tells both code in LLVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache.
+				setUUIDFromStringHash(result->mAvatarID, uri);
 			}
 		}
 		
+		mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result));
+		
 		LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
 	}
 	
 	return result;
 }
 
-void LLVoiceClient::updateMuteState(participantState *p)
+bool LLVoiceClient::participantState::updateMuteState()
 {
-	if(p->mAvatarIDValid)
+	bool result = false;
+	
+	if(mAvatarIDValid)
 	{
-		bool isMuted = LLMuteList::getInstance()->isMuted(p->mAvatarID, LLMute::flagVoiceChat);
-		if(p->mOnMuteList != isMuted)
+		bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat);
+		if(mOnMuteList != isMuted)
 		{
-			p->mOnMuteList = isMuted;
-			p->mVolumeDirty = true;
+			mOnMuteList = isMuted;
 			mVolumeDirty = true;
+			result = true;
 		}
 	}
+	return result;
 }
 
-void LLVoiceClient::removeParticipant(LLVoiceClient::participantState *participant)
+void LLVoiceClient::sessionState::removeParticipant(LLVoiceClient::participantState *participant)
 {
 	if(participant)
 	{
-		participantMap::iterator iter = mParticipantMap.find(participant->mURI);
-				
+		participantMap::iterator iter = mParticipantsByURI.find(&(participant->mURI));
+		participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(&(participant->mAvatarID));
+		
 		LL_DEBUGS("Voice") << "participant \"" << participant->mURI <<  "\" (" << participant->mAvatarID << ") removed." << LL_ENDL;
-
-		mParticipantMap.erase(iter);
-		delete participant;
-		mParticipantMapChanged = true;
+		
+		if(iter == mParticipantsByURI.end())
+		{
+			LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL;
+		}
+		else if(iter2 == mParticipantsByUUID.end())
+		{
+			LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL;
+		}
+		else if(iter->second != iter2->second)
+		{
+			LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL;
+		}
+		else
+		{
+			mParticipantsByURI.erase(iter);
+			mParticipantsByUUID.erase(iter2);
+			
+			delete participant;
+			mParticipantsChanged = true;
+		}
 	}
 }
 
-void LLVoiceClient::removeAllParticipants()
+void LLVoiceClient::sessionState::removeAllParticipants()
 {
 	LL_DEBUGS("Voice") << "called" << LL_ENDL;
 
-	while(!mParticipantMap.empty())
+	while(!mParticipantsByURI.empty())
+	{
+		removeParticipant(mParticipantsByURI.begin()->second);
+	}
+	
+	if(!mParticipantsByUUID.empty())
 	{
-		removeParticipant(mParticipantMap.begin()->second);
+		LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL
 	}
 }
 
 LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
 {
-	return &mParticipantMap;
+	participantMap *result = NULL;
+	if(mAudioSession)
+	{
+		result = &(mAudioSession->mParticipantsByURI);
+	}
+	return result;
 }
 
 
-LLVoiceClient::participantState *LLVoiceClient::findParticipant(const std::string &uri)
+LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri)
 {
 	participantState *result = NULL;
 	
-	// Don't find any participants if we're not connected.  This is so that we don't continue to get stale data
-	// after the daemon dies.
-	if(mConnected)
+	participantMap::iterator iter = mParticipantsByURI.find(&uri);
+
+	if(iter == mParticipantsByURI.end())
 	{
-		participantMap::iterator iter = mParticipantMap.find(uri);
-	
-		if(iter != mParticipantMap.end())
+		if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI))
 		{
-			result = iter->second;
+			// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant.
+			// Look up the other URI
+			iter = mParticipantsByURI.find(&(mSIPURI));
 		}
 	}
-			
+
+	if(iter != mParticipantsByURI.end())
+	{
+		result = iter->second;
+	}
+		
 	return result;
 }
 
-
-LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvatar *avatar)
+LLVoiceClient::participantState* LLVoiceClient::sessionState::findParticipantByID(const LLUUID& id)
 {
 	participantState * result = NULL;
+	participantUUIDMap::iterator iter = mParticipantsByUUID.find(&id);
 
-	// You'd think this would work, but it doesn't...
-//	std::string uri = sipURIFromAvatar(avatar);
-	
-	// Currently, the URI is just the account name.
-	std::string loginName = nameFromAvatar(avatar);
-	result = findParticipant(loginName);
-	
-	if(result != NULL)
+	if(iter != mParticipantsByUUID.end())
 	{
-		if(!result->mAvatarIDValid)
-		{
-			result->mAvatarID = avatar->getID();
-			result->mAvatarIDValid = true;
-			
-			// We just figured out the avatar ID, so the participant list has "changed" from the perspective of anyone who uses that to identify participants.
-			mParticipantMapChanged = true;
-			
-			updateMuteState(result);
-		}
-		
-
+		result = iter->second;
 	}
 
 	return result;
@@ -3193,43 +4938,19 @@ LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvat
 LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id)
 {
 	participantState * result = NULL;
-
-	// Currently, the URI is just the account name.
-	std::string loginName = nameFromID(id);
-	result = findParticipant(loginName);
-
-	return result;
-}
-
-
-void LLVoiceClient::clearChannelMap(void)
-{
-	mChannelMap.clear();
-}
-
-void LLVoiceClient::addChannelMapEntry(std::string &name, std::string &uri)
-{
-	LL_DEBUGS("Voice") << "Adding channel name mapping: " << name << " -> " << uri << LL_ENDL;
-	mChannelMap.insert(channelMap::value_type(name, uri));
-}
-
-std::string LLVoiceClient::findChannelURI(std::string &name)
-{
-	std::string result;
 	
-	channelMap::iterator iter = mChannelMap.find(name);
-
-	if(iter != mChannelMap.end())
+	if(mAudioSession)
 	{
-		result = iter->second;
+		result = mAudioSession->findParticipantByID(id);
 	}
 	
 	return result;
 }
 
+
 void LLVoiceClient::parcelChanged()
 {
-	if(getState() >= stateLoggedIn)
+	if(getState() >= stateNoChannel)
 	{
 		// If the user is logged in, start a channel lookup.
 		LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
@@ -3243,7 +4964,7 @@ void LLVoiceClient::parcelChanged()
 	}
 	else
 	{
-		// The transition to stateLoggedIn needs to kick this off again.
+		// The transition to stateNoChannel needs to kick this off again.
 		LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL;
 	}
 }
@@ -3251,12 +4972,17 @@ void LLVoiceClient::parcelChanged()
 void LLVoiceClient::switchChannel(
 	std::string uri,
 	bool spatial,
-	bool noReconnect,
+	bool no_reconnect,
+	bool is_p2p,
 	std::string hash)
 {
 	bool needsSwitch = false;
 	
-	LL_DEBUGS("Voice") << "called in state " << state2string(getState()) << " with uri \"" << uri << "\"" << LL_ENDL;
+	LL_DEBUGS("Voice") 
+		<< "called in state " << state2string(getState()) 
+		<< " with uri \"" << uri << "\"" 
+		<< (spatial?", spatial is true":", spatial is false")
+		<< LL_ENDL;
 	
 	switch(getState())
 	{
@@ -3266,18 +4992,18 @@ void LLVoiceClient::switchChannel(
 			// Always switch to the new URI from these states.
 			needsSwitch = true;
 		break;
-		
+
 		default:
 			if(mSessionTerminateRequested)
 			{
 				// If a terminate has been requested, we need to compare against where the URI we're already headed to.
-				if(mNextSessionURI != uri)
+				if(mNextAudioSession && (mNextAudioSession->mSIPURI != uri))
 					needsSwitch = true;
 			}
 			else
 			{
 				// Otherwise, compare against the URI we're in now.
-				if(mSessionURI != uri)
+				if(mAudioSession && (mAudioSession->mSIPURI != uri))
 					needsSwitch = true;
 			}
 		break;
@@ -3285,22 +5011,28 @@ void LLVoiceClient::switchChannel(
 	
 	if(needsSwitch)
 	{
-		mNextSessionURI = uri;
-		mNextSessionHash = hash;
-		mNextSessionHandle.clear();
-		mNextP2PSessionURI.clear();
-		mNextSessionSpatial = spatial;
-		mNextSessionNoReconnect = noReconnect;
-		
 		if(uri.empty())
 		{
 			// Leave any channel we may be in
 			LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL;
+
+			sessionState *oldSession = mNextAudioSession;
+			mNextAudioSession = NULL;
+
+			// The old session may now need to be deleted.
+			reapSession(oldSession);
+
 			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
 		}
 		else
 		{
 			LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL;
+
+			mNextAudioSession = addSession(uri);
+			mNextAudioSession->mHash = hash;
+			mNextAudioSession->mIsSpatial = spatial;
+			mNextAudioSession->mReconnect = !no_reconnect;
+			mNextAudioSession->mIsP2P = is_p2p;
 		}
 		
 		if(getState() <= stateNoChannel)
@@ -3315,15 +5047,10 @@ void LLVoiceClient::switchChannel(
 	}
 }
 
-void LLVoiceClient::joinSession(std::string handle, std::string uri)
+void LLVoiceClient::joinSession(sessionState *session)
 {
-	mNextSessionURI.clear();
-	mNextSessionHash.clear();
-	mNextP2PSessionURI = uri;
-	mNextSessionHandle = handle;
-	mNextSessionSpatial = false;
-	mNextSessionNoReconnect = false;
-
+	mNextAudioSession = session;
+	
 	if(getState() <= stateNoChannel)
 	{
 		// We're already set up to join a channel, just needed to fill in the session handle
@@ -3339,7 +5066,7 @@ void LLVoiceClient::setNonSpatialChannel(
 	const std::string &uri,
 	const std::string &credentials)
 {
-	switchChannel(uri, false, false, credentials);
+	switchChannel(uri, false, false, false, credentials);
 }
 
 void LLVoiceClient::setSpatialChannel(
@@ -3347,51 +5074,216 @@ void LLVoiceClient::setSpatialChannel(
 	const std::string &credentials)
 {
 	mSpatialSessionURI = uri;
+	mSpatialSessionCredentials = credentials;
 	mAreaVoiceDisabled = mSpatialSessionURI.empty();
 
 	LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
 	
-	if(mNonSpatialChannel || !mNextSessionSpatial)
+	if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial)))
 	{
 		// User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels.
 		LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
 	}
 	else
 	{
-		switchChannel(mSpatialSessionURI, true, false, credentials);
+		switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials);
 	}
 }
 
-void LLVoiceClient::callUser(LLUUID &uuid)
+void LLVoiceClient::callUser(const LLUUID &uuid)
 {
 	std::string userURI = sipURIFromID(uuid);
 
-	switchChannel(userURI, false, true);
+	switchChannel(userURI, false, true, true);
+}
+
+LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uuid)
+{
+	// Figure out if a session with the user already exists
+	sessionState *session = findSession(uuid);
+	if(!session)
+	{
+		// No session with user, need to start one.
+		std::string uri = sipURIFromID(uuid);
+		session = addSession(uri);
+		session->mIsSpatial = false;
+		session->mReconnect = false;	
+		session->mIsP2P = true;
+		session->mCallerID = uuid;
+	}
+	
+	if(session)
+	{
+		if(session->mHandle.empty())
+		{
+			// Session isn't active -- start it up.
+			sessionCreateSendMessage(session, false, true);
+		}
+		else
+		{	
+			// Session is already active -- start up text.
+			sessionTextConnectSendMessage(session);
+		}
+	}
+	
+	return session;
+}
+
+bool LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)
+{
+	bool result = false;
+
+	// Attempt to locate the indicated session
+	sessionState *session = startUserIMSession(participant_id);
+	if(session)
+	{
+		// found the session, attempt to send the message
+		session->mTextMsgQueue.push(message);
+		
+		// Try to send queued messages (will do nothing if the session is not open yet)
+		sendQueuedTextMessages(session);
+
+		// The message is queued, so we succeed.
+		result = true;
+	}	
+	else
+	{
+		LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL;
+	}
+	
+	return result;
 }
 
-void LLVoiceClient::answerInvite(std::string &sessionHandle, LLUUID& other_user_id)
+void LLVoiceClient::sendQueuedTextMessages(sessionState *session)
 {
-	joinSession(sessionHandle, sipURIFromID(other_user_id));
+	if(session->mTextStreamState == 1)
+	{
+		if(!session->mTextMsgQueue.empty())
+		{
+			std::ostringstream stream;
+			
+			while(!session->mTextMsgQueue.empty())
+			{
+				std::string message = session->mTextMsgQueue.front();
+				session->mTextMsgQueue.pop();
+				stream
+				<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">"
+					<< "<SessionHandle>" << session->mHandle << "</SessionHandle>"
+					<< "<MessageHeader>text/HTML</MessageHeader>"
+					<< "<MessageBody>" << message << "</MessageBody>"
+				<< "</Request>"
+				<< "\n\n\n";
+			}		
+			writeString(stream.str());
+		}
+	}
+	else
+	{
+		// Session isn't connected yet, defer until later.
+	}
+}
+
+void LLVoiceClient::endUserIMSession(const LLUUID &uuid)
+{
+	// Figure out if a session with the user exists
+	sessionState *session = findSession(uuid);
+	if(session)
+	{
+		// found the session
+		if(!session->mHandle.empty())
+		{
+			sessionTextDisconnectSendMessage(session);
+		}
+	}	
+	else
+	{
+		LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL;
+	}
+}
+
+bool LLVoiceClient::answerInvite(std::string &sessionHandle)
+{
+	// this is only ever used to answer incoming p2p call invites.
+	
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		session->mIsSpatial = false;
+		session->mReconnect = false;	
+		session->mIsP2P = true;
+
+		joinSession(session);
+		return true;
+	}
+	
+	return false;
+}
+
+bool LLVoiceClient::isOnlineSIP(const LLUUID &id)
+{
+	bool result = false;
+	buddyListEntry *buddy = findBuddy(id);
+	if(buddy)
+	{
+		result = buddy->mOnlineSLim;
+		LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL;
+	}
+
+	if(!result)
+	{
+		// This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM.
+		sessionState *session = findSession(id);
+		if(session && !session->mHandle.empty())
+		{
+			if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle))
+			{
+				LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL;
+				// we have a p2p text session open with this user, so by definition they're online.
+				result = true;
+			}
+		}
+	}
+	
+	return result;
 }
 
 void LLVoiceClient::declineInvite(std::string &sessionHandle)
 {
-	sessionTerminateByHandle(sessionHandle);
+	sessionState *session = findSession(sessionHandle);
+	if(session)
+	{
+		sessionMediaDisconnectSendMessage(session);
+	}
 }
 
 void LLVoiceClient::leaveNonSpatialChannel()
 {
-	switchChannel(mSpatialSessionURI);
+	LL_DEBUGS("Voice") 
+		<< "called in state " << state2string(getState()) 
+		<< LL_ENDL;
+	
+	// Make sure we don't rejoin the current session.	
+	sessionState *oldNextSession = mNextAudioSession;
+	mNextAudioSession = NULL;
+	
+	// Most likely this will still be the current session at this point, but check it anyway.
+	reapSession(oldNextSession);
+	
+	verifySessionState();
+	
+	sessionTerminate();
 }
 
 std::string LLVoiceClient::getCurrentChannel()
 {
+	std::string result;
+	
 	if((getState() == stateRunning) && !mSessionTerminateRequested)
 	{
-		return mSessionURI;
+		result = getAudioSessionURI();
 	}
 	
-	return "";
+	return result;
 }
 
 bool LLVoiceClient::inProximalChannel()
@@ -3400,7 +5292,7 @@ bool LLVoiceClient::inProximalChannel()
 	
 	if((getState() == stateRunning) && !mSessionTerminateRequested)
 	{
-		result = !mNonSpatialChannel;
+		result = inSpatialChannel();
 	}
 	
 	return result;
@@ -3412,7 +5304,7 @@ std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
 	result = "sip:";
 	result += nameFromID(id);
 	result += "@";
-	result += mAccountServerName;
+	result += mVoiceSIPURIHostName;
 	
 	return result;
 }
@@ -3425,7 +5317,7 @@ std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
 		result = "sip:";
 		result += nameFromID(avatar->getID());
 		result += "@";
-		result += mAccountServerName;
+		result += mVoiceSIPURIHostName;
 	}
 	
 	return result;
@@ -3444,6 +5336,13 @@ std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
 std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
 {
 	std::string result;
+	
+	if (uuid.isNull()) {
+		//VIVOX, the uuid emtpy look for the mURIString and return that instead.
+		//result.assign(uuid.mURIStringName);
+		LLStringUtil::replaceChar(result, '_', ' ');
+		return result;
+	}
 	// Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
 	result = "x";
 	
@@ -3457,13 +5356,24 @@ std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
 	// If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
 	// echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
 	
+	// The reverse transform can be done with:
+	// echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p
+	
 	return result;
 }
 
-bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid)
+bool LLVoiceClient::IDFromName(const std::string inName, LLUUID &uuid)
 {
 	bool result = false;
 	
+	// SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com"
+	// If it is, convert to a bare name before doing the transform.
+	std::string name = nameFromsipURI(inName);
+	
+	// Doesn't look like a SIP URI, assume it's an actual name.
+	if(name.empty())
+		name = inName;
+
 	// This will only work if the name is of the proper form.
 	// As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
 	// "xFnPP04IpREWNkuw1cOXlhw=="
@@ -3485,6 +5395,13 @@ bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid)
 			memcpy(uuid.mData, rawuuid, UUID_BYTES);
 			result = true;
 		}
+	} 
+	
+	if(!result)
+	{
+		// VIVOX:  not a standard account name, just copy the URI name mURIString field
+		// and hope for the best.  bpj
+		uuid.setNull();  // VIVOX, set the uuid field to nulls
 	}
 	
 	return result;
@@ -3501,13 +5418,59 @@ std::string LLVoiceClient::sipURIFromName(std::string &name)
 	result = "sip:";
 	result += name;
 	result += "@";
-	result += mAccountServerName;
+	result += mVoiceSIPURIHostName;
 
 //	LLStringUtil::toLower(result);
 
 	return result;
 }
 
+std::string LLVoiceClient::nameFromsipURI(const std::string &uri)
+{
+	std::string result;
+
+	std::string::size_type sipOffset, atOffset;
+	sipOffset = uri.find("sip:");
+	atOffset = uri.find("@");
+	if((sipOffset != std::string::npos) && (atOffset != std::string::npos))
+	{
+		result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4));
+	}
+	
+	return result;
+}
+
+bool LLVoiceClient::inSpatialChannel(void)
+{
+	bool result = false;
+	
+	if(mAudioSession)
+		result = mAudioSession->mIsSpatial;
+		
+	return result;
+}
+
+std::string LLVoiceClient::getAudioSessionURI()
+{
+	std::string result;
+	
+	if(mAudioSession)
+		result = mAudioSession->mSIPURI;
+		
+	return result;
+}
+
+std::string LLVoiceClient::getAudioSessionHandle()
+{
+	std::string result;
+	
+	if(mAudioSession)
+		result = mAudioSession->mHandle;
+		
+	return result;
+}
+
+
 /////////////////////////////
 // Sending updates of current state
 
@@ -3546,7 +5509,8 @@ void LLVoiceClient::updatePosition(void)
 			LLMatrix3 rot;
 			LLVector3d pos;
 
-			// MBW -- XXX -- Setting both camera and avatar velocity to 0 for now.  May figure it out later...
+			// TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here...
+			// They're currently always set to zero.
 
 			// Send the current camera position to the voice code
 			rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (),  LLViewerCamera::getInstance()->getUpAxis());		
@@ -3561,7 +5525,7 @@ void LLVoiceClient::updatePosition(void)
 			rot = agent->getRootJoint()->getWorldRotation().getMatrix3();
 	
 			pos = agent->getPositionGlobal();
-			// MBW -- XXX -- Can we get the head offset from outside the LLVOAvatar?
+			// TODO: Can we get the head offset from outside the LLVOAvatar?
 //			pos += LLVector3d(mHeadOffset);
 			pos += LLVector3d(0.f, 0.f, 1.f);
 		
@@ -3748,56 +5712,34 @@ void LLVoiceClient::setEarLocation(S32 loc)
 
 void LLVoiceClient::setVoiceVolume(F32 volume)
 {
-	LL_DEBUGS("Voice") << "volume is " << volume << LL_ENDL;
+	int scaled_volume = scale_speaker_volume(volume);	
 
-	// incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
-	// Map it as follows: 0.0 -> -100, 0.5 -> 24, 1.0 -> 50
-	
-	volume -= 0.5f;		// offset volume to the range [-0.5 ... 0.5], with 0 at the default.
-	int scaledVolume = 24;	// offset scaledVolume by its default level
-	if(volume < 0.0f)
-		scaledVolume += ((int)(volume * 248.0f));	// (24 - (-100)) * 2
-	else
-		scaledVolume += ((int)(volume * 52.0f));	// (50 - 24) * 2
-	
-	if(scaledVolume != mSpeakerVolume)
+	if(scaled_volume != mSpeakerVolume)
 	{
-		if((scaledVolume == -100) || (mSpeakerVolume == -100))
+		if((scaled_volume == 0) || (mSpeakerVolume == 0))
 		{
 			mSpeakerMuteDirty = true;
 		}
 
-		mSpeakerVolume = scaledVolume;
+		mSpeakerVolume = scaled_volume;
 		mSpeakerVolumeDirty = true;
 	}
 }
 
 void LLVoiceClient::setMicGain(F32 volume)
 {
-	int scaledVolume = ((int)(volume * 100.0f)) - 100;
-	if(scaledVolume != mMicVolume)
+	int scaled_volume = scale_mic_volume(volume);
+	
+	if(scaled_volume != mMicVolume)
 	{
-		mMicVolume = scaledVolume;
+		mMicVolume = scaled_volume;
 		mMicVolumeDirty = true;
 	}
 }
 
-void LLVoiceClient::setVivoxDebugServerName(std::string &serverName)
-{
-	if(!mAccountServerName.empty())
-	{
-		// The name has been filled in already, which means we know whether we're connecting to agni or not.
-		if(!sConnectingToAgni)
-		{
-			// Only use the setting if we're connecting to a development grid -- always use bhr when on agni.
-			mAccountServerName = serverName;
-		}
-	}
-}
-
 void LLVoiceClient::keyDown(KEY key, MASK mask)
 {	
-	LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL;
+//	LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL;
 
 	if (gKeyboard->getKeyRepeated(key))
 	{
@@ -3935,19 +5877,6 @@ BOOL LLVoiceClient::getUsingPTT(const LLUUID& id)
 	return result;
 }
 
-BOOL LLVoiceClient::getPTTPressed(const LLUUID& id)
-{
-	BOOL result = FALSE;
-	
-	participantState *participant = findParticipantByID(id);
-	if(participant)
-	{
-		result = participant->mPTT;
-	}
-	
-	return result;
-}
-
 BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
 {
 	BOOL result = FALSE;
@@ -3979,144 +5908,841 @@ F32 LLVoiceClient::getUserVolume(const LLUUID& id)
 
 void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
 {
+	if(mAudioSession)
+	{
+		participantState *participant = findParticipantByID(id);
+		if (participant)
+		{
+			// volume can amplify by as much as 4x!
+			S32 ivol = (S32)(400.f * volume * volume);
+			participant->mUserVolume = llclamp(ivol, 0, 400);
+			participant->mVolumeDirty = TRUE;
+			mAudioSession->mVolumeDirty = TRUE;
+		}
+	}
+}
+
+std::string LLVoiceClient::getGroupID(const LLUUID& id)
+{
+	std::string result;
+
 	participantState *participant = findParticipantByID(id);
-	if (participant)
+	if(participant)
 	{
-		// volume can amplify by as much as 4x!
-		S32 ivol = (S32)(400.f * volume * volume);
-		participant->mUserVolume = llclamp(ivol, 0, 400);
-		participant->mVolumeDirty = TRUE;
-		mVolumeDirty = TRUE;
+		result = participant->mGroupID;
 	}
+	
+	return result;
+}
+
+BOOL LLVoiceClient::getAreaVoiceDisabled()
+{
+	return mAreaVoiceDisabled;
 }
 
+void LLVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame)
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL;
+	
+	if(!mMainSessionGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Start</RecordingControlType>" 
+		<< "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>"
+		<< "<Filename>" << "" << "</Filename>"
+		<< "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>"
+		<< "<LoopModeDurationSeconds>" << seconds << "</LoopModeDurationSeconds>"
+		<< "</Request>\n\n\n";
+
 
+		writeString(stream.str());
+	}
+}
 
-LLVoiceClient::serviceType LLVoiceClient::getServiceType(const LLUUID& id)
+void LLVoiceClient::recordingLoopSave(const std::string& filename)
 {
-	serviceType result = serviceTypeUnknown;
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL;
 
-	participantState *participant = findParticipantByID(id);
-	if(participant)
+	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Flush</RecordingControlType>" 
+		<< "<Filename>" << filename << "</Filename>"
+		<< "</Request>\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVoiceClient::recordingStop()
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL;
+
+	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Stop</RecordingControlType>" 
+		<< "</Request>\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVoiceClient::filePlaybackStart(const std::string& filename)
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL;
+
+	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
+	{
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Start</RecordingControlType>" 
+		<< "<Filename>" << filename << "</Filename>"
+		<< "</Request>\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVoiceClient::filePlaybackStop()
+{
+//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL;
+
+	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty())
 	{
-		result = participant->mServiceType;
+		std::ostringstream stream;
+		stream
+		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">"
+		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>"
+		<< "<RecordingControlType>Stop</RecordingControlType>" 
+		<< "</Request>\n\n\n";
+
+		writeString(stream.str());
+	}
+}
+
+void LLVoiceClient::filePlaybackSetPaused(bool paused)
+{
+	// TODO: Implement once Vivox gives me a sample
+}
+
+void LLVoiceClient::filePlaybackSetMode(bool vox, float speed)
+{
+	// TODO: Implement once Vivox gives me a sample
+}
+
+LLVoiceClient::sessionState::sessionState() :
+	mMediaStreamState(streamStateUnknown),
+	mTextStreamState(streamStateUnknown),
+	mCreateInProgress(false),
+	mMediaConnectInProgress(false),
+	mVoiceInvitePending(false),
+	mTextInvitePending(false),
+	mSynthesizedCallerID(false),
+	mIsChannel(false),
+	mIsSpatial(false),
+	mIsP2P(false),
+	mIncoming(false),
+	mVoiceEnabled(false),
+	mReconnect(false),
+	mVolumeDirty(false),
+	mParticipantsChanged(false)
+{
+}
+
+LLVoiceClient::sessionState::~sessionState()
+{
+	removeAllParticipants();
+}
+
+LLVoiceClient::sessionIterator LLVoiceClient::sessionsBegin(void)
+{
+	return mSessions.begin();
+}
+
+LLVoiceClient::sessionIterator LLVoiceClient::sessionsEnd(void)
+{
+	return mSessions.end();
+}
+
+
+LLVoiceClient::sessionState *LLVoiceClient::findSession(const std::string &handle)
+{
+	sessionState *result = NULL;
+	sessionMap::iterator iter = mSessionsByHandle.find(&handle);
+	if(iter != mSessionsByHandle.end())
+	{
+		result = iter->second;
 	}
 	
 	return result;
 }
 
-std::string LLVoiceClient::getGroupID(const LLUUID& id)
+LLVoiceClient::sessionState *LLVoiceClient::findSessionBeingCreatedByURI(const std::string &uri)
+{	
+	sessionState *result = NULL;
+	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	{
+		sessionState *session = *iter;
+		if(session->mCreateInProgress && (session->mSIPURI == uri))
+		{
+			result = session;
+			break;
+		}
+	}
+	
+	return result;
+}
+
+LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participant_id)
 {
-	std::string result;
+	sessionState *result = NULL;
+	
+	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	{
+		sessionState *session = *iter;
+		if(session->mCallerID == participant_id)
+		{
+			result = session;
+			break;
+		}
+	}
+	
+	return result;
+}
 
-	participantState *participant = findParticipantByID(id);
-	if(participant)
+LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle)
+{
+	sessionState *result = NULL;
+	
+	if(handle.empty())
 	{
-		result = participant->mGroupID;
+		// No handle supplied.
+		// Check whether there's already a session with this URI
+		for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+		{
+			sessionState *s = *iter;
+			if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri))
+			{
+				// TODO: I need to think about this logic... it's possible that this case should raise an internal error.
+				result = s;
+				break;
+			}
+		}
+	}
+	else // (!handle.empty())
+	{
+		// Check for an existing session with this handle
+		sessionMap::iterator iter = mSessionsByHandle.find(&handle);
+		
+		if(iter != mSessionsByHandle.end())
+		{
+			result = iter->second;
+		}
+	}
+
+	if(!result)
+	{
+		// No existing session found.
+		
+		LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL;
+		result = new sessionState();
+		result->mSIPURI = uri;
+		result->mHandle = handle;
+		
+		mSessions.insert(result);
+
+		if(!result->mHandle.empty())
+		{
+			mSessionsByHandle.insert(sessionMap::value_type(&(result->mHandle), result));
+		}
+	}
+	else
+	{
+		// Found an existing session
+		
+		if(uri != result->mSIPURI)
+		{
+			// TODO: Should this be an internal error?
+			LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL;
+			setSessionURI(result, uri);
+		}
+
+		if(handle != result->mHandle)
+		{
+			// TODO: Should this be an internal error?
+			LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL;
+			setSessionHandle(result, handle);
+		}
+		
+		LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL;
+	}
+
+	verifySessionState();
+		
+	return result;
+}
+
+void LLVoiceClient::setSessionHandle(sessionState *session, const std::string &handle)
+{
+	// Have to remove the session from the handle-indexed map before changing the handle, or things will break badly.
+	
+	if(!session->mHandle.empty())
+	{
+		// Remove session from the map if it should have been there.
+		sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
+		if(iter != mSessionsByHandle.end())
+		{
+			if(iter->second != session)
+			{
+				LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL;
+			}
+
+			mSessionsByHandle.erase(iter);
+		}
+		else
+		{
+			LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL;
+		}
+	}
+			
+	session->mHandle = handle;
+
+	if(!handle.empty())
+	{
+		mSessionsByHandle.insert(sessionMap::value_type(&(session->mHandle), session));
+	}
+
+	verifySessionState();
+}
+
+void LLVoiceClient::setSessionURI(sessionState *session, const std::string &uri)
+{
+	// There used to be a map of session URIs to sessions, which made this complex....
+	session->mSIPURI = uri;
+
+	verifySessionState();
+}
+
+void LLVoiceClient::deleteSession(sessionState *session)
+{
+	// Remove the session from the handle map
+	if(!session->mHandle.empty())
+	{
+		sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle));
+		if(iter != mSessionsByHandle.end())
+		{
+			if(iter->second != session)
+			{
+				LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL
+			}
+			mSessionsByHandle.erase(iter);
+		}
+	}
+
+	// Remove the session from the URI map
+	mSessions.erase(session);
+	
+	// At this point, the session should be unhooked from all lists and all state should be consistent.
+	verifySessionState();
+
+	// If this is the current audio session, clean up the pointer which will soon be dangling.
+	if(mAudioSession == session)
+	{
+		mAudioSession = NULL;
+		mAudioSessionChanged = true;
+	}
+
+	// ditto for the next audio session
+	if(mNextAudioSession == session)
+	{
+		mNextAudioSession = NULL;
+	}
+
+	// delete the session
+	delete session;
+}
+
+void LLVoiceClient::deleteAllSessions()
+{
+	LL_DEBUGS("Voice") << "called" << LL_ENDL;
+
+	while(!mSessions.empty())
+	{
+		deleteSession(*(sessionsBegin()));
+	}
+	
+	if(!mSessionsByHandle.empty())
+	{
+		LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL
+	}
+}
+
+void LLVoiceClient::verifySessionState(void)
+{
+	// This is mostly intended for debugging problems with session state management.
+	LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL;
+
+	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	{
+		sessionState *session = *iter;
+
+		LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL;
+		
+		if(!session->mHandle.empty())
+		{
+			// every session with a non-empty handle needs to be in the handle map
+			sessionMap::iterator i2 = mSessionsByHandle.find(&(session->mHandle));
+			if(i2 == mSessionsByHandle.end())
+			{
+				LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL;
+			}
+			else
+			{
+				if(i2->second != session)
+				{
+					LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL;
+				}
+			}
+		}
+	}
+		
+	// check that every entry in the handle map points to a valid session in the session set
+	for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++)
+	{
+		sessionState *session = iter->second;
+		sessionIterator i2 = mSessions.find(session);
+		if(i2 == mSessions.end())
+		{
+			LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL;
+		}
+		else
+		{
+			if(session->mHandle != (*i2)->mHandle)
+			{
+				LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL;
+			}
+		}
+	}
+}
+
+LLVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) :
+	mURI(uri)
+{
+	mOnlineSL = false;
+	mOnlineSLim = false;
+	mCanSeeMeOnline = true;
+	mHasBlockListEntry = false;
+	mHasAutoAcceptListEntry = false;
+	mNameResolved = false;
+	mInVivoxBuddies = false;
+	mInSLFriends = false;
+	mNeedsNameUpdate = false;
+}
+
+void LLVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName)
+{
+	buddyListEntry *buddy = addBuddy(uri, displayName);
+	buddy->mInVivoxBuddies = true;	
+}
+
+LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri)
+{
+	std::string empty;
+	buddyListEntry *buddy = addBuddy(uri, empty);
+	if(buddy->mDisplayName.empty())
+	{
+		buddy->mNameResolved = false;
+	}
+	return buddy;
+}
+
+LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri, const std::string &displayName)
+{
+	buddyListEntry *result = NULL;
+	buddyListMap::iterator iter = mBuddyListMap.find(&uri);
+	
+	if(iter != mBuddyListMap.end())
+	{
+		// Found a matching buddy already in the map.
+		LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL;
+		result = iter->second;
+	}
+
+	if(!result)
+	{
+		// participant isn't already in one list or the other.
+		LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL;
+		result = new buddyListEntry(uri);
+		result->mDisplayName = displayName;
+
+		if(IDFromName(uri, result->mUUID)) 
+		{
+			// Extracted UUID from name successfully.
+		}
+		else
+		{
+			LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL;
+		}
+
+		mBuddyListMap.insert(buddyListMap::value_type(&(result->mURI), result));
 	}
 	
 	return result;
 }
 
-BOOL LLVoiceClient::getAreaVoiceDisabled()
+LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const std::string &uri)
 {
-	return mAreaVoiceDisabled;
+	buddyListEntry *result = NULL;
+	buddyListMap::iterator iter = mBuddyListMap.find(&uri);
+	if(iter != mBuddyListMap.end())
+	{
+		result = iter->second;
+	}
+	
+	return result;
+}
+
+LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const LLUUID &id)
+{
+	buddyListEntry *result = NULL;
+	buddyListMap::iterator iter;
+
+	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
+	{
+		if(iter->second->mUUID == id)
+		{
+			result = iter->second;
+			break;
+		}
+	}
+	
+	return result;
+}
+
+LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddyByDisplayName(const std::string &name)
+{
+	buddyListEntry *result = NULL;
+	buddyListMap::iterator iter;
+
+	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++)
+	{
+		if(iter->second->mDisplayName == name)
+		{
+			result = iter->second;
+			break;
+		}
+	}
+	
+	return result;
+}
+
+void LLVoiceClient::deleteBuddy(const std::string &uri)
+{
+	buddyListMap::iterator iter = mBuddyListMap.find(&uri);
+	if(iter != mBuddyListMap.end())
+	{
+		LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL;
+		buddyListEntry *buddy = iter->second;
+		mBuddyListMap.erase(iter);
+		delete buddy;
+	}
+	else
+	{
+		LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL;
+	}
+	
+}
+
+void LLVoiceClient::deleteAllBuddies(void)
+{
+	while(!mBuddyListMap.empty())
+	{
+		deleteBuddy(*(mBuddyListMap.begin()->first));
+	}
+	
+	// Don't want to correlate with friends list when we've emptied the buddy list.
+	mBuddyListMapPopulated = false;
+	
+	// Don't want to correlate with friends list when we've reset the block rules.
+	mBlockRulesListReceived = false;
+	mAutoAcceptRulesListReceived = false;
+}
+
+void LLVoiceClient::deleteAllBlockRules(void)
+{
+	// Clear the block list entry flags from all local buddy list entries
+	buddyListMap::iterator buddy_it;
+	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
+	{
+		buddy_it->second->mHasBlockListEntry = false;
+	}
+}
+
+void LLVoiceClient::deleteAllAutoAcceptRules(void)
+{
+	// Clear the auto-accept list entry flags from all local buddy list entries
+	buddyListMap::iterator buddy_it;
+	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++)
+	{
+		buddy_it->second->mHasAutoAcceptListEntry = false;
+	}
+}
+
+void LLVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly)
+{
+	buddyListEntry *buddy = NULL;
+
+	// blockMask is the SIP URI of a friends list entry
+	buddyListMap::iterator iter = mBuddyListMap.find(&blockMask);
+	if(iter != mBuddyListMap.end())
+	{
+		LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL;
+		buddy = iter->second;
+	}
+
+	if(buddy == NULL)
+	{
+		LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL;
+		buddy = addBuddy(blockMask);
+	}
+	
+	if(buddy != NULL)
+	{
+		buddy->mHasBlockListEntry = true;
+	}
+}
+
+void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy)
+{
+	buddyListEntry *buddy = NULL;
+
+	// blockMask is the SIP URI of a friends list entry
+	buddyListMap::iterator iter = mBuddyListMap.find(&autoAcceptMask);
+	if(iter != mBuddyListMap.end())
+	{
+		LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL;
+		buddy = iter->second;
+	}
+
+	if(buddy == NULL)
+	{
+		LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL;
+		buddy = addBuddy(autoAcceptMask);
+	}
+
+	if(buddy != NULL)
+	{
+		buddy->mHasAutoAcceptListEntry = true;
+	}
+}
+
+void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString)
+{
+	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done.
+	mBlockRulesListReceived = true;
+}
+
+void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString)
+{
+	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done.
+	mAutoAcceptRulesListReceived = true;
 }
 
 void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
 {
-	mObservers.insert(observer);
+	mParticipantObservers.insert(observer);
 }
 
 void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
 {
-	mObservers.erase(observer);
+	mParticipantObservers.erase(observer);
 }
 
-void LLVoiceClient::notifyObservers()
+void LLVoiceClient::notifyParticipantObservers()
 {
-	for (observer_set_t::iterator it = mObservers.begin();
-		it != mObservers.end();
+	for (observer_set_t::iterator it = mParticipantObservers.begin();
+		it != mParticipantObservers.end();
 		)
 	{
 		LLVoiceClientParticipantObserver* observer = *it;
 		observer->onChange();
 		// In case onChange() deleted an entry.
-		it = mObservers.upper_bound(observer);
+		it = mParticipantObservers.upper_bound(observer);
 	}
 }
 
-void LLVoiceClient::addStatusObserver(LLVoiceClientStatusObserver* observer)
+void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)
 {
 	mStatusObservers.insert(observer);
 }
 
-void LLVoiceClient::removeStatusObserver(LLVoiceClientStatusObserver* observer)
+void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)
 {
 	mStatusObservers.erase(observer);
 }
 
 void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
 {
-	if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
+	if(mAudioSession)
 	{
-		switch(mVivoxErrorStatusCode)
+		if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
 		{
-		case 20713:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; 		break;
-		case 20714:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; 	break;
-		case 20715:
-			//invalid channel, we may be using a set of poorly cached
-			//info
-			status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
-			break;
-		case 1009:
-			//invalid username and password
-			status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
-			break;
-		}
-
-		// Reset the error code to make sure it won't be reused later by accident.
-		mVivoxErrorStatusCode = 0;
-	}
-	
-	if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL 
-		//NOT_FOUND || TEMPORARILY_UNAVAILABLE || REQUEST_TIMEOUT
-		&& (mVivoxErrorStatusCode == 404 || mVivoxErrorStatusCode == 480 || mVivoxErrorStatusCode == 408)) 
-	{
-		// call failed because other user was not available
-		// treat this as an error case
-		status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+			switch(mAudioSession->mErrorStatusCode)
+			{
+				case 20713:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; 		break;
+				case 20714:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; 	break;
+				case 20715:
+					//invalid channel, we may be using a set of poorly cached
+					//info
+					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+					break;
+				case 1009:
+					//invalid username and password
+					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+					break;
+			}
 
-		// Reset the error code to make sure it won't be reused later by accident.
-		mVivoxErrorStatusCode = 0;
+			// Reset the error code to make sure it won't be reused later by accident.
+			mAudioSession->mErrorStatusCode = 0;
+		}
+		else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL)
+		{
+			switch(mAudioSession->mErrorStatusCode)
+			{
+				case 404:	// NOT_FOUND
+				case 480:	// TEMPORARILY_UNAVAILABLE
+				case 408:	// REQUEST_TIMEOUT
+					// call failed because other user was not available
+					// treat this as an error case
+					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
+
+					// Reset the error code to make sure it won't be reused later by accident.
+					mAudioSession->mErrorStatusCode = 0;
+				break;
+			}
+		}
 	}
-	
-	LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status)  << ", session URI " << mSessionURI << LL_ENDL;
+		
+	LL_DEBUGS("Voice") 
+		<< " " << LLVoiceClientStatusObserver::status2string(status)  
+		<< ", session URI " << getAudioSessionURI() 
+		<< (inSpatialChannel()?", proximal is true":", proximal is false")
+	<< LL_ENDL;
 
 	for (status_observer_set_t::iterator it = mStatusObservers.begin();
 		it != mStatusObservers.end();
 		)
 	{
 		LLVoiceClientStatusObserver* observer = *it;
-		observer->onChange(status, mSessionURI, !mNonSpatialChannel);
+		observer->onChange(status, getAudioSessionURI(), inSpatialChannel());
 		// In case onError() deleted an entry.
 		it = mStatusObservers.upper_bound(observer);
 	}
 
 }
 
+void LLVoiceClient::addObserver(LLFriendObserver* observer)
+{
+	mFriendObservers.insert(observer);
+}
+
+void LLVoiceClient::removeObserver(LLFriendObserver* observer)
+{
+	mFriendObservers.erase(observer);
+}
+
+void LLVoiceClient::notifyFriendObservers()
+{
+	for (friend_observer_set_t::iterator it = mFriendObservers.begin();
+		it != mFriendObservers.end();
+		)
+	{
+		LLFriendObserver* observer = *it;
+		it++;
+		// The only friend-related thing we notify on is online/offline transitions.
+		observer->changed(LLFriendObserver::ONLINE);
+	}
+}
+
+void LLVoiceClient::lookupName(const LLUUID &id)
+{
+	gCacheName->getName(id, onAvatarNameLookup);
+}
+
 //static
-// void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* user_data)
-// {
-// 	participantState* statep = gVoiceClient->findParticipantByID(id);
+void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* user_data)
+{
+	if(gVoiceClient)
+	{
+		std::string name = llformat("%s %s", first.c_str(), last.c_str());
+		gVoiceClient->avatarNameResolved(id, name);
+	}
+}
 
-// 	if (statep)
-// 	{
-// 		statep->mDisplayName = first + " " + last;
-// 	}
+void LLVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name)
+{
+	// If the avatar whose name just resolved is on our friends list, resync the friends list.
+	if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL)
+	{
+		mFriendsListDirty = true;
+	}
 	
-// 	gVoiceClient->notifyObservers();
-// }
+	// Iterate over all sessions.
+	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++)
+	{
+		sessionState *session = *iter;
+
+		// Check for this user as a participant in this session
+		participantState *participant = session->findParticipantByID(id);
+		if(participant)
+		{
+			// Found -- fill in the name
+			participant->mAccountName = name;
+			// and post a "participants updated" message to listeners later.
+			session->mParticipantsChanged = true;
+		}
+		
+		// Check whether this is a p2p session whose caller name just resolved
+		if(session->mCallerID == id)
+		{
+			// this session's "caller ID" just resolved.  Fill in the name.
+			session->mName = name;
+			if(session->mTextInvitePending)
+			{
+				session->mTextInvitePending = false;
+
+				// We don't need to call gIMMgr->addP2PSession() here.  The first incoming message will create the panel.				
+			}
+			if(session->mVoiceInvitePending)
+			{
+				session->mVoiceInvitePending = false;
+
+				gIMMgr->inviteToSession(
+					session->mIMSessionID,
+					session->mName,
+					session->mCallerID, 
+					session->mName, 
+					IM_SESSION_P2P_INVITE, 
+					LLIMMgr::INVITATION_TYPE_VOICE,
+					session->mHandle,
+					session->mSIPURI);
+			}
+			
+		}
+	}
+}
 
 class LLViewerParcelVoiceInfo : public LLHTTPNode
 {
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index d0b7839..9fc6a7d 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -41,6 +41,7 @@ class LLVivoxProtocolParser;
 #include "v3math.h"
 #include "llframetimer.h"
 #include "llviewerregion.h"
+#include "llcallingcard.h"   // for LLFriendObserver
 
 class LLVoiceClientParticipantObserver
 {
@@ -91,41 +92,10 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient>
 
 	public:
 		
-		enum serviceType
-		{
-			serviceTypeUnknown,	// Unknown, returned if no data on the avatar is available
-			serviceTypeA,		// spatialized local chat
-			serviceTypeB,		// remote multi-party chat
-			serviceTypeC		// one-to-one and small group chat
-		};
 		static F32 OVERDRIVEN_POWER_LEVEL;
 
 		void updateSettings(); // call after loading settings and whenever they change
 	
-		/////////////////////////////
-		// session control messages
-		void connect();
-
-		void connectorCreate();
-		void connectorShutdown();
-
-		void requestVoiceAccountProvision(S32 retries = 3);
-		void userAuthorized(
-			const std::string& firstName,
-			const std::string& lastName,
-			const LLUUID &agentID);
-		void login(const std::string& accountName, const std::string &password);
-		void loginSendMessage();
-		void logout();
-		void logoutSendMessage();
-		
-		void channelGetListSendMessage();
-		void sessionCreateSendMessage();
-		void sessionConnectSendMessage();
-		void sessionTerminate();
-		void sessionTerminateSendMessage();
-		void sessionTerminateByHandle(std::string &sessionHandle);
-		
 		void getCaptureDevicesSendMessage();
 		void getRenderDevicesSendMessage();
 		
@@ -170,23 +140,32 @@ class LLVoiceClient: public LLSingleton<LLVoiceClient>
 		
 		/////////////////////////////
 		// Response/Event handlers
-		void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle);
-		void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle);
-		void channelGetListResponse(int statusCode, std::string &statusString);
-		void sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle);
-		void sessionConnectResponse(int statusCode, std::string &statusString);
-		void sessionTerminateResponse(int statusCode, std::string &statusString);
+		void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID);
+		void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases);
+		void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle);
+		void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle);
+		void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString);
 		void logoutResponse(int statusCode, std::string &statusString);
 		void connectorShutdownResponse(int statusCode, std::string &statusString);
 
-		void loginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state);
-		void sessionNewEvent(std::string &accountHandle, std::string &eventSessionHandle, int state, std::string &nameString, std::string &uriString);
-		void sessionStateChangeEvent(std::string &uriString, int statusCode, std::string &statusString, std::string &sessionHandle, int state,  bool isChannel, std::string &nameString);
-		void participantStateChangeEvent(std::string &uriString, int statusCode, std::string &statusString, int state,  std::string &nameString, std::string &displayNameString, int participantType);
-		void participantPropertiesEvent(std::string &uriString, int statusCode, std::string &statusString, bool isLocallyMuted, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy);
+		void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state);
+		void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming);
+		void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming);
+		void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString);
+		void sessionGroupAddedEvent(std::string &sessionGroupHandle);
+		void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle);
+		void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType);
+		void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString);
+		void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy);
 		void auxAudioPropertiesEvent(F32 energy);
-	
+		void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString);
+		void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString);
+		void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string &notificationType);
+		void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType);
+		
+		void buddyListChanged();
 		void muteListChanged();
+		void updateFriends(U32 mask);
 		
 		/////////////////////////////
 		// Sending updates of current state
@@ -210,7 +189,6 @@ static	void updatePosition(void);
 		void setVoiceVolume(F32 volume);
 		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(BOOL enabled);
 		BOOL lipSyncEnabled();
 
@@ -225,57 +203,261 @@ static	void updatePosition(void);
 		BOOL getIsSpeaking(const LLUUID& id);
 		BOOL getIsModeratorMuted(const LLUUID& id);
 		F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is...
-		BOOL getPTTPressed(const LLUUID& id);			// This is the inverse of the "locally muted" property.
 		BOOL getOnMuteList(const LLUUID& id);
 		F32 getUserVolume(const LLUUID& id);
 		std::string getDisplayName(const LLUUID& id);
 		
 		// MBW -- XXX -- Not sure how to get this data out of the TVC
 		BOOL getUsingPTT(const LLUUID& id);
-		serviceType getServiceType(const LLUUID& id);	// type of chat the user is involved in (see bHear scope doc for definitions of A/B/C)
 		std::string getGroupID(const LLUUID& id);		// group ID if the user is in group chat (empty string if not applicable)
 
 		/////////////////////////////
 		BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled.
 											// Use this to determine whether to show a "no speech" icon in the menu bar.
+		
+		/////////////////////////////
+		// Recording controls
+		void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200);
+		void recordingLoopSave(const std::string& filename);
+		void recordingStop();
+		
+		// Playback controls
+		void filePlaybackStart(const std::string& filename);
+		void filePlaybackStop();
+		void filePlaybackSetPaused(bool paused);
+		void filePlaybackSetMode(bool vox = false, float speed = 1.0f);
+		
+		
+		// This is used by the string-keyed maps below, to avoid storing the string twice.
+		// The 'const std::string *' in the key points to a string actually stored in the object referenced by the map.
+		// The add and delete operations for each map allocate and delete in the right order to avoid dangling references.
+		// The default compare operation would just compare pointers, which is incorrect, so they must use this comparitor instead.
+		struct stringMapComparitor
+		{
+			bool operator()(const std::string* a, const std::string * b) const
+			{
+				return a->compare(*b) < 0;
+			}
+		};
 
+		struct uuidMapComparitor
+		{
+			bool operator()(const LLUUID* a, const LLUUID * b) const
+			{
+				return *a < *b;
+			}
+		};
+		
 		struct participantState
 		{
 		public:
 			participantState(const std::string &uri);
+
+			bool updateMuteState();
+
 			std::string mURI;
-			std::string mName;
+			LLUUID mAvatarID;
+			std::string mAccountName;
 			std::string mDisplayName;
-			bool mPTT;
-			bool mIsSpeaking;
-			bool mIsModeratorMuted;
 			LLFrameTimer mSpeakingTimeout;
 			F32	mLastSpokeTimestamp;
 			F32 mPower;
 			int mVolume;
-			serviceType mServiceType;
 			std::string mGroupID;
-			bool mOnMuteList;		// true if this avatar is on the user's mute list (and should be muted)
 			int mUserVolume;
+			bool mPTT;
+			bool mIsSpeaking;
+			bool mIsModeratorMuted;
+			bool mOnMuteList;		// true if this avatar is on the user's mute list (and should be muted)
 			bool mVolumeDirty;		// true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed)
 			bool mAvatarIDValid;
-			LLUUID mAvatarID;
+			bool mIsSelf;
+		};
+		typedef std::map<const std::string *, participantState*, stringMapComparitor> participantMap;
+
+		typedef std::map<const LLUUID *, participantState*, uuidMapComparitor> participantUUIDMap;
+	
+		enum streamState
+		{
+			streamStateUnknown = 0,
+			streamStateIdle = 1,
+			streamStateConnected = 2,
+			streamStateRinging = 3,
 		};
-		typedef std::map<std::string, participantState*> participantMap;
 		
-		participantState *findParticipant(const std::string &uri);
-		participantState *findParticipantByAvatar(LLVOAvatar *avatar);
+		struct sessionState
+		{
+		public:
+			sessionState();
+			~sessionState();
+
+			participantState *addParticipant(const std::string &uri);
+			// Note: after removeParticipant returns, the participant* that was passed to it will have been deleted.
+			// Take care not to use the pointer again after that.
+			void removeParticipant(participantState *participant);
+			void removeAllParticipants();
+
+			participantState *findParticipant(const std::string &uri);
+			participantState *findParticipantByID(const LLUUID& id);
+
+			std::string mHandle;
+			std::string mGroupHandle;
+			std::string mSIPURI;
+			std::string mAlias;
+			std::string mName;
+			std::string mAlternateSIPURI;
+			std::string mHash;			// Channel password
+			std::string mErrorStatusString;
+			std::queue<std::string> mTextMsgQueue;
+			
+			LLUUID		mIMSessionID;
+			LLUUID		mCallerID;
+			int			mErrorStatusCode;
+			int			mMediaStreamState;
+			int			mTextStreamState;
+			bool		mCreateInProgress;	// True if a Session.Create has been sent for this session and no response has been received yet.
+			bool		mMediaConnectInProgress;	// True if a Session.MediaConnect has been sent for this session and no response has been received yet.
+			bool		mVoiceInvitePending;	// True if a voice invite is pending for this session (usually waiting on a name lookup)
+			bool		mTextInvitePending;		// True if a text invite is pending for this session (usually waiting on a name lookup)
+			bool		mSynthesizedCallerID;	// True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup.
+			bool		mIsChannel;	// True for both group and spatial channels (false for p2p, PSTN)
+			bool		mIsSpatial;	// True for spatial channels
+			bool		mIsP2P;
+			bool		mIncoming;
+			bool		mVoiceEnabled;
+			bool		mReconnect;	// Whether we should try to reconnect to this session if it's dropped
+			// Set to true when the mute state of someone in the participant list changes.
+			// The code will have to walk the list to find the changed participant(s).
+			bool		mVolumeDirty;
+
+			bool		mParticipantsChanged;
+			participantMap mParticipantsByURI;
+			participantUUIDMap mParticipantsByUUID;
+		};
+
 		participantState *findParticipantByID(const LLUUID& id);
-		
 		participantMap *getParticipantList(void);
+		
+		typedef std::map<const std::string*, sessionState*, stringMapComparitor> sessionMap;
+		typedef std::set<sessionState*> sessionSet;
+				
+		typedef sessionSet::iterator sessionIterator;
+		sessionIterator sessionsBegin(void);
+		sessionIterator sessionsEnd(void);
+
+		sessionState *findSession(const std::string &handle);
+		sessionState *findSessionBeingCreatedByURI(const std::string &uri);
+		sessionState *findSession(const LLUUID &participant_id);
+		sessionState *findSessionByCreateID(const std::string &create_id);
+		
+		sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null);
+		void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null);
+		void setSessionURI(sessionState *session, const std::string &uri);
+		void deleteSession(sessionState *session);
+		void deleteAllSessions(void);
+
+		void verifySessionState(void);
+
+		void joinedAudioSession(sessionState *session);
+		void leftAudioSession(sessionState *session);
+
+		// This is called in several places where the session _may_ need to be deleted.
+		// It contains logic for whether to delete the session or keep it around.
+		void reapSession(sessionState *session);
+		
+		// Returns true if the session seems to indicate we've moved to a region on a different voice server
+		bool sessionNeedsRelog(sessionState *session);
+		
+		struct buddyListEntry
+		{
+			buddyListEntry(const std::string &uri);
+			std::string mURI;
+			std::string mDisplayName;
+			LLUUID	mUUID;
+			bool mOnlineSL;
+			bool mOnlineSLim;
+			bool mCanSeeMeOnline;
+			bool mHasBlockListEntry;
+			bool mHasAutoAcceptListEntry;
+			bool mNameResolved;
+			bool mInSLFriends;
+			bool mInVivoxBuddies;
+			bool mNeedsNameUpdate;
+		};
+
+		typedef std::map<const std::string*, buddyListEntry*, stringMapComparitor> buddyListMap;
+		
+		// This should be called when parsing a buddy list entry sent by SLVoice.		
+		void processBuddyListEntry(const std::string &uri, const std::string &displayName);
+
+		buddyListEntry *addBuddy(const std::string &uri);
+		buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName);
+		buddyListEntry *findBuddy(const std::string &uri);
+		buddyListEntry *findBuddy(const LLUUID &id);
+		buddyListEntry *findBuddyByDisplayName(const std::string &name);
+		void deleteBuddy(const std::string &uri);
+		void deleteAllBuddies(void);
+
+		void deleteAllBlockRules(void);
+		void addBlockRule(const std::string &blockMask, const std::string &presenceOnly);
+		void deleteAllAutoAcceptRules(void);
+		void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy);
+		void accountListBlockRulesResponse(int statusCode, const std::string &statusString);						
+		void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString);						
+		
+		/////////////////////////////
+		// session control messages
+		void connectorCreate();
+		void connectorShutdown();
 
+		void requestVoiceAccountProvision(S32 retries = 3);
+		void userAuthorized(
+			const std::string& firstName,
+			const std::string& lastName,
+			const LLUUID &agentID);
+		void login(
+			const std::string& account_name,
+			const std::string& password,
+			const std::string& voice_sip_uri_hostname,
+			const std::string& voice_account_server_uri);
+		void loginSendMessage();
+		void logout();
+		void logoutSendMessage();
+
+		void accountListBlockRulesSendMessage();
+		void accountListAutoAcceptRulesSendMessage();
+		
+		void sessionGroupCreateSendMessage();
+		void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false);
+		void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false);
+		void sessionMediaConnectSendMessage(sessionState *session);		// just joins the audio session
+		void sessionTextConnectSendMessage(sessionState *session);		// just joins the text session
+		void sessionTerminateSendMessage(sessionState *session);
+		void sessionMediaDisconnectSendMessage(sessionState *session);
+		void sessionTextDisconnectSendMessage(sessionState *session);
+
+		// Pokes the state machine to leave the audio session next time around.
+		void sessionTerminate();	
+		
+		// Pokes the state machine to shut down the connector and restart it.
+		void requestRelog();
+		
+		// Does the actual work to get out of the audio session
+		void leaveAudioSession();
+		
 		void addObserver(LLVoiceClientParticipantObserver* observer);
 		void removeObserver(LLVoiceClientParticipantObserver* observer);
 
-		void addStatusObserver(LLVoiceClientStatusObserver* observer);
-		void removeStatusObserver(LLVoiceClientStatusObserver* observer);
+		void addObserver(LLVoiceClientStatusObserver* observer);
+		void removeObserver(LLVoiceClientStatusObserver* observer);
+
+		void addObserver(LLFriendObserver* observer);
+		void removeObserver(LLFriendObserver* observer);
+		
+		void lookupName(const LLUUID &id);
+		static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* user_data);
+		void avatarNameResolved(const LLUUID &id, const std::string &name);
 		
-// 		static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group, void* user_data);
 		typedef std::vector<std::string> deviceList;
 
 		deviceList *getCaptureDevices();
@@ -287,8 +469,16 @@ static	void updatePosition(void);
 		void setSpatialChannel(
 			const std::string &uri,
 			const std::string &credentials);
-		void callUser(LLUUID &uuid);
-		void answerInvite(std::string &sessionHandle, LLUUID& other_user_id);
+		// start a voice session with the specified user
+		void callUser(const LLUUID &uuid);
+		
+		// Send a text message to the specified user, initiating the session if necessary.
+		bool sendTextMessage(const LLUUID& participant_id, const std::string& message);
+		
+		// close any existing text IM session with the specified user
+		void endUserIMSession(const LLUUID &uuid);
+		
+		bool answerInvite(std::string &sessionHandle);
 		void declineInvite(std::string &sessionHandle);
 		void leaveNonSpatialChannel();
 
@@ -301,7 +491,11 @@ static	void updatePosition(void);
 		bool inProximalChannel();
 
 		std::string sipURIFromID(const LLUUID &id);
-
+				
+		// Returns true if the indicated user is online via SIP presence according to SLVoice.
+		// Note that we only get SIP presence data for other users that are in our vivox buddy list.
+		bool isOnlineSIP(const LLUUID &id);
+		
 	private:
 
 		// internal state for a simple state machine.  This is used to deal with the asynchronous nature of some of the messages.
@@ -313,6 +507,7 @@ static	void updatePosition(void);
 			stateDaemonLaunched,		// Daemon has been launched
 			stateConnecting,			// connect() call has been issued
 			stateIdle,					// socket is connected, ready for messaging
+			stateNeedsProvision,		// Need to do a ProvisionVoiceAccountRequest
 			stateConnectorStart,		// connector needs to be started
 			stateConnectorStarting,		// waiting for connector handle
 			stateConnectorStarted,		// connector handle received
@@ -322,12 +517,11 @@ static	void updatePosition(void);
 			stateNeedsLogin,			// send login request
 			stateLoggingIn,				// waiting for account handle
 			stateLoggedIn,				// account handle received
+			stateCreatingSessionGroup,	// Creating the main session group
 			stateNoChannel,				// 
 			stateMicTuningStart,
 			stateMicTuningRunning,		
 			stateMicTuningStop,
-			stateSessionCreate,			// need to send Session.Create command
-			stateSessionConnect,		// need to send Session.Connect command
 			stateJoiningSession,		// waiting for session handle
 			stateSessionJoined,			// session handle received
 			stateRunning,				// in session, steady state
@@ -354,7 +548,7 @@ static	void updatePosition(void);
 		
 		state mState;
 		bool mSessionTerminateRequested;
-		bool mNonSpatialChannel;
+		bool mRelogRequested;
 		
 		void setState(state inState);
 		state getState(void)  { return mState; };
@@ -377,18 +571,7 @@ static	void updatePosition(void);
 		std::string mAccountDisplayName;
 		std::string mAccountFirstName;
 		std::string mAccountLastName;
-		
-		std::string mNextP2PSessionURI;		// URI of the P2P session to join next
-		std::string mNextSessionURI;		// URI of the session to join next
-		std::string mNextSessionHandle;		// Session handle of the session to join next
-		std::string mNextSessionHash;		// Password hash for the session to join next
-		bool mNextSessionSpatial;			// Will next session be a spatial chat?
-		bool mNextSessionNoReconnect;		// Next session should not auto-reconnect (i.e. user -> user chat)
-		bool mNextSessionResetOnClose;		// If this is true, go back to spatial chat when the next session terminates.
-		
-		std::string mSessionStateEventHandle;	// session handle received in SessionStateChangeEvents
-		std::string mSessionStateEventURI;		// session URI received in SessionStateChangeEvents
-		
+				
 		bool mTuningMode;
 		float mTuningEnergy;
 		std::string mTuningAudioFile;
@@ -399,32 +582,40 @@ static	void updatePosition(void);
 		state mTuningExitState;					// state to return to when we leave tuning mode.
 		
 		std::string mSpatialSessionURI;
-		
-		bool mSessionResetOnClose;
-		
-		int mVivoxErrorStatusCode;		
-		std::string mVivoxErrorStatusString;
+		std::string mSpatialSessionCredentials;
+
+		std::string mMainSessionGroupHandle; // handle of the "main" session group.
 		
 		std::string mChannelName;			// Name of the channel to be looked up 
 		bool mAreaVoiceDisabled;
-		std::string mSessionURI;			// URI of the session we're in.
-		bool mSessionP2P;					// true if this session is a p2p call
+		sessionState *mAudioSession;		// Session state for the current audio session
+		bool mAudioSessionChanged;			// set to true when the above pointer gets changed, so observers can be notified.
+
+		sessionState *mNextAudioSession;	// Session state for the audio session we're trying to join
+
+//		std::string mSessionURI;			// URI of the session we're in.
+//		std::string mSessionHandle;		// returned by ?
 		
 		S32 mCurrentParcelLocalID;			// Used to detect parcel boundary crossings
 		std::string mCurrentRegionName;		// Used to detect parcel boundary crossings
 		
 		std::string mConnectorHandle;	// returned by "Create Connector" message
 		std::string mAccountHandle;		// returned by login message		
-		std::string mSessionHandle;		// returned by ?
+		int 		mNumberOfAliases;
 		U32 mCommandCookie;
 	
-		std::string mAccountServerName;
-		std::string mAccountServerURI;
+		std::string mVoiceAccountServerURI;
+		std::string mVoiceSIPURIHostName;
 		
 		int mLoginRetryCount;
 		
-		participantMap mParticipantMap;
-		bool mParticipantMapChanged;
+		sessionMap mSessionsByHandle;				// Active sessions, indexed by session handle.  Sessions which are being initiated may not be in this map.
+		sessionSet mSessions;						// All sessions, not indexed.  This is the canonical session list.
+		
+		bool mBuddyListMapPopulated;
+		bool mBlockRulesListReceived;
+		bool mAutoAcceptRulesListReceived;
+		buddyListMap mBuddyListMap;
 		
 		deviceList mCaptureDevices;
 		deviceList mRenderDevices;
@@ -434,40 +625,41 @@ static	void updatePosition(void);
 		bool mCaptureDeviceDirty;
 		bool mRenderDeviceDirty;
 		
-		participantState *addParticipant(const std::string &uri);
-		// Note: after removeParticipant returns, the participant* that was passed to it will have been deleted.
-		// Take care not to use the pointer again after that.
-		void removeParticipant(participantState *participant);
-		void removeAllParticipants();
-
-		void updateMuteState(participantState *participant);
-
-		typedef std::map<std::string, std::string> channelMap;
-		channelMap mChannelMap;
-		
-		// These are used by the parser when processing a channel list response.
-		void clearChannelMap(void);
-		void addChannelMapEntry(std::string &name, std::string &uri);
-		std::string findChannelURI(std::string &name);
-			
 		// This should be called when the code detects we have changed parcels.
 		// It initiates the call to the server that gets the parcel channel.
 		void parcelChanged();
 		
-	void switchChannel(std::string uri = std::string(), bool spatial = true, bool noReconnect = false, std::string hash = "");
-		void joinSession(std::string handle, std::string uri);
+	void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = "");
+		void joinSession(sessionState *session);
 		
-		std::string nameFromAvatar(LLVOAvatar *avatar);
-		std::string nameFromID(const LLUUID &id);
-		bool IDFromName(const std::string name, LLUUID &uuid);
-		std::string displayNameFromAvatar(LLVOAvatar *avatar);
+static 	std::string nameFromAvatar(LLVOAvatar *avatar);
+static	std::string nameFromID(const LLUUID &id);
+static	bool IDFromName(const std::string name, LLUUID &uuid);
+static	std::string displayNameFromAvatar(LLVOAvatar *avatar);
 		std::string sipURIFromAvatar(LLVOAvatar *avatar);
 		std::string sipURIFromName(std::string &name);
+		
+		// Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not.
+static	std::string nameFromsipURI(const std::string &uri);		
+
+		bool inSpatialChannel(void);
+		std::string getAudioSessionURI();
+		std::string getAudioSessionHandle();
 				
 		void sendPositionalUpdate(void);
 		
 		void buildSetCaptureDevice(std::ostringstream &stream);
 		void buildSetRenderDevice(std::ostringstream &stream);
+		void buildLocalAudioUpdates(std::ostringstream &stream);
+		
+		void clearAllLists();
+		void checkFriend(const LLUUID& id);
+		void sendFriendsListUpdates();
+
+		// start a text IM session with the specified user
+		// This will be asynchronous, the session may be established at a future time.
+		sessionState* startUserIMSession(const LLUUID& uuid);
+		void sendQueuedTextMessages(sessionState *session);
 		
 		void enforceTether(void);
 		
@@ -491,10 +683,9 @@ static	void updatePosition(void);
 		bool		mPTTIsToggle;
 		bool		mUserPTTState;
 		bool		mMuteMic;
-		
-		// Set to true when the mute state of someone in the participant list changes.
-		// The code will have to walk the list to find the changed participant(s).
-		bool		mVolumeDirty;
+				
+		// Set to true when the friends list is known to have changed.
+		bool		mFriendsListDirty;
 		
 		enum
 		{
@@ -522,14 +713,18 @@ static	void updatePosition(void);
 		BOOL		mLipSyncEnabled;
 
 		typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t;
-		observer_set_t mObservers;
+		observer_set_t mParticipantObservers;
 
-		void notifyObservers();
+		void notifyParticipantObservers();
 
 		typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t;
 		status_observer_set_t mStatusObservers;
 		
 		void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status);
+
+		typedef std::set<LLFriendObserver*> friend_observer_set_t;
+		friend_observer_set_t mFriendObservers;
+		void notifyFriendObservers();
 };
 
 extern LLVoiceClient *gVoiceClient;
diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp
index b595073..4e326ed 100644
--- a/indra/newview/llwindebug.cpp
+++ b/indra/newview/llwindebug.cpp
@@ -121,6 +121,14 @@ MODULE32_NEST	Module32Next_;
 #define	CALL_TRACE_MAX	((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40))	//max number of traced calls
 #define	NL				L"\r\n"	//new line
 
+
+typedef struct STACK
+{
+	STACK *	Ebp;
+	PBYTE	Ret_Addr;
+	DWORD	Param[0];
+} STACK, * PSTACK;
+
 BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, LPWSTR Module_Name, PBYTE & Module_Addr);
 void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, 
 						   const CONTEXT* context_record, 
@@ -337,6 +345,31 @@ PBYTE get_valid_frame(PBYTE esp)
 
 	return NULL;
 }
+
+bool shouldUseStackWalker(PSTACK Ebp, int max_depth)
+{
+	WCHAR	Module_Name[MAX_PATH];
+	PBYTE	Module_Addr = 0;
+	int depth = 0;
+
+	while (depth < max_depth) 
+	{
+		if (IsBadReadPtr(Ebp, sizeof(PSTACK)) || 
+			IsBadReadPtr(Ebp->Ebp, sizeof(PSTACK)) ||
+			Ebp->Ebp < Ebp ||
+			Ebp->Ebp - Ebp > 0xFFFFFF ||
+			IsBadCodePtr(FARPROC(Ebp->Ebp->Ret_Addr)) ||
+			!Get_Module_By_Ret_Addr(Ebp->Ebp->Ret_Addr, Module_Name, Module_Addr))
+		{
+			return true;
+		}
+		depth++;
+		Ebp = Ebp->Ebp;
+	}
+
+	return false;
+}
+
 //******************************************************************
 void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record, 
 						   const CONTEXT* context_record, 
@@ -354,17 +387,10 @@ void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record,
 
 	bool fake_frame = false;
 	bool ebp_used = false;
-	const int HEURISTIC_MAX_WALK = 10;
+	const int HEURISTIC_MAX_WALK = 20;
 	int heuristic_walk_i = 0;
 	int Ret_Addr_I = 0;
 
-	typedef struct STACK
-	{
-		STACK *	Ebp;
-		PBYTE	Ret_Addr;
-		DWORD	Param[0];
-	} STACK, * PSTACK;
-
 	STACK	Stack = {0, 0};
 	PSTACK	Ebp;
 
@@ -433,10 +459,9 @@ void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record,
 
 		// is next ebp valid?
 		// only run if we've never found a good ebp
+		// and make sure the one after is valid as well
 		if(	!ebp_used && 
-			(IsBadReadPtr(Ebp->Ebp, sizeof(PSTACK)) || 
-			IsBadCodePtr(FARPROC(Ebp->Ebp->Ret_Addr)) ||
-			!Get_Module_By_Ret_Addr(Ebp->Ebp->Ret_Addr, Module_Name, Module_Addr)))
+			shouldUseStackWalker(Ebp, 2))
 		{
 			heuristic_walk_i++;
 			PBYTE new_ebp = get_valid_frame(Esp);
@@ -451,9 +476,9 @@ void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record,
 			Ebp = Ebp->Ebp;
 		}
 	}
-
+/* TODO remove or turn this code back on to edit the stack after i see a few raw ones. -Palmer
 	// Now go back through and edit out heuristic stacks that could very well be bogus.
-	// Leave the top and the last stack chosen by the heuristic, however.
+	// Leave the top and the last 3 stack chosen by the heuristic, however.
 	if(heuristic_walk_i > 2)
 	{
 		info["CallStack"][0] = tmp_info["CallStack"][0];
@@ -470,7 +495,10 @@ void WINAPI Get_Call_Stack(const EXCEPTION_RECORD* exception_record,
 	{
 		info = tmp_info;
 	}
-
+*/
+	info = tmp_info;
+	info["HeuristicWalkI"] = heuristic_walk_i;
+	info["EbpUsed"] = ebp_used;
 
 } //Get_Call_Stack
 
diff --git a/indra/newview/skins/default/xui/de/alerts.xml b/indra/newview/skins/default/xui/de/alerts.xml
index fe984ae..68b4507 100644
--- a/indra/newview/skins/default/xui/de/alerts.xml
+++ b/indra/newview/skins/default/xui/de/alerts.xml
@@ -625,7 +625,7 @@ Damit Waffen funktionieren, müssen Skripts erlaubt sein.
 	</alert>
 	<alert name="PromptRecipientEmail">
 		<message name="message">
-			Geben Sie die E-Mail-Adresse des Empfängers ein.
+			Bitte geben Sie für den/die Empfänger eine gültige Email-Adresse ein.
 		</message>
 	</alert>
 	<alert name="PromptSelfEmail">
@@ -4537,6 +4537,9 @@ Entspricht dem Azimut.
 		</message>
 	</alert>
 	<alert name="DebitPermissionDetails" title="Info zur Abfrage der Abbucherlaubnis">
+		<message name="message">
+			Wenn Sie dieser Anfrage zustimmen, erhält das Skript die Erlaubnis, regelmäßig Linden-Dollar (L$) von Ihrem Konto abzubuchen. Diese Erlaubnis kann nur zurückgezogen werden, wenn der Eigentümer das Objekt löscht oder die Skripts in dem Objekt zurücksetzt.
+		</message>
 		<option name="OK">
 			OK
 		</option>
diff --git a/indra/newview/skins/default/xui/de/floater_about.xml b/indra/newview/skins/default/xui/de/floater_about.xml
index 1b7722f..82df651 100644
--- a/indra/newview/skins/default/xui/de/floater_about.xml
+++ b/indra/newview/skins/default/xui/de/floater_about.xml
@@ -26,7 +26,7 @@ Alle Rechte vorbehalten. Details siehe licenses.txt.
 Voice-Chat-Audiocoding: Polycom(R) Siren14(TM) (ITU-T Empf.G.722.1 Anhang C)
 
 
-Wir können nur eine kurze Distanz in die Zukunft blicken, aber dort können wir eine Menge sehen, was getan werden muss. --Alan Turing
+I get by with a little help from my friends. (etwa: Ich überlebe mit ein bisschen Unterstützung von meinen Freunden.) --Richard Starkey
 	</text_editor>
 	<text name="you_are_at">
 		Sie befinden sich in [POSITION]
diff --git a/indra/newview/skins/default/xui/de/panel_group_land_money.xml b/indra/newview/skins/default/xui/de/panel_group_land_money.xml
index 247051f..eee6c5c 100644
--- a/indra/newview/skins/default/xui/de/panel_group_land_money.xml
+++ b/indra/newview/skins/default/xui/de/panel_group_land_money.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <panel label="Land und L$" name="land_money_tab">
 	<text name="help_text">
-		Es werden Parzellen in Gruppenbesitz und Beitragsdetails angezeigt. Solange der Wert für „Insgesamt verwendetes Land“ unter oder gleich dem Wert für „Gesamtbeitrag“ ist, wird eine Warnung angezeigt. Die Registerkarten „Details“ und „Verkäufe“ enthalten Informationen über die Gruppenfinanzen.
+		Es werden Parzellen in Gruppenbesitz und Beitragsdetails angezeigt. Ein Warnhinweis wird angezeigt, solange der Wert für das Insgesamt verwendete Land gleich oder weniger ist als der Gesamtbeitrag. Die Reiter „Details“ und „Verkäufe“ enthalten Informationen über die Gruppenfinanzen.
 	</text>
 	<button label="?" name="help_button" />
 	<text name="cant_view_group_land_text">
diff --git a/indra/newview/skins/default/xui/de/panel_voice_controls.xml b/indra/newview/skins/default/xui/de/panel_voice_controls.xml
index 80eb0e7..9674161 100644
--- a/indra/newview/skins/default/xui/de/panel_voice_controls.xml
+++ b/indra/newview/skins/default/xui/de/panel_voice_controls.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <panel name="voice_controls">
+	<!-- WARNING: separate formatting and translations of these same strings (for using the Silver skin) is controlled by a different file in \skins\silver\xui\de\panel_voice_controls.xml You must make changes to translations in both files.-->
 	<button name="speakers_btn"
 	     tool_tip="Liste der Einwohner in der Umgebung anzeigen, die Voice-Chat verwenden" />
 	<button label="Reden" name="push_to_talk" 
 	     tool_tip="Zum Sprechen Taste gedrückt halten" width="70" left_delta="37"/>
 	<button name="ptt_lock"
 	     tool_tip="Klicken Sie auf das Schloss, um in den Gesprächsmodus zu wechseln" left_delta="2"/>
-	<icon bottom="-19" height="16" image_name="icn_voice_ptt-off.tga" left_delta="49"
-	     name="voice_volume" width="16" />
+	<icon left_delta="49" name="voice_volume" width="16" />
 	<button name="show_channel" tool_tip="Klicken, um den aktuellen Voice-Kanal anzuzeigen" left_delta="16"/>
 </panel>
diff --git a/indra/newview/skins/default/xui/en-us/panel_audio_device.xml b/indra/newview/skins/default/xui/en-us/panel_audio_device.xml
index 39820e5..58e427f 100644
--- a/indra/newview/skins/default/xui/en-us/panel_audio_device.xml
+++ b/indra/newview/skins/default/xui/en-us/panel_audio_device.xml
@@ -42,7 +42,7 @@
 		Adjust the slider to control how loud you sound to other Residents. To test the input level, simply speak into your microphone.
 	</text_editor>
 	<volume_slider bottom_delta="-18" enabled="true" follows="left|top" height="17"
-	     increment="0.05" initial_val="0.5" left_delta="5" max_val="1" min_val="0"
+	     increment="0.05" initial_val="1.0" left_delta="5" max_val="2" min_val="0"
 	     mouse_opaque="true" name="mic_volume_slider"
 	     tool_tip="Change the volume using this slider" width="90" />
 	<text bottom_delta="-4" follows="left|top" height="20" left_delta="95"
diff --git a/indra/newview/skins/default/xui/en-us/panel_voice_remote.xml b/indra/newview/skins/default/xui/en-us/panel_voice_remote.xml
index c785e72..38242e6 100644
--- a/indra/newview/skins/default/xui/en-us/panel_voice_remote.xml
+++ b/indra/newview/skins/default/xui/en-us/panel_voice_remote.xml
@@ -3,6 +3,6 @@
      enabled="true" follows="right|bottom" height="20" left="0"
      mouse_opaque="true" name="voice_remote" use_bounding_rect="true"
      width="130">
-	<panel bottom="1" filename="panel_bg_tab.xml" height="22" left="0" width="130" />
-	<panel bottom="3" filename="panel_voice_controls.xml" height="20" left="1" width="130" />
+	<panel bottom="1" filename="panel_bg_tab.xml" name="panel_bg_tab" height="22" left="0" width="130" />
+	<panel bottom="3" filename="panel_voice_controls.xml" name="panel_voice_controls" height="20" left="1" width="130" />
 </panel>
diff --git a/indra/newview/skins/default/xui/en-us/panel_voice_remote_expanded.xml b/indra/newview/skins/default/xui/en-us/panel_voice_remote_expanded.xml
index 7464467..61a7480 100644
--- a/indra/newview/skins/default/xui/en-us/panel_voice_remote_expanded.xml
+++ b/indra/newview/skins/default/xui/en-us/panel_voice_remote_expanded.xml
@@ -2,7 +2,7 @@
 <panel bg_visible="false" border="false" border_visible="false" bottom="0"
      follows="right|bottom" height="45" left="0" mouse_opaque="true"
      name="voice_remote" use_bounding_rect="true" width="130">
-	<panel bottom="1" filename="panel_bg_tab.xml" height="47" left="0" width="130" />
+	<panel bottom="1" filename="panel_bg_tab.xml" name="panel_bg_tab" height="47" left="0" width="130" />
 	<button bottom="-20" height="20" image_overlay="icn_voice-call-end.tga" label=""
 	     left="4" name="end_call_btn" tool_tip="Click to hang up" width="20" />
 	<button bottom="-21" height="22" image_disabled="icn_rounded-text-field.tga"
diff --git a/indra/newview/skins/default/xui/fr/alerts.xml b/indra/newview/skins/default/xui/fr/alerts.xml
index a8ea588..c3db76d 100644
--- a/indra/newview/skins/default/xui/fr/alerts.xml
+++ b/indra/newview/skins/default/xui/fr/alerts.xml
@@ -637,7 +637,7 @@ Pour que les armes fonctionnent, les scripts doivent être autorisés.
 	</alert>
 	<alert name="PromptRecipientEmail">
 		<message name="message">
-			Veuillez saisir l&apos;adresse e-mail du destinataire.
+			Veuillez saisir une adresse e-mail valide pour le(s) destinataire(s).
 		</message>
 	</alert>
 	<alert name="PromptSelfEmail">
diff --git a/indra/newview/skins/default/xui/fr/floater_about.xml b/indra/newview/skins/default/xui/fr/floater_about.xml
index 716ce10..b96aa70 100644
--- a/indra/newview/skins/default/xui/fr/floater_about.xml
+++ b/indra/newview/skins/default/xui/fr/floater_about.xml
@@ -26,7 +26,7 @@
   Codage Audio du chat vocal : Polycom(R) Siren14(TM) (ITU-T Rec. G.722.1 Annex C)
 
 
-  On ne peut pas voir très loin devant nous, mais l&apos;on peut déjà y voir qu&apos;il y a beaucoup à faire. -Alan Turing
+  Je m&apos;en sors avec un peu d&apos;aide de mes amis. --Richard Starkey
 	</text_editor>
 	<string name="you_are_at">
 		Votre position : [POSITION]
diff --git a/indra/newview/skins/default/xui/fr/panel_group_land_money.xml b/indra/newview/skins/default/xui/fr/panel_group_land_money.xml
index 485c676..8b1af1f 100644
--- a/indra/newview/skins/default/xui/fr/panel_group_land_money.xml
+++ b/indra/newview/skins/default/xui/fr/panel_group_land_money.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="Terrain et L$" name="land_money_tab">
 	<text name="help_text">
-		Les parcelles appartenant au groupe sont indiquées avec le détail des contributions. Un avertissement apparaît si la surface utilisée par le groupe est supérieure ou égale au total des contributions. Les onglets Détails et Ventes correspondent aux finances du groupe.
+		Les parcelles appartenant au groupe sont indiquées avec le détails des contributions. Un avertissement apparaît si la surface utilisée par le groupe est supérieure ou égale au total des contributions. Les onglets Détails et Ventes correspondent aux finances du groupe.
 	</text>
 	<button label="?" name="help_button"/>
 	<text name="cant_view_group_land_text">
diff --git a/indra/newview/skins/default/xui/fr/panel_overlaybar.xml b/indra/newview/skins/default/xui/fr/panel_overlaybar.xml
index dab1f79..3f5567d 100644
--- a/indra/newview/skins/default/xui/fr/panel_overlaybar.xml
+++ b/indra/newview/skins/default/xui/fr/panel_overlaybar.xml
@@ -1,15 +1,19 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="overlay">
 	<layout_stack name="overlay_layout_panel">
-		<layout_panel name="main_panel">
-			<layout_stack name="chatbar_and_buttons">
-				<layout_panel name="state_buttons">
+		<layout_panel name="main_panel" width="410" min_width="410">
+			<layout_stack name="chatbar_and_buttons" right="410">
+				<layout_panel name="padding" width="410" />
+				<layout_panel name="state_buttons" width="410">
 					<button label="IM reçus" label_selected="IM reçus" name="IM Received" tool_tip="Vous avez reçu des messages instantanés. Cliquez pour afficher les IM."/>
 					<button label="Disponible" label_selected="Disponible" name="Set Not Busy" tool_tip="Le chat et les IM ne s&apos;affichent pas. Cliquez ici pour ne plus être en mode occupé(e)."/>
 					<button label="Vue subjective" label_selected="Vue subjective" name="Mouselook" tool_tip="Utilisez la souris pour diriger votre vue. Si vous avez un pistolet, vous pouvez tirer en cliquant."/>
 					<button label="Me lever" label_selected="Me lever" name="Stand Up" tool_tip="Cliquez ici pour vous lever."/>
 				</layout_panel>
+				<layout_panel name="chat_bar" />
 			</layout_stack>
 		</layout_panel>
+		<layout_panel name="media_remote_container" width="220" min_width="220" />
+		<layout_panel name="voice_remote_container" width="140" min_width="138" />
 	</layout_stack>
 </panel>
diff --git a/indra/newview/skins/default/xui/fr/panel_voice_controls.xml b/indra/newview/skins/default/xui/fr/panel_voice_controls.xml
index decd8cc..fc747ce 100644
--- a/indra/newview/skins/default/xui/fr/panel_voice_controls.xml
+++ b/indra/newview/skins/default/xui/fr/panel_voice_controls.xml
@@ -1,8 +1,10 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<panel name="voice_controls">
+<panel name="voice_controls" width="138">
+	<!-- WARNING: separate formatting and translations of these same strings (for using the Silver skin) is controlled by a different file in \skins\silver\xui\fr\panel_voice_controls.xml You must make changes to translations in both files.-->
 	<button name="speakers_btn"
 	     tool_tip="Affichez la liste des résidents utilisant le chat vocal près de vous" />
-	<button label="Parler" name="push_to_talk" tool_tip="Appuyez sur le bouton pour parler" />
+	<button label="Parler" name="push_to_talk" tool_tip="Appuyez sur le bouton pour parler"  width="74" />
 	<button name="ptt_lock" tool_tip="Cliquez sur Verrouiller pour passer au mode Parler" />
-	<button name="show_channel" tool_tip="Cliquez pour afficher le canal vocal utilisé" />
+	<icon name="voice_volume" left_delta="50" />
+	<button name="show_channel" tool_tip="Cliquez pour afficher le canal vocal utilisé" left_delta="20"/>
 </panel>
diff --git a/indra/newview/skins/default/xui/fr/panel_voice_remote.xml b/indra/newview/skins/default/xui/fr/panel_voice_remote.xml
new file mode 100644
index 0000000..96e3032
--- /dev/null
+++ b/indra/newview/skins/default/xui/fr/panel_voice_remote.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel name="voice_remote" width="140">
+	<panel name="panel_bg_tab" width="138" />
+	<panel name="panel_voice_controls" width="140" />
+</panel>
diff --git a/indra/newview/skins/default/xui/fr/panel_voice_remote_expanded.xml b/indra/newview/skins/default/xui/fr/panel_voice_remote_expanded.xml
index f673a3b..c466bca 100644
--- a/indra/newview/skins/default/xui/fr/panel_voice_remote_expanded.xml
+++ b/indra/newview/skins/default/xui/fr/panel_voice_remote_expanded.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
 <panel name="voice_remote">
+	<panel name="panel_bg_tab" width="138" />
 	<button name="end_call_btn" tool_tip="Cliquer pour raccrocher" />
 </panel>
diff --git a/indra/newview/skins/default/xui/ja/alerts.xml b/indra/newview/skins/default/xui/ja/alerts.xml
index 4fe99b6..f4512c2 100644
--- a/indra/newview/skins/default/xui/ja/alerts.xml
+++ b/indra/newview/skins/default/xui/ja/alerts.xml
@@ -656,7 +656,7 @@ L$が不足しているのでこのグループに参加することができま
 	</alert>
 	<alert name="PromptRecipientEmail">
 		<message name="message">
-			受信者のEメール・アドレスを入力してください。
+			受信者の有効なEメールアドレスを入力してください。
 		</message>
 	</alert>
 	<alert name="PromptSelfEmail">
diff --git a/indra/newview/skins/default/xui/ja/floater_about.xml b/indra/newview/skins/default/xui/ja/floater_about.xml
index 86b8883..10a1d89 100644
--- a/indra/newview/skins/default/xui/ja/floater_about.xml
+++ b/indra/newview/skins/default/xui/ja/floater_about.xml
@@ -26,7 +26,7 @@
   Voice chat Audio coding: Polycom(R) Siren14(TM) (ITU-T Rec. G.722.1 Annex C)
 
 
-  道のりは短いが、すべきことは山のようにある –アラン・チューニング
+  友達の助けがあれば何とかやっていける --リチャード・スターキー
 	</text_editor>
 	<text name="you_are_at">
 		[POSITION]
diff --git a/indra/newview/skins/default/xui/ja/floater_about_land.xml b/indra/newview/skins/default/xui/ja/floater_about_land.xml
index eda8fe8..3b8aee6 100644
--- a/indra/newview/skins/default/xui/ja/floater_about_land.xml
+++ b/indra/newview/skins/default/xui/ja/floater_about_land.xml
@@ -27,7 +27,7 @@
 				売り出し中:
 			</text>
 			<text length="1" name="Not for sale." type="string">
-				販売対象外。
+				販売対象外
 			</text>
 			<text length="1" name="For Sale: Price L$[PRICE]." type="string">
 				料金:L$[PRICE]
@@ -37,10 +37,10 @@
 				販売先:[BUYER]
 			</text>
 			<text length="1" name="Sell with landowners objects in parcel." type="string">
-				販売に含めるオブジェクト。
+				オブジェクトも販売価格に含まれます
 			</text>
 			<text length="1" name="Selling with no objects in parcel." type="string">
-				販売に含めないオブジェクト。
+				オブジェクトは販売対象外です
 			</text>
 			<button label="土地販売の取り消し" label_selected="土地販売の取り消し" name="Cancel Land Sale"/>
 			<text length="1" name="Claimed:" type="string">
diff --git a/indra/newview/skins/default/xui/ja/floater_buy_land.xml b/indra/newview/skins/default/xui/ja/floater_buy_land.xml
index c67bdaf..85bafea 100644
--- a/indra/newview/skins/default/xui/ja/floater_buy_land.xml
+++ b/indra/newview/skins/default/xui/ja/floater_buy_land.xml
@@ -22,10 +22,10 @@
 		この地域で購入した土地:
 	</text>
 	<text name="resellable_clause">
-		この地域で購入した土地は、再販が可能な場合とそうでない場合があります。
+		この地域で購入した土地は、再販が可能な場合とそうでない場合があります
 	</text>
 	<text name="changeable_clause">
-		統合または再分割される可能性も、されない可能性もあります。
+		統合または再分割される可能性も、されない可能性もあります
 	</text>
 	<text name="covenant_text">
 		不動産約款に同意してください:
@@ -33,7 +33,7 @@
 	<text_editor name="covenant_editor">
 		ロード中...
 	</text_editor>
-	<check_box label="上記の約款に同意します。" name="agree_covenant" />
+	<check_box label="上記の約款に同意します" name="agree_covenant" />
 	<text name="info_parcel_label">
 		区画:
 	</text>
@@ -43,10 +43,10 @@
 	<text name="info_size_label">
 		サイズ:
 	</text>
-	<text name="info_size">
+	<text name="info_size" bottom_delta="-37" height="48" >
 		1024平方メートル
 	</text>
-	<text name="info_price_label">
+	<text name="info_price_label" >
 		価格:
 	</text>
 	<text name="info_price">
@@ -56,14 +56,14 @@
 		この土地を買うと:
 	</text>
 	<text name="error_message">
-		何か変です。
+		何か変です
 	</text>
 	<button label="ウェブ・サイトに移動" name="error_web" />
 	<text name="account_action">
 		プレミアム・メンバーにアップグレード
 	</text>
 	<text name="account_reason">
-		土地を保有できるのはプレミアム・メンバーだけです。
+		土地を保有できるのはプレミアム・メンバーだけです
 	</text>
 	<combo_box name="account_level">
 		<combo_item name="US$9.95/month,billedmonthly">
@@ -77,7 +77,7 @@
 		</combo_item>
 	</combo_box>
 	<text name="land_use_action">
-		毎月の土地使用料をUS$40に引き上げます。
+		毎月の土地使用料をUS$40に引き上げます
 	</text>
 	<text name="land_use_reason">
 		あなたは1,309平方メートルの土地を所有しています。 
@@ -119,32 +119,32 @@
 		統合/再分割不可能
 	</text>
 	<text name="cant_buy_for_group">
-		あなたはアクティブなグループ用の土地購入を許可されていません。
+		あなたはアクティブなグループ用の土地購入を許可されていません
 	</text>
 	<text name="no_land_selected">
-		土地が選択されていません。
+		土地が選択されていません
 	</text>
 	<text name="multiple_parcels_selected">
 		複数の異なった区画を選択しました。 
 これより小さなエリアを選択してください。
 	</text>
 	<text name="no_permission">
-		あなたはアクティブなグループ用の土地購入を許可されていません。
+		あなたはアクティブなグループ用の土地購入を許可されていません
 	</text>
 	<text name="parcel_not_for_sale">
-		選択された区画は販売対象外です。
+		選択された区画は販売対象外です
 	</text>
 	<text name="group_already_owns">
-		グループはすでにこの区画を所有しています。
+		グループはすでにこの区画を所有しています
 	</text>
 	<text name="you_already_own">
-		あなたはすでにこの区画を所有しています。
+		あなたはすでにこの区画を所有しています
 	</text>
 	<text name="set_to_sell_to_other">
-		選択された区画は、他の人への販売が決まっています。
+		選択された区画は、他の人への販売が決まっています
 	</text>
 	<text name="no_public_land">
-		選択された地域に、公共の土地はありません。
+		選択された地域に、公共の土地はありません
 	</text>
 	<text name="not_owned_by_you">
 		他の使用者に所有された土地が選択されています。 
@@ -156,7 +156,7 @@
 (数分かかることがあります)
 	</text>
 	<text name="fetching_error">
-		土地購入の情報入手中にエラーが起きました。
+		土地購入の情報入手中にエラーが起きました
 	</text>
 	<text name="buying_will">
 		この土地を買うと:
@@ -174,32 +174,33 @@
 		一切必要なし
 	</text>
 	<text name="must_upgrade">
-		アカウントをアップグレードしないと、土地を所有できません。
+		アカウントをアップグレードしないと、土地を所有できません
 	</text>
 	<text name="cant_own_land">
-		あなたのアカウントで土地を所有できます。
+		あなたのアカウントで土地を所有できます
 	</text>
 	<text name="land_holdings">
-		あなたは [BUYER] 平方メートルの土地を所有しています。
+		あなたは [BUYER] 平方メートルの土地を所有しています
 	</text>
 	<text name="pay_to_for_land">
-		この土地の代金として、[SELLER]にL$[AMOUNT]を支払う
+		この土地の代金として、[SELLER]にL$[AMOUNT]を支払う
 	</text>
 	<text name="buy_for_US">
 		約US$ [AMOUNT2]でL$ [AMOUNT]を購入
 	</text>
 	<text name="parcel_meters">
-		この区画は [AMOUNT] 平方メートルあります。
+		この区画は [AMOUNT] 平方メートルあります
 	</text>
 	<text name="premium_land">
-		この土地はプレミアムなので、 [AMOUNT] 平方メートル として請求されます。
+		この土地はプレミアムなので、 [AMOUNT] 平方メートル として請求されます
 	</text>
 	<text name="discounted_land">
-		この土地は値下げされ、[AMOUNT]平方メートルとして請求されます。
+		この土地は値下げされ、[AMOUNT]平方メートルとして請求されます
 	</text>
 	<text name="meters_supports_object">
 		[AMOUNT]平方メートルであれば、
-[AMOUNT2]個のオブジェクトがサポートされます。
+[AMOUNT2]個のオブジェクトがサポ
+ートされます
 	</text>
 	<text name="sold_with_objects">
 		オブジェクトと共に販売済み
@@ -207,19 +208,19 @@
 	<text name="insufficient_land_credits">
 		この [GROUP] が区画の購入手続きを完了するには、 
 価格に見合うだけの寄付された土地の利用実績が
-必要です。
+必要です
 	</text>
 	<text name="have_enough_lindens">
-		あなたの所持する L$ [AMOUNT]で、この土地を購入できます。
+		あなたの所持する L$ [AMOUNT]で、この土地を購入できます
 	</text>
 	<text name="not_enough_lindens">
-		あなたの所持金は L$ [AMOUNT]だけで、あとL$ [AMOUNT2] 必要です。
+		あなたの所持金は L$ [AMOUNT]だけで、あとL$ [AMOUNT2] 必要です
 	</text>
 	<text name="balance_left">
-		購入後、あなたの残額はL$[AMOUNT]になります。
+		購入後、あなたの残額はL$[AMOUNT]になります
 	</text>
 	<text name="balance_needed">
-		この土地を買うには、少なくとも L$ [AMOUNT] を購入する必要があります。
+		この土地を買うには、少なくとも L$ [AMOUNT] を購入する必要があります
 	</text>
 	<text name="no_parcel_selected">
 		(区画が選定されていません)
diff --git a/indra/newview/skins/default/xui/ja/floater_land_holdings.xml b/indra/newview/skins/default/xui/ja/floater_land_holdings.xml
index 949031d..ba85592 100644
--- a/indra/newview/skins/default/xui/ja/floater_land_holdings.xml
+++ b/indra/newview/skins/default/xui/ja/floater_land_holdings.xml
@@ -1,22 +1,19 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="land holdings floater" title="自分の土地">
 	<scroll_list name="parcel list">
-		<column name="name" />
-		<column name="location" />
-		<column name="area" />
-		<column label="" name="hidden" />
+		<column label="名前" name="name"/>
+		<column label="位置" name="location"/>
+		<column label="面積" name="area"/>
+		<column label="" name="hidden"/>
 	</scroll_list>
-	<button label="テレポート" label_selected="テレポート" name="Teleport"
-	     tool_tip="この土地の中心にテレポート" />
-	<button label="地図上に表示" label_selected="地図上に表示"
-	     name="Show on Map"
-	     tool_tip="この土地を世界地図に表示します。" />
+	<button label="テレポート" label_selected="テレポート" name="Teleport" tool_tip="この土地の中心にテレポート"/>
+	<button label="地図上に表示" label_selected="地図上に表示" name="Show on Map" tool_tip="この土地を世界地図に表示します。"/>
 	<text name="contrib_label">
 		あなたのグループへの貢献:
 	</text>
 	<scroll_list name="grant list">
-		<column name="group" />
-		<column name="area" />
+		<column label="グループ名" name="group"/>
+		<column label="面積" name="area"/>
 	</scroll_list>
 	<text name="allowed_label">
 		現在の支払いプランでの許可された保有地:
diff --git a/indra/newview/skins/default/xui/ja/floater_sell_land.xml b/indra/newview/skins/default/xui/ja/floater_sell_land.xml
index 4b8650a..1db0e2a 100644
--- a/indra/newview/skins/default/xui/ja/floater_sell_land.xml
+++ b/indra/newview/skins/default/xui/ja/floater_sell_land.xml
@@ -1,15 +1,15 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<floater name="sell land" title="土地を販売">
+<floater name="sell land" title="土地を販売" height="511" min_height="511" >
 	<text name="info_parcel_label">
 		区画:
 	</text>
-	<text name="info_parcel">
+	<text name="info_parcel" left="62">
 		区画名
 	</text>
 	<text name="info_size_label">
 		サイズ:
 	</text>
-	<text name="info_size">
+	<text name="info_size" left="62">
 		[AREA]平方メートル
 	</text>
 	<text name="info_action">
@@ -19,19 +19,19 @@
 		価格を設定:
 	</text>
 	<text name="price_text">
-		この土地に対する適切な価格を選択してください。
+		この土地に対する適切な価格を選択してください
 	</text>
 	<text name="price_ld">
 		L$
 	</text>
-	<text name="price_per_m">
+	<text name="price_per_m" width="230" left_delta="106">
 		(1平方メートル当たりL$ [PER_METER])
 	</text>
 	<text name="sell_to_label">
 		土地の販売先:
 	</text>
-	<text name="sell_to_text">
-		誰にでも販売するか、特定の購入者にのみ販売するかを選択してください。
+	<text name="sell_to_text" right="-6">
+		誰にでも販売するか、特定の人にのみ販売するかを選択してください
 	</text>
 	<combo_box name="sell_to">
 		<combo_item name="--selectone--">
@@ -49,20 +49,22 @@
 		土地と共にオブジェクトを販売しますか?
 	</text>
 	<text name="sell_objects_text">
-		区画上にある土地オーナーの譲渡可能なオブジェクトは、所有権が変更されます。
+		区画上にある譲渡可能なオブジェクトは所有権が変更されます
 	</text>
-	<radio_group name="sell_objects">
+	<radio_group name="sell_objects" height="72" bottom_delta="-76">
 		<radio_item name="no">
-			いいえ、オブジェクト所有権を保持します
+			いいえ、オブジェクト所有権
+を保持します
 		</radio_item>
-		<radio_item name="yes">
-			はい、土地と一緒にオブジェクトを売ります。
+		<radio_item name="yes" bottom="-56">
+			はい、土地と一緒にオブジェ
+クトを売ります
 		</radio_item>
 	</radio_group>
-	<button label="オブジェクトを表示" name="show_objects" />
+	<button label="オブジェクトを表示" name="show_objects" width="138"/>
 	<text name="nag_message_label">
-		注意:返品や交換はできません。
+		注意:返品や交換はできません
 	</text>
-	<button label="売り地の設定" name="sell_btn" />
+	<button label="売り地の設定" name="sell_btn"  bottom="-505"/>
 	<button label="取り消し" name="cancel_btn" />
 </floater>
diff --git a/indra/newview/skins/default/xui/ja/floater_top_objects.xml b/indra/newview/skins/default/xui/ja/floater_top_objects.xml
index 819a715..e59f72b 100644
--- a/indra/newview/skins/default/xui/ja/floater_top_objects.xml
+++ b/indra/newview/skins/default/xui/ja/floater_top_objects.xml
@@ -1,51 +1,55 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <floater name="top_objects" title="ロード中...">
 	<text name="title_text">
 		ロード中...
 	</text>
 	<scroll_list name="objects_list">
-		<column label="数" name="score" />
-		<column label="名前" name="name" />
-		<column label="所有者" name="owner" />
-		<column label="ロケーション" name="location" />
-		<column label="時間" name="time" />
+		<column label="数" name="score"/>
+		<column label="名前" name="name"/>
+		<column label="所有者" name="owner"/>
+		<column label="ロケーション" name="location"/>
+		<column label="時間" name="time"/>
+		<column label="Monoタイム" name="mono_time"/>
 	</scroll_list>
 	<text name="id_text">
 		物体ID:
 	</text>
-	<button label="標識を表示" name="show_beacon_btn" />
+	<button label="標識を表示" name="show_beacon_btn"/>
 	<text name="obj_name_text">
 		物体名:
 	</text>
-	<button label="フィルタ" name="filter_object_btn" />
+	<button label="フィルタ" name="filter_object_btn"/>
 	<text name="owner_name_text">
 		所有者名:
 	</text>
-	<button label="フィルタ" name="filter_owner_btn" />
-	<button label="選択内容を返却" name="return_selected_btn" />
-	<button label="すべて返却" name="return_all_btn" />
-	<button label="選択内容を無効化" name="disable_selected_btn" />
-	<button label="すべて無効化" name="disable_all_btn" />
-	<button label="更新" name="refresh_btn" />
-	<text name="top_scripts_title">
+	<button label="フィルタ" name="filter_owner_btn"/>
+	<button label="選択内容を返却" name="return_selected_btn"/>
+	<button label="すべて返却" name="return_all_btn"/>
+	<button label="選択内容を無効化" name="disable_selected_btn"/>
+	<button label="すべて無効化" name="disable_all_btn"/>
+	<button label="更新" name="refresh_btn"/>
+	<string name="top_scripts_title">
 		トップ・スクリプト
-	</text>
-	<text name="top_scripts_text">
+	</string>
+	<string name="top_scripts_text">
 		[COUNT]スクリプト全体の実行時間は[TIME]ミリ秒。
-	</text>
-	<text name="scripts_score_label">
+	</string>
+	<string name="scripts_score_label">
 		時間
-	</text>
-	<text name="top_colliders_title">
+	</string>
+	<string name="scripts_mono_time_label">
+		Monoタイム
+	</string>
+	<string name="top_colliders_title">
 		上部コライダー
-	</text>
-	<text name="top_colliders_text">
+	</string>
+	<string name="top_colliders_text">
 		上位[COUNT]個の物体は多くの衝突可能性があります。
-	</text>
-	<text name="colliders_score_label">
+	</string>
+	<string name="colliders_score_label">
 		数
-	</text>
-	<text name="none_descriptor">
+	</string>
+	<string name="none_descriptor">
 		何も見つかりませんでした。
-	</text>
+	</string>
 </floater>
diff --git a/indra/newview/skins/default/xui/ja/panel_group_general.xml b/indra/newview/skins/default/xui/ja/panel_group_general.xml
index ee2d093..382e85e 100644
--- a/indra/newview/skins/default/xui/ja/panel_group_general.xml
+++ b/indra/newview/skins/default/xui/ja/panel_group_general.xml
@@ -1,16 +1,16 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="一般" name="general_tab">
-	<text name="help_text">
+	<string name="help_text">
 		[一般]タブには、このグループに関する
 一般情報、オーナーと表示可能なメンバーのリスト 
 一般的な[グループ環境設定]、メンバーのオプションが含まれます。
 
 オプションの上でマウスカーソルを動かすと、ヘルプ情報を得られます。
-	</text>
+	</string>
 	<string name="group_info_unchanged">
 		グループ設定が変更されました。
 	</string>
-	<button label="?" label_selected="?" name="help_button" />
+	<button label="?" label_selected="?" name="help_button"/>
 	<line_editor label="新しいグループ名をここに入力してください。" name="group_name_editor" width="330"/>
 	<text name="group_name" width="330">
 		新しいグループ名をここに入力してください。
@@ -24,20 +24,12 @@
 	<text name="group_charter_label">
 		グループ憲章
 	</text>
-	<texture_picker label="グループ記章" name="insignia"
-	     tool_tip="写真をクリックして選択" />
+	<texture_picker label="グループ記章" name="insignia" tool_tip="写真をクリックして選択"/>
 	<text_editor name="charter">
 		グループ憲章
 	</text_editor>
-	<button label="参加する(L$0)" label_selected="参加する(L$0)"
-	     name="join_button" />
-	<button label="詳細表示" label_selected="詳細表示" name="info_button" />
-	<text>
-		オーナーと可視メンバー
-	</text>
-	<text>
-		(オーナーは太字)
-	</text>
+	<button label="参加する(L$0)" label_selected="参加する(L$0)" name="join_button"/>
+	<button label="詳細表示" label_selected="詳細表示" name="info_button"/>
 	<text name="text_owners_and_visible_members">
 		オーナーと可視メンバー
 	</text>
@@ -45,39 +37,19 @@
 		(オーナーは太字で表示されています)
 	</text>
 	<name_list name="visible_members">
-		<column name="name" />
-		<column name="title" />
-		<column name="online" />
+		<column label="メンバー名" name="name"/>
+		<column label="タイトル" name="title"/>
+		<column label="最終ログイン" name="online"/>
 	</name_list>
 	<text name="text_group_preferences">
 		グループ環境設定
 	</text>
-	<text name="incomplete_member_data_str">
-		メンバー・データを検索
-	</text>
-	<text name="confirm_group_create_str">
-		このグループの作成にはL$100かかります。 
-このグループの作成にL$100ものお金をかけてもかまいませんか? 慎重に判断してください。
-48時間以内に誰もこのグループに参加しなかった場合、グループは解体され、グループの名前は今後使用できなくなります。
-	</text>
-	<text>
-		グループの環境設定
-	</text>
 	<panel name="preferences_container">
-		<check_box label="検索に表示" name="show_in_group_list"
-		     tool_tip="このグループを検索結果に表示させます" />
-		<check_box label="ウェブ上で公開" name="publish_on_web"
-		     tool_tip="このグループの情報をウェブ上で公開できるようにするかどうかを指定します" />
-		<check_box label="会員募集" name="open_enrollement"
-		     tool_tip="招待状なしに自由にこのグループに加入することを許可するかどうかを指定します" />
-		<check_box label="入会費:L$" name="check_enrollment_fee"
-		     tool_tip="このグループへの参加に入会費を必須とするかどうかを指定します" />
-		<spinner name="spin_enrollment_fee"
-		     tool_tip="「入会費」がチェックされている場合、新規会員は入会時にここに指定された金額を支払う必要があります" />
-		<check_box label="成人向けグループ" name="mature"
-		     tool_tip="あなたのグループ情報が成人向けとみなされるかどうかを指定します" />
-		<combo_box name="group_mature_check"
-		     tool_tip="あなたのグループ情報が成人向けとみなされるかどうかを指定します">
+		<check_box label="検索に表示" name="show_in_group_list" tool_tip="このグループを検索結果に表示させます"/>
+		<check_box label="会員募集" name="open_enrollement" tool_tip="招待状なしに自由にこのグループに加入することを許可するかどうかを指定します"/>
+		<check_box label="入会費:L$" name="check_enrollment_fee" tool_tip="このグループへの参加に入会費を必須とするかどうかを指定します"/>
+		<spinner name="spin_enrollment_fee" tool_tip="「入会費」がチェックされている場合、新規会員は入会時にここに指定された金額を支払う必要があります"/>
+		<combo_box name="group_mature_check" tool_tip="あなたのグループ情報が成人向けとみなされるかどうかを指定します">
 			<combo_item name="select_mature">
 				-成熟度を選択-
 			</combo_item>
@@ -92,12 +64,17 @@
 			<text name="active_title_label">
 				私のアクティブ・タイトル
 			</text>
-			<combo_box name="active_title" />
+			<combo_box name="active_title" tool_tip="このグループをアクティブ設定にした際アバターのネームタグに表示されるタイトルの設定"/>
 		</panel>
-		<check_box label="グループ通知を受信" name="receive_notices"
-		     tool_tip="グループからの通知を受信するかどうかを指定します。 このグループがスパム行為をしている場合は、このボックスのチェックを外してください" />
-		<check_box label="プロフィールにグループを表示"
-		     name="list_groups_in_profile"
-		     tool_tip="このグループをあなたのプロフィールに表示するかどうかを設定します" />
+		<check_box label="グループ通知を受信" name="receive_notices" tool_tip="グループからの通知を受信するかどうかを指定します。 このグループがスパム行為をしている場合は、このボックスのチェックを外してください"/>
+		<check_box label="プロフィールにグループを表示" name="list_groups_in_profile" tool_tip="このグループをあなたのプロフィールに表示するかどうかを設定します"/>
 	</panel>
+	<string name="incomplete_member_data_str">
+		メンバー・データを検索
+	</string>
+	<string name="confirm_group_create_str">
+		このグループの作成にはL$100かかります。 
+このグループの作成にL$100ものお金をかけてもかまいませんか? 慎重に判断してください。
+48時間以内に誰もこのグループに参加しなかった場合、グループは解体され、グループの名前は今後使用できなくなります。
+	</string>
 </panel>
diff --git a/indra/newview/skins/default/xui/ja/panel_group_land_money.xml b/indra/newview/skins/default/xui/ja/panel_group_land_money.xml
index 9d76935..9331071 100644
--- a/indra/newview/skins/default/xui/ja/panel_group_land_money.xml
+++ b/indra/newview/skins/default/xui/ja/panel_group_land_money.xml
@@ -1,19 +1,15 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="土地とL$" name="land_money_tab">
-	<text name="help_text">
-		グループ所有の区画が資金提供の詳細と共にリストに示されます。
- 土地利用の合計が、
-資金提供の合計以下になるまで警告が表示されます。
- プランニング、詳細、販売の各タブには、
-グループ財政に関する情報が表示されます。
-	</text>
-	<button label="?" name="help_button" />
-	<text name="cant_view_group_land_text">
+	<string name="help_text">
+		グループ所有の土地は貢献値の詳細と共に表示されます。土地利用の合計が寄付総額かそれ以下になるまでは警告が表示されます。「詳細」と「販売」タブにグループの資金に関する情報が表示されます。
+	</string>
+	<button label="?" name="help_button"/>
+	<string name="cant_view_group_land_text">
 		あなたはグループ所有の土地表示を許されていません。
-	</text>
-	<text name="cant_view_group_accounting_text">
+	</string>
+	<string name="cant_view_group_accounting_text">
 		あなたはグループの会計情報の表示を許されていません。
-	</text>
+	</string>
 	<string name="loading_txt">
 		ロード中...
 	</string>
@@ -21,13 +17,12 @@
 		グループの保有地
 	</text>
 	<scroll_list name="group_parcel_list">
-		<column name="name" />
-		<column name="location" />
-		<column name="area" />
-		<column label="" name="hidden" />
+		<column label="区画名" name="name"/>
+		<column label="地域(リージョン)" name="location"/>
+		<column label="面積" name="area"/>
+		<column label="" name="hidden"/>
 	</scroll_list>
-	<button label="地図上に表示" label_selected="地図上に表示"
-	     name="map_button" />
+	<button label="地図上に表示" label_selected="地図上に表示" name="map_button"/>
 	<text name="total_contributed_land_label">
 		寄付総額:
 	</text>
@@ -49,9 +44,6 @@
 	<text name="your_contribution_label">
 		あなたの貢献:
 	</text>
-	<text name="your_contribution_max_value_append">
-		平方メートル
-	</text>
 	<string name="land_contrib_error">
 		土地の出資設定を行うことができませんでした。
 	</string>
@@ -65,28 +57,19 @@
 		グループL$
 	</text>
 	<tab_container name="group_money_tab_container">
-		<panel label="計画" name="group_money_planning_tab">
-			<text_editor name="group_money_planning_text">
-				計算中…
-			</text_editor>
-		</panel>
 		<panel label="詳細" name="group_money_details_tab">
 			<text_editor name="group_money_details_text">
 				計算中…
 			</text_editor>
-			<button label="前の週" label_selected="前の週" name="earlier_details_button"
-			     tool_tip="前の週の履歴" />
-			<button label="次の週" label_selected="次の週" name="later_details_button"
-			     tool_tip="次の週の履歴" />
+			<button label="前の週" label_selected="前の週" name="earlier_details_button" tool_tip="前の週の履歴"/>
+			<button label="次の週" label_selected="次の週" name="later_details_button" tool_tip="次の週の履歴"/>
 		</panel>
 		<panel label="販売" name="group_money_sales_tab">
 			<text_editor name="group_money_sales_text">
 				計算中…
 			</text_editor>
-			<button label="前の週" label_selected="前の週" name="earlier_sales_button"
-			     tool_tip="前の週の履歴" />
-			<button label="次の週" label_selected="次の週" name="later_sales_button"
-			     tool_tip="次の週の履歴" />
+			<button label="前の週" label_selected="前の週" name="earlier_sales_button" tool_tip="前の週の履歴"/>
+			<button label="次の週" label_selected="次の週" name="later_sales_button" tool_tip="次の週の履歴"/>
 		</panel>
 	</tab_container>
 </panel>
diff --git a/indra/newview/skins/default/xui/ja/panel_group_notices.xml b/indra/newview/skins/default/xui/ja/panel_group_notices.xml
index 66ef789..35b072a 100644
--- a/indra/newview/skins/default/xui/ja/panel_group_notices.xml
+++ b/indra/newview/skins/default/xui/ja/panel_group_notices.xml
@@ -1,45 +1,44 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="通知" name="notices_tab">
-	<text name="help_text">
+	<string name="help_text">
 		通知機能を使うと、グループ内ですばやく情報を伝達できます。
 複数のあて先にメッセージを送信できるほか、
-必要に応じてアイテムをメッセージに添付することもできます。 通知は、
-受信アビリティがある役割のグループ・メンバーにのみ配信されます。
- 通知をオフにするには、一般タブを使います。
-	</text>
-	<text name="no_notices_text">
+必要に応じてアイテムをメッセージに添付することもできます。 
+通知は、受信アビリティがある役割のグループ・メンバーにのみ配信されます。
+通知をオフにするには、一般タブを使います。
+	</string>
+	<string name="no_notices_text">
 		過去の通知はありません。
-	</text>
-	<button label="?" label_selected="?" name="help_button" />
+	</string>
+	<button label="?" label_selected="?" name="help_button"/>
 	<text name="lbl">
 		グループ通知アーカイブ
 	</text>
 	<text name="lbl2">
-		通知は、14日間保存されます。閲覧したい通知をクリックしてください。
+		通知は14日間保存されます。閲覧したい通知をクリックしてください。
 新着の通知をチェックするには、[更新]ボタンをクリックします。
 ただし、各グループの通知リストは1日あたり200通に制限されます。
 	</text>
 	<scroll_list name="notice_list">
-		<column label="" name="icon" />
-		<column name="subject" />
-		<column name="from" />
-		<column name="date" />
+		<column label="" name="icon"/>
+		<column label="件名" name="subject"/>
+		<column label="送り主" name="from"/>
+		<column label="日付" name="date"/>
 	</scroll_list>
 	<text name="notice_list_none_found">
 		何も見つかりませんでした。
 	</text>
-	<button label="新しい通知を作成" label_selected="新しい通知を作成"
-	     name="create_new_notice" />
-	<button label="更新" label_selected="リスト更新" name="refresh_notices" />
+	<button label="新しい通知を作成" label_selected="新しい通知を作成" name="create_new_notice"/>
+	<button label="更新" label_selected="リスト更新" name="refresh_notices"/>
 	<panel label="新しい通知を作成" name="panel_create_new_notice">
 		<text name="lbl">
 			通知を作成
 		</text>
 		<text name="lbl2">
-			通知を送るには、件名を入力します。 持ち物のアイテムを
-1つ、このパネルにドラッグして、通知に添付
-できます。 添付できるのは、コピー、譲渡可能なアイテ
-ムで、フォルダーに送ることはできません。
+			件名を入力して通知を送ります。
+持ち物のアイテムを1つこのパネルにドラッグして、
+通知に添付することができます。 添付できるのはコピー、
+譲渡が可能なアイテムで、フォルダを送ることはできません。
 		</text>
 		<text name="lbl3">
 			件名:
@@ -50,10 +49,9 @@
 		<text name="lbl5">
 			添付:
 		</text>
-		<button label="添付物を削除" label_selected="添付物を削除"
-		     name="remove_attachment" />
-		<button label="通知を送信" label_selected="通知を送信" name="send_notice" />
-		<panel name="drop_target" />
+		<button label="添付物を削除" label_selected="添付物を削除" name="remove_attachment"/>
+		<button label="通知を送信" label_selected="通知を送信" name="send_notice"/>
+		<panel name="drop_target" tool_tip="持ち物からアイテムをメッセージ欄にドラッグしてください。通知と一緒に送信されます。送信するにはコピー、譲渡が可能なオブジェクトである必要があります。"/>
 	</panel>
 	<panel label="過去の通知を表示" name="panel_view_past_notice">
 		<text name="lbl">
@@ -68,7 +66,6 @@
 		<text name="lbl4">
 			メッセージ:
 		</text>
-		<button label="添付物を開く" label_selected="添付物を開く"
-		     name="open_attachment" />
+		<button label="添付物を開く" label_selected="添付物を開く" name="open_attachment"/>
 	</panel>
 </panel>
diff --git a/indra/newview/skins/default/xui/ja/panel_group_roles.xml b/indra/newview/skins/default/xui/ja/panel_group_roles.xml
index 0208331..5ed3bf7 100644
--- a/indra/newview/skins/default/xui/ja/panel_group_roles.xml
+++ b/indra/newview/skins/default/xui/ja/panel_group_roles.xml
@@ -1,15 +1,12 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel label="メンバーと役割" name="roles_tab">
-	<text name="default_needs_apply_text">
+	<string name="default_needs_apply_text">
 		現在のサブタブに、未適用の変更があります。
-	</text>
-	<text name="want_apply_text">
+	</string>
+	<string name="want_apply_text">
 		これらの変更を適用しますか?
-	</text>
-	<text name="cant_delete_role">
-		「全員」と「オーナー」は特別な役割なので、削除できません。
-	</text>
-	<button label="?" name="help_button" />
+	</string>
+	<button label="?" name="help_button"/>
 	<panel name="members_header">
 		<text name="static">
 			メンバーと役割
@@ -47,52 +44,52 @@
 	</panel>
 	<tab_container name="roles_tab_container">
 		<panel label="メンバー" name="members_sub_tab" tool_tip="Members">
-			<button label="検索" name="search_button" />
-			<button label="全てを表示" name="show_all_button" />
+			<button label="検索" name="search_button"/>
+			<button label="全てを表示" name="show_all_button"/>
 			<name_list name="member_list">
-				<column label="メンバー名" name="name" />
-				<column name="donated" />
-				<column label="前回のログイン" name="online" />
+				<column label="メンバー名" name="name"/>
+				<column label="寄付されたTier" name="donated"/>
+				<column label="最終ログイン" name="online"/>
 			</name_list>
-			<button label="新しい人を招待..." name="member_invite" />
-			<button label="グループから追放" name="member_eject" />
-			<text name="help_text">
+			<button label="新しい人を招待..." name="member_invite"/>
+			<button label="グループから追放" name="member_eject"/>
+			<string name="help_text">
 				あなたはメンバーに割り当てられた役割を付加、削除できます。
 Ctrl キーを押しながらメンバー名をクリックすると
 複数の人を選択できます。
-			</text>
+			</string>
 		</panel>
 		<panel label="役割" name="roles_sub_tab">
-			<button label="検索" name="search_button" />
-			<button label="すべてを表示" name="show_all_button" />
+			<button label="検索" name="search_button"/>
+			<button label="すべてを表示" name="show_all_button"/>
 			<scroll_list name="role_list">
-				<column label="役割名" name="name" />
-				<column label="肩書き" name="title" />
-				<column label="メンバー" name="members" />
+				<column label="役割名" name="name"/>
+				<column label="肩書き" name="title"/>
+				<column label="メンバー" name="members"/>
 			</scroll_list>
-			<button label="新しい役割を作成..." name="role_create" />
-			<button label="役割を削除" name="role_delete" />
-			<text name="help_text">
+			<button label="新しい役割を作成..." name="role_create"/>
+			<button label="役割を削除" name="role_delete"/>
+			<string name="help_text">
 				役割にはタイトルがあり、メンバーが行使可能な
 能力のリストが定義されます。 メンバーは、
 1つまたは複数の役割に属することができます。 1つのグループに対し、
 「全員」と「オーナー」の役割を含めて最高で10の役割を持たせることができます。
-			</text>
+			</string>
 			<string name="cant_delete_role">
 				「全員」と「オーナー」は特別な役割なので、削除できません。
 			</string>
 		</panel>
 		<panel label="能力" name="actions_sub_tab">
-			<button label="検索" name="search_button" />
-			<button label="すべてを表示" name="show_all_button" />
+			<button label="検索" name="search_button"/>
+			<button label="すべてを表示" name="show_all_button"/>
 			<scroll_list name="action_list" tool_tip="能力を選択して詳細を表示します。">
-				<column label="" name="icon" />
-				<column label="" name="action" />
+				<column label="" name="icon"/>
+				<column label="" name="action"/>
 			</scroll_list>
-			<text name="help_text">
+			<string name="help_text">
 				このグループ内で役割を与えられているメンバーが実行できる操作は、
 能力によって決まります。 さまざまな能力が用意されています。
-			</text>
+			</string>
 		</panel>
 	</tab_container>
 	<panel name="members_footer">
@@ -103,13 +100,12 @@ Ctrl キーを押しながらメンバー名をクリックすると
 			許可された能力
 		</text>
 		<scroll_list name="member_assigned_roles">
-			<column label="" name="checkbox" />
-			<column label="" name="role" />
+			<column label="" name="checkbox"/>
+			<column label="" name="role"/>
 		</scroll_list>
-		<scroll_list name="member_allowed_actions"
-		     tool_tip="許可された能力の詳細は「能力」タブをご覧ください。">
-			<column label="" name="icon" />
-			<column label="" name="action" />
+		<scroll_list name="member_allowed_actions" tool_tip="許可された能力の詳細は「能力」タブをご覧ください。">
+			<column label="" name="icon"/>
+			<column label="" name="action"/>
 		</scroll_list>
 	</panel>
 	<panel name="roles_footer">
@@ -134,17 +130,14 @@ Ctrl キーを押しながらメンバー名をクリックすると
 		<text name="static4">
 			割当られたメンバー
 		</text>
-		<text name="static5"
-		     tool_tip="現在選択されている役割で実行できる能力のリスト">
+		<text name="static5" tool_tip="現在選択されている役割で実行できる能力のリスト">
 			許可された能力
 		</text>
-		<check_box label="メンバーが可視状態" name="role_visible_in_list"
-		     tool_tip="この役割のメンバーをグループ外の人の一般タブに表示するかどうかを設定します。" />
-		<scroll_list name="role_allowed_actions"
-		     tool_tip="許可された能力の詳細は「能力」タブをご覧ください。">
-			<column label="" name="icon" />
-			<column label="" name="checkbox" />
-			<column label="" name="action" />
+		<check_box label="メンバーが可視状態" name="role_visible_in_list" tool_tip="この役割のメンバーをグループ外の人の一般タブに表示するかどうかを設定します。"/>
+		<scroll_list name="role_allowed_actions" tool_tip="許可された能力の詳細は「能力」タブをご覧ください。">
+			<column label="" name="icon"/>
+			<column label="" name="checkbox"/>
+			<column label="" name="action"/>
 		</scroll_list>
 	</panel>
 	<panel name="actions_footer">
@@ -161,4 +154,4 @@ Ctrl キーを押しながらメンバー名をクリックすると
 			能力のあるメンバー
 		</text>
 	</panel>
-</panel>
+</panel>
\ No newline at end of file
diff --git a/indra/newview/skins/default/xui/ja/panel_preferences_general.xml b/indra/newview/skins/default/xui/ja/panel_preferences_general.xml
index e891986..4c00e9d 100644
--- a/indra/newview/skins/default/xui/ja/panel_preferences_general.xml
+++ b/indra/newview/skins/default/xui/ja/panel_preferences_general.xml
@@ -79,29 +79,35 @@
 		<combo_item length="1" name="English" type="string">
 			English (英語)
 		</combo_item>
-		<combo_item length="1" name="Chinese" type="string">
-			中文 (简体) (中国語) – ベータ
+		<combo_item length="1" name="Danish" type="string">
+			Dansk (デンマーク語) – ベータ
 		</combo_item>
 		<combo_item length="1" name="Deutsch(German)" type="string">
 			Deutsch (ドイツ語) – ベータ
 		</combo_item>
+		<combo_item length="1" name="Spanish" type="string">
+			Español (スペイン語) – ベータ
+		</combo_item>
 		<combo_item length="1" name="French" type="string">
 			Français (フランス語) – ベータ
 		</combo_item>
-		<combo_item length="1" name="(Japanese)" type="string">
-			日本語 – ベータ
-		</combo_item>
-		<combo_item length="1" name="(Korean)" type="string">
-			한국어 (韓国語) – ベータ
+		<combo_item name="Hungarian">
+			Magyar (ハンガリー語) - ベータ
 		</combo_item>
-		<combo_item length="1" name="Danish" type="string">
-			Dansk (デンマーク語) - Beta
+		<combo_item name="Polish">
+			Polski (ポーランド語) - ベータ
 		</combo_item>
 		<combo_item length="1" name="Portugese" type="string">
 			Português (ポルトガル語) – ベータ
 		</combo_item>
-		<combo_item length="1" name="Spanish" type="string">
-			Español (スペイン語) – ベータ
+		<combo_item length="1" name="Chinese" type="string">
+			中文 (简体) (中国語) - ベータ
+		</combo_item>
+		<combo_item length="1" name="(Japanese)" type="string">
+			日本語 – ベータ
+		</combo_item>
+		<combo_item length="1" name="(Korean)" type="string">
+			한국어 (韓国語) – ベータ
 		</combo_item>
 	</combo_box>
 	<check_box label="言語をオブジェクトと共有" name="language_is_public" tool_tip="優先言語をインワールドのオブジェクトが認識する"/>
diff --git a/indra/newview/skins/default/xui/pt/alerts.xml b/indra/newview/skins/default/xui/pt/alerts.xml
index a384f60..2a56fd5 100644
--- a/indra/newview/skins/default/xui/pt/alerts.xml
+++ b/indra/newview/skins/default/xui/pt/alerts.xml
@@ -719,7 +719,7 @@ Pagando mais, faz com que seu anúncio apareça em posição mais alta na lista
 	</alert>
 	<alert name="ResetShowNextTimeDialogs">
 		<message name="message">
-			Você gostaria de reabilitar todas estas popups que você indicou previamente como &apos;Não mostrar-me novamente&apos;?
+			Você gostaria de reabilitar todas estas pop ups que você indicou previamente como &apos;Não me mostrar novamente&apos;? 
 		</message>
 		<option name="OK">
 			OK
@@ -894,7 +894,7 @@ Se continuar com problemas, por favor visite: http://www.secondlife.com/support
 			Aviso: Seu sistema não é compatível com os requisitos mínimos exigidos pelo Second Life. Se você continuar usando o Second Life pode experimentar uma performance ruim. Infelizmente não podemos oferecer suporte técnico para configurações de sistema não suportado. 
 
 MINSPECS
-Do you wish to visit [_URL] for more information?
+Você deseja visitar [_URL] para maiores informações?
 		</message>
 		<ignore name="ignore">
 			Ao detectar hardware não suportado
@@ -3801,7 +3801,7 @@ Detalhe de Terreno: Ajusta a quantidade de detalhes que você gostaria de ver pa
 	</alert>
 	<alert name="EnvSettingsHelpButton">
 		<message name="message">
-			Essas configurações ajustar a forma como o ambiente parece localmente no seu computador. Sua placa de vídeo precisa suportar o sombreador atmosférico, a fim de ter acesso a todas as definições. 
+			Essas configurações ajustam a forma como o ambiente parece localmente no seu computador. Sua placa de vídeo precisa suportar o sombreador atmosférico, a fim de ter acesso a todas as definições. 
  
 Ajuste o controle gradual &quot;Hora do Dia&quot;  para alterar o dia da fase localmente sobre o espectador.
  
@@ -3809,7 +3809,7 @@ Ajuste o controle gradual de &quot;Cobertura das nuvens&quot; para controlar qua
  
 Pegue uma cor da paleta de cores do &quot;Cord a Água&quot; para mudar a cor desta. 
  
-Ajuste o controle gradual de &quot;Névoa de Água&quot; para controlar o quão denso é a névoa dentro da água.
+Ajuste o controle gradual de &quot;Névoa de Água&quot; para controlar o quão densa é a névoa dentro da água.
  
 Clique &quot;Usar Horário do Terreno&quot; para redefinir a hora do dia para a região para o horário atual do dia e permanecer ligados a ela. 
  
@@ -3822,15 +3822,15 @@ Clique &quot;Água Avançada &quot; para abrir um editor com configurações mai
 		<message name="message">
 			Este editor de dia dá a você o controle sobre o ciclo de dia/noite do Second Life. Este é o ciclo usado pelo editor básico de clima do controle gradual da hora do dia. 
 
-O editor do ciclo de dia trabalha configurado por keyframes (quadros-chave). Estes são pontos (representados pelos ícones cinza no gráfico de horário) que possui o padrão de céu associado a eles.  Conforme o dia passa, o céu de Windlight &quot; anima &quot; a intersecção entre esses keyframes (quadros-chave). 
+O editor do ciclo de dia trabalha configurado por keyframes (quadros-chave). Estes são pontos (representados pelos ícones cinza no gráfico de horário) que possuem o padrão de céu associado a eles.  Conforme o dia passa, o céu de Windlight &quot; anima &quot; a intersecção entre esses keyframes (quadros-chave). 
 
 A seta amarela acima da linha de tempo representa a sua vista atua, baseada no horário do dia. Clique e arraste para ver como o seu dia será animado. Você pode adicionar ou deletar as keyframes (quadro-chave) pressionando os botões Adicionar Chave e Deletar chave ao lado direito da linha de tempo.
 
 Você pode configurar a posição do tempo de uma keyframe (quadro-chave) arrastando-o pela linha do tempo, ou configurando manualmente no quadro de configurações do seu keyframe (quadro-chave), será possível a você associar o seu keyframe a este respectivo padrão WindLight. 
 
-A duração do ciclo determina a duração geral do &quot;dia&quot;. Configurá-la para um valor baixo (por exmplo, 2 min.) quer dizer que a linha do tempo de 24 horas será animada completamente em apenas dois minutos reais!  Assim que estiveres satisfeito com a linha do tempo e o ciclo do keyframe (quadro chave), use os botões Play e Stop para uma prévia de como ficará o resultado. Lembre-se você também pode utilizar a seta amarela indicadora do tempo acima da linha do tempo par aver o ciclo animado interativamente. Usando o botão do tempo do terreno irá sincronizá-lo a duração do seu dia ao ciclo diário do terreno.
+A duração do ciclo determina a duração geral do &quot;dia&quot;. Configurá-la para um valor baixo (por exmplo, 2 min.) quer dizer que a linha do tempo de 24 horas será animada completamente em apenas dois minutos reais!  Assim que estiveres satisfeito com a linha do tempo e o ciclo do keyframe (quadro chave), use os botões Play e Stop para uma prévia de como ficará o resultado. Lembre-se você também pode utilizar a seta amarela indicadora do tempo acima da linha do tempo para ver o ciclo animado interativamente. Usando o botão do tempo do terreno irá sincronizar a duração do seu dia ao ciclo diário do terreno.
 
-Assim que estiver satisfeito com o seu ciclo diário, pode salvá-lo ou carregar no botão de teste de dia . Note que agora nós permitimos apenas um Ciclo de dia.
+Assim que estiver satisfeito com o seu ciclo diário, pode salvá-lo ou carregar no botão de teste de dia . Note que agora nós permitimos apenas um Ciclo de dia.  
 		</message>
 	</alert>
 	<alert name="HelpBlueHorizon">
@@ -3942,7 +3942,7 @@ Similar ao azimute.
 	</alert>
 	<alert name="HelpClassicClouds">
 		<message name="message">
-			Marque esta check box para permitir a reprodução do nas nuvens classicas mais velhas do Second Life, além das nuvens WindLight.
+			Marque esta check box para permitir a reprodução das nuvens clássicas mais velhas do Second Life, além das nuvens WindLight. 
 		</message>
 	</alert>
 	<alert name="HelpWaterFogColor">
diff --git a/indra/newview/skins/default/xui/pt/floater_about_land.xml b/indra/newview/skins/default/xui/pt/floater_about_land.xml
index 51064bc..c61ef4b 100644
--- a/indra/newview/skins/default/xui/pt/floater_about_land.xml
+++ b/indra/newview/skins/default/xui/pt/floater_about_land.xml
@@ -344,7 +344,7 @@ Vá para o menu Mundo &gt; Sobre a Terra ou selecione outro terreno para mostrar
 			<line_editor name="url_description" tool_tip="Texto mostrado ao lado do botão reproduzir/carregar" left="97"/>
 			<text length="1" name="Media texture:" type="string">
 				Substituir
-Texture:
+Textura:
 			</text>
 			<texture_picker label="" name="media texture" tool_tip="Clique para escolher uma imagem" left="97"/>
 			<text name="replace_texture_help">
@@ -353,8 +353,8 @@ a página web depois que você clicar na seta de
 reproduzir.)
 			</text>
 			<text name="Options:">
-				Mídia
-Options:
+				Opções de
+Mídia:
 			</text>
 			<check_box left="97" label="Escala automática" name="media_auto_scale" tool_tip="Marcando esta opção o conteúdo se ajustará ao lote automaticamente. O lote pode se tornar lento e com baixa qualidade visual nenhum outro ajuste de textura ou alinhamento será necessário."/>
 			<check_box label="Mídia em Repetição" name="media_loop" tool_tip="Executar a mídia repetidamente. Quando a mídia terminar a execução, ela reiniciará do começo."/>
@@ -387,7 +387,7 @@ Options:
 					Usar um canal especial privado
 				</radio_item>
 				<radio_item name="Disabled">
-					Desabilitar audio espacial neste terreno
+					Desabilitar áudio espacial neste terreno
 				</radio_item>
 			</radio_group>
 		</panel>
diff --git a/indra/newview/skins/default/xui/pt/floater_day_cycle_options.xml b/indra/newview/skins/default/xui/pt/floater_day_cycle_options.xml
index bf99231..a76d06e 100644
--- a/indra/newview/skins/default/xui/pt/floater_day_cycle_options.xml
+++ b/indra/newview/skins/default/xui/pt/floater_day_cycle_options.xml
@@ -4,7 +4,7 @@
 		<panel label="Ciclo do dia" name="Day Cycle">
 			<button label="?" name="WLDayCycleHelp"/>
 			<text name="WL12am">
-				12:00 meia noite
+				12:00 meia-noite
 			</text>
 			<text name="WL3am">
 				3:00 da manhã
diff --git a/indra/newview/skins/default/xui/pt/floater_im.xml b/indra/newview/skins/default/xui/pt/floater_im.xml
index 81090d5..2d1769e 100644
--- a/indra/newview/skins/default/xui/pt/floater_im.xml
+++ b/indra/newview/skins/default/xui/pt/floater_im.xml
@@ -7,7 +7,7 @@
 		[FIRST] [LAST] está offline.
 	</string>
 	<string name="invite_message">
-		Clique no botão [BUTTON NAME] para aceitar/ conectar para esse pate- papo em voz.
+		Clique no botão [BUTTON NAME] para aceitar/ conectar para esse bate-papo em voz. 
 	</string>
 	<string name="muted_message">
 		Você emudeceu este residente. Enviar uma mensagem vai automaticamente reativá-lo novamente .
diff --git a/indra/newview/skins/default/xui/pt/floater_lagmeter.xml b/indra/newview/skins/default/xui/pt/floater_lagmeter.xml
index d0f0d40..1e90862 100644
--- a/indra/newview/skins/default/xui/pt/floater_lagmeter.xml
+++ b/indra/newview/skins/default/xui/pt/floater_lagmeter.xml
@@ -63,7 +63,7 @@
 		Causa possível: Carregamento de Imagens
 	</string>
 	<string name="client_texture_memory_cause_msg">
-		Causa possível: Muitas imagens na memoria
+		Causa possível: Muitas imagens na memória
 	</string>
 	<string name="client_complex_objects_cause_msg">
 		Causa possível: Muitos objetos complexos na cena
diff --git a/indra/newview/skins/default/xui/pt/floater_media_browser.xml b/indra/newview/skins/default/xui/pt/floater_media_browser.xml
index c2387d8..d684b75 100644
--- a/indra/newview/skins/default/xui/pt/floater_media_browser.xml
+++ b/indra/newview/skins/default/xui/pt/floater_media_browser.xml
@@ -9,11 +9,11 @@
 			<button label="Ir" name="go" left_delta="245" width="35"  />
 		</layout_panel>
 		<layout_panel name="parcel_owner_controls">
-			<button label="Enviar a URL Atual Para o Terreno" name="assign"/>
+			<button label="Enviar a URL atual para o terreno" name="assign"/>
 		</layout_panel>
 		<layout_panel name="external_controls">
 			<button label="Abrir no meu Navegador de Web" name="open_browser" width="196"/>
-			<check_box label="Sempre abrir no meu navegador de web" name="open_always" left_delta="201" />
+			<check_box label="Sempre abrir no meu Navegador de Web" name="open_always" left_delta="201" />
 			<button label="Fechar" name="close"/>
 		</layout_panel>
 	</layout_stack>
diff --git a/indra/newview/skins/default/xui/pt/floater_preview_notecard_keep_discard.xml b/indra/newview/skins/default/xui/pt/floater_preview_notecard_keep_discard.xml
index c5ba6b2..a42280a 100644
--- a/indra/newview/skins/default/xui/pt/floater_preview_notecard_keep_discard.xml
+++ b/indra/newview/skins/default/xui/pt/floater_preview_notecard_keep_discard.xml
@@ -9,7 +9,7 @@
 	<button label="Guardar" label_selected="Guardar" name="Keep"/>
 	<button label="Descartar" label_selected="Descartar" name="Discard"/>
 	<string name="no_object">
-		Não foi possível encontrar o objeto que contem esta nota.:
+		Não foi possível encontrar o objeto que contém esta nota.:
 	</string>
 	<string name="not_allowed">
 		Você não tem permissão para ler esta nota.
diff --git a/indra/newview/skins/default/xui/pt/floater_snapshot.xml b/indra/newview/skins/default/xui/pt/floater_snapshot.xml
index 9180ae6..83bd2ca 100644
--- a/indra/newview/skins/default/xui/pt/floater_snapshot.xml
+++ b/indra/newview/skins/default/xui/pt/floater_snapshot.xml
@@ -29,8 +29,8 @@
 		</flyout_button_item>
 	</flyout_button>
 	<button label="Cancelar" name="discard_btn"/>
-	<button label="Mais &gt;&gt;" name="more_btn" tool_tip="Opções Avançadas"/>
-	<button label="&lt;&lt; Menos" name="less_btn" tool_tip="Opções Avançadas"/>
+	<button label="Mais &gt;" name="more_btn" tool_tip="Opções Avançadas"/>
+	<button label="&lt; Menos" name="less_btn" tool_tip="Opções Avançadas"/>
 	<text name="type_label2">
 		Tamanho
 	</text>
diff --git a/indra/newview/skins/default/xui/pt/floater_windlight_options.xml b/indra/newview/skins/default/xui/pt/floater_windlight_options.xml
index e42137b..ff22a87 100644
--- a/indra/newview/skins/default/xui/pt/floater_windlight_options.xml
+++ b/indra/newview/skins/default/xui/pt/floater_windlight_options.xml
@@ -100,7 +100,7 @@
 				I
 			</text>
 			<text name="WLEastAngleText">
-				Angulo Leste
+				Ângulo Leste
 			</text>
 			<button label="?" name="WLEastAngleHelp"/>
 			<text name="SunGlowText">
@@ -114,7 +114,7 @@
 			</text>
 			<button label="?" name="WLSceneGammaHelp"/>
 			<text name="WLStarText">
-				Brilho da Estrêla
+				Brilho da Estrela
 			</text>
 			<button label="?" name="WLStarBrightnessHelp"/>
 		</panel>
diff --git a/indra/newview/skins/default/xui/pt/menu_viewer.xml b/indra/newview/skins/default/xui/pt/menu_viewer.xml
index 56195b5..229f457 100644
--- a/indra/newview/skins/default/xui/pt/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/pt/menu_viewer.xml
@@ -125,9 +125,9 @@
 		<menu_item_separator label="-----------" name="separator6"/>
 		<menu label="Configurações de ambiente" name="Environment Settings">
 			<menu_item_call label="Amanhecer" name="Sunrise"/>
-			<menu_item_call label="Meio dia" name="Noon"/>
+			<menu_item_call label="Meio-dia" name="Noon"/>
 			<menu_item_call label="Pôr do sol" name="Sunset"/>
-			<menu_item_call label="Meia noite" name="Midnight"/>
+			<menu_item_call label="Meia-noite" name="Midnight"/>
 			<menu_item_call label="Reverter ao padrão da região" name="Revert to Region Default"/>
 			<menu_item_separator label="-----------" name="separator"/>
 			<menu_item_call label="Editor de ambiente" name="Environment Editor"/>
@@ -178,7 +178,7 @@
 		<menu_item_call label="Ajuda Second Life" name="Second Life Help"/>
 		<menu_item_call label="Tutorial" name="Tutorial"/>
 		<menu_item_separator label="-----------" name="separator"/>
-		<menu_item_call label="Blog official da Linden..." name="Official Linden Blog..."/>
+		<menu_item_call label="Blog oficial da Linden..." name="Official Linden Blog..."/>
 		<menu_item_separator label="-----------" name="separator2"/>
 		<menu_item_call label="Portal de Scripts..." name="Scripting Portal..."/>
 		<menu_item_separator label="-----------" name="separator3"/>
diff --git a/indra/newview/skins/default/xui/pt/mime_types.xml b/indra/newview/skins/default/xui/pt/mime_types.xml
index be20865..7b6225e 100644
--- a/indra/newview/skins/default/xui/pt/mime_types.xml
+++ b/indra/newview/skins/default/xui/pt/mime_types.xml
@@ -43,13 +43,13 @@
 	</widgetset>
 	<widgetset name="audio">
 		<label name="audio_label">
-			Audio
+			Áudio
 		</label>
 		<tooltip name="audio_tooltip">
-			Há um audio neste local
+			Há um áudio neste local
 		</tooltip>
 		<playtip name="audio_playtip">
-			Iniciar audio disponível nest local
+			Iniciar áudio disponível neste local
 		</playtip>
 	</widgetset>
 	<scheme name="rtsp">
@@ -64,12 +64,12 @@
 	</mimetype>
 	<mimetype name="none/none">
 		<label name="none/none_label">
-			- None - Nenhum -
+			- Nenhum - 
 		</label>
 	</mimetype>
 	<mimetype name="audio/*">
 		<label name="audio2_label">
-			Audio
+			Áudio
 		</label>
 	</mimetype>
 	<mimetype name="video/*">
diff --git a/indra/newview/skins/default/xui/pt/notify.xml b/indra/newview/skins/default/xui/pt/notify.xml
index 0768c72..4714c72 100644
--- a/indra/newview/skins/default/xui/pt/notify.xml
+++ b/indra/newview/skins/default/xui/pt/notify.xml
@@ -83,7 +83,7 @@
 	<notify name="UnableToLoadGesture">
 		<message name="message">
 			Impossível de carregar o gesto [NAME].
-Por favor, tente de novo.
+Por favor, tente novamente.
 		</message>
 	</notify>
 	<notify name="InventoryLoaded">
@@ -223,7 +223,7 @@ Por favor, tente de novo.
 	</notify>
 	<notify name="TeleportToLandmark">
 		<message name="message">
-			Agora que você chegou à mainland, você pode teletransportar-se para locais como &apos;[NAME]&apos; clicando no botão de Inventário, no canto direito inferior de sua tela, e selecionando a pasta Landmarks.
+			Agora que você chegou à mainland, você pode se teletransportar para locais como '[NAME]' clicando no botão de Inventário, no canto direito inferior de sua tela, e selecionando a pasta Landmarks.
 Dê um duplo-clique na landmark e clique Teletransportar para viajar para o local desejado.
 		</message>
 	</notify>
@@ -349,7 +349,7 @@ Dê um duplo-clique num cartão de visitas e em Mensagem Instantânea e digite s
 	</notify>
 	<notify name="NoVoice">
 		<message name="message">
-			O uso de voz está desabilkitado nesta terra.
+			O uso de voz está desabilitado nesta terra.
 		</message>
 	</notify>
 	<notify name="NoBuild">
@@ -580,7 +580,7 @@ Dê um duplo-clique num cartão de visitas e em Mensagem Instantânea e digite s
 Você tentou vestir um/uma [TYPE] que não carregou.
 
 Não se apavore
-Tente novamente em alguns minutes para que tudo se ajeite
+Tente novamente em alguns minutos para que tudo se ajeite
 		</message>
 	</notify>
 	<notify name="FailedToLoadWearable">
@@ -678,7 +678,7 @@ Tente novamente em alguns minutos para que tudo se ajeite.
 	</notify>
 	<notify name="ScriptQuestionCautionChatDenied">
 		<message name="message">
-			&apos;[OBJECTNAME]&apos;, um objeto de  &apos;[OWNERNAME]&apos;, localizado em [REGIONNAME] a [REGIONPOS], revogou suas permissoes para: [PERMISSIONS].
+			&apos;[OBJECTNAME]&apos;, um objeto de  &apos;[OWNERNAME]&apos;, localizado em [REGIONNAME] a [REGIONPOS], revogou suas permissões para: [PERMISSIONS].
 		</message>
 	</notify>
 	<notify name="ScriptQuestionCaution">
@@ -686,7 +686,7 @@ Tente novamente em alguns minutos para que tudo se ajeite.
 			&apos;[OBJECTNAME]&apos;, um objeto de  &apos;[NAME]&apos;, gostaria de:
  
 [QUESTIONS]
-Se você não confia neste objeto ou em seu criador, você deveria negar-lhe o pedido. Para informações adicionais clique no botão Detalhes.
+Se você não confia neste objeto ou em seu criador, você deveria negar o pedido. Para informações adicionais clique no botão Detalhes.
 
 Autorizar este pedido?
 		</message>
@@ -861,7 +861,7 @@ Clique Aceitar para jumtar-se à chamada ou Recusar para recusar o convite. Cliq
 	<notify name="InviteAdHoc">
 		<message name="message">
 			[NAME] está lhe convidando para uma conversa em conferência...
- Clique Aceitar para juntar-se à conversa, ouu Recusar para recusar o convite. Clique Mudo para calar este usuário.
+ Clique Aceitar para se juntar à conversa, ou Recusar para recusar o convite. Clique Mudo para calar este usuário.
 		</message>
 		<option name="Accept">
 			Aceitar
@@ -905,7 +905,7 @@ Clique Aceitar para jumtar-se à chamada ou Recusar para recusar o convite. Cliq
 	</notify>
 	<notify name="VoiceChannelJoinFailed">
 		<message name="message">
-			Falha na conexão com [VOICE_CHANNEL_NAME], tente novamente mais tarde. Voce será reconectado agora a Converda com voz local.
+			Falha na conexão com [VOICE_CHANNEL_NAME], tente novamente mais tarde. Voce será reconectado agora a Conversa com voz local. 
 		</message>
 	</notify>
 	<notify name="VoiceLoginRetry">
@@ -935,7 +935,7 @@ Clique Aceitar para jumtar-se à chamada ou Recusar para recusar o convite. Cliq
 	</notify>
 	<notify name="VoiceCallGenericError">
 		<message name="message">
-			Ocorreu um erro enquanto você tentava conectar-se à conversa de voz de [VOICE_CHANNEL_NAME].  Favor tentar novamente mais tarde.
+			Ocorreu um erro enquanto você tentava se conectar à conversa de voz de [VOICE_CHANNEL_NAME].  Favor tentar novamente mais tarde.
 		</message>
 	</notify>
 	<notify name="ServerVersionChanged">
diff --git a/indra/newview/skins/default/xui/pt/panel_friends.xml b/indra/newview/skins/default/xui/pt/panel_friends.xml
index 5815605..9a69ec1 100644
--- a/indra/newview/skins/default/xui/pt/panel_friends.xml
+++ b/indra/newview/skins/default/xui/pt/panel_friends.xml
@@ -7,7 +7,7 @@
 		<column name="icon_online_status" tool_tip="Status Online"/>
 		<column label="Name" name="friend_name" tool_tip="Nome"/>
 		<column name="icon_visible_online" tool_tip="Amigo pod ever quando você está online"/>
-		<column name="icon_visible_map" tool_tip="Amigo pode localiza-lo no mapa"/>
+		<column name="icon_visible_map" tool_tip="Amigo pode localizá-lo no mapa"/>
 		<column name="icon_edit_mine" tool_tip="Amigo pode editar, apagar ou pegar os seus objetos"/>
 		<column name="icon_edit_theirs" tool_tip="Você pode editar os objetos deste amigo"/>
 	</scroll_list>
diff --git a/indra/newview/skins/default/xui/pt/panel_group_general.xml b/indra/newview/skins/default/xui/pt/panel_group_general.xml
index ec57397..9670730 100644
--- a/indra/newview/skins/default/xui/pt/panel_group_general.xml
+++ b/indra/newview/skins/default/xui/pt/panel_group_general.xml
@@ -41,13 +41,13 @@
 		Preferências de Grupos
 	</text>
 	<panel name="preferences_container">
-		<check_box label="Mostre na busca" name="show_in_group_list" tool_tip="Deixe as pessoas ver este grupo nos resultados de busca."/>
+		<check_box label="Mostre na busca" name="show_in_group_list" tool_tip="Deixe as pessoas verem este grupo nos resultados de busca. "/>
 		<check_box label="Aberta inscrição" name="open_enrollement" tool_tip="Esse grupo permite que novos membros entrem sem serem convidados"/>
 		<check_box label="Taxa de inscrição: L$" name="check_enrollment_fee" tool_tip="É necessário uma taxa de registro."/>
 		<spinner name="spin_enrollment_fee" tool_tip="Os novos membros terão de pagar a taxa de registro para fazerem parte do grupo."/>
 		<combo_box name="group_mature_check" tool_tip="Define se a informação do seu grupo é considerada adulta.">
 			<combo_item name="select_mature">
-				- Selecinar conteúdo adulto -
+				- Selecionar conteúdo adulto -
 			</combo_item>
 			<combo_item name="mature">
 				Conteúdo adulto
@@ -71,6 +71,6 @@
 	<string name="confirm_group_create_str">
 		Criar este grupo irá custar L$100. 
 Você está realmente, realmente, REALMENTE seguro que deseja gastar L$100 para criar este grupo? 
-Esteja consciente de que ninguém mais se juntar este grupo dentro de 48 horas, será dissolvido e o nome não estará disponível para uso futuro.
+Esteja consciente de que se ninguém mais se juntar este grupo dentro de 48 horas, este será dissolvido e o nome não estará disponível para uso futuro.
 	</string>
 </panel>
diff --git a/indra/newview/skins/default/xui/pt/panel_group_voting.xml b/indra/newview/skins/default/xui/pt/panel_group_voting.xml
index d324452..7bfb203 100644
--- a/indra/newview/skins/default/xui/pt/panel_group_voting.xml
+++ b/indra/newview/skins/default/xui/pt/panel_group_voting.xml
@@ -65,7 +65,7 @@
 		Abrir propostas do grupo
 	</string>
 	<string name="proposals_header_view_inst">
-		Dê um duplo click na proposto a qual você gostaria de votar ou pressione Criar Proposto para criar uma nova proposta
+		Dê um duplo click na proposta a qual você gostaria de votar ou pressione Criar Proposto para criar uma nova proposta 
 	</string>
 	<string name="proposals_header_create_txt">
 		Criar proposta
diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_audio.xml b/indra/newview/skins/default/xui/pt/panel_preferences_audio.xml
index 89bb405..3661e56 100644
--- a/indra/newview/skins/default/xui/pt/panel_preferences_audio.xml
+++ b/indra/newview/skins/default/xui/pt/panel_preferences_audio.xml
@@ -7,12 +7,12 @@
 		Preferências de &#10;Streaming:
 	</text>
 	<text name="audio_prefs_text">
-		Preferências de Audio:
+		Preferências de Áudio:
 	</text>
 	<panel label="Volume" name="Volume Panel"/>
 	<check_box label="Tocar música em streaming quando disponível (usa mais banda)" name="streaming_music"/>
 	<check_box label="Tocar corrente de Media quando disponível (usa mais banda)" name="streaming_video"/>
-	<check_box label="Tocar media automaticamente" name="auto_streaming_video"/>
+	<check_box label="Tocar mídia automaticamente" name="auto_streaming_video"/>
 	<check_box label="Silenciar Áudio quando minimizar janela" name="mute_when_minimized"/>
 	<slider label="Efeito Doppler" name="Doppler Effect"/>
 	<slider label="Fator Distância" name="Distance Factor"/>
diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_im.xml b/indra/newview/skins/default/xui/pt/panel_preferences_im.xml
index 6f1d678..e142dc1 100644
--- a/indra/newview/skins/default/xui/pt/panel_preferences_im.xml
+++ b/indra/newview/skins/default/xui/pt/panel_preferences_im.xml
@@ -3,7 +3,7 @@
 	<text length="1" name="text_box" type="string">
 		Minha situação online:
 	</text>
-	<check_box label="Apenas meus Amigos e Grupos podem ver-me online" name="online_visibility"/>
+	<check_box label="Apenas meus Amigos e Grupos podem me ver online" name="online_visibility"/>
 	<text length="1" name="text_box2" type="string">
 		Opções MI:
 	</text>
diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_input.xml b/indra/newview/skins/default/xui/pt/panel_preferences_input.xml
index 38a3f90..a7cdaf5 100644
--- a/indra/newview/skins/default/xui/pt/panel_preferences_input.xml
+++ b/indra/newview/skins/default/xui/pt/panel_preferences_input.xml
@@ -10,7 +10,7 @@
 	<text length="1" name=" Auto Fly Options:" type="string">
 		Vôo Automático:
 	</text>
-	<check_box label="Voar/Aterrizar pressionando teclar para cima/baixo" name="automatic_fly"/>
+	<check_box label="Voar/Aterrissar pressionando teclar para cima/baixo" name="automatic_fly"/>
 	<text length="1" name=" Camera Options:" type="string">
 		Opções de Câmera:
 	</text>
diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_web.xml b/indra/newview/skins/default/xui/pt/panel_preferences_web.xml
index eb0690c..088a892 100644
--- a/indra/newview/skins/default/xui/pt/panel_preferences_web.xml
+++ b/indra/newview/skins/default/xui/pt/panel_preferences_web.xml
@@ -24,5 +24,5 @@
 		Endereço:
 	</text>
 	<line_editor name="web_proxy_editor" tool_tip="Nome ou endereço de IP do proxy que você gostaria de usar" left="237" />
-	<spinner label="Numero da porta:" name="web_proxy_port" label_width="95" width="170"/>
+	<spinner label="Número da porta:" name="web_proxy_port" label_width="95" width="170"/>
 </panel>
diff --git a/indra/newview/skins/default/xui/pt/panel_region_debug.xml b/indra/newview/skins/default/xui/pt/panel_region_debug.xml
index 0c2ab2b..3a9ff9e 100644
--- a/indra/newview/skins/default/xui/pt/panel_region_debug.xml
+++ b/indra/newview/skins/default/xui/pt/panel_region_debug.xml
@@ -14,7 +14,7 @@
 	<button label="?" name="disable_physics_help"/>
 	<button label="Aplicar" name="apply_btn"/>
 	<text name="objret_text_lbl">
-		Retornar Objeto
+		Retornar objeto
 	</text>
 	<text name="resident_text_lbl">
 		Residente:
diff --git a/indra/newview/skins/default/xui/pt/panel_region_estate.xml b/indra/newview/skins/default/xui/pt/panel_region_estate.xml
index eb3aa5a..423444d 100644
--- a/indra/newview/skins/default/xui/pt/panel_region_estate.xml
+++ b/indra/newview/skins/default/xui/pt/panel_region_estate.xml
@@ -26,7 +26,7 @@ todas as regiões do Terreno.
 	<text name="Only Allow">
 		Acesso restrito a:
 	</text>
-	<check_box label="Residentes com informações de pagamento em arquivo" name="limit_payment" tool_tip="Banir residents não identificados."/>
+	<check_box label="Residentes com informações de pagamento em arquivo" name="limit_payment" tool_tip="Banir residentes não identificados."/>
 	<check_box label="Idade verificada -adultos" name="limit_age_verified" tool_tip="Banir residentes que não tenham confirmado a sua idade. Ver support.secondlife.com para mais informações."/>
 	<check_box label="Permitir conversa de voz" name="voice_chat_check"/>
 	<button label="?" name="voice_chat_help"/>
diff --git a/indra/newview/skins/default/xui/pt/panel_voice_controls.xml b/indra/newview/skins/default/xui/pt/panel_voice_controls.xml
index 549256f..29ecfbf 100644
--- a/indra/newview/skins/default/xui/pt/panel_voice_controls.xml
+++ b/indra/newview/skins/default/xui/pt/panel_voice_controls.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="voice_controls">
-	<button label="" name="speakers_btn" tool_tip="Mostra os residents que estão usando bate-papo em voz em torno de ti."/>
+	<button label="" name="speakers_btn" tool_tip="Mostra os residentes que estão usando bate-papo em voz em torno de ti."/>
 	<button label="Falar" name="push_to_talk" tool_tip="Segure o botão para falar"/>
 	<button label="" name="ptt_lock" tool_tip="Clique na fechadura para entrar no modo de falar"/>
 	<button label="" name="show_channel" tool_tip="Clique para mostrar o canal corrente de voz"/>
diff --git a/indra/newview/skins/default/xui/pt/panel_voice_enable.xml b/indra/newview/skins/default/xui/pt/panel_voice_enable.xml
index c6e6b0f..b159ec7 100644
--- a/indra/newview/skins/default/xui/pt/panel_voice_enable.xml
+++ b/indra/newview/skins/default/xui/pt/panel_voice_enable.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes"?>
 <panel name="content_panel">
 	<text_editor name="voice_intro_text1">
-		Bem-vindo ao Bate-Papo em voz do Second Life! O Bate-papo em Voz lhe permite falar com outros residentes. Este assistente irá guiá-lo através dos passos necessários para configurar o Bate-papo em voz. Utiliza-lo exige falantes e áudio, se quiser falar com outras pessoas, use um microfone ou headset estéreo com microfone.
+		Bem-vindo ao Bate-Papo em voz do Second Life! O Bate-papo em Voz lhe permite falar com outros residentes. Este assistente irá guiá-lo através dos passos necessários para configurar o Bate-papo em voz. Utilizá-lo exige falantes e áudio, se quiser falar com outras pessoas, use um microfone ou headset estéreo com microfone.
 	</text_editor>
 	<text_editor name="voice_intro_text2">
 		Bate-papo em voz é ativado por padrão. Quer continuar usando a conversa em voz?
diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml
index c57cefa..bc0e1ea 100644
--- a/indra/newview/skins/default/xui/pt/strings.xml
+++ b/indra/newview/skins/default/xui/pt/strings.xml
@@ -260,7 +260,7 @@
 		Dança 8
 	</string>
 	<string name="anim_express_disdain">
-		Desdén
+		Desdém
 	</string>
 	<string name="anim_drink">
 		Beber
@@ -362,7 +362,7 @@
 		Sorrir
 	</string>
 	<string name="anim_smoke_idle">
-		Fumar a toa
+		Fumar à toa
 	</string>
 	<string name="anim_smoke_inhale">
 		Inalar Fumaça
diff --git a/indra/newview/skins/default/xui/pt/teleport_strings.xml b/indra/newview/skins/default/xui/pt/teleport_strings.xml
index df6dc2c..15d07d2 100644
--- a/indra/newview/skins/default/xui/pt/teleport_strings.xml
+++ b/indra/newview/skins/default/xui/pt/teleport_strings.xml
@@ -28,7 +28,7 @@ www.secondlife.com/support
 			A fluxo nesta região é atualmente tão alto e entupido que seu pedido de teleporte não será possível teleportar com êxito em tempo oportuno. Por favor, tente novamente em alguns minutos ou vá a uma área menos ocupada.
 		</message>
 		<message name="expired_tport">
-			Desculpe, mas o sistema não conseguiu concluir o seu pedido teleporte em tempo hábil. Por favor, tente novamente em alguns minutos.
+			Desculpe, mas o sistema não conseguiu concluir o seu pedido de teleporte em tempo hábil. Por favor, tente novamente em alguns minutos.
 		</message>
 		<message name="expired_region_handoff">
 			Desculpe, mas o sistema não pôde concluir a sua travessia de região em tempo hábil. Por favor, tente novamente em alguns minutos.
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 09c72bb..0a2d925 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -232,11 +232,6 @@ class WindowsManifest(ViewerManifest):
         # Vivox runtimes
         if self.prefix(src="vivox-runtime/i686-win32", dst=""):
             self.path("SLVoice.exe")
-            self.path("SLVoiceAgent.exe")
-            self.path("libeay32.dll")
-            self.path("srtp.dll")
-            self.path("ssleay32.dll")
-            self.path("tntk.dll")
             self.path("alut.dll")
             self.path("vivoxsdk.dll")
             self.path("ortp.dll")
@@ -432,7 +427,6 @@ class DarwinManifest(ViewerManifest):
                 self.path("vivox-runtime/universal-darwin/libortp.dylib", "libortp.dylib")
                 self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib")
                 self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice")
-                self.path("vivox-runtime/universal-darwin/SLVoiceAgent.app", "SLVoiceAgent.app")
 
                 # llkdu dynamic library
 #                self.path("../../libraries/universal-darwin/lib_release/libllkdu.dylib", "libllkdu.dylib")
@@ -565,13 +559,6 @@ class LinuxManifest(ViewerManifest):
 
 
     def package_finish(self):
-        # stripping all the libs removes a few megabytes from the end-user package
-        for s,d in self.file_list:
-            if re.search("lib/lib.+\.so.*", d):
-                self.run_command('strip -S %s' % d)
-            if re.search("app_settings/mozilla-runtime-.*/lib.+\.so.*", d):
-                self.run_command('strip %s' % d)
-
         if 'installer_name' in self.args:
             installer_name = self.args['installer_name']
         else:
@@ -642,9 +629,9 @@ class Linux_i686Manifest(LinuxManifest):
             self.path("libSDL-1.2.so.0")
             self.path("libELFIO.so")
             self.path("libopenjpeg.so.2")
-            #self.path("libtcmalloc.so.0") - bugged
-            #self.path("libstacktrace.so.0") - probably bugged
 #            self.path("libllkdu.so", "../bin/libllkdu.so") # llkdu goes in bin for some reason
+            self.path("libalut.so")
+            self.path("libopenal.so", "libopenal.so.1")
             self.end_prefix("lib")
 
             # Vivox runtimes
@@ -652,10 +639,8 @@ class Linux_i686Manifest(LinuxManifest):
                     self.path("SLVoice")
                     self.end_prefix()
             if self.prefix(src="vivox-runtime/i686-linux", dst="lib"):
-                    self.path("libopenal.so.1")
                     self.path("libortp.so")
                     self.path("libvivoxsdk.so")
-                    self.path("libalut.so")
                     self.end_prefix("lib")
 
 class Linux_x86_64Manifest(LinuxManifest):
diff --git a/install.xml b/install.xml
index 928bdd8..6fb47ac 100644
--- a/install.xml
+++ b/install.xml
@@ -1184,6 +1184,39 @@ anguage Infrstructure (CLI) international standard</string>
           </map>
         </map>
       </map>
+      <key>openal</key>
+      <map>
+        <key>copyright</key>
+        <string>Copyright (C) 2008 by authors.</string>
+        <key>description</key>
+        <string>3D Audio library</string>
+        <key>license</key>
+        <string>lgpl</string>
+        <key>packages</key>
+        <map>
+          <key>darwin</key>
+          <map>
+            <key>md5sum</key>
+            <string>a0757244e3e6688fde2ffeea35cc1f96</string>
+            <key>url</key>
+            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openal-darwin-20080924.tar.bz2</uri>
+          </map>
+          <key>linux</key>
+          <map>
+            <key>md5sum</key>
+            <string>f0d9a8d1318b519cffe6c40c9cac4e21</string>
+            <key>url</key>
+            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openal-linux-20081010c-59a71b14-plughw.tar.bz2</uri>
+          </map>
+          <key>windows</key>
+          <map>
+            <key>md5sum</key>
+            <string>a0757244e3e6688fde2ffeea35cc1f96</string>
+            <key>url</key>
+            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/openal-windows-20080924.tar.bz2</uri>
+          </map>
+        </map>
+      </map>
       <key>openSSL</key>
       <map>
         <key>license</key>
@@ -1410,16 +1443,16 @@ anguage Infrstructure (CLI) international standard</string>
           <key>darwin</key>
           <map>
             <key>md5sum</key>
-            <string>4d786ce5732b70cf230f0eebcec9571c</string>
+            <string>649cba53ec790f15248af620252f8300</string>
             <key>url</key>
-            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/vivox-darwin-20080613.tar.bz2</uri>
+            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/vivox-2.1.3010.5556-darwin-20081125.tar.bz2</uri>
           </map>
           <key>linux</key>
           <map>
             <key>md5sum</key>
-            <string>3b7dc33c106e1c4c10fc515249ac7bda</string>
+            <string>8155db9c21df2f5fb5ffb52847ef0ee1</string>
             <key>url</key>
-            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/vivox-linux-20080613.tar.bz2</uri>
+            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/vivox-2.1.3010.5556-linux-20081125.tar.bz2</uri>
           </map>
           <key>linux32</key>
           <map>
@@ -1431,9 +1464,9 @@ anguage Infrstructure (CLI) international standard</string>
           <key>windows</key>
           <map>
             <key>md5sum</key>
-            <string>ef09fcc8dea5d5fc911667f03c8b8a99</string>
+            <string>3b368e7f14db6a03af39580709bdde39</string>
             <key>url</key>
-            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/vivox-windows-20080613.tar.bz2</uri>
+            <uri>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/vivox-2.1.3010.5556-windows-20081125.tar.bz2</uri>
           </map>
         </map>
       </map>

-- 
A client for connecting to 3D metaverses such as Linden Labs Secondlife(tm) and OpenSim grids



More information about the Pkg-games-commits mailing list