Imported existing code
This commit is contained in:
95
indra/llaudio/CMakeLists.txt
Normal file
95
indra/llaudio/CMakeLists.txt
Normal file
@@ -0,0 +1,95 @@
|
||||
# -*- cmake -*-
|
||||
|
||||
project(llaudio)
|
||||
|
||||
include(00-Common)
|
||||
include(Audio)
|
||||
include(LLAudio)
|
||||
include(FMOD)
|
||||
include(OPENAL)
|
||||
include(LLCommon)
|
||||
include(LLMath)
|
||||
include(LLMessage)
|
||||
include(LLVFS)
|
||||
|
||||
include_directories(
|
||||
${LLAUDIO_INCLUDE_DIRS}
|
||||
${FMOD_INCLUDE_DIR}
|
||||
${LLCOMMON_INCLUDE_DIRS}
|
||||
${LLMATH_INCLUDE_DIRS}
|
||||
${LLMESSAGE_INCLUDE_DIRS}
|
||||
${LLVFS_INCLUDE_DIRS}
|
||||
${OGG_INCLUDE_DIRS}
|
||||
${VORBISENC_INCLUDE_DIRS}
|
||||
${VORBISFILE_INCLUDE_DIRS}
|
||||
${VORBIS_INCLUDE_DIRS}
|
||||
${OPENAL_LIB_INCLUDE_DIRS}
|
||||
${FREEAULT_LIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(llaudio_SOURCE_FILES
|
||||
llaudioengine.cpp
|
||||
lllistener.cpp
|
||||
llaudiodecodemgr.cpp
|
||||
llvorbisdecode.cpp
|
||||
llvorbisencode.cpp
|
||||
)
|
||||
|
||||
set(llaudio_HEADER_FILES
|
||||
CMakeLists.txt
|
||||
|
||||
llaudioengine.h
|
||||
lllistener.h
|
||||
llaudiodecodemgr.h
|
||||
llvorbisdecode.h
|
||||
llvorbisencode.h
|
||||
llwindgen.h
|
||||
)
|
||||
|
||||
if (FMOD)
|
||||
list(APPEND llaudio_SOURCE_FILES
|
||||
llaudioengine_fmod.cpp
|
||||
lllistener_fmod.cpp
|
||||
llstreamingaudio_fmod.cpp
|
||||
)
|
||||
|
||||
list(APPEND llaudio_HEADER_FILES
|
||||
llaudioengine_fmod.h
|
||||
lllistener_fmod.h
|
||||
llstreamingaudio_fmod.h
|
||||
)
|
||||
|
||||
if (LINUX)
|
||||
if (${CXX_VERSION} MATCHES "4.[23]")
|
||||
set_source_files_properties(llaudioengine_fmod.cpp
|
||||
COMPILE_FLAGS -Wno-error=write-strings)
|
||||
endif (${CXX_VERSION} MATCHES "4.[23]")
|
||||
endif (LINUX)
|
||||
endif (FMOD)
|
||||
|
||||
if (OPENAL)
|
||||
list(APPEND llaudio_SOURCE_FILES
|
||||
llaudioengine_openal.cpp
|
||||
lllistener_openal.cpp
|
||||
)
|
||||
|
||||
list(APPEND llaudio_HEADER_FILES
|
||||
llaudioengine_openal.h
|
||||
lllistener_openal.h
|
||||
)
|
||||
endif (OPENAL)
|
||||
|
||||
set_source_files_properties(${llaudio_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
|
||||
list(APPEND llaudio_SOURCE_FILES ${llaudio_HEADER_FILES})
|
||||
|
||||
add_library (llaudio ${llaudio_SOURCE_FILES})
|
||||
add_dependencies(llaudio prepare)
|
||||
target_link_libraries(
|
||||
llaudio
|
||||
${VORBISENC_LIBRARIES}
|
||||
${VORBISFILE_LIBRARIES}
|
||||
${VORBIS_LIBRARIES}
|
||||
${OGG_LIBRARIES}
|
||||
)
|
||||
672
indra/llaudio/llaudiodecodemgr.cpp
Normal file
672
indra/llaudio/llaudiodecodemgr.cpp
Normal file
@@ -0,0 +1,672 @@
|
||||
/**
|
||||
* @file llaudiodecodemgr.cpp
|
||||
*
|
||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llaudiodecodemgr.h"
|
||||
|
||||
#include "llvorbisdecode.h"
|
||||
#include "llaudioengine.h"
|
||||
#include "lllfsthread.h"
|
||||
#include "llvfile.h"
|
||||
#include "llstring.h"
|
||||
#include "lldir.h"
|
||||
#include "llendianswizzle.h"
|
||||
#include "llassetstorage.h"
|
||||
|
||||
#include "vorbis/codec.h"
|
||||
#include "vorbis/vorbisfile.h"
|
||||
#include "llvorbisencode.h"
|
||||
|
||||
extern LLAudioEngine *gAudiop;
|
||||
|
||||
LLAudioDecodeMgr *gAudioDecodeMgrp = NULL;
|
||||
|
||||
static const S32 WAV_HEADER_SIZE = 44;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class LLVorbisDecodeState : public LLRefCount
|
||||
{
|
||||
public:
|
||||
class WriteResponder : public LLLFSThread::Responder
|
||||
{
|
||||
public:
|
||||
WriteResponder(LLVorbisDecodeState* decoder) : mDecoder(decoder) {}
|
||||
~WriteResponder() {}
|
||||
void completed(S32 bytes)
|
||||
{
|
||||
mDecoder->ioComplete(bytes);
|
||||
}
|
||||
LLPointer<LLVorbisDecodeState> mDecoder;
|
||||
};
|
||||
|
||||
LLVorbisDecodeState(const LLUUID &uuid, const std::string &out_filename);
|
||||
|
||||
BOOL initDecode();
|
||||
BOOL decodeSection(); // Return TRUE if done.
|
||||
BOOL finishDecode();
|
||||
|
||||
void flushBadFile();
|
||||
|
||||
void ioComplete(S32 bytes) { mBytesRead = bytes; }
|
||||
BOOL isValid() const { return mValid; }
|
||||
BOOL isDone() const { return mDone; }
|
||||
const LLUUID &getUUID() const { return mUUID; }
|
||||
|
||||
protected:
|
||||
virtual ~LLVorbisDecodeState();
|
||||
|
||||
BOOL mValid;
|
||||
BOOL mDone;
|
||||
LLAtomicS32 mBytesRead;
|
||||
LLUUID mUUID;
|
||||
|
||||
std::vector<U8> mWAVBuffer;
|
||||
#if !defined(USE_WAV_VFILE)
|
||||
std::string mOutFilename;
|
||||
LLLFSThread::handle_t mFileHandle;
|
||||
#endif
|
||||
|
||||
LLVFile *mInFilep;
|
||||
OggVorbis_File mVF;
|
||||
S32 mCurrentSection;
|
||||
};
|
||||
|
||||
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 std::string &out_filename)
|
||||
{
|
||||
mDone = FALSE;
|
||||
mValid = FALSE;
|
||||
mBytesRead = -1;
|
||||
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);
|
||||
}
|
||||
|
||||
S32 sample_count = ov_pcm_total(&mVF, -1);
|
||||
size_t size_guess = (size_t)sample_count;
|
||||
vorbis_info* vi = ov_info(&mVF, -1);
|
||||
size_guess *= vi->channels;
|
||||
size_guess *= 2;
|
||||
size_guess += 2048;
|
||||
|
||||
bool abort_decode = false;
|
||||
|
||||
if( vi->channels < 1 || vi->channels > LLVORBIS_CLIP_MAX_CHANNELS )
|
||||
{
|
||||
abort_decode = true;
|
||||
llwarns << "Bad channel count: " << vi->channels << llendl;
|
||||
}
|
||||
|
||||
if( (size_t)sample_count > LLVORBIS_CLIP_REJECT_SAMPLES )
|
||||
{
|
||||
abort_decode = true;
|
||||
llwarns << "Illegal sample count: " << sample_count << llendl;
|
||||
}
|
||||
|
||||
if( size_guess > LLVORBIS_CLIP_REJECT_SIZE )
|
||||
{
|
||||
abort_decode = true;
|
||||
llwarns << "Illegal sample size: " << size_guess << llendl;
|
||||
}
|
||||
|
||||
if( abort_decode )
|
||||
{
|
||||
llwarns << "Canceling initDecode. Bad asset: " << mUUID << llendl;
|
||||
llwarns << "Bad asset encoded by: " << ov_comment(&mVF,-1)->vendor << llendl;
|
||||
delete mInFilep;
|
||||
mInFilep = NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
//
|
||||
// FUDGECAKES!!! 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((S32)mWAVBuffer.size() >= (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)) < (S32)mWAVBuffer.size())
|
||||
{
|
||||
memcpy(&mWAVBuffer[WAV_HEADER_SIZE], pcmout, (2 * fade_length)); /*Flawfinder: ignore*/
|
||||
}
|
||||
S32 near_end = mWAVBuffer.size() - (2 * fade_length);
|
||||
if ((S32)mWAVBuffer.size() >= ( 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) < (S32)mWAVBuffer.size())
|
||||
{
|
||||
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)
|
||||
mBytesRead = -1;
|
||||
mFileHandle = LLLFSThread::sLocal->write(mOutFilename, &mWAVBuffer[0], 0, mWAVBuffer.size(),
|
||||
new WriteResponder(this));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mFileHandle != LLLFSThread::nullHandle())
|
||||
{
|
||||
if (mBytesRead >= 0)
|
||||
{
|
||||
if (mBytesRead == 0)
|
||||
{
|
||||
llwarns << "Unable to write file in LLVorbisDecodeState::finishDecode" << llendl;
|
||||
mValid = FALSE;
|
||||
return TRUE; // we've finished
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE; // not done
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void LLVorbisDecodeState::flushBadFile()
|
||||
{
|
||||
if (mInFilep)
|
||||
{
|
||||
llwarns << "Flushing bad vorbis file from VFS for " << mUUID << llendl;
|
||||
mInFilep->remove();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class LLAudioDecodeMgr::Impl
|
||||
{
|
||||
friend class LLAudioDecodeMgr;
|
||||
public:
|
||||
Impl() {};
|
||||
~Impl() {};
|
||||
|
||||
void processQueue(const F32 num_secs = 0.005);
|
||||
|
||||
protected:
|
||||
LLLinkedQueue<LLUUID> mDecodeQueue;
|
||||
LLPointer<LLVorbisDecodeState> mCurrentDecodep;
|
||||
};
|
||||
|
||||
|
||||
void LLAudioDecodeMgr::Impl::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);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
||||
std::string uuid_str;
|
||||
std::string d_path;
|
||||
|
||||
LLTimer timer;
|
||||
timer.reset();
|
||||
|
||||
uuid.toString(uuid_str);
|
||||
d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf";
|
||||
|
||||
mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
|
||||
if (!mCurrentDecodep->initDecode())
|
||||
{
|
||||
mCurrentDecodep = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LLAudioDecodeMgr::LLAudioDecodeMgr()
|
||||
{
|
||||
mImpl = new Impl;
|
||||
}
|
||||
|
||||
LLAudioDecodeMgr::~LLAudioDecodeMgr()
|
||||
{
|
||||
delete mImpl;
|
||||
}
|
||||
|
||||
void LLAudioDecodeMgr::processQueue(const F32 num_secs)
|
||||
{
|
||||
mImpl->processQueue(num_secs);
|
||||
}
|
||||
|
||||
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.
|
||||
mImpl->mDecodeQueue.push(uuid);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
63
indra/llaudio/llaudiodecodemgr.h
Normal file
63
indra/llaudio/llaudiodecodemgr.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* @file llaudiodecodemgr.h
|
||||
*
|
||||
* $LicenseInfo:firstyear=2003&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2003-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#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);
|
||||
BOOL addDecodeRequest(const LLUUID &uuid);
|
||||
void addAudioRequest(const LLUUID &uuid);
|
||||
|
||||
protected:
|
||||
class Impl;
|
||||
Impl* mImpl;
|
||||
};
|
||||
|
||||
extern LLAudioDecodeMgr *gAudioDecodeMgrp;
|
||||
|
||||
#endif
|
||||
1773
indra/llaudio/llaudioengine.cpp
Normal file
1773
indra/llaudio/llaudioengine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
455
indra/llaudio/llaudioengine.h
Normal file
455
indra/llaudio/llaudioengine.h
Normal file
@@ -0,0 +1,455 @@
|
||||
/**
|
||||
* @file audioengine.h
|
||||
* @brief Definition of LLAudioEngine base class abstracting the audio support
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LL_AUDIOENGINE_H
|
||||
#define LL_AUDIOENGINE_H
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include "v3math.h"
|
||||
#include "v3dmath.h"
|
||||
#include "lltimer.h"
|
||||
#include "lluuid.h"
|
||||
#include "llframetimer.h"
|
||||
#include "llassettype.h"
|
||||
|
||||
#include "lllistener.h"
|
||||
|
||||
const F32 LL_WIND_UPDATE_INTERVAL = 0.1f;
|
||||
const F32 LL_ROLLOFF_MULTIPLIER_UNDER_WATER = 5.f; // How much sounds are weaker under water
|
||||
const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f;
|
||||
|
||||
const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f;
|
||||
const F32 DEFAULT_MIN_DISTANCE = 2.0f;
|
||||
|
||||
#define MAX_CHANNELS 30
|
||||
#define MAX_BUFFERS 40 // Some extra for preloading, maybe?
|
||||
|
||||
// This define is intended to allow us to switch from os based wav
|
||||
// file loading to vfs based wav file loading. The problem is that I
|
||||
// am unconvinced that the LLWaveFile works for loading sounds from
|
||||
// memory. So, until that is fixed up, changed, whatever, this remains
|
||||
// undefined.
|
||||
//#define USE_WAV_VFILE
|
||||
|
||||
class LLVFS;
|
||||
|
||||
class LLAudioSource;
|
||||
class LLAudioData;
|
||||
class LLAudioChannel;
|
||||
class LLAudioChannelOpenAL;
|
||||
class LLAudioBuffer;
|
||||
class LLStreamingAudioInterface;
|
||||
|
||||
|
||||
//
|
||||
// LLAudioEngine definition
|
||||
//
|
||||
|
||||
class LLAudioEngine
|
||||
{
|
||||
friend class LLAudioChannelOpenAL; // bleh. channel needs some listener methods.
|
||||
|
||||
public:
|
||||
enum LLAudioType
|
||||
{
|
||||
AUDIO_TYPE_NONE = 0,
|
||||
AUDIO_TYPE_SFX = 1,
|
||||
AUDIO_TYPE_UI = 2,
|
||||
AUDIO_TYPE_AMBIENT = 3,
|
||||
AUDIO_TYPE_COUNT = 4 // last
|
||||
};
|
||||
|
||||
LLAudioEngine();
|
||||
virtual ~LLAudioEngine();
|
||||
|
||||
// initialization/startup/shutdown
|
||||
virtual bool init(const S32 num_channels, void *userdata);
|
||||
virtual std::string getDriverName(bool verbose) = 0;
|
||||
virtual void shutdown();
|
||||
|
||||
// Used by the mechanics of the engine
|
||||
//virtual void processQueue(const LLUUID &sound_guid);
|
||||
virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at);
|
||||
virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0;
|
||||
virtual void idle(F32 max_decode_time = 0.f);
|
||||
virtual void updateChannels();
|
||||
|
||||
//
|
||||
// "End user" functionality
|
||||
//
|
||||
virtual bool isWindEnabled();
|
||||
virtual void enableWind(bool state_b);
|
||||
|
||||
// Use these for temporarily muting the audio system.
|
||||
// Does not change buffers, initialization, etc. but
|
||||
// stops playing new sounds.
|
||||
void setMuted(bool muted);
|
||||
bool getMuted() const { return mMuted; }
|
||||
#ifdef USE_PLUGIN_MEDIA
|
||||
LLPluginClassMedia* initializeMedia(const std::string& media_type);
|
||||
#endif
|
||||
F32 getMasterGain();
|
||||
void setMasterGain(F32 gain);
|
||||
|
||||
F32 getSecondaryGain(S32 type);
|
||||
void setSecondaryGain(S32 type, F32 gain);
|
||||
|
||||
F32 getInternetStreamGain();
|
||||
|
||||
virtual void setDopplerFactor(F32 factor);
|
||||
virtual F32 getDopplerFactor();
|
||||
virtual void setRolloffFactor(F32 factor);
|
||||
virtual F32 getRolloffFactor();
|
||||
virtual void setMaxWindGain(F32 gain);
|
||||
|
||||
|
||||
// Methods actually related to setting up and removing sounds
|
||||
// Owner ID is the owner of the object making the request
|
||||
void triggerSound(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain,
|
||||
const S32 type = LLAudioEngine::AUDIO_TYPE_NONE,
|
||||
const LLVector3d &pos_global = LLVector3d::zero);
|
||||
bool preloadSound(const LLUUID &id);
|
||||
|
||||
void addAudioSource(LLAudioSource *asp);
|
||||
void cleanupAudioSource(LLAudioSource *asp);
|
||||
|
||||
LLAudioSource *findAudioSource(const LLUUID &source_id);
|
||||
LLAudioData *getAudioData(const LLUUID &audio_uuid);
|
||||
|
||||
// Internet stream implementation manipulation
|
||||
LLStreamingAudioInterface *getStreamingAudioImpl();
|
||||
void setStreamingAudioImpl(LLStreamingAudioInterface *impl);
|
||||
// Internet stream methods - these will call down into the *mStreamingAudioImpl if it exists
|
||||
void startInternetStream(const std::string& url);
|
||||
void stopInternetStream();
|
||||
void pauseInternetStream(int pause);
|
||||
void updateInternetStream(); // expected to be called often
|
||||
int isInternetStreamPlaying();
|
||||
// use a value from 0.0 to 1.0, inclusive
|
||||
void setInternetStreamGain(F32 vol);
|
||||
std::string getInternetStreamURL();
|
||||
|
||||
// For debugging usage
|
||||
virtual LLVector3 getListenerPos();
|
||||
|
||||
LLAudioBuffer *getFreeBuffer(); // Get a free buffer, or flush an existing one if you have to.
|
||||
LLAudioChannel *getFreeChannel(const F32 priority); // Get a free channel or flush an existing one if your priority is higher
|
||||
void cleanupBuffer(LLAudioBuffer *bufferp);
|
||||
|
||||
bool hasDecodedFile(const LLUUID &uuid);
|
||||
bool hasLocalFile(const LLUUID &uuid);
|
||||
|
||||
bool updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid = LLUUID::null);
|
||||
|
||||
|
||||
// Asset callback when we're retrieved a sound from the asset server.
|
||||
void startNextTransfer();
|
||||
static void assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status);
|
||||
|
||||
friend class LLPipeline; // For debugging
|
||||
public:
|
||||
F32 mMaxWindGain; // Hack. Public to set before fade in?
|
||||
|
||||
protected:
|
||||
virtual LLAudioBuffer *createBuffer() = 0;
|
||||
virtual LLAudioChannel *createChannel() = 0;
|
||||
|
||||
virtual bool initWind() = 0;
|
||||
virtual void cleanupWind() = 0;
|
||||
virtual void setInternalGain(F32 gain) = 0;
|
||||
|
||||
void commitDeferredChanges();
|
||||
|
||||
virtual void allocateListener() = 0;
|
||||
|
||||
|
||||
// listener methods
|
||||
virtual void setListenerPos(LLVector3 vec);
|
||||
virtual void setListenerVelocity(LLVector3 vec);
|
||||
virtual void orientListener(LLVector3 up, LLVector3 at);
|
||||
virtual void translateListener(LLVector3 vec);
|
||||
|
||||
|
||||
F64 mapWindVecToGain(LLVector3 wind_vec);
|
||||
F64 mapWindVecToPitch(LLVector3 wind_vec);
|
||||
F64 mapWindVecToPan(LLVector3 wind_vec);
|
||||
|
||||
protected:
|
||||
LLListener *mListenerp;
|
||||
|
||||
bool mMuted;
|
||||
void* mUserData;
|
||||
|
||||
S32 mLastStatus;
|
||||
|
||||
S32 mNumChannels;
|
||||
bool mEnableWind;
|
||||
|
||||
LLUUID mCurrentTransfer; // Audio file currently being transferred by the system
|
||||
LLFrameTimer mCurrentTransferTimer;
|
||||
|
||||
// A list of all audio sources that are known to the viewer at this time.
|
||||
// This is most likely a superset of the ones that we actually have audio
|
||||
// data for, or are playing back.
|
||||
typedef std::map<LLUUID, LLAudioSource *> source_map;
|
||||
typedef std::map<LLUUID, LLAudioData *> data_map;
|
||||
|
||||
source_map mAllSources;
|
||||
data_map mAllData;
|
||||
|
||||
LLAudioChannel *mChannels[MAX_CHANNELS];
|
||||
|
||||
// Buffers needs to change into a different data structure, as the number of buffers
|
||||
// that we have active should be limited by RAM usage, not count.
|
||||
LLAudioBuffer *mBuffers[MAX_BUFFERS];
|
||||
|
||||
F32 mMasterGain;
|
||||
F32 mInternalGain; // Actual gain set; either mMasterGain or 0 when mMuted is true.
|
||||
F32 mSecondaryGain[AUDIO_TYPE_COUNT];
|
||||
|
||||
F32 mNextWindUpdate;
|
||||
|
||||
LLFrameTimer mWindUpdateTimer;
|
||||
|
||||
private:
|
||||
void setDefaults();
|
||||
LLStreamingAudioInterface *mStreamingAudioImpl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Standard audio source. Can be derived from for special sources, such as those attached to objects.
|
||||
//
|
||||
|
||||
|
||||
class LLAudioSource
|
||||
{
|
||||
public:
|
||||
// owner_id is the id of the agent responsible for making this sound
|
||||
// play, for example, the owner of the object currently playing it
|
||||
LLAudioSource(const LLUUID &id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE);
|
||||
virtual ~LLAudioSource();
|
||||
|
||||
virtual void update(); // Update this audio source
|
||||
void updatePriority();
|
||||
|
||||
void preload(const LLUUID &audio_id); // Only used for preloading UI sounds, now.
|
||||
|
||||
void addAudioData(LLAudioData *adp, bool set_current = TRUE);
|
||||
|
||||
void setAmbient(const bool ambient) { mAmbient = ambient; }
|
||||
bool isAmbient() const { return mAmbient; }
|
||||
|
||||
void setLoop(const bool loop) { mLoop = loop; }
|
||||
bool isLoop() const { return mLoop; }
|
||||
|
||||
void setSyncMaster(const bool master) { mSyncMaster = master; }
|
||||
bool isSyncMaster() const { return mSyncMaster; }
|
||||
|
||||
void setSyncSlave(const bool slave) { mSyncSlave = slave; }
|
||||
bool isSyncSlave() const { return mSyncSlave; }
|
||||
|
||||
void setQueueSounds(const bool queue) { mQueueSounds = queue; }
|
||||
bool isQueueSounds() const { return mQueueSounds; }
|
||||
|
||||
void setPlayedOnce(const bool played_once) { mPlayedOnce = played_once; }
|
||||
|
||||
void setType(S32 type) { mType = type; }
|
||||
S32 getType() { return mType; }
|
||||
|
||||
void setPositionGlobal(const LLVector3d &position_global) { mPositionGlobal = position_global; }
|
||||
LLVector3d getPositionGlobal() const { return mPositionGlobal; }
|
||||
LLVector3 getVelocity() const { return mVelocity; }
|
||||
F32 getPriority() const { return mPriority; }
|
||||
|
||||
// Gain should always be clamped between 0 and 1.
|
||||
F32 getGain() const { return mGain; }
|
||||
virtual void setGain(const F32 gain) { mGain = llclamp(gain, 0.f, 1.f); }
|
||||
|
||||
const LLUUID &getID() const { return mID; }
|
||||
bool isDone() const;
|
||||
bool isMuted() const { return mSourceMuted; }
|
||||
|
||||
LLAudioData *getCurrentData();
|
||||
LLAudioData *getQueuedData();
|
||||
LLAudioBuffer *getCurrentBuffer();
|
||||
|
||||
bool setupChannel();
|
||||
bool play(const LLUUID &audio_id); // Start the audio source playing
|
||||
|
||||
bool hasPendingPreloads() const; // Has preloads that haven't been done yet
|
||||
|
||||
friend class LLAudioEngine;
|
||||
friend class LLAudioChannel;
|
||||
protected:
|
||||
void setChannel(LLAudioChannel *channelp);
|
||||
LLAudioChannel *getChannel() const { return mChannelp; }
|
||||
|
||||
protected:
|
||||
LLUUID mID; // The ID of the source is that of the object if it's attached to an object.
|
||||
LLUUID mOwnerID; // owner of the object playing the sound
|
||||
F32 mPriority;
|
||||
F32 mGain;
|
||||
bool mSourceMuted;
|
||||
bool mAmbient;
|
||||
bool mLoop;
|
||||
bool mSyncMaster;
|
||||
bool mSyncSlave;
|
||||
bool mQueueSounds;
|
||||
bool mPlayedOnce;
|
||||
S32 mType;
|
||||
LLVector3d mPositionGlobal;
|
||||
LLVector3 mVelocity;
|
||||
|
||||
//LLAudioSource *mSyncMasterp; // If we're a slave, the source that we're synced to.
|
||||
LLAudioChannel *mChannelp; // If we're currently playing back, this is the channel that we're assigned to.
|
||||
LLAudioData *mCurrentDatap;
|
||||
LLAudioData *mQueuedDatap;
|
||||
|
||||
typedef std::map<LLUUID, LLAudioData *> data_map;
|
||||
data_map mPreloadMap;
|
||||
|
||||
LLFrameTimer mAgeTimer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Generic metadata about a particular piece of audio data.
|
||||
// The actual data is handled by the derived LLAudioBuffer classes which are
|
||||
// derived for each audio engine.
|
||||
//
|
||||
|
||||
|
||||
class LLAudioData
|
||||
{
|
||||
public:
|
||||
LLAudioData(const LLUUID &uuid);
|
||||
bool load();
|
||||
|
||||
LLUUID getID() const { return mID; }
|
||||
LLAudioBuffer *getBuffer() const { return mBufferp; }
|
||||
|
||||
bool hasLocalData() const { return mHasLocalData; }
|
||||
bool hasDecodedData() const { return mHasDecodedData; }
|
||||
bool hasValidData() const { return mHasValidData; }
|
||||
|
||||
void setHasLocalData(const bool hld) { mHasLocalData = hld; }
|
||||
void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; }
|
||||
void setHasValidData(const bool hvd) { mHasValidData = hvd; }
|
||||
|
||||
friend class LLAudioEngine; // Severe laziness, bad.
|
||||
|
||||
protected:
|
||||
LLUUID mID;
|
||||
LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here.
|
||||
bool mHasLocalData;
|
||||
bool mHasDecodedData;
|
||||
bool mHasValidData;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Base class for an audio channel, i.e. a channel which is capable of playing back a sound.
|
||||
// Management of channels is done generically, methods for actually manipulating the channel
|
||||
// are derived for each audio engine.
|
||||
//
|
||||
|
||||
|
||||
class LLAudioChannel
|
||||
{
|
||||
public:
|
||||
LLAudioChannel();
|
||||
virtual ~LLAudioChannel();
|
||||
|
||||
virtual void setSource(LLAudioSource *sourcep);
|
||||
LLAudioSource *getSource() const { return mCurrentSourcep; }
|
||||
|
||||
void setSecondaryGain(F32 gain) { mSecondaryGain = gain; }
|
||||
F32 getSecondaryGain() { return mSecondaryGain; }
|
||||
|
||||
friend class LLAudioEngine;
|
||||
friend class LLAudioSource;
|
||||
protected:
|
||||
virtual void play() = 0;
|
||||
virtual void playSynced(LLAudioChannel *channelp) = 0;
|
||||
virtual void cleanup() = 0;
|
||||
virtual bool isPlaying() = 0;
|
||||
void setWaiting(const bool waiting) { mWaiting = waiting; }
|
||||
bool isWaiting() const { return mWaiting; }
|
||||
|
||||
virtual bool updateBuffer(); // Check to see if the buffer associated with the source changed, and update if necessary.
|
||||
virtual void update3DPosition() = 0;
|
||||
virtual void updateLoop() = 0; // Update your loop/completion status, for use by queueing/syncing.
|
||||
protected:
|
||||
LLAudioSource *mCurrentSourcep;
|
||||
LLAudioBuffer *mCurrentBufferp;
|
||||
bool mLoopedThisFrame;
|
||||
bool mWaiting; // Waiting for sync.
|
||||
F32 mSecondaryGain;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// Basically an interface class to the engine-specific implementation
|
||||
// of audio data that's ready for playback.
|
||||
// Will likely get more complex as we decide to do stuff like real streaming audio.
|
||||
|
||||
|
||||
class LLAudioBuffer
|
||||
{
|
||||
public:
|
||||
virtual ~LLAudioBuffer() {};
|
||||
virtual bool loadWAV(const std::string& filename) = 0;
|
||||
virtual U32 getLength() = 0;
|
||||
|
||||
friend class LLAudioEngine;
|
||||
friend class LLAudioChannel;
|
||||
friend class LLAudioData;
|
||||
protected:
|
||||
bool mInUse;
|
||||
LLAudioData *mAudioDatap;
|
||||
LLFrameTimer mLastUseTimer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
extern LLAudioEngine* gAudiop;
|
||||
|
||||
#endif
|
||||
784
indra/llaudio/llaudioengine_fmod.cpp
Normal file
784
indra/llaudio/llaudioengine_fmod.cpp
Normal file
@@ -0,0 +1,784 @@
|
||||
/**
|
||||
* @file audioengine_fmod.cpp
|
||||
* @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llstreamingaudio.h"
|
||||
#include "llstreamingaudio_fmod.h"
|
||||
|
||||
#include "llaudioengine_fmod.h"
|
||||
#include "lllistener_fmod.h"
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llmath.h"
|
||||
#include "llrand.h"
|
||||
|
||||
#include "fmod.h"
|
||||
#include "fmod_errors.h"
|
||||
#include "lldir.h"
|
||||
#include "llapr.h"
|
||||
|
||||
#include "sound_ids.h"
|
||||
|
||||
|
||||
extern "C" {
|
||||
void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata);
|
||||
}
|
||||
|
||||
|
||||
LLAudioEngine_FMOD::LLAudioEngine_FMOD()
|
||||
{
|
||||
mInited = false;
|
||||
mWindGen = NULL;
|
||||
mWindDSP = NULL;
|
||||
}
|
||||
|
||||
|
||||
LLAudioEngine_FMOD::~LLAudioEngine_FMOD()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata)
|
||||
{
|
||||
LLAudioEngine::init(num_channels, userdata);
|
||||
|
||||
// Reserve one extra channel for the http stream.
|
||||
if (!FSOUND_SetMinHardwareChannels(num_channels + 1))
|
||||
{
|
||||
LL_WARNS("AppInit") << "FMOD::init[0](), error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
|
||||
}
|
||||
|
||||
LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() initializing FMOD" << LL_ENDL;
|
||||
|
||||
F32 version = FSOUND_GetVersion();
|
||||
if (version < FMOD_VERSION)
|
||||
{
|
||||
LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version
|
||||
<< ")! You should be using FMOD " << FMOD_VERSION << LL_ENDL;
|
||||
//return false;
|
||||
}
|
||||
|
||||
U32 fmod_flags = 0x0;
|
||||
|
||||
#if LL_WINDOWS
|
||||
// Windows needs to know which window is frontmost.
|
||||
// This must be called before FSOUND_Init() per the FMOD docs.
|
||||
// This could be used to let FMOD handle muting when we lose focus,
|
||||
// but we don't actually want to do that because we want to distinguish
|
||||
// between minimized and not-focused states.
|
||||
if (!FSOUND_SetHWND(userdata))
|
||||
{
|
||||
LL_WARNS("AppInit") << "Error setting FMOD window: "
|
||||
<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
// Play audio when we don't have focus.
|
||||
// (For example, IM client on top of us.)
|
||||
// This means we also try to play audio when minimized,
|
||||
// so we manually handle muting in that case. JC
|
||||
fmod_flags |= FSOUND_INIT_GLOBALFOCUS;
|
||||
#endif
|
||||
|
||||
#if LL_LINUX
|
||||
// initialize the FMOD engine
|
||||
|
||||
// This is a hack to use only FMOD's basic FPU mixer
|
||||
// when the LL_VALGRIND environmental variable is set,
|
||||
// otherwise valgrind will fall over on FMOD's MMX detection
|
||||
if (getenv("LL_VALGRIND")) /*Flawfinder: ignore*/
|
||||
{
|
||||
LL_INFOS("AppInit") << "Pacifying valgrind in FMOD init." << LL_ENDL;
|
||||
FSOUND_SetMixer(FSOUND_MIXER_QUALITY_FPU);
|
||||
}
|
||||
|
||||
// If we don't set an output method, Linux FMOD always
|
||||
// decides on OSS and fails otherwise. So we'll manually
|
||||
// try ESD, then OSS, then ALSA.
|
||||
// Why this order? See SL-13250, but in short, OSS emulated
|
||||
// on top of ALSA is ironically more reliable than raw ALSA.
|
||||
// Ack, and ESD has more reliable failure modes - but has worse
|
||||
// latency - than all of them, so wins for now.
|
||||
bool audio_ok = false;
|
||||
|
||||
if (!audio_ok)
|
||||
if (NULL == getenv("LL_BAD_FMOD_ESD")) /*Flawfinder: ignore*/
|
||||
{
|
||||
LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL;
|
||||
if(FSOUND_SetOutput(FSOUND_OUTPUT_ESD) &&
|
||||
FSOUND_Init(44100, num_channels, fmod_flags))
|
||||
{
|
||||
LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY"
|
||||
<< LL_ENDL;
|
||||
audio_ok = true;
|
||||
} else {
|
||||
LL_WARNS("AppInit") << "ESD audio output FAILED to initialize: "
|
||||
<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
|
||||
}
|
||||
} else {
|
||||
LL_DEBUGS("AppInit") << "ESD audio output SKIPPED" << LL_ENDL;
|
||||
}
|
||||
|
||||
if (!audio_ok)
|
||||
if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/
|
||||
{
|
||||
LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL;
|
||||
if(FSOUND_SetOutput(FSOUND_OUTPUT_OSS) &&
|
||||
FSOUND_Init(44100, num_channels, fmod_flags))
|
||||
{
|
||||
LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL;
|
||||
audio_ok = true;
|
||||
} else {
|
||||
LL_WARNS("AppInit") << "OSS audio output FAILED to initialize: "
|
||||
<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
|
||||
}
|
||||
} else {
|
||||
LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
|
||||
}
|
||||
|
||||
if (!audio_ok)
|
||||
if (NULL == getenv("LL_BAD_FMOD_ALSA")) /*Flawfinder: ignore*/
|
||||
{
|
||||
LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
|
||||
if(FSOUND_SetOutput(FSOUND_OUTPUT_ALSA) &&
|
||||
FSOUND_Init(44100, num_channels, fmod_flags))
|
||||
{
|
||||
LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
|
||||
audio_ok = true;
|
||||
} else {
|
||||
LL_WARNS("AppInit") << "ALSA audio output FAILED to initialize: "
|
||||
<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
|
||||
}
|
||||
} else {
|
||||
LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
|
||||
}
|
||||
|
||||
if (!audio_ok)
|
||||
{
|
||||
LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// On Linux, FMOD causes a SIGPIPE for some netstream error
|
||||
// conditions (an FMOD bug); ignore SIGPIPE so it doesn't crash us.
|
||||
// NOW FIXED in FMOD 3.x since 2006-10-01.
|
||||
//signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
// We're interested in logging which output method we
|
||||
// ended up with, for QA purposes.
|
||||
switch (FSOUND_GetOutput())
|
||||
{
|
||||
case FSOUND_OUTPUT_NOSOUND: LL_DEBUGS("AppInit") << "Audio output: NoSound" << LL_ENDL; break;
|
||||
case FSOUND_OUTPUT_OSS: LL_DEBUGS("AppInit") << "Audio output: OSS" << LL_ENDL; break;
|
||||
case FSOUND_OUTPUT_ESD: LL_DEBUGS("AppInit") << "Audio output: ESD" << LL_ENDL; break;
|
||||
case FSOUND_OUTPUT_ALSA: LL_DEBUGS("AppInit") << "Audio output: ALSA" << LL_ENDL; break;
|
||||
default: LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break;
|
||||
};
|
||||
|
||||
#else // LL_LINUX
|
||||
|
||||
// initialize the FMOD engine
|
||||
if (!FSOUND_Init(44100, num_channels, fmod_flags))
|
||||
{
|
||||
LL_WARNS("AppInit") << "Error initializing FMOD: "
|
||||
<< FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// set up our favourite FMOD-native streaming audio implementation if none has already been added
|
||||
if (!getStreamingAudioImpl()) // no existing implementation added
|
||||
setStreamingAudioImpl(new LLStreamingAudio_FMOD());
|
||||
|
||||
LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() FMOD initialized correctly" << LL_ENDL;
|
||||
|
||||
mInited = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::string LLAudioEngine_FMOD::getDriverName(bool verbose)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
F32 version = FSOUND_GetVersion();
|
||||
return llformat("FMOD version %f", version);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "FMOD";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLAudioEngine_FMOD::allocateListener(void)
|
||||
{
|
||||
mListenerp = (LLListener *) new LLListener_FMOD();
|
||||
if (!mListenerp)
|
||||
{
|
||||
llwarns << "Listener creation failed" << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLAudioEngine_FMOD::shutdown()
|
||||
{
|
||||
if (mWindDSP)
|
||||
{
|
||||
FSOUND_DSP_SetActive(mWindDSP,false);
|
||||
FSOUND_DSP_Free(mWindDSP);
|
||||
}
|
||||
|
||||
stopInternetStream();
|
||||
|
||||
LLAudioEngine::shutdown();
|
||||
|
||||
llinfos << "LLAudioEngine_FMOD::shutdown() closing FMOD" << llendl;
|
||||
FSOUND_Close();
|
||||
llinfos << "LLAudioEngine_FMOD::shutdown() done closing FMOD" << llendl;
|
||||
|
||||
delete mListenerp;
|
||||
mListenerp = NULL;
|
||||
}
|
||||
|
||||
|
||||
LLAudioBuffer * LLAudioEngine_FMOD::createBuffer()
|
||||
{
|
||||
return new LLAudioBufferFMOD();
|
||||
}
|
||||
|
||||
|
||||
LLAudioChannel * LLAudioEngine_FMOD::createChannel()
|
||||
{
|
||||
return new LLAudioChannelFMOD();
|
||||
}
|
||||
|
||||
|
||||
bool LLAudioEngine_FMOD::initWind()
|
||||
{
|
||||
if (!mWindGen)
|
||||
{
|
||||
bool enable;
|
||||
|
||||
switch (FSOUND_GetMixer())
|
||||
{
|
||||
case FSOUND_MIXER_MMXP5:
|
||||
case FSOUND_MIXER_MMXP6:
|
||||
case FSOUND_MIXER_QUALITY_MMXP5:
|
||||
case FSOUND_MIXER_QUALITY_MMXP6:
|
||||
enable = (typeid(MIXBUFFERFORMAT) == typeid(S16));
|
||||
break;
|
||||
case FSOUND_MIXER_BLENDMODE:
|
||||
enable = (typeid(MIXBUFFERFORMAT) == typeid(S32));
|
||||
break;
|
||||
case FSOUND_MIXER_QUALITY_FPU:
|
||||
enable = (typeid(MIXBUFFERFORMAT) == typeid(F32));
|
||||
break;
|
||||
default:
|
||||
// FSOUND_GetMixer() does not return a valid mixer type on Darwin
|
||||
LL_INFOS("AppInit") << "Unknown FMOD mixer type, assuming default" << LL_ENDL;
|
||||
enable = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
{
|
||||
mWindGen = new LLWindGen<MIXBUFFERFORMAT>(FSOUND_GetOutputRate());
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("AppInit") << "Incompatible FMOD mixer type, wind noise disabled" << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
mNextWindUpdate = 0.0;
|
||||
|
||||
if (mWindGen && !mWindDSP)
|
||||
{
|
||||
mWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, mWindGen);
|
||||
}
|
||||
if (mWindDSP)
|
||||
{
|
||||
FSOUND_DSP_SetActive(mWindDSP, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LLAudioEngine_FMOD::cleanupWind()
|
||||
{
|
||||
if (mWindDSP)
|
||||
{
|
||||
FSOUND_DSP_SetActive(mWindDSP, false);
|
||||
FSOUND_DSP_Free(mWindDSP);
|
||||
mWindDSP = NULL;
|
||||
}
|
||||
|
||||
delete mWindGen;
|
||||
mWindGen = NULL;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_water)
|
||||
{
|
||||
LLVector3 wind_pos;
|
||||
F64 pitch;
|
||||
F64 center_freq;
|
||||
|
||||
if (!mEnableWind)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
|
||||
{
|
||||
|
||||
// wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
|
||||
// need to convert this to the conventional orientation DS3D and OpenAL use
|
||||
// where +X = right, +Y = up, +Z = backwards
|
||||
|
||||
wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
|
||||
|
||||
// cerr << "Wind update" << endl;
|
||||
|
||||
pitch = 1.0 + mapWindVecToPitch(wind_vec);
|
||||
center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
|
||||
|
||||
mWindGen->mTargetFreq = (F32)center_freq;
|
||||
mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
|
||||
mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
//-----------------------------------------------------------------------
|
||||
void LLAudioEngine_FMOD::setSourceMinDistance(U16 source_num, F64 distance)
|
||||
{
|
||||
if (!mInited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (mBuffer[source_num])
|
||||
{
|
||||
mMinDistance[source_num] = (F32) distance;
|
||||
if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num]))
|
||||
{
|
||||
llwarns << "FMOD::setSourceMinDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLAudioEngine_FMOD::setSourceMaxDistance(U16 source_num, F64 distance)
|
||||
{
|
||||
if (!mInited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (mBuffer[source_num])
|
||||
{
|
||||
mMaxDistance[source_num] = (F32) distance;
|
||||
if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num]))
|
||||
{
|
||||
llwarns << "FMOD::setSourceMaxDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
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)
|
||||
{
|
||||
*volume = 0;
|
||||
*freq = 0;
|
||||
*inside = 0;
|
||||
*outside = 0;
|
||||
*orient = LLVector3::zero;
|
||||
*out_volume = 0;
|
||||
*min_dist = 0.f;
|
||||
*max_dist = 0.f;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLAudioEngine_FMOD::setInternalGain(F32 gain)
|
||||
{
|
||||
if (!mInited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gain = llclamp( gain, 0.0f, 1.0f );
|
||||
FSOUND_SetSFXMasterVolume( llround( 255.0f * gain ) );
|
||||
|
||||
LLStreamingAudioInterface *saimpl = getStreamingAudioImpl();
|
||||
if ( saimpl )
|
||||
{
|
||||
// fmod likes its streaming audio channel gain re-asserted after
|
||||
// master volume change.
|
||||
saimpl->setGain(saimpl->getGain());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// LLAudioChannelFMOD implementation
|
||||
//
|
||||
|
||||
LLAudioChannelFMOD::LLAudioChannelFMOD() : LLAudioChannel(), mChannelID(0), mLastSamplePos(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
LLAudioChannelFMOD::~LLAudioChannelFMOD()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
|
||||
bool LLAudioChannelFMOD::updateBuffer()
|
||||
{
|
||||
if (LLAudioChannel::updateBuffer())
|
||||
{
|
||||
// Base class update returned true, which means that we need to actually
|
||||
// set up the channel for a different buffer.
|
||||
|
||||
LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentSourcep->getCurrentBuffer();
|
||||
|
||||
// Grab the FMOD sample associated with the buffer
|
||||
FSOUND_SAMPLE *samplep = bufferp->getSample();
|
||||
if (!samplep)
|
||||
{
|
||||
// This is bad, there should ALWAYS be a sample associated with a legit
|
||||
// buffer.
|
||||
llerrs << "No FMOD sample!" << llendl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Actually play the sound. Start it off paused so we can do all the necessary
|
||||
// setup.
|
||||
mChannelID = FSOUND_PlaySoundEx(FSOUND_FREE, samplep, FSOUND_DSP_GetSFXUnit(), true);
|
||||
|
||||
//llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl;
|
||||
}
|
||||
|
||||
// If we have a source for the channel, we need to update its gain.
|
||||
if (mCurrentSourcep)
|
||||
{
|
||||
// SJB: warnings can spam and hurt framerate, disabling
|
||||
if (!FSOUND_SetVolume(mChannelID, llround(getSecondaryGain() * mCurrentSourcep->getGain() * 255.0f)))
|
||||
{
|
||||
// llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
}
|
||||
|
||||
if (!FSOUND_SetLoopMode(mChannelID, mCurrentSourcep->isLoop() ? FSOUND_LOOP_NORMAL : FSOUND_LOOP_OFF))
|
||||
{
|
||||
// llwarns << "Channel " << mChannelID << "Source ID: " << mCurrentSourcep->getID()
|
||||
// << " at " << mCurrentSourcep->getPositionGlobal() << llendl;
|
||||
// llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void LLAudioChannelFMOD::update3DPosition()
|
||||
{
|
||||
if (!mChannelID)
|
||||
{
|
||||
// We're not actually a live channel (i.e., we're not playing back anything)
|
||||
return;
|
||||
}
|
||||
|
||||
LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentBufferp;
|
||||
if (!bufferp)
|
||||
{
|
||||
// We don't have a buffer associated with us (should really have been picked up
|
||||
// by the above if.
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentSourcep->isAmbient())
|
||||
{
|
||||
// Ambient sound, don't need to do any positional updates.
|
||||
bufferp->set3DMode(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Localized sound. Update the position and velocity of the sound.
|
||||
bufferp->set3DMode(true);
|
||||
|
||||
LLVector3 float_pos;
|
||||
float_pos.setVec(mCurrentSourcep->getPositionGlobal());
|
||||
if (!FSOUND_3D_SetAttributes(mChannelID, float_pos.mV, mCurrentSourcep->getVelocity().mV))
|
||||
{
|
||||
LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::update3DPosition error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLAudioChannelFMOD::updateLoop()
|
||||
{
|
||||
if (!mChannelID)
|
||||
{
|
||||
// May want to clear up the loop/sample counters.
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Hack: We keep track of whether we looped or not by seeing when the
|
||||
// sample position looks like it's going backwards. Not reliable; may
|
||||
// yield false negatives.
|
||||
//
|
||||
U32 cur_pos = FSOUND_GetCurrentPosition(mChannelID);
|
||||
if (cur_pos < (U32)mLastSamplePos)
|
||||
{
|
||||
mLoopedThisFrame = true;
|
||||
}
|
||||
mLastSamplePos = cur_pos;
|
||||
}
|
||||
|
||||
|
||||
void LLAudioChannelFMOD::cleanup()
|
||||
{
|
||||
if (!mChannelID)
|
||||
{
|
||||
//llinfos << "Aborting cleanup with no channelID." << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
//llinfos << "Cleaning up channel: " << mChannelID << llendl;
|
||||
if (!FSOUND_StopSound(mChannelID))
|
||||
{
|
||||
LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
}
|
||||
|
||||
mCurrentBufferp = NULL;
|
||||
mChannelID = 0;
|
||||
}
|
||||
|
||||
|
||||
void LLAudioChannelFMOD::play()
|
||||
{
|
||||
if (!mChannelID)
|
||||
{
|
||||
llwarns << "Playing without a channelID, aborting" << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FSOUND_SetPaused(mChannelID, false))
|
||||
{
|
||||
llwarns << "LLAudioChannelFMOD::play error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
}
|
||||
getSource()->setPlayedOnce(true);
|
||||
}
|
||||
|
||||
|
||||
void LLAudioChannelFMOD::playSynced(LLAudioChannel *channelp)
|
||||
{
|
||||
LLAudioChannelFMOD *fmod_channelp = (LLAudioChannelFMOD*)channelp;
|
||||
if (!(fmod_channelp->mChannelID && mChannelID))
|
||||
{
|
||||
// Don't have channels allocated to both the master and the slave
|
||||
return;
|
||||
}
|
||||
|
||||
U32 position = FSOUND_GetCurrentPosition(fmod_channelp->mChannelID) % mCurrentBufferp->getLength();
|
||||
// Try to match the position of our sync master
|
||||
if (!FSOUND_SetCurrentPosition(mChannelID, position))
|
||||
{
|
||||
llwarns << "LLAudioChannelFMOD::playSynced unable to set current position" << llendl;
|
||||
}
|
||||
|
||||
// Start us playing
|
||||
play();
|
||||
}
|
||||
|
||||
|
||||
bool LLAudioChannelFMOD::isPlaying()
|
||||
{
|
||||
if (!mChannelID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return FSOUND_IsPlaying(mChannelID) && (!FSOUND_GetPaused(mChannelID));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// LLAudioBufferFMOD implementation
|
||||
//
|
||||
|
||||
|
||||
LLAudioBufferFMOD::LLAudioBufferFMOD()
|
||||
{
|
||||
mSamplep = NULL;
|
||||
}
|
||||
|
||||
|
||||
LLAudioBufferFMOD::~LLAudioBufferFMOD()
|
||||
{
|
||||
if (mSamplep)
|
||||
{
|
||||
// Clean up the associated FMOD sample if it exists.
|
||||
FSOUND_Sample_Free(mSamplep);
|
||||
mSamplep = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LLAudioBufferFMOD::loadWAV(const std::string& filename)
|
||||
{
|
||||
// Try to open a wav file from disk. This will eventually go away, as we don't
|
||||
// really want to block doing this.
|
||||
if (filename.empty())
|
||||
{
|
||||
// invalid filename, abort.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LLAPRFile::isExist(filename, LL_APR_RPB))
|
||||
{
|
||||
// File not found, abort.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mSamplep)
|
||||
{
|
||||
// If there's already something loaded in this buffer, clean it up.
|
||||
FSOUND_Sample_Free(mSamplep);
|
||||
mSamplep = NULL;
|
||||
}
|
||||
|
||||
// Load up the wav file into an fmod sample
|
||||
#if LL_WINDOWS
|
||||
// MikeS. - Loading the sound file manually and then handing it over to FMOD,
|
||||
// since FMOD uses posix IO internally,
|
||||
// which doesn't work with unicode file paths.
|
||||
LLFILE* sound_file = LLFile::fopen(filename,"rb"); /* Flawfinder: ignore */
|
||||
if (sound_file)
|
||||
{
|
||||
fseek(sound_file,0,SEEK_END);
|
||||
U32 file_length = ftell(sound_file); //Find the length of the file by seeking to the end and getting the offset
|
||||
size_t read_count;
|
||||
fseek(sound_file,0,SEEK_SET); //Seek back to the beginning
|
||||
char* buffer = new char[file_length];
|
||||
llassert(buffer);
|
||||
read_count = fread((void*)buffer,file_length,1,sound_file);//Load it..
|
||||
if(ferror(sound_file)==0 && (read_count == 1)){//No read error, and we got 1 chunk of our size...
|
||||
unsigned int mode_flags = FSOUND_LOOP_NORMAL | FSOUND_LOADMEMORY;
|
||||
//FSOUND_16BITS | FSOUND_MONO | FSOUND_LOADMEMORY | FSOUND_LOOP_NORMAL;
|
||||
mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, buffer, mode_flags , 0, file_length);
|
||||
}
|
||||
delete[] buffer;
|
||||
fclose(sound_file);
|
||||
}
|
||||
#else
|
||||
mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, filename.c_str(), FSOUND_LOOP_NORMAL, 0, 0);
|
||||
#endif
|
||||
|
||||
if (!mSamplep)
|
||||
{
|
||||
// We failed to load the file for some reason.
|
||||
llwarns << "Could not load data '" << filename << "': "
|
||||
<< FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
|
||||
//
|
||||
// If we EVER want to load wav files provided by end users, we need
|
||||
// to rethink this!
|
||||
//
|
||||
// file is probably corrupt - remove it.
|
||||
LLFile::remove(filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Everything went well, return true
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
U32 LLAudioBufferFMOD::getLength()
|
||||
{
|
||||
if (!mSamplep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return FSOUND_Sample_GetLength(mSamplep);
|
||||
}
|
||||
|
||||
|
||||
void LLAudioBufferFMOD::set3DMode(bool use3d)
|
||||
{
|
||||
U16 current_mode = FSOUND_Sample_GetMode(mSamplep);
|
||||
|
||||
if (use3d)
|
||||
{
|
||||
if (!FSOUND_Sample_SetMode(mSamplep, (current_mode & (~FSOUND_2D))))
|
||||
{
|
||||
llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FSOUND_Sample_SetMode(mSamplep, current_mode | FSOUND_2D))
|
||||
{
|
||||
llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata)
|
||||
{
|
||||
// originalbuffer = fmod's original mixbuffer.
|
||||
// newbuffer = the buffer passed from the previous DSP unit.
|
||||
// length = length in samples at this mix time.
|
||||
// userdata = user parameter passed through in FSOUND_DSP_Create.
|
||||
|
||||
LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *windgen =
|
||||
(LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *)userdata;
|
||||
|
||||
newbuffer = windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT *)newbuffer, length);
|
||||
|
||||
return newbuffer;
|
||||
}
|
||||
130
indra/llaudio/llaudioengine_fmod.h
Normal file
130
indra/llaudio/llaudioengine_fmod.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @file audioengine_fmod.h
|
||||
* @brief Definition of LLAudioEngine class abstracting the audio
|
||||
* support as a FMOD 3D implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_AUDIOENGINE_FMOD_H
|
||||
#define LL_AUDIOENGINE_FMOD_H
|
||||
|
||||
#include "llaudioengine.h"
|
||||
#include "lllistener_fmod.h"
|
||||
#include "llwindgen.h"
|
||||
|
||||
#include "fmod.h"
|
||||
|
||||
class LLAudioStreamManagerFMOD;
|
||||
|
||||
class LLAudioEngine_FMOD : public LLAudioEngine
|
||||
{
|
||||
public:
|
||||
LLAudioEngine_FMOD();
|
||||
virtual ~LLAudioEngine_FMOD();
|
||||
|
||||
// initialization/startup/shutdown
|
||||
virtual bool init(const S32 num_channels, void *user_data);
|
||||
virtual std::string getDriverName(bool verbose);
|
||||
virtual void allocateListener();
|
||||
|
||||
virtual void shutdown();
|
||||
|
||||
/*virtual*/ bool initWind();
|
||||
/*virtual*/ void cleanupWind();
|
||||
|
||||
/*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water);
|
||||
|
||||
#if LL_DARWIN
|
||||
typedef S32 MIXBUFFERFORMAT;
|
||||
#else
|
||||
typedef S16 MIXBUFFERFORMAT;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to.
|
||||
/*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel.
|
||||
|
||||
/*virtual*/ void setInternalGain(F32 gain);
|
||||
protected:
|
||||
static signed char F_CALLBACKAPI callbackMetaData(char* name, char* value, void* userdata);
|
||||
|
||||
//F32 mMinDistance[MAX_BUFFERS];
|
||||
//F32 mMaxDistance[MAX_BUFFERS];
|
||||
|
||||
bool mInited;
|
||||
|
||||
// On Windows, userdata is the HWND of the application window.
|
||||
void* mUserData;
|
||||
|
||||
LLWindGen<MIXBUFFERFORMAT> *mWindGen;
|
||||
FSOUND_DSPUNIT *mWindDSP;
|
||||
};
|
||||
|
||||
|
||||
class LLAudioChannelFMOD : public LLAudioChannel
|
||||
{
|
||||
public:
|
||||
LLAudioChannelFMOD();
|
||||
virtual ~LLAudioChannelFMOD();
|
||||
|
||||
protected:
|
||||
/*virtual*/ void play();
|
||||
/*virtual*/ void playSynced(LLAudioChannel *channelp);
|
||||
/*virtual*/ void cleanup();
|
||||
/*virtual*/ bool isPlaying();
|
||||
|
||||
/*virtual*/ bool updateBuffer();
|
||||
/*virtual*/ void update3DPosition();
|
||||
/*virtual*/ void updateLoop();
|
||||
|
||||
protected:
|
||||
int mChannelID;
|
||||
S32 mLastSamplePos;
|
||||
};
|
||||
|
||||
|
||||
class LLAudioBufferFMOD : public LLAudioBuffer
|
||||
{
|
||||
public:
|
||||
LLAudioBufferFMOD();
|
||||
virtual ~LLAudioBufferFMOD();
|
||||
|
||||
/*virtual*/ bool loadWAV(const std::string& filename);
|
||||
/*virtual*/ U32 getLength();
|
||||
friend class LLAudioChannelFMOD;
|
||||
|
||||
void set3DMode(bool use3d);
|
||||
protected:
|
||||
FSOUND_SAMPLE *getSample() { return mSamplep; }
|
||||
protected:
|
||||
FSOUND_SAMPLE *mSamplep;
|
||||
};
|
||||
|
||||
|
||||
#endif // LL_AUDIOENGINE_FMOD_H
|
||||
548
indra/llaudio/llaudioengine_openal.cpp
Normal file
548
indra/llaudio/llaudioengine_openal.cpp
Normal file
@@ -0,0 +1,548 @@
|
||||
/**
|
||||
* @file audioengine_openal.cpp
|
||||
* @brief implementation of audio engine using OpenAL
|
||||
* support as a OpenAL 3D implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "lldir.h"
|
||||
|
||||
#include "llaudioengine_openal.h"
|
||||
#include "lllistener_openal.h"
|
||||
|
||||
|
||||
LLAudioEngine_OpenAL::LLAudioEngine_OpenAL()
|
||||
:
|
||||
mWindGen(NULL),
|
||||
mWindBuf(NULL),
|
||||
mWindBufFreq(0),
|
||||
mWindBufSamples(0),
|
||||
mWindBufBytes(0),
|
||||
mWindSource(AL_NONE),
|
||||
mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS)
|
||||
{
|
||||
}
|
||||
|
||||
// virtual
|
||||
LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL()
|
||||
{
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata)
|
||||
{
|
||||
mWindGen = NULL;
|
||||
LLAudioEngine::init(num_channels, userdata);
|
||||
|
||||
if(!alutInit(NULL, NULL))
|
||||
{
|
||||
llwarns << "LLAudioEngine_OpenAL::init() ALUT initialization failed: " << alutGetErrorString (alutGetError ()) << llendl;
|
||||
return false;
|
||||
}
|
||||
|
||||
llinfos << "LLAudioEngine_OpenAL::init() OpenAL successfully initialized" << llendl;
|
||||
|
||||
llinfos << "OpenAL version: "
|
||||
<< ll_safe_string(alGetString(AL_VERSION)) << llendl;
|
||||
llinfos << "OpenAL vendor: "
|
||||
<< ll_safe_string(alGetString(AL_VENDOR)) << llendl;
|
||||
llinfos << "OpenAL renderer: "
|
||||
<< ll_safe_string(alGetString(AL_RENDERER)) << llendl;
|
||||
|
||||
ALint major = alutGetMajorVersion ();
|
||||
ALint minor = alutGetMinorVersion ();
|
||||
llinfos << "ALUT version: " << major << "." << minor << llendl;
|
||||
|
||||
ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
|
||||
|
||||
alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major);
|
||||
alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &minor);
|
||||
llinfos << "ALC version: " << major << "." << minor << llendl;
|
||||
|
||||
llinfos << "ALC default device: "
|
||||
<< ll_safe_string(alcGetString(device,
|
||||
ALC_DEFAULT_DEVICE_SPECIFIER))
|
||||
<< llendl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// virtual
|
||||
std::string LLAudioEngine_OpenAL::getDriverName(bool verbose)
|
||||
{
|
||||
ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext());
|
||||
std::ostringstream version;
|
||||
|
||||
version <<
|
||||
"OpenAL";
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
version <<
|
||||
", version " <<
|
||||
ll_safe_string(alGetString(AL_VERSION)) <<
|
||||
" / " <<
|
||||
ll_safe_string(alGetString(AL_VENDOR)) <<
|
||||
" / " <<
|
||||
ll_safe_string(alGetString(AL_RENDERER));
|
||||
|
||||
if (device)
|
||||
version <<
|
||||
": " <<
|
||||
ll_safe_string(alcGetString(device,
|
||||
ALC_DEFAULT_DEVICE_SPECIFIER));
|
||||
}
|
||||
|
||||
return version.str();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void LLAudioEngine_OpenAL::allocateListener()
|
||||
{
|
||||
mListenerp = (LLListener *) new LLListener_OpenAL();
|
||||
if(!mListenerp)
|
||||
{
|
||||
llwarns << "LLAudioEngine_OpenAL::allocateListener() Listener creation failed" << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
void LLAudioEngine_OpenAL::shutdown()
|
||||
{
|
||||
llinfos << "About to LLAudioEngine::shutdown()" << llendl;
|
||||
LLAudioEngine::shutdown();
|
||||
|
||||
llinfos << "About to alutExit()" << llendl;
|
||||
if(!alutExit())
|
||||
{
|
||||
llwarns << "Nuts." << llendl;
|
||||
llwarns << "LLAudioEngine_OpenAL::shutdown() ALUT shutdown failed: " << alutGetErrorString (alutGetError ()) << llendl;
|
||||
}
|
||||
|
||||
llinfos << "LLAudioEngine_OpenAL::shutdown() OpenAL successfully shut down" << llendl;
|
||||
|
||||
delete mListenerp;
|
||||
mListenerp = NULL;
|
||||
}
|
||||
|
||||
LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer()
|
||||
{
|
||||
return new LLAudioBufferOpenAL();
|
||||
}
|
||||
|
||||
LLAudioChannel *LLAudioEngine_OpenAL::createChannel()
|
||||
{
|
||||
return new LLAudioChannelOpenAL();
|
||||
}
|
||||
|
||||
void LLAudioEngine_OpenAL::setInternalGain(F32 gain)
|
||||
{
|
||||
//llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl;
|
||||
alListenerf(AL_GAIN, gain);
|
||||
}
|
||||
|
||||
LLAudioChannelOpenAL::LLAudioChannelOpenAL()
|
||||
:
|
||||
mALSource(AL_NONE),
|
||||
mLastSamplePos(0)
|
||||
{
|
||||
alGenSources(1, &mALSource);
|
||||
}
|
||||
|
||||
LLAudioChannelOpenAL::~LLAudioChannelOpenAL()
|
||||
{
|
||||
cleanup();
|
||||
alDeleteSources(1, &mALSource);
|
||||
}
|
||||
|
||||
void LLAudioChannelOpenAL::cleanup()
|
||||
{
|
||||
alSourceStop(mALSource);
|
||||
mCurrentBufferp = NULL;
|
||||
}
|
||||
|
||||
void LLAudioChannelOpenAL::play()
|
||||
{
|
||||
if (mALSource == AL_NONE)
|
||||
{
|
||||
llwarns << "Playing without a mALSource, aborting" << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isPlaying())
|
||||
{
|
||||
alSourcePlay(mALSource);
|
||||
getSource()->setPlayedOnce(true);
|
||||
}
|
||||
}
|
||||
|
||||
void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp)
|
||||
{
|
||||
if (channelp)
|
||||
{
|
||||
LLAudioChannelOpenAL *masterchannelp =
|
||||
(LLAudioChannelOpenAL*)channelp;
|
||||
if (mALSource != AL_NONE &&
|
||||
masterchannelp->mALSource != AL_NONE)
|
||||
{
|
||||
// we have channels allocated to master and slave
|
||||
ALfloat master_offset;
|
||||
alGetSourcef(masterchannelp->mALSource, AL_SEC_OFFSET,
|
||||
&master_offset);
|
||||
|
||||
llinfos << "Syncing with master at " << master_offset
|
||||
<< "sec" << llendl;
|
||||
// *TODO: detect when this fails, maybe use AL_SAMPLE_
|
||||
alSourcef(mALSource, AL_SEC_OFFSET, master_offset);
|
||||
}
|
||||
}
|
||||
play();
|
||||
}
|
||||
|
||||
bool LLAudioChannelOpenAL::isPlaying()
|
||||
{
|
||||
if (mALSource != AL_NONE)
|
||||
{
|
||||
ALint state;
|
||||
alGetSourcei(mALSource, AL_SOURCE_STATE, &state);
|
||||
if(state == AL_PLAYING)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLAudioChannelOpenAL::updateBuffer()
|
||||
{
|
||||
if (LLAudioChannel::updateBuffer())
|
||||
{
|
||||
// Base class update returned true, which means that we need to actually
|
||||
// set up the source for a different buffer.
|
||||
LLAudioBufferOpenAL *bufferp = (LLAudioBufferOpenAL *)mCurrentSourcep->getCurrentBuffer();
|
||||
ALuint buffer = bufferp->getBuffer();
|
||||
alSourcei(mALSource, AL_BUFFER, buffer);
|
||||
mLastSamplePos = 0;
|
||||
}
|
||||
|
||||
if (mCurrentSourcep)
|
||||
{
|
||||
alSourcef(mALSource, AL_GAIN,
|
||||
mCurrentSourcep->getGain() * getSecondaryGain());
|
||||
alSourcei(mALSource, AL_LOOPING,
|
||||
mCurrentSourcep->isLoop() ? AL_TRUE : AL_FALSE);
|
||||
alSourcef(mALSource, AL_ROLLOFF_FACTOR,
|
||||
gAudiop->mListenerp->getRolloffFactor());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void LLAudioChannelOpenAL::updateLoop()
|
||||
{
|
||||
if (mALSource == AL_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Hack: We keep track of whether we looped or not by seeing when the
|
||||
// sample position looks like it's going backwards. Not reliable; may
|
||||
// yield false negatives.
|
||||
//
|
||||
ALint cur_pos;
|
||||
alGetSourcei(mALSource, AL_SAMPLE_OFFSET, &cur_pos);
|
||||
if (cur_pos < mLastSamplePos)
|
||||
{
|
||||
mLoopedThisFrame = true;
|
||||
}
|
||||
mLastSamplePos = cur_pos;
|
||||
}
|
||||
|
||||
|
||||
void LLAudioChannelOpenAL::update3DPosition()
|
||||
{
|
||||
if(!mCurrentSourcep)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (mCurrentSourcep->isAmbient())
|
||||
{
|
||||
alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0);
|
||||
alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0);
|
||||
alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||
} else {
|
||||
LLVector3 float_pos;
|
||||
float_pos.setVec(mCurrentSourcep->getPositionGlobal());
|
||||
alSourcefv(mALSource, AL_POSITION, float_pos.mV);
|
||||
alSourcefv(mALSource, AL_VELOCITY, mCurrentSourcep->getVelocity().mV);
|
||||
alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_FALSE);
|
||||
}
|
||||
|
||||
alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain() * getSecondaryGain());
|
||||
}
|
||||
|
||||
LLAudioBufferOpenAL::LLAudioBufferOpenAL()
|
||||
{
|
||||
mALBuffer = AL_NONE;
|
||||
}
|
||||
|
||||
LLAudioBufferOpenAL::~LLAudioBufferOpenAL()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void LLAudioBufferOpenAL::cleanup()
|
||||
{
|
||||
if(mALBuffer != AL_NONE)
|
||||
{
|
||||
alDeleteBuffers(1, &mALBuffer);
|
||||
mALBuffer = AL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
bool LLAudioBufferOpenAL::loadWAV(const std::string& filename)
|
||||
{
|
||||
cleanup();
|
||||
mALBuffer = alutCreateBufferFromFile(filename.c_str());
|
||||
if(mALBuffer == AL_NONE)
|
||||
{
|
||||
ALenum error = alutGetError();
|
||||
if (gDirUtilp->fileExists(filename))
|
||||
{
|
||||
llwarns <<
|
||||
"LLAudioBufferOpenAL::loadWAV() Error loading "
|
||||
<< filename
|
||||
<< " " << alutGetErrorString(error) << llendl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's common for the file to not actually exist.
|
||||
lldebugs <<
|
||||
"LLAudioBufferOpenAL::loadWAV() Error loading "
|
||||
<< filename
|
||||
<< " " << alutGetErrorString(error) << llendl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
U32 LLAudioBufferOpenAL::getLength()
|
||||
{
|
||||
if(mALBuffer == AL_NONE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
ALint length;
|
||||
alGetBufferi(mALBuffer, AL_SIZE, &length);
|
||||
return length / 2; // convert size in bytes to size in (16-bit) samples
|
||||
}
|
||||
|
||||
// ------------
|
||||
|
||||
bool LLAudioEngine_OpenAL::initWind()
|
||||
{
|
||||
ALenum error;
|
||||
llinfos << "LLAudioEngine_OpenAL::initWind() start" << llendl;
|
||||
|
||||
mNumEmptyWindALBuffers = MAX_NUM_WIND_BUFFERS;
|
||||
|
||||
alGetError(); /* clear error */
|
||||
|
||||
alGenSources(1,&mWindSource);
|
||||
|
||||
if((error=alGetError()) != AL_NO_ERROR)
|
||||
{
|
||||
llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind sources: "<<error<<llendl;
|
||||
}
|
||||
|
||||
mWindGen = new LLWindGen<WIND_SAMPLE_T>;
|
||||
|
||||
mWindBufFreq = mWindGen->getInputSamplingRate();
|
||||
mWindBufSamples = llceil(mWindBufFreq * WIND_BUFFER_SIZE_SEC);
|
||||
mWindBufBytes = mWindBufSamples * 2 /*stereo*/ * sizeof(WIND_SAMPLE_T);
|
||||
|
||||
mWindBuf = new WIND_SAMPLE_T [mWindBufSamples * 2 /*stereo*/];
|
||||
|
||||
if(mWindBuf==NULL)
|
||||
{
|
||||
llerrs << "LLAudioEngine_OpenAL::initWind() Error creating wind memory buffer" << llendl;
|
||||
return false;
|
||||
}
|
||||
|
||||
llinfos << "LLAudioEngine_OpenAL::initWind() done" << llendl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LLAudioEngine_OpenAL::cleanupWind()
|
||||
{
|
||||
llinfos << "LLAudioEngine_OpenAL::cleanupWind()" << llendl;
|
||||
|
||||
if (mWindSource != AL_NONE)
|
||||
{
|
||||
// detach and delete all outstanding buffers on the wind source
|
||||
alSourceStop(mWindSource);
|
||||
ALint processed;
|
||||
alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
while (processed--)
|
||||
{
|
||||
ALuint buffer = AL_NONE;
|
||||
alSourceUnqueueBuffers(mWindSource, 1, &buffer);
|
||||
alDeleteBuffers(1, &buffer);
|
||||
}
|
||||
|
||||
// delete the wind source itself
|
||||
alDeleteSources(1, &mWindSource);
|
||||
|
||||
mWindSource = AL_NONE;
|
||||
}
|
||||
|
||||
delete[] mWindBuf;
|
||||
mWindBuf = NULL;
|
||||
|
||||
delete mWindGen;
|
||||
mWindGen = NULL;
|
||||
}
|
||||
|
||||
void LLAudioEngine_OpenAL::updateWind(LLVector3 wind_vec, F32 camera_altitude)
|
||||
{
|
||||
LLVector3 wind_pos;
|
||||
F64 pitch;
|
||||
F64 center_freq;
|
||||
ALenum error;
|
||||
|
||||
if (!mEnableWind)
|
||||
return;
|
||||
|
||||
if(!mWindBuf)
|
||||
return;
|
||||
|
||||
if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
|
||||
{
|
||||
|
||||
// wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up)
|
||||
// need to convert this to the conventional orientation DS3D and OpenAL use
|
||||
// where +X = right, +Y = up, +Z = backwards
|
||||
|
||||
wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
|
||||
|
||||
pitch = 1.0 + mapWindVecToPitch(wind_vec);
|
||||
center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0));
|
||||
|
||||
mWindGen->mTargetFreq = (F32)center_freq;
|
||||
mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
|
||||
mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec);
|
||||
|
||||
alSourcei(mWindSource, AL_LOOPING, AL_FALSE);
|
||||
alSource3f(mWindSource, AL_POSITION, 0.0, 0.0, 0.0);
|
||||
alSource3f(mWindSource, AL_VELOCITY, 0.0, 0.0, 0.0);
|
||||
alSourcef(mWindSource, AL_ROLLOFF_FACTOR, 0.0);
|
||||
alSourcei(mWindSource, AL_SOURCE_RELATIVE, AL_TRUE);
|
||||
}
|
||||
|
||||
// ok lets make a wind buffer now
|
||||
|
||||
ALint processed, queued, unprocessed;
|
||||
alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
alGetSourcei(mWindSource, AL_BUFFERS_QUEUED, &queued);
|
||||
unprocessed = queued - processed;
|
||||
|
||||
// ensure that there are always at least 3x as many filled buffers
|
||||
// queued as we managed to empty since last time.
|
||||
mNumEmptyWindALBuffers = llmin(mNumEmptyWindALBuffers + processed * 3 - unprocessed, MAX_NUM_WIND_BUFFERS-unprocessed);
|
||||
mNumEmptyWindALBuffers = llmax(mNumEmptyWindALBuffers, 0);
|
||||
|
||||
//llinfos << "mNumEmptyWindALBuffers: " << mNumEmptyWindALBuffers <<" (" << unprocessed << ":" << processed << ")" << llendl;
|
||||
|
||||
while(processed--) // unqueue old buffers
|
||||
{
|
||||
ALuint buffer;
|
||||
ALenum error;
|
||||
alGetError(); /* clear error */
|
||||
alSourceUnqueueBuffers(mWindSource, 1, &buffer);
|
||||
error = alGetError();
|
||||
if(error != AL_NO_ERROR)
|
||||
{
|
||||
llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (unqueuing) buffers" << llendl;
|
||||
}
|
||||
else
|
||||
{
|
||||
alDeleteBuffers(1, &buffer);
|
||||
}
|
||||
}
|
||||
|
||||
unprocessed += mNumEmptyWindALBuffers;
|
||||
while (mNumEmptyWindALBuffers > 0) // fill+queue new buffers
|
||||
{
|
||||
ALuint buffer;
|
||||
alGetError(); /* clear error */
|
||||
alGenBuffers(1,&buffer);
|
||||
if((error=alGetError()) != AL_NO_ERROR)
|
||||
{
|
||||
llwarns << "LLAudioEngine_OpenAL::updateWind() Error creating wind buffer: " << error << llendl;
|
||||
break;
|
||||
}
|
||||
|
||||
alBufferData(buffer,
|
||||
AL_FORMAT_STEREO16,
|
||||
mWindGen->windGenerate(mWindBuf,
|
||||
mWindBufSamples),
|
||||
mWindBufBytes,
|
||||
mWindBufFreq);
|
||||
error = alGetError();
|
||||
if(error != AL_NO_ERROR)
|
||||
{
|
||||
llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (bufferdata) buffers" << llendl;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers(mWindSource, 1, &buffer);
|
||||
error = alGetError();
|
||||
if(error != AL_NO_ERROR)
|
||||
{
|
||||
llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (queuing) buffers" << llendl;
|
||||
}
|
||||
|
||||
--mNumEmptyWindALBuffers;
|
||||
}
|
||||
|
||||
ALint playing;
|
||||
alGetSourcei(mWindSource, AL_SOURCE_STATE, &playing);
|
||||
if(playing != AL_PLAYING)
|
||||
{
|
||||
alSourcePlay(mWindSource);
|
||||
|
||||
lldebugs << "Wind had stopped - probably ran out of buffers - restarting: " << (unprocessed+mNumEmptyWindALBuffers) << " now queued." << llendl;
|
||||
}
|
||||
}
|
||||
|
||||
114
indra/llaudio/llaudioengine_openal.h
Normal file
114
indra/llaudio/llaudioengine_openal.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @file audioengine_openal.cpp
|
||||
* @brief implementation of audio engine using OpenAL
|
||||
* support as a OpenAL 3D implementation
|
||||
*
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LL_AUDIOENGINE_OPENAL_H
|
||||
#define LL_AUDIOENGINE_OPENAL_H
|
||||
|
||||
#include "llaudioengine.h"
|
||||
#include "lllistener_openal.h"
|
||||
#include "llwindgen.h"
|
||||
|
||||
class LLAudioEngine_OpenAL : public LLAudioEngine
|
||||
{
|
||||
public:
|
||||
LLAudioEngine_OpenAL();
|
||||
virtual ~LLAudioEngine_OpenAL();
|
||||
|
||||
virtual bool init(const S32 num_channels, void *user_data);
|
||||
virtual std::string getDriverName(bool verbose);
|
||||
virtual void allocateListener();
|
||||
|
||||
virtual void shutdown();
|
||||
|
||||
void setInternalGain(F32 gain);
|
||||
|
||||
LLAudioBuffer* createBuffer();
|
||||
LLAudioChannel* createChannel();
|
||||
|
||||
/*virtual*/ bool initWind();
|
||||
/*virtual*/ void cleanupWind();
|
||||
/*virtual*/ void updateWind(LLVector3 direction, F32 camera_altitude);
|
||||
|
||||
private:
|
||||
void * windDSP(void *newbuffer, int length);
|
||||
typedef S16 WIND_SAMPLE_T;
|
||||
LLWindGen<WIND_SAMPLE_T> *mWindGen;
|
||||
S16 *mWindBuf;
|
||||
U32 mWindBufFreq;
|
||||
U32 mWindBufSamples;
|
||||
U32 mWindBufBytes;
|
||||
ALuint mWindSource;
|
||||
int mNumEmptyWindALBuffers;
|
||||
|
||||
static const int MAX_NUM_WIND_BUFFERS = 80;
|
||||
static const float WIND_BUFFER_SIZE_SEC = 0.05f; // 1/20th sec
|
||||
};
|
||||
|
||||
class LLAudioChannelOpenAL : public LLAudioChannel
|
||||
{
|
||||
public:
|
||||
LLAudioChannelOpenAL();
|
||||
virtual ~LLAudioChannelOpenAL();
|
||||
protected:
|
||||
/*virtual*/ void play();
|
||||
/*virtual*/ void playSynced(LLAudioChannel *channelp);
|
||||
/*virtual*/ void cleanup();
|
||||
/*virtual*/ bool isPlaying();
|
||||
|
||||
/*virtual*/ bool updateBuffer();
|
||||
/*virtual*/ void update3DPosition();
|
||||
/*virtual*/ void updateLoop();
|
||||
|
||||
ALuint mALSource;
|
||||
ALint mLastSamplePos;
|
||||
};
|
||||
|
||||
class LLAudioBufferOpenAL : public LLAudioBuffer{
|
||||
public:
|
||||
LLAudioBufferOpenAL();
|
||||
virtual ~LLAudioBufferOpenAL();
|
||||
|
||||
bool loadWAV(const std::string& filename);
|
||||
U32 getLength();
|
||||
|
||||
friend class LLAudioChannelOpenAL;
|
||||
protected:
|
||||
void cleanup();
|
||||
ALuint getBuffer() {return mALBuffer;}
|
||||
|
||||
ALuint mALBuffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
142
indra/llaudio/lllistener.cpp
Normal file
142
indra/llaudio/lllistener.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @file listener.cpp
|
||||
* @brief Implementation of LISTENER class abstracting the audio support
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "lllistener.h"
|
||||
|
||||
#define DEFAULT_AT 0.0f,0.0f,-1.0f
|
||||
#define DEFAULT_UP 0.0f,1.0f,0.0f
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// constructor
|
||||
//-----------------------------------------------------------------------
|
||||
LLListener::LLListener()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
LLListener::~LLListener()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::init(void)
|
||||
{
|
||||
mPosition.zeroVec();
|
||||
mListenAt.setVec(DEFAULT_AT);
|
||||
mListenUp.setVec(DEFAULT_UP);
|
||||
mVelocity.zeroVec();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::translate(LLVector3 offset)
|
||||
{
|
||||
mPosition += offset;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::setPosition(LLVector3 pos)
|
||||
{
|
||||
mPosition = pos;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
LLVector3 LLListener::getPosition(void)
|
||||
{
|
||||
return(mPosition);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
LLVector3 LLListener::getAt(void)
|
||||
{
|
||||
return(mListenAt);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
LLVector3 LLListener::getUp(void)
|
||||
{
|
||||
return(mListenUp);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::setVelocity(LLVector3 vel)
|
||||
{
|
||||
mVelocity = vel;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::orient(LLVector3 up, LLVector3 at)
|
||||
{
|
||||
mListenUp = up;
|
||||
mListenAt = at;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::set(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at)
|
||||
{
|
||||
mPosition = pos;
|
||||
mVelocity = vel;
|
||||
|
||||
setPosition(pos);
|
||||
setVelocity(vel);
|
||||
orient(up,at);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::setDopplerFactor(F32 factor)
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
F32 LLListener::getDopplerFactor()
|
||||
{
|
||||
return (1.f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::setRolloffFactor(F32 factor)
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
F32 LLListener::getRolloffFactor()
|
||||
{
|
||||
return (1.f);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener::commitDeferredChanges()
|
||||
{
|
||||
}
|
||||
|
||||
78
indra/llaudio/lllistener.h
Normal file
78
indra/llaudio/lllistener.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @file listener.h
|
||||
* @brief Description of LISTENER base class abstracting the audio support.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LISTENER_H
|
||||
#define LL_LISTENER_H
|
||||
|
||||
#include "v3math.h"
|
||||
|
||||
class LLListener
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
LLVector3 mPosition;
|
||||
LLVector3 mVelocity;
|
||||
LLVector3 mListenAt;
|
||||
LLVector3 mListenUp;
|
||||
|
||||
public:
|
||||
|
||||
private:
|
||||
protected:
|
||||
public:
|
||||
LLListener();
|
||||
virtual ~LLListener();
|
||||
virtual void init();
|
||||
|
||||
virtual void set(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at);
|
||||
|
||||
virtual void setPosition(LLVector3 pos);
|
||||
virtual void setVelocity(LLVector3 vel);
|
||||
|
||||
virtual void orient(LLVector3 up, LLVector3 at);
|
||||
virtual void translate(LLVector3 offset);
|
||||
|
||||
virtual void setDopplerFactor(F32 factor);
|
||||
virtual void setRolloffFactor(F32 factor);
|
||||
|
||||
virtual LLVector3 getPosition();
|
||||
virtual LLVector3 getAt();
|
||||
virtual LLVector3 getUp();
|
||||
|
||||
virtual F32 getDopplerFactor();
|
||||
virtual F32 getRolloffFactor();
|
||||
|
||||
virtual void commitDeferredChanges();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
74
indra/llaudio/lllistener_ds3d.h
Normal file
74
indra/llaudio/lllistener_ds3d.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @file listener_ds3d.h
|
||||
* @brief Description of LISTENER class abstracting the audio support
|
||||
* as a DirectSound 3D implementation (windows only)
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LISTENER_DS3D_H
|
||||
#define LL_LISTENER_DS3D_H
|
||||
|
||||
#include "lllistener.h"
|
||||
|
||||
#include <dmusici.h>
|
||||
#include <dsound.h>
|
||||
#include <ks.h>
|
||||
|
||||
class LLListener_DS3D : public LLListener
|
||||
{
|
||||
private:
|
||||
protected:
|
||||
IDirectSound3DListener8 *m3DListener;
|
||||
public:
|
||||
|
||||
private:
|
||||
protected:
|
||||
public:
|
||||
LLListener_DS3D();
|
||||
virtual ~LLListener_DS3D();
|
||||
virtual void init();
|
||||
|
||||
virtual void setDS3DLPtr (IDirectSound3DListener8 *listener_p);
|
||||
|
||||
virtual void translate(LLVector3 offset);
|
||||
virtual void setPosition(LLVector3 pos);
|
||||
virtual void setVelocity(LLVector3 vel);
|
||||
virtual void orient(LLVector3 up, LLVector3 at);
|
||||
|
||||
virtual void setDopplerFactor(F32 factor);
|
||||
virtual F32 getDopplerFactor();
|
||||
virtual void setRolloffFactor(F32 factor);
|
||||
virtual F32 getRolloffFactor();
|
||||
|
||||
virtual void commitDeferredChanges();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
131
indra/llaudio/lllistener_fmod.cpp
Normal file
131
indra/llaudio/lllistener_fmod.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* @file listener_fmod.cpp
|
||||
* @brief implementation of LISTENER class abstracting the audio
|
||||
* support as a FMOD 3D implementation (windows only)
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llaudioengine.h"
|
||||
#include "lllistener_fmod.h"
|
||||
#include "fmod.h"
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// constructor
|
||||
//-----------------------------------------------------------------------
|
||||
LLListener_FMOD::LLListener_FMOD()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
LLListener_FMOD::~LLListener_FMOD()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener_FMOD::init(void)
|
||||
{
|
||||
// do inherited
|
||||
LLListener::init();
|
||||
mDopplerFactor = 1.0f;
|
||||
mRolloffFactor = 1.0f;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener_FMOD::translate(LLVector3 offset)
|
||||
{
|
||||
LLListener::translate(offset);
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener_FMOD::setPosition(LLVector3 pos)
|
||||
{
|
||||
LLListener::setPosition(pos);
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener_FMOD::setVelocity(LLVector3 vel)
|
||||
{
|
||||
LLListener::setVelocity(vel);
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener_FMOD::orient(LLVector3 up, LLVector3 at)
|
||||
{
|
||||
LLListener::orient(up, at);
|
||||
|
||||
// Welcome to the transition between right and left
|
||||
// (coordinate systems, that is)
|
||||
// Leaving the at vector alone results in a L/R reversal
|
||||
// since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed
|
||||
at = -at;
|
||||
|
||||
FSOUND_3D_Listener_SetAttributes(NULL, NULL, at.mV[0],at.mV[1],at.mV[2], up.mV[0],up.mV[1],up.mV[2]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
void LLListener_FMOD::commitDeferredChanges()
|
||||
{
|
||||
FSOUND_Update();
|
||||
}
|
||||
|
||||
|
||||
void LLListener_FMOD::setRolloffFactor(F32 factor)
|
||||
{
|
||||
mRolloffFactor = factor;
|
||||
FSOUND_3D_SetRolloffFactor(factor);
|
||||
}
|
||||
|
||||
|
||||
F32 LLListener_FMOD::getRolloffFactor()
|
||||
{
|
||||
return mRolloffFactor;
|
||||
}
|
||||
|
||||
|
||||
void LLListener_FMOD::setDopplerFactor(F32 factor)
|
||||
{
|
||||
mDopplerFactor = factor;
|
||||
FSOUND_3D_SetDopplerFactor(factor);
|
||||
}
|
||||
|
||||
|
||||
F32 LLListener_FMOD::getDopplerFactor()
|
||||
{
|
||||
return mDopplerFactor;
|
||||
}
|
||||
|
||||
|
||||
64
indra/llaudio/lllistener_fmod.h
Normal file
64
indra/llaudio/lllistener_fmod.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @file listener_fmod.h
|
||||
* @brief Description of LISTENER class abstracting the audio support
|
||||
* as an FMOD 3D implementation (windows and Linux)
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LISTENER_FMOD_H
|
||||
#define LL_LISTENER_FMOD_H
|
||||
|
||||
#include "lllistener.h"
|
||||
|
||||
class LLListener_FMOD : public LLListener
|
||||
{
|
||||
public:
|
||||
LLListener_FMOD();
|
||||
virtual ~LLListener_FMOD();
|
||||
virtual void init();
|
||||
|
||||
virtual void translate(LLVector3 offset);
|
||||
virtual void setPosition(LLVector3 pos);
|
||||
virtual void setVelocity(LLVector3 vel);
|
||||
virtual void orient(LLVector3 up, LLVector3 at);
|
||||
virtual void commitDeferredChanges();
|
||||
|
||||
virtual void setDopplerFactor(F32 factor);
|
||||
virtual F32 getDopplerFactor();
|
||||
virtual void setRolloffFactor(F32 factor);
|
||||
virtual F32 getRolloffFactor();
|
||||
|
||||
protected:
|
||||
F32 mDopplerFactor;
|
||||
F32 mRolloffFactor;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
116
indra/llaudio/lllistener_openal.cpp
Normal file
116
indra/llaudio/lllistener_openal.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @file audioengine_openal.cpp
|
||||
* @brief implementation of audio engine using OpenAL
|
||||
* support as a OpenAL 3D implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llaudioengine.h"
|
||||
|
||||
#include "lllistener_openal.h"
|
||||
|
||||
LLListener_OpenAL::LLListener_OpenAL()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
LLListener_OpenAL::~LLListener_OpenAL()
|
||||
{
|
||||
}
|
||||
|
||||
void LLListener_OpenAL::translate(LLVector3 offset)
|
||||
{
|
||||
//llinfos << "LLListener_OpenAL::translate() : " << offset << llendl;
|
||||
LLListener::translate(offset);
|
||||
}
|
||||
|
||||
void LLListener_OpenAL::setPosition(LLVector3 pos)
|
||||
{
|
||||
//llinfos << "LLListener_OpenAL::setPosition() : " << pos << llendl;
|
||||
LLListener::setPosition(pos);
|
||||
}
|
||||
|
||||
void LLListener_OpenAL::setVelocity(LLVector3 vel)
|
||||
{
|
||||
LLListener::setVelocity(vel);
|
||||
}
|
||||
|
||||
void LLListener_OpenAL::orient(LLVector3 up, LLVector3 at)
|
||||
{
|
||||
//llinfos << "LLListener_OpenAL::orient() up: " << up << " at: " << at << llendl;
|
||||
LLListener::orient(up, at);
|
||||
}
|
||||
|
||||
void LLListener_OpenAL::commitDeferredChanges()
|
||||
{
|
||||
ALfloat orientation[6];
|
||||
orientation[0] = mListenAt.mV[0];
|
||||
orientation[1] = mListenAt.mV[1];
|
||||
orientation[2] = mListenAt.mV[2];
|
||||
orientation[3] = mListenUp.mV[0];
|
||||
orientation[4] = mListenUp.mV[1];
|
||||
orientation[5] = mListenUp.mV[2];
|
||||
|
||||
ALfloat velocity[3];
|
||||
velocity[0] = mVelocity.mV[0];
|
||||
velocity[1] = mVelocity.mV[1];
|
||||
velocity[2] = mVelocity.mV[2];
|
||||
|
||||
alListenerfv(AL_ORIENTATION, orientation);
|
||||
alListenerfv(AL_POSITION, mPosition.mV);
|
||||
alListenerfv(AL_VELOCITY, velocity);
|
||||
}
|
||||
|
||||
void LLListener_OpenAL::setDopplerFactor(F32 factor)
|
||||
{
|
||||
//llinfos << "LLListener_OpenAL::setDopplerFactor() : " << factor << llendl;
|
||||
alDopplerFactor(factor);
|
||||
}
|
||||
|
||||
F32 LLListener_OpenAL::getDopplerFactor()
|
||||
{
|
||||
ALfloat factor;
|
||||
factor = alGetFloat(AL_DOPPLER_FACTOR);
|
||||
//llinfos << "LLListener_OpenAL::getDopplerFactor() : " << factor << llendl;
|
||||
return factor;
|
||||
}
|
||||
|
||||
|
||||
void LLListener_OpenAL::setRolloffFactor(F32 factor)
|
||||
{
|
||||
mRolloffFactor = factor;
|
||||
}
|
||||
|
||||
F32 LLListener_OpenAL::getRolloffFactor()
|
||||
{
|
||||
return mRolloffFactor;
|
||||
}
|
||||
|
||||
|
||||
64
indra/llaudio/lllistener_openal.h
Normal file
64
indra/llaudio/lllistener_openal.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @file listener_openal.h
|
||||
* @brief Description of LISTENER class abstracting the audio support
|
||||
* as an OpenAL implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LISTENER_OPENAL_H
|
||||
#define LL_LISTENER_OPENAL_H
|
||||
|
||||
#include "lllistener.h"
|
||||
|
||||
#include "AL/al.h"
|
||||
#include "AL/alut.h"
|
||||
|
||||
class LLListener_OpenAL : public LLListener
|
||||
{
|
||||
public:
|
||||
LLListener_OpenAL();
|
||||
virtual ~LLListener_OpenAL();
|
||||
|
||||
virtual void translate(LLVector3 offset);
|
||||
virtual void setPosition(LLVector3 pos);
|
||||
virtual void setVelocity(LLVector3 vel);
|
||||
virtual void orient(LLVector3 up, LLVector3 at);
|
||||
virtual void commitDeferredChanges();
|
||||
|
||||
virtual void setDopplerFactor(F32 factor);
|
||||
virtual F32 getDopplerFactor();
|
||||
virtual void setRolloffFactor(F32 factor);
|
||||
virtual F32 getRolloffFactor();
|
||||
|
||||
protected:
|
||||
F32 mRolloffFactor;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
56
indra/llaudio/llstreamingaudio.h
Normal file
56
indra/llaudio/llstreamingaudio.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @file streamingaudio.h
|
||||
* @author Tofu Linden
|
||||
* @brief Definition of LLStreamingAudioInterface base class abstracting the streaming audio interface
|
||||
*
|
||||
* $LicenseInfo:firstyear=2009&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_STREAMINGAUDIO_H
|
||||
#define LL_STREAMINGAUDIO_H
|
||||
|
||||
#include "stdtypes.h" // from llcommon
|
||||
|
||||
// Entirely abstract. Based exactly on the historic API.
|
||||
class LLStreamingAudioInterface
|
||||
{
|
||||
public:
|
||||
virtual ~LLStreamingAudioInterface() {}
|
||||
|
||||
virtual void start(const std::string& url) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void pause(int pause) = 0;
|
||||
virtual void update() = 0;
|
||||
virtual int isPlaying() = 0;
|
||||
// use a value from 0.0 to 1.0, inclusive
|
||||
virtual void setGain(F32 vol) = 0;
|
||||
virtual F32 getGain() = 0;
|
||||
virtual std::string getURL() = 0;
|
||||
};
|
||||
|
||||
#endif // LL_STREAMINGAUDIO_H
|
||||
362
indra/llaudio/llstreamingaudio_fmod.cpp
Normal file
362
indra/llaudio/llstreamingaudio_fmod.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
/**
|
||||
* @file streamingaudio_fmod.cpp
|
||||
* @brief LLStreamingAudio_FMOD implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2009&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llmath.h"
|
||||
|
||||
#include "fmod.h"
|
||||
#include "fmod_errors.h"
|
||||
|
||||
#include "llstreamingaudio_fmod.h"
|
||||
|
||||
|
||||
class LLAudioStreamManagerFMOD
|
||||
{
|
||||
public:
|
||||
LLAudioStreamManagerFMOD(const std::string& url);
|
||||
int startStream();
|
||||
bool stopStream(); // Returns true if the stream was successfully stopped.
|
||||
bool ready();
|
||||
|
||||
const std::string& getURL() { return mInternetStreamURL; }
|
||||
|
||||
int getOpenState();
|
||||
protected:
|
||||
FSOUND_STREAM* mInternetStream;
|
||||
bool mReady;
|
||||
|
||||
std::string mInternetStreamURL;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Internet Streaming
|
||||
//---------------------------------------------------------------------------
|
||||
LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() :
|
||||
mCurrentInternetStreamp(NULL),
|
||||
mFMODInternetStreamChannel(-1),
|
||||
mGain(1.0f)
|
||||
{
|
||||
// Number of milliseconds of audio to buffer for the audio card.
|
||||
// Must be larger than the usual Second Life frame stutter time.
|
||||
FSOUND_Stream_SetBufferSize(200);
|
||||
|
||||
// Here's where we set the size of the network buffer and some buffering
|
||||
// parameters. In this case we want a network buffer of 16k, we want it
|
||||
// to prebuffer 40% of that when we first connect, and we want it
|
||||
// to rebuffer 80% of that whenever we encounter a buffer underrun.
|
||||
|
||||
// Leave the net buffer properties at the default.
|
||||
//FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80);
|
||||
}
|
||||
|
||||
|
||||
LLStreamingAudio_FMOD::~LLStreamingAudio_FMOD()
|
||||
{
|
||||
// nothing interesting/safe to do.
|
||||
}
|
||||
|
||||
|
||||
void LLStreamingAudio_FMOD::start(const std::string& url)
|
||||
{
|
||||
//if (!mInited)
|
||||
//{
|
||||
// llwarns << "startInternetStream before audio initialized" << llendl;
|
||||
// return;
|
||||
//}
|
||||
|
||||
// "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
|
||||
stop();
|
||||
|
||||
if (!url.empty())
|
||||
{
|
||||
llinfos << "Starting internet stream: " << url << llendl;
|
||||
mCurrentInternetStreamp = new LLAudioStreamManagerFMOD(url);
|
||||
mURL = url;
|
||||
}
|
||||
else
|
||||
{
|
||||
llinfos << "Set internet stream to null" << llendl;
|
||||
mURL.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLStreamingAudio_FMOD::update()
|
||||
{
|
||||
// Kill dead internet streams, if possible
|
||||
std::list<LLAudioStreamManagerFMOD *>::iterator iter;
|
||||
for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
|
||||
{
|
||||
LLAudioStreamManagerFMOD *streamp = *iter;
|
||||
if (streamp->stopStream())
|
||||
{
|
||||
llinfos << "Closed dead stream" << llendl;
|
||||
delete streamp;
|
||||
mDeadStreams.erase(iter++);
|
||||
}
|
||||
else
|
||||
{
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't do anything if there are no streams playing
|
||||
if (!mCurrentInternetStreamp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int open_state = mCurrentInternetStreamp->getOpenState();
|
||||
|
||||
if (!open_state)
|
||||
{
|
||||
// Stream is live
|
||||
|
||||
// start the stream if it's ready
|
||||
if (mFMODInternetStreamChannel < 0)
|
||||
{
|
||||
mFMODInternetStreamChannel = mCurrentInternetStreamp->startStream();
|
||||
|
||||
if (mFMODInternetStreamChannel != -1)
|
||||
{
|
||||
// Reset volume to previously set volume
|
||||
setGain(getGain());
|
||||
FSOUND_SetPaused(mFMODInternetStreamChannel, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(open_state)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
// success
|
||||
break;
|
||||
case -1:
|
||||
// stream handle is invalid
|
||||
llwarns << "InternetStream - invalid handle" << llendl;
|
||||
stop();
|
||||
return;
|
||||
case -2:
|
||||
// opening
|
||||
break;
|
||||
case -3:
|
||||
// failed to open, file not found, perhaps
|
||||
llwarns << "InternetSteam - failed to open" << llendl;
|
||||
stop();
|
||||
return;
|
||||
case -4:
|
||||
// connecting
|
||||
break;
|
||||
case -5:
|
||||
// buffering
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LLStreamingAudio_FMOD::stop()
|
||||
{
|
||||
if (mFMODInternetStreamChannel != -1)
|
||||
{
|
||||
FSOUND_SetPaused(mFMODInternetStreamChannel, true);
|
||||
FSOUND_SetPriority(mFMODInternetStreamChannel, 0);
|
||||
mFMODInternetStreamChannel = -1;
|
||||
}
|
||||
|
||||
if (mCurrentInternetStreamp)
|
||||
{
|
||||
llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl;
|
||||
if (mCurrentInternetStreamp->stopStream())
|
||||
{
|
||||
delete mCurrentInternetStreamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl;
|
||||
mDeadStreams.push_back(mCurrentInternetStreamp);
|
||||
}
|
||||
mCurrentInternetStreamp = NULL;
|
||||
//mURL.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void LLStreamingAudio_FMOD::pause(int pauseopt)
|
||||
{
|
||||
if (pauseopt < 0)
|
||||
{
|
||||
pauseopt = mCurrentInternetStreamp ? 1 : 0;
|
||||
}
|
||||
|
||||
if (pauseopt)
|
||||
{
|
||||
if (mCurrentInternetStreamp)
|
||||
{
|
||||
stop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
start(getURL());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// A stream is "playing" if it has been requested to start. That
|
||||
// doesn't necessarily mean audio is coming out of the speakers.
|
||||
int LLStreamingAudio_FMOD::isPlaying()
|
||||
{
|
||||
if (mCurrentInternetStreamp)
|
||||
{
|
||||
return 1; // Active and playing
|
||||
}
|
||||
else if (!mURL.empty())
|
||||
{
|
||||
return 2; // "Paused"
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
F32 LLStreamingAudio_FMOD::getGain()
|
||||
{
|
||||
return mGain;
|
||||
}
|
||||
|
||||
|
||||
std::string LLStreamingAudio_FMOD::getURL()
|
||||
{
|
||||
return mURL;
|
||||
}
|
||||
|
||||
|
||||
void LLStreamingAudio_FMOD::setGain(F32 vol)
|
||||
{
|
||||
mGain = vol;
|
||||
|
||||
if (mFMODInternetStreamChannel != -1)
|
||||
{
|
||||
vol = llclamp(vol, 0.f, 1.f);
|
||||
int vol_int = llround(vol * 255.f);
|
||||
FSOUND_SetVolumeAbsolute(mFMODInternetStreamChannel, vol_int);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// manager of possibly-multiple internet audio streams
|
||||
|
||||
LLAudioStreamManagerFMOD::LLAudioStreamManagerFMOD(const std::string& url) :
|
||||
mInternetStream(NULL),
|
||||
mReady(false)
|
||||
{
|
||||
mInternetStreamURL = url;
|
||||
mInternetStream = FSOUND_Stream_Open(url.c_str(), FSOUND_NORMAL | FSOUND_NONBLOCKING, 0, 0);
|
||||
if (!mInternetStream)
|
||||
{
|
||||
llwarns << "Couldn't open fmod stream, error "
|
||||
<< FMOD_ErrorString(FSOUND_GetError())
|
||||
<< llendl;
|
||||
mReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mReady = true;
|
||||
}
|
||||
|
||||
int LLAudioStreamManagerFMOD::startStream()
|
||||
{
|
||||
// We need a live and opened stream before we try and play it.
|
||||
if (!mInternetStream || getOpenState())
|
||||
{
|
||||
llwarns << "No internet stream to start playing!" << llendl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Make sure the stream is set to 2D mode.
|
||||
FSOUND_Stream_SetMode(mInternetStream, FSOUND_2D);
|
||||
|
||||
return FSOUND_Stream_PlayEx(FSOUND_FREE, mInternetStream, NULL, true);
|
||||
}
|
||||
|
||||
bool LLAudioStreamManagerFMOD::stopStream()
|
||||
{
|
||||
if (mInternetStream)
|
||||
{
|
||||
int read_percent = 0;
|
||||
int status = 0;
|
||||
int bitrate = 0;
|
||||
unsigned int flags = 0x0;
|
||||
FSOUND_Stream_Net_GetStatus(mInternetStream, &status, &read_percent, &bitrate, &flags);
|
||||
|
||||
bool close = true;
|
||||
switch (status)
|
||||
{
|
||||
case FSOUND_STREAM_NET_CONNECTING:
|
||||
close = false;
|
||||
break;
|
||||
case FSOUND_STREAM_NET_NOTCONNECTED:
|
||||
case FSOUND_STREAM_NET_BUFFERING:
|
||||
case FSOUND_STREAM_NET_READY:
|
||||
case FSOUND_STREAM_NET_ERROR:
|
||||
default:
|
||||
close = true;
|
||||
}
|
||||
|
||||
if (close)
|
||||
{
|
||||
FSOUND_Stream_Close(mInternetStream);
|
||||
mInternetStream = NULL;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int LLAudioStreamManagerFMOD::getOpenState()
|
||||
{
|
||||
int open_state = FSOUND_Stream_GetOpenState(mInternetStream);
|
||||
return open_state;
|
||||
}
|
||||
68
indra/llaudio/llstreamingaudio_fmod.h
Normal file
68
indra/llaudio/llstreamingaudio_fmod.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @file streamingaudio_fmod.h
|
||||
* @author Tofu Linden
|
||||
* @brief Definition of LLStreamingAudio_FMOD implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2009&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_STREAMINGAUDIO_FMOD_H
|
||||
#define LL_STREAMINGAUDIO_FMOD_H
|
||||
|
||||
#include "stdtypes.h" // from llcommon
|
||||
|
||||
#include "llstreamingaudio.h"
|
||||
|
||||
class LLAudioStreamManagerFMOD;
|
||||
|
||||
class LLStreamingAudio_FMOD : public LLStreamingAudioInterface
|
||||
{
|
||||
public:
|
||||
LLStreamingAudio_FMOD();
|
||||
/*virtual*/ ~LLStreamingAudio_FMOD();
|
||||
|
||||
/*virtual*/ void start(const std::string& url);
|
||||
/*virtual*/ void stop();
|
||||
/*virtual*/ void pause(int pause);
|
||||
/*virtual*/ void update();
|
||||
/*virtual*/ int isPlaying();
|
||||
/*virtual*/ void setGain(F32 vol);
|
||||
/*virtual*/ F32 getGain();
|
||||
/*virtual*/ std::string getURL();
|
||||
|
||||
private:
|
||||
LLAudioStreamManagerFMOD *mCurrentInternetStreamp;
|
||||
int mFMODInternetStreamChannel;
|
||||
std::list<LLAudioStreamManagerFMOD *> mDeadStreams;
|
||||
|
||||
std::string mURL;
|
||||
F32 mGain;
|
||||
};
|
||||
|
||||
|
||||
#endif // LL_STREAMINGAUDIO_FMOD_H
|
||||
326
indra/llaudio/llvorbisdecode.cpp
Normal file
326
indra/llaudio/llvorbisdecode.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
/**
|
||||
* @file vorbisdecode.cpp
|
||||
* @brief Vorbis decoding routine routine for Indra.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "vorbis/codec.h"
|
||||
#include "vorbis/vorbisfile.h"
|
||||
#include "llerror.h"
|
||||
#include "llmath.h"
|
||||
#include "llvfile.h"
|
||||
|
||||
#if 0
|
||||
|
||||
size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource)
|
||||
{
|
||||
LLVFile *file = (LLVFile *)datasource;
|
||||
|
||||
if (size > 0 && file->read((U8*)ptr, 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();
|
||||
}
|
||||
|
||||
|
||||
BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname)
|
||||
{
|
||||
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;
|
||||
|
||||
char pcmout[4096]; /*Flawfinder: ignore*/
|
||||
|
||||
unsigned char temp[64]; /*Flawfinder: ignore*/
|
||||
|
||||
LLVFile *in_vfile;
|
||||
|
||||
U32 data_length = 0;
|
||||
|
||||
llinfos << "Vorbis decode from vfile: " << in_uuid << llendl;
|
||||
|
||||
in_vfile = new LLVFile(vfs, in_uuid, LLAssetType::AT_SOUND);
|
||||
if (! in_vfile->getSize())
|
||||
{
|
||||
llwarning("unable to open vorbis source vfile for reading",0);
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
//**********************************
|
||||
LLAPRFile outfile ;
|
||||
outfile.open(out_fname,LL_APR_WPB);
|
||||
//**********************************
|
||||
if (!outfile.getFileHandle())
|
||||
{
|
||||
llwarning("unable to open vorbis destination file for writing",0);
|
||||
return(FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
// write the .wav format header
|
||||
//"RIFF"
|
||||
temp[0] = 0x52;
|
||||
temp[1] = 0x49;
|
||||
temp[2] = 0x46;
|
||||
temp[3] = 0x46;
|
||||
|
||||
// length = datalen + 36 (to be filled in later)
|
||||
temp[4] = 0x00;
|
||||
temp[5] = 0x00;
|
||||
temp[6] = 0x00;
|
||||
temp[7] = 0x00;
|
||||
|
||||
//"WAVE"
|
||||
temp[8] = 0x57;
|
||||
temp[9] = 0x41;
|
||||
temp[10] = 0x56;
|
||||
temp[11] = 0x45;
|
||||
|
||||
// "fmt "
|
||||
temp[12] = 0x66;
|
||||
temp[13] = 0x6D;
|
||||
temp[14] = 0x74;
|
||||
temp[15] = 0x20;
|
||||
|
||||
// chunk size = 16
|
||||
temp[16] = 0x10;
|
||||
temp[17] = 0x00;
|
||||
temp[18] = 0x00;
|
||||
temp[19] = 0x00;
|
||||
|
||||
// format (1 = PCM)
|
||||
temp[20] = 0x01;
|
||||
temp[21] = 0x00;
|
||||
|
||||
// number of channels
|
||||
temp[22] = 0x01;
|
||||
temp[23] = 0x00;
|
||||
|
||||
// samples per second
|
||||
temp[24] = 0x44;
|
||||
temp[25] = 0xAC;
|
||||
temp[26] = 0x00;
|
||||
temp[27] = 0x00;
|
||||
|
||||
// average bytes per second
|
||||
temp[28] = 0x88;
|
||||
temp[29] = 0x58;
|
||||
temp[30] = 0x01;
|
||||
temp[31] = 0x00;
|
||||
|
||||
// bytes to output at a single time
|
||||
temp[32] = 0x02;
|
||||
temp[33] = 0x00;
|
||||
|
||||
// 16 bits per sample
|
||||
temp[34] = 0x10;
|
||||
temp[35] = 0x00;
|
||||
|
||||
// "data"
|
||||
temp[36] = 0x64;
|
||||
temp[37] = 0x61;
|
||||
temp[38] = 0x74;
|
||||
temp[39] = 0x61;
|
||||
|
||||
|
||||
// these are the length of the data chunk, to be filled in later
|
||||
temp[40] = 0x00;
|
||||
temp[41] = 0x00;
|
||||
temp[42] = 0x00;
|
||||
temp[43] = 0x00;
|
||||
|
||||
outfile.write(temp, 44);
|
||||
}
|
||||
|
||||
OggVorbis_File vf;
|
||||
int eof=0;
|
||||
int current_section;
|
||||
|
||||
int r = ov_open_callbacks(in_vfile, &vf, NULL, 0, vfs_callbacks);
|
||||
if(r < 0)
|
||||
{
|
||||
llwarns << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << in_uuid << llendl;
|
||||
return(FALSE);
|
||||
}
|
||||
|
||||
{
|
||||
char **ptr=ov_comment(&vf,-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);
|
||||
}
|
||||
|
||||
while(!eof){
|
||||
long ret=ov_read(&vf,pcmout,sizeof(pcmout),0,2,1,¤t_section);
|
||||
if (ret == 0) {
|
||||
/* EOF */
|
||||
eof=1;
|
||||
// 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. */
|
||||
llwarning("Error in vorbis stream",0);
|
||||
break;
|
||||
|
||||
} else {
|
||||
// llinfos << "Vorbis read " << ret << "bytes" << llendl;
|
||||
/* we don't bother dealing with sample rate changes, etc, but.
|
||||
you'll have to*/
|
||||
data_length += outfile.write(pcmout, ret);
|
||||
}
|
||||
}
|
||||
|
||||
ov_clear(&vf);
|
||||
|
||||
// write "data" chunk length
|
||||
outfile.seek(APR_SET,40);
|
||||
outfile.write(&data_length,4);
|
||||
|
||||
// write overall "RIFF" length
|
||||
data_length += 36;
|
||||
outfile.seek(APR_SET,4);
|
||||
outfile.write(&data_length,1*4);
|
||||
|
||||
// FUCK!!! Vorbis encode/decode messes up loop point transitions (pop)
|
||||
// do a cheap-and-cheesy crossfade
|
||||
|
||||
|
||||
S16 *samplep;
|
||||
S32 i;
|
||||
S32 fade_length;
|
||||
|
||||
fade_length = llmin((S32)128,(S32)(data_length-36)/8);
|
||||
|
||||
outfile.seek(APR_SET,44);
|
||||
outfile.read(pcmout,2*fade_length); //read first 16 samples
|
||||
|
||||
samplep = (S16 *)pcmout;
|
||||
|
||||
for (i = 0 ;i < fade_length; i++)
|
||||
{
|
||||
*samplep++ = ((F32)*samplep * ((F32)i/(F32)fade_length));
|
||||
}
|
||||
|
||||
outfile.seek(APR_SET,44);
|
||||
outfile.write(pcmout,2*fade_length); //write back xfaded first 16 samples
|
||||
|
||||
outfile.seek(APR_END,-fade_length*2);
|
||||
outfile.read(pcmout,2*fade_length); //read last 16 samples
|
||||
|
||||
samplep = (S16 *)pcmout;
|
||||
|
||||
for (i = fade_length-1 ; i >= 0; i--)
|
||||
{
|
||||
*samplep++ = ((F32)*samplep * ((F32)i/(F32)fade_length));
|
||||
}
|
||||
|
||||
outfile.seek(SEEK_END,-fade_length*2);
|
||||
outfile.write(pcmout,2*fade_length); //write back xfaded last 16 samples
|
||||
//*******************
|
||||
outfile.close();
|
||||
//*******************
|
||||
|
||||
if ((36 == data_length) || (!(eof)))
|
||||
{
|
||||
llwarning("BAD Vorbis DECODE!, removing .wav!",0);
|
||||
LLFile::remove(out_fname);
|
||||
return (FALSE);
|
||||
}
|
||||
|
||||
// fprintf(stderr,"Done.\n");
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
#endif
|
||||
42
indra/llaudio/llvorbisdecode.h
Normal file
42
indra/llaudio/llvorbisdecode.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file vorbisdecode.h
|
||||
* @brief Vorbis decoding routine routine for Indra.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_VORBISDECODE_H
|
||||
#define LL_VORBISDECODE_H
|
||||
|
||||
class LLVFS;
|
||||
class LLUUID;
|
||||
|
||||
BOOL decode_vorbis_file(LLVFS *vfs, const LLUUID &in_uuid, char *out_fname);
|
||||
|
||||
#endif
|
||||
|
||||
505
indra/llaudio/llvorbisencode.cpp
Normal file
505
indra/llaudio/llvorbisencode.cpp
Normal file
@@ -0,0 +1,505 @@
|
||||
/**
|
||||
* @file vorbisencode.cpp
|
||||
* @brief Vorbis encoding routine routine for Indra.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "vorbis/vorbisenc.h"
|
||||
|
||||
#include "llvorbisencode.h"
|
||||
#include "llerror.h"
|
||||
#include "llrand.h"
|
||||
#include "llmath.h"
|
||||
#include "llapr.h"
|
||||
|
||||
//#if LL_DARWIN
|
||||
// MBW -- XXX -- Getting rid of SecondLifeVorbis for now -- no fmod means no name collisions.
|
||||
#if 0
|
||||
#include "VorbisFramework.h"
|
||||
|
||||
#define vorbis_analysis mac_vorbis_analysis
|
||||
#define vorbis_analysis_headerout mac_vorbis_analysis_headerout
|
||||
#define vorbis_analysis_init mac_vorbis_analysis_init
|
||||
#define vorbis_encode_ctl mac_vorbis_encode_ctl
|
||||
#define vorbis_encode_setup_init mac_vorbis_encode_setup_init
|
||||
#define vorbis_encode_setup_managed mac_vorbis_encode_setup_managed
|
||||
|
||||
#define vorbis_info_init mac_vorbis_info_init
|
||||
#define vorbis_info_clear mac_vorbis_info_clear
|
||||
#define vorbis_comment_init mac_vorbis_comment_init
|
||||
#define vorbis_comment_clear mac_vorbis_comment_clear
|
||||
#define vorbis_block_init mac_vorbis_block_init
|
||||
#define vorbis_block_clear mac_vorbis_block_clear
|
||||
#define vorbis_dsp_clear mac_vorbis_dsp_clear
|
||||
#define vorbis_analysis_buffer mac_vorbis_analysis_buffer
|
||||
#define vorbis_analysis_wrote mac_vorbis_analysis_wrote
|
||||
#define vorbis_analysis_blockout mac_vorbis_analysis_blockout
|
||||
|
||||
#define ogg_stream_packetin mac_ogg_stream_packetin
|
||||
#define ogg_stream_init mac_ogg_stream_init
|
||||
#define ogg_stream_flush mac_ogg_stream_flush
|
||||
#define ogg_stream_pageout mac_ogg_stream_pageout
|
||||
#define ogg_page_eos mac_ogg_page_eos
|
||||
#define ogg_stream_clear mac_ogg_stream_clear
|
||||
|
||||
#endif
|
||||
|
||||
S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg)
|
||||
{
|
||||
U16 num_channels = 0;
|
||||
U32 sample_rate = 0;
|
||||
U32 bits_per_sample = 0;
|
||||
U32 physical_file_size = 0;
|
||||
U32 chunk_length = 0;
|
||||
U32 raw_data_length = 0;
|
||||
U32 bytes_per_sec = 0;
|
||||
BOOL uncompressed_pcm = FALSE;
|
||||
|
||||
unsigned char wav_header[44]; /*Flawfinder: ignore*/
|
||||
|
||||
error_msg.clear();
|
||||
|
||||
//********************************
|
||||
LLAPRFile infile ;
|
||||
infile.open(in_fname,LL_APR_RB, LLAPRFile::global);
|
||||
//********************************
|
||||
if (!infile.getFileHandle())
|
||||
{
|
||||
error_msg = "CannotUploadSoundFile";
|
||||
return(LLVORBISENC_SOURCE_OPEN_ERR);
|
||||
}
|
||||
|
||||
infile.read(wav_header, 44);
|
||||
physical_file_size = infile.seek(APR_END,0);
|
||||
|
||||
if (strncmp((char *)&(wav_header[0]),"RIFF",4))
|
||||
{
|
||||
error_msg = "SoundFileNotRIFF";
|
||||
return(LLVORBISENC_WAV_FORMAT_ERR);
|
||||
}
|
||||
|
||||
if (strncmp((char *)&(wav_header[8]),"WAVE",4))
|
||||
{
|
||||
error_msg = "SoundFileNotRIFF";
|
||||
return(LLVORBISENC_WAV_FORMAT_ERR);
|
||||
}
|
||||
|
||||
// parse the chunks
|
||||
|
||||
U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
|
||||
|
||||
while ((file_pos + 8)< physical_file_size)
|
||||
{
|
||||
infile.seek(APR_SET,file_pos);
|
||||
infile.read(wav_header, 44);
|
||||
|
||||
chunk_length = ((U32) wav_header[7] << 24)
|
||||
+ ((U32) wav_header[6] << 16)
|
||||
+ ((U32) wav_header[5] << 8)
|
||||
+ wav_header[4];
|
||||
|
||||
// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
|
||||
|
||||
if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
|
||||
{
|
||||
if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
|
||||
{
|
||||
uncompressed_pcm = TRUE;
|
||||
}
|
||||
num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
|
||||
sample_rate = ((U32) wav_header[15] << 24)
|
||||
+ ((U32) wav_header[14] << 16)
|
||||
+ ((U32) wav_header[13] << 8)
|
||||
+ wav_header[12];
|
||||
bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
|
||||
bytes_per_sec = ((U32) wav_header[19] << 24)
|
||||
+ ((U32) wav_header[18] << 16)
|
||||
+ ((U32) wav_header[17] << 8)
|
||||
+ wav_header[16];
|
||||
}
|
||||
else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
|
||||
{
|
||||
raw_data_length = chunk_length;
|
||||
}
|
||||
file_pos += (chunk_length + 8);
|
||||
chunk_length = 0;
|
||||
}
|
||||
//****************
|
||||
infile.close();
|
||||
//****************
|
||||
|
||||
if (!uncompressed_pcm)
|
||||
{
|
||||
error_msg = "SoundFileNotPCM";
|
||||
return(LLVORBISENC_PCM_FORMAT_ERR);
|
||||
}
|
||||
|
||||
if ((num_channels < 1) || (num_channels > LLVORBIS_CLIP_MAX_CHANNELS))
|
||||
{
|
||||
error_msg = "SoundFileInvalidChannelCount";
|
||||
return(LLVORBISENC_MULTICHANNEL_ERR);
|
||||
}
|
||||
|
||||
if (sample_rate != LLVORBIS_CLIP_SAMPLE_RATE)
|
||||
{
|
||||
error_msg = "SoundFileInvalidSampleRate";
|
||||
return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
if ((bits_per_sample != 16) && (bits_per_sample != 8))
|
||||
{
|
||||
error_msg = "SoundFileInvalidWordSize";
|
||||
return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
|
||||
}
|
||||
|
||||
if (!raw_data_length)
|
||||
{
|
||||
error_msg = "SoundFileInvalidHeader";
|
||||
return(LLVORBISENC_CLIP_TOO_LONG);
|
||||
}
|
||||
|
||||
F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
|
||||
|
||||
if (clip_length > LLVORBIS_CLIP_MAX_TIME)
|
||||
{
|
||||
error_msg = "SoundFileInvalidTooLong";
|
||||
return(LLVORBISENC_CLIP_TOO_LONG);
|
||||
}
|
||||
|
||||
return(LLVORBISENC_NOERR);
|
||||
}
|
||||
|
||||
S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname)
|
||||
{
|
||||
#define READ_BUFFER 1024
|
||||
unsigned char readbuffer[READ_BUFFER*4+44]; /* out of the data segment, not the stack */ /*Flawfinder: ignore*/
|
||||
|
||||
ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
|
||||
ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
|
||||
ogg_packet op; /* one raw packet of data for decode */
|
||||
|
||||
vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
|
||||
vorbis_comment vc; /* struct that stores all the user comments */
|
||||
|
||||
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
|
||||
vorbis_block vb; /* local working space for packet->PCM decode */
|
||||
|
||||
int eos=0;
|
||||
int result;
|
||||
|
||||
U16 num_channels = 0;
|
||||
U32 sample_rate = 0;
|
||||
U32 bits_per_sample = 0;
|
||||
|
||||
S32 format_error = 0;
|
||||
std::string error_msg;
|
||||
if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
|
||||
{
|
||||
llwarns << error_msg << ": " << in_fname << llendl;
|
||||
return(format_error);
|
||||
}
|
||||
|
||||
#if 1
|
||||
unsigned char wav_header[44]; /*Flawfinder: ignore*/
|
||||
|
||||
S32 data_left = 0;
|
||||
|
||||
LLAPRFile infile ;
|
||||
infile.open(in_fname,LL_APR_RB, LLAPRFile::global);
|
||||
if (!infile.getFileHandle())
|
||||
{
|
||||
llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
|
||||
<< llendl;
|
||||
return(LLVORBISENC_SOURCE_OPEN_ERR);
|
||||
}
|
||||
|
||||
LLAPRFile outfile ;
|
||||
outfile.open(out_fname,LL_APR_WPB, LLAPRFile::global);
|
||||
if (!outfile.getFileHandle())
|
||||
{
|
||||
llwarns << "Couldn't open upload sound file for reading: " << in_fname
|
||||
<< llendl;
|
||||
return(LLVORBISENC_DEST_OPEN_ERR);
|
||||
}
|
||||
|
||||
// parse the chunks
|
||||
U32 chunk_length = 0;
|
||||
U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
|
||||
|
||||
while (infile.eof() != APR_EOF)
|
||||
{
|
||||
infile.seek(APR_SET,file_pos);
|
||||
infile.read(wav_header, 44);
|
||||
|
||||
chunk_length = ((U32) wav_header[7] << 24)
|
||||
+ ((U32) wav_header[6] << 16)
|
||||
+ ((U32) wav_header[5] << 8)
|
||||
+ wav_header[4];
|
||||
|
||||
// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl;
|
||||
|
||||
if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
|
||||
{
|
||||
num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
|
||||
sample_rate = ((U32) wav_header[15] << 24)
|
||||
+ ((U32) wav_header[14] << 16)
|
||||
+ ((U32) wav_header[13] << 8)
|
||||
+ wav_header[12];
|
||||
bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
|
||||
}
|
||||
else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
|
||||
{
|
||||
infile.seek(APR_SET,file_pos+8);
|
||||
// leave the file pointer at the beginning of the data chunk data
|
||||
data_left = chunk_length;
|
||||
break;
|
||||
}
|
||||
file_pos += (chunk_length + 8);
|
||||
chunk_length = 0;
|
||||
}
|
||||
|
||||
|
||||
/********** Encode setup ************/
|
||||
|
||||
/* choose an encoding mode */
|
||||
/* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
|
||||
vorbis_info_init(&vi);
|
||||
|
||||
// always encode to mono
|
||||
|
||||
// SL-52913 & SL-53779 determined this quality level to be our 'good
|
||||
// enough' general-purpose quality level with a nice low bitrate.
|
||||
// Equivalent to oggenc -q0.5
|
||||
F32 quality = 0.05f;
|
||||
// quality = (bitrate==128000 ? 0.4f : 0.1);
|
||||
|
||||
// if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
|
||||
if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
|
||||
// if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
|
||||
// vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
|
||||
// vorbis_encode_setup_init(&vi))
|
||||
{
|
||||
llwarns << "unable to initialize vorbis codec at quality " << quality << llendl;
|
||||
// llwarns << "unable to initialize vorbis codec at bitrate " << bitrate << llendl;
|
||||
return(LLVORBISENC_DEST_OPEN_ERR);
|
||||
}
|
||||
|
||||
/* add a comment */
|
||||
vorbis_comment_init(&vc);
|
||||
// vorbis_comment_add(&vc,"Linden");
|
||||
|
||||
/* set up the analysis state and auxiliary encoding storage */
|
||||
vorbis_analysis_init(&vd,&vi);
|
||||
vorbis_block_init(&vd,&vb);
|
||||
|
||||
/* set up our packet->stream encoder */
|
||||
/* pick a random serial number; that way we can more likely build
|
||||
chained streams just by concatenation */
|
||||
ogg_stream_init(&os, ll_rand());
|
||||
|
||||
/* Vorbis streams begin with three headers; the initial header (with
|
||||
most of the codec setup parameters) which is mandated by the Ogg
|
||||
bitstream spec. The second header holds any comment fields. The
|
||||
third header holds the bitstream codebook. We merely need to
|
||||
make the headers, then pass them to libvorbis one at a time;
|
||||
libvorbis handles the additional Ogg bitstream constraints */
|
||||
|
||||
{
|
||||
ogg_packet header;
|
||||
ogg_packet header_comm;
|
||||
ogg_packet header_code;
|
||||
|
||||
vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
|
||||
ogg_stream_packetin(&os,&header); /* automatically placed in its own
|
||||
page */
|
||||
ogg_stream_packetin(&os,&header_comm);
|
||||
ogg_stream_packetin(&os,&header_code);
|
||||
|
||||
/* We don't have to write out here, but doing so makes streaming
|
||||
* much easier, so we do, flushing ALL pages. This ensures the actual
|
||||
* audio data will start on a new page
|
||||
*/
|
||||
while(!eos){
|
||||
int result=ogg_stream_flush(&os,&og);
|
||||
if(result==0)break;
|
||||
outfile.write(og.header, og.header_len);
|
||||
outfile.write(og.body, og.body_len);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
while(!eos)
|
||||
{
|
||||
long bytes_per_sample = bits_per_sample/8;
|
||||
|
||||
long bytes=(long)infile.read(readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
|
||||
|
||||
if (bytes==0)
|
||||
{
|
||||
/* end of file. this can be done implicitly in the mainline,
|
||||
but it's easier to see here in non-clever fashion.
|
||||
Tell the library we're at end of stream so that it can handle
|
||||
the last frame and mark end of stream in the output properly */
|
||||
|
||||
vorbis_analysis_wrote(&vd,0);
|
||||
// eos = 1;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
long i;
|
||||
long samples;
|
||||
int temp;
|
||||
|
||||
data_left -= bytes;
|
||||
/* data to encode */
|
||||
|
||||
/* expose the buffer to submit data */
|
||||
float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
|
||||
|
||||
i = 0;
|
||||
samples = bytes / (num_channels * bytes_per_sample);
|
||||
|
||||
if (num_channels == 2)
|
||||
{
|
||||
if (bytes_per_sample == 2)
|
||||
{
|
||||
/* uninterleave samples */
|
||||
for(i=0; i<samples ;i++)
|
||||
{
|
||||
temp = ((signed char *)readbuffer)[i*4+1]; /*Flawfinder: ignore*/
|
||||
temp += ((signed char *)readbuffer)[i*4+3]; /*Flawfinder: ignore*/
|
||||
temp <<= 8;
|
||||
temp += readbuffer[i*4];
|
||||
temp += readbuffer[i*4+2];
|
||||
|
||||
buffer[0][i] = ((float)temp) / 65536.f;
|
||||
}
|
||||
}
|
||||
else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
|
||||
{
|
||||
/* uninterleave samples */
|
||||
for(i=0; i<samples ;i++)
|
||||
{
|
||||
temp = readbuffer[i*2+0];
|
||||
temp += readbuffer[i*2+1];
|
||||
temp -= 256;
|
||||
buffer[0][i] = ((float)temp) / 256.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (num_channels == 1)
|
||||
{
|
||||
if (bytes_per_sample == 2)
|
||||
{
|
||||
for(i=0; i < samples ;i++)
|
||||
{
|
||||
temp = ((signed char*)readbuffer)[i*2+1];
|
||||
temp <<= 8;
|
||||
temp += readbuffer[i*2];
|
||||
buffer[0][i] = ((float)temp) / 32768.f;
|
||||
}
|
||||
}
|
||||
else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
|
||||
{
|
||||
for(i=0; i < samples ;i++)
|
||||
{
|
||||
temp = readbuffer[i];
|
||||
temp -= 128;
|
||||
buffer[0][i] = ((float)temp) / 128.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* tell the library how much we actually submitted */
|
||||
vorbis_analysis_wrote(&vd,i);
|
||||
}
|
||||
|
||||
/* vorbis does some data preanalysis, then divvies up blocks for
|
||||
more involved (potentially parallel) processing. Get a single
|
||||
block for encoding now */
|
||||
while(vorbis_analysis_blockout(&vd,&vb)==1)
|
||||
{
|
||||
|
||||
/* analysis */
|
||||
/* Do the main analysis, creating a packet */
|
||||
vorbis_analysis(&vb, NULL);
|
||||
vorbis_bitrate_addblock(&vb);
|
||||
|
||||
while(vorbis_bitrate_flushpacket(&vd, &op))
|
||||
{
|
||||
|
||||
/* weld the packet into the bitstream */
|
||||
ogg_stream_packetin(&os,&op);
|
||||
|
||||
/* write out pages (if any) */
|
||||
while(!eos)
|
||||
{
|
||||
result = ogg_stream_pageout(&os,&og);
|
||||
|
||||
if(result==0)
|
||||
break;
|
||||
|
||||
outfile.write(og.header, og.header_len);
|
||||
outfile.write(og.body, og.body_len);
|
||||
|
||||
/* this could be set above, but for illustrative purposes, I do
|
||||
it here (to show that vorbis does know where the stream ends) */
|
||||
|
||||
if(ogg_page_eos(&og))
|
||||
eos=1;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* clean up and exit. vorbis_info_clear() must be called last */
|
||||
|
||||
ogg_stream_clear(&os);
|
||||
vorbis_block_clear(&vb);
|
||||
vorbis_dsp_clear(&vd);
|
||||
vorbis_comment_clear(&vc);
|
||||
vorbis_info_clear(&vi);
|
||||
|
||||
/* ogg_page and ogg_packet structs always point to storage in
|
||||
libvorbis. They're never freed or manipulated directly */
|
||||
|
||||
// fprintf(stderr,"Vorbis encoding: Done.\n");
|
||||
llinfos << "Vorbis encoding: Done." << llendl;
|
||||
|
||||
#endif
|
||||
return(LLVORBISENC_NOERR);
|
||||
|
||||
}
|
||||
64
indra/llaudio/llvorbisencode.h
Normal file
64
indra/llaudio/llvorbisencode.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @file vorbisencode.h
|
||||
* @brief Vorbis encoding routine routine for Indra.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2000-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_VORBISENCODE_H
|
||||
#define LL_VORBISENCODE_H
|
||||
|
||||
const S32 LLVORBISENC_NOERR = 0; // no error
|
||||
const S32 LLVORBISENC_SOURCE_OPEN_ERR = 1; // error opening source
|
||||
const S32 LLVORBISENC_DEST_OPEN_ERR = 2; // error opening destination
|
||||
const S32 LLVORBISENC_WAV_FORMAT_ERR = 3; // not a WAV
|
||||
const S32 LLVORBISENC_PCM_FORMAT_ERR = 4; // not a PCM
|
||||
const S32 LLVORBISENC_MONO_ERR = 5; // can't do mono
|
||||
const S32 LLVORBISENC_STEREO_ERR = 6; // can't do stereo
|
||||
const S32 LLVORBISENC_MULTICHANNEL_ERR = 7; // can't do stereo
|
||||
const S32 LLVORBISENC_UNSUPPORTED_SAMPLE_RATE = 8; // unsupported sample rate
|
||||
const S32 LLVORBISENC_UNSUPPORTED_WORD_SIZE = 9; // unsupported word size
|
||||
const S32 LLVORBISENC_CLIP_TOO_LONG = 10; // source file is too long
|
||||
|
||||
const F32 LLVORBIS_CLIP_MAX_TIME = 10.0f;
|
||||
const U8 LLVORBIS_CLIP_MAX_CHANNELS = 2;
|
||||
const U32 LLVORBIS_CLIP_SAMPLE_RATE = 44100;
|
||||
const U32 LLVORBIS_CLIP_MAX_SAMPLES_PER_CHANNEL = (U32)(LLVORBIS_CLIP_MAX_TIME * LLVORBIS_CLIP_SAMPLE_RATE);
|
||||
const U32 LLVORBIS_CLIP_MAX_SAMPLES = LLVORBIS_CLIP_MAX_SAMPLES_PER_CHANNEL * LLVORBIS_CLIP_MAX_CHANNELS;
|
||||
const size_t LLVORBIS_CLIP_MAX_SAMPLE_DATA = LLVORBIS_CLIP_MAX_SAMPLES * 2; // 2 = 16-bit
|
||||
|
||||
// Treat anything this long as a bad asset. A little fudge factor at the end:
|
||||
// Make that a lot of fudge factor. We're allowing 30 sec for now - 3x legal upload
|
||||
const size_t LLVORBIS_CLIP_REJECT_SAMPLES = LLVORBIS_CLIP_MAX_SAMPLES * 3;
|
||||
const size_t LLVORBIS_CLIP_REJECT_SIZE = LLVORBIS_CLIP_MAX_SAMPLE_DATA * 3;
|
||||
|
||||
S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg);
|
||||
S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname);
|
||||
|
||||
#endif
|
||||
|
||||
181
indra/llaudio/llwindgen.h
Normal file
181
indra/llaudio/llwindgen.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* @file windgen.h
|
||||
* @brief Templated wind noise generation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2002&license=viewergpl$
|
||||
*
|
||||
* Copyright (c) 2002-2009, Linden Research, Inc.
|
||||
*
|
||||
* Second Life Viewer Source Code
|
||||
* The source code in this file ("Source Code") is provided by Linden Lab
|
||||
* to you under the terms of the GNU General Public License, version 2.0
|
||||
* ("GPL"), unless you have obtained a separate licensing agreement
|
||||
* ("Other License"), formally executed by you and Linden Lab. Terms of
|
||||
* the GPL can be found in doc/GPL-license.txt in this distribution, or
|
||||
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
|
||||
*
|
||||
* There are special exceptions to the terms and conditions of the GPL as
|
||||
* it is applied to this Source Code. View the full text of the exception
|
||||
* in the file doc/FLOSS-exception.txt in this software distribution, or
|
||||
* online at
|
||||
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
|
||||
*
|
||||
* By copying, modifying or distributing this software, you acknowledge
|
||||
* that you have read and understood your obligations described above,
|
||||
* and agree to abide by those obligations.
|
||||
*
|
||||
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
|
||||
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
|
||||
* COMPLETENESS OR PERFORMANCE.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
#ifndef WINDGEN_H
|
||||
#define WINDGEN_H
|
||||
|
||||
#include "llcommon.h"
|
||||
|
||||
template <class MIXBUFFERFORMAT_T>
|
||||
class LLWindGen
|
||||
{
|
||||
public:
|
||||
LLWindGen(const U32 sample_rate = 44100) :
|
||||
mTargetGain(0.f),
|
||||
mTargetFreq(100.f),
|
||||
mTargetPanGainR(0.5f),
|
||||
mInputSamplingRate(sample_rate),
|
||||
mSubSamples(2),
|
||||
mFilterBandWidth(50.f),
|
||||
mBuf0(0.0f),
|
||||
mBuf1(0.0f),
|
||||
mBuf2(0.0f),
|
||||
mY0(0.0f),
|
||||
mY1(0.0f),
|
||||
mCurrentGain(0.f),
|
||||
mCurrentFreq(100.f),
|
||||
mCurrentPanGainR(0.5f)
|
||||
{
|
||||
mSamplePeriod = (F32)mSubSamples / (F32)mInputSamplingRate;
|
||||
mB2 = expf(-F_TWO_PI * mFilterBandWidth * mSamplePeriod);
|
||||
}
|
||||
|
||||
const U32 getInputSamplingRate() { return mInputSamplingRate; }
|
||||
|
||||
// newbuffer = the buffer passed from the previous DSP unit.
|
||||
// numsamples = length in samples-per-channel at this mix time.
|
||||
// NOTE: generates L/R interleaved stereo
|
||||
MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples)
|
||||
{
|
||||
MIXBUFFERFORMAT_T *cursamplep = newbuffer;
|
||||
|
||||
// Filter coefficients
|
||||
F32 a0 = 0.0f, b1 = 0.0f;
|
||||
|
||||
// No need to clip at normal volumes
|
||||
bool clip = mCurrentGain > 2.0f;
|
||||
|
||||
bool interp_freq = false;
|
||||
|
||||
//if the frequency isn't changing much, we don't need to interpolate in the inner loop
|
||||
if (llabs(mTargetFreq - mCurrentFreq) < (mCurrentFreq * 0.112))
|
||||
{
|
||||
// calculate resonant filter coefficients
|
||||
mCurrentFreq = mTargetFreq;
|
||||
b1 = (-4.0f * mB2) / (1.0f + mB2) * cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod));
|
||||
a0 = (1.0f - mB2) * sqrtf(1.0f - (b1 * b1) / (4.0f * mB2));
|
||||
}
|
||||
else
|
||||
{
|
||||
interp_freq = true;
|
||||
}
|
||||
|
||||
while (numsamples)
|
||||
{
|
||||
F32 next_sample;
|
||||
|
||||
// Start with white noise
|
||||
// This expression is fragile, rearrange it and it will break!
|
||||
next_sample = (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8);
|
||||
|
||||
// Apply a pinking filter
|
||||
// Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/
|
||||
mBuf0 = mBuf0 * 0.99765f + next_sample * 0.0990460f;
|
||||
mBuf1 = mBuf1 * 0.96300f + next_sample * 0.2965164f;
|
||||
mBuf2 = mBuf2 * 0.57000f + next_sample * 1.0526913f;
|
||||
|
||||
next_sample = mBuf0 + mBuf1 + mBuf2 + next_sample * 0.1848f;
|
||||
|
||||
if (interp_freq)
|
||||
{
|
||||
// calculate and interpolate resonant filter coefficients
|
||||
mCurrentFreq = (0.999f * mCurrentFreq) + (0.001f * mTargetFreq);
|
||||
b1 = (-4.0f * mB2) / (1.0f + mB2) * cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod));
|
||||
a0 = (1.0f - mB2) * sqrtf(1.0f - (b1 * b1) / (4.0f * mB2));
|
||||
}
|
||||
|
||||
// Apply a resonant low-pass filter on the pink noise
|
||||
next_sample = a0 * next_sample - b1 * mY0 - mB2 * mY1;
|
||||
mY1 = mY0;
|
||||
mY0 = next_sample;
|
||||
|
||||
mCurrentGain = (0.999f * mCurrentGain) + (0.001f * mTargetGain);
|
||||
mCurrentPanGainR = (0.999f * mCurrentPanGainR) + (0.001f * mTargetPanGainR);
|
||||
|
||||
// For a 3dB pan law use:
|
||||
// next_sample *= mCurrentGain * ((mCurrentPanGainR*(mCurrentPanGainR-1)*1.652+1.413);
|
||||
next_sample *= mCurrentGain;
|
||||
|
||||
// delta is used to interpolate between synthesized samples
|
||||
F32 delta = (next_sample - mLastSample) / (F32)mSubSamples;
|
||||
|
||||
// Fill the audio buffer, clipping if necessary
|
||||
for (U8 i=mSubSamples; i && numsamples; --i, --numsamples)
|
||||
{
|
||||
mLastSample = mLastSample + delta;
|
||||
S32 sample_right = (S32)(mLastSample * mCurrentPanGainR);
|
||||
S32 sample_left = (S32)mLastSample - sample_right;
|
||||
|
||||
if (!clip)
|
||||
{
|
||||
*cursamplep = (MIXBUFFERFORMAT_T)sample_left;
|
||||
++cursamplep;
|
||||
*cursamplep = (MIXBUFFERFORMAT_T)sample_right;
|
||||
++cursamplep;
|
||||
}
|
||||
else
|
||||
{
|
||||
*cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_left, (S32)S16_MIN, (S32)S16_MAX);
|
||||
++cursamplep;
|
||||
*cursamplep = (MIXBUFFERFORMAT_T)llclamp(sample_right, (S32)S16_MIN, (S32)S16_MAX);
|
||||
++cursamplep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newbuffer;
|
||||
}
|
||||
|
||||
public:
|
||||
F32 mTargetGain;
|
||||
F32 mTargetFreq;
|
||||
F32 mTargetPanGainR;
|
||||
|
||||
private:
|
||||
U32 mInputSamplingRate;
|
||||
U8 mSubSamples;
|
||||
F32 mSamplePeriod;
|
||||
F32 mFilterBandWidth;
|
||||
F32 mB2;
|
||||
|
||||
F32 mBuf0;
|
||||
F32 mBuf1;
|
||||
F32 mBuf2;
|
||||
F32 mY0;
|
||||
F32 mY1;
|
||||
|
||||
F32 mCurrentGain;
|
||||
F32 mCurrentFreq;
|
||||
F32 mCurrentPanGainR;
|
||||
F32 mLastSample;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user