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