diff options
Diffstat (limited to 'linden/indra/llaudio/llaudioengine_openal.cpp')
-rw-r--r-- | linden/indra/llaudio/llaudioengine_openal.cpp | 548 |
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 | |||
41 | static const float WIND_BUFFER_SIZE_SEC = 0.05f; // 1/20th sec | ||
42 | |||
43 | LLAudioEngine_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 | ||
56 | LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL() | ||
57 | { | ||
58 | } | ||
59 | |||
60 | // virtual | ||
61 | bool 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 | ||
100 | std::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 | ||
129 | void 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 | ||
139 | void 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 | |||
157 | LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer() | ||
158 | { | ||
159 | return new LLAudioBufferOpenAL(); | ||
160 | } | ||
161 | |||
162 | LLAudioChannel *LLAudioEngine_OpenAL::createChannel() | ||
163 | { | ||
164 | return new LLAudioChannelOpenAL(); | ||
165 | } | ||
166 | |||
167 | void LLAudioEngine_OpenAL::setInternalGain(F32 gain) | ||
168 | { | ||
169 | //llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl; | ||
170 | alListenerf(AL_GAIN, gain); | ||
171 | } | ||
172 | |||
173 | LLAudioChannelOpenAL::LLAudioChannelOpenAL() | ||
174 | : | ||
175 | mALSource(AL_NONE), | ||
176 | mLastSamplePos(0) | ||
177 | { | ||
178 | alGenSources(1, &mALSource); | ||
179 | } | ||
180 | |||
181 | LLAudioChannelOpenAL::~LLAudioChannelOpenAL() | ||
182 | { | ||
183 | cleanup(); | ||
184 | alDeleteSources(1, &mALSource); | ||
185 | } | ||
186 | |||
187 | void LLAudioChannelOpenAL::cleanup() | ||
188 | { | ||
189 | alSourceStop(mALSource); | ||
190 | mCurrentBufferp = NULL; | ||
191 | } | ||
192 | |||
193 | void 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 | |||
208 | void 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 | |||
231 | bool 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 | |||
246 | bool 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 | |||
272 | void 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 | |||
293 | void 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 | |||
315 | LLAudioBufferOpenAL::LLAudioBufferOpenAL() | ||
316 | { | ||
317 | mALBuffer = AL_NONE; | ||
318 | } | ||
319 | |||
320 | LLAudioBufferOpenAL::~LLAudioBufferOpenAL() | ||
321 | { | ||
322 | cleanup(); | ||
323 | } | ||
324 | |||
325 | void LLAudioBufferOpenAL::cleanup() | ||
326 | { | ||
327 | if(mALBuffer != AL_NONE) | ||
328 | { | ||
329 | alDeleteBuffers(1, &mALBuffer); | ||
330 | mALBuffer = AL_NONE; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | bool 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 | |||
362 | U32 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 | |||
375 | void 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 | |||
408 | void 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 | |||
438 | void 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 | |||