123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856 |
- /**
- * @file llaudioengine_fmod.cpp
- * @brief Implementation of LLAudioEngine class abstracting the audio support
- * as a FMOD Studio implementation
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2014, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "lldir.h"
- #include "llmath.h"
- #include "llrand.h"
- #include "llversionviewer.h" // For LL_CHANNEL
- #include "fmod.hpp"
- #include "fmod_errors.h"
- #include "llstreamingaudio.h"
- #include "llstreamingaudio_fmod.h"
- #include "llaudioengine_fmod.h"
- #include "lllistener_fmod.h"
- // Set to 1 to override FMOD memory management with ours
- #define LL_MANAGE_FMOD_MEMORY 0
- // static variables
- #if LL_LINUX
- bool LLAudioEngine_FMOD::sNoALSA = false;
- bool LLAudioEngine_FMOD::sNoPulseAudio = false;
- #endif
- FMOD::ChannelGroup* LLAudioEngine_FMOD::sChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = { NULL };
- // Helper functions and callbacks
- bool checkFMerr(S32 result, const char* str)
- {
- if (result == (S32)FMOD_OK)
- {
- return false;
- }
- LL_DEBUGS("FMOD") << str << " Error: "
- << FMOD_ErrorString((FMOD_RESULT)result) << LL_ENDL;
- return true;
- }
- #if LL_MANAGE_FMOD_MEMORY
- static void* F_CALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type,
- const char* sourcestr)
- {
- if (type & FMOD_MEMORY_STREAM_DECODE)
- {
- llinfos << "Decode buffer size: " << size << llendl;
- }
- else if (type & FMOD_MEMORY_STREAM_FILE)
- {
- llinfos << "Stream buffer size: " << size << llendl;
- }
- return ll_aligned_malloc_16(size);
- }
- static void* F_CALL decode_realloc(void* ptr, unsigned int size,
- FMOD_MEMORY_TYPE type,
- const char* sourcestr)
- {
- return ll_aligned_realloc_16(ptr, size, 0);
- }
- static void F_CALL decode_dealloc(void* ptr, FMOD_MEMORY_TYPE type,
- const char* sourcestr)
- {
- ll_aligned_free_16(ptr);
- }
- #endif
- // *NOTE: this is almost certainly being called on the mixer thread, not the
- // main thread. May have implications for callees or audio engine shutdown.
- FMOD_RESULT F_CALL 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.
- if (!dsp_state) // Paranoia ?... Better safe than sorry ! HB
- {
- return FMOD_ERR_DSP_NOTFOUND; // Seems appropriate to me... HB
- }
- FMOD::DSP* thisdsp = (FMOD::DSP*)dsp_state->instance;
- if (!thisdsp)
- {
- return FMOD_ERR_DSP_NOTFOUND; // Seems appropriate to me... HB
- }
- LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT>* windgen;
- thisdsp->getUserData((void**)&windgen);
- if (windgen)
- {
- windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT*)newbuffer,
- length);
- }
- return FMOD_OK;
- }
- //
- // LLAudioEngine_FMOD implementation
- //
- LLAudioEngine_FMOD::LLAudioEngine_FMOD(bool enable_profiler)
- : mInited(false),
- mWindGen(NULL),
- mWindDSP(NULL),
- mSystem(NULL),
- mEnableProfiler(enable_profiler)
- {
- mWindDSPDesc = new FMOD_DSP_DESCRIPTION();
- }
- LLAudioEngine_FMOD::~LLAudioEngine_FMOD()
- {
- delete mWindDSPDesc;
- mWindDSPDesc = NULL;
- }
- bool LLAudioEngine_FMOD::init(void* userdata)
- {
- LL_DEBUGS("AppInit") << "Initializing FMOD" << LL_ENDL;
- mAudioDevice.clear();
- #if LL_MANAGE_FMOD_MEMORY
- FMOD_RESULT result = FMOD::Memory_Initialize(NULL, 0, &decode_alloc,
- &decode_realloc,
- &decode_dealloc,
- FMOD_MEMORY_ALL);
- checkFMerr(result, "FMOD::Memory_Initialize");
- #else
- FMOD_RESULT result;
- #endif
- result = FMOD::System_Create(&mSystem);
- if (checkFMerr(result, "FMOD::System_Create"))
- {
- return false;
- }
- // Will call LLAudioEngine_FMOD::allocateListener, which needs a valid
- // mSystem pointer.
- LLAudioEngine::init(userdata);
- unsigned int version;
- result = mSystem->getVersion(&version);
- checkFMerr(result, "FMOD::System::getVersion");
- if (version < FMOD_VERSION)
- {
- llwarns << "You are using the wrong FMOD Studio version (" << version
- << ") ! You should be using FMOD Studio " << FMOD_VERSION
- << llendl;
- }
- // In this case, all sounds, PLUS wind and stream will be software.
- result = mSystem->setSoftwareChannels(MAX_AUDIO_CHANNELS + 2);
- checkFMerr(result, "FMOD::System::setSoftwareChannels");
- FMOD_ADVANCEDSETTINGS adv_settings;
- memset(&adv_settings, 0, sizeof(FMOD_ADVANCEDSETTINGS));
- adv_settings.cbSize = sizeof(FMOD_ADVANCEDSETTINGS);
- adv_settings.resamplerMethod = FMOD_DSP_RESAMPLER_LINEAR;
- result = mSystem->setAdvancedSettings(&adv_settings);
- checkFMerr(result, "FMOD::System::setAdvancedSettings");
- U32 fmod_flags = FMOD_INIT_NORMAL | FMOD_INIT_THREAD_UNSAFE |
- FMOD_INIT_3D_RIGHTHANDED;
- if (mEnableProfiler)
- {
- fmod_flags |= FMOD_INIT_PROFILE_ENABLE;
- mSystem->createChannelGroup("None", &sChannelGroups[AUDIO_TYPE_NONE]);
- mSystem->createChannelGroup("SFX", &sChannelGroups[AUDIO_TYPE_SFX]);
- mSystem->createChannelGroup("UI", &sChannelGroups[AUDIO_TYPE_UI]);
- mSystem->createChannelGroup("Ambient",
- &sChannelGroups[AUDIO_TYPE_AMBIENT]);
- }
- #if LL_LINUX
- bool audio_ok = false;
- if (sNoPulseAudio)
- {
- LL_DEBUGS("AppInit") << "PulseAudio audio output SKIPPED" << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL;
- if (mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK &&
- (result = mSystem->init(MAX_AUDIO_CHANNELS + 2, fmod_flags,
- (void*)LL_CHANNEL)) == FMOD_OK)
- {
- mAudioDevice = "PulseAudio";
- audio_ok = true;
- }
- else
- {
- checkFMerr(result, "PulseAudio audio output FAILED to initialize");
- }
- }
- if (audio_ok || sNoALSA)
- {
- LL_DEBUGS("AppInit") << "ALSA audio output SKIPPED" << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
- if (mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK &&
- (result = mSystem->init(MAX_AUDIO_CHANNELS + 2, fmod_flags, 0)) == FMOD_OK)
- {
- mAudioDevice = "ALSA";
- audio_ok = true;
- }
- else
- {
- checkFMerr(result, "ALSA audio output FAILED to initialize");
- }
- }
- if (!audio_ok)
- {
- llwarns << "Overall audio init failure." << llendl;
- return false;
- }
- llinfos << mAudioDevice << " output initialized" << llendl;
- // We are 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_DEBUGS("AppInit") << "Audio output: NoSound" << LL_ENDL;
- break;
- case FMOD_OUTPUTTYPE_PULSEAUDIO:
- LL_DEBUGS("AppInit") << "Audio output: PulseAudio" << LL_ENDL;
- break;
- case FMOD_OUTPUTTYPE_ALSA:
- LL_DEBUGS("AppInit") << "Audio output: ALSA" << LL_ENDL;
- break;
- default:
- llinfos << "Audio output: Unknown !" << llendl;
- }
- #else // LL_LINUX
- // Initialize the FMOD engine
- result = mSystem->init(MAX_AUDIO_CHANNELS + 2, fmod_flags, 0);
- if (result == FMOD_ERR_OUTPUT_CREATEBUFFER)
- {
- // Ok, the format selected is not supported by this soundcard. Switch
- // it back to stereo...
- result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0);
- checkFMerr(result, "Error falling back to stereo mode");
- // ... and re-init.
- result = mSystem->init(MAX_AUDIO_CHANNELS + 2, fmod_flags, 0);
- }
- if (checkFMerr(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_FMOD(mSystem));
- }
- LL_DEBUGS("AppInit") << "FMOD Studio initialized correctly" << LL_ENDL;
- FMOD_ADVANCEDSETTINGS adv_settings_dump = {};
- mSystem->getAdvancedSettings(&adv_settings_dump);
- LL_DEBUGS("AppInit") << "FMOD Studio resampler: "
- << adv_settings.resamplerMethod << " bytes"
- << LL_ENDL;
- int r_numbuffers;
- unsigned int r_bufferlength;
- mSystem->getDSPBufferSize(&r_bufferlength, &r_numbuffers);
- int r_samplerate, r_channels;
- char r_name[512];
- mSystem->getDriverInfo(0, r_name, 511, NULL, &r_samplerate, NULL, &r_channels);
- r_name[511] = '\0';
- // Optimistic default; I suspect if sample rate is 0, everything breaks:
- int latency = 100;
- if (r_samplerate != 0)
- {
- latency = (int)(1000.f * r_bufferlength * r_numbuffers / r_samplerate);
- }
- llinfos << "FMOD device: " << r_name << " with parameters: "
- << r_samplerate << " Hz, " << r_channels << " channels - Buffers: "
- << r_numbuffers << " * " << r_bufferlength << " bytes - Latency: "
- << latency << "ms." << llendl;
- mInited = true;
- return true;
- }
- std::string LLAudioEngine_FMOD::getDriverName(bool verbose)
- {
- if (!mSystem)
- {
- llwarns << "FMOD not properly initialized !" << llendl;
- return "FMODEx_NOT_INITIALIZED";
- }
- if (!verbose)
- {
- return "FMODStudio";
- }
- std::string result = "FMOD Studio";
- unsigned int version;
- if (!checkFMerr(mSystem->getVersion(&version), "FMOD::System::getVersion"))
- {
- result += llformat(" v%1x.%02x.%02x", version >> 16,
- version >> 8 & 0x000000FF, version & 0x000000FF);
- }
- if (!mAudioDevice.empty())
- {
- result += " (" + mAudioDevice + ")";
- }
- return result;
- }
- void LLAudioEngine_FMOD::allocateListener()
- {
- mListenerp = (LLListener*)new(std::nothrow) LLListener_FMOD(mSystem);
- if (!mListenerp)
- {
- llwarns << "Listener creation failed" << llendl;
- }
- }
- void LLAudioEngine_FMOD::shutdown()
- {
- if (mWindDSP)
- {
- cleanupWind();
- }
- stopInternetStream();
- llinfos << "Shutting down the audio engine..." << llendl;
- LLAudioEngine::shutdown();
- if (mSystem)
- {
- llinfos << "Closing FMOD Studio" << llendl;
- mSystem->close();
- mSystem->release();
- }
- llinfos << "Done closing FMOD Studio" << llendl;
- delete mListenerp;
- mListenerp = NULL;
- }
- LLAudioBuffer* LLAudioEngine_FMOD::createBuffer()
- {
- return new LLAudioBufferFMOD(mSystem);
- }
- LLAudioChannel* LLAudioEngine_FMOD::createChannel()
- {
- return new LLAudioChannelFMOD(mSystem);
- }
- bool LLAudioEngine_FMOD::initWind()
- {
- mNextWindUpdate = 0.f;
- if (!mWindDSP)
- {
- if (!mWindDSPDesc)
- {
- llwarns << "mWindDSPDesc is NULL !" << llendl;
- return false;
- }
- if (mWindGen)
- {
- LL_DEBUGS("FMOD") << "mWindGen was non-NULL. Deleting." << LL_ENDL;
- delete mWindGen;
- }
- // Set everything to zero:
- memset(mWindDSPDesc, 0, sizeof(*mWindDSPDesc));
- mWindDSPDesc->pluginsdkversion = FMOD_PLUGIN_SDK_VERSION;
- // Set name to "Wind Unit"
- strncpy(mWindDSPDesc->name, "Wind Unit", sizeof(mWindDSPDesc->name));
- mWindDSPDesc->numoutputbuffers = 1;
- // Assign the callback, which may be called from arbitrary threads
- mWindDSPDesc->read = &windCallback;
- bool error = checkFMerr(mSystem->createDSP(mWindDSPDesc, &mWindDSP),
- "FMOD::createDSP");
- if (error || !mWindDSP)
- {
- llwarns << "Failed to create the wind DSP" << llendl;
- return false;
- }
- int frequency = 44100;
- FMOD_SPEAKERMODE mode;
- if (!checkFMerr(mSystem->getSoftwareFormat(&frequency, &mode, NULL),
- "FMOD::System::getSoftwareFormat"))
- {
- mWindGen = new LLWindGen<MIXBUFFERFORMAT>((U32)frequency);
- if (!checkFMerr(mWindDSP->setUserData((void*)mWindGen),
- "FMOD::DSP::setUserData") &&
- !checkFMerr(mWindDSP->setChannelFormat(FMOD_CHANNELMASK_STEREO,
- 2, mode),
- "FMOD::DSP::setChannelFormat") &&
- !checkFMerr(mSystem->playDSP(mWindDSP, NULL, false, NULL),
- "FMOD::System::playDSP"))
- {
- return true;
- }
- llwarns << "Failed to initialize the wind DSP" << llendl;
- cleanupWind();
- }
- }
- return false;
- }
- void LLAudioEngine_FMOD::cleanupWind()
- {
- if (mWindDSP)
- {
- FMOD::ChannelGroup* group = NULL;
- bool error = checkFMerr(mSystem->getMasterChannelGroup(&group),
- "FMOD::System::getMasterChannelGroup");
- if (!error && group)
- {
- group->removeDSP(mWindDSP);
- }
- mWindDSP->release();
- mWindDSP = NULL;
- }
- if (mWindGen)
- {
- delete mWindGen;
- mWindGen = NULL;
- }
- }
- void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec,
- F32 camera_height_above_water)
- {
- if (mEnableWind && mWindGen &&
- mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
- {
- // Wind comes in as Linden coordinate (+X = forward, +Y = left, +Z =
- // up), so we need to convert this to the conventional orientation DS3D
- // and OpenAL use where +X = right, +Y = up, +Z = backwards
- wind_vec.set(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
- mWindGen->mTargetFreq =
- 80.f * powf(1.f + mapWindVecToPitch(wind_vec),
- 2.5f * (mapWindVecToGain(wind_vec) + 1.f));
- mWindGen->mTargetGain = mapWindVecToGain(wind_vec) * mMaxWindGain;
- mWindGen->mTargetPanGainR = mapWindVecToPan(wind_vec);
- }
- }
- void LLAudioEngine_FMOD::setInternalGain(F32 gain)
- {
- if (!mInited || !mSystem)
- {
- return;
- }
- gain = llclamp(gain, 0.f, 1.f);
- FMOD::ChannelGroup* master_group;
- mSystem->getMasterChannelGroup(&master_group);
- if (!master_group)
- {
- LL_DEBUGS("FMOD") << "Could not get master group." << LL_ENDL;
- return;
- }
- master_group->setVolume(gain);
- LLStreamingAudioInterface* saimpl = getStreamingAudioImpl();
- if (saimpl)
- {
- // FMOD Studio likes its streaming audio channel gain re-asserted after
- // master volume change.
- saimpl->setGain(saimpl->getGain());
- }
- }
- //
- // LLAudioChannelFMOD implementation
- //
- LLAudioChannelFMOD::LLAudioChannelFMOD(FMOD::System* system)
- : LLAudioChannel(),
- mSystemp(system),
- mChannelp(NULL),
- mLastSamplePos(0)
- {
- }
- LLAudioChannelFMOD::~LLAudioChannelFMOD()
- {
- cleanup();
- }
- bool LLAudioChannelFMOD::updateBuffer()
- {
- FMOD_RESULT result;
- 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();
- if (!bufferp)
- {
- llwarns << "No current buffer !" << llendl;
- mCurrentBufferp = NULL;
- return false;
- }
- // Grab the FMOD sound associated with the buffer
- FMOD::Sound* soundp = bufferp->getSound();
- if (!soundp)
- {
- // This is bad, there should ALWAYS be a sound associated with a
- // legit buffer.
- llwarns << "No FMOD sound" << llendl;
- #if 1
- mCurrentBufferp = NULL;
- #endif
- return false;
- }
- // Actually play the sound. Start it off paused so we can do all the
- // necessary setup.
- if (!mChannelp)
- {
- result = getSystem()->playSound(soundp, NULL, true, &mChannelp);
- checkFMerr(result, "FMOD::System::playSound");
- if (!mChannelp)
- {
- LL_DEBUGS("FMOD") << "Could not allocate a new channel"
- << LL_ENDL;
- mCurrentBufferp = NULL;
- return false;
- }
- }
- }
- // If we have a source for the channel, we need to update its gain.
- if (mCurrentSourcep)
- {
- result = mChannelp->setVolume(getSecondaryGain() *
- mCurrentSourcep->getGain());
- if (result != FMOD_OK)
- {
- LL_DEBUGS("FMOD") << checkFMerr(result, "FMOD::Channel::setVolume")
- << LL_ENDL;
- }
- mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL
- : FMOD_LOOP_OFF);
- }
- return true;
- }
- void LLAudioChannelFMOD::update3DPosition()
- {
- if (!mChannelp)
- {
- // We are not actually a live channel, i.e. we are not playing anything
- return;
- }
- LLAudioBufferFMOD* bufferp =
- (LLAudioBufferFMOD*)mCurrentBufferp;
- if (!bufferp)
- {
- // We do not have a buffer associated with us (should really have been
- // picked up by the above test).
- return;
- }
- if (mCurrentSourcep->isAmbient())
- {
- // Ambient sound, so we do not need to do any positional updates.
- set3DMode(false);
- }
- else
- {
- // Localized sound. Update the position and velocity of the sound.
- set3DMode(true);
- LLVector3 float_pos(mCurrentSourcep->getPositionGlobal());
- FMOD_RESULT result =
- mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV,
- (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV);
- checkFMerr(result, "FMOD::Channel::set3DAttributes");
- }
- }
- void LLAudioChannelFMOD::updateLoop()
- {
- if (!mChannelp)
- {
- // May want to clear up the loop/sample counters.
- return;
- }
- //
- //* HACK: we keep track of whether we looped or not by seeing when the
- // sample position looks like it's going backwards. Not reliable; may
- // yield false negatives.
- //
- unsigned int cur_pos;
- mChannelp->getPosition(&cur_pos, FMOD_TIMEUNIT_PCMBYTES);
- if ((U32)cur_pos < mLastSamplePos)
- {
- mLoopedThisFrame = true;
- }
- mLastSamplePos = cur_pos;
- }
- void LLAudioChannelFMOD::cleanup()
- {
- if (mChannelp)
- {
- LL_DEBUGS("FMOD") << "Cleaning-up channel " << std::hex << mChannelp
- << std::dec << LL_ENDL;
- checkFMerr(mChannelp->stop(), "FMOD::Channel::stop");
- mCurrentBufferp = NULL;
- mChannelp = NULL;
- }
- }
- void LLAudioChannelFMOD::play()
- {
- if (!mChannelp)
- {
- llwarns << "Playing without a channelID, aborting" << llendl;
- return;
- }
- checkFMerr(mChannelp->setPaused(false), "FMOD::Channel::pause");
- getSource()->setPlayedOnce(true);
- FMOD::ChannelGroup* group =
- LLAudioEngine_FMOD::sChannelGroups[getSource()->getType()];
- if (group)
- {
- mChannelp->setChannelGroup(group);
- }
- }
- void LLAudioChannelFMOD::playSynced(LLAudioChannel* channelp)
- {
- LLAudioChannelFMOD* fmod_channelp =
- (LLAudioChannelFMOD*)channelp;
- if (!(fmod_channelp->mChannelp && mChannelp))
- {
- // No channel allocated to both the master and the slave
- return;
- }
- unsigned int cur_pos;
- if (checkFMerr(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
- checkFMerr(mChannelp->setPosition(cur_pos, FMOD_TIMEUNIT_PCMBYTES),
- "Unable to set current position");
- // Start us playing
- play();
- }
- bool LLAudioChannelFMOD::isPlaying()
- {
- if (!mChannelp)
- {
- return false;
- }
- bool paused, playing;
- mChannelp->getPaused(&paused);
- mChannelp->isPlaying(&playing);
- return !paused && playing;
- }
- void LLAudioChannelFMOD::set3DMode(bool use3d)
- {
- FMOD_MODE current_mode;
- if (mChannelp->getMode(¤t_mode) != FMOD_OK)
- {
- return;
- }
- FMOD_MODE new_mode = current_mode;
- new_mode &= ~(use3d ? FMOD_2D : FMOD_3D);
- new_mode |= use3d ? FMOD_3D : FMOD_2D;
- if (current_mode != new_mode)
- {
- mChannelp->setMode(new_mode);
- }
- }
- //
- // LLAudioBufferFMOD implementation
- //
- LLAudioBufferFMOD::LLAudioBufferFMOD(FMOD::System* system)
- : mSystemp(system),
- mSoundp(NULL)
- {
- }
- LLAudioBufferFMOD::~LLAudioBufferFMOD()
- {
- if (mSoundp)
- {
- mSoundp->release();
- mSoundp = NULL;
- }
- }
- bool LLAudioBufferFMOD::loadWAV(const std::string& filename)
- {
- // Try to open a wav file from disk. This will eventually go away, as we
- // do not really want to block doing this.
- if (filename.empty())
- {
- // Invalid filename, abort.
- return false;
- }
- if (!LLFile::isfile(filename))
- {
- // File not found, abort.
- return false;
- }
- if (mSoundp)
- {
- // If there is already something loaded in this buffer, clean it up.
- mSoundp->release();
- mSoundp = NULL;
- }
- FMOD_MODE base_mode = FMOD_LOOP_NORMAL;
- FMOD_CREATESOUNDEXINFO exinfo;
- memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
- exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
- // Hint to speed up loading:
- exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_WAV;
- // Load up the wav file into an fmod sample
- FMOD_RESULT result =
- getSystem()->createSound(filename.c_str(), base_mode, &exinfo,
- &mSoundp);
- if (result != FMOD_OK)
- {
- // We failed to load the file for some reason.
- llwarns << "Could not load data '" << filename << "': "
- << FMOD_ErrorString(result) << llendl;
- // If we EVER want to load wav files provided by end users, we need
- // to rethink this!
- //
- // File is probably corrupt: remove it.
- LLFile::remove(filename);
- return false;
- }
- // Everything went well, return true
- return true;
- }
- U32 LLAudioBufferFMOD::getLength()
- {
- unsigned int length = 0;
- if (mSoundp)
- {
- mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES);
- }
- return length;
- }
|