PageRenderTime 45ms CodeModel.GetById 8ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llaudio/llaudioengine_openal.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 544 lines | 402 code | 91 blank | 51 comment | 51 complexity | 8053992ea8607557c1527cb64faeeff7 MD5 | raw file
  1/**
  2 * @file audioengine_openal.cpp
  3 * @brief implementation of audio engine using OpenAL
  4 * support as a OpenAL 3D implementation
  5 *
  6 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 */
 27
 28#include "linden_common.h"
 29#include "lldir.h"
 30
 31#include "llaudioengine_openal.h"
 32#include "lllistener_openal.h"
 33
 34
 35const float LLAudioEngine_OpenAL::WIND_BUFFER_SIZE_SEC = 0.05f;
 36
 37LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
 38	:
 39	mWindGen(NULL),
 40	mWindBuf(NULL),
 41	mWindBufFreq(0),
 42	mWindBufSamples(0),
 43	mWindBufBytes(0),
 44	mWindSource(AL_NONE),
 45	mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS)
 46{
 47}
 48
 49// virtual
 50LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
 51{
 52}
 53
 54// virtual
 55bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata)
 56{
 57	mWindGen = NULL;
 58	LLAudioEngine::init(num_channels, userdata);
 59
 60	if(!alutInit(NULL, NULL))
 61	{
 62		llwarns << "LLAudioEngine_OpenAL::init() ALUT initialization failed: " << alutGetErrorString (alutGetError ()) << llendl;
 63		return false;
 64	}
 65
 66	llinfos << "LLAudioEngine_OpenAL::init() OpenAL successfully initialized" << llendl;
 67
 68	llinfos << "OpenAL version: "
 69		<< ll_safe_string(alGetString(AL_VERSION)) << llendl;
 70	llinfos << "OpenAL vendor: "
 71		<< ll_safe_string(alGetString(AL_VENDOR)) << llendl;
 72	llinfos << "OpenAL renderer: "
 73		<< ll_safe_string(alGetString(AL_RENDERER)) << llendl;
 74
 75	ALint major = alutGetMajorVersion ();
 76	ALint minor = alutGetMinorVersion ();
 77	llinfos << "ALUT version: " << major << "." << minor << llendl;
 78
 79	ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
 80
 81	alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major);
 82	alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &minor);
 83	llinfos << "ALC version: " << major << "." << minor << llendl;
 84
 85	llinfos << "ALC default device: "
 86		<< ll_safe_string(alcGetString(device,
 87					       ALC_DEFAULT_DEVICE_SPECIFIER))
 88		<< llendl;
 89
 90	return true;
 91}
 92
 93// virtual
 94std::string LLAudioEngine_OpenAL::getDriverName(bool verbose)
 95{
 96	ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
 97	std::ostringstream version;
 98
 99	version <<
100		"OpenAL";
101
102	if (verbose)
103	{
104		version <<
105			", version " <<
106			ll_safe_string(alGetString(AL_VERSION)) <<
107			" / " <<
108			ll_safe_string(alGetString(AL_VENDOR)) <<
109			" / " <<
110			ll_safe_string(alGetString(AL_RENDERER));
111		
112		if (device)
113			version <<
114				": " <<
115				ll_safe_string(alcGetString(device,
116				    ALC_DEFAULT_DEVICE_SPECIFIER));
117	}
118
119	return version.str();
120}
121
122// virtual
123void LLAudioEngine_OpenAL::allocateListener()
124{
125	mListenerp = (LLListener *) new LLListener_OpenAL();
126	if(!mListenerp)
127	{
128		llwarns << "LLAudioEngine_OpenAL::allocateListener() Listener creation failed" << llendl;
129	}
130}
131
132// virtual
133void LLAudioEngine_OpenAL::shutdown()
134{
135	llinfos << "About to LLAudioEngine::shutdown()" << llendl;
136	LLAudioEngine::shutdown();
137
138	llinfos << "About to alutExit()" << llendl;
139	if(!alutExit())
140	{
141		llwarns << "Nuts." << llendl;
142		llwarns << "LLAudioEngine_OpenAL::shutdown() ALUT shutdown failed: " << alutGetErrorString (alutGetError ()) << llendl;
143	}
144
145	llinfos << "LLAudioEngine_OpenAL::shutdown() OpenAL successfully shut down" << llendl;
146
147	delete mListenerp;
148	mListenerp = NULL;
149}
150
151LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer()
152{
153	return new LLAudioBufferOpenAL();
154}
155
156LLAudioChannel *LLAudioEngine_OpenAL::createChannel()
157{
158	return new LLAudioChannelOpenAL();
159}
160
161void LLAudioEngine_OpenAL::setInternalGain(F32 gain)
162{
163	//llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl;
164	alListenerf(AL_GAIN, gain);
165}
166
167LLAudioChannelOpenAL::LLAudioChannelOpenAL()
168	:
169	mALSource(AL_NONE),
170	mLastSamplePos(0)
171{
172	alGenSources(1, &mALSource);
173}
174
175LLAudioChannelOpenAL::~LLAudioChannelOpenAL()
176{
177	cleanup();
178	alDeleteSources(1, &mALSource);
179}
180
181void LLAudioChannelOpenAL::cleanup()
182{
183	alSourceStop(mALSource);
184	mCurrentBufferp = NULL;
185}
186
187void LLAudioChannelOpenAL::play()
188{
189	if (mALSource == AL_NONE)
190	{
191		llwarns << "Playing without a mALSource, aborting" << llendl;
192		return;
193	}
194
195	if(!isPlaying())
196	{
197		alSourcePlay(mALSource);
198		getSource()->setPlayedOnce(true);
199	}
200}
201
202void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp)
203{
204	if (channelp)
205	{
206		LLAudioChannelOpenAL *masterchannelp =
207			(LLAudioChannelOpenAL*)channelp;
208		if (mALSource != AL_NONE &&
209		    masterchannelp->mALSource != AL_NONE)
210		{
211			// we have channels allocated to master and slave
212			ALfloat master_offset;
213			alGetSourcef(masterchannelp->mALSource, AL_SEC_OFFSET,
214				     &master_offset);
215
216			llinfos << "Syncing with master at " << master_offset
217				<< "sec" << llendl;
218			// *TODO: detect when this fails, maybe use AL_SAMPLE_
219			alSourcef(mALSource, AL_SEC_OFFSET, master_offset);
220		}
221	}
222	play();
223}
224
225bool LLAudioChannelOpenAL::isPlaying()
226{
227	if (mALSource != AL_NONE)
228	{
229		ALint state;
230		alGetSourcei(mALSource, AL_SOURCE_STATE, &state);
231		if(state == AL_PLAYING)
232		{
233			return true;
234		}
235	}
236		
237	return false;
238}
239
240bool LLAudioChannelOpenAL::updateBuffer()
241{
242	if (LLAudioChannel::updateBuffer())
243	{
244		// Base class update returned true, which means that we need to actually
245		// set up the source for a different buffer.
246		LLAudioBufferOpenAL *bufferp = (LLAudioBufferOpenAL *)mCurrentSourcep->getCurrentBuffer();
247		ALuint buffer = bufferp->getBuffer();
248		alSourcei(mALSource, AL_BUFFER, buffer);
249		mLastSamplePos = 0;
250	}
251
252	if (mCurrentSourcep)
253	{
254		alSourcef(mALSource, AL_GAIN,
255			  mCurrentSourcep->getGain() * getSecondaryGain());
256		alSourcei(mALSource, AL_LOOPING,
257			  mCurrentSourcep->isLoop() ? AL_TRUE : AL_FALSE);
258		alSourcef(mALSource, AL_ROLLOFF_FACTOR,
259			  gAudiop->mListenerp->getRolloffFactor());
260	}
261
262	return true;
263}
264
265
266void LLAudioChannelOpenAL::updateLoop()
267{
268	if (mALSource == AL_NONE)
269	{
270		return;
271	}
272
273	// Hack:  We keep track of whether we looped or not by seeing when the
274	// sample position looks like it's going backwards.  Not reliable; may
275	// yield false negatives.
276	//
277	ALint cur_pos;
278	alGetSourcei(mALSource, AL_SAMPLE_OFFSET, &cur_pos);
279	if (cur_pos < mLastSamplePos)
280	{
281		mLoopedThisFrame = true;
282	}
283	mLastSamplePos = cur_pos;
284}
285
286
287void LLAudioChannelOpenAL::update3DPosition()
288{
289	if(!mCurrentSourcep)
290	{
291		return;
292	}
293	if (mCurrentSourcep->isAmbient())
294	{
295		alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0);
296		alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0);
297		alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_TRUE);
298	} else {
299		LLVector3 float_pos;
300		float_pos.setVec(mCurrentSourcep->getPositionGlobal());
301		alSourcefv(mALSource, AL_POSITION, float_pos.mV);
302		alSourcefv(mALSource, AL_VELOCITY, mCurrentSourcep->getVelocity().mV);
303		alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_FALSE);
304	}
305
306	alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain() * getSecondaryGain());
307}
308
309LLAudioBufferOpenAL::LLAudioBufferOpenAL()
310{
311	mALBuffer = AL_NONE;
312}
313
314LLAudioBufferOpenAL::~LLAudioBufferOpenAL()
315{
316	cleanup();
317}
318
319void LLAudioBufferOpenAL::cleanup()
320{
321	if(mALBuffer != AL_NONE)
322	{
323		alDeleteBuffers(1, &mALBuffer);
324		mALBuffer = AL_NONE;
325	}
326}
327
328bool LLAudioBufferOpenAL::loadWAV(const std::string& filename)
329{
330	cleanup();
331	mALBuffer = alutCreateBufferFromFile(filename.c_str());
332	if(mALBuffer == AL_NONE)
333	{
334		ALenum error = alutGetError(); 
335		if (gDirUtilp->fileExists(filename))
336		{
337			llwarns <<
338				"LLAudioBufferOpenAL::loadWAV() Error loading "
339				<< filename
340				<< " " << alutGetErrorString(error) << llendl;
341		}
342		else
343		{
344			// It's common for the file to not actually exist.
345			lldebugs <<
346				"LLAudioBufferOpenAL::loadWAV() Error loading "
347				 << filename
348				 << " " << alutGetErrorString(error) << llendl;
349		}
350		return false;
351	}
352
353	return true;
354}
355
356U32 LLAudioBufferOpenAL::getLength()
357{
358	if(mALBuffer == AL_NONE)
359	{
360		return 0;
361	}
362	ALint length;
363	alGetBufferi(mALBuffer, AL_SIZE, &length);
364	return length / 2; // convert size in bytes to size in (16-bit) samples
365}
366
367// ------------
368
369bool LLAudioEngine_OpenAL::initWind()
370{
371	ALenum error;
372	llinfos << "LLAudioEngine_OpenAL::initWind() start" << llendl;
373
374	mNumEmptyWindALBuffers = MAX_NUM_WIND_BUFFERS;
375
376	alGetError(); /* clear error */
377	
378	alGenSources(1,&mWindSource);
379	
380	if((error=alGetError()) != AL_NO_ERROR)
381	{
382		llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind sources: "<<error<<llendl;
383	}
384
385	mWindGen = new LLWindGen<WIND_SAMPLE_T>;
386
387	mWindBufFreq = mWindGen->getInputSamplingRate();
388	mWindBufSamples = llceil(mWindBufFreq * WIND_BUFFER_SIZE_SEC);
389	mWindBufBytes = mWindBufSamples * 2 /*stereo*/ * sizeof(WIND_SAMPLE_T);
390
391	mWindBuf = new WIND_SAMPLE_T [mWindBufSamples * 2 /*stereo*/];
392
393	if(mWindBuf==NULL)
394	{
395		llerrs << "LLAudioEngine_OpenAL::initWind() Error creating wind memory buffer" << llendl;
396		return false;
397	}
398
399	llinfos << "LLAudioEngine_OpenAL::initWind() done" << llendl;
400
401	return true;
402}
403
404void LLAudioEngine_OpenAL::cleanupWind()
405{
406	llinfos << "LLAudioEngine_OpenAL::cleanupWind()" << llendl;
407
408	if (mWindSource != AL_NONE)
409	{
410		// detach and delete all outstanding buffers on the wind source
411		alSourceStop(mWindSource);
412		ALint processed;
413		alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
414		while (processed--)
415		{
416			ALuint buffer = AL_NONE;
417			alSourceUnqueueBuffers(mWindSource, 1, &buffer);
418			alDeleteBuffers(1, &buffer);
419		}
420
421		// delete the wind source itself
422		alDeleteSources(1, &mWindSource);
423
424		mWindSource = AL_NONE;
425	}
426	
427	delete[] mWindBuf;
428	mWindBuf = NULL;
429
430	delete mWindGen;
431	mWindGen = NULL;
432}
433
434void LLAudioEngine_OpenAL::updateWind(LLVector3 wind_vec, F32 camera_altitude)
435{
436	LLVector3 wind_pos;
437	F64 pitch;
438	F64 center_freq;
439	ALenum error;
440	
441	if (!mEnableWind)
442		return;
443	
444	if(!mWindBuf)
445		return;
446	
447	if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
448	{
449		
450		// wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
451		// need to convert this to the conventional orientation DS3D and OpenAL use
452		// where +X = right, +Y = up, +Z = backwards
453		
454		wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
455		
456		pitch = 1.0 + mapWindVecToPitch(wind_vec);
457		center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
458		
459		mWindGen->mTargetFreq = (F32)center_freq;
460		mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
461		mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
462		
463		alSourcei(mWindSource, AL_LOOPING, AL_FALSE);
464		alSource3f(mWindSource, AL_POSITION, 0.0, 0.0, 0.0);
465		alSource3f(mWindSource, AL_VELOCITY, 0.0, 0.0, 0.0);
466		alSourcef(mWindSource, AL_ROLLOFF_FACTOR, 0.0);
467		alSourcei(mWindSource, AL_SOURCE_RELATIVE, AL_TRUE);
468	}
469
470	// ok lets make a wind buffer now
471
472	ALint processed, queued, unprocessed;
473	alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
474	alGetSourcei(mWindSource, AL_BUFFERS_QUEUED, &queued);
475	unprocessed = queued - processed;
476
477	// ensure that there are always at least 3x as many filled buffers
478	// queued as we managed to empty since last time.
479	mNumEmptyWindALBuffers = llmin(mNumEmptyWindALBuffers + processed * 3 - unprocessed, MAX_NUM_WIND_BUFFERS-unprocessed);
480	mNumEmptyWindALBuffers = llmax(mNumEmptyWindALBuffers, 0);
481
482	//llinfos << "mNumEmptyWindALBuffers: " << mNumEmptyWindALBuffers	<<" (" << unprocessed << ":" << processed << ")" << llendl;
483
484	while(processed--) // unqueue old buffers
485	{
486		ALuint buffer;
487		ALenum error;
488		alGetError(); /* clear error */
489		alSourceUnqueueBuffers(mWindSource, 1, &buffer);
490		error = alGetError();
491		if(error != AL_NO_ERROR)
492		{
493			llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (unqueuing) buffers" << llendl;
494		}
495		else
496		{
497			alDeleteBuffers(1, &buffer);
498		}
499	}
500
501	unprocessed += mNumEmptyWindALBuffers;
502	while (mNumEmptyWindALBuffers > 0) // fill+queue new buffers
503	{
504		ALuint buffer;
505		alGetError(); /* clear error */
506		alGenBuffers(1,&buffer);
507		if((error=alGetError()) != AL_NO_ERROR)
508		{
509			llwarns << "LLAudioEngine_OpenAL::updateWind() Error creating wind buffer: " << error << llendl;
510			break;
511		}
512
513		alBufferData(buffer,
514			     AL_FORMAT_STEREO16,
515			     mWindGen->windGenerate(mWindBuf,
516						    mWindBufSamples),
517			     mWindBufBytes,
518			     mWindBufFreq);
519		error = alGetError();
520		if(error != AL_NO_ERROR)
521		{
522			llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (bufferdata) buffers" << llendl;
523		}
524		
525		alSourceQueueBuffers(mWindSource, 1, &buffer);
526		error = alGetError();
527		if(error != AL_NO_ERROR)
528		{
529			llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (queuing) buffers" << llendl;
530		}
531
532		--mNumEmptyWindALBuffers;
533	}
534
535	ALint playing;
536	alGetSourcei(mWindSource, AL_SOURCE_STATE, &playing);
537	if(playing != AL_PLAYING)
538	{
539		alSourcePlay(mWindSource);
540
541		lldebugs << "Wind had stopped - probably ran out of buffers - restarting: " << (unprocessed+mNumEmptyWindALBuffers) << " now queued." << llendl;
542	}
543}
544