diff options
Diffstat (limited to 'linden/indra/llaudio/audioengine_openal.cpp')
-rw-r--r-- | linden/indra/llaudio/audioengine_openal.cpp | 422 |
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 | ||
42 | static int empty_num_wind_buffers = MAX_NUM_WIND_BUFFERS; | ||
43 | static const float wind_buffer_size_sec = 0.05f; // 1/20th sec | ||
44 | static const U32 wind_gen_freq = LLWindGen<S16>::getInputSamplingRate(); | ||
45 | static ALuint wind_source; | ||
46 | static S16 *winddata=NULL; | ||
47 | |||
48 | |||
49 | LLAudioEngine_OpenAL::LLAudioEngine_OpenAL() | ||
50 | { | ||
51 | mWindGen = NULL; | ||
52 | } | ||
53 | |||
54 | LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL() | ||
55 | { | ||
56 | } | ||
57 | |||
58 | bool 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 | |||
98 | void 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 | |||
107 | void 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 | |||
125 | LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer() | ||
126 | { | ||
127 | return new LLAudioBufferOpenAL(); | ||
128 | } | ||
129 | |||
130 | LLAudioChannel *LLAudioEngine_OpenAL::createChannel() | ||
131 | { | ||
132 | return new LLAudioChannelOpenAL(); | ||
133 | } | ||
134 | |||
135 | void LLAudioEngine_OpenAL::setInternalGain(F32 gain) | ||
136 | { | ||
137 | //llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl; | ||
138 | alListenerf(AL_GAIN, gain); | ||
139 | } | ||
140 | |||
141 | LLAudioChannelOpenAL::LLAudioChannelOpenAL() | ||
142 | { | ||
143 | alGenSources(1, &mALSource); | ||
144 | } | ||
145 | |||
146 | LLAudioChannelOpenAL::~LLAudioChannelOpenAL() | ||
147 | { | ||
148 | cleanup(); | ||
149 | alDeleteSources(1, &mALSource); | ||
150 | } | ||
151 | |||
152 | void LLAudioChannelOpenAL::cleanup() | ||
153 | { | ||
154 | alSourceStop(mALSource); | ||
155 | mCurrentBufferp = NULL; | ||
156 | } | ||
157 | |||
158 | void LLAudioChannelOpenAL::play() | ||
159 | { | ||
160 | if(!isPlaying()){ | ||
161 | alSourcePlay(mALSource); | ||
162 | getSource()->setPlayedOnce(true); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp) | ||
167 | { | ||
168 | play(); | ||
169 | } | ||
170 | |||
171 | bool 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 | |||
181 | bool 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 | |||
196 | void 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 | |||
223 | LLAudioBufferOpenAL::LLAudioBufferOpenAL() | ||
224 | { | ||
225 | mALBuffer = AL_NONE; | ||
226 | } | ||
227 | |||
228 | LLAudioBufferOpenAL::~LLAudioBufferOpenAL() | ||
229 | { | ||
230 | cleanup(); | ||
231 | } | ||
232 | |||
233 | void LLAudioBufferOpenAL::cleanup() | ||
234 | { | ||
235 | if(mALBuffer != AL_NONE) | ||
236 | { | ||
237 | alDeleteBuffers(1, &mALBuffer); | ||
238 | mALBuffer = AL_NONE; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | bool 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 | |||
266 | U32 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 | |||
277 | void 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 | |||
303 | void 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 | |||
315 | void 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 | |||