#include "pch.h"
#include "soundbase.h"
#include "ds3dutil.h"
#include "ds3dbuffer.h"
namespace SoundEngine {
inline LONG DS3DSoundBuffer::DSoundVolume(float fGain)
{
ZAssert((fGain >= -100) && (fGain <= 0));
return (LONG)(100 * fGain);
}
HRESULT DS3DSoundBuffer::CreateBuffer8(IDirectSound8* pDirectSound, ISoundPCMData* pdata,
DWORD dwBufferSize, bool bStatic, bool bSupport3D, ISoundEngine::Quality quality,
bool bAllowHardware)
{
HRESULT hr;
if (!pdata || !pDirectSound)
{
ZAssert(false);
return E_POINTER;
}
m_b3D = bSupport3D;
m_dwSampleRate = pdata->GetSampleRate();
WAVEFORMATEX waveformatex;
DSBUFFERDESC dsbufferdesc;
waveformatex.cbSize = 0; waveformatex.wFormatTag = WAVE_FORMAT_PCM;
waveformatex.nChannels = pdata->GetNumberOfChannels();
waveformatex.nSamplesPerSec = m_dwSampleRate;
waveformatex.wBitsPerSample = pdata->GetBitsPerSample();
waveformatex.nBlockAlign = waveformatex.wBitsPerSample / 8 * waveformatex.nChannels;
waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign;
dsbufferdesc.dwSize = sizeof(DSBUFFERDESC);
dsbufferdesc.dwFlags =
(bSupport3D ? DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE : 0)
| DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME
| DSBCAPS_GETCURRENTPOSITION2
;
dsbufferdesc.dwBufferBytes = dwBufferSize;
dsbufferdesc.dwReserved = 0;
dsbufferdesc.lpwfxFormat = &waveformatex;
#if DIRECTSOUND_VERSION >= 0x0700
if (bAllowHardware)
dsbufferdesc.dwFlags |= DSBCAPS_LOCDEFER;
else
dsbufferdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
if (bSupport3D)
{
switch (quality)
{
case ISoundEngine::minQuality:
dsbufferdesc.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;
break;
case ISoundEngine::midQuality:
dsbufferdesc.guid3DAlgorithm = DS3DALG_DEFAULT;
break;
case ISoundEngine::maxQuality:
dsbufferdesc.guid3DAlgorithm = DS3DALG_HRTF_LIGHT;
break;
};
}
else
{
dsbufferdesc.guid3DAlgorithm = GUID_NULL;
}
#endif
LPDIRECTSOUNDBUFFER mdDsb = NULL;
hr = pDirectSound->CreateSoundBuffer(&dsbufferdesc, &mdDsb, NULL);
if (ZFailed(hr)) return hr;
hr = mdDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) &m_pdirectsoundbuffer8);
mdDsb->Release();
if (ZFailed(hr)) return hr;
if (bSupport3D)
{
hr = m_pdirectsoundbuffer8->QueryInterface(IID_IDirectSound3DBuffer, (void**)&m_pdirectsound3Dbuffer);
if (ZFailed(hr)) return hr;
}
BYTE nFillValue = (waveformatex.wBitsPerSample == 8) ? 0x80 : 0;
LPVOID writePtr;
DWORD writeBytes;
m_pdirectsoundbuffer8->Lock(0, 0, &writePtr, &writeBytes, NULL, NULL, DSBLOCK_ENTIREBUFFER);
memset(writePtr, nFillValue, writeBytes); m_pdirectsoundbuffer8->Unlock(writePtr, writeBytes, NULL, NULL);
return S_OK;
};
HRESULT DS3DSoundBuffer::CreateBuffer(IDirectSound* pDirectSound, ISoundPCMData* pdata,
DWORD dwBufferSize, bool bStatic, bool bSupport3D, ISoundEngine::Quality quality,
bool bAllowHardware)
{
HRESULT hr;
if (!pdata || !pDirectSound)
{
ZAssert(false);
return E_POINTER;
}
m_b3D = bSupport3D;
m_dwSampleRate = pdata->GetSampleRate();
WAVEFORMATEX waveformatex;
DSBUFFERDESC dsbufferdesc;
waveformatex.cbSize = 0; waveformatex.wFormatTag = WAVE_FORMAT_PCM;
waveformatex.nChannels = pdata->GetNumberOfChannels();
waveformatex.nSamplesPerSec = m_dwSampleRate;
waveformatex.wBitsPerSample = pdata->GetBitsPerSample();
waveformatex.nBlockAlign = waveformatex.wBitsPerSample / 8 * waveformatex.nChannels;
waveformatex.nAvgBytesPerSec = waveformatex.nSamplesPerSec * waveformatex.nBlockAlign;
dsbufferdesc.dwSize = sizeof(DSBUFFERDESC);
dsbufferdesc.dwFlags =
(bSupport3D ? DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE : 0)
| DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLVOLUME
| DSBCAPS_GETCURRENTPOSITION2
| (bStatic ? DSBCAPS_STATIC : 0)
;
dsbufferdesc.dwBufferBytes = dwBufferSize;
dsbufferdesc.dwReserved = 0;
dsbufferdesc.lpwfxFormat = &waveformatex;
#if DIRECTSOUND_VERSION >= 0x0700
if (bAllowHardware)
dsbufferdesc.dwFlags |= DSBCAPS_LOCDEFER;
else
dsbufferdesc.dwFlags |= DSBCAPS_LOCSOFTWARE;
if (bSupport3D)
{
switch (quality)
{
case ISoundEngine::minQuality:
dsbufferdesc.guid3DAlgorithm = DS3DALG_NO_VIRTUALIZATION;
break;
case ISoundEngine::midQuality:
dsbufferdesc.guid3DAlgorithm = DS3DALG_DEFAULT;
break;
case ISoundEngine::maxQuality:
dsbufferdesc.guid3DAlgorithm = DS3DALG_HRTF_LIGHT;
break;
};
}
else
{
dsbufferdesc.guid3DAlgorithm = GUID_NULL;
}
#endif
hr = pDirectSound->CreateSoundBuffer(&dsbufferdesc, &m_pdirectsoundbuffer, NULL);
if (FAILED(hr)) return hr;
if (bSupport3D)
{
hr = m_pdirectsoundbuffer->QueryInterface(IID_IDirectSound3DBuffer, (void**)&m_pdirectsound3Dbuffer);
if (ZFailed(hr)) return hr;
}
return S_OK;
};
HRESULT DS3DSoundBuffer::DuplicateBuffer8(IDirectSound8* pDirectSound, DS3DSoundBuffer* pBuffer)
{
HRESULT hr;
if (!pBuffer || !pDirectSound)
{
ZAssert(false);
return E_POINTER;
}
if (!pBuffer->m_pdirectsoundbuffer8)
{
ZAssert(false);
return E_INVALIDARG;
}
m_b3D = pBuffer->m_b3D;
m_bListenerRelative = pBuffer->m_bListenerRelative;
m_dwSampleRate = pBuffer->m_dwSampleRate;
LPDIRECTSOUNDBUFFER mdDsb = NULL;
hr = pDirectSound->DuplicateSoundBuffer(pBuffer->m_pdirectsoundbuffer8, &mdDsb);
if (ZFailed(hr)) return hr;
hr = mdDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) &m_pdirectsoundbuffer8);
mdDsb->Release();
if (ZFailed(hr)) return hr;
hr = m_pdirectsoundbuffer8->SetVolume(DSoundVolume(m_fGain));
if (ZFailed(hr)) return hr;
hr = m_pdirectsoundbuffer8->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
if (ZFailed(hr)) return hr;
if (pBuffer->m_pdirectsound3Dbuffer != NULL)
{
DS3DBUFFER ds3dbuf;
hr = m_pdirectsoundbuffer8->QueryInterface(IID_IDirectSound3DBuffer, (void**)&m_pdirectsound3Dbuffer);
if (ZFailed(hr)) return hr;
ds3dbuf.dwSize = sizeof(ds3dbuf);
ConvertVector(ds3dbuf.vPosition, m_vectPosition);
ConvertVector(ds3dbuf.vVelocity, m_vectVelocity);
ds3dbuf.dwInsideConeAngle = (LONG)m_fInnerAngle;
ds3dbuf.dwOutsideConeAngle = (LONG)m_fOuterAngle;
ConvertVector(ds3dbuf.vConeOrientation, m_vectOrientation);
ds3dbuf.lConeOutsideVolume = DSoundVolume(m_fOutsideGain);
ds3dbuf.flMaxDistance = m_fMinimumDistance * c_fMinToMaxDistanceRatio;
ds3dbuf.flMinDistance = m_fMinimumDistance;
ds3dbuf.dwMode = m_b3D
? (m_bListenerRelative ? DS3DMODE_HEADRELATIVE : DS3DMODE_NORMAL)
: DS3DMODE_DISABLE;
hr = m_pdirectsound3Dbuffer->SetAllParameters(&ds3dbuf, DS3D_IMMEDIATE);
if (ZFailed(hr)) return hr;
}
return S_OK;
};
HRESULT DS3DSoundBuffer::DuplicateBuffer(IDirectSound* pDirectSound, DS3DSoundBuffer* pBuffer)
{
HRESULT hr;
if (!pBuffer || !pDirectSound)
{
ZAssert(false);
return E_POINTER;
}
if (!pBuffer->m_pdirectsoundbuffer)
{
ZAssert(false);
return E_INVALIDARG;
}
m_b3D = pBuffer->m_b3D;
m_bListenerRelative = pBuffer->m_bListenerRelative;
m_dwSampleRate = pBuffer->m_dwSampleRate;
hr = pDirectSound->DuplicateSoundBuffer(pBuffer->m_pdirectsoundbuffer, &m_pdirectsoundbuffer);
if (FAILED(hr)) return hr;
hr = m_pdirectsoundbuffer->SetVolume(DSoundVolume(m_fGain));
if (ZFailed(hr)) return hr;
hr = m_pdirectsoundbuffer->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
if (ZFailed(hr)) return hr;
if (pBuffer->m_pdirectsound3Dbuffer != NULL)
{
DS3DBUFFER ds3dbuf;
hr = m_pdirectsoundbuffer->QueryInterface(IID_IDirectSound3DBuffer, (void**)&m_pdirectsound3Dbuffer);
if (ZFailed(hr)) return hr;
ds3dbuf.dwSize = sizeof(ds3dbuf);
ConvertVector(ds3dbuf.vPosition, m_vectPosition);
ConvertVector(ds3dbuf.vVelocity, m_vectVelocity);
ds3dbuf.dwInsideConeAngle = (LONG)m_fInnerAngle;
ds3dbuf.dwOutsideConeAngle = (LONG)m_fOuterAngle;
ConvertVector(ds3dbuf.vConeOrientation, m_vectOrientation);
ds3dbuf.lConeOutsideVolume = DSoundVolume(m_fOutsideGain);
ds3dbuf.flMaxDistance = m_fMinimumDistance * c_fMinToMaxDistanceRatio;
ds3dbuf.flMinDistance = m_fMinimumDistance;
ds3dbuf.dwMode = m_b3D
? (m_bListenerRelative ? DS3DMODE_HEADRELATIVE : DS3DMODE_NORMAL)
: DS3DMODE_DISABLE;
hr = m_pdirectsound3Dbuffer->SetAllParameters(&ds3dbuf, DS3D_IMMEDIATE);
if (ZFailed(hr)) return hr;
}
return S_OK;
};
HRESULT DS3DSoundBuffer::StartImpl(bool bLooping)
{
HRESULT hr;
DWORD dwFlags = bLooping ? DSBPLAY_LOOPING : 0;
if (m_fPitch != 1.0)
{
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->SetFrequency((LONG)m_dwSampleRate);
else
hr = m_pdirectsoundbuffer->SetFrequency((LONG)m_dwSampleRate);
if (ZFailed(hr)) return hr;
}
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Play(0, 0, dwFlags);
else
hr = m_pdirectsoundbuffer->Play(0, 0, dwFlags);
if (hr == DSERR_BUFFERLOST)
{
debugf("DSound buffer lost.\n");
hr = RestoreBuffer();
if (ZFailed(hr)) return hr;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Play(0, 0, dwFlags);
else
hr = m_pdirectsoundbuffer->Play(0, 0, dwFlags);
}
if (FAILED(hr))
{
ZAssert(hr == DSERR_INVALIDCALL);
debugf("Error starting sound: %X\n", hr);
return hr;
}
if (m_fPitch != 1.0)
{
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
else
hr = m_pdirectsoundbuffer->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
if (ZFailed(hr)) return hr;
}
return S_OK;
}
DS3DSoundBuffer::DS3DSoundBuffer() :
m_vectPosition(0, 0, 0),
m_vectVelocity(0, 0, 0),
m_vectOrientation(1.1f, 0, 0), m_fMinimumDistance(DS3D_DEFAULTMINDISTANCE + 0.01f), m_fInnerAngle(360), m_fOuterAngle(360), m_fOutsideGain(0),
m_fGain(0),
m_fPitch(1.0f),
m_bListenerRelative(false),
m_pdirectsoundbuffer8(NULL),
m_pdirectsoundbuffer(NULL)
{
}
DS3DSoundBuffer::~DS3DSoundBuffer()
{
m_pdirectsoundbuffer = NULL;
m_pdirectsoundbuffer8 = NULL;
m_pdirectsound3Dbuffer = NULL;
}
HRESULT DS3DSoundBuffer::UpdateState(float fGain, float fPitch, bool bCanDefer)
{
HRESULT hr;
if (m_fGain != fGain)
{
if (fGain < -100 || fGain > 0)
{
ZAssert(false);
return E_INVALIDARG;
}
m_fGain = fGain;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->SetVolume(DSoundVolume(fGain));
else
hr = m_pdirectsoundbuffer->SetVolume(DSoundVolume(m_fGain));
if (ZFailed(hr)) return hr;
}
if (m_fPitch != fPitch)
{
if (fPitch < (DSBFREQUENCY_MIN / m_dwSampleRate)
|| (fPitch > DSBFREQUENCY_MAX / m_dwSampleRate))
{
ZAssert(false);
return E_INVALIDARG;
}
m_fPitch = fPitch;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
else
hr = m_pdirectsoundbuffer->SetFrequency((LONG)(m_fPitch*m_dwSampleRate));
if (ZFailed(hr)) return hr;
}
return S_OK;
};
HRESULT DS3DSoundBuffer::UpdateState3D(
const Vector& vectPosition,
const Vector& vectVelocity,
const Vector& vectOrientation,
float fMinimumDistance,
float fInnerAngle,
float fOuterAngle,
float fOutsideGain,
bool b3D,
bool bListenerRelative,
bool bCanDefer
)
{
if (m_pdirectsound3Dbuffer == NULL)
{
ZAssert(FALSE);
return E_NOTIMPL;
}
if ((m_vectPosition != vectPosition)
|| (m_vectVelocity != vectVelocity)
|| (m_vectOrientation != vectOrientation)
|| (m_fMinimumDistance != fMinimumDistance)
|| (m_fInnerAngle != fInnerAngle)
|| (m_fOuterAngle != fOuterAngle)
|| (m_fOutsideGain != fOutsideGain)
|| (m_b3D != b3D)
|| (m_bListenerRelative != bListenerRelative)
)
{
HRESULT hr;
DS3DBUFFER ds3dbuf;
ZAssertIsUnitVector(vectOrientation);
if ((fMinimumDistance <= 0)
|| (fInnerAngle < 0 || fInnerAngle > 360)
|| (fOuterAngle < 0 || fOuterAngle > 360)
|| (fOutsideGain < -100 || fOutsideGain > 0)
)
{
ZAssert(false);
return E_INVALIDARG;
}
m_vectPosition = vectPosition;
m_vectVelocity = vectVelocity;
m_vectOrientation = vectOrientation;
m_fMinimumDistance = fMinimumDistance;
m_fInnerAngle = fInnerAngle;
m_fOuterAngle = fOuterAngle;
m_fOutsideGain = fOutsideGain;
m_b3D = b3D;
m_bListenerRelative = bListenerRelative;
ds3dbuf.dwSize = sizeof(ds3dbuf);
ConvertVector(ds3dbuf.vPosition, m_vectPosition);
ConvertVector(ds3dbuf.vVelocity, m_vectVelocity);
ds3dbuf.dwInsideConeAngle = (LONG)m_fInnerAngle;
ds3dbuf.dwOutsideConeAngle = (LONG)m_fOuterAngle;
ConvertVector(ds3dbuf.vConeOrientation, m_vectOrientation);
ds3dbuf.lConeOutsideVolume = DSoundVolume(m_fOutsideGain);
ds3dbuf.flMaxDistance = m_fMinimumDistance * c_fMinToMaxDistanceRatio;
ds3dbuf.flMinDistance = m_fMinimumDistance;
ds3dbuf.dwMode = m_b3D
? (m_bListenerRelative ? DS3DMODE_HEADRELATIVE : DS3DMODE_NORMAL)
: DS3DMODE_DISABLE;
hr = m_pdirectsound3Dbuffer->SetAllParameters(
&ds3dbuf, bCanDefer ? DS3D_DEFERRED : DS3D_IMMEDIATE);
if (ZFailed(hr)) return hr;
}
return S_OK;
};
HRESULT DS3DSoundBuffer::GetStatus(bool& bPlaying, bool& bBufferLost)
{
HRESULT hr;
DWORD dwStatus;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->GetStatus(&dwStatus);
else
hr = m_pdirectsoundbuffer->GetStatus(&dwStatus);
bPlaying = (dwStatus & DSBSTATUS_PLAYING) != 0;
bBufferLost = (dwStatus & DSBSTATUS_BUFFERLOST) != 0;
return hr;
};
#ifdef _DEBUG
ZString DS3DSoundBuffer::DebugDump(const ZString& strIndent)
{
DWORD dwStatus;
if(m_pdirectsoundbuffer8)
m_pdirectsoundbuffer8->GetStatus(&dwStatus);
else
m_pdirectsoundbuffer->GetStatus(&dwStatus);
return strIndent + "DS3DSoundBuffer: "
+ (m_b3D ? " 3D" : " 2D")
+ ((dwStatus & DSBSTATUS_PLAYING) ? " playing" : "")
+ ((dwStatus & DSBSTATUS_LOOPING) ? " looping" : "")
#if DIRECTSOUND_VERSION >= 0x0700
+ ((dwStatus & DSBSTATUS_LOCHARDWARE) ? " in hardware" : "")
+ ((dwStatus & DSBSTATUS_LOCSOFTWARE) ? " in software" : "")
#endif
+ (m_b3D ? ("\n" + strIndent + " Pos("
+ m_vectPosition.X() + ", " + m_vectPosition.Y() + ", "
+ m_vectPosition.Z() + ")")
+ (m_bListenerRelative ? " listener rel" : "")
: ZString(""))
+ "\n";
}
#endif
DS3DStaticSoundBuffer::BufferCache DS3DStaticSoundBuffer::bufferCache;
HRESULT DS3DStaticSoundBuffer::RestoreBuffer()
{
HRESULT hr;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Restore();
else
hr = m_pdirectsoundbuffer->Restore();
if (ZFailed(hr)) return hr;
hr = LoadData();
if (ZFailed(hr)) return hr;
return S_OK;
};
HRESULT DS3DStaticSoundBuffer::LoadData()
{
void *pvBlock1Data, *pvBlock2Data;
DWORD dwBlock1Length, dwBlock2Length;
HRESULT hr;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Lock(
0, m_pdata->GetSize(),
&pvBlock1Data, &dwBlock1Length,
&pvBlock2Data, &dwBlock2Length,
0
);
else
hr = m_pdirectsoundbuffer->Lock(
0, m_pdata->GetSize(),
&pvBlock1Data, &dwBlock1Length,
&pvBlock2Data, &dwBlock2Length,
0
);
if (hr == DSERR_BUFFERLOST)
{
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Restore();
else
hr = m_pdirectsoundbuffer->Restore();
if (ZFailed(hr)) return hr;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Lock(
0, m_pdata->GetSize(),
&pvBlock1Data, &dwBlock1Length,
&pvBlock2Data, &dwBlock2Length,
0
);
else
hr = m_pdirectsoundbuffer->Lock(
0, m_pdata->GetSize(),
&pvBlock1Data, &dwBlock1Length,
&pvBlock2Data, &dwBlock2Length,
0
);
}
if (ZFailed(hr)) return hr;
m_pdata->GetData(pvBlock1Data, 0, dwBlock1Length);
if (pvBlock2Data)
{
m_pdata->GetData(pvBlock2Data, dwBlock1Length, dwBlock2Length);
}
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Unlock(
pvBlock1Data, dwBlock1Length,
pvBlock2Data, dwBlock2Length
);
else
hr = m_pdirectsoundbuffer->Unlock(
pvBlock1Data, dwBlock1Length,
pvBlock2Data, dwBlock2Length
);
if (ZFailed(hr)) return hr;
return S_OK;
}
DS3DStaticSoundBuffer::DS3DStaticSoundBuffer() :
m_bHasBeenPlayed(false)
{
m_iterSelf = bufferCache.end();
}
DS3DStaticSoundBuffer::~DS3DStaticSoundBuffer()
{
if (m_iterSelf != bufferCache.end())
{
bufferCache.erase(m_iterSelf);
}
}
HRESULT DS3DStaticSoundBuffer::Init8(IDirectSound8* pDirectSound, ISoundPCMData* pdata,
bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware
)
{
HRESULT hr;
m_bLooping = bLooping;
m_pdata = pdata;
CacheKey cacheKey(pdata, bSupport3D, quality, bAllowHardware);
BufferCache::iterator iterCache = bufferCache.find(cacheKey);
if ((iterCache != bufferCache.end()) && false) {
hr = DuplicateBuffer8(pDirectSound, (*iterCache).second);
if (FAILED(hr)) return hr;
}
else
{
hr = CreateBuffer8(pDirectSound, pdata, pdata->GetSize(), true, bSupport3D, quality, bAllowHardware);
if (FAILED(hr)) return hr;
hr = LoadData();
if (ZFailed(hr)) return hr;
}
m_iterSelf = bufferCache.insert(BufferCache::value_type(cacheKey, this));
return S_OK;
}
HRESULT DS3DStaticSoundBuffer::Init(IDirectSound* pDirectSound, ISoundPCMData* pdata,
bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware
)
{
HRESULT hr;
m_bLooping = bLooping;
m_pdata = pdata;
CacheKey cacheKey(pdata, bSupport3D, quality, bAllowHardware);
BufferCache::iterator iterCache = bufferCache.find(cacheKey);
if (iterCache != bufferCache.end())
{
hr = DuplicateBuffer(pDirectSound, (*iterCache).second);
if (FAILED(hr)) return hr;
}
else
{
hr = CreateBuffer(pDirectSound, pdata, pdata->GetSize(), true, bSupport3D, quality, bAllowHardware);
if (FAILED(hr)) return hr;
hr = LoadData();
if (ZFailed(hr)) return hr;
}
m_iterSelf = bufferCache.insert(BufferCache::value_type(cacheKey, this));
return S_OK;
}
HRESULT DS3DStaticSoundBuffer::Start(DWORD dwPosition, bool bIsStopping)
{
HRESULT hr;
ZAssert(dwPosition <= m_pdata->GetSize());
if (m_bHasBeenPlayed || dwPosition != 0)
{
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->SetCurrentPosition(dwPosition);
else
hr = m_pdirectsoundbuffer->SetCurrentPosition(dwPosition);
if (ZFailed(hr)) return hr;
}
hr = StartImpl(m_bLooping);
if (FAILED(hr)) return hr;
m_bHasBeenPlayed = true;
return S_OK;
};
HRESULT DS3DStaticSoundBuffer::Stop(bool bForceNow)
{
if(m_pdirectsoundbuffer8)
return m_pdirectsoundbuffer8->Stop();
else
return m_pdirectsoundbuffer->Stop();
};
HRESULT DS3DStaticSoundBuffer::GetPosition(DWORD& dwPosition)
{
if(m_pdirectsoundbuffer8)
return m_pdirectsoundbuffer8->GetCurrentPosition(&dwPosition, NULL);
else
return m_pdirectsoundbuffer->GetCurrentPosition(&dwPosition, NULL);
};
inline bool DS3DStreamingSoundBuffer::CrossedBoundary(DWORD dwTrigger, DWORD dwStart, DWORD dwEnd)
{
return dwTrigger < m_dwBufferSize
&& ((dwStart <= dwTrigger && (dwEnd >= dwTrigger || dwEnd < dwStart))
|| (dwStart > dwTrigger && (dwEnd < dwStart && dwEnd >= dwTrigger)));
}
void DS3DStreamingSoundBuffer::ReadPointerUpdate(DWORD dwReadOffset)
{
if (CrossedBoundary(m_dwStopOffset, m_dwLastReadOffset, dwReadOffset))
{
m_bPlayingSilence = true;
}
m_dwLastReadOffset = dwReadOffset;
}
DWORD DS3DStreamingSoundBuffer::GetPlayedSourceOffset(DWORD dwLastPlayedPosition)
{
if (m_bPlayingSilence)
{
return m_pdata->GetSize();
}
else
{
DWORD dwWriteOffset = (m_dwStopOffset < m_dwBufferSize)
? m_dwStopOffset : m_dwWriteOffset;
if (dwLastPlayedPosition > dwWriteOffset)
{
int nOffset = (int)(m_dwSourceOffset - m_dwBufferSize - dwWriteOffset
+ dwLastPlayedPosition);
if (nOffset < 0)
return nOffset + m_dwBufferSize;
else
return nOffset;
}
else
{
return m_dwSourceOffset - dwWriteOffset + dwLastPlayedPosition;
}
}
};
HRESULT DS3DStreamingSoundBuffer::RestoreBuffer()
{
HRESULT hr;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Restore();
else
hr = m_pdirectsoundbuffer->Restore();
if (ZFailed(hr)) return hr;
DWORD dwLastPlayedPosition;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->GetCurrentPosition(&dwLastPlayedPosition, NULL);
else
hr = m_pdirectsoundbuffer->GetCurrentPosition(&dwLastPlayedPosition, NULL);
if (ZFailed(hr)) return hr;
m_dwWriteOffset = dwLastPlayedPosition;
ReadPointerUpdate(dwLastPlayedPosition);
m_dwSourceOffset = GetPlayedSourceOffset(dwLastPlayedPosition);
hr = UpdateBufferContents();
if (ZFailed(hr)) return hr;
return S_OK;
};
HRESULT DS3DStreamingSoundBuffer::UpdateBufferContents(bool bTrustWritePtr)
{
HRESULT hr;
DWORD dwReadOffset, dwMinWriteOffset;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->GetCurrentPosition(&dwReadOffset, &dwMinWriteOffset);
else
hr = m_pdirectsoundbuffer->GetCurrentPosition(&dwReadOffset, &dwMinWriteOffset);
if (ZFailed(hr)) return hr;
if ((((dwReadOffset < m_dwWriteOffset)
&& (dwMinWriteOffset < dwReadOffset || dwMinWriteOffset > m_dwWriteOffset))
|| ((dwReadOffset >= m_dwWriteOffset)
&& (dwMinWriteOffset < dwReadOffset && dwMinWriteOffset > m_dwWriteOffset))
)
&& !bTrustWritePtr
)
{
debugf("sound buffer underflow; read %d, write %d, min write %d\n",
dwReadOffset, m_dwWriteOffset, dwMinWriteOffset);
m_dwWriteOffset = dwMinWriteOffset;
}
ReadPointerUpdate(dwReadOffset);
DWORD dwLength = ((dwReadOffset > m_dwWriteOffset)
? (dwReadOffset - m_dwWriteOffset)
: (m_dwBufferSize + dwReadOffset - m_dwWriteOffset));
void *pvBlock1Data, *pvBlock2Data;
DWORD dwBlock1Length, dwBlock2Length;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Lock(
m_dwWriteOffset, dwLength,
&pvBlock1Data, &dwBlock1Length,
&pvBlock2Data, &dwBlock2Length,
0
);
else
hr = m_pdirectsoundbuffer->Lock(
m_dwWriteOffset, dwLength,
&pvBlock1Data, &dwBlock1Length,
&pvBlock2Data, &dwBlock2Length,
0
);
if (hr == DSERR_BUFFERLOST)
{
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Restore();
else
hr = m_pdirectsoundbuffer->Restore();
if (ZFailed(hr)) return hr;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Lock(
m_dwWriteOffset, dwLength,
&pvBlock1Data, &dwBlock1Length,
&pvBlock2Data, &dwBlock2Length,
0
);
else
hr = m_pdirectsoundbuffer->Lock(
m_dwWriteOffset, dwLength,
&pvBlock1Data, &dwBlock1Length,
&pvBlock2Data, &dwBlock2Length,
0
);
}
if (ZFailed(hr)) return hr;
hr = StreamData(pvBlock1Data, dwBlock1Length);
if (ZFailed(hr)) return hr;
if (pvBlock2Data)
{
hr = StreamData(pvBlock2Data, dwBlock2Length);
if (ZFailed(hr)) return hr;
}
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Unlock(
pvBlock1Data, dwBlock1Length,
pvBlock2Data, dwBlock2Length
);
else
hr = m_pdirectsoundbuffer->Unlock(
pvBlock1Data, dwBlock1Length,
pvBlock2Data, dwBlock2Length
);
if (ZFailed(hr)) return hr;
return S_OK;
}
HRESULT DS3DStreamingSoundBuffer::StreamData(void *pvBuffer, DWORD dwLength)
{
ZAssert(pvBuffer && dwLength != 0);
DWORD dwLengthRemaining = dwLength;
BYTE* pcWritePtr = (BYTE*)pvBuffer;
DWORD dwSourceSize = m_pdata->GetSize();
do {
DWORD dwCopyLength = min(dwLengthRemaining, dwSourceSize - m_dwSourceOffset);
if (dwCopyLength > 0)
m_pdata->GetData(pcWritePtr, m_dwSourceOffset, dwCopyLength);
pcWritePtr += dwCopyLength;
m_dwSourceOffset += dwCopyLength;
dwLengthRemaining -= dwCopyLength;
if (m_dwSourceOffset == dwSourceSize && m_bLooping)
m_dwSourceOffset = 0;
} while (dwLengthRemaining > 0 && m_bLooping);
if (dwLengthRemaining != 0)
{
BYTE nFillValue = (m_pdata->GetBitsPerSample() == 8) ? 0x80 : 0;
memset(pcWritePtr, nFillValue, dwLengthRemaining);
if (m_dwStopOffset >= m_dwBufferSize)
{
m_dwStopOffset = m_dwWriteOffset + dwLength - dwLengthRemaining;
}
}
m_dwWriteOffset = (m_dwWriteOffset + dwLength) % m_dwBufferSize;
return S_OK;
}
HRESULT DS3DStreamingSoundBuffer::Init(IDirectSound* pDirectSound, ISoundPCMData* pdata,
bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware,
float fBufferLength
)
{
HRESULT hr;
m_bLooping = bLooping;
m_pdata = pdata;
m_dwBufferSize = (DWORD)(fBufferLength * pdata->GetBytesPerSec());
hr = CreateBuffer(pDirectSound, pdata, m_dwBufferSize, false, bSupport3D, quality, bAllowHardware);
if (FAILED(hr)) return hr;
return S_OK;
}
HRESULT DS3DStreamingSoundBuffer::Init8(IDirectSound8* pDirectSound, ISoundPCMData* pdata,
bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware,
float fBufferLength
)
{
HRESULT hr;
m_bLooping = bLooping;
m_pdata = pdata;
m_dwBufferSize = (DWORD)(fBufferLength * pdata->GetBytesPerSec());
hr = CreateBuffer8(pDirectSound, pdata, m_dwBufferSize, false, bSupport3D, quality, bAllowHardware);
if (FAILED(hr)) return hr;
return S_OK;
}
bool DS3DStreamingSoundBuffer::Execute()
{
HRESULT hr = UpdateBufferContents();
ZAssert(SUCCEEDED(hr));
if (m_bPlayingSilence)
{
HRESULT hr;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->Stop();
else
hr = m_pdirectsoundbuffer->Stop();
ZAssert(SUCCEEDED(hr));
return false;
}
else
{
return true;
}
}
DS3DStreamingSoundBuffer::DS3DStreamingSoundBuffer() :
m_bHasBeenPlayed(false)
{
}
DS3DStreamingSoundBuffer::~DS3DStreamingSoundBuffer()
{
if (m_bHasBeenPlayed)
{
m_threadUpdate.RemoveTask(this);
}
}
HRESULT DS3DStreamingSoundBuffer::Init8(IDirectSound8* pDirectSound, ISoundPCMData* pdata,
bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware
)
{
return Init8(pDirectSound, pdata, bLooping, bSupport3D, quality, bAllowHardware, 1.0f); }
HRESULT DS3DStreamingSoundBuffer::Init(IDirectSound* pDirectSound, ISoundPCMData* pdata,
bool bLooping, bool bSupport3D, ISoundEngine::Quality quality, bool bAllowHardware
)
{
return Init(pDirectSound, pdata, bLooping, bSupport3D, quality, bAllowHardware, 1.0f);
}
HRESULT DS3DStreamingSoundBuffer::Start(DWORD dwPosition, bool bIsStopping)
{
HRESULT hr;
ZAssert(!m_threadUpdate.HasTask(this));
if (m_bHasBeenPlayed)
{
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->SetCurrentPosition(0);
else
hr = m_pdirectsoundbuffer->SetCurrentPosition(0);
if (ZFailed(hr)) return hr;
}
ZAssert(dwPosition <= m_pdata->GetSize());
m_dwSourceOffset = dwPosition;
m_dwWriteOffset = 0;
m_dwLastReadOffset = 0;
m_dwStopOffset = m_dwBufferSize;
m_bPlayingSilence = false;
UpdateBufferContents();
hr = StartImpl(true);
if (FAILED(hr)) return hr;
m_bHasBeenPlayed = true;
m_threadUpdate.AddTask(this);
return S_OK;
};
HRESULT DS3DStreamingSoundBuffer::Stop(bool bForceNow)
{
m_threadUpdate.RemoveTask(this);
if(m_pdirectsoundbuffer8)
return m_pdirectsoundbuffer8->Stop();
else
return m_pdirectsoundbuffer->Stop();
};
HRESULT DS3DStreamingSoundBuffer::GetPosition(DWORD& dwPosition)
{
DWORD dwBufferPosition;
HRESULT hr;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->GetCurrentPosition(&dwBufferPosition, NULL);
else
hr = m_pdirectsoundbuffer->GetCurrentPosition(&dwBufferPosition, NULL);
if (ZFailed(hr)) return hr;
ReadPointerUpdate(dwBufferPosition);
dwPosition = GetPlayedSourceOffset(dwBufferPosition);
return S_OK;
};
HRESULT DS3DStreamingSoundBuffer::GetStatus(bool& bPlaying, bool& bBufferLost)
{
HRESULT hr;
DWORD dwStatus;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->GetStatus(&dwStatus);
else
hr = m_pdirectsoundbuffer->GetStatus(&dwStatus);
if (ZFailed(hr)) return hr;
bPlaying = (dwStatus & DSBSTATUS_PLAYING) != 0;
bBufferLost = (dwStatus & DSBSTATUS_BUFFERLOST) != 0;
return S_OK;
};
TaskListThread DS3DStreamingSoundBuffer::m_threadUpdate(THREAD_PRIORITY_TIME_CRITICAL, 250); DS3DASRSoundBuffer::~DS3DASRSoundBuffer()
{
if (m_bHasBeenPlayed)
{
m_threadUpdate.RemoveTask(this);
m_bHasBeenPlayed = false;
}
}
void DS3DASRSoundBuffer::ReadPointerUpdate(DWORD dwReadOffset)
{
if (CrossedBoundary(m_dwSustainBufferOffset, m_dwLastReadOffset, dwReadOffset))
{
m_bPlayingSustain = true;
}
if (CrossedBoundary(m_dwReleaseBufferOffset, m_dwLastReadOffset, dwReadOffset))
{
ZAssert(m_bEnding);
m_bPlayingRelease = true;
}
DS3DStreamingSoundBuffer::ReadPointerUpdate(dwReadOffset);
}
DWORD DS3DASRSoundBuffer::GetPlayedSourceOffset(DWORD dwLastPlayedPosition)
{
if (m_bPlayingSilence)
{
return m_pdata->GetSize();
}
else
{
DWORD dwReferenceOffset;
DWORD dwSourceOffset;
if (!m_bPlayingSustain)
{
if (m_dwSustainBufferOffset < m_dwBufferSize)
{
dwReferenceOffset = m_dwSustainBufferOffset;
dwSourceOffset = m_dwLoopOffset;
}
else
{
dwReferenceOffset = m_dwWriteOffset;
dwSourceOffset = m_dwSourceOffset;
}
}
else if (!m_bPlayingRelease)
{
if (m_dwReleaseBufferOffset < m_dwBufferSize)
{
dwReferenceOffset = m_dwReleaseBufferOffset;
dwSourceOffset = m_dwLoopOffset + m_dwLoopLength;
}
else
{
dwReferenceOffset = m_dwWriteOffset;
dwSourceOffset = m_dwSourceOffset;
}
}
else
{
if (m_dwStopOffset < m_dwBufferSize)
{
dwReferenceOffset = m_dwStopOffset;
dwSourceOffset = m_pdata->GetSize();
}
else
{
dwReferenceOffset = m_dwWriteOffset;
dwSourceOffset = m_dwSourceOffset;
}
}
int nDelta = int(dwReferenceOffset) - int(dwLastPlayedPosition);
if (nDelta <= 0)
{
nDelta += m_dwBufferSize;
if (nDelta < 0)
{
ZAssert(false);
nDelta = 0;
}
}
if (m_bPlayingSustain && !m_bPlayingRelease)
{
int nOffset = dwSourceOffset - (nDelta % m_dwLoopLength);
if (nOffset < m_dwLoopOffset)
nOffset += m_dwLoopLength;
return nOffset;
}
else
{
ZAssert(int(dwSourceOffset) - nDelta >= 0);
return int(dwSourceOffset) - nDelta;
}
}
};
HRESULT DS3DASRSoundBuffer::StreamData(void *pvBuffer, DWORD dwLength)
{
ZAssert(pvBuffer && dwLength != 0);
DWORD dwSourceSize = m_pdata->GetSize();
DWORD dwLengthRemaining = dwLength;
BYTE* pcWritePtr = (BYTE*)pvBuffer;
DWORD dwCopyLength;
if (m_dwSourceOffset < m_dwLoopOffset)
{
dwCopyLength = min(dwLengthRemaining, m_dwLoopOffset - m_dwSourceOffset);
if (dwCopyLength > 0)
{
m_pdata->GetData(pcWritePtr, m_dwSourceOffset, dwCopyLength);
}
pcWritePtr += dwCopyLength;
m_dwSourceOffset += dwCopyLength;
dwLengthRemaining -= dwCopyLength;
if (m_dwSourceOffset >= m_dwLoopOffset)
{
ZAssert(m_dwSourceOffset == m_dwLoopOffset);
m_dwSustainBufferOffset =
m_dwWriteOffset + dwLength - dwLengthRemaining;
}
}
while (dwLengthRemaining > 0 && (
(m_dwSourceOffset == m_dwLoopOffset && !m_bEnding)
|| (m_dwSourceOffset > m_dwLoopOffset
&& (m_dwSourceOffset < m_dwLoopOffset + m_dwLoopLength))))
{
dwCopyLength = min(dwLengthRemaining,
m_dwLoopOffset + m_dwLoopLength - m_dwSourceOffset);
ZAssert(dwCopyLength > 0);
m_pdata->GetData(pcWritePtr, m_dwSourceOffset, dwCopyLength);
pcWritePtr += dwCopyLength;
m_dwSourceOffset += dwCopyLength;
dwLengthRemaining -= dwCopyLength;
if (m_dwSourceOffset >= m_dwLoopOffset + m_dwLoopLength)
{
ZAssert(m_dwSourceOffset == m_dwLoopOffset + m_dwLoopLength);
m_dwSourceOffset = m_dwLoopOffset;
if (m_bEnding)
{
m_dwReleaseBufferOffset =
m_dwWriteOffset + dwLength - dwLengthRemaining;
}
}
}
if (dwLengthRemaining > 0 && m_dwSourceOffset < dwSourceSize)
{
ZAssert(m_bEnding);
ZAssert(m_dwSourceOffset == m_dwLoopOffset
|| (m_dwSourceOffset >= m_dwLoopOffset + m_dwLoopLength));
if (m_dwSourceOffset < m_dwLoopOffset + m_dwLoopLength)
m_dwSourceOffset = m_dwLoopOffset + m_dwLoopLength;
dwCopyLength = min(dwLengthRemaining, dwSourceSize - m_dwSourceOffset);
if (dwCopyLength > 0)
{
m_pdata->GetData(pcWritePtr, m_dwSourceOffset, dwCopyLength);
}
pcWritePtr += dwCopyLength;
m_dwSourceOffset += dwCopyLength;
dwLengthRemaining -= dwCopyLength;
if (m_dwSourceOffset >= dwSourceSize)
{
ZAssert(m_dwSourceOffset == dwSourceSize);
m_dwStopOffset = m_dwWriteOffset + dwLength - dwLengthRemaining;
}
}
if (dwLengthRemaining != 0)
{
BYTE nFillValue = (m_pdata->GetBitsPerSample() == 8) ? 0x80 : 0;
memset(pcWritePtr, nFillValue, dwLengthRemaining);
}
m_dwWriteOffset = (m_dwWriteOffset + dwLength) % m_dwBufferSize;
return S_OK;
}
HRESULT DS3DASRSoundBuffer::Init(IDirectSound* pDirectSound, ISoundPCMData* pdata,
DWORD dwLoopOffset, DWORD dwLoopLength, bool bSupport3D,
ISoundEngine::Quality quality, bool bAllowHardware
)
{
ZAssert(dwLoopOffset + dwLoopLength < pdata->GetSize());
ZAssert(dwLoopLength > 0);
HRESULT hr;
m_pdata = pdata;
m_dwLoopOffset = dwLoopOffset;
m_dwLoopLength = dwLoopLength;
hr = DS3DStreamingSoundBuffer::Init(
pDirectSound, pdata, false, bSupport3D, quality, bAllowHardware, 1.0f); if (ZFailed(hr)) return hr;
return S_OK;
}
HRESULT DS3DASRSoundBuffer::Init8(IDirectSound8* pDirectSound, ISoundPCMData* pdata,
DWORD dwLoopOffset, DWORD dwLoopLength, bool bSupport3D,
ISoundEngine::Quality quality, bool bAllowHardware
)
{
ZAssert(dwLoopOffset + dwLoopLength < pdata->GetSize());
ZAssert(dwLoopLength > 0);
HRESULT hr;
m_pdata = pdata;
m_dwLoopOffset = dwLoopOffset;
m_dwLoopLength = dwLoopLength;
hr = DS3DStreamingSoundBuffer::Init8(
pDirectSound, pdata, false, bSupport3D, quality, bAllowHardware, 1.0f); if (ZFailed(hr)) return hr;
return S_OK;
}
HRESULT DS3DASRSoundBuffer::Start(DWORD dwPosition, bool bIsStopping)
{
ZAssert(bIsStopping || dwPosition < m_dwLoopOffset + m_dwLoopLength);
DWORD dwSourceLength = m_pdata->GetSize();
m_dwSustainBufferOffset = m_dwBufferSize;
m_bPlayingSustain = dwPosition >= m_dwLoopOffset;
m_dwReleaseBufferOffset = m_dwBufferSize;
m_bPlayingRelease = dwPosition >= m_dwLoopOffset + m_dwLoopLength;
ZAssert(m_bPlayingRelease ? bIsStopping : true);
m_bEnding = bIsStopping;
DS3DStreamingSoundBuffer::Start(dwPosition, bIsStopping);
return S_OK;
};
HRESULT DS3DASRSoundBuffer::Stop(bool bForceNow)
{
if (bForceNow)
{
return DS3DStreamingSoundBuffer::Stop(bForceNow);
}
else if (!m_bEnding)
{
m_threadUpdate.RemoveTask(this);
HRESULT hr;
DWORD dwWritePosition;
if(m_pdirectsoundbuffer8)
hr = m_pdirectsoundbuffer8->GetCurrentPosition(NULL, &dwWritePosition);
else
hr = m_pdirectsoundbuffer->GetCurrentPosition(NULL, &dwWritePosition);
if (ZFailed(hr)) return hr;
int nWriteDelta = dwWritePosition - m_dwLastReadOffset;
if (nWriteDelta < 0)
{
nWriteDelta += m_dwBufferSize;
}
ZAssert(nWriteDelta >= 0);
DWORD dwLastPlayedSourceOffset = GetPlayedSourceOffset(m_dwLastReadOffset);
if (dwLastPlayedSourceOffset + nWriteDelta > m_dwLoopOffset)
{
m_dwSourceOffset =
(dwLastPlayedSourceOffset + nWriteDelta - m_dwLoopOffset)
% m_dwLoopLength + m_dwLoopOffset;
}
else
{
m_dwSourceOffset = dwLastPlayedSourceOffset + nWriteDelta;
}
m_dwWriteOffset = dwWritePosition;
m_bEnding = true;
hr = UpdateBufferContents(true);
if (ZFailed(hr)) return hr;
m_threadUpdate.AddTask(this);
}
return S_OK;
};
};