#include "pch.h"
#include "soundbase.h"
#include "soundutil.h"
namespace SoundEngine {
class SoundFile : public ISoundPCMData SoundDebugImpl
{
private:
typedef std::map<ZString, ISoundPCMData* > SoundFileCache;
static SoundFileCache m_mapOpenFiles;
unsigned m_uChannels;
unsigned m_uBitsPerSample;
unsigned m_uSampleRate;
unsigned m_uSize;
void* m_pvData;
void* m_pvFileContents;
ZString m_strFilename;
bool m_bIsOgg;
SoundFile()
: m_pvData(NULL), m_pvFileContents(NULL), m_bIsOgg(false) {};
~SoundFile()
{
if (m_pvFileContents)
{
UnmapViewOfFile(m_pvFileContents);
}
if (m_pvData)
{
m_mapOpenFiles.erase(m_strFilename);
if(m_bIsOgg)
free(m_pvData);
}
}
HRESULT OpenFile(const ZString& strFilename)
{
ZAssert(m_pvFileContents == NULL);
HANDLE hFile, hMapping;
hFile = CreateFile(strFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
ZAssert(false);
return STG_E_FILENOTFOUND;
}
const DWORD dwSequentialFileThreshold = 1000000;
if (GetFileSize(hFile, NULL) > dwSequentialFileThreshold)
{
CloseHandle(hFile);
hFile = CreateFile(strFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
ZAssert(false);
return STG_E_FILENOTFOUND;
}
}
hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
CloseHandle(hFile);
if (hMapping == INVALID_HANDLE_VALUE)
{
ZAssert(false);
return E_HANDLE;
}
m_pvFileContents = MapViewOfFile (hMapping, FILE_MAP_READ, 0, 0, 0);
CloseHandle(hMapping);
if (m_pvFileContents == NULL)
{
ZAssert(false);
return E_HANDLE;
}
return S_OK;
}
HRESULT ParseWaveData(void* pvRiffFile)
{
ZAssert(m_pvData == NULL);
DWORD *pdwCurrent = (DWORD*)pvRiffFile;
DWORD dwFileType = *(pdwCurrent++);
if (dwFileType != mmioFOURCC('R', 'I', 'F', 'F'))
{
ZAssert(false);
return E_FAIL;
}
DWORD dwLength = *(pdwCurrent++);
DWORD *pdwEnd = (DWORD*)(((BYTE*)pdwCurrent) + dwLength);
DWORD dwFormat = *(pdwCurrent++);
if (dwFormat != mmioFOURCC('W', 'A', 'V', 'E'))
{
ZAssert(false);
return E_FAIL;
}
bool bFoundFormat = false;
bool bFoundData = false;
while ((pdwCurrent < pdwEnd) && (!bFoundFormat || !bFoundData))
{
DWORD dwBlockType = *(pdwCurrent++);
DWORD dwBlockLength = *(pdwCurrent++);
switch (dwBlockType)
{
case mmioFOURCC('f', 'm', 't', ' '):
{
if (dwBlockLength < sizeof(WAVEFORMAT))
{
m_pvData = NULL;
ZAssert(false);
return E_FAIL;
}
WAVEFORMAT *pFormat = (WAVEFORMAT*)pdwCurrent;
if (pFormat->wFormatTag != WAVE_FORMAT_PCM)
{
m_pvData = NULL;
ZAssert(false);
return E_NOTIMPL;
}
m_uChannels = pFormat->nChannels;
m_uBitsPerSample = pFormat->nBlockAlign / pFormat->nChannels * 8;
m_uSampleRate = pFormat->nSamplesPerSec;
bFoundFormat = true;
}
break;
case mmioFOURCC('d', 'a', 't', 'a'):
{
m_pvData = pdwCurrent;
m_uSize = dwBlockLength;
bFoundData = true;
}
break;
}
pdwCurrent = (DWORD*)(((BYTE*)pdwCurrent) + ((dwBlockLength+1)&~1));
}
if (!bFoundFormat || !bFoundData)
{
m_pvData = NULL;
ZAssert(false);
return E_FAIL;
}
ZAssert(m_pvData != NULL);
return S_OK;
}
HRESULT DecodeOGG(const ZString& strFilename)
{
char pcmout[4096];
OggVorbis_File vf;
bool eof = false;
int current_section;
BYTE* writePtr;
FILE* OggFile = NULL;
#if _MSC_VER >= 1400 fopen_s(&OggFile, strFilename, "rb");
#else
OggFile = fopen(strFilename, "rb");
#endif
if(OggFile == NULL)
{
debugf("%s: File not found.\n", strFilename);
return E_FAIL;
}
if(ov_open(OggFile, &vf, NULL, 0) < 0)
{
debugf("%s is not a valid Ogg bitstream.\n", strFilename);
fclose(OggFile);
return E_FAIL;
}
{
vorbis_info *vi = ov_info(&vf, -1);
m_uChannels = vi->channels; m_uSampleRate = vi->rate; m_uBitsPerSample = 16; long sampleCount = (long)ov_pcm_total(&vf, -1);
m_uSize = (m_uBitsPerSample / 8) * sampleCount * m_uChannels; m_pvData = malloc(m_uSize);
writePtr = (BYTE*)m_pvData;
}
while(!eof)
{
long ret = ov_read(&vf, pcmout, 4096, 0, (m_uBitsPerSample / 8), 1, ¤t_section);
if(ret == 0)
{
eof = true;
}
else if(ret < 0)
{
}
else
{
#if _MSC_VER >= 1400 memcpy_s(writePtr, 4096, pcmout, ret); #else
memcpy(writePtr, pcmout, ret);
#endif
writePtr += ret; }
}
ov_clear(&vf);
fclose(OggFile);
return S_OK;
}
void ToMono()
{
if(m_uChannels == 1)
return;
BYTE bytePerSample = m_uBitsPerSample / 8;
unsigned int monoSize = m_uSize / m_uChannels;
unsigned int numSamples = monoSize / bytePerSample;
void* monoData = malloc(monoSize);
BYTE* writePtr = (BYTE*)monoData;
BYTE* readPtr = (BYTE*)m_pvData;
for(unsigned int i = 0; i < numSamples; i++)
{
memcpy(writePtr, readPtr, bytePerSample);
writePtr += bytePerSample;
readPtr += bytePerSample * m_uChannels;
}
m_uChannels = 1;
m_uSize = monoSize;
m_pvData = monoData; return;
}
HRESULT Init(const ZString& strFilename, bool convert) {
HRESULT hr;
hr = OpenFile(strFilename);
if (FAILED(hr)) return hr;
hr = ParseWaveData(m_pvFileContents);
if (FAILED(hr)) return hr;
if (convert)
ToMono();
m_strFilename = strFilename;
m_bIsOgg = false;
m_mapOpenFiles.insert(std::pair<ZString, ISoundPCMData*>(m_strFilename, this));
return S_OK;
}
HRESULT InitOgg(const ZString& strFilename, bool convert) {
HRESULT hr;
hr = DecodeOGG(strFilename);
if (FAILED(hr)) return hr;
if (convert)
ToMono();
m_strFilename = strFilename;
m_bIsOgg = true;
m_mapOpenFiles.insert(std::pair<ZString, ISoundPCMData*>(m_strFilename, this));
return S_OK;
}
public:
static HRESULT LoadWaveFile(TRef<ISoundPCMData>& pdata, const ZString& strFilename, bool convertMono) {
SoundFileCache::iterator iterfile;
iterfile = m_mapOpenFiles.find(strFilename);
if (iterfile != m_mapOpenFiles.end())
{
pdata = (*iterfile).second;
return S_OK;
}
else
{
TRef<SoundFile> psoundfile = new SoundFile();
HRESULT hr;
if(strFilename.Right(4) == ".ogg") hr = psoundfile->InitOgg(strFilename, convertMono ); else
hr = psoundfile->Init(strFilename, convertMono); if (FAILED(hr)) return hr;
pdata = psoundfile;
return S_OK;
}
}
unsigned GetNumberOfChannels()
{
ZAssert(m_pvData != NULL); return m_uChannels;
};
virtual unsigned GetBitsPerSample()
{
ZAssert(m_pvData != NULL); return m_uBitsPerSample;
};
virtual unsigned GetSampleRate()
{
ZAssert(m_pvData != NULL); return m_uSampleRate;
};
virtual unsigned GetSize()
{
ZAssert(m_pvData != NULL); return m_uSize;
};
void GetData(void* dest, unsigned nOffset, unsigned nLength)
{
ZAssert(m_pvData != NULL); ZAssert(nOffset + nLength <= m_uSize);
memcpy(dest, ((BYTE*)m_pvData) + nOffset, nLength);
};
#ifdef _DEBUG
ZString DebugDump(const ZString& strIndent = "")
{
return strIndent + ZString("SoundFile \"") + m_strFilename
+ "\" \n" + strIndent + " (" + ZString((int)GetSampleRate())
+ " Hz x " + ZString((int)GetBitsPerSample())
+ " bits x " + ZString((int)GetNumberOfChannels())
+ " channels, " + ZString(GetSize()/(float)GetBytesPerSec())
+ " seconds)\n";
}
#endif
};
SoundFile::SoundFileCache SoundFile::m_mapOpenFiles;
HRESULT LoadWaveFile(TRef<ISoundPCMData>& data, const ZString& strFilename, const bool convertMono) {
return SoundFile::LoadWaveFile(data, strFilename, convertMono);
}
class DummyPCMDataImpl : public ISoundPCMData
{
enum { datasize = 44100 };
BYTE m_data[datasize];
public:
virtual unsigned GetNumberOfChannels()
{
return 2;
};
virtual unsigned GetBitsPerSample()
{
return 16;
};
virtual unsigned GetSampleRate()
{
return 44100;
};
virtual unsigned GetSize()
{
return datasize;
};
virtual void GetData(void* dest, unsigned nOffset, unsigned nLength)
{
assert(nOffset + nLength <= datasize);
memset(dest, 0, nLength);
};
};
TRef<ISoundPCMData> psoundDataDummy;
HRESULT CreateDummyPCMData(TRef<ISoundPCMData>& pdata)
{
if (psoundDataDummy == NULL)
psoundDataDummy = new DummyPCMDataImpl();
pdata = psoundDataDummy;
return S_OK;
}
GenericSoundSource::GenericSoundSource() :
m_vectPosition(0, 0, 0),
m_vectVelocity(0, 0, 0),
m_vectOrientation(0, 0, 1),
m_bIsPlaying(true)
{
}
GenericSoundSource::GenericSoundSource(
const Vector& vectPosition,
const Vector& vectVelocity,
const Vector& vectOrientation,
bool bListenerRelative
) :
m_vectPosition(vectPosition),
m_vectVelocity(vectVelocity),
m_vectOrientation(vectOrientation),
m_bListenerRelative(bListenerRelative),
m_bIsPlaying(true)
{
}
void GenericSoundSource::SetPosition(const Vector& vectPosition)
{
m_vectPosition = vectPosition;
}
void GenericSoundSource::SetVelocity(const Vector& vectVelocity)
{
m_vectVelocity = vectVelocity;
}
void GenericSoundSource::SetOrientation(const Vector& vectOrientation)
{
ZAssert(vectOrientation.Length() > 0.98 && vectOrientation.Length() < 1.02);
m_vectOrientation = vectOrientation;
}
void GenericSoundSource::SetIsListenerRelative(bool bListenerRelative)
{
m_bListenerRelative = bListenerRelative;
}
void GenericSoundSource::Stop()
{
m_bIsPlaying = false;
}
HRESULT GenericSoundSource::GetPosition(Vector& vectPosition)
{
vectPosition = m_vectPosition;
return S_OK;
};
HRESULT GenericSoundSource::GetVelocity(Vector& vectVelocity)
{
vectVelocity = m_vectVelocity;
return S_OK;
};
HRESULT GenericSoundSource::GetOrientation(Vector& vectOrientation)
{
vectOrientation = m_vectOrientation;
return S_OK;
};
HRESULT GenericSoundSource::IsListenerRelative()
{
return m_bListenerRelative ? S_OK : S_FALSE;
};
HRESULT GenericSoundSource::IsPlaying()
{
return m_bIsPlaying ? S_OK : S_FALSE;
};
HRESULT SoundPositionSourceWrapper::GetPosition(Vector& vectPosition)
{
return m_pBase->GetPosition(vectPosition);
};
HRESULT SoundPositionSourceWrapper::GetVelocity(Vector& vectVelocity)
{
return m_pBase->GetVelocity(vectVelocity);
};
HRESULT SoundPositionSourceWrapper::GetOrientation(Vector& vectOrientation)
{
return m_pBase->GetOrientation(vectOrientation);
};
HRESULT SoundPositionSourceWrapper::IsPlaying()
{
return m_pBase->IsPlaying();
};
GenericListener::GenericListener() :
m_vectUp(0, 1, 0)
{
};
void GenericListener::SetUpDirection(const Vector& vectUp)
{
m_vectUp = vectUp;
};
HRESULT GenericListener::GetUpDirection(Vector& vectUp)
{
vectUp = m_vectUp;
return S_OK;
};
HRESULT GenericListener::GetPosition(Vector& vectPosition)
{
return GenericSoundSource::GetPosition(vectPosition);
};
HRESULT GenericListener::GetVelocity(Vector& vectVelocity)
{
return GenericSoundSource::GetVelocity(vectVelocity);
};
HRESULT GenericListener::GetOrientation(Vector& vectOrientation)
{
return GenericSoundSource::GetOrientation(vectOrientation);
};
HRESULT GenericListener::IsPlaying()
{
return GenericSoundSource::IsPlaying();
};
HRESULT SoundInstanceWrapper::Stop(bool bForceNow)
{
return m_pBase->Stop(bForceNow);
};
HRESULT SoundInstanceWrapper::IsPlaying()
{
return m_pBase->IsPlaying();
};
IEventSource* SoundInstanceWrapper::GetFinishEventSource()
{
return m_pBase->GetFinishEventSource();
};
TRef<ISoundTweakable> SoundInstanceWrapper::GetISoundTweakable()
{
return m_pBase->GetISoundTweakable();
};
TRef<ISoundTweakable3D> SoundInstanceWrapper::GetISoundTweakable3D()
{
return m_pBase->GetISoundTweakable3D();
};
HRESULT SoundTweakableWrapper::SetGain(float fGain)
{
return m_pBase->SetGain(fGain);
};
HRESULT SoundTweakableWrapper::SetPitch(float fPitch)
{
return m_pBase->SetPitch(fPitch);
};
HRESULT SoundTweakableWrapper::SetPriority(float fPriority)
{
return m_pBase->SetPriority(fPriority);
};
HRESULT SoundTweakable3DWrapper::Set3D(bool b3D)
{
return m_pBase->Set3D(b3D);
};
HRESULT SoundTweakable3DWrapper::SetMinimumDistance(float fMinimumDistance)
{
return m_pBase->SetMinimumDistance(fMinimumDistance);
};
HRESULT SoundTweakable3DWrapper::SetCone(float fInnerAngle, float fOuterAngle, float fOutsideGain)
{
return m_pBase->SetCone(fInnerAngle, fOuterAngle, fOutsideGain);
};
StubbedTweakableSoundInstance::StubbedTweakableSoundInstance(ISoundPositionSource* pposSource) :
m_bPrioritySet(false),
m_bGainSet(false),
m_bPitchSet(false),
m_pposSource(pposSource),
m_b3DSet(false),
m_bMinimumDistanceSet(false),
m_bConeSet(false),
m_bStopped(false),
m_bForcedStop(false)
{
m_peventsourceStopped = new EventSourceImpl();
}
void StubbedTweakableSoundInstance::Update(DWORD dwElapsedTime, bool bHasStopped)
{
if (bHasStopped)
{
m_peventsourceStopped->Trigger();
}
}
bool StubbedTweakableSoundInstance::OnEvent(IIntegerEventSource* pevent, int value)
{
if (m_pposSource && (m_pposSource->IsPlaying() != S_OK))
{
Stop(true);
}
bool bIsPlaying = IsPlaying() == S_OK;
Update(value, !bIsPlaying);
return bIsPlaying;
}
void StubbedTweakableSoundInstance::UpdateSound(ISoundInstance* psound)
{
TRef<ISoundTweakable> ptweak = psound->GetISoundTweakable();
ZAssert(ptweak);
if (m_bGainSet)
ZSucceeded(ptweak->SetGain(m_fGain));
if (m_bPitchSet)
ZSucceeded(ptweak->SetPitch(m_fPitch));
if (m_bPrioritySet)
ZSucceeded(ptweak->SetPriority(m_fPriority));
if (m_pposSource)
{
TRef<ISoundTweakable3D> ptweak3d =
psound->GetISoundTweakable3D();
ZAssert(ptweak3d);
if (m_b3DSet)
ZSucceeded(ptweak3d->Set3D(m_b3D));
if (m_bConeSet)
ZSucceeded(ptweak3d->SetCone(m_fInnerAngle, m_fOuterAngle,
m_fOutsideGain));
if (m_bMinimumDistanceSet)
ZSucceeded(ptweak3d->SetMinimumDistance(m_fMinimumDistance));
}
}
HRESULT StubbedTweakableSoundInstance::Stop(bool bForceNow)
{
m_bStopped = true;
m_bForcedStop = m_bForcedStop || bForceNow;
return S_OK;
};
HRESULT StubbedTweakableSoundInstance::IsPlaying()
{
return m_bStopped;
};
IEventSource* StubbedTweakableSoundInstance::GetFinishEventSource()
{
return m_peventsourceStopped;
};
TRef<ISoundTweakable> StubbedTweakableSoundInstance::GetISoundTweakable()
{
return this;
};
TRef<ISoundTweakable3D> StubbedTweakableSoundInstance::GetISoundTweakable3D()
{
if (m_pposSource)
return this;
else
return NULL;
};
HRESULT StubbedTweakableSoundInstance::SetGain(float fGain)
{
if (fGain > 0)
{
ZAssert(false);
return E_INVALIDARG;
}
m_fGain = max(fGain, -100.0f);
m_bGainSet = true;
return S_OK;
};
HRESULT StubbedTweakableSoundInstance::SetPitch(float fPitch)
{
m_fPitch = fPitch;
m_bPitchSet = true;
return S_OK;
};
HRESULT StubbedTweakableSoundInstance::SetPriority(float fPriority)
{
m_fPriority = fPriority;
m_bPrioritySet = true;
return S_OK;
};
HRESULT StubbedTweakableSoundInstance::Set3D(bool b3D)
{
m_b3D = b3D;
m_b3DSet = true;
return S_OK;
};
HRESULT StubbedTweakableSoundInstance::SetMinimumDistance(float fMinimumDistance)
{
if (fMinimumDistance <= 0)
{
ZAssert(false);
return E_INVALIDARG;
}
m_fMinimumDistance = fMinimumDistance;
m_bMinimumDistanceSet = true;
return S_OK;
};
HRESULT StubbedTweakableSoundInstance::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;
m_bConeSet = true;
return S_OK;
};
HRESULT SoundBufferSourceDelegate::CreateStaticBuffer(TRef<ISoundInstance>& psoundNew,
ISoundPCMData* pcmdata, bool bLooping, ISoundPositionSource* psource)
{
return m_pBase->CreateStaticBuffer(psoundNew, pcmdata, bLooping, psource);
};
HRESULT SoundBufferSourceDelegate::CreateASRBuffer(TRef<ISoundInstance>& psoundNew,
ISoundPCMData* pcmdata, unsigned uLoopStart, unsigned uLoopLength,
ISoundPositionSource* psource)
{
return m_pBase->CreateASRBuffer(psoundNew, pcmdata, uLoopStart, uLoopLength, psource);
};
IIntegerEventSource* SoundBufferSourceDelegate::GetUpdateEventSource()
{
return m_pBase->GetUpdateEventSource();
};
};