Skip to content
Snippets Groups Projects
Commit 0801a8de authored by Rye Mutt's avatar Rye Mutt :bread:
Browse files

smack fmod

parent 260c71c2
No related branches found
No related tags found
No related merge requests found
/** /**
* @file audioengine_fmodstudio.cpp * @file audioengine_fmodstudio.cpp
* @brief Implementation of LLAudioEngine class abstracting the audio * @brief Implementation of LLAudioEngine class abstracting the audio
* support as a FMOD Studio implementation * support as a FMODSTUDIO implementation
* *
* $LicenseInfo:firstyear=2014&license=viewerlgpl$ * $LicenseInfo:firstyear=2020&license=viewerlgpl$
* Second Life Viewer Source Code * Second Life Viewer Source Code
* Copyright (C) 2014, Linden Research, Inc. * Copyright (C) 2020, Linden Research, Inc.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
...@@ -38,14 +38,12 @@ ...@@ -38,14 +38,12 @@
#include "llmath.h" #include "llmath.h"
#include "llrand.h" #include "llrand.h"
#include "fmod.hpp" #include "fmodstudio/fmod.hpp"
#include "fmod_errors.h" #include "fmodstudio/fmod_errors.h"
#include "lldir.h" #include "lldir.h"
#include "sound_ids.h" #include "sound_ids.h"
const U32 EXTRA_SOUND_CHANNELS = 32;
FMOD_RESULT F_CALLBACK windDSPCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels); FMOD_RESULT F_CALLBACK windDSPCallback(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] = {nullptr}; FMOD::ChannelGroup *LLAudioEngine_FMODSTUDIO::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {nullptr};
...@@ -93,7 +91,7 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title ...@@ -93,7 +91,7 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title
} }
// In this case, all sounds, PLUS wind and stream will be software. // In this case, all sounds, PLUS wind and stream will be software.
result = mSystem->setSoftwareChannels(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS); result = mSystem->setSoftwareChannels(LL_MAX_AUDIO_CHANNELS + 2);
Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels"); Check_FMOD_Error(result,"FMOD::System::setSoftwareChannels");
FMOD_ADVANCEDSETTINGS adv_settings = { }; FMOD_ADVANCEDSETTINGS adv_settings = { };
...@@ -115,11 +113,13 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title ...@@ -115,11 +113,13 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title
result = mSystem->setAdvancedSettings(&adv_settings); result = mSystem->setAdvancedSettings(&adv_settings);
Check_FMOD_Error(result, "FMOD::System::setAdvancedSettings"); Check_FMOD_Error(result, "FMOD::System::setAdvancedSettings");
U32 fmod_flags = FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED | FMOD_INIT_THREAD_UNSAFE; // FMOD_INIT_THREAD_UNSAFE Disables thread safety for API calls.
if(mEnableProfiler) // Only use this if FMOD is being called from a single thread, and if Studio API is not being used.
{ U32 fmod_flags = FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED | FMOD_INIT_THREAD_UNSAFE;
fmod_flags |= FMOD_INIT_PROFILE_ENABLE; if (mEnableProfiler)
} {
fmod_flags |= FMOD_INIT_PROFILE_ENABLE;
}
#if LL_LINUX #if LL_LINUX
bool audio_ok = false; bool audio_ok = false;
...@@ -130,7 +130,7 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title ...@@ -130,7 +130,7 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title
{ {
LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL; LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL;
if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO)) == FMOD_OK && if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO)) == FMOD_OK &&
(result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK) (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, const_cast<char*>(app_title.c_str()))) == FMOD_OK)
{ {
LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL; LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL;
audio_ok = true; audio_ok = true;
...@@ -151,30 +151,30 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title ...@@ -151,30 +151,30 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title
{ {
LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL;
if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA)) == FMOD_OK && if((result = mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA)) == FMOD_OK &&
(result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0)) == FMOD_OK) (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0)) == FMOD_OK)
{ {
LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL;
audio_ok = true; audio_ok = true;
} }
else else
{ {
Check_FMOD_Error(result, "ALSA audio output FAILED to initialize"); Check_FMOD_Error(result, "ALSA audio output FAILED to initialize");
} }
} }
else else
{ {
LL_DEBUGS("AppInit") << "ALSA audio output SKIPPED" << LL_ENDL; LL_DEBUGS("AppInit") << "ALSA audio output SKIPPED" << LL_ENDL;
} }
} }
if (!audio_ok) if (!audio_ok)
{ {
LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL; LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL;
return false; return false;
} }
// We're interested in logging which output method we // We're interested in logging which output method we
// ended up with, for QA purposes. // ended up with, for QA purposes.
FMOD_OUTPUTTYPE output_type; FMOD_OUTPUTTYPE output_type;
if(!Check_FMOD_Error(mSystem->getOutput(&output_type), "FMOD::System::getOutput")) if(!Check_FMOD_Error(mSystem->getOutput(&output_type), "FMOD::System::getOutput"))
{ {
switch (output_type) switch (output_type)
...@@ -192,22 +192,22 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title ...@@ -192,22 +192,22 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title
#else // LL_LINUX #else // LL_LINUX
// initialize the FMOD engine // initialize the FMOD engine
result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0); result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0);
if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) if (Check_FMOD_Error(result, "Error initializing FMOD Studio with default settins, retrying with other format"))
{ {
/* result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0/*- ignore*/);
Ok, the speaker mode selected isn't supported by this soundcard. Switch it if (Check_FMOD_Error(result, "Error setting sotware format. Can't init."))
back to stereo... {
*/ return false;
result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0); }
Check_FMOD_Error(result,"Error falling back to stereo mode"); result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0);
/* }
... and re-init. if (Check_FMOD_Error(result, "Error initializing FMOD Studio"))
*/ {
result = mSystem->init(LL_MAX_AUDIO_CHANNELS + EXTRA_SOUND_CHANNELS, fmod_flags, 0); // If it fails here and (result == FMOD_ERR_OUTPUT_CREATEBUFFER),
} // we can retry with other settings
if(Check_FMOD_Error(result, "Error initializing FMOD Studio")) return false;
return false; }
#endif #endif
if (mEnableProfiler) if (mEnableProfiler)
...@@ -367,58 +367,59 @@ void LLAudioEngine_FMODSTUDIO::cleanupWind() ...@@ -367,58 +367,59 @@ void LLAudioEngine_FMODSTUDIO::cleanupWind()
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
void LLAudioEngine_FMODSTUDIO::updateWind(LLVector3 wind_vec, F32 camera_height_above_water) void LLAudioEngine_FMODSTUDIO::updateWind(LLVector3 wind_vec, F32 camera_height_above_water)
{ {
LLVector3 wind_pos; LLVector3 wind_pos;
F64 pitch; F64 pitch;
F64 center_freq; F64 center_freq;
if (!mEnableWind) if (!mEnableWind)
{ {
return; 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);
}
}
//----------------------------------------------------------------------- if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL))
void LLAudioEngine_FMODSTUDIO::setInternalGain(F32 gain) {
{
if (!mInited)
{
return;
}
gain = llclamp( gain, 0.0f, 1.0f ); // 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
FMOD::ChannelGroup *master_group; wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]);
if(Check_FMOD_Error(mSystem->getMasterChannelGroup(&master_group), "FMOD::System::getMasterChannelGroup"))
return;
master_group->setVolume(gain); // cerr << "Wind update" << endl;
LLStreamingAudioInterface *saimpl = getStreamingAudioImpl(); pitch = 1.0 + mapWindVecToPitch(wind_vec);
if ( saimpl ) center_freq = 80.0 * pow(pitch, 2.5*(mapWindVecToGain(wind_vec) + 1.0));
{
// fmod likes its streaming audio channel gain re-asserted after mWindGen->mTargetFreq = (F32)center_freq;
// master volume change. mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain;
saimpl->setGain(saimpl->getGain()); 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 = NULL;
if (!Check_FMOD_Error(mSystem->getMasterChannelGroup(&master_group), "FMOD::System::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());
}
} }
// //
...@@ -490,78 +491,78 @@ bool LLAudioChannelFMODSTUDIO::updateBuffer() ...@@ -490,78 +491,78 @@ bool LLAudioChannelFMODSTUDIO::updateBuffer()
return false; return false;
} }
return true; return true;
} }
void LLAudioChannelFMODSTUDIO::update3DPosition() void LLAudioChannelFMODSTUDIO::update3DPosition()
{ {
if (!mChannelp) if (!mChannelp)
{ {
// We're not actually a live channel (i.e., we're not playing back anything) // We're not actually a live channel (i.e., we're not playing back anything)
return; return;
} }
LLAudioBufferFMODSTUDIO *bufferp = (LLAudioBufferFMODSTUDIO *)mCurrentBufferp; LLAudioBufferFMODSTUDIO *bufferp = (LLAudioBufferFMODSTUDIO *)mCurrentBufferp;
if (!bufferp) if (!bufferp)
{ {
// We don't have a buffer associated with us (should really have been picked up // We don't have a buffer associated with us (should really have been picked up
// by the above if. // by the above if.
return; return;
} }
if (mCurrentSourcep->isForcedPriority()) if (mCurrentSourcep->isForcedPriority())
{ {
// Prioritized UI and preview sounds don't need to do any positional updates. // Prioritized UI and preview sounds don't need to do any positional updates.
set3DMode(false); set3DMode(false);
} }
else else
{ {
// Localized sound. Update the position and velocity of the sound. // Localized sound. Update the position and velocity of the sound.
set3DMode(true); set3DMode(true);
LLVector3 float_pos; LLVector3 float_pos;
float_pos.setVec(mCurrentSourcep->getPositionGlobal()); float_pos.setVec(mCurrentSourcep->getPositionGlobal());
FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV); FMOD_RESULT result = mChannelp->set3DAttributes((FMOD_VECTOR*)float_pos.mV, (FMOD_VECTOR*)mCurrentSourcep->getVelocity().mV);
Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes"); Check_FMOD_Error(result, "FMOD::Channel::set3DAttributes");
} }
} }
void LLAudioChannelFMODSTUDIO::updateLoop() void LLAudioChannelFMODSTUDIO::updateLoop()
{ {
if (!mChannelp) if (!mChannelp)
{ {
// May want to clear up the loop/sample counters. // May want to clear up the loop/sample counters.
return; return;
} }
// //
// Hack: We keep track of whether we looped or not by seeing when the // 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 // sample position looks like it's going backwards. Not reliable; may
// yield false negatives. // yield false negatives.
// //
U32 cur_pos; U32 cur_pos;
Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES),"FMOD::Channel::getPosition"); Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES),"FMOD::Channel::getPosition");
if (cur_pos < (U32)mLastSamplePos) if (cur_pos < (U32)mLastSamplePos)
{ {
mLoopedThisFrame = true; mLoopedThisFrame = true;
} }
mLastSamplePos = cur_pos; mLastSamplePos = cur_pos;
} }
void LLAudioChannelFMODSTUDIO::cleanup() void LLAudioChannelFMODSTUDIO::cleanup()
{ {
if (!mChannelp) if (!mChannelp)
{ {
//LL_INFOS() << "Aborting cleanup with no channel handle." << LL_ENDL; // Aborting cleanup with no channel handle.
return; return;
} }
//LL_INFOS() << "Cleaning up channel: " << mChannelID << LL_ENDL; //Cleaning up channel mChannelID
Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); Check_FMOD_Error(mChannelp->stop(), "FMOD::Channel::stop");
mCurrentBufferp = nullptr; mCurrentBufferp = nullptr;
mChannelp = nullptr; mChannelp = nullptr;
...@@ -570,55 +571,55 @@ void LLAudioChannelFMODSTUDIO::cleanup() ...@@ -570,55 +571,55 @@ void LLAudioChannelFMODSTUDIO::cleanup()
void LLAudioChannelFMODSTUDIO::play() void LLAudioChannelFMODSTUDIO::play()
{ {
if (!mChannelp) if (!mChannelp)
{ {
LL_WARNS() << "Playing without a channel handle, aborting" << LL_ENDL; LL_WARNS() << "Playing without a channel handle, aborting" << LL_ENDL;
return; return;
} }
Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::setPaused"); Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::setPaused");
getSource()->setPlayedOnce(true); getSource()->setPlayedOnce(true);
if(LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()]) if (LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()])
Check_FMOD_Error(mChannelp->setChannelGroup(LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()]),"FMOD::Channel::setChannelGroup"); Check_FMOD_Error(mChannelp->setChannelGroup(LLAudioEngine_FMODSTUDIO::mChannelGroups[getSource()->getType()]),"FMOD::Channel::setChannelGroup");
} }
void LLAudioChannelFMODSTUDIO::playSynced(LLAudioChannel *channelp) void LLAudioChannelFMODSTUDIO::playSynced(LLAudioChannel *channelp)
{ {
LLAudioChannelFMODSTUDIO *fmod_channelp = (LLAudioChannelFMODSTUDIO*)channelp; LLAudioChannelFMODSTUDIO *fmod_channelp = (LLAudioChannelFMODSTUDIO*)channelp;
if (!(fmod_channelp->mChannelp && mChannelp)) if (!(fmod_channelp->mChannelp && mChannelp))
{ {
// Don't have channels allocated to both the master and the slave // Don't have channels allocated to both the master and the slave
return; return;
} }
U32 cur_pos; U32 cur_pos;
if(Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position")) if (Check_FMOD_Error(mChannelp->getPosition(&cur_pos, FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position"))
return; return;
cur_pos %= mCurrentBufferp->getLength(); 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 // Try to match the position of our sync master
play(); Check_FMOD_Error(mChannelp->setPosition(cur_pos, FMOD_TIMEUNIT_PCMBYTES), "Unable to set current position");
// Start us playing
play();
} }
bool LLAudioChannelFMODSTUDIO::isPlaying() bool LLAudioChannelFMODSTUDIO::isPlaying()
{ {
if (!mChannelp) if (!mChannelp)
{ {
return false; return false;
} }
bool paused, playing; bool paused, playing;
Check_FMOD_Error(mChannelp->getPaused(&paused),"FMOD::Channel::getPaused"); Check_FMOD_Error(mChannelp->getPaused(&paused),"FMOD::Channel::getPaused");
Check_FMOD_Error(mChannelp->isPlaying(&playing),"FMOD::Channel::isPlaying"); Check_FMOD_Error(mChannelp->isPlaying(&playing),"FMOD::Channel::isPlaying");
return !paused && playing; return !paused && playing;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment