aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llaudio
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
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 'linden/indra/llaudio')
-rw-r--r--linden/indra/llaudio/audioengine.cpp1672
-rw-r--r--linden/indra/llaudio/audioengine.h428
-rw-r--r--linden/indra/llaudio/audioengine_fmod.cpp1176
-rw-r--r--linden/indra/llaudio/audioengine_fmod.h158
-rw-r--r--linden/indra/llaudio/files.lst7
-rw-r--r--linden/indra/llaudio/listener.cpp153
-rw-r--r--linden/indra/llaudio/listener.h78
-rw-r--r--linden/indra/llaudio/listener_ds3d.h71
-rw-r--r--linden/indra/llaudio/listener_fmod.cpp145
-rw-r--r--linden/indra/llaudio/listener_fmod.h66
-rw-r--r--linden/indra/llaudio/listener_openal.h56
-rw-r--r--linden/indra/llaudio/llaudio.vcproj223
-rw-r--r--linden/indra/llaudio/llaudiodecodemgr.cpp635
-rw-r--r--linden/indra/llaudio/llaudiodecodemgr.h66
-rw-r--r--linden/indra/llaudio/vorbisdecode.cpp321
-rw-r--r--linden/indra/llaudio/vorbisdecode.h37
-rw-r--r--linden/indra/llaudio/vorbisencode.cpp506
-rw-r--r--linden/indra/llaudio/vorbisencode.h49
18 files changed, 5847 insertions, 0 deletions
diff --git a/linden/indra/llaudio/audioengine.cpp b/linden/indra/llaudio/audioengine.cpp
new file mode 100644
index 0000000..46e621a
--- /dev/null
+++ b/linden/indra/llaudio/audioengine.cpp
@@ -0,0 +1,1672 @@
1/**
2 * @file audioengine.cpp
3 * @brief implementation of LLAudioEngine class abstracting the Open
4 * AL audio support
5 *
6 * Copyright (c) 2000-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#if LL_LINUX
37#include <time.h>
38#endif
39
40//#include "message.h"
41
42#include "audioengine.h"
43
44#include "llerror.h"
45#include "llmath.h"
46
47#include "sound_ids.h" // temporary hack for min/max distances
48
49#include "llvfs.h"
50#include "lldir.h"
51#include "llaudiodecodemgr.h"
52#include "llassetstorage.h"
53
54// necessary for grabbing sounds from sim (implemented in viewer)
55extern void request_sound(const LLUUID &sound_guid);
56
57LLAudioEngine* gAudiop = NULL;
58
59// Maximum amount of time we wait for a transfer to complete before starting
60// off another one.
61const F32 MAX_CURRENT_TRANSFER_TIME = 60.f;
62
63//
64// LLAudioEngine implementation
65//
66
67
68LLAudioEngine::LLAudioEngine()
69{
70}
71
72
73LLAudioEngine::~LLAudioEngine()
74{
75}
76
77
78BOOL LLAudioEngine::init(const S32 num_channels, void* userdata)
79{
80 mMuted = FALSE;
81 mMasterGain = 1.f;
82 mInternetStreamGain = 0.125f;
83 mUserData = userdata;
84 mLastStatus = 0;
85
86 mNumChannels = num_channels;
87
88
89 mMaxWindGain = 1.0;
90
91 mEnableWind = FALSE;
92
93 S32 i;
94 for (i = 0; i < MAX_BUFFERS; i++)
95 {
96 mBuffers[i] = NULL;
97 }
98 for (i = 0; i < num_channels; i++)
99 {
100 mChannels[i] = NULL;
101 }
102
103 allocateListener();
104
105 // Initialize the decode manager
106 gAudioDecodeMgrp = new LLAudioDecodeMgr;
107
108 return TRUE;
109}
110
111
112void LLAudioEngine::shutdown()
113{
114 // Clean up decode manager
115 delete gAudioDecodeMgrp;
116 gAudioDecodeMgrp = NULL;
117
118 // Clean up audio sources
119 source_map::iterator iter_src;
120 for (iter_src = mAllSources.begin(); iter_src != mAllSources.end(); iter_src++)
121 {
122 delete iter_src->second;
123 }
124
125
126 // Clean up audio data
127 data_map::iterator iter_data;
128 for (iter_data = mAllData.begin(); iter_data != mAllData.end(); iter_data++)
129 {
130 delete iter_data->second;
131 }
132
133
134 // Clean up channels
135 S32 i;
136 if (mChannels)
137 {
138 for (i = 0; i < MAX_CHANNELS; i++)
139 {
140 if (mChannels[i])
141 {
142 delete mChannels[i];
143 mChannels[i] = NULL;
144 }
145 }
146 }
147
148 // Clean up buffers
149 if (mBuffers)
150 {
151 for (i = 0; i < MAX_BUFFERS; i++)
152 {
153 if (mBuffers[i])
154 {
155 delete mBuffers[i];
156 mBuffers[i] = NULL;
157 }
158 }
159 }
160}
161
162
163void LLAudioEngine::updateChannels()
164{
165 S32 i;
166 for (i = 0; i < MAX_CHANNELS; i++)
167 {
168 if (mChannels[i])
169 {
170 mChannels[i]->updateBuffer();
171 mChannels[i]->update3DPosition();
172 mChannels[i]->updateLoop();
173 }
174 }
175}
176
177static const F32 default_max_decode_time = .002f; // 2 ms
178void LLAudioEngine::idle(F32 max_decode_time)
179{
180 if (max_decode_time <= 0.f)
181 {
182 max_decode_time = default_max_decode_time;
183 }
184
185 // "Update" all of our audio sources, clean up dead ones.
186 // Primarily does position updating, cleanup of unused audio sources.
187 // Also does regeneration of the current priority of each audio source.
188
189 if (getMuted())
190 {
191 setInternalGain(0.f);
192 }
193 else
194 {
195 setInternalGain(getMasterGain());
196 }
197
198 S32 i;
199 for (i = 0; i < MAX_BUFFERS; i++)
200 {
201 if (mBuffers[i])
202 {
203 mBuffers[i]->mInUse = FALSE;
204 }
205 }
206
207 F32 max_priority = -1.f;
208 LLAudioSource *max_sourcep = NULL; // Maximum priority source without a channel
209 source_map::iterator iter;
210 for (iter = mAllSources.begin(); iter != mAllSources.end();)
211 {
212 LLAudioSource *sourcep = iter->second;
213
214 // Update this source
215 sourcep->update();
216 sourcep->updatePriority();
217
218 if (sourcep->isDone())
219 {
220 // The source is done playing, clean it up.
221 delete sourcep;
222 mAllSources.erase(iter++);
223 continue;
224 }
225
226 if (!sourcep->getChannel() && sourcep->getCurrentBuffer())
227 {
228 // We could potentially play this sound if its priority is high enough.
229 if (sourcep->getPriority() > max_priority)
230 {
231 max_priority = sourcep->getPriority();
232 max_sourcep = sourcep;
233 }
234 }
235
236 // Move on to the next source
237 iter++;
238 }
239
240 // Now, do priority-based organization of audio sources.
241 // All channels used, check priorities.
242 // Find channel with lowest priority
243 if (max_sourcep)
244 {
245 LLAudioChannel *channelp = getFreeChannel(max_priority);
246 if (channelp)
247 {
248 //llinfos << "Replacing source in channel due to priority!" << llendl;
249 max_sourcep->setChannel(channelp);
250 channelp->setSource(max_sourcep);
251 if (max_sourcep->isSyncSlave())
252 {
253 // A sync slave, it doesn't start playing until it's synced up with the master.
254 // Flag this channel as waiting for sync, and return true.
255 channelp->setWaiting(TRUE);
256 }
257 else
258 {
259 channelp->setWaiting(FALSE);
260 channelp->play();
261 }
262 }
263 }
264
265
266 // Do this BEFORE we update the channels
267 // Update the channels to sync up with any changes that the source made,
268 // such as changing what sound was playing.
269 updateChannels();
270
271 // Update queued sounds (switch to next queued data if the current has finished playing)
272 for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
273 {
274 // This is lame, instead of this I could actually iterate through all the sources
275 // attached to each channel, since only those with active channels
276 // can have anything interesting happen with their queue? (Maybe not true)
277 LLAudioSource *sourcep = iter->second;
278 if (!sourcep->mQueuedDatap)
279 {
280 // Nothing queued, so we don't care.
281 continue;
282 }
283
284 LLAudioChannel *channelp = sourcep->getChannel();
285 if (!channelp)
286 {
287 // This sound isn't playing, so we just process move the queue
288 sourcep->mCurrentDatap = sourcep->mQueuedDatap;
289 sourcep->mQueuedDatap = NULL;
290
291 // Reset the timer so the source doesn't die.
292 sourcep->mAgeTimer.reset();
293 // Make sure we have the buffer set up if we just decoded the data
294 if (sourcep->mCurrentDatap)
295 {
296 updateBufferForData(sourcep->mCurrentDatap);
297 }
298
299 // Actually play the associated data.
300 sourcep->setupChannel();
301 channelp = sourcep->getChannel();
302 if (channelp)
303 {
304 channelp->updateBuffer();
305 sourcep->getChannel()->play();
306 }
307 continue;
308 }
309 else
310 {
311 // Check to see if the current sound is done playing, or looped.
312 if (!channelp->isPlaying())
313 {
314 sourcep->mCurrentDatap = sourcep->mQueuedDatap;
315 sourcep->mQueuedDatap = NULL;
316
317 // Reset the timer so the source doesn't die.
318 sourcep->mAgeTimer.reset();
319
320 // Make sure we have the buffer set up if we just decoded the data
321 if (sourcep->mCurrentDatap)
322 {
323 updateBufferForData(sourcep->mCurrentDatap);
324 }
325
326 // Actually play the associated data.
327 sourcep->setupChannel();
328 channelp->updateBuffer();
329 sourcep->getChannel()->play();
330 }
331 else if (sourcep->isLoop())
332 {
333 // It's a loop, we need to check and see if we're done with it.
334 if (channelp->mLoopedThisFrame)
335 {
336 sourcep->mCurrentDatap = sourcep->mQueuedDatap;
337 sourcep->mQueuedDatap = NULL;
338
339 // Actually, should do a time sync so if we're a loop master/slave
340 // we don't drift away.
341 sourcep->setupChannel();
342 sourcep->getChannel()->play();
343 }
344 }
345 }
346 }
347
348 // Lame, update the channels AGAIN.
349 // Update the channels to sync up with any changes that the source made,
350 // such as changing what sound was playing.
351 updateChannels();
352
353 // Hack! For now, just use a global sync master;
354 LLAudioSource *sync_masterp = NULL;
355 LLAudioChannel *master_channelp = NULL;
356 F32 max_sm_priority = -1.f;
357 for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
358 {
359 LLAudioSource *sourcep = iter->second;
360 if (sourcep->isSyncMaster())
361 {
362 if (sourcep->getPriority() > max_sm_priority)
363 {
364 sync_masterp = sourcep;
365 master_channelp = sync_masterp->getChannel();
366 max_sm_priority = sourcep->getPriority();
367 }
368 }
369 }
370
371 if (master_channelp && master_channelp->mLoopedThisFrame)
372 {
373 // Synchronize loop slaves with their masters
374 // Update queued sounds (switch to next queued data if the current has finished playing)
375 for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter)
376 {
377 LLAudioSource *sourcep = iter->second;
378
379 if (!sourcep->isSyncSlave())
380 {
381 // Not a loop slave, we don't need to do anything
382 continue;
383 }
384
385 LLAudioChannel *channelp = sourcep->getChannel();
386 if (!channelp)
387 {
388 // Not playing, don't need to bother.
389 continue;
390 }
391
392 if (!channelp->isPlaying())
393 {
394 // Now we need to check if our loop master has just looped, and
395 // start playback if that's the case.
396 if (sync_masterp->getChannel())
397 {
398 channelp->playSynced(master_channelp);
399 channelp->setWaiting(FALSE);
400 }
401 }
402 }
403 }
404
405 // Sync up everything that the audio engine needs done.
406 commitDeferredChanges();
407
408 // Flush unused buffers that are stale enough
409 for (i = 0; i < MAX_BUFFERS; i++)
410 {
411 if (mBuffers[i])
412 {
413 if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f)
414 {
415 //llinfos << "Flushing unused buffer!" << llendl;
416 mBuffers[i]->mAudioDatap->mBufferp = NULL;
417 delete mBuffers[i];
418 mBuffers[i] = NULL;
419 }
420 }
421 }
422
423
424 // Clear all of the looped flags for the channels
425 for (i = 0; i < MAX_CHANNELS; i++)
426 {
427 if (mChannels[i])
428 {
429 mChannels[i]->mLoopedThisFrame = FALSE;
430 }
431 }
432
433 // Decode audio files
434 gAudioDecodeMgrp->processQueue(max_decode_time);
435
436 // Call this every frame, just in case we somehow
437 // missed picking it up in all the places that can add
438 // or request new data.
439 startNextTransfer();
440}
441
442BOOL LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid)
443{
444 if (!adp)
445 {
446 return FALSE;
447 }
448
449 // Update the audio buffer first - load a sound if we have it.
450 // Note that this could potentially cause us to waste time updating buffers
451 // for sounds that actually aren't playing, although this should be mitigated
452 // by the fact that we limit the number of buffers, and we flush buffers based
453 // on priority.
454 if (!adp->getBuffer())
455 {
456 if (adp->hasDecodedData())
457 {
458 adp->load();
459 }
460 else if (adp->hasLocalData())
461 {
462 if (audio_uuid.notNull())
463 {
464 gAudioDecodeMgrp->addDecodeRequest(audio_uuid);
465 }
466 }
467 else
468 {
469 return FALSE;
470 }
471 }
472 return TRUE;
473}
474
475
476void LLAudioEngine::enableWind(BOOL enable)
477{
478 if (enable && (!mEnableWind))
479 {
480 initWind();
481 mEnableWind = enable;
482 }
483 else if (mEnableWind && (!enable))
484 {
485 mEnableWind = enable;
486 cleanupWind();
487 }
488}
489
490
491LLAudioBuffer *LLAudioEngine::getFreeBuffer()
492{
493 S32 i;
494 for (i = 0; i < MAX_BUFFERS; i++)
495 {
496 if (!mBuffers[i])
497 {
498 mBuffers[i] = createBuffer();
499 return mBuffers[i];
500 }
501 }
502
503
504 // Grab the oldest unused buffer
505 F32 max_age = -1.f;
506 S32 buffer_id = -1;
507 for (i = 0; i < MAX_BUFFERS; i++)
508 {
509 if (mBuffers[i])
510 {
511 if (!mBuffers[i]->mInUse)
512 {
513 if (mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > max_age)
514 {
515 max_age = mBuffers[i]->mLastUseTimer.getElapsedTimeF32();
516 buffer_id = i;
517 }
518 }
519 }
520 }
521
522 if (buffer_id >= 0)
523 {
524 llinfos << "Taking over unused buffer " << buffer_id << llendl;
525 //llinfos << "Flushing unused buffer!" << llendl;
526 mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL;
527 delete mBuffers[buffer_id];
528 mBuffers[buffer_id] = createBuffer();
529 return mBuffers[buffer_id];
530 }
531 return NULL;
532}
533
534
535LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority)
536{
537 S32 i;
538 for (i = 0; i < mNumChannels; i++)
539 {
540 if (!mChannels[i])
541 {
542 // No channel allocated here, use it.
543 mChannels[i] = createChannel();
544 return mChannels[i];
545 }
546 else
547 {
548 // Channel is allocated but not playing right now, use it.
549 if (!mChannels[i]->isPlaying() && !mChannels[i]->isWaiting())
550 {
551 mChannels[i]->cleanup();
552 if (mChannels[i]->getSource())
553 {
554 mChannels[i]->getSource()->setChannel(NULL);
555 }
556 return mChannels[i];
557 }
558 }
559 }
560
561 // All channels used, check priorities.
562 // Find channel with lowest priority and see if we want to replace it.
563 F32 min_priority = 10000.f;
564 LLAudioChannel *min_channelp = NULL;
565
566 for (i = 0; i < mNumChannels; i++)
567 {
568 LLAudioChannel *channelp = mChannels[i];
569 LLAudioSource *sourcep = channelp->getSource();
570 if (sourcep->getPriority() < min_priority)
571 {
572 min_channelp = channelp;
573 min_priority = sourcep->getPriority();
574 }
575 }
576
577 if (min_priority > priority)
578 {
579 // All playing channels have higher priority, return.
580 return NULL;
581 }
582
583 // Flush the minimum priority channel, and return it.
584 min_channelp->cleanup();
585 min_channelp->getSource()->setChannel(NULL);
586 return min_channelp;
587}
588
589
590void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp)
591{
592 S32 i;
593 for (i = 0; i < MAX_BUFFERS; i++)
594 {
595 if (mBuffers[i] == bufferp)
596 {
597 delete mBuffers[i];
598 mBuffers[i] = NULL;
599 }
600 }
601}
602
603
604BOOL LLAudioEngine::preloadSound(const LLUUID &uuid)
605{
606 gAudiop->getAudioData(uuid); // We don't care about the return value, this is just to make sure
607 // that we have an entry, which will mean that the audio engine knows about this
608
609 if (gAudioDecodeMgrp->addDecodeRequest(uuid))
610 {
611 // This means that we do have a local copy, and we're working on decoding it.
612 return TRUE;
613 }
614
615 // At some point we need to have the audio/asset system check the static VFS
616 // before it goes off and fetches stuff from the server.
617 //llwarns << "Used internal preload for non-local sound" << llendl;
618 return FALSE;
619}
620
621
622BOOL LLAudioEngine::isWindEnabled()
623{
624 return mEnableWind;
625}
626
627
628void LLAudioEngine::setMuted(BOOL muted)
629{
630 mMuted = muted;
631 enableWind(!mMuted);
632}
633
634
635void LLAudioEngine::setMasterGain(const F32 gain)
636{
637 mMasterGain = gain;
638 setInternalGain(gain);
639}
640
641F32 LLAudioEngine::getMasterGain()
642{
643 return mMasterGain;
644}
645
646F32 LLAudioEngine::getInternetStreamGain()
647{
648 return mInternetStreamGain;
649}
650
651void LLAudioEngine::setMaxWindGain(F32 gain)
652{
653 mMaxWindGain = gain;
654}
655
656
657F64 LLAudioEngine::mapWindVecToGain(LLVector3 wind_vec)
658{
659 F64 gain = 0.0;
660
661 gain = wind_vec.magVec();
662
663 if (gain)
664 {
665 if (gain > 20)
666 {
667 gain = 20;
668 }
669 gain = gain/20.0;
670 }
671
672 return (gain);
673}
674
675
676F64 LLAudioEngine::mapWindVecToPitch(LLVector3 wind_vec)
677{
678 LLVector3 listen_right;
679 F64 theta;
680
681 // Wind frame is in listener-relative coordinates
682 LLVector3 norm_wind = wind_vec;
683 norm_wind.normVec();
684 listen_right.setVec(1.0,0.0,0.0);
685
686 // measure angle between wind vec and listener right axis (on 0,PI)
687 theta = acos(norm_wind * listen_right);
688
689 // put it on 0, 1
690 theta /= F_PI;
691
692 // put it on [0, 0.5, 0]
693 if (theta > 0.5) theta = 1.0-theta;
694 if (theta < 0) theta = 0;
695
696 return (theta);
697}
698
699
700F64 LLAudioEngine::mapWindVecToPan(LLVector3 wind_vec)
701{
702 LLVector3 listen_right;
703 F64 theta;
704
705 // Wind frame is in listener-relative coordinates
706 listen_right.setVec(1.0,0.0,0.0);
707
708 LLVector3 norm_wind = wind_vec;
709 norm_wind.normVec();
710
711 // measure angle between wind vec and listener right axis (on 0,PI)
712 theta = acos(norm_wind * listen_right);
713
714 // put it on 0, 1
715 theta /= F_PI;
716
717 return (theta);
718}
719
720
721void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const LLVector3d &pos_global)
722{
723 // Create a new source (since this can't be associated with an existing source.
724 //llinfos << "Localized: " << audio_uuid << llendl;
725
726 if (mMuted)
727 {
728 return;
729 }
730
731 LLUUID source_id;
732 source_id.generate();
733
734 LLAudioSource *asp = new LLAudioSource(source_id, owner_id, gain);
735 gAudiop->addAudioSource(asp);
736 if (pos_global.isExactlyZero())
737 {
738 asp->setAmbient(TRUE);
739 }
740 else
741 {
742 asp->setPositionGlobal(pos_global);
743 }
744 asp->updatePriority();
745 asp->play(audio_uuid);
746}
747
748
749void LLAudioEngine::setListenerPos(LLVector3 aVec)
750{
751 mListenerp->setPosition(aVec);
752}
753
754
755LLVector3 LLAudioEngine::getListenerPos()
756{
757 if (mListenerp)
758 {
759 return(mListenerp->getPosition());
760 }
761 else
762 {
763 return(LLVector3::zero);
764 }
765}
766
767
768void LLAudioEngine::setListenerVelocity(LLVector3 aVec)
769{
770 mListenerp->setVelocity(aVec);
771}
772
773
774void LLAudioEngine::translateListener(LLVector3 aVec)
775{
776 mListenerp->translate(aVec);
777}
778
779
780void LLAudioEngine::orientListener(LLVector3 up, LLVector3 at)
781{
782 mListenerp->orient(up, at);
783}
784
785
786void LLAudioEngine::setListener(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at)
787{
788 mListenerp->set(pos,vel,up,at);
789}
790
791
792void LLAudioEngine::setDopplerFactor(F32 factor)
793{
794 if (mListenerp)
795 {
796 mListenerp->setDopplerFactor(factor);
797 }
798}
799
800
801F32 LLAudioEngine::getDopplerFactor()
802{
803 if (mListenerp)
804 {
805 return mListenerp->getDopplerFactor();
806 }
807 else
808 {
809 return 0.f;
810 }
811}
812
813
814void LLAudioEngine::setDistanceFactor(F32 factor)
815{
816 if (mListenerp)
817 {
818 mListenerp->setDistanceFactor(factor);
819 }
820}
821
822
823F32 LLAudioEngine::getDistanceFactor()
824{
825 if (mListenerp)
826 {
827 return mListenerp->getDistanceFactor();
828 }
829 else
830 {
831 return 0.f;
832 }
833}
834
835
836void LLAudioEngine::setRolloffFactor(F32 factor)
837{
838 if (mListenerp)
839 {
840 mListenerp->setRolloffFactor(factor);
841 }
842}
843
844
845F32 LLAudioEngine::getRolloffFactor()
846{
847 if (mListenerp)
848 {
849 return mListenerp->getRolloffFactor();
850 }
851 else
852 {
853 return 0.f;
854 }
855}
856
857
858void LLAudioEngine::commitDeferredChanges()
859{
860 mListenerp->commitDeferredChanges();
861}
862
863
864LLAudioSource *LLAudioEngine::findAudioSource(const LLUUID &source_id)
865{
866 source_map::iterator iter;
867 iter = mAllSources.find(source_id);
868
869 if (iter == mAllSources.end())
870 {
871 return NULL;
872 }
873 else
874 {
875 return iter->second;
876 }
877}
878
879
880LLAudioData *LLAudioEngine::getAudioData(const LLUUID &audio_uuid)
881{
882 data_map::iterator iter;
883 iter = mAllData.find(audio_uuid);
884 if (iter == mAllData.end())
885 {
886 // Create the new audio data
887 LLAudioData *adp = new LLAudioData(audio_uuid);
888 mAllData[audio_uuid] = adp;
889 return adp;
890 }
891 else
892 {
893 return iter->second;
894 }
895}
896
897void LLAudioEngine::addAudioSource(LLAudioSource *asp)
898{
899 mAllSources[asp->getID()] = asp;
900}
901
902
903void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp)
904{
905 source_map::iterator iter;
906 iter = mAllSources.find(asp->getID());
907 if (iter == mAllSources.end())
908 {
909 llwarns << "Cleaning up unknown audio source!" << llendl;
910 return;
911 }
912 delete asp;
913 mAllSources.erase(iter);
914}
915
916
917BOOL LLAudioEngine::hasDecodedFile(const LLUUID &uuid)
918{
919 char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
920 uuid.toString(uuid_str);
921
922 std::string wav_path;
923 wav_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str);
924 wav_path += ".dsf";
925
926 if (gDirUtilp->fileExists(wav_path))
927 {
928 return TRUE;
929 }
930 else
931 {
932 return FALSE;
933 }
934}
935
936
937BOOL LLAudioEngine::hasLocalFile(const LLUUID &uuid)
938{
939 // See if it's in the VFS.
940 return gVFS->getExists(uuid, LLAssetType::AT_SOUND);
941}
942
943
944void LLAudioEngine::startNextTransfer()
945{
946 //llinfos << "LLAudioEngine::startNextTransfer()" << llendl;
947 if (mCurrentTransfer.notNull() || getMuted())
948 {
949 //llinfos << "Transfer in progress, aborting" << llendl;
950 return;
951 }
952
953 // Get the ID for the next asset that we want to transfer.
954 // Pick one in the following order:
955 LLUUID asset_id;
956 S32 i;
957 LLAudioSource *asp = NULL;
958 LLAudioData *adp = NULL;
959 data_map::iterator data_iter;
960
961 // Check all channels for currently playing sounds.
962 F32 max_pri = -1.f;
963 for (i = 0; i < MAX_CHANNELS; i++)
964 {
965 if (!mChannels[i])
966 {
967 continue;
968 }
969
970 asp = mChannels[i]->getSource();
971 if (!asp)
972 {
973 continue;
974 }
975 if (asp->getPriority() <= max_pri)
976 {
977 continue;
978 }
979
980 if (asp->getPriority() <= max_pri)
981 {
982 continue;
983 }
984
985 adp = asp->getCurrentData();
986 if (!adp)
987 {
988 continue;
989 }
990
991 if (!adp->hasLocalData() && adp->hasValidData())
992 {
993 asset_id = adp->getID();
994 max_pri = asp->getPriority();
995 }
996 }
997
998 // Check all channels for currently queued sounds.
999 if (asset_id.isNull())
1000 {
1001 max_pri = -1.f;
1002 for (i = 0; i < MAX_CHANNELS; i++)
1003 {
1004 if (!mChannels[i])
1005 {
1006 continue;
1007 }
1008
1009 LLAudioSource *asp;
1010 asp = mChannels[i]->getSource();
1011 if (!asp)
1012 {
1013 continue;
1014 }
1015
1016 if (asp->getPriority() <= max_pri)
1017 {
1018 continue;
1019 }
1020
1021 adp = asp->getQueuedData();
1022 if (!adp)
1023 {
1024 continue;
1025 }
1026
1027 if (!adp->hasLocalData() && adp->hasValidData())
1028 {
1029 asset_id = adp->getID();
1030 max_pri = asp->getPriority();
1031 }
1032 }
1033 }
1034
1035 // Check all live channels for other sounds (preloads).
1036 if (asset_id.isNull())
1037 {
1038 max_pri = -1.f;
1039 for (i = 0; i < MAX_CHANNELS; i++)
1040 {
1041 if (!mChannels[i])
1042 {
1043 continue;
1044 }
1045
1046 LLAudioSource *asp;
1047 asp = mChannels[i]->getSource();
1048 if (!asp)
1049 {
1050 continue;
1051 }
1052
1053 if (asp->getPriority() <= max_pri)
1054 {
1055 continue;
1056 }
1057
1058
1059 for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++)
1060 {
1061 LLAudioData *adp = data_iter->second;
1062 if (!adp)
1063 {
1064 continue;
1065 }
1066
1067 if (!adp->hasLocalData() && adp->hasValidData())
1068 {
1069 asset_id = adp->getID();
1070 max_pri = asp->getPriority();
1071 }
1072 }
1073 }
1074 }
1075
1076 // Check all sources
1077 if (asset_id.isNull())
1078 {
1079 max_pri = -1.f;
1080 source_map::iterator source_iter;
1081 for (source_iter = mAllSources.begin(); source_iter != mAllSources.end(); source_iter++)
1082 {
1083 asp = source_iter->second;
1084 if (!asp)
1085 {
1086 continue;
1087 }
1088
1089 if (asp->getPriority() <= max_pri)
1090 {
1091 continue;
1092 }
1093
1094 adp = asp->getCurrentData();
1095 if (adp && !adp->hasLocalData() && adp->hasValidData())
1096 {
1097 asset_id = adp->getID();
1098 max_pri = asp->getPriority();
1099 continue;
1100 }
1101
1102 adp = asp->getQueuedData();
1103 if (adp && !adp->hasLocalData() && adp->hasValidData())
1104 {
1105 asset_id = adp->getID();
1106 max_pri = asp->getPriority();
1107 continue;
1108 }
1109
1110 for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++)
1111 {
1112 LLAudioData *adp = data_iter->second;
1113 if (!adp)
1114 {
1115 continue;
1116 }
1117
1118 if (!adp->hasLocalData() && adp->hasValidData())
1119 {
1120 asset_id = adp->getID();
1121 max_pri = asp->getPriority();
1122 break;
1123 }
1124 }
1125 }
1126 }
1127
1128 if (asset_id.notNull())
1129 {
1130 llinfos << "Getting asset data for: " << asset_id << llendl;
1131 gAudiop->mCurrentTransfer = asset_id;
1132 gAudiop->mCurrentTransferTimer.reset();
1133 gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND,
1134 assetCallback, NULL);
1135 }
1136 else
1137 {
1138 //llinfos << "No pending transfers?" << llendl;
1139 }
1140}
1141
1142
1143// static
1144void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code)
1145{
1146 if (result_code)
1147 {
1148 llinfos << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl;
1149 // Need to mark data as bad to avoid constant rerequests.
1150 LLAudioData *adp = gAudiop->getAudioData(uuid);
1151 if (adp)
1152 {
1153 adp->setHasValidData(FALSE);
1154 adp->setHasLocalData(FALSE);
1155 adp->setHasDecodedData(FALSE);
1156 }
1157 }
1158 else
1159 {
1160 LLAudioData *adp = gAudiop->getAudioData(uuid);
1161 if (!adp)
1162 {
1163 // Should never happen
1164 llwarns << "Got asset callback without audio data for " << uuid << llendl;
1165 }
1166 else
1167 {
1168 adp->setHasValidData(TRUE);
1169 adp->setHasLocalData(TRUE);
1170 gAudioDecodeMgrp->addDecodeRequest(uuid);
1171 }
1172 }
1173 gAudiop->mCurrentTransfer = LLUUID::null;
1174 gAudiop->startNextTransfer();
1175}
1176
1177
1178//
1179// LLAudioSource implementation
1180//
1181
1182
1183LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 gain)
1184: mID(id),
1185 mOwnerID(owner_id),
1186 mPriority(0.f),
1187 mGain(gain),
1188 mAmbient(FALSE),
1189 mLoop(FALSE),
1190 mSyncMaster(FALSE),
1191 mSyncSlave(FALSE),
1192 mQueueSounds(FALSE),
1193 mPlayedOnce(FALSE),
1194 mChannelp(NULL),
1195 mCurrentDatap(NULL),
1196 mQueuedDatap(NULL)
1197{
1198}
1199
1200
1201LLAudioSource::~LLAudioSource()
1202{
1203 if (mChannelp)
1204 {
1205 // Stop playback of this sound
1206 mChannelp->setSource(NULL);
1207 mChannelp = NULL;
1208 }
1209}
1210
1211
1212void LLAudioSource::setChannel(LLAudioChannel *channelp)
1213{
1214 if (channelp == mChannelp)
1215 {
1216 return;
1217 }
1218
1219 mChannelp = channelp;
1220}
1221
1222
1223void LLAudioSource::update()
1224{
1225 if (!getCurrentBuffer())
1226 {
1227 if (getCurrentData())
1228 {
1229 // Hack - try and load the sound. Will do this as a callback
1230 // on decode later.
1231 if (getCurrentData()->load())
1232 {
1233 play(getCurrentData()->getID());
1234 }
1235 }
1236 }
1237}
1238
1239void LLAudioSource::updatePriority()
1240{
1241 if (isAmbient())
1242 {
1243 mPriority = 1.f;
1244 }
1245 else
1246 {
1247 // Priority is based on distance
1248 LLVector3 dist_vec;
1249 dist_vec.setVec(getPositionGlobal());
1250 dist_vec -= gAudiop->getListenerPos();
1251 F32 dist_squared = llmax(1.f, dist_vec.magVecSquared());
1252
1253 mPriority = mGain / dist_squared;
1254 }
1255}
1256
1257BOOL LLAudioSource::setupChannel()
1258{
1259 LLAudioData *adp = getCurrentData();
1260
1261 if (!adp->getBuffer())
1262 {
1263 // We're not ready to play back the sound yet, so don't try and allocate a channel for it.
1264 //llwarns << "Aborting, no buffer" << llendl;
1265 return FALSE;
1266 }
1267
1268
1269 if (!mChannelp)
1270 {
1271 // Update the priority, in case we need to push out another channel.
1272 updatePriority();
1273
1274 setChannel(gAudiop->getFreeChannel(getPriority()));
1275 }
1276
1277 if (!mChannelp)
1278 {
1279 // Ugh, we don't have any free channels.
1280 // Now we have to reprioritize.
1281 // For now, just don't play the sound.
1282 //llwarns << "Aborting, no free channels" << llendl;
1283 return FALSE;
1284 }
1285
1286 mChannelp->setSource(this);
1287 return TRUE;
1288}
1289
1290
1291BOOL LLAudioSource::play(const LLUUID &audio_uuid)
1292{
1293 if (audio_uuid.isNull())
1294 {
1295 if (getChannel())
1296 {
1297 getChannel()->setSource(NULL);
1298 setChannel(NULL);
1299 addAudioData(NULL, TRUE);
1300 }
1301 }
1302 // Reset our age timeout if someone attempts to play the source.
1303 mAgeTimer.reset();
1304
1305 LLAudioData *adp = gAudiop->getAudioData(audio_uuid);
1306
1307 BOOL has_buffer = gAudiop->updateBufferForData(adp, audio_uuid);
1308
1309
1310 addAudioData(adp);
1311
1312 if (!has_buffer)
1313 {
1314 // Don't bother trying to set up a channel or anything, we don't have an audio buffer.
1315 return FALSE;
1316 }
1317
1318 if (!setupChannel())
1319 {
1320 return FALSE;
1321 }
1322
1323 if (isSyncSlave())
1324 {
1325 // A sync slave, it doesn't start playing until it's synced up with the master.
1326 // Flag this channel as waiting for sync, and return true.
1327 getChannel()->setWaiting(TRUE);
1328 return TRUE;
1329 }
1330
1331 getChannel()->play();
1332 return TRUE;
1333}
1334
1335
1336BOOL LLAudioSource::isDone()
1337{
1338 const F32 MAX_AGE = 60.f;
1339 const F32 MAX_UNPLAYED_AGE = 15.f;
1340 if (isLoop())
1341 {
1342 // Looped sources never die on their own.
1343 return FALSE;
1344 }
1345
1346
1347 if (hasPendingPreloads())
1348 {
1349 return FALSE;
1350 }
1351
1352 if (mQueuedDatap)
1353 {
1354 // Don't kill this sound if we've got something queued up to play.
1355 return FALSE;
1356 }
1357
1358 F32 elapsed = mAgeTimer.getElapsedTimeF32();
1359
1360 // This is a single-play source
1361 if (!mChannelp)
1362 {
1363 if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce)
1364 {
1365 // We don't have a channel assigned, and it's been
1366 // over 5 seconds since we tried to play it. Don't bother.
1367 //llinfos << "No channel assigned, source is done" << llendl;
1368 return TRUE;
1369 }
1370 else
1371 {
1372 return FALSE;
1373 }
1374 }
1375
1376 if (mChannelp->isPlaying())
1377 {
1378 if (elapsed > MAX_AGE)
1379 {
1380 // Arbitarily cut off non-looped sounds when they're 20 seconds old.
1381 return TRUE;
1382 }
1383 else
1384 {
1385 // Sound is still playing and we haven't timed out, don't kill it.
1386 return FALSE;
1387 }
1388 }
1389
1390 if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce)
1391 {
1392 // The sound isn't playing back after 5 seconds or we're already done playing it, kill it.
1393 return TRUE;
1394 }
1395
1396 return FALSE;
1397}
1398
1399
1400void LLAudioSource::addAudioData(LLAudioData *adp, const BOOL set_current)
1401{
1402 // Only handle a single piece of audio data associated with a source right now,
1403 // until I implement prefetch.
1404 if (set_current)
1405 {
1406 if (!mCurrentDatap)
1407 {
1408 mCurrentDatap = adp;
1409 if (mChannelp)
1410 {
1411 mChannelp->updateBuffer();
1412 mChannelp->play();
1413 }
1414
1415 // Make sure the audio engine knows that we want to request this sound.
1416 gAudiop->startNextTransfer();
1417 return;
1418 }
1419 else if (mQueueSounds)
1420 {
1421 // If we have current data, and we're queuing, put
1422 // the object onto the queue.
1423 if (mQueuedDatap)
1424 {
1425 // We only queue one sound at a time, and it's a FIFO.
1426 // Don't put it onto the queue.
1427 return;
1428 }
1429
1430 if (adp == mCurrentDatap && isLoop())
1431 {
1432 // No point in queueing the same sound if
1433 // we're looping.
1434 return;
1435 }
1436 mQueuedDatap = adp;
1437
1438 // Make sure the audio engine knows that we want to request this sound.
1439 gAudiop->startNextTransfer();
1440 }
1441 else
1442 {
1443 if (mCurrentDatap != adp)
1444 {
1445 // Right now, if we're currently playing this sound in a channel, we
1446 // update the buffer that the channel's associated with
1447 // and play it. This may not be the correct behavior.
1448 mCurrentDatap = adp;
1449 if (mChannelp)
1450 {
1451 mChannelp->updateBuffer();
1452 mChannelp->play();
1453 }
1454 // Make sure the audio engine knows that we want to request this sound.
1455 gAudiop->startNextTransfer();
1456 }
1457 }
1458 }
1459 else
1460 {
1461 // Add it to the preload list.
1462 mPreloadMap[adp->getID()] = adp;
1463 gAudiop->startNextTransfer();
1464 }
1465}
1466
1467
1468BOOL LLAudioSource::hasPendingPreloads() const
1469{
1470 // Check to see if we've got any preloads on deck for this source
1471 data_map::const_iterator iter;
1472 for (iter = mPreloadMap.begin(); iter != mPreloadMap.end(); iter++)
1473 {
1474 LLAudioData *adp = iter->second;
1475 if (!adp->hasDecodedData())
1476 {
1477 // This source is still waiting for a preload
1478 return TRUE;
1479 }
1480 }
1481
1482 return FALSE;
1483}
1484
1485
1486LLAudioData *LLAudioSource::getCurrentData()
1487{
1488 return mCurrentDatap;
1489}
1490
1491LLAudioData *LLAudioSource::getQueuedData()
1492{
1493 return mQueuedDatap;
1494}
1495
1496LLAudioBuffer *LLAudioSource::getCurrentBuffer()
1497{
1498 if (!mCurrentDatap)
1499 {
1500 return NULL;
1501 }
1502
1503 return mCurrentDatap->getBuffer();
1504}
1505
1506
1507
1508
1509//
1510// LLAudioChannel implementation
1511//
1512
1513
1514LLAudioChannel::LLAudioChannel() :
1515 mCurrentSourcep(NULL),
1516 mCurrentBufferp(NULL),
1517 mLoopedThisFrame(FALSE),
1518 mWaiting(FALSE)
1519{
1520}
1521
1522
1523LLAudioChannel::~LLAudioChannel()
1524{
1525 // Need to disconnect any sources which are using this channel.
1526 //llinfos << "Cleaning up audio channel" << llendl;
1527 if (mCurrentSourcep)
1528 {
1529 mCurrentSourcep->setChannel(NULL);
1530 }
1531 mCurrentBufferp = NULL;
1532}
1533
1534
1535void LLAudioChannel::setSource(LLAudioSource *sourcep)
1536{
1537 //llinfos << this << ": setSource(" << sourcep << ")" << llendl;
1538
1539 if (!sourcep)
1540 {
1541 // Clearing the source for this channel, don't need to do anything.
1542 //llinfos << "Clearing source for channel" << llendl;
1543 cleanup();
1544 mCurrentSourcep = NULL;
1545 mWaiting = FALSE;
1546 return;
1547 }
1548
1549 if (sourcep == mCurrentSourcep)
1550 {
1551 // Don't reallocate the channel, this will make FMOD goofy.
1552 //llinfos << "Calling setSource with same source!" << llendl;
1553 }
1554
1555 mCurrentSourcep = sourcep;
1556 updateBuffer();
1557 update3DPosition();
1558}
1559
1560
1561BOOL LLAudioChannel::updateBuffer()
1562{
1563 if (!mCurrentSourcep)
1564 {
1565 // This channel isn't associated with any source, nothing
1566 // to be updated
1567 return FALSE;
1568 }
1569
1570 LLAudioBuffer *bufferp = mCurrentSourcep->getCurrentBuffer();
1571 if (bufferp == mCurrentBufferp)
1572 {
1573 if (bufferp)
1574 {
1575 // The source hasn't changed what buffer it's playing
1576 bufferp->mLastUseTimer.reset();
1577 bufferp->mInUse = TRUE;
1578 }
1579 return FALSE;
1580 }
1581
1582 //
1583 // The source changed what buffer it's playing. Whe need to clean up the
1584 // existing fmod channel
1585 //
1586 cleanup();
1587
1588 mCurrentBufferp = bufferp;
1589 if (bufferp)
1590 {
1591 bufferp->mLastUseTimer.reset();
1592 bufferp->mInUse = TRUE;
1593 }
1594
1595 if (!mCurrentBufferp)
1596 {
1597 // There's no new buffer to be played, so we just abort.
1598 return FALSE;
1599 }
1600
1601 return TRUE;
1602}
1603
1604
1605
1606
1607//
1608// LLAudioData implementation
1609//
1610
1611
1612LLAudioData::LLAudioData(const LLUUID &uuid) :
1613 mID(uuid),
1614 mBufferp(NULL),
1615 mHasLocalData(FALSE),
1616 mHasDecodedData(FALSE),
1617 mHasValidData(TRUE)
1618{
1619 if (uuid.isNull())
1620 {
1621 // This is a null sound.
1622 return;
1623 }
1624
1625 if (gAudiop->hasDecodedFile(uuid))
1626 {
1627 // Already have a decoded version, don't need to decode it.
1628 mHasLocalData = TRUE;
1629 mHasDecodedData = TRUE;
1630 }
1631 else if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
1632 {
1633 mHasLocalData = TRUE;
1634 }
1635}
1636
1637
1638BOOL LLAudioData::load()
1639{
1640 // For now, just assume we're going to use one buffer per audiodata.
1641 if (mBufferp)
1642 {
1643 // We already have this sound in a buffer, don't do anything.
1644 llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl;
1645 return TRUE;
1646 }
1647
1648 mBufferp = gAudiop->getFreeBuffer();
1649 if (!mBufferp)
1650 {
1651 // No free buffers, abort.
1652 llinfos << "Not able to allocate a new audio buffer, aborting." << llendl;
1653 return FALSE;
1654 }
1655
1656 char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
1657 char wav_path[MAX_PATH]; /*Flawfinder: ignore*/
1658 mID.toString(uuid_str);
1659 snprintf(wav_path, MAX_PATH, "%s.dsf",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str()); /*Flawfinder: ignore*/
1660
1661 if (!mBufferp->loadWAV(wav_path))
1662 {
1663 // Hrm. Right now, let's unset the buffer, since it's empty.
1664 gAudiop->cleanupBuffer(mBufferp);
1665 mBufferp = NULL;
1666
1667 return FALSE;
1668 }
1669 mBufferp->mAudioDatap = this;
1670 return TRUE;
1671}
1672
diff --git a/linden/indra/llaudio/audioengine.h b/linden/indra/llaudio/audioengine.h
new file mode 100644
index 0000000..83f822b
--- /dev/null
+++ b/linden/indra/llaudio/audioengine.h
@@ -0,0 +1,428 @@
1/**
2 * @file audioengine.h
3 * @brief Definition of LLAudioEngine base class abstracting the audio support
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28
29#ifndef LL_AUDIOENGINE_H
30#define LL_AUDIOENGINE_H
31
32#include <list>
33#include <map>
34
35#ifndef LL_FMOD
36# define LL_FMOD 1
37#endif
38
39#include "listener.h"
40#include "v3math.h"
41#include "v3dmath.h"
42#include "listener.h"
43#include "lltimer.h"
44#include "lluuid.h"
45#include "llframetimer.h"
46#include "llassettype.h"
47
48const F32 LL_WIND_UPDATE_INTERVAL = 0.1f;
49const F32 LL_ROLLOFF_MULTIPLIER_UNDER_WATER = 5.f; // How much sounds are weaker under water
50const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f;
51
52const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f;
53const F32 DEFAULT_MIN_DISTANCE = 2.0f;
54
55#define MAX_CHANNELS 30
56#define MAX_BUFFERS 40 // Some extra for preloading, maybe?
57
58// This define is intended to allow us to switch from os based wav
59// file loading to vfs based wav file loading. The problem is that I
60// am unconvinced that the LLWaveFile works for loading sounds from
61// memory. So, until that is fixed up, changed, whatever, this remains
62// undefined.
63//#define USE_WAV_VFILE
64
65class LLVFS;
66
67class LLAudioSource;
68class LLAudioData;
69class LLAudioChannel;
70class LLAudioBuffer;
71
72
73
74//
75// LLAudioEngine definition
76//
77
78class LLAudioEngine
79{
80public:
81 LLAudioEngine();
82 virtual ~LLAudioEngine();
83
84 // initialization/startup/shutdown
85 //virtual BOOL init();
86
87 virtual BOOL init(const S32 num_channels, void *userdata);
88 virtual void shutdown();
89
90 // Used by the mechanics of the engine
91 //virtual void processQueue(const LLUUID &sound_guid);
92 virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at);
93 virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0;
94 virtual void idle(F32 max_decode_time = 0.f);
95 virtual void updateChannels();
96
97 //
98 // "End user" functionality
99 //
100 virtual BOOL isWindEnabled();
101 virtual void enableWind(BOOL state_b);
102
103 // Use these for temporarily muting the audio system.
104 // Does not change buffers, initialization, etc. but
105 // stops playing new sounds.
106 virtual void setMuted(BOOL muted);
107 virtual BOOL getMuted() const { return mMuted; }
108
109 F32 getMasterGain();
110 void setMasterGain(F32 gain);
111
112 F32 getInternetStreamGain();
113
114 virtual void setDopplerFactor(F32 factor);
115 virtual F32 getDopplerFactor();
116 virtual void setDistanceFactor(F32 factor);
117 virtual F32 getDistanceFactor();
118 virtual void setRolloffFactor(F32 factor);
119 virtual F32 getRolloffFactor();
120 virtual void setMaxWindGain(F32 gain);
121
122
123 // Methods actually related to setting up and removing sounds
124 // Owner ID is the owner of the object making the request
125 void triggerSound(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, const LLVector3d &pos_global = LLVector3d::zero);
126 BOOL preloadSound(const LLUUID &id);
127
128 void addAudioSource(LLAudioSource *asp);
129 void cleanupAudioSource(LLAudioSource *asp);
130
131 LLAudioSource *findAudioSource(const LLUUID &source_id);
132 LLAudioData *getAudioData(const LLUUID &audio_uuid);
133
134
135 virtual void startInternetStream(const char* url) = 0;
136 virtual void stopInternetStream() = 0;
137 virtual void pauseInternetStream(int pause) = 0;
138 virtual int isInternetStreamPlaying() = 0;
139 virtual void getInternetStreamInfo(char* artist, char* title) { artist[0] = 0; title[0] = 0; }
140 // use a value from 0.0 to 1.0, inclusive
141 virtual void setInternetStreamGain(F32 vol) { mInternetStreamGain = vol; }
142 virtual const char* getInternetStreamURL() { return ""; }
143
144 // For debugging usage
145 virtual LLVector3 getListenerPos();
146
147 LLAudioBuffer *getFreeBuffer(); // Get a free buffer, or flush an existing one if you have to.
148 LLAudioChannel *getFreeChannel(const F32 priority); // Get a free channel or flush an existing one if your priority is higher
149 void cleanupBuffer(LLAudioBuffer *bufferp);
150
151 BOOL hasDecodedFile(const LLUUID &uuid);
152 BOOL hasLocalFile(const LLUUID &uuid);
153
154 BOOL updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid = LLUUID::null);
155
156
157 // Asset callback when we're retrieved a sound from the asset server.
158 void startNextTransfer();
159 static void assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code);
160
161
162 friend class LLPipeline; // For debugging
163public:
164 F32 mMaxWindGain; // Hack. Public to set before fade in?
165
166protected:
167 virtual LLAudioBuffer *createBuffer() = 0;
168 virtual LLAudioChannel *createChannel() = 0;
169
170 virtual void initWind() = 0;
171 virtual void cleanupWind() = 0;
172 virtual void setInternalGain(F32 gain) = 0;
173
174 void commitDeferredChanges();
175
176 virtual void allocateListener() = 0;
177
178
179 // Internet stream methods
180 virtual void initInternetStream() {}
181 virtual void updateInternetStream() {}
182
183
184 // listener methods
185 virtual void setListenerPos(LLVector3 vec);
186 virtual void setListenerVelocity(LLVector3 vec);
187 virtual void orientListener(LLVector3 up, LLVector3 at);
188 virtual void translateListener(LLVector3 vec);
189
190
191 F64 mapWindVecToGain(LLVector3 wind_vec);
192 F64 mapWindVecToPitch(LLVector3 wind_vec);
193 F64 mapWindVecToPan(LLVector3 wind_vec);
194
195protected:
196 LLListener *mListenerp;
197
198 BOOL mMuted;
199 void* mUserData;
200
201 S32 mLastStatus;
202
203 S32 mNumChannels;
204 BOOL mEnableWind;
205
206 LLUUID mCurrentTransfer; // Audio file currently being transferred by the system
207 LLFrameTimer mCurrentTransferTimer;
208
209 // A list of all audio sources that are known to the viewer at this time.
210 // This is most likely a superset of the ones that we actually have audio
211 // data for, or are playing back.
212 typedef std::map<LLUUID, LLAudioSource *> source_map;
213 typedef std::map<LLUUID, LLAudioData *> data_map;
214
215 source_map mAllSources;
216 data_map mAllData;
217
218 LLAudioChannel *mChannels[MAX_CHANNELS];
219
220 // Buffers needs to change into a different data structure, as the number of buffers
221 // that we have active should be limited by RAM usage, not count.
222 LLAudioBuffer *mBuffers[MAX_BUFFERS];
223
224 F32 mMasterGain;
225
226 // Hack! Internet streams are treated differently from other sources!
227 F32 mInternetStreamGain;
228
229 F32 mNextWindUpdate;
230
231 LLFrameTimer mWindUpdateTimer;
232};
233
234
235
236
237//
238// Standard audio source. Can be derived from for special sources, such as those attached to objects.
239//
240
241
242class LLAudioSource
243{
244public:
245 // owner_id is the id of the agent responsible for making this sound
246 // play, for example, the owner of the object currently playing it
247 LLAudioSource(const LLUUID &id, const LLUUID& owner_id, const F32 gain);
248 virtual ~LLAudioSource();
249
250 virtual void update(); // Update this audio source
251 void updatePriority();
252
253 void preload(const LLUUID &audio_id); // Only used for preloading UI sounds, now.
254
255 void addAudioData(LLAudioData *adp, BOOL set_current = TRUE);
256
257 void setAmbient(const BOOL ambient) { mAmbient = ambient; }
258 BOOL isAmbient() const { return mAmbient; }
259
260 void setLoop(const BOOL loop) { mLoop = loop; }
261 BOOL isLoop() const { return mLoop; }
262
263 void setSyncMaster(const BOOL master) { mSyncMaster = master; }
264 BOOL isSyncMaster() const { return mSyncMaster; }
265
266 void setSyncSlave(const BOOL slave) { mSyncSlave = slave; }
267 BOOL isSyncSlave() const { return mSyncSlave; }
268
269 void setQueueSounds(const BOOL queue) { mQueueSounds = queue; }
270 BOOL isQueueSounds() const { return mQueueSounds; }
271
272 void setPlayedOnce(const BOOL played_once) { mPlayedOnce = played_once; }
273
274 void setPositionGlobal(const LLVector3d &position_global) { mPositionGlobal = position_global; }
275 LLVector3d getPositionGlobal() const { return mPositionGlobal; }
276 LLVector3 getVelocity() const { return mVelocity; }
277 F32 getPriority() const { return mPriority; }
278
279 // Gain should always be clamped between 0 and 1.
280 F32 getGain() const { return mGain; }
281 virtual void setGain(const F32 gain) { mGain = llclamp(gain, 0.f, 1.f); }
282
283 const LLUUID &getID() const { return mID; }
284 BOOL isDone();
285
286 LLAudioData *getCurrentData();
287 LLAudioData *getQueuedData();
288 LLAudioBuffer *getCurrentBuffer();
289
290 BOOL setupChannel();
291 BOOL play(const LLUUID &audio_id); // Start the audio source playing
292
293 BOOL hasPendingPreloads() const; // Has preloads that haven't been done yet
294
295 friend class LLAudioEngine;
296 friend class LLAudioChannel;
297protected:
298 void setChannel(LLAudioChannel *channelp);
299 LLAudioChannel *getChannel() const { return mChannelp; }
300
301protected:
302 LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
303 LLUUID mOwnerID; // owner of the object playing the sound
304 F32 mPriority;
305 F32 mGain;
306 BOOL mAmbient;
307 BOOL mLoop;
308 BOOL mSyncMaster;
309 BOOL mSyncSlave;
310 BOOL mQueueSounds;
311 BOOL mPlayedOnce;
312 LLVector3d mPositionGlobal;
313 LLVector3 mVelocity;
314
315 //LLAudioSource *mSyncMasterp; // If we're a slave, the source that we're synced to.
316 LLAudioChannel *mChannelp; // If we're currently playing back, this is the channel that we're assigned to.
317 LLAudioData *mCurrentDatap;
318 LLAudioData *mQueuedDatap;
319
320 typedef std::map<LLUUID, LLAudioData *> data_map;
321 data_map mPreloadMap;
322
323 LLFrameTimer mAgeTimer;
324};
325
326
327
328
329//
330// Generic metadata about a particular piece of audio data.
331// The actual data is handled by the derived LLAudioBuffer classes which are
332// derived for each audio engine.
333//
334
335
336class LLAudioData
337{
338public:
339 LLAudioData(const LLUUID &uuid);
340 BOOL load();
341
342 LLUUID getID() const { return mID; }
343 LLAudioBuffer *getBuffer() const { return mBufferp; }
344
345 BOOL hasLocalData() const { return mHasLocalData; }
346 BOOL hasDecodedData() const { return mHasDecodedData; }
347 BOOL hasValidData() const { return mHasValidData; }
348
349 void setHasLocalData(const BOOL hld) { mHasLocalData = hld; }
350 void setHasDecodedData(const BOOL hdd) { mHasDecodedData = hdd; }
351 void setHasValidData(const BOOL hvd) { mHasValidData = hvd; }
352
353 friend class LLAudioEngine; // Severe laziness, bad.
354
355protected:
356 LLUUID mID;
357 LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here.
358 BOOL mHasLocalData;
359 BOOL mHasDecodedData;
360 BOOL mHasValidData;
361};
362
363
364//
365// Base class for an audio channel, i.e. a channel which is capable of playing back a sound.
366// Management of channels is done generically, methods for actually manipulating the channel
367// are derived for each audio engine.
368//
369
370
371class LLAudioChannel
372{
373public:
374 LLAudioChannel();
375 virtual ~LLAudioChannel();
376
377 virtual void setSource(LLAudioSource *sourcep);
378 LLAudioSource *getSource() const { return mCurrentSourcep; }
379
380 friend class LLAudioEngine;
381 friend class LLAudioSource;
382protected:
383 virtual void play() = 0;
384 virtual void playSynced(LLAudioChannel *channelp) = 0;
385 virtual void cleanup() = 0;
386 virtual BOOL isPlaying() = 0;
387 void setWaiting(const BOOL waiting) { mWaiting = waiting; }
388 BOOL isWaiting() const { return mWaiting; }
389
390 virtual BOOL updateBuffer(); // Check to see if the buffer associated with the source changed, and update if necessary.
391 virtual void update3DPosition() = 0;
392 virtual void updateLoop() = 0; // Update your loop/completion status, for use by queueing/syncing.
393protected:
394 LLAudioSource *mCurrentSourcep;
395 LLAudioBuffer *mCurrentBufferp;
396 BOOL mLoopedThisFrame;
397 BOOL mWaiting; // Waiting for sync.
398};
399
400
401
402
403// Basically an interface class to the engine-specific implementation
404// of audio data that's ready for playback.
405// Will likely get more complex as we decide to do stuff like real streaming audio.
406
407
408class LLAudioBuffer
409{
410public:
411 virtual ~LLAudioBuffer() {};
412 virtual BOOL loadWAV(const char *filename) = 0;
413 virtual U32 getLength() = 0;
414
415 friend class LLAudioEngine;
416 friend class LLAudioChannel;
417 friend class LLAudioData;
418protected:
419 BOOL mInUse;
420 LLAudioData *mAudioDatap;
421 LLFrameTimer mLastUseTimer;
422};
423
424
425
426extern LLAudioEngine* gAudiop;
427
428#endif
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
diff --git a/linden/indra/llaudio/audioengine_fmod.h b/linden/indra/llaudio/audioengine_fmod.h
new file mode 100644
index 0000000..9274b1b
--- /dev/null
+++ b/linden/indra/llaudio/audioengine_fmod.h
@@ -0,0 +1,158 @@
1/**
2 * @file audioengine_fmod.h
3 * @brief Definition 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#ifndef LL_AUDIOENGINE_FMOD_H
30#define LL_AUDIOENGINE_FMOD_H
31
32#include "audioengine.h"
33
34#if LL_FMOD
35
36#include "listener_fmod.h"
37
38#include "fmod.h"
39
40class LLAudioStreamFMOD;
41
42class LLAudioEngine_FMOD : public LLAudioEngine
43{
44public:
45 LLAudioEngine_FMOD();
46 virtual ~LLAudioEngine_FMOD();
47
48 // initialization/startup/shutdown
49 virtual BOOL init(const S32 num_channels, void *user_data);
50 virtual void allocateListener();
51
52 virtual void shutdown();
53
54 virtual void idle(F32 max_decode_time = 0.f);
55
56 // Internet stream methods
57 virtual void initInternetStream();
58 virtual void startInternetStream(const char* url);
59 virtual void updateInternetStream();
60 virtual void stopInternetStream();
61 virtual void pauseInternetStream(int pause);
62 virtual int isInternetStreamPlaying();
63 virtual void getInternetStreamInfo(char* artist, char* title);
64 virtual void setInternetStreamGain(F32 vol);
65 virtual const char* getInternetStreamURL();
66
67 /*virtual*/ void initWind();
68 /*virtual*/ void cleanupWind();
69
70 /*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water);
71
72protected:
73 /*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to.
74 /*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel.
75
76 /*virtual*/ void setInternalGain(F32 gain);
77protected:
78 static signed char F_CALLBACKAPI callbackMetaData(char* name, char* value, void* userdata);
79
80 LLAudioStreamFMOD *mCurrentInternetStreamp;
81 char mInternetStreamURL[1024]; /*Flawfinder: ignore*/
82 int mInternetStreamChannel;
83
84 std::list<LLAudioStreamFMOD *> mDeadStreams;
85
86 //F32 mMinDistance[MAX_BUFFERS];
87 //F32 mMaxDistance[MAX_BUFFERS];
88
89 S32 mFadeIn;
90 BOOL mInited;
91
92 // On Windows, userdata is the HWND of the application window.
93 void* mUserData;
94
95};
96
97
98class LLAudioChannelFMOD : public LLAudioChannel
99{
100public:
101 LLAudioChannelFMOD();
102 virtual ~LLAudioChannelFMOD();
103
104protected:
105 /*virtual*/ void play();
106 /*virtual*/ void playSynced(LLAudioChannel *channelp);
107 /*virtual*/ void cleanup();
108 /*virtual*/ BOOL isPlaying();
109
110 /*virtual*/ BOOL updateBuffer();
111 /*virtual*/ void update3DPosition();
112 /*virtual*/ void updateLoop();
113
114protected:
115 int mChannelID;
116 S32 mLastSamplePos;
117};
118
119
120class LLAudioBufferFMOD : public LLAudioBuffer
121{
122public:
123 LLAudioBufferFMOD();
124 virtual ~LLAudioBufferFMOD();
125
126 /*virtual*/ BOOL loadWAV(const char *filename);
127 /*virtual*/ U32 getLength();
128 friend class LLAudioChannelFMOD;
129
130 void set3DMode(BOOL use3d);
131protected:
132 FSOUND_SAMPLE *getSample() { return mSamplep; }
133protected:
134 FSOUND_SAMPLE *mSamplep;
135};
136
137class LLAudioStreamFMOD
138{
139public:
140 LLAudioStreamFMOD(const char *url);
141 int startStream();
142 BOOL stopStream(); // Returns true if the stream was successfully stopped.
143 BOOL ready();
144
145 char *getURL() { return mInternetStreamURL; }
146
147 int getOpenState();
148protected:
149 FSOUND_STREAM* mInternetStream;
150 BOOL mReady;
151
152 char mInternetStreamURL[1024]; /*Flawfinder: ignore*/
153};
154
155#endif
156
157#endif
158
diff --git a/linden/indra/llaudio/files.lst b/linden/indra/llaudio/files.lst
new file mode 100644
index 0000000..abc8710
--- /dev/null
+++ b/linden/indra/llaudio/files.lst
@@ -0,0 +1,7 @@
1llaudio/audioengine.cpp
2llaudio/audioengine_fmod.cpp
3llaudio/listener.cpp
4llaudio/listener_fmod.cpp
5llaudio/llaudiodecodemgr.cpp
6llaudio/vorbisdecode.cpp
7llaudio/vorbisencode.cpp
diff --git a/linden/indra/llaudio/listener.cpp b/linden/indra/llaudio/listener.cpp
new file mode 100644
index 0000000..cff8e79
--- /dev/null
+++ b/linden/indra/llaudio/listener.cpp
@@ -0,0 +1,153 @@
1/**
2 * @file listener.cpp
3 * @brief Implementation of LISTENER class abstracting the audio support
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include "listener.h"
31
32#define DEFAULT_AT 0.0f,0.0f,-1.0f
33#define DEFAULT_UP 0.0f,1.0f,0.0f
34
35//-----------------------------------------------------------------------
36// constructor
37//-----------------------------------------------------------------------
38LLListener::LLListener()
39{
40 init();
41}
42
43//-----------------------------------------------------------------------
44LLListener::~LLListener()
45{
46}
47
48//-----------------------------------------------------------------------
49void LLListener::init(void)
50{
51 mPosition.zeroVec();
52 mListenAt.setVec(DEFAULT_AT);
53 mListenUp.setVec(DEFAULT_UP);
54 mVelocity.zeroVec();
55}
56
57//-----------------------------------------------------------------------
58void LLListener::free(void)
59{
60}
61
62//-----------------------------------------------------------------------
63void LLListener::translate(LLVector3 offset)
64{
65 mPosition += offset;
66}
67
68//-----------------------------------------------------------------------
69void LLListener::setPosition(LLVector3 pos)
70{
71 mPosition = pos;
72}
73
74//-----------------------------------------------------------------------
75LLVector3 LLListener::getPosition(void)
76{
77 return(mPosition);
78}
79
80//-----------------------------------------------------------------------
81LLVector3 LLListener::getAt(void)
82{
83 return(mListenAt);
84}
85
86//-----------------------------------------------------------------------
87LLVector3 LLListener::getUp(void)
88{
89 return(mListenUp);
90}
91
92//-----------------------------------------------------------------------
93void LLListener::setVelocity(LLVector3 vel)
94{
95 mVelocity = vel;
96}
97
98//-----------------------------------------------------------------------
99void LLListener::orient(LLVector3 up, LLVector3 at)
100{
101 mListenUp = up;
102 mListenAt = at;
103}
104
105//-----------------------------------------------------------------------
106void LLListener::set(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at)
107{
108 mPosition = pos;
109 mVelocity = vel;
110
111 setPosition(pos);
112 setVelocity(vel);
113 orient(up,at);
114}
115
116//-----------------------------------------------------------------------
117void LLListener::setDopplerFactor(F32 factor)
118{
119}
120
121//-----------------------------------------------------------------------
122F32 LLListener::getDopplerFactor()
123{
124 return (1.f);
125}
126
127//-----------------------------------------------------------------------
128void LLListener::setDistanceFactor(F32 factor)
129{
130}
131
132//-----------------------------------------------------------------------
133F32 LLListener::getDistanceFactor()
134{
135 return (1.f);
136}
137
138//-----------------------------------------------------------------------
139void LLListener::setRolloffFactor(F32 factor)
140{
141}
142
143//-----------------------------------------------------------------------
144F32 LLListener::getRolloffFactor()
145{
146 return (1.f);
147}
148
149//-----------------------------------------------------------------------
150void LLListener::commitDeferredChanges()
151{
152}
153
diff --git a/linden/indra/llaudio/listener.h b/linden/indra/llaudio/listener.h
new file mode 100644
index 0000000..bdc3ba2
--- /dev/null
+++ b/linden/indra/llaudio/listener.h
@@ -0,0 +1,78 @@
1/**
2 * @file listener.h
3 * @brief Description of LISTENER base class abstracting the audio support.
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_LISTENER_H
29#define LL_LISTENER_H
30
31#include <string.h>
32
33#include "v3math.h"
34
35class LLListener
36{
37 private:
38 protected:
39 LLVector3 mPosition;
40 LLVector3 mVelocity;
41 LLVector3 mListenAt;
42 LLVector3 mListenUp;
43
44 public:
45
46 private:
47 protected:
48 public:
49 LLListener();
50 virtual ~LLListener();
51 virtual void init();
52 virtual void free();
53
54 virtual void set(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at);
55
56 virtual void setPosition(LLVector3 pos);
57 virtual void setVelocity(LLVector3 vel);
58
59 virtual void orient(LLVector3 up, LLVector3 at);
60 virtual void translate(LLVector3 offset);
61
62 virtual void setDopplerFactor(F32 factor);
63 virtual void setDistanceFactor(F32 factor);
64 virtual void setRolloffFactor(F32 factor);
65
66 virtual LLVector3 getPosition();
67 virtual LLVector3 getAt();
68 virtual LLVector3 getUp();
69
70 virtual F32 getDopplerFactor();
71 virtual F32 getDistanceFactor();
72 virtual F32 getRolloffFactor();
73
74 virtual void commitDeferredChanges();
75};
76
77#endif
78
diff --git a/linden/indra/llaudio/listener_ds3d.h b/linden/indra/llaudio/listener_ds3d.h
new file mode 100644
index 0000000..e62040c
--- /dev/null
+++ b/linden/indra/llaudio/listener_ds3d.h
@@ -0,0 +1,71 @@
1/**
2 * @file listener_ds3d.h
3 * @brief Description of LISTENER class abstracting the audio support
4 * as a DirectSound 3D implementation (windows only)
5 *
6 * Copyright (c) 2000-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#ifndef LL_LISTENER_DS3D_H
30#define LL_LISTENER_DS3D_H
31
32#include "listener.h"
33
34#include <dmusici.h>
35#include <dsound.h>
36#include <ks.h>
37
38class LLListener_DS3D : public LLListener
39{
40 private:
41 protected:
42 IDirectSound3DListener8 *m3DListener;
43 public:
44
45 private:
46 protected:
47 public:
48 LLListener_DS3D();
49 virtual ~LLListener_DS3D();
50 virtual void init();
51
52 virtual void setDS3DLPtr (IDirectSound3DListener8 *listener_p);
53
54 virtual void translate(LLVector3 offset);
55 virtual void setPosition(LLVector3 pos);
56 virtual void setVelocity(LLVector3 vel);
57 virtual void orient(LLVector3 up, LLVector3 at);
58
59 virtual void setDopplerFactor(F32 factor);
60 virtual F32 getDopplerFactor();
61 virtual void setDistanceFactor(F32 factor);
62 virtual F32 getDistanceFactor();
63 virtual void setRolloffFactor(F32 factor);
64 virtual F32 getRolloffFactor();
65
66 virtual void commitDeferredChanges();
67};
68
69#endif
70
71
diff --git a/linden/indra/llaudio/listener_fmod.cpp b/linden/indra/llaudio/listener_fmod.cpp
new file mode 100644
index 0000000..d4f57c0
--- /dev/null
+++ b/linden/indra/llaudio/listener_fmod.cpp
@@ -0,0 +1,145 @@
1/**
2 * @file listener_fmod.cpp
3 * @brief implementation of LISTENER class abstracting the audio
4 * support as a FMOD 3D implementation (windows only)
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#include "audioengine.h"
31
32#if LL_FMOD
33
34#include "listener_fmod.h"
35#include "fmod.h"
36
37//-----------------------------------------------------------------------
38// constructor
39//-----------------------------------------------------------------------
40LLListener_FMOD::LLListener_FMOD()
41{
42 init();
43}
44
45//-----------------------------------------------------------------------
46LLListener_FMOD::~LLListener_FMOD()
47{
48}
49
50//-----------------------------------------------------------------------
51void LLListener_FMOD::init(void)
52{
53 // do inherited
54 LLListener::init();
55 mDopplerFactor = 1.0f;
56 mDistanceFactor = 1.0f;
57 mRolloffFactor = 1.0f;
58}
59
60//-----------------------------------------------------------------------
61void LLListener_FMOD::translate(LLVector3 offset)
62{
63 LLListener::translate(offset);
64
65 FSOUND_3D_Listener_SetAttributes(mPosition.mV, NULL, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]);
66}
67
68//-----------------------------------------------------------------------
69void LLListener_FMOD::setPosition(LLVector3 pos)
70{
71 LLListener::setPosition(pos);
72
73 FSOUND_3D_Listener_SetAttributes(pos.mV, NULL, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]);
74}
75
76//-----------------------------------------------------------------------
77void LLListener_FMOD::setVelocity(LLVector3 vel)
78{
79 LLListener::setVelocity(vel);
80
81 FSOUND_3D_Listener_SetAttributes(NULL, vel.mV, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]);
82}
83
84//-----------------------------------------------------------------------
85void LLListener_FMOD::orient(LLVector3 up, LLVector3 at)
86{
87 LLListener::orient(up, at);
88
89 // Welcome to the transition between right and left
90 // (coordinate systems, that is)
91 // Leaving the at vector alone results in a L/R reversal
92 // since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed
93 at = -at;
94
95 FSOUND_3D_Listener_SetAttributes(NULL, NULL, at.mV[0],at.mV[1],at.mV[2], up.mV[0],up.mV[1],up.mV[2]);
96}
97
98//-----------------------------------------------------------------------
99void LLListener_FMOD::commitDeferredChanges()
100{
101 FSOUND_Update();
102}
103
104
105void LLListener_FMOD::setRolloffFactor(F32 factor)
106{
107 mRolloffFactor = factor;
108 FSOUND_3D_SetRolloffFactor(factor);
109}
110
111
112F32 LLListener_FMOD::getRolloffFactor()
113{
114 return mRolloffFactor;
115}
116
117
118void LLListener_FMOD::setDopplerFactor(F32 factor)
119{
120 mDopplerFactor = factor;
121 FSOUND_3D_SetDopplerFactor(factor);
122}
123
124
125F32 LLListener_FMOD::getDopplerFactor()
126{
127 return mDopplerFactor;
128}
129
130
131void LLListener_FMOD::setDistanceFactor(F32 factor)
132{
133 mDistanceFactor = factor;
134 FSOUND_3D_SetDistanceFactor(factor);
135}
136
137
138F32 LLListener_FMOD::getDistanceFactor()
139{
140 return mDistanceFactor;
141}
142
143#endif // LL_FMOD
144
145
diff --git a/linden/indra/llaudio/listener_fmod.h b/linden/indra/llaudio/listener_fmod.h
new file mode 100644
index 0000000..521cc16
--- /dev/null
+++ b/linden/indra/llaudio/listener_fmod.h
@@ -0,0 +1,66 @@
1/**
2 * @file listener_fmod.h
3 * @brief Description of LISTENER class abstracting the audio support
4 * as an FMOD 3D implementation (windows and Linux)
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#ifndef LL_LISTENER_FMOD_H
30#define LL_LISTENER_FMOD_H
31
32#include "listener.h"
33
34class LLListener_FMOD : public LLListener
35{
36 private:
37 protected:
38 F32 mDopplerFactor;
39 F32 mDistanceFactor;
40 F32 mRolloffFactor;
41 public:
42
43 private:
44 protected:
45 public:
46 LLListener_FMOD();
47 virtual ~LLListener_FMOD();
48 virtual void init();
49
50 virtual void translate(LLVector3 offset);
51 virtual void setPosition(LLVector3 pos);
52 virtual void setVelocity(LLVector3 vel);
53 virtual void orient(LLVector3 up, LLVector3 at);
54 virtual void commitDeferredChanges();
55
56 virtual void setDopplerFactor(F32 factor);
57 virtual F32 getDopplerFactor();
58 virtual void setDistanceFactor(F32 factor);
59 virtual F32 getDistanceFactor();
60 virtual void setRolloffFactor(F32 factor);
61 virtual F32 getRolloffFactor();
62};
63
64#endif
65
66
diff --git a/linden/indra/llaudio/listener_openal.h b/linden/indra/llaudio/listener_openal.h
new file mode 100644
index 0000000..e87c504
--- /dev/null
+++ b/linden/indra/llaudio/listener_openal.h
@@ -0,0 +1,56 @@
1/**
2 * @file listener_openal.h
3 * @brief Description of LISTENER class abstracting the audio support
4 * as an OpenAL implementation
5 *
6 * Copyright (c) 2000-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#ifndef LL_LISTENER_OPENAL_H
30#define LL_LISTENER_OPENAL_H
31
32#include "listener.h"
33
34#include "AL/al.h"
35#include "AL/alut.h"
36
37class LLListener_OpenAL : public LLListener
38{
39 private:
40 protected:
41 public:
42
43 private:
44 protected:
45 public:
46 LLListener_OpenAL();
47 virtual ~LLListener_OpenAL();
48
49 virtual void translate(LLVector3 offset);
50 virtual void setPosition(LLVector3 pos);
51 virtual void setVelocity(LLVector3 vel);
52 virtual void orient(LLVector3 up, LLVector3 at);
53};
54
55#endif
56
diff --git a/linden/indra/llaudio/llaudio.vcproj b/linden/indra/llaudio/llaudio.vcproj
new file mode 100644
index 0000000..7073b88
--- /dev/null
+++ b/linden/indra/llaudio/llaudio.vcproj
@@ -0,0 +1,223 @@
1<?xml version="1.0" encoding="Windows-1252"?>
2<VisualStudioProject
3 ProjectType="Visual C++"
4 Version="7.10"
5 Name="llaudio"
6 ProjectGUID="{93B2BA29-FBE9-4376-92C1-6108DCFE09D3}"
7 RootNamespace="llaudio"
8 Keyword="Win32Proj">
9 <Platforms>
10 <Platform
11 Name="Win32"/>
12 </Platforms>
13 <Configurations>
14 <Configuration
15 Name="Debug|Win32"
16 OutputDirectory="../lib_$(ConfigurationName)/i686-win32"
17 IntermediateDirectory="Debug"
18 ConfigurationType="4"
19 CharacterSet="1">
20 <Tool
21 Name="VCCLCompilerTool"
22 Optimization="0"
23 AdditionalIncludeDirectories="..\llcommon;..\llmath;..\llvfs;..\llmessage;..\..\libraries\i686-win32\include;..\..\libraries\include\;"
24 PreprocessorDefinitions="WIN32;_DEBUG;_LIB;LL_WINDOWS;LL_DEBUG"
25 MinimalRebuild="TRUE"
26 BasicRuntimeChecks="3"
27 RuntimeLibrary="1"
28 StructMemberAlignment="4"
29 ForceConformanceInForLoopScope="TRUE"
30 UsePrecompiledHeader="0"
31 WarningLevel="3"
32 WarnAsError="TRUE"
33 Detect64BitPortabilityProblems="FALSE"
34 DebugInformationFormat="4"/>
35 <Tool
36 Name="VCCustomBuildTool"/>
37 <Tool
38 Name="VCLibrarianTool"
39 OutputFile="$(OutDir)/llaudio.lib"/>
40 <Tool
41 Name="VCMIDLTool"/>
42 <Tool
43 Name="VCPostBuildEventTool"/>
44 <Tool
45 Name="VCPreBuildEventTool"/>
46 <Tool
47 Name="VCPreLinkEventTool"/>
48 <Tool
49 Name="VCResourceCompilerTool"/>
50 <Tool
51 Name="VCWebServiceProxyGeneratorTool"/>
52 <Tool
53 Name="VCXMLDataGeneratorTool"/>
54 <Tool
55 Name="VCManagedWrapperGeneratorTool"/>
56 <Tool
57 Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
58 </Configuration>
59 <Configuration
60 Name="Release|Win32"
61 OutputDirectory="../lib_$(ConfigurationName)/i686-win32"
62 IntermediateDirectory="Release"
63 ConfigurationType="4"
64 CharacterSet="1">
65 <Tool
66 Name="VCCLCompilerTool"
67 AdditionalIncludeDirectories="..\llcommon;..\llmath;..\llvfs;..\llmessage;..\..\libraries\i686-win32\include;..\..\libraries\include\"
68 PreprocessorDefinitions="WIN32;NDEBUG;_LIB;LL_WINDOWS;LL_RELEASE"
69 RuntimeLibrary="0"
70 StructMemberAlignment="0"
71 ForceConformanceInForLoopScope="TRUE"
72 UsePrecompiledHeader="0"
73 WarningLevel="3"
74 WarnAsError="TRUE"
75 Detect64BitPortabilityProblems="FALSE"
76 DebugInformationFormat="3"/>
77 <Tool
78 Name="VCCustomBuildTool"/>
79 <Tool
80 Name="VCLibrarianTool"
81 OutputFile="$(OutDir)/llaudio.lib"/>
82 <Tool
83 Name="VCMIDLTool"/>
84 <Tool
85 Name="VCPostBuildEventTool"/>
86 <Tool
87 Name="VCPreBuildEventTool"/>
88 <Tool
89 Name="VCPreLinkEventTool"/>
90 <Tool
91 Name="VCResourceCompilerTool"/>
92 <Tool
93 Name="VCWebServiceProxyGeneratorTool"/>
94 <Tool
95 Name="VCXMLDataGeneratorTool"/>
96 <Tool
97 Name="VCManagedWrapperGeneratorTool"/>
98 <Tool
99 Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
100 </Configuration>
101 <Configuration
102 Name="ReleaseNoOpt|Win32"
103 OutputDirectory="../lib_$(ConfigurationName)/i686-win32"
104 IntermediateDirectory="$(ConfigurationName)"
105 ConfigurationType="4"
106 CharacterSet="1">
107 <Tool
108 Name="VCCLCompilerTool"
109 Optimization="0"
110 AdditionalIncludeDirectories="..\llcommon;..\llmath;..\llvfs;..\llmessage;..\..\libraries\i686-win32\include;..\..\libraries\include\"
111 PreprocessorDefinitions="WIN32;NDEBUG;_LIB;LL_WINDOWS;LL_RELEASE"
112 RuntimeLibrary="0"
113 StructMemberAlignment="0"
114 ForceConformanceInForLoopScope="TRUE"
115 UsePrecompiledHeader="0"
116 WarningLevel="3"
117 WarnAsError="TRUE"
118 Detect64BitPortabilityProblems="FALSE"
119 DebugInformationFormat="3"/>
120 <Tool
121 Name="VCCustomBuildTool"/>
122 <Tool
123 Name="VCLibrarianTool"
124 OutputFile="$(OutDir)/llaudio.lib"/>
125 <Tool
126 Name="VCMIDLTool"/>
127 <Tool
128 Name="VCPostBuildEventTool"/>
129 <Tool
130 Name="VCPreBuildEventTool"/>
131 <Tool
132 Name="VCPreLinkEventTool"/>
133 <Tool
134 Name="VCResourceCompilerTool"/>
135 <Tool
136 Name="VCWebServiceProxyGeneratorTool"/>
137 <Tool
138 Name="VCXMLDataGeneratorTool"/>
139 <Tool
140 Name="VCManagedWrapperGeneratorTool"/>
141 <Tool
142 Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
143 </Configuration>
144 </Configurations>
145 <References>
146 </References>
147 <Files>
148 <Filter
149 Name="Source Files"
150 Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
151 UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
152 <File
153 RelativePath=".\audioengine.cpp">
154 </File>
155 <File
156 RelativePath=".\audioengine_fmod.cpp">
157 </File>
158 <File
159 RelativePath=".\listener.cpp">
160 </File>
161 <File
162 RelativePath=".\listener_fmod.cpp">
163 </File>
164 <File
165 RelativePath=".\llaudiodecodemgr.cpp">
166 </File>
167 <File
168 RelativePath=".\vorbisdecode.cpp">
169 </File>
170 <File
171 RelativePath=".\vorbisencode.cpp">
172 </File>
173 </Filter>
174 <Filter
175 Name="Header Files"
176 Filter="h;hpp;hxx;hm;inl;inc;xsd"
177 UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
178 <File
179 RelativePath=".\audioengine.h">
180 </File>
181 <File
182 RelativePath=".\audioengine_fmod.h">
183 </File>
184 <File
185 RelativePath=".\fmod.h">
186 </File>
187 <File
188 RelativePath=".\fmod_errors.h">
189 </File>
190 <File
191 RelativePath=".\listener.h">
192 </File>
193 <File
194 RelativePath=".\listener_ds3d.h">
195 </File>
196 <File
197 RelativePath=".\listener_fmod.h">
198 </File>
199 <File
200 RelativePath=".\listener_openal.h">
201 </File>
202 <File
203 RelativePath=".\llaudiodecodemgr.h">
204 </File>
205 <File
206 RelativePath=".\vorbisdecode.h">
207 </File>
208 <File
209 RelativePath=".\vorbisencode.h">
210 </File>
211 <File
212 RelativePath=".\wavefile.h">
213 </File>
214 </Filter>
215 <Filter
216 Name="Resource Files"
217 Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
218 UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
219 </Filter>
220 </Files>
221 <Globals>
222 </Globals>
223</VisualStudioProject>
diff --git a/linden/indra/llaudio/llaudiodecodemgr.cpp b/linden/indra/llaudio/llaudiodecodemgr.cpp
new file mode 100644
index 0000000..d98ff5d
--- /dev/null
+++ b/linden/indra/llaudio/llaudiodecodemgr.cpp
@@ -0,0 +1,635 @@
1/**
2 * @file llaudiodecodemgr.cpp
3 *
4 * Copyright (c) 2003-2007, Linden Research, Inc.
5 *
6 * The source code in this file ("Source Code") is provided by Linden Lab
7 * to you under the terms of the GNU General Public License, version 2.0
8 * ("GPL"), unless you have obtained a separate licensing agreement
9 * ("Other License"), formally executed by you and Linden Lab. Terms of
10 * the GPL can be found in doc/GPL-license.txt in this distribution, or
11 * online at http://secondlife.com/developers/opensource/gplv2
12 *
13 * There are special exceptions to the terms and conditions of the GPL as
14 * it is applied to this Source Code. View the full text of the exception
15 * in the file doc/FLOSS-exception.txt in this software distribution, or
16 * online at http://secondlife.com/developers/opensource/flossexception
17 *
18 * By copying, modifying or distributing this software, you acknowledge
19 * that you have read and understood your obligations described above,
20 * and agree to abide by those obligations.
21 *
22 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
23 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
24 * COMPLETENESS OR PERFORMANCE.
25 */
26
27#include "linden_common.h"
28
29#include <vector>
30#include <iterator>
31#include <algorithm>
32#include <stdio.h>
33
34#include "llaudiodecodemgr.h"
35
36#include "vorbisdecode.h"
37#include "audioengine.h"
38#include "lllfsthread.h"
39#include "llvfile.h"
40#include "llstring.h"
41#include "lldir.h"
42#include "llendianswizzle.h"
43#include "audioengine.h"
44#include "llassetstorage.h"
45
46#include "vorbis/codec.h"
47#include "vorbis/vorbisfile.h"
48
49extern LLAudioEngine *gAudiop;
50
51LLAudioDecodeMgr *gAudioDecodeMgrp = NULL;
52
53static const S32 WAV_HEADER_SIZE = 44;
54
55class LLVorbisDecodeState
56{
57public:
58 LLVorbisDecodeState(const LLUUID &uuid, const LLString &out_filename);
59 virtual ~LLVorbisDecodeState();
60
61 BOOL initDecode();
62 BOOL decodeSection(); // Return TRUE if done.
63 BOOL finishDecode();
64
65 void flushBadFile();
66
67 BOOL isValid() const { return mValid; }
68 BOOL isDone() const { return mDone; }
69 const LLUUID &getUUID() const { return mUUID; }
70protected:
71 BOOL mValid;
72 BOOL mDone;
73 LLUUID mUUID;
74
75 std::vector<U8> mWAVBuffer;
76#if !defined(USE_WAV_VFILE)
77 LLString mOutFilename;
78 LLLFSThread::handle_t mFileHandle;
79#endif
80
81 LLVFile *mInFilep;
82 OggVorbis_File mVF;
83 S32 mCurrentSection;
84};
85
86void LLVorbisDecodeState::flushBadFile()
87{
88 if (mInFilep)
89 {
90 llwarns << "Flushing bad vorbis file from VFS for " << mUUID << llendl;
91 mInFilep->remove();
92 }
93}
94
95
96LLAudioDecodeMgr::LLAudioDecodeMgr()
97{
98 mCurrentDecodep = NULL;
99}
100
101LLAudioDecodeMgr::~LLAudioDecodeMgr()
102{
103 delete mCurrentDecodep;
104 mCurrentDecodep = NULL;
105}
106
107
108void LLAudioDecodeMgr::processQueue(const F32 num_secs)
109{
110 LLUUID uuid;
111
112 LLTimer decode_timer;
113
114 BOOL done = FALSE;
115 while (!done)
116 {
117 if (mCurrentDecodep)
118 {
119 BOOL res;
120
121 // Decode in a loop until we're done or have run out of time.
122 while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs))
123 {
124 // decodeSection does all of the work above
125 }
126
127 if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid())
128 {
129 // We had an error when decoding, abort.
130 llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl;
131 mCurrentDecodep->flushBadFile();
132 LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
133 adp->setHasValidData(FALSE);
134 delete mCurrentDecodep;
135 mCurrentDecodep = NULL;
136 done = TRUE;
137 }
138
139 if (!res)
140 {
141 // We've used up out time slice, bail...
142 done = TRUE;
143 }
144 else if (mCurrentDecodep)
145 {
146 if (mCurrentDecodep->finishDecode())
147 {
148 // We finished!
149 if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
150 {
151 LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
152 adp->setHasDecodedData(TRUE);
153 adp->setHasValidData(TRUE);
154
155 // At this point, we could see if anyone needs this sound immediately, but
156 // I'm not sure that there's a reason to - we need to poll all of the playing
157 // sounds anyway.
158 //llinfos << "Finished the vorbis decode, now what?" << llendl;
159 }
160 else
161 {
162 llinfos << "Vorbis decode failed!!!" << llendl;
163 }
164 delete mCurrentDecodep;
165 mCurrentDecodep = NULL;
166 }
167 done = TRUE; // done for now
168 }
169 }
170
171 if (!done)
172 {
173 if (!mDecodeQueue.getLength())
174 {
175 // Nothing else on the queue.
176 done = TRUE;
177 }
178 else
179 {
180 LLUUID uuid;
181 mDecodeQueue.pop(uuid);
182 if (gAudiop->hasDecodedFile(uuid))
183 {
184 // This file has already been decoded, don't decode it again.
185 continue;
186 }
187
188 lldebugs << "Decoding " << uuid << " from audio queue!" << llendl;
189
190 char uuid_str[64]; /*Flawfinder: ignore*/
191 char d_path[LL_MAX_PATH]; /*Flawfinder: ignore*/
192
193 LLTimer timer;
194 timer.reset();
195
196 uuid.toString(uuid_str);
197 snprintf(d_path, LL_MAX_PATH, "%s.dsf", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str()); /*Flawfinder: ignore*/
198
199 mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
200 if (!mCurrentDecodep->initDecode())
201 {
202 delete mCurrentDecodep;
203 mCurrentDecodep = NULL;
204 }
205 }
206 }
207 }
208}
209
210
211BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
212{
213 if (gAudiop->hasDecodedFile(uuid))
214 {
215 // Already have a decoded version, don't need to decode it.
216 return TRUE;
217 }
218
219 if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
220 {
221 // Just put it on the decode queue.
222 gAudioDecodeMgrp->mDecodeQueue.push(uuid);
223 return TRUE;
224 }
225
226 return FALSE;
227}
228
229
230S32 LLAudioDecodeMgr::getRequestCount()
231{
232 /*
233 S32 count = 0;
234 if (mCurrentTransfer.notNull())
235 {
236 count++;
237 }
238
239 count += mRequestQueue.getLength();
240 return count;
241 */
242 return 0;
243}
244
245
246
247
248
249
250
251
252size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource)
253{
254 LLVFile *file = (LLVFile *)datasource;
255
256 if (file->read((U8*)ptr, (S32)(size * nmemb))) /*Flawfinder: ignore*/
257 {
258 S32 read = file->getLastBytesRead();
259 return read / size; /*Flawfinder: ignore*/
260 }
261 else
262 {
263 return 0;
264 }
265}
266
267int vfs_seek(void *datasource, ogg_int64_t offset, int whence)
268{
269 LLVFile *file = (LLVFile *)datasource;
270
271 // vfs has 31-bit files
272 if (offset > S32_MAX)
273 {
274 return -1;
275 }
276
277 S32 origin;
278 switch (whence) {
279 case SEEK_SET:
280 origin = 0;
281 break;
282 case SEEK_END:
283 origin = file->getSize();
284 break;
285 case SEEK_CUR:
286 origin = -1;
287 break;
288 default:
289 llerrs << "Invalid whence argument to vfs_seek" << llendl;
290 return -1;
291 }
292
293 if (file->seek((S32)offset, origin))
294 {
295 return 0;
296 }
297 else
298 {
299 return -1;
300 }
301}
302
303int vfs_close (void *datasource)
304{
305 LLVFile *file = (LLVFile *)datasource;
306
307 delete file;
308
309 return 0;
310}
311
312long vfs_tell (void *datasource)
313{
314 LLVFile *file = (LLVFile *)datasource;
315
316 return file->tell();
317}
318
319
320
321
322LLVorbisDecodeState::LLVorbisDecodeState(const LLUUID &uuid, const LLString &out_filename)
323{
324 mDone = FALSE;
325 mValid = FALSE;
326 mUUID = uuid;
327 mInFilep = NULL;
328 mCurrentSection = 0;
329#if !defined(USE_WAV_VFILE)
330 mOutFilename = out_filename;
331 mFileHandle = LLLFSThread::nullHandle();
332#endif
333 // No default value for mVF, it's an ogg structure?
334}
335
336LLVorbisDecodeState::~LLVorbisDecodeState()
337{
338 if (!mDone)
339 {
340 delete mInFilep;
341 mInFilep = NULL;
342 }
343}
344
345
346BOOL LLVorbisDecodeState::initDecode()
347{
348 ov_callbacks vfs_callbacks;
349 vfs_callbacks.read_func = vfs_read;
350 vfs_callbacks.seek_func = vfs_seek;
351 vfs_callbacks.close_func = vfs_close;
352 vfs_callbacks.tell_func = vfs_tell;
353
354 //llinfos << "Initing decode from vfile: " << mUUID << llendl;
355
356 mInFilep = new LLVFile(gVFS, mUUID, LLAssetType::AT_SOUND);
357 if (!mInFilep || !mInFilep->getSize())
358 {
359 llwarns << "unable to open vorbis source vfile for reading" << llendl;
360 delete mInFilep;
361 mInFilep = NULL;
362 return FALSE;
363 }
364
365 int r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, vfs_callbacks);
366 if(r < 0)
367 {
368 llwarns << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << mUUID << llendl;
369 return(FALSE);
370 }
371
372 size_t size_guess = (size_t)ov_pcm_total(&mVF, -1);
373 vorbis_info* vi = ov_info(&mVF, -1);
374 size_guess *= vi->channels;
375 size_guess *= 2;
376 size_guess += 2048;
377 mWAVBuffer.reserve(size_guess);
378 mWAVBuffer.resize(WAV_HEADER_SIZE);
379
380 {
381 // write the .wav format header
382 //"RIFF"
383 mWAVBuffer[0] = 0x52;
384 mWAVBuffer[1] = 0x49;
385 mWAVBuffer[2] = 0x46;
386 mWAVBuffer[3] = 0x46;
387
388 // length = datalen + 36 (to be filled in later)
389 mWAVBuffer[4] = 0x00;
390 mWAVBuffer[5] = 0x00;
391 mWAVBuffer[6] = 0x00;
392 mWAVBuffer[7] = 0x00;
393
394 //"WAVE"
395 mWAVBuffer[8] = 0x57;
396 mWAVBuffer[9] = 0x41;
397 mWAVBuffer[10] = 0x56;
398 mWAVBuffer[11] = 0x45;
399
400 // "fmt "
401 mWAVBuffer[12] = 0x66;
402 mWAVBuffer[13] = 0x6D;
403 mWAVBuffer[14] = 0x74;
404 mWAVBuffer[15] = 0x20;
405
406 // chunk size = 16
407 mWAVBuffer[16] = 0x10;
408 mWAVBuffer[17] = 0x00;
409 mWAVBuffer[18] = 0x00;
410 mWAVBuffer[19] = 0x00;
411
412 // format (1 = PCM)
413 mWAVBuffer[20] = 0x01;
414 mWAVBuffer[21] = 0x00;
415
416 // number of channels
417 mWAVBuffer[22] = 0x01;
418 mWAVBuffer[23] = 0x00;
419
420 // samples per second
421 mWAVBuffer[24] = 0x44;
422 mWAVBuffer[25] = 0xAC;
423 mWAVBuffer[26] = 0x00;
424 mWAVBuffer[27] = 0x00;
425
426 // average bytes per second
427 mWAVBuffer[28] = 0x88;
428 mWAVBuffer[29] = 0x58;
429 mWAVBuffer[30] = 0x01;
430 mWAVBuffer[31] = 0x00;
431
432 // bytes to output at a single time
433 mWAVBuffer[32] = 0x02;
434 mWAVBuffer[33] = 0x00;
435
436 // 16 bits per sample
437 mWAVBuffer[34] = 0x10;
438 mWAVBuffer[35] = 0x00;
439
440 // "data"
441 mWAVBuffer[36] = 0x64;
442 mWAVBuffer[37] = 0x61;
443 mWAVBuffer[38] = 0x74;
444 mWAVBuffer[39] = 0x61;
445
446 // these are the length of the data chunk, to be filled in later
447 mWAVBuffer[40] = 0x00;
448 mWAVBuffer[41] = 0x00;
449 mWAVBuffer[42] = 0x00;
450 mWAVBuffer[43] = 0x00;
451 }
452
453 //{
454 //char **ptr=ov_comment(&mVF,-1)->user_comments;
455// vorbis_info *vi=ov_info(&vf,-1);
456 //while(*ptr){
457 // fprintf(stderr,"%s\n",*ptr);
458 // ++ptr;
459 //}
460// fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate);
461// fprintf(stderr,"\nDecoded length: %ld samples\n", (long)ov_pcm_total(&vf,-1));
462// fprintf(stderr,"Encoded by: %s\n\n",ov_comment(&vf,-1)->vendor);
463 //}
464 return TRUE;
465}
466
467BOOL LLVorbisDecodeState::decodeSection()
468{
469 if (!mInFilep)
470 {
471 llwarns << "No VFS file to decode in vorbis!" << llendl;
472 return TRUE;
473 }
474 if (mDone)
475 {
476// llwarns << "Already done with decode, aborting!" << llendl;
477 return TRUE;
478 }
479 char pcmout[4096]; /*Flawfinder: ignore*/
480
481 BOOL eof = FALSE;
482 long ret=ov_read(&mVF, pcmout, sizeof(pcmout), 0, 2, 1, &mCurrentSection);
483 if (ret == 0)
484 {
485 /* EOF */
486 eof = TRUE;
487 mDone = TRUE;
488 mValid = TRUE;
489// llinfos << "Vorbis EOF" << llendl;
490 }
491 else if (ret < 0)
492 {
493 /* error in the stream. Not a problem, just reporting it in
494 case we (the app) cares. In this case, we don't. */
495
496 llwarns << "BAD vorbis decode in decodeSection." << llendl;
497
498 mValid = FALSE;
499 mDone = TRUE;
500 // We're done, return TRUE.
501 return TRUE;
502 }
503 else
504 {
505// llinfos << "Vorbis read " << ret << "bytes" << llendl;
506 /* we don't bother dealing with sample rate changes, etc, but.
507 you'll have to*/
508 std::copy(pcmout, pcmout+ret, std::back_inserter(mWAVBuffer));
509 }
510 return eof;
511}
512
513BOOL LLVorbisDecodeState::finishDecode()
514{
515 if (!isValid())
516 {
517 llwarns << "Bogus vorbis decode state for " << getUUID() << ", aborting!" << llendl;
518 return TRUE; // We've finished
519 }
520
521#if !defined(USE_WAV_VFILE)
522 if (mFileHandle == LLLFSThread::nullHandle())
523#endif
524 {
525 ov_clear(&mVF);
526
527 // write "data" chunk length, in little-endian format
528 S32 data_length = mWAVBuffer.size() - WAV_HEADER_SIZE;
529 mWAVBuffer[40] = (data_length) & 0x000000FF;
530 mWAVBuffer[41] = (data_length >> 8) & 0x000000FF;
531 mWAVBuffer[42] = (data_length >> 16) & 0x000000FF;
532 mWAVBuffer[43] = (data_length >> 24) & 0x000000FF;
533 // write overall "RIFF" length, in little-endian format
534 data_length += 36;
535 mWAVBuffer[4] = (data_length) & 0x000000FF;
536 mWAVBuffer[5] = (data_length >> 8) & 0x000000FF;
537 mWAVBuffer[6] = (data_length >> 16) & 0x000000FF;
538 mWAVBuffer[7] = (data_length >> 24) & 0x000000FF;
539
540 //
541 // FUCK!!! Vorbis encode/decode messes up loop point transitions (pop)
542 // do a cheap-and-cheesy crossfade
543 //
544 {
545 S16 *samplep;
546 S32 i;
547 S32 fade_length;
548 char pcmout[4096]; /*Flawfinder: ignore*/
549
550 fade_length = llmin((S32)128,(S32)(data_length-36)/8);
551 if((S32)mWAVBuffer.size() >= (WAV_HEADER_SIZE + 2* fade_length))
552 {
553 memcpy(pcmout, &mWAVBuffer[WAV_HEADER_SIZE], (2 * fade_length)); /*Flawfinder: ignore*/
554 }
555 llendianswizzle(&pcmout, 2, fade_length);
556
557 samplep = (S16 *)pcmout;
558 for (i = 0 ;i < fade_length; i++)
559 {
560 *samplep = llfloor((F32)*samplep * ((F32)i/(F32)fade_length));
561 samplep++;
562 }
563
564 llendianswizzle(&pcmout, 2, fade_length);
565 if((WAV_HEADER_SIZE+(2 * fade_length)) < (S32)mWAVBuffer.size())
566 {
567 memcpy(&mWAVBuffer[WAV_HEADER_SIZE], pcmout, (2 * fade_length)); /*Flawfinder: ignore*/
568 }
569 S32 near_end = mWAVBuffer.size() - (2 * fade_length);
570 if ((S32)mWAVBuffer.size() >= ( near_end + 2* fade_length))
571 {
572 memcpy(pcmout, &mWAVBuffer[near_end], (2 * fade_length)); /*Flawfinder: ignore*/
573 }
574 llendianswizzle(&pcmout, 2, fade_length);
575
576 samplep = (S16 *)pcmout;
577 for (i = fade_length-1 ; i >= 0; i--)
578 {
579 *samplep = llfloor((F32)*samplep * ((F32)i/(F32)fade_length));
580 samplep++;
581 }
582
583 llendianswizzle(&pcmout, 2, fade_length);
584 if (near_end + (2 * fade_length) < (S32)mWAVBuffer.size())
585 {
586 memcpy(&mWAVBuffer[near_end], pcmout, (2 * fade_length));/*Flawfinder: ignore*/
587 }
588 }
589
590 if (36 == data_length)
591 {
592 llwarns << "BAD Vorbis decode in finishDecode!" << llendl;
593 mValid = FALSE;
594 return TRUE; // we've finished
595 }
596#if !defined(USE_WAV_VFILE)
597 mFileHandle = LLLFSThread::sLocal->write(mOutFilename, &mWAVBuffer[0], 0, data_length);
598#endif
599 }
600
601 if (mFileHandle != LLLFSThread::nullHandle())
602 {
603 LLLFSThread::status_t s = LLLFSThread::sLocal->getRequestStatus(mFileHandle);
604 if (s != LLLFSThread::STATUS_COMPLETE)
605 {
606 if (s != LLLFSThread::STATUS_QUEUED && s != LLLFSThread::STATUS_INPROGRESS)
607 {
608 llerrs << "Bad file status in LLVorbisDecodeState::finishDecode: " << s << llendl;
609 }
610 return FALSE; // not finished
611 }
612 else
613 {
614 LLLFSThread::Request* req = (LLLFSThread::Request*)LLLFSThread::sLocal->getRequest(mFileHandle);
615 if (req->getBytesRead() == 0) //!= req->getBytes() // should be safe, but needs testing
616 {
617 llwarns << "Unable to write file in LLVorbisDecodeState::finishDecode" << llendl;
618 mValid = FALSE;
619 return TRUE; // we've finished
620 }
621 }
622 LLLFSThread::sLocal->completeRequest(mFileHandle);
623 }
624
625 mDone = TRUE;
626
627#if defined(USE_WAV_VFILE)
628 // write the data.
629 LLVFile output(gVFS, mUUID, LLAssetType::AT_SOUND_WAV);
630 output.write(&mWAVBuffer[0], mWAVBuffer.size());
631#endif
632 //llinfos << "Finished decode for " << getUUID() << llendl;
633
634 return TRUE;
635}
diff --git a/linden/indra/llaudio/llaudiodecodemgr.h b/linden/indra/llaudio/llaudiodecodemgr.h
new file mode 100644
index 0000000..3480004
--- /dev/null
+++ b/linden/indra/llaudio/llaudiodecodemgr.h
@@ -0,0 +1,66 @@
1/**
2 * @file llaudiodecodemgr.h
3 *
4 * Copyright (c) 2003-2007, Linden Research, Inc.
5 *
6 * The source code in this file ("Source Code") is provided by Linden Lab
7 * to you under the terms of the GNU General Public License, version 2.0
8 * ("GPL"), unless you have obtained a separate licensing agreement
9 * ("Other License"), formally executed by you and Linden Lab. Terms of
10 * the GPL can be found in doc/GPL-license.txt in this distribution, or
11 * online at http://secondlife.com/developers/opensource/gplv2
12 *
13 * There are special exceptions to the terms and conditions of the GPL as
14 * it is applied to this Source Code. View the full text of the exception
15 * in the file doc/FLOSS-exception.txt in this software distribution, or
16 * online at http://secondlife.com/developers/opensource/flossexception
17 *
18 * By copying, modifying or distributing this software, you acknowledge
19 * that you have read and understood your obligations described above,
20 * and agree to abide by those obligations.
21 *
22 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
23 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
24 * COMPLETENESS OR PERFORMANCE.
25 */
26
27#ifndef LL_LLAUDIODECODEMGR_H
28#define LL_LLAUDIODECODEMG_H
29
30#include "stdtypes.h"
31
32#include "lllinkedqueue.h"
33#include "lluuid.h"
34
35#include "llassettype.h"
36#include "llframetimer.h"
37
38class LLVFS;
39class LLVorbisDecodeState;
40
41
42class LLAudioDecodeMgr
43{
44public:
45 LLAudioDecodeMgr();
46 ~LLAudioDecodeMgr();
47
48 void processQueue(const F32 num_secs = 0.005);
49
50 LLLinkedQueue<LLUUID> mDecodeQueue;
51
52 LLVorbisDecodeState *mCurrentDecodep;
53
54 BOOL addDecodeRequest(const LLUUID &uuid);
55 void addAudioRequest(const LLUUID &uuid);
56
57 S32 getRequestCount();
58
59protected:
60 LLLinkedQueue<LLUUID> mDownloadQueue;
61 LLFrameTimer mCurrentTransferAge;
62};
63
64extern LLAudioDecodeMgr *gAudioDecodeMgrp;
65
66#endif
diff --git a/linden/indra/llaudio/vorbisdecode.cpp b/linden/indra/llaudio/vorbisdecode.cpp
new file mode 100644
index 0000000..4308b71
--- /dev/null
+++ b/linden/indra/llaudio/vorbisdecode.cpp
@@ -0,0 +1,321 @@
1/**
2 * @file vorbisdecode.cpp
3 * @brief Vorbis decoding routine routine for Indra.
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <math.h>
33
34#include "vorbis/codec.h"
35#include "vorbis/vorbisfile.h"
36#include "llerror.h"
37#include "llmath.h"
38#include "llvfile.h"
39
40#if 0
41
42size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource)
43{
44 LLVFile *file = (LLVFile *)datasource;
45
46 if (size > 0 && file->read((U8*)ptr, size * nmemb)) /*Flawfinder: ignore*/
47 {
48 S32 read = file->getLastBytesRead();
49 return read / size; /*Flawfinder: ignore*/
50 }
51 else
52 {
53 return 0;
54 }
55}
56
57int vfs_seek(void *datasource, ogg_int64_t offset, int whence)
58{
59 LLVFile *file = (LLVFile *)datasource;
60
61 // vfs has 31-bit files
62 if (offset > S32_MAX)
63 {
64 return -1;
65 }
66
67 S32 origin;
68 switch (whence) {
69 case SEEK_SET:
70 origin = 0;
71 break;
72 case SEEK_END:
73 origin = file->getSize();
74 break;
75 case SEEK_CUR:
76 origin = -1;
77 break;
78 default:
79 llerrs << "Invalid whence argument to vfs_seek" << llendl;
80 return -1;
81 }
82
83 if (file->seek((S32)offset, origin))
84 {
85 return 0;
86 }
87 else
88 {
89 return -1;
90 }
91}
92
93int vfs_close (void *datasource)
94{
95 LLVFile *file = (LLVFile *)datasource;
96
97 delete file;
98
99 return 0;
100}
101
102long vfs_tell (void *datasource)
103{
104 LLVFile *file = (LLVFile *)datasource;
105
106 return file->tell();
107}
108
109
110BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname)
111{
112 ov_callbacks vfs_callbacks;
113 vfs_callbacks.read_func = vfs_read;
114 vfs_callbacks.seek_func = vfs_seek;
115 vfs_callbacks.close_func = vfs_close;
116 vfs_callbacks.tell_func = vfs_tell;
117
118 char pcmout[4096]; /*Flawfinder: ignore*/
119
120 unsigned char temp[64]; /*Flawfinder: ignore*/
121
122 LLVFile *in_vfile;
123
124 U32 data_length = 0;
125
126 llinfos << "Vorbis decode from vfile: " << in_uuid << llendl;
127
128 in_vfile = new LLVFile(vfs, in_uuid, LLAssetType::AT_SOUND);
129 if (! in_vfile->getSize())
130 {
131 llwarning("unable to open vorbis source vfile for reading",0);
132 return(FALSE);
133 }
134 apr_file_t* outfp = ll_apr_file_open(out_fname,LL_APR_WPB);
135 if (!outfp)
136 {
137 llwarning("unable to open vorbis destination file for writing",0);
138 return(FALSE);
139 }
140 else
141 {
142 // write the .wav format header
143 //"RIFF"
144 temp[0] = 0x52;
145 temp[1] = 0x49;
146 temp[2] = 0x46;
147 temp[3] = 0x46;
148
149 // length = datalen + 36 (to be filled in later)
150 temp[4] = 0x00;
151 temp[5] = 0x00;
152 temp[6] = 0x00;
153 temp[7] = 0x00;
154
155 //"WAVE"
156 temp[8] = 0x57;
157 temp[9] = 0x41;
158 temp[10] = 0x56;
159 temp[11] = 0x45;
160
161 // "fmt "
162 temp[12] = 0x66;
163 temp[13] = 0x6D;
164 temp[14] = 0x74;
165 temp[15] = 0x20;
166
167 // chunk size = 16
168 temp[16] = 0x10;
169 temp[17] = 0x00;
170 temp[18] = 0x00;
171 temp[19] = 0x00;
172
173 // format (1 = PCM)
174 temp[20] = 0x01;
175 temp[21] = 0x00;
176
177 // number of channels
178 temp[22] = 0x01;
179 temp[23] = 0x00;
180
181 // samples per second
182 temp[24] = 0x44;
183 temp[25] = 0xAC;
184 temp[26] = 0x00;
185 temp[27] = 0x00;
186
187 // average bytes per second
188 temp[28] = 0x88;
189 temp[29] = 0x58;
190 temp[30] = 0x01;
191 temp[31] = 0x00;
192
193 // bytes to output at a single time
194 temp[32] = 0x02;
195 temp[33] = 0x00;
196
197 // 16 bits per sample
198 temp[34] = 0x10;
199 temp[35] = 0x00;
200
201 // "data"
202 temp[36] = 0x64;
203 temp[37] = 0x61;
204 temp[38] = 0x74;
205 temp[39] = 0x61;
206
207
208 // these are the length of the data chunk, to be filled in later
209 temp[40] = 0x00;
210 temp[41] = 0x00;
211 temp[42] = 0x00;
212 temp[43] = 0x00;
213
214
215 ll_apr_file_write(outfp, temp, 44);
216 }
217
218 OggVorbis_File vf;
219 int eof=0;
220 int current_section;
221
222 int r = ov_open_callbacks(in_vfile, &vf, NULL, 0, vfs_callbacks);
223 if(r < 0)
224 {
225 llwarns << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << in_uuid << llendl;
226 return(FALSE);
227 }
228
229 {
230 char **ptr=ov_comment(&vf,-1)->user_comments;
231// vorbis_info *vi=ov_info(&vf,-1);
232 while(*ptr){
233 fprintf(stderr,"%s\n",*ptr);
234 ++ptr;
235 }
236// fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate);
237// fprintf(stderr,"\nDecoded length: %ld samples\n", (long)ov_pcm_total(&vf,-1));
238// fprintf(stderr,"Encoded by: %s\n\n",ov_comment(&vf,-1)->vendor);
239 }
240
241 while(!eof){
242 long ret=ov_read(&vf,pcmout,sizeof(pcmout),0,2,1,&current_section);
243 if (ret == 0) {
244 /* EOF */
245 eof=1;
246// llinfos << "Vorbis EOF" << llendl;
247 } else if (ret < 0) {
248 /* error in the stream. Not a problem, just reporting it in
249 case we (the app) cares. In this case, we don't. */
250 llwarning("Error in vorbis stream",0);
251 break;
252
253 } else {
254// llinfos << "Vorbis read " << ret << "bytes" << llendl;
255 /* we don't bother dealing with sample rate changes, etc, but.
256 you'll have to*/
257 data_length += ll_apr_file_write(outfp, pcmout, ret);
258 }
259 }
260
261 ov_clear(&vf);
262
263 // write "data" chunk length
264 ll_apr_file_seek(outfp,APR_SET,40);
265 ll_apr_file_write(outfp,&data_length,4);
266
267 // write overall "RIFF" length
268 data_length += 36;
269 ll_apr_file_seek(outfp,APR_SET,4);
270 ll_apr_file_write(outfp,&data_length,1*4);
271
272 // FUCK!!! Vorbis encode/decode messes up loop point transitions (pop)
273 // do a cheap-and-cheesy crossfade
274
275
276 S16 *samplep;
277 S32 i;
278 S32 fade_length;
279
280 fade_length = llmin((S32)128,(S32)(data_length-36)/8);
281
282 ll_apr_file_seek(outfp,APR_SET,44);
283 ll_apr_file_read(outfp, pcmout,2*fade_length); //read first 16 samples
284
285 samplep = (S16 *)pcmout;
286
287 for (i = 0 ;i < fade_length; i++)
288 {
289 *samplep++ = ((F32)*samplep * ((F32)i/(F32)fade_length));
290 }
291
292 ll_apr_file_seek(outfp,APR_SET,44);
293 ll_apr_file_write(outfp,pcmout,2*fade_length); //write back xfaded first 16 samples
294
295 ll_apr_file_seek(outfp,APR_END,-fade_length*2);
296 ll_apr_file_read(outfp, pcmout,2*fade_length); //read last 16 samples
297
298 samplep = (S16 *)pcmout;
299
300 for (i = fade_length-1 ; i >= 0; i--)
301 {
302 *samplep++ = ((F32)*samplep * ((F32)i/(F32)fade_length));
303 }
304
305 ll_apr_file_seek(outfp,SEEK_END,-fade_length*2);
306 ll_apr_file_write(outfp,pcmout,2*fade_length); //write back xfaded last 16 samples
307
308 apr_file_close(outfp);
309
310 if ((36 == data_length) || (!(eof)))
311 {
312 llwarning("BAD Vorbis DECODE!, removing .wav!",0);
313 LLFile::remove(out_fname);
314 return (FALSE);
315 }
316
317// fprintf(stderr,"Done.\n");
318
319 return(TRUE);
320}
321#endif
diff --git a/linden/indra/llaudio/vorbisdecode.h b/linden/indra/llaudio/vorbisdecode.h
new file mode 100644
index 0000000..e5f1b26
--- /dev/null
+++ b/linden/indra/llaudio/vorbisdecode.h
@@ -0,0 +1,37 @@
1/**
2 * @file vorbisdecode.h
3 * @brief Vorbis decoding routine routine for Indra.
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_VORBISDECODE_H
29#define LL_VORBISDECODE_H
30
31class LLVFS;
32class LLUUID;
33
34BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname);
35
36#endif
37
diff --git a/linden/indra/llaudio/vorbisencode.cpp b/linden/indra/llaudio/vorbisencode.cpp
new file mode 100644
index 0000000..6007940
--- /dev/null
+++ b/linden/indra/llaudio/vorbisencode.cpp
@@ -0,0 +1,506 @@
1/**
2 * @file vorbisencode.cpp
3 * @brief Vorbis encoding routine routine for Indra.
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#include "linden_common.h"
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <math.h>
33
34#include "vorbisencode.h"
35#include "vorbis/vorbisenc.h"
36#include "llerror.h"
37#include "llrand.h"
38#include "llmath.h"
39#include "llapr.h"
40
41//#if LL_DARWIN
42// MBW -- XXX -- Getting rid of SecondLifeVorbis for now -- no fmod means no name collisions.
43#if 0
44#include "VorbisFramework.h"
45
46#define vorbis_analysis mac_vorbis_analysis
47#define vorbis_analysis_headerout mac_vorbis_analysis_headerout
48#define vorbis_analysis_init mac_vorbis_analysis_init
49#define vorbis_encode_ctl mac_vorbis_encode_ctl
50#define vorbis_encode_setup_init mac_vorbis_encode_setup_init
51#define vorbis_encode_setup_managed mac_vorbis_encode_setup_managed
52
53#define vorbis_info_init mac_vorbis_info_init
54#define vorbis_info_clear mac_vorbis_info_clear
55#define vorbis_comment_init mac_vorbis_comment_init
56#define vorbis_comment_clear mac_vorbis_comment_clear
57#define vorbis_block_init mac_vorbis_block_init
58#define vorbis_block_clear mac_vorbis_block_clear
59#define vorbis_dsp_clear mac_vorbis_dsp_clear
60#define vorbis_analysis_buffer mac_vorbis_analysis_buffer
61#define vorbis_analysis_wrote mac_vorbis_analysis_wrote
62#define vorbis_analysis_blockout mac_vorbis_analysis_blockout
63
64#define ogg_stream_packetin mac_ogg_stream_packetin
65#define ogg_stream_init mac_ogg_stream_init
66#define ogg_stream_flush mac_ogg_stream_flush
67#define ogg_stream_pageout mac_ogg_stream_pageout
68#define ogg_page_eos mac_ogg_page_eos
69#define ogg_stream_clear mac_ogg_stream_clear
70
71#endif
72
73S32 check_for_invalid_wav_formats(const char *in_fname, char *error_msg)
74{
75 U16 num_channels = 0;
76 U32 sample_rate = 0;
77 U32 bits_per_sample = 0;
78 U32 physical_file_size = 0;
79 U32 chunk_length = 0;
80 U32 raw_data_length = 0;
81 U32 bytes_per_sec = 0;
82 BOOL uncompressed_pcm = FALSE;
83
84 unsigned char wav_header[44]; /*Flawfinder: ignore*/
85
86 error_msg[0] = '\0';
87
88 apr_file_t* infp = ll_apr_file_open(in_fname,LL_APR_RB);
89 if (!infp)
90 {
91 strcpy(error_msg, "CannotUploadSoundFile"); /*Flawfinder: ignore*/
92 return(LLVORBISENC_SOURCE_OPEN_ERR);
93 }
94
95 ll_apr_file_read(infp, wav_header, 44);
96 physical_file_size = ll_apr_file_seek(infp,APR_END,0);
97
98 if (strncmp((char *)&(wav_header[0]),"RIFF",4))
99 {
100 strcpy(error_msg, "SoundFileNotRIFF"); /*Flawfinder: ignore*/
101 apr_file_close(infp);
102 return(LLVORBISENC_WAV_FORMAT_ERR);
103 }
104
105 if (strncmp((char *)&(wav_header[8]),"WAVE",4))
106 {
107 strcpy(error_msg, "SoundFileNotRIFF"); /*Flawfinder: ignore*/
108 apr_file_close(infp);
109 return(LLVORBISENC_WAV_FORMAT_ERR);
110 }
111
112 // parse the chunks
113
114 U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
115
116 while ((file_pos + 8)< physical_file_size)
117 {
118 ll_apr_file_seek(infp,APR_SET,file_pos);
119 ll_apr_file_read(infp, wav_header, 44);
120
121 chunk_length = ((U32) wav_header[7] << 24)
122 + ((U32) wav_header[6] << 16)
123 + ((U32) wav_header[5] << 8)
124 + wav_header[4];
125
126// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
127
128 if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
129 {
130 if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
131 {
132 uncompressed_pcm = TRUE;
133 }
134 num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
135 sample_rate = ((U32) wav_header[15] << 24)
136 + ((U32) wav_header[14] << 16)
137 + ((U32) wav_header[13] << 8)
138 + wav_header[12];
139 bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
140 bytes_per_sec = ((U32) wav_header[19] << 24)
141 + ((U32) wav_header[18] << 16)
142 + ((U32) wav_header[17] << 8)
143 + wav_header[16];
144 }
145 else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
146 {
147 raw_data_length = chunk_length;
148 }
149 file_pos += (chunk_length + 8);
150 chunk_length = 0;
151 }
152
153 apr_file_close(infp);
154
155 if (!uncompressed_pcm)
156 {
157 strcpy(error_msg, "SoundFileNotPCM"); /*Flawfinder: ignore*/
158 return(LLVORBISENC_PCM_FORMAT_ERR);
159 }
160
161 if ((num_channels < 1) || (num_channels > 2))
162 {
163 strcpy(error_msg, "SoundFileInvalidChannelCount"); /*Flawfinder: ignore*/
164 return(LLVORBISENC_MULTICHANNEL_ERR);
165 }
166
167 if (sample_rate != 44100)
168 {
169 strcpy(error_msg, "SoundFileInvalidSampleRate"); /*Flawfinder: ignore*/
170 return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
171 }
172
173 if ((bits_per_sample != 16) && (bits_per_sample != 8))
174 {
175 strcpy(error_msg, "SoundFileInvalidWordSize"); /*Flawfinder: ignore*/
176 return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
177 }
178
179 if (!raw_data_length)
180 {
181 strcpy(error_msg, "SoundFileInvalidHeader"); /*Flawfinder: ignore*/
182 return(LLVORBISENC_CLIP_TOO_LONG);
183 }
184
185 F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
186
187 if (clip_length > 10.0f)
188 {
189 strcpy(error_msg, "SoundFileInvalidTooLong"); /*Flawfinder: ignore*/
190 return(LLVORBISENC_CLIP_TOO_LONG);
191 }
192
193 return(LLVORBISENC_NOERR);
194}
195
196S32 encode_vorbis_file(const char *in_fname, const char *out_fname)
197{
198 return(encode_vorbis_file_at(in_fname,out_fname, 128000));
199}
200
201S32 encode_vorbis_file_at(const char *in_fname, const char *out_fname, S32 bitrate)
202{
203#define READ_BUFFER 1024
204 unsigned char readbuffer[READ_BUFFER*4+44]; /* out of the data segment, not the stack */ /*Flawfinder: ignore*/
205
206 ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
207 ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
208 ogg_packet op; /* one raw packet of data for decode */
209
210 vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
211 vorbis_comment vc; /* struct that stores all the user comments */
212
213 vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
214 vorbis_block vb; /* local working space for packet->PCM decode */
215
216 int eos=0;
217 int result;
218
219 U16 num_channels = 0;
220 U32 sample_rate = 0;
221 U32 bits_per_sample = 0;
222
223 S32 format_error = 0;
224 char error_msg[MAX_STRING]; /*Flawfinder: ignore*/
225 if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
226 {
227 llwarns << error_msg << ": " << in_fname << llendl;
228 return(format_error);
229 }
230
231#if 1
232 unsigned char wav_header[44]; /*Flawfinder: ignore*/
233
234 S32 data_left = 0;
235
236 apr_file_t* infp = ll_apr_file_open(in_fname,LL_APR_RB);
237 if (!infp)
238 {
239 llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
240 << llendl;
241 return(LLVORBISENC_SOURCE_OPEN_ERR);
242 }
243 apr_file_t* outfp = ll_apr_file_open(out_fname,LL_APR_WPB);
244 if (!outfp)
245 {
246 llwarns << "Couldn't open upload sound file for reading: " << in_fname
247 << llendl;
248 apr_file_close (infp);
249 return(LLVORBISENC_DEST_OPEN_ERR);
250 }
251
252 // parse the chunks
253 U32 chunk_length = 0;
254 U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
255
256 while (apr_file_eof(infp) != APR_EOF)
257 {
258 ll_apr_file_seek(infp,APR_SET,file_pos);
259 ll_apr_file_read(infp, wav_header, 44);
260
261 chunk_length = ((U32) wav_header[7] << 24)
262 + ((U32) wav_header[6] << 16)
263 + ((U32) wav_header[5] << 8)
264 + wav_header[4];
265
266// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
267
268 if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
269 {
270 num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
271 sample_rate = ((U32) wav_header[15] << 24)
272 + ((U32) wav_header[14] << 16)
273 + ((U32) wav_header[13] << 8)
274 + wav_header[12];
275 bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
276 }
277 else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
278 {
279 ll_apr_file_seek(infp,APR_SET,file_pos+8);
280 // leave the file pointer at the beginning of the data chunk data
281 data_left = chunk_length;
282 break;
283 }
284 file_pos += (chunk_length + 8);
285 chunk_length = 0;
286 }
287
288// apr_file_close(infp);
289
290 /********** Encode setup ************/
291
292 /* choose an encoding mode */
293 /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
294 vorbis_info_init(&vi);
295
296 // always encode to mono
297// vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1);
298// if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
299
300// F32 quality = 0;
301// quality = (bitrate==128000 ? 0.4f : 0.1);
302
303 if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
304// if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
305// if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
306// vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
307// vorbis_encode_setup_init(&vi))
308 {
309// llwarns << "unable to initialize vorbis codec at quality " << quality << llendl;
310 llwarns << "unable to initialize vorbis codec at bitrate " << bitrate << llendl;
311 return(LLVORBISENC_DEST_OPEN_ERR);
312 }
313
314 /* add a comment */
315 vorbis_comment_init(&vc);
316// vorbis_comment_add(&vc,"Linden");
317
318 /* set up the analysis state and auxiliary encoding storage */
319 vorbis_analysis_init(&vd,&vi);
320 vorbis_block_init(&vd,&vb);
321
322 /* set up our packet->stream encoder */
323 /* pick a random serial number; that way we can more likely build
324 chained streams just by concatenation */
325 ogg_stream_init(&os,(int)frand(0xFFFFFF));
326
327 /* Vorbis streams begin with three headers; the initial header (with
328 most of the codec setup parameters) which is mandated by the Ogg
329 bitstream spec. The second header holds any comment fields. The
330 third header holds the bitstream codebook. We merely need to
331 make the headers, then pass them to libvorbis one at a time;
332 libvorbis handles the additional Ogg bitstream constraints */
333
334 {
335 ogg_packet header;
336 ogg_packet header_comm;
337 ogg_packet header_code;
338
339 vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
340 ogg_stream_packetin(&os,&header); /* automatically placed in its own
341 page */
342 ogg_stream_packetin(&os,&header_comm);
343 ogg_stream_packetin(&os,&header_code);
344
345 /* We don't have to write out here, but doing so makes streaming
346 * much easier, so we do, flushing ALL pages. This ensures the actual
347 * audio data will start on a new page
348 */
349 while(!eos){
350 int result=ogg_stream_flush(&os,&og);
351 if(result==0)break;
352 ll_apr_file_write(outfp, og.header, og.header_len);
353 ll_apr_file_write(outfp, og.body, og.body_len);
354 }
355
356 }
357
358
359 while(!eos)
360 {
361 long bytes_per_sample = bits_per_sample/8;
362
363 long bytes=(long)ll_apr_file_read(infp, readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
364
365 if (bytes==0)
366 {
367 /* end of file. this can be done implicitly in the mainline,
368 but it's easier to see here in non-clever fashion.
369 Tell the library we're at end of stream so that it can handle
370 the last frame and mark end of stream in the output properly */
371
372 vorbis_analysis_wrote(&vd,0);
373// eos = 1;
374
375 }
376 else
377 {
378 long i;
379 long samples;
380 int temp;
381
382 data_left -= bytes;
383 /* data to encode */
384
385 /* expose the buffer to submit data */
386 float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
387
388 i = 0;
389 samples = bytes / (num_channels * bytes_per_sample);
390
391 if (num_channels == 2)
392 {
393 if (bytes_per_sample == 2)
394 {
395 /* uninterleave samples */
396 for(i=0; i<samples ;i++)
397 {
398 temp = ((signed char *)readbuffer)[i*4+1]; /*Flawfinder: ignore*/
399 temp += ((signed char *)readbuffer)[i*4+3]; /*Flawfinder: ignore*/
400 temp <<= 8;
401 temp += readbuffer[i*4];
402 temp += readbuffer[i*4+2];
403
404 buffer[0][i] = ((float)temp) / 65536.f;
405 }
406 }
407 else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
408 {
409 /* uninterleave samples */
410 for(i=0; i<samples ;i++)
411 {
412 temp = readbuffer[i*2+0];
413 temp += readbuffer[i*2+1];
414 temp -= 256;
415 buffer[0][i] = ((float)temp) / 256.f;
416 }
417 }
418 }
419 else if (num_channels == 1)
420 {
421 if (bytes_per_sample == 2)
422 {
423 for(i=0; i < samples ;i++)
424 {
425 temp = ((signed char*)readbuffer)[i*2+1];
426 temp <<= 8;
427 temp += readbuffer[i*2];
428 buffer[0][i] = ((float)temp) / 32768.f;
429 }
430 }
431 else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
432 {
433 for(i=0; i < samples ;i++)
434 {
435 temp = readbuffer[i];
436 temp -= 128;
437 buffer[0][i] = ((float)temp) / 128.f;
438 }
439 }
440 }
441
442 /* tell the library how much we actually submitted */
443 vorbis_analysis_wrote(&vd,i);
444 }
445
446 /* vorbis does some data preanalysis, then divvies up blocks for
447 more involved (potentially parallel) processing. Get a single
448 block for encoding now */
449 while(vorbis_analysis_blockout(&vd,&vb)==1)
450 {
451
452 /* analysis */
453 /* Do the main analysis, creating a packet */
454 vorbis_analysis(&vb, NULL);
455 vorbis_bitrate_addblock(&vb);
456
457 while(vorbis_bitrate_flushpacket(&vd, &op))
458 {
459
460 /* weld the packet into the bitstream */
461 ogg_stream_packetin(&os,&op);
462
463 /* write out pages (if any) */
464 while(!eos)
465 {
466 result = ogg_stream_pageout(&os,&og);
467
468 if(result==0)
469 break;
470
471 ll_apr_file_write(outfp, og.header, og.header_len);
472 ll_apr_file_write(outfp, og.body, og.body_len);
473
474 /* this could be set above, but for illustrative purposes, I do
475 it here (to show that vorbis does know where the stream ends) */
476
477 if(ogg_page_eos(&og))
478 eos=1;
479
480 }
481 }
482 }
483 }
484
485
486
487 /* clean up and exit. vorbis_info_clear() must be called last */
488
489 ogg_stream_clear(&os);
490 vorbis_block_clear(&vb);
491 vorbis_dsp_clear(&vd);
492 vorbis_comment_clear(&vc);
493 vorbis_info_clear(&vi);
494
495 /* ogg_page and ogg_packet structs always point to storage in
496 libvorbis. They're never freed or manipulated directly */
497
498// fprintf(stderr,"Vorbis encoding: Done.\n");
499 llinfos << "Vorbis encoding: Done." << llendl;
500 apr_file_close(outfp);
501 apr_file_close(infp);
502
503#endif
504 return(LLVORBISENC_NOERR);
505
506}
diff --git a/linden/indra/llaudio/vorbisencode.h b/linden/indra/llaudio/vorbisencode.h
new file mode 100644
index 0000000..3813c50
--- /dev/null
+++ b/linden/indra/llaudio/vorbisencode.h
@@ -0,0 +1,49 @@
1/**
2 * @file vorbisencode.h
3 * @brief Vorbis encoding routine routine for Indra.
4 *
5 * Copyright (c) 2000-2007, Linden Research, Inc.
6 *
7 * The source code in this file ("Source Code") is provided by Linden Lab
8 * to you under the terms of the GNU General Public License, version 2.0
9 * ("GPL"), unless you have obtained a separate licensing agreement
10 * ("Other License"), formally executed by you and Linden Lab. Terms of
11 * the GPL can be found in doc/GPL-license.txt in this distribution, or
12 * online at http://secondlife.com/developers/opensource/gplv2
13 *
14 * There are special exceptions to the terms and conditions of the GPL as
15 * it is applied to this Source Code. View the full text of the exception
16 * in the file doc/FLOSS-exception.txt in this software distribution, or
17 * online at http://secondlife.com/developers/opensource/flossexception
18 *
19 * By copying, modifying or distributing this software, you acknowledge
20 * that you have read and understood your obligations described above,
21 * and agree to abide by those obligations.
22 *
23 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
24 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
25 * COMPLETENESS OR PERFORMANCE.
26 */
27
28#ifndef LL_VORBISENCODE_H
29#define LL_VORBISENCODE_H
30
31const S32 LLVORBISENC_NOERR = 0; // no error
32const S32 LLVORBISENC_SOURCE_OPEN_ERR = 1; // error opening source
33const S32 LLVORBISENC_DEST_OPEN_ERR = 2; // error opening destination
34const S32 LLVORBISENC_WAV_FORMAT_ERR = 3; // not a WAV
35const S32 LLVORBISENC_PCM_FORMAT_ERR = 4; // not a PCM
36const S32 LLVORBISENC_MONO_ERR = 5; // can't do mono
37const S32 LLVORBISENC_STEREO_ERR = 6; // can't do stereo
38const S32 LLVORBISENC_MULTICHANNEL_ERR = 7; // can't do stereo
39const S32 LLVORBISENC_UNSUPPORTED_SAMPLE_RATE = 8; // unsupported sample rate
40const S32 LLVORBISENC_UNSUPPORTED_WORD_SIZE = 9; // unsupported word size
41const S32 LLVORBISENC_CLIP_TOO_LONG = 10; // source file is too long
42
43
44S32 check_for_invalid_wav_formats(const char *in_fname, char *error_msg);
45S32 encode_vorbis_file(const char *in_fname, const char *out_fname);
46S32 encode_vorbis_file_at(const char *in_fname, const char *out_fname, S32 bitrate);
47
48#endif
49