A stab at fmodex support on windows. To use, install 'FMOD Ex Programmers API' to its defualt program files directory, and run develop.py with -DFMODEX:BOOL=ON set. That /should/ be all it takes.

This commit is contained in:
Shyotl
2011-12-22 18:01:07 -06:00
parent 6920bee5e2
commit c73414f1a1
14 changed files with 1914 additions and 31 deletions

View File

@@ -35,7 +35,7 @@ set(cmake_SOURCE_FILES
FindNDOF.cmake
FindOpenJPEG.cmake
FindXmlRpcEpi.cmake
FMOD.cmake
FMOD.cmake
FreeType.cmake
GStreamer010Plugin.cmake
GooglePerfTools.cmake
@@ -84,6 +84,10 @@ set(cmake_SOURCE_FILES
ZLIB.cmake
)
if(FMODEX)
list(APPEND cmake_SOURCE_FILES FMODEX.cmake)
endif(FMODEX)
source_group("Shared Rules" FILES ${cmake_SOURCE_FILES})
set(master_SOURCE_FILES

View File

@@ -243,12 +243,51 @@ set(all_targets ${all_targets} ${out_targets})
set(release_src_dir "${CMAKE_SOURCE_DIR}/../libraries/i686-win32/lib/release")
set(release_files
libtcmalloc_minimal.dll
fmod.dll
libhunspell.dll
libapr-1.dll
libaprutil-1.dll
libapriconv-1.dll
)
if(FMODEX)
find_path(FMODEX_BINARY_DIR fmodex.dll
${release_src_dir}
${FMODEX_SDK_DIR}/api
${FMODEX_SDK_DIR}
)
if(FMODEX_BINARY_DIR)
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets fmodex.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ReleaseSSE2" out_targets fmodex.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets fmodex.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMODEX_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets fmodex.dll)
set(all_targets ${all_targets} ${out_targets})
endif(FMODEX_BINARY_DIR)
endif(FMODEX)
if(FMOD)
find_path(FMOD_BINARY_DIR fmod.dll
${release_src_dir}
${FMOD_SDK_DIR}/api
${FMOD_SDK_DIR}
)
if(FMOD_BINARY_DIR)
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets fmod.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ReleaseSSE2" out_targets fmod.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets fmod.dll)
set(all_targets ${all_targets} ${out_targets})
copy_if_different("${FMOD_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets fmod.dll)
set(all_targets ${all_targets} ${out_targets})
else(FMOD_BINARY_DIR)
list(APPEND release_files fmod.dll) #Required for compile. This will cause an error in copying binaries.
endif(FMOD_BINARY_DIR)
endif(FMOD)
copy_if_different(
${release_src_dir}

83
indra/cmake/FMODEX.cmake Normal file
View File

@@ -0,0 +1,83 @@
# -*- cmake -*-
include(Linking)
if(INSTALL_PROPRIETARY)
include(Prebuilt)
use_prebuilt_binary(fmodex)
endif(INSTALL_PROPRIETARY)
find_library(FMODEX_LIBRARY_RELEASE
NAMES fmodex fmodex_vc fmodexL_vc
PATHS
${ARCH_PREBUILT_DIRS_RELEASE}
)
find_library(FMODEX_LIBRARY_DEBUG
NAMES fmodex fmodex_vc fmodexL_vc
PATHS
${ARCH_PREBUILT_DIRS_DEBUG}
)
if (FMODEX_LIBRARY_RELEASE AND FMODEX_LIBRARY_DEBUG)
set(FMODEX_LIBRARY
debug ${FMODEX_LIBRARY_DEBUG}
optimized ${FMODEX_LIBRARY_RELEASE})
elseif (FMODEX_LIBRARY_RELEASE)
set(FMODEX_LIBRARY ${FMODEX_LIBRARY_RELEASE})
endif (FMODEX_LIBRARY_RELEASE AND FMODEX_LIBRARY_DEBUG)
if (NOT FMODEX_LIBRARY)
set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.")
if (FMODEX_SDK_DIR)
find_library(FMODEX_LIBRARY
fmodex fmodex_vc fmodexL_vc
PATHS
${FMODEX_SDK_DIR}/api/lib
${FMODEX_SDK_DIR}/api
${FMODEX_SDK_DIR}/lib
${FMODEX_SDK_DIR}
)
endif(FMODEX_SDK_DIR)
if(WINDOWS AND NOT FMODEX_LIBRARY)
set(FMODEX_PROG_DIR "$ENV{PROGRAMFILES}/FMOD SoundSystem/FMOD Programmers API Windows")
find_library(FMODEX_LIBRARY
fmodex_vc fmodexL_vc
PATHS
${FMODEX_PROG_DIR}/api/lib
${FMODEX_PROG_DIR}/api
${FMODEX_PROG_DIR}
)
if(FMODEX_LIBRARY)
message(STATUS "Found fmodex in ${FMODEX_PROG_DIR}")
set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR})
set(FMODEX_SDK_DIR ${FMODEX_PROG_DIR} CACHE PATH "Path to the FMOD Ex SDK." FORCE)
endif(FMODEX_LIBRARY)
endif(WINDOWS AND NOT FMODEX_LIBRARY)
endif (NOT FMODEX_LIBRARY)
find_path(FMODEX_INCLUDE_DIR fmod.h
${LIBS_PREBUILT_DIR}/include/fmodex
${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/fmodex
${FMODEX_SDK_DIR}/api/inc
${FMODEX_SDK_DIR}/inc
${FMODEX_SDK_DIR}
)
if (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
set(FMODEX ON CACHE BOOL "Use closed source FMOD Ex sound library.")
else (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
set(FMODEX_LIBRARY "")
set(FMODEX_INCLUDE_DIR "")
if (FMODEX)
message(STATUS "No support for FMOD Ex audio (need to set FMODEX_SDK_DIR?)")
endif (FMODEX)
set(FMODEX OFF CACHE BOOL "Use closed source FMOD Ex sound library.")
set(FMODEX OFF)
endif (FMODEX_LIBRARY AND FMODEX_INCLUDE_DIR)
if (FMODEX)
message(STATUS "Building with FMOD Ex audio support")
endif (FMODEX)

View File

@@ -5,16 +5,30 @@ project(llaudio)
include(00-Common)
include(Audio)
include(LLAudio)
include(FMOD)
if(FMODEX)
include(FMODEX)
if(FMODEX)
set(FMOD OFF)
endif(FMODEX)
endif(FMODEX)
if(NOT FMODEX)
include(FMOD)
endif(NOT FMODEX)
include(OPENAL)
include(LLCommon)
include(LLMath)
include(LLMessage)
include(LLVFS)
if(FMODEX)
include_directories(${FMODEX_INCLUDE_DIR})
endif(FMODEX)
if(FMOD)
include_directories(${FMOD_INCLUDE_DIR})
endif(FMOD)
include_directories(
${LLAUDIO_INCLUDE_DIRS}
${FMOD_INCLUDE_DIR}
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
@@ -46,6 +60,19 @@ set(llaudio_HEADER_FILES
llwindgen.h
)
if (FMODEX)
list(APPEND llaudio_SOURCE_FILES
llaudioengine_fmodex.cpp
lllistener_fmodex.cpp
llstreamingaudio_fmodex.cpp
)
list(APPEND llaudio_HEADER_FILES
llaudioengine_fmodex.h
lllistener_fmodex.h
llstreamingaudio_fmodex.h
)
endif (FMODEX)
if (FMOD)
list(APPEND llaudio_SOURCE_FILES
llaudioengine_fmod.cpp

View File

@@ -0,0 +1,840 @@
/**
* @file audioengine_FMODEX.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_fmodex.h"
#include "llaudioengine_fmodex.h"
#include "lllistener_fmodex.h"
#include "llerror.h"
#include "llmath.h"
#include "llrand.h"
#include "fmod.hpp"
#include "fmod_errors.h"
#include "lldir.h"
#include "llapr.h"
#include "sound_ids.h"
#if LL_WINDOWS //Some ugly code to make missing fmodex.dll not cause a fatal error.
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include <DelayImp.h>
#pragma comment(lib, "delayimp.lib")
bool attemptDelayLoad()
{
__try
{
if( FAILED( __HrLoadAllImportsForDll( "fmodex.dll" ) ) )
return false;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
return false;
}
return true;
}
#endif
FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels);
LLAudioEngine_FMODEX::LLAudioEngine_FMODEX()
{
mInited = false;
mWindGen = NULL;
mWindDSP = NULL;
mSystem = NULL;
}
LLAudioEngine_FMODEX::~LLAudioEngine_FMODEX()
{
}
inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string)
{
if(result == FMOD_OK)
return false;
llwarns << string << " Error: " << FMOD_ErrorString(result) << llendl;
return true;
}
bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata)
{
#if LL_WINDOWS
if(!attemptDelayLoad())
return false;
#endif
U32 version;
FMOD_RESULT result;
int numdrivers;
FMOD_SPEAKERMODE speakermode;
FMOD_CAPS caps;
char name[256];
LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() initializing FMOD" << LL_ENDL;
result = FMOD::System_Create(&mSystem);
if(Check_FMOD_Error(result, "FMOD::System_Create"))
return false;
//will call LLAudioEngine_FMODEX::allocateListener, which needs a valid mSystem pointer.
LLAudioEngine::init(num_channels, userdata);
result = mSystem->getVersion(&version);
Check_FMOD_Error(result, "FMOD::System::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;
}
#if LL_WINDOWS
//Is this block applicable to linux?
{
result = mSystem->getNumDrivers(&numdrivers);
Check_FMOD_Error(result, "FMOD::System::getNumDrivers");
if (numdrivers == 0)
{
result = mSystem->setOutput(FMOD_OUTPUTTYPE_NOSOUND);
Check_FMOD_Error(result, "FMOD::System::setOutput");
}
else
{
result = mSystem->getDriverCaps(0, &caps, 0, &speakermode);
Check_FMOD_Error(result,"FMOD::System::getDriverCaps");
/*
Set the user selected speaker mode.
*/
result = mSystem->setSpeakerMode(speakermode);
Check_FMOD_Error(result, "FMOD::System::getDriverCaps");
if (caps & FMOD_CAPS_HARDWARE_EMULATED)
{
/*
The user has the 'Acceleration' slider set to off! This is really bad
for latency! You might want to warn the user about this.
*/
result = mSystem->setDSPBufferSize(1024, 10);
Check_FMOD_Error(result, "FMOD::System::setDSPBufferSize");
}
result = mSystem->getDriverInfo(0, name, 256, 0);
Check_FMOD_Error(result, "FMOD::System::getDriverInfo");
if (strstr(name, "SigmaTel"))
{
/*
Sigmatel sound devices crackle for some reason if the format is PCM 16bit.
PCM floating point output seems to solve it.
*/
result = mSystem->setSoftwareFormat(48000, FMOD_SOUND_FORMAT_PCMFLOAT, 0,0, FMOD_DSP_RESAMPLER_LINEAR);
Check_FMOD_Error(result,"FMOD::System::setSoftwareFormat");
}
}
}
#endif //LL_WINDOWS
// Reserve one extra channel for the http stream.
result = mSystem->setHardwareChannels(num_channels + 1);
Check_FMOD_Error(result,"FMOD::System::setHardwareChannels");
U32 fmod_flags = FMOD_INIT_NORMAL;
#if LL_LINUX
// 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_FMODEX_ESD")) /*Flawfinder: ignore*/
{
LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL;
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_ESD) == FMOD_OK &&
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY" << LL_ENDL;
audio_ok = true;
}
else
{
Check_FMOD_Error(result, "ESD audio output FAILED to initialize");
}
}
else
{
LL_DEBUGS("AppInit") << "ESD audio output SKIPPED" << LL_ENDL;
}
}
if (!audio_ok)
{
if (NULL == getenv("LL_BAD_FMODEX_OSS")) /*Flawfinder: ignore*/
{
LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL;
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK &&
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL;
audio_ok = true;
}
else
{
Check_FMOD_Error(result, "OSS audio output FAILED to initialize" << LL_ENDL;
}
}
else
{
LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL;
}
}
if (!audio_ok)
{
if (NULL == getenv("LL_BAD_FMODEX_ALSA")) /*Flawfinder: ignore*/
{
LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
if(mSystem->SetOutput(FMOD_OUTPUTTYPE_ALSA) &&
(result = mSystem->init(num_channels, fmod_flags, 0)) == FMOD_OK)
{
LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
audio_ok = true;
}
else
{
Check_FMOD_Error(result, "ALSA audio output FAILED to initialize");
}
} 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.
FMOD_OUTPUTTYPE output_type;
mSystem->getOutput(output_type);
switch (output_type)
{
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
result = mSystem->init( num_channels, fmod_flags, 0);
if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
{
/*
Ok, the speaker mode selected isn't supported by this soundcard. Switch it
back to stereo...
*/
result = mSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
Check_FMOD_Error(result,"Error falling back to stereo mode");
/*
... and re-init.
*/
result = mSystem->init(100, FMOD_INIT_NORMAL, 0);
}
if(Check_FMOD_Error(result, "Error initializing FMOD"))
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_FMODEX(mSystem));
LL_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() FMOD initialized correctly" << LL_ENDL;
mInited = true;
return true;
}
std::string LLAudioEngine_FMODEX::getDriverName(bool verbose)
{
llassert_always(mSystem);
if (verbose)
{
U32 version;
if(!Check_FMOD_Error(mSystem->getVersion(&version), "FMOD::System::getVersion"))
{
return llformat("FMOD version %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF);
}
}
return "FMOD";
}
void LLAudioEngine_FMODEX::allocateListener(void)
{
mListenerp = (LLListener *) new LLListener_FMODEX(mSystem);
if (!mListenerp)
{
llwarns << "Listener creation failed" << llendl;
}
}
void LLAudioEngine_FMODEX::shutdown()
{
stopInternetStream();
LLAudioEngine::shutdown();
llinfos << "LLAudioEngine_FMODEX::shutdown() closing FMOD" << llendl;
mSystem->close();
mSystem->release();
llinfos << "LLAudioEngine_FMODEX::shutdown() done closing FMOD" << llendl;
delete mListenerp;
mListenerp = NULL;
}
LLAudioBuffer * LLAudioEngine_FMODEX::createBuffer()
{
return new LLAudioBufferFMODEX(mSystem);
}
LLAudioChannel * LLAudioEngine_FMODEX::createChannel()
{
return new LLAudioChannelFMODEX(mSystem);
}
bool LLAudioEngine_FMODEX::initWind()
{
if (!mWindGen)
{
int samplerate;
FMOD_SOUND_FORMAT format;
mSystem->getSoftwareFormat(&samplerate,&format,NULL,NULL,NULL,NULL);
//May need to check format. eg, PCM16 may require 16bit LLWindGen.
/*bool enable;
FMOD_SOUND_FORMAT format;
mSystem->getFormat(0,&format,0,0);
switch (format)
{
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;
}*/
mWindGen = new LLWindGen<MIXBUFFERFORMAT>(samplerate);
}
mNextWindUpdate = 0.0;
if (!mWindDSP)
{
FMOD_DSP_DESCRIPTION dspdesc;
memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); //Set everything to zero
strncpy(dspdesc.name,"Wind Unit", sizeof(dspdesc.name)); //Set name to "Wind Unit"
dspdesc.read = &windCallback; //Assign callback.
dspdesc.userdata = (void*)mWindGen;
Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP");
}
if (mWindDSP)
{
mSystem->addDSP(mWindDSP, NULL);
return true;
}
return false;
}
void LLAudioEngine_FMODEX::cleanupWind()
{
if (mWindDSP)
{
mWindDSP->remove();
mWindDSP->release();
mWindDSP = NULL;
}
delete mWindGen;
mWindGen = NULL;
}
//-----------------------------------------------------------------------
void LLAudioEngine_FMODEX::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_FMODEX::setInternalGain(F32 gain)
{
if (!mInited)
{
return;
}
gain = llclamp( gain, 0.0f, 1.0f );
FMOD::ChannelGroup *master_group;
mSystem->getMasterChannelGroup(&master_group);
master_group->setVolume(gain);
LLStreamingAudioInterface *saimpl = getStreamingAudioImpl();
if ( saimpl )
{
// fmod likes its streaming audio channel gain re-asserted after
// master volume change.
saimpl->setGain(saimpl->getGain());
}
}
//
// LLAudioChannelFMODEX implementation
//
LLAudioChannelFMODEX::LLAudioChannelFMODEX(FMOD::System *system) : LLAudioChannel(), mSystem(system), mChannelp(NULL), mLastSamplePos(0)
{
}
LLAudioChannelFMODEX::~LLAudioChannelFMODEX()
{
cleanup();
}
bool LLAudioChannelFMODEX::updateBuffer()
{
if (LLAudioChannel::updateBuffer())
{
// Base class update returned true, which means that we need to actually
// set up the channel for a different buffer.
LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentSourcep->getCurrentBuffer();
// Grab the FMOD sample associated with the buffer
FMOD::Sound *soundp = bufferp->getSound();
if (!soundp)
{
// This is bad, there should ALWAYS be a sound associated with a legit
// buffer.
llerrs << "No FMOD sound!" << llendl;
return false;
}
// Actually play the sound. Start it off paused so we can do all the necessary
// setup.
FMOD_RESULT result = mSystem->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp);
Check_FMOD_Error(result, "FMOD::System::playSound");
//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
U32 result;
result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain());
//Check_FMOD_Error(result, "LLAudioChannelFMODEX::updateBuffer");
result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
/*if(Check_FMOD_ERROR(result, "LLAudioChannelFMODEX::updateBuffer"))
{
S32 index;
mChannelp->getIndex(&index);
llwarns << "Channel " << index << "Source ID: " << mCurrentSourcep->getID()
<< " at " << mCurrentSourcep->getPositionGlobal() << llendl;
}*/
}
return true;
}
void LLAudioChannelFMODEX::update3DPosition()
{
if (!mChannelp)
{
// We're not actually a live channel (i.e., we're not playing back anything)
return;
}
LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)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.
set3DMode(false);
}
else
{
// Localized sound. Update the position and velocity of the sound.
set3DMode(true);
LLVector3 float_pos;
float_pos.setVec(mCurrentSourcep->getPositionGlobal());
FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV);
Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes");
}
}
void LLAudioChannelFMODEX::updateLoop()
{
if (!mChannelp)
{
// 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;
mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES);
if (cur_pos < (U32)mLastSamplePos)
{
mLoopedThisFrame = true;
}
mLastSamplePos = cur_pos;
}
void LLAudioChannelFMODEX::cleanup()
{
if (!mChannelp)
{
//llinfos << "Aborting cleanup with no channel handle." << llendl;
return;
}
//llinfos << "Cleaning up channel: " << mChannelID << llendl;
Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop");
mCurrentBufferp = NULL;
mChannelp = NULL;
}
void LLAudioChannelFMODEX::play()
{
if (!mChannelp)
{
llwarns << "Playing without a channel handle, aborting" << llendl;
return;
}
Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::pause");
getSource()->setPlayedOnce(true);
}
void LLAudioChannelFMODEX::playSynced(LLAudioChannel *channelp)
{
LLAudioChannelFMODEX *fmod_channelp = (LLAudioChannelFMODEX*)channelp;
if (!(fmod_channelp->mChannelp && mChannelp))
{
// Don't have channels allocated to both the master and the slave
return;
}
U32 cur_pos;
if(Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position"))
return;
cur_pos %= mCurrentBufferp->getLength();
// Try to match the position of our sync master
Check_FMOD_Error(mChannelp->setPosition(cur_pos,FMOD_TIMEUNIT_PCMBYTES),"Unable to set current position");
// Start us playing
play();
}
bool LLAudioChannelFMODEX::isPlaying()
{
if (!mChannelp)
{
return false;
}
bool paused, playing;
mChannelp->getPaused(&paused);
mChannelp->isPlaying(&playing);
return !paused && playing;
}
//
// LLAudioChannelFMODEX implementation
//
LLAudioBufferFMODEX::LLAudioBufferFMODEX(FMOD::System *system) : mSystem(system), mSoundp(NULL)
{
}
LLAudioBufferFMODEX::~LLAudioBufferFMODEX()
{
if(mSoundp)
{
mSoundp->release();
mSoundp = NULL;
}
}
bool LLAudioBufferFMODEX::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 (mSoundp)
{
// If there's already something loaded in this buffer, clean it up.
mSoundp->release();
mSoundp = NULL;
}
bool creation_attempted = false;
FMOD_RESULT result = FMOD_OK;
FMOD_MODE base_mode = FMOD_SOFTWARE | FMOD_LOOP_NORMAL;
// 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...
FMOD_CREATESOUNDEXINFO info;
memset(&info, 0, sizeof(info));
info.cbsize = sizeof(info);
info.length = file_length;
result = mSystem->createSound(buffer, base_mode | FMOD_OPENMEMORY , &info, &mSoundp);
creation_attempted = true;
}
delete[] buffer;
fclose(sound_file);
}
#else
result = mSystem->createSound(filename.c_str(), base_mode, 0, &mSoundp);
creation_attempted = true;
#endif
if (creation_attempted && result != FMOD_OK)
{
// We failed to load the file for some reason.
llwarns << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << 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 LLAudioBufferFMODEX::getLength()
{
if (!mSoundp)
{
return 0;
}
U32 length;
mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES);
return length;
}
void LLAudioChannelFMODEX::set3DMode(bool use3d)
{
FMOD_MODE current_mode;
if(mChannelp->getMode(&current_mode) != FMOD_OK)
return;
FMOD_MODE new_mode = current_mode;
new_mode &= ~(use3d ? FMOD_2D : FMOD_3D);
new_mode |= use3d ? FMOD_3D : FMOD_2D;
if(current_mode != new_mode)
{
mChannelp->setMode(new_mode);
}
}
FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *originalbuffer, float *newbuffer, unsigned int length, int inchannels, int outchannels)
{
// 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_FMODEX::MIXBUFFERFORMAT> *windgen;
FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance;
thisdsp->getUserData((void **)&windgen);
S32 channels, configwidth, configheight;
thisdsp->getInfo(0, 0, &channels, &configwidth, &configheight);
windgen->windGenerate((LLAudioEngine_FMODEX::MIXBUFFERFORMAT *)newbuffer, length);
return FMOD_OK;
}

