From 1bc887f887e7ea6b08016a83a0f56d3e3a5da128 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sun, 21 Apr 2013 05:55:47 +0200 Subject: [PATCH] Add the new audio init code (experimental) --- indra/llaudio/llaudioengine_fmod.cpp | 804 +++++++++++++++------------ 1 file changed, 439 insertions(+), 365 deletions(-) diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp index 168bb5f60..f8448339d 100644 --- a/indra/llaudio/llaudioengine_fmod.cpp +++ b/indra/llaudio/llaudioengine_fmod.cpp @@ -1,182 +1,295 @@ /** - * @file audioengine_fmod.cpp + * @file audioengine_FMODEX.cpp * @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * 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 * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. + * 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 * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * 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. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * 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 "llstreamingaudio_fmodex.h" -#include "llaudioengine_fmod.h" -#include "lllistener_fmod.h" +#include "llaudioengine_fmodex.h" +#include "lllistener_fmodex.h" #include "llerror.h" #include "llmath.h" #include "llrand.h" -#include "fmod.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 +#pragma comment(lib, "delayimp.lib") -extern "C" { - void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata); +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_FMOD::LLAudioEngine_FMOD() +FMOD::ChannelGroup *LLAudioEngine_FMODEX::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0}; + +LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler) { mInited = false; mWindGen = NULL; mWindDSP = NULL; + mSystem = NULL; + mEnableProfiler = enable_profiler; } -LLAudioEngine_FMOD::~LLAudioEngine_FMOD() +LLAudioEngine_FMODEX::~LLAudioEngine_FMODEX() { } -bool LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata) +inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) { - LLAudioEngine::init(num_channels, userdata); + if(result == FMOD_OK) + return false; + llwarns << string << " Error: " << FMOD_ErrorString(result) << llendl; + return true; +} - // Reserve one extra channel for the http stream. - if (!FSOUND_SetMinHardwareChannels(num_channels + 1)) +void* F_STDCALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + if(type & FMOD_MEMORY_STREAM_DECODE) { - LL_WARNS("AppInit") << "FMOD::init[0](), error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; + llinfos << "Decode buffer size: " << size << llendl; } - - LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() initializing FMOD" << LL_ENDL; - - F32 version = FSOUND_GetVersion(); - if (version < FMOD_VERSION) + else if(type & FMOD_MEMORY_STREAM_FILE) { - LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version - << ")! You should be using FMOD " << FMOD_VERSION << LL_ENDL; - //return false; + llinfos << "Strean buffer size: " << size << llendl; } + return new char[size]; +} +void* F_STDCALL decode_realloc(void *ptr, unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + memset(ptr,0,size); + return ptr; +} +void F_STDCALL decode_dealloc(void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + delete[] (char*)ptr; +} - U32 fmod_flags = 0x0; +bool LLAudioEngine_FMODEX::init(const S32 num_channels, void* userdata) +{ #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; + if(!attemptDelayLoad()) 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; - fmod_flags |= FSOUND_INIT_DSOUND_HRTF_FULL; #endif -#if LL_LINUX - // initialize the FMOD engine + U32 version; + FMOD_RESULT result; - // 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_DEBUGS("AppInit") << "LLAudioEngine_FMODEX::init() initializing FMOD" << LL_ENDL; + + //result = FMOD::Memory_Initialize(NULL, 0, &decode_alloc, &decode_realloc, &decode_dealloc, FMOD_MEMORY_STREAM_DECODE | FMOD_MEMORY_STREAM_FILE); + //if(Check_FMOD_Error(result, "FMOD::Memory_Initialize")) + // return false; + + 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_INFOS("AppInit") << "Pacifying valgrind in FMOD init." << LL_ENDL; - FSOUND_SetMixer(FSOUND_MIXER_QUALITY_FPU); + LL_WARNS("AppInit") << "Error : You are using the wrong FMOD Ex version (" << version + << ")! You should be using FMOD Ex" << FMOD_VERSION << LL_ENDL; } - // 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. +#if LL_WINDOWS + int numdrivers; + FMOD_SPEAKERMODE speakermode; + FMOD_CAPS caps; + char name[256]; + + { + 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::setSpeakerMode"); + 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); + name[255] = '\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"); + } + else if (strstr(name, "Traktor")) + { + /* + Traktor sound devices are annoying. Apply hacks here. + */ + LL_INFOS("AppInit") << "Traktor Workarond Test" << LL_ENDL; + + result = mSystem->setSoftwareFormat(96000, FMOD_SOUND_FORMAT_PCMFLOAT, 0,0, + FMOD_DSP_RESAMPLER_CUBIC); + Check_FMOD_Error(result,"FMOD::System::setSoftwareFormat"); + } + } + } +#endif // LL_WINDOWS + + // In this case, all sounds, PLUS wind and stream will be software. + result = mSystem->setSoftwareChannels(num_channels + 2); + Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels"); + + U32 fmod_flags = FMOD_INIT_NORMAL; + if(mEnableProfiler) + { + fmod_flags |= FMOD_INIT_ENABLE_PROFILE; + mSystem->createChannelGroup("None", &mChannelGroups[AUDIO_TYPE_NONE]); + mSystem->createChannelGroup("SFX", &mChannelGroups[AUDIO_TYPE_SFX]); + mSystem->createChannelGroup("UI", &mChannelGroups[AUDIO_TYPE_UI]); + mSystem->createChannelGroup("Ambient", &mChannelGroups[AUDIO_TYPE_AMBIENT]); + } + +#if LL_LINUX bool audio_ok = false; if (!audio_ok) { - if (NULL == getenv("LL_BAD_FMOD_ESD")) /*Flawfinder: ignore*/ + if (NULL == getenv("LL_BAD_FMOD_PULSEAUDIO")) /*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") << "Trying PulseAudio audio output..." << LL_ENDL; + if(mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK && + (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { - LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY" - << LL_ENDL; + LL_DEBUGS("AppInit") << "PulseAudio 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*/ + else + { + Check_FMOD_Error(result, "PulseAudio audio output FAILED to initialize"); + } + } + else { - 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; - } + LL_DEBUGS("AppInit") << "PulseAudio 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)) + if(mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK && + (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) { 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 + { + Check_FMOD_Error(result, "ALSA audio output FAILED to initialize"); } - } else { + } + else + { + LL_DEBUGS("AppInit") << "ALSA 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(mSystem->setOutput(FMOD_OUTPUTTYPE_OSS) == FMOD_OK && + (result = mSystem->init(num_channels + 2, 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"); + } + } + else + { LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; } } @@ -186,39 +299,82 @@ bool LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata) 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()) + 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; + case FMOD_OUTPUTTYPE_NOSOUND: + LL_INFOS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_PULSEAUDIO: + LL_INFOS("AppInit") << "Audio output: PulseAudio" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_ALSA: + LL_INFOS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; + case FMOD_OUTPUTTYPE_OSS: + LL_INFOS("AppInit") << "Audio output: OSS" << 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)) + result = mSystem->init( num_channels + 2, fmod_flags, 0); + if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) { - LL_WARNS("AppInit") << "Error initializing FMOD: " - << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; - return false; + /* + 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( num_channels + 2, fmod_flags, 0); } - + if(Check_FMOD_Error(result, "Error initializing FMOD Ex")) + 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()); + setStreamingAudioImpl(new LLStreamingAudio_FMODEX(mSystem)); - LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() FMOD initialized correctly" << LL_ENDL; + LL_INFOS("AppInit") << "LLAudioEngine_FMODEX::init() FMOD Ex initialized correctly" << LL_ENDL; + + int r_numbuffers, r_samplerate, r_channels, r_bits; + unsigned int r_bufferlength; + char r_name[256]; + FMOD_SPEAKERMODE r_speaker_mode; + mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers); + mSystem->getSoftwareFormat(&r_samplerate, NULL, &r_channels, NULL, NULL, &r_bits); + mSystem->getDriverInfo(0, r_name, 255, 0); + mSystem->getSpeakerMode(&r_speaker_mode); + std::string speaker_mode_str = "unknown"; + switch(r_speaker_mode) + { + #define SPEAKER_MODE_CASE(MODE) case MODE: speaker_mode_str = #MODE; break; + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_RAW) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MONO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_STEREO) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_QUAD) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SURROUND) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_5POINT1) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_7POINT1) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_SRS5_1_MATRIX) + SPEAKER_MODE_CASE(FMOD_SPEAKERMODE_MYEARS) + default:; + #undef SPEAKER_MODE_CASE + } + + r_name[255] = '\0'; + int latency = 1000 * r_bufferlength * r_numbuffers / r_samplerate; + + LL_INFOS("AppInit") << "FMOD device: "<< r_name << "\n" + << "Output mode: "<< speaker_mode_str << "\n" + << "FMOD Ex parameters: " << r_samplerate << " Hz * " << r_channels << " * " <getVersion(&version), "FMOD::System::getVersion")) + { + return llformat("FMOD Ex %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF); + } } + return "FMODEx"; } -void LLAudioEngine_FMOD::allocateListener(void) +void LLAudioEngine_FMODEX::allocateListener(void) { - mListenerp = (LLListener *) new LLListener_FMOD(); + mListenerp = (LLListener *) new LLListener_FMODEX(mSystem); if (!mListenerp) { llwarns << "Listener creation failed" << llendl; @@ -250,98 +407,72 @@ void LLAudioEngine_FMOD::allocateListener(void) } -void LLAudioEngine_FMOD::shutdown() +void LLAudioEngine_FMODEX::shutdown() { - if (mWindDSP) - { - FSOUND_DSP_SetActive(mWindDSP,false); - FSOUND_DSP_Free(mWindDSP); - } - stopInternetStream(); + llinfos << "About to LLAudioEngine::shutdown()" << llendl; LLAudioEngine::shutdown(); - llinfos << "LLAudioEngine_FMOD::shutdown() closing FMOD" << llendl; - FSOUND_Close(); - llinfos << "LLAudioEngine_FMOD::shutdown() done closing FMOD" << llendl; + llinfos << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl; + mSystem->close(); + mSystem->release(); + llinfos << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl; delete mListenerp; mListenerp = NULL; } -LLAudioBuffer * LLAudioEngine_FMOD::createBuffer() +LLAudioBuffer * LLAudioEngine_FMODEX::createBuffer() { - return new LLAudioBufferFMOD(); + return new LLAudioBufferFMODEX(mSystem); } -LLAudioChannel * LLAudioEngine_FMOD::createChannel() +LLAudioChannel * LLAudioEngine_FMODEX::createChannel() { - return new LLAudioChannelFMOD(); + return new LLAudioChannelFMODEX(mSystem); } - -bool LLAudioEngine_FMOD::initWind() +bool LLAudioEngine_FMODEX::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(FSOUND_GetOutputRate()); - } - else - { - LL_WARNS("AppInit") << "Incompatible FMOD mixer type, wind noise disabled" << LL_ENDL; - } - } - mNextWindUpdate = 0.0; - if (mWindGen && !mWindDSP) + if (!mWindDSP) { - mWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, mWindGen); + 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.channels=2; + dspdesc.read = &windCallback; //Assign callback. + if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP")) + return false; + + if(mWindGen) + delete mWindGen; + + float frequency = 44100; + mWindDSP->getDefaults(&frequency,0,0,0); + mWindGen = new LLWindGen((U32)frequency); + mWindDSP->setUserData((void*)mWindGen); } + if (mWindDSP) { - FSOUND_DSP_SetActive(mWindDSP, true); + mSystem->playDSP(FMOD_CHANNEL_FREE, mWindDSP, false, 0); return true; } - return false; } -void LLAudioEngine_FMOD::cleanupWind() +void LLAudioEngine_FMODEX::cleanupWind() { if (mWindDSP) { - FSOUND_DSP_SetActive(mWindDSP, false); - FSOUND_DSP_Free(mWindDSP); + mWindDSP->remove(); + mWindDSP->release(); mWindDSP = NULL; } @@ -351,7 +482,7 @@ void LLAudioEngine_FMOD::cleanupWind() //----------------------------------------------------------------------- -void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_water) +void LLAudioEngine_FMODEX::updateWind(LLVector3 wind_vec, F32 camera_height_above_water) { LLVector3 wind_pos; F64 pitch; @@ -382,59 +513,8 @@ void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_ } } -/* //----------------------------------------------------------------------- -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) +void LLAudioEngine_FMODEX::setInternalGain(F32 gain) { if (!mInited) { @@ -442,7 +522,11 @@ void LLAudioEngine_FMOD::setInternalGain(F32 gain) } gain = llclamp( gain, 0.0f, 1.0f ); - FSOUND_SetSFXMasterVolume( llround( 255.0f * gain ) ); + + FMOD::ChannelGroup *master_group; + mSystem->getMasterChannelGroup(&master_group); + + master_group->setVolume(gain); LLStreamingAudioInterface *saimpl = getStreamingAudioImpl(); if ( saimpl ) @@ -454,43 +538,46 @@ void LLAudioEngine_FMOD::setInternalGain(F32 gain) } // -// LLAudioChannelFMOD implementation +// LLAudioChannelFMODEX implementation // -LLAudioChannelFMOD::LLAudioChannelFMOD() : LLAudioChannel(), mChannelID(0), mLastSamplePos(0) +LLAudioChannelFMODEX::LLAudioChannelFMODEX(FMOD::System *system) : LLAudioChannel(), mSystemp(system), mChannelp(NULL), mLastSamplePos(0) { } -LLAudioChannelFMOD::~LLAudioChannelFMOD() +LLAudioChannelFMODEX::~LLAudioChannelFMODEX() { cleanup(); } - -bool LLAudioChannelFMOD::updateBuffer() +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. - LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentSourcep->getCurrentBuffer(); + LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentSourcep->getCurrentBuffer(); // Grab the FMOD sample associated with the buffer - FSOUND_SAMPLE *samplep = bufferp->getSample(); - if (!samplep) + FMOD::Sound *soundp = bufferp->getSound(); + if (!soundp) { - // This is bad, there should ALWAYS be a sample associated with a legit + // This is bad, there should ALWAYS be a sound associated with a legit // buffer. - llerrs << "No FMOD sample!" << llendl; + llerrs << "No FMOD sound!" << 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); + if(!mChannelp) + { + FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp); + Check_FMOD_Error(result, "FMOD::System::playSound"); + } //llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl; } @@ -499,32 +586,34 @@ bool LLAudioChannelFMOD::updateBuffer() if (mCurrentSourcep) { // SJB: warnings can spam and hurt framerate, disabling - if (!FSOUND_SetVolume(mChannelID, llround(getSecondaryGain() * mCurrentSourcep->getGain() * 255.0f))) + FMOD_RESULT result; + + result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain()); + //Check_FMOD_Error(result, "FMOD::Channel::setVolume"); + + result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); + /*if(Check_FMOD_Error(result, "FMOD::Channel::setMode")) { -// 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; - } + S32 index; + mChannelp->getIndex(&index); + llwarns << "Channel " << index << "Source ID: " << mCurrentSourcep->getID() + << " at " << mCurrentSourcep->getPositionGlobal() << llendl; + }*/ } return true; } -void LLAudioChannelFMOD::update3DPosition() +void LLAudioChannelFMODEX::update3DPosition() { - if (!mChannelID) + if (!mChannelp) { // We're not actually a live channel (i.e., we're not playing back anything) return; } - LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentBufferp; + LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentBufferp; if (!bufferp) { // We don't have a buffer associated with us (should really have been picked up @@ -535,26 +624,24 @@ void LLAudioChannelFMOD::update3DPosition() if (mCurrentSourcep->isAmbient()) { // Ambient sound, don't need to do any positional updates. - bufferp->set3DMode(false); + set3DMode(false); } else { // Localized sound. Update the position and velocity of the sound. - bufferp->set3DMode(true); + 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; - } + FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV); + Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes"); } } -void LLAudioChannelFMOD::updateLoop() +void LLAudioChannelFMODEX::updateLoop() { - if (!mChannelID) + if (!mChannelp) { // May want to clear up the loop/sample counters. return; @@ -565,7 +652,9 @@ void LLAudioChannelFMOD::updateLoop() // sample position looks like it's going backwards. Not reliable; may // yield false negatives. // - U32 cur_pos = FSOUND_GetCurrentPosition(mChannelID); + U32 cur_pos; + mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES); + if (cur_pos < (U32)mLastSamplePos) { mLoopedThisFrame = true; @@ -574,97 +663,97 @@ void LLAudioChannelFMOD::updateLoop() } -void LLAudioChannelFMOD::cleanup() +void LLAudioChannelFMODEX::cleanup() { - if (!mChannelID) + if (!mChannelp) { - //llinfos << "Aborting cleanup with no channelID." << llendl; + //llinfos << "Aborting cleanup with no channel handle." << llendl; return; } //llinfos << "Cleaning up channel: " << mChannelID << llendl; - if (!FSOUND_StopSound(mChannelID)) - { - LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } + Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); mCurrentBufferp = NULL; - mChannelID = 0; + mChannelp = NULL; } -void LLAudioChannelFMOD::play() +void LLAudioChannelFMODEX::play() { - if (!mChannelID) + if (!mChannelp) { - llwarns << "Playing without a channelID, aborting" << llendl; + llwarns << "Playing without a channel handle, aborting" << llendl; return; } - if (!FSOUND_SetPaused(mChannelID, false)) - { - llwarns << "LLAudioChannelFMOD::play error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; - } + Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::pause"); + getSource()->setPlayedOnce(true); + + if(LLAudioEngine_FMODEX::mChannelGroups[getSource()->getType()]) + mChannelp->setChannelGroup(LLAudioEngine_FMODEX::mChannelGroups[getSource()->getType()]); } -void LLAudioChannelFMOD::playSynced(LLAudioChannel *channelp) +void LLAudioChannelFMODEX::playSynced(LLAudioChannel *channelp) { - LLAudioChannelFMOD *fmod_channelp = (LLAudioChannelFMOD*)channelp; - if (!(fmod_channelp->mChannelID && mChannelID)) + LLAudioChannelFMODEX *fmod_channelp = (LLAudioChannelFMODEX*)channelp; + if (!(fmod_channelp->mChannelp && mChannelp)) { // Don't have channels allocated to both the master and the slave return; } - U32 position = FSOUND_GetCurrentPosition(fmod_channelp->mChannelID) % mCurrentBufferp->getLength(); + 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 - if (!FSOUND_SetCurrentPosition(mChannelID, position)) - { - llwarns << "LLAudioChannelFMOD::playSynced unable to set current position" << llendl; - } + Check_FMOD_Error(mChannelp->setPosition(cur_pos,FMOD_TIMEUNIT_PCMBYTES),"Unable to set current position"); // Start us playing play(); } -bool LLAudioChannelFMOD::isPlaying() +bool LLAudioChannelFMODEX::isPlaying() { - if (!mChannelID) + if (!mChannelp) { return false; } - return FSOUND_IsPlaying(mChannelID) && (!FSOUND_GetPaused(mChannelID)); + bool paused, playing; + mChannelp->getPaused(&paused); + mChannelp->isPlaying(&playing); + return !paused && playing; } - // -// LLAudioBufferFMOD implementation +// LLAudioChannelFMODEX implementation // -LLAudioBufferFMOD::LLAudioBufferFMOD() +LLAudioBufferFMODEX::LLAudioBufferFMODEX(FMOD::System *system) : mSystemp(system), mSoundp(NULL) { - mSamplep = NULL; } -LLAudioBufferFMOD::~LLAudioBufferFMOD() +LLAudioBufferFMODEX::~LLAudioBufferFMODEX() { - if (mSamplep) + if(mSoundp) { - // Clean up the associated FMOD sample if it exists. - FSOUND_Sample_Free(mSamplep); - mSamplep = NULL; + mSoundp->release(); + mSoundp = NULL; } } -bool LLAudioBufferFMOD::loadWAV(const std::string& filename) +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. @@ -680,45 +769,29 @@ bool LLAudioBufferFMOD::loadWAV(const std::string& filename) return false; } - if (mSamplep) + if (mSoundp) { // If there's already something loaded in this buffer, clean it up. - FSOUND_Sample_Free(mSamplep); - mSamplep = NULL; + mSoundp->release(); + mSoundp = NULL; } + FMOD_MODE base_mode = FMOD_LOOP_NORMAL | FMOD_SOFTWARE; + FMOD_CREATESOUNDEXINFO exinfo; + memset(&exinfo,0,sizeof(exinfo)); + exinfo.cbsize = sizeof(exinfo); + exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV; //Hint to speed up loading. // 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); - } + FMOD_RESULT result = getSystem()->createSound((const char*)utf8str_to_utf16str(filename).c_str(), base_mode | FMOD_UNICODE, &exinfo, &mSoundp); #else - mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, filename.c_str(), FSOUND_LOOP_NORMAL, 0, 0); + FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp); #endif - if (!mSamplep) + if (result != FMOD_OK) { // We failed to load the file for some reason. - llwarns << "Could not load data '" << filename << "': " - << FMOD_ErrorString(FSOUND_GetError()) << llendl; + llwarns << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl; // // If we EVER want to load wav files provided by end users, we need @@ -734,49 +807,50 @@ bool LLAudioBufferFMOD::loadWAV(const std::string& filename) } -U32 LLAudioBufferFMOD::getLength() +U32 LLAudioBufferFMODEX::getLength() { - if (!mSamplep) + if (!mSoundp) { return 0; } - return FSOUND_Sample_GetLength(mSamplep); + U32 length; + mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES); + return length; } -void LLAudioBufferFMOD::set3DMode(bool use3d) +void LLAudioChannelFMODEX::set3DMode(bool use3d) { - U16 current_mode = FSOUND_Sample_GetMode(mSamplep); - - if (use3d) + FMOD_MODE current_mode; + if(mChannelp->getMode(¤t_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) { - 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; - } + mChannelp->setMode(new_mode); } } -void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata) +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 *windgen = - (LLWindGen *)userdata; - - newbuffer = windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT *)newbuffer, length); + LLWindGen *windgen; + FMOD::DSP *thisdsp = (FMOD::DSP *)dsp_state->instance; - return newbuffer; + 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; }