summaryrefslogtreecommitdiff
path: root/indra/llaudio
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llaudio')
-rw-r--r--indra/llaudio/llaudiodecodemgr.cpp616
-rw-r--r--indra/llaudio/llaudiodecodemgr.h47
2 files changed, 663 insertions, 0 deletions
diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp
new file mode 100644
index 0000000000..a819a9fa03
--- /dev/null
+++ b/indra/llaudio/llaudiodecodemgr.cpp
@@ -0,0 +1,616 @@
+/**
+ * @file llaudiodecodemgr.cpp
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <vector>
+#include <iterator>
+#include <algorithm>
+#include <stdio.h>
+
+#include "llaudiodecodemgr.h"
+
+#include "vorbisdecode.h"
+#include "audioengine.h"
+#include "lllfsthread.h"
+#include "llvfile.h"
+#include "llstring.h"
+#include "lldir.h"
+#include "llendianswizzle.h"
+#include "audioengine.h"
+#include "llassetstorage.h"
+
+#include "vorbis/codec.h"
+#include "vorbis/vorbisfile.h"
+
+extern LLAudioEngine *gAudiop;
+
+LLAudioDecodeMgr *gAudioDecodeMgrp = NULL;
+
+const S32 wav_header_size = 44;
+
+class LLVorbisDecodeState
+{
+public:
+ LLVorbisDecodeState(const LLUUID &uuid, const LLString &out_filename);
+ virtual ~LLVorbisDecodeState();
+
+ BOOL initDecode();
+ BOOL decodeSection(); // Return TRUE if done.
+ BOOL finishDecode();
+
+ void flushBadFile();
+
+ BOOL isValid() const { return mValid; }
+ BOOL isDone() const { return mDone; }
+ const LLUUID &getUUID() const { return mUUID; }
+protected:
+ BOOL mValid;
+ BOOL mDone;
+ LLUUID mUUID;
+
+ std::vector<U8> mWAVBuffer;
+#if !defined(USE_WAV_VFILE)
+ LLString mOutFilename;
+ LLLFSThread::handle_t mFileHandle;
+#endif
+
+ LLVFile *mInFilep;
+ OggVorbis_File mVF;
+ S32 mCurrentSection;
+};
+
+void LLVorbisDecodeState::flushBadFile()
+{
+ if (mInFilep)
+ {
+ llwarns << "Flushing bad vorbis file from VFS for " << mUUID << llendl;
+ mInFilep->remove();
+ }
+}
+
+
+LLAudioDecodeMgr::LLAudioDecodeMgr()
+{
+ mCurrentDecodep = NULL;
+}
+
+LLAudioDecodeMgr::~LLAudioDecodeMgr()
+{
+ delete mCurrentDecodep;
+ mCurrentDecodep = NULL;
+}
+
+
+void LLAudioDecodeMgr::processQueue(const F32 num_secs)
+{
+ LLUUID uuid;
+
+ LLTimer decode_timer;
+
+ BOOL done = FALSE;
+ while (!done)
+ {
+ if (mCurrentDecodep)
+ {
+ BOOL res;
+
+ // Decode in a loop until we're done or have run out of time.
+ while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs))
+ {
+ // decodeSection does all of the work above
+ }
+
+ if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid())
+ {
+ // We had an error when decoding, abort.
+ llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl;
+ mCurrentDecodep->flushBadFile();
+ LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
+ adp->setHasValidData(FALSE);
+ delete mCurrentDecodep;
+ mCurrentDecodep = NULL;
+ done = TRUE;
+ }
+
+ if (!res)
+ {
+ // We've used up out time slice, bail...
+ done = TRUE;
+ }
+ else if (mCurrentDecodep)
+ {
+ if (mCurrentDecodep->finishDecode())
+ {
+ // We finished!
+ if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
+ {
+ LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
+ adp->setHasDecodedData(TRUE);
+ adp->setHasValidData(TRUE);
+
+ // At this point, we could see if anyone needs this sound immediately, but
+ // I'm not sure that there's a reason to - we need to poll all of the playing
+ // sounds anyway.
+ //llinfos << "Finished the vorbis decode, now what?" << llendl;
+ }
+ else
+ {
+ llinfos << "Vorbis decode failed!!!" << llendl;
+ }
+ delete mCurrentDecodep;
+ mCurrentDecodep = NULL;
+ }
+ done = TRUE; // done for now
+ }
+ }
+
+ if (!done)
+ {
+ if (!mDecodeQueue.getLength())
+ {
+ // Nothing else on the queue.
+ done = TRUE;
+ }
+ else
+ {
+ LLUUID uuid;
+ mDecodeQueue.pop(uuid);
+ if (gAudiop->hasDecodedFile(uuid))
+ {
+ // This file has already been decoded, don't decode it again.
+ continue;
+ }
+
+ lldebugs << "Decoding " << uuid << " from audio queue!" << llendl;
+
+ char uuid_str[64]; /*Flawfinder: ignore*/
+ char d_path[LL_MAX_PATH]; /*Flawfinder: ignore*/
+
+ LLTimer timer;
+ timer.reset();
+
+ uuid.toString(uuid_str);
+ snprintf(d_path, LL_MAX_PATH, "%s.dsf", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str()); /*Flawfinder: ignore*/
+
+ mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
+ if (!mCurrentDecodep->initDecode())
+ {
+ delete mCurrentDecodep;
+ mCurrentDecodep = NULL;
+ }
+ }
+ }
+ }
+}
+
+
+BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
+{
+ if (gAudiop->hasDecodedFile(uuid))
+ {
+ // Already have a decoded version, don't need to decode it.
+ return TRUE;
+ }
+
+ if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
+ {
+ // Just put it on the decode queue.
+ gAudioDecodeMgrp->mDecodeQueue.push(uuid);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+S32 LLAudioDecodeMgr::getRequestCount()
+{
+ /*
+ S32 count = 0;
+ if (mCurrentTransfer.notNull())
+ {
+ count++;
+ }
+
+ count += mRequestQueue.getLength();
+ return count;
+ */
+ return 0;
+}
+
+
+
+
+
+
+
+
+size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+ LLVFile *file = (LLVFile *)datasource;
+
+ if (file->read((U8*)ptr, (S32)(size * nmemb))) /*Flawfinder: ignore*/
+ {
+ S32 read = file->getLastBytesRead();
+ return read / size; /*Flawfinder: ignore*/
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+int vfs_seek(void *datasource, ogg_int64_t offset, int whence)
+{
+ LLVFile *file = (LLVFile *)datasource;
+
+ // vfs has 31-bit files
+ if (offset > S32_MAX)
+ {
+ return -1;
+ }
+
+ S32 origin;
+ switch (whence) {
+ case SEEK_SET:
+ origin = 0;
+ break;
+ case SEEK_END:
+ origin = file->getSize();
+ break;
+ case SEEK_CUR:
+ origin = -1;
+ break;
+ default:
+ llerrs << "Invalid whence argument to vfs_seek" << llendl;
+ return -1;
+ }
+
+ if (file->seek((S32)offset, origin))
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+int vfs_close (void *datasource)
+{
+ LLVFile *file = (LLVFile *)datasource;
+
+ delete file;
+
+ return 0;
+}
+
+long vfs_tell (void *datasource)
+{
+ LLVFile *file = (LLVFile *)datasource;
+
+ return file->tell();
+}
+
+
+
+
+LLVorbisDecodeState::LLVorbisDecodeState(const LLUUID &uuid, const LLString &out_filename)
+{
+ mDone = FALSE;
+ mValid = FALSE;
+ mUUID = uuid;
+ mInFilep = NULL;
+ mCurrentSection = 0;
+#if !defined(USE_WAV_VFILE)
+ mOutFilename = out_filename;
+ mFileHandle = LLLFSThread::nullHandle();
+#endif
+ // No default value for mVF, it's an ogg structure?
+}
+
+LLVorbisDecodeState::~LLVorbisDecodeState()
+{
+ if (!mDone)
+ {
+ delete mInFilep;
+ mInFilep = NULL;
+ }
+}
+
+
+BOOL LLVorbisDecodeState::initDecode()
+{
+ ov_callbacks vfs_callbacks;
+ vfs_callbacks.read_func = vfs_read;
+ vfs_callbacks.seek_func = vfs_seek;
+ vfs_callbacks.close_func = vfs_close;
+ vfs_callbacks.tell_func = vfs_tell;
+
+ //llinfos << "Initing decode from vfile: " << mUUID << llendl;
+
+ mInFilep = new LLVFile(gVFS, mUUID, LLAssetType::AT_SOUND);
+ if (!mInFilep || !mInFilep->getSize())
+ {
+ llwarns << "unable to open vorbis source vfile for reading" << llendl;
+ delete mInFilep;
+ mInFilep = NULL;
+ return FALSE;
+ }
+
+ int r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, vfs_callbacks);
+ if(r < 0)
+ {
+ llwarns << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << mUUID << llendl;
+ return(FALSE);
+ }
+
+ size_t size_guess = (size_t)ov_pcm_total(&mVF, -1);
+ vorbis_info* vi = ov_info(&mVF, -1);
+ size_guess *= vi->channels;
+ size_guess *= 2;
+ size_guess += 2048;
+ mWAVBuffer.reserve(size_guess);
+ mWAVBuffer.resize(wav_header_size);
+
+ {
+ // write the .wav format header
+ //"RIFF"
+ mWAVBuffer[0] = 0x52;
+ mWAVBuffer[1] = 0x49;
+ mWAVBuffer[2] = 0x46;
+ mWAVBuffer[3] = 0x46;
+
+ // length = datalen + 36 (to be filled in later)
+ mWAVBuffer[4] = 0x00;
+ mWAVBuffer[5] = 0x00;
+ mWAVBuffer[6] = 0x00;
+ mWAVBuffer[7] = 0x00;
+
+ //"WAVE"
+ mWAVBuffer[8] = 0x57;
+ mWAVBuffer[9] = 0x41;
+ mWAVBuffer[10] = 0x56;
+ mWAVBuffer[11] = 0x45;
+
+ // "fmt "
+ mWAVBuffer[12] = 0x66;
+ mWAVBuffer[13] = 0x6D;
+ mWAVBuffer[14] = 0x74;
+ mWAVBuffer[15] = 0x20;
+
+ // chunk size = 16
+ mWAVBuffer[16] = 0x10;
+ mWAVBuffer[17] = 0x00;
+ mWAVBuffer[18] = 0x00;
+ mWAVBuffer[19] = 0x00;
+
+ // format (1 = PCM)
+ mWAVBuffer[20] = 0x01;
+ mWAVBuffer[21] = 0x00;
+
+ // number of channels
+ mWAVBuffer[22] = 0x01;
+ mWAVBuffer[23] = 0x00;
+
+ // samples per second
+ mWAVBuffer[24] = 0x44;
+ mWAVBuffer[25] = 0xAC;
+ mWAVBuffer[26] = 0x00;
+ mWAVBuffer[27] = 0x00;
+
+ // average bytes per second
+ mWAVBuffer[28] = 0x88;
+ mWAVBuffer[29] = 0x58;
+ mWAVBuffer[30] = 0x01;
+ mWAVBuffer[31] = 0x00;
+
+ // bytes to output at a single time
+ mWAVBuffer[32] = 0x02;
+ mWAVBuffer[33] = 0x00;
+
+ // 16 bits per sample
+ mWAVBuffer[34] = 0x10;
+ mWAVBuffer[35] = 0x00;
+
+ // "data"
+ mWAVBuffer[36] = 0x64;
+ mWAVBuffer[37] = 0x61;
+ mWAVBuffer[38] = 0x74;
+ mWAVBuffer[39] = 0x61;
+
+ // these are the length of the data chunk, to be filled in later
+ mWAVBuffer[40] = 0x00;
+ mWAVBuffer[41] = 0x00;
+ mWAVBuffer[42] = 0x00;
+ mWAVBuffer[43] = 0x00;
+ }
+
+ //{
+ //char **ptr=ov_comment(&mVF,-1)->user_comments;
+// vorbis_info *vi=ov_info(&vf,-1);
+ //while(*ptr){
+ // fprintf(stderr,"%s\n",*ptr);
+ // ++ptr;
+ //}
+// fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate);
+// fprintf(stderr,"\nDecoded length: %ld samples\n", (long)ov_pcm_total(&vf,-1));
+// fprintf(stderr,"Encoded by: %s\n\n",ov_comment(&vf,-1)->vendor);
+ //}
+ return TRUE;
+}
+
+BOOL LLVorbisDecodeState::decodeSection()
+{
+ if (!mInFilep)
+ {
+ llwarns << "No VFS file to decode in vorbis!" << llendl;
+ return TRUE;
+ }
+ if (mDone)
+ {
+// llwarns << "Already done with decode, aborting!" << llendl;
+ return TRUE;
+ }
+ char pcmout[4096]; /*Flawfinder: ignore*/
+
+ BOOL eof = FALSE;
+ long ret=ov_read(&mVF, pcmout, sizeof(pcmout), 0, 2, 1, &mCurrentSection);
+ if (ret == 0)
+ {
+ /* EOF */
+ eof = TRUE;
+ mDone = TRUE;
+ mValid = TRUE;
+// llinfos << "Vorbis EOF" << llendl;
+ }
+ else if (ret < 0)
+ {
+ /* error in the stream. Not a problem, just reporting it in
+ case we (the app) cares. In this case, we don't. */
+
+ llwarns << "BAD vorbis decode in decodeSection." << llendl;
+
+ mValid = FALSE;
+ mDone = TRUE;
+ // We're done, return TRUE.
+ return TRUE;
+ }
+ else
+ {
+// llinfos << "Vorbis read " << ret << "bytes" << llendl;
+ /* we don't bother dealing with sample rate changes, etc, but.
+ you'll have to*/
+ std::copy(pcmout, pcmout+ret, std::back_inserter(mWAVBuffer));
+ }
+ return eof;
+}
+
+BOOL LLVorbisDecodeState::finishDecode()
+{
+ if (!isValid())
+ {
+ llwarns << "Bogus vorbis decode state for " << getUUID() << ", aborting!" << llendl;
+ return TRUE; // We've finished
+ }
+
+#if !defined(USE_WAV_VFILE)
+ if (mFileHandle == LLLFSThread::nullHandle())
+#endif
+ {
+ ov_clear(&mVF);
+
+ // write "data" chunk length, in little-endian format
+ S32 data_length = mWAVBuffer.size() - wav_header_size;
+ mWAVBuffer[40] = (data_length) & 0x000000FF;
+ mWAVBuffer[41] = (data_length >> 8) & 0x000000FF;
+ mWAVBuffer[42] = (data_length >> 16) & 0x000000FF;
+ mWAVBuffer[43] = (data_length >> 24) & 0x000000FF;
+ // write overall "RIFF" length, in little-endian format
+ data_length += 36;
+ mWAVBuffer[4] = (data_length) & 0x000000FF;
+ mWAVBuffer[5] = (data_length >> 8) & 0x000000FF;
+ mWAVBuffer[6] = (data_length >> 16) & 0x000000FF;
+ mWAVBuffer[7] = (data_length >> 24) & 0x000000FF;
+
+ //
+ // FUCK!!! Vorbis encode/decode messes up loop point transitions (pop)
+ // do a cheap-and-cheesy crossfade
+ //
+ {
+ S16 *samplep;
+ S32 i;
+ S32 fade_length;
+ char pcmout[4096]; /*Flawfinder: ignore*/
+
+ fade_length = llmin((S32)128,(S32)(data_length-36)/8);
+ if (sizeof(mWAVBuffer) >= (wav_header_size + 2* fade_length))
+ {
+ memcpy(pcmout, &mWAVBuffer[wav_header_size], (2 * fade_length)); /*Flawfinder: ignore*/
+ }
+ llendianswizzle(&pcmout, 2, fade_length);
+
+ samplep = (S16 *)pcmout;
+ for (i = 0 ;i < fade_length; i++)
+ {
+ *samplep = llfloor((F32)*samplep * ((F32)i/(F32)fade_length));
+ samplep++;
+ }
+
+ llendianswizzle(&pcmout, 2, fade_length);
+ if ((wav_header_size+(2 * fade_length)) < sizeof(mWAVBuffer))
+ {
+ memcpy(&mWAVBuffer[wav_header_size], pcmout, (2 * fade_length)); /*Flawfinder: ignore*/
+ }
+ S32 near_end = mWAVBuffer.size() - (2 * fade_length);
+ if (sizeof(mWAVBuffer) >= ( near_end + 2* fade_length))
+ {
+ memcpy(pcmout, &mWAVBuffer[near_end], (2 * fade_length)); /*Flawfinder: ignore*/
+ }
+ llendianswizzle(&pcmout, 2, fade_length);
+
+ samplep = (S16 *)pcmout;
+ for (i = fade_length-1 ; i >= 0; i--)
+ {
+ *samplep = llfloor((F32)*samplep * ((F32)i/(F32)fade_length));
+ samplep++;
+ }
+
+ llendianswizzle(&pcmout, 2, fade_length);
+ if (near_end + (2 * fade_length) < sizeof(mWAVBuffer))
+ {
+ memcpy(&mWAVBuffer[near_end], pcmout, (2 * fade_length));/*Flawfinder: ignore*/
+ }
+ }
+
+ if (36 == data_length)
+ {
+ llwarns << "BAD Vorbis decode in finishDecode!" << llendl;
+ mValid = FALSE;
+ return TRUE; // we've finished
+ }
+#if !defined(USE_WAV_VFILE)
+ mFileHandle = LLLFSThread::sLocal->write(mOutFilename, &mWAVBuffer[0], 0, data_length);
+#endif
+ }
+
+ if (mFileHandle != LLLFSThread::nullHandle())
+ {
+ LLLFSThread::status_t s = LLLFSThread::sLocal->getRequestStatus(mFileHandle);
+ if (s != LLLFSThread::STATUS_COMPLETE)
+ {
+ if (s != LLLFSThread::STATUS_QUEUED && s != LLLFSThread::STATUS_INPROGRESS)
+ {
+ llerrs << "Bad file status in LLVorbisDecodeState::finishDecode: " << s << llendl;
+ }
+ return FALSE; // not finished
+ }
+ else
+ {
+ LLLFSThread::Request* req = (LLLFSThread::Request*)LLLFSThread::sLocal->getRequest(mFileHandle);
+ if (req->getBytesRead() == 0) //!= req->getBytes() // should be safe, but needs testing
+ {
+ llwarns << "Unable to write file in LLVorbisDecodeState::finishDecode" << llendl;
+ mValid = FALSE;
+ return TRUE; // we've finished
+ }
+ }
+ LLLFSThread::sLocal->completeRequest(mFileHandle);
+ }
+
+ mDone = TRUE;
+
+#if defined(USE_WAV_VFILE)
+ // write the data.
+ LLVFile output(gVFS, mUUID, LLAssetType::AT_SOUND_WAV);
+ output.write(&mWAVBuffer[0], mWAVBuffer.size());
+#endif
+ //llinfos << "Finished decode for " << getUUID() << llendl;
+
+ return TRUE;
+}
diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h
new file mode 100644
index 0000000000..ea1358b8e9
--- /dev/null
+++ b/indra/llaudio/llaudiodecodemgr.h
@@ -0,0 +1,47 @@
+/**
+ * @file llaudiodecodemgr.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAUDIODECODEMGR_H
+#define LL_LLAUDIODECODEMG_H
+
+#include "stdtypes.h"
+
+#include "lllinkedqueue.h"
+#include "lluuid.h"
+
+#include "llassettype.h"
+#include "llframetimer.h"
+
+class LLVFS;
+class LLVorbisDecodeState;
+
+
+class LLAudioDecodeMgr
+{
+public:
+ LLAudioDecodeMgr();
+ ~LLAudioDecodeMgr();
+
+ void processQueue(const F32 num_secs = 0.005);
+
+ LLLinkedQueue<LLUUID> mDecodeQueue;
+
+ LLVorbisDecodeState *mCurrentDecodep;
+
+ BOOL addDecodeRequest(const LLUUID &uuid);
+ void addAudioRequest(const LLUUID &uuid);
+
+ S32 getRequestCount();
+
+protected:
+ LLLinkedQueue<LLUUID> mDownloadQueue;
+ LLFrameTimer mCurrentTransferAge;
+};
+
+extern LLAudioDecodeMgr *gAudioDecodeMgrp;
+
+#endif