2
3
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:
rarified
2021-05-15 12:49:46 -06:00
parent c3f4a640a3
commit 96903e8dca
4002 changed files with 159 additions and 644 deletions

View 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
}

View 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

View 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

View 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==*/

File diff suppressed because it is too large Load Diff

View 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

View 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();
}

View 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

View 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 */
}

View 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

View 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();
}

View 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

View 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);
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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;
}

View 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

View 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);
}

View 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

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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 );
}

View 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

View 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 );
}

View 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

View 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 );
}

View 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

View 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 );
}

View 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

View 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");
}

View 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

View 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;
}

View 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