#include "pch.h"
#include "soundbase.h"
#include "ds3dutil.h"
#include "ds3dbuffer.h"
#include "ds3dvirtualbuffer.h"
namespace SoundEngine {
BufferPositionTracker::BufferPositionTracker(ISoundPCMData* pdata, bool bLooping) :
m_dwBytesPerSec(pdata->GetBytesPerSec()),
m_dwLength(pdata->GetSize()),
m_dwPositionMask(~(pdata->GetBytesPerSample() - 1)),
m_dwPosition(0),
m_bLooping(bLooping),
m_bEnding(false),
m_bASR(false)
{
};
BufferPositionTracker::BufferPositionTracker(ISoundPCMData* pdata,
DWORD dwLoopStart, DWORD dwLoopLength) :
m_dwBytesPerSec(pdata->GetBytesPerSec()),
m_dwLength(pdata->GetSize()),
m_dwPositionMask(~(pdata->GetBytesPerSample() - 1)),
m_dwPosition(0),
m_bASR(true),
m_bEnding(false),
m_dwLoopStart(dwLoopStart),
m_dwLoopLength(dwLoopLength)
{
ZAssert(m_dwLoopStart + m_dwLoopLength <= m_dwLength);
};
void BufferPositionTracker::Stop(bool bForceNow)
{
if (!m_bASR || bForceNow)
{
m_dwPosition = m_dwLoopLength;
}
m_bEnding = true;
};
void BufferPositionTracker::AddTime(DWORD dwElapsedTime)
{
DWORD dwElapsedBytes =
(DWORD)(dwElapsedTime * (__int64)m_dwBytesPerSec / 1000);
if (!m_bASR)
{
m_dwPosition += dwElapsedBytes;
if (m_dwPosition > m_dwLength)
{
if (m_bLooping && !m_bEnding)
{
m_dwPosition %= m_dwLength;
}
else
{
m_dwPosition = m_dwLength;
}
}
}
else {
if (m_bEnding)
{
bool bWasInAttack = m_dwPosition < m_dwLoopStart;
m_dwPosition += dwElapsedBytes;
if (bWasInAttack && (m_dwPosition > m_dwLoopStart))
{
m_dwPosition += m_dwLoopLength;
}
if (m_dwPosition > m_dwLength)
{
m_dwPosition = m_dwLength;
}
}
else
{
m_dwPosition += dwElapsedBytes;
if (m_dwPosition >= m_dwLoopStart + m_dwLoopLength)
{
m_dwPosition =
((m_dwPosition - m_dwLoopStart) % m_dwLoopLength)
+ m_dwLoopStart;
}
}
}
};
void BufferPositionTracker::SetPosition(DWORD dwPosition)
{
m_dwPosition = min(dwPosition, m_dwLength);
};
HRESULT DSVirtualSoundBuffer::PrepareBuffer8(IDirectSound8* pDirectSound,
ISoundEngine::Quality quality, bool bAllowHardware, bool bSupport3D)
{
HRESULT hr;
if (m_pds3dbuffer == NULL)
{
if (m_bufferPositionTracker.IsASR())
{
DS3DASRSoundBuffer* pds3dASRBuffer = new DS3DASRSoundBuffer();
hr = pds3dASRBuffer->Init8(pDirectSound, m_pdata,
m_bufferPositionTracker.GetLoopOffset(),
m_bufferPositionTracker.GetLoopLength(),
bSupport3D, quality, bAllowHardware);
if (ZFailed(hr)) return hr;
m_pds3dbuffer = pds3dASRBuffer;
}
else if (m_pdata->GetSize() > 1000000)
{
DS3DStreamingSoundBuffer* pds3DStreamingBuffer = new DS3DStreamingSoundBuffer();
hr = pds3DStreamingBuffer->Init8(pDirectSound, m_pdata,
m_bufferPositionTracker.IsLooping(), bSupport3D, quality, bAllowHardware);
if (ZFailed(hr)) return hr;
m_pds3dbuffer = pds3DStreamingBuffer;
}
else
{
DS3DStaticSoundBuffer* pds3DStaticBuffer = new DS3DStaticSoundBuffer();
hr = pds3DStaticBuffer->Init8(pDirectSound, m_pdata,
m_bufferPositionTracker.IsLooping(), bSupport3D, quality, bAllowHardware);
if (FAILED(hr)) return hr;
m_pds3dbuffer = pds3DStaticBuffer;
}
}
m_quality = quality;
m_bAllowHardware = bAllowHardware;
hr = m_pds3dbuffer->UpdateState(m_fGain, m_fPitch, false);
if (ZFailed(hr)) return hr;
return S_OK;
}
HRESULT DSVirtualSoundBuffer::PrepareBuffer(IDirectSound* pDirectSound,
ISoundEngine::Quality quality, bool bAllowHardware, bool bSupport3D)
{
HRESULT hr;
if (m_pds3dbuffer == NULL)
{
if (m_bufferPositionTracker.IsASR())
{
DS3DASRSoundBuffer* pds3dASRBuffer = new DS3DASRSoundBuffer();
hr = pds3dASRBuffer->Init(pDirectSound, m_pdata,
m_bufferPositionTracker.GetLoopOffset(),
m_bufferPositionTracker.GetLoopLength(),
bSupport3D, quality, bAllowHardware);
if (ZFailed(hr)) return hr;
m_pds3dbuffer = pds3dASRBuffer;
}
else if (m_pdata->GetSize() > 1000000)
{
DS3DStreamingSoundBuffer* pds3DStreamingBuffer = new DS3DStreamingSoundBuffer();
hr = pds3DStreamingBuffer->Init(pDirectSound, m_pdata,
m_bufferPositionTracker.IsLooping(), bSupport3D, quality, bAllowHardware);
if (ZFailed(hr)) return hr;
m_pds3dbuffer = pds3DStreamingBuffer;
}
else
{
DS3DStaticSoundBuffer* pds3DStaticBuffer = new DS3DStaticSoundBuffer();
hr = pds3DStaticBuffer->Init(pDirectSound, m_pdata,
m_bufferPositionTracker.IsLooping(), bSupport3D, quality, bAllowHardware);
if (FAILED(hr)) return hr;
m_pds3dbuffer = pds3DStaticBuffer;
}
}
m_quality = quality;
m_bAllowHardware = bAllowHardware;
hr = m_pds3dbuffer->UpdateState(m_fGain, m_fPitch, false);
if (ZFailed(hr)) return hr;
return S_OK;
}
DSVirtualSoundBuffer::DSVirtualSoundBuffer(ISoundPCMData* pdata, bool bLooping) :
m_bufferPositionTracker(pdata, bLooping),
m_pdata(pdata),
m_bStarted(false),
m_bStopped(false),
m_bRetryStop(false),
m_bBufferPlaying(false),
m_bAudible(false),
m_fAge(0),
m_fPriority(1.0f),
m_fGain(0.0f),
m_fPitch(1.0f),
m_fDynamicPriority(0.0f)
{
};
DSVirtualSoundBuffer::DSVirtualSoundBuffer(ISoundPCMData* pdata, DWORD dwLoopOffset, DWORD dwLoopLength) :
m_bufferPositionTracker(pdata, dwLoopOffset, dwLoopLength),
m_pdata(pdata),
m_bStarted(false),
m_bStopped(false),
m_bRetryStop(false),
m_bBufferPlaying(false),
m_bAudible(false),
m_fAge(0),
m_fPriority(1.0f),
m_fGain(0.0f),
m_fPitch(1.0f),
m_fDynamicPriority(0.0f)
{
};
HRESULT DSVirtualSoundBuffer::Update(DWORD dwTimeElapsed,
const Vector& vectListenerPosition, float fRolloffFactor)
{
HRESULT hr;
bool bWasStopped = m_bStopped;
if (m_bRetryStop && m_bBufferPlaying)
m_pds3dbuffer->Stop(true);
if (!m_bStarted)
{
m_bStarted = true;
}
else
{
m_bufferPositionTracker.AddTime((DWORD)(dwTimeElapsed * m_fPitch));
m_fAge += dwTimeElapsed/1000.0f;
}
if (m_bBufferPlaying)
{
bool bPlaying, bBufferLost;
hr = m_pds3dbuffer->GetStatus(bPlaying, bBufferLost);
if (ZFailed(hr)) return hr;
if (bBufferLost)
{
m_bBufferPlaying = false;
}
else
{
m_bStopped = !bPlaying;
}
}
if (!m_bBufferPlaying)
{
m_bStopped = m_bufferPositionTracker.IsStopped();
}
else
{
hr = m_pds3dbuffer->UpdateState(m_fGain, m_fPitch);
if (ZFailed(hr)) return hr;
}
if (m_bStopped && !bWasStopped)
{
m_pds3dbuffer = NULL;
m_bBufferPlaying = FALSE;
if (m_peventsourceFinished != NULL)
{
m_peventsourceFinished->Trigger();
}
}
m_bAudible = !m_bStopped && (m_fGain > c_fMinAudible);
m_fDynamicPriority = m_fGain + m_fPriority
+ (m_bBufferPlaying ? 0.1f : 0)
+ max(0.0f, 1.0f * (2 - m_fAge));
return S_OK;
};
HRESULT DSVirtualSoundBuffer::StartBuffer8(IDirectSound8* pDirectSound,
ISoundEngine::Quality quality, bool bAllowHardware)
{
HRESULT hr;
if (m_bBufferPlaying)
{
ZAssert(false); return S_FALSE;
}
hr = PrepareBuffer8(pDirectSound, quality, bAllowHardware, false);
if (FAILED(hr)) return hr;
hr = m_pds3dbuffer->Start(m_bufferPositionTracker.GetPosition(),
m_bufferPositionTracker.IsStopping());
if (FAILED(hr)) return hr;
m_bBufferPlaying = true;
return S_OK;
};
HRESULT DSVirtualSoundBuffer::StartBuffer(IDirectSound* pDirectSound,
ISoundEngine::Quality quality, bool bAllowHardware)
{
HRESULT hr;
if (m_bBufferPlaying)
{
ZAssert(false); return S_FALSE;
}
hr = PrepareBuffer(pDirectSound, quality, bAllowHardware, false);
if (FAILED(hr)) return hr;
hr = m_pds3dbuffer->Start(m_bufferPositionTracker.GetPosition(),
m_bufferPositionTracker.IsStopping());
if (FAILED(hr)) return hr;
m_bBufferPlaying = true;
return S_OK;
};
HRESULT DSVirtualSoundBuffer::StopBuffer()
{
HRESULT hr;
if (!m_bBufferPlaying)
{
ZAssert(false); return S_FALSE;
}
hr = m_pds3dbuffer->Stop(true);
if (ZFailed(hr)) return hr;
DWORD dwPosition;
hr = m_pds3dbuffer->GetPosition(dwPosition);
if (ZFailed(hr)) return hr;
if (dwPosition != 0) m_bufferPositionTracker.SetPosition(dwPosition);
m_bBufferPlaying = false;
return S_OK;
}
HRESULT DSVirtualSoundBuffer::SetGain(float fGain)
{
if (fGain > 0)
{
ZAssert(false);
return E_INVALIDARG;
}
m_fGain = max(fGain, -100.0f);
return S_OK;
};
HRESULT DSVirtualSoundBuffer::SetPitch(float fPitch)
{
fPitch = max(fPitch, DSBFREQUENCY_MIN / m_pdata->GetSampleRate());
fPitch = min(fPitch, DSBFREQUENCY_MAX / m_pdata->GetSampleRate());
m_fPitch = fPitch;
return S_OK;
};
HRESULT DSVirtualSoundBuffer::SetPriority(float fPriority)
{
m_fPriority = fPriority;
return S_OK;
};
HRESULT DSVirtualSoundBuffer::Stop(bool bForceNow)
{
HRESULT hr;
if (m_bBufferPlaying)
{
hr = m_pds3dbuffer->Stop(bForceNow);
if (ZFailed(hr))
{
m_bRetryStop = true;
return hr;
}
}
m_bufferPositionTracker.Stop(bForceNow);
return S_OK;
};
HRESULT DSVirtualSoundBuffer::IsPlaying()
{
return m_bStopped ? S_FALSE : S_OK;
};
IEventSource* DSVirtualSoundBuffer::GetFinishEventSource()
{
if (m_peventsourceFinished == NULL)
{
m_peventsourceFinished = new EventSourceImpl();
}
return m_peventsourceFinished;
};
TRef<ISoundTweakable> DSVirtualSoundBuffer::GetISoundTweakable()
{
return this;
};
TRef<ISoundTweakable3D> DSVirtualSoundBuffer::GetISoundTweakable3D()
{
return NULL;
};
#ifdef _DEBUG
ZString DSVirtualSoundBuffer::DebugDump(const ZString& strIndent)
{
return strIndent + "DSVirtualSoundBuffer: Priority " + ZString(m_fDynamicPriority)
+ ", Age " + ZString(m_fAge)
+ ", gain " + ZString(m_fGain)
+ ", pitch " + ZString(m_fPitch) + "\n"
+ SoundDebugDump(m_pdata, strIndent + " ")
+ ((m_pds3dbuffer) ? SoundDebugDump(m_pds3dbuffer, strIndent + " ") : "");
}
#endif
DS3DVirtualSoundBuffer::DS3DVirtualSoundBuffer(ISoundPCMData* pdata, bool bLooping,
TRef<ISoundPositionSource> psource) :
DSVirtualSoundBuffer(pdata, bLooping),
m_pposSource(psource),
m_b3D(false),
m_bListenerRelative(false),
m_bPlayingIn3D(true),
m_fMinimumDistance(20.0f),
m_fInnerAngle(360),
m_fOuterAngle(360),
m_fOutsideGain(0.0f),
m_vectPosition(0,0,0),
m_vectVelocity(0,0,0),
m_vectOrientation(0,0,1)
{
};
DS3DVirtualSoundBuffer::DS3DVirtualSoundBuffer(ISoundPCMData* pdata, DWORD dwLoopOffset,
DWORD dwLoopLength, TRef<ISoundPositionSource> psource) :
DSVirtualSoundBuffer(pdata, dwLoopOffset, dwLoopLength),
m_pposSource(psource),
m_b3D(false),
m_bListenerRelative(false),
m_bPlayingIn3D(true),
m_fMinimumDistance(20.0f),
m_fInnerAngle(360),
m_fOuterAngle(360),
m_fOutsideGain(0.0f),
m_vectPosition(0,0,0),
m_vectVelocity(0,0,0),
m_vectOrientation(0,0,1)
{
};
HRESULT DS3DVirtualSoundBuffer::PrepareBuffer8(IDirectSound8* pDirectSound,
ISoundEngine::Quality quality, bool bAllowHardware, bool bSupport3D)
{
HRESULT hr;
if (quality != m_quality || m_bAllowHardware != bAllowHardware)
m_pds3dbuffer = NULL;
hr = DSVirtualSoundBuffer::PrepareBuffer8(pDirectSound, quality, bAllowHardware, true);
if (FAILED(hr)) return hr;
hr = m_pds3dbuffer->UpdateState3D(
m_vectPosition, m_vectVelocity, m_vectOrientation,
m_fMinimumDistance,
m_fInnerAngle, m_fOuterAngle, m_fOutsideGain,
m_bPlayingIn3D,
m_bListenerRelative,
false
);
if (ZFailed(hr)) return hr;
return S_OK;
};
HRESULT DS3DVirtualSoundBuffer::PrepareBuffer(IDirectSound* pDirectSound,
ISoundEngine::Quality quality, bool bAllowHardware, bool bSupport3D)
{
HRESULT hr;
if (quality != m_quality || m_bAllowHardware != bAllowHardware)
m_pds3dbuffer = NULL;
hr = DSVirtualSoundBuffer::PrepareBuffer(pDirectSound, quality, bAllowHardware, true);
if (FAILED(hr)) return hr;
hr = m_pds3dbuffer->UpdateState3D(
m_vectPosition, m_vectVelocity, m_vectOrientation,
m_fMinimumDistance,
m_fInnerAngle, m_fOuterAngle, m_fOutsideGain,
m_bPlayingIn3D,
m_bListenerRelative,
false
);
if (ZFailed(hr)) return hr;
return S_OK;
};
HRESULT DS3DVirtualSoundBuffer::Update(DWORD dwTimeElapsed,
const Vector& vectListenerPosition, float fRolloffFactor)
{
HRESULT hr;
hr = m_pposSource->IsPlaying();
if (ZFailed(hr)) return hr;
bool bSourceIsValid = hr == S_OK;
if (!bSourceIsValid)
{
Stop(true);
}
hr = DSVirtualSoundBuffer::Update(dwTimeElapsed, vectListenerPosition,
fRolloffFactor);
if (ZFailed(hr)) return hr;
if (bSourceIsValid)
{
hr = m_pposSource->GetPosition(m_vectPosition);
if (ZFailed(hr)) return hr;
hr = m_pposSource->IsListenerRelative();
if (ZFailed(hr)) return hr;
m_bListenerRelative = hr == S_OK;
m_bPlayingIn3D = m_b3D
&& m_vectPosition
!= (m_bListenerRelative ? Vector(0,0,0) : vectListenerPosition);
if (m_bPlayingIn3D)
{
hr = m_pposSource->GetVelocity(m_vectVelocity);
if (ZFailed(hr)) return hr;
Vector vectRelListener = m_bListenerRelative
? -m_vectPosition : (vectListenerPosition - m_vectPosition);
float fDistanceSquared = vectRelListener.LengthSquared();
float fMinimumDistanceSquared = m_fMinimumDistance * m_fMinimumDistance;
float fMaximumDistanceSquared = fMinimumDistanceSquared
* c_fMinToMaxDistanceRatio * c_fMinToMaxDistanceRatio;
if (fDistanceSquared > fMaximumDistanceSquared || !m_bAudible)
{
m_bAudible = false;
m_fDynamicPriority += -100;
}
else
{
float fDistanceGain = min(0.0f, (float)(
fRolloffFactor * -10 * log(fDistanceSquared/fMinimumDistanceSquared)
));
float fConeGain = 0;
if (m_fOutsideGain != 0.0f && m_fInnerAngle != 360
&& !m_bListenerRelative) {
const float fRadiansToDegrees = 57.2957795f;
hr = m_pposSource->GetOrientation(m_vectOrientation);
if (ZFailed(hr)) return hr;
ZAssertIsUnitVector(m_vectOrientation);
float fPhi = (float)acos(m_vectOrientation * vectRelListener
/ sqrt(fDistanceSquared)) * fRadiansToDegrees;
if (fPhi > m_fInnerAngle)
{
if (fPhi < m_fOuterAngle)
{
fConeGain = m_fOutsideGain
* (fPhi - m_fInnerAngle)
/ (m_fOuterAngle - m_fInnerAngle);
}
else
{
fConeGain = m_fOutsideGain;
}
}
}
float fTotalGain = m_fGain + fDistanceGain + fConeGain;
m_bAudible = fTotalGain > c_fMinAudible;
m_fDynamicPriority += fDistanceGain + fConeGain;
}
}
};
if (m_bBufferPlaying)
{
hr = m_pds3dbuffer->UpdateState3D(
m_vectPosition, m_vectVelocity, m_vectOrientation,
m_fMinimumDistance,
m_fInnerAngle, m_fOuterAngle, m_fOutsideGain,
m_bPlayingIn3D,
m_bListenerRelative
);
if (ZFailed(hr)) return hr;
}
return S_OK;
}
HRESULT DS3DVirtualSoundBuffer::Set3D(bool b3D)
{
m_b3D = b3D;
return S_OK;
};
HRESULT DS3DVirtualSoundBuffer::SetMinimumDistance(float fMinimumDistance)
{
if (fMinimumDistance <= 0)
{
ZAssert(false);
return E_INVALIDARG;
}
m_fMinimumDistance = fMinimumDistance;
return S_OK;
};
HRESULT DS3DVirtualSoundBuffer::SetCone(float fInnerAngle, float fOuterAngle, float fOutsideGain)
{
if ((fInnerAngle < 0 || fInnerAngle > 360)
|| (fOuterAngle < 0 || fOuterAngle > 360)
|| (fOuterAngle < fInnerAngle)
|| (fOutsideGain < -100 || fOutsideGain > 0)
)
{
ZAssert(false);
return E_INVALIDARG;
};
m_fInnerAngle = fInnerAngle;
m_fOuterAngle = fOuterAngle;
m_fOutsideGain = fOutsideGain;
return S_OK;
};
HRESULT DS3DVirtualSoundBuffer::SetGain(float fGain)
{
return DSVirtualSoundBuffer::SetGain(fGain);
};
HRESULT DS3DVirtualSoundBuffer::SetPitch(float fPitch)
{
return DSVirtualSoundBuffer::SetPitch(fPitch);
};
HRESULT DS3DVirtualSoundBuffer::SetPriority(float fPriority)
{
return DSVirtualSoundBuffer::SetPriority(fPriority);
};
TRef<ISoundTweakable> DS3DVirtualSoundBuffer::GetISoundTweakable()
{
return DSVirtualSoundBuffer::GetISoundTweakable();
};
TRef<ISoundTweakable3D> DS3DVirtualSoundBuffer::GetISoundTweakable3D()
{
return this;
};
};