aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llaudio/llaudioengine_openal.cpp
diff options
context:
space:
mode:
authorArmin Weatherwax2010-06-14 12:04:49 +0200
committerArmin Weatherwax2010-09-23 15:38:25 +0200
commit35df5441d3e2789663532c948731aff3a1e04728 (patch)
treeac7674289784a5f96106ea507637055a8dada78a /linden/indra/llaudio/llaudioengine_openal.cpp
parentChanged version to Experimental 2010.09.18 (diff)
downloadmeta-impy-35df5441d3e2789663532c948731aff3a1e04728.zip
meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.gz
meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.bz2
meta-impy-35df5441d3e2789663532c948731aff3a1e04728.tar.xz
llmediaplugins first step
Diffstat (limited to 'linden/indra/llaudio/llaudioengine_openal.cpp')
-rw-r--r--linden/indra/llaudio/llaudioengine_openal.cpp546
1 files changed, 546 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..99ab18e
--- /dev/null
+++ b/linden/indra/llaudio/llaudioengine_openal.cpp
@@ -0,0 +1,546 @@
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
41LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
42 :
43 mWindGen(NULL),
44 mWindBuf(NULL),
45 mWindBufFreq(0),
46 mWindBufSamples(0),
47 mWindBufBytes(0),
48 mWindSource(AL_NONE),
49 mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS)
50{
51}
52
53// virtual
54LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
55{
56}
57
58// virtual
59bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata)
60{
61 mWindGen = NULL;
62 LLAudioEngine::init(num_channels, userdata);
63
64 if(!alutInit(NULL, NULL))
65 {
66 llwarns << "LLAudioEngine_OpenAL::init() ALUT initialization failed: " << alutGetErrorString (alutGetError ()) << llendl;
67 return false;
68 }
69
70 llinfos << "LLAudioEngine_OpenAL::init() OpenAL successfully initialized" << llendl;
71
72 llinfos << "OpenAL version: "
73 << ll_safe_string(alGetString(AL_VERSION)) << llendl;
74 llinfos << "OpenAL vendor: "
75 << ll_safe_string(alGetString(AL_VENDOR)) << llendl;
76 llinfos << "OpenAL renderer: "
77 << ll_safe_string(alGetString(AL_RENDERER)) << llendl;
78
79 ALint major = alutGetMajorVersion ();
80 ALint minor = alutGetMinorVersion ();
81 llinfos << "ALUT version: " << major << "." << minor << llendl;
82
83 ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
84
85 alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major);
86 alcGetIntegerv(device, ALC_MINOR_VERSION, 1, &minor);
87 llinfos << "ALC version: " << major << "." << minor << llendl;
88
89 llinfos << "ALC default device: "
90 << ll_safe_string(alcGetString(device,
91 ALC_DEFAULT_DEVICE_SPECIFIER))
92 << llendl;
93
94 return true;
95}
96
97// virtual
98std::string LLAudioEngine_OpenAL::getDriverName(bool verbose)
99{
100 ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
101 std::ostringstream version;
102
103 version <<
104 "OpenAL";
105
106 if (verbose)
107 {
108 version <<
109 ", version " <<
110 ll_safe_string(alGetString(AL_VERSION)) <<
111 " / " <<
112 ll_safe_string(alGetString(AL_VENDOR)) <<
113 " / " <<
114 ll_safe_string(alGetString(AL_RENDERER));
115
116 if (device)
117 version <<
118 ": " <<
119 ll_safe_string(alcGetString(device,
120 ALC_DEFAULT_DEVICE_SPECIFIER));
121 }
122
123 return version.str();
124}
125
126// virtual
127void LLAudioEngine_OpenAL::allocateListener()
128{
129 mListenerp = (LLListener *) new LLListener_OpenAL();
130 if(!mListenerp)
131 {
132 llwarns << "LLAudioEngine_OpenAL::allocateListener() Listener creation failed" << llendl;
133 }
134}
135
136// virtual
137void LLAudioEngine_OpenAL::shutdown()
138{
139 llinfos << "About to LLAudioEngine::shutdown()" << llendl;
140 LLAudioEngine::shutdown();
141
142 llinfos << "About to alutExit()" << llendl;
143 if(!alutExit())
144 {
145 llwarns << "Nuts." << llendl;
146 llwarns << "LLAudioEngine_OpenAL::shutdown() ALUT shutdown failed: " << alutGetErrorString (alutGetError ()) << llendl;
147 }
148
149 llinfos << "LLAudioEngine_OpenAL::shutdown() OpenAL successfully shut down" << llendl;
150
151 delete mListenerp;
152 mListenerp = NULL;
153}
154
155LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer()
156{
157 return new LLAudioBufferOpenAL();
158}
159
160LLAudioChannel *LLAudioEngine_OpenAL::createChannel()
161{
162 return new LLAudioChannelOpenAL();
163}
164
165void LLAudioEngine_OpenAL::setInternalGain(F32 gain)
166{
167 //llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl;
168 alListenerf(AL_GAIN, gain);
169}
170
171LLAudioChannelOpenAL::LLAudioChannelOpenAL()
172 :
173 mALSource(AL_NONE),
174 mLastSamplePos(0)
175{
176 alGenSources(1, &mALSource);
177}
178
179LLAudioChannelOpenAL::~LLAudioChannelOpenAL()
180{
181 cleanup();
182 alDeleteSources(1, &mALSource);
183}
184
185void LLAudioChannelOpenAL::cleanup()
186{
187 alSourceStop(mALSource);
188 mCurrentBufferp = NULL;
189}
190
191void LLAudioChannelOpenAL::play()
192{
193 if (mALSource == AL_NONE)
194 {
195 llwarns << "Playing without a mALSource, aborting" << llendl;
196 return;
197 }
198
199 if(!isPlaying())
200 {
201 alSourcePlay(mALSource);
202 getSource()->setPlayedOnce(true);
203 }
204}
205
206void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp)
207{
208 if (channelp)
209 {
210 LLAudioChannelOpenAL *masterchannelp =
211 (LLAudioChannelOpenAL*)channelp;
212 if (mALSource != AL_NONE &&
213 masterchannelp->mALSource != AL_NONE)
214 {
215 // we have channels allocated to master and slave
216 ALfloat master_offset;
217 alGetSourcef(masterchannelp->mALSource, AL_SEC_OFFSET,
218 &master_offset);
219
220 llinfos << "Syncing with master at " << master_offset
221 << "sec" << llendl;
222 // *TODO: detect when this fails, maybe use AL_SAMPLE_
223 alSourcef(mALSource, AL_SEC_OFFSET, master_offset);
224 }
225 }
226 play();
227}
228
229bool LLAudioChannelOpenAL::isPlaying()
230{
231 if (mALSource != AL_NONE)
232 {
233 ALint state;
234 alGetSourcei(mALSource, AL_SOURCE_STATE, &state);
235 if(state == AL_PLAYING)
236 {
237 return true;
238 }
239 }
240
241 return false;
242}
243
244bool LLAudioChannelOpenAL::updateBuffer()
245{
246 if (LLAudioChannel::updateBuffer())
247 {
248 // Base class update returned true, which means that we need to actually
249 // set up the source for a different buffer.
250 LLAudioBufferOpenAL *bufferp = (LLAudioBufferOpenAL *)mCurrentSourcep->getCurrentBuffer();
251 ALuint buffer = bufferp->getBuffer();
252 alSourcei(mALSource, AL_BUFFER, buffer);
253 mLastSamplePos = 0;
254 }
255
256 if (mCurrentSourcep)
257 {
258 alSourcef(mALSource, AL_GAIN,
259 mCurrentSourcep->getGain() * getSecondaryGain());
260 alSourcei(mALSource, AL_LOOPING,
261 mCurrentSourcep->isLoop() ? AL_TRUE : AL_FALSE);
262 alSourcef(mALSource, AL_ROLLOFF_FACTOR,
263 gAudiop->mListenerp->getRolloffFactor());
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 alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_TRUE);
302 } else {
303 LLVector3 float_pos;
304 float_pos.setVec(mCurrentSourcep->getPositionGlobal());
305 alSourcefv(mALSource, AL_POSITION, float_pos.mV);
306 alSourcefv(mALSource, AL_VELOCITY, mCurrentSourcep->getVelocity().mV);
307 alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_FALSE);
308 }
309
310 alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain() * getSecondaryGain());
311}
312
313LLAudioBufferOpenAL::LLAudioBufferOpenAL()
314{
315 mALBuffer = AL_NONE;
316}
317
318LLAudioBufferOpenAL::~LLAudioBufferOpenAL()
319{
320 cleanup();
321}
322
323void LLAudioBufferOpenAL::cleanup()
324{
325 if(mALBuffer != AL_NONE)
326 {
327 alDeleteBuffers(1, &mALBuffer);
328 mALBuffer = AL_NONE;
329 }
330}
331
332bool LLAudioBufferOpenAL::loadWAV(const std::string& filename)
333{
334 cleanup();
335 mALBuffer = alutCreateBufferFromFile(filename.c_str());
336 if(mALBuffer == AL_NONE)
337 {
338 ALenum error = alutGetError();
339 if (gDirUtilp->fileExists(filename))
340 {
341 llwarns <<
342 "LLAudioBufferOpenAL::loadWAV() Error loading "
343 << filename
344 << " " << alutGetErrorString(error) << llendl;
345 }
346 else
347 {
348 // It's common for the file to not actually exist.
349 lldebugs <<
350 "LLAudioBufferOpenAL::loadWAV() Error loading "
351 << filename
352 << " " << alutGetErrorString(error) << llendl;
353 }
354 return false;
355 }
356
357 return true;
358}
359
360U32 LLAudioBufferOpenAL::getLength()
361{
362 if(mALBuffer == AL_NONE)
363 {
364 return 0;
365 }
366 ALint length;
367 alGetBufferi(mALBuffer, AL_SIZE, &length);
368 return length / 2; // convert size in bytes to size in (16-bit) samples
369}
370
371// ------------
372
373void LLAudioEngine_OpenAL::initWind()
374{
375 ALenum error;
376 llinfos << "LLAudioEngine_OpenAL::initWind() start" << llendl;
377
378 mNumEmptyWindALBuffers = MAX_NUM_WIND_BUFFERS;
379
380 alGetError(); /* clear error */
381
382 alGenSources(1,&mWindSource);
383
384 if((error=alGetError()) != AL_NO_ERROR)
385 {
386 llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind sources: "<<error<<llendl;
387 }
388
389 mWindGen = new LLWindGen<WIND_SAMPLE_T>;
390
391 mWindBufFreq = mWindGen->getInputSamplingRate();
392 mWindBufSamples = llceil(mWindBufFreq * WIND_BUFFER_SIZE_SEC);
393 mWindBufBytes = mWindBufSamples * 2 /*stereo*/ * sizeof(WIND_SAMPLE_T);
394
395 mWindBuf = new WIND_SAMPLE_T [mWindBufSamples * 2 /*stereo*/];
396
397 if(mWindBuf==NULL)
398 {
399 llerrs << "LLAudioEngine_OpenAL::initWind() Error creating wind memory buffer" << llendl;
400 mEnableWind=false;
401 }
402
403 llinfos << "LLAudioEngine_OpenAL::initWind() done" << llendl;
404}
405
406void LLAudioEngine_OpenAL::cleanupWind()
407{
408 llinfos << "LLAudioEngine_OpenAL::cleanupWind()" << llendl;
409
410 if (mWindSource != AL_NONE)
411 {
412 // detach and delete all outstanding buffers on the wind source
413 alSourceStop(mWindSource);
414 ALint processed;
415 alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
416 while (processed--)
417 {
418 ALuint buffer = AL_NONE;
419 alSourceUnqueueBuffers(mWindSource, 1, &buffer);
420 alDeleteBuffers(1, &buffer);
421 }
422
423 // delete the wind source itself
424 alDeleteSources(1, &mWindSource);
425
426 mWindSource = AL_NONE;
427 }
428
429 delete[] mWindBuf;
430 mWindBuf = NULL;
431
432 delete mWindGen;
433 mWindGen = NULL;
434}
435
436void LLAudioEngine_OpenAL::updateWind(LLVector3 wind_vec, F32 camera_altitude)
437{
438 LLVector3 wind_pos;
439 F64 pitch;
440 F64 center_freq;
441 ALenum error;
442
443 if (!mEnableWind)
444 return;
445
446 if(!mWindBuf)
447 return;
448
449 if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
450 {
451
452 // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
453 // need to convert this to the conventional orientation DS3D and OpenAL use
454 // where +X = right, +Y = up, +Z = backwards
455
456 wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
457
458 pitch = 1.0 + mapWindVecToPitch(wind_vec);
459 center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
460
461 mWindGen->mTargetFreq = (F32)center_freq;
462 mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
463 mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
464
465 alSourcei(mWindSource, AL_LOOPING, AL_FALSE);
466 alSource3f(mWindSource, AL_POSITION, 0.0, 0.0, 0.0);
467 alSource3f(mWindSource, AL_VELOCITY, 0.0, 0.0, 0.0);
468 alSourcef(mWindSource, AL_ROLLOFF_FACTOR, 0.0);
469 alSourcei(mWindSource, AL_SOURCE_RELATIVE, AL_TRUE);
470 }
471
472 // ok lets make a wind buffer now
473
474 ALint processed, queued, unprocessed;
475 alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
476 alGetSourcei(mWindSource, AL_BUFFERS_QUEUED, &queued);
477 unprocessed = queued - processed;
478
479 // ensure that there are always at least 3x as many filled buffers
480 // queued as we managed to empty since last time.
481 mNumEmptyWindALBuffers = llmin(mNumEmptyWindALBuffers + processed * 3 - unprocessed, MAX_NUM_WIND_BUFFERS-unprocessed);
482 mNumEmptyWindALBuffers = llmax(mNumEmptyWindALBuffers, 0);
483
484 //llinfos << "mNumEmptyWindALBuffers: " << mNumEmptyWindALBuffers <<" (" << unprocessed << ":" << processed << ")" << llendl;
485
486 while(processed--) // unqueue old buffers
487 {
488 ALuint buffer;
489 ALenum error;
490 alGetError(); /* clear error */
491 alSourceUnqueueBuffers(mWindSource, 1, &buffer);
492 error = alGetError();
493 if(error != AL_NO_ERROR)
494 {
495 llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (unqueuing) buffers" << llendl;
496 }
497 else
498 {
499 alDeleteBuffers(1, &buffer);
500 }
501 }
502
503 unprocessed += mNumEmptyWindALBuffers;
504 while (mNumEmptyWindALBuffers > 0) // fill+queue new buffers
505 {
506 ALuint buffer;
507 alGetError(); /* clear error */
508 alGenBuffers(1,&buffer);
509 if((error=alGetError()) != AL_NO_ERROR)
510 {
511 llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind buffer: " << error << llendl;
512 break;
513 }
514
515 alBufferData(buffer,
516 AL_FORMAT_STEREO16,
517 mWindGen->windGenerate(mWindBuf,
518 mWindBufSamples, 2),
519 mWindBufBytes,
520 mWindBufFreq);
521 error = alGetError();
522 if(error != AL_NO_ERROR)
523 {
524 llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (bufferdata) buffers" << llendl;
525 }
526
527 alSourceQueueBuffers(mWindSource, 1, &buffer);
528 error = alGetError();
529 if(error != AL_NO_ERROR)
530 {
531 llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (queuing) buffers" << llendl;
532 }
533
534 --mNumEmptyWindALBuffers;
535 }
536
537 ALint playing;
538 alGetSourcei(mWindSource, AL_SOURCE_STATE, &playing);
539 if(playing != AL_PLAYING)
540 {
541 alSourcePlay(mWindSource);
542
543 lldebugs << "Wind had stopped - probably ran out of buffers - restarting: " << (unprocessed+mNumEmptyWindALBuffers) << " now queued." << llendl;
544 }
545}
546