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.cpp422
1 files changed, 422 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..606d9f6
--- /dev/null
+++ b/linden/indra/llaudio/audioengine_openal.cpp
@@ -0,0 +1,422 @@
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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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
40// Variables and definitions for Wind
41#define MAX_NUM_WIND_BUFFERS 40
42static int empty_num_wind_buffers = MAX_NUM_WIND_BUFFERS;
43static const float wind_buffer_size_sec = 0.05f; // 1/20th sec
44static const U32 wind_gen_freq = LLWindGen<S16>::getInputSamplingRate();
45static ALuint wind_source;
46static S16 *winddata=NULL;
47
48
49LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
50{
51 mWindGen = NULL;
52}
53
54LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
55{
56}
57
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 llwarns << "LLAudioEngine_OpenAL::init() ALUT initialization failed: " << alutGetErrorString (alutGetError ()) << llendl;
66 return false;
67 }
68
69 llinfos << "LLAudioEngine_OpenAL::init() OpenAL successfully initialized" << llendl;
70
71 llinfos << "OpenAL version: "
72 << ll_safe_string(alGetString(AL_VERSION)) << llendl;
73 llinfos << "OpenAL vendor: "
74 << ll_safe_string(alGetString(AL_VENDOR)) << llendl;
75 llinfos << "OpenAL renderer: "
76 << ll_safe_string(alGetString(AL_RENDERER)) << llendl;
77
78 ALint major = alutGetMajorVersion ();
79 ALint minor = alutGetMinorVersion ();
80 llinfos << "ALUT version: " << major << "." << minor << llendl;
81
82 ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
83
84 alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major);
85 alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &minor);
86 llinfos << "ALC version: " << major << "." << minor << llendl;
87
88 llinfos << "ALC default device: "
89 << ll_safe_string(alcGetString(device,
90 ALC_DEFAULT_DEVICE_SPECIFIER))
91 << llendl;
92
93 llinfos << "LLAudioEngine_OpenAL::init() Speed of sound is: " << alGetFloat(AL_SPEED_OF_SOUND) << llendl;
94
95 return true;
96}
97
98void LLAudioEngine_OpenAL::allocateListener()
99{
100 mListenerp = (LLListener *) new LLListener_OpenAL();
101 if(!mListenerp)
102 {
103 llwarns << "LLAudioEngine_OpenAL::allocateListener() Listener creation failed" << llendl;
104 }
105}
106
107void LLAudioEngine_OpenAL::shutdown()
108{
109 llinfos << "About to LLAudioEngine::shutdown()" << llendl;
110 LLAudioEngine::shutdown();
111
112 llinfos << "About to alutExit()" << llendl;
113 if(!alutExit())
114 {
115 llwarns << "Nuts." << llendl;
116 llwarns << "LLAudioEngine_OpenAL::shutdown() ALUT shutdown failed: " << alutGetErrorString (alutGetError ()) << llendl;
117 }
118
119 llinfos << "LLAudioEngine_OpenAL::shutdown() OpenAL successfully shut down" << llendl;
120
121 delete mListenerp;
122 mListenerp = NULL;
123}
124
125LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer()
126{
127 return new LLAudioBufferOpenAL();
128}
129
130LLAudioChannel *LLAudioEngine_OpenAL::createChannel()
131{
132 return new LLAudioChannelOpenAL();
133}
134
135void LLAudioEngine_OpenAL::setInternalGain(F32 gain)
136{
137 //llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl;
138 alListenerf(AL_GAIN, gain);
139}
140
141LLAudioChannelOpenAL::LLAudioChannelOpenAL()
142{
143 alGenSources(1, &mALSource);
144}
145
146LLAudioChannelOpenAL::~LLAudioChannelOpenAL()
147{
148 cleanup();
149 alDeleteSources(1, &mALSource);
150}
151
152void LLAudioChannelOpenAL::cleanup()
153{
154 alSourceStop(mALSource);
155 mCurrentBufferp = NULL;
156}
157
158void LLAudioChannelOpenAL::play()
159{
160 if(!isPlaying()){
161 alSourcePlay(mALSource);
162 getSource()->setPlayedOnce(true);
163 }
164}
165
166void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp)
167{
168 play();
169}
170
171bool LLAudioChannelOpenAL::isPlaying()
172{
173 ALint state;
174 alGetSourcei(mALSource, AL_SOURCE_STATE, &state);
175 if(state == AL_PLAYING){
176 return true;
177 }
178 return false;
179}
180
181bool LLAudioChannelOpenAL::updateBuffer()
182{
183 if (LLAudioChannel::updateBuffer())
184 {
185 // Base class update returned true, which means that we need to actually
186 // set up the source for a different buffer.
187 LLAudioBufferOpenAL *bufferp = (LLAudioBufferOpenAL *)mCurrentSourcep->getCurrentBuffer();
188 alSourcei(mALSource, AL_BUFFER, bufferp->getBuffer());
189 alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain());
190 alSourcei(mALSource, AL_LOOPING, mCurrentSourcep->isLoop() ? AL_TRUE : AL_FALSE);
191 }
192
193 return true;
194}
195
196void LLAudioChannelOpenAL::update3DPosition()
197{
198 if(!mCurrentSourcep)
199 {
200 return;
201 }
202 if (mCurrentSourcep->isAmbient())
203 {
204 alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0);
205 alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0);
206 //alSource3f(mALSource, AL_DIRECTION, 0.0, 0.0, 0.0);
207 alSourcef (mALSource, AL_ROLLOFF_FACTOR, 0.0);
208 alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_TRUE);
209 } else {
210 LLVector3 float_pos;
211 float_pos.setVec(mCurrentSourcep->getPositionGlobal());
212 alSourcefv(mALSource, AL_POSITION, float_pos.mV);
213 //llinfos << "LLAudioChannelOpenAL::update3DPosition() Velocity: " << mCurrentSourcep->getVelocity() << llendl;
214 alSourcefv(mALSource, AL_VELOCITY, mCurrentSourcep->getVelocity().mV);
215 //alSource3f(mALSource, AL_DIRECTION, 0.0, 0.0, 0.0);
216 alSourcef (mALSource, AL_ROLLOFF_FACTOR, 1.0);
217 alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_FALSE);
218 }
219 //llinfos << "LLAudioChannelOpenAL::update3DPosition() Gain: " << mCurrentSourcep->getGain() << llendl;
220 alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain());
221}
222
223LLAudioBufferOpenAL::LLAudioBufferOpenAL()
224{
225 mALBuffer = AL_NONE;
226}
227
228LLAudioBufferOpenAL::~LLAudioBufferOpenAL()
229{
230 cleanup();
231}
232
233void LLAudioBufferOpenAL::cleanup()
234{
235 if(mALBuffer != AL_NONE)
236 {
237 alDeleteBuffers(1, &mALBuffer);
238 mALBuffer = AL_NONE;
239 }
240}
241
242bool LLAudioBufferOpenAL::loadWAV(const std::string& filename)
243{
244 cleanup();
245 mALBuffer = alutCreateBufferFromFile(filename.c_str());
246 if(mALBuffer == AL_NONE){
247 ALenum error = alutGetError();
248 if (gDirUtilp->fileExists(filename)) {
249 llwarns <<
250 "LLAudioBufferOpenAL::loadWAV() Error loading "
251 << filename
252 << " " << alutGetErrorString(error) << llendl;
253 } else {
254 // It's common for the file to not actually exist.
255 lldebugs <<
256 "LLAudioBufferOpenAL::loadWAV() Error loading "
257 << filename
258 << " " << alutGetErrorString(error) << llendl;
259 }
260 return false;
261 }
262
263 return true;
264}
265
266U32 LLAudioBufferOpenAL::getLength(){
267 if(mALBuffer == AL_NONE){
268 return 0;
269 }
270 ALint length;
271 alGetBufferi(mALBuffer, AL_SIZE, &length);
272 return length >> 2;
273}
274
275// ------------
276
277void LLAudioEngine_OpenAL::initWind(){
278 ALenum error;
279 llinfos << "LLAudioEngine_OpenAL::initWind() start" << llendl;
280
281 alGetError(); /* clear error */
282
283 alGenSources(1,&wind_source);
284
285 if((error=alGetError()) != AL_NO_ERROR)
286 {
287 llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind sources: "<<error<<llendl;
288 }
289
290 winddata=(S16*)malloc(sizeof(S16)*llceil(wind_gen_freq*wind_buffer_size_sec*2*2)); //200ms @wind_gen_freqHz Stereo
291
292 if(winddata==NULL)
293 {
294 llerrs << "LLAudioEngine_OpenAL::initWind() Error creating wind memory buffer" << llendl;
295 mEnableWind=false;
296 }
297
298 mWindGen = new LLWindGen<S16>;
299
300 llinfos << "LLAudioEngine_OpenAL::initWind() done" << llendl;
301}
302
303void LLAudioEngine_OpenAL::cleanupWind(){
304 llinfos << "LLAudioEngine_OpenAL::cleanupWind()" << llendl;
305
306 alDeleteSources(1, &wind_source);
307
308 if(winddata)
309 free(winddata);
310
311 delete mWindGen;
312 mWindGen = NULL;
313}
314
315void LLAudioEngine_OpenAL::updateWind(LLVector3 wind_vec, F32 camera_altitude)
316{
317 LLVector3 wind_pos;
318 F64 pitch;
319 F64 center_freq;
320 ALenum error;
321
322 mMaxWindGain=1.0;
323
324 if (!mEnableWind)
325 return;
326
327 if(!winddata)
328 return;
329
330 if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
331 {
332
333 // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
334 // need to convert this to the conventional orientation DS3D and OpenAL use
335 // where +X = right, +Y = up, +Z = backwards
336
337 wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
338
339 pitch = 1.0 + mapWindVecToPitch(wind_vec);
340 center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
341
342 mWindGen->mTargetFreq = (F32)center_freq;
343 mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
344 mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
345
346 alSourcei(wind_source, AL_LOOPING, AL_FALSE);
347 alSource3f(wind_source, AL_POSITION, 0.0, 0.0, 0.0);
348 alSource3f(wind_source, AL_VELOCITY, 0.0, 0.0, 0.0);
349 alSourcef(wind_source, AL_ROLLOFF_FACTOR, 0.0);
350 alSourcei(wind_source, AL_SOURCE_RELATIVE, AL_TRUE);
351 }
352
353 // ok lets make a wind buffer now
354
355 int processed, queued, unprocessed;
356 alGetSourcei(wind_source, AL_BUFFERS_PROCESSED, &processed);
357 alGetSourcei(wind_source, AL_BUFFERS_QUEUED, &queued);
358 unprocessed = queued - processed;
359
360 // ensure that there are always at least 3x as many filled buffers
361 // queued as we managed to empty since last time.
362 empty_num_wind_buffers = llmin(empty_num_wind_buffers + processed * 3 - unprocessed, MAX_NUM_WIND_BUFFERS-unprocessed);
363 empty_num_wind_buffers = llmax(empty_num_wind_buffers, 0);
364
365 //llinfos << "empty_num_wind_buffers: " << empty_num_wind_buffers <<" (" << unprocessed << ":" << processed << ")" << llendl;
366
367 while(processed--) // unqueue old buffers
368 {
369 ALuint buffer;
370 int error;
371 alGetError(); /* clear error */
372 alSourceUnqueueBuffers(wind_source, 1, &buffer);
373 error = alGetError();
374 if(error != AL_NO_ERROR)
375 {
376 llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (unqueuing) buffers" << llendl;
377 }
378 else
379 {
380 alDeleteBuffers(1, &buffer);
381 }
382 }
383
384 while (empty_num_wind_buffers > 0) // fill+queue new buffers
385 {
386 ALuint buffer;
387 alGetError(); /* clear error */
388 alGenBuffers(1,&buffer);
389 if((error=alGetError()) != AL_NO_ERROR)
390 {
391 llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind buffer: " << error << llendl;
392 break;
393 }
394
395 alBufferData(buffer,
396 AL_FORMAT_STEREO16,
397 mWindGen->windGenerate(winddata,
398 int(wind_gen_freq*wind_buffer_size_sec), 2),
399 int(2*wind_gen_freq*wind_buffer_size_sec*sizeof(S16)),
400 wind_gen_freq);
401 error = alGetError();
402 if(error != AL_NO_ERROR)
403 llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (bufferdata) buffers" << llendl;
404
405 alSourceQueueBuffers(wind_source, 1, &buffer);
406 error = alGetError();
407 if(error != AL_NO_ERROR)
408 llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (queuing) buffers" << llendl;
409
410 --empty_num_wind_buffers;
411 }
412
413 int playing;
414 alGetSourcei(wind_source, AL_SOURCE_STATE, &playing);
415 if(playing != AL_PLAYING)
416 {
417 alSourcePlay(wind_source);
418
419 llinfos << "Wind had stopped - probably ran out of buffers - restarting: " << (unprocessed+empty_num_wind_buffers) << " now queued." << llendl;
420 }
421}
422