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..a956131
--- /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
39LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
40 :
41 mWindGen(NULL),
42 mWindBuf(NULL),
43 mWindBufFreq(0),
44 mWindBufSamples(0),
45 mWindBufBytes(0),
46 mWindSource(AL_NONE),
47 mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS)
48{
49}
50
51// virtual
52LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
53{
54}
55
56// virtual
57bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata)
58{
59 mWindGen = NULL;
60 LLAudioEngine::init(num_channels, userdata);
61
62 if(!alutInit(NULL, NULL))
63 {
64 LL_WARNS("OpenAL") << "LLAudioEngine_OpenAL::init() ALUT initialization failed: " << alutGetErrorString (alutGetError ()) << LL_ENDL;
65 return false;
66 }
67
68 LL_INFOS("OpenAL") << "LLAudioEngine_OpenAL::init() OpenAL successfully initialized" << LL_ENDL;
69
70 LL_INFOS("OpenAL") << "OpenAL version: "
71 << ll_safe_string(alGetString(AL_VERSION)) << LL_ENDL;
72 LL_INFOS("OpenAL") << "OpenAL vendor: "
73 << ll_safe_string(alGetString(AL_VENDOR)) << LL_ENDL;
74 LL_INFOS("OpenAL") << "OpenAL renderer: "
75 << ll_safe_string(alGetString(AL_RENDERER)) << LL_ENDL;
76
77 ALint major = alutGetMajorVersion ();
78 ALint minor = alutGetMinorVersion ();
79 LL_INFOS("OpenAL") << "ALUT version: " << major << "." << minor << LL_ENDL;
80
81 ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
82
83 alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major);
84 alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &minor);
85 LL_INFOS("OpenAL") << "ALC version: " << major << "." << minor << LL_ENDL;
86
87 LL_INFOS("OpenAL") << "ALC default device: "
88 << ll_safe_string(alcGetString(device,
89 ALC_DEFAULT_DEVICE_SPECIFIER))
90 << LL_ENDL;
91
92 return true;
93}
94
95// virtual
96std::string LLAudioEngine_OpenAL::getDriverName(bool verbose)
97{
98 ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
99 std::ostringstream version;
100
101 version <<
102 "OpenAL";
103
104 if (verbose)
105 {
106 version <<
107 ", version " <<
108 ll_safe_string(alGetString(AL_VERSION)) <<
109 " / " <<
110 ll_safe_string(alGetString(AL_VENDOR)) <<
111 " / " <<
112 ll_safe_string(alGetString(AL_RENDERER));
113
114 if (device)
115 version <<
116 ": " <<
117 ll_safe_string(alcGetString(device,
118 ALC_DEFAULT_DEVICE_SPECIFIER));
119 }
120
121 return version.str();
122}
123
124// virtual
125void LLAudioEngine_OpenAL::allocateListener()
126{
127 mListenerp = (LLListener *) new LLListener_OpenAL();
128 if(!mListenerp)
129 {
130 LL_WARNS("OpenAL") << "LLAudioEngine_OpenAL::allocateListener() Listener creation failed" << LL_ENDL;
131 }
132}
133
134// virtual
135void LLAudioEngine_OpenAL::shutdown()
136{
137 LL_INFOS("OpenAL") << "About to LLAudioEngine::shutdown()" << LL_ENDL;
138 LLAudioEngine::shutdown();
139
140 LL_INFOS("OpenAL") << "About to alutExit()" << LL_ENDL;
141 if(!alutExit())
142 {
143 LL_WARNS("OpenAL") << "Nuts." << LL_ENDL;
144 LL_WARNS("OpenAL") << "LLAudioEngine_OpenAL::shutdown() ALUT shutdown failed: " << alutGetErrorString (alutGetError ()) << LL_ENDL;
145 }
146
147 LL_INFOS("OpenAL") << "LLAudioEngine_OpenAL::shutdown() OpenAL successfully shut down" << LL_ENDL;
148
149 delete mListenerp;
150 mListenerp = NULL;
151}
152
153LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer()
154{
155 return new LLAudioBufferOpenAL();
156}
157
158LLAudioChannel *LLAudioEngine_OpenAL::createChannel()
159{
160 return new LLAudioChannelOpenAL();
161}
162
163void LLAudioEngine_OpenAL::setInternalGain(F32 gain)
164{
165 //LL_INFOS("OpenAL") << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << LL_ENDL;
166 alListenerf(AL_GAIN, gain);
167}
168
169LLAudioChannelOpenAL::LLAudioChannelOpenAL()
170 :
171 mALSource(AL_NONE),
172 mLastSamplePos(0)
173{
174 alGenSources(1, &mALSource);
175}
176
177LLAudioChannelOpenAL::~LLAudioChannelOpenAL()
178{
179 cleanup();
180 alDeleteSources(1, &mALSource);
181}
182
183void LLAudioChannelOpenAL::cleanup()
184{
185 alSourceStop(mALSource);
186 mCurrentBufferp = NULL;
187}
188
189void LLAudioChannelOpenAL::play()
190{
191 if (mALSource == AL_NONE)
192 {
193 LL_WARNS("OpenAL") << "Playing without a mALSource, aborting" << LL_ENDL;
194 return;
195 }
196
197 if(!isPlaying())
198 {
199 alSourcePlay(mALSource);
200 getSource()->setPlayedOnce(true);
201 }
202}
203
204void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp)
205{
206 if (channelp)
207 {
208 LLAudioChannelOpenAL *masterchannelp =
209 (LLAudioChannelOpenAL*)channelp;
210 if (mALSource != AL_NONE &&
211 masterchannelp->mALSource != AL_NONE)
212 {
213 // we have channels allocated to master and slave
214 ALfloat master_offset;
215 alGetSourcef(masterchannelp->mALSource, AL_SEC_OFFSET,
216 &master_offset);
217
218 LL_INFOS("OpenAL") << "Syncing with master at " << master_offset
219 << "sec" << LL_ENDL;
220 // *TODO: detect when this fails, maybe use AL_SAMPLE_
221 alSourcef(mALSource, AL_SEC_OFFSET, master_offset);
222 }
223 }
224 play();
225}
226
227bool LLAudioChannelOpenAL::isPlaying()
228{
229 if (mALSource != AL_NONE)
230 {
231 ALint state;
232 alGetSourcei(mALSource, AL_SOURCE_STATE, &state);
233 if(state == AL_PLAYING)
234 {
235 return true;
236 }
237 }
238
239 return false;
240}
241
242bool LLAudioChannelOpenAL::updateBuffer()
243{
244 if (LLAudioChannel::updateBuffer())
245 {
246 // Base class update returned true, which means that we need to actually
247 // set up the source for a different buffer.
248 LLAudioBufferOpenAL *bufferp = (LLAudioBufferOpenAL *)mCurrentSourcep->getCurrentBuffer();
249 ALuint buffer = bufferp->getBuffer();
250 alSourcei(mALSource, AL_BUFFER, buffer);
251 mLastSamplePos = 0;
252 }
253
254 if (mCurrentSourcep)
255 {
256 alSourcef(mALSource, AL_GAIN,
257 mCurrentSourcep->getGain() * getSecondaryGain());
258 alSourcei(mALSource, AL_LOOPING,
259 mCurrentSourcep->isLoop() ? AL_TRUE : AL_FALSE);
260 alSourcef(mALSource, AL_ROLLOFF_FACTOR,
261 gAudiop->mListenerp->getRolloffFactor());
262 alSourcef(mALSource, AL_REFERENCE_DISTANCE,
263 gAudiop->mListenerp->getDistanceFactor());
264 }
265
266 return true;
267}
268
269
270void LLAudioChannelOpenAL::updateLoop()
271{
272 if (mALSource == AL_NONE)
273 {
274 return;
275 }
276
277 // Hack: We keep track of whether we looped or not by seeing when the
278 // sample position looks like it's going backwards. Not reliable; may
279 // yield false negatives.
280 //
281 ALint cur_pos;
282 alGetSourcei(mALSource, AL_SAMPLE_OFFSET, &cur_pos);
283 if (cur_pos < mLastSamplePos)
284 {
285 mLoopedThisFrame = true;
286 }
287 mLastSamplePos = cur_pos;
288}
289
290
291void LLAudioChannelOpenAL::update3DPosition()
292{
293 if(!mCurrentSourcep)
294 {
295 return;
296 }
297 if (mCurrentSourcep->isAmbient())
298 {
299 alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0);
300 alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0);
301 //alSource3f(mALSource, AL_DIRECTION, 0.0, 0.0, 0.0);
302 alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_TRUE);
303 } else {
304 LLVector3 float_pos;
305 float_pos.setVec(mCurrentSourcep->getPositionGlobal());
306 alSourcefv(mALSource, AL_POSITION, float_pos.mV);
307 alSourcefv(mALSource, AL_VELOCITY, mCurrentSourcep->getVelocity().mV);
308 //alSource3f(mALSource, AL_DIRECTION, 0.0, 0.0, 0.0);
309 alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_FALSE);
310 }
311
312 alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain() * getSecondaryGain());
313}
314
315LLAudioBufferOpenAL::LLAudioBufferOpenAL()
316{
317 mALBuffer = AL_NONE;
318}
319
320LLAudioBufferOpenAL::~LLAudioBufferOpenAL()
321{
322 cleanup();
323}
324
325void LLAudioBufferOpenAL::cleanup()
326{
327 if(mALBuffer != AL_NONE)
328 {
329 alDeleteBuffers(1, &mALBuffer);
330 mALBuffer = AL_NONE;
331 }
332}
333
334bool LLAudioBufferOpenAL::loadWAV(const std::string& filename)
335{
336 cleanup();
337 mALBuffer = alutCreateBufferFromFile(filename.c_str());
338 if(mALBuffer == AL_NONE)
339 {
340 ALenum error = alutGetError();
341 if (gDirUtilp->fileExists(filename))
342 {
343 LL_WARNS("OpenAL") <<
344 "LLAudioBufferOpenAL::loadWAV() Error loading "
345 << filename
346 << " " << alutGetErrorString(error) << LL_ENDL;
347 }
348 else
349 {
350 // It's common for the file to not actually exist.
351 LL_DEBUGS("OpenAL") <<
352 "LLAudioBufferOpenAL::loadWAV() Error loading "
353 << filename
354 << " " << alutGetErrorString(error) << LL_ENDL;
355 }
356 return false;
357 }
358
359 return true;
360}
361
362U32 LLAudioBufferOpenAL::getLength()
363{
364 if(mALBuffer == AL_NONE)
365 {
366 return 0;
367 }
368 ALint length;
369 alGetBufferi(mALBuffer, AL_SIZE, &length);
370 return length >> 2;
371}
372
373// ------------
374
375void LLAudioEngine_OpenAL::initWind()
376{
377 ALenum error;
378 LL_INFOS("OpenAL") << "LLAudioEngine_OpenAL::initWind() start" << LL_ENDL;
379
380 mNumEmptyWindALBuffers = MAX_NUM_WIND_BUFFERS;
381
382 alGetError(); /* clear error */
383
384 alGenSources(1,&mWindSource);
385
386 if((error=alGetError()) != AL_NO_ERROR)
387 {
388 LL_WARNS("OpenAL") << "LLAudioEngine_OpenAL::initWind() Error creating wind sources: "<<error<<LL_ENDL;
389 }
390
391 mWindGen = new LLWindGen<WIND_SAMPLE_T>;
392 const float WIND_BUFFER_SIZE_SEC = 0.05f; // 1/20th sec
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}