diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llaudio | |
parent | README.txt (diff) | |
download | meta-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.cpp | 1672 | ||||
-rw-r--r-- | linden/indra/llaudio/audioengine.h | 428 | ||||
-rw-r--r-- | linden/indra/llaudio/audioengine_fmod.cpp | 1176 | ||||
-rw-r--r-- | linden/indra/llaudio/audioengine_fmod.h | 158 | ||||
-rw-r--r-- | linden/indra/llaudio/files.lst | 7 | ||||
-rw-r--r-- | linden/indra/llaudio/listener.cpp | 153 | ||||
-rw-r--r-- | linden/indra/llaudio/listener.h | 78 | ||||
-rw-r--r-- | linden/indra/llaudio/listener_ds3d.h | 71 | ||||
-rw-r--r-- | linden/indra/llaudio/listener_fmod.cpp | 145 | ||||
-rw-r--r-- | linden/indra/llaudio/listener_fmod.h | 66 | ||||
-rw-r--r-- | linden/indra/llaudio/listener_openal.h | 56 | ||||
-rw-r--r-- | linden/indra/llaudio/llaudio.vcproj | 223 | ||||
-rw-r--r-- | linden/indra/llaudio/llaudiodecodemgr.cpp | 635 | ||||
-rw-r--r-- | linden/indra/llaudio/llaudiodecodemgr.h | 66 | ||||
-rw-r--r-- | linden/indra/llaudio/vorbisdecode.cpp | 321 | ||||
-rw-r--r-- | linden/indra/llaudio/vorbisdecode.h | 37 | ||||
-rw-r--r-- | linden/indra/llaudio/vorbisencode.cpp | 506 | ||||
-rw-r--r-- | linden/indra/llaudio/vorbisencode.h | 49 |
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) | ||
55 | extern void request_sound(const LLUUID &sound_guid); | ||
56 | |||
57 | LLAudioEngine* gAudiop = NULL; | ||
58 | |||
59 | // Maximum amount of time we wait for a transfer to complete before starting | ||
60 | // off another one. | ||
61 | const F32 MAX_CURRENT_TRANSFER_TIME = 60.f; | ||
62 | |||
63 | // | ||
64 | // LLAudioEngine implementation | ||
65 | // | ||
66 | |||
67 | |||
68 | LLAudioEngine::LLAudioEngine() | ||
69 | { | ||
70 | } | ||
71 | |||
72 | |||
73 | LLAudioEngine::~LLAudioEngine() | ||
74 | { | ||
75 | } | ||
76 | |||
77 | |||
78 | BOOL 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 | |||
112 | void 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 | |||
163 | void 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 | |||
177 | static const F32 default_max_decode_time = .002f; // 2 ms | ||
178 | void 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 | |||
442 | BOOL 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 | |||
476 | void 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 | |||
491 | LLAudioBuffer *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 | |||
535 | LLAudioChannel * 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 | |||
590 | void 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 | |||
604 | BOOL 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 | |||
622 | BOOL LLAudioEngine::isWindEnabled() | ||
623 | { | ||
624 | return mEnableWind; | ||
625 | } | ||
626 | |||
627 | |||
628 | void LLAudioEngine::setMuted(BOOL muted) | ||
629 | { | ||
630 | mMuted = muted; | ||
631 | enableWind(!mMuted); | ||
632 | } | ||
633 | |||
634 | |||
635 | void LLAudioEngine::setMasterGain(const F32 gain) | ||
636 | { | ||
637 | mMasterGain = gain; | ||
638 | setInternalGain(gain); | ||
639 | } | ||
640 | |||
641 | F32 LLAudioEngine::getMasterGain() | ||
642 | { | ||
643 | return mMasterGain; | ||
644 | } | ||
645 | |||
646 | F32 LLAudioEngine::getInternetStreamGain() | ||
647 | { | ||
648 | return mInternetStreamGain; | ||
649 | } | ||
650 | |||
651 | void LLAudioEngine::setMaxWindGain(F32 gain) | ||
652 | { | ||
653 | mMaxWindGain = gain; | ||
654 | } | ||
655 | |||
656 | |||
657 | F64 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 | |||
676 | F64 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 | |||
700 | F64 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 | |||
721 | void 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 | |||
749 | void LLAudioEngine::setListenerPos(LLVector3 aVec) | ||
750 | { | ||
751 | mListenerp->setPosition(aVec); | ||
752 | } | ||
753 | |||
754 | |||
755 | LLVector3 LLAudioEngine::getListenerPos() | ||
756 | { | ||
757 | if (mListenerp) | ||
758 | { | ||
759 | return(mListenerp->getPosition()); | ||
760 | } | ||
761 | else | ||
762 | { | ||
763 | return(LLVector3::zero); | ||
764 | } | ||
765 | } | ||
766 | |||
767 | |||
768 | void LLAudioEngine::setListenerVelocity(LLVector3 aVec) | ||
769 | { | ||
770 | mListenerp->setVelocity(aVec); | ||
771 | } | ||
772 | |||
773 | |||
774 | void LLAudioEngine::translateListener(LLVector3 aVec) | ||
775 | { | ||
776 | mListenerp->translate(aVec); | ||
777 | } | ||
778 | |||
779 | |||
780 | void LLAudioEngine::orientListener(LLVector3 up, LLVector3 at) | ||
781 | { | ||
782 | mListenerp->orient(up, at); | ||
783 | } | ||
784 | |||
785 | |||
786 | void LLAudioEngine::setListener(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at) | ||
787 | { | ||
788 | mListenerp->set(pos,vel,up,at); | ||
789 | } | ||
790 | |||
791 | |||
792 | void LLAudioEngine::setDopplerFactor(F32 factor) | ||
793 | { | ||
794 | if (mListenerp) | ||
795 | { | ||
796 | mListenerp->setDopplerFactor(factor); | ||
797 | } | ||
798 | } | ||
799 | |||
800 | |||
801 | F32 LLAudioEngine::getDopplerFactor() | ||
802 | { | ||
803 | if (mListenerp) | ||
804 | { | ||
805 | return mListenerp->getDopplerFactor(); | ||
806 | } | ||
807 | else | ||
808 | { | ||
809 | return 0.f; | ||
810 | } | ||
811 | } | ||
812 | |||
813 | |||
814 | void LLAudioEngine::setDistanceFactor(F32 factor) | ||
815 | { | ||
816 | if (mListenerp) | ||
817 | { | ||
818 | mListenerp->setDistanceFactor(factor); | ||
819 | } | ||
820 | } | ||
821 | |||
822 | |||
823 | F32 LLAudioEngine::getDistanceFactor() | ||
824 | { | ||
825 | if (mListenerp) | ||
826 | { | ||
827 | return mListenerp->getDistanceFactor(); | ||
828 | } | ||
829 | else | ||
830 | { | ||
831 | return 0.f; | ||
832 | } | ||
833 | } | ||
834 | |||
835 | |||
836 | void LLAudioEngine::setRolloffFactor(F32 factor) | ||
837 | { | ||
838 | if (mListenerp) | ||
839 | { | ||
840 | mListenerp->setRolloffFactor(factor); | ||
841 | } | ||
842 | } | ||
843 | |||
844 | |||
845 | F32 LLAudioEngine::getRolloffFactor() | ||
846 | { | ||
847 | if (mListenerp) | ||
848 | { | ||
849 | return mListenerp->getRolloffFactor(); | ||
850 | } | ||
851 | else | ||
852 | { | ||
853 | return 0.f; | ||
854 | } | ||
855 | } | ||
856 | |||
857 | |||
858 | void LLAudioEngine::commitDeferredChanges() | ||
859 | { | ||
860 | mListenerp->commitDeferredChanges(); | ||
861 | } | ||
862 | |||
863 | |||
864 | LLAudioSource *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 | |||
880 | LLAudioData *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 | |||
897 | void LLAudioEngine::addAudioSource(LLAudioSource *asp) | ||
898 | { | ||
899 | mAllSources[asp->getID()] = asp; | ||
900 | } | ||
901 | |||
902 | |||
903 | void 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 | |||
917 | BOOL 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 | |||
937 | BOOL 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 | |||
944 | void 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 | ||
1144 | void 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 | |||
1183 | LLAudioSource::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 | |||
1201 | LLAudioSource::~LLAudioSource() | ||
1202 | { | ||
1203 | if (mChannelp) | ||
1204 | { | ||
1205 | // Stop playback of this sound | ||
1206 | mChannelp->setSource(NULL); | ||
1207 | mChannelp = NULL; | ||
1208 | } | ||
1209 | } | ||
1210 | |||
1211 | |||
1212 | void LLAudioSource::setChannel(LLAudioChannel *channelp) | ||
1213 | { | ||
1214 | if (channelp == mChannelp) | ||
1215 | { | ||
1216 | return; | ||
1217 | } | ||
1218 | |||
1219 | mChannelp = channelp; | ||
1220 | } | ||
1221 | |||
1222 | |||
1223 | void 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 | |||
1239 | void 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 | |||
1257 | BOOL 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 | |||
1291 | BOOL 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 | |||
1336 | BOOL 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 | |||
1400 | void 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 | |||
1468 | BOOL 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 | |||
1486 | LLAudioData *LLAudioSource::getCurrentData() | ||
1487 | { | ||
1488 | return mCurrentDatap; | ||
1489 | } | ||
1490 | |||
1491 | LLAudioData *LLAudioSource::getQueuedData() | ||
1492 | { | ||
1493 | return mQueuedDatap; | ||
1494 | } | ||
1495 | |||
1496 | LLAudioBuffer *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 | |||
1514 | LLAudioChannel::LLAudioChannel() : | ||
1515 | mCurrentSourcep(NULL), | ||
1516 | mCurrentBufferp(NULL), | ||
1517 | mLoopedThisFrame(FALSE), | ||
1518 | mWaiting(FALSE) | ||
1519 | { | ||
1520 | } | ||
1521 | |||
1522 | |||
1523 | LLAudioChannel::~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 | |||
1535 | void 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 | |||
1561 | BOOL 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 | |||
1612 | LLAudioData::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 | |||
1638 | BOOL 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 | |||
48 | const F32 LL_WIND_UPDATE_INTERVAL = 0.1f; | ||
49 | const F32 LL_ROLLOFF_MULTIPLIER_UNDER_WATER = 5.f; // How much sounds are weaker under water | ||
50 | const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f; | ||
51 | |||
52 | const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f; | ||
53 | const 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 | |||
65 | class LLVFS; | ||
66 | |||
67 | class LLAudioSource; | ||
68 | class LLAudioData; | ||
69 | class LLAudioChannel; | ||
70 | class LLAudioBuffer; | ||
71 | |||
72 | |||
73 | |||
74 | // | ||
75 | // LLAudioEngine definition | ||
76 | // | ||
77 | |||
78 | class LLAudioEngine | ||
79 | { | ||
80 | public: | ||
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 | ||
163 | public: | ||
164 | F32 mMaxWindGain; // Hack. Public to set before fade in? | ||
165 | |||
166 | protected: | ||
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 | |||
195 | protected: | ||
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 | |||
242 | class LLAudioSource | ||
243 | { | ||
244 | public: | ||
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; | ||
297 | protected: | ||
298 | void setChannel(LLAudioChannel *channelp); | ||
299 | LLAudioChannel *getChannel() const { return mChannelp; } | ||
300 | |||
301 | protected: | ||
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 | |||
336 | class LLAudioData | ||
337 | { | ||
338 | public: | ||
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 | |||
355 | protected: | ||
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 | |||
371 | class LLAudioChannel | ||
372 | { | ||
373 | public: | ||
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; | ||
382 | protected: | ||
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. | ||
393 | protected: | ||
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 | |||
408 | class LLAudioBuffer | ||
409 | { | ||
410 | public: | ||
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; | ||
418 | protected: | ||
419 | BOOL mInUse; | ||
420 | LLAudioData *mAudioDatap; | ||
421 | LLFrameTimer mLastUseTimer; | ||
422 | }; | ||
423 | |||
424 | |||
425 | |||
426 | extern 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 | |||
54 | void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata); | ||
55 | FSOUND_DSPUNIT *gWindDSP = NULL; | ||
56 | |||
57 | // These globals for the wind filter. Blech! | ||
58 | F64 gbuf0 = 0.0; | ||
59 | F64 gbuf1 = 0.0; | ||
60 | F64 gbuf2 = 0.0; | ||
61 | F64 gbuf3 = 0.0; | ||
62 | F64 gbuf4 = 0.0; | ||
63 | F64 gbuf5 = 0.0; | ||
64 | F64 gY0 = 0.0; | ||
65 | F64 gY1 = 0.0; | ||
66 | |||
67 | F32 gTargetGain = 0.f; | ||
68 | F32 gCurrentGain = 0.f; | ||
69 | F32 gTargetFreq = 100.f; | ||
70 | F32 gCurrentFreq = 100.f; | ||
71 | F32 gTargetPanGainR = 0.5f; | ||
72 | F32 gCurrentPanGainR = 0.5f; | ||
73 | |||
74 | |||
75 | // Safe strcpy | ||
76 | #if LL_WINDOWS || LL_LINUX | ||
77 | static 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 | |||
99 | LLAudioEngine_FMOD::LLAudioEngine_FMOD() | ||
100 | { | ||
101 | mInited = FALSE; | ||
102 | mCurrentInternetStreamp = NULL; | ||
103 | mInternetStreamChannel = -1; | ||
104 | } | ||
105 | |||
106 | |||
107 | LLAudioEngine_FMOD::~LLAudioEngine_FMOD() | ||
108 | { | ||
109 | } | ||
110 | |||
111 | |||
112 | BOOL 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 | |||
272 | void LLAudioEngine_FMOD::idle(F32 max_decode_time) | ||
273 | { | ||
274 | LLAudioEngine::idle(max_decode_time); | ||
275 | |||
276 | updateInternetStream(); | ||
277 | } | ||
278 | |||
279 | |||
280 | void 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 | |||
290 | void 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 | |||
311 | LLAudioBuffer *LLAudioEngine_FMOD::createBuffer() | ||
312 | { | ||
313 | return new LLAudioBufferFMOD(); | ||
314 | } | ||
315 | |||
316 | |||
317 | LLAudioChannel *LLAudioEngine_FMOD::createChannel() | ||
318 | { | ||
319 | return new LLAudioChannelFMOD(); | ||
320 | } | ||
321 | |||
322 | |||
323 | void 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 | |||
337 | void 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 | //----------------------------------------------------------------------- | ||
349 | void 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 | //----------------------------------------------------------------------- | ||
382 | void 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 | //----------------------------------------------------------------------- | ||
399 | void 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 | //----------------------------------------------------------------------- | ||
416 | void LLAudioEngine_FMOD::get3DParams(S32 source_num, S32 *volume, S32 *freq, S32 *inside, S32 *outside, LLVector3 *orient, S32 *out_volume, F32 *min_dist, F32 *max_dist) | ||
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 | //----------------------------------------------------------------------- | ||
432 | void 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 | |||
453 | LLAudioChannelFMOD::LLAudioChannelFMOD() : LLAudioChannel(), mChannelID(0), mLastSamplePos(0) | ||
454 | { | ||
455 | } | ||
456 | |||
457 | |||
458 | LLAudioChannelFMOD::~LLAudioChannelFMOD() | ||
459 | { | ||
460 | cleanup(); | ||
461 | } | ||
462 | |||
463 | |||
464 | BOOL 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 | |||
511 | void 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 | |||
547 | void 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 | |||
568 | void 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 | |||
587 | void 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 | |||
603 | void 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 | |||
624 | BOOL 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 | |||
641 | LLAudioBufferFMOD::LLAudioBufferFMOD() | ||
642 | { | ||
643 | mSamplep = NULL; | ||
644 | } | ||
645 | |||
646 | |||
647 | LLAudioBufferFMOD::~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 | |||
658 | BOOL 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 | |||
731 | U32 LLAudioBufferFMOD::getLength() | ||
732 | { | ||
733 | if (!mSamplep) | ||
734 | { | ||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | return FSOUND_Sample_GetLength(mSamplep); | ||
739 | } | ||
740 | |||
741 | |||
742 | void 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 | //--------------------------------------------------------------------------- | ||
767 | void 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 | |||
784 | void 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 | |||
808 | signed 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 | |||
832 | void 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 | |||
911 | void 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 | |||
937 | void 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. | ||
960 | int 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 | |||
977 | void 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 | |||
984 | void 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 | |||
996 | const char* LLAudioEngine_FMOD::getInternetStreamURL() | ||
997 | { | ||
998 | return mInternetStreamURL; | ||
999 | } | ||
1000 | |||
1001 | |||
1002 | LLAudioStreamFMOD::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 | |||
1021 | int 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 | |||
1036 | BOOL 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 | |||
1077 | int 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 | |||
1090 | inline 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 | |||
1100 | void * 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 | |||
40 | class LLAudioStreamFMOD; | ||
41 | |||
42 | class LLAudioEngine_FMOD : public LLAudioEngine | ||
43 | { | ||
44 | public: | ||
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 | |||
72 | protected: | ||
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); | ||
77 | protected: | ||
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 | |||
98 | class LLAudioChannelFMOD : public LLAudioChannel | ||
99 | { | ||
100 | public: | ||
101 | LLAudioChannelFMOD(); | ||
102 | virtual ~LLAudioChannelFMOD(); | ||
103 | |||
104 | protected: | ||
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 | |||
114 | protected: | ||
115 | int mChannelID; | ||
116 | S32 mLastSamplePos; | ||
117 | }; | ||
118 | |||
119 | |||
120 | class LLAudioBufferFMOD : public LLAudioBuffer | ||
121 | { | ||
122 | public: | ||
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); | ||
131 | protected: | ||
132 | FSOUND_SAMPLE *getSample() { return mSamplep; } | ||
133 | protected: | ||
134 | FSOUND_SAMPLE *mSamplep; | ||
135 | }; | ||
136 | |||
137 | class LLAudioStreamFMOD | ||
138 | { | ||
139 | public: | ||
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(); | ||
148 | protected: | ||
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 @@ | |||
1 | llaudio/audioengine.cpp | ||
2 | llaudio/audioengine_fmod.cpp | ||
3 | llaudio/listener.cpp | ||
4 | llaudio/listener_fmod.cpp | ||
5 | llaudio/llaudiodecodemgr.cpp | ||
6 | llaudio/vorbisdecode.cpp | ||
7 | llaudio/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 | //----------------------------------------------------------------------- | ||
38 | LLListener::LLListener() | ||
39 | { | ||
40 | init(); | ||
41 | } | ||
42 | |||
43 | //----------------------------------------------------------------------- | ||
44 | LLListener::~LLListener() | ||
45 | { | ||
46 | } | ||
47 | |||
48 | //----------------------------------------------------------------------- | ||
49 | void LLListener::init(void) | ||
50 | { | ||
51 | mPosition.zeroVec(); | ||
52 | mListenAt.setVec(DEFAULT_AT); | ||
53 | mListenUp.setVec(DEFAULT_UP); | ||
54 | mVelocity.zeroVec(); | ||
55 | } | ||
56 | |||
57 | //----------------------------------------------------------------------- | ||
58 | void LLListener::free(void) | ||
59 | { | ||
60 | } | ||
61 | |||
62 | //----------------------------------------------------------------------- | ||
63 | void LLListener::translate(LLVector3 offset) | ||
64 | { | ||
65 | mPosition += offset; | ||
66 | } | ||
67 | |||
68 | //----------------------------------------------------------------------- | ||
69 | void LLListener::setPosition(LLVector3 pos) | ||
70 | { | ||
71 | mPosition = pos; | ||
72 | } | ||
73 | |||
74 | //----------------------------------------------------------------------- | ||
75 | LLVector3 LLListener::getPosition(void) | ||
76 | { | ||
77 | return(mPosition); | ||
78 | } | ||
79 | |||
80 | //----------------------------------------------------------------------- | ||
81 | LLVector3 LLListener::getAt(void) | ||
82 | { | ||
83 | return(mListenAt); | ||
84 | } | ||
85 | |||
86 | //----------------------------------------------------------------------- | ||
87 | LLVector3 LLListener::getUp(void) | ||
88 | { | ||
89 | return(mListenUp); | ||
90 | } | ||
91 | |||
92 | //----------------------------------------------------------------------- | ||
93 | void LLListener::setVelocity(LLVector3 vel) | ||
94 | { | ||
95 | mVelocity = vel; | ||
96 | } | ||
97 | |||
98 | //----------------------------------------------------------------------- | ||
99 | void LLListener::orient(LLVector3 up, LLVector3 at) | ||
100 | { | ||
101 | mListenUp = up; | ||
102 | mListenAt = at; | ||
103 | } | ||
104 | |||
105 | //----------------------------------------------------------------------- | ||
106 | void 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 | //----------------------------------------------------------------------- | ||
117 | void LLListener::setDopplerFactor(F32 factor) | ||
118 | { | ||
119 | } | ||
120 | |||
121 | //----------------------------------------------------------------------- | ||
122 | F32 LLListener::getDopplerFactor() | ||
123 | { | ||
124 | return (1.f); | ||
125 | } | ||
126 | |||
127 | //----------------------------------------------------------------------- | ||
128 | void LLListener::setDistanceFactor(F32 factor) | ||
129 | { | ||
130 | } | ||
131 | |||
132 | //----------------------------------------------------------------------- | ||
133 | F32 LLListener::getDistanceFactor() | ||
134 | { | ||
135 | return (1.f); | ||
136 | } | ||
137 | |||
138 | //----------------------------------------------------------------------- | ||
139 | void LLListener::setRolloffFactor(F32 factor) | ||
140 | { | ||
141 | } | ||
142 | |||
143 | //----------------------------------------------------------------------- | ||
144 | F32 LLListener::getRolloffFactor() | ||
145 | { | ||
146 | return (1.f); | ||
147 | } | ||
148 | |||
149 | //----------------------------------------------------------------------- | ||
150 | void 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 | |||
35 | class 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 | |||
38 | class 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 | //----------------------------------------------------------------------- | ||
40 | LLListener_FMOD::LLListener_FMOD() | ||
41 | { | ||
42 | init(); | ||
43 | } | ||
44 | |||
45 | //----------------------------------------------------------------------- | ||
46 | LLListener_FMOD::~LLListener_FMOD() | ||
47 | { | ||
48 | } | ||
49 | |||
50 | //----------------------------------------------------------------------- | ||
51 | void 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 | //----------------------------------------------------------------------- | ||
61 | void 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 | //----------------------------------------------------------------------- | ||
69 | void 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 | //----------------------------------------------------------------------- | ||
77 | void 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 | //----------------------------------------------------------------------- | ||
85 | void 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 | //----------------------------------------------------------------------- | ||
99 | void LLListener_FMOD::commitDeferredChanges() | ||
100 | { | ||
101 | FSOUND_Update(); | ||
102 | } | ||
103 | |||
104 | |||
105 | void LLListener_FMOD::setRolloffFactor(F32 factor) | ||
106 | { | ||
107 | mRolloffFactor = factor; | ||
108 | FSOUND_3D_SetRolloffFactor(factor); | ||
109 | } | ||
110 | |||
111 | |||
112 | F32 LLListener_FMOD::getRolloffFactor() | ||
113 | { | ||
114 | return mRolloffFactor; | ||
115 | } | ||
116 | |||
117 | |||
118 | void LLListener_FMOD::setDopplerFactor(F32 factor) | ||
119 | { | ||
120 | mDopplerFactor = factor; | ||
121 | FSOUND_3D_SetDopplerFactor(factor); | ||
122 | } | ||
123 | |||
124 | |||
125 | F32 LLListener_FMOD::getDopplerFactor() | ||
126 | { | ||
127 | return mDopplerFactor; | ||
128 | } | ||
129 | |||
130 | |||
131 | void LLListener_FMOD::setDistanceFactor(F32 factor) | ||
132 | { | ||
133 | mDistanceFactor = factor; | ||
134 | FSOUND_3D_SetDistanceFactor(factor); | ||
135 | } | ||
136 | |||
137 | |||
138 | F32 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 | |||
34 | class 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 | |||
37 | class 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 | |||
49 | extern LLAudioEngine *gAudiop; | ||
50 | |||
51 | LLAudioDecodeMgr *gAudioDecodeMgrp = NULL; | ||
52 | |||
53 | static const S32 WAV_HEADER_SIZE = 44; | ||
54 | |||
55 | class LLVorbisDecodeState | ||
56 | { | ||
57 | public: | ||
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; } | ||
70 | protected: | ||
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 | |||
86 | void 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 | |||
96 | LLAudioDecodeMgr::LLAudioDecodeMgr() | ||
97 | { | ||
98 | mCurrentDecodep = NULL; | ||
99 | } | ||
100 | |||
101 | LLAudioDecodeMgr::~LLAudioDecodeMgr() | ||
102 | { | ||
103 | delete mCurrentDecodep; | ||
104 | mCurrentDecodep = NULL; | ||
105 | } | ||
106 | |||
107 | |||
108 | void 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 | |||
211 | BOOL 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 | |||
230 | S32 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 | |||
252 | size_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 | |||
267 | int 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 | |||
303 | int vfs_close (void *datasource) | ||
304 | { | ||
305 | LLVFile *file = (LLVFile *)datasource; | ||
306 | |||
307 | delete file; | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | long vfs_tell (void *datasource) | ||
313 | { | ||
314 | LLVFile *file = (LLVFile *)datasource; | ||
315 | |||
316 | return file->tell(); | ||
317 | } | ||
318 | |||
319 | |||
320 | |||
321 | |||
322 | LLVorbisDecodeState::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 | |||
336 | LLVorbisDecodeState::~LLVorbisDecodeState() | ||
337 | { | ||
338 | if (!mDone) | ||
339 | { | ||
340 | delete mInFilep; | ||
341 | mInFilep = NULL; | ||
342 | } | ||
343 | } | ||
344 | |||
345 | |||
346 | BOOL 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 | |||
467 | BOOL 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 | |||
513 | BOOL 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 | |||
38 | class LLVFS; | ||
39 | class LLVorbisDecodeState; | ||
40 | |||
41 | |||
42 | class LLAudioDecodeMgr | ||
43 | { | ||
44 | public: | ||
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 | |||
59 | protected: | ||
60 | LLLinkedQueue<LLUUID> mDownloadQueue; | ||
61 | LLFrameTimer mCurrentTransferAge; | ||
62 | }; | ||
63 | |||
64 | extern 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 | |||
42 | size_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 | |||
57 | int 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 | |||
93 | int vfs_close (void *datasource) | ||
94 | { | ||
95 | LLVFile *file = (LLVFile *)datasource; | ||
96 | |||
97 | delete file; | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | long vfs_tell (void *datasource) | ||
103 | { | ||
104 | LLVFile *file = (LLVFile *)datasource; | ||
105 | |||
106 | return file->tell(); | ||
107 | } | ||
108 | |||
109 | |||
110 | BOOL 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,¤t_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 | |||
31 | class LLVFS; | ||
32 | class LLUUID; | ||
33 | |||
34 | BOOL 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 | |||
73 | S32 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 | |||
196 | S32 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 | |||
201 | S32 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 | |||
31 | const S32 LLVORBISENC_NOERR = 0; // no error | ||
32 | const S32 LLVORBISENC_SOURCE_OPEN_ERR = 1; // error opening source | ||
33 | const S32 LLVORBISENC_DEST_OPEN_ERR = 2; // error opening destination | ||
34 | const S32 LLVORBISENC_WAV_FORMAT_ERR = 3; // not a WAV | ||
35 | const S32 LLVORBISENC_PCM_FORMAT_ERR = 4; // not a PCM | ||
36 | const S32 LLVORBISENC_MONO_ERR = 5; // can't do mono | ||
37 | const S32 LLVORBISENC_STEREO_ERR = 6; // can't do stereo | ||
38 | const S32 LLVORBISENC_MULTICHANNEL_ERR = 7; // can't do stereo | ||
39 | const S32 LLVORBISENC_UNSUPPORTED_SAMPLE_RATE = 8; // unsupported sample rate | ||
40 | const S32 LLVORBISENC_UNSUPPORTED_WORD_SIZE = 9; // unsupported word size | ||
41 | const S32 LLVORBISENC_CLIP_TOO_LONG = 10; // source file is too long | ||
42 | |||
43 | |||
44 | S32 check_for_invalid_wav_formats(const char *in_fname, char *error_msg); | ||
45 | S32 encode_vorbis_file(const char *in_fname, const char *out_fname); | ||
46 | S32 encode_vorbis_file_at(const char *in_fname, const char *out_fname, S32 bitrate); | ||
47 | |||
48 | #endif | ||
49 | |||