diff options
author | Armin Weatherwax | 2010-06-14 12:04:49 +0200 |
---|---|---|
committer | Armin Weatherwax | 2010-09-23 15:38:25 +0200 |
commit | 35df5441d3e2789663532c948731aff3a1e04728 (patch) | |
tree | ac7674289784a5f96106ea507637055a8dada78a /linden/indra/llaudio/llaudioengine_fmod.cpp | |
parent | Changed version to Experimental 2010.09.18 (diff) | |
download | meta-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_fmod.cpp')
-rw-r--r-- | linden/indra/llaudio/llaudioengine_fmod.cpp | 766 |
1 files changed, 766 insertions, 0 deletions
diff --git a/linden/indra/llaudio/llaudioengine_fmod.cpp b/linden/indra/llaudio/llaudioengine_fmod.cpp new file mode 100644 index 0000000..85ae863 --- /dev/null +++ b/linden/indra/llaudio/llaudioengine_fmod.cpp | |||
@@ -0,0 +1,766 @@ | |||
1 | /** | ||
2 | * @file audioengine_fmod.cpp | ||
3 | * @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation | ||
4 | * | ||
5 | * $LicenseInfo:firstyear=2002&license=viewergpl$ | ||
6 | * | ||
7 | * Copyright (c) 2002-2009, Linden Research, Inc. | ||
8 | * | ||
9 | * Second Life Viewer Source Code | ||
10 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
11 | * to you under the terms of the GNU General Public License, version 2.0 | ||
12 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
13 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
14 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
15 | * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 | ||
16 | * | ||
17 | * There are special exceptions to the terms and conditions of the GPL as | ||
18 | * it is applied to this Source Code. View the full text of the exception | ||
19 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
20 | * online at | ||
21 | * http://secondlifegrid.net/programs/open_source/licensing/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 | |||
35 | #include "llstreamingaudio.h" | ||
36 | #include "llstreamingaudio_fmod.h" | ||
37 | |||
38 | #include "llaudioengine_fmod.h" | ||
39 | #include "lllistener_fmod.h" | ||
40 | |||
41 | #include "llerror.h" | ||
42 | #include "llmath.h" | ||
43 | #include "llrand.h" | ||
44 | |||
45 | #include "fmod.h" | ||
46 | #include "fmod_errors.h" | ||
47 | #include "lldir.h" | ||
48 | #include "llapr.h" | ||
49 | |||
50 | #include "sound_ids.h" | ||
51 | |||
52 | |||
53 | extern "C" { | ||
54 | void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata); | ||
55 | } | ||
56 | |||
57 | FSOUND_DSPUNIT *gWindDSP = NULL; | ||
58 | |||
59 | |||
60 | LLAudioEngine_FMOD::LLAudioEngine_FMOD() | ||
61 | { | ||
62 | mInited = false; | ||
63 | mWindGen = NULL; | ||
64 | } | ||
65 | |||
66 | |||
67 | LLAudioEngine_FMOD::~LLAudioEngine_FMOD() | ||
68 | { | ||
69 | } | ||
70 | |||
71 | |||
72 | bool LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata) | ||
73 | { | ||
74 | LLAudioEngine::init(num_channels, userdata); | ||
75 | |||
76 | // Reserve one extra channel for the http stream. | ||
77 | if (!FSOUND_SetMinHardwareChannels(num_channels + 1)) | ||
78 | { | ||
79 | LL_WARNS("AppInit") << "FMOD::init[0](), error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; | ||
80 | } | ||
81 | |||
82 | LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() initializing FMOD" << LL_ENDL; | ||
83 | |||
84 | F32 version = FSOUND_GetVersion(); | ||
85 | if (version < FMOD_VERSION) | ||
86 | { | ||
87 | LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version | ||
88 | << ")! You should be using FMOD " << FMOD_VERSION << LL_ENDL; | ||
89 | //return false; | ||
90 | } | ||
91 | |||
92 | U32 fmod_flags = 0x0; | ||
93 | |||
94 | #if LL_WINDOWS | ||
95 | // Windows needs to know which window is frontmost. | ||
96 | // This must be called before FSOUND_Init() per the FMOD docs. | ||
97 | // This could be used to let FMOD handle muting when we lose focus, | ||
98 | // but we don't actually want to do that because we want to distinguish | ||
99 | // between minimized and not-focused states. | ||
100 | if (!FSOUND_SetHWND(userdata)) | ||
101 | { | ||
102 | LL_WARNS("AppInit") << "Error setting FMOD window: " | ||
103 | << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; | ||
104 | return false; | ||
105 | } | ||
106 | // Play audio when we don't have focus. | ||
107 | // (For example, IM client on top of us.) | ||
108 | // This means we also try to play audio when minimized, | ||
109 | // so we manually handle muting in that case. JC | ||
110 | fmod_flags |= FSOUND_INIT_GLOBALFOCUS; | ||
111 | #endif | ||
112 | |||
113 | #if LL_LINUX | ||
114 | // initialize the FMOD engine | ||
115 | |||
116 | // This is a hack to use only FMOD's basic FPU mixer | ||
117 | // when the LL_VALGRIND environmental variable is set, | ||
118 | // otherwise valgrind will fall over on FMOD's MMX detection | ||
119 | if (getenv("LL_VALGRIND")) /*Flawfinder: ignore*/ | ||
120 | { | ||
121 | LL_INFOS("AppInit") << "Pacifying valgrind in FMOD init." << LL_ENDL; | ||
122 | FSOUND_SetMixer(FSOUND_MIXER_QUALITY_FPU); | ||
123 | } | ||
124 | |||
125 | // If we don't set an output method, Linux FMOD always | ||
126 | // decides on OSS and fails otherwise. So we'll manually | ||
127 | // try ESD, then OSS, then ALSA. | ||
128 | // Why this order? See SL-13250, but in short, OSS emulated | ||
129 | // on top of ALSA is ironically more reliable than raw ALSA. | ||
130 | // Ack, and ESD has more reliable failure modes - but has worse | ||
131 | // latency - than all of them, so wins for now. | ||
132 | bool audio_ok = false; | ||
133 | |||
134 | if (!audio_ok) | ||
135 | if (NULL == getenv("LL_BAD_FMOD_ESD")) /*Flawfinder: ignore*/ | ||
136 | { | ||
137 | LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL; | ||
138 | if(FSOUND_SetOutput(FSOUND_OUTPUT_ESD) && | ||
139 | FSOUND_Init(44100, num_channels, fmod_flags)) | ||
140 | { | ||
141 | LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY" | ||
142 | << LL_ENDL; | ||
143 | audio_ok = true; | ||
144 | } else { | ||
145 | LL_WARNS("AppInit") << "ESD audio output FAILED to initialize: " | ||
146 | << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; | ||
147 | } | ||
148 | } else { | ||
149 | LL_DEBUGS("AppInit") << "ESD audio output SKIPPED" << LL_ENDL; | ||
150 | } | ||
151 | |||
152 | if (!audio_ok) | ||
153 | if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ | ||
154 | { | ||
155 | LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; | ||
156 | if(FSOUND_SetOutput(FSOUND_OUTPUT_OSS) && | ||
157 | FSOUND_Init(44100, num_channels, fmod_flags)) | ||
158 | { | ||
159 | LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; | ||
160 | audio_ok = true; | ||
161 | } else { | ||
162 | LL_WARNS("AppInit") << "OSS audio output FAILED to initialize: " | ||
163 | << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; | ||
164 | } | ||
165 | } else { | ||
166 | LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; | ||
167 | } | ||
168 | |||
169 | if (!audio_ok) | ||
170 | if (NULL == getenv("LL_BAD_FMOD_ALSA")) /*Flawfinder: ignore*/ | ||
171 | { | ||
172 | LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; | ||
173 | if(FSOUND_SetOutput(FSOUND_OUTPUT_ALSA) && | ||
174 | FSOUND_Init(44100, num_channels, fmod_flags)) | ||
175 | { | ||
176 | LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; | ||
177 | audio_ok = true; | ||
178 | } else { | ||
179 | LL_WARNS("AppInit") << "ALSA audio output FAILED to initialize: " | ||
180 | << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; | ||
181 | } | ||
182 | } else { | ||
183 | LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; | ||
184 | } | ||
185 | |||
186 | if (!audio_ok) | ||
187 | { | ||
188 | LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL; | ||
189 | return false; | ||
190 | } | ||
191 | |||
192 | // On Linux, FMOD causes a SIGPIPE for some netstream error | ||
193 | // conditions (an FMOD bug); ignore SIGPIPE so it doesn't crash us. | ||
194 | // NOW FIXED in FMOD 3.x since 2006-10-01. | ||
195 | //signal(SIGPIPE, SIG_IGN); | ||
196 | |||
197 | // We're interested in logging which output method we | ||
198 | // ended up with, for QA purposes. | ||
199 | switch (FSOUND_GetOutput()) | ||
200 | { | ||
201 | case FSOUND_OUTPUT_NOSOUND: LL_DEBUGS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; | ||
202 | case FSOUND_OUTPUT_OSS: LL_DEBUGS("AppInit") << "Audio output: OSS" << LL_ENDL; break; | ||
203 | case FSOUND_OUTPUT_ESD: LL_DEBUGS("AppInit") << "Audio output: ESD" << LL_ENDL; break; | ||
204 | case FSOUND_OUTPUT_ALSA: LL_DEBUGS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; | ||
205 | default: LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; | ||
206 | }; | ||
207 | |||
208 | #else // LL_LINUX | ||
209 | |||
210 | // initialize the FMOD engine | ||
211 | if (!FSOUND_Init(44100, num_channels, fmod_flags)) | ||
212 | { | ||
213 | LL_WARNS("AppInit") << "Error initializing FMOD: " | ||
214 | << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; | ||
215 | return false; | ||
216 | } | ||
217 | |||
218 | #endif | ||
219 | |||
220 | // set up our favourite FMOD-native streaming audio implementation if none has already been added | ||
221 | if (!getStreamingAudioImpl()) // no existing implementation added | ||
222 | setStreamingAudioImpl(new LLStreamingAudio_FMOD()); | ||
223 | |||
224 | LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() FMOD initialized correctly" << LL_ENDL; | ||
225 | |||
226 | mInited = true; | ||
227 | |||
228 | return true; | ||
229 | } | ||
230 | |||
231 | |||
232 | std::string LLAudioEngine_FMOD::getDriverName(bool verbose) | ||
233 | { | ||
234 | if (verbose) | ||
235 | { | ||
236 | F32 version = FSOUND_GetVersion(); | ||
237 | return llformat("FMOD version %f", version); | ||
238 | } | ||
239 | else | ||
240 | { | ||
241 | return "FMOD"; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | |||
246 | void LLAudioEngine_FMOD::allocateListener(void) | ||
247 | { | ||
248 | mListenerp = (LLListener *) new LLListener_FMOD(); | ||
249 | if (!mListenerp) | ||
250 | { | ||
251 | llwarns << "Listener creation failed" << llendl; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | |||
256 | void LLAudioEngine_FMOD::shutdown() | ||
257 | { | ||
258 | if (gWindDSP) | ||
259 | { | ||
260 | FSOUND_DSP_SetActive(gWindDSP,false); | ||
261 | FSOUND_DSP_Free(gWindDSP); | ||
262 | } | ||
263 | |||
264 | stopInternetStream(); | ||
265 | |||
266 | LLAudioEngine::shutdown(); | ||
267 | |||
268 | llinfos << "LLAudioEngine_FMOD::shutdown() closing FMOD" << llendl; | ||
269 | FSOUND_Close(); | ||
270 | llinfos << "LLAudioEngine_FMOD::shutdown() done closing FMOD" << llendl; | ||
271 | |||
272 | delete mListenerp; | ||
273 | mListenerp = NULL; | ||
274 | } | ||
275 | |||
276 | |||
277 | LLAudioBuffer * LLAudioEngine_FMOD::createBuffer() | ||
278 | { | ||
279 | return new LLAudioBufferFMOD(); | ||
280 | } | ||
281 | |||
282 | |||
283 | LLAudioChannel * LLAudioEngine_FMOD::createChannel() | ||
284 | { | ||
285 | return new LLAudioChannelFMOD(); | ||
286 | } | ||
287 | |||
288 | |||
289 | void LLAudioEngine_FMOD::initWind() | ||
290 | { | ||
291 | mWindGen = new LLWindGen<MIXBUFFERFORMAT>; | ||
292 | |||
293 | if (!gWindDSP) | ||
294 | { | ||
295 | gWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, mWindGen); | ||
296 | } | ||
297 | if (gWindDSP) | ||
298 | { | ||
299 | FSOUND_DSP_SetActive(gWindDSP, true); | ||
300 | } | ||
301 | mNextWindUpdate = 0.0; | ||
302 | } | ||
303 | |||
304 | |||
305 | void LLAudioEngine_FMOD::cleanupWind() | ||
306 | { | ||
307 | if (gWindDSP) | ||
308 | { | ||
309 | FSOUND_DSP_SetActive(gWindDSP, false); | ||
310 | FSOUND_DSP_Free(gWindDSP); | ||
311 | gWindDSP = NULL; | ||
312 | } | ||
313 | |||
314 | delete mWindGen; | ||
315 | mWindGen = NULL; | ||
316 | } | ||
317 | |||
318 | |||
319 | //----------------------------------------------------------------------- | ||
320 | void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_water) | ||
321 | { | ||
322 | LLVector3 wind_pos; | ||
323 | F64 pitch; | ||
324 | F64 center_freq; | ||
325 | |||
326 | if (!mEnableWind) | ||
327 | { | ||
328 | return; | ||
329 | } | ||
330 | |||
331 | if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL)) | ||
332 | { | ||
333 | |||
334 | // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up) | ||
335 | // need to convert this to the conventional orientation DS3D and OpenAL use | ||
336 | // where +X = right, +Y = up, +Z = backwards | ||
337 | |||
338 | wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]); | ||
339 | |||
340 | // cerr << "Wind update" << endl; | ||
341 | |||
342 | pitch = 1.0 + mapWindVecToPitch(wind_vec); | ||
343 | center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0)); | ||
344 | |||
345 | mWindGen->mTargetFreq = (F32)center_freq; | ||
346 | mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain; | ||
347 | mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | /* | ||
352 | //----------------------------------------------------------------------- | ||
353 | void LLAudioEngine_FMOD::setSourceMinDistance(U16 source_num, F64 distance) | ||
354 | { | ||
355 | if (!mInited) | ||
356 | { | ||
357 | return; | ||
358 | } | ||
359 | if (mBuffer[source_num]) | ||
360 | { | ||
361 | mMinDistance[source_num] = (F32) distance; | ||
362 | if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num])) | ||
363 | { | ||
364 | llwarns << "FMOD::setSourceMinDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
365 | } | ||
366 | } | ||
367 | } | ||
368 | |||
369 | //----------------------------------------------------------------------- | ||
370 | void LLAudioEngine_FMOD::setSourceMaxDistance(U16 source_num, F64 distance) | ||
371 | { | ||
372 | if (!mInited) | ||
373 | { | ||
374 | return; | ||
375 | } | ||
376 | if (mBuffer[source_num]) | ||
377 | { | ||
378 | mMaxDistance[source_num] = (F32) distance; | ||
379 | if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num])) | ||
380 | { | ||
381 | llwarns << "FMOD::setSourceMaxDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | |||
386 | //----------------------------------------------------------------------- | ||
387 | void LLAudioEngine_FMOD::get3DParams(S32 source_num, S32 *volume, S32 *freq, S32 *inside, S32 *outside, LLVector3 *orient, S32 *out_volume, F32 *min_dist, F32 *max_dist) | ||
388 | { | ||
389 | *volume = 0; | ||
390 | *freq = 0; | ||
391 | *inside = 0; | ||
392 | *outside = 0; | ||
393 | *orient = LLVector3::zero; | ||
394 | *out_volume = 0; | ||
395 | *min_dist = 0.f; | ||
396 | *max_dist = 0.f; | ||
397 | } | ||
398 | |||
399 | */ | ||
400 | |||
401 | |||
402 | //----------------------------------------------------------------------- | ||
403 | void LLAudioEngine_FMOD::setInternalGain(F32 gain) | ||
404 | { | ||
405 | if (!mInited) | ||
406 | { | ||
407 | return; | ||
408 | } | ||
409 | |||
410 | gain = llclamp( gain, 0.0f, 1.0f ); | ||
411 | FSOUND_SetSFXMasterVolume( llround( 255.0f * gain ) ); | ||
412 | |||
413 | LLStreamingAudioInterface *saimpl = getStreamingAudioImpl(); | ||
414 | if ( saimpl ) | ||
415 | { | ||
416 | // fmod likes its streaming audio channel gain re-asserted after | ||
417 | // master volume change. | ||
418 | saimpl->setGain(saimpl->getGain()); | ||
419 | } | ||
420 | } | ||
421 | |||
422 | // | ||
423 | // LLAudioChannelFMOD implementation | ||
424 | // | ||
425 | |||
426 | LLAudioChannelFMOD::LLAudioChannelFMOD() : LLAudioChannel(), mChannelID(0), mLastSamplePos(0) | ||
427 | { | ||
428 | } | ||
429 | |||
430 | |||
431 | LLAudioChannelFMOD::~LLAudioChannelFMOD() | ||
432 | { | ||
433 | cleanup(); | ||
434 | } | ||
435 | |||
436 | |||
437 | bool LLAudioChannelFMOD::updateBuffer() | ||
438 | { | ||
439 | if (LLAudioChannel::updateBuffer()) | ||
440 | { | ||
441 | // Base class update returned true, which means that we need to actually | ||
442 | // set up the channel for a different buffer. | ||
443 | |||
444 | LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentSourcep->getCurrentBuffer(); | ||
445 | |||
446 | // Grab the FMOD sample associated with the buffer | ||
447 | FSOUND_SAMPLE *samplep = bufferp->getSample(); | ||
448 | if (!samplep) | ||
449 | { | ||
450 | // This is bad, there should ALWAYS be a sample associated with a legit | ||
451 | // buffer. | ||
452 | llerrs << "No FMOD sample!" << llendl; | ||
453 | return false; | ||
454 | } | ||
455 | |||
456 | |||
457 | // Actually play the sound. Start it off paused so we can do all the necessary | ||
458 | // setup. | ||
459 | mChannelID = FSOUND_PlaySoundEx(FSOUND_FREE, samplep, FSOUND_DSP_GetSFXUnit(), true); | ||
460 | |||
461 | //llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl; | ||
462 | } | ||
463 | |||
464 | // If we have a source for the channel, we need to update its gain. | ||
465 | if (mCurrentSourcep) | ||
466 | { | ||
467 | // SJB: warnings can spam and hurt framerate, disabling | ||
468 | if (!FSOUND_SetVolume(mChannelID, llround(getSecondaryGain() * mCurrentSourcep->getGain() * 255.0f))) | ||
469 | { | ||
470 | // llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
471 | } | ||
472 | |||
473 | if (!FSOUND_SetLoopMode(mChannelID, mCurrentSourcep->isLoop() ? FSOUND_LOOP_NORMAL : FSOUND_LOOP_OFF)) | ||
474 | { | ||
475 | // llwarns << "Channel " << mChannelID << "Source ID: " << mCurrentSourcep->getID() | ||
476 | // << " at " << mCurrentSourcep->getPositionGlobal() << llendl; | ||
477 | // llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | return true; | ||
482 | } | ||
483 | |||
484 | |||
485 | void LLAudioChannelFMOD::update3DPosition() | ||
486 | { | ||
487 | if (!mChannelID) | ||
488 | { | ||
489 | // We're not actually a live channel (i.e., we're not playing back anything) | ||
490 | return; | ||
491 | } | ||
492 | |||
493 | LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentBufferp; | ||
494 | if (!bufferp) | ||
495 | { | ||
496 | // We don't have a buffer associated with us (should really have been picked up | ||
497 | // by the above if. | ||
498 | return; | ||
499 | } | ||
500 | |||
501 | if (mCurrentSourcep->isAmbient()) | ||
502 | { | ||
503 | // Ambient sound, don't need to do any positional updates. | ||
504 | bufferp->set3DMode(false); | ||
505 | } | ||
506 | else | ||
507 | { | ||
508 | // Localized sound. Update the position and velocity of the sound. | ||
509 | bufferp->set3DMode(true); | ||
510 | |||
511 | LLVector3 float_pos; | ||
512 | float_pos.setVec(mCurrentSourcep->getPositionGlobal()); | ||
513 | if (!FSOUND_3D_SetAttributes(mChannelID, float_pos.mV, mCurrentSourcep->getVelocity().mV)) | ||
514 | { | ||
515 | LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::update3DPosition error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; | ||
516 | } | ||
517 | } | ||
518 | } | ||
519 | |||
520 | |||
521 | void LLAudioChannelFMOD::updateLoop() | ||
522 | { | ||
523 | if (!mChannelID) | ||
524 | { | ||
525 | // May want to clear up the loop/sample counters. | ||
526 | return; | ||
527 | } | ||
528 | |||
529 | // | ||
530 | // Hack: We keep track of whether we looped or not by seeing when the | ||
531 | // sample position looks like it's going backwards. Not reliable; may | ||
532 | // yield false negatives. | ||
533 | // | ||
534 | U32 cur_pos = FSOUND_GetCurrentPosition(mChannelID); | ||
535 | if (cur_pos < (U32)mLastSamplePos) | ||
536 | { | ||
537 | mLoopedThisFrame = true; | ||
538 | } | ||
539 | mLastSamplePos = cur_pos; | ||
540 | } | ||
541 | |||
542 | |||
543 | void LLAudioChannelFMOD::cleanup() | ||
544 | { | ||
545 | if (!mChannelID) | ||
546 | { | ||
547 | //llinfos << "Aborting cleanup with no channelID." << llendl; | ||
548 | return; | ||
549 | } | ||
550 | |||
551 | //llinfos << "Cleaning up channel: " << mChannelID << llendl; | ||
552 | if (!FSOUND_StopSound(mChannelID)) | ||
553 | { | ||
554 | LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
555 | } | ||
556 | |||
557 | mCurrentBufferp = NULL; | ||
558 | mChannelID = 0; | ||
559 | } | ||
560 | |||
561 | |||
562 | void LLAudioChannelFMOD::play() | ||
563 | { | ||
564 | if (!mChannelID) | ||
565 | { | ||
566 | llwarns << "Playing without a channelID, aborting" << llendl; | ||
567 | return; | ||
568 | } | ||
569 | |||
570 | if (!FSOUND_SetPaused(mChannelID, false)) | ||
571 | { | ||
572 | llwarns << "LLAudioChannelFMOD::play error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
573 | } | ||
574 | getSource()->setPlayedOnce(true); | ||
575 | } | ||
576 | |||
577 | |||
578 | void LLAudioChannelFMOD::playSynced(LLAudioChannel *channelp) | ||
579 | { | ||
580 | LLAudioChannelFMOD *fmod_channelp = (LLAudioChannelFMOD*)channelp; | ||
581 | if (!(fmod_channelp->mChannelID && mChannelID)) | ||
582 | { | ||
583 | // Don't have channels allocated to both the master and the slave | ||
584 | return; | ||
585 | } | ||
586 | |||
587 | U32 position = FSOUND_GetCurrentPosition(fmod_channelp->mChannelID) % mCurrentBufferp->getLength(); | ||
588 | // Try to match the position of our sync master | ||
589 | if (!FSOUND_SetCurrentPosition(mChannelID, position)) | ||
590 | { | ||
591 | llwarns << "LLAudioChannelFMOD::playSynced unable to set current position" << llendl; | ||
592 | } | ||
593 | |||
594 | // Start us playing | ||
595 | play(); | ||
596 | } | ||
597 | |||
598 | |||
599 | bool LLAudioChannelFMOD::isPlaying() | ||
600 | { | ||
601 | if (!mChannelID) | ||
602 | { | ||
603 | return false; | ||
604 | } | ||
605 | |||
606 | return FSOUND_IsPlaying(mChannelID) && (!FSOUND_GetPaused(mChannelID)); | ||
607 | } | ||
608 | |||
609 | |||
610 | |||
611 | // | ||
612 | // LLAudioBufferFMOD implementation | ||
613 | // | ||
614 | |||
615 | |||
616 | LLAudioBufferFMOD::LLAudioBufferFMOD() | ||
617 | { | ||
618 | mSamplep = NULL; | ||
619 | } | ||
620 | |||
621 | |||
622 | LLAudioBufferFMOD::~LLAudioBufferFMOD() | ||
623 | { | ||
624 | if (mSamplep) | ||
625 | { | ||
626 | // Clean up the associated FMOD sample if it exists. | ||
627 | FSOUND_Sample_Free(mSamplep); | ||
628 | mSamplep = NULL; | ||
629 | } | ||
630 | } | ||
631 | |||
632 | |||
633 | bool LLAudioBufferFMOD::loadWAV(const std::string& filename) | ||
634 | { | ||
635 | // Try to open a wav file from disk. This will eventually go away, as we don't | ||
636 | // really want to block doing this. | ||
637 | if (filename.empty()) | ||
638 | { | ||
639 | // invalid filename, abort. | ||
640 | return false; | ||
641 | } | ||
642 | |||
643 | if (!LLAPRFile::isExist(filename, LL_APR_RPB)) | ||
644 | { | ||
645 | // File not found, abort. | ||
646 | return false; | ||
647 | } | ||
648 | |||
649 | if (mSamplep) | ||
650 | { | ||
651 | // If there's already something loaded in this buffer, clean it up. | ||
652 | FSOUND_Sample_Free(mSamplep); | ||
653 | mSamplep = NULL; | ||
654 | } | ||
655 | |||
656 | // Load up the wav file into an fmod sample | ||
657 | #if LL_WINDOWS | ||
658 | // MikeS. - Loading the sound file manually and then handing it over to FMOD, | ||
659 | // since FMOD uses posix IO internally, | ||
660 | // which doesn't work with unicode file paths. | ||
661 | LLFILE* sound_file = LLFile::fopen(filename,"rb"); /* Flawfinder: ignore */ | ||
662 | if (sound_file) | ||
663 | { | ||
664 | fseek(sound_file,0,SEEK_END); | ||
665 | U32 file_length = ftell(sound_file); //Find the length of the file by seeking to the end and getting the offset | ||
666 | size_t read_count; | ||
667 | fseek(sound_file,0,SEEK_SET); //Seek back to the beginning | ||
668 | char* buffer = new char[file_length]; | ||
669 | llassert(buffer); | ||
670 | read_count = fread((void*)buffer,file_length,1,sound_file);//Load it.. | ||
671 | if(ferror(sound_file)==0 && (read_count == 1)){//No read error, and we got 1 chunk of our size... | ||
672 | unsigned int mode_flags = FSOUND_LOOP_NORMAL | FSOUND_LOADMEMORY; | ||
673 | //FSOUND_16BITS | FSOUND_MONO | FSOUND_LOADMEMORY | FSOUND_LOOP_NORMAL; | ||
674 | mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, buffer, mode_flags , 0, file_length); | ||
675 | } | ||
676 | delete[] buffer; | ||
677 | fclose(sound_file); | ||
678 | } | ||
679 | #else | ||
680 | mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, filename.c_str(), FSOUND_LOOP_NORMAL, 0, 0); | ||
681 | #endif | ||
682 | |||
683 | if (!mSamplep) | ||
684 | { | ||
685 | // We failed to load the file for some reason. | ||
686 | llwarns << "Could not load data '" << filename << "': " | ||
687 | << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
688 | |||
689 | // | ||
690 | // If we EVER want to load wav files provided by end users, we need | ||
691 | // to rethink this! | ||
692 | // | ||
693 | // file is probably corrupt - remove it. | ||
694 | LLFile::remove(filename); | ||
695 | return false; | ||
696 | } | ||
697 | |||
698 | // Everything went well, return true | ||
699 | return true; | ||
700 | } | ||
701 | |||
702 | |||
703 | U32 LLAudioBufferFMOD::getLength() | ||
704 | { | ||
705 | if (!mSamplep) | ||
706 | { | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | return FSOUND_Sample_GetLength(mSamplep); | ||
711 | } | ||
712 | |||
713 | |||
714 | void LLAudioBufferFMOD::set3DMode(bool use3d) | ||
715 | { | ||
716 | U16 current_mode = FSOUND_Sample_GetMode(mSamplep); | ||
717 | |||
718 | if (use3d) | ||
719 | { | ||
720 | if (!FSOUND_Sample_SetMode(mSamplep, (current_mode & (~FSOUND_2D)))) | ||
721 | { | ||
722 | llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
723 | } | ||
724 | } | ||
725 | else | ||
726 | { | ||
727 | if (!FSOUND_Sample_SetMode(mSamplep, current_mode | FSOUND_2D)) | ||
728 | { | ||
729 | llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; | ||
730 | } | ||
731 | } | ||
732 | } | ||
733 | |||
734 | |||
735 | void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata) | ||
736 | { | ||
737 | // originalbuffer = fmod's original mixbuffer. | ||
738 | // newbuffer = the buffer passed from the previous DSP unit. | ||
739 | // length = length in samples at this mix time. | ||
740 | // param = user parameter passed through in FSOUND_DSP_Create. | ||
741 | // | ||
742 | // modify the buffer in some fashion | ||
743 | |||
744 | LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *windgen = | ||
745 | (LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *)userdata; | ||
746 | U8 stride; | ||
747 | |||
748 | #if LL_DARWIN | ||
749 | stride = sizeof(LLAudioEngine_FMOD::MIXBUFFERFORMAT); | ||
750 | #else | ||
751 | int mixertype = FSOUND_GetMixer(); | ||
752 | if (mixertype == FSOUND_MIXER_BLENDMODE || | ||
753 | mixertype == FSOUND_MIXER_QUALITY_FPU) | ||
754 | { | ||
755 | stride = 4; | ||
756 | } | ||
757 | else | ||
758 | { | ||
759 | stride = 2; | ||
760 | } | ||
761 | #endif | ||
762 | |||
763 | newbuffer = windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT *)newbuffer, length, stride); | ||
764 | |||
765 | return newbuffer; | ||
766 | } | ||