#include "pch.h"
#include "soundbase.h"
#include "soundutil.h"
#include "soundtemplates.h"
#include "regkey.h"
namespace SoundEngine {
class WaveFileTemplate : public ISoundTemplate
{
private:
TRef<ISoundPCMData> m_pdata;
ZString m_strFilename;
public:
HRESULT Init(const ZString& strFilename)
{
m_strFilename = strFilename;
HANDLE hFile;
hFile = CreateFile(strFilename, 0, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);
if (INVALID_HANDLE_VALUE == hFile)
return STG_E_FILENOTFOUND;
else
{
CloseHandle(hFile);
return S_OK;
}
};
HRESULT Init(ISoundPCMData* pdata)
{
if (!pdata)
return E_INVALIDARG;
m_pdata = pdata;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
{
if (!m_pdata)
{
ZAssert(!m_strFilename.IsEmpty());
bool m_bConvertMono = false; HKEY hKey;
DWORD dwResult = 0;
if (ERROR_SUCCESS == ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, ALLEGIANCE_REGISTRY_KEY_ROOT,
0, KEY_READ, &hKey))
{
DWORD dwSize = sizeof(dwResult);
DWORD dwType = REG_DWORD;
::RegQueryValueEx(hKey, "MonoOff", NULL, &dwType, (BYTE*)&dwResult, &dwSize);
::RegCloseKey(hKey);
if (dwType != REG_DWORD)
dwResult = 0;
}
if ( dwResult == 1 )
m_bConvertMono = false;
else
m_bConvertMono = true;
if (FAILED(LoadWaveFile(m_pdata, m_strFilename, m_bConvertMono))) {
if (ZFailed(CreateDummyPCMData(m_pdata)))
return E_FAIL;
}
}
if (!pbufferSource)
{
ZAssert(false);
return E_POINTER;
}
return pbufferSource->CreateStaticBuffer(psoundNew, m_pdata, false, psource);
}
};
HRESULT CreateWaveFileSoundTemplate(TRef<ISoundTemplate>& pstDest, const ZString& strFilename)
{
TRef<WaveFileTemplate> ptemplate = new WaveFileTemplate();
HRESULT hr = ptemplate->Init(strFilename);
if (SUCCEEDED(hr))
pstDest = ptemplate;
return hr;
};
HRESULT CreateDummySoundTemplate(TRef<ISoundTemplate>& pstDest)
{
HRESULT hr;
TRef<ISoundPCMData> pdata;
hr = CreateDummyPCMData(pdata);
if (ZFailed(hr))
return hr;
TRef<WaveFileTemplate> ptemplate = new WaveFileTemplate();
hr = ptemplate->Init(pdata);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class ThreeDSoundTemplate : public ISoundTemplate
{
private:
TRef<ISoundTemplate> m_pstBase;
float m_fMinimumDistance;
public:
HRESULT Init(ISoundTemplate* pstSource, float fMinimumDistance)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
if (fMinimumDistance <= 0)
{
ZAssert(false);
return E_INVALIDARG;
}
m_pstBase = pstSource;
m_fMinimumDistance = fMinimumDistance;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
{
HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
if (ZFailed(hr)) return hr;
TRef<ISoundTweakable3D> ptweak3d = psoundNew->GetISoundTweakable3D();
if (psource && !ptweak3d)
{
ZAssert(false);
return E_FAIL;
}
if (ptweak3d)
{
hr = ptweak3d->Set3D(true);
if (ZFailed(hr)) return hr;
hr = ptweak3d->SetMinimumDistance(m_fMinimumDistance);
if (ZFailed(hr)) return hr;
}
return S_OK;
}
};
HRESULT Create3DSoundTemplate(TRef<ISoundTemplate>& pstDest,
ISoundTemplate* pstSource, float fMinimumDistance)
{
TRef<ThreeDSoundTemplate> ptemplate = new ThreeDSoundTemplate();
HRESULT hr = ptemplate->Init(pstSource, fMinimumDistance);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class SoundConeTemplate : public ISoundTemplate
{
private:
TRef<ISoundTemplate> m_pstBase;
float m_fInnerAngle, m_fOuterAngle, m_fOutsideGain;
public:
HRESULT Init(ISoundTemplate* pstSource, float fInnerAngle, float fOuterAngle, float fOutsideGain)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
if ((fInnerAngle < 0 || fInnerAngle > 360)
|| (fOuterAngle < 0 || fOuterAngle > 360)
|| (fOutsideGain < -100 || fOutsideGain > 0)
)
{
ZAssert(false);
return E_INVALIDARG;
}
m_pstBase = pstSource;
m_fInnerAngle = fInnerAngle;
m_fOuterAngle = fOuterAngle;
m_fOutsideGain = fOutsideGain;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
{
if (!psource)
{
ZAssert(false);
return E_INVALIDARG;
}
HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
if (ZFailed(hr)) return hr;
TRef<ISoundTweakable3D> ptweak3d = psoundNew->GetISoundTweakable3D();
if (!ptweak3d)
{
ZAssert(false);
return E_FAIL;
}
hr = ptweak3d->SetCone(m_fInnerAngle, m_fOuterAngle, m_fOutsideGain);
return S_OK;
}
};
HRESULT CreateSoundConeTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
float fInnerAngle, float fOuterAngle, float fOutsideGain)
{
TRef<SoundConeTemplate> ptemplate = new SoundConeTemplate();
HRESULT hr = ptemplate->Init(pstSource, fInnerAngle, fOuterAngle, fOutsideGain);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class SoundPitchTemplate : public ISoundTemplate
{
private:
TRef<ISoundTemplate> m_pstBase;
float m_fPitch;
public:
class PitchInstance : public SoundInstanceWrapper, public SoundTweakableWrapper
{
float m_fPitch;
public:
PitchInstance(float fPitch, ISoundInstance* pinstance,
ISoundTweakable* ptweak) :
SoundInstanceWrapper(pinstance),
SoundTweakableWrapper(ptweak),
m_fPitch(fPitch)
{};
HRESULT SetPitch(float fPitch)
{
return SoundTweakableWrapper::SetPitch(m_fPitch * fPitch);
};
TRef<ISoundTweakable> GetISoundTweakable()
{
return this;
};
};
public:
HRESULT Init(ISoundTemplate* pstSource, float fPitch)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
if (fPitch < 0.25f || fPitch > 4.0f)
{
ZAssert(false);
return E_INVALIDARG;
}
m_pstBase = pstSource;
m_fPitch = fPitch;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
{
HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
if (ZFailed(hr)) return hr;
TRef<ISoundTweakable> ptweak = psoundNew->GetISoundTweakable();
if (!ptweak)
{
ZAssert(false);
return E_FAIL;
}
hr = ptweak->SetPitch(m_fPitch);
if (ZFailed(hr)) return hr;
psoundNew = new PitchInstance(m_fPitch, psoundNew, ptweak);
return S_OK;
}
};
HRESULT CreatePitchSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
float fPitch)
{
TRef<SoundPitchTemplate> ptemplate = new SoundPitchTemplate();
HRESULT hr = ptemplate->Init(pstSource, fPitch);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class SoundGainTemplate : public ISoundTemplate
{
private:
TRef<ISoundTemplate> m_pstBase;
float m_fGain;
public:
class GainInstance : public SoundInstanceWrapper, public SoundTweakableWrapper
{
float m_fGain;
public:
GainInstance(float fGain, ISoundInstance* pinstance,
ISoundTweakable* ptweak) :
SoundInstanceWrapper(pinstance),
SoundTweakableWrapper(ptweak),
m_fGain(fGain)
{};
HRESULT SetGain(float fGain)
{
return SoundTweakableWrapper::SetGain(m_fGain + fGain);
};
TRef<ISoundTweakable> GetISoundTweakable()
{
return this;
};
};
public:
HRESULT Init(ISoundTemplate* pstSource, float fGain)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
if (fGain > 0.0f || fGain < -100.0f)
{
ZAssert(false);
return E_INVALIDARG;
}
m_pstBase = pstSource;
m_fGain = fGain;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
{
HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
if (ZFailed(hr)) return hr;
TRef<ISoundTweakable> ptweak = psoundNew->GetISoundTweakable();
if (!ptweak)
{
ZAssert(false);
return E_FAIL;
}
hr = ptweak->SetGain(m_fGain);
if (ZFailed(hr)) return hr;
psoundNew = new GainInstance(m_fGain, psoundNew, ptweak);
return S_OK;
}
};
HRESULT CreateGainSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
float fGain)
{
TRef<SoundGainTemplate> ptemplate = new SoundGainTemplate();
HRESULT hr = ptemplate->Init(pstSource, fGain);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class SoundPriorityTemplate : public ISoundTemplate
{
private:
TRef<ISoundTemplate> m_pstBase;
float m_fPriority;
public:
class PriorityInstance : public SoundInstanceWrapper, public SoundTweakableWrapper
{
float m_fPriority;
public:
PriorityInstance(float fPriority, ISoundInstance* pinstance,
ISoundTweakable* ptweak) :
SoundInstanceWrapper(pinstance),
SoundTweakableWrapper(ptweak),
m_fPriority(fPriority)
{};
HRESULT SetPriority(float fPriority)
{
return SoundTweakableWrapper::SetPriority(m_fPriority + fPriority);
};
TRef<ISoundTweakable> GetISoundTweakable()
{
return this;
};
};
public:
HRESULT Init(ISoundTemplate* pstSource, float fPriority)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
m_pstBase = pstSource;
m_fPriority = fPriority;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
{
HRESULT hr = m_pstBase->CreateSound(psoundNew, pbufferSource, psource);
if (ZFailed(hr)) return hr;
TRef<ISoundTweakable> ptweak = psoundNew->GetISoundTweakable();
if (!ptweak)
{
ZAssert(false);
return E_FAIL;
}
hr = ptweak->SetPriority(m_fPriority);
if (ZFailed(hr)) return hr;
psoundNew = new PriorityInstance(m_fPriority, psoundNew, ptweak);
return S_OK;
}
};
HRESULT CreatePrioritySoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
float fPriority)
{
TRef<SoundPriorityTemplate> ptemplate = new SoundPriorityTemplate();
HRESULT hr = ptemplate->Init(pstSource, fPriority);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class LoopingSoundTemplate : public ISoundTemplate
{
TRef<ISoundTemplate> m_pstBase;
class LoopingSoundBufferSource : public SoundBufferSourceWrapper
{
public:
LoopingSoundBufferSource(ISoundBufferSource* pBase) :
SoundBufferSourceWrapper(pBase) {};
HRESULT CreateStaticBuffer(TRef<ISoundInstance>& psoundNew,
ISoundPCMData* pcmdata, bool bLooping, ISoundPositionSource* psource)
{
return m_pBase->CreateStaticBuffer(psoundNew, pcmdata, true, psource);
}
};
public:
HRESULT Init(ISoundTemplate* pstSource)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
m_pstBase = pstSource;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
{
if (!pbufferSource)
{
ZAssert(false);
return E_POINTER;
}
TRef<ISoundBufferSource> pbufferLoopingSource = new LoopingSoundBufferSource(pbufferSource);
return m_pstBase->CreateSound(psoundNew, pbufferLoopingSource, psource);
};
};
HRESULT CreateLoopingSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource)
{
TRef<LoopingSoundTemplate> ptemplate = new LoopingSoundTemplate();
HRESULT hr = ptemplate->Init(pstSource);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class ASRSoundTemplate : public ISoundTemplate
{
TRef<ISoundTemplate> m_pstBase;
float m_fLoopStart, m_fLoopLength;
class ASRSoundBufferSource : public SoundBufferSourceWrapper
{
float m_fLoopStart, m_fLoopLength;
public:
ASRSoundBufferSource(ISoundBufferSource* pBase, float fLoopStart,
float fLoopLength) :
SoundBufferSourceWrapper(pBase),
m_fLoopStart(fLoopStart),
m_fLoopLength(fLoopLength)
{};
HRESULT CreateStaticBuffer(TRef<ISoundInstance>& psoundNew,
ISoundPCMData* pcmdata, bool bLooping, ISoundPositionSource* psource)
{
unsigned uBytesPerSec = pcmdata->GetBytesPerSec();
float fLength = pcmdata->GetSize()/float(uBytesPerSec);
if (fLength < m_fLoopStart + m_fLoopLength)
{
ZAssert(false);
return E_INVALIDARG;
}
unsigned uLoopOffset = unsigned(m_fLoopStart * uBytesPerSec);
unsigned uLoopLength = unsigned(m_fLoopLength * uBytesPerSec);
unsigned uBytesPerSample = pcmdata->GetBytesPerSample();
uLoopOffset -= uLoopOffset % uBytesPerSample;
uLoopLength -= uLoopLength % uBytesPerSample;
return m_pBase->CreateASRBuffer(psoundNew, pcmdata,
uLoopOffset, uLoopLength, psource);
}
};
public:
HRESULT Init(ISoundTemplate* pstSource, float fLoopStart, float fLoopLength)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
if (fLoopStart < 0.0f || fLoopLength <= 0.0f)
{
ZAssert(false);
return E_INVALIDARG;
}
m_fLoopStart = fLoopStart;
m_fLoopLength = fLoopLength;
m_pstBase = pstSource;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
{
if (!pbufferSource)
{
ZAssert(false);
return E_POINTER;
}
TRef<ISoundBufferSource> pbufferASRSource =
new ASRSoundBufferSource(pbufferSource, m_fLoopStart, m_fLoopLength);
return m_pstBase->CreateSound(psoundNew, pbufferASRSource, psource);
};
};
HRESULT CreateASRSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
float fLoopStart, float fLoopLength)
{
TRef<ASRSoundTemplate> ptemplate = new ASRSoundTemplate();
HRESULT hr = ptemplate->Init(pstSource, fLoopStart, fLoopLength);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class SoundPairWrapper : public ISoundInstance, public ISoundTweakable3D, public IEventSink
{
protected:
TRef<ISoundInstance> m_pBase1;
TRef<ISoundInstance> m_pBase2;
TRef<ISoundTweakable> m_ptweak1;
TRef<ISoundTweakable> m_ptweak2;
TRef<ISoundTweakable3D> m_ptweak3D1;
TRef<ISoundTweakable3D> m_ptweak3D2;
TRef<EventSourceImpl> m_peventsourceFinished;
TRef<IEventSink> m_peventDelegate;
int m_nNumChildrenFinished;
public:
SoundPairWrapper(ISoundInstance* pBase1, ISoundInstance* pBase2) :
m_pBase1(pBase1), m_pBase2(pBase2), m_nNumChildrenFinished(0)
{
ZAssert(pBase1 != NULL);
ZAssert(pBase2 != NULL);
};
~SoundPairWrapper()
{
if (m_peventDelegate != NULL)
{
m_pBase1->GetFinishEventSource()->RemoveSink(m_peventDelegate);
m_pBase2->GetFinishEventSource()->RemoveSink(m_peventDelegate);
}
}
virtual HRESULT Stop(bool bForceNow = false)
{
HRESULT hr = m_pBase1->Stop(bForceNow);
if (ZFailed(hr)) return hr;
return m_pBase2->Stop(bForceNow);
};
virtual HRESULT IsPlaying()
{
HRESULT hr = m_pBase1->IsPlaying();
if (ZFailed(hr) || hr == S_OK) return hr;
return m_pBase2->IsPlaying();
};
virtual IEventSource* GetFinishEventSource()
{
if (m_peventsourceFinished == NULL)
{
m_peventsourceFinished = new EventSourceImpl();
m_peventDelegate = IEventSink::CreateDelegate(this);
m_pBase1->GetFinishEventSource()->AddSink(m_peventDelegate);
m_pBase2->GetFinishEventSource()->AddSink(m_peventDelegate);
}
return m_peventsourceFinished;
};
virtual TRef<ISoundTweakable> GetISoundTweakable()
{
if (!m_ptweak1 || !m_ptweak2)
{
m_ptweak1 = m_pBase1->GetISoundTweakable();
m_ptweak2 = m_pBase2->GetISoundTweakable();
}
if (!m_ptweak1 || !m_ptweak2)
{
return NULL;
}
else
{
return this;
}
}
virtual TRef<ISoundTweakable3D> GetISoundTweakable3D()
{
if (!m_ptweak3D1 || !m_ptweak3D2)
{
m_ptweak3D1 = m_pBase1->GetISoundTweakable3D();
m_ptweak3D2 = m_pBase2->GetISoundTweakable3D();
}
if (!m_ptweak3D1 || !m_ptweak3D2)
{
return NULL;
}
else
{
return this;
}
}
virtual bool OnEvent(IEventSource* pevent)
{
m_nNumChildrenFinished++;
ZAssert(m_nNumChildrenFinished <= 2);
if (m_nNumChildrenFinished >= 2)
{
m_peventsourceFinished->Trigger();
}
return true;
};
virtual HRESULT SetGain(float fGain)
{
HRESULT hr = m_ptweak1->SetGain(fGain);
if (ZFailed(hr)) return hr;
return m_ptweak2->SetGain(fGain);
};
virtual HRESULT SetPitch(float fPitch)
{
HRESULT hr = m_ptweak1->SetPitch(fPitch);
if (ZFailed(hr)) return hr;
return m_ptweak2->SetPitch(fPitch);
}
virtual HRESULT SetPriority(float fPriority)
{
HRESULT hr = m_ptweak1->SetPriority(fPriority);
if (ZFailed(hr)) return hr;
return m_ptweak2->SetPriority(fPriority);
}
virtual HRESULT Set3D(bool b3D)
{
HRESULT hr = m_ptweak3D1->Set3D(b3D);
if (ZFailed(hr)) return hr;
return m_ptweak3D2->Set3D(b3D);
}
virtual HRESULT SetMinimumDistance(float fMinimumDistance)
{
HRESULT hr = m_ptweak3D1->SetMinimumDistance(fMinimumDistance);
if (ZFailed(hr)) return hr;
return m_ptweak3D2->SetMinimumDistance(fMinimumDistance);
}
virtual HRESULT SetCone(float fInnerAngle, float fOuterAngle, float fOutsideGain)
{
HRESULT hr = m_ptweak3D1->SetCone(fInnerAngle, fOuterAngle, fOutsideGain);
if (ZFailed(hr)) return hr;
return m_ptweak3D2->SetCone(fInnerAngle, fOuterAngle, fOutsideGain);
}
};
class PairedSoundTemplate : public ISoundTemplate
{
private:
TRef<ISoundTemplate> m_pstBase1;
TRef<ISoundTemplate> m_pstBase2;
public:
HRESULT Init(ISoundTemplate* pstSource1, ISoundTemplate* pstSource2)
{
if (!pstSource1 || !pstSource2)
{
ZAssert(false);
return E_POINTER;
}
m_pstBase1 = pstSource1;
m_pstBase2 = pstSource2;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
{
TRef<ISoundInstance> psound1, psound2;
HRESULT hr;
hr = m_pstBase1->CreateSound(psound1, pbufferSource, psource);
if (ZFailed(hr)) return hr;
hr = m_pstBase2->CreateSound(psound2, pbufferSource, psource);
if (ZFailed(hr)) return hr;
if (!psound1 || !psound2)
{
ZAssert(false);
return E_FAIL;
}
psoundNew = new SoundPairWrapper(psound1, psound2);
return S_OK;
}
};
HRESULT CreatePairedSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource1,
ISoundTemplate* pstSource2)
{
TRef<PairedSoundTemplate> ptemplate = new PairedSoundTemplate();
HRESULT hr = ptemplate->Init(pstSource1, pstSource2);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class RepeatFirePCMData : public ISoundPCMData
{
TRef<ISoundPCMData> m_pdataBase;
void* m_pvData;
DWORD m_dwSize;
DWORD m_dwRepeatLength;
RepeatFirePCMData(ISoundPCMData* pdata, DWORD dwRepeatLength)
{
ZAssert(pdata);
ZAssert(dwRepeatLength > 0);
m_pvData = NULL;
m_pdataBase = pdata;
m_dwRepeatLength = dwRepeatLength;
m_dwSize = m_pdataBase->GetSize() + m_dwRepeatLength * 2;
}
typedef std::pair<ISoundPCMData*, DWORD> RepeatFireCacheKey;
typedef std::map<RepeatFireCacheKey, TRef<RepeatFirePCMData> >
RepeatFireDataCache;
static RepeatFireDataCache s_cache;
static int s_nCacheRefCount;
public:
static void AddCacheRef()
{
++s_nCacheRefCount;
}
static void ReleaseCacheRef()
{
--s_nCacheRefCount;
ZAssert(s_nCacheRefCount >= 0);
if (s_nCacheRefCount == 0 && !s_cache.empty())
{
s_cache.clear();
}
}
static RepeatFirePCMData* Create(ISoundPCMData* pdata, float fRepeatDelay)
{
ZAssert(s_nCacheRefCount > 0);
if (!pdata)
return NULL;
DWORD dwRepeatLength = (DWORD)(fRepeatDelay * (pdata->GetBytesPerSec()))
& ~(pdata->GetBytesPerSample() - 1);
if (dwRepeatLength > 0x1000000 || dwRepeatLength == 0)
return NULL;
RepeatFireDataCache::iterator iter;
RepeatFireCacheKey key(pdata, dwRepeatLength);
iter = s_cache.find(key);
if (iter == s_cache.end())
{
iter = (s_cache.insert(std::pair<RepeatFireCacheKey, TRef<RepeatFirePCMData> >(
key, new RepeatFirePCMData(pdata, dwRepeatLength)
))).first;
}
return (*iter).second;
}
~RepeatFirePCMData()
{
if (m_pvData)
delete [] (char*)m_pvData;
}
DWORD GetLoopStart()
{
return m_dwRepeatLength*2;
}
DWORD GetLoopLength()
{
return m_dwRepeatLength;
}
virtual unsigned GetNumberOfChannels()
{
return m_pdataBase->GetNumberOfChannels();
};
virtual unsigned GetBitsPerSample()
{
return m_pdataBase->GetBitsPerSample();
};
virtual unsigned GetSampleRate()
{
return m_pdataBase->GetSampleRate();
}
virtual unsigned GetSize()
{
return m_dwSize;
}
virtual void GetData(void* dest, unsigned nOffset, unsigned nLength)
{
if (!m_pvData)
{
m_pvData = new char[m_dwSize];
m_pdataBase->GetData((char*)(m_pvData) + m_dwRepeatLength * 2, 0,
m_dwSize - m_dwRepeatLength * 2);
memcpy(m_pvData, (char*)(m_pvData) + m_dwRepeatLength * 2, m_dwRepeatLength);
if (m_pdataBase->GetBitsPerSample() == 8)
{
unsigned char* pcSample2Start =
(unsigned char*)(m_pvData) + m_dwRepeatLength;
unsigned char* pcSample3Start =
(unsigned char*)(m_pvData) + m_dwRepeatLength * 2;
unsigned char* pcSample1End =
(unsigned char*)(m_pvData) + (m_dwSize - m_dwRepeatLength * 2);
unsigned char* pcSample2End =
(unsigned char*)(m_pvData) + (m_dwSize - m_dwRepeatLength);
unsigned char* pcAdd;
for (pcAdd = pcSample2Start; pcAdd < pcSample3Start; ++pcAdd)
{
*pcAdd = (unsigned char)(max(0, min(0xFF,
(int)pcAdd[m_dwRepeatLength] + (int)pcAdd[m_dwRepeatLength*2] - 0x7F
)));
}
for (; pcAdd < pcSample1End; ++pcAdd)
{
*pcAdd = (unsigned char)(max(0, min(0xFF,
(int)pcAdd[0] + (int)pcAdd[m_dwRepeatLength]
+ (int)pcAdd[m_dwRepeatLength*2] - 0x7F*2
)));
}
for (; pcAdd < pcSample2End; ++pcAdd)
{
*pcAdd = (unsigned char)(max(0, min(0xFF,
(int)pcAdd[0] + (int)pcAdd[m_dwRepeatLength] - 0x7F
)));
}
}
else
{
ZAssert(m_pdataBase->GetBitsPerSample() == 16);
ZAssert(m_dwRepeatLength % 2 == 0);
ZAssert(m_dwSize % 2 == 0);
short* pwSample2Start =
(short*)(m_pvData) + m_dwRepeatLength/2;
short* pwSample3Start =
(short*)(m_pvData) + m_dwRepeatLength;
short* pwSample1End =
(short*)(m_pvData) + (m_dwSize - m_dwRepeatLength*2)/2;
short* pwSample2End =
(short*)(m_pvData) + (m_dwSize - m_dwRepeatLength)/2;
short* pwAdd;
for (pwAdd = pwSample2Start; pwAdd < pwSample2Start; ++pwAdd)
{
*pwAdd = (short)(max(-0x8000, min(0x7FFF,
(int)pwAdd[m_dwRepeatLength/2] + (int)pwAdd[m_dwRepeatLength]
)));
}
for (; pwAdd < pwSample1End; ++pwAdd)
{
*pwAdd = (short)(max(-0x8000, min(0x7FFF,
(int)pwAdd[0] + (int)pwAdd[m_dwRepeatLength/2]
+ (int)pwAdd[m_dwRepeatLength]
)));
}
for (; pwAdd < pwSample2End; ++pwAdd)
{
*pwAdd = (short)(max(-0x8000, min(0x7FFF,
(int)pwAdd[0] + (int)pwAdd[m_dwRepeatLength/2]
)));
}
}
}
ZAssert(nOffset + nLength <= m_dwSize);
memcpy(dest, ((BYTE*)m_pvData) + nOffset, nLength);
};
};
RepeatFirePCMData::RepeatFireDataCache RepeatFirePCMData::s_cache;
int RepeatFirePCMData::s_nCacheRefCount = 0;
class RepeatFireTemplate : public ISoundTemplate
{
TRef<ISoundTemplate> m_pstBase;
float m_fRepeatRate;
class RepeatFireBufferSource : public SoundBufferSourceWrapper
{
float m_fRepeatRate;
TRef<RepeatFirePCMData> m_ppcmdataRepeatLast;
TRef<ISoundPCMData> m_ppcmdataBaseLast;
public:
RepeatFireBufferSource(ISoundBufferSource* pBase, float fRepeatRate) :
SoundBufferSourceWrapper(pBase),
m_fRepeatRate(fRepeatRate)
{};
HRESULT CreateStaticBuffer(TRef<ISoundInstance>& psoundNew,
ISoundPCMData* pcmdata, bool bLooping, ISoundPositionSource* psource)
{
unsigned uBytesPerSec = pcmdata->GetBytesPerSec();
TRef<RepeatFirePCMData> ppcmdataRepeat;
if (pcmdata == m_ppcmdataBaseLast)
{
ppcmdataRepeat = m_ppcmdataRepeatLast;
}
else
{
m_ppcmdataBaseLast = pcmdata;
ppcmdataRepeat = m_ppcmdataRepeatLast =
RepeatFirePCMData::Create(pcmdata, m_fRepeatRate);
}
if (!ppcmdataRepeat)
{
ZAssert(false);
return E_FAIL;
}
return m_pBase->CreateASRBuffer(psoundNew, ppcmdataRepeat,
ppcmdataRepeat->GetLoopStart(),
ppcmdataRepeat->GetLoopLength(),
psource
);
}
};
public:
RepeatFireTemplate()
{
RepeatFirePCMData::AddCacheRef();
};
~RepeatFireTemplate()
{
RepeatFirePCMData::ReleaseCacheRef();
};
HRESULT Init(ISoundTemplate* pstSource, float fRepeatRate)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
if (fRepeatRate <= 0.0f)
{
ZAssert(false);
return E_INVALIDARG;
}
m_fRepeatRate = fRepeatRate;
m_pstBase = pstSource;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
{
if (!pbufferSource)
{
ZAssert(false);
return E_POINTER;
}
TRef<ISoundBufferSource> pbufferRepeatFireSource =
new RepeatFireBufferSource(pbufferSource, m_fRepeatRate);
return m_pstBase->CreateSound(psoundNew, pbufferRepeatFireSource, psource);
};
};
HRESULT CreateRepeatingFireSoundTemplate(TRef<ISoundTemplate>& pstDest,
ISoundTemplate* pstSource,
float fFireDelay)
{
TRef<RepeatFireTemplate> ptemplate = new RepeatFireTemplate();
HRESULT hr = ptemplate->Init(pstSource, fFireDelay);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
};
class RandomSoundTemplate : public IRandomSoundTemplate
{
private:
typedef std::map<float, ZAdapt<TRef<ISoundTemplate> > > TemplateMap;
TemplateMap m_templates;
float m_fTotalWeight;
ISoundTemplate* m_pstLast;
public:
RandomSoundTemplate() :
m_fTotalWeight(0),
m_pstLast(NULL)
{
}
HRESULT AddSoundTemplate(ISoundTemplate* pstSource, float fWeight)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
if (fWeight <= 0.0f)
{
ZAssert(false);
return E_INVALIDARG;
}
m_templates.insert(std::pair<float, ZAdapt<TRef<ISoundTemplate> > >(
m_fTotalWeight, TRef<ISoundTemplate>(pstSource)));
m_fTotalWeight += fWeight;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource = NULL)
{
ISoundTemplate* pstNew;
ZAssert(m_templates.size() > 0);
do
{
float fChoice = (rand()+1) * m_fTotalWeight / (RAND_MAX + 1);
pstNew = (TRef<ISoundTemplate>&)(*(--(m_templates.lower_bound(fChoice)))).second;
}
while (m_templates.size() > 2 && pstNew == m_pstLast);
pstNew->CreateSound(psoundNew, pbufferSource, psource);
m_pstLast = pstNew;
return S_OK;
};
};
HRESULT CreateRandomSoundTemplate(TRef<IRandomSoundTemplate>& pstDest)
{
pstDest = new RandomSoundTemplate();
return S_OK;
};
class IntermittentSoundTemplate : public ISoundTemplate
{
private:
TRef<ISoundTemplate> m_pstBase;
float m_fPeriod;
bool m_bMultipleSounds;
public:
class IntermittentInstance : public StubbedTweakableSoundInstance
{
float m_fPeriodMS;
TRef<ISoundTemplate> m_ptemplate;
bool m_bMultipleSounds;
TRef<ISoundBufferSource> m_pbufferSource;
typedef std::list<ZAdapt<TRef<ISoundInstance> > > SoundInstanceList;
SoundInstanceList m_listPlayingSounds;
public:
IntermittentInstance(float fPeriod, bool bMultipleSounds,
ISoundTemplate* ptemplate, ISoundBufferSource* pbufferSource,
ISoundPositionSource* pposSource) :
StubbedTweakableSoundInstance(pposSource),
m_fPeriodMS(fPeriod*1000),
m_bMultipleSounds(bMultipleSounds),
m_ptemplate(ptemplate),
m_pbufferSource(pbufferSource)
{
m_pbufferSource->GetUpdateEventSource()->AddSink(this);
};
virtual void Update(DWORD dwElapsedTime, bool bHasStopped)
{
StubbedTweakableSoundInstance::Update(dwElapsedTime, bHasStopped);
if (!bHasStopped)
{
SoundInstanceList::iterator iter;
for (iter = m_listPlayingSounds.begin();
iter != m_listPlayingSounds.end();)
{
if ((*iter)->IsPlaying() == S_OK)
{
++iter;
}
else
{
iter = m_listPlayingSounds.erase(iter);
}
}
if (m_bMultipleSounds || m_listPlayingSounds.empty())
{
float fProbability = 1 - (float)pow((float)2, -((float)dwElapsedTime/m_fPeriodMS));
if (rand() < fProbability * RAND_MAX)
{
TRef<ISoundInstance> pNewSound;
if (ZSucceeded(m_ptemplate->CreateSound(
pNewSound, m_pbufferSource, m_pposSource)))
{
m_listPlayingSounds.push_back(pNewSound);
UpdateSound(pNewSound);
}
}
}
}
}
HRESULT Stop(bool bForceNow)
{
ZSucceeded(StubbedTweakableSoundInstance::Stop(bForceNow));
SoundInstanceList::iterator iter;
for (iter = m_listPlayingSounds.begin();
iter != m_listPlayingSounds.end(); ++iter)
{
ZSucceeded((*iter)->Stop(bForceNow));
}
return S_OK;
};
virtual HRESULT SetGain(float fGain)
{
ZSucceeded(StubbedTweakableSoundInstance::SetGain(fGain));
SoundInstanceList::iterator iter;
for (iter = m_listPlayingSounds.begin();
iter != m_listPlayingSounds.end(); ++iter)
{
ZSucceeded((*iter)->GetISoundTweakable()->SetGain(fGain));
}
return S_OK;
};
virtual HRESULT SetPitch(float fPitch)
{
ZSucceeded(StubbedTweakableSoundInstance::SetPitch(fPitch));
SoundInstanceList::iterator iter;
for (iter = m_listPlayingSounds.begin();
iter != m_listPlayingSounds.end(); ++iter)
{
ZSucceeded((*iter)->GetISoundTweakable()->SetPitch(fPitch));
}
return S_OK;
};
virtual HRESULT SetPriority(float fPriority)
{
ZSucceeded(StubbedTweakableSoundInstance::SetPriority(fPriority));
SoundInstanceList::iterator iter;
for (iter = m_listPlayingSounds.begin();
iter != m_listPlayingSounds.end(); ++iter)
{
ZSucceeded((*iter)->GetISoundTweakable()->SetPriority(fPriority));
}
return S_OK;
};
virtual HRESULT Set3D(bool b3D)
{
ZSucceeded(StubbedTweakableSoundInstance::Set3D(b3D));
SoundInstanceList::iterator iter;
for (iter = m_listPlayingSounds.begin();
iter != m_listPlayingSounds.end(); ++iter)
{
TRef<ISoundTweakable3D> ptweak3d = (*iter)->GetISoundTweakable3D();
if (ptweak3d)
ZSucceeded(ptweak3d->Set3D(b3D));
}
return S_OK;
};
virtual HRESULT SetMinimumDistance(float fMinimumDistance)
{
ZSucceeded(StubbedTweakableSoundInstance::SetMinimumDistance(fMinimumDistance));
SoundInstanceList::iterator iter;
for (iter = m_listPlayingSounds.begin();
iter != m_listPlayingSounds.end(); ++iter)
{
TRef<ISoundTweakable3D> ptweak3d = (*iter)->GetISoundTweakable3D();
if (ptweak3d)
ZSucceeded(ptweak3d->SetMinimumDistance(fMinimumDistance));
}
return S_OK;
};
virtual HRESULT SetCone(float fInnerAngle, float fOuterAngle, float fOutsideGain)
{
ZSucceeded(StubbedTweakableSoundInstance::SetCone(fInnerAngle, fOuterAngle, fOutsideGain));
SoundInstanceList::iterator iter;
for (iter = m_listPlayingSounds.begin();
iter != m_listPlayingSounds.end(); ++iter)
{
TRef<ISoundTweakable3D> ptweak3d = (*iter)->GetISoundTweakable3D();
if (ptweak3d)
ZSucceeded(ptweak3d->SetCone(fInnerAngle, fOuterAngle, fOutsideGain));
}
return S_OK;
};
};
public:
HRESULT Init(ISoundTemplate* pstSource, float fPeriod, bool bMultipleSounds)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
m_pstBase = pstSource;
m_fPeriod = fPeriod;
m_bMultipleSounds = bMultipleSounds;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
{
if (!pbufferSource)
{
ZAssert(false);
return E_POINTER;
}
psoundNew = new IntermittentInstance(m_fPeriod, m_bMultipleSounds,
m_pstBase, pbufferSource, psource);
return S_OK;
}
};
HRESULT CreateIntermittentSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
float fPeriod, bool bMultipleSounds)
{
TRef<IntermittentSoundTemplate> ptemplate = new IntermittentSoundTemplate();
HRESULT hr = ptemplate->Init(pstSource, fPeriod, bMultipleSounds);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
}
class DelayedSoundInstance : public ISoundInstance, public IEventSink
{
protected:
TRef<ISoundInstance> m_pBase;
TRef<ISoundTemplate> m_pstSource;
TRef<ISoundBufferSource> m_pbufferSource;
TRef<ISoundPositionSource> m_psource;
TRef<EventSourceImpl> m_peventsource;
TRef<IEventSink> m_psinkDelegate;
public:
DelayedSoundInstance(ISoundTemplate* pstSource,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource) :
m_pstSource(pstSource),
m_pbufferSource(pbufferSource),
m_psource(psource)
{
ZAssert(m_pstSource != NULL);
ZAssert(m_pbufferSource != NULL);
}
~DelayedSoundInstance()
{
if (m_psinkDelegate)
{
m_pBase->GetFinishEventSource()->RemoveSink(m_psinkDelegate);
m_psinkDelegate = NULL;
}
}
virtual HRESULT Start()
{
HRESULT hr = m_pstSource->CreateSound(m_pBase, m_pbufferSource, m_psource);
if (SUCCEEDED(hr))
{
m_psinkDelegate = IEventSink::CreateDelegate(this);
m_pBase->GetFinishEventSource()->AddSink(m_psinkDelegate);
m_pstSource = NULL;
m_pbufferSource = NULL;
m_psource = NULL;
}
return hr;
}
virtual HRESULT Stop(bool bForceNow = false)
{
if (m_pBase)
return m_pBase->Stop();
else if (m_peventsource)
{
m_peventsource->Trigger();
m_pstSource = NULL;
}
return S_OK;
}
virtual bool OnEvent(IEventSource* pevent)
{
m_pBase = NULL;
m_psinkDelegate = NULL;
if (m_peventsource)
m_peventsource->Trigger();
return false;
}
virtual HRESULT IsPlaying()
{
if (m_pBase)
return m_pBase->IsPlaying();
else
return m_pstSource ? S_OK : S_FALSE;
}
virtual IEventSource* GetFinishEventSource()
{
if (m_peventsource)
return m_peventsource;
else if (m_pBase)
return m_pBase->GetFinishEventSource();
else
return m_peventsource = new EventSourceImpl();
}
virtual TRef<ISoundTweakable> GetISoundTweakable()
{
assert(false); if (m_pBase)
return m_pBase->GetISoundTweakable();
else
return NULL;
}
virtual TRef<ISoundTweakable3D> GetISoundTweakable3D()
{
assert(false); if (m_pBase)
return m_pBase->GetISoundTweakable3D();
else
return NULL;
}
};
class SoundMutex : public ISoundMutex, public IEventSink
{
private:
struct TemplateData
{
TRef<DelayedSoundInstance> pinstance;
Time timeExpiration;
Time timeUrgency;
TemplateData()
{}
TemplateData(TRef<DelayedSoundInstance> pinstance_,
Time timeExpiration_, Time timeUrgency_)
: pinstance(pinstance_), timeExpiration(timeExpiration_),
timeUrgency(timeUrgency_)
{}
};
typedef std::multimap<ZAdapt<TRef<ISoundTemplate> >, TemplateData> TemplateMap;
TemplateMap m_templatesPending;
TRef<ISoundTemplate> m_pstCurrent;
TRef<ISoundInstance> m_psoundCurrent;
TRef<IEventSource> m_peventSoundFinished;
HRESULT StartSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource,
ISoundTemplate* pstSource)
{
ZAssert(!m_pstCurrent);
ZAssert(!m_peventSoundFinished);
ZAssert(!m_psoundCurrent);
HRESULT hr = pstSource->CreateSound(psoundNew, pbufferSource, psource);
if (SUCCEEDED(hr))
{
if (!psoundNew)
{
ZAssert(false);
return E_FAIL;
}
m_pstCurrent = pstSource;
m_psoundCurrent = psoundNew;
m_peventSoundFinished = psoundNew->GetFinishEventSource();
m_peventSoundFinished->AddSink(this);
}
return hr;
}
void QueueNextSound()
{
ZAssert(!m_pstCurrent);
ZAssert(!m_peventSoundFinished);
ZAssert(!m_psoundCurrent);
Time now = Time::Now();
TemplateMap::iterator iterTemplate = m_templatesPending.begin();
TemplateMap::iterator iterMostUrgentSound = m_templatesPending.end();
float fMaxUrgency = -1e10f;
while (iterTemplate != m_templatesPending.end())
{
if ((*iterTemplate).second.timeExpiration - now < 0.0f)
iterTemplate = m_templatesPending.erase(iterTemplate);
else
{
float fUrgency = now - (*iterTemplate).second.timeUrgency;
if (fUrgency > fMaxUrgency)
{
fMaxUrgency = fUrgency;
iterMostUrgentSound = iterTemplate;
}
++iterTemplate;
}
}
if (iterMostUrgentSound != m_templatesPending.end())
{
HRESULT hr = (*iterMostUrgentSound).second.pinstance->Start();
if (SUCCEEDED(hr))
{
m_pstCurrent = (*iterMostUrgentSound).first;
m_peventSoundFinished =
(*iterMostUrgentSound).second.pinstance->GetFinishEventSource();
m_peventSoundFinished->AddSink(this);
m_psoundCurrent = (*iterMostUrgentSound).second.pinstance;
}
m_templatesPending.erase(iterMostUrgentSound);
if (FAILED(hr))
{
QueueNextSound();
}
}
}
public:
HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource,
ISoundTemplate* pstSource, float fTimeout, float fPriority, bool bSingleInstance)
{
if (!m_pstCurrent)
{
return StartSound(psoundNew, pbufferSource, psource, pstSource);
}
else
{
if (bSingleInstance)
{
if (m_pstCurrent == pstSource)
{
psoundNew = m_psoundCurrent;
return S_OK;
}
TemplateMap::iterator iterOld = m_templatesPending.find(TRef<ISoundTemplate>(pstSource));
if (iterOld != m_templatesPending.end())
{
(*iterOld).second.timeExpiration = Time::Now() + fTimeout;
psoundNew = (*iterOld).second.pinstance;
return S_OK;
}
}
Time timeExpiration = Time::Now() + fTimeout;
TRef<DelayedSoundInstance> pinstance = new DelayedSoundInstance(pstSource, pbufferSource, psource);
m_templatesPending.insert(TemplateMap::value_type(TRef<ISoundTemplate>(pstSource),
TemplateData(pinstance, timeExpiration, timeExpiration - fPriority)));
psoundNew = pinstance;
return S_OK;
}
}
virtual bool OnEvent(IEventSource* pevent)
{
m_pstCurrent = NULL;
m_psoundCurrent = NULL;
m_peventSoundFinished = NULL;
QueueNextSound();
return false;
}
virtual HRESULT Reset()
{
m_pstCurrent = NULL;
m_psoundCurrent = NULL;
if (m_peventSoundFinished)
m_peventSoundFinished->RemoveSink(this);
m_peventSoundFinished = NULL;
m_templatesPending.clear();
return S_OK;
}
};
HRESULT CreateSoundMutex(TRef<ISoundMutex>& pmutex)
{
pmutex = new SoundMutex();
return S_OK;
}
class SerializedSoundTemplate : public ISoundTemplate
{
private:
TRef<ISoundTemplate> m_pstBase;
TRef<SoundMutex> m_pmutex;
float m_fTimeout;
float m_fPriority;
bool m_bSingleInstance;
public:
HRESULT Init(ISoundTemplate* pstSource, ISoundMutex* pmutex,
float fTimeout, float fPriority, bool bSingleInstance)
{
if (!pstSource)
{
ZAssert(false);
return E_POINTER;
}
if (fTimeout < 0)
{
ZAssert(false);
return E_INVALIDARG;
}
CastTo(m_pmutex, pmutex);
if (!m_pmutex)
{
ZAssert(false);
return E_INVALIDARG;
}
m_pstBase = pstSource;
m_fTimeout = fTimeout;
m_fPriority = fPriority;
m_bSingleInstance = bSingleInstance;
return S_OK;
};
virtual HRESULT CreateSound(TRef<ISoundInstance>& psoundNew,
ISoundBufferSource* pbufferSource, ISoundPositionSource* psource)
{
return m_pmutex->CreateSound(psoundNew, pbufferSource, psource,
m_pstBase, m_fTimeout, m_fPriority, m_bSingleInstance);
}
};
HRESULT CreateSerializedSoundTemplate(TRef<ISoundTemplate>& pstDest, ISoundTemplate* pstSource,
ISoundMutex* pmutex, float fTimeout, float fPriority,
bool bSingleInstance)
{
TRef<SerializedSoundTemplate> ptemplate = new SerializedSoundTemplate();
HRESULT hr = ptemplate->Init(pstSource, pmutex, fTimeout, fPriority, bSingleInstance);
if (ZSucceeded(hr))
pstDest = ptemplate;
return hr;
}
};