mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-14 02:27:40 -04:00
CWE Directory Reorganization
Rearrange directory structure of CWE to be loosely equivalent to the H'uru Plasma repository. Part 1: Movement of directories and files.
This commit is contained in:
243
Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.cpp
Normal file
243
Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plAudioCaps - Utility class to query and detect available audio options.//
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HeadSpin.h"
|
||||
#include "al.h"
|
||||
#include "alc.h"
|
||||
#include "plEAXEffects.h"
|
||||
|
||||
#include "plAudioCaps.h"
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
#include <eax.h>
|
||||
#include <eaxlegacy.h>
|
||||
#endif
|
||||
#include <DShow.h>
|
||||
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
#define MAX_NUM_SOURCES 128
|
||||
#define kLogMe if( fLog != nil ) fLog->AddLineF(
|
||||
#define MAX_AUDIOCARD_NAME 256
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Detector Functions //////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
plAudioCaps plAudioCapsDetector::fCaps;
|
||||
hsBool plAudioCapsDetector::fGotCaps = false;
|
||||
|
||||
plStatusLog *plAudioCapsDetector::fLog = nil;
|
||||
|
||||
plAudioCapsDetector::plAudioCapsDetector()
|
||||
{
|
||||
}
|
||||
|
||||
plAudioCapsDetector::~plAudioCapsDetector()
|
||||
{
|
||||
}
|
||||
|
||||
//// Detect //////////////////////////////////////////////////////////////////
|
||||
// Our big function that does all of our work
|
||||
|
||||
plAudioCaps &plAudioCapsDetector::Detect( hsBool logIt, hsBool init )
|
||||
{
|
||||
// If we already have the device capabilities, just return them
|
||||
if(fGotCaps) return fCaps;
|
||||
fCaps.fIsAvailable = true;
|
||||
|
||||
ALCdevice * device;
|
||||
ALCcontext * context;
|
||||
|
||||
if(init)
|
||||
{
|
||||
device = alcOpenDevice(0);
|
||||
if(!device)
|
||||
{
|
||||
fCaps.fIsAvailable = false;
|
||||
}
|
||||
|
||||
context = alcCreateContext(device, 0);
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fCaps.fIsAvailable = false;
|
||||
}
|
||||
alcMakeContextCurrent(context);
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
fCaps.fIsAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
EnumerateAudioDevices();
|
||||
|
||||
if( logIt )
|
||||
fLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "audioCaps.log" );
|
||||
else
|
||||
fLog = nil;
|
||||
|
||||
kLogMe 0xff00ff00, "Starting audio caps detection..." );
|
||||
|
||||
// find the max number of sources
|
||||
ALuint sources[MAX_NUM_SOURCES];
|
||||
ALuint i = 0;
|
||||
for(; i < MAX_NUM_SOURCES; i++)
|
||||
{
|
||||
alGenSources(1, &sources[i]);
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
break;
|
||||
fCaps.fMaxNumSources++;
|
||||
}
|
||||
alDeleteSources(i, sources);
|
||||
kLogMe 0xffffffff, "Max Number of sources: %d", i);
|
||||
plStatusLog::AddLineS("audio.log", "Max Number of sources: %d", i);
|
||||
|
||||
// Detect EAX support
|
||||
kLogMe 0xff00ff00, "Attempting to detect EAX support..." );
|
||||
fCaps.fEAXAvailable = IDetectEAX( );
|
||||
|
||||
kLogMe 0xff00ff00, "Audio caps detection COMPLETE." );
|
||||
delete fLog;
|
||||
|
||||
fGotCaps = true; // We've got the device capabilities
|
||||
|
||||
if(init)
|
||||
{
|
||||
alcMakeContextCurrent(nil);
|
||||
alcDestroyContext(context);
|
||||
alcCloseDevice(device);
|
||||
}
|
||||
return fCaps;
|
||||
}
|
||||
|
||||
void plAudioCapsDetector::EnumerateAudioDevices()
|
||||
{
|
||||
ICreateDevEnum *pDevEnum;
|
||||
IEnumMoniker *pEnumMon;
|
||||
IMoniker *pMoniker;
|
||||
ULONG cFetched;
|
||||
HRESULT hr;
|
||||
char audioCardName[MAX_AUDIOCARD_NAME];
|
||||
short *pShort;
|
||||
|
||||
// Enumerate audio devices
|
||||
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pDevEnum);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
hr = pDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumMon, 0);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
while(pEnumMon->Next(1, &pMoniker, &cFetched) == S_OK)
|
||||
{
|
||||
if(pMoniker)
|
||||
{
|
||||
IPropertyBag *pPropBag;
|
||||
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
VARIANT varName;
|
||||
VariantInit(&varName);
|
||||
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
|
||||
memset(audioCardName, 0, MAX_AUDIOCARD_NAME);
|
||||
pShort = varName.piVal;
|
||||
|
||||
// copy from wide character array to char array
|
||||
for(int i = 0; *pShort != 0 && i < MAX_AUDIOCARD_NAME; pShort++, i++)
|
||||
{
|
||||
audioCardName[i] = (char)*pShort;
|
||||
}
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
plStatusLog::AddLineS("audiocaps.log", audioCardName );
|
||||
}
|
||||
VariantClear(&varName);
|
||||
pPropBag->Release();
|
||||
}
|
||||
pMoniker->Release();
|
||||
}
|
||||
}
|
||||
pEnumMon->Release();
|
||||
}
|
||||
pDevEnum->Release();
|
||||
}
|
||||
}
|
||||
|
||||
//// IDetectEAX //////////////////////////////////////////////////////////////
|
||||
// Attempt to actually init the EAX listener.Note that we can potentially do
|
||||
// this even if we didn't load the EAX Unified driver (we could just be
|
||||
// running EAX 3.0 native), so you can really just think of the EAX Unified
|
||||
// init code above as a way of trying to make sure this line here will
|
||||
// succeed as often as possible.
|
||||
|
||||
hsBool plAudioCapsDetector::IDetectEAX( )
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
hsBool gotSupport = true;
|
||||
|
||||
if(!alIsExtensionPresent((ALchar *)"EAX4.0")) // is eax 4 supported
|
||||
{
|
||||
if(!alIsExtensionPresent((ALchar *) "EAX4.0Emulated")) // is an earlier version of eax supported
|
||||
{
|
||||
kLogMe 0xff00ff00, "EAX not supported");
|
||||
gotSupport = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fCaps.fEAXUnified = true;
|
||||
kLogMe 0xff00ff00, "EAX 4 Emulated supported");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kLogMe 0xff00ff00, "EAX 4 available");
|
||||
}
|
||||
return gotSupport;
|
||||
#else
|
||||
kLogMe 0xff00ff00, "EAX disabled in this build");
|
||||
return false;
|
||||
#endif
|
||||
}
|
100
Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.h
Normal file
100
Sources/Plasma/PubUtilLib/plAudio/plAudioCaps.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plAudioCaps - Utility class to query and detect available audio options.//
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _plAudioCaps_h
|
||||
#define _plAudioCaps_h
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "hsTemplates.h"
|
||||
|
||||
class plAudioCapsDetector;
|
||||
class plStatusLog;
|
||||
|
||||
class plAudioCaps
|
||||
{
|
||||
public:
|
||||
|
||||
plAudioCaps() { Clear(); }
|
||||
|
||||
void Clear( void )
|
||||
{
|
||||
fIsAvailable = false;
|
||||
fEAXAvailable = false;
|
||||
fEAXUnified = false;
|
||||
fMaxNumSources = 0;
|
||||
}
|
||||
|
||||
hsBool IsAvailable( void ) const { return fIsAvailable; }
|
||||
hsBool IsEAXAvailable( void ) const { return fEAXAvailable; }
|
||||
hsBool UsingEAXUnified( void ) const { return fEAXUnified; }
|
||||
unsigned GetMaxNumVoices() { return fMaxNumSources; }
|
||||
|
||||
protected:
|
||||
friend class plAudioCapsDetector;
|
||||
|
||||
hsBool fIsAvailable, fEAXAvailable, fEAXUnified;
|
||||
unsigned fMaxNumSources;
|
||||
};
|
||||
|
||||
class plAudioCapsDetector
|
||||
{
|
||||
public:
|
||||
plAudioCapsDetector();
|
||||
virtual ~plAudioCapsDetector();
|
||||
|
||||
static plAudioCaps &Detect( hsBool log = false, hsBool init = false );
|
||||
|
||||
protected:
|
||||
static plStatusLog *fLog;
|
||||
static plAudioCaps fCaps;
|
||||
static hsBool fGotCaps;
|
||||
|
||||
static hsBool IDetectEAX( );
|
||||
static void EnumerateAudioDevices();
|
||||
};
|
||||
|
||||
#endif //_plAudioCaps_h
|
73
Sources/Plasma/PubUtilLib/plAudio/plAudioCreatable.h
Normal file
73
Sources/Plasma/PubUtilLib/plAudio/plAudioCreatable.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
|
||||
#ifndef plAudioCreatable_inc
|
||||
#define plAudioCreatable_inc
|
||||
|
||||
#include "../pnFactory/plCreator.h"
|
||||
|
||||
#include "plAudioSystem.h"
|
||||
|
||||
REGISTER_CREATABLE( plAudioSystem );
|
||||
|
||||
#include "plSound.h"
|
||||
|
||||
REGISTER_NONCREATABLE( plSound );
|
||||
REGISTER_CREATABLE( plSoundVolumeApplicator );
|
||||
|
||||
#include "plWin32Sound.h"
|
||||
#include "plWin32StaticSound.h"
|
||||
#include "plWin32StreamingSound.h"
|
||||
#include "plWin32GroupedSound.h"
|
||||
|
||||
REGISTER_NONCREATABLE( plWin32Sound );
|
||||
REGISTER_CREATABLE( plWin32StaticSound );
|
||||
REGISTER_CREATABLE( plWin32LinkSound );
|
||||
REGISTER_CREATABLE( plWin32StreamingSound );
|
||||
REGISTER_CREATABLE( plWin32GroupedSound );
|
||||
|
||||
#include "plEAXListenerMod.h"
|
||||
REGISTER_CREATABLE( plEAXListenerMod );
|
||||
|
||||
#include "plAudioReaderCreatable.h"
|
||||
|
||||
#endif // plAudioCreatable_inc
|
41
Sources/Plasma/PubUtilLib/plAudio/plAudioReaderCreatable.h
Normal file
41
Sources/Plasma/PubUtilLib/plAudio/plAudioReaderCreatable.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
1299
Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp
Normal file
1299
Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp
Normal file
File diff suppressed because it is too large
Load Diff
280
Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h
Normal file
280
Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h
Normal file
@ -0,0 +1,280 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#ifndef plAudioSystem_h
|
||||
#define plAudioSystem_h
|
||||
|
||||
#include "hsStlUtils.h"
|
||||
#include "hsWindowHndl.h"
|
||||
#include "hsTemplates.h"
|
||||
#include "hsGeometry3.h"
|
||||
#include "../pnKeyedObject/hsKeyedObject.h"
|
||||
|
||||
#define DEFAULT_AUDIO_DEVICE_NAME "Generic Software"
|
||||
|
||||
typedef wchar_t WCHAR;
|
||||
|
||||
class plSound;
|
||||
class plSoftSoundNode;
|
||||
class plgAudioSys;
|
||||
class plStatusLog;
|
||||
class plEAXListenerMod;
|
||||
|
||||
typedef struct ALCdevice_struct ALCdevice;
|
||||
typedef struct ALCcontext_struct ALCcontext;
|
||||
|
||||
|
||||
class DeviceDescriptor
|
||||
{
|
||||
public:
|
||||
DeviceDescriptor(const char *name, hsBool supportsEAX):
|
||||
fDeviceName(name),
|
||||
fSupportsEAX(supportsEAX)
|
||||
{
|
||||
}
|
||||
const char *GetDeviceName() { return fDeviceName.c_str();}
|
||||
hsBool SupportsEAX() { return fSupportsEAX; }
|
||||
|
||||
private:
|
||||
std::string fDeviceName;
|
||||
hsBool fSupportsEAX;
|
||||
};
|
||||
|
||||
class plAudioSystem : public hsKeyedObject
|
||||
{
|
||||
public:
|
||||
plAudioSystem();
|
||||
~plAudioSystem();
|
||||
|
||||
CLASSNAME_REGISTER( plAudioSystem );
|
||||
GETINTERFACE_ANY( plAudioSystem, hsKeyedObject );
|
||||
|
||||
enum
|
||||
{
|
||||
kThreadSndRef = 0,
|
||||
kRefEAXRegion
|
||||
};
|
||||
|
||||
hsBool Init(hsWindowHndl hWnd);
|
||||
void Shutdown();
|
||||
|
||||
void SetActive( hsBool b );
|
||||
|
||||
void SetListenerPos(const hsPoint3 pos);
|
||||
void SetListenerVelocity(const hsVector3 vel);
|
||||
void SetListenerOrientation(const hsVector3 view, const hsVector3 up);
|
||||
void SetMaxNumberOfActiveSounds(); // sets the max number of active sounds based on the priority cutoff
|
||||
void SetDistanceModel(int i);
|
||||
|
||||
virtual hsBool MsgReceive(plMessage* msg);
|
||||
double GetTime();
|
||||
|
||||
void NextDebugSound( void );
|
||||
hsPoint3 GetCurrListenerPos( void ) const { return fCurrListenerPos; }
|
||||
|
||||
int GetNumAudioDevices();
|
||||
const char *GetAudioDeviceName(int index);
|
||||
hsBool SupportsEAX(const char *deviceName);
|
||||
|
||||
void SetFadeLength(float lengthSec);
|
||||
|
||||
protected:
|
||||
|
||||
friend class plgAudioSys;
|
||||
|
||||
ALCdevice * fDevice;
|
||||
ALCcontext * fContext;
|
||||
ALCdevice * fCaptureDevice;
|
||||
|
||||
plSoftSoundNode *fSoftRegionSounds;
|
||||
plSoftSoundNode *fActiveSofts;
|
||||
plStatusLog *fDebugActiveSoundDisplay;
|
||||
|
||||
static Int32 fMaxNumSounds, fNumSoundsSlop; // max number of sounds the engine is allowed to audibly play. Different than fMaxNumSources. That is the max number of sounds the audio card can play
|
||||
plSoftSoundNode *fCurrDebugSound;
|
||||
hsTArray<plKey> fPendingRegisters;
|
||||
|
||||
hsPoint3 fCurrListenerPos;//, fCommittedListenerPos;
|
||||
hsBool fActive, fUsingEAX, fRestartOnDestruct, fWaitingForShutdown;
|
||||
__int64 fStartTime;
|
||||
|
||||
hsTArray<hsKeyedObject *> fMyRefs;
|
||||
hsTArray<plEAXListenerMod *> fEAXRegions;
|
||||
|
||||
hsPoint3 fLastPos;
|
||||
hsBool fAvatarPosSet; // used for listener stuff
|
||||
|
||||
hsBool fDisplayNumBuffers;
|
||||
|
||||
std::vector<DeviceDescriptor> fDeviceList; // list of openal device names
|
||||
|
||||
double fStartFade;
|
||||
float fFadeLength;
|
||||
unsigned int fMaxNumSources; // max openal sources
|
||||
double fLastUpdateTimeMs;
|
||||
|
||||
void RegisterSoftSound( const plKey soundKey );
|
||||
void UnregisterSoftSound( const plKey soundKey );
|
||||
void IUpdateSoftSounds( const hsPoint3 &newPosition );
|
||||
UInt32 IScaleVolume(float volume);
|
||||
void IEnumerateDevices();
|
||||
|
||||
public:
|
||||
hsBool fListenerInit;
|
||||
};
|
||||
|
||||
class plgAudioSys
|
||||
{
|
||||
public:
|
||||
enum ASChannel
|
||||
{
|
||||
kSoundFX,
|
||||
kAmbience,
|
||||
kBgndMusic,
|
||||
kGUI,
|
||||
kNPCVoice,
|
||||
kVoice,
|
||||
kNumChannels
|
||||
};
|
||||
|
||||
enum DebugFlags
|
||||
{
|
||||
kDisableRightSelect = 0x00000001,
|
||||
kDisableLeftSelect = 0x00000002
|
||||
};
|
||||
|
||||
enum AudioMode
|
||||
{
|
||||
kDisabled,
|
||||
kSoftware,
|
||||
kHardware,
|
||||
kHardwarePlusEAX,
|
||||
};
|
||||
static void Init(hsWindowHndl hWnd);
|
||||
static hsBool Hardware() { return fUseHardware; }
|
||||
static void SetUseHardware(hsBool b);
|
||||
static void SetActive(hsBool b);
|
||||
static void SetMuted( hsBool b );
|
||||
static void EnableEAX( hsBool b );
|
||||
static hsBool Active() { return fInit; }
|
||||
static void Shutdown();
|
||||
static void Activate(hsBool b);
|
||||
static hsBool IsMuted( void ) { return fMuted; }
|
||||
static hsWindowHndl hWnd() { return fWnd; }
|
||||
static plAudioSystem* Sys() { return fSys; }
|
||||
static void Restart( void );
|
||||
static hsBool UsingEAX( void ) { return fSys->fUsingEAX; }
|
||||
|
||||
static void NextDebugSound( void );
|
||||
|
||||
static void SetChannelVolume( ASChannel chan, hsScalar vol );
|
||||
static hsScalar GetChannelVolume( ASChannel chan );
|
||||
|
||||
static void Set2D3DBias( hsScalar bias );
|
||||
static hsScalar Get2D3Dbias();
|
||||
|
||||
static void SetGlobalFadeVolume( hsScalar vol );
|
||||
static hsScalar GetGlobalFadeVolume( void ) { return fGlobalFadeVolume; }
|
||||
|
||||
static void SetDebugFlag( UInt32 flag, hsBool set = true ) { if( set ) fDebugFlags |= flag; else fDebugFlags &= ~flag; }
|
||||
static hsBool IsDebugFlagSet( UInt32 flag ) { return fDebugFlags & flag; }
|
||||
static void ClearDebugFlags( void ) { fDebugFlags = 0; }
|
||||
|
||||
static hsScalar GetStreamingBufferSize( void ) { return fStreamingBufferSize; }
|
||||
static void SetStreamingBufferSize( hsScalar size ) { fStreamingBufferSize = size; }
|
||||
|
||||
static UInt8 GetPriorityCutoff( void ) { return fPriorityCutoff; }
|
||||
static void SetPriorityCutoff( UInt8 cut ) { fPriorityCutoff = cut; if(fSys) fSys->SetMaxNumberOfActiveSounds(); }
|
||||
|
||||
static hsBool AreExtendedLogsEnabled( void ) { return fEnableExtendedLogs; }
|
||||
static void EnableExtendedLogs( hsBool e ) { fEnableExtendedLogs = e; }
|
||||
|
||||
static hsScalar GetStreamFromRAMCutoff( void ) { return fStreamFromRAMCutoff; }
|
||||
static void SetStreamFromRAMCutoff( hsScalar c ) { fStreamFromRAMCutoff = c; }
|
||||
|
||||
static void SetListenerPos(const hsPoint3 pos);
|
||||
static void SetListenerVelocity(const hsVector3 vel);
|
||||
static void SetListenerOrientation(const hsVector3 view, const hsVector3 up);
|
||||
|
||||
static void ShowNumBuffers(hsBool b) { if(fSys) fSys->fDisplayNumBuffers = b; }
|
||||
|
||||
static void SetAudioMode(AudioMode mode);
|
||||
static int GetAudioMode();
|
||||
static hsBool LogStreamingUpdates() { return fLogStreamingUpdates; }
|
||||
static void SetLogStreamingUpdates(hsBool logUpdates) { fLogStreamingUpdates = logUpdates; }
|
||||
static void SetDeviceName(const char *device, hsBool restart = false);
|
||||
static const char *GetDeviceName() { return fDeviceName.c_str(); }
|
||||
static int GetNumAudioDevices();
|
||||
static const char *GetAudioDeviceName(int index);
|
||||
static ALCdevice *GetCaptureDevice();
|
||||
static hsBool SupportsEAX(const char *deviceName);
|
||||
static void RegisterSoftSound( const plKey soundKey );
|
||||
static void UnregisterSoftSound( const plKey soundKey );
|
||||
|
||||
static hsBool IsRestarting() {return fRestarting;}
|
||||
|
||||
private:
|
||||
friend class plAudioSystem;
|
||||
|
||||
static plAudioSystem* fSys;
|
||||
static hsBool fInit;
|
||||
static hsBool fActive;
|
||||
static hsBool fMuted;
|
||||
static hsWindowHndl fWnd;
|
||||
static hsBool fUseHardware;
|
||||
static hsBool fDelayedActivate;
|
||||
static hsScalar fChannelVolumes[ kNumChannels ];
|
||||
static hsScalar fGlobalFadeVolume;
|
||||
static UInt32 fDebugFlags;
|
||||
static hsBool fEnableEAX;
|
||||
static hsScalar fStreamingBufferSize;
|
||||
static UInt8 fPriorityCutoff;
|
||||
static hsBool fEnableExtendedLogs;
|
||||
static hsScalar fStreamFromRAMCutoff;
|
||||
static hsScalar f2D3DBias;
|
||||
static hsBool fLogStreamingUpdates;
|
||||
static std::string fDeviceName;
|
||||
static hsBool fRestarting;
|
||||
static hsBool fMutedStateChange;
|
||||
|
||||
};
|
||||
|
||||
#endif //plAudioSystem_h
|
792
Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp
Normal file
792
Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp
Normal file
@ -0,0 +1,792 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plDSoundBuffer - Simple wrapper class for a DirectSound buffer. //
|
||||
// Allows us to simplify all the work done behind the //
|
||||
// scenes in plWin32BufferThread. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "hsThread.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
#include "al.h"
|
||||
|
||||
#include "plgDispatch.h"
|
||||
#include "plAudioSystem.h"
|
||||
#include "../plAudioCore/plAudioCore.h"
|
||||
#include "../plAudioCore/plAudioFileReader.h"
|
||||
#include "plEAXEffects.h"
|
||||
|
||||
#include "plProfile.h"
|
||||
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
#include <dsound.h>
|
||||
|
||||
UInt32 plDSoundBuffer::fNumBuffers = 0;
|
||||
plProfile_CreateCounterNoReset( "Playing", "Sound", SoundPlaying );
|
||||
plProfile_CreateCounterNoReset( "Allocated", "Sound", NumAllocated );
|
||||
|
||||
|
||||
//// Constructor/Destructor //////////////////////////////////////////////////
|
||||
|
||||
plDSoundBuffer::plDSoundBuffer( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool isLooping, hsBool tryStatic, bool streaming )
|
||||
{
|
||||
fLooping = isLooping;
|
||||
fValid = false;
|
||||
fBufferDesc = nil;
|
||||
|
||||
fLockPtr = nil;
|
||||
fLockLength = 0;
|
||||
|
||||
fStreaming = streaming;
|
||||
|
||||
buffer = 0;
|
||||
source = 0;
|
||||
for(int i = 0; i < STREAMING_BUFFERS; ++i)
|
||||
{
|
||||
streamingBuffers[i] = 0;
|
||||
}
|
||||
|
||||
IAllocate( size, bufferDesc, enable3D, tryStatic );
|
||||
fNumBuffers++;
|
||||
}
|
||||
|
||||
plDSoundBuffer::~plDSoundBuffer()
|
||||
{
|
||||
IRelease();
|
||||
fNumBuffers--;
|
||||
|
||||
}
|
||||
|
||||
//// IAllocate ///////////////////////////////////////////////////////////////
|
||||
|
||||
void plDSoundBuffer::IAllocate( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool tryStatic )
|
||||
{
|
||||
// Create a DSound buffer description
|
||||
fBufferDesc = new plWAVHeader;
|
||||
*fBufferDesc = bufferDesc;
|
||||
fBufferSize = size;
|
||||
|
||||
// Do we want to try EAX?
|
||||
if( plgAudioSys::UsingEAX() )
|
||||
fEAXSource.Init( this );
|
||||
|
||||
fValid = true;
|
||||
plProfile_Inc( NumAllocated );
|
||||
}
|
||||
|
||||
//// IRelease ////////////////////////////////////////////////////////////////
|
||||
|
||||
void plDSoundBuffer::IRelease( void )
|
||||
{
|
||||
if( IsPlaying() )
|
||||
Stop();
|
||||
|
||||
// Release stuff
|
||||
fEAXSource.Release();
|
||||
alSourcei(source, AL_BUFFER, nil);
|
||||
alDeleteSources(1, &source);
|
||||
if(buffer)
|
||||
alDeleteBuffers( 1, &buffer );
|
||||
else
|
||||
alDeleteBuffers(STREAMING_BUFFERS, streamingBuffers);
|
||||
source = 0;
|
||||
buffer = 0;
|
||||
|
||||
alGetError();
|
||||
|
||||
memset(streamingBuffers, 0, STREAMING_BUFFERS * sizeof(unsigned));
|
||||
delete fBufferDesc;
|
||||
fBufferDesc = nil;
|
||||
fBufferSize = 0;
|
||||
|
||||
fValid = false;
|
||||
plProfile_Dec( NumAllocated );
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* OpenAL
|
||||
*
|
||||
***/
|
||||
|
||||
int plDSoundBuffer::IGetALFormat(unsigned bitsPerSample, unsigned int numChannels)
|
||||
{
|
||||
int format = 0;
|
||||
switch(bitsPerSample)
|
||||
{
|
||||
case 8:
|
||||
format = (numChannels == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
|
||||
break;
|
||||
case 16:
|
||||
format = (numChannels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
|
||||
break;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
bool plDSoundBuffer::FillBuffer(void *data, unsigned bytes, plWAVHeader *header)
|
||||
{
|
||||
if(source)
|
||||
{
|
||||
alSourcei(source, AL_BUFFER, nil);
|
||||
alDeleteSources(1, &source);
|
||||
}
|
||||
if(buffer)
|
||||
alDeleteBuffers(1, &buffer);
|
||||
source = 0;
|
||||
buffer = 0;
|
||||
|
||||
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
|
||||
ALenum error = alGetError();
|
||||
alGenBuffers(1, &buffer);
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
alBufferData(buffer, format, data, bytes, header->fNumSamplesPerSec );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to fill sound buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
alGenSources(1, &source);
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Just make it quiet for now
|
||||
SetScalarVolume(0);
|
||||
|
||||
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048);
|
||||
alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
alSourcei(source, AL_BUFFER, buffer);
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to attach buffer to source %d", error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
// OpenAL Streaming functions
|
||||
//============================================================================
|
||||
|
||||
// this function is used when restarting the audio system. It is needed to restart a streaming source from where it left off
|
||||
bool plDSoundBuffer::SetupStreamingSource(plAudioFileReader *stream)
|
||||
{
|
||||
unsigned char data[STREAM_BUFFER_SIZE];
|
||||
unsigned int size;
|
||||
ALenum error;
|
||||
|
||||
alGetError();
|
||||
int numBuffersToQueue = 0;
|
||||
|
||||
// fill buffers with data
|
||||
for( int i = 0; i < STREAMING_BUFFERS; i++ )
|
||||
{
|
||||
size = stream->NumBytesLeft() < STREAM_BUFFER_SIZE ? stream->NumBytesLeft() : STREAM_BUFFER_SIZE;
|
||||
if(!size)
|
||||
{
|
||||
if(IsLooping())
|
||||
{
|
||||
stream->SetPosition(0);
|
||||
}
|
||||
}
|
||||
|
||||
stream->Read(size, data);
|
||||
numBuffersToQueue++;
|
||||
|
||||
alGenBuffers( 1, &streamingBuffers[i] );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
|
||||
alBufferData( streamingBuffers[i], format, data, size, fBufferDesc->fNumSamplesPerSec );
|
||||
if( (error = alGetError()) != AL_NO_ERROR )
|
||||
plStatusLog::AddLineS("audio.log", "alBufferData");
|
||||
}
|
||||
|
||||
// Generate AL Source
|
||||
alGenSources( 1, &source );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source);
|
||||
return false;
|
||||
}
|
||||
alSourcei(source, AL_BUFFER, nil);
|
||||
SetScalarVolume(0);
|
||||
|
||||
|
||||
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048);
|
||||
alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers( source, numBuffersToQueue, streamingBuffers );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to queue buffers %d", error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// this function is used when starting up a streaming sound, as opposed to restarting it due to an audio system restart.
|
||||
bool plDSoundBuffer::SetupStreamingSource(void *data, unsigned bytes)
|
||||
{
|
||||
unsigned char bufferData[STREAM_BUFFER_SIZE];
|
||||
unsigned int size;
|
||||
ALenum error;
|
||||
char *pData = (char *)data;
|
||||
|
||||
alGetError();
|
||||
int numBuffersToQueue = 0;
|
||||
|
||||
// fill buffers with data
|
||||
for( int i = 0; i < STREAMING_BUFFERS; i++ )
|
||||
{
|
||||
size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE;
|
||||
if(!size)
|
||||
break;
|
||||
|
||||
MemCopy(bufferData, pData, size);
|
||||
pData += size;
|
||||
bytes-= size;
|
||||
numBuffersToQueue++;
|
||||
|
||||
alGenBuffers( 1, &streamingBuffers[i] );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
|
||||
alBufferData( streamingBuffers[i], format, bufferData, size, fBufferDesc->fNumSamplesPerSec );
|
||||
if( (error = alGetError()) != AL_NO_ERROR )
|
||||
plStatusLog::AddLineS("audio.log", "alBufferData");
|
||||
}
|
||||
|
||||
// Generate AL Source
|
||||
alGenSources( 1, &source );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source);
|
||||
return false;
|
||||
}
|
||||
alSourcei(source, AL_BUFFER, nil);
|
||||
SetScalarVolume(0);
|
||||
|
||||
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048);
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers( source, numBuffersToQueue, streamingBuffers );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to queue buffers %d", error);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
int plDSoundBuffer::BuffersProcessed( void )
|
||||
{
|
||||
if(alIsSource(source)==AL_FALSE)
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "BuffersProcessed, source invalid");
|
||||
return 0;
|
||||
}
|
||||
ALint processed = 0;
|
||||
alGetSourcei( source, AL_BUFFERS_PROCESSED, &processed );
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "alGetSourcei failed");
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
int plDSoundBuffer::BuffersQueued( void )
|
||||
{
|
||||
if(alIsSource(source)==AL_FALSE) return 0;
|
||||
ALint queued = 0;
|
||||
alGetSourcei( source, AL_BUFFERS_QUEUED, &queued );
|
||||
alGetError();
|
||||
|
||||
return queued;
|
||||
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
bool plDSoundBuffer::StreamingFillBuffer(plAudioFileReader *stream)
|
||||
{
|
||||
if(!source)
|
||||
return false;
|
||||
|
||||
ALenum error;
|
||||
ALuint bufferId;
|
||||
unsigned char data[STREAM_BUFFER_SIZE];
|
||||
int buffersProcessed = BuffersProcessed();
|
||||
hsBool finished = false;
|
||||
|
||||
for(int i = 0; i < buffersProcessed; i++)
|
||||
{
|
||||
alSourceUnqueueBuffers( source, 1, &bufferId );
|
||||
if( (error = alGetError()) != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to unqueue buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!finished)
|
||||
{
|
||||
if(stream->NumBytesLeft() == 0)
|
||||
{
|
||||
// if at anytime we run out of data, and we are looping, reset the data stream and continue to fill buffers
|
||||
if(IsLooping())
|
||||
{
|
||||
stream->SetPosition(0); // we are looping, so reset data stream, and keep filling buffers
|
||||
}
|
||||
else
|
||||
{
|
||||
finished = true; // no more data, but we could still be playing, so we don't want to stop the sound yet
|
||||
}
|
||||
}
|
||||
|
||||
if(!finished)
|
||||
{ unsigned int size = stream->NumBytesLeft() < STREAM_BUFFER_SIZE ? stream->NumBytesLeft() : STREAM_BUFFER_SIZE;
|
||||
stream->Read(size, data);
|
||||
|
||||
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
|
||||
alBufferData( bufferId, format, data, size, fBufferDesc->fNumSamplesPerSec );
|
||||
if( (error = alGetError()) != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to copy data to sound buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers( source, 1, &bufferId );
|
||||
if( (error = alGetError()) != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to queue buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!IsPlaying() && !finished)
|
||||
{
|
||||
alSourcePlay(source);
|
||||
}
|
||||
alGetError();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Voice playback functions
|
||||
*
|
||||
***/
|
||||
|
||||
bool plDSoundBuffer::GetAvailableBufferId(unsigned *bufferId)
|
||||
{
|
||||
if(mAvailableBuffers.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*bufferId = mAvailableBuffers.front();
|
||||
mAvailableBuffers.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool plDSoundBuffer::SetupVoiceSource( void )
|
||||
{
|
||||
ALenum error = alGetError();
|
||||
|
||||
// Generate AL Buffers
|
||||
alGenBuffers( STREAMING_BUFFERS, streamingBuffers );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
|
||||
for( int i = 0; i < STREAMING_BUFFERS; i++ )
|
||||
{
|
||||
mAvailableBuffers.push_back(streamingBuffers[i]);
|
||||
}
|
||||
|
||||
// Generate AL Source
|
||||
alGenSources( 1, &source );
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source);
|
||||
return false;
|
||||
}
|
||||
|
||||
SetScalarVolume(0);
|
||||
|
||||
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048);
|
||||
error = alGetError();
|
||||
if( error != AL_NO_ERROR )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
alSourcei(source, AL_BUFFER, nil);
|
||||
error = alGetError();
|
||||
//alSourcei(source, AL_PITCH, 0);
|
||||
|
||||
// dont queue any buffers here
|
||||
return true;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void plDSoundBuffer::UnQueueVoiceBuffers( void )
|
||||
{
|
||||
unsigned buffersProcessed = BuffersProcessed();
|
||||
if(buffersProcessed)
|
||||
plStatusLog::AddLineS("audio.log", "unqueuing buffers %d", buffersProcessed);
|
||||
for(int i = 0; i < buffersProcessed; i++)
|
||||
{
|
||||
ALuint unQueued;
|
||||
alSourceUnqueueBuffers( source, 1, &unQueued );
|
||||
if(alGetError() == AL_NO_ERROR)
|
||||
{
|
||||
mAvailableBuffers.push_back(unQueued);
|
||||
}
|
||||
else
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to unqueue buffer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
bool plDSoundBuffer::VoiceFillBuffer(void *data, unsigned bytes, unsigned bufferId)
|
||||
{
|
||||
if(!source)
|
||||
return false;
|
||||
|
||||
ALenum error;
|
||||
unsigned int size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE;
|
||||
|
||||
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
|
||||
alBufferData( bufferId, format, data, size, fBufferDesc->fNumSamplesPerSec );
|
||||
if( (error = alGetError()) != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to copy data to sound buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
alSourceQueueBuffers( source, 1, &bufferId );
|
||||
if( (error = alGetError()) != AL_NO_ERROR )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Failed to queue buffer %d", error);
|
||||
return false;
|
||||
}
|
||||
if(!IsPlaying())
|
||||
{
|
||||
alSourcePlay(source);
|
||||
}
|
||||
alGetError();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//// SetLooping //////////////////////////////////////////////////////////////
|
||||
|
||||
void plDSoundBuffer::SetLooping( hsBool loop )
|
||||
{
|
||||
fLooping = loop;
|
||||
}
|
||||
|
||||
void plDSoundBuffer::SetMinDistance( int dist )
|
||||
{
|
||||
alSourcei(source, AL_REFERENCE_DISTANCE, dist);
|
||||
ALenum error;
|
||||
if((error = alGetError()) != AL_NO_ERROR)
|
||||
plStatusLog::AddLineS("audio.log", "Failed to set min distance");
|
||||
}
|
||||
|
||||
void plDSoundBuffer::SetMaxDistance( int dist )
|
||||
{
|
||||
alSourcei(source, AL_MAX_DISTANCE, dist);
|
||||
ALenum error;
|
||||
if((error = alGetError()) != AL_NO_ERROR)
|
||||
plStatusLog::AddLineS("audio.log", "Failed to set min distance");
|
||||
}
|
||||
|
||||
//// Play ////////////////////////////////////////////////////////////////////
|
||||
|
||||
void plDSoundBuffer::Play( void )
|
||||
{
|
||||
if(!source)
|
||||
return;
|
||||
ALenum error = alGetError(); // clear error
|
||||
|
||||
// we dont want openal to loop our streaming buffers, or the buffer will loop back on itself. We will handle looping in the streaming sound
|
||||
if(fLooping && !fStreaming)
|
||||
alSourcei(source, AL_LOOPING, AL_TRUE);
|
||||
else
|
||||
alSourcei(source, AL_LOOPING, AL_FALSE);
|
||||
|
||||
error = alGetError();
|
||||
alSourcePlay(source);
|
||||
error = alGetError();
|
||||
if(error != AL_NO_ERROR)
|
||||
plStatusLog::AddLineS("voice.log", "Play failed");
|
||||
|
||||
plProfile_Inc( SoundPlaying );
|
||||
|
||||
}
|
||||
|
||||
//// Pause ////////////////////////////////////////////////////////////////////
|
||||
|
||||
void plDSoundBuffer::Pause( void )
|
||||
{
|
||||
if (!source)
|
||||
return;
|
||||
alSourcePause(source);
|
||||
alGetError();
|
||||
}
|
||||
|
||||
//// Stop ////////////////////////////////////////////////////////////////////
|
||||
|
||||
void plDSoundBuffer::Stop( void )
|
||||
{
|
||||
if(!source)
|
||||
return;
|
||||
alSourceStop(source);
|
||||
alGetError();
|
||||
plProfile_Dec( SoundPlaying );
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void plDSoundBuffer::SetPosition(float x, float y, float z)
|
||||
{
|
||||
alSource3f(source, AL_POSITION, x, y, -z); // negate z coord, since openal uses opposite handedness
|
||||
alGetError();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void plDSoundBuffer::SetOrientation(float x, float y, float z)
|
||||
{
|
||||
alSource3f(source, AL_ORIENTATION, x, y, -z); // negate z coord, since openal uses opposite handedness
|
||||
alGetError();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void plDSoundBuffer::SetVelocity(float x, float y, float z)
|
||||
{
|
||||
alSource3f(source, AL_VELOCITY, 0, 0, 0); // no doppler shift
|
||||
alGetError();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void plDSoundBuffer::SetConeAngles(int inner, int outer)
|
||||
{
|
||||
alSourcei(source, AL_CONE_INNER_ANGLE, inner);
|
||||
alSourcei(source, AL_CONE_OUTER_ANGLE, outer);
|
||||
alGetError();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void plDSoundBuffer::SetConeOrientation(float x, float y, float z)
|
||||
{
|
||||
alSource3f(source, AL_DIRECTION, x, y, -z); // negate z coord, since openal uses opposite handedness
|
||||
alGetError();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// vol range: -5000 - 0
|
||||
void plDSoundBuffer::SetConeOutsideVolume(int vol)
|
||||
{
|
||||
float volume = (float)vol / 5000.0f + 1.0f; // mb to scalar
|
||||
alSourcef(source, AL_CONE_OUTER_GAIN, volume);
|
||||
alGetError();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
void plDSoundBuffer::Rewind( void )
|
||||
{
|
||||
alSourceRewind(source);
|
||||
alGetError();
|
||||
}
|
||||
|
||||
//// IsPlaying ///////////////////////////////////////////////////////////////
|
||||
|
||||
hsBool plDSoundBuffer::IsPlaying( void )
|
||||
{
|
||||
ALint state = AL_STOPPED;
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &state);
|
||||
alGetError();
|
||||
return state == AL_PLAYING;
|
||||
}
|
||||
|
||||
//// IsEAXAccelerated ////////////////////////////////////////////////////////
|
||||
|
||||
hsBool plDSoundBuffer::IsEAXAccelerated( void ) const
|
||||
{
|
||||
return fEAXSource.IsValid();
|
||||
}
|
||||
|
||||
//// BytePosToMSecs //////////////////////////////////////////////////////////
|
||||
|
||||
UInt32 plDSoundBuffer::BytePosToMSecs( UInt32 bytePos ) const
|
||||
{
|
||||
return (UInt32)(bytePos * 1000 / (hsScalar)fBufferDesc->fAvgBytesPerSec);
|
||||
}
|
||||
|
||||
//// GetBufferBytePos ////////////////////////////////////////////////////////
|
||||
|
||||
UInt32 plDSoundBuffer::GetBufferBytePos( hsScalar timeInSecs ) const
|
||||
{
|
||||
hsAssert( fBufferDesc != nil, "Nil buffer description when calling GetBufferBytePos()" );
|
||||
|
||||
UInt32 byte = (UInt32)( timeInSecs * (hsScalar)fBufferDesc->fNumSamplesPerSec );
|
||||
byte *= fBufferDesc->fBlockAlign;
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
//// GetLengthInBytes ////////////////////////////////////////////////////////
|
||||
|
||||
UInt32 plDSoundBuffer::GetLengthInBytes( void ) const
|
||||
{
|
||||
return fBufferSize;
|
||||
}
|
||||
|
||||
//// SetEAXSettings //////////////////////////////////////////////////////////
|
||||
|
||||
void plDSoundBuffer::SetEAXSettings( plEAXSourceSettings *settings, hsBool force )
|
||||
{
|
||||
fEAXSource.SetFrom( settings, source, force );
|
||||
}
|
||||
|
||||
//// GetBlockAlign ///////////////////////////////////////////////////////////
|
||||
|
||||
UInt8 plDSoundBuffer::GetBlockAlign( void ) const
|
||||
{
|
||||
return ( fBufferDesc != nil ) ? fBufferDesc->fBlockAlign : 0;
|
||||
}
|
||||
|
||||
//// SetScalarVolume /////////////////////////////////////////////////////////
|
||||
// Sets the volume, but on a range from 0 to 1
|
||||
|
||||
void plDSoundBuffer::SetScalarVolume( hsScalar volume )
|
||||
{
|
||||
if(source)
|
||||
{
|
||||
ALenum error;
|
||||
alSourcef(source, AL_GAIN, volume);
|
||||
if((error = alGetError()) != AL_NO_ERROR)
|
||||
plStatusLog::AddLineS("audio.log", "failed to set volume on source %d", error);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned plDSoundBuffer::GetByteOffset( void )
|
||||
{
|
||||
ALint bytes;
|
||||
alGetSourcei(source, AL_BYTE_OFFSET, &bytes);
|
||||
ALenum error = alGetError();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
float plDSoundBuffer::GetTimeOffsetSec( void )
|
||||
{
|
||||
float time;
|
||||
alGetSourcef(source, AL_SEC_OFFSET, &time);
|
||||
ALenum error = alGetError();
|
||||
return time;
|
||||
}
|
||||
|
||||
void plDSoundBuffer::SetTimeOffsetSec(float seconds)
|
||||
{
|
||||
alSourcef(source, AL_SEC_OFFSET, seconds);
|
||||
ALenum error = alGetError();
|
||||
}
|
||||
|
||||
void plDSoundBuffer::SetTimeOffsetBytes(unsigned bytes)
|
||||
{
|
||||
alSourcef(source, AL_BYTE_OFFSET, bytes);
|
||||
ALenum error = alGetError();
|
||||
}
|
||||
|
167
Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h
Normal file
167
Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.h
Normal file
@ -0,0 +1,167 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plDSoundBuffer - Simple wrapper class for a DirectSound buffer. //
|
||||
// Allows us to simplify all the work done behind the //
|
||||
// scenes in plWin32BufferThread. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _plDSoundBuffer_h
|
||||
#define _plDSoundBuffer_h
|
||||
|
||||
#include "hsStlUtils.h"
|
||||
#include "hsTemplates.h"
|
||||
#include "plEAXEffects.h"
|
||||
#define STREAMING_BUFFERS 16
|
||||
#define STREAM_BUFFER_SIZE 4608*4
|
||||
|
||||
//#define VOICE_BUFFERS 4
|
||||
//#define VOICE_BUFFER_SIZE 4608
|
||||
|
||||
class plWAVHeader;
|
||||
class plAudioFileReader;
|
||||
|
||||
|
||||
// Ported to OpenAL from DirectSound May 2006. Idealy the openal sources would be seperate from this class.
|
||||
// OpenAl sound buffer, and source.
|
||||
class plDSoundBuffer
|
||||
{
|
||||
public:
|
||||
plDSoundBuffer( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool looping, hsBool tryStatic = false, bool streaming = false );
|
||||
~plDSoundBuffer();
|
||||
|
||||
void Play();
|
||||
void Stop();
|
||||
void Pause();
|
||||
void Rewind() ;
|
||||
|
||||
UInt32 GetLengthInBytes() const;
|
||||
void SetScalarVolume( hsScalar volume ); // Sets the volume, but on a range from 0 to 1
|
||||
|
||||
unsigned GetSource() { return source; }
|
||||
void SetPosition(float x, float y, float z);
|
||||
void SetOrientation(float x, float y, float z);
|
||||
void SetVelocity(float x, float y, float z);
|
||||
void SetConeAngles(int inner, int outer);
|
||||
void SetConeOrientation(float x, float y, float z);
|
||||
void SetConeOutsideVolume(int vol);
|
||||
|
||||
void SetLooping( hsBool loop );
|
||||
void SetMinDistance( int dist);
|
||||
void SetMaxDistance( int dist );
|
||||
|
||||
hsBool IsValid() const { return fValid; }
|
||||
hsBool IsPlaying();
|
||||
hsBool IsLooping() const { return fLooping; }
|
||||
hsBool IsEAXAccelerated() const;
|
||||
|
||||
bool FillBuffer(void *data, unsigned bytes, plWAVHeader *header);
|
||||
|
||||
// Streaming support
|
||||
bool SetupStreamingSource(plAudioFileReader *stream);
|
||||
bool SetupStreamingSource(void *data, unsigned bytes);
|
||||
int BuffersProcessed();
|
||||
bool StreamingFillBuffer(plAudioFileReader *stream);
|
||||
|
||||
bool SetupVoiceSource();
|
||||
bool VoiceFillBuffer(void *data, unsigned bytes, unsigned buferId);
|
||||
void UnQueueVoiceBuffers();
|
||||
|
||||
|
||||
unsigned GetByteOffset();
|
||||
UInt32 GetBufferBytePos( hsScalar timeInSecs ) const;
|
||||
UInt32 BytePosToMSecs( UInt32 bytePos ) const;
|
||||
|
||||
void SetEAXSettings( plEAXSourceSettings *settings, hsBool force = false );
|
||||
void SetTimeOffsetBytes(unsigned bytes);
|
||||
UInt8 GetBlockAlign( void ) const;
|
||||
static UInt32 GetNumBuffers() { return fNumBuffers; }
|
||||
float GetDefaultMinDistance() { return fDefaultMinDistance; }
|
||||
bool GetAvailableBufferId(unsigned *bufferId);
|
||||
unsigned GetNumQueuedBuffers(){ return fNumQueuedBuffers;} // returns the max number of buffers queued on a source
|
||||
|
||||
float GetTimeOffsetSec();
|
||||
void SetTimeOffsetSec(float seconds);
|
||||
int BuffersQueued();
|
||||
|
||||
protected:
|
||||
|
||||
enum BufferType
|
||||
{
|
||||
kStatic,
|
||||
kStreaming,
|
||||
kVoice,
|
||||
};
|
||||
|
||||
BufferType fType;
|
||||
hsBool fValid, fLooping;
|
||||
UInt32 fLockLength;
|
||||
void * fLockPtr;
|
||||
|
||||
hsTArray<UInt32> fPosNotifys;
|
||||
bool fStreaming;
|
||||
plWAVHeader* fBufferDesc;
|
||||
UInt32 fBufferSize;
|
||||
|
||||
unsigned buffer; // used if this is not a streaming buffer
|
||||
unsigned streamingBuffers[STREAMING_BUFFERS]; // used if this is a streaming buffer
|
||||
std::list<unsigned> mAvailableBuffers; // used for doing our own buffer management. Specifically voice chat, since we dont want old buffers queued
|
||||
|
||||
unsigned source;
|
||||
unsigned int fStreamingBufferSize;
|
||||
|
||||
plEAXSource fEAXSource;
|
||||
|
||||
static UInt32 fNumBuffers;
|
||||
static float fDefaultMinDistance;
|
||||
|
||||
unsigned fNumQueuedBuffers;
|
||||
hsScalar fPrevVolume;
|
||||
|
||||
void IAllocate( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool tryStatic );
|
||||
void IRelease();
|
||||
int IGetALFormat(unsigned bitsPerSample, unsigned int numChannels);
|
||||
};
|
||||
|
||||
#endif //_plDSoundBuffer_h
|
745
Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.cpp
Normal file
745
Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.cpp
Normal file
@ -0,0 +1,745 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plEAXEffects - Various classes and wrappers to support EAX //
|
||||
// acceleration. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
# pragma message("==>Compiling with EAX_SDK")
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1400
|
||||
// name EAX libraries here rather than in the projects so that we don't try to
|
||||
// link them unless EAX_SDK_AVAILABLE (avoids the need for separate projects
|
||||
// or configurations for the cases with/without EAX)
|
||||
#pragma comment(lib, "eax.lib")
|
||||
#pragma comment(lib, "eaxguid.lib")
|
||||
#endif // _MSC_VER < 1400
|
||||
#endif // EAX_SDK_AVAILABLE
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "hsThread.h"
|
||||
#ifndef EAX_SDK_AVAILABLE
|
||||
#include <EFX-Util.h>
|
||||
#endif
|
||||
#include "plEAXEffects.h"
|
||||
#include "../plAudioCore/plAudioCore.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
#include "hsTemplates.h"
|
||||
#include "plEAXListenerMod.h"
|
||||
#include "hsStream.h"
|
||||
#include "plAudioSystem.h"
|
||||
#include <al.h>
|
||||
|
||||
#include <dxerr.h>
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
#include <eax.h>
|
||||
#include <eax-util.h>
|
||||
#include <eaxlegacy.h>
|
||||
#endif
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
#define kDebugLog if( myLog != nil ) myLog->AddLineF(
|
||||
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
static EAXGet s_EAXGet;
|
||||
static EAXSet s_EAXSet;
|
||||
#endif
|
||||
|
||||
|
||||
//// GetInstance /////////////////////////////////////////////////////////////
|
||||
|
||||
plEAXListener &plEAXListener::GetInstance( void )
|
||||
{
|
||||
static plEAXListener instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
//// Constructor/Destructor //////////////////////////////////////////////////
|
||||
|
||||
plEAXListener::plEAXListener()
|
||||
{
|
||||
fInited = false;
|
||||
ClearProcessCache();
|
||||
}
|
||||
|
||||
plEAXListener::~plEAXListener()
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
//// Init ////////////////////////////////////////////////////////////////////
|
||||
|
||||
hsBool plEAXListener::Init( void )
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
if( fInited )
|
||||
return true;
|
||||
|
||||
if(!alIsExtensionPresent((ALchar *)"EAX4.0")) // is eax 4 supported
|
||||
{
|
||||
if(!alIsExtensionPresent((ALchar *) "EAX4.0Emulated")) // is an earlier version of eax supported
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "EAX not supported");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "EAX 4 Emulated supported");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "EAX 4 available");
|
||||
}
|
||||
|
||||
// EAX is supported
|
||||
s_EAXGet = (EAXGet)alGetProcAddress((ALchar *)"EAXGet");
|
||||
s_EAXSet = (EAXSet)alGetProcAddress((ALchar *)"EAXSet");
|
||||
if(!s_EAXGet || ! s_EAXSet)
|
||||
{
|
||||
IFail( "EAX initialization failed", true );
|
||||
return false;
|
||||
}
|
||||
fInited = true;
|
||||
|
||||
#if 1
|
||||
try
|
||||
{
|
||||
// Make an EAX call here to prevent problems on WDM driver
|
||||
unsigned int lRoom = -10000;
|
||||
|
||||
SetGlobalEAXProperty(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ROOM, &lRoom, sizeof( unsigned int ));
|
||||
}
|
||||
catch ( ... )
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "Unable to set EAX Property Set, disabling EAX...");
|
||||
plgAudioSys::EnableEAX(false);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
ClearProcessCache();
|
||||
|
||||
return true;
|
||||
#else /* !EAX_SDK_AVAILABLE */
|
||||
plStatusLog::AddLineS("audio.log", "EAX disabled in this build");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
//// Shutdown ////////////////////////////////////////////////////////////////
|
||||
|
||||
void plEAXListener::Shutdown( void )
|
||||
{
|
||||
if( !fInited )
|
||||
return;
|
||||
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
s_EAXSet = nil;
|
||||
s_EAXGet = nil;
|
||||
#endif
|
||||
IRelease();
|
||||
}
|
||||
|
||||
|
||||
bool plEAXListener::SetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize )
|
||||
{
|
||||
if(fInited)
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
return s_EAXSet(&guid, ulProperty, 0, pData, ulDataSize) == AL_NO_ERROR;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool plEAXListener::GetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
|
||||
{
|
||||
if(fInited)
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
return s_EAXGet(&guid, ulProperty, 0, pData, ulDataSize) == AL_NO_ERROR;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool plEAXSource::SetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
return s_EAXSet(&guid, ulProperty, source, pData, ulDataSize) == AL_NO_ERROR;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool plEAXSource::GetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
return s_EAXGet(&guid, ulProperty, source, pData, ulDataSize) == AL_NO_ERROR;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//// IRelease ////////////////////////////////////////////////////////////////
|
||||
|
||||
void plEAXListener::IRelease( void )
|
||||
{
|
||||
fInited = false;
|
||||
}
|
||||
|
||||
//// IFail ///////////////////////////////////////////////////////////////////
|
||||
|
||||
void plEAXListener::IFail( hsBool major )
|
||||
{
|
||||
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed,
|
||||
"ERROR in plEAXListener: Could not set global eax params");
|
||||
|
||||
if( major )
|
||||
IRelease();
|
||||
}
|
||||
|
||||
void plEAXListener::IFail( const char *msg, hsBool major )
|
||||
{
|
||||
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed,
|
||||
"ERROR in plEAXListener: %s", msg );
|
||||
|
||||
if( major )
|
||||
IRelease();
|
||||
}
|
||||
|
||||
//// IMuteProperties /////////////////////////////////////////////////////////
|
||||
// Mutes the given properties, so if you have some props that you want
|
||||
// half strength, this function will do it for ya.
|
||||
|
||||
void plEAXListener::IMuteProperties( EAXREVERBPROPERTIES *props, hsScalar percent )
|
||||
{
|
||||
// We only mute the room, roomHF and roomLF, since those control the overall effect
|
||||
// application. All three are a direct linear blend as defined by eax-util.cpp, so
|
||||
// this should be rather easy
|
||||
|
||||
hsScalar invPercent = 1.f - percent;
|
||||
|
||||
// The old way, as dictated by EAX sample code...
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
props->lRoom = (int)( ( (float)EAXLISTENER_MINROOM * invPercent ) + ( (float)props->lRoom * percent ) );
|
||||
#endif
|
||||
// The new way, as suggested by EAX guys...
|
||||
// props->lRoom = (int)( 2000.f * log( invPercent ) ) + props->lRoom;
|
||||
|
||||
// props->lRoomLF = (int)( ( (float)EAXLISTENER_MINROOMLF * invPercent ) + ( (float)props->lRoomLF * percent ) );
|
||||
// props->lRoomHF = (int)( ( (float)EAXLISTENER_MINROOMHF * invPercent ) + ( (float)props->lRoomHF * percent ) );
|
||||
}
|
||||
|
||||
//// ClearProcessCache ///////////////////////////////////////////////////////
|
||||
// Clears the cache settings used to speed up ProcessMods(). Call whenever
|
||||
// the list of mods changed.
|
||||
|
||||
void plEAXListener::ClearProcessCache( void )
|
||||
{
|
||||
fLastBigRegion = nil;
|
||||
fLastModCount = -1;
|
||||
fLastWasEmpty = false;
|
||||
fLastSingleStrength = -1.f;
|
||||
}
|
||||
|
||||
//// ProcessMods /////////////////////////////////////////////////////////////
|
||||
// 9.13.02 mcn - Updated the caching method. Now fLastBigRegion will point
|
||||
// to a region iff it's the only region from the last pass that had a
|
||||
// strength > 0. The reason we can't do our trick before is because even if
|
||||
// we have a region w/ strength 1, another region might power up from 1 and
|
||||
// thus suddenly alter the total reverb settings. Thus, the only time we
|
||||
// can wisely skip is if our current big region == fLastBigRegion *and*
|
||||
// the total strength is the same.
|
||||
|
||||
void plEAXListener::ProcessMods( hsTArray<plEAXListenerMod *> &modArray )
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
int i;
|
||||
float totalStrength;
|
||||
hsBool firstOne;
|
||||
|
||||
plEAXListenerMod *thisBigRegion = nil;
|
||||
EAXLISTENERPROPERTIES finalProps;
|
||||
static int oldTime = timeGetTime(); // Get starting time
|
||||
int newTime;
|
||||
hsBool bMorphing = false;
|
||||
|
||||
static plStatusLog *myLog = nil;
|
||||
|
||||
if( myLog == nil && plgAudioSys::AreExtendedLogsEnabled() )
|
||||
myLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "EAX Reverbs", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kDontWriteFile );
|
||||
else if( myLog != nil && !plgAudioSys::AreExtendedLogsEnabled() )
|
||||
{
|
||||
delete myLog;
|
||||
myLog = nil;
|
||||
}
|
||||
|
||||
if( myLog != nil )
|
||||
myLog->Clear();
|
||||
|
||||
if( modArray.GetCount() != fLastModCount )
|
||||
{
|
||||
kDebugLog "Clearing cache..." );
|
||||
ClearProcessCache(); // Code path changed, clear the entire cache
|
||||
fLastModCount = modArray.GetCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
kDebugLog "" );
|
||||
}
|
||||
|
||||
if( modArray.GetCount() > 0 )
|
||||
{
|
||||
kDebugLog "%d regions to calc", modArray.GetCount() );
|
||||
|
||||
// Reset and find a new one if applicable
|
||||
thisBigRegion = nil;
|
||||
|
||||
// Accumulate settings from all the active listener regions (shouldn't be too many, we hope)
|
||||
totalStrength = 0.f;
|
||||
firstOne = true;
|
||||
for( i = 0; i < modArray.GetCount(); i++ )
|
||||
{
|
||||
float strength = modArray[ i ]->GetStrength();
|
||||
kDebugLog "%4.2f - %s", strength, modArray[ i ]->GetKey()->GetUoid().GetObjectName() );
|
||||
if( strength > 0.f )
|
||||
{
|
||||
// fLastBigRegion will point to a region iff it's the only region w/ strength > 0
|
||||
if( totalStrength == 0.f )
|
||||
thisBigRegion = modArray[ i ];
|
||||
else
|
||||
thisBigRegion = nil;
|
||||
|
||||
if( firstOne )
|
||||
{
|
||||
// First one, just init to it
|
||||
memcpy( &finalProps, modArray[ i ]->GetListenerProps(), sizeof( finalProps ) );
|
||||
totalStrength = strength;
|
||||
firstOne = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
hsScalar scale = strength / ( totalStrength + strength );
|
||||
EAX3ListenerInterpolate( &finalProps, modArray[ i ]->GetListenerProps(), scale, &finalProps, false );
|
||||
totalStrength += strength;
|
||||
bMorphing = true;
|
||||
}
|
||||
|
||||
if( totalStrength >= 1.f )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( firstOne )
|
||||
{
|
||||
// No regions of strength > 0, so just make it quiet
|
||||
kDebugLog "Reverb should be quiet" );
|
||||
if( fLastWasEmpty )
|
||||
return;
|
||||
|
||||
memcpy( &finalProps, &EAX30_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXLISTENERPROPERTIES ) );
|
||||
finalProps.lRoom = EAXLISTENER_MINROOM;
|
||||
// finalProps.lRoomLF = EAXLISTENER_MINROOMLF;
|
||||
// finalProps.lRoomHF = EAXLISTENER_MINROOMHF;
|
||||
fLastWasEmpty = true;
|
||||
fLastBigRegion = nil;
|
||||
fLastSingleStrength = -1.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
fLastWasEmpty = false;
|
||||
|
||||
if( thisBigRegion == fLastBigRegion && totalStrength == fLastSingleStrength )
|
||||
// Cached values should be the same, so we can bail at this point
|
||||
return;
|
||||
|
||||
fLastBigRegion = thisBigRegion;
|
||||
fLastSingleStrength = ( thisBigRegion != nil ) ? totalStrength : -1.f;
|
||||
|
||||
if( totalStrength < 1.f )
|
||||
{
|
||||
kDebugLog "Total strength < 1; muting result" );
|
||||
// All of them together is less than full strength, so mute our result
|
||||
IMuteProperties( &finalProps, totalStrength );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
kDebugLog "No regions at all; disabling reverb" );
|
||||
// No regions whatsoever, so disable listener props entirely
|
||||
if( fLastWasEmpty )
|
||||
return;
|
||||
|
||||
memcpy( &finalProps, &EAX30_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXLISTENERPROPERTIES ) );
|
||||
finalProps.lRoom = EAXLISTENER_MINROOM;
|
||||
// finalProps.lRoomLF = EAXLISTENER_MINROOMLF;
|
||||
// finalProps.lRoomHF = EAXLISTENER_MINROOMHF;
|
||||
fLastWasEmpty = true;
|
||||
}
|
||||
|
||||
// if were morphing between regions, do 10th of a second check, otherwise just let it
|
||||
// change due to opt out(caching) feature.
|
||||
if(bMorphing)
|
||||
{
|
||||
newTime = timeGetTime();
|
||||
|
||||
// Update, at most, ten times per second
|
||||
if((newTime - oldTime) < 100) return;
|
||||
|
||||
oldTime = newTime; // update time
|
||||
}
|
||||
//finalProps.flAirAbsorptionHF *= 0.3048f; // Convert to feet
|
||||
//kDebugLog "** Updating property set **" );
|
||||
|
||||
|
||||
if(!SetGlobalEAXProperty(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, &finalProps, sizeof( finalProps )))
|
||||
{
|
||||
IFail( false );
|
||||
}
|
||||
#endif /* EAX_SDK_AVAILABLE */
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Source Settings /////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//// Constructor/Destructor //////////////////////////////////////////////////
|
||||
|
||||
plEAXSourceSettings::plEAXSourceSettings()
|
||||
{
|
||||
fDirtyParams = kAll;
|
||||
Enable( false );
|
||||
}
|
||||
|
||||
plEAXSourceSettings::~plEAXSourceSettings()
|
||||
{
|
||||
}
|
||||
|
||||
//// Read/Write/Set //////////////////////////////////////////////////////////
|
||||
|
||||
void plEAXSourceSettings::Read( hsStream *s )
|
||||
{
|
||||
fEnabled = s->ReadBool();
|
||||
if( fEnabled )
|
||||
{
|
||||
fRoom = s->ReadSwap16();
|
||||
fRoomHF = s->ReadSwap16();
|
||||
fRoomAuto = s->ReadBool();
|
||||
fRoomHFAuto = s->ReadBool();
|
||||
|
||||
fOutsideVolHF = s->ReadSwap16();
|
||||
|
||||
fAirAbsorptionFactor = s->ReadSwapFloat();
|
||||
fRoomRolloffFactor = s->ReadSwapFloat();
|
||||
fDopplerFactor = s->ReadSwapFloat();
|
||||
fRolloffFactor = s->ReadSwapFloat();
|
||||
|
||||
fSoftStarts.Read( s );
|
||||
fSoftEnds.Read( s );
|
||||
|
||||
fOcclusionSoftValue = -1.f;
|
||||
SetOcclusionSoftValue( s->ReadSwapFloat() );
|
||||
|
||||
fDirtyParams = kAll;
|
||||
}
|
||||
else
|
||||
Enable( false ); // Force init of params
|
||||
}
|
||||
|
||||
void plEAXSourceSettings::Write( hsStream *s )
|
||||
{
|
||||
s->WriteBool( fEnabled );
|
||||
if( fEnabled )
|
||||
{
|
||||
s->WriteSwap16( fRoom );
|
||||
s->WriteSwap16( fRoomHF );
|
||||
s->WriteBool( fRoomAuto );
|
||||
s->WriteBool( fRoomHFAuto );
|
||||
|
||||
s->WriteSwap16( fOutsideVolHF );
|
||||
|
||||
s->WriteSwapFloat( fAirAbsorptionFactor );
|
||||
s->WriteSwapFloat( fRoomRolloffFactor );
|
||||
s->WriteSwapFloat( fDopplerFactor );
|
||||
s->WriteSwapFloat( fRolloffFactor );
|
||||
|
||||
fSoftStarts.Write( s );
|
||||
fSoftEnds.Write( s );
|
||||
|
||||
s->WriteSwapFloat( fOcclusionSoftValue );
|
||||
}
|
||||
}
|
||||
|
||||
void plEAXSourceSettings::SetRoomParams( Int16 room, Int16 roomHF, hsBool roomAuto, hsBool roomHFAuto )
|
||||
{
|
||||
fRoom = room;
|
||||
fRoomHF = roomHF;
|
||||
fRoomAuto = roomAuto;
|
||||
fRoomHFAuto = roomHFAuto;
|
||||
fDirtyParams |= kRoom;
|
||||
}
|
||||
|
||||
void plEAXSourceSettings::Enable( hsBool e )
|
||||
{
|
||||
fEnabled = e;
|
||||
if( !e )
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
fRoom = EAXBUFFER_MINROOM;
|
||||
fRoomHF = EAXBUFFER_MINROOMHF;
|
||||
#else
|
||||
fRoom = 0;
|
||||
fRoomHF = 0;
|
||||
#endif
|
||||
fRoomAuto = true;
|
||||
fRoomHFAuto = true;
|
||||
|
||||
fOutsideVolHF = 0;
|
||||
|
||||
fAirAbsorptionFactor = 1.f;
|
||||
fRoomRolloffFactor = 0.f;
|
||||
fDopplerFactor = 0.f;
|
||||
fRolloffFactor = 0.f;
|
||||
|
||||
fOcclusionSoftValue = 0.f;
|
||||
fSoftStarts.Reset();
|
||||
fSoftEnds.Reset();
|
||||
fCurrSoftValues.Reset();
|
||||
fDirtyParams = kAll;
|
||||
}
|
||||
}
|
||||
|
||||
void plEAXSourceSettings::SetOutsideVolHF( Int16 vol )
|
||||
{
|
||||
fOutsideVolHF = vol;
|
||||
fDirtyParams |= kOutsideVolHF;
|
||||
}
|
||||
|
||||
void plEAXSourceSettings::SetFactors( hsScalar airAbsorption, hsScalar roomRolloff, hsScalar doppler, hsScalar rolloff )
|
||||
{
|
||||
fAirAbsorptionFactor = airAbsorption;
|
||||
fRoomRolloffFactor = roomRolloff;
|
||||
fDopplerFactor = doppler;
|
||||
fRolloffFactor = rolloff;
|
||||
fDirtyParams |= kFactors;
|
||||
}
|
||||
|
||||
void plEAXSourceSettings::SetOcclusionSoftValue( hsScalar value )
|
||||
{
|
||||
if( fOcclusionSoftValue != value )
|
||||
{
|
||||
fOcclusionSoftValue = value;
|
||||
IRecalcSofts( kOcclusion );
|
||||
fDirtyParams |= kOcclusion;
|
||||
}
|
||||
}
|
||||
|
||||
void plEAXSourceSettings::IRecalcSofts( UInt8 whichOnes )
|
||||
{
|
||||
hsScalar percent, invPercent;
|
||||
|
||||
if( whichOnes & kOcclusion )
|
||||
{
|
||||
percent = fOcclusionSoftValue;
|
||||
invPercent = 1.f - percent;
|
||||
|
||||
Int16 occ = (Int16)( ( (float)fSoftStarts.GetOcclusion() * invPercent ) + ( (float)fSoftEnds.GetOcclusion() * percent ) );
|
||||
hsScalar lfRatio = (hsScalar)( ( fSoftStarts.GetOcclusionLFRatio() * invPercent ) + ( fSoftEnds.GetOcclusionLFRatio() * percent ) );
|
||||
hsScalar roomRatio = (hsScalar)( ( fSoftStarts.GetOcclusionRoomRatio() * invPercent ) + ( fSoftEnds.GetOcclusionRoomRatio() * percent ) );
|
||||
hsScalar directRatio = (hsScalar)( ( fSoftStarts.GetOcclusionDirectRatio() * invPercent ) + ( fSoftEnds.GetOcclusionDirectRatio() * percent ) );
|
||||
|
||||
fCurrSoftValues.SetOcclusion( occ, lfRatio, roomRatio, directRatio );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Source Soft Settings ////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void plEAXSourceSoftSettings::Reset( void )
|
||||
{
|
||||
fOcclusion = 0;
|
||||
fOcclusionLFRatio = 0.25f;
|
||||
fOcclusionRoomRatio = 1.5f;
|
||||
fOcclusionDirectRatio = 1.f;
|
||||
}
|
||||
|
||||
void plEAXSourceSoftSettings::Read( hsStream *s )
|
||||
{
|
||||
s->ReadSwap( &fOcclusion );
|
||||
s->ReadSwap( &fOcclusionLFRatio );
|
||||
s->ReadSwap( &fOcclusionRoomRatio );
|
||||
s->ReadSwap( &fOcclusionDirectRatio );
|
||||
}
|
||||
|
||||
void plEAXSourceSoftSettings::Write( hsStream *s )
|
||||
{
|
||||
s->WriteSwap( fOcclusion );
|
||||
s->WriteSwap( fOcclusionLFRatio );
|
||||
s->WriteSwap( fOcclusionRoomRatio );
|
||||
s->WriteSwap( fOcclusionDirectRatio );
|
||||
}
|
||||
|
||||
void plEAXSourceSoftSettings::SetOcclusion( Int16 occ, hsScalar lfRatio, hsScalar roomRatio, hsScalar directRatio )
|
||||
{
|
||||
fOcclusion = occ;
|
||||
fOcclusionLFRatio = lfRatio;
|
||||
fOcclusionRoomRatio = roomRatio;
|
||||
fOcclusionDirectRatio = directRatio;
|
||||
}
|
||||
|
||||
//// Constructor/Destructor //////////////////////////////////////////////////
|
||||
|
||||
plEAXSource::plEAXSource()
|
||||
{
|
||||
fInit = false;
|
||||
|
||||
}
|
||||
|
||||
plEAXSource::~plEAXSource()
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
//// Init/Release ////////////////////////////////////////////////////////////
|
||||
|
||||
void plEAXSource::Init( plDSoundBuffer *parent )
|
||||
{
|
||||
fInit = true;
|
||||
// Init some default params
|
||||
plEAXSourceSettings defaultParams;
|
||||
SetFrom( &defaultParams, parent->GetSource() );
|
||||
}
|
||||
|
||||
void plEAXSource::Release( void )
|
||||
{
|
||||
fInit = false;
|
||||
}
|
||||
|
||||
hsBool plEAXSource::IsValid( void ) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//// SetFrom /////////////////////////////////////////////////////////////////
|
||||
|
||||
void plEAXSource::SetFrom( plEAXSourceSettings *settings, unsigned source, hsBool force )
|
||||
{
|
||||
UInt32 dirtyParams;
|
||||
if(source == 0 || !fInit)
|
||||
return;
|
||||
|
||||
if( force )
|
||||
dirtyParams = plEAXSourceSettings::kAll;
|
||||
else
|
||||
dirtyParams = settings->fDirtyParams;
|
||||
|
||||
// Do the params
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
if( dirtyParams & plEAXSourceSettings::kRoom )
|
||||
{
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOM, &settings->fRoom, sizeof(settings->fRoom));
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOMHF, &settings->fRoomHF, sizeof(settings->fRoomHF));
|
||||
}
|
||||
|
||||
if( dirtyParams & plEAXSourceSettings::kOutsideVolHF )
|
||||
{
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, &settings->fOutsideVolHF, sizeof(settings->fOutsideVolHF));
|
||||
}
|
||||
|
||||
if( dirtyParams & plEAXSourceSettings::kFactors )
|
||||
{
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, &settings->fDopplerFactor, sizeof(settings->fDopplerFactor));
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, &settings->fRolloffFactor, sizeof(settings->fRolloffFactor));
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, &settings->fRoomRolloffFactor, sizeof(settings->fRoomRolloffFactor));
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, &settings->fAirAbsorptionFactor, sizeof(settings->fAirAbsorptionFactor));
|
||||
}
|
||||
|
||||
if( dirtyParams & plEAXSourceSettings::kOcclusion )
|
||||
{
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSION, &settings->GetCurrSofts().fOcclusion, sizeof(settings->GetCurrSofts().fOcclusion));
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, &settings->GetCurrSofts().fOcclusionLFRatio, sizeof(settings->GetCurrSofts().fOcclusionLFRatio));
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, &settings->GetCurrSofts().fOcclusionRoomRatio, sizeof(settings->GetCurrSofts().fOcclusionRoomRatio));
|
||||
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, &settings->GetCurrSofts().fOcclusionDirectRatio, sizeof(settings->GetCurrSofts().fOcclusionDirectRatio));
|
||||
}
|
||||
#endif /* EAX_SDK_AVAILABLE */
|
||||
|
||||
settings->ClearDirtyParams();
|
||||
|
||||
// Do all the flags in one pass
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
DWORD flags;
|
||||
|
||||
|
||||
if( GetSourceEAXProperty( source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_FLAGS, &flags, sizeof( DWORD )) )
|
||||
{
|
||||
if( settings->GetRoomAuto() )
|
||||
flags |= EAXBUFFERFLAGS_ROOMAUTO;
|
||||
else
|
||||
flags &= ~EAXBUFFERFLAGS_ROOMAUTO;
|
||||
|
||||
if( settings->GetRoomHFAuto() )
|
||||
flags |= EAXBUFFERFLAGS_ROOMHFAUTO;
|
||||
else
|
||||
flags &= ~EAXBUFFERFLAGS_ROOMHFAUTO;
|
||||
|
||||
if( SetSourceEAXProperty( source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_FLAGS, &flags, sizeof( DWORD ) ) )
|
||||
{
|
||||
return; // All worked, return here
|
||||
}
|
||||
|
||||
// Flag setting failed somehow
|
||||
hsAssert( false, "Unable to set EAX buffer flags" );
|
||||
}
|
||||
#endif /* EAX_SDK_AVAILABLE */
|
||||
}
|
||||
|
||||
|
212
Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.h
Normal file
212
Sources/Plasma/PubUtilLib/plAudio/plEAXEffects.h
Normal file
@ -0,0 +1,212 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plEAXEffects - Various classes and wrappers to support EAX //
|
||||
// acceleration. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _plEAXEffects_h
|
||||
#define _plEAXEffects_h
|
||||
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "hsTemplates.h"
|
||||
|
||||
|
||||
//// Listener Settings Class Definition ///////////////////////////////////////
|
||||
|
||||
class plDSoundBuffer;
|
||||
class plEAXListenerMod;
|
||||
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
typedef struct _EAXREVERBPROPERTIES EAXREVERBPROPERTIES;
|
||||
#else
|
||||
#include <EFX-Util.h>
|
||||
#endif
|
||||
|
||||
class plEAXListener
|
||||
{
|
||||
public:
|
||||
~plEAXListener();
|
||||
static plEAXListener &GetInstance( void );
|
||||
|
||||
hsBool Init( void );
|
||||
void Shutdown( void );
|
||||
|
||||
bool SetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize );
|
||||
bool GetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize );
|
||||
|
||||
void ProcessMods( hsTArray<plEAXListenerMod *> &modArray );
|
||||
void ClearProcessCache( void );
|
||||
|
||||
protected:
|
||||
plEAXListener();
|
||||
void IFail( hsBool major );
|
||||
void IFail( const char *msg, hsBool major );
|
||||
void IRelease( void );
|
||||
|
||||
void IMuteProperties( EAXREVERBPROPERTIES *props, hsScalar percent );
|
||||
|
||||
hsBool fInited;
|
||||
|
||||
// Cache info
|
||||
Int32 fLastModCount;
|
||||
hsBool fLastWasEmpty;
|
||||
hsScalar fLastSingleStrength;
|
||||
plEAXListenerMod *fLastBigRegion;
|
||||
|
||||
};
|
||||
|
||||
//// Soft Buffer Settings Class Definition ////////////////////////////////////
|
||||
// Used to hold buffer settings that will be attenuated by a soft volume,
|
||||
// to make the main settings class a bit cleaner
|
||||
|
||||
class hsStream;
|
||||
class plEAXSourceSoftSettings
|
||||
{
|
||||
public:
|
||||
Int16 fOcclusion;
|
||||
hsScalar fOcclusionLFRatio, fOcclusionRoomRatio, fOcclusionDirectRatio;
|
||||
|
||||
void Read( hsStream *s );
|
||||
void Write( hsStream *s );
|
||||
|
||||
void SetOcclusion( Int16 occ, hsScalar lfRatio, hsScalar roomRatio, hsScalar directRatio );
|
||||
Int16 GetOcclusion( void ) const { return fOcclusion; }
|
||||
hsScalar GetOcclusionLFRatio( void ) const { return fOcclusionLFRatio; }
|
||||
hsScalar GetOcclusionRoomRatio( void ) const { return fOcclusionRoomRatio; }
|
||||
hsScalar GetOcclusionDirectRatio( void ) const { return fOcclusionDirectRatio; }
|
||||
|
||||
void Reset( void );
|
||||
};
|
||||
|
||||
//// Buffer Settings Class Definition /////////////////////////////////////////
|
||||
|
||||
class plEAXSource;
|
||||
|
||||
class plEAXSourceSettings
|
||||
{
|
||||
public:
|
||||
plEAXSourceSettings();
|
||||
virtual ~plEAXSourceSettings();
|
||||
|
||||
void Read( hsStream *s );
|
||||
void Write( hsStream *s );
|
||||
|
||||
void Enable( hsBool e );
|
||||
hsBool IsEnabled( void ) const { return fEnabled; }
|
||||
|
||||
void SetRoomParams( Int16 room, Int16 roomHF, hsBool roomAuto, hsBool roomHFAuto );
|
||||
Int16 GetRoom( void ) const { return fRoom; }
|
||||
Int16 GetRoomHF( void ) const { return fRoomHF; }
|
||||
hsBool GetRoomAuto( void ) const { return fRoomAuto; }
|
||||
hsBool GetRoomHFAuto( void ) const { return fRoomHFAuto; }
|
||||
|
||||
void SetOutsideVolHF( Int16 vol );
|
||||
Int16 GetOutsideVolHF( void ) const { return fOutsideVolHF; }
|
||||
|
||||
void SetFactors( hsScalar airAbsorption, hsScalar roomRolloff, hsScalar doppler, hsScalar rolloff );
|
||||
hsScalar GetAirAbsorptionFactor( void ) const { return fAirAbsorptionFactor; }
|
||||
hsScalar GetRoomRolloffFactor( void ) const { return fRoomRolloffFactor; }
|
||||
hsScalar GetDopplerFactor( void ) const { return fDopplerFactor; }
|
||||
hsScalar GetRolloffFactor( void ) const { return fRolloffFactor; }
|
||||
|
||||
plEAXSourceSoftSettings &GetSoftStarts( void ) { return fSoftStarts; }
|
||||
plEAXSourceSoftSettings &GetSoftEnds( void ) { return fSoftEnds; }
|
||||
|
||||
plEAXSourceSoftSettings &GetCurrSofts( void ) { return fCurrSoftValues; }
|
||||
|
||||
void SetOcclusionSoftValue( hsScalar value );
|
||||
hsScalar GetOcclusionSoftValue( void ) const { return fOcclusionSoftValue; }
|
||||
|
||||
void ClearDirtyParams( void ) const { fDirtyParams = 0; }
|
||||
|
||||
protected:
|
||||
friend class plEAXSource;
|
||||
friend plEAXSourceSoftSettings;
|
||||
|
||||
hsBool fEnabled;
|
||||
Int16 fRoom, fRoomHF;
|
||||
hsBool fRoomAuto, fRoomHFAuto;
|
||||
Int16 fOutsideVolHF;
|
||||
hsScalar fAirAbsorptionFactor, fRoomRolloffFactor, fDopplerFactor, fRolloffFactor;
|
||||
plEAXSourceSoftSettings fSoftStarts, fSoftEnds, fCurrSoftValues;
|
||||
hsScalar fOcclusionSoftValue;
|
||||
mutable UInt32 fDirtyParams;
|
||||
|
||||
enum ParamSets
|
||||
{
|
||||
kOcclusion = 0x01,
|
||||
kRoom = 0x02,
|
||||
kOutsideVolHF = 0x04,
|
||||
kFactors = 0x08,
|
||||
kAll = 0xff
|
||||
};
|
||||
|
||||
void IRecalcSofts( UInt8 whichOnes );
|
||||
};
|
||||
|
||||
//// Source Class Definition //////////////////////////////////////////////////
|
||||
|
||||
class plEAXSource
|
||||
{
|
||||
public:
|
||||
friend plEAXSourceSettings;
|
||||
friend plEAXSourceSoftSettings;
|
||||
|
||||
plEAXSource();
|
||||
virtual ~plEAXSource();
|
||||
|
||||
void Init( plDSoundBuffer *parent );
|
||||
void Release( void );
|
||||
hsBool IsValid( void ) const;
|
||||
bool SetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize);
|
||||
bool GetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize);
|
||||
void SetFrom( plEAXSourceSettings *settings, unsigned source, hsBool force = false );
|
||||
|
||||
private:
|
||||
hsBool fInit;
|
||||
};
|
||||
|
||||
#endif //_plEAXEffects_h
|
253
Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.cpp
Normal file
253
Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.cpp
Normal file
@ -0,0 +1,253 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plEAXListenerMod //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef EAX_SDK_AVAILABLE
|
||||
#include <EFX-Util.h>
|
||||
#endif
|
||||
#include "hsTypes.h"
|
||||
#include "plEAXListenerMod.h"
|
||||
#include "../plIntersect/plSoftVolume.h"
|
||||
#include "hsResMgr.h"
|
||||
#include "plgDispatch.h"
|
||||
#include "plAudioSystem.h"
|
||||
#include "../pnMessage/plAudioSysMsg.h"
|
||||
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
#include <eax-util.h>
|
||||
#endif
|
||||
|
||||
|
||||
plEAXListenerMod::plEAXListenerMod()
|
||||
{
|
||||
fListenerProps = TRACKED_NEW EAXREVERBPROPERTIES;
|
||||
fSoftRegion = nil;
|
||||
fRegistered = false;
|
||||
fGetsMessages = false;
|
||||
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
memcpy( fListenerProps, &REVERB_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXREVERBPROPERTIES ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
plEAXListenerMod::~plEAXListenerMod()
|
||||
{
|
||||
// Tell the audio sys we're going away
|
||||
IUnRegister();
|
||||
|
||||
// Unregister for audioSys messages
|
||||
if( fGetsMessages )
|
||||
{
|
||||
plgDispatch::Dispatch()->UnRegisterForExactType( plAudioSysMsg::Index(), GetKey() );
|
||||
fGetsMessages = false;
|
||||
}
|
||||
|
||||
delete fListenerProps;
|
||||
}
|
||||
|
||||
void plEAXListenerMod::IRegister( void )
|
||||
{
|
||||
if( !fGetsMessages )
|
||||
{
|
||||
plgDispatch::Dispatch()->RegisterForExactType( plAudioSysMsg::Index(), GetKey() );
|
||||
fGetsMessages = true;
|
||||
}
|
||||
|
||||
if( fRegistered || GetKey() == nil )
|
||||
return;
|
||||
|
||||
plKey sysKey = hsgResMgr::ResMgr()->FindKey( plUoid( kAudioSystem_KEY ) );
|
||||
if( sysKey != nil )
|
||||
{
|
||||
plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( sysKey, plRefMsg::kOnCreate, 0, plAudioSystem::kRefEAXRegion );
|
||||
hsgResMgr::ResMgr()->AddViaNotify( GetKey(), refMsg, plRefFlags::kPassiveRef );
|
||||
fRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
void plEAXListenerMod::IUnRegister( void )
|
||||
{
|
||||
if( !fRegistered || GetKey() == nil )
|
||||
return;
|
||||
|
||||
plKey sysKey = hsgResMgr::ResMgr()->FindKey( plUoid( kAudioSystem_KEY ) );
|
||||
if( sysKey != nil && GetKey() != nil )
|
||||
sysKey->Release( GetKey() );
|
||||
|
||||
fRegistered = false;
|
||||
}
|
||||
|
||||
hsBool plEAXListenerMod::IEval( double secs, hsScalar del, UInt32 dirty )
|
||||
{
|
||||
IRegister();
|
||||
return false;
|
||||
}
|
||||
|
||||
hsBool plEAXListenerMod::MsgReceive( plMessage* pMsg )
|
||||
{
|
||||
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( pMsg );
|
||||
if( refMsg != nil )
|
||||
{
|
||||
switch( refMsg->fType )
|
||||
{
|
||||
case kRefSoftRegion:
|
||||
if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) )
|
||||
{
|
||||
fSoftRegion = plSoftVolume::ConvertNoRef( refMsg->GetRef() );
|
||||
fSoftRegion->SetCheckListener();
|
||||
}
|
||||
else if( refMsg->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) )
|
||||
{
|
||||
fSoftRegion = nil;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
plAudioSysMsg *sysMsg = plAudioSysMsg::ConvertNoRef( pMsg );
|
||||
if( sysMsg != nil )
|
||||
{
|
||||
if( sysMsg->GetAudFlag() == plAudioSysMsg::kActivate )
|
||||
{
|
||||
IRegister();
|
||||
}
|
||||
else if( sysMsg->GetAudFlag() == plAudioSysMsg::kDeActivate )
|
||||
{
|
||||
IUnRegister();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return plSingleModifier::MsgReceive( pMsg );
|
||||
}
|
||||
|
||||
void plEAXListenerMod::Read( hsStream* s, hsResMgr* mgr )
|
||||
{
|
||||
plSingleModifier::Read( s, mgr );
|
||||
|
||||
// Read in the soft region
|
||||
mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, kRefSoftRegion ), plRefFlags::kActiveRef );
|
||||
|
||||
// Read the listener params
|
||||
fListenerProps->ulEnvironment = s->ReadSwap32();
|
||||
fListenerProps->flEnvironmentSize = s->ReadSwapFloat();
|
||||
fListenerProps->flEnvironmentDiffusion = s->ReadSwapFloat();
|
||||
fListenerProps->lRoom = s->ReadSwap32();
|
||||
fListenerProps->lRoomHF = s->ReadSwap32();
|
||||
fListenerProps->lRoomLF = s->ReadSwap32();
|
||||
fListenerProps->flDecayTime = s->ReadSwapFloat();
|
||||
fListenerProps->flDecayHFRatio = s->ReadSwapFloat();
|
||||
fListenerProps->flDecayLFRatio = s->ReadSwapFloat();
|
||||
fListenerProps->lReflections = s->ReadSwap32();
|
||||
fListenerProps->flReflectionsDelay = s->ReadSwapFloat();
|
||||
//fListenerProps->vReflectionsPan; // early reflections panning vector
|
||||
fListenerProps->lReverb = s->ReadSwap32(); // late reverberation level relative to room effect
|
||||
fListenerProps->flReverbDelay = s->ReadSwapFloat();
|
||||
//fListenerProps->vReverbPan; // late reverberation panning vector
|
||||
fListenerProps->flEchoTime = s->ReadSwapFloat();
|
||||
fListenerProps->flEchoDepth = s->ReadSwapFloat();
|
||||
fListenerProps->flModulationTime = s->ReadSwapFloat();
|
||||
fListenerProps->flModulationDepth = s->ReadSwapFloat();
|
||||
fListenerProps->flAirAbsorptionHF = s->ReadSwapFloat();
|
||||
fListenerProps->flHFReference = s->ReadSwapFloat();
|
||||
fListenerProps->flLFReference = s->ReadSwapFloat();
|
||||
fListenerProps->flRoomRolloffFactor = s->ReadSwapFloat();
|
||||
fListenerProps->ulFlags = s->ReadSwap32();
|
||||
|
||||
// Done reading, time to tell the audio sys we exist
|
||||
IRegister();
|
||||
}
|
||||
|
||||
void plEAXListenerMod::Write( hsStream* s, hsResMgr* mgr )
|
||||
{
|
||||
plSingleModifier::Write( s, mgr );
|
||||
|
||||
// Write the soft region key
|
||||
mgr->WriteKey( s, fSoftRegion );
|
||||
|
||||
// Write the listener params
|
||||
s->WriteSwap32( fListenerProps->ulEnvironment );
|
||||
s->WriteSwapFloat( fListenerProps->flEnvironmentSize );
|
||||
s->WriteSwapFloat( fListenerProps->flEnvironmentDiffusion );
|
||||
s->WriteSwap32( fListenerProps->lRoom );
|
||||
s->WriteSwap32( fListenerProps->lRoomHF );
|
||||
s->WriteSwap32( fListenerProps->lRoomLF );
|
||||
s->WriteSwapFloat( fListenerProps->flDecayTime );
|
||||
s->WriteSwapFloat( fListenerProps->flDecayHFRatio );
|
||||
s->WriteSwapFloat( fListenerProps->flDecayLFRatio );
|
||||
s->WriteSwap32( fListenerProps->lReflections );
|
||||
s->WriteSwapFloat( fListenerProps->flReflectionsDelay );
|
||||
//s->WriteSwapFloat( fListenerProps->vReflectionsPan; // early reflections panning vector
|
||||
s->WriteSwap32( fListenerProps->lReverb ); // late reverberation level relative to room effect
|
||||
s->WriteSwapFloat( fListenerProps->flReverbDelay );
|
||||
//s->WriteSwapFloat( fListenerProps->vReverbPan; // late reverberation panning vector
|
||||
s->WriteSwapFloat( fListenerProps->flEchoTime );
|
||||
s->WriteSwapFloat( fListenerProps->flEchoDepth );
|
||||
s->WriteSwapFloat( fListenerProps->flModulationTime );
|
||||
s->WriteSwapFloat( fListenerProps->flModulationDepth );
|
||||
s->WriteSwapFloat( fListenerProps->flAirAbsorptionHF );
|
||||
s->WriteSwapFloat( fListenerProps->flHFReference );
|
||||
s->WriteSwapFloat( fListenerProps->flLFReference );
|
||||
s->WriteSwapFloat( fListenerProps->flRoomRolloffFactor );
|
||||
s->WriteSwap32( fListenerProps->ulFlags );
|
||||
}
|
||||
|
||||
|
||||
void plEAXListenerMod::SetFromPreset( UInt32 preset )
|
||||
{
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
memcpy( fListenerProps, &REVERB_ORIGINAL_PRESETS[ preset ], sizeof( EAXREVERBPROPERTIES ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
float plEAXListenerMod::GetStrength( void )
|
||||
{
|
||||
if( fSoftRegion == nil )
|
||||
return 0.f;
|
||||
|
||||
return fSoftRegion->GetListenerStrength();
|
||||
}
|
93
Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.h
Normal file
93
Sources/Plasma/PubUtilLib/plAudio/plEAXListenerMod.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plEAXListenerMod Header //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _plEAXListenerMod_h
|
||||
#define _plEAXListenerMod_h
|
||||
|
||||
|
||||
#include "../pnModifier/plSingleModifier.h"
|
||||
|
||||
class plMessage;
|
||||
class plSoftVolume;
|
||||
#ifdef EAX_SDK_AVAILABLE
|
||||
typedef struct _EAXREVERBPROPERTIES EAXREVERBPROPERTIES;
|
||||
#endif
|
||||
|
||||
class plEAXListenerMod : public plSingleModifier
|
||||
{
|
||||
public:
|
||||
|
||||
plEAXListenerMod();
|
||||
virtual ~plEAXListenerMod();
|
||||
|
||||
CLASSNAME_REGISTER( plEAXListenerMod );
|
||||
GETINTERFACE_ANY( plEAXListenerMod, plSingleModifier );
|
||||
|
||||
enum Refs
|
||||
{
|
||||
kRefSoftRegion = 0,
|
||||
};
|
||||
|
||||
virtual hsBool MsgReceive( plMessage* pMsg );
|
||||
virtual void Read( hsStream* s, hsResMgr* mgr );
|
||||
virtual void Write( hsStream* s, hsResMgr* mgr );
|
||||
float GetStrength( void );
|
||||
|
||||
EAXREVERBPROPERTIES * GetListenerProps( void ) { return fListenerProps; }
|
||||
void SetFromPreset( UInt32 preset );
|
||||
|
||||
protected:
|
||||
plSoftVolume *fSoftRegion;
|
||||
EAXREVERBPROPERTIES *fListenerProps;
|
||||
hsBool fRegistered, fGetsMessages;
|
||||
|
||||
void IRegister( void );
|
||||
void IUnRegister( void );
|
||||
virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval()
|
||||
};
|
||||
|
||||
#endif // _plEAXListenerMod_h
|
358
Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.cpp
Normal file
358
Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.cpp
Normal file
@ -0,0 +1,358 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plOGGCodec - Plasma codec support for the OGG/Vorbis file format. //
|
||||
// //
|
||||
//// Notes ///////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// 2.7.2003 - Created by mcn. If only life were really this simple... //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <vorbis/codec.h>
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "plOGGCodec.h"
|
||||
|
||||
#include "hsTimer.h"
|
||||
#include "../pnNetCommon/plNetApp.h"
|
||||
|
||||
plOGGCodec::DecodeFormat plOGGCodec::fDecodeFormat = plOGGCodec::k16bitSigned;
|
||||
UInt8 plOGGCodec::fDecodeFlags = 0;
|
||||
|
||||
//// Constructor/Destructor //////////////////////////////////////////////////
|
||||
|
||||
plOGGCodec::plOGGCodec( const char *path, plAudioCore::ChannelSelect whichChan ) : fFileHandle( nil )
|
||||
{
|
||||
fOggFile = nil;
|
||||
IOpen( path, whichChan );
|
||||
fCurHeaderPos = 0;
|
||||
fHeadBuf = nil;
|
||||
}
|
||||
|
||||
void plOGGCodec::BuildActualWaveHeader()
|
||||
{
|
||||
// Build an actual WAVE header for this ogg
|
||||
int fmtSize = 16;
|
||||
short fmt = 1;
|
||||
int factsize = 4;
|
||||
int factdata = 0;
|
||||
int size = fDataSize+48; // size of data with header except for first four bytes
|
||||
|
||||
fHeadBuf = (UInt8 *) ALLOC(56);
|
||||
memcpy(fHeadBuf, "RIFF", 4);
|
||||
memcpy(fHeadBuf+4, &size, 4);
|
||||
memcpy(fHeadBuf+8, "WAVE", 4);
|
||||
memcpy(fHeadBuf+12, "fmt ", 4);
|
||||
memcpy(fHeadBuf+16, &fmtSize, 4);
|
||||
memcpy(fHeadBuf+20, &fmt, 2); /* format */
|
||||
memcpy(fHeadBuf+22, &fHeader.fNumChannels, 2);
|
||||
memcpy(fHeadBuf+24, &fHeader.fNumSamplesPerSec, 4);
|
||||
memcpy(fHeadBuf+28, &fHeader.fAvgBytesPerSec, 4);
|
||||
memcpy(fHeadBuf+32, &fHeader.fBlockAlign, 4);
|
||||
memcpy(fHeadBuf+34, &fHeader.fBitsPerSample, 2);
|
||||
memcpy(fHeadBuf+36, "fact", 4);
|
||||
memcpy(fHeadBuf+40, &factsize, 4);
|
||||
memcpy(fHeadBuf+44, &factdata, 4);
|
||||
memcpy(fHeadBuf+48, "data", 4);
|
||||
memcpy(fHeadBuf+52, &fDataSize, 4);
|
||||
}
|
||||
|
||||
bool plOGGCodec::ReadFromHeader(int numBytes, void *data)
|
||||
{
|
||||
if(fCurHeaderPos < 56)
|
||||
{
|
||||
memcpy(data, fHeadBuf+fCurHeaderPos, numBytes);
|
||||
fCurHeaderPos += numBytes;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void plOGGCodec::IOpen( const char *path, plAudioCore::ChannelSelect whichChan )
|
||||
{
|
||||
hsAssert( path != nil, "Invalid path specified in plOGGCodec reader" );
|
||||
|
||||
// plNetClientApp::StaticDebugMsg("Ogg Open %s, t=%f, start", path, hsTimer::GetSeconds());
|
||||
|
||||
strncpy( fFilename, path, sizeof( fFilename ) );
|
||||
fWhichChannel = whichChan;
|
||||
|
||||
/// Open the file as a plain binary stream
|
||||
fFileHandle = fopen( path, "rb" );
|
||||
if( fFileHandle != nil )
|
||||
{
|
||||
/// Create the OGG data struct
|
||||
fOggFile = TRACKED_NEW OggVorbis_File;
|
||||
|
||||
/// Open the OGG decompressor
|
||||
if( ov_open( fFileHandle, fOggFile, NULL, 0 ) < 0 )
|
||||
{
|
||||
IError( "Unable to open OGG source file" );
|
||||
return;
|
||||
}
|
||||
|
||||
/// Construct some header info from the ogg info
|
||||
vorbis_info *vInfo = ov_info( fOggFile, -1 );
|
||||
|
||||
fHeader.fFormatTag = 1;
|
||||
fHeader.fNumChannels = vInfo->channels;
|
||||
fHeader.fNumSamplesPerSec = vInfo->rate;
|
||||
|
||||
// Funny thing about the bits per sample: we get to CHOOSE. Go figure!
|
||||
fHeader.fBitsPerSample = ( fDecodeFormat == k8bitUnsigned ) ? 8 : 16;
|
||||
|
||||
// Why WAV files hold this info when it can be calculated is beyond me...
|
||||
fHeader.fBlockAlign = ( fHeader.fBitsPerSample * fHeader.fNumChannels ) >> 3;
|
||||
fHeader.fAvgBytesPerSec = fHeader.fNumSamplesPerSec * fHeader.fBlockAlign;
|
||||
|
||||
|
||||
/// The size in bytes of our PCM data stream
|
||||
/// Note: OGG sometimes seems to be off by 1 sample, which causes our reads to suck
|
||||
/// because we end up waiting for 1 more sample than we actually have. So, on the
|
||||
/// assumption that OGG is just slightly wrong sometimes, we just subtract 1 sample
|
||||
/// from what it tells us. As Brice put it, who's going to miss 1/40,000'ths of a second?
|
||||
fDataSize = (UInt32)(( ov_pcm_total( fOggFile, -1 ) - 1 ) * fHeader.fBlockAlign);
|
||||
|
||||
/// Channel select
|
||||
if( fWhichChannel != plAudioCore::kAll )
|
||||
{
|
||||
fChannelAdjust = 2;
|
||||
fChannelOffset = ( fWhichChannel == plAudioCore::kLeft ) ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fChannelAdjust = 1;
|
||||
fChannelOffset = 0;
|
||||
}
|
||||
|
||||
|
||||
/// Construct our fake header for channel adjustment
|
||||
fFakeHeader = fHeader;
|
||||
fFakeHeader.fAvgBytesPerSec /= fChannelAdjust;
|
||||
fFakeHeader.fNumChannels /= (UInt16)fChannelAdjust;
|
||||
fFakeHeader.fBlockAlign /= (UInt16)fChannelAdjust;
|
||||
|
||||
SetPosition( 0 );
|
||||
}
|
||||
// plNetClientApp::StaticDebugMsg("Ogg Open %s, t=%f, end", path, hsTimer::GetSeconds());
|
||||
}
|
||||
|
||||
plOGGCodec::~plOGGCodec()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void plOGGCodec::Close( void )
|
||||
{
|
||||
// plNetClientApp::StaticDebugMsg("Ogg Close, t=%f, start", hsTimer::GetSeconds());
|
||||
FREE(fHeadBuf);
|
||||
fHeadBuf = nil;
|
||||
if( fOggFile != nil )
|
||||
{
|
||||
ov_clear( fOggFile );
|
||||
DEL(fOggFile);
|
||||
fOggFile = nil;
|
||||
}
|
||||
|
||||
if( fFileHandle != nil )
|
||||
{
|
||||
fclose( fFileHandle );
|
||||
fFileHandle = nil;
|
||||
}
|
||||
// plNetClientApp::StaticDebugMsg("Ogg Close, t=%f, end", hsTimer::GetSeconds());
|
||||
}
|
||||
|
||||
void plOGGCodec::IError( const char *msg )
|
||||
{
|
||||
hsAssert( false, msg );
|
||||
Close();
|
||||
}
|
||||
|
||||
plWAVHeader &plOGGCodec::GetHeader( void )
|
||||
{
|
||||
hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" );
|
||||
|
||||
return fFakeHeader;
|
||||
}
|
||||
|
||||
float plOGGCodec::GetLengthInSecs( void )
|
||||
{
|
||||
hsAssert( IsValid(), "GetLengthInSecs() called on an invalid OGG file" );
|
||||
|
||||
// Just query ogg directly...starting to see how cool ogg is yet?
|
||||
return (float)ov_time_total( fOggFile, -1 );
|
||||
}
|
||||
|
||||
hsBool plOGGCodec::SetPosition( UInt32 numBytes )
|
||||
{
|
||||
hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" );
|
||||
|
||||
|
||||
if( !ov_seekable( fOggFile ) )
|
||||
{
|
||||
hsAssert( false, "Trying to set position on an unseekable OGG stream!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// The numBytes position is in uncompressed space and should be sample-aligned anyway,
|
||||
// so this should be just fine here.
|
||||
ogg_int64_t newSample = ( numBytes / (fFakeHeader.fBlockAlign * fChannelAdjust) );
|
||||
|
||||
// Now please note how freaking easy it is here to do accurate or fast seeking...
|
||||
// Also note that if we're doing our channel extraction, we MUST do it the accurate way
|
||||
if( ( fDecodeFlags & kFastSeeking ) && fChannelAdjust == 1 )
|
||||
{
|
||||
if( ov_pcm_seek_page( fOggFile, newSample ) != 0 )
|
||||
{
|
||||
IError( "Unable to seek OGG stream" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( ov_pcm_seek( fOggFile, newSample ) != 0 )
|
||||
{
|
||||
IError( "Unable to seek OGG stream" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
hsBool plOGGCodec::Read( UInt32 numBytes, void *buffer )
|
||||
{
|
||||
hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" );
|
||||
// plNetClientApp::StaticDebugMsg("Ogg Read, t=%f, start", hsTimer::GetSeconds());
|
||||
|
||||
int bytesPerSample = ( fDecodeFormat == k16bitSigned ) ? 2 : 1;
|
||||
int isSigned = ( fDecodeFormat == k16bitSigned ) ? 1 : 0;
|
||||
int currSection;
|
||||
|
||||
if( fWhichChannel == plAudioCore::kAll )
|
||||
{
|
||||
// Easy, just a straight read
|
||||
char *uBuffer = (char *)buffer;
|
||||
|
||||
while( numBytes > 0 )
|
||||
{
|
||||
// Supposedly we should pay attention to currSection in case of bitrate changes,
|
||||
// but hopefully we'll never have those....
|
||||
|
||||
long bytesRead = ov_read( fOggFile, uBuffer, numBytes, 0, bytesPerSample, isSigned, &currSection );
|
||||
|
||||
// Since our job is so simple, do some extra error checking
|
||||
if( bytesRead == OV_HOLE )
|
||||
{
|
||||
IError( "Unable to read from OGG file: missing data" );
|
||||
return false;
|
||||
}
|
||||
else if( bytesRead == OV_EBADLINK )
|
||||
{
|
||||
IError( "Unable to read from OGG file: corrupt link" );
|
||||
return false;
|
||||
}
|
||||
else if( bytesRead == 0 )
|
||||
{
|
||||
IError( "Unable to finish reading from OGG file: end of stream" );
|
||||
return false;
|
||||
}
|
||||
else if( bytesRead < 0 )
|
||||
{
|
||||
IError( "Unable to read from OGG file: unknown error" );
|
||||
return false;
|
||||
}
|
||||
|
||||
numBytes -= bytesRead;
|
||||
uBuffer += bytesRead;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Read in 4k chunks and extract
|
||||
static char trashBuffer[ 4096 ];
|
||||
|
||||
long toRead, i, thisRead, sampleSize = fFakeHeader.fBlockAlign;
|
||||
|
||||
for( ; numBytes > 0; )
|
||||
{
|
||||
/// Read 4k worth of samples
|
||||
toRead = ( sizeof( trashBuffer ) < numBytes * fChannelAdjust ) ? sizeof( trashBuffer ) : numBytes * fChannelAdjust;
|
||||
|
||||
|
||||
thisRead = ov_read( fOggFile, (char *)trashBuffer, toRead, 0, bytesPerSample, isSigned, &currSection );
|
||||
if( thisRead < 0 )
|
||||
return false;
|
||||
|
||||
/// Copy every other sample out
|
||||
int sampleOffset = (fChannelOffset == 1) ? sampleSize : 0;
|
||||
for (i = 0; i < thisRead; i += sampleSize * 2)
|
||||
{
|
||||
memcpy(buffer, &trashBuffer[i + sampleOffset], sampleSize);
|
||||
buffer = (void*)((UInt8*)buffer + sampleSize);
|
||||
|
||||
numBytes -= sampleSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// plNetClientApp::StaticDebugMsg("Ogg Read, t=%f, end", hsTimer::GetSeconds());
|
||||
return true;
|
||||
}
|
||||
|
||||
UInt32 plOGGCodec::NumBytesLeft( void )
|
||||
{
|
||||
if(!IsValid())
|
||||
{
|
||||
hsAssert( false, "GetHeader() called on an invalid OGG file" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt32)(( fDataSize - ( ov_pcm_tell( fOggFile ) * fHeader.fBlockAlign ) ) / fChannelAdjust);
|
||||
}
|
122
Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.h
Normal file
122
Sources/Plasma/PubUtilLib/plAudio/plOGGCodec.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plOGGCodec - Plasma codec support for the OGG/Vorbis file format. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _plOGGCodec_h
|
||||
#define _plOGGCodec_h
|
||||
|
||||
#include "../plAudioCore/plAudioFileReader.h"
|
||||
|
||||
|
||||
//// Class Definition ////////////////////////////////////////////////////////
|
||||
|
||||
struct OggVorbis_File;
|
||||
|
||||
class plOGGCodec : public plAudioFileReader
|
||||
{
|
||||
public:
|
||||
|
||||
plOGGCodec( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll );
|
||||
virtual ~plOGGCodec();
|
||||
|
||||
enum DecodeFormat
|
||||
{
|
||||
k8bitUnsigned,
|
||||
k16bitSigned
|
||||
};
|
||||
|
||||
enum DecodeFlags
|
||||
{
|
||||
kFastSeeking = 0x01
|
||||
};
|
||||
|
||||
virtual plWAVHeader &GetHeader( void );
|
||||
|
||||
virtual void Close( void );
|
||||
|
||||
virtual UInt32 GetDataSize( void ) { return fDataSize / fChannelAdjust; }
|
||||
virtual float GetLengthInSecs( void );
|
||||
|
||||
virtual hsBool SetPosition( UInt32 numBytes );
|
||||
virtual hsBool Read( UInt32 numBytes, void *buffer );
|
||||
virtual UInt32 NumBytesLeft( void );
|
||||
|
||||
virtual hsBool IsValid( void ) { return ( fOggFile != nil ) ? true : false; }
|
||||
|
||||
static void SetDecodeFormat( DecodeFormat f ) { fDecodeFormat = f; }
|
||||
static void SetDecodeFlag( UInt8 flag, hsBool on ) { if( on ) fDecodeFlags |= flag; else fDecodeFlags &= ~flag; }
|
||||
static UInt8 GetDecodeFlags( void ) { return fDecodeFlags; }
|
||||
void ResetWaveHeaderRef() { fCurHeaderPos = 0; }
|
||||
void BuildActualWaveHeader();
|
||||
bool ReadFromHeader(int numBytes, void *data); // read from Actual wave header
|
||||
|
||||
protected:
|
||||
|
||||
enum
|
||||
{
|
||||
kPCMFormatTag = 1
|
||||
};
|
||||
|
||||
char fFilename[ 512 ];
|
||||
FILE *fFileHandle;
|
||||
OggVorbis_File *fOggFile;
|
||||
|
||||
plWAVHeader fHeader, fFakeHeader;
|
||||
UInt32 fDataStartPos, fCurrDataPos, fDataSize;
|
||||
|
||||
plAudioCore::ChannelSelect fWhichChannel;
|
||||
UInt32 fChannelAdjust, fChannelOffset;
|
||||
|
||||
static DecodeFormat fDecodeFormat;
|
||||
static UInt8 fDecodeFlags;
|
||||
UInt8 * fHeadBuf;
|
||||
int fCurHeaderPos;
|
||||
|
||||
void IError( const char *msg );
|
||||
void IOpen( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll );
|
||||
};
|
||||
|
||||
#endif //_plOGGCodec_h
|
1544
Sources/Plasma/PubUtilLib/plAudio/plSound.cpp
Normal file
1544
Sources/Plasma/PubUtilLib/plAudio/plSound.cpp
Normal file
File diff suppressed because it is too large
Load Diff
413
Sources/Plasma/PubUtilLib/plAudio/plSound.h
Normal file
413
Sources/Plasma/PubUtilLib/plAudio/plSound.h
Normal file
@ -0,0 +1,413 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plSound.h - Base sound class header //
|
||||
// //
|
||||
//// History /////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// 10.12.01 mcn - Added preliminary soft region (volume) support. //
|
||||
// 7.12.02 mcn - Added EAX support //
|
||||
// 7.15.02 mcn - Added support for animated volumes //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef plSound_h
|
||||
#define plSound_h
|
||||
|
||||
#include "hsTemplates.h"
|
||||
#include "hsGeometry3.h"
|
||||
#include "plEAXEffects.h"
|
||||
#include "../pnNetCommon/plSynchedObject.h"
|
||||
#include "../plAvatar/plAGChannel.h"
|
||||
#include "../plAvatar/plAGApplicator.h"
|
||||
#include "../plAudioCore/plSoundBuffer.h"
|
||||
|
||||
class hsResMgr;
|
||||
class hsStream;
|
||||
class plSoundProxy;
|
||||
class plDrawableSpans;
|
||||
class hsGMaterial;
|
||||
class plSoundMsg;
|
||||
class plSoftVolume;
|
||||
class plGraphPlate;
|
||||
struct hsMatrix44;
|
||||
class plSoundBuffer;
|
||||
class plSceneObject;
|
||||
class plSoundVolumeApplicator;
|
||||
|
||||
// Set this to 1 to do our own distance attenuation (model doesn't work yet tho)
|
||||
#define MCN_HACK_OUR_ATTEN 0
|
||||
#define MAX_INCIDENTALS 4
|
||||
|
||||
class plSound : public plSynchedObject
|
||||
{
|
||||
friend class plSoundSDLModifier;
|
||||
friend class plSoundVolumeApplicator;
|
||||
|
||||
public:
|
||||
plSound();
|
||||
virtual ~plSound();
|
||||
|
||||
CLASSNAME_REGISTER( plSound );
|
||||
GETINTERFACE_ANY( plSound, plSynchedObject );
|
||||
|
||||
enum Property
|
||||
{
|
||||
kPropIs3DSound = 0x00000001,
|
||||
kPropDisableLOD = 0x00000002,
|
||||
kPropLooping = 0x00000004,
|
||||
kPropAutoStart = 0x00000008,
|
||||
kPropLocalOnly = 0x00000010, // Disables network synching and triggering
|
||||
kPropLoadOnlyOnCall = 0x00000020, // Only load and unload when we're told to
|
||||
kPropFullyDisabled = 0x00000040, // This sound should never play while this is set
|
||||
// Only plWin32LinkSound uses it. Placed here as a TODO though...
|
||||
kPropDontFade = 0x00000080,
|
||||
kPropIncidental = 0x00000100 // Incidental sound, will be played thru the incidental manager
|
||||
};
|
||||
|
||||
enum Type
|
||||
{
|
||||
kStartType,
|
||||
kSoundFX = kStartType, // For now, 3D sounds are always marked as this
|
||||
kAmbience,
|
||||
kBackgroundMusic,
|
||||
kGUISound,
|
||||
kNPCVoices,
|
||||
kNumTypes
|
||||
};
|
||||
|
||||
enum Refs
|
||||
{
|
||||
kRefSoftVolume = 0,
|
||||
kRefDataBuffer, // plugins only
|
||||
kRefParentSceneObject,
|
||||
kRefSoftOcclusionRegion
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kSoftRegion = 0
|
||||
};
|
||||
|
||||
enum StreamType
|
||||
{
|
||||
kNoStream,
|
||||
kStreamFromRAM,
|
||||
kStreamFromDisk,
|
||||
kStreamCompressed
|
||||
};
|
||||
|
||||
class plFadeParams
|
||||
{
|
||||
friend class plSound;
|
||||
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
kLinear,
|
||||
kLogarithmic,
|
||||
kExponential
|
||||
};
|
||||
|
||||
hsScalar fLengthInSecs; // Time to take to fade
|
||||
hsScalar fVolStart; // Set one of these two for fade in/out,
|
||||
hsScalar fVolEnd; // the other becomes the current volume
|
||||
UInt8 fType;
|
||||
hsBool fStopWhenDone; // Actually stop the sound once the fade is complete
|
||||
hsBool fFadeSoftVol; // Fade the soft volume instead of fCurrVolume
|
||||
|
||||
plFadeParams() { fLengthInSecs = 0.f; fCurrTime = -1.f; fStopWhenDone = false; fFadeSoftVol = false; fVolStart = fVolEnd = 0.f; fType = kLinear; }
|
||||
|
||||
plFadeParams( Type type, hsScalar len, hsScalar start, hsScalar end )
|
||||
{
|
||||
fLengthInSecs = len; fVolStart = start; fVolEnd = end; fType = type;
|
||||
fStopWhenDone = false;
|
||||
fFadeSoftVol = false;
|
||||
fCurrTime = -1.f;
|
||||
}
|
||||
|
||||
void Read( hsStream *s );
|
||||
void Write( hsStream *s );
|
||||
|
||||
hsScalar InterpValue();
|
||||
|
||||
protected:
|
||||
hsScalar fCurrTime; // -1 if we aren't active, else it's how far we're into the animation
|
||||
};
|
||||
|
||||
virtual hsBool LoadSound( hsBool is3D ) = 0;
|
||||
hsScalar GetVirtualStartTime() const { return (hsScalar)fVirtualStartTime; }
|
||||
|
||||
virtual void Play();
|
||||
void SynchedPlay( unsigned bytes );
|
||||
void SynchedPlay( hsScalar virtualStartTime );
|
||||
virtual void Stop();
|
||||
virtual void FastForwardPlay();
|
||||
virtual void FastForwardToggle();
|
||||
virtual void SetMin(const int m); // sets minimum falloff distance
|
||||
virtual void SetMax(const int m); // sets maximum falloff distance
|
||||
virtual int GetMin() const;
|
||||
virtual int GetMax() const;
|
||||
virtual void SetVolume(const float volume);
|
||||
virtual float GetVolume() const { return fCurrVolume; }
|
||||
hsScalar GetMaxVolume() { return fMaxVolume; }
|
||||
virtual hsBool IsPlaying() { return fPlaying; }
|
||||
void SetTime(double t);
|
||||
virtual double GetTime() { return 0.f; }
|
||||
virtual void Activate(hsBool forcePlay = false);
|
||||
virtual void DeActivate();
|
||||
virtual void SetLength(double l) { fLength = l; }
|
||||
virtual void SetMuted( hsBool muted );
|
||||
virtual hsBool IsMuted() { return fMuted; }
|
||||
void Disable() { fDistAttenuation = 0; }
|
||||
virtual plSoundMsg* GetStatus(plSoundMsg* pMsg){return NULL;}
|
||||
virtual void SetConeOrientation(hsScalar x, hsScalar y, hsScalar z);
|
||||
virtual void SetOuterVolume( const int v ); // volume for the outer cone (if applicable)
|
||||
virtual void SetConeAngles( int inner, int outer );
|
||||
virtual void SetPosition(const hsPoint3 pos);
|
||||
virtual void SetVelocity(const hsVector3 vel);
|
||||
virtual hsPoint3 GetPosition() const;
|
||||
virtual hsVector3 GetVelocity() const;
|
||||
|
||||
virtual void Update();
|
||||
|
||||
plSoundBuffer * GetDataBuffer() const { return (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); }
|
||||
hsScalar QueryCurrVolume() const; // Returns the current volume, attenuated
|
||||
|
||||
const char * GetFileName() const;
|
||||
virtual double GetLength();
|
||||
|
||||
void SetProperty( Property prop, hsBool on ) { if( on ) fProperties |= prop; else fProperties &= ~prop; }
|
||||
hsBool IsPropertySet( Property prop ) const { return ( fProperties & prop ) ? true : false; }
|
||||
|
||||
virtual void RefreshVolume();
|
||||
|
||||
virtual void SetStartPos(unsigned bytes) = 0;
|
||||
virtual unsigned GetByteOffset(){return 0;}
|
||||
virtual float GetActualTimeSec() = 0;
|
||||
|
||||
virtual void AddCallbacks(plSoundMsg* pMsg) = 0;
|
||||
virtual void RemoveCallbacks(plSoundMsg* pMsg) = 0;
|
||||
|
||||
virtual UInt8 GetChannelSelect() const { return 0; } // Only defined on Win32Sound right now, should be here tho
|
||||
|
||||
virtual void Read(hsStream* s, hsResMgr* mgr);
|
||||
virtual void Write(hsStream* s, hsResMgr* mgr);
|
||||
|
||||
virtual void SetFadeInEffect( plFadeParams::Type type, hsScalar length );
|
||||
virtual void SetFadeOutEffect( plFadeParams::Type type, hsScalar length );
|
||||
virtual hsScalar CalcSoftVolume( hsBool enable, hsScalar distToListenerSquared );
|
||||
virtual void UpdateSoftVolume( hsBool enable, hsBool firstTime = false );
|
||||
|
||||
virtual hsBool MsgReceive( plMessage* pMsg );
|
||||
virtual hsBool DirtySynchState( const char *sdlName = nil, UInt32 sendFlags = 0 ); // call when state has changed
|
||||
|
||||
// Tests whether this sound is within range of the given position, not counting soft regions
|
||||
hsBool IsWithinRange( const hsPoint3 &listenerPos, hsScalar *distSquared );
|
||||
|
||||
// Type setting and getting, from the Types enum
|
||||
void SetType( UInt8 type ) { fType = type; }
|
||||
UInt8 GetType() const { return fType; }
|
||||
|
||||
// Priority stuff
|
||||
void SetPriority( UInt8 pri ) { fPriority = pri; }
|
||||
UInt8 GetPriority() const { return fPriority; }
|
||||
|
||||
// Visualization
|
||||
virtual plDrawableSpans* CreateProxy(const hsMatrix44& l2w, hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo);
|
||||
|
||||
// Forced loading/unloading (for when the audio system's LOD just doesn't cut it)
|
||||
virtual void ForceLoad( );
|
||||
virtual void ForceUnload();
|
||||
|
||||
// Note: ONLY THE AUDIOSYS SHOULD CALL THIS. If you're not the audioSys, get lost.
|
||||
static void SetCurrDebugPlate( const plKey soundKey );
|
||||
|
||||
void RegisterOnAudioSys();
|
||||
void UnregisterOnAudioSys();
|
||||
|
||||
// Also only for the audio system
|
||||
hsScalar GetVolumeRank();
|
||||
void ForceUnregisterFromAudioSys();
|
||||
|
||||
static void SetLoadOnDemand( hsBool activate ) { fLoadOnDemandFlag = activate; }
|
||||
static void SetLoadFromDiskOnDemand( hsBool activate ) { fLoadFromDiskOnDemand = activate; }
|
||||
|
||||
const plEAXSourceSettings &GetEAXSettings() const { return fEAXSettings; }
|
||||
plEAXSourceSettings &GetEAXSettings() { return fEAXSettings; }
|
||||
virtual StreamType GetStreamType() const { return kNoStream; }
|
||||
virtual void FreeSoundData();
|
||||
|
||||
|
||||
protected:
|
||||
hsBool fPlaying;
|
||||
hsBool fActive;
|
||||
double fTime;
|
||||
int fMaxFalloff;
|
||||
int fMinFalloff;
|
||||
hsScalar fCurrVolume;
|
||||
hsScalar fDesiredVol; // Equal to fCurrVolume except when we're fading or muted
|
||||
hsScalar fFadedVolume;
|
||||
hsScalar fMaxVolume;
|
||||
|
||||
int fOuterVol;
|
||||
int fInnerCone;
|
||||
int fOuterCone;
|
||||
double fLength;
|
||||
|
||||
int fProperties;
|
||||
UInt8 fType;
|
||||
UInt8 fPriority;
|
||||
|
||||
hsBool fMuted, fFading, fRegisteredForTime, fPlayOnReactivate, fFreeData;
|
||||
hsBool fNotHighEnoughPriority; // Set whenever the audioSys calls UpdateSoftVolume() with enable=false,
|
||||
// thus indicating that we slipped off the top 16 most wanted list.
|
||||
|
||||
// Do these need to be synched values? They weren't before...
|
||||
hsVector3 fConeOrientation;
|
||||
hsPoint3 f3DPosition;
|
||||
hsVector3 f3DVelocity;
|
||||
hsBool fPlayWhenLoaded;
|
||||
|
||||
double fSynchedStartTimeSec;
|
||||
|
||||
// Just around for reference and sending messages upward (synched state)
|
||||
plSceneObject *fOwningSceneObject;
|
||||
|
||||
// EAX Settings storage here
|
||||
plEAXSourceSettings fEAXSettings;
|
||||
hsBool fQueued;
|
||||
|
||||
plFadeParams fFadeInParams, fFadeOutParams;
|
||||
plFadeParams fCoolSoftVolumeTrickParams;
|
||||
plFadeParams *fCurrFadeParams;
|
||||
|
||||
plSoftVolume *fSoftRegion;
|
||||
hsScalar fSoftVolume;
|
||||
hsScalar fDistAttenuation, fDistToListenerSquared;
|
||||
double fVirtualStartTime;
|
||||
hsBool fRegistered;
|
||||
static unsigned fIncidentalsPlaying;
|
||||
|
||||
plSoftVolume *fSoftOcclusionRegion;
|
||||
|
||||
plSoundBuffer *fDataBuffer; // Not always around
|
||||
hsBool fDataBufferLoaded;
|
||||
plKey fDataBufferKey; // Always around
|
||||
|
||||
static plGraphPlate *fDebugPlate;
|
||||
static plSound *fCurrDebugPlateSound;
|
||||
|
||||
static hsBool fLoadOnDemandFlag, fLoadFromDiskOnDemand;
|
||||
hsBool fLoading;
|
||||
|
||||
void IUpdateDebugPlate();
|
||||
void IPrintDbgMessage( const char *msg, hsBool isErr = false );
|
||||
|
||||
virtual void ISetActualVolume(float v) = 0;
|
||||
virtual void IActuallyStop();
|
||||
virtual hsBool IActuallyPlaying() = 0;
|
||||
virtual void IActuallyPlay() = 0;
|
||||
virtual void IFreeBuffers() = 0;
|
||||
|
||||
//NOTE: if isIncidental is true the entire sound will be loaded.
|
||||
virtual plSoundBuffer::ELoadReturnVal IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental = false );
|
||||
virtual void ISetActualTime( double t ) = 0;
|
||||
|
||||
virtual hsBool IActuallyLoaded() = 0;
|
||||
virtual void IRefreshEAXSettings( hsBool force = false ) = 0;
|
||||
|
||||
virtual hsScalar IGetChannelVolume() const;
|
||||
|
||||
void ISynchToStartTime();
|
||||
void ISynchedPlay( double virtualStartTime );
|
||||
void IStartFade( plFadeParams *params, hsScalar offsetIntoFade = 0.f );
|
||||
void IStopFade( hsBool shuttingDown = false, hsBool SetVolEnd = true);
|
||||
|
||||
hsBool IWillBeAbleToPlay();
|
||||
|
||||
void ISetSoftRegion( plSoftVolume *region );
|
||||
hsScalar IAttenuateActualVolume( hsScalar volume ) const;
|
||||
void ISetSoftOcclusionRegion( plSoftVolume *region );
|
||||
|
||||
// Override to make sure the buffer is available before the base class is called
|
||||
virtual void IRefreshParams();
|
||||
|
||||
virtual bool ILoadDataBuffer();
|
||||
virtual void IUnloadDataBuffer();
|
||||
|
||||
//virtual void ISetMinDistance( const int m ) = 0;
|
||||
//virtual void ISetMaxDistance( const int m ) = 0;
|
||||
//virtual void ISetOuterVolume( const int v ) = 0;
|
||||
//virtual void ISetConeAngles( int inner, int outer ) = 0;
|
||||
//virtual void ISetActualConeOrient( hsVector3 &vector ) = 0;
|
||||
//virtual void ISetVelocity( const hsVector3 vel ) = 0;
|
||||
//virtual void ISetPosition( const hsPoint3 pos ) = 0;
|
||||
|
||||
virtual void IRead( hsStream *s, hsResMgr *mgr );
|
||||
virtual void IWrite( hsStream *s, hsResMgr *mgr );
|
||||
};
|
||||
|
||||
|
||||
//// plSoundVolumeApplicator /////////////////////////////////////////////////
|
||||
// Tiny helper for handling animated volumes
|
||||
|
||||
class plSoundVolumeApplicator : public plAGApplicator
|
||||
{
|
||||
public:
|
||||
plSoundVolumeApplicator() { }
|
||||
plSoundVolumeApplicator( UInt32 index ) { fIndex = index; }
|
||||
|
||||
CLASSNAME_REGISTER( plSoundVolumeApplicator );
|
||||
GETINTERFACE_ANY( plSoundVolumeApplicator, plAGApplicator );
|
||||
|
||||
virtual plAGApplicator *CloneWithChannel( plAGChannel *channel );
|
||||
virtual void Write( hsStream *stream, hsResMgr *mgr );
|
||||
virtual void Read( hsStream *s, hsResMgr *mgr );
|
||||
|
||||
protected:
|
||||
UInt32 fIndex;
|
||||
virtual void IApply( const plAGModifier *mod, double time );
|
||||
};
|
||||
|
||||
#endif //plWin32Sound_h
|
199
Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.cpp
Normal file
199
Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.cpp
Normal file
@ -0,0 +1,199 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plSoundEvent //
|
||||
// //
|
||||
//// Notes ///////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// 10.30.2001 - Created by mcn. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "plSoundEvent.h"
|
||||
|
||||
#include "plgDispatch.h"
|
||||
#include "../pnMessage/plEventCallbackMsg.h"
|
||||
#include "../pnMessage/plSoundMsg.h"
|
||||
#include "plSound.h"
|
||||
|
||||
plSoundEvent::plSoundEvent( Types type, plSound *owner )
|
||||
{
|
||||
fType = type;
|
||||
fBytePosTime = 0;
|
||||
fOwner = owner;
|
||||
fCallbacks.Reset();
|
||||
fCallbackEndingFlags.Reset();
|
||||
}
|
||||
|
||||
plSoundEvent::plSoundEvent( Types type, UInt32 bytePos, plSound *owner )
|
||||
{
|
||||
fType = type;
|
||||
fBytePosTime = bytePos;
|
||||
fOwner = owner;
|
||||
fCallbacks.Reset();
|
||||
fCallbackEndingFlags.Reset();
|
||||
}
|
||||
|
||||
plSoundEvent::plSoundEvent()
|
||||
{
|
||||
fType = kStart;
|
||||
fBytePosTime = 0;
|
||||
fOwner = nil;
|
||||
fCallbacks.Reset();
|
||||
fCallbackEndingFlags.Reset();
|
||||
}
|
||||
|
||||
plSoundEvent::~plSoundEvent()
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
for( i = 0; i < fCallbacks.GetCount(); i++ )
|
||||
hsRefCnt_SafeUnRef( fCallbacks[ i ] );
|
||||
}
|
||||
|
||||
void plSoundEvent::AddCallback( plEventCallbackMsg *msg )
|
||||
{
|
||||
hsRefCnt_SafeRef( msg );
|
||||
fCallbacks.Append( msg );
|
||||
fCallbackEndingFlags.Append( 0 );
|
||||
}
|
||||
|
||||
hsBool plSoundEvent::RemoveCallback( plEventCallbackMsg *msg )
|
||||
{
|
||||
int idx = fCallbacks.Find( msg );
|
||||
if( idx != fCallbacks.kMissingIndex )
|
||||
{
|
||||
hsRefCnt_SafeUnRef( msg );
|
||||
fCallbacks.Remove( idx );
|
||||
fCallbackEndingFlags.Remove( idx );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void plSoundEvent::SendCallbacks( void )
|
||||
{
|
||||
int j;
|
||||
plSoundMsg *sMsg;
|
||||
|
||||
|
||||
for( j = fCallbacks.GetCount() - 1; j >= 0; j-- )
|
||||
{
|
||||
plEventCallbackMsg *msg = fCallbacks[ j ];
|
||||
|
||||
if (!msg->HasBCastFlag(plMessage::kNetPropagate) || !fOwner ||
|
||||
fOwner->IsLocallyOwned() == plSynchedObject::kYes )
|
||||
{
|
||||
/// Do this a bit differently so we can do our MsgSend last
|
||||
sMsg = nil;
|
||||
|
||||
// Ref to make sure the dispatcher doesn't delete it on us
|
||||
hsRefCnt_SafeRef( msg );
|
||||
if( msg->fRepeats == 0 && fCallbackEndingFlags[ j ] == 0 )
|
||||
{
|
||||
// Note: we get fancy here. We never want to remove the callback directly,
|
||||
// because the sound won't know about it. So instead, send it a message to
|
||||
// remove the callback for us
|
||||
sMsg = TRACKED_NEW plSoundMsg();
|
||||
sMsg->SetBCastFlag( plMessage::kLocalPropagate, true );
|
||||
sMsg->AddReceiver( fOwner->GetKey() );
|
||||
sMsg->SetCmd( plSoundMsg::kRemoveCallbacks );
|
||||
sMsg->AddCallback( msg );
|
||||
}
|
||||
|
||||
// If this isn't infinite, decrement the number of repeats
|
||||
if( msg->fRepeats > 0 )
|
||||
msg->fRepeats--;
|
||||
|
||||
// And finally...
|
||||
if( fCallbackEndingFlags[ j ] == 0 )
|
||||
{
|
||||
plgDispatch::MsgSend( msg, true );
|
||||
}
|
||||
|
||||
if( sMsg != nil )
|
||||
{
|
||||
plgDispatch::MsgSend( sMsg, true );
|
||||
fCallbackEndingFlags[ j ] = 0xff; // Our special flag to mean "hey, don't
|
||||
// process this, just waiting for
|
||||
// it to die"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UInt32 plSoundEvent::GetNumCallbacks( void ) const
|
||||
{
|
||||
return fCallbacks.GetCount();
|
||||
}
|
||||
|
||||
int plSoundEvent::GetType( void ) const
|
||||
{
|
||||
return (int)fType;
|
||||
}
|
||||
|
||||
void plSoundEvent::SetType( Types type )
|
||||
{
|
||||
fType = type;
|
||||
}
|
||||
|
||||
UInt32 plSoundEvent::GetTime( void ) const
|
||||
{
|
||||
return fBytePosTime;
|
||||
}
|
||||
|
||||
plSoundEvent::Types plSoundEvent::GetTypeFromCallbackMsg( plEventCallbackMsg *msg )
|
||||
{
|
||||
switch( msg->fEvent )
|
||||
{
|
||||
case ::kStart: return kStart;
|
||||
case ::kTime: return kTime;
|
||||
case ::kStop: return kStop;
|
||||
case ::kLoop: return kLoop;
|
||||
}
|
||||
|
||||
return kStop;
|
||||
}
|
99
Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.h
Normal file
99
Sources/Plasma/PubUtilLib/plAudio/plSoundEvent.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plSoundEvent - Event node for handling callback thread stuff //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _plSoundEvent_h
|
||||
#define _plSoundEvent_h
|
||||
|
||||
#include "hsTemplates.h"
|
||||
|
||||
class plEventCallbackMsg;
|
||||
class plSound;
|
||||
|
||||
//// plSoundEvent ////////////////////////////////////////////////////////////
|
||||
// Storage class for an event node.
|
||||
|
||||
class plSoundEvent
|
||||
{
|
||||
public:
|
||||
|
||||
enum Types
|
||||
{
|
||||
kStart,
|
||||
kStop,
|
||||
kTime,
|
||||
kLoop
|
||||
};
|
||||
|
||||
plSoundEvent( Types type, plSound *owner );
|
||||
plSoundEvent( Types type, UInt32 bytePos, plSound *owner );
|
||||
plSoundEvent();
|
||||
~plSoundEvent();
|
||||
|
||||
void AddCallback( plEventCallbackMsg *msg );
|
||||
hsBool RemoveCallback( plEventCallbackMsg *msg );
|
||||
|
||||
UInt32 GetNumCallbacks( void ) const;
|
||||
int GetType( void ) const;
|
||||
void SetType( Types type );
|
||||
UInt32 GetTime( void ) const;
|
||||
|
||||
void SendCallbacks( void );
|
||||
|
||||
static Types GetTypeFromCallbackMsg( plEventCallbackMsg *msg );
|
||||
|
||||
protected:
|
||||
|
||||
Types fType;
|
||||
UInt32 fBytePosTime;
|
||||
plSound *fOwner;
|
||||
|
||||
hsTArray<plEventCallbackMsg *> fCallbacks;
|
||||
hsTArray<UInt8> fCallbackEndingFlags;
|
||||
};
|
||||
|
||||
|
||||
#endif //_plSoundEvent_h
|
718
Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp
Normal file
718
Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.cpp
Normal file
@ -0,0 +1,718 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#include "hsTypes.h"
|
||||
#include "hsWindows.h"
|
||||
#include "hsTimer.h"
|
||||
#include "hsResMgr.h"
|
||||
#include "al.h"
|
||||
#include "alc.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
#include "speex.h"
|
||||
#include "speex_bits.h"
|
||||
#include "hsGeometry3.h"
|
||||
#include "plVoiceChat.h"
|
||||
#include "plAudioSystem.h"
|
||||
#include "plgDispatch.h"
|
||||
#include "../plAudible/plWinAudible.h"
|
||||
#include "../plNetMessage/plNetMessage.h"
|
||||
#include "../plPipeline/plPlates.h"
|
||||
#include "hsConfig.h"
|
||||
#include "../plAvatar/plAvatarMgr.h"
|
||||
#include "../plAvatar/plArmatureMod.h"
|
||||
#include "hsQuat.h"
|
||||
#include "../plAudioCore/plAudioCore.h"
|
||||
|
||||
// DEBUG for printing to the console
|
||||
#include "../plMessage/plConsoleMsg.h"
|
||||
#include "../plPipeline/plDebugText.h"
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
#define MICROPHONE "ui_microphone.png"
|
||||
#define TALKING "ui_speaker.png"
|
||||
#define NUM_CHANNELS 1
|
||||
#define VOICE_STOP_MS 2000
|
||||
#define MAX_DATA_SIZE 1024 * 4 // 4 KB
|
||||
|
||||
hsBool plVoiceRecorder::fCompress = true;
|
||||
hsBool plVoiceRecorder::fRecording = true;
|
||||
hsBool plVoiceRecorder::fNetVoice = false;
|
||||
short plVoiceRecorder::fSampleRate = FREQUENCY;
|
||||
hsScalar plVoiceRecorder::fRecordThreshhold = 200.0f;
|
||||
hsBool plVoiceRecorder::fShowIcons = true;
|
||||
hsBool plVoiceRecorder::fMicAlwaysOpen = false;
|
||||
hsBool plVoicePlayer::fEnabled = true;
|
||||
|
||||
plVoiceRecorder::plVoiceRecorder()
|
||||
{
|
||||
plPlateManager::Instance().CreatePlate( &fDisabledIcon );
|
||||
fDisabledIcon->CreateFromResource( MICROPHONE );
|
||||
fDisabledIcon->SetPosition(-0.90, -0.90);
|
||||
fDisabledIcon->SetSize(0.064, 0.064, true);
|
||||
fDisabledIcon->SetVisible(false);
|
||||
|
||||
plPlateManager::Instance().CreatePlate( &fTalkIcon );
|
||||
fTalkIcon->CreateFromResource( TALKING );
|
||||
fTalkIcon->SetPosition(-0.9,-0.9);
|
||||
fTalkIcon->SetSize(0.0675, 0.09);
|
||||
fTalkIcon->SetVisible(false);
|
||||
}
|
||||
|
||||
plVoiceRecorder::~plVoiceRecorder()
|
||||
{
|
||||
if(fDisabledIcon)
|
||||
plPlateManager::Instance().DestroyPlate( fDisabledIcon);
|
||||
fDisabledIcon = nil;
|
||||
|
||||
if (fTalkIcon)
|
||||
plPlateManager::Instance().DestroyPlate( fTalkIcon );
|
||||
fTalkIcon = nil;
|
||||
}
|
||||
|
||||
void plVoiceRecorder::IncreaseRecordingThreshhold()
|
||||
{
|
||||
fRecordThreshhold += (100 * hsTimer::GetDelSysSeconds());
|
||||
if (fRecordThreshhold >= 10000.0f)
|
||||
fRecordThreshhold = 10000.0f;
|
||||
|
||||
plDebugText &txt = plDebugText::Instance();
|
||||
char str[256];
|
||||
sprintf(str, "RecordThreshhold %f\n", fRecordThreshhold);
|
||||
txt.DrawString(400,300,str);
|
||||
}
|
||||
|
||||
void plVoiceRecorder::DecreaseRecordingThreshhold()
|
||||
{
|
||||
fRecordThreshhold -= (100 * hsTimer::GetDelSysSeconds());
|
||||
if (fRecordThreshhold <= 50.0f)
|
||||
fRecordThreshhold = 50.0f;
|
||||
|
||||
plDebugText &txt = plDebugText::Instance();
|
||||
char str[256];
|
||||
sprintf(str, "RecordThreshhold %f\n", fRecordThreshhold);
|
||||
txt.DrawString(400,300,str);
|
||||
}
|
||||
|
||||
// Set the quality of speex encoder
|
||||
void plVoiceRecorder::SetQuality(int quality)
|
||||
{
|
||||
char str[] = "Voice quality setting out of range. Must be between 1 and 10 inclusive";
|
||||
if(quality < 1 || quality > 10)
|
||||
{
|
||||
plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, str );
|
||||
plgDispatch::MsgSend( cMsg );
|
||||
return;
|
||||
}
|
||||
|
||||
if(plSpeex::GetInstance()->IsUsingVBR())
|
||||
{
|
||||
// Sets average bit rate between 4kb and 13kb
|
||||
int AverageBitrate = quality * 1000 + 3000;
|
||||
plSpeex::GetInstance()->SetABR(AverageBitrate);
|
||||
}
|
||||
else
|
||||
{
|
||||
plSpeex::GetInstance()->SetQuality(quality);
|
||||
}
|
||||
}
|
||||
|
||||
// toggle variable bit rate
|
||||
void plVoiceRecorder::SetVBR(bool vbr)
|
||||
{
|
||||
plSpeex::GetInstance()->VBR(vbr);
|
||||
SetQuality(plSpeex::GetInstance()->GetQuality()); // update proper quality param
|
||||
}
|
||||
|
||||
void plVoiceRecorder::SetComplexity(int c)
|
||||
{
|
||||
char str[] = "Voice quality setting out of range. Must be between 1 and 10 inclusive";
|
||||
if(c < 1 || c > 10)
|
||||
{
|
||||
plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, str );
|
||||
plgDispatch::MsgSend( cMsg );
|
||||
return;
|
||||
}
|
||||
plSpeex::GetInstance()->SetComplexity((UInt8) c);
|
||||
}
|
||||
|
||||
void plVoiceRecorder::SetENH(hsBool b)
|
||||
{
|
||||
plSpeex::GetInstance()->SetENH(b);
|
||||
}
|
||||
|
||||
void plVoiceRecorder::SetMikeOpen(hsBool b)
|
||||
{
|
||||
ALCdevice *device = plgAudioSys::GetCaptureDevice();
|
||||
if (fRecording && device)
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
alcCaptureStart(device);
|
||||
}
|
||||
else
|
||||
{
|
||||
alcCaptureStop(device);
|
||||
}
|
||||
DrawTalkIcon(b);
|
||||
fMikeOpen = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawDisabledIcon(b); // voice recording is unavailable or disabled
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceRecorder::DrawDisabledIcon(hsBool b)
|
||||
{
|
||||
if (!fDisabledIcon)
|
||||
{
|
||||
// at least try and make one here...
|
||||
plPlateManager::Instance().CreatePlate( &fDisabledIcon );
|
||||
if (fDisabledIcon)
|
||||
{
|
||||
fDisabledIcon->CreateFromResource( MICROPHONE );
|
||||
fDisabledIcon->SetPosition(-0.90, -0.90);
|
||||
fDisabledIcon->SetSize(0.064, 0.064, true);
|
||||
fDisabledIcon->SetVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (fDisabledIcon)
|
||||
{
|
||||
fDisabledIcon->SetSize(0.064, 0.064, true); // Re-compute plate size in case the aspect ratio has changed.
|
||||
fDisabledIcon->SetVisible(b);
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceRecorder::DrawTalkIcon(hsBool b)
|
||||
{
|
||||
if (!fTalkIcon)
|
||||
{
|
||||
plPlateManager::Instance().CreatePlate( &fTalkIcon );
|
||||
if (fTalkIcon)
|
||||
{ fTalkIcon->CreateFromResource( TALKING );
|
||||
fTalkIcon->SetPosition(-0.9,-0.9);
|
||||
fTalkIcon->SetSize(0.064, 0.064, true);
|
||||
fTalkIcon->SetVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (fTalkIcon)
|
||||
{
|
||||
fTalkIcon->SetSize(0.064, 0.064, true); // Re-compute plate size in case the aspect ratio has changed.
|
||||
fTalkIcon->SetVisible(b);
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceRecorder::Update(double time)
|
||||
{
|
||||
if(!fRecording)
|
||||
return;
|
||||
|
||||
int EncoderFrameSize = plSpeex::GetInstance()->GetFrameSize();
|
||||
if(EncoderFrameSize == -1)
|
||||
return;
|
||||
|
||||
ALCdevice *captureDevice = plgAudioSys::GetCaptureDevice();
|
||||
if(!captureDevice)
|
||||
return;
|
||||
|
||||
unsigned minSamples = EncoderFrameSize * 10;
|
||||
|
||||
ALCint samples;
|
||||
alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples );
|
||||
|
||||
if (samples > 0)
|
||||
{
|
||||
if (samples >= minSamples)
|
||||
{
|
||||
int numFrames = (int)(samples / EncoderFrameSize); // the number of frames that have been captured
|
||||
int totalSamples = numFrames * EncoderFrameSize;
|
||||
|
||||
// cap uncompressed data
|
||||
if(totalSamples > MAX_DATA_SIZE)
|
||||
totalSamples = MAX_DATA_SIZE;
|
||||
|
||||
// convert to correct units:
|
||||
short *buffer = TRACKED_NEW short[totalSamples];
|
||||
|
||||
alcCaptureSamples(captureDevice, buffer, totalSamples);
|
||||
|
||||
if (!CompressionEnabled())
|
||||
{
|
||||
plNetMsgVoice pMsg;
|
||||
pMsg.SetNetProtocol(kNetProtocolCli2Game);
|
||||
pMsg.SetVoiceData((char *)buffer, totalSamples * sizeof(short));
|
||||
// set frame size here;
|
||||
pMsg.SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID());
|
||||
//if (false) //plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kEchoVoice))
|
||||
// pMsg.SetBit(plNetMessage::kEchoBackToSender);
|
||||
plNetClientApp::GetInstance()->SendMsg(&pMsg);
|
||||
|
||||
}
|
||||
else // use the speex voice compression lib
|
||||
{
|
||||
UInt8 *packet = TRACKED_NEW UInt8[totalSamples]; // packet to send encoded data in
|
||||
int packedLength = 0; // the size of the packet that will be sent
|
||||
hsRAMStream ram; // ram stream to hold output data from speex
|
||||
UInt8 numFrames = totalSamples / EncoderFrameSize; // number of frames to be encoded
|
||||
|
||||
// encode the data using speex
|
||||
plSpeex::GetInstance()->Encode(buffer, numFrames, &packedLength, &ram);
|
||||
|
||||
if (packedLength)
|
||||
{
|
||||
// extract data from ram stream into packet
|
||||
ram.Rewind();
|
||||
ram.Read(packedLength, packet);
|
||||
plNetMsgVoice pMsg;
|
||||
pMsg.SetNetProtocol(kNetProtocolCli2Game);
|
||||
|
||||
pMsg.SetVoiceData((char *)packet, packedLength);
|
||||
pMsg.SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID());
|
||||
pMsg.SetFlag(VOICE_ENCODED); // Set encoded flag
|
||||
pMsg.SetNumFrames(numFrames);
|
||||
if (plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kEchoVoice))
|
||||
pMsg.SetBit(plNetMessage::kEchoBackToSender);
|
||||
|
||||
plNetClientApp::GetInstance()->SendMsg(&pMsg);
|
||||
}
|
||||
delete[] packet;
|
||||
}
|
||||
delete[] buffer;
|
||||
}
|
||||
else if(!fMikeOpen)
|
||||
{
|
||||
short *buffer = TRACKED_NEW short[samples];
|
||||
// the mike has since closed, and there isn't enough data to meet our minimum, so throw this data out
|
||||
alcCaptureSamples(captureDevice, buffer, samples);
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plVoicePlayer::plVoicePlayer()
|
||||
{
|
||||
}
|
||||
|
||||
plVoicePlayer::~plVoicePlayer()
|
||||
{
|
||||
}
|
||||
|
||||
void plVoicePlayer::PlaybackUncompressedVoiceMessage(void* data, unsigned size)
|
||||
{
|
||||
if(fEnabled)
|
||||
{
|
||||
if(!fSound.IsPlaying())
|
||||
{
|
||||
fSound.Play();
|
||||
}
|
||||
fSound.AddVoiceData(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
void plVoicePlayer::PlaybackVoiceMessage(void* data, unsigned size, int numFramesInBuffer)
|
||||
{
|
||||
if(fEnabled)
|
||||
{
|
||||
int numBytes; // the number of bytes that speex decompressed the data to.
|
||||
int bufferSize = numFramesInBuffer * plSpeex::GetInstance()->GetFrameSize();
|
||||
short *nBuff = TRACKED_NEW short[bufferSize];
|
||||
memset(nBuff, 0, bufferSize);
|
||||
|
||||
// Decode the encoded voice data using speex
|
||||
if(!plSpeex::GetInstance()->Decode((UInt8 *)data, size, numFramesInBuffer, &numBytes, nBuff))
|
||||
{
|
||||
delete[] nBuff;
|
||||
return;
|
||||
}
|
||||
|
||||
BYTE* newBuff;
|
||||
newBuff = (BYTE*)nBuff; // Convert to byte data
|
||||
PlaybackUncompressedVoiceMessage(newBuff, numBytes); // playback uncompressed data
|
||||
delete[] nBuff;
|
||||
}
|
||||
}
|
||||
|
||||
void plVoicePlayer::SetVelocity(const hsVector3 vel)
|
||||
{
|
||||
fSound.SetVelocity(vel);
|
||||
}
|
||||
|
||||
void plVoicePlayer::SetPosition(const hsPoint3 pos)
|
||||
{
|
||||
fSound.SetPosition(pos);
|
||||
}
|
||||
|
||||
void plVoicePlayer::SetOrientation(const hsPoint3 pos)
|
||||
{
|
||||
fSound.SetConeOrientation(pos.fX, pos.fY, pos.fZ);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* plVoiceSound
|
||||
*
|
||||
***/
|
||||
unsigned plVoiceSound::fCount = 0;
|
||||
|
||||
plVoiceSound::plVoiceSound()
|
||||
{
|
||||
fInnerCone = 90;
|
||||
fOuterCone = 240;
|
||||
fOuterVol = -2000;
|
||||
|
||||
fMinFalloff = 15;
|
||||
fMaxFalloff = 75;
|
||||
|
||||
fProperties = 0;
|
||||
fCurrVolume = 1.0;
|
||||
fDesiredVol = 1.0;
|
||||
|
||||
fPriority = 1;
|
||||
fType = plgAudioSys::kVoice;
|
||||
|
||||
fEAXSettings.SetRoomParams(-1200, -100, 0, 0);
|
||||
fLastUpdate = 0;
|
||||
|
||||
char keyName[32];
|
||||
StrPrintf(keyName, arrsize(keyName), "VoiceSound_%d", fCount);
|
||||
fCount++;
|
||||
hsgResMgr::ResMgr()->NewKey(keyName, this, plLocation::kGlobalFixedLoc);
|
||||
}
|
||||
|
||||
plVoiceSound::~plVoiceSound()
|
||||
{
|
||||
}
|
||||
|
||||
hsBool plVoiceSound::LoadSound( hsBool is3D )
|
||||
{
|
||||
if( fFailed )
|
||||
return false;
|
||||
if( !plgAudioSys::Active() || fDSoundBuffer )
|
||||
return false;
|
||||
|
||||
if( fPriority > plgAudioSys::GetPriorityCutoff() )
|
||||
return false; // Don't set the failed flag, just return
|
||||
|
||||
plWAVHeader header;
|
||||
header.fFormatTag = WAVE_FORMAT_PCM;
|
||||
header.fBitsPerSample = 16;
|
||||
header.fNumChannels = 1;
|
||||
header.fNumSamplesPerSec = FREQUENCY;
|
||||
header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 2;
|
||||
header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign;
|
||||
|
||||
fDSoundBuffer = TRACKED_NEW plDSoundBuffer(0, header, true, false, false, true);
|
||||
if(!fDSoundBuffer)
|
||||
return false;
|
||||
fDSoundBuffer->SetupVoiceSource();
|
||||
|
||||
IRefreshParams();
|
||||
IRefreshEAXSettings( true );
|
||||
fDSoundBuffer->SetScalarVolume(1.0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void plVoiceSound::Play()
|
||||
{
|
||||
fPlaying = true;
|
||||
if( IWillBeAbleToPlay() )
|
||||
{
|
||||
IRefreshParams();
|
||||
SetVolume( fDesiredVol );
|
||||
IActuallyPlay();
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceSound::IDerivedActuallyPlay( void )
|
||||
{
|
||||
if( !fReallyPlaying )
|
||||
{
|
||||
fDSoundBuffer->Play();
|
||||
fReallyPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceSound::AddVoiceData(void *data, unsigned bytes)
|
||||
{
|
||||
unsigned size;
|
||||
unsigned bufferId;
|
||||
if(!fDSoundBuffer)
|
||||
{
|
||||
if(!LoadSound(true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fDSoundBuffer->UnQueueVoiceBuffers(); // attempt to unque any buffers that have finished
|
||||
while(bytes > 0)
|
||||
{
|
||||
size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE;
|
||||
if(!fDSoundBuffer->GetAvailableBufferId(&bufferId))
|
||||
break; // if there isn't any room for the data, it is currently thrown out
|
||||
|
||||
fDSoundBuffer->VoiceFillBuffer(data, size, bufferId);
|
||||
bytes -= size;
|
||||
}
|
||||
fLastUpdate = hsTimer::GetMilliSeconds();
|
||||
}
|
||||
|
||||
void plVoiceSound::Update()
|
||||
{
|
||||
if(IsPlaying())
|
||||
{
|
||||
if((hsTimer::GetMilliSeconds() - fLastUpdate) > VOICE_STOP_MS)
|
||||
{
|
||||
Stop(); // terminating case for playback. Wait for x number of milliseconds, and stop.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void plVoiceSound::IRefreshParams()
|
||||
{
|
||||
plSound::IRefreshParams();
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Speex Voice Encoding/Decoding
|
||||
*
|
||||
***/
|
||||
|
||||
plSpeex::plSpeex() :
|
||||
fBits(nil),
|
||||
fEncoderState(nil),
|
||||
fDecoderState(nil),
|
||||
fSampleRate(plVoiceRecorder::GetSampleRate()),
|
||||
fFrameSize(-1),
|
||||
fQuality(7),
|
||||
fVBR(true), // variable bit rate on
|
||||
fAverageBitrate(8000), // 8kb bitrate
|
||||
fComplexity(3),
|
||||
fENH(false),
|
||||
fInitialized(false)
|
||||
{
|
||||
fBits = TRACKED_NEW SpeexBits;
|
||||
Init(kNarrowband); // if no one initialized us initialize using a narrowband encoder
|
||||
}
|
||||
|
||||
plSpeex::~plSpeex()
|
||||
{
|
||||
Shutdown();
|
||||
delete fBits;
|
||||
fBits = nil;
|
||||
}
|
||||
|
||||
hsBool plSpeex::Init(Mode mode)
|
||||
{
|
||||
int enh = 1;
|
||||
|
||||
// setup speex
|
||||
speex_bits_init(fBits);
|
||||
fBitsInit = true;
|
||||
|
||||
if(mode == kNarrowband)
|
||||
{
|
||||
fEncoderState = speex_encoder_init(&speex_nb_mode); // narrowband
|
||||
fDecoderState = speex_decoder_init(&speex_nb_mode);
|
||||
}
|
||||
else if(mode == kWideband)
|
||||
{
|
||||
fEncoderState = speex_encoder_init(&speex_wb_mode);
|
||||
fDecoderState = speex_decoder_init(&speex_wb_mode);
|
||||
}
|
||||
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_GET_FRAME_SIZE, &fFrameSize); // get frame size
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_COMPLEXITY, &fComplexity); // 3
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_SAMPLING_RATE, &fSampleRate); // 8 khz
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR_QUALITY, &fQuality); // 7
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR, &fVBR); // use variable bit rate
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_ABR, &fAverageBitrate); // default to 8kb
|
||||
|
||||
speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &fENH); // perceptual enhancement
|
||||
|
||||
fInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
hsBool plSpeex::Shutdown()
|
||||
{
|
||||
//shutdown speex
|
||||
if(fDecoderState)
|
||||
{
|
||||
speex_decoder_destroy(fDecoderState);
|
||||
fDecoderState = nil;
|
||||
}
|
||||
|
||||
if(fEncoderState)
|
||||
{
|
||||
speex_encoder_destroy(fEncoderState);
|
||||
fEncoderState = nil;
|
||||
}
|
||||
|
||||
if(fBitsInit)
|
||||
{
|
||||
speex_bits_destroy(fBits);
|
||||
fBitsInit = false;
|
||||
}
|
||||
fInitialized = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
hsBool plSpeex::Encode(short *data, int numFrames, int *packedLength, hsRAMStream *out)
|
||||
{
|
||||
*packedLength = 0;
|
||||
|
||||
short *pData = data; // pointer to input data
|
||||
float *input = TRACKED_NEW float[fFrameSize]; // input to speex - used as am intermediate array since speex requires float data
|
||||
BYTE frameLength; // number of bytes speex compressed frame to
|
||||
BYTE *frameData = TRACKED_NEW BYTE[fFrameSize]; // holds one frame of encoded data
|
||||
|
||||
// encode data
|
||||
for( int i = 0; i < numFrames; i++ )
|
||||
{
|
||||
// convert input data to floats
|
||||
for( int j = 0; j < fFrameSize; j++ )
|
||||
{
|
||||
input[j] = pData[j];
|
||||
}
|
||||
|
||||
speex_bits_reset(fBits); // reset bit structure
|
||||
|
||||
// encode data using speex
|
||||
speex_encode(fEncoderState, input, fBits);
|
||||
frameLength = speex_bits_write(fBits, (char *)frameData, fFrameSize);
|
||||
|
||||
// write data - length and bytes
|
||||
out->WriteSwap(frameLength);
|
||||
*packedLength += sizeof(frameLength); // add length of encoded frame
|
||||
out->Write(frameLength, frameData);
|
||||
*packedLength += frameLength; // update length
|
||||
|
||||
pData += fFrameSize; // move input pointer
|
||||
}
|
||||
|
||||
delete[] frameData;
|
||||
delete[] input;
|
||||
return true;
|
||||
}
|
||||
|
||||
hsBool plSpeex::Decode(UInt8 *data, int size, int numFrames, int *numOutputBytes, short *out)
|
||||
{
|
||||
if(!fInitialized) return false;
|
||||
*numOutputBytes = 0;
|
||||
|
||||
hsReadOnlyStream stream( size, data );
|
||||
float *speexOutput = TRACKED_NEW float[fFrameSize]; // holds output from speex
|
||||
short *pOut = out; // pointer to output short buffer
|
||||
|
||||
// create buffer for input data
|
||||
BYTE *frameData = TRACKED_NEW BYTE[fFrameSize]; // holds the current frames data to be decoded
|
||||
BYTE frameLen; // holds the length of the current frame being decoded.
|
||||
|
||||
|
||||
// Decode data
|
||||
for (int i = 0; i < numFrames; i++)
|
||||
{
|
||||
stream.ReadSwap( &frameLen ); // read the length of the current frame to be decoded
|
||||
stream.Read( frameLen, frameData ); // read the data
|
||||
|
||||
memset(speexOutput, 0, fFrameSize * sizeof(float));
|
||||
speex_bits_read_from(fBits, (char *)frameData, frameLen); // give data to speex
|
||||
speex_decode(fDecoderState, fBits, speexOutput); // decode data
|
||||
|
||||
for(int j = 0; j < fFrameSize; j++)
|
||||
{
|
||||
pOut[j] = (short)(speexOutput[j]); // convert floats to shorts
|
||||
}
|
||||
|
||||
pOut += fFrameSize;
|
||||
}
|
||||
|
||||
delete[] frameData;
|
||||
delete[] speexOutput;
|
||||
|
||||
*numOutputBytes = (numFrames * fFrameSize) * sizeof(short); // length of decoded voice data(out) in bytes
|
||||
if(*numOutputBytes == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sets variable bit rate on/off
|
||||
void plSpeex::VBR(hsBool b)
|
||||
{
|
||||
fVBR = b;
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR, &fVBR);
|
||||
}
|
||||
|
||||
|
||||
// Sets the average bit rate
|
||||
void plSpeex::SetABR(UInt32 abr)
|
||||
{
|
||||
fAverageBitrate = abr;
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_ABR, &fAverageBitrate);
|
||||
}
|
||||
|
||||
// Sets the quality of encoding
|
||||
void plSpeex::SetQuality(UInt32 quality)
|
||||
{
|
||||
fQuality = quality;
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_QUALITY, &fQuality);
|
||||
}
|
||||
|
||||
void plSpeex::SetENH(hsBool b)
|
||||
{
|
||||
fENH = b;
|
||||
speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &fENH);
|
||||
}
|
||||
|
||||
void plSpeex::SetComplexity(UInt8 c)
|
||||
{
|
||||
fComplexity = c;
|
||||
speex_encoder_ctl(fEncoderState, SPEEX_SET_COMPLEXITY, &fComplexity);
|
||||
}
|
212
Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.h
Normal file
212
Sources/Plasma/PubUtilLib/plAudio/plVoiceChat.h
Normal file
@ -0,0 +1,212 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#ifndef plVoiceChat_h
|
||||
#define plVoiceChat_h
|
||||
|
||||
#include "hsTemplates.h"
|
||||
#include "plWin32Sound.h"
|
||||
#include "hsThread.h"
|
||||
|
||||
// voice flags
|
||||
#define VOICE_ENCODED ( 1 << 0 )
|
||||
#define VOICE_NARROWBAND ( 1 << 1 )
|
||||
#define VOICE_ENH ( 1 << 2 )
|
||||
#define BUFFER_LEN_SECONDS 4
|
||||
#define FREQUENCY 8000
|
||||
|
||||
struct hsVector3;
|
||||
struct SpeexBits;
|
||||
class plWinAudible;
|
||||
class plPlate;
|
||||
class plStatusLog;
|
||||
class plSpeex;
|
||||
typedef struct ALCdevice_struct ALCdevice;
|
||||
|
||||
|
||||
// Sound used for playing back dynamic voice chat data. this allows us to hook voice chat into the audio system
|
||||
class plVoiceSound : public plWin32Sound
|
||||
{
|
||||
public:
|
||||
plVoiceSound();
|
||||
~plVoiceSound();
|
||||
hsBool LoadSound( hsBool is3D );
|
||||
void AddVoiceData(void *data, unsigned bytes);
|
||||
void Update();
|
||||
void Play();
|
||||
virtual void SetStartPos(unsigned bytes){}
|
||||
|
||||
private:
|
||||
virtual bool ILoadDataBuffer( void ){ return true; }
|
||||
virtual void IUnloadDataBuffer( void ){}
|
||||
|
||||
virtual void IDerivedActuallyPlay( void );
|
||||
virtual void ISetActualTime( double t ){}
|
||||
virtual float GetActualTimeSec() { return 0.0f; }
|
||||
virtual void IRefreshParams( void );
|
||||
static unsigned fCount;
|
||||
double fLastUpdate;
|
||||
};
|
||||
|
||||
class plVoicePlayer
|
||||
{
|
||||
public:
|
||||
plVoicePlayer();
|
||||
~plVoicePlayer();
|
||||
void PlaybackVoiceMessage(void* data, unsigned size, int numFramesInBuffer);
|
||||
void PlaybackUncompressedVoiceMessage(void* data, unsigned size);
|
||||
void SetVelocity(const hsVector3 vel);
|
||||
void SetPosition(const hsPoint3 pos);
|
||||
void SetOrientation(const hsPoint3 pos);
|
||||
|
||||
void SetTalkIcon(int index, UInt32 str){}
|
||||
void ClearTalkIcon(){}
|
||||
plVoiceSound *GetSoundPtr() { return &fSound; }
|
||||
static void Enable(hsBool enable) { fEnabled = enable; }
|
||||
|
||||
private:
|
||||
plVoiceSound fSound;
|
||||
static hsBool fEnabled;
|
||||
};
|
||||
|
||||
class plVoiceRecorder
|
||||
{
|
||||
public:
|
||||
plVoiceRecorder();
|
||||
~plVoiceRecorder();
|
||||
|
||||
void Update(double time);
|
||||
void SetMikeOpen(hsBool b);
|
||||
void DrawTalkIcon(hsBool b);
|
||||
void DrawDisabledIcon(hsBool b);
|
||||
|
||||
void SetTalkIcon(int index, UInt32 str);
|
||||
void ClearTalkIcon();
|
||||
|
||||
static hsBool RecordingEnabled() { return fRecording; }
|
||||
static hsBool NetVoiceEnabled() { return fNetVoice; }
|
||||
static hsBool CompressionEnabled() { return fCompress; }
|
||||
static void EnablePushToTalk(hsBool b) { fMicAlwaysOpen = !b; }
|
||||
static void EnableIcons(hsBool b) { fShowIcons = b; }
|
||||
static void EnableRecording(hsBool b) { fRecording = b; }
|
||||
static void EnableNetVoice(hsBool b) { fNetVoice = b; }
|
||||
static void EnableCompression(hsBool b) { fCompress = b; }
|
||||
static void SetSampleRate(short s) { fSampleRate = s; }
|
||||
static void SetSquelch(hsScalar f) { fRecordThreshhold = f; }
|
||||
|
||||
static void IncreaseRecordingThreshhold();
|
||||
static void DecreaseRecordingThreshhold();
|
||||
|
||||
static void SetQuality(int quality); // sets the quality of encoding
|
||||
static void SetMode(int mode); // sets nb or wb mode
|
||||
static void SetVBR(bool vbr);
|
||||
static void SetComplexity(int c);
|
||||
static void SetENH(hsBool b);
|
||||
static short GetSampleRate() { return fSampleRate; }
|
||||
|
||||
private:
|
||||
|
||||
hsBool fMikeOpen;
|
||||
hsBool fMikeJustClosed;
|
||||
static hsBool fMicAlwaysOpen;
|
||||
static hsBool fShowIcons;
|
||||
static hsBool fCompress;
|
||||
static hsBool fNetVoice;
|
||||
static hsBool fRecording;
|
||||
static short fSampleRate;
|
||||
plPlate* fDisabledIcon;
|
||||
plPlate* fTalkIcon;
|
||||
static hsScalar fRecordThreshhold;
|
||||
};
|
||||
|
||||
|
||||
// Speex voice encoder/decoder class
|
||||
class plSpeex
|
||||
{
|
||||
public:
|
||||
~plSpeex();
|
||||
|
||||
enum Mode
|
||||
{
|
||||
kNarrowband,
|
||||
kWideband,
|
||||
kUltraWideband
|
||||
};
|
||||
static plSpeex *GetInstance()
|
||||
{
|
||||
static plSpeex instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
hsBool Init(Mode mode);
|
||||
hsBool Shutdown();
|
||||
hsBool Encode(short *data, int numFrames, int *packedLength, hsRAMStream *out);
|
||||
hsBool Decode(UInt8 *data, int size, int numFrames, int *numOutputBytes, short *out);
|
||||
int GetFrameSize() { return fFrameSize; }
|
||||
void VBR(hsBool b); // turn variable bit rate on/off
|
||||
void SetVBR(UInt32 vbr); // Set variable bit rate quality
|
||||
void ABR(hsBool b); // turn average bit rate on/off
|
||||
void SetABR(UInt32 abr); // Set average bit rate quality
|
||||
void SetQuality(UInt32 quality); // Set encoder quality
|
||||
hsBool IsUsingVBR() { return fVBR; }
|
||||
int GetQuality() { return fQuality; }
|
||||
void SetENH(hsBool b);
|
||||
void SetComplexity(UInt8 c);
|
||||
|
||||
hsBool Initialized() { return fInitialized; }
|
||||
|
||||
private:
|
||||
plSpeex();
|
||||
SpeexBits* fBits; // main speex structure
|
||||
hsBool fBitsInit;
|
||||
void* fEncoderState;
|
||||
void* fDecoderState;
|
||||
int fSampleRate;
|
||||
int fFrameSize; // frame size from speex - 160 for nb
|
||||
int fQuality; // 0-10 speex encode quality
|
||||
hsBool fVBR; // toggle variable bit rate
|
||||
int fAverageBitrate; // n-bits per second
|
||||
UInt8 fComplexity; // 1-10 sets cpu resources allowed for encoder
|
||||
hsBool fENH; // perceptual enhancement
|
||||
hsBool fInitialized;
|
||||
};
|
||||
|
||||
#endif //plVoiceChat_h
|
226
Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.cpp
Normal file
226
Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plWAVClipBuffer - Helper class for writing out WAV data in a buffered //
|
||||
// manner, with support for clipping off the specified //
|
||||
// amount at the end, but without knowing beforehand //
|
||||
// exactly how much data we'll have. //
|
||||
// //
|
||||
// The algorithm goes something like this: we keep two buffers, both the //
|
||||
// size of the amount we want to clip. We then start filling in the first //
|
||||
// buffer, overflowing into the second buffer and wrapping back to the //
|
||||
// first again in a circular fashion. When we fill up one buffer and are //
|
||||
// about to advance to the next, we write that next buffer out. Why? //
|
||||
// Because we know that, even if we got no more data in, we have enough //
|
||||
// data in the first buffer to clip out the amount we want, so the other //
|
||||
// half (which will have older data, being a circular buffer) can be //
|
||||
// written out safely. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "plWAVClipBuffer.h"
|
||||
#include "hsStream.h"
|
||||
#include "hsUtils.h"
|
||||
|
||||
#include "plWavFile.h"
|
||||
|
||||
|
||||
//// Constructor/Destructor //////////////////////////////////////////////////
|
||||
|
||||
plWAVClipBuffer::plWAVClipBuffer( UInt32 clipSize, CWaveFile *outFile )
|
||||
{
|
||||
fBuffers[ 0 ] = fBuffers[ 1 ] = nil;
|
||||
fFlushCalled = true;
|
||||
Init( clipSize, outFile );
|
||||
}
|
||||
|
||||
plWAVClipBuffer::~plWAVClipBuffer()
|
||||
{
|
||||
IShutdown();
|
||||
}
|
||||
|
||||
//// Init & IShutdown ////////////////////////////////////////////////////////
|
||||
|
||||
void plWAVClipBuffer::Init( UInt32 clipSize, CWaveFile *outFile )
|
||||
{
|
||||
IShutdown();
|
||||
if( clipSize > 0 )
|
||||
{
|
||||
fBuffers[ 0 ] = TRACKED_NEW UInt8[ clipSize ];
|
||||
fBuffers[ 1 ] = TRACKED_NEW UInt8[ clipSize ];
|
||||
memset( fBuffers[ 0 ], 0, clipSize );
|
||||
memset( fBuffers[ 1 ], 0, clipSize );
|
||||
}
|
||||
fWhichBuffer = 0;
|
||||
fBufferSize = clipSize;
|
||||
fCursor = 0;
|
||||
fFirstFlip = true;
|
||||
fOutFile = outFile;
|
||||
fFlushCalled = false;
|
||||
}
|
||||
|
||||
void plWAVClipBuffer::IShutdown( void )
|
||||
{
|
||||
hsAssert( fFlushCalled, "WAVClipBuffer shut down without flushing it!!!" );
|
||||
|
||||
delete [] fBuffers[ 0 ];
|
||||
delete [] fBuffers[ 1 ];
|
||||
}
|
||||
|
||||
//// WriteData ///////////////////////////////////////////////////////////////
|
||||
// The main workhorse; call this to add data to the buffer.
|
||||
|
||||
hsBool plWAVClipBuffer::WriteData( UInt32 size, UInt8 *data )
|
||||
{
|
||||
while( size > 0 )
|
||||
{
|
||||
UInt32 toWrite = fBufferSize - fCursor;
|
||||
if( size < toWrite )
|
||||
{
|
||||
// Just write, haven't filled a buffer yet
|
||||
memcpy( fBuffers[ fWhichBuffer ] + fCursor, data, size );
|
||||
data += size;
|
||||
fCursor += size;
|
||||
return true; // All done!
|
||||
}
|
||||
|
||||
// Fill up to the end of a buffer, then flip
|
||||
memcpy( fBuffers[ fWhichBuffer ] + fCursor, data, toWrite );
|
||||
data += toWrite;
|
||||
fCursor += toWrite;
|
||||
size -= toWrite;
|
||||
|
||||
// Flip now...
|
||||
fWhichBuffer = 1 - fWhichBuffer;
|
||||
fCursor = 0;
|
||||
|
||||
// Now we can write out this buffer, since it'll be old data and
|
||||
// we have enough in the other buffer to clip with. The *only*
|
||||
// time we don't want to do this is the first time we flip, since
|
||||
// at that point, the buffer we just flipped to hasn't been filled yet.
|
||||
// (Every time afterwards, we'll always be flipping to a buffer with old
|
||||
// data).
|
||||
if( fFirstFlip )
|
||||
fFirstFlip = false;
|
||||
else
|
||||
{
|
||||
// Write it out before we overwrite it!
|
||||
UINT written;
|
||||
HRESULT hr = fOutFile->Write( fBufferSize, fBuffers[ fWhichBuffer ], &written );
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
hsAssert( false, "ERROR writing WMA stream to WAV file" );
|
||||
return false;
|
||||
}
|
||||
else if( written != fBufferSize )
|
||||
{
|
||||
hsAssert( false, "Unable to write all of WMA stream to WAV file" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanly got here, so just return success
|
||||
return true;
|
||||
}
|
||||
|
||||
//// Flush ///////////////////////////////////////////////////////////////////
|
||||
// Writes out the remaining data, minus our clip value (which is fBufferSize)
|
||||
// So here's our situation: at this point, one of two things could be true:
|
||||
//
|
||||
// 1) We haven't received enough data to clip by, at which point we don't
|
||||
// write any more and bail (this will be true if fFirstFlip is still true)
|
||||
//
|
||||
// 2) Our cursor is at 0, which means we have one filled buffer that hasn't been
|
||||
// written out and our current buffer is empty. At this point, we discard the
|
||||
// filled buffer (which is precisely the length we want to clip by) and we're done.
|
||||
//
|
||||
// 3) The buffer we're on should be partially filled, while the other one is older
|
||||
// data. So, we want to write out the older data and the partial buffer all the way,
|
||||
// except for the clip size. Since we can therefore never write out any data in the
|
||||
// partial buffer (since that count will always be less than the clip size and thus be
|
||||
// the second half of what we clip), we simply figure out how much of the other one we
|
||||
// clip and write out the rest.
|
||||
|
||||
hsBool plWAVClipBuffer::Flush( void )
|
||||
{
|
||||
fFlushCalled = true;
|
||||
|
||||
if( fFirstFlip )
|
||||
return false; // We failed--not enough data to clip with
|
||||
|
||||
if( fCursor == 0 )
|
||||
{
|
||||
// Our current buffer is empty, so the other buffer is precisely what we clip.
|
||||
// So just discard and return successfully
|
||||
return true;
|
||||
}
|
||||
|
||||
// The hard case--we always discard the partial buffer we're on, so figure out
|
||||
// how much we want to save of the other buffer. The math is:
|
||||
// Partial buffer amount we're clipping = fCursor
|
||||
// Amount of other buffer we're clipping = fBufferSize - fCursor
|
||||
// Amount of other buffer we're writing = fBufferSize - ( fBufferSize - fCursor ) = fCursor
|
||||
// Go figure :)
|
||||
|
||||
UInt32 toWrite = fCursor;
|
||||
|
||||
UINT written;
|
||||
HRESULT hr = fOutFile->Write( toWrite, fBuffers[ 1 - fWhichBuffer ], &written );
|
||||
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
hsAssert( false, "ERROR writing WMA stream to WAV file" );
|
||||
return false;
|
||||
}
|
||||
else if( written != toWrite )
|
||||
{
|
||||
hsAssert( false, "Unable to write all of WMA stream to WAV file" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// All done!
|
||||
return true;
|
||||
}
|
94
Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.h
Normal file
94
Sources/Plasma/PubUtilLib/plAudio/plWAVClipBuffer.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plWAVClipBuffer - Helper class for writing out WAV data in a buffered //
|
||||
// manner, with support for clipping off the specified //
|
||||
// amount at the end, but without knowing beforehand //
|
||||
// exactly how much data we'll have. //
|
||||
// //
|
||||
// The algorithm goes something like this: we keep two buffers, both the //
|
||||
// size of the amount we want to clip. We then start filling in the first //
|
||||
// buffer, overflowing into the second buffer and wrapping back to the //
|
||||
// first again in a circular fashion. When we fill up one buffer and are //
|
||||
// about to advance to the next, we write that next buffer out. Why? //
|
||||
// Because we know that, even if we got no more data in, we have enough //
|
||||
// data in the first buffer to clip out the amount we want, so the other //
|
||||
// half (which will have older data, being a circular buffer) can be //
|
||||
// written out safely. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _plWAVClipBuffer_h
|
||||
#define _plWAVClipBuffer_h
|
||||
|
||||
//// Class Definition ////////////////////////////////////////////////////////
|
||||
|
||||
class CWaveFile;
|
||||
class plWAVClipBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
plWAVClipBuffer( UInt32 clipSize, CWaveFile *outFile );
|
||||
~plWAVClipBuffer();
|
||||
|
||||
// Inits the buffer. Can re-init if you wish
|
||||
void Init( UInt32 clipSize, CWaveFile *outFile );
|
||||
|
||||
// Writes/adds data to the buffer
|
||||
hsBool WriteData( UInt32 size, UInt8 *data );
|
||||
|
||||
// Call at the end, flushes the buffer and performs the clipping
|
||||
hsBool Flush( void );
|
||||
|
||||
protected:
|
||||
UInt8 *fBuffers[ 2 ];
|
||||
UInt8 fWhichBuffer; // 0 or 1
|
||||
UInt32 fCursor, fBufferSize;
|
||||
hsBool fFirstFlip, fFlushCalled;
|
||||
|
||||
CWaveFile *fOutFile;
|
||||
|
||||
void IShutdown( void );
|
||||
};
|
||||
|
||||
#endif //_plWAVClipBuffer_h
|
1104
Sources/Plasma/PubUtilLib/plAudio/plWavFile.cpp
Normal file
1104
Sources/Plasma/PubUtilLib/plAudio/plWavFile.cpp
Normal file
File diff suppressed because it is too large
Load Diff
130
Sources/Plasma/PubUtilLib/plAudio/plWavFile.h
Normal file
130
Sources/Plasma/PubUtilLib/plAudio/plWavFile.h
Normal file
@ -0,0 +1,130 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#ifndef plWavFile_H
|
||||
#define plWavFile_H
|
||||
|
||||
#define WAVEFILE_READ 1
|
||||
#define WAVEFILE_WRITE 2
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "hsWindows.h"
|
||||
#include "hsStlUtils.h"
|
||||
#include <mmsystem.h>
|
||||
#include "../plAudioCore/plAudioFileReader.h"
|
||||
|
||||
|
||||
struct plSoundMarker
|
||||
{
|
||||
char *fName;
|
||||
double fOffset; // in Secs
|
||||
|
||||
plSoundMarker () { fName = NULL;fOffset = 0.0; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Name: class CWaveFile
|
||||
// Desc: Encapsulates reading or writing sound data to or from a wave file
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
class CWaveFile : public plAudioFileReader
|
||||
{
|
||||
public:
|
||||
CWaveFile();
|
||||
~CWaveFile();
|
||||
|
||||
HRESULT Open(const char *strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags );
|
||||
HRESULT OpenFromMemory( BYTE* pbData, ULONG ulDataSize, WAVEFORMATEX* pwfx, DWORD dwFlags );
|
||||
|
||||
HRESULT Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead );
|
||||
HRESULT AdvanceWithoutRead( DWORD dwSizeToRead, DWORD* pdwSizeRead );
|
||||
HRESULT Write( UINT nSizeToWrite, BYTE* pbData, UINT* pnSizeWrote );
|
||||
|
||||
DWORD GetSize();
|
||||
HRESULT ResetFile();
|
||||
WAVEFORMATEX* GetFormat() { return m_pwfx; };
|
||||
|
||||
DWORD GetNumMarkers() { return fMarkers.size() ; };
|
||||
plSoundMarker *GetSoundMarker(int i) { return fMarkers[i]; }
|
||||
|
||||
|
||||
// Overloads for plAudioFileReader
|
||||
CWaveFile( const char *path, plAudioCore::ChannelSelect whichChan );
|
||||
virtual hsBool OpenForWriting( const char *path, plWAVHeader &header );
|
||||
virtual plWAVHeader &GetHeader( void );
|
||||
virtual void Close( void );
|
||||
virtual UInt32 GetDataSize( void );
|
||||
virtual float GetLengthInSecs( void );
|
||||
|
||||
virtual hsBool SetPosition( UInt32 numBytes );
|
||||
virtual hsBool Read( UInt32 numBytes, void *buffer );
|
||||
virtual UInt32 NumBytesLeft( void );
|
||||
virtual UInt32 Write( UInt32 bytes, void *buffer );
|
||||
|
||||
virtual hsBool IsValid( void );
|
||||
WAVEFORMATEX* m_pwfx; // Pointer to WAVEFORMATEX structure
|
||||
HMMIO m_hmmio; // MM I/O handle for the WAVE
|
||||
MMCKINFO m_ck; // Multimedia RIFF chunk
|
||||
MMCKINFO m_ckRiff; // Use in opening a WAVE file
|
||||
DWORD m_dwSize; // The size of the wave file
|
||||
MMIOINFO m_mmioinfoOut;
|
||||
DWORD m_dwFlags;
|
||||
BOOL m_bIsReadingFromMemory;
|
||||
BYTE* m_pbData;
|
||||
BYTE* m_pbDataCur;
|
||||
ULONG m_ulDataSize;
|
||||
|
||||
plWAVHeader fHeader;
|
||||
|
||||
std::vector<plSoundMarker*> fMarkers;
|
||||
double fSecsPerSample;
|
||||
|
||||
protected:
|
||||
HRESULT ReadMMIO();
|
||||
HRESULT WriteMMIO( WAVEFORMATEX *pwfxDest );
|
||||
HRESULT IClose();
|
||||
};
|
||||
|
||||
#endif // plWavFile_H
|
409
Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.cpp
Normal file
409
Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.cpp
Normal file
@ -0,0 +1,409 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plWin32GroupedSound - Grouped version of a static sound. Lots of short //
|
||||
// sounds stored in the buffer, all share the same //
|
||||
// DSound playback buffer and only one plays at a //
|
||||
// time. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "hsTypes.h"
|
||||
|
||||
#include "plWin32GroupedSound.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
|
||||
#include "plAudioSystem.h"
|
||||
#include "../plAudioCore/plSoundBuffer.h"
|
||||
#include "../plAudioCore/plSoundDeswizzler.h"
|
||||
#include "plgDispatch.h"
|
||||
#include "../pnMessage/plSoundMsg.h"
|
||||
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
#include "plProfile.h"
|
||||
#include "hsResMgr.h"
|
||||
|
||||
plProfile_Extern( MemSounds );
|
||||
plProfile_Extern( StaticSndShoveTime );
|
||||
plProfile_Extern( StaticSwizzleTime );
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
plWin32GroupedSound::plWin32GroupedSound()
|
||||
{
|
||||
fCurrentSound = 0;
|
||||
}
|
||||
|
||||
plWin32GroupedSound::~plWin32GroupedSound()
|
||||
{
|
||||
DeActivate();
|
||||
}
|
||||
|
||||
void plWin32GroupedSound::SetPositionArray( UInt16 numSounds, UInt32 *posArray, hsScalar *volumeArray )
|
||||
{
|
||||
UInt16 i;
|
||||
|
||||
|
||||
fStartPositions.SetCountAndZero( numSounds );
|
||||
fVolumes.SetCountAndZero( numSounds );
|
||||
for( i = 0; i < numSounds; i++ )
|
||||
{
|
||||
fStartPositions[ i ] = posArray[ i ];
|
||||
fVolumes[ i ] = volumeArray[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
//// IRead/IWrite ////////////////////////////////////////////////////////////
|
||||
|
||||
void plWin32GroupedSound::IRead( hsStream *s, hsResMgr *mgr )
|
||||
{
|
||||
plWin32StaticSound::IRead( s, mgr );
|
||||
UInt16 i, n = s->ReadSwap16();
|
||||
fStartPositions.SetCountAndZero( n );
|
||||
fVolumes.SetCountAndZero( n );
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
fStartPositions[ i ] = s->ReadSwap32();
|
||||
fVolumes[ i ] = s->ReadSwapScalar();
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32GroupedSound::IWrite( hsStream *s, hsResMgr *mgr )
|
||||
{
|
||||
plWin32StaticSound::IWrite( s, mgr );
|
||||
|
||||
s->WriteSwap16( fStartPositions.GetCount() );
|
||||
UInt16 i;
|
||||
for( i = 0; i < fStartPositions.GetCount(); i++ )
|
||||
{
|
||||
s->WriteSwap32( fStartPositions[ i ] );
|
||||
s->WriteSwapScalar( fVolumes[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
//// LoadSound ///////////////////////////////////////////////////////////////
|
||||
|
||||
hsBool plWin32GroupedSound::LoadSound( hsBool is3D )
|
||||
{
|
||||
if( fFailed )
|
||||
return false;
|
||||
|
||||
if( fPriority > plgAudioSys::GetPriorityCutoff() )
|
||||
return false; // Don't set the failed flag, just return
|
||||
|
||||
if( !plgAudioSys::Active() || fDSoundBuffer != nil )
|
||||
return false;
|
||||
|
||||
|
||||
// Debug flag #1
|
||||
if( fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) )
|
||||
{
|
||||
// Force a fail
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// We need it to be resident to read in
|
||||
plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true);
|
||||
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
|
||||
if(!buffer)
|
||||
{
|
||||
return plSoundBuffer::kError;
|
||||
}
|
||||
|
||||
if( retVal == plSoundBuffer::kPending) // we are still reading data.
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// We need it to be resident to read in
|
||||
if( retVal == plSoundBuffer::kError)
|
||||
{
|
||||
char str[ 256 ];
|
||||
sprintf( str, "Unable to open .wav file %s", fDataBufferKey ? fDataBufferKey->GetName() : "nil");
|
||||
IPrintDbgMessage( str, true );
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
SetProperty( kPropIs3DSound, is3D );
|
||||
|
||||
|
||||
plWAVHeader header = buffer->GetHeader();
|
||||
|
||||
// Debug flag #2
|
||||
if( fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) )
|
||||
{
|
||||
// Force a fail
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate the maximum size for our buffer. This will be the length of the longest sound we're going to
|
||||
// have to play.
|
||||
UInt16 i;
|
||||
UInt32 maxSoundSize, len;
|
||||
for( i = 1, maxSoundSize = 0; i < fStartPositions.GetCount(); i++ )
|
||||
{
|
||||
len = fStartPositions[ i ] - fStartPositions[ i - 1 ];
|
||||
if( len > maxSoundSize )
|
||||
maxSoundSize = len;
|
||||
}
|
||||
len = buffer->GetDataLength() - fStartPositions[ fStartPositions.GetCount() - 1 ];
|
||||
if( len > maxSoundSize )
|
||||
maxSoundSize = len;
|
||||
|
||||
// Based on that, allocate our buffer
|
||||
UInt32 bufferSize = maxSoundSize - ( maxSoundSize % header.fBlockAlign );
|
||||
|
||||
if( header.fNumChannels > 1 && is3D )
|
||||
{
|
||||
// We can only do a single channel of 3D sound. So copy over one (later)
|
||||
bufferSize /= header.fNumChannels;
|
||||
header.fBlockAlign /= header.fNumChannels;
|
||||
header.fAvgBytesPerSec /= header.fNumChannels;
|
||||
header.fNumChannels = 1;
|
||||
}
|
||||
fNumDestChannels = (UInt8)(header.fNumChannels);
|
||||
fNumDestBytesPerSample = (UInt8)(header.fBlockAlign);
|
||||
|
||||
// Create our DSound buffer (or rather, the wrapper around it)
|
||||
fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet( kPropLooping ), true );
|
||||
if( !fDSoundBuffer->IsValid() )
|
||||
{
|
||||
char str[256];
|
||||
sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName());
|
||||
IPrintDbgMessage( str, true );
|
||||
fFailed = true;
|
||||
|
||||
delete fDSoundBuffer;
|
||||
fDSoundBuffer = nil;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
IRefreshEAXSettings( true );
|
||||
|
||||
// Fill the buffer with whatever our current sound is.
|
||||
IFillCurrentSound( 0 );
|
||||
|
||||
// Logging
|
||||
char str[ 256 ];
|
||||
sprintf( str, " Grouped %s %s allocated (%d msec).", buffer->GetFileName() != nil ? "file" : "buffer",
|
||||
buffer->GetFileName() != nil ? buffer->GetFileName() : buffer->GetKey()->GetUoid().GetObjectName(),
|
||||
//fDSoundBuffer->IsHardwareAccelerated() ? "hardware" : "software",
|
||||
//fDSoundBuffer->IsStaticVoice() ? "static" : "dynamic",
|
||||
#ifdef PL_PROFILE_ENABLED
|
||||
gProfileVarStaticSndShoveTime.GetValue() );
|
||||
#else
|
||||
0 );
|
||||
#endif
|
||||
IPrintDbgMessage( str );
|
||||
if( GetKey() != nil && GetKeyName() != nil && strstr( GetKeyName(), "Footstep" ) != nil )
|
||||
;
|
||||
else
|
||||
plStatusLog::AddLineS( "audioTimes.log", "%s (%s)", str, GetKey() ? GetKeyName() : "unkeyed" );
|
||||
|
||||
fTotalBytes = bufferSize;
|
||||
|
||||
plProfile_NewMem( MemSounds, fTotalBytes );
|
||||
|
||||
// All done!
|
||||
// if( fLoadFromDiskOnDemand )
|
||||
// IUnloadDataBuffer();
|
||||
FreeSoundData();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
//// GetSoundLength //////////////////////////////////////////////////////////
|
||||
// Gets the length (in seconds) of the given sound index from the group.
|
||||
|
||||
hsScalar plWin32GroupedSound::GetSoundLength( Int16 soundIndex )
|
||||
{
|
||||
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
|
||||
if(buffer)
|
||||
{
|
||||
return (hsScalar)IGetSoundByteLength( soundIndex ) / buffer->GetHeader().fAvgBytesPerSec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//// IGetSoundByteLength /////////////////////////////////////////////////////
|
||||
// Byte version of above.
|
||||
|
||||
UInt32 plWin32GroupedSound::IGetSoundByteLength( Int16 soundIndex )
|
||||
{
|
||||
|
||||
if( soundIndex == fStartPositions.GetCount() - 1 )
|
||||
return ((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetDataLength() - fStartPositions[ soundIndex ];
|
||||
else
|
||||
return fStartPositions[ soundIndex + 1 ] - fStartPositions[ soundIndex ];
|
||||
}
|
||||
|
||||
//// IGetDataPointer/Length //////////////////////////////////////////////////
|
||||
// Abstracting a few things here for the incidentalMgr
|
||||
|
||||
void *plWin32GroupedSound::IGetDataPointer( void ) const
|
||||
{
|
||||
return ( fDataBufferKey->ObjectIsLoaded() ) ? (void *)( (UInt8 *)((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetData() + fStartPositions[ fCurrentSound ] ) : nil;
|
||||
}
|
||||
|
||||
UInt32 plWin32GroupedSound::IGetDataLength( void ) const
|
||||
{
|
||||
return ( fDataBufferKey->ObjectIsLoaded() ) ? fCurrentSoundLength : 0;
|
||||
}
|
||||
|
||||
//// IFillCurrentSound ///////////////////////////////////////////////////////
|
||||
// Fills the DSoundBuffer with data from the current sound from our sound
|
||||
// group, optionally switching what our current sound is.
|
||||
|
||||
void plWin32GroupedSound::IFillCurrentSound( Int16 newCurrent /*= -1*/ )
|
||||
{
|
||||
//void *dataPtr;
|
||||
//UInt32 dataLength;
|
||||
|
||||
if( !fDSoundBuffer && plgAudioSys::Active() )
|
||||
LoadSound( IsPropertySet( kPropIs3DSound ) );
|
||||
|
||||
plProfile_BeginTiming( StaticSndShoveTime );
|
||||
|
||||
// Make sure we're stopped first. Don't want to be filling while we're playing
|
||||
Stop();
|
||||
|
||||
if( newCurrent != -1 )
|
||||
{
|
||||
fCurrentSound = (UInt16)newCurrent;
|
||||
|
||||
if( fCurrentSound >= fStartPositions.GetCount() )
|
||||
{
|
||||
// Invalid index!
|
||||
hsAssert( false, "Invalid index in plWin32GroupedSound::IFillCurrentSound()" );
|
||||
fCurrentSound = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set our length based on the current sound
|
||||
fCurrentSoundLength = IGetSoundByteLength( fCurrentSound );
|
||||
if( fDataBufferKey->ObjectIsLoaded() )
|
||||
SetLength( fCurrentSoundLength / ((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetHeader().fAvgBytesPerSec );
|
||||
|
||||
// Update our volume as well
|
||||
SetVolume( fVolumes[ fCurrentSound ] );
|
||||
}
|
||||
|
||||
if( fDSoundBuffer != nil )
|
||||
{
|
||||
/// Lock our buffer
|
||||
//fDSoundBuffer->Lock( dataLength, dataPtr );
|
||||
|
||||
/// Copy or de-swizzle?
|
||||
//if( fDataBuffer->GetHeader().fNumChannels == fNumDestChannels )
|
||||
{
|
||||
// Just copy
|
||||
//memcpy( dataPtr, (Byte *)fDataBuffer->GetData() + fStartPositions[ fCurrentSound ], fCurrentSoundLength );
|
||||
//dataPtr = (Byte *)dataPtr + fCurrentSoundLength;
|
||||
//dataLength -= fCurrentSoundLength;
|
||||
}
|
||||
//else
|
||||
{
|
||||
// We're extracting a single channel of sound into our sound buffer, so it isn't a straight copy...
|
||||
|
||||
/*plProfile_BeginTiming( StaticSwizzleTime );
|
||||
|
||||
plSoundDeswizzler deswiz( (Byte *)fDataBuffer->GetData() + fStartPositions[ fCurrentSound ], fCurrentSoundLength,
|
||||
(UInt8)(fDataBuffer->GetHeader().fNumChannels), fNumDestBytesPerSample );
|
||||
|
||||
deswiz.Extract( fChannelSelect, dataPtr );
|
||||
|
||||
dataPtr = (Byte *)dataPtr + fCurrentSoundLength / fDataBuffer->GetHeader().fNumChannels;
|
||||
dataLength -= fCurrentSoundLength / fDataBuffer->GetHeader().fNumChannels;
|
||||
|
||||
plProfile_EndTiming( StaticSwizzleTime );*/
|
||||
}
|
||||
|
||||
/// Fill the remaining part with empty space
|
||||
//memset( dataPtr, 0, dataLength );
|
||||
|
||||
/// Finally, unlock!
|
||||
//fDSoundBuffer->Unlock();
|
||||
}
|
||||
|
||||
/// All done!
|
||||
plProfile_EndTiming( StaticSndShoveTime );
|
||||
}
|
||||
|
||||
void plWin32GroupedSound::IDerivedActuallyPlay( void )
|
||||
{
|
||||
// Ensure there's a stop notify for us
|
||||
if( !fReallyPlaying )
|
||||
{
|
||||
fDSoundBuffer->Play();
|
||||
fReallyPlaying = true;
|
||||
}
|
||||
|
||||
plSoundEvent *event = IFindEvent( plSoundEvent::kStart );
|
||||
if( event != nil )
|
||||
event->SendCallbacks();
|
||||
}
|
||||
|
||||
hsBool plWin32GroupedSound::MsgReceive( plMessage* pMsg )
|
||||
{
|
||||
plSoundMsg *soundMsg = plSoundMsg::ConvertNoRef( pMsg );
|
||||
if( soundMsg != nil && soundMsg->Cmd( plSoundMsg::kSelectFromGroup ) )
|
||||
{
|
||||
IFillCurrentSound( soundMsg->fIndex );
|
||||
return true;
|
||||
}
|
||||
else if( soundMsg != nil && soundMsg->Cmd( plSoundMsg::kPlay ) )
|
||||
{
|
||||
Play();
|
||||
//plIncidentalMgr::GetInstance()->Play( this, plIncidentalMgr::kNormal );
|
||||
return true;
|
||||
}
|
||||
|
||||
return plWin32StaticSound::MsgReceive( pMsg );
|
||||
}
|
||||
|
99
Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.h
Normal file
99
Sources/Plasma/PubUtilLib/plAudio/plWin32GroupedSound.h
Normal file
@ -0,0 +1,99 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plWin32GroupedSound - Grouped version of a static sound. Lots of short //
|
||||
// sounds stored in the buffer, all share the same //
|
||||
// DSound playback buffer and only one plays at a //
|
||||
// time. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef plWin32GroupedSound_h
|
||||
#define plWin32GroupedSound_h
|
||||
|
||||
#include "plWin32StaticSound.h"
|
||||
|
||||
class hsResMgr;
|
||||
class plDSoundBuffer;
|
||||
class plEventCallbackMsg;
|
||||
|
||||
#include "plSoundEvent.h"
|
||||
|
||||
class plWin32GroupedSound : public plWin32StaticSound
|
||||
{
|
||||
public:
|
||||
plWin32GroupedSound();
|
||||
~plWin32GroupedSound();
|
||||
|
||||
CLASSNAME_REGISTER( plWin32GroupedSound );
|
||||
GETINTERFACE_ANY( plWin32GroupedSound, plWin32StaticSound );
|
||||
|
||||
virtual hsBool LoadSound( hsBool is3D );
|
||||
virtual hsBool MsgReceive( plMessage *pMsg );
|
||||
void SetPositionArray( UInt16 numSounds, UInt32 *posArray, hsScalar *volumeArray );
|
||||
hsScalar GetSoundLength( Int16 soundIndex );
|
||||
virtual double GetLength() { return GetSoundLength( fCurrentSound ); }
|
||||
|
||||
protected:
|
||||
UInt16 fCurrentSound;
|
||||
UInt32 fCurrentSoundLength;
|
||||
hsTArray<UInt32> fStartPositions; // In bytes
|
||||
hsTArray<hsScalar> fVolumes;
|
||||
|
||||
// Some extra handy info for us
|
||||
UInt8 fNumDestChannels, fNumDestBytesPerSample;
|
||||
|
||||
virtual void IDerivedActuallyPlay( void );
|
||||
|
||||
virtual void IRead( hsStream *s, hsResMgr *mgr );
|
||||
virtual void IWrite( hsStream *s, hsResMgr *mgr );
|
||||
|
||||
UInt32 IGetSoundByteLength( Int16 soundIndex );
|
||||
void IFillCurrentSound( Int16 newCurrent = -1 );
|
||||
|
||||
// Abstracting a few things here for the incidentalMgr
|
||||
virtual void * IGetDataPointer( void ) const;
|
||||
virtual UInt32 IGetDataLength( void ) const;
|
||||
};
|
||||
|
||||
#endif //plWin32GroupedSound_h
|
445
Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp
Normal file
445
Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp
Normal file
@ -0,0 +1,445 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#include <direct.h>
|
||||
#include "HeadSpin.h"
|
||||
#include "hsGeometry3.h"
|
||||
#include "hsTimer.h"
|
||||
#include "hsResMgr.h"
|
||||
#include "plgDispatch.h"
|
||||
|
||||
#include "plProfile.h"
|
||||
#include "plWin32Sound.h"
|
||||
#include "plAudioSystem.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
#include "plWavFile.h"
|
||||
|
||||
#include "../plAudible/plWinAudible.h"
|
||||
#include "../plNetMessage/plNetMessage.h"
|
||||
#include "../pnNetCommon/plNetApp.h"
|
||||
#include "../pnMessage/plSoundMsg.h"
|
||||
#include "../pnMessage/plEventCallbackMsg.h"
|
||||
#include "../plPipeline/plPlates.h"
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
plProfile_CreateMemCounter("Sounds", "Memory", MemSounds);
|
||||
plProfile_Extern(SoundPlaying);
|
||||
|
||||
plWin32Sound::plWin32Sound() :
|
||||
fFailed(false),
|
||||
fPositionInited(false),
|
||||
fAwaitingPosition(false),
|
||||
fTotalBytes(0),
|
||||
fReallyPlaying(false),
|
||||
fChannelSelect(0),
|
||||
fDSoundBuffer(nil)
|
||||
{
|
||||
}
|
||||
|
||||
plWin32Sound::~plWin32Sound()
|
||||
{
|
||||
}
|
||||
|
||||
void plWin32Sound::Activate( hsBool forcePlay )
|
||||
{
|
||||
if( fFailed )
|
||||
return;
|
||||
|
||||
plSound::Activate( forcePlay );
|
||||
}
|
||||
|
||||
void plWin32Sound::DeActivate()
|
||||
{
|
||||
plSound::DeActivate();
|
||||
IFreeBuffers();
|
||||
}
|
||||
|
||||
void plWin32Sound::IFreeBuffers( void )
|
||||
{
|
||||
if( fDSoundBuffer != nil )
|
||||
{
|
||||
delete fDSoundBuffer;
|
||||
fDSoundBuffer = nil;
|
||||
plProfile_DelMem(MemSounds, fTotalBytes);
|
||||
fTotalBytes = 0;
|
||||
}
|
||||
|
||||
fPositionInited = false;
|
||||
fAwaitingPosition = false;
|
||||
}
|
||||
|
||||
void plWin32Sound::Update()
|
||||
{
|
||||
plSound::Update();
|
||||
}
|
||||
|
||||
void plWin32Sound::IActuallyPlay( void )
|
||||
{
|
||||
//plSound::Play();
|
||||
if (!fDSoundBuffer && plgAudioSys::Active())
|
||||
LoadSound( IsPropertySet( kPropIs3DSound ) );
|
||||
if(!fLoading )
|
||||
{
|
||||
if (fDSoundBuffer && plgAudioSys::Active() )
|
||||
{
|
||||
|
||||
// Sometimes base/derived classes can be annoying
|
||||
IDerivedActuallyPlay();
|
||||
RefreshVolume();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we can't load (for ex., if audio is off), then we act like we played and then stopped
|
||||
// really fast. Thus, we need to send *all* of our callbacks off immediately and then Stop().
|
||||
UInt32 i;
|
||||
for( i = 0; i < fSoundEvents.GetCount(); i++ )
|
||||
{
|
||||
fSoundEvents[ i ]->SendCallbacks();
|
||||
}
|
||||
|
||||
// Now stop, 'cause we played really really really really fast
|
||||
fPlaying = false;
|
||||
fPlayOnReactivate = IsPropertySet( kPropLooping );
|
||||
IActuallyStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32Sound::IActuallyStop()
|
||||
{
|
||||
if( fReallyPlaying )
|
||||
{
|
||||
if( fDSoundBuffer != nil )
|
||||
{
|
||||
if(IsPropertySet(kPropIncidental))
|
||||
{
|
||||
--fIncidentalsPlaying;
|
||||
}
|
||||
fDSoundBuffer->Stop();
|
||||
plStatusLog::AddLineS("impacts.log", "Stopping %s", GetKeyName());
|
||||
|
||||
}
|
||||
fReallyPlaying = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( fDSoundBuffer != nil && fDSoundBuffer->IsPlaying() )
|
||||
{
|
||||
plStatusLog::AddLineS( "audio.log", 0xffff0000, "WARNING: BUFFER FLAGGED AS STOPPED BUT NOT STOPPED - %s", GetKey() ? GetKeyName() : nil );
|
||||
fDSoundBuffer->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
// send callbacks
|
||||
plSoundEvent *event = IFindEvent( plSoundEvent::kStop );
|
||||
if( event != nil )
|
||||
{
|
||||
event->SendCallbacks();
|
||||
}
|
||||
|
||||
plSound::IActuallyStop();
|
||||
}
|
||||
|
||||
plSoundMsg* plWin32Sound::GetStatus(plSoundMsg* pMsg)
|
||||
{
|
||||
plSoundMsg* pReply = TRACKED_NEW plSoundMsg;
|
||||
pReply->AddReceiver( pMsg->GetSender() );
|
||||
pReply->SetCmd(plSoundMsg::kStatusReply);
|
||||
pReply->fLoop = IsPropertySet( kPropLooping );
|
||||
pReply->fPlaying = IsPlaying();
|
||||
return pReply;
|
||||
}
|
||||
|
||||
void plWin32Sound::SetMin( const int m )
|
||||
{
|
||||
plSound::SetMin(m);
|
||||
if(fDSoundBuffer)
|
||||
{
|
||||
fDSoundBuffer->SetMinDistance(m);
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32Sound::SetMax( const int m )
|
||||
{
|
||||
plSound::SetMax(m);
|
||||
if( fDSoundBuffer )
|
||||
{
|
||||
fDSoundBuffer->SetMaxDistance( m );
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32Sound::SetOuterVolume( const int v )
|
||||
{
|
||||
plSound::SetOuterVolume(v);
|
||||
if(fDSoundBuffer)
|
||||
{
|
||||
fDSoundBuffer->SetConeOutsideVolume(v);
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32Sound::SetConeAngles( int inner, int outer )
|
||||
{
|
||||
plSound::SetConeAngles(inner, outer);
|
||||
if(fDSoundBuffer)
|
||||
{
|
||||
fDSoundBuffer->SetConeAngles(inner, outer);
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32Sound::SetConeOrientation( hsScalar x, hsScalar y, hsScalar z )
|
||||
{
|
||||
plSound::SetConeOrientation(x, y, z);
|
||||
if(fDSoundBuffer)
|
||||
{
|
||||
fDSoundBuffer->SetConeOrientation(x, z, y);
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32Sound::SetVelocity( const hsVector3 vel )
|
||||
{
|
||||
plSound::SetVelocity(vel);
|
||||
if( fDSoundBuffer)
|
||||
fDSoundBuffer->SetVelocity(vel.fX, vel.fZ, vel.fY);
|
||||
}
|
||||
|
||||
void plWin32Sound::SetPosition( const hsPoint3 pos )
|
||||
{
|
||||
plSound::SetPosition(pos);
|
||||
if(fDSoundBuffer)
|
||||
{
|
||||
// in openal sounds that are mono are played as positional. Since gui's may be positioned way off in space, the sound may not be audible.
|
||||
// doing this allows us to play mono gui sounds and still hear them, since this attaches the sound to the listener.
|
||||
if(fType == kGUISound)
|
||||
{
|
||||
hsPoint3 listenerPos = plgAudioSys::Sys()->GetCurrListenerPos();
|
||||
fDSoundBuffer->SetPosition(listenerPos.fX, listenerPos.fZ, listenerPos.fY);
|
||||
}
|
||||
else
|
||||
{
|
||||
fDSoundBuffer->SetPosition(pos.fX, pos.fZ, pos.fY);
|
||||
}
|
||||
}
|
||||
|
||||
fPositionInited = true;
|
||||
if( fAwaitingPosition )
|
||||
{
|
||||
// If this is set, then we tried to set the volume before the position. Since
|
||||
// this results in some ghastly sound popping, we wait until we set the position
|
||||
// (here), and then call our volume again
|
||||
RefreshVolume();
|
||||
fAwaitingPosition = false;
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32Sound::ISetActualVolume(float volume)
|
||||
{
|
||||
float vol = IAttenuateActualVolume( volume ) * IGetChannelVolume();
|
||||
if( fDSoundBuffer )
|
||||
{
|
||||
if( fPositionInited || !IsPropertySet( kPropIs3DSound ) )
|
||||
{
|
||||
fDSoundBuffer->SetScalarVolume( vol );
|
||||
}
|
||||
else
|
||||
{
|
||||
// If position isn't inited, we don't want to set the volume yet,
|
||||
// so set this flag so we know to set the volume once we DO get the position
|
||||
fAwaitingPosition = true;
|
||||
}
|
||||
}
|
||||
IUpdateDebugPlate(); // Byte me.
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// The base class will make sure all our params are correct and up-to-date,
|
||||
// but before it does its work, we have to make sure our buffer is ready to
|
||||
// be updated.
|
||||
void plWin32Sound::IRefreshParams( void )
|
||||
{
|
||||
if (!fDSoundBuffer && plgAudioSys::Active())
|
||||
LoadSound( IsPropertySet( kPropIs3DSound ) );
|
||||
|
||||
else
|
||||
{
|
||||
// There is a gap between the ds buffer stopping and the sound being marked as stopped.
|
||||
// If the sound is asked to play again during this time frame it will never actually be
|
||||
// played because it is still marked as playing. Not only that, but we'll lose a hardware voice too.
|
||||
// This will fix that by starting it up again.
|
||||
if(plgAudioSys::Active())
|
||||
fDSoundBuffer->Play();
|
||||
}
|
||||
plSound::IRefreshParams();
|
||||
}
|
||||
|
||||
void plWin32Sound::IRefreshEAXSettings( hsBool force )
|
||||
{
|
||||
if( fDSoundBuffer != nil )
|
||||
fDSoundBuffer->SetEAXSettings( &GetEAXSettings(), force );
|
||||
}
|
||||
|
||||
void plWin32Sound::IAddCallback( plEventCallbackMsg *pMsg )
|
||||
{
|
||||
plSoundEvent::Types type = plSoundEvent::GetTypeFromCallbackMsg( pMsg );
|
||||
plSoundEvent *event;
|
||||
|
||||
if( type == plSoundEvent::kTime )
|
||||
{
|
||||
UInt32 byteTime = ( fDSoundBuffer != nil ) ? fDSoundBuffer->GetBufferBytePos( pMsg->fEventTime ) : 0;
|
||||
|
||||
event = IFindEvent( type, byteTime );
|
||||
|
||||
if( event == nil )
|
||||
{
|
||||
// Add a new sound event for this guy
|
||||
event = TRACKED_NEW plSoundEvent( type, byteTime, this );
|
||||
//fDSoundBuffer->AddPosNotify( byteTime );
|
||||
fSoundEvents.Append( event );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
event = IFindEvent( type );
|
||||
|
||||
if( event == nil )
|
||||
{
|
||||
// Add a new sound event for this guy
|
||||
event = TRACKED_NEW plSoundEvent( type, this );
|
||||
fSoundEvents.Append( event );
|
||||
}
|
||||
}
|
||||
|
||||
event->AddCallback( pMsg );
|
||||
}
|
||||
|
||||
void plWin32Sound::IRemoveCallback( plEventCallbackMsg *pMsg )
|
||||
{
|
||||
plSoundEvent::Types type = plSoundEvent::GetTypeFromCallbackMsg( pMsg );
|
||||
|
||||
for(int i = 0; i < fSoundEvents.GetCount(); ++i)
|
||||
{
|
||||
if( fSoundEvents[ i ]->GetType() == type )
|
||||
{
|
||||
if( fSoundEvents[ i ]->RemoveCallback( pMsg ) )
|
||||
{
|
||||
if( fSoundEvents[ i ]->GetNumCallbacks() == 0 )
|
||||
{
|
||||
//if( fSoundEvents[ i ]->GetType() == plSoundEvent::kTime )
|
||||
//fDSoundBuffer->RemovePosNotify( fSoundEvents[ i ]->GetTime() );
|
||||
|
||||
delete fSoundEvents[ i ];
|
||||
fSoundEvents.Remove( i );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plSoundEvent *plWin32Sound::IFindEvent( plSoundEvent::Types type, UInt32 bytePos )
|
||||
{
|
||||
for(int i = 0; i < fSoundEvents.GetCount(); ++i )
|
||||
{
|
||||
if( fSoundEvents[ i ]->GetType() == type )
|
||||
{
|
||||
if( type != plSoundEvent::kTime || bytePos == fSoundEvents[ i ]->GetTime() )
|
||||
return fSoundEvents[ i ];
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
void plWin32Sound::RemoveCallbacks(plSoundMsg* pSoundMsg)
|
||||
{
|
||||
for(int i = 0; i < pSoundMsg->GetNumCallbacks(); ++i )
|
||||
IRemoveCallback( pSoundMsg->GetEventCallback( i ) );
|
||||
}
|
||||
|
||||
void plWin32Sound::AddCallbacks(plSoundMsg* pSoundMsg)
|
||||
{
|
||||
for(int i = 0; i < pSoundMsg->GetNumCallbacks(); ++i )
|
||||
IAddCallback( pSoundMsg->GetEventCallback( i ) );
|
||||
}
|
||||
|
||||
hsBool plWin32Sound::MsgReceive( plMessage* pMsg )
|
||||
{
|
||||
plEventCallbackMsg *e = plEventCallbackMsg::ConvertNoRef( pMsg );
|
||||
if( e != nil )
|
||||
{
|
||||
if( e->fEvent == kStop )
|
||||
{
|
||||
fPlaying = false;
|
||||
fPlayOnReactivate = false; // Just to make sure...
|
||||
|
||||
// Do we have an ending fade?
|
||||
if( fFadeOutParams.fLengthInSecs > 0 )
|
||||
{
|
||||
IStartFade( &fFadeOutParams );
|
||||
plSoundEvent *event = IFindEvent( plSoundEvent::kStop );
|
||||
if( event != nil )
|
||||
event->SendCallbacks();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( fFading )
|
||||
IStopFade();
|
||||
|
||||
fCurrVolume = 0.f;
|
||||
this->ISetActualVolume( fCurrVolume );
|
||||
}
|
||||
this->IActuallyStop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return plSound::MsgReceive( pMsg );
|
||||
}
|
||||
|
||||
void plWin32Sound::IRead( hsStream *s, hsResMgr *mgr )
|
||||
{
|
||||
plSound::IRead( s, mgr );
|
||||
fChannelSelect = s->ReadByte();
|
||||
}
|
||||
|
||||
void plWin32Sound::IWrite( hsStream *s, hsResMgr *mgr )
|
||||
{
|
||||
plSound::IWrite( s, mgr );
|
||||
s->WriteByte( fChannelSelect );
|
||||
}
|
136
Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.h
Normal file
136
Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.h
Normal file
@ -0,0 +1,136 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#ifndef plWin32Sound_h
|
||||
#define plWin32Sound_h
|
||||
|
||||
#include "hsTemplates.h"
|
||||
#include "plSound.h"
|
||||
#include "hsThread.h"
|
||||
#include "plSoundEvent.h"
|
||||
|
||||
#define NUM_MAX_HANDLES 16
|
||||
#define REPEAT_INFINITE 0xffffffff
|
||||
|
||||
struct hsVector3;
|
||||
class hsResMgr;
|
||||
class plEventCallbackMsg;
|
||||
class plSoundMsg;
|
||||
class DSoundCallbackHandle;
|
||||
class plSoundEvent;
|
||||
class plDSoundBuffer;
|
||||
|
||||
class plWin32Sound : public plSound
|
||||
{
|
||||
public:
|
||||
|
||||
plWin32Sound();
|
||||
virtual ~plWin32Sound();
|
||||
|
||||
CLASSNAME_REGISTER( plWin32Sound );
|
||||
GETINTERFACE_ANY( plWin32Sound, plSound );
|
||||
|
||||
virtual void Activate(hsBool forcePlay = false);
|
||||
virtual void DeActivate();
|
||||
|
||||
virtual void AddCallbacks(plSoundMsg* pMsg);
|
||||
virtual void RemoveCallbacks(plSoundMsg* pMsg);
|
||||
|
||||
virtual plSoundMsg* GetStatus(plSoundMsg* pMsg);
|
||||
virtual hsBool MsgReceive(plMessage* pMsg);
|
||||
virtual void Update();
|
||||
|
||||
virtual void SetMin(const int m); // sets minimum falloff distance
|
||||
virtual void SetMax(const int m); // sets maximum falloff distance
|
||||
virtual void SetConeOrientation(hsScalar x, hsScalar y, hsScalar z);
|
||||
virtual void SetOuterVolume( const int v ); // volume for the outer cone (if applicable)
|
||||
virtual void SetConeAngles( int inner, int outer );
|
||||
virtual void SetPosition(const hsPoint3 pos);
|
||||
virtual void SetVelocity(const hsVector3 vel);
|
||||
|
||||
enum ChannelSelect
|
||||
{
|
||||
kLeftChannel,
|
||||
kRightChannel
|
||||
};
|
||||
|
||||
// Selects a channel source from a multi-channel (stereo) file. Ignored if source is mono
|
||||
void SetChannelSelect( ChannelSelect source ) { fChannelSelect = (UInt8)source; }
|
||||
virtual UInt8 GetChannelSelect( void ) const { return fChannelSelect; }
|
||||
|
||||
protected:
|
||||
|
||||
plDSoundBuffer * fDSoundBuffer;
|
||||
|
||||
hsBool fFailed;
|
||||
hsBool fPositionInited, fAwaitingPosition;
|
||||
hsBool fReallyPlaying;
|
||||
UInt32 fTotalBytes;
|
||||
|
||||
hsBool fWasPlaying;
|
||||
|
||||
UInt8 fChannelSelect; // For selecting a mono channel from a stereo file
|
||||
|
||||
hsTArray<plSoundEvent *> fSoundEvents;
|
||||
|
||||
virtual void ISetActualVolume(float v);
|
||||
virtual void IActuallyStop( void );
|
||||
virtual hsBool IActuallyPlaying( void ) { return fReallyPlaying; }
|
||||
virtual void IActuallyPlay( void );
|
||||
virtual void IFreeBuffers( void );
|
||||
virtual hsBool IActuallyLoaded( void ) { return ( fDSoundBuffer != nil ) ? true : false; }
|
||||
|
||||
// Override to make sure the buffer is available before the base class is called
|
||||
virtual void IRefreshParams( void );
|
||||
|
||||
virtual void IDerivedActuallyPlay( void ) = 0;
|
||||
|
||||
virtual void IAddCallback( plEventCallbackMsg *pMsg );
|
||||
virtual void IRemoveCallback( plEventCallbackMsg *pMsg );
|
||||
plSoundEvent *IFindEvent( plSoundEvent::Types type, UInt32 bytePos = 0 );
|
||||
|
||||
virtual void IRead( hsStream *s, hsResMgr *mgr );
|
||||
virtual void IWrite( hsStream *s, hsResMgr *mgr );
|
||||
|
||||
virtual void IRefreshEAXSettings( hsBool force = false );
|
||||
};
|
||||
|
||||
#endif //plWin32Sound_h
|
343
Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.cpp
Normal file
343
Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.cpp
Normal file
@ -0,0 +1,343 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#include "HeadSpin.h"
|
||||
#include "hsGeometry3.h"
|
||||
#include "hsResMgr.h"
|
||||
#include "plgDispatch.h"
|
||||
#include "plProfile.h"
|
||||
#include "plWin32StaticSound.h"
|
||||
#include "plWin32Sound.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
#include "plAudioSystem.h"
|
||||
#include "../plAudioCore/plSoundBuffer.h"
|
||||
#include "../plAudioCore/plSoundDeswizzler.h"
|
||||
#include "../pnMessage/plEventCallbackMsg.h"
|
||||
#include "../pnMessage/plAudioSysMsg.h"
|
||||
#include "../plMessage/plLinkToAgeMsg.h"
|
||||
#include "../plMessage/plAvatarMsg.h"
|
||||
|
||||
#include "../plPipeline/plPlates.h"
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
plProfile_Extern(MemSounds);
|
||||
plProfile_CreateAsynchTimer( "Static Shove Time", "Sound", StaticSndShoveTime );
|
||||
plProfile_CreateAsynchTimer( "Static Swizzle Time", "Sound", StaticSwizzleTime );
|
||||
|
||||
plWin32StaticSound::plWin32StaticSound()
|
||||
{
|
||||
}
|
||||
|
||||
plWin32StaticSound::~plWin32StaticSound()
|
||||
{
|
||||
DeActivate();
|
||||
IUnloadDataBuffer();
|
||||
}
|
||||
|
||||
void plWin32StaticSound::Activate( hsBool forcePlay )
|
||||
{
|
||||
plWin32Sound::Activate( forcePlay );
|
||||
}
|
||||
|
||||
void plWin32StaticSound::DeActivate()
|
||||
{
|
||||
plWin32Sound::DeActivate();
|
||||
}
|
||||
|
||||
hsBool plWin32StaticSound::LoadSound( hsBool is3D )
|
||||
{
|
||||
if (fFailed)
|
||||
return false;
|
||||
|
||||
if( fPriority > plgAudioSys::GetPriorityCutoff() )
|
||||
return false; // Don't set the failed flag, just return
|
||||
|
||||
if (plgAudioSys::Active() && !fDSoundBuffer)
|
||||
{
|
||||
// Debug flag #1
|
||||
if( fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) )
|
||||
{
|
||||
// Force a fail
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need it to be resident to read in
|
||||
plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true);
|
||||
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
|
||||
if(!buffer)
|
||||
{
|
||||
return plSoundBuffer::kError;
|
||||
}
|
||||
|
||||
if( retVal == plSoundBuffer::kPending) // we are still reading data.
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if( retVal == plSoundBuffer::kError )
|
||||
{
|
||||
char str[ 256 ];
|
||||
sprintf( str, "Unable to open .wav file %s", fDataBufferKey ? fDataBufferKey->GetName() : "nil");
|
||||
IPrintDbgMessage( str, true );
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
SetProperty( kPropIs3DSound, is3D );
|
||||
|
||||
plWAVHeader header = buffer->GetHeader();
|
||||
|
||||
// Debug flag #2
|
||||
if( fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) )
|
||||
{
|
||||
// Force a fail
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
UInt32 bufferSize = buffer->GetDataLength();
|
||||
|
||||
if( header.fNumChannels > 1 && is3D )
|
||||
{
|
||||
// We can only do a single channel of 3D sound. So copy over one (later)
|
||||
bufferSize /= header.fNumChannels;
|
||||
header.fBlockAlign /= header.fNumChannels;
|
||||
header.fAvgBytesPerSec /= header.fNumChannels;
|
||||
header.fNumChannels = 1;
|
||||
}
|
||||
|
||||
hsBool tryStatic = true;
|
||||
// If we want FX, we can't use a static voice, but EAX doesn't fit under that limitation :)
|
||||
if( 0 )
|
||||
tryStatic = false;
|
||||
|
||||
// Create our DSound buffer (or rather, the wrapper around it)
|
||||
fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet( kPropLooping ), tryStatic );
|
||||
if( !fDSoundBuffer->IsValid() )
|
||||
{
|
||||
char str[256];
|
||||
sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName());
|
||||
IPrintDbgMessage( str, true );
|
||||
fFailed = true;
|
||||
|
||||
delete fDSoundBuffer;
|
||||
fDSoundBuffer = nil;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
plProfile_BeginTiming( StaticSndShoveTime );
|
||||
|
||||
if(!fDSoundBuffer->FillBuffer(buffer->GetData(), buffer->GetDataLength(), &header))
|
||||
{
|
||||
delete fDSoundBuffer;
|
||||
fDSoundBuffer = nil;
|
||||
plStatusLog::AddLineS("audio.log", "Could not play static sound, no voices left %s", GetKeyName());
|
||||
return false;
|
||||
}
|
||||
|
||||
plProfile_EndTiming( StaticSndShoveTime );
|
||||
IRefreshEAXSettings( true );
|
||||
|
||||
fTotalBytes = bufferSize;
|
||||
|
||||
plProfile_NewMem(MemSounds, fTotalBytes);
|
||||
|
||||
// get pertinent info
|
||||
hsScalar length = (hsScalar)bufferSize / (hsScalar)header.fAvgBytesPerSec;
|
||||
SetLength(length);
|
||||
|
||||
if( fLoadFromDiskOnDemand && !IsPropertySet( kPropLoadOnlyOnCall ) )
|
||||
FreeSoundData();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void plWin32StaticSound::Update()
|
||||
{
|
||||
plWin32Sound::Update();
|
||||
if(fDSoundBuffer)
|
||||
{
|
||||
if(fPlaying) // we think we are playing
|
||||
{
|
||||
if(!fDSoundBuffer->IsPlaying()) // are we actually playing
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void plWin32StaticSound::IDerivedActuallyPlay( void )
|
||||
{
|
||||
// Ensure there's a stop notify for us
|
||||
if( !fReallyPlaying )
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(IsPropertySet(kPropIncidental))
|
||||
{
|
||||
if(fIncidentalsPlaying >= MAX_INCIDENTALS)
|
||||
break;
|
||||
++fIncidentalsPlaying;
|
||||
}
|
||||
|
||||
fDSoundBuffer->Play();
|
||||
fReallyPlaying = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
plSoundEvent *event = IFindEvent( plSoundEvent::kStart );
|
||||
if( event != nil )
|
||||
event->SendCallbacks();
|
||||
}
|
||||
|
||||
float plWin32StaticSound::GetActualTimeSec()
|
||||
{
|
||||
if(fDSoundBuffer)
|
||||
return fDSoundBuffer->GetTimeOffsetSec();
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void plWin32StaticSound::ISetActualTime(double t)
|
||||
{
|
||||
if( !fDSoundBuffer && plgAudioSys::Active())
|
||||
LoadSound( IsPropertySet( kPropIs3DSound ) );
|
||||
if( fDSoundBuffer )
|
||||
{
|
||||
if(!t)
|
||||
fDSoundBuffer->SetTimeOffsetSec((float)t);
|
||||
}
|
||||
}
|
||||
|
||||
hsBool plWin32StaticSound::MsgReceive( plMessage* pMsg )
|
||||
{
|
||||
return plWin32Sound::MsgReceive( pMsg );
|
||||
}
|
||||
|
||||
void plWin32StaticSound::IRemoveCallback( plEventCallbackMsg *pCBMsg )
|
||||
{
|
||||
plWin32Sound::IRemoveCallback( pCBMsg );
|
||||
}
|
||||
|
||||
void plWin32StaticSound::IAddCallback( plEventCallbackMsg *pCBMsg )
|
||||
{
|
||||
if( plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStop &&
|
||||
plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStart )
|
||||
{
|
||||
hsAssert( false, "Static sounds only support start and stop callbacks at this time." );
|
||||
return;
|
||||
}
|
||||
plWin32Sound::IAddCallback( pCBMsg );
|
||||
}
|
||||
|
||||
|
||||
plWin32LinkSound::plWin32LinkSound()
|
||||
{
|
||||
SetLocalOnly(true); // linking sounds already synch at a higher level
|
||||
SetProperty( kPropDontFade, true );
|
||||
}
|
||||
|
||||
void plWin32LinkSound::Read(hsStream* s, hsResMgr* mgr)
|
||||
{
|
||||
plWin32StaticSound::Read(s, mgr);
|
||||
|
||||
plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), GetKey());
|
||||
plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey());
|
||||
plgDispatch::Dispatch()->RegisterForExactType(plPseudoLinkEffectMsg::Index(), GetKey());
|
||||
|
||||
SetLocalOnly(true); // linking sounds already synch at a higher level
|
||||
SetProperty( kPropDontFade, true );
|
||||
}
|
||||
|
||||
void plWin32LinkSound::Write(hsStream* s, hsResMgr* mgr)
|
||||
{
|
||||
plWin32StaticSound::Write(s, mgr);
|
||||
}
|
||||
|
||||
hsBool plWin32LinkSound::MsgReceive( plMessage* pMsg )
|
||||
{
|
||||
plLinkEffectBCMsg *msg = plLinkEffectBCMsg::ConvertNoRef( pMsg );
|
||||
if( msg != nil && !msg->HasLinkFlag(plLinkEffectBCMsg::kMute))
|
||||
{
|
||||
if (msg->fLinkKey->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID())
|
||||
{
|
||||
if (!IsPropertySet(kPropFullyDisabled))
|
||||
{
|
||||
ISetActualTime(0);
|
||||
Play();
|
||||
//Activate(true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
plPseudoLinkEffectMsg *psmsg = plPseudoLinkEffectMsg::ConvertNoRef( pMsg );
|
||||
if( psmsg != nil)
|
||||
{
|
||||
if (psmsg->fAvatarKey->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID())
|
||||
{
|
||||
if (!IsPropertySet(kPropFullyDisabled))
|
||||
{
|
||||
ISetActualTime(0);
|
||||
//Play();
|
||||
Activate(true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
plAvatarStealthModeMsg *sMsg = plAvatarStealthModeMsg::ConvertNoRef(pMsg);
|
||||
if (sMsg)
|
||||
{
|
||||
if (sMsg->GetSender()->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID())
|
||||
{
|
||||
SetProperty(kPropFullyDisabled, (sMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked));
|
||||
plNetApp::StaticDebugMsg("plWin32LinkSound: rcvd avatarStealth msg, cloaked=%d", sMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return plWin32StaticSound::MsgReceive( pMsg );
|
||||
}
|
96
Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.h
Normal file
96
Sources/Plasma/PubUtilLib/plAudio/plWin32StaticSound.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#ifndef plWin32StaticSound_h
|
||||
#define plWin32StaticSound_h
|
||||
|
||||
#include "plWin32Sound.h"
|
||||
|
||||
class hsResMgr;
|
||||
class plDSoundBuffer;
|
||||
class plEventCallbackMsg;
|
||||
|
||||
#include "plSoundEvent.h"
|
||||
|
||||
class plWin32StaticSound : public plWin32Sound
|
||||
{
|
||||
public:
|
||||
plWin32StaticSound();
|
||||
~plWin32StaticSound();
|
||||
|
||||
CLASSNAME_REGISTER( plWin32StaticSound );
|
||||
GETINTERFACE_ANY( plWin32StaticSound, plWin32Sound );
|
||||
|
||||
virtual void Activate( hsBool forcePlay = false );
|
||||
virtual void DeActivate();
|
||||
virtual hsBool LoadSound( hsBool is3D );
|
||||
virtual void Update();
|
||||
virtual hsBool MsgReceive(plMessage* pMsg);
|
||||
virtual void SetStartPos(unsigned bytes){}
|
||||
|
||||
protected:
|
||||
hsBool fRegisteredOnThread;
|
||||
|
||||
virtual void IDerivedActuallyPlay( void );
|
||||
virtual void ISetActualTime( double t );
|
||||
virtual float GetActualTimeSec();
|
||||
|
||||
virtual void IAddCallback( plEventCallbackMsg *pCBMsg );
|
||||
virtual void IRemoveCallback( plEventCallbackMsg *pCBMsg );
|
||||
|
||||
};
|
||||
|
||||
// Same as a plWin32StaticSound, except this registers for a plLinkEffectBCMsg to play the sound on linking.
|
||||
class plWin32LinkSound : public plWin32StaticSound
|
||||
{
|
||||
public:
|
||||
plWin32LinkSound();
|
||||
~plWin32LinkSound() { }
|
||||
|
||||
CLASSNAME_REGISTER( plWin32LinkSound );
|
||||
GETINTERFACE_ANY( plWin32LinkSound, plWin32StaticSound );
|
||||
|
||||
virtual void Read(hsStream* s, hsResMgr* mgr);
|
||||
virtual void Write(hsStream* s, hsResMgr* mgr);
|
||||
virtual hsBool MsgReceive(plMessage* pMsg);
|
||||
};
|
||||
|
||||
#endif //plWin32StaticSound_h
|
509
Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp
Normal file
509
Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp
Normal file
@ -0,0 +1,509 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#include <direct.h>
|
||||
#include "HeadSpin.h"
|
||||
#include "hsTimer.h"
|
||||
#include "hsGeometry3.h"
|
||||
#include "plgDispatch.h"
|
||||
#include "plProfile.h"
|
||||
|
||||
#include "plWin32Sound.h"
|
||||
#include "plWin32StreamingSound.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
#include "plAudioSystem.h"
|
||||
|
||||
#include "../plAudioCore/plAudioFileReader.h"
|
||||
#include "../plAudioCore/plSoundBuffer.h"
|
||||
#include "../plAudioCore/plSoundDeswizzler.h"
|
||||
#include "../pnMessage/plSoundMsg.h"
|
||||
#include "../pnMessage/plEventCallbackMsg.h"
|
||||
#include "../plStatusLog/plStatusLog.h"
|
||||
|
||||
#define STREAMING_UPDATE_MS 200
|
||||
|
||||
plProfile_Extern(MemSounds);
|
||||
plProfile_CreateAsynchTimer( "Stream Shove Time", "Sound", StreamSndShoveTime );
|
||||
plProfile_CreateAsynchTimer( "Stream Swizzle Time", "Sound", StreamSwizzleTime );
|
||||
plProfile_Extern( SoundLoadTime );
|
||||
|
||||
plWin32StreamingSound::plWin32StreamingSound() :
|
||||
fDataStream(nil),
|
||||
fBlankBufferFillCounter(0),
|
||||
fDeswizzler(nil),
|
||||
fStreamType(kNoStream),
|
||||
fLastStreamingUpdate(0),
|
||||
fStopping(false),
|
||||
fPlayWhenStopped(false),
|
||||
fStartPos(0)
|
||||
{
|
||||
fBufferLengthInSecs = plgAudioSys::GetStreamingBufferSize();
|
||||
}
|
||||
|
||||
plWin32StreamingSound::~plWin32StreamingSound()
|
||||
{
|
||||
/// Call before we delete our dataStream
|
||||
DeActivate();
|
||||
IUnloadDataBuffer();
|
||||
|
||||
delete fDataStream;
|
||||
fDataStream = nil;
|
||||
fSrcFilename[ 0 ] = 0;
|
||||
|
||||
delete fDeswizzler;
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::DeActivate()
|
||||
{
|
||||
plWin32Sound::DeActivate();
|
||||
}
|
||||
|
||||
// Change the filename used by this streaming sound, so we can play a different file
|
||||
void plWin32StreamingSound::SetFilename(const char *filename, bool isCompressed)
|
||||
{
|
||||
fNewFilename = filename;
|
||||
fIsCompressed = isCompressed;
|
||||
fFailed = false; // just in case the last sound failed to load turn this off so it can try to load the new sound
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Override, 'cause we don't want to actually LOAD the sound for streaming,
|
||||
// just make sure it's decompressed and such and ready to stream.
|
||||
plSoundBuffer::ELoadReturnVal plWin32StreamingSound::IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental /* = false */ )
|
||||
{
|
||||
if(fPlayWhenStopped)
|
||||
return plSoundBuffer::kPending;
|
||||
hsBool sfxPath = fNewFilename.size() ? false : true;
|
||||
|
||||
if( fDataStream != nil && fNewFilename.size() == 0)
|
||||
return plSoundBuffer::kSuccess; // Already loaded
|
||||
|
||||
if(!ILoadDataBuffer())
|
||||
{
|
||||
return plSoundBuffer::kError;
|
||||
}
|
||||
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
|
||||
if(!buffer)
|
||||
return plSoundBuffer::kError;
|
||||
|
||||
// The databuffer also needs to know if the source is compressed or not
|
||||
if(fNewFilename.length())
|
||||
{
|
||||
buffer->SetFileName(fNewFilename.c_str());
|
||||
buffer->SetFlag(plSoundBuffer::kStreamCompressed, fIsCompressed);
|
||||
fNewFilename.clear();
|
||||
if(fReallyPlaying)
|
||||
{
|
||||
fPlayWhenStopped = true;
|
||||
return plSoundBuffer::kPending;
|
||||
}
|
||||
}
|
||||
|
||||
if( buffer->IsValid() )
|
||||
{
|
||||
plAudioFileReader::StreamType type = plAudioFileReader::kStreamNative;
|
||||
fStreamType = kStreamCompressed;
|
||||
|
||||
if(!buffer->HasFlag(plSoundBuffer::kStreamCompressed))
|
||||
{
|
||||
if(buffer->GetDataLengthInSecs() > plgAudioSys::GetStreamFromRAMCutoff( ))
|
||||
{
|
||||
fStreamType = kStreamFromDisk;
|
||||
type = plAudioFileReader::kStreamWAV;
|
||||
}
|
||||
else
|
||||
{
|
||||
fStreamType = kStreamFromRAM;
|
||||
type = plAudioFileReader::kStreamRAM;
|
||||
}
|
||||
}
|
||||
|
||||
if(!fStartPos)
|
||||
{
|
||||
if(buffer->AsyncLoad(type, isIncidental ? 0 : STREAMING_BUFFERS * STREAM_BUFFER_SIZE ) == plSoundBuffer::kPending)
|
||||
{
|
||||
fPlayWhenLoaded = playWhenLoaded;
|
||||
fLoading = true;
|
||||
return plSoundBuffer::kPending;
|
||||
}
|
||||
}
|
||||
|
||||
char str[ 256 ];
|
||||
strncpy( fSrcFilename, buffer->GetFileName(), sizeof( fSrcFilename ) );
|
||||
bool streamCompressed = (buffer->HasFlag(plSoundBuffer::kStreamCompressed) != 0);
|
||||
|
||||
delete fDataStream;
|
||||
fDataStream = buffer->GetAudioReader();
|
||||
if(!fDataStream)
|
||||
{
|
||||
plAudioCore::ChannelSelect select = buffer->GetReaderSelect();
|
||||
|
||||
bool streamCompressed = (buffer->HasFlag(plSoundBuffer::kStreamCompressed) != 0);
|
||||
|
||||
/// Open da file
|
||||
CHAR strPath[ MAX_PATH ];
|
||||
|
||||
_getcwd(strPath, MAX_PATH);
|
||||
if(sfxPath)
|
||||
strcat( strPath, "\\sfx\\" );
|
||||
else
|
||||
{
|
||||
// if we've changing the filename don't append 'sfx', just append a '\' since a path to the folder is expected
|
||||
strcat( strPath, "\\");
|
||||
}
|
||||
strcat( strPath, fSrcFilename );
|
||||
fDataStream = plAudioFileReader::CreateReader(strPath, select,type);
|
||||
}
|
||||
|
||||
if( fDataStream == nil || !fDataStream->IsValid() )
|
||||
{
|
||||
delete fDataStream;
|
||||
fDataStream = nil;
|
||||
return plSoundBuffer::kError;
|
||||
}
|
||||
|
||||
sprintf( str, " Readied file %s for streaming", fSrcFilename );
|
||||
IPrintDbgMessage( str );
|
||||
|
||||
// dont free sound data until we have a chance to use it in load sound
|
||||
|
||||
return fDataStream ? plSoundBuffer::kSuccess : plSoundBuffer::kError;
|
||||
}
|
||||
|
||||
plStatusLog::AddLineS("audio.log", "EnsureLoadable failed for streaming sound %d", fDataBufferKey->GetName());
|
||||
return plSoundBuffer::kError;
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::IFreeBuffers( void )
|
||||
{
|
||||
plWin32Sound::IFreeBuffers();
|
||||
if( fLoadFromDiskOnDemand && !IsPropertySet( kPropLoadOnlyOnCall ) )
|
||||
{
|
||||
// if the audio system has just restarted, dont delete the datastream. This way we can pick up where we left off instead of restarting the sound.
|
||||
if(!plgAudioSys::IsRestarting())
|
||||
{
|
||||
// we are deleting the stream, we must release the sound data.
|
||||
FreeSoundData();
|
||||
delete fDataStream;
|
||||
fDataStream = nil;
|
||||
}
|
||||
fSrcFilename[ 0 ] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// Overload from plSound. Basically sets up the streaming file and fills the
|
||||
// first half of our buffer. We'll fill the rest of the buffer as we get
|
||||
// notifications for such.
|
||||
|
||||
hsBool plWin32StreamingSound::LoadSound( hsBool is3D )
|
||||
{
|
||||
if( fFailed )
|
||||
return false;
|
||||
if( !plgAudioSys::Active() || fDSoundBuffer )
|
||||
return false;
|
||||
|
||||
if( fPriority > plgAudioSys::GetPriorityCutoff() )
|
||||
return false; // Don't set the failed flag, just return
|
||||
|
||||
// Debug flag #1
|
||||
if( is3D && fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) )
|
||||
{
|
||||
// Force a fail
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true);
|
||||
if(retVal == plSoundBuffer::kPending)
|
||||
return true;
|
||||
|
||||
if( retVal == plSoundBuffer::kError )
|
||||
{
|
||||
char str[ 256 ];
|
||||
sprintf( str, "Unable to open streaming source %s", fDataBufferKey->GetName() );
|
||||
IPrintDbgMessage( str, true );
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
SetProperty( kPropIs3DSound, is3D );
|
||||
|
||||
plWAVHeader header = fDataStream->GetHeader();
|
||||
UInt32 bufferSize = (UInt32)(fBufferLengthInSecs * header.fAvgBytesPerSec);
|
||||
|
||||
// Debug flag #2
|
||||
if( is3D && fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) )
|
||||
{
|
||||
// Force a fail
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( header.fNumChannels > 1 && is3D )
|
||||
{
|
||||
// We can only do a single channel of 3D sound. So copy over one (later)
|
||||
bufferSize /= header.fNumChannels;
|
||||
header.fBlockAlign /= header.fNumChannels;
|
||||
header.fAvgBytesPerSec /= header.fNumChannels;
|
||||
header.fNumChannels = 1;
|
||||
}
|
||||
|
||||
// Actually create the buffer now (always looping)
|
||||
fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet(kPropLooping), false, true );
|
||||
if( !fDSoundBuffer->IsValid() )
|
||||
{
|
||||
fDataStream->Close();
|
||||
delete fDataStream;
|
||||
fDataStream = nil;
|
||||
|
||||
delete fDSoundBuffer;
|
||||
fDSoundBuffer = nil;
|
||||
|
||||
char str[256];
|
||||
sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName());
|
||||
IPrintDbgMessage( str, true );
|
||||
fFailed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fTotalBytes = (UInt32)bufferSize;
|
||||
plProfile_NewMem(MemSounds, fTotalBytes);
|
||||
|
||||
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
|
||||
if(!buffer)
|
||||
return false;
|
||||
|
||||
bool setupSource = true;
|
||||
if(!buffer->GetData() || fStartPos)
|
||||
{
|
||||
if(fStartPos && fStartPos <= fDataStream->NumBytesLeft())
|
||||
{
|
||||
fDataStream->SetPosition(fStartPos);
|
||||
plStatusLog::AddLineS("syncaudio.log", "startpos %d", fStartPos);
|
||||
}
|
||||
|
||||
// if we get here we are not starting from the beginning of the sound. We still have an audio loaded and need to pick up where we left off
|
||||
if(!fDSoundBuffer->SetupStreamingSource(fDataStream))
|
||||
{
|
||||
setupSource = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this sound is starting from the beginning. Get the data and start it.
|
||||
if(!fDSoundBuffer->SetupStreamingSource(buffer->GetData(), buffer->GetAsyncLoadLength()))
|
||||
{
|
||||
setupSource = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!setupSource)
|
||||
{
|
||||
fDataStream->Close();
|
||||
delete fDataStream;
|
||||
fDataStream = nil;
|
||||
delete fDSoundBuffer;
|
||||
fDSoundBuffer = nil;
|
||||
|
||||
plStatusLog::AddLineS("audio.log", "Could not play streaming sound, no voices left %s", GetKeyName());
|
||||
return false;
|
||||
}
|
||||
FreeSoundData();
|
||||
|
||||
IRefreshEAXSettings( true );
|
||||
|
||||
// Debug info
|
||||
char str[ 256 ];
|
||||
sprintf( str, " Streaming %s.", fSrcFilename);
|
||||
IPrintDbgMessage( str );
|
||||
|
||||
plStatusLog::AddLineS( "audioTimes.log", 0xffffffff, "Streaming %4.2f secs of %s", fDataStream->GetLengthInSecs(), GetKey()->GetUoid().GetObjectName() );
|
||||
|
||||
// Get pertinent info
|
||||
SetLength( (hsScalar)fDataStream->GetLengthInSecs() );
|
||||
|
||||
// Set up our deswizzler, if necessary
|
||||
delete fDeswizzler;
|
||||
if( fDataStream->GetHeader().fNumChannels != header.fNumChannels )
|
||||
fDeswizzler = TRACKED_NEW plSoundDeswizzler( (UInt32)(fBufferLengthInSecs * fDataStream->GetHeader().fAvgBytesPerSec),
|
||||
(UInt8)(fDataStream->GetHeader().fNumChannels),
|
||||
header.fBitsPerSample / 8 );
|
||||
else
|
||||
fDeswizzler = nil;
|
||||
|
||||
// LEAVE THE WAV FILE OPEN! (We *are* streaming, after all :)
|
||||
return true;
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::IStreamUpdate()
|
||||
{
|
||||
if(!fReallyPlaying)
|
||||
return;
|
||||
|
||||
if(hsTimer::GetMilliSeconds() - fLastStreamingUpdate < STREAMING_UPDATE_MS) // filter out update requests so we aren't doing this more that we need to
|
||||
return;
|
||||
|
||||
plProfile_BeginTiming( StreamSndShoveTime );
|
||||
if(fDSoundBuffer)
|
||||
{
|
||||
if(fDSoundBuffer->BuffersQueued() == 0 && fDataStream->NumBytesLeft() == 0 && !fDSoundBuffer->IsLooping())
|
||||
{
|
||||
// If we are fading out it's possible that we will hit this multiple times causing this sound to try and fade out multiple times, never allowing it to actually stop
|
||||
if(!fStopping)
|
||||
{
|
||||
fStopping = true;
|
||||
Stop();
|
||||
plProfile_EndTiming( StreamSndShoveTime );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!fDSoundBuffer->StreamingFillBuffer(fDataStream))
|
||||
{
|
||||
plStatusLog::AddLineS("audio.log", "%s Streaming buffer fill failed", GetKeyName());
|
||||
}
|
||||
}
|
||||
plProfile_EndTiming( StreamSndShoveTime );
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::Update()
|
||||
{
|
||||
plWin32Sound::Update();
|
||||
IStreamUpdate();
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::IDerivedActuallyPlay( void )
|
||||
{
|
||||
fStopping = false;
|
||||
if( !fReallyPlaying )
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(fSynchedStartTimeSec)
|
||||
{
|
||||
// if we are synching to another sound this is our latency time
|
||||
fDSoundBuffer->SetTimeOffsetSec((float)(hsTimer::GetSeconds() - fSynchedStartTimeSec));
|
||||
fSynchedStartTimeSec = 0;
|
||||
}
|
||||
|
||||
if(IsPropertySet(kPropIncidental))
|
||||
{
|
||||
if(fIncidentalsPlaying >= MAX_INCIDENTALS)
|
||||
break;
|
||||
++fIncidentalsPlaying;
|
||||
}
|
||||
fDSoundBuffer->Play();
|
||||
fReallyPlaying = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Send start callbacks
|
||||
plSoundEvent *event = IFindEvent( plSoundEvent::kStart );
|
||||
if( event != nil )
|
||||
event->SendCallbacks();
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::IActuallyStop()
|
||||
{
|
||||
fStopping = false;
|
||||
plWin32Sound::IActuallyStop();
|
||||
if(fPlayWhenStopped)
|
||||
{
|
||||
fPlayWhenStopped = false;
|
||||
Play();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned plWin32StreamingSound::GetByteOffset()
|
||||
{
|
||||
if(fDataStream && fDSoundBuffer)
|
||||
{
|
||||
unsigned bytesQueued = fDSoundBuffer->BuffersQueued() * STREAM_BUFFER_SIZE;
|
||||
unsigned offset = fDSoundBuffer->GetByteOffset();
|
||||
long byteoffset = ((fDataStream->GetDataSize() - fDataStream->NumBytesLeft()) - bytesQueued) + offset;
|
||||
|
||||
return byteoffset < 0 ? fDataStream->GetDataSize() - abs(byteoffset) : byteoffset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
float plWin32StreamingSound::GetActualTimeSec()
|
||||
{
|
||||
if(fDataStream && fDSoundBuffer)
|
||||
return fDSoundBuffer->BytePosToMSecs(fDataStream->NumBytesLeft()) / 1000.0f;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::SetStartPos(unsigned bytes)
|
||||
{
|
||||
fStartPos = bytes;
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::ISetActualTime( double t )
|
||||
{
|
||||
//fStartTimeSec = 0;
|
||||
if(fDataStream && fDSoundBuffer)
|
||||
fDataStream->SetPosition(fDSoundBuffer->GetBufferBytePos((float)t));
|
||||
//else
|
||||
// fStartTimeSec = t;
|
||||
}
|
||||
|
||||
hsBool plWin32StreamingSound::MsgReceive( plMessage* pMsg )
|
||||
{
|
||||
return plWin32Sound::MsgReceive( pMsg );
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::IRemoveCallback( plEventCallbackMsg *pCBMsg )
|
||||
{
|
||||
plWin32Sound::IRemoveCallback( pCBMsg );
|
||||
}
|
||||
|
||||
void plWin32StreamingSound::IAddCallback( plEventCallbackMsg *pCBMsg )
|
||||
{
|
||||
if( plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStop &&
|
||||
plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStart )
|
||||
{
|
||||
hsAssert( false, "Streaming sounds only support start and stop callbacks at this time." );
|
||||
return;
|
||||
}
|
||||
plWin32Sound::IAddCallback( pCBMsg );
|
||||
}
|
106
Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.h
Normal file
106
Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#ifndef plWin32StreamingSound_h
|
||||
#define plWin32StreamingSound_h
|
||||
|
||||
#include "plWin32Sound.h"
|
||||
#include "../pnUtils/pnUtils.h"
|
||||
|
||||
class plDSoundBuffer;
|
||||
class DSoundCallbackHandle;
|
||||
class plAudioFileReader;
|
||||
class plStreamingSoundThread;
|
||||
enum CallbackHandleType;
|
||||
class plSoundDeswizzler;
|
||||
|
||||
class plWin32StreamingSound : public plWin32Sound
|
||||
{
|
||||
public:
|
||||
plWin32StreamingSound();
|
||||
~plWin32StreamingSound();
|
||||
|
||||
CLASSNAME_REGISTER( plWin32StreamingSound );
|
||||
GETINTERFACE_ANY( plWin32StreamingSound, plWin32Sound );
|
||||
|
||||
virtual void DeActivate();
|
||||
virtual hsBool LoadSound( hsBool is3D );
|
||||
virtual float GetActualTimeSec();
|
||||
virtual unsigned GetByteOffset();
|
||||
virtual StreamType GetStreamType() const { return fStreamType; }
|
||||
virtual void SetFilename(const char *filename, bool isCompressed);
|
||||
virtual void Update(); // temp
|
||||
void StreamUpdate();
|
||||
virtual hsBool MsgReceive( plMessage *pMsg );
|
||||
|
||||
protected:
|
||||
hsScalar fTimeAtBufferStart;
|
||||
plAudioFileReader *fDataStream;
|
||||
hsScalar fBufferLengthInSecs;
|
||||
UInt8 fBlankBufferFillCounter;
|
||||
plSoundDeswizzler *fDeswizzler;
|
||||
char fSrcFilename[ 256 ];
|
||||
StreamType fStreamType;
|
||||
bool fIsCompressed; // this applies only to the new sound file specified in fNewFilename, so we can play both ogg's and wav's
|
||||
std::string fNewFilename; // allow the filename to be changed so we can play from a different source.
|
||||
// ultimately this filename will be given to fDataBuffer, but since it's not always around we'll store it here
|
||||
hsBool fStopping;
|
||||
|
||||
double fLastStreamingUpdate;
|
||||
bool fPlayWhenStopped;
|
||||
unsigned fStartPos;
|
||||
|
||||
hsScalar IGetTimeAtBufferStart( void ) { return fTimeAtBufferStart; }
|
||||
virtual void SetStartPos(unsigned bytes);
|
||||
|
||||
virtual void IDerivedActuallyPlay( void );
|
||||
void IActuallyStop();
|
||||
virtual void ISetActualTime( double t );
|
||||
|
||||
virtual void IAddCallback( plEventCallbackMsg *pMsg );
|
||||
virtual void IRemoveCallback( plEventCallbackMsg *pMsg );
|
||||
|
||||
virtual void IFreeBuffers( void );
|
||||
void IStreamUpdate();
|
||||
virtual plSoundBuffer::ELoadReturnVal IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental = false );
|
||||
};
|
||||
|
||||
#endif //plWin32StreamingSound_h
|
122
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp
Normal file
122
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
|
||||
#include "plWin32VideoSound.h"
|
||||
|
||||
#include "hsResMgr.h"
|
||||
#include "plDSoundBuffer.h"
|
||||
|
||||
static int uniqueID = 0;
|
||||
plWin32VideoSound::plWin32VideoSound(const plWAVHeader& header) : plWin32Sound()
|
||||
{
|
||||
fCurrVolume = 1.0f;
|
||||
fDesiredVol = 1.0f;
|
||||
fSoftVolume = 1.0f;
|
||||
fType = kGUISound;
|
||||
|
||||
fWAVHeader = header;
|
||||
fDSoundBuffer = new plDSoundBuffer(0, fWAVHeader, false, false);
|
||||
|
||||
uniqueID++;
|
||||
|
||||
char keyName[32];
|
||||
StrPrintf(keyName, arrsize(keyName), "VideoSound_%d", uniqueID);
|
||||
|
||||
hsgResMgr::ResMgr()->NewKey(keyName, this, plLocation::kGlobalFixedLoc);
|
||||
}
|
||||
|
||||
plWin32VideoSound::~plWin32VideoSound()
|
||||
{
|
||||
if (fDSoundBuffer)
|
||||
delete fDSoundBuffer;
|
||||
}
|
||||
|
||||
void plWin32VideoSound::Play()
|
||||
{
|
||||
IActuallyPlay();
|
||||
}
|
||||
|
||||
void plWin32VideoSound::Pause(bool on)
|
||||
{
|
||||
if (on)
|
||||
fDSoundBuffer->Pause();
|
||||
else if (!fReallyPlaying)
|
||||
fDSoundBuffer->Play();
|
||||
fReallyPlaying = !on;
|
||||
}
|
||||
|
||||
void plWin32VideoSound::FillSoundBuffer(void* buffer, size_t size)
|
||||
{
|
||||
fDSoundBuffer->FillBuffer(buffer, size, &fWAVHeader);
|
||||
fDSoundBuffer->SetScalarVolume(1.0f);
|
||||
}
|
||||
|
||||
void plWin32VideoSound::IDerivedActuallyPlay()
|
||||
{
|
||||
if (!fReallyPlaying) {
|
||||
fDSoundBuffer->Play();
|
||||
fReallyPlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
hsBool plWin32VideoSound::LoadSound(hsBool is3D)
|
||||
{
|
||||
hsAssert(false, "unimplemented cause unnecessary for this class");
|
||||
return false;
|
||||
}
|
||||
|
||||
void plWin32VideoSound::SetStartPos(unsigned bytes)
|
||||
{
|
||||
//do nothing
|
||||
hsAssert(false, "unimplemented cause unnecessary for this class");
|
||||
}
|
||||
|
||||
float plWin32VideoSound::GetActualTimeSec()
|
||||
{
|
||||
hsAssert(false, "unimplemented cause unnecessary for this class");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void plWin32VideoSound::ISetActualTime(double t)
|
||||
{
|
||||
hsAssert(false, "unimplemented cause unnecessary for this class");
|
||||
}
|
67
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h
Normal file
67
Sources/Plasma/PubUtilLib/plAudio/plWin32VideoSound.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
|
||||
#ifndef plWin32VideoSound_h
|
||||
#define plWin32VideoSound_h
|
||||
|
||||
#include "plWin32Sound.h"
|
||||
|
||||
class plWin32VideoSound : public plWin32Sound
|
||||
{
|
||||
public:
|
||||
plWin32VideoSound(const plWAVHeader& header);
|
||||
virtual ~plWin32VideoSound();
|
||||
|
||||
virtual void Play();
|
||||
virtual void Pause(bool on);
|
||||
void FillSoundBuffer(void* buffer, size_t size);
|
||||
|
||||
protected:
|
||||
void IDerivedActuallyPlay();
|
||||
hsBool LoadSound(hsBool is3D);
|
||||
void SetStartPos(unsigned bytes);
|
||||
float GetActualTimeSec();
|
||||
void ISetActualTime(double t);
|
||||
|
||||
plWAVHeader fWAVHeader;
|
||||
};
|
||||
#endif
|
377
Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.cpp
Normal file
377
Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.cpp
Normal file
@ -0,0 +1,377 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plWinMicLevel - Annoying class to deal with the annoying problem of //
|
||||
// setting the microphone recording volume in Windows. //
|
||||
// Yeah, you'd THINK there'd be some easier way... //
|
||||
// //
|
||||
//// Notes ///////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// 5.8.2001 - Created by mcn. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "hsTypes.h"
|
||||
#include "plWinMicLevel.h"
|
||||
#include "hsWindows.h"
|
||||
#include <mmsystem.h>
|
||||
|
||||
|
||||
//// Our Local Static Data ///////////////////////////////////////////////////
|
||||
|
||||
int sNumMixers = 0;
|
||||
HMIXER sMixerHandle = nil;
|
||||
MIXERCAPS sMixerCaps;
|
||||
|
||||
DWORD sMinValue = 0, sMaxValue = 0;
|
||||
DWORD sVolControlID = 0;
|
||||
|
||||
|
||||
//// Local Static Helpers ////////////////////////////////////////////////////
|
||||
|
||||
hsBool IGetMuxMicVolumeControl( void );
|
||||
hsBool IGetBaseMicVolumeControl( void );
|
||||
|
||||
hsBool IGetControlValue( DWORD &value );
|
||||
hsBool ISetControlValue( DWORD value );
|
||||
|
||||
MIXERLINE *IGetLineByType( DWORD type );
|
||||
MIXERLINE *IGetLineByID( DWORD id );
|
||||
MIXERCONTROL *IGetControlByType( MIXERLINE *line, DWORD type );
|
||||
MIXERLINE *IGetMixerSubLineByType( MIXERCONTROL *mux, DWORD type );
|
||||
|
||||
|
||||
//// The Publics /////////////////////////////////////////////////////////////
|
||||
|
||||
hsScalar plWinMicLevel::GetLevel( void )
|
||||
{
|
||||
if( !CanSetLevel() )
|
||||
return -1;
|
||||
|
||||
DWORD rawValue;
|
||||
if( !IGetControlValue( rawValue ) )
|
||||
return -1;
|
||||
|
||||
return (hsScalar)( rawValue - sMinValue ) / (hsScalar)( sMaxValue - sMinValue );
|
||||
}
|
||||
|
||||
void plWinMicLevel::SetLevel( hsScalar level )
|
||||
{
|
||||
if( !CanSetLevel() )
|
||||
return;
|
||||
|
||||
DWORD rawValue = (DWORD)(( level * ( sMaxValue - sMinValue ) ) + sMinValue);
|
||||
|
||||
ISetControlValue( rawValue );
|
||||
}
|
||||
|
||||
hsBool plWinMicLevel::CanSetLevel( void )
|
||||
{
|
||||
// Just to init
|
||||
plWinMicLevel &instance = IGetInstance();
|
||||
|
||||
return ( sMixerHandle != nil ) ? true : false;
|
||||
}
|
||||
|
||||
|
||||
//// Protected Init Stuff ////////////////////////////////////////////////////
|
||||
|
||||
plWinMicLevel &plWinMicLevel::IGetInstance( void )
|
||||
{
|
||||
static plWinMicLevel sInstance;
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
plWinMicLevel::plWinMicLevel()
|
||||
{
|
||||
sMixerHandle = nil;
|
||||
memset( &sMixerCaps, 0, sizeof( sMixerCaps ) );
|
||||
|
||||
// Get the number of mixers in the system
|
||||
sNumMixers = ::mixerGetNumDevs();
|
||||
|
||||
// So long as we have one, open the first one
|
||||
if( sNumMixers == 0 )
|
||||
return;
|
||||
|
||||
if( ::mixerOpen( &sMixerHandle, 0,
|
||||
nil, // Window handle to receive callback messages
|
||||
nil, MIXER_OBJECTF_MIXER ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
sMixerHandle = nil; // Just to be sure
|
||||
return;
|
||||
}
|
||||
|
||||
if( ::mixerGetDevCaps( (UINT)sMixerHandle, &sMixerCaps, sizeof( sMixerCaps ) ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
// Oh well, who cares
|
||||
}
|
||||
|
||||
// Try to get the Mux/mixer-based mic volume control first, since that seems to work better/more often/at all
|
||||
if( !IGetMuxMicVolumeControl() )
|
||||
{
|
||||
// Failed, so try getting the volume control from the base mic-in line
|
||||
if( !IGetBaseMicVolumeControl() )
|
||||
{
|
||||
IShutdown();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plWinMicLevel::~plWinMicLevel()
|
||||
{
|
||||
IShutdown();
|
||||
}
|
||||
|
||||
void plWinMicLevel::IShutdown( void )
|
||||
{
|
||||
if( sMixerHandle != nil )
|
||||
::mixerClose( sMixerHandle );
|
||||
|
||||
sMixerHandle = nil;
|
||||
}
|
||||
|
||||
//// IGetMuxMicVolumeControl /////////////////////////////////////////////////
|
||||
// Tries to get the volume control of the microphone subline of the MUX or
|
||||
// mixer control of the WaveIn destination line of the audio system (whew!)
|
||||
// Note: testing indcates that this works but the direct SRC_MICROPHONE
|
||||
// doesn't, hence we try this one first.
|
||||
|
||||
hsBool IGetMuxMicVolumeControl( void )
|
||||
{
|
||||
if( sMixerHandle == nil )
|
||||
return false;
|
||||
|
||||
// Get the WaveIn destination line
|
||||
MIXERLINE *waveInLine = IGetLineByType( MIXERLINE_COMPONENTTYPE_DST_WAVEIN );
|
||||
if( waveInLine == nil )
|
||||
return false;
|
||||
|
||||
// Get the mixer or MUX controller from the line
|
||||
MIXERCONTROL *control = IGetControlByType( waveInLine, MIXERCONTROL_CONTROLTYPE_MIXER );
|
||||
if( control == nil )
|
||||
control = IGetControlByType( waveInLine, MIXERCONTROL_CONTROLTYPE_MUX );
|
||||
if( control == nil )
|
||||
return false;
|
||||
|
||||
// Get the microphone sub-component
|
||||
// Note: this eventually calls IGetLineByType(), which destroys the waveInLine pointer we had before
|
||||
MIXERLINE *micLine = IGetMixerSubLineByType( control, MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE );
|
||||
if( micLine == nil )
|
||||
return false;
|
||||
|
||||
// Get the volume subcontroller
|
||||
MIXERCONTROL *micVolCtrl = IGetControlByType( micLine, MIXERCONTROL_CONTROLTYPE_VOLUME );
|
||||
if( micVolCtrl == nil )
|
||||
return false;
|
||||
|
||||
// Found it! store our values
|
||||
char *dbgLineName = micLine->szName;
|
||||
char *dbgControlName = micVolCtrl->szName;
|
||||
sMinValue = micVolCtrl->Bounds.dwMinimum;
|
||||
sMaxValue = micVolCtrl->Bounds.dwMaximum;
|
||||
sVolControlID = micVolCtrl->dwControlID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//// IGetBaseMicVolumeControl ////////////////////////////////////////////////
|
||||
// Tries to get the volume control of the mic-in line. See
|
||||
// IGetMuxMicVolumeControl for why we don't do this one first.
|
||||
|
||||
hsBool IGetBaseMicVolumeControl( void )
|
||||
{
|
||||
if( sMixerHandle == nil )
|
||||
return false;
|
||||
|
||||
// Get the mic source line
|
||||
MIXERLINE *micLine = IGetLineByType( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE );
|
||||
if( micLine == nil )
|
||||
return false;
|
||||
|
||||
// Get the volume subcontroller
|
||||
MIXERCONTROL *micVolCtrl = IGetControlByType( micLine, MIXERCONTROL_CONTROLTYPE_VOLUME );
|
||||
if( micVolCtrl == nil )
|
||||
return false;
|
||||
|
||||
// Found it! store our values
|
||||
char *dbgLineName = micLine->szName;
|
||||
char *dbgControlName = micVolCtrl->szName;
|
||||
sMinValue = micVolCtrl->Bounds.dwMinimum;
|
||||
sMaxValue = micVolCtrl->Bounds.dwMaximum;
|
||||
sVolControlID = micVolCtrl->dwControlID;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//// IGetControlValue ////////////////////////////////////////////////////////
|
||||
// Gets the raw value of the current volume control.
|
||||
|
||||
hsBool IGetControlValue( DWORD &value )
|
||||
{
|
||||
if( sMixerHandle == nil )
|
||||
return false;
|
||||
|
||||
MIXERCONTROLDETAILS_UNSIGNED mxcdVolume;
|
||||
MIXERCONTROLDETAILS mxcd;
|
||||
mxcd.cbStruct = sizeof( MIXERCONTROLDETAILS );
|
||||
mxcd.dwControlID = sVolControlID;
|
||||
mxcd.cChannels = 1;
|
||||
mxcd.cMultipleItems = 0;
|
||||
mxcd.cbDetails = sizeof( MIXERCONTROLDETAILS_UNSIGNED );
|
||||
mxcd.paDetails = &mxcdVolume;
|
||||
|
||||
if( ::mixerGetControlDetails( (HMIXEROBJ)sMixerHandle, &mxcd,
|
||||
MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
|
||||
return false;
|
||||
|
||||
value = mxcdVolume.dwValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
//// ISetControlValue ////////////////////////////////////////////////////////
|
||||
// Sets the raw value of the current volume control.
|
||||
|
||||
hsBool ISetControlValue( DWORD value )
|
||||
{
|
||||
if( sMixerHandle == nil )
|
||||
return false;
|
||||
|
||||
MIXERCONTROLDETAILS_UNSIGNED mxcdVolume = { value };
|
||||
MIXERCONTROLDETAILS mxcd;
|
||||
mxcd.cbStruct = sizeof( MIXERCONTROLDETAILS );
|
||||
mxcd.dwControlID = sVolControlID;
|
||||
mxcd.cChannels = 1;
|
||||
mxcd.cMultipleItems = 0;
|
||||
mxcd.cbDetails = sizeof( MIXERCONTROLDETAILS_UNSIGNED );
|
||||
mxcd.paDetails = &mxcdVolume;
|
||||
|
||||
if( ::mixerSetControlDetails( (HMIXEROBJ)sMixerHandle, &mxcd,
|
||||
MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//// Helper Functions ////////////////////////////////////////////////////////
|
||||
|
||||
MIXERLINE *IGetLineByType( DWORD type )
|
||||
{
|
||||
static MIXERLINE mxl;
|
||||
|
||||
mxl.cbStruct = sizeof( MIXERLINE );
|
||||
mxl.dwComponentType = type;
|
||||
if( ::mixerGetLineInfo( (HMIXEROBJ)sMixerHandle, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE ) != MMSYSERR_NOERROR )
|
||||
return nil;
|
||||
|
||||
return &mxl;
|
||||
}
|
||||
|
||||
MIXERLINE *IGetLineByID( DWORD id )
|
||||
{
|
||||
static MIXERLINE mxl;
|
||||
|
||||
mxl.cbStruct = sizeof( MIXERLINE );
|
||||
mxl.dwLineID = id;
|
||||
if( ::mixerGetLineInfo( (HMIXEROBJ)sMixerHandle, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID ) != MMSYSERR_NOERROR )
|
||||
return nil;
|
||||
|
||||
return &mxl;
|
||||
}
|
||||
|
||||
MIXERCONTROL *IGetControlByType( MIXERLINE *line, DWORD type )
|
||||
{
|
||||
static MIXERCONTROL mxc;
|
||||
|
||||
MIXERLINECONTROLS mxlc;
|
||||
mxlc.cbStruct = sizeof( MIXERLINECONTROLS );
|
||||
mxlc.dwLineID = line->dwLineID;
|
||||
mxlc.dwControlType = type;
|
||||
mxlc.cControls = 1;
|
||||
mxlc.cbmxctrl = sizeof( MIXERCONTROL );
|
||||
mxlc.pamxctrl = &mxc;
|
||||
if( ::mixerGetLineControls( (HMIXEROBJ)sMixerHandle, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE ) != MMSYSERR_NOERROR )
|
||||
return nil;
|
||||
|
||||
return &mxc;
|
||||
}
|
||||
|
||||
MIXERLINE *IGetMixerSubLineByType( MIXERCONTROL *mux, DWORD type )
|
||||
{
|
||||
// A mixer or MUX is really a combination of MORE lines. And beautifully, you can't
|
||||
// just ask for a single one off of it, you have to ask for them all and search through yourself
|
||||
MIXERCONTROLDETAILS_LISTTEXT *lineInfo = TRACKED_NEW MIXERCONTROLDETAILS_LISTTEXT[ mux->cMultipleItems ];
|
||||
if( lineInfo == nil )
|
||||
return nil;
|
||||
|
||||
MIXERCONTROLDETAILS details;
|
||||
details.cbStruct = sizeof( MIXERCONTROLDETAILS );
|
||||
details.dwControlID = mux->dwControlID;
|
||||
details.cChannels = 1;
|
||||
details.cMultipleItems = mux->cMultipleItems;
|
||||
details.cbDetails = sizeof( MIXERCONTROLDETAILS_LISTTEXT );
|
||||
details.paDetails = lineInfo;
|
||||
if( ::mixerGetControlDetails( (HMIXEROBJ)sMixerHandle, &details, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_LISTTEXT ) != MMSYSERR_NOERROR )
|
||||
{
|
||||
delete [] lineInfo;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Loop through and find the one with the right component type. But of course it doesn't give us that offhand...
|
||||
for( unsigned int i = 0; i < mux->cMultipleItems; i++ )
|
||||
{
|
||||
MIXERLINE *line = IGetLineByID( lineInfo[ i ].dwParam1 );
|
||||
if( line->dwComponentType == type )
|
||||
{
|
||||
delete [] lineInfo;
|
||||
return line;
|
||||
}
|
||||
}
|
||||
|
||||
delete [] lineInfo;
|
||||
return nil;
|
||||
}
|
||||
|
80
Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.h
Normal file
80
Sources/Plasma/PubUtilLib/plAudio/plWinMicLevel.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#ifndef _plWinMicLevel_h
|
||||
#define _plWinMicLevel_h
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// plWinMicLevel - Annoying class to deal with the annoying problem of //
|
||||
// setting the microphone recording volume in Windows. //
|
||||
// Yeah, you'd THINK there'd be some easier way... //
|
||||
// //
|
||||
//// Notes ///////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// 5.8.2001 - Created by mcn. //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//// Class Definition ////////////////////////////////////////////////////////
|
||||
|
||||
class plWinMicLevel
|
||||
{
|
||||
public:
|
||||
|
||||
~plWinMicLevel();
|
||||
// Gets the microphone volume, range 0-1, -1 if error
|
||||
static hsScalar GetLevel( void );
|
||||
|
||||
// Sets the microphone volume, range 0-1
|
||||
static void SetLevel( hsScalar level );
|
||||
|
||||
// Returns whether we can set the level
|
||||
static hsBool CanSetLevel( void );
|
||||
|
||||
protected:
|
||||
plWinMicLevel(); // Protected constructor for IGetInstance. Just to init some stuff
|
||||
static plWinMicLevel &IGetInstance( void );
|
||||
void IShutdown( void );
|
||||
};
|
||||
|
||||
#endif // _plWinMicLevel_h
|
Reference in New Issue
Block a user