aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llaudio/audioengine_fmod.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llaudio/audioengine_fmod.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to '')
-rw-r--r--linden/indra/llaudio/audioengine_fmod.cpp1176
1 files changed, 1176 insertions, 0 deletions
diff --git a/linden/indra/llaudio/audioengine_fmod.cpp b/linden/indra/llaudio/audioengine_fmod.cpp
new file mode 100644
index 0000000..a4ddd1f
--- /dev/null
+++ b/linden/indra/llaudio/audioengine_fmod.cpp
@@ -0,0 +1,1176 @@
1/**
2 * @file audioengine_fmod.cpp
3 * @brief Implementation of LLAudioEngine class abstracting the audio
4 * support as a FMOD 3D implementation
5 *
6 * Copyright (c) 2002-2007, Linden Research, Inc.
7 *
8 * The source code in this file ("Source Code") is provided by Linden Lab
9 * to you under the terms of the GNU General Public License, version 2.0
10 * ("GPL"), unless you have obtained a separate licensing agreement
11 * ("Other License"), formally executed by you and Linden Lab. Terms of
12 * the GPL can be found in doc/GPL-license.txt in this distribution, or
13 * online at http://secondlife.com/developers/opensource/gplv2
14 *
15 * There are special exceptions to the terms and conditions of the GPL as
16 * it is applied to this Source Code. View the full text of the exception
17 * in the file doc/FLOSS-exception.txt in this software distribution, or
18 * online at http://secondlife.com/developers/opensource/flossexception
19 *
20 * By copying, modifying or distributing this software, you acknowledge
21 * that you have read and understood your obligations described above,
22 * and agree to abide by those obligations.
23 *
24 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
25 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
26 * COMPLETENESS OR PERFORMANCE.
27 */
28
29#include "linden_common.h"
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <math.h>
35
36#include "audioengine_fmod.h"
37
38#if LL_FMOD
39
40#include "listener_fmod.h"
41
42#include "llerror.h"
43#include "llmath.h"
44#include "llrand.h"
45
46#include "fmod.h"
47#include "fmod_errors.h"
48#include "lldir.h"
49#include "llapr.h"
50
51#include "sound_ids.h"
52
53
54void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata);
55FSOUND_DSPUNIT *gWindDSP = NULL;
56
57// These globals for the wind filter. Blech!
58F64 gbuf0 = 0.0;
59F64 gbuf1 = 0.0;
60F64 gbuf2 = 0.0;
61F64 gbuf3 = 0.0;
62F64 gbuf4 = 0.0;
63F64 gbuf5 = 0.0;
64F64 gY0 = 0.0;
65F64 gY1 = 0.0;
66
67F32 gTargetGain = 0.f;
68F32 gCurrentGain = 0.f;
69F32 gTargetFreq = 100.f;
70F32 gCurrentFreq = 100.f;
71F32 gTargetPanGainR = 0.5f;
72F32 gCurrentPanGainR = 0.5f;
73
74
75// Safe strcpy
76#if LL_WINDOWS || LL_LINUX
77static size_t strlcpy( char* dest, const char* src, size_t dst_size )
78{
79 size_t source_len = 0;
80 size_t min_len = 0;
81 if( dst_size > 0 )
82 {
83 if( src )
84 {
85 source_len = strlen(src); /*Flawfinder: ignore*/
86 min_len = llmin( dst_size - 1, source_len );
87 memcpy(dest, src, min_len); /*Flawfinder: ignore*/
88 }
89 dest[min_len] = '\0';
90 }
91 return source_len;
92}
93#else
94// apple ships with the non-standard strlcpy in /usr/include/string.h:
95// size_t strlcpy(char *, const char *, size_t);
96#endif
97
98
99LLAudioEngine_FMOD::LLAudioEngine_FMOD()
100{
101 mInited = FALSE;
102 mCurrentInternetStreamp = NULL;
103 mInternetStreamChannel = -1;
104}
105
106
107LLAudioEngine_FMOD::~LLAudioEngine_FMOD()
108{
109}
110
111
112BOOL LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
113{
114 mFadeIn = -10000;
115
116 LLAudioEngine::init(num_channels, userdata);
117
118 // Reserve one extra channel for the http stream.
119 if (!FSOUND_SetMinHardwareChannels(num_channels + 1))
120 {
121 llwarns<< "FMOD::init[0](), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
122 }
123
124 llinfos << "LLAudioEngine_FMOD::init() initializing FMOD" << llendl;
125
126 F32 version = FSOUND_GetVersion();
127 if (version < FMOD_VERSION)
128 {
129 llwarns << "Error : You are using the wrong FMOD version (" << version
130 << ")! You should be using FMOD " << FMOD_VERSION << llendl;
131 //return FALSE;
132 }
133
134 U32 fmod_flags = 0x0;
135
136#if LL_WINDOWS
137 // Windows needs to know which window is frontmost.
138 // This must be called before FSOUND_Init() per the FMOD docs.
139 // This could be used to let FMOD handle muting when we lose focus,
140 // but we don't actually want to do that because we want to distinguish
141 // between minimized and not-focused states.
142 if (!FSOUND_SetHWND(userdata))
143 {
144 llwarns << "Error setting FMOD window: "
145 << FMOD_ErrorString(FSOUND_GetError()) << llendl;
146 return FALSE;
147 }
148 // Play audio when we don't have focus.
149 // (For example, IM client on top of us.)
150 // This means we also try to play audio when minimized,
151 // so we manually handle muting in that case. JC
152 fmod_flags |= FSOUND_INIT_GLOBALFOCUS;
153#endif
154
155#if LL_LINUX
156 // initialize the FMOD engine
157
158 // This is a hack to use only FMOD's basic FPU mixer
159 // when the LL_VALGRIND environmental variable is set,
160 // otherwise valgrind will fall over on FMOD's MMX detection
161 if (getenv("LL_VALGRIND")) /*Flawfinder: ignore*/
162 {
163 llinfos << "Pacifying valgrind in FMOD init." << llendl;
164 FSOUND_SetMixer(FSOUND_MIXER_QUALITY_FPU);
165 }
166
167 // If we don't set an output method, Linux FMOD always
168 // decides on OSS and fails otherwise. So we'll manually
169 // try ESD, then OSS, then ALSA.
170 // Why this order? See SL-13250, but in short, OSS emulated
171 // on top of ALSA is ironically more reliable than raw ALSA.
172 // Ack, and ESD has more reliable failure modes - but has worse
173 // latency - than all of them, so wins for now.
174 BOOL audio_ok = FALSE;
175
176 if (!audio_ok)
177 if (NULL == getenv("LL_BAD_ESD")) /*Flawfinder: ignore*/
178 {
179 llinfos << "Trying ESD audio output..." << llendl;
180 if(FSOUND_SetOutput(FSOUND_OUTPUT_ESD) &&
181 FSOUND_Init(44100, num_channels, fmod_flags))
182 {
183 llinfos << "ESD audio output initialized OKAY"
184 << llendl;
185 audio_ok = TRUE;
186 } else {
187 llwarns << "ESD audio output FAILED to initialize: "
188 << FMOD_ErrorString(FSOUND_GetError()) << llendl;
189 }
190 } else {
191 llinfos << "ESD audio output SKIPPED" << llendl;
192 }
193
194 if (!audio_ok)
195 if (NULL == getenv("LL_BAD_OSS")) /*Flawfinder: ignore*/
196 {
197 llinfos << "Trying OSS audio output..." << llendl;
198 if(FSOUND_SetOutput(FSOUND_OUTPUT_OSS) &&
199 FSOUND_Init(44100, num_channels, fmod_flags))
200 {
201 llinfos << "OSS audio output initialized OKAY" << llendl;
202 audio_ok = TRUE;
203 } else {
204 llwarns << "OSS audio output FAILED to initialize: "
205 << FMOD_ErrorString(FSOUND_GetError()) << llendl;
206 }
207 } else {
208 llinfos << "OSS audio output SKIPPED" << llendl;
209 }
210
211 if (!audio_ok)
212 if (NULL == getenv("LL_BAD_ALSA")) /*Flawfinder: ignore*/
213 {
214 llinfos << "Trying ALSA audio output..." << llendl;
215 if(FSOUND_SetOutput(FSOUND_OUTPUT_ALSA) &&
216 FSOUND_Init(44100, num_channels, fmod_flags))
217 {
218 llinfos << "ALSA audio output initialized OKAY" << llendl;
219 audio_ok = TRUE;
220 } else {
221 llwarns << "ALSA audio output FAILED to initialize: "
222 << FMOD_ErrorString(FSOUND_GetError()) << llendl;
223 }
224 } else {
225 llinfos << "OSS audio output SKIPPED" << llendl;
226 }
227
228 if (!audio_ok)
229 {
230 llwarns << "Overall audio init failure." << llendl;
231 return FALSE;
232 }
233
234 // On Linux, FMOD causes a SIGPIPE for some netstream error
235 // conditions (an FMOD bug); ignore SIGPIPE so it doesn't crash us.
236 // NOW FIXED in FMOD 3.x since 2006-10-01.
237 //signal(SIGPIPE, SIG_IGN);
238
239 // We're interested in logging which output method we
240 // ended up with, for QA purposes.
241 switch (FSOUND_GetOutput())
242 {
243 case FSOUND_OUTPUT_NOSOUND: llinfos << "Audio output: NoSound" << llendl; break;
244 case FSOUND_OUTPUT_OSS: llinfos << "Audio output: OSS" << llendl; break;
245 case FSOUND_OUTPUT_ESD: llinfos << "Audio output: ESD" << llendl; break;
246 case FSOUND_OUTPUT_ALSA: llinfos << "Audio output: ALSA" << llendl; break;
247 default: llinfos << "Audio output: Unknown!" << llendl; break;
248 };
249
250#else // LL_LINUX
251
252 // initialize the FMOD engine
253 if (!FSOUND_Init(44100, num_channels, fmod_flags))
254 {
255 llwarns << "Error initializing FMOD: "
256 << FMOD_ErrorString(FSOUND_GetError()) << llendl;
257 return FALSE;
258 }
259
260#endif
261
262 initInternetStream();
263
264 llinfos << "LLAudioEngine_FMOD::init() FMOD initialized correctly" << llendl;
265
266 mInited = TRUE;
267
268 return TRUE;
269}
270
271
272void LLAudioEngine_FMOD::idle(F32 max_decode_time)
273{
274 LLAudioEngine::idle(max_decode_time);
275
276 updateInternetStream();
277}
278
279
280void LLAudioEngine_FMOD::allocateListener(void)
281{
282 mListenerp = (LLListener *) new LLListener_FMOD();
283 if (!mListenerp)
284 {
285 llwarns << "Listener creation failed" << llendl;
286 }
287}
288
289
290void LLAudioEngine_FMOD::shutdown()
291{
292 if (gWindDSP)
293 {
294 FSOUND_DSP_SetActive(gWindDSP,FALSE);
295 FSOUND_DSP_Free(gWindDSP);
296 }
297
298 stopInternetStream();
299
300 LLAudioEngine::shutdown();
301
302 llinfos << "LLAudioEngine_FMOD::shutdown() closing FMOD" << llendl;
303 FSOUND_Close();
304 llinfos << "LLAudioEngine_FMOD::shutdown() done closing FMOD" << llendl;
305
306 delete mListenerp;
307 mListenerp = NULL;
308}
309
310
311LLAudioBuffer *LLAudioEngine_FMOD::createBuffer()
312{
313 return new LLAudioBufferFMOD();
314}
315
316
317LLAudioChannel *LLAudioEngine_FMOD::createChannel()
318{
319 return new LLAudioChannelFMOD();
320}
321
322
323void LLAudioEngine_FMOD::initWind()
324{
325 if (!gWindDSP)
326 {
327 gWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, NULL);
328 }
329 if (gWindDSP)
330 {
331 FSOUND_DSP_SetActive(gWindDSP, TRUE);
332 }
333 mNextWindUpdate = 0.0;
334}
335
336
337void LLAudioEngine_FMOD::cleanupWind()
338{
339 if (gWindDSP)
340 {
341 FSOUND_DSP_SetActive(gWindDSP, FALSE);
342 FSOUND_DSP_Free(gWindDSP);
343 gWindDSP = NULL;
344 }
345}
346
347
348//-----------------------------------------------------------------------
349void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_water)
350{
351 LLVector3 wind_pos;
352 F64 pitch;
353 F64 center_freq;
354
355 if (!mEnableWind)
356 {
357 return;
358 }
359
360 if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
361 {
362
363 // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
364 // need to convert this to the conventional orientation DS3D and OpenAL use
365 // where +X = right, +Y = up, +Z = backwards
366
367 wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
368
369 // cerr << "Wind update" << endl;
370
371 pitch = 1.0 + mapWindVecToPitch(wind_vec);
372 center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
373
374 gTargetFreq = (F32)center_freq;
375 gTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
376 gTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
377 }
378}
379
380/*
381//-----------------------------------------------------------------------
382void LLAudioEngine_FMOD::setSourceMinDistance(U16 source_num, F64 distance)
383{
384 if (!mInited)
385 {
386 return;
387 }
388 if (mBuffer[source_num])
389 {
390 mMinDistance[source_num] = (F32) distance;
391 if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num]))
392 {
393 llwarns << "FMOD::setSourceMinDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
394 }
395 }
396}
397
398//-----------------------------------------------------------------------
399void LLAudioEngine_FMOD::setSourceMaxDistance(U16 source_num, F64 distance)
400{
401 if (!mInited)
402 {
403 return;
404 }
405 if (mBuffer[source_num])
406 {
407 mMaxDistance[source_num] = (F32) distance;
408 if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num]))
409 {
410 llwarns << "FMOD::setSourceMaxDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
411 }
412 }
413}
414
415//-----------------------------------------------------------------------
416void 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)
417{
418 *volume = 0;
419 *freq = 0;
420 *inside = 0;
421 *outside = 0;
422 *orient = LLVector3::zero;
423 *out_volume = 0;
424 *min_dist = 0.f;
425 *max_dist = 0.f;
426}
427
428*/
429
430
431//-----------------------------------------------------------------------
432void LLAudioEngine_FMOD::setInternalGain(F32 gain)
433{
434 if (!mInited)
435 {
436 return;
437 }
438
439 gain = llclamp( gain, 0.0f, 1.0f );
440 FSOUND_SetSFXMasterVolume( llround( 255.0f * gain ) );
441
442 if ( mInternetStreamChannel != -1 )
443 {
444 F32 clamp_internet_stream_gain = llclamp( mInternetStreamGain, 0.0f, 1.0f );
445 FSOUND_SetVolumeAbsolute( mInternetStreamChannel, llround( 255.0f * clamp_internet_stream_gain ) );
446 }
447}
448
449//
450// LLAudioChannelFMOD implementation
451//
452
453LLAudioChannelFMOD::LLAudioChannelFMOD() : LLAudioChannel(), mChannelID(0), mLastSamplePos(0)
454{
455}
456
457
458LLAudioChannelFMOD::~LLAudioChannelFMOD()
459{
460 cleanup();
461}
462
463
464BOOL LLAudioChannelFMOD::updateBuffer()
465{
466 if (LLAudioChannel::updateBuffer())
467 {
468 // Base class update returned TRUE, which means that we need to actually
469 // set up the channel for a different buffer.
470
471 LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentSourcep->getCurrentBuffer();
472
473 // Grab the FMOD sample associated with the buffer
474 FSOUND_SAMPLE *samplep = bufferp->getSample();
475 if (!samplep)
476 {
477 // This is bad, there should ALWAYS be a sample associated with a legit
478 // buffer.
479 llerrs << "No FMOD sample!" << llendl;
480 return FALSE;
481 }
482
483
484 // Actually play the sound. Start it off paused so we can do all the necessary
485 // setup.
486 mChannelID = FSOUND_PlaySoundEx(FSOUND_FREE, samplep, FSOUND_DSP_GetSFXUnit(), TRUE);
487
488 //llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl;
489 }
490
491 // If we have a source for the channel, we need to update its gain.
492 if (mCurrentSourcep)
493 {
494 if (!FSOUND_SetVolume(mChannelID, llround(mCurrentSourcep->getGain() * 255.0f)))
495 {
496 llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
497 }
498
499 if (!FSOUND_SetLoopMode(mChannelID, mCurrentSourcep->isLoop() ? FSOUND_LOOP_NORMAL : FSOUND_LOOP_OFF))
500 {
501 llwarns << "Channel " << mChannelID << llendl;
502 llwarns << "Source ID: " << mCurrentSourcep->getID() << " at " << mCurrentSourcep->getPositionGlobal() << llendl;
503 llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
504 }
505 }
506
507 return TRUE;
508}
509
510
511void LLAudioChannelFMOD::update3DPosition()
512{
513 if (!mChannelID)
514 {
515 // We're not actually a live channel (i.e., we're not playing back anything)
516 return;
517 }
518
519 LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentBufferp;
520 if (!bufferp)
521 {
522 // We don't have a buffer associated with us (should really have been picked up
523 // by the above if.
524 return;
525 }
526
527 if (mCurrentSourcep->isAmbient())
528 {
529 // Ambient sound, don't need to do any positional updates.
530 bufferp->set3DMode(FALSE);
531 }
532 else
533 {
534 // Localized sound. Update the position and velocity of the sound.
535 bufferp->set3DMode(TRUE);
536
537 LLVector3 float_pos;
538 float_pos.setVec(mCurrentSourcep->getPositionGlobal());
539 if (!FSOUND_3D_SetAttributes(mChannelID, float_pos.mV, mCurrentSourcep->getVelocity().mV))
540 {
541 llwarns << "LLAudioChannelFMOD::update3DPosition error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
542 }
543 }
544}
545
546
547void LLAudioChannelFMOD::updateLoop()
548{
549 if (!mChannelID)
550 {
551 // May want to clear up the loop/sample counters.
552 return;
553 }
554
555 //
556 // Hack: We keep track of whether we looped or not by seeing when the sign of the last sample
557 // flips. This is pretty crappy.
558 //
559 U32 cur_pos = FSOUND_GetCurrentPosition(mChannelID);
560 if (cur_pos < (U32)mLastSamplePos)
561 {
562 mLoopedThisFrame = TRUE;
563 }
564 mLastSamplePos = cur_pos;
565}
566
567
568void LLAudioChannelFMOD::cleanup()
569{
570 if (!mChannelID)
571 {
572 //llinfos << "Aborting cleanup with no channelID." << llendl;
573 return;
574 }
575
576 //llinfos << "Cleaning up channel: " << mChannelID << llendl;
577 if (!FSOUND_StopSound(mChannelID))
578 {
579 llwarns << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
580 }
581
582 mCurrentBufferp = NULL;
583 mChannelID = 0;
584}
585
586
587void LLAudioChannelFMOD::play()
588{
589 if (!mChannelID)
590 {
591 llwarns << "Playing without a channelID, aborting" << llendl;
592 return;
593 }
594
595 if (!FSOUND_SetPaused(mChannelID, FALSE))
596 {
597 llwarns << "LLAudioChannelFMOD::play error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
598 }
599 getSource()->setPlayedOnce(TRUE);
600}
601
602
603void LLAudioChannelFMOD::playSynced(LLAudioChannel *channelp)
604{
605 LLAudioChannelFMOD *fmod_channelp = (LLAudioChannelFMOD*)channelp;
606 if (!(fmod_channelp->mChannelID && mChannelID))
607 {
608 // Don't have channels allocated to both the master and the slave
609 return;
610 }
611
612 U32 position = FSOUND_GetCurrentPosition(fmod_channelp->mChannelID) % mCurrentBufferp->getLength();
613 // Try to match the position of our sync master
614 if (!FSOUND_SetCurrentPosition(mChannelID, position))
615 {
616 llwarns << "LLAudioChannelFMOD::playSynced unable to set current position" << llendl;
617 }
618
619 // Start us playing
620 play();
621}
622
623
624BOOL LLAudioChannelFMOD::isPlaying()
625{
626 if (!mChannelID)
627 {
628 return FALSE;
629 }
630
631 return FSOUND_IsPlaying(mChannelID) && (!FSOUND_GetPaused(mChannelID));
632}
633
634
635
636//
637// LLAudioBufferFMOD implementation
638//
639
640
641LLAudioBufferFMOD::LLAudioBufferFMOD()
642{
643 mSamplep = NULL;
644}
645
646
647LLAudioBufferFMOD::~LLAudioBufferFMOD()
648{
649 if (mSamplep)
650 {
651 // Clean up the associated FMOD sample if it exists.
652 FSOUND_Sample_Free(mSamplep);
653 mSamplep = NULL;
654 }
655}
656
657
658BOOL LLAudioBufferFMOD::loadWAV(const char *filename)
659{
660 // Try to open a wav file from disk. This will eventually go away, as we don't
661 // really want to block doing this.
662 if (filename == NULL)
663 {
664 // invalid filename, abort.
665 return FALSE;
666 }
667
668 S32 file_size = 0;
669 apr_file_t* apr_file = ll_apr_file_open(filename, LL_APR_RPB, &file_size);
670 if (!apr_file)
671 {
672 // File not found, abort.
673 return FALSE;
674 }
675 apr_file_close(apr_file);
676
677 if (mSamplep)
678 {
679 // If there's already something loaded in this buffer, clean it up.
680 FSOUND_Sample_Free(mSamplep);
681 mSamplep = NULL;
682 }
683
684 // Load up the wav file into an fmod sample
685#if LL_WINDOWS
686 // MikeS. - Loading the sound file manually and then handing it over to FMOD,
687 // since FMOD uses posix IO internally,
688 // which doesn't work with unicode file paths.
689 FILE* sound_file = LLFile::fopen(filename,"rb");
690 if (sound_file)
691 {
692 fseek(sound_file,0,SEEK_END);
693 U32 file_length = ftell(sound_file); //Find the length of the file by seeking to the end and getting the offset
694 size_t read_count;
695 fseek(sound_file,0,SEEK_SET); //Seek back to the beginning
696 char* buffer = new char[file_length];
697 llassert(buffer);
698 read_count = fread((void*)buffer,file_length,1,sound_file);//Load it..
699 if(ferror(sound_file)==0 && (read_count == 1)){//No read error, and we got 1 chunk of our size...
700 unsigned int mode_flags = FSOUND_LOOP_NORMAL | FSOUND_LOADMEMORY;
701 //FSOUND_16BITS | FSOUND_MONO | FSOUND_LOADMEMORY | FSOUND_LOOP_NORMAL;
702 mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, buffer, mode_flags , 0, file_length);
703 }
704 delete[] buffer;
705 fclose(sound_file);
706 }
707#else
708 mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, filename, FSOUND_LOOP_NORMAL, 0, 0);
709#endif
710
711 if (!mSamplep)
712 {
713 // We failed to load the file for some reason.
714 llwarns << "Could not load data '" << filename << "': "
715 << FMOD_ErrorString(FSOUND_GetError()) << llendl;
716
717 //
718 // If we EVER want to load wav files provided by end users, we need
719 // to rethink this!
720 //
721 // file is probably corrupt - remove it.
722 LLFile::remove(filename);
723 return FALSE;
724 }
725
726 // Everything went well, return TRUE
727 return TRUE;
728}
729
730
731U32 LLAudioBufferFMOD::getLength()
732{
733 if (!mSamplep)
734 {
735 return 0;
736 }
737
738 return FSOUND_Sample_GetLength(mSamplep);
739}
740
741
742void LLAudioBufferFMOD::set3DMode(BOOL use3d)
743{
744 U16 current_mode = FSOUND_Sample_GetMode(mSamplep);
745
746 if (use3d)
747 {
748 if (!FSOUND_Sample_SetMode(mSamplep, (current_mode & (~FSOUND_2D))))
749 {
750 llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
751 }
752 }
753 else
754 {
755 if (!FSOUND_Sample_SetMode(mSamplep, current_mode | FSOUND_2D))
756 {
757 llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
758 }
759 }
760}
761
762
763
764//---------------------------------------------------------------------------
765// Internet Streaming
766//---------------------------------------------------------------------------
767void LLAudioEngine_FMOD::initInternetStream()
768{
769 // Number of milliseconds of audio to buffer for the audio card.
770 // Must be larger than the usual Second Life frame stutter time.
771 FSOUND_Stream_SetBufferSize(200);
772
773 // Here's where we set the size of the network buffer and some buffering
774 // parameters. In this case we want a network buffer of 16k, we want it
775 // to prebuffer 40% of that when we first connect, and we want it
776 // to rebuffer 80% of that whenever we encounter a buffer underrun.
777
778 // Leave the net buffer properties at the default.
779 //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80);
780 mInternetStreamURL[0] = 0;
781}
782
783
784void LLAudioEngine_FMOD::startInternetStream(const char* url)
785{
786 if (!mInited)
787 {
788 llwarns << "startInternetStream before audio initialized" << llendl;
789 return;
790 }
791
792 // "stop" stream but don't clear url, etc. in calse url == mInternetStreamURL
793 stopInternetStream();
794 if (url)
795 {
796 llinfos << "Starting internet stream: " << url << llendl;
797 mCurrentInternetStreamp = new LLAudioStreamFMOD(url);
798 strlcpy(mInternetStreamURL, url, 1024);
799 }
800 else
801 {
802 llinfos << "Set internet stream to null" << llendl;
803 mInternetStreamURL[0] = 0;
804 }
805}
806
807
808signed char F_CALLBACKAPI LLAudioEngine_FMOD::callbackMetaData(char *name, char *value, void *userdata)
809{
810 /*
811 LLAudioEngine_FMOD* self = (LLAudioEngine_FMOD*)userdata;
812
813 if (!strcmp("ARTIST", name))
814 {
815 strlcpy(self->mInternetStreamArtist, value, 256);
816 self->mInternetStreamNewMetaData = TRUE;
817 return TRUE;
818 }
819
820 if (!strcmp("TITLE", name))
821 {
822 strlcpy(self->mInternetStreamTitle, value, 256);
823 self->mInternetStreamNewMetaData = TRUE;
824 return TRUE;
825 }
826 */
827
828 return TRUE;
829}
830
831
832void LLAudioEngine_FMOD::updateInternetStream()
833{
834 // Kill dead internet streams, if possible
835 std::list<LLAudioStreamFMOD *>::iterator iter;
836 for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
837 {
838 LLAudioStreamFMOD *streamp = *iter;
839 if (streamp->stopStream())
840 {
841 llinfos << "Closed dead stream" << llendl;
842 delete streamp;
843 mDeadStreams.erase(iter++);
844 }
845 else
846 {
847 iter++;
848 }
849 }
850
851 // Don't do anything if there are no streams playing
852 if (!mCurrentInternetStreamp)
853 {
854 return;
855 }
856
857 int open_state = mCurrentInternetStreamp->getOpenState();
858
859 if (!open_state)
860 {
861 // Stream is live
862
863
864 // start the stream if it's ready
865 if (mInternetStreamChannel < 0)
866 {
867 mInternetStreamChannel = mCurrentInternetStreamp->startStream();
868
869 if (mInternetStreamChannel != -1)
870 {
871 // Reset volume to previously set volume
872 setInternetStreamGain(mInternetStreamGain);
873 FSOUND_SetPaused(mInternetStreamChannel, FALSE);
874 //FSOUND_Stream_Net_SetMetadataCallback(mInternetStream, callbackMetaData, this);
875 }
876 }
877 }
878
879 switch(open_state)
880 {
881 default:
882 case 0:
883 // success
884 break;
885 case -1:
886 // stream handle is invalid
887 llwarns << "InternetStream - invalid handle" << llendl;
888 stopInternetStream();
889 return;
890 case -2:
891 // opening
892 //strlcpy(mInternetStreamArtist, "Opening", 256);
893 break;
894 case -3:
895 // failed to open, file not found, perhaps
896 llwarns << "InternetSteam - failed to open" << llendl;
897 stopInternetStream();
898 return;
899 case -4:
900 // connecting
901 //strlcpy(mInternetStreamArtist, "Connecting", 256);
902 break;
903 case -5:
904 // buffering
905 //strlcpy(mInternetStreamArtist, "Buffering", 256);
906 break;
907 }
908
909}
910
911void LLAudioEngine_FMOD::stopInternetStream()
912{
913 if (mInternetStreamChannel != -1)
914 {
915 FSOUND_SetPaused(mInternetStreamChannel, TRUE);
916 FSOUND_SetPriority(mInternetStreamChannel, 0);
917 mInternetStreamChannel = -1;
918 }
919
920 if (mCurrentInternetStreamp)
921 {
922 llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl;
923 if (mCurrentInternetStreamp->stopStream())
924 {
925 delete mCurrentInternetStreamp;
926 }
927 else
928 {
929 llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl;
930 mDeadStreams.push_back(mCurrentInternetStreamp);
931 }
932 mCurrentInternetStreamp = NULL;
933 //mInternetStreamURL[0] = 0;
934 }
935}
936
937void LLAudioEngine_FMOD::pauseInternetStream(int pause)
938{
939 if (pause < 0)
940 {
941 pause = mCurrentInternetStreamp ? 1 : 0;
942 }
943
944 if (pause)
945 {
946 if (mCurrentInternetStreamp)
947 {
948 stopInternetStream();
949 }
950 }
951 else if (mInternetStreamURL)
952 {
953 startInternetStream(mInternetStreamURL);
954 }
955}
956
957
958// A stream is "playing" if it has been requested to start. That
959// doesn't necessarily mean audio is coming out of the speakers.
960int LLAudioEngine_FMOD::isInternetStreamPlaying()
961{
962 if (mCurrentInternetStreamp)
963 {
964 return 1; // Active and playing
965 }
966 else if (mInternetStreamURL[0])
967 {
968 return 2; // "Paused"
969 }
970 else
971 {
972 return 0;
973 }
974}
975
976
977void LLAudioEngine_FMOD::getInternetStreamInfo(char* artist_out, char* title_out)
978{
979 //strlcpy(artist_out, mInternetStreamArtist, 256);
980 //strlcpy(title_out, mInternetStreamTitle, 256);
981}
982
983
984void LLAudioEngine_FMOD::setInternetStreamGain(F32 vol)
985{
986 LLAudioEngine::setInternetStreamGain(vol);
987 if (mInternetStreamChannel != -1)
988 {
989 vol = llclamp(vol, 0.f, 1.f);
990 int vol_int = llround(vol * 255.f);
991 FSOUND_SetVolumeAbsolute(mInternetStreamChannel, vol_int);
992 }
993}
994
995
996const char* LLAudioEngine_FMOD::getInternetStreamURL()
997{
998 return mInternetStreamURL;
999}
1000
1001
1002LLAudioStreamFMOD::LLAudioStreamFMOD(const char *url) :
1003 mInternetStream(NULL),
1004 mReady(FALSE)
1005{
1006 mInternetStreamURL[0] = 0;
1007 strlcpy(mInternetStreamURL, url, 1024);
1008 mInternetStream = FSOUND_Stream_Open(url, FSOUND_NORMAL | FSOUND_NONBLOCKING, 0, 0);
1009 if (!mInternetStream)
1010 {
1011 llwarns << "Couldn't open fmod stream, error "
1012 << FMOD_ErrorString(FSOUND_GetError())
1013 << llendl;
1014 mReady = FALSE;
1015 return;
1016 }
1017
1018 mReady = TRUE;
1019}
1020
1021int LLAudioStreamFMOD::startStream()
1022{
1023 // We need a live and opened stream before we try and play it.
1024 if (!mInternetStream || getOpenState())
1025 {
1026 llwarns << "No internet stream to start playing!" << llendl;
1027 return -1;
1028 }
1029
1030 // Make sure the stream is set to 2D mode.
1031 FSOUND_Stream_SetMode(mInternetStream, FSOUND_2D);
1032
1033 return FSOUND_Stream_PlayEx(FSOUND_FREE, mInternetStream, NULL, TRUE);
1034}
1035
1036BOOL LLAudioStreamFMOD::stopStream()
1037{
1038 if (mInternetStream)
1039 {
1040 int read_percent = 0;
1041 int status = 0;
1042 int bitrate = 0;
1043 unsigned int flags = 0x0;
1044 FSOUND_Stream_Net_GetStatus(mInternetStream, &status, &read_percent, &bitrate, &flags);
1045
1046 BOOL close = TRUE;
1047 switch (status)
1048 {
1049 case FSOUND_STREAM_NET_CONNECTING:
1050 close = FALSE;
1051 break;
1052 case FSOUND_STREAM_NET_NOTCONNECTED:
1053 case FSOUND_STREAM_NET_BUFFERING:
1054 case FSOUND_STREAM_NET_READY:
1055 case FSOUND_STREAM_NET_ERROR:
1056 default:
1057 close = TRUE;
1058 }
1059
1060 if (close)
1061 {
1062 FSOUND_Stream_Close(mInternetStream);
1063 mInternetStream = NULL;
1064 return TRUE;
1065 }
1066 else
1067 {
1068 return FALSE;
1069 }
1070 }
1071 else
1072 {
1073 return TRUE;
1074 }
1075}
1076
1077int LLAudioStreamFMOD::getOpenState()
1078{
1079 int open_state = FSOUND_Stream_GetOpenState(mInternetStream);
1080 return open_state;
1081}
1082
1083/* This determines the format of the mixbuffer being passed in. change if you want to support int32 or float32 */
1084#if LL_DARWIN
1085 #define MIXBUFFERFORMAT S32
1086#else
1087 #define MIXBUFFERFORMAT S16
1088#endif
1089
1090inline MIXBUFFERFORMAT clipSample(MIXBUFFERFORMAT sample, MIXBUFFERFORMAT min, MIXBUFFERFORMAT max)
1091{
1092 if (sample > max)
1093 sample = max;
1094 else if (sample < min)
1095 sample = min;
1096
1097 return sample;
1098}
1099
1100void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void*)
1101{
1102// originalbuffer = fsounds original mixbuffer.
1103// newbuffer = the buffer passed from the previous DSP unit.
1104// length = length in samples at this mix time.
1105// param = user parameter passed through in FSOUND_DSP_Create.
1106//
1107// modify the buffer in some fashion
1108
1109 U8 *cursamplep = (U8*)newbuffer;
1110 U8 wordsize = 2;
1111
1112#if LL_DARWIN
1113 wordsize = sizeof(MIXBUFFERFORMAT);
1114#else
1115 int mixertype = FSOUND_GetMixer();
1116 if (mixertype == FSOUND_MIXER_BLENDMODE || mixertype == FSOUND_MIXER_QUALITY_FPU)
1117 {
1118 wordsize = 4;
1119 }
1120#endif
1121
1122 double bandwidth = 50;
1123 double inputSamplingRate = 44100;
1124 double a0,b1,b2;
1125
1126 // calculate resonant filter coeffs
1127 b2 = exp(-(F_TWO_PI) * (bandwidth / inputSamplingRate));
1128
1129 while (length--)
1130 {
1131 gCurrentFreq = (float)((0.999 * gCurrentFreq) + (0.001 * gTargetFreq));
1132 gCurrentGain = (float)((0.999 * gCurrentGain) + (0.001 * gTargetGain));
1133 gCurrentPanGainR = (float)((0.999 * gCurrentPanGainR) + (0.001 * gTargetPanGainR));
1134 b1 = (-4.0 * b2) / (1.0 + b2) * cos(F_TWO_PI * (gCurrentFreq / inputSamplingRate));
1135 a0 = (1.0 - b2) * sqrt(1.0 - (b1 * b1) / (4.0 * b2));
1136 double nextSample;
1137
1138 // start with white noise
1139 nextSample = frand(2.0f) - 1.0f;
1140
1141#if 1 // LLAE_WIND_PINK apply pinking filter
1142 gbuf0 = 0.997f * gbuf0 + 0.0126502f * nextSample;
1143 gbuf1 = 0.985f * gbuf1 + 0.0139083f * nextSample;
1144 gbuf2 = 0.950f * gbuf2 + 0.0205439f * nextSample;
1145 gbuf3 = 0.850f * gbuf3 + 0.0387225f * nextSample;
1146 gbuf4 = 0.620f * gbuf4 + 0.0465932f * nextSample;
1147 gbuf5 = 0.250f * gbuf5 + 0.1093477f * nextSample;
1148
1149 nextSample = gbuf0 + gbuf1 + gbuf2 + gbuf3 + gbuf4 + gbuf5;
1150#endif
1151
1152#if 1 //LLAE_WIND_RESONANT // do a resonant filter on the noise
1153 nextSample = (double)( a0 * nextSample - b1 * gY0 - b2 * gY1 );
1154
1155 gY1 = gY0;
1156 gY0 = nextSample;
1157#endif
1158
1159 nextSample *= gCurrentGain;
1160
1161 MIXBUFFERFORMAT sample;
1162
1163 sample = llfloor(((F32)nextSample*32768.f*(1.0f - gCurrentPanGainR))+0.5f);
1164 *(MIXBUFFERFORMAT*)cursamplep = clipSample((*(MIXBUFFERFORMAT*)cursamplep) + sample, -32768, 32767);
1165 cursamplep += wordsize;
1166
1167 sample = llfloor(((F32)nextSample*32768.f*gCurrentPanGainR)+0.5f);
1168 *(MIXBUFFERFORMAT*)cursamplep = clipSample((*(MIXBUFFERFORMAT*)cursamplep) + sample, -32768, 32767);
1169 cursamplep += wordsize;
1170 }
1171
1172 return newbuffer;
1173}
1174
1175#endif // LL_FMOD
1176