PageRenderTime 147ms CodeModel.GetById 61ms app.highlight 45ms RepoModel.GetById 35ms app.codeStats 0ms

/indra/newview/llvieweraudio.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 504 lines | 349 code | 64 blank | 91 comment | 55 complexity | 8ef9989609345a39555f18a5d44b3acf MD5 | raw file
  1/** 
  2 * @file llvieweraudio.cpp
  3 * @brief Audio functions that used to be in viewer.cpp
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "llviewerprecompiledheaders.h"
 28
 29#include "llaudioengine.h"
 30#include "llagent.h"
 31#include "llagentcamera.h"
 32#include "llappviewer.h"
 33#include "llvieweraudio.h"
 34#include "llviewercamera.h"
 35#include "llviewercontrol.h"
 36#include "llviewerwindow.h"
 37#include "llvoiceclient.h"
 38#include "llviewermedia.h"
 39#include "llprogressview.h"
 40#include "llcallbacklist.h"
 41#include "llstartup.h"
 42#include "llviewerparcelmgr.h"
 43#include "llparcel.h"
 44
 45/////////////////////////////////////////////////////////
 46
 47LLViewerAudio::LLViewerAudio() :
 48	mDone(true),
 49	mFadeState(FADE_IDLE),
 50	mFadeTime(),
 51    mIdleListnerActive(false),
 52	mForcedTeleportFade(false)
 53{
 54	mTeleportFailedConnection = LLViewerParcelMgr::getInstance()->
 55		setTeleportFailedCallback(boost::bind(&LLViewerAudio::onTeleportFailed, this));
 56}
 57
 58LLViewerAudio::~LLViewerAudio()
 59{
 60	mTeleportFailedConnection.disconnect();
 61}
 62
 63void LLViewerAudio::registerIdleListener()
 64{
 65	if(mIdleListnerActive==false)
 66	{
 67		mIdleListnerActive = true;
 68		doOnIdleRepeating(boost::bind(boost::bind(&LLViewerAudio::onIdleUpdate, this)));
 69	}
 70
 71}
 72
 73void LLViewerAudio::startInternetStreamWithAutoFade(std::string streamURI)
 74{
 75	// Old and new stream are identical
 76	if (mNextStreamURI == streamURI)
 77	{
 78		return;
 79	}
 80
 81	// Record the URI we are going to be switching to	
 82	mNextStreamURI = streamURI;
 83
 84	switch (mFadeState)
 85	{
 86		case FADE_IDLE:
 87		// If a stream is playing fade it out first
 88		if (!gAudiop->getInternetStreamURL().empty())
 89		{
 90			// The order of these tests is important, state FADE_OUT will be processed below
 91			mFadeState = FADE_OUT;
 92		}
 93		// Otherwise the new stream can be faded in
 94		else
 95		{
 96			mFadeState = FADE_IN;
 97			gAudiop->startInternetStream(mNextStreamURI);
 98			startFading();
 99			registerIdleListener();
100			break;
101		}
102
103		case FADE_OUT:
104			startFading();
105			registerIdleListener();
106			break;
107
108		case FADE_IN:
109			registerIdleListener();
110			break;
111
112		default:
113			llwarns << "Unknown fading state: " << mFadeState << llendl;
114			break;
115	}
116}
117
118// A return of false from onIdleUpdate means it will be called again next idle update.
119// A return of true means we have finished with it and the callback will be deleted.
120bool LLViewerAudio::onIdleUpdate()
121{
122	bool fadeIsFinished = false;
123
124	// There is a delay in the login sequence between when the parcel information has
125	// arrived and the music stream is started and when the audio system is called to set
126	// initial volume levels.  This code extends the fade time so you hear a full fade in.
127	if ((LLStartUp::getStartupState() < STATE_STARTED))
128	{
129		stream_fade_timer.reset();
130		stream_fade_timer.setTimerExpirySec(mFadeTime);
131	}
132
133	if (mDone)
134	{
135		//  This should be a rare or never occurring state.
136		if (mFadeState == FADE_IDLE)
137		{
138			deregisterIdleListener();
139			fadeIsFinished = true; // Stop calling onIdleUpdate
140		}
141
142		// we have finished the current fade operation
143		if (mFadeState == FADE_OUT)
144		{
145			// Clear URI
146			gAudiop->startInternetStream(LLStringUtil::null);
147			gAudiop->stopInternetStream();
148				
149			if (!mNextStreamURI.empty())
150			{
151				mFadeState = FADE_IN;
152				gAudiop->startInternetStream(mNextStreamURI);
153				startFading();
154			}
155			else
156			{
157				mFadeState = FADE_IDLE;
158				deregisterIdleListener();
159				fadeIsFinished = true; // Stop calling onIdleUpdate
160			}
161		}
162		else if (mFadeState == FADE_IN)
163		{
164			if (mNextStreamURI != gAudiop->getInternetStreamURL())
165			{
166				mFadeState = FADE_OUT;
167				startFading();
168			}
169			else
170			{
171				mFadeState = FADE_IDLE;
172				deregisterIdleListener();
173				fadeIsFinished = true; // Stop calling onIdleUpdate
174			}
175		}
176	}
177
178	return fadeIsFinished;
179}
180
181void LLViewerAudio::stopInternetStreamWithAutoFade()
182{
183	mFadeState = FADE_IDLE;
184	mNextStreamURI = LLStringUtil::null;
185	mDone = true;
186
187	gAudiop->startInternetStream(LLStringUtil::null);
188	gAudiop->stopInternetStream();
189}
190
191void LLViewerAudio::startFading()
192{
193	const F32 AUDIO_MUSIC_FADE_IN_TIME = 3.0f;
194	const F32 AUDIO_MUSIC_FADE_OUT_TIME = 2.0f;
195	// This minimum fade time prevents divide by zero and negative times
196	const F32 AUDIO_MUSIC_MINIMUM_FADE_TIME = 0.01f;
197
198	if(mDone)
199	{
200		// The fade state here should only be one of FADE_IN or FADE_OUT, but, in case it is not,
201		// rather than check for both states assume a fade in and check for the fade out case.
202		mFadeTime = AUDIO_MUSIC_FADE_IN_TIME;
203		if (LLViewerAudio::getInstance()->getFadeState() == LLViewerAudio::FADE_OUT)
204		{
205			mFadeTime = AUDIO_MUSIC_FADE_OUT_TIME;
206		}
207
208		// Prevent invalid fade time
209		mFadeTime = llmax(mFadeTime, AUDIO_MUSIC_MINIMUM_FADE_TIME);
210
211		stream_fade_timer.reset();
212		stream_fade_timer.setTimerExpirySec(mFadeTime);
213		mDone = false;
214	}
215}
216
217F32 LLViewerAudio::getFadeVolume()
218{
219	F32 fade_volume = 1.0f;
220
221	if (stream_fade_timer.hasExpired())
222	{
223		mDone = true;
224		// If we have been fading out set volume to 0 until the next fade state occurs to prevent
225		// an audio transient.
226		if (LLViewerAudio::getInstance()->getFadeState() == LLViewerAudio::FADE_OUT)
227		{
228			fade_volume = 0.0f;
229		}
230	}
231
232	if (!mDone)
233	{
234		// Calculate how far we are into the fade time
235		fade_volume = stream_fade_timer.getElapsedTimeF32() / mFadeTime;
236		
237		if (LLViewerAudio::getInstance()->getFadeState() == LLViewerAudio::FADE_OUT)
238		{
239			// If we are not fading in then we are fading out, so invert the fade
240			// direction; start loud and move towards zero volume.
241			fade_volume = 1.0f - fade_volume;
242		}
243	}
244
245	return fade_volume;
246}
247
248void LLViewerAudio::onTeleportFailed()
249{
250	if (gAudiop)
251	{
252		LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
253		if (parcel)
254		{
255			mNextStreamURI = parcel->getMusicURL();
256		}
257	}
258}
259
260void init_audio() 
261{
262	if (!gAudiop) 
263	{
264		llwarns << "Failed to create an appropriate Audio Engine" << llendl;
265		return;
266	}
267	LLVector3d lpos_global = gAgentCamera.getCameraPositionGlobal();
268	LLVector3 lpos_global_f;
269
270	lpos_global_f.setVec(lpos_global);
271					
272	gAudiop->setListener(lpos_global_f,
273						  LLVector3::zero,	// LLViewerCamera::getInstance()->getVelocity(),    // !!! BUG need to replace this with smoothed velocity!
274						  LLViewerCamera::getInstance()->getUpAxis(),
275						  LLViewerCamera::getInstance()->getAtAxis());
276
277// load up our initial set of sounds we'll want so they're in memory and ready to be played
278
279	BOOL mute_audio = gSavedSettings.getBOOL("MuteAudio");
280
281	if (!mute_audio && FALSE == gSavedSettings.getBOOL("NoPreload"))
282	{
283		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndAlert")));
284		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndBadKeystroke")));
285		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndChatFromObject")));
286		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndClick")));
287		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndClickRelease")));
288		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndHealthReductionF")));
289		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndHealthReductionM")));
290		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndIncomingChat")));
291		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndIncomingIM")));
292		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndInvApplyToObject")));
293		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndInvalidOp")));
294		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndInventoryCopyToInv")));
295		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndMoneyChangeDown")));
296		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndMoneyChangeUp")));
297		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectCopyToInv")));
298		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectCreate")));
299		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectDelete")));
300		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectRezIn")));
301		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndObjectRezOut")));
302		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndSnapshot")));
303		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStartAutopilot")));
304		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStartFollowpilot")));
305		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStartIM")));
306		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndStopAutopilot")));
307		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTeleportOut")));
308		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTextureApplyToObject")));
309		//gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTextureCopyToInv")));
310		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndTyping")));
311		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndWindowClose")));
312		gAudiop->preloadSound(LLUUID(gSavedSettings.getString("UISndWindowOpen")));
313	}
314
315	audio_update_volume(true);
316}
317
318void audio_update_volume(bool force_update)
319{
320	F32 master_volume = gSavedSettings.getF32("AudioLevelMaster");
321	BOOL mute_audio = gSavedSettings.getBOOL("MuteAudio");
322
323	LLProgressView* progress = gViewerWindow->getProgressView();
324	BOOL progress_view_visible = FALSE;
325
326	if (progress)
327	{
328		progress_view_visible = progress->getVisible();
329	}
330
331	if (!gViewerWindow->getActive() && gSavedSettings.getBOOL("MuteWhenMinimized"))
332	{
333		mute_audio = TRUE;
334	}
335	F32 mute_volume = mute_audio ? 0.0f : 1.0f;
336
337	// Sound Effects
338	if (gAudiop) 
339	{
340		gAudiop->setMasterGain ( master_volume );
341
342		gAudiop->setDopplerFactor(gSavedSettings.getF32("AudioLevelDoppler"));
343		gAudiop->setRolloffFactor(gSavedSettings.getF32("AudioLevelRolloff"));
344		gAudiop->setMuted(mute_audio || progress_view_visible);
345		
346		if (force_update)
347		{
348			audio_update_wind(true);
349		}
350
351		// handle secondary gains
352		gAudiop->setSecondaryGain(LLAudioEngine::AUDIO_TYPE_SFX,
353								  gSavedSettings.getBOOL("MuteSounds") ? 0.f : gSavedSettings.getF32("AudioLevelSFX"));
354		gAudiop->setSecondaryGain(LLAudioEngine::AUDIO_TYPE_UI,
355								  gSavedSettings.getBOOL("MuteUI") ? 0.f : gSavedSettings.getF32("AudioLevelUI"));
356		gAudiop->setSecondaryGain(LLAudioEngine::AUDIO_TYPE_AMBIENT,
357								  gSavedSettings.getBOOL("MuteAmbient") ? 0.f : gSavedSettings.getF32("AudioLevelAmbient"));
358	}
359
360	// Streaming Music
361	if (gAudiop) 
362	{
363		if (progress_view_visible  && !LLViewerAudio::getInstance()->getForcedTeleportFade())
364		{
365			LLViewerAudio::getInstance()->setForcedTeleportFade(true);
366			LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(LLStringUtil::null);
367			LLViewerAudio::getInstance()->setNextStreamURI(LLStringUtil::null);
368		}
369
370		if (!progress_view_visible && LLViewerAudio::getInstance()->getForcedTeleportFade() == true)
371		{
372			LLViewerAudio::getInstance()->setForcedTeleportFade(false);
373		}
374
375		F32 music_volume = gSavedSettings.getF32("AudioLevelMusic");
376		BOOL music_muted = gSavedSettings.getBOOL("MuteMusic");
377		F32 fade_volume = LLViewerAudio::getInstance()->getFadeVolume();
378
379		music_volume = mute_volume * master_volume * music_volume * fade_volume;
380		gAudiop->setInternetStreamGain (music_muted ? 0.f : music_volume);
381	}
382
383	// Streaming Media
384	F32 media_volume = gSavedSettings.getF32("AudioLevelMedia");
385	BOOL media_muted = gSavedSettings.getBOOL("MuteMedia");
386	media_volume = mute_volume * master_volume * media_volume;
387	LLViewerMedia::setVolume( media_muted ? 0.0f : media_volume );
388
389	// Voice
390	if (LLVoiceClient::getInstance())
391	{
392		F32 voice_volume = gSavedSettings.getF32("AudioLevelVoice");
393		voice_volume = mute_volume * master_volume * voice_volume;
394		BOOL voice_mute = gSavedSettings.getBOOL("MuteVoice");
395		LLVoiceClient::getInstance()->setVoiceVolume(voice_mute ? 0.f : voice_volume);
396		LLVoiceClient::getInstance()->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic"));
397
398		if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized")))
399		{
400			LLVoiceClient::getInstance()->setMuteMic(true);
401		}
402		else
403		{
404			LLVoiceClient::getInstance()->setMuteMic(false);
405		}
406	}
407}
408
409void audio_update_listener()
410{
411	if (gAudiop)
412	{
413		// update listener position because agent has moved	
414		LLVector3d lpos_global = gAgentCamera.getCameraPositionGlobal();		
415		LLVector3 lpos_global_f;
416		lpos_global_f.setVec(lpos_global);
417	
418		gAudiop->setListener(lpos_global_f,
419							 // LLViewerCamera::getInstance()VelocitySmoothed, 
420							 // LLVector3::zero,	
421							 gAgent.getVelocity(),    // !!! *TODO: need to replace this with smoothed velocity!
422							 LLViewerCamera::getInstance()->getUpAxis(),
423							 LLViewerCamera::getInstance()->getAtAxis());
424	}
425}
426
427void audio_update_wind(bool force_update)
428{
429#ifdef kAUDIO_ENABLE_WIND
430	//
431	//  Extract height above water to modulate filter by whether above/below water 
432	// 
433	LLViewerRegion* region = gAgent.getRegion();
434	if (region)
435	{
436		static F32 last_camera_water_height = -1000.f;
437		LLVector3 camera_pos = gAgentCamera.getCameraPositionAgent();
438		F32 camera_water_height = camera_pos.mV[VZ] - region->getWaterHeight();
439		
440		//
441		//  Don't update rolloff factor unless water surface has been crossed
442		//
443		if (force_update || (last_camera_water_height * camera_water_height) < 0.f)
444		{
445            static LLUICachedControl<F32> rolloff("AudioLevelRolloff", 1.0f);
446			if (camera_water_height < 0.f)
447			{
448				gAudiop->setRolloffFactor(rolloff * LL_ROLLOFF_MULTIPLIER_UNDER_WATER);
449			}
450			else 
451			{
452				gAudiop->setRolloffFactor(rolloff);
453			}
454		}
455        
456        // Scale down the contribution of weather-simulation wind to the
457        // ambient wind noise.  Wind velocity averages 3.5 m/s, with gusts to 7 m/s
458        // whereas steady-state avatar walk velocity is only 3.2 m/s.
459        // Without this the world feels desolate on first login when you are
460        // standing still.
461        static LLUICachedControl<F32> wind_level("AudioLevelWind", 0.5f);
462        LLVector3 scaled_wind_vec = gWindVec * wind_level;
463        
464        // Mix in the avatar's motion, subtract because when you walk north,
465        // the apparent wind moves south.
466        LLVector3 final_wind_vec = scaled_wind_vec - gAgent.getVelocity();
467        
468		// rotate the wind vector to be listener (agent) relative
469		gRelativeWindVec = gAgent.getFrameAgent().rotateToLocal( final_wind_vec );
470
471		// don't use the setter setMaxWindGain() because we don't
472		// want to screw up the fade-in on startup by setting actual source gain
473		// outside the fade-in.
474		F32 master_volume  = gSavedSettings.getBOOL("MuteAudio") ? 0.f : gSavedSettings.getF32("AudioLevelMaster");
475		F32 ambient_volume = gSavedSettings.getBOOL("MuteAmbient") ? 0.f : gSavedSettings.getF32("AudioLevelAmbient");
476		F32 max_wind_volume = master_volume * ambient_volume;
477
478		const F32 WIND_SOUND_TRANSITION_TIME = 2.f;
479		// amount to change volume this frame
480		F32 volume_delta = (LLFrameTimer::getFrameDeltaTimeF32() / WIND_SOUND_TRANSITION_TIME) * max_wind_volume;
481		if (force_update) 
482		{
483			// initialize wind volume (force_update) by using large volume_delta
484			// which is sufficient to completely turn off or turn on wind noise
485			volume_delta = 1.f;
486		}
487
488		// mute wind when not flying
489		if (gAgent.getFlying())
490		{
491			// volume increases by volume_delta, up to no more than max_wind_volume
492			gAudiop->mMaxWindGain = llmin(gAudiop->mMaxWindGain + volume_delta, max_wind_volume);
493		}
494		else
495		{
496			// volume decreases by volume_delta, down to no less than 0
497			gAudiop->mMaxWindGain = llmax(gAudiop->mMaxWindGain - volume_delta, 0.f);
498		}
499		
500		last_camera_water_height = camera_water_height;
501		gAudiop->updateWind(gRelativeWindVec, camera_water_height);
502	}
503#endif
504}