Sync FMOD Studio implementation with upstream so we can keep our version when merging with upstream

master
Ansariel 2020-04-09 00:43:56 +02:00
parent db30295c98
commit 041d5a5c0b
6 changed files with 1079 additions and 1038 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
/**
* @file audioengine_fmodstudio.h
* @brief Definition of LLAudioEngine class abstracting the audio
* support as a FMOD Studio implementation
* support as a FMODSTUDIO implementation
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
* 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
* modify it under the terms of the GNU Lesser General Public
@ -103,8 +103,8 @@ public:
class LLAudioChannelFMODSTUDIO : public LLAudioChannel
{
public:
LLAudioChannelFMODSTUDIO(FMOD::System *audioengine);
virtual ~LLAudioChannelFMODSTUDIO();
LLAudioChannelFMODSTUDIO(FMOD::System *audioengine);
virtual ~LLAudioChannelFMODSTUDIO();
protected:
/*virtual*/ void play();
@ -128,8 +128,8 @@ protected:
class LLAudioBufferFMODSTUDIO : public LLAudioBuffer
{
public:
LLAudioBufferFMODSTUDIO(FMOD::System *audioengine);
virtual ~LLAudioBufferFMODSTUDIO();
LLAudioBufferFMODSTUDIO(FMOD::System *audioengine);
virtual ~LLAudioBufferFMODSTUDIO();
/*virtual*/ bool loadWAV(const std::string& filename);
/*virtual*/ U32 getLength();

View File

@ -1,11 +1,11 @@
/**
* @file listener_fmodstudio.cpp
* @brief Implementation of LISTENER class abstracting the audio
* support as a FMOD Studio implementation
* support as a FMODSTUDIO implementation
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2020, 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
@ -28,17 +28,15 @@
#include "linden_common.h"
#include "llaudioengine.h"
#include "lllistener_fmodstudio.h"
#include "fmod.hpp"
#include "fmodstudio/fmod.hpp"
//-----------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------
LLListener_FMODSTUDIO::LLListener_FMODSTUDIO(FMOD::System *system)
: LLListener(),
mDopplerFactor(1.0f),
mRolloffFactor(1.0f)
LLListener_FMODSTUDIO::LLListener_FMODSTUDIO(FMOD::System *system)
{
mSystem = system;
mSystem = system;
init();
}
//-----------------------------------------------------------------------
@ -46,82 +44,90 @@ 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);
LLListener::translate(offset);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, NULL, NULL);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, NULL, NULL);
}
//-----------------------------------------------------------------------
void LLListener_FMODSTUDIO::setPosition(LLVector3 pos)
{
LLListener::setPosition(pos);
LLListener::setPosition(pos);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, NULL, NULL);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*)mPosition.mV, NULL, NULL, NULL);
}
//-----------------------------------------------------------------------
void LLListener_FMODSTUDIO::setVelocity(LLVector3 vel)
{
LLListener::setVelocity(vel);
LLListener::setVelocity(vel);
mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, NULL, NULL);
mSystem->set3DListenerAttributes(0, NULL, (FMOD_VECTOR*)mVelocity.mV, NULL, NULL);
}
//-----------------------------------------------------------------------
void LLListener_FMODSTUDIO::orient(LLVector3 up, LLVector3 at)
{
LLListener::orient(up, at);
LLListener::orient(up, at);
mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV);
mSystem->set3DListenerAttributes(0, NULL, NULL, (FMOD_VECTOR*)at.mV, (FMOD_VECTOR*)up.mV);
}
//-----------------------------------------------------------------------
void LLListener_FMODSTUDIO::commitDeferredChanges()
{
if(!mSystem)
{
return;
}
if (!mSystem)
{
return;
}
mSystem->update();
mSystem->update();
}
void LLListener_FMODSTUDIO::setRolloffFactor(F32 factor)
{
//An internal FMOD Studio 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 fmod studio, which makes it not skip 3D processing next update call.
if(mRolloffFactor != factor)
{
LLVector3 tmp_pos = mPosition - LLVector3(0.f,0.f,.1f);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*) tmp_pos.mV, NULL, NULL, NULL);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*) mPosition.mV, NULL, NULL, NULL);
}
mRolloffFactor = factor;
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
//An internal FMOD optimization skips 3D updates if there have not been changes to the 3D sound environment.
// (this was true for FMODex, looks to be still true for FMOD STUDIO)
//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 fmod, which makes it not skip 3D processing next update call.
if (mRolloffFactor != factor)
{
LLVector3 pos = mPosition - LLVector3(0.f,0.f,.1f);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*) pos.mV, NULL, NULL, NULL);
mSystem->set3DListenerAttributes(0, (FMOD_VECTOR*) mPosition.mV, NULL, NULL, NULL);
}
mRolloffFactor = factor;
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
}
F32 LLListener_FMODSTUDIO::getRolloffFactor()
{
return mRolloffFactor;
return mRolloffFactor;
}
void LLListener_FMODSTUDIO::setDopplerFactor(F32 factor)
{
mDopplerFactor = factor;
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
mDopplerFactor = factor;
mSystem->set3DSettings(mDopplerFactor, 1.f, mRolloffFactor);
}
F32 LLListener_FMODSTUDIO::getDopplerFactor()
{
return mDopplerFactor;
return mDopplerFactor;
}

View File

@ -3,9 +3,9 @@
* @brief Description of LISTENER class abstracting the audio support
* as an FMOD Studio implementation (windows and Linux)
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2020, 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
@ -39,24 +39,25 @@ namespace FMOD
//Interfaces
class LLListener_FMODSTUDIO : public LLListener
{
public:
LLListener_FMODSTUDIO(FMOD::System *system);
virtual ~LLListener_FMODSTUDIO();
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 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;
virtual void setDopplerFactor(F32 factor);
virtual F32 getDopplerFactor();
virtual void setRolloffFactor(F32 factor);
virtual F32 getRolloffFactor();
protected:
FMOD::System *mSystem;
F32 mDopplerFactor;
F32 mRolloffFactor;
};
#endif

View File

@ -2,9 +2,9 @@
* @file streamingaudio_fmodstudio.cpp
* @brief LLStreamingAudio_FMODSTUDIO implementation
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2020, 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
@ -28,38 +28,38 @@
#include "llmath.h"
#include "fmod.hpp"
#include "fmod_errors.h"
#include "fmodstudio/fmod.hpp"
#include "fmodstudio/fmod_errors.h"
#include "llstreamingaudio_fmodstudio.h"
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;
if (result == FMOD_OK)
return false;
LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL;
return true;
}
class LLAudioStreamManagerFMODSTUDIO
{
public:
LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url);
FMOD::Channel* startStream();
bool stopStream(); // Returns true if the stream was successfully stopped.
bool ready();
LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, 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; }
const std::string& getURL() { return mInternetStreamURL; }
FMOD_RESULT getOpenState(FMOD_OPENSTATE& openstate, unsigned int* percentbuffered = NULL, bool* starving = NULL, bool* diskbusy = NULL);
FMOD_RESULT getOpenState(FMOD_OPENSTATE& openstate, unsigned int* percentbuffered = NULL, bool* starving = NULL, bool* diskbusy = NULL);
protected:
FMOD::System* mSystem;
FMOD::ChannelGroup* mChannelGroup;
FMOD::Channel* mStreamChannel;
FMOD::Sound* mInternetStream;
bool mReady;
FMOD::System* mSystem;
FMOD::ChannelGroup* mChannelGroup;
FMOD::Channel* mStreamChannel;
FMOD::Sound* mInternetStream;
bool mReady;
std::string mInternetStreamURL;
std::string mInternetStreamURL;
};
@ -68,290 +68,290 @@ protected:
// Internet Streaming
//---------------------------------------------------------------------------
LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) :
mSystem(system),
mCurrentInternetStreamp(NULL),
mStreamGroup(NULL),
mFMODInternetStreamChannelp(NULL),
mGain(1.0f)
mSystem(system),
mCurrentInternetStreamp(NULL),
mStreamGroup(NULL),
mFMODInternetStreamChannelp(NULL),
mGain(1.0f)
{
FMOD_RESULT result;
FMOD_RESULT result;
// 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
result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
Check_FMOD_Error(result, "FMOD::System::setStreamBufferSize");
// 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
result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
Check_FMOD_Error(result, "FMOD::System::setStreamBufferSize");
Check_FMOD_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup");
Check_FMOD_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup");
}
LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO()
{
stop();
for (U32 i = 0; i < 100; ++i)
{
if (releaseDeadStreams())
break;
ms_sleep(10);
}
stop();
for (U32 i = 0; i < 100; ++i)
{
if (releaseDeadStreams())
break;
ms_sleep(10);
}
}
void LLStreamingAudio_FMODSTUDIO::start(const std::string& url)
{
//if (!mInited)
//{
// LL_WARNS() << "startInternetStream before audio initialized" << LL_ENDL;
// return;
//}
//if (!mInited)
//{
// LL_WARNS() << "startInternetStream before audio initialized" << LL_ENDL;
// return;
//}
// "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
stop();
// "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
stop();
if (!url.empty())
{
if(mDeadStreams.empty())
{
LL_INFOS() << "Starting internet stream: " << url << LL_ENDL;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, url);
mURL = url;
}
else
{
LL_INFOS() << "Deferring stream load until buffer release: " << url << LL_ENDL;
mPendingURL = url;
}
}
else
{
LL_INFOS() << "Set internet stream to null" << LL_ENDL;
mURL.clear();
}
if (!url.empty())
{
if(mDeadStreams.empty())
{
LL_INFOS() << "Starting internet stream: " << url << LL_ENDL;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, url);
mURL = url;
}
else
{
LL_INFOS() << "Deferring stream load until buffer release: " << url << LL_ENDL;
mPendingURL = url;
}
}
else
{
LL_INFOS() << "Set internet stream to null" << LL_ENDL;
mURL.clear();
}
}
void LLStreamingAudio_FMODSTUDIO::update()
{
if (!releaseDeadStreams())
{
llassert_always(mCurrentInternetStreamp == NULL);
return;
}
if (!releaseDeadStreams())
{
llassert_always(mCurrentInternetStreamp == NULL);
return;
}
if(!mPendingURL.empty())
{
llassert_always(mCurrentInternetStreamp == NULL);
LL_INFOS() << "Starting internet stream: " << mPendingURL << LL_ENDL;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, mPendingURL);
mURL = mPendingURL;
mPendingURL.clear();
}
if(!mPendingURL.empty())
{
llassert_always(mCurrentInternetStreamp == NULL);
LL_INFOS() << "Starting internet stream: " << mPendingURL << LL_ENDL;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, mPendingURL);
mURL = mPendingURL;
mPendingURL.clear();
}
// Don't do anything if there are no streams playing
if (!mCurrentInternetStreamp)
{
return;
}
// Don't do anything if there are no streams playing
if (!mCurrentInternetStreamp)
{
return;
}
unsigned int progress;
bool starving;
bool diskbusy;
FMOD_OPENSTATE open_state;
FMOD_RESULT result = mCurrentInternetStreamp->getOpenState(open_state, &progress, &starving, &diskbusy);
unsigned int progress;
bool starving;
bool diskbusy;
FMOD_OPENSTATE open_state;
FMOD_RESULT result = mCurrentInternetStreamp->getOpenState(open_state, &progress, &starving, &diskbusy);
if (result != FMOD_OK || open_state == FMOD_OPENSTATE_ERROR)
{
stop();
return;
}
else if (open_state == FMOD_OPENSTATE_READY)
{
// Stream is live
if (result != FMOD_OK || open_state == FMOD_OPENSTATE_ERROR)
{
stop();
return;
}
else 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());
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused");
}
}
// start the stream if it's ready
if (!mFMODInternetStreamChannelp &&
(mFMODInternetStreamChannelp = mCurrentInternetStreamp->startStream()))
{
// Reset volume to previously set volume
setGain(getGain());
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused");
}
}
if(mFMODInternetStreamChannelp)
{
FMOD::Sound *sound = NULL;
if (mFMODInternetStreamChannelp)
{
FMOD::Sound *sound = NULL;
if (!Check_FMOD_Error(mFMODInternetStreamChannelp->getCurrentSound(&sound), "FMOD::Channel::getCurrentSound") && sound)
{
FMOD_TAG tag;
S32 tagcount, dirtytagcount;
if (!Check_FMOD_Error(mFMODInternetStreamChannelp->getCurrentSound(&sound), "FMOD::Channel::getCurrentSound") && sound)
{
FMOD_TAG tag;
S32 tagcount, dirtytagcount;
if (!Check_FMOD_Error(sound->getNumTags(&tagcount, &dirtytagcount), "FMOD::Sound::getNumTags") && dirtytagcount)
{
LL_DEBUGS("StreamMetadata") << "Tag count: " << tagcount << " Dirty tag count: " << dirtytagcount << LL_ENDL;
if (!Check_FMOD_Error(sound->getNumTags(&tagcount, &dirtytagcount), "FMOD::Sound::getNumTags") && dirtytagcount)
{
LL_DEBUGS("StreamMetadata") << "Tag count: " << tagcount << " Dirty tag count: " << dirtytagcount << LL_ENDL;
// <DKO> Stream metadata - originally by Shyotl Khur
mMetadata.clear();
mNewMetadata = true;
// </DKO>
for(S32 i = 0; i < tagcount; ++i)
{
if(Check_FMOD_Error(sound->getTag(NULL, i, &tag), "FMOD::Sound::getTag"))
continue;
// <DKO> Stream metadata - originally by Shyotl Khur
mMetadata.clear();
mNewMetadata = true;
// </DKO>
for(S32 i = 0; i < tagcount; ++i)
{
if(Check_FMOD_Error(sound->getTag(NULL, i, &tag), "FMOD::Sound::getTag"))
continue;
LL_DEBUGS("StreamMetadata") << "Tag name: " << tag.name << " - Tag type: " << tag.type << " - Tag data type: " << tag.datatype << LL_ENDL;
LL_DEBUGS("StreamMetadata") << "Tag name: " << tag.name << " - Tag type: " << tag.type << " - Tag data type: " << tag.datatype << LL_ENDL;
std::string name = tag.name;
switch(tag.type)
{
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_VORBISCOMMENT):
{
if(name == "title") name = "TITLE";
else if(name == "artist") name = "ARTIST";
break;
}
case(FMOD_TAGTYPE_FMOD):
{
if (!strcmp(tag.name, "Sample Rate Change"))
{
LL_INFOS() << "Stream forced changing sample rate to " << *((float *)tag.data) << LL_ENDL;
mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data));
}
continue;
}
default:
break;
}
switch(tag.datatype)
{
case(FMOD_TAGDATATYPE_INT):
{
(mMetadata)[name]=*(LLSD::Integer*)(tag.data);
LL_DEBUGS("StreamMetadata") << tag.name << ": " << *(int*)(tag.data) << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_FLOAT):
{
(mMetadata)[name]=*(LLSD::Real*)(tag.data);
LL_DEBUGS("StreamMetadata") << tag.name << ": " << *(float*)(tag.data) << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING):
{
std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen));
(mMetadata)[name]=out;
LL_DEBUGS("StreamMetadata") << tag.name << ": " << out << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING_UTF8):
{
std::string out((char*)tag.data);
(mMetadata)[name]=out;
LL_DEBUGS("StreamMetadata") << tag.name << ": " << out << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING_UTF16):
{
std::string out((char*)tag.data,tag.datalen);
(mMetadata)[std::string(tag.name)]=out;
LL_DEBUGS("StreamMetadata") << tag.name << ": " << out << LL_ENDL;
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;
LL_DEBUGS("StreamMetadata") << tag.name << ": " << out << LL_ENDL;
break;
}
default:
break;
}
}
}
std::string name = tag.name;
switch(tag.type)
{
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_VORBISCOMMENT):
{
if(name == "title") name = "TITLE";
else if(name == "artist") name = "ARTIST";
break;
}
case(FMOD_TAGTYPE_FMOD):
{
if (!strcmp(tag.name, "Sample Rate Change"))
{
LL_INFOS() << "Stream forced changing sample rate to " << *((float *)tag.data) << LL_ENDL;
mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data));
}
continue;
}
default:
break;
}
switch(tag.datatype)
{
case(FMOD_TAGDATATYPE_INT):
{
(mMetadata)[name]=*(LLSD::Integer*)(tag.data);
LL_DEBUGS("StreamMetadata") << tag.name << ": " << *(int*)(tag.data) << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_FLOAT):
{
(mMetadata)[name]=*(LLSD::Real*)(tag.data);
LL_DEBUGS("StreamMetadata") << tag.name << ": " << *(float*)(tag.data) << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING):
{
std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen));
(mMetadata)[name]=out;
LL_DEBUGS("StreamMetadata") << tag.name << ": " << out << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING_UTF8):
{
std::string out((char*)tag.data);
(mMetadata)[name]=out;
LL_DEBUGS("StreamMetadata") << tag.name << ": " << out << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING_UTF16):
{
std::string out((char*)tag.data,tag.datalen);
(mMetadata)[std::string(tag.name)]=out;
LL_DEBUGS("StreamMetadata") << tag.name << ": " << out << LL_ENDL;
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;
LL_DEBUGS("StreamMetadata") << tag.name << ": " << out << LL_ENDL;
break;
}
default:
break;
}
}
}
if(starving)
{
bool paused = false;
if (mFMODInternetStreamChannelp->getPaused(&paused) == FMOD_OK && !paused)
{
LL_INFOS() << "Stream starvation detected! Pausing stream until buffer nearly full." << LL_ENDL;
LL_INFOS() << " (diskbusy="<<diskbusy<<")" << LL_ENDL;
LL_INFOS() << " (progress="<<progress<<")" << LL_ENDL;
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused");
}
}
else if(progress > 80)
{
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused");
}
}
}
if (starving)
{
bool paused = false;
if (mFMODInternetStreamChannelp->getPaused(&paused) == FMOD_OK && !paused)
{
LL_INFOS() << "Stream starvation detected! Pausing stream until buffer nearly full." << LL_ENDL;
LL_INFOS() << " (diskbusy="<<diskbusy<<")" << LL_ENDL;
LL_INFOS() << " (progress="<<progress<<")" << LL_ENDL;
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused");
}
}
else if (progress > 80)
{
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused");
}
}
}
}
void LLStreamingAudio_FMODSTUDIO::stop()
{
mPendingURL.clear();
mPendingURL.clear();
if (mFMODInternetStreamChannelp)
{
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused");
Check_FMOD_Error(mFMODInternetStreamChannelp->setPriority(0), "FMOD::Channel::setPriority");
mFMODInternetStreamChannelp = NULL;
}
if (mFMODInternetStreamChannelp)
{
Check_FMOD_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused");
Check_FMOD_Error(mFMODInternetStreamChannelp->setPriority(0), "FMOD::Channel::setPriority");
mFMODInternetStreamChannelp = NULL;
}
if (mCurrentInternetStreamp)
{
LL_INFOS() << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
if (mCurrentInternetStreamp->stopStream())
{
delete mCurrentInternetStreamp;
}
else
{
LL_WARNS() << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
mDeadStreams.push_back(mCurrentInternetStreamp);
}
mCurrentInternetStreamp = NULL;
}
if (mCurrentInternetStreamp)
{
LL_INFOS() << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
if (mCurrentInternetStreamp->stopStream())
{
delete mCurrentInternetStreamp;
}
else
{
LL_WARNS() << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
mDeadStreams.push_back(mCurrentInternetStreamp);
}
mCurrentInternetStreamp = NULL;
}
}
void LLStreamingAudio_FMODSTUDIO::pause(int pauseopt)
{
if (pauseopt < 0)
{
pauseopt = mCurrentInternetStreamp ? 1 : 0;
}
if (pauseopt < 0)
{
pauseopt = mCurrentInternetStreamp ? 1 : 0;
}
if (pauseopt)
{
if (mCurrentInternetStreamp)
{
stop();
}
}
else
{
start(getURL());
}
if (pauseopt)
{
if (mCurrentInternetStreamp)
{
stop();
}
}
else
{
start(getURL());
}
}
@ -359,179 +359,179 @@ void LLStreamingAudio_FMODSTUDIO::pause(int pauseopt)
// 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;
}
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;
return mGain;
}
std::string LLStreamingAudio_FMODSTUDIO::getURL()
{
return mURL;
return mURL;
}
void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol)
{
mGain = vol;
mGain = vol;
if (mFMODInternetStreamChannelp)
{
vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here?
if (mFMODInternetStreamChannelp)
{
vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here?
Check_FMOD_Error(mFMODInternetStreamChannelp->setVolume(vol), "FMOD::Channel::setVolume");
}
Check_FMOD_Error(mFMODInternetStreamChannelp->setVolume(vol), "FMOD::Channel::setVolume");
}
}
// <DKO> Streamtitle display
// virtual
bool LLStreamingAudio_FMODSTUDIO::getNewMetadata(LLSD& metadata)
{
if (mCurrentInternetStreamp)
{
if (mNewMetadata)
{
metadata = mMetadata;
mNewMetadata = false;
return true;
}
return mNewMetadata;
}
if (mCurrentInternetStreamp)
{
if (mNewMetadata)
{
metadata = mMetadata;
mNewMetadata = false;
return true;
}
return mNewMetadata;
}
metadata = LLSD();
return false;
metadata = LLSD();
return false;
}
// </DKO>
///////////////////////////////////////////////////////
// manager of possibly-multiple internet audio streams
LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url) :
mSystem(system),
mChannelGroup(group),
mStreamChannel(NULL),
mInternetStream(NULL),
mReady(false)
mSystem(system),
mChannelGroup(group),
mStreamChannel(NULL),
mInternetStream(NULL),
mReady(false)
{
mInternetStreamURL = url;
mInternetStreamURL = url;
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, 0, &mInternetStream);
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, 0, &mInternetStream);
if (result!= FMOD_OK)
{
LL_WARNS() << "Couldn't open fmod stream, error "
<< FMOD_ErrorString(result)
<< LL_ENDL;
mReady = false;
return;
}
if (result!= FMOD_OK)
{
LL_WARNS() << "Couldn't open fmod stream, error "
<< FMOD_ErrorString(result)
<< LL_ENDL;
mReady = false;
return;
}
mReady = true;
mReady = true;
}
FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream()
{
// We need a live and opened stream before we try and play it.
FMOD_OPENSTATE open_state;
if (getOpenState(open_state) != FMOD_OK || open_state != FMOD_OPENSTATE_READY)
{
LL_WARNS() << "No internet stream to start playing!" << LL_ENDL;
return NULL;
}
// We need a live and opened stream before we try and play it.
FMOD_OPENSTATE open_state;
if (getOpenState(open_state) != FMOD_OK || open_state != FMOD_OPENSTATE_READY)
{
LL_WARNS() << "No internet stream to start playing!" << LL_ENDL;
return NULL;
}
if(mStreamChannel)
return mStreamChannel; //Already have a channel for this stream.
if(mStreamChannel)
return mStreamChannel; //Already have a channel for this stream.
Check_FMOD_Error(mSystem->playSound(mInternetStream, mChannelGroup, true, &mStreamChannel), "FMOD::System::playSound");
return mStreamChannel;
Check_FMOD_Error(mSystem->playSound(mInternetStream, mChannelGroup, true, &mStreamChannel), "FMOD::System::playSound");
return mStreamChannel;
}
bool LLAudioStreamManagerFMODSTUDIO::stopStream()
{
if (mInternetStream)
{
bool close = true;
FMOD_OPENSTATE open_state;
if (getOpenState(open_state) == FMOD_OK)
{
switch (open_state)
{
case FMOD_OPENSTATE_CONNECTING:
close = false;
break;
default:
close = true;
}
}
if (mInternetStream)
{
bool close = true;
FMOD_OPENSTATE open_state;
if (getOpenState(open_state) == FMOD_OK)
{
switch (open_state)
{
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;
}
if (close && mInternetStream->release() == FMOD_OK)
{
mStreamChannel = NULL;
mInternetStream = NULL;
return true;
}
else
{
return false;
}
}
else
{
return true;
}
}
FMOD_RESULT LLAudioStreamManagerFMODSTUDIO::getOpenState(FMOD_OPENSTATE& state, unsigned int* percentbuffered, bool* starving, bool* diskbusy)
{
if (!mInternetStream)
return FMOD_ERR_INVALID_HANDLE;
FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy);
Check_FMOD_Error(result, "FMOD::Sound::getOpenState");
return result;
if (!mInternetStream)
return FMOD_ERR_INVALID_HANDLE;
FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy);
Check_FMOD_Error(result, "FMOD::Sound::getOpenState");
return result;
}
void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime)
{
Check_FMOD_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize");
FMOD_ADVANCEDSETTINGS settings;
memset(&settings,0,sizeof(settings));
settings.cbSize=sizeof(settings);
settings.defaultDecodeBufferSize = decodebuffertime;//ms
Check_FMOD_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings");
Check_FMOD_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize");
FMOD_ADVANCEDSETTINGS settings;
memset(&settings,0,sizeof(settings));
settings.cbSize=sizeof(settings);
settings.defaultDecodeBufferSize = decodebuffertime;//ms
Check_FMOD_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings");
}
bool LLStreamingAudio_FMODSTUDIO::releaseDeadStreams()
{
// Kill dead internet streams, if possible
std::list<LLAudioStreamManagerFMODSTUDIO *>::iterator iter;
for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
{
LLAudioStreamManagerFMODSTUDIO *streamp = *iter;
if (streamp->stopStream())
{
LL_INFOS() << "Closed dead stream" << LL_ENDL;
delete streamp;
mDeadStreams.erase(iter++);
}
else
{
iter++;
}
}
// Kill dead internet streams, if possible
std::list<LLAudioStreamManagerFMODSTUDIO *>::iterator iter;
for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
{
LLAudioStreamManagerFMODSTUDIO *streamp = *iter;
if (streamp->stopStream())
{
LL_INFOS() << "Closed dead stream" << LL_ENDL;
delete streamp;
mDeadStreams.erase(iter++);
}
else
{
iter++;
}
}
return mDeadStreams.empty();
return mDeadStreams.empty();
}

View File

@ -2,9 +2,9 @@
* @file streamingaudio_fmodstudio.h
* @brief Definition of LLStreamingAudio_FMODSTUDIO implementation
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
* Copyright (C) 2020, 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
@ -45,41 +45,41 @@ namespace FMOD
//Interfaces
class LLStreamingAudio_FMODSTUDIO : public LLStreamingAudioInterface
{
public:
LLStreamingAudio_FMODSTUDIO(FMOD::System *system);
/*virtual*/ ~LLStreamingAudio_FMODSTUDIO();
public:
LLStreamingAudio_FMODSTUDIO(FMOD::System *system);
/*virtual*/ ~LLStreamingAudio_FMODSTUDIO();
/*virtual*/ void start(const std::string& url);
/*virtual*/ void stop();
/*virtual*/ void pause(S32 pause);
/*virtual*/ void update();
/*virtual*/ S32 isPlaying();
/*virtual*/ void setGain(F32 vol);
/*virtual*/ F32 getGain();
/*virtual*/ std::string getURL();
/*virtual*/ void start(const std::string& url);
/*virtual*/ void stop();
/*virtual*/ void pause(S32 pause);
/*virtual*/ void update();
/*virtual*/ S32 isPlaying();
/*virtual*/ void setGain(F32 vol);
/*virtual*/ F32 getGain();
/*virtual*/ std::string getURL();
/*virtual*/ bool supportsAdjustableBufferSizes(){return true;}
/*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime);
//Streamtitle display DKO
virtual bool getNewMetadata(LLSD& metadata);
// DKO
/*virtual*/ bool supportsAdjustableBufferSizes(){return true;}
/*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime);
//Streamtitle display DKO
virtual bool getNewMetadata(LLSD& metadata);
// DKO
private:
bool releaseDeadStreams();
bool releaseDeadStreams();
FMOD::System *mSystem;
FMOD::System *mSystem;
LLAudioStreamManagerFMODSTUDIO *mCurrentInternetStreamp;
FMOD::ChannelGroup* mStreamGroup;
FMOD::Channel *mFMODInternetStreamChannelp;
std::list<LLAudioStreamManagerFMODSTUDIO *> mDeadStreams;
LLAudioStreamManagerFMODSTUDIO *mCurrentInternetStreamp;
FMOD::ChannelGroup* mStreamGroup;
FMOD::Channel *mFMODInternetStreamChannelp;
std::list<LLAudioStreamManagerFMODSTUDIO *> mDeadStreams;
std::string mURL;
std::string mPendingURL;
F32 mGain;
// <DKO> Streamtitle display
bool mNewMetadata;
LLSD mMetadata;
// </DKO> Streamtitle display
std::string mURL;
std::string mPendingURL;
F32 mGain;
// <DKO> Streamtitle display
bool mNewMetadata;
LLSD mMetadata;
// </DKO> Streamtitle display
};