View File

@@ -0,0 +1,132 @@
/**
* @file audioengine_FMODEX.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_FMODEX_H
#define LL_AUDIOENGINE_FMODEX_H
#include "llaudioengine.h"
#include "lllistener_fmod.h"
#include "llwindgen.h"
//Stubs
class LLAudioStreamManagerFMODEX;
namespace FMOD
{
class System;
class Channel;
class Sound;
class DSP;
}
//Interfaces
class LLAudioEngine_FMODEX : public LLAudioEngine
{
public:
LLAudioEngine_FMODEX();
virtual ~LLAudioEngine_FMODEX();
// 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);
typedef F32 MIXBUFFERFORMAT;
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);
bool mInited;
// On Windows, userdata is the HWND of the application window.
void* mUserData;
LLWindGen<MIXBUFFERFORMAT> *mWindGen;
FMOD::DSP *mWindDSP;
FMOD::System *mSystem;
};
class LLAudioChannelFMODEX : public LLAudioChannel
{
public:
LLAudioChannelFMODEX(FMOD::System *system);
virtual ~LLAudioChannelFMODEX();
protected:
/*virtual*/ void play();
/*virtual*/ void playSynced(LLAudioChannel *channelp);
/*virtual*/ void cleanup();
/*virtual*/ bool isPlaying();
/*virtual*/ bool updateBuffer();
/*virtual*/ void update3DPosition();
/*virtual*/ void updateLoop();
void set3DMode(bool use3d);
protected:
FMOD::System *mSystem;
FMOD::Channel *mChannelp;
S32 mLastSamplePos;
};
class LLAudioBufferFMODEX : public LLAudioBuffer
{
public:
LLAudioBufferFMODEX(FMOD::System *system);
virtual ~LLAudioBufferFMODEX();
/*virtual*/ bool loadWAV(const std::string& filename);
/*virtual*/ U32 getLength();
friend class LLAudioChannelFMODEX;
void set3DMode(bool use3d);
protected:
FMOD::Sound *getSound() { return mSoundp; }
FMOD::System *mSystem;
FMOD::Sound *mSoundp;
};
#endif // LL_AUDIOENGINE_FMODEX_H

View File

@@ -0,0 +1,132 @@
/**
* @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_fmodex.h"
#include "fmod.hpp"
//-----------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------
LLListener_FMODEX::LLListener_FMODEX(FMOD::System *system)
{
mSystem = system;
init();
}
//-----------------------------------------------------------------------
LLListener_FMODEX::~LLListener_FMODEX()
{
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::init(void)
{
// do inherited
LLListener::init();
mDopplerFactor = 1.0f;
mRolloffFactor = 1.0f;
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::translate(LLVector3 offset)
{
LLListener::translate(offset);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::setPosition(LLVector3 pos)
{
LLListener::setPosition(pos);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::setVelocity(LLVector3 vel)
{
LLListener::setVelocity(vel);
mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::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;
mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODEX::commitDeferredChanges()
{
mSystem->update();
}
void LLListener_FMODEX::setRolloffFactor(F32 factor)
{
mRolloffFactor = factor;
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
}
F32 LLListener_FMODEX::getRolloffFactor()
{
return mRolloffFactor;
}
void LLListener_FMODEX::setDopplerFactor(F32 factor)
{
mDopplerFactor = factor;
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
}
F32 LLListener_FMODEX::getDopplerFactor()
{
return mDopplerFactor;
}

View File

@@ -0,0 +1,71 @@
/**
* @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_FMODEX_H
#define LL_LISTENER_FMODEX_H
#include "lllistener.h"
//Stubs
namespace FMOD
{
class System;
}
//Interfaces
class LLListener_FMODEX : public LLListener
{
public:
LLListener_FMODEX(FMOD::System *system);
virtual ~LLListener_FMODEX();
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:
FMOD::System *mSystem;
F32 mDopplerFactor;
F32 mRolloffFactor;
};
#endif

View File

@@ -0,0 +1,422 @@
/**
* @file streamingaudio_fmod.cpp
* @brief LLStreamingAudio_FMODEX 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.hpp"
#include "fmod_errors.h"
#include "llstreamingaudio_fmodex.h"
class LLAudioStreamManagerFMODEX
{
public:
LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url);
FMOD::Channel* startStream();
bool stopStream(); // Returns true if the stream was successfully stopped.
bool ready();
const std::string& getURL() { return mInternetStreamURL; }
FMOD_OPENSTATE getOpenState();
protected:
FMOD::System* mSystem;
FMOD::Channel* mStreamChannel;
FMOD::Sound* mInternetStream;
bool mReady;
std::string mInternetStreamURL;
};
//---------------------------------------------------------------------------
// Internet Streaming
//---------------------------------------------------------------------------
LLStreamingAudio_FMODEX::LLStreamingAudio_FMODEX(FMOD::System *system) :
mSystem(system),
mCurrentInternetStreamp(NULL),
mFMODInternetStreamChannelp(NULL),
mGain(1.0f),
mMetaData(NULL)
{
// Number of milliseconds of audio to buffer for the audio card.
// Must be larger than the usual Second Life frame stutter time.
mSystem->setStreamBufferSize(200, FMOD_TIMEUNIT_MS);
// 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_FMODEX::~LLStreamingAudio_FMODEX()
{
// nothing interesting/safe to do.
}
void LLStreamingAudio_FMODEX::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 LLAudioStreamManagerFMODEX(mSystem,url);
mURL = url;
mMetaData = new LLSD;
}
else
{
llinfos << "Set internet stream to null" << llendl;
mURL.clear();
}
}
void LLStreamingAudio_FMODEX::update()
{
// Kill dead internet streams, if possible
std::list<LLAudioStreamManagerFMODEX *>::iterator iter;
for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
{
LLAudioStreamManagerFMODEX *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;
}
FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState();
if (open_state == FMOD_OPENSTATE_READY)
{
// Stream is live
// start the stream if it's ready
if (!mFMODInternetStreamChannelp &&
(mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream()))
{
// Reset volume to previously set volume
setGain(getGain());
mFMODInternetStreamChannelp->setPaused(false);
}
}
else if(open_state == FMOD_OPENSTATE_ERROR)
{
stop();
return;
}
if(mFMODInternetStreamChannelp)
{
if(!mMetaData)
mMetaData = new LLSD;
FMOD::Sound *sound = NULL;
if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound)
{
FMOD_TAG tag;
S32 tagcount, dirtytagcount;
if(sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount)
{
mMetaData->clear();
for(S32 i = 0; i < tagcount; ++i)
{
if(sound->getTag(NULL, i, &tag)!=FMOD_OK)
continue;
std::string name = tag.name;
switch(tag.type) //Crappy tag translate table.
{
case(FMOD_TAGTYPE_ID3V2):
if(name == "TIT2") name = "TITLE";
else if(name == "TPE1") name = "ARTIST";
break;
case(FMOD_TAGTYPE_ASF):
if(name == "Title") name = "TITLE";
else if(name == "WM/AlbumArtist") name = "ARTIST";
break;
default:
break;
}
switch(tag.datatype)
{
case(FMOD_TAGDATATYPE_INT):
(*mMetaData)[name]=*(LLSD::Integer*)(tag.data);
llinfos << tag.name << ": " << *(int*)(tag.data) << llendl;
break;
case(FMOD_TAGDATATYPE_FLOAT):
(*mMetaData)[name]=*(LLSD::Float*)(tag.data);
llinfos << tag.name << ": " << *(float*)(tag.data) << llendl;
break;
case(FMOD_TAGDATATYPE_STRING):
{
std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen));
(*mMetaData)[name]=out;
llinfos << tag.name << ": " << out << llendl;
}
break;
case(FMOD_TAGDATATYPE_STRING_UTF16):
{
std::string out((char*)tag.data,tag.datalen);
(*mMetaData)[std::string(tag.name)]=out;
llinfos << tag.name << ": " << out << llendl;
}
break;
case(FMOD_TAGDATATYPE_STRING_UTF16BE):
{
std::string out((char*)tag.data,tag.datalen);
U16* buf = (U16*)out.c_str();
for(U32 j = 0; j < out.size()/2; ++j)
(((buf[j] & 0xff)<<8) | ((buf[j] & 0xff00)>>8));
(*mMetaData)[std::string(tag.name)]=out;
llinfos << tag.name << ": " << out << llendl;
}
default:
break;
}
}
}
}
}
}
void LLStreamingAudio_FMODEX::stop()
{
if(mMetaData)
{
delete mMetaData;
mMetaData = NULL;
}
if (mFMODInternetStreamChannelp)
{
mFMODInternetStreamChannelp->setPaused(true);
mFMODInternetStreamChannelp->setPriority(0);
mFMODInternetStreamChannelp = NULL;
}
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_FMODEX::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_FMODEX::isPlaying()
{
if (mCurrentInternetStreamp)
{
return 1; // Active and playing
}
else if (!mURL.empty())
{
return 2; // "Paused"
}
else
{
return 0;
}
}
F32 LLStreamingAudio_FMODEX::getGain()
{
return mGain;
}
std::string LLStreamingAudio_FMODEX::getURL()
{
return mURL;
}
void LLStreamingAudio_FMODEX::setGain(F32 vol)
{
mGain = vol;
if (mFMODInternetStreamChannelp)
{
vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here?
mFMODInternetStreamChannelp->setVolume(vol);
}
}
///////////////////////////////////////////////////////
// manager of possibly-multiple internet audio streams
LLAudioStreamManagerFMODEX::LLAudioStreamManagerFMODEX(FMOD::System *system, const std::string& url) :
mSystem(system),
mStreamChannel(NULL),
mInternetStream(NULL),
mReady(false)
{
mInternetStreamURL = url;
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING, 0, &mInternetStream);
if (result!= FMOD_OK)
{
llwarns << "Couldn't open fmod stream, error "
<< FMOD_ErrorString(result)
<< llendl;
mReady = false;
return;
}
mReady = true;
}
FMOD::Channel *LLAudioStreamManagerFMODEX::startStream()
{
// We need a live and opened stream before we try and play it.
if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY)
{
llwarns << "No internet stream to start playing!" << llendl;
return NULL;
}
if(mStreamChannel)
return mStreamChannel; //Already have a channel for this stream.
mSystem->playSound(FMOD_CHANNEL_FREE, mInternetStream, true, &mStreamChannel);
return mStreamChannel;
}
bool LLAudioStreamManagerFMODEX::stopStream()
{
if (mInternetStream)
{
bool close = true;
switch (getOpenState())
{
case FMOD_OPENSTATE_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)
{
mInternetStream->release();
mStreamChannel = NULL;
mInternetStream = NULL;
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
FMOD_OPENSTATE LLAudioStreamManagerFMODEX::getOpenState()
{
FMOD_OPENSTATE state;
mInternetStream->getOpenState(&state,NULL,NULL,NULL);
return state;
}

View File

@@ -0,0 +1,80 @@
/**
* @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"
//Stubs
class LLAudioStreamManagerFMODEX;
namespace FMOD
{
class System;
class Channel;
}
//Interfaces
class LLStreamingAudio_FMODEX : public LLStreamingAudioInterface
{
public:
LLStreamingAudio_FMODEX(FMOD::System *system);
/*virtual*/ ~LLStreamingAudio_FMODEX();
/*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();
/*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not supported.
private:
FMOD::System *mSystem;
LLAudioStreamManagerFMODEX *mCurrentInternetStreamp;
FMOD::Channel *mFMODInternetStreamChannelp;
std::list<LLAudioStreamManagerFMODEX *> mDeadStreams;
std::string mURL;
F32 mGain;
LLSD *mMetaData;
};
#endif // LL_STREAMINGAUDIO_FMOD_H

View File

@@ -33,6 +33,7 @@
#define WINDGEN_H
#include "llcommon.h"
#include "llrand.h"
template <class MIXBUFFERFORMAT_T>
class LLWindGen
@@ -60,7 +61,9 @@ public:
}
const U32 getInputSamplingRate() { return mInputSamplingRate; }
const F32 getNextSample();
const F32 getClampedSample(bool clamp, F32 sample);
// 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
@@ -95,7 +98,7 @@ public:
// 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);
next_sample = getNextSample();
// Apply a pinking filter
// Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/
@@ -132,23 +135,13 @@ public:
for (U8 i=mSubSamples; i && numsamples; --i, --numsamples)
{
mLastSample = mLastSample + delta;
S32 sample_right = (S32)(mLastSample * mCurrentPanGainR);
S32 sample_left = (S32)mLastSample - sample_right;
MIXBUFFERFORMAT_T sample_right = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample * mCurrentPanGainR);
MIXBUFFERFORMAT_T sample_left = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample - (F32)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;
}
*cursamplep = sample_left;
++cursamplep;
*cursamplep = sample_right;
++cursamplep;
}
}
@@ -179,4 +172,9 @@ private:
F32 mLastSample;
};
template<class T> inline const F32 LLWindGen<T>::getNextSample() { return (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8); }
template<> inline const F32 LLWindGen<F32>::getNextSample() { return ll_frand()-.5f; }
template<class T> inline const F32 LLWindGen<T>::getClampedSample(bool clamp, F32 sample) { return clamp ? (F32)llclamp((S32)sample,(S32)S16_MIN,(S32)S16_MAX) : sample; }
template<> inline const F32 LLWindGen<F32>::getClampedSample(bool clamp, F32 sample) { return sample; }
#endif

View File

@@ -7,7 +7,15 @@ include(Boost)
include(DBusGlib)
include(DirectX)
include(ELFIO)
include(FMOD)
if(FMODEX)
include(FMODEX)
if(FMODEX)
set(FMOD OFF)
endif(FMODEX)
endif(FMODEX)
if(NOT FMODEX)
include(FMOD)
endif(NOT FMODEX)
include(OPENAL)
include(HUNSPELL)
include(FindOpenGL)
@@ -1168,7 +1176,6 @@ if (WINDOWS)
comdlg32
${DINPUT_LIBRARY}
${DXGUID_LIBRARY}
fmodvc
gdi32
kernel32
odbc32
@@ -1182,6 +1189,13 @@ if (WINDOWS)
winspool
)
if(FMODEX)
list(APPEND viewer_LIBRARIES ${FMODEX_LIBRARY})
endif(FMODEX)
if(FMOD)
list(APPEND viewer_LIBRARIES ${FMOD_LIBRARY})
endif(FMOD)
find_library(INTEL_MEMOPS_LIBRARY
NAMES ll_intel_memops
PATHS
@@ -1288,13 +1302,23 @@ if (OPENAL)
set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_OPENAL")
endif (OPENAL)
if (FMOD)
set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMOD")
if (FMOD OR FMODEX)
if(FMODEX)
set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX")
endif(FMODEX)
if(FMOD)
set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMOD")
endif(FMOD)
if (NOT WINDOWS)
set(fmodwrapper_SOURCE_FILES fmodwrapper.cpp)
add_library(fmodwrapper SHARED ${fmodwrapper_SOURCE_FILES})
set(fmodwrapper_needed_LIBRARIES ${FMOD_LIBRARY})
if(FMODEX)
set(fmodwrapper_needed_LIBRARIES ${FMODEX_LIBRARY})
endif(FMODEX)
if(FMOD)
set(fmodwrapper_needed_LIBRARIES "${fmodwrapper_needed_LIBRARIES} ${FMOD_LIBRARY}")
endif(FMOD)
if (DARWIN)
list(APPEND fmodwrapper_needed_LIBRARIES ${CARBON_LIBRARY})
set_target_properties(
@@ -1308,7 +1332,7 @@ if (FMOD)
set(FMODWRAPPER_LIBRARY fmodwrapper)
target_link_libraries(fmodwrapper ${fmodwrapper_needed_LIBRARIES})
endif (NOT WINDOWS)
endif (FMOD)
endif (FMOD OR FMODEX)
set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}")
@@ -1347,9 +1371,13 @@ if (WINDOWS)
set(release_flags "/MAP:Release/${VIEWER_BINARY_NAME}.map")
endif()
if (FMODEX)
set(EXTRA_LINKER_FLAGS "/DELAYLOAD:fmodex.dll")
endif (FMODEX)
set_target_properties(${VIEWER_BINARY_NAME}
PROPERTIES
LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS ${GOOGLE_PERFTOOLS_LINKER_FLAGS}"
LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS ${GOOGLE_PERFTOOLS_LINKER_FLAGS} ${EXTRA_LINKER_FLAGS}"
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\""
LINK_FLAGS_RELEASE ${release_flags}
)

View File

@@ -32,12 +32,24 @@
extern "C"
{
#if LL_FMODEX
void FSOUND_Sound_Init(void);
#endif
#if LL_FMOD
void FSOUND_Init(void);
#endif
}
void* fmodwrapper(void)
{
// When building the fmodwrapper library, the linker doesn't seem to want to bring in libfmod.a unless I explicitly
// reference at least one symbol in the library. This seemed like the simplest way.
return (void*)&FSOUND_Init;
void *ret = NULL;
#if LL_FMODEX
ret = (void*)&FSOUND_Sound_Init;
#endif
#if LL_FMOD
ret = (void*)&FSOUND_Init;
#endif
return ret;
}

View File

@@ -43,7 +43,11 @@
#include "llviewermedia_streamingaudio.h"
#include "llaudioengine.h"
#ifdef LL_FMOD
#if LL_FMODEX
# include "llaudioengine_fmodex.h"
#endif
#if LL_FMOD
# include "llaudioengine_fmod.h"
#endif
@@ -666,6 +670,17 @@ bool idle_startup()
}
#endif
#ifdef LL_FMODEX
if (!gAudiop
#if !LL_WINDOWS
&& NULL == getenv("LL_BAD_FMODEX_DRIVER")
#endif // !LL_WINDOWS
)
{
gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODEX();
}
#endif
#ifdef LL_FMOD
if (!gAudiop
#if !LL_WINDOWS