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