From 90d2dce07aab45e7f2b023e868878ccac7fe054d Mon Sep 17 00:00:00 2001 From: Drake Arconis Date: Sun, 10 Aug 2014 17:41:10 -0400 Subject: [PATCH] Add FMOD Studio support --- indra/cmake/CMakeLists.txt | 2 +- indra/cmake/CopyWinLibs.cmake | 42 +- indra/cmake/FMOD.cmake | 52 - indra/cmake/FMODEX.cmake | 4 + indra/cmake/FMODSTUDIO.cmake | 83 ++ indra/llaudio/CMakeLists.txt | 48 +- indra/llaudio/llaudioengine_fmod.cpp | 789 ------------- indra/llaudio/llaudioengine_fmod.h | 124 -- indra/llaudio/llaudioengine_fmodstudio.cpp | 1016 +++++++++++++++++ indra/llaudio/llaudioengine_fmodstudio.h | 139 +++ indra/llaudio/lllistener_fmod.cpp | 125 -- indra/llaudio/lllistener_fmod.h | 58 - indra/llaudio/lllistener_fmodstudio.cpp | 141 +++ indra/llaudio/lllistener_fmodstudio.h | 71 ++ indra/llaudio/llstreamingaudio_fmod.cpp | 385 ------- indra/llaudio/llstreamingaudio_fmod.h | 68 -- indra/llaudio/llstreamingaudio_fmodstudio.cpp | 505 ++++++++ indra/llaudio/llstreamingaudio_fmodstudio.h | 87 ++ indra/llmath/llmath.h | 2 +- indra/newview/CMakeLists.txt | 95 +- indra/newview/llstartup.cpp | 31 +- 21 files changed, 2157 insertions(+), 1710 deletions(-) delete mode 100644 indra/cmake/FMOD.cmake create mode 100644 indra/cmake/FMODSTUDIO.cmake delete mode 100644 indra/llaudio/llaudioengine_fmod.cpp delete mode 100644 indra/llaudio/llaudioengine_fmod.h create mode 100644 indra/llaudio/llaudioengine_fmodstudio.cpp create mode 100644 indra/llaudio/llaudioengine_fmodstudio.h delete mode 100644 indra/llaudio/lllistener_fmod.cpp delete mode 100644 indra/llaudio/lllistener_fmod.h create mode 100644 indra/llaudio/lllistener_fmodstudio.cpp create mode 100644 indra/llaudio/lllistener_fmodstudio.h delete mode 100644 indra/llaudio/llstreamingaudio_fmod.cpp delete mode 100644 indra/llaudio/llstreamingaudio_fmod.h create mode 100644 indra/llaudio/llstreamingaudio_fmodstudio.cpp create mode 100644 indra/llaudio/llstreamingaudio_fmodstudio.h diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index d2e9f2249..c6d716fed 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -29,8 +29,8 @@ set(cmake_SOURCE_FILES ELFIO.cmake EXPAT.cmake ExamplePlugin.cmake - FMOD.cmake FMODEX.cmake + FMODSTUDIO.cmake FindAPR.cmake FindBerkeleyDB.cmake FindCARes.cmake diff --git a/indra/cmake/CopyWinLibs.cmake b/indra/cmake/CopyWinLibs.cmake index 17d5eab02..fb5093f1b 100644 --- a/indra/cmake/CopyWinLibs.cmake +++ b/indra/cmake/CopyWinLibs.cmake @@ -233,6 +233,29 @@ if(WORD_SIZE EQUAL 32) ) endif(WORD_SIZE EQUAL 32) +if(FMODSTUDIO) + if (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod.dll") + else (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod64.dll") + endif (WORD_SIZE EQUAL 32) + + find_path(FMODSTUDIO_BINARY_DIR "${fmodstudio_dll_file}" + "${release_src_dir}" + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_SDK_DIR}" + NO_DEFAULT_PATH + ) + + if(FMODSTUDIO_BINARY_DIR) + copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Release" out_targets "${fmodstudio_dll_file}") + set(all_targets ${all_targets} ${out_targets}) + copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/RelWithDebInfo" out_targets "${fmodstudio_dll_file}") + set(all_targets ${all_targets} ${out_targets}) + copy_if_different("${FMODSTUDIO_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/Debug" out_targets "${fmodstudio_dll_file}") + set(all_targets ${all_targets} ${out_targets}) + endif(FMODSTUDIO_BINARY_DIR) +endif(FMODSTUDIO) if(FMODEX) if (WORD_SIZE EQUAL 32) @@ -258,25 +281,6 @@ if(FMODEX) 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}/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} "${CMAKE_CURRENT_BINARY_DIR}/Release" diff --git a/indra/cmake/FMOD.cmake b/indra/cmake/FMOD.cmake deleted file mode 100644 index a2d9245ff..000000000 --- a/indra/cmake/FMOD.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# -*- cmake -*- - -include(Linking) - -if(INSTALL_PROPRIETARY) - include(Prebuilt) - use_prebuilt_binary(fmod) -endif(INSTALL_PROPRIETARY) - -find_library(FMOD_LIBRARY - NAMES fmod fmodvc fmod-3.75 - PATHS - optimized ${ARCH_PREBUILT_DIRS_RELEASE} - debug ${ARCH_PREBUILT_DIRS_DEBUG} - ) - -if (NOT FMOD_LIBRARY) - set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.") - if (FMOD_SDK_DIR) - find_library(FMOD_LIBRARY - NAMES fmodvc fmod-3.75 fmod - PATHS - ${FMOD_SDK_DIR}/api/lib - ${FMOD_SDK_DIR}/api - ${FMOD_SDK_DIR}/lib - ${FMOD_SDK_DIR} - ) - endif (FMOD_SDK_DIR) -endif (NOT FMOD_LIBRARY) - -find_path(FMOD_INCLUDE_DIR fmod.h - ${LIBS_PREBUILT_DIR}/include - ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include - ${FMOD_SDK_DIR}/api/inc - ${FMOD_SDK_DIR}/inc - ${FMOD_SDK_DIR} - ) - -if (FMOD_LIBRARY AND FMOD_INCLUDE_DIR) - set(FMOD ON CACHE BOOL "Use closed source FMOD sound library.") -else (FMOD_LIBRARY AND FMOD_INCLUDE_DIR) - set(FMOD_LIBRARY "") - set(FMOD_INCLUDE_DIR "") - if (FMOD) - message(STATUS "No support for FMOD audio (need to set FMOD_SDK_DIR?)") - endif (FMOD) - set(FMOD OFF CACHE BOOL "Use closed source FMOD sound library.") -endif (FMOD_LIBRARY AND FMOD_INCLUDE_DIR) - -if (FMOD) - message(STATUS "Building with FMOD audio support") -endif (FMOD) diff --git a/indra/cmake/FMODEX.cmake b/indra/cmake/FMODEX.cmake index f3425f1b2..7a7bdc5ac 100644 --- a/indra/cmake/FMODEX.cmake +++ b/indra/cmake/FMODEX.cmake @@ -2,6 +2,10 @@ include(Linking) +if (FMODEX AND FMODSTUDIO) + message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) +endif (FMODEX AND FMODSTUDIO) + if (NOT FMODEX_LIBRARY) set(FMODEX_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.") if (FMODEX_SDK_DIR) diff --git a/indra/cmake/FMODSTUDIO.cmake b/indra/cmake/FMODSTUDIO.cmake new file mode 100644 index 000000000..b2afae207 --- /dev/null +++ b/indra/cmake/FMODSTUDIO.cmake @@ -0,0 +1,83 @@ +# -*- cmake -*- + +include(Linking) + +if (FMODEX AND FMODSTUDIO) + message( FATAL_ERROR "You can not enable two FMOD variants at the same time." ) +endif (FMODEX AND FMODSTUDIO) + +if (NOT FMODSTUDIO_LIBRARY) + set(FMODSTUDIO_SDK_DIR CACHE PATH "Path to the FMOD Ex SDK.") + if (FMODSTUDIO_SDK_DIR) + if(WORD_SIZE EQUAL 32) + find_library(FMODSTUDIO_LIBRARY + fmod_vc fmodL_vc fmod fmodL + PATHS + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_SDK_DIR}/api/lowlevel" + "${FMODSTUDIO_SDK_DIR}" + ) + elseif(WORD_SIZE EQUAL 64) + find_library(FMODSTUDIO_LIBRARY + fmod64_vc fmodL64_vc fmod64 fmodL64 + PATHS + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_SDK_DIR}/api/lowlevel" + "${FMODSTUDIO_SDK_DIR}" + ) + endif(WORD_SIZE EQUAL 32) + endif(FMODSTUDIO_SDK_DIR) + if(WINDOWS AND NOT FMODSTUDIO_SDK_DIR) + GET_FILENAME_COMPONENT(FMODSTUDIO_PROG_DIR [HKEY_CURRENT_USER\\Software\\FMOD\ Studio\ API\ Windows] ABSOLUTE CACHE) + if(WORD_SIZE EQUAL 32) + find_library(FMODSTUDIO_LIBRARY + fmod_vc fmodL_vc + PATHS + "${FMODSTUDIO_PROG_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_PROG_DIR}/api/lowlevel" + "${FMODSTUDIO_PROG_DIR}" + ) + else(WORD_SIZE EQUAL 32) + find_library(FMODSTUDIO_LIBRARY + fmod64_vc fmodL64_vc + PATHS + "${FMODSTUDIO_PROG_DIR}/api/lowlevel/lib" + "${FMODSTUDIO_PROG_DIR}/api/lowlevel" + "${FMODSTUDIO_PROG_DIR}" + ) + endif(WORD_SIZE EQUAL 32) + if(FMODSTUDIO_LIBRARY) + message(STATUS "Found fmodstudio in ${FMODSTUDIO_PROG_DIR}") + set(FMODSTUDIO_SDK_DIR "${FMODSTUDIO_PROG_DIR}") + set(FMODSTUDIO_SDK_DIR "${FMODSTUDIO_PROG_DIR}" CACHE PATH "Path to the FMOD Studio SDK." FORCE) + endif(FMODSTUDIO_LIBRARY) + endif(WINDOWS AND NOT FMODSTUDIO_SDK_DIR) +endif (NOT FMODSTUDIO_LIBRARY) + +find_path(FMODSTUDIO_INCLUDE_DIR fmod.hpp + "${LIBS_PREBUILT_DIR}/include/fmodstudio" + "${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/fmodstudio" + "${FMODSTUDIO_SDK_DIR}/api/lowlevel/inc" + "${FMODSTUDIO_SDK_DIR}" + ) + +if(DARWIN) + set(FMODSTUDIO_ORIG_LIBRARY "${FMODSTUDIO_LIBRARY}") + set(FMODSTUDIO_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/libfmod.dylib") +endif(DARWIN) + +if (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) + set(FMODSTUDIO ON CACHE BOOL "Use closed source FMOD Studio sound library.") +else (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) + set(FMODSTUDIO_LIBRARY "") + set(FMODSTUDIO_INCLUDE_DIR "") + if (FMODSTUDIO) + message(STATUS "No support for FMOD Studio audio (need to set FMODSTUDIO_SDK_DIR?)") + endif (FMODSTUDIO) + set(FMODSTUDIO OFF CACHE BOOL "Use closed source FMOD Studio sound library.") + set(FMODSTUDIO OFF) +endif (FMODSTUDIO_LIBRARY AND FMODSTUDIO_INCLUDE_DIR) + +if (FMODSTUDIO) + message(STATUS "Building with FMOD Studio audio support") +endif (FMODSTUDIO) diff --git a/indra/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 22d814e03..943bd9f03 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -5,25 +5,25 @@ project(llaudio) include(00-Common) include(Audio) include(LLAudio) +if (FMODSTUDIO) + include(FMODSTUDIO) +endif (FMODSTUDIO) if (FMODEX) include(FMODEX) - set(FMOD OFF) endif (FMODEX) -if (NOT FMODEX) - include(FMOD) -endif (NOT FMODEX) include(OPENAL) include(LLCommon) include(LLMath) include(LLMessage) include(LLVFS) +if (FMODSTUDIO) + include_directories(${FMODSTUDIO_INCLUDE_DIR}) +endif(FMODSTUDIO) + if (FMODEX) include_directories(${FMODEX_INCLUDE_DIR}) endif(FMODEX) -if(FMOD) - include_directories(${FMOD_INCLUDE_DIR}) -endif (FMOD) include_directories( ${LLAUDIO_INCLUDE_DIRS} @@ -58,6 +58,20 @@ set(llaudio_HEADER_FILES llwindgen.h ) +if (FMODSTUDIO) + list(APPEND llaudio_SOURCE_FILES + llaudioengine_fmodstudio.cpp + lllistener_fmodstudio.cpp + llstreamingaudio_fmodstudio.cpp + ) + + list(APPEND llaudio_HEADER_FILES + llaudioengine_fmodstudio.h + lllistener_fmodstudio.h + llstreamingaudio_fmodstudio.h + ) +endif (FMODSTUDIO) + if (FMODEX) list(APPEND llaudio_SOURCE_FILES llaudioengine_fmodex.cpp @@ -71,26 +85,6 @@ if (FMODEX) llstreamingaudio_fmodex.h ) endif (FMODEX) -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 diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp deleted file mode 100644 index 01beaa9d5..000000000 --- a/indra/llaudio/llaudioengine_fmod.cpp +++ /dev/null @@ -1,789 +0,0 @@ -/** - * @file audioengine_fmod.cpp - * @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * 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. - * - * 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. - * - * 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 - * $/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; - fmod_flags |= FSOUND_INIT_DSOUND_HRTF_FULL; -#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(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() -{ - LLAudioChannel::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; - } - - mChannelID = 0; - mLastSamplePos = 0; -} - - -void LLAudioChannelFMOD::play() -{ - if (!mChannelID) - { - llwarns << "Playing without a channelID, aborting" << llendl; - return; - } - - if(!FSOUND_IsPaused(mChannelID)) - { - FSOUND_SetPaused(mChannelID, true); - FSOUND_SetCurrentPosition(mChannelID, 0); - } - - 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() : LLAudioBuffer() -{ - 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 *windgen = - (LLWindGen *)userdata; - - newbuffer = windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT *)newbuffer, length); - - return newbuffer; -} diff --git a/indra/llaudio/llaudioengine_fmod.h b/indra/llaudio/llaudioengine_fmod.h deleted file mode 100644 index 4582a5d57..000000000 --- a/indra/llaudio/llaudioengine_fmod.h +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @file audioengine_fmod.h - * @brief Definition of LLAudioEngine class abstracting the audio - * support as a FMOD 3D implementation - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * 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. - * - * 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. - * - * 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 - * $/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 *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 diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp new file mode 100644 index 000000000..9b5ccebc0 --- /dev/null +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -0,0 +1,1016 @@ +/** + * @file audioengine_FMODSTUDIO.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_fmodstudio.h" + +#include "llaudioengine_fmodstudio.h" +#include "lllistener_fmodstudio.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 fmod.dll not cause a fatal error. +#define WIN32_LEAN_AND_MEAN +#include "windows.h" +#include +#pragma comment(lib, "delayimp.lib") + +bool attemptDelayLoad() +{ + __try + { +#if defined(_WIN64) + if( FAILED( __HrLoadAllImportsForDll( "fmod64.dll" ) ) ) + return false; +#else + if( FAILED( __HrLoadAllImportsForDll( "fmod.dll" ) ) ) + return false; +#endif + } + __except( EXCEPTION_EXECUTE_HANDLER ) + { + return false; + } + return true; +} +#endif + +static bool sVerboseDebugging = false; + +FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); + +FMOD::ChannelGroup *LLAudioEngine_FMODSTUDIO::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0}; + +//This class is designed to keep track of all sound<->channel assocations. +//Used to verify validity of sound and channel pointers, as well as catch cases were sounds +//are released with active channels still attached. +class CFMODSoundChecks +{ + typedef std::map > active_sounds_t; + typedef std::set dead_sounds_t; + typedef std::map active_channels_t; + typedef std::map dead_channels_t; + + active_sounds_t mActiveSounds; + dead_sounds_t mDeadSounds; + active_channels_t mActiveChannels; + dead_channels_t mDeadChannels; +public: + enum STATUS + { + ACTIVE, + DEAD, + UNKNOWN + }; + STATUS getPtrStatus(LLAudioChannel* channel) + { + if(!channel) + return UNKNOWN; + return getPtrStatus(dynamic_cast(channel)->mChannelp); + } + STATUS getPtrStatus(LLAudioBuffer* sound) + { + if(!sound) + return UNKNOWN; + return getPtrStatus(dynamic_cast(sound)->mSoundp); + } + STATUS getPtrStatus(FMOD::Channel* channel) + { + if(!channel) + return UNKNOWN; + else if(mActiveChannels.find(channel) != mActiveChannels.end()) + return ACTIVE; + else if(mDeadChannels.find(channel) != mDeadChannels.end()) + return DEAD; + return UNKNOWN; + } + STATUS getPtrStatus(FMOD::Sound* sound) + { + if(!sound) + return UNKNOWN; + if(mActiveSounds.find(sound) != mActiveSounds.end()) + return ACTIVE; + else if(mDeadSounds.find(sound) != mDeadSounds.end()) + return DEAD; + return UNKNOWN; + } + void addNewSound(FMOD::Sound* sound) + { + assertActiveState(sound,true,false); + + mDeadSounds.erase(sound); + mActiveSounds.insert(std::make_pair(sound,std::set())); + } + void removeSound(FMOD::Sound* sound) + { + assertActiveState(sound,true); + + active_sounds_t::const_iterator it = mActiveSounds.find(sound); + llassert(it != mActiveSounds.end()); + if(it != mActiveSounds.end()) + { + if(!it->second.empty()) + { + LL_WARNS("AudioImpl") << "Removing sound " << sound << " with attached channels: \n"; + for(std::set::iterator it2 = it->second.begin(); it2 != it->second.end();++it2) + { + switch(getPtrStatus(*it2)) + { + case ACTIVE: + LL_CONT << " Channel " << *it2 << " ACTIVE\n"; + break; + case DEAD: + LL_CONT << " Channel " << *it2 << " DEAD\n"; + break; + default: + LL_CONT << " Channel " << *it2 << " UNKNOWN\n"; + } + } + LL_CONT << LL_ENDL; + } + llassert(it->second.empty()); + mDeadSounds.insert(sound); + mActiveSounds.erase(sound); + } + } + void addNewChannelToSound(FMOD::Sound* sound,FMOD::Channel* channel) + { + assertActiveState(sound,true); + assertActiveState(channel,true,false); + + mActiveSounds[sound].insert(channel); + mActiveChannels.insert(std::make_pair(channel,sound)); + } + void removeChannel(FMOD::Channel* channel) + { + assertActiveState(channel,true); + + active_channels_t::const_iterator it = mActiveChannels.find(channel); + llassert(it != mActiveChannels.end()); + if(it != mActiveChannels.end()) + { +#ifdef SHOW_ASSERT + STATUS status = getPtrStatus(it->second); + llassert(status != DEAD); + llassert(status != UNKNOWN); +#endif + + active_sounds_t::iterator it2 = mActiveSounds.find(it->second); + llassert(it2 != mActiveSounds.end()); + if(it2 != mActiveSounds.end()) + { + it2->second.erase(channel); + } + mDeadChannels.insert(*it); + mActiveChannels.erase(channel); + } + } + + template + void assertActiveState(T ptr, bool try_log=false, bool active=true) + { +#ifndef SHOW_ASSERT + if(try_log && sVerboseDebugging) +#endif + { + CFMODSoundChecks::STATUS chan = getPtrStatus(ptr); + if(try_log && sVerboseDebugging) + { + if(active) + { + if(chan == CFMODSoundChecks::DEAD) + LL_WARNS("AudioImpl") << __FUNCTION__ << ": Using unexpectedly dead " << typeid(T*).name() << " " << ptr << LL_ENDL; + else if(chan == CFMODSoundChecks::UNKNOWN) + LL_WARNS("AudioImpl") << __FUNCTION__ << ": Using unexpectedly unknown " << typeid(T*).name() << " " << ptr << LL_ENDL; + } + else if(chan == CFMODSoundChecks::ACTIVE) + LL_WARNS("AudioImpl") << __FUNCTION__ << ": Using unexpectedly active " << typeid(T*).name() << " " << ptr << LL_ENDL; + } + llassert( active == (chan == CFMODSoundChecks::ACTIVE) ); + } + } +} gSoundCheck; + +LLAudioEngine_FMODSTUDIO::LLAudioEngine_FMODSTUDIO(bool enable_profiler, bool verbose_debugging) +{ + sVerboseDebugging = verbose_debugging; + mInited = false; + mWindGen = NULL; + mWindDSP = NULL; + mSystem = NULL; + mEnableProfiler = enable_profiler; +} + + +LLAudioEngine_FMODSTUDIO::~LLAudioEngine_FMODSTUDIO() +{ +} + + +inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) +{ + if(result == FMOD_OK) + return false; + LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL; + return true; +} + +void* F_STDCALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr) +{ + if(type & FMOD_MEMORY_STREAM_DECODE) + { + LL_INFOS("AudioImpl") << "Decode buffer size: " << size << LL_ENDL; + } + else if(type & FMOD_MEMORY_STREAM_FILE) + { + LL_INFOS("AudioImpl") << "Stream buffer size: " << size << LL_ENDL; + } + 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; +} + +bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata) +{ + +#if LL_WINDOWS + if(!attemptDelayLoad()) + return false; +#endif + + U32 version; + FMOD_RESULT result; + + LL_DEBUGS("AppInit") << "LLAudioEngine_FMODSTUDIO::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_FMODSTUDIO::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 Studio version (" << version + << ")! You should be using FMOD Studio" << FMOD_VERSION << LL_ENDL; + } + + // 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_PROFILE_ENABLE; + 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_PULSEAUDIO")) /*Flawfinder: ignore*/ + { + 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") << "PulseAudio output initialized OKAY" << LL_ENDL; + audio_ok = true; + } + else + { + Check_FMOD_Error(result, "PulseAudio audio output FAILED to initialize"); + } + } + else + { + 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(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 + { + Check_FMOD_Error(result, "ALSA audio output FAILED to initialize"); + } + } + 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; + } + } + if (!audio_ok) + { + LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL; + return false; + } + + // 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 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 + result = mSystem->init( num_channels + 2, 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->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 2); + 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 Studio")) + 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_FMODSTUDIO(mSystem)); + + LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() FMOD Studio initialized correctly" << LL_ENDL; + + int r_numbuffers, r_samplerate; + unsigned int r_bufferlength; + char r_name[256]; + FMOD_SPEAKERMODE speaker_mode; + mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers); + mSystem->getSoftwareFormat(&r_samplerate, &speaker_mode, NULL); + mSystem->getDriverInfo(0, r_name, NULL, 255, NULL, NULL, &speaker_mode, NULL); + std::string speaker_mode_str = "unknown"; + switch(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) + default:; + #undef SPEAKER_MODE_CASE + } + + r_name[255] = '\0'; + int latency = 1000.0 * r_bufferlength * r_numbuffers /r_samplerate; + + LL_INFOS("AppInit") << "FMOD device: "<< r_name << "\n" + << "Output mode: "<< speaker_mode_str << "\n" + << "FMOD Studio parameters: " << r_samplerate << " Hz * " <<" bit\n" + << "\tbuffer " << r_bufferlength << " * " << r_numbuffers << " (" << latency <<"ms)" << LL_ENDL; + + mInited = true; + + return true; +} + + +std::string LLAudioEngine_FMODSTUDIO::getDriverName(bool verbose) +{ + llassert_always(mSystem); + if (verbose) + { + U32 version; + if(!Check_FMOD_Error(mSystem->getVersion(&version), "FMOD::System::getVersion")) + { + return llformat("FMOD Studio %1x.%02x.%02x", version >> 16, version >> 8 & 0x000000FF, version & 0x000000FF); + } + } + return "FMODEx"; +} + + +void LLAudioEngine_FMODSTUDIO::allocateListener(void) +{ + mListenerp = (LLListener *) new LLListener_FMODSTUDIO(mSystem); + if (!mListenerp) + { + LL_WARNS("AudioImpl") << "Listener creation failed" << LL_ENDL; + } +} + + +void LLAudioEngine_FMODSTUDIO::shutdown() +{ + stopInternetStream(); + + LL_INFOS("AudioImpl") << "About to LLAudioEngine::shutdown()" << LL_ENDL; + LLAudioEngine::shutdown(); + + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() closing FMOD Studio" << LL_ENDL; + if ( mSystem ) // speculative fix for MAINT-2657 + { + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() Requesting FMOD Studio system closure" << LL_ENDL; + mSystem->close(); + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() Requesting FMOD Studio system release" << LL_ENDL; + mSystem->release(); + } + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODSTUDIO::shutdown() done closing FMOD Studio" << LL_ENDL; + + delete mListenerp; + mListenerp = NULL; +} + + +LLAudioBuffer * LLAudioEngine_FMODSTUDIO::createBuffer() +{ + return new LLAudioBufferFMODSTUDIO(mSystem); +} + + +LLAudioChannel * LLAudioEngine_FMODSTUDIO::createChannel() +{ + return new LLAudioChannelFMODSTUDIO(mSystem); +} + +bool LLAudioEngine_FMODSTUDIO::initWind() +{ + //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. + // if(Check_FMOD_Error(mSystem->createDSP(&dspdesc, &mWindDSP), "FMOD::createDSP")) + // return false; + + // if(mWindGen) + // delete mWindGen; + // + // int frequency = 44100; + // mSystem->getSoftwareFormat(&frequency, NULL, NULL); + // mWindGen = new LLWindGen((U32)frequency); + // mWindDSP->setUserData((void*)mWindGen); + //} + + //if (mWindDSP) + //{ + // mSystem->playDSP(mWindDSP, NULL, false, 0); + // return true; + //} + return false; +} + + +void LLAudioEngine_FMODSTUDIO::cleanupWind() +{ + //if (mWindDSP) + //{ + // mWindDSP->remove(); + // mWindDSP->release(); + // mWindDSP = NULL; + //} + + //delete mWindGen; + //mWindGen = NULL; +} + + +//----------------------------------------------------------------------- +void LLAudioEngine_FMODSTUDIO::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_FMODSTUDIO::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()); + } +} + +// +// LLAudioChannelFMODSTUDIO implementation +// + +LLAudioChannelFMODSTUDIO::LLAudioChannelFMODSTUDIO(FMOD::System *system) : LLAudioChannel(), mSystemp(system), mChannelp(NULL), mLastSamplePos(0) +{ +} + + +LLAudioChannelFMODSTUDIO::~LLAudioChannelFMODSTUDIO() +{ + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Destructing Audio Channel. mChannelp = " << mChannelp << LL_ENDL; + + cleanup(); +} + +static FMOD_RESULT F_CALLBACK channel_callback(FMOD_CHANNELCONTROL *channel, FMOD_CHANNELCONTROL_TYPE controltype, FMOD_CHANNELCONTROL_CALLBACK_TYPE callbacktype, void *commanddata1, void *commanddata2) +{ + if (controltype == FMOD_CHANNELCONTROL_CHANNEL && + callbacktype == FMOD_CHANNELCONTROL_CALLBACK_END) + { + FMOD::Channel* chan = reinterpret_cast(channel); + LLAudioChannelFMODSTUDIO* audio_channel = NULL; + chan->getUserData((void**)&audio_channel); + if(audio_channel) + { + audio_channel->onRelease(); + } + } + return FMOD_OK; +} + +void LLAudioChannelFMODSTUDIO::onRelease() +{ + llassert(mChannelp); + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Fmod signaled channel release for channel " << mChannelp << LL_ENDL; + gSoundCheck.removeChannel(mChannelp); + mChannelp = NULL; //Null out channel here so cleanup doesn't try to redundantly stop it. + cleanup(); +} + +bool LLAudioChannelFMODSTUDIO::updateBuffer() +{ + if (LLAudioChannel::updateBuffer()) + { + // Base class update returned true, which means the channel buffer was changed, and not is null. + + // Grab the FMOD sample associated with the buffer + FMOD::Sound *soundp = ((LLAudioBufferFMODSTUDIO*)mCurrentBufferp)->getSound(); + if (!soundp) + { + // This is bad, there should ALWAYS be a sound associated with a legit + // buffer. + LL_ERRS("AudioImpl") << "No FMOD sound!" << LL_ENDL; + return false; + } + + // Actually play the sound. Start it off paused so we can do all the necessary + // setup. + if(!mChannelp) + { + FMOD_RESULT result = getSystem()->playSound(soundp, NULL, true, &mChannelp); + Check_FMOD_Error(result, "FMOD::System::playSound"); + if(result == FMOD_OK) + { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Created channel " << mChannelp << " for sound " << soundp << LL_ENDL; + + gSoundCheck.addNewChannelToSound(soundp,mChannelp); + mChannelp->setCallback(&channel_callback); + mChannelp->setUserData(this); + } + } + } + + // If we have a source for the channel, we need to update its gain. + if (mCurrentSourcep && mChannelp) + { + FMOD_RESULT result; + + gSoundCheck.assertActiveState(this); + result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain()); + Check_FMOD_Error(result, "FMOD::Channel::setVolume"); + result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); + Check_FMOD_Error(result, "FMOD::Channel::setMode"); + } + + return true; +} + + +void LLAudioChannelFMODSTUDIO::update3DPosition() +{ + if (!mChannelp) + { + // We're not actually a live channel (i.e., we're not playing back anything) + return; + } + + LLAudioBufferFMODSTUDIO *bufferp = (LLAudioBufferFMODSTUDIO *)mCurrentBufferp; + if (!bufferp) + { + // We don't have a buffer associated with us (should really have been picked up + // by the above if. + return; + } + + gSoundCheck.assertActiveState(this); + + 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 LLAudioChannelFMODSTUDIO::updateLoop() +{ + if (!mChannelp) + { + // May want to clear up the loop/sample counters. + return; + } + + gSoundCheck.assertActiveState(this); + + // + // 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; + Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES),"FMOD::Channel::getPosition"); + + if (cur_pos < (U32)mLastSamplePos) + { + mLoopedThisFrame = true; + } + mLastSamplePos = cur_pos; +} + + +void LLAudioChannelFMODSTUDIO::cleanup() +{ + LLAudioChannel::cleanup(); + + if (!mChannelp) + { + llassert(mCurrentBufferp == NULL); + //LL_INFOS("AudioImpl") << "Aborting cleanup with no channel handle." << LL_ENDL; + return; + } + + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Stopping channel " << mChannelp << LL_ENDL; + + gSoundCheck.removeChannel(mChannelp); + mChannelp->setCallback(NULL); + Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); + + mChannelp = NULL; + mLastSamplePos = 0; +} + + +void LLAudioChannelFMODSTUDIO::play() +{ + if (!mChannelp) + { + LL_WARNS("AudioImpl") << "Playing without a channel handle, aborting" << LL_ENDL; + return; + } + + gSoundCheck.assertActiveState(this,true); + + bool paused=true; + Check_FMOD_Error(mChannelp->getPaused(&paused), "FMOD::Channel::getPaused"); + if(!paused) + { + Check_FMOD_Error(mChannelp->setPaused(true), "FMOD::Channel::setPaused"); + Check_FMOD_Error(mChannelp->setPosition(0,FMOD_TIMEUNIT_PCMBYTES), "FMOD::Channel::setPosition"); + } + Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::setPaused"); + + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Playing channel " << mChannelp << LL_ENDL; + + getSource()->setPlayedOnce(true); + + if(LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()]) + Check_FMOD_Error(mChannelp->setChannelGroup(LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()]),"FMOD::Channel::setChannelGroup"); +} + + +void LLAudioChannelFMODSTUDIO::playSynced(LLAudioChannel *channelp) +{ + LLAudioChannelFMODSTUDIO *fmod_channelp = (LLAudioChannelFMODSTUDIO*)channelp; + if (!(fmod_channelp->mChannelp && mChannelp)) + { + // Don't have channels allocated to both the master and the slave + return; + } + + gSoundCheck.assertActiveState(this,true); + + U32 cur_pos; + if(Check_FMOD_Error(fmod_channelp->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 LLAudioChannelFMODSTUDIO::isPlaying() +{ + if (!mChannelp) + { + return false; + } + + gSoundCheck.assertActiveState(this); + + bool paused, playing; + Check_FMOD_Error(mChannelp->getPaused(&paused),"FMOD::Channel::getPaused"); + Check_FMOD_Error(mChannelp->isPlaying(&playing),"FMOD::Channel::isPlaying"); + return !paused && playing; +} + + +// +// LLAudioChannelFMODSTUDIO implementation +// + + +LLAudioBufferFMODSTUDIO::LLAudioBufferFMODSTUDIO(FMOD::System *system) : LLAudioBuffer(), mSystemp(system), mSoundp(NULL) +{ +} + + +LLAudioBufferFMODSTUDIO::~LLAudioBufferFMODSTUDIO() +{ + if(mSoundp) + { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Release sound " << mSoundp << LL_ENDL; + + gSoundCheck.removeSound(mSoundp); + Check_FMOD_Error(mSoundp->release(),"FMOD::Sound::Release"); + mSoundp = NULL; + } +} + + +bool LLAudioBufferFMODSTUDIO::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) + { + gSoundCheck.removeSound(mSoundp); + // If there's already something loaded in this buffer, clean it up. + Check_FMOD_Error(mSoundp->release(),"FMOD::Sound::release"); + mSoundp = NULL; + } + + FMOD_MODE base_mode = FMOD_LOOP_NORMAL; + 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 + FMOD_RESULT result = getSystem()->createSound((const char*)utf8str_to_utf16str(filename).c_str(), base_mode | FMOD_UNICODE, &exinfo, &mSoundp); +#else + FMOD_RESULT result = getSystem()->createSound(filename.c_str(), base_mode, &exinfo, &mSoundp); +#endif + + if (result != FMOD_OK) + { + // We failed to load the file for some reason. + LL_WARNS("AudioImpl") << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << LL_ENDL; + + // + // 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; + } + + gSoundCheck.addNewSound(mSoundp); + + // Everything went well, return true + return true; +} + + +U32 LLAudioBufferFMODSTUDIO::getLength() +{ + if (!mSoundp) + { + return 0; + } + + gSoundCheck.assertActiveState(this); + U32 length; + Check_FMOD_Error(mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES),"FMOD::Sound::getLength"); + return length; +} + + +void LLAudioChannelFMODSTUDIO::set3DMode(bool use3d) +{ + gSoundCheck.assertActiveState(this); + + FMOD_MODE current_mode; + if(Check_FMOD_Error(mChannelp->getMode(¤t_mode),"FMOD::Channel::getMode")) + 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) + { + Check_FMOD_Error(mChannelp->setMode(new_mode),"FMOD::Channel::setMode"); + } +} + + +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; + 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_FMODSTUDIO::MIXBUFFERFORMAT *)newbuffer, length); + + return FMOD_OK; +} diff --git a/indra/llaudio/llaudioengine_fmodstudio.h b/indra/llaudio/llaudioengine_fmodstudio.h new file mode 100644 index 000000000..730a702a1 --- /dev/null +++ b/indra/llaudio/llaudioengine_fmodstudio.h @@ -0,0 +1,139 @@ +/** + * @file audioengine_FMODSTUDIO.h + * @brief Definition of LLAudioEngine class abstracting the audio + * support as a FMOD Studio 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_FMODSTUDIO_H +#define LL_AUDIOENGINE_FMODSTUDIO_H + +#include "llaudioengine.h" +#include "lllistener_fmodstudio.h" +#include "llwindgen.h" + +//Stubs +class LLAudioStreamManagerFMODSTUDIO; +namespace FMOD +{ + class System; + class Channel; + class ChannelGroup; + class Sound; + class DSP; +} + +//Interfaces +class LLAudioEngine_FMODSTUDIO : public LLAudioEngine +{ +public: + LLAudioEngine_FMODSTUDIO(bool enable_profiler, bool verbose_debugging); + virtual ~LLAudioEngine_FMODSTUDIO(); + + // 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; + + FMOD::System *getSystem() const {return mSystem;} +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; + + LLWindGen *mWindGen; + + FMOD::DSP *mWindDSP; + FMOD::System *mSystem; + bool mEnableProfiler; + +public: + static FMOD::ChannelGroup *mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT]; +}; + + +class LLAudioChannelFMODSTUDIO : public LLAudioChannel +{ +public: + LLAudioChannelFMODSTUDIO(FMOD::System *audioengine); + virtual ~LLAudioChannelFMODSTUDIO(); + void onRelease(); +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 *getSystem() const {return mSystemp;} + FMOD::System *mSystemp; + FMOD::Channel *mChannelp; + S32 mLastSamplePos; + + friend class CFMODSoundChecks; +}; + + +class LLAudioBufferFMODSTUDIO : public LLAudioBuffer +{ +public: + LLAudioBufferFMODSTUDIO(FMOD::System *audioengine); + virtual ~LLAudioBufferFMODSTUDIO(); + + /*virtual*/ bool loadWAV(const std::string& filename); + /*virtual*/ U32 getLength(); + friend class LLAudioChannelFMODSTUDIO; +protected: + FMOD::System *getSystem() const {return mSystemp;} + FMOD::System *mSystemp; + FMOD::Sound *getSound() const{ return mSoundp; } + FMOD::Sound *mSoundp; + + friend class CFMODSoundChecks; +}; + + +#endif // LL_AUDIOENGINE_FMODSTUDIO_H diff --git a/indra/llaudio/lllistener_fmod.cpp b/indra/llaudio/lllistener_fmod.cpp deleted file mode 100644 index 0138f4345..000000000 --- a/indra/llaudio/lllistener_fmod.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/** - * @file listener_fmod.cpp - * @brief implementation of LISTENER class abstracting the audio - * support as a FMOD 3D implementation (windows only) - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * 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. - * - * 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. - * - * 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 - * $/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; -} - - diff --git a/indra/llaudio/lllistener_fmod.h b/indra/llaudio/lllistener_fmod.h deleted file mode 100644 index 818da05d5..000000000 --- a/indra/llaudio/lllistener_fmod.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @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=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * 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. - * - * 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. - * - * 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 - * $/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 - - diff --git a/indra/llaudio/lllistener_fmodstudio.cpp b/indra/llaudio/lllistener_fmodstudio.cpp new file mode 100644 index 000000000..f6ee7908b --- /dev/null +++ b/indra/llaudio/lllistener_fmodstudio.cpp @@ -0,0 +1,141 @@ +/** + * @file listener_fmodstudio.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_fmodstudio.h" +#include "fmod.hpp" + +//----------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------- +LLListener_FMODSTUDIO::LLListener_FMODSTUDIO(FMOD::System *system) +{ + mSystem = system; + init(); +} + +//----------------------------------------------------------------------- +LLListener_FMODSTUDIO::~LLListener_FMODSTUDIO() +{ +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::init(void) +{ + // do inherited + LLListener::init(); + mDopplerFactor = 1.0f; + mRolloffFactor = 1.0f; +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::translate(LLVector3 offset) +{ + LLListener::translate(offset); + + mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV); +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::setPosition(LLVector3 pos) +{ + LLListener::setPosition(pos); + + mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV); +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::setVelocity(LLVector3 vel) +{ + LLListener::setVelocity(vel); + + mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, (FMOD_VECTOR*)mListenAt.mV, (FMOD_VECTOR*)mListenUp.mV); +} + +//----------------------------------------------------------------------- +void LLListener_FMODSTUDIO::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_FMODSTUDIO::commitDeferredChanges() +{ + mSystem->update(); +} + + +void LLListener_FMODSTUDIO::setRolloffFactor(F32 factor) +{ + //An internal FMODEx optimization skips 3D updates if there have not been changes to the 3D sound environment. + //Sadly, a change in rolloff is not accounted for, thus we must touch the listener properties as well. + //In short: Changing the position ticks a dirtyflag inside fmodstudio, which makes it not skip 3D processing next update call. + if(mRolloffFactor != factor) + { + LLVector3 pos = mVelocity - LLVector3(0.f,0.f,.1f); + mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)pos.mV, NULL, NULL, NULL); + mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mVelocity.mV, NULL, NULL, NULL); + } + mRolloffFactor = factor; + mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor); +} + + +F32 LLListener_FMODSTUDIO::getRolloffFactor() +{ + return mRolloffFactor; +} + + +void LLListener_FMODSTUDIO::setDopplerFactor(F32 factor) +{ + mDopplerFactor = factor; + mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor); +} + + +F32 LLListener_FMODSTUDIO::getDopplerFactor() +{ + return mDopplerFactor; +} + + diff --git a/indra/llaudio/lllistener_fmodstudio.h b/indra/llaudio/lllistener_fmodstudio.h new file mode 100644 index 000000000..516f4eae8 --- /dev/null +++ b/indra/llaudio/lllistener_fmodstudio.h @@ -0,0 +1,71 @@ +/** + * @file listener_fmodstudio.h + * @brief Description of LISTENER class abstracting the audio support + * as an FMOD Studio 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_FMODSTUDIO_H +#define LL_LISTENER_FMODSTUDIO_H + +#include "lllistener.h" + +//Stubs +namespace FMOD +{ + class System; +} + +//Interfaces +class LLListener_FMODSTUDIO : public LLListener +{ + public: + LLListener_FMODSTUDIO(FMOD::System *system); + virtual ~LLListener_FMODSTUDIO(); + 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 + + diff --git a/indra/llaudio/llstreamingaudio_fmod.cpp b/indra/llaudio/llstreamingaudio_fmod.cpp deleted file mode 100644 index 4036b8df6..000000000 --- a/indra/llaudio/llstreamingaudio_fmod.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/** - * @file streamingaudio_fmod.cpp - * @brief LLStreamingAudio_FMOD implementation - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * 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. - * - * 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. - * - * 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 - * $/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(); - - FSOUND_STREAM* getStream() { return mInternetStream; } -protected: - FSOUND_STREAM* mInternetStream; - bool mReady; - - std::string mInternetStreamURL; -}; - - - -//--------------------------------------------------------------------------- -// Internet Streaming -//--------------------------------------------------------------------------- -LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() : - mCurrentInternetStreamp(NULL), - mFMODInternetStreamChannel(-1), - 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. - 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. -} - -signed char F_CALLBACKAPI MetaDataCallback(char *name, char *value, void *userdata) -{ - std::string szName(name); - if(szName == "TITLE" || szName=="TIT2" || szName=="Title") - (*(LLSD*)userdata)["TITLE"] = value; - if(szName == "ARTIST" || szName=="TPE1" || szName =="WM/AlbumTitle") - (*(LLSD*)userdata)["ARTIST"] = value; - else - (*(LLSD*)userdata)[std::string(name)] = value; - return true; -} - -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; - if(mCurrentInternetStreamp->getStream()) - { - mMetaData = new LLSD; - } - } - else - { - llinfos << "Set internet stream to null" << llendl; - mURL.clear(); - } -} - - -void LLStreamingAudio_FMOD::update() -{ - // Kill dead internet streams, if possible - std::list::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); - if(mCurrentInternetStreamp->getStream() && mMetaData) - { - FSOUND_Stream_Net_SetMetadataCallback(mCurrentInternetStreamp->getStream(),&MetaDataCallback, mMetaData); - } - } - } - } - - 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 << "InternetStream - failed to open" << llendl; - stop(); - return; - case -4: - // connecting - break; - case -5: - // buffering - break; - } - -} - -void LLStreamingAudio_FMOD::stop() -{ - if(mMetaData) - { - if(mCurrentInternetStreamp && mCurrentInternetStreamp->getStream()) - FSOUND_Stream_Net_SetMetadataCallback(mCurrentInternetStreamp->getStream(),NULL,NULL); - delete mMetaData; - mMetaData = NULL; - } - 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 * 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; -} diff --git a/indra/llaudio/llstreamingaudio_fmod.h b/indra/llaudio/llstreamingaudio_fmod.h deleted file mode 100644 index 4bb65c54d..000000000 --- a/indra/llaudio/llstreamingaudio_fmod.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @file streamingaudio_fmod.h - * @author Tofu Linden - * @brief Definition of LLStreamingAudio_FMOD implementation - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * 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. - * - * 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. - * - * 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 - * $/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(); - - /*virtual*/ bool supportsMetaData(){return true;} - /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing. - /*virtual*/ bool supportsWaveData(){return false;} - /*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1){return false;} -private: - LLAudioStreamManagerFMOD *mCurrentInternetStreamp; - int mFMODInternetStreamChannel; - std::list mDeadStreams; - - std::string mURL; - F32 mGain; - - LLSD *mMetaData; -}; - - -#endif // LL_STREAMINGAUDIO_FMOD_H diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp new file mode 100644 index 000000000..a2ffe2108 --- /dev/null +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -0,0 +1,505 @@ +/** + * @file streamingaudio_fmodstudio.cpp + * @brief LLStreamingAudio_FMODSTUDIO 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_fmodstudio.h" + + +class LLAudioStreamManagerFMODSTUDIO +{ +public: + LLAudioStreamManagerFMODSTUDIO(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(unsigned int* percentbuffered=NULL, bool* starving=NULL, bool* diskbusy=NULL); +protected: + FMOD::System* mSystem; + FMOD::Channel* mStreamChannel; + FMOD::Sound* mInternetStream; + bool mReady; + + std::string mInternetStreamURL; +}; + + + +//--------------------------------------------------------------------------- +// Internet Streaming +//--------------------------------------------------------------------------- +LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(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. + const U32 buffer_seconds = 10; //sec + const U32 estimated_bitrate = 128; //kbit/sec + mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + + // 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_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO() +{ + // nothing interesting/safe to do. +} + + +void LLStreamingAudio_FMODSTUDIO::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()) + { + if(mDeadStreams.empty()) + { + llinfos << "Starting internet stream: " << url << llendl; + mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem,url); + mURL = url; + mMetaData = new LLSD; + } + else + { + llinfos << "Deferring stream load until buffer release: " << url << llendl; + mPendingURL = url; + } + } + else + { + llinfos << "Set internet stream to null" << llendl; + mURL.clear(); + } +} + + +void LLStreamingAudio_FMODSTUDIO::update() +{ + // Kill dead internet streams, if possible + std::list::iterator iter; + for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) + { + LLAudioStreamManagerFMODSTUDIO *streamp = *iter; + if (streamp->stopStream()) + { + llinfos << "Closed dead stream" << llendl; + delete streamp; + mDeadStreams.erase(iter++); + } + else + { + iter++; + } + } + + if(!mDeadStreams.empty()) + { + llassert_always(mCurrentInternetStreamp == NULL); + return; + } + + if(!mPendingURL.empty()) + { + llassert_always(mCurrentInternetStreamp == NULL); + llinfos << "Starting internet stream: " << mPendingURL << llendl; + mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem,mPendingURL); + mURL = mPendingURL; + mMetaData = new LLSD; + mPendingURL.clear(); + } + + // Don't do anything if there are no streams playing + if (!mCurrentInternetStreamp) + { + return; + } + + unsigned int progress; + bool starving; + bool diskbusy; + FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy); + + 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; + case(FMOD_TAGTYPE_FMOD): + if (!strcmp(tag.name, "Sample Rate Change")) + { + llinfos << "Stream forced changing sample rate to " << *((float *)tag.data) << llendl; + mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)); + } + continue; + 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; + } + } + } + if(starving) + { + bool paused = false; + mFMODInternetStreamChannelp->getPaused(&paused); + if(!paused) + { + llinfos << "Stream starvation detected! Pausing stream until buffer nearly full." << llendl; + llinfos << " (diskbusy="<setPaused(true); + } + } + else if(progress > 80) + { + mFMODInternetStreamChannelp->setPaused(false); + } + } + } +} + +void LLStreamingAudio_FMODSTUDIO::stop() +{ + mPendingURL.clear(); + + 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_FMODSTUDIO::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_FMODSTUDIO::isPlaying() +{ + if (mCurrentInternetStreamp) + { + return 1; // Active and playing + } + else if (!mURL.empty() || !mPendingURL.empty()) + { + return 2; // "Paused" + } + else + { + return 0; + } +} + + +F32 LLStreamingAudio_FMODSTUDIO::getGain() +{ + return mGain; +} + + +std::string LLStreamingAudio_FMODSTUDIO::getURL() +{ + return mURL; +} + + +void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol) +{ + mGain = vol; + + if (mFMODInternetStreamChannelp) + { + vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here? + + mFMODInternetStreamChannelp->setVolume(vol); + } +} + +/*virtual*/ bool LLStreamingAudio_FMODSTUDIO::getWaveData(float* arr, S32 count, S32 stride/*=1*/) +{ + //if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp) + // return false; + + //bool muted=false; + //mFMODInternetStreamChannelp->getMute(&muted); + //if(muted) + // return false; + + //static std::vector local_array(count); //Have to have an extra buffer to mix channels. Bleh. + //if(count > (S32)local_array.size()) //Expand the array if needed. Try to minimize allocation calls, so don't ever shrink. + // local_array.resize(count); + + //if( mFMODInternetStreamChannelp->getWaveData(&local_array[0],count,0) == FMOD_OK && + // mFMODInternetStreamChannelp->getWaveData(&arr[0],count,1) == FMOD_OK ) + //{ + // for(S32 i = count-1;i>=0;i-=stride) + // { + // arr[i] += local_array[i]; + // arr[i] *= .5f; + // } + // return true; + //} + return false; +} + +/////////////////////////////////////////////////////// +// manager of possibly-multiple internet audio streams + +LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(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 | FMOD_IGNORETAGS, 0, &mInternetStream); + + if (result!= FMOD_OK) + { + llwarns << "Couldn't open fmod stream, error " + << FMOD_ErrorString(result) + << llendl; + mReady = false; + return; + } + + mReady = true; +} + +FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::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(mInternetStream, NULL, true, &mStreamChannel); + return mStreamChannel; +} + +bool LLAudioStreamManagerFMODSTUDIO::stopStream() +{ + if (mInternetStream) + { + bool close = true; + switch (getOpenState()) + { + case FMOD_OPENSTATE_CONNECTING: + close = false; + break; + default: + close = true; + } + + if (close && mInternetStream->release() == FMOD_OK) + { + mStreamChannel = NULL; + mInternetStream = NULL; + return true; + } + else + { + return false; + } + } + else + { + return true; + } +} + +FMOD_OPENSTATE LLAudioStreamManagerFMODSTUDIO::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy) +{ + FMOD_OPENSTATE state; + mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + return state; +} + +void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) +{ + mSystem->setStreamBufferSize(streambuffertime/1000*128*128, FMOD_TIMEUNIT_RAWBYTES); + FMOD_ADVANCEDSETTINGS settings; + memset(&settings,0,sizeof(settings)); + settings.cbSize=sizeof(settings); + settings.defaultDecodeBufferSize = decodebuffertime;//ms + mSystem->setAdvancedSettings(&settings); +} diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.h b/indra/llaudio/llstreamingaudio_fmodstudio.h new file mode 100644 index 000000000..142256339 --- /dev/null +++ b/indra/llaudio/llstreamingaudio_fmodstudio.h @@ -0,0 +1,87 @@ +/** + * @file streamingaudio_fmodstudio.h + * @author Tofu Linden + * @brief Definition of LLStreamingAudio_FMODSTUDIO 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_FMODSTUDIO_H +#define LL_STREAMINGAUDIO_FMODSTUDIO_H + +#include "stdtypes.h" // from llcommon + +#include "llstreamingaudio.h" +#include "lltimer.h" + +//Stubs +class LLAudioStreamManagerFMODSTUDIO; +namespace FMOD +{ + class System; + class Channel; +} + +//Interfaces +class LLStreamingAudio_FMODSTUDIO : public LLStreamingAudioInterface +{ + public: + LLStreamingAudio_FMODSTUDIO(FMOD::System *system); + /*virtual*/ ~LLStreamingAudio_FMODSTUDIO(); + + /*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*/ bool supportsMetaData(){return true;} + /*virtual*/ const LLSD *getMetaData(){return mMetaData;} //return NULL if not playing. + /*virtual*/ bool supportsWaveData(){return false;} + /*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1); + /*virtual*/ bool supportsAdjustableBufferSizes(){return true;} + /*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime); +private: + FMOD::System *mSystem; + + LLAudioStreamManagerFMODSTUDIO *mCurrentInternetStreamp; + FMOD::Channel *mFMODInternetStreamChannelp; + std::list mDeadStreams; + + std::string mURL; + std::string mPendingURL; + F32 mGain; + + LLSD *mMetaData; +}; + + +#endif // LL_STREAMINGAUDIO_FMOD_H diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h index 998b78750..ec5415e50 100644 --- a/indra/llmath/llmath.h +++ b/indra/llmath/llmath.h @@ -78,7 +78,7 @@ const F32 DEG_TO_RAD = 0.017453292519943295769236907684886f; const F32 RAD_TO_DEG = 57.295779513082320876798154814105f; const F32 F_APPROXIMATELY_ZERO = 0.00001f; const F32 F_LN10 = 2.3025850929940456840179914546844f; -const F32 OO_LN10 = 0.43429448190325182765112891891661; +const F32 OO_LN10 = 0.4342944819032518276511289189166f; const F32 F_LN2 = 0.69314718056f; const F32 OO_LN2 = 1.4426950408889634073599246810019f; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4668e2c97..244c8ed8c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -7,13 +7,12 @@ include(Boost) include(DBusGlib) include(DirectX) include(ELFIO) +if(FMODSTUDIO) + include(FMODSTUDIO) +endif(FMODSTUDIO) if(FMODEX) include(FMODEX) - set(FMOD OFF) endif(FMODEX) -if(FMOD) - include(FMOD) -endif(FMOD) include(OPENAL) include(FindOpenGL) include(Hunspell) @@ -1302,12 +1301,13 @@ if (WINDOWS) winspool ) + if(FMODSTUDIO) + list(APPEND viewer_LIBRARIES ${FMODSTUDIO_LIBRARY}) + endif(FMODSTUDIO) + 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 @@ -1407,37 +1407,15 @@ if (OPENAL) set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_OPENAL") endif (OPENAL) -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 (FMODSTUDIO) + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODSTUDIO") + set(FMODWRAPPER_LIBRARY ${FMODSTUDIO_LIBRARY}) +endif (FMODSTUDIO) - if (DARWIN AND FMOD) - set(fmodwrapper_SOURCE_FILES fmodwrapper.cpp) - add_library(fmodwrapper SHARED ${fmodwrapper_SOURCE_FILES}) - set(fmodwrapper_needed_LIBRARIES ${FMOD_LIBRARY} ${CARBON_LIBRARY}) - set_target_properties( - fmodwrapper - PROPERTIES - BUILD_WITH_INSTALL_RPATH 1 - INSTALL_NAME_DIR "@executable_path/../Resources" - LINK_FLAGS "-unexported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/fmod_hidden_symbols.exp" - ) - set(FMODWRAPPER_LIBRARY fmodwrapper) - target_link_libraries(fmodwrapper ${fmodwrapper_needed_LIBRARIES}) - else (DARWIN AND FMOD) - # fmodwrapper unnecessary on linux or windows, for fmod and fmodex - if (FMODEX) - set(FMODWRAPPER_LIBRARY ${FMODEX_LIBRARY}) - endif (FMODEX) - if (FMOD) - set(FMODWRAPPER_LIBRARY ${FMOD_LIBRARY}) - endif (FMOD) - endif (DARWIN AND FMOD) -endif (FMOD OR FMODEX) +if (FMODEX) + set(LLSTARTUP_COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS} -DLL_FMODEX") + set(FMODWRAPPER_LIBRARY ${FMODEX_LIBRARY}) +endif (FMODEX) set_source_files_properties(llstartup.cpp PROPERTIES COMPILE_FLAGS "${LLSTARTUP_COMPILE_FLAGS}") @@ -1462,19 +1440,27 @@ set(PACKAGE OFF CACHE BOOL if (WINDOWS) set(release_flags "/MAPRelease/${VIEWER_BINARY_NAME}.map") - if (FMOD) + if (FMODSTUDIO) + if (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod.dll") + else (WORD_SIZE EQUAL 32) + set(fmodstudio_dll_file "fmod64.dll") + endif (WORD_SIZE EQUAL 32) + if(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMOD_BINARY_DIR}/fmod.dll") + set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODSTUDIO_BINARY_DIR}/${fmodstudio_dll_file}") else(MANIFEST_LIBRARIES) - set(MANIFEST_LIBRARIES "--extra_libraries=${FMOD_BINARY_DIR}/fmod.dll") + set(MANIFEST_LIBRARIES "--extra_libraries=${FMODSTUDIO_BINARY_DIR}/${fmodstudio_dll_file}") endif(MANIFEST_LIBRARIES) - endif (FMOD) + set(EXTRA_LINKER_FLAGS "/DELAYLOAD:${fmodstudio_dll_file}") + endif (FMODSTUDIO) + if (FMODEX) - if (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex.dll") - else (WORD_SIZE EQUAL 32) - set(fmodex_dll_file "fmodex64.dll") - endif (WORD_SIZE EQUAL 32) + if (WORD_SIZE EQUAL 32) + set(fmodex_dll_file "fmodex.dll") + else (WORD_SIZE EQUAL 32) + set(fmodex_dll_file "fmodex64.dll") + endif (WORD_SIZE EQUAL 32) if(MANIFEST_LIBRARIES) set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODEX_BINARY_DIR}/${fmodex_dll_file}") @@ -1651,6 +1637,14 @@ if (LINUX) set(product ${VIEWER_BRANDING_NAME_CAMELCASE}-${ARCH}-${viewer_VERSION}) + if (FMODSTUDIO) + if(MANIFEST_LIBRARIES) + set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODSTUDIO_LIBRARY}") + else(MANIFEST_LIBRARIES) + set(MANIFEST_LIBRARIES "--extra_libraries=${FMODSTUDIO_LIBRARY}") + endif(MANIFEST_LIBRARIES) + endif (FMODSTUDIO) + if (FMODEX) if(MANIFEST_LIBRARIES) set(MANIFEST_LIBRARIES "${MANIFEST_LIBRARIES}|${FMODEX_LIBRARY}") @@ -1751,6 +1745,15 @@ if (DARWIN) add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit basic_plugin_filepicker) + if (FMODSTUDIO) + add_custom_command(OUTPUT "${FMODSTUDIO_LIBRARY}" + COMMAND cp "${FMODSTUDIO_ORIG_LIBRARY}" "${FMODSTUDIO_LIBRARY}" + COMMAND install_name_tool -id "@executable_path/../Resources/libfmod.dylib" ${FMODSTUDIO_LIBRARY} + DEPENDS "${FMODSTUDIO_ORIG_LIBRARY}") + add_custom_target(fmodstudio_modified_library DEPENDS "${FMODSTUDIO_LIBRARY}") + add_dependencies(${VIEWER_BINARY_NAME} fmodstudio_modified_library) + endif (FMODSTUDIO) + if (FMODEX) add_custom_command(OUTPUT "${FMODEX_LIBRARY}" COMMAND cp "${FMODEX_ORIG_LIBRARY}" "${FMODEX_LIBRARY}" diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 3621179de..dc1e19f1f 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -44,12 +44,13 @@ #include "llviewermedia_streamingaudio.h" #include "llaudioengine.h" -#if LL_FMODEX -# include "llaudioengine_fmodex.h" + +#if LL_FMODSTUDIO +# include "llaudioengine_fmodstudio.h" #endif -#if LL_FMOD -# include "llaudioengine_fmod.h" +#if LL_FMODEX +# include "llaudioengine_fmodex.h" #endif #ifdef LL_OPENAL @@ -415,6 +416,17 @@ void init_audio() { gAudiop = NULL; +#ifdef LL_FMODSTUDIO + if (!gAudiop +#if !LL_WINDOWS + && NULL == getenv("LL_BAD_FMODSTUDIO_DRIVER") +#endif // !LL_WINDOWS + ) + { + gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODSTUDIO(gSavedSettings.getBOOL("SHEnableFMODExProfiler"), gSavedSettings.getBOOL("SHEnableFMODEXVerboseDebugging")); + } +#endif + #ifdef LL_FMODEX if (!gAudiop #if !LL_WINDOWS @@ -437,17 +449,6 @@ void init_audio() } #endif -#ifdef LL_FMOD - if (!gAudiop -#if !LL_WINDOWS - && NULL == getenv("LL_BAD_FMOD_DRIVER") -#endif // !LL_WINDOWS - ) - { - gAudiop = (LLAudioEngine *) new LLAudioEngine_FMOD(); - } -#endif - if (gAudiop) { #if LL_WINDOWS