Files
GTASource/rage/scaleform/Src/GSoundRenderer/GSoundRendererFMODImpl.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1727 lines
50 KiB
C++

/**********************************************************************
Filename : GSoundRendererFMODImpl.cpp
Content : Sound Driver - FMOD Ex
Created : November, 2008
Authors : Andrew Reisse, Maxim Didenko, Vladislav Merker
Copyright : (c) 1998-2009 Scaleform Corp. All Rights Reserved.
Licensees may use this file in accordance with the valid Scaleform
Commercial License Agreement provided with the software.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
**********************************************************************/
#ifndef INC_GSOUNDPLAYERFMODIMPL_H
#define INC_GSOUNDPLAYERFMODIMPL_H
#include "GSoundRendererFMOD.h"
#ifndef GFC_NO_SOUND
#include "GSoundRendererComImpl.h"
#include "GFunctions.h"
#include "GArray.h"
#include "GAtomic.h"
#include "GHash.h"
#include "GStd.h"
#include "GAllocator.h"
#include "GTimer.h"
#include "GThreads.h"
#include "GHeapNew.h"
#include <fmod.hpp>
#include <fmod_errors.h>
#if defined(GFC_SOUND_FMOD_DESIGNER) && (defined(GFC_OS_WIN32) || defined(GFC_OS_MAC))
#include <fmod_event.hpp>
#include <fmod_event_net.hpp>
#endif
class GSoundRendererFMODImpl;
class GSoundChannelFMODImpl;
//////////////////////////////////////////////////////////////////////////
//
class GSoundSampleFMODImpl : public GSoundSampleImplNode
{
public:
GSoundRendererFMODImpl* pPlayer;
FMOD::Sound* pSound;
GPtr<GSoundDataBase> pSoundData;
GSoundSampleFMODImpl(GSoundRendererFMODImpl* pp);
~GSoundSampleFMODImpl();
virtual GSoundRenderer* GetSoundRenderer() const;
virtual bool IsDataValid() const { return pSound != 0; }
virtual Float GetDuration() const;
// Returns the size, in bytes
virtual SInt GetBytesTotal() const;
// Returns the number of bytes loaded
virtual SInt GetBytesLoaded() const;
virtual void ReleaseResource();
void ReleaseFMODObjects();
bool IsPlaying(void*);
virtual GSoundChannelFMODImpl* Start(bool paused);
FMOD_RESULT CreateSubSound(GSoundData* psd, FMOD::Sound** psound);
FMOD_RESULT CreateSubSound(GSoundFile* psd, FMOD::Sound** psound);
FMOD_RESULT CreateSubSound(GAppendableSoundData* psd, FMOD::Sound** psound);
};
class GSoundChannelFMODImpl : public GSoundChannelImplNode
{
public:
GSoundRendererFMODImpl* pPlayer;
GSoundSampleFMODImpl* pSample;
FMOD::Channel* pChan;
GHash<FMOD_SYNCPOINT*, Transform> Tranforms;
GSoundChannelFMODImpl (GSoundRendererFMODImpl* pp, GSoundSampleFMODImpl* ps, FMOD::Channel* pc);
~GSoundChannelFMODImpl();
GSoundRenderer* GetSoundRenderer() const;
GSoundSample* GetSample() const { return pSample; }
virtual void ReleaseResource();
void ReleaseFMODObjects();
virtual void Stop();
virtual void Pause(bool pause);
virtual bool IsPlaying() const;
virtual void SetPosition(Float seconds);
virtual Float GetPosition();
virtual void Loop(SInt count, Float start = 0, Float end = 0);
virtual Float GetVolume();
virtual void SetVolume(Float volume);
virtual Float GetPan();
virtual void SetPan(Float volume);
virtual void SetTransforms(const GArray<Transform>& transforms);
virtual void SetSpatialInfo(const GArray<Vector> []) {}
static FMOD_RESULT F_CALLBACK CallBackFunc(
FMOD_CHANNEL* pchan, FMOD_CHANNEL_CALLBACKTYPE cb, void* cmddata1, void* cmddata2);
};
//////////////////////////////////////////////////////////////////////////
//
class GSoundChannelFMODImplAux;
class GSoundSampleFMODImplAux;
typedef GHash<GSoundRenderer::AuxStreamer*, GArray<GSoundChannelFMODImplAux*> > AuxStreamersType;
class GSoundRendererFMODImpl : public GSoundRendererFMOD
{
public:
FMOD::System* pDevice;
#if defined(GFC_SOUND_FMOD_DESIGNER) && (defined(GFC_OS_WIN32) || defined(GFC_OS_MAC))
FMOD::EventSystem* pEventSys;
#endif
#ifndef GFC_NO_THREADSUPPORT
GPtr<GThread> pUpdateThread;
GEvent Event;
volatile bool StopThread;
#endif
GLock AuxStreamsLock;
AuxStreamersType AuxStreamers;
GDListNode SampleList;
GLock SampleListLock;
bool CallFMODUpdate;
bool ThreadedUpdate;
#ifdef GFC_OS_XBOX360
int ProcNumber;
#endif
Float SystemBitRate;
GSoundRendererFMODImpl();
virtual ~GSoundRendererFMODImpl() { xFinalize(); }
virtual bool GetRenderCaps(UInt32 *pcaps);
virtual FMOD::System* GetFMODSystem() { return pDevice; }
#if defined(GFC_SOUND_FMOD_DESIGNER) && (defined(GFC_OS_WIN32) || defined(GFC_OS_MAC))
virtual FMOD::EventSystem* GetFMODEventSystem() { return pEventSys; }
#endif
virtual bool Initialize(FMOD::System* pd, bool call_fmod_update, bool threaded_update
#ifdef GFC_OS_XBOX360
, int processor_core = 5
#endif
);
virtual void Finalize() { xFinalize(); }
void xFinalize(); // Avoid call to a virtual method from the destructor
virtual GSoundSampleFMODImpl* CreateSampleFromData(GSoundDataBase* psd);
virtual GSoundSampleFMODImpl* CreateSampleFromFile(const char* fname, bool streaming);
virtual GSoundSampleFMODImpl* CreateSampleFromAuxStreamer(AuxStreamer* pStreamder,
UInt32 channels,
UInt32 samplerate,
AuxStreamer::PCMFormat fmt);
virtual GSoundChannelFMODImpl* PlaySample(GSoundSample* ps, bool paused);
virtual Float GetMasterVolume();
virtual void SetMasterVolume(Float volume);
virtual Float Update();
virtual void Mute(bool mute);
void AttachAuxStreamer(GSoundChannelFMODImplAux*);
void DetachAuxStreamer(GSoundChannelFMODImplAux*);
void LogError(FMOD_RESULT result);
void PrintStatistics();
Float UpdateAuxStreams();
#ifndef GFC_NO_THREADSUPPORT
static SInt UpdateFunc(GThread*, void* h);
void PulseEvent() { Event.PulseEvent(); }
#endif
};
//////////////////////////////////////////////////////////////////////////
//
FMOD_RESULT F_CALLBACK DecodeOpen(const char *sd, int unicode, unsigned int *filesize, void **handle, void **userdata)
{
GUNUSED2(unicode, handle);
GAppendableSoundData* psd = (GAppendableSoundData*)sd;
psd->SeekPos(0);
*userdata = psd;
*filesize = 0x0FFFFFFF;
return FMOD_OK;
}
FMOD_RESULT F_CALLBACK DecodeClose(void *handle, void *userdata)
{
GUNUSED2(userdata, handle);
GAppendableSoundData* psd = (GAppendableSoundData*)userdata;
psd->SeekPos(0);
return FMOD_OK;
}
FMOD_RESULT F_CALLBACK DecodeRead(void *handle, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata)
{
GUNUSED(handle);
GAppendableSoundData* psd = (GAppendableSoundData*)userdata;
*bytesread = psd->GetData((UByte*)buffer, sizebytes);
return FMOD_OK;
}
FMOD_RESULT F_CALLBACK DecodeSeek(void *handle, unsigned int pos, void *userdata)
{
GUNUSED(handle);
GAppendableSoundData* psd = (GAppendableSoundData*)userdata;
if (!psd->SeekPos(pos))
return FMOD_ERR_FILE_COULDNOTSEEK;
return FMOD_OK;
}
FMOD_RESULT CreateSubSound(GSoundRendererFMODImpl* prenderer, GAppendableSoundData* psd, FMOD::Sound** psound)
{
FMOD_CREATESOUNDEXINFO exinfo;
memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
exinfo.length = 0x0FFFFFFF;
int flags = FMOD_OPENONLY | FMOD_SOFTWARE | FMOD_IGNORETAGS;
switch (psd->GetFormat() & GSoundData::Sample_Format)
{
case GSoundData::Sample_PCM:
exinfo.format = (psd->GetFormat() & 0x7) == 2 ? FMOD_SOUND_FORMAT_PCM16 : FMOD_SOUND_FORMAT_PCM8;
exinfo.defaultfrequency = psd->GetRate();
exinfo.numchannels = (psd->GetFormat() & GSoundData::Sample_Stereo) ? 2 : 1;
flags |= FMOD_OPENRAW;
break;
case GSoundData::Sample_MP3:
exinfo.format = FMOD_SOUND_FORMAT_MPEG;
break;
default:
return FMOD_ERR_FORMAT;
}
exinfo.useropen = &DecodeOpen;
exinfo.userclose= &DecodeClose;
exinfo.userread = &DecodeRead;
exinfo.userseek = &DecodeSeek;
exinfo.decodebuffersize = 1024*8;
FMOD_RESULT result = prenderer->pDevice->createStream((const char*)psd,flags, &exinfo, psound);
return result;
}
//////////////////////////////////////////////////////////////////////////
//
class GSwfSoundStreamer : public GSoundRenderer::AuxStreamer, public GDListNode
{
public:
GSwfSoundStreamer(GSoundRendererFMODImpl* prenderer, GAppendableSoundData* psd);
~GSwfSoundStreamer();
virtual UInt GetPCMData(UByte* pdata, UInt datasize);
virtual bool SetPosition(Float seconds);
virtual void ReleaseResource();
void ReleaseFMODObjects();
bool CreateReader();
bool GetSoundFormat(GSoundRenderer::AuxStreamer::PCMFormat* fmt, UInt32 *channels, UInt32 *samplerate);
GPtr<GAppendableSoundData> pSoundData;
FMOD::Sound* pSound;
GSoundRendererFMODImpl* pRenderer;
};
GSwfSoundStreamer::GSwfSoundStreamer(GSoundRendererFMODImpl* prenderer, GAppendableSoundData* psd)
: GDListNode(&prenderer->SampleList), pSoundData(psd), pSound(NULL), pRenderer(prenderer)
{
}
void GSwfSoundStreamer::ReleaseResource()
{
pRenderer = NULL;
if (GetRefCount() > 0)
ReleaseFMODObjects();
if (pNext)
RemoveNode();
}
void GSwfSoundStreamer::ReleaseFMODObjects()
{
if (pSound)
pSound->release();
pSound = NULL;
}
GSwfSoundStreamer::~GSwfSoundStreamer()
{
ReleaseFMODObjects();
if(pFirst)
RemoveNode();
}
UInt GSwfSoundStreamer::GetPCMData(UByte* pdata, UInt datasize)
{
UInt got_bytes = 0;
if (pSound)
pSound->readData(pdata,datasize,&got_bytes);
return got_bytes;
}
bool GSwfSoundStreamer::SetPosition(Float seconds)
{
if (pSound && pSoundData)
{
UInt latency = pSoundData ? pSoundData->GetSeekSample() : 0;
Float sample_rate;
pSound->getDefaults(&sample_rate,NULL,NULL,NULL);
UInt sample = UInt(seconds * sample_rate);
FMOD_RESULT res = pSound->seekData(sample + latency);
return res == FMOD_OK;
}
return false;
}
bool GSwfSoundStreamer::CreateReader()
{
if (pRenderer)
return CreateSubSound(pRenderer, pSoundData, &pSound) == FMOD_OK;
return false;
}
bool GSwfSoundStreamer::GetSoundFormat(GSoundRenderer::AuxStreamer::PCMFormat* fmt, UInt32 *channels, UInt32 *samplerate)
{
if (!pSound)
return false;
FMOD_SOUND_FORMAT format = FMOD_SOUND_FORMAT_NONE;
int chans = 0;
FMOD_RESULT ret;
ret = pSound->getFormat(NULL, &format, &chans, NULL);
if (ret != FMOD_OK)
return false;
*channels = chans;
Float frequency = 0.0;
ret = pSound->getDefaults(&frequency, NULL, NULL, NULL);
if (ret != FMOD_OK)
return false;
*samplerate = (UInt32)frequency;
if (format == FMOD_SOUND_FORMAT_PCM16)
*fmt = PCM_SInt16;
else if (format == FMOD_SOUND_FORMAT_PCMFLOAT)
*fmt = PCM_Float;
else
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////
//
#define AUX_SOUND_READBUFLEN_MS 300
#define AUX_SOUND_LEN_MS (AUX_SOUND_READBUFLEN_MS * 5)
class GSoundChannelFMODImplAux : public GSoundChannelFMODImpl
{
public:
GSoundChannelFMODImplAux(GSoundRendererFMODImpl* pp, GSoundSampleFMODImplAux* ps, FMOD::Channel* pc);
~GSoundChannelFMODImplAux();
virtual Float GetPosition();
virtual void SetPosition(Float seconds);
virtual void Stop();
virtual void Pause(bool pause);
Float Update();
UInt64 StartTick;
UInt64 StopTick;
UInt64 TotalTicks;
UInt64 NextFillTick;
bool Paused;
bool Starving;
GLock ChannelLock;
Float StartSecond;
};
class GSoundSampleFMODImplAux : public GSoundSampleFMODImpl
{
public:
typedef GSoundRenderer::AuxStreamer AuxStreamer;
GSoundSampleFMODImplAux(GSoundRendererFMODImpl* pp, AuxStreamer* pstreamer, AuxStreamer::PCMFormat fmt,
UInt32 channels, UInt32 samplerate);
~GSoundSampleFMODImplAux();
virtual GSoundChannelFMODImplAux* Start(bool paused);
UInt ReadAndFillSound();
void ClearSoundBuffer();
bool SeekData(Float seconds);
UInt GetTotalBytesRead() const { return TotatBytesRead; }
UInt64 GetTotalBytesReadInTicks() const { return BytesToTicks(TotatBytesRead); }
UInt GetFillPosition() const { return FillPosition; }
UInt TicksToSoundPos(UInt64 ticks) const
{
return UInt((((ticks / 1000) % AUX_SOUND_LEN_MS) * (SampleRate / 1000)) * Channels * (Bits / 8));
}
UInt64 BytesToTicks(UInt bytes) const
{
return UInt64(bytes) * 8/ Bits / Channels * 1000000 / SampleRate;
}
inline UInt DistToFillBuffPos(UInt pos) const;
GPtr<AuxStreamer> pStreamer;
UInt32 Channels;
UInt32 SampleRate;
UInt Bits;
UByte* pBlockBuffer;
UInt BlockSize;
UInt SoundLength;
FMOD_SOUND_FORMAT Format;
UInt FillPosition;
UInt TotatBytesRead;
};
GSoundSampleFMODImplAux::GSoundSampleFMODImplAux(GSoundRendererFMODImpl* pp, AuxStreamer* pstreamer, AuxStreamer::PCMFormat fmt,
UInt32 channels, UInt32 samplerate)
: GSoundSampleFMODImpl(pp), Channels(channels), SampleRate(samplerate), pBlockBuffer(0), BlockSize(0),
FillPosition(0), TotatBytesRead(0)
{
pStreamer = pstreamer;
Format = fmt == AuxStreamer::PCM_SInt16 ? FMOD_SOUND_FORMAT_PCM16 : FMOD_SOUND_FORMAT_PCMFLOAT;
Bits = fmt == GSoundRenderer::AuxStreamer::PCM_SInt16 ? 16 : 32;
SoundLength = (AUX_SOUND_LEN_MS * (SampleRate /1000)) * Channels * (Bits / 8);
FMOD_CREATESOUNDEXINFO exinfo;
memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); // Required
exinfo.length = SoundLength; // Length of PCM data in bytes of whole song (for Sound::getLength)
exinfo.numchannels = Channels; // Number of channels in the sound
exinfo.defaultfrequency = SampleRate; // Default playback rate of sound
exinfo.format = Format;
FMOD_MODE flags = FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_SOFTWARE;
FMOD_RESULT result = pPlayer->pDevice->createSound(0, flags, &exinfo, &pSound);
if (result != FMOD_OK)
{
pSound = NULL;
pPlayer->LogError(result);
return;
}
unsigned int sl = 0;
pSound->getLength(&sl, FMOD_TIMEUNIT_PCMBYTES);
GASSERT(sl == SoundLength);
}
GSoundSampleFMODImplAux::~GSoundSampleFMODImplAux()
{
if (pBlockBuffer)
GFREE(pBlockBuffer);
}
GSoundChannelFMODImplAux* GSoundSampleFMODImplAux::Start(bool paused)
{
if (!pSound)
return NULL;
FMOD::Channel* pchannel;
FMOD_RESULT result;
result = pPlayer->pDevice->playSound(FMOD_CHANNEL_REUSE, pSound, paused, &pchannel);
if (result != FMOD_OK)
{
pPlayer->LogError(result);
return NULL;
}
BlockSize = (AUX_SOUND_READBUFLEN_MS * SampleRate/1000) * Channels * (Bits / 8);
if (pBlockBuffer)
GFREE(pBlockBuffer);
pBlockBuffer = (UByte*)GALLOC(BlockSize, GStat_Sound_Mem);
GSoundChannelFMODImplAux* pChannel = GNEW GSoundChannelFMODImplAux(pPlayer, this, pchannel);
pPlayer->AttachAuxStreamer(pChannel);
return pChannel;
}
void GSoundSampleFMODImplAux::ClearSoundBuffer()
{
FMOD_RESULT ret;
void* ptr1, *ptr2;
unsigned int len1, len2;
ret = pSound->lock(0, SoundLength, &ptr1, &ptr2, &len1, &len2);
if (ret == FMOD_OK)
{
GMemUtil::Set(ptr1, 0, len1);
ret = pSound->unlock(ptr1, ptr2, len1, len2);
GASSERT(ret == FMOD_OK);
GUNUSED(ret);
}
}
UInt GSoundSampleFMODImplAux::ReadAndFillSound()
{
FMOD_RESULT ret;
void* ptr1, *ptr2;
unsigned int len1, len2;
UInt got_bytes = pStreamer->GetPCMData(pBlockBuffer, BlockSize);
if (got_bytes < BlockSize)
GMemUtil::Set(&pBlockBuffer[got_bytes], 0, BlockSize-got_bytes );
if (Channels == 6)
{
// CRI: L,R,Ls,Rs,C,LFE
// FMOD: L,R,C,LFE,Ls,Rs
if (Format == FMOD_SOUND_FORMAT_PCMFLOAT)
{
Float tmp;
Float* psample = (Float*)pBlockBuffer;
for(size_t i = 0; i < got_bytes/4; i += 6)
{
tmp = psample[i+2]; psample[i+2] = psample[i+4]; psample[i+4] = tmp;
tmp = psample[i+3]; psample[i+3] = psample[i+5]; psample[i+5] = tmp;
}
}
else
{
UInt16 tmp;
UInt16* psample = (UInt16*)pBlockBuffer;
for(size_t i = 0; i < got_bytes/2; i += 6)
{
tmp = psample[i+2]; psample[i+2] = psample[i+4]; psample[i+4] = tmp;
tmp = psample[i+3]; psample[i+3] = psample[i+5]; psample[i+5] = tmp;
}
}
}
ret = pSound->lock(FillPosition, BlockSize, &ptr1, &ptr2, &len1, &len2);
if (ret == FMOD_OK)
{
if (BlockSize > len1)
{
GMemUtil::Copy(ptr1, pBlockBuffer, len1);
if (BlockSize-len1 > len2)
{
GMemUtil::Copy(ptr2, pBlockBuffer+len1, len2);
}
else
{
GMemUtil::Copy(ptr2, pBlockBuffer+len1, BlockSize-len1);
GMemUtil::Set(((UByte*)ptr2)+(BlockSize-len1), 0, len2 - (BlockSize-len1));
}
}
else
{
GMemUtil::Copy(ptr1, pBlockBuffer, BlockSize);
GMemUtil::Set(((UByte*)ptr1)+BlockSize, 0, len1-BlockSize);
GMemUtil::Set(ptr2, 0, len2);
}
ret = pSound->unlock(ptr1, ptr2, len1, len2);
GASSERT(ret == FMOD_OK);
GUNUSED(ret);
}
TotatBytesRead += got_bytes;
FillPosition += got_bytes;
if (FillPosition >= SoundLength)
FillPosition -= SoundLength;
return got_bytes;
}
inline UInt GSoundSampleFMODImplAux::DistToFillBuffPos(UInt pos) const
{
UInt bytes_diff;
if (pos > FillPosition)
bytes_diff = (FillPosition + SoundLength) - pos;
else
bytes_diff = FillPosition - pos;
return bytes_diff;
}
bool GSoundSampleFMODImplAux::SeekData(Float seconds)
{
if (pStreamer && pStreamer->SetPosition(seconds))
{
TotatBytesRead = 0;
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
//
GSoundChannelFMODImplAux::GSoundChannelFMODImplAux(GSoundRendererFMODImpl* pp, GSoundSampleFMODImplAux* ps, FMOD::Channel* pc)
: GSoundChannelFMODImpl(pp, ps, pc), StartTick(GTimer::GetProfileTicks()), Starving(false), StartSecond(0.0)
{
ps->AddRef();
TotalTicks = 0;
NextFillTick = 0;
pChan->getPaused(&Paused);
if (Paused)
StopTick = StartTick;
else
{
ps->ReadAndFillSound();
StartTick = GTimer::GetProfileTicks();
NextFillTick = ps->GetTotalBytesReadInTicks();
}
}
GSoundChannelFMODImplAux::~GSoundChannelFMODImplAux()
{
pSample->Release();
}
Float GSoundChannelFMODImplAux::GetPosition()
{
GLock::Locker lock(&ChannelLock);
if (!Paused && !Starving)
{
GSoundSampleFMODImplAux* psample = (GSoundSampleFMODImplAux*)pSample;
UInt64 totalTicksRead = psample->GetTotalBytesReadInTicks();
UInt64 curtick = GTimer::GetProfileTicks();
UInt64 pos = TotalTicks + (curtick - StartTick);
if (pos > totalTicksRead)
{
Starving = true;
StopTick = curtick;
TotalTicks = totalTicksRead;
return StartSecond + TotalTicks / 1000000.f;
}
return StartSecond + pos / 1000000.f;
}
return StartSecond + TotalTicks / 1000000.f;
}
void GSoundChannelFMODImplAux::SetPosition(Float seconds)
{
GLock::Locker lock(&ChannelLock);
GSoundSampleFMODImplAux* psample = (GSoundSampleFMODImplAux*)pSample;
if (psample && psample->SeekData(seconds))
{
StartSecond = seconds;
TotalTicks = 0;
NextFillTick = 0;
StartTick = GTimer::GetProfileTicks();
UInt newPlayPos = psample->GetFillPosition();
psample->ReadAndFillSound();
pChan->setPosition(newPlayPos, FMOD_TIMEUNIT_PCMBYTES);
Starving = false;
NextFillTick = psample->GetTotalBytesReadInTicks();
}
}
void GSoundChannelFMODImplAux::Stop()
{
if (pPlayer)
pPlayer->DetachAuxStreamer(this);
StopTick = GTimer::GetProfileTicks();
}
void GSoundChannelFMODImplAux::Pause(bool pause)
{
GLock::Locker lock(&ChannelLock);
if (Paused == pause)
return;
GSoundChannelFMODImpl::Pause(pause);
Paused = pause;
if (Paused)
{
StopTick = GTimer::GetProfileTicks();
TotalTicks += StopTick - StartTick;
}
else
{
UInt64 curtick = GTimer::GetProfileTicks();
StartTick = curtick;
#ifndef GFC_NO_THREADSUPPORT
pPlayer->PulseEvent();
#endif
}
}
Float GSoundChannelFMODImplAux::Update()
{
GLock::Locker lock(&ChannelLock);
GASSERT(pSample);
if (!IsPlaying())
return 0.5f;
if (Paused)
return 0.1f;
GSoundSampleFMODImplAux* psample = (GSoundSampleFMODImplAux*)pSample;
UInt64 totalTicksRead = psample->GetTotalBytesReadInTicks();
UInt64 curtick = GTimer::GetProfileTicks();
UInt64 pos = TotalTicks + (curtick - StartTick);
if (!Starving && pos > totalTicksRead)
{
Starving = true;
StopTick = curtick;
TotalTicks = totalTicksRead;
}
UInt dist = 0;
if (!Starving)
{
UInt fmodpos = 0;
FMOD_RESULT ret = pChan->getPosition(&fmodpos, FMOD_TIMEUNIT_PCMBYTES);
GASSERT(ret == FMOD_OK);
GUNUSED(ret);
GASSERT(fmodpos <= psample->SoundLength);
dist = psample->DistToFillBuffPos(fmodpos);
}
if (dist < psample->BlockSize / 3 )
{
UInt got_bytes = 0;
if (Starving)
{
psample->FillPosition = 0;
got_bytes = psample->ReadAndFillSound();
if (got_bytes > 0)
{
pChan->setPosition(0, FMOD_TIMEUNIT_PCMBYTES);
Starving = false;
StartTick = GTimer::GetProfileTicks();
pos = TotalTicks;
}
else
{
psample->ClearSoundBuffer();
return 0.02f;
}
}
else
got_bytes = psample->ReadAndFillSound();
if (got_bytes > 0)
{
NextFillTick = psample->GetTotalBytesReadInTicks();
Float t = (NextFillTick - pos) / 1000000.f *2/3;
return t < 0.02f ? 0.02f : t;
}
return 0.02f;
}
else
{
Float t = (NextFillTick - pos) / 1000000.f / 2;
return t < 0.02f ? 0.02f : t;
}
}
//////////////////////////////////////////////////////////////////////////
//
void GSoundRendererFMODImpl::AttachAuxStreamer(GSoundChannelFMODImplAux* pchan)
{
if (!pchan || !pchan->pSample)
return;
GSoundSampleFMODImplAux* psample = (GSoundSampleFMODImplAux*)pchan->pSample;
if (!psample->pStreamer)
return;
AuxStreamsLock.Lock();
GArray<GSoundChannelFMODImplAux*>* pcont = AuxStreamers.Get(psample->pStreamer);
if (!pcont)
{
AuxStreamers.Set(psample->pStreamer, GArray<GSoundChannelFMODImplAux*>());
pcont = AuxStreamers.Get(psample->pStreamer);
}
pcont->PushBack(pchan);
pchan->AddRef();
AuxStreamsLock.Unlock();
#ifndef GFC_NO_THREADSUPPORT
if(ThreadedUpdate && !pUpdateThread)
{
StopThread = false;
GThread::CreateParams params(UpdateFunc, this, 32*1024);
#ifdef GFC_OS_WII
params.priority = GThread::AboveNormalPriority;
#else
params.priority = GThread::HighestPriority;
#endif
#ifdef GFC_OS_XBOX360
params.processor = ProcNumber;
#endif
pUpdateThread = *GNEW GThread(params);
pUpdateThread->Start();
}
Event.PulseEvent();
#endif
}
void GSoundRendererFMODImpl::DetachAuxStreamer(GSoundChannelFMODImplAux* pchan)
{
if (!pchan || !pchan->pSample)
return;
GSoundSampleFMODImplAux* psample = (GSoundSampleFMODImplAux*)pchan->pSample;
if (!psample->pStreamer)
return;
AuxStreamsLock.Lock();
GArray<GSoundChannelFMODImplAux*>* pcont = AuxStreamers.Get(psample->pStreamer);
bool found = false;
if (pcont)
{
size_t nulls = 0;
for(size_t i = 0; i < pcont->GetSize(); ++i)
{
if ((*pcont)[i] == pchan)
{
(*pcont)[i] = NULL;
found = true;
}
if ((*pcont)[i] == NULL)
nulls++;
}
if (nulls == pcont->GetSize())
AuxStreamers.Remove(psample->pStreamer);
#ifndef GFC_NO_THREADSUPPORT
if (AuxStreamers.GetSize() == 0 && ThreadedUpdate && pUpdateThread)
{
StopThread = true;
pUpdateThread = NULL;
Event.PulseEvent();
}
#endif
}
if (!found)
pchan = NULL;
AuxStreamsLock.Unlock();
if (pchan)
{
pchan->pChan->stop();
pchan->Release();
}
}
GSoundSampleFMODImpl* GSoundRendererFMODImpl::CreateSampleFromData(GSoundDataBase* psd)
{
if (!psd)
return NULL;
GLock::Locker lock(&SampleListLock);
if (psd->IsStreamSample() && !psd->IsFileSample())
{
GAppendableSoundData* pasd = (GAppendableSoundData*)psd;
GPtr<GSwfSoundStreamer> pstreamer = *GNEW GSwfSoundStreamer(this, pasd);
if (!pstreamer->CreateReader())
return NULL;
AuxStreamer::PCMFormat fmt;
UInt32 channels;
UInt32 samplerate;
if (!pstreamer->GetSoundFormat(&fmt, &channels,&samplerate))
return NULL;
return CreateSampleFromAuxStreamer(pstreamer, channels, samplerate, fmt);
}
GPtr<GSoundSampleFMODImpl> psample = *GNEW GSoundSampleFMODImpl(this);
bool created = false;
psample->pSoundData = psd;
if (psd->IsFileSample())
{
GSoundFile* ps = (GSoundFile*) psd;
created = (psample->CreateSubSound(ps, &psample->pSound) == FMOD_OK);
}
else
{
GSoundData* ps = (GSoundData*) psd;
created = (psample->CreateSubSound(ps, &psample->pSound) == FMOD_OK);
}
if (created)
{
psample->AddRef();
return psample;
}
return NULL;
}
GSoundSampleFMODImpl* GSoundRendererFMODImpl::CreateSampleFromAuxStreamer(
AuxStreamer* pstreamder, UInt32 channels, UInt32 samplerate, AuxStreamer::PCMFormat fmt)
{
GLock::Locker lock(&SampleListLock);
GSoundSampleFMODImplAux* psample = new GSoundSampleFMODImplAux(this, pstreamder, fmt, channels, samplerate);
return psample;
}
GSoundSampleFMODImpl* GSoundRendererFMODImpl::CreateSampleFromFile(const char* fname, bool streaming)
{
GSoundSampleFMODImpl* psample = NULL;
{
GLock::Locker lock(&SampleListLock);
psample = new GSoundSampleFMODImpl(this);
}
FMOD_RESULT result;
if (streaming)
result = pDevice->createStream(fname,FMOD_SOFTWARE | FMOD_LOOP_OFF | FMOD_2D, NULL, &(psample->pSound));
else
result = pDevice->createSound(fname,FMOD_SOFTWARE | FMOD_LOOP_OFF | FMOD_2D, NULL, &(psample->pSound));
if (result != FMOD_OK)
{
LogError(result);
psample->pSound = NULL;
psample->Release();
return NULL;
}
return psample;
}
bool GSoundRendererFMODImpl::Initialize(FMOD::System* pd, bool call_fmod_update, bool threaded_update
#ifdef GFC_OS_XBOX360
, int processor_core
#endif
)
{
CallFMODUpdate = call_fmod_update;
ThreadedUpdate = threaded_update;
#ifdef GFC_OS_XBOX360
ProcNumber = processor_core;
#endif
pDevice = pd;
if (pDevice)
{
int rate = 0;
pDevice->getSoftwareFormat(&rate,0,0,0,0,0);
SystemBitRate = rate * 1.0f;
#if defined(GFC_SOUND_FMOD_DESIGNER) && (defined(GFC_OS_WIN32) || defined(GFC_OS_MAC))
int result = FMOD::EventSystem_Create(&pEventSys);
if (result == FMOD_OK)
{
FMOD::NetEventSystem_Init(pEventSys);
result = pEventSys->init(64, FMOD_INIT_NORMAL, 0, FMOD_EVENT_INIT_NORMAL);
if (result != FMOD_OK)
{
pEventSys->release();
FMOD::NetEventSystem_Shutdown();
pEventSys = NULL;
}
}
#endif
}
return pDevice != NULL;
}
void GSoundRendererFMODImpl::xFinalize()
{
#if defined(GFC_SOUND_FMOD_DESIGNER) && (defined(GFC_OS_WIN32) || defined(GFC_OS_MAC))
if (pEventSys)
{
pEventSys->release();
FMOD::NetEventSystem_Shutdown();
pEventSys = NULL;
}
#endif
#ifndef GFC_NO_THREADSUPPORT
if (pUpdateThread)
{
StopThread = true;
Event.PulseEvent();
pUpdateThread->Wait();
}
#endif
{
GLock::Locker guard(&SampleListLock);
while (SampleList.pFirst != &SampleList)
SampleList.pFirst->ReleaseResource();
}
}
#ifndef GFC_NO_THREADSUPPORT
SInt GSoundRendererFMODImpl::UpdateFunc(GThread*, void* h)
{
GSoundRendererFMODImpl* pRenderer = (GSoundRendererFMODImpl*)h;
UInt wait_time = 2000; // GFC_WAIT_INFINITE;
while(1)
{
pRenderer->Event.Wait(wait_time);
if (pRenderer->StopThread)
break;
GLock::Locker lock(&pRenderer->AuxStreamsLock);
wait_time = (UInt)(pRenderer->UpdateAuxStreams() * 1000);
}
return 0;
}
#endif
Float GSoundRendererFMODImpl::UpdateAuxStreams()
{
Float nextcall = 0.5f;
for (AuxStreamersType::Iterator it = AuxStreamers.Begin(); it != AuxStreamers.End(); ++it)
{
GArray<GSoundChannelFMODImplAux*>& cont = it->Second;
for(size_t i = 0; i < cont.GetSize(); ++i)
{
GSoundChannelFMODImplAux* pchan = cont[i];
if (pchan)
{
Float t = pchan->Update();
if (t < nextcall) nextcall = t;
}
}
}
return nextcall;
}
Float GSoundRendererFMODImpl::Update()
{
Float nextcall = 0.5f;
#ifndef GFC_NO_THREADSUPPORT
if (!pUpdateThread)
nextcall = UpdateAuxStreams();
#else
nextcall = UpdateAuxStreams();
#endif
if (CallFMODUpdate)
{
pDevice->update();
#if defined(GFC_SOUND_FMOD_DESIGNER) && (defined(GFC_OS_WIN32) || defined(GFC_OS_MAC))
if (pEventSys) {
pEventSys->update();
FMOD::NetEventSystem_Update();
}
#endif
}
return nextcall;
}
void GSoundRendererFMODImpl::Mute(bool mute)
{
FMOD::ChannelGroup *pcg;
FMOD_RESULT result;
result = pDevice->getMasterChannelGroup(&pcg);
if (result == FMOD_OK)
{
pcg->setMute(mute);
}
}
void GSoundRendererFMODImpl::LogError(FMOD_RESULT result)
{
if (result != FMOD_OK && result != FMOD_ERR_INVALID_HANDLE && result != FMOD_ERR_CHANNEL_STOLEN)
fprintf(stderr,"FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
}
GSoundChannelFMODImpl* GSoundRendererFMODImpl::PlaySample(GSoundSample* ps, bool paused)
{
GSoundSampleFMODImpl* psample = (GSoundSampleFMODImpl *) ps;
if (!psample)
return NULL;
return psample->Start(paused);
}
GSoundRendererFMODImpl::GSoundRendererFMODImpl() :
pDevice(NULL),
#if defined(GFC_SOUND_FMOD_DESIGNER) && (defined(GFC_OS_WIN32) || defined(GFC_OS_MAC))
pEventSys(NULL),
#endif
CallFMODUpdate(false), SystemBitRate(0.0f)
{
#ifndef GFC_NO_THREADSUPPORT
StopThread = false;
#endif
}
Float GSoundRendererFMODImpl::GetMasterVolume()
{
FMOD_RESULT result;
float v = 1.0;
FMOD::ChannelGroup* pcgrp;
result = pDevice->getMasterChannelGroup(&pcgrp);
if (result == FMOD_OK)
{
result = pcgrp->getVolume(&v);
if (result == FMOD_OK)
return v;
else
LogError(result);
}
else
LogError(result);
return v;
}
void GSoundRendererFMODImpl::SetMasterVolume(Float volume)
{
FMOD_RESULT result;
FMOD::ChannelGroup* pcgrp;
result = pDevice->getMasterChannelGroup(&pcgrp);
if (result == FMOD_OK)
{
result = pcgrp->setVolume(volume);
LogError(result);
}
else
LogError(result);
}
bool GSoundRendererFMODImpl::GetRenderCaps(UInt32 *pcaps)
{
if (!pcaps)
return false;
*pcaps = 0;
return true;
}
//////////////////////////////////////////////////////////////////////////
//
GSoundSampleFMODImpl::GSoundSampleFMODImpl(GSoundRendererFMODImpl* prenderer)
: GSoundSampleImplNode(&prenderer->SampleList)
{
pPlayer = prenderer;
pSound = NULL;
}
GSoundSampleFMODImpl::~GSoundSampleFMODImpl()
{
ReleaseFMODObjects();
if (!pPlayer)
return;
GLock::Locker guard(&pPlayer->SampleListLock);
if (pFirst)
RemoveNode();
}
void GSoundSampleFMODImpl::ReleaseFMODObjects()
{
if (pSound)
pSound->release();
pSound = NULL;
}
void GSoundSampleFMODImpl::ReleaseResource()
{
pPlayer = 0;
if (AddRef_NotZero())
{
ReleaseFMODObjects();
if (pNext) // We may have been released by user
RemoveNode();
Release();
} else {
if (pNext) // We may have been released by user
RemoveNode();
}
}
Float GSoundSampleFMODImpl::GetDuration() const
{
if (pSound)
{
unsigned slen = 0;
FMOD_RESULT result = pSound->getLength(&slen, FMOD_TIMEUNIT_MS);
if (result == FMOD_OK)
return Float(slen/1000.f);
pPlayer->LogError(result);
}
return 0.0f;
}
SInt GSoundSampleFMODImpl::GetBytesTotal() const
{
if (pSound)
{
unsigned slen = 0;
FMOD_RESULT result = pSound->getLength(&slen, FMOD_TIMEUNIT_RAWBYTES);
if (result == FMOD_OK)
return slen;
pPlayer->LogError(result);
}
return 0;
}
SInt GSoundSampleFMODImpl::GetBytesLoaded() const
{
if (pSound)
{
unsigned slen = 0;
FMOD_RESULT result = pSound->getLength(&slen, FMOD_TIMEUNIT_RAWBYTES);
if (result == FMOD_OK)
return slen;
pPlayer->LogError(result);
}
return 0;
}
inline GSoundRenderer* GSoundSampleFMODImpl::GetSoundRenderer() const
{
return pPlayer;
}
//////////////////////////////////////////////////////////////////////////
//
/*
struct FilePos : GNewOverrideBase<GStat_Sound_Mem>
{
UInt pos;
};
FMOD_RESULT F_CALLBACK OpenFile(const char *sd, int unicode, unsigned int *filesize, void **handle, void **userdata)
{
GUNUSED2(unicode, userdata);
GSoundData* psoundData = (GSoundData*)sd;
*filesize = psoundData->GetDataSize();
FilePos* fp = new FilePos;
fp->pos = 0;
*handle = fp;
*userdata = psoundData;
return FMOD_OK;
}
FMOD_RESULT F_CALLBACK CloseFile(void *handle, void *userdata)
{
GUNUSED(userdata);
if (!handle)
{
return FMOD_ERR_INVALID_PARAM;
}
FilePos* fp = (FilePos*) handle;
delete fp;
return FMOD_OK;
}
FMOD_RESULT F_CALLBACK ReadFile(void *handle, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata)
{
if (!handle)
{
return FMOD_ERR_INVALID_PARAM;
}
if (bytesread)
{
FilePos* fp = (FilePos*) handle;
GSoundData* psoundData = (GSoundData*)userdata;
if ((fp->pos + sizebytes) > psoundData->GetDataSize())
sizebytes = psoundData->GetDataSize() - fp->pos;
if (sizebytes == 0)
{
*bytesread = 0;
return FMOD_ERR_FILE_EOF;
}
memcpy(buffer, psoundData->GetData() + fp->pos, sizebytes);
fp->pos += sizebytes;
*bytesread = sizebytes;
}
return FMOD_OK;
}
FMOD_RESULT F_CALLBACK SeekInFile(void *handle, unsigned int pos, void *userdata)
{
if (!handle)
{
return FMOD_ERR_INVALID_PARAM;
}
FilePos* fp = (FilePos*) handle;
GSoundData* psoundData = (GSoundData*)userdata;
if (pos >= psoundData->GetDataSize())
return FMOD_ERR_FILE_COULDNOTSEEK;
fp->pos = pos;
return FMOD_OK;
}
FMOD_RESULT GSoundSampleFMODImpl::CreateSubSound(GSoundData* psd, FMOD::Sound** psound)
{
FMOD_CREATESOUNDEXINFO exinfo;
GMemUtil::Set(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
exinfo.useropen = &OpenFile;
exinfo.userclose= &CloseFile;
exinfo.userread = &ReadFile;
exinfo.userseek = &SeekInFile;
int flags = FMOD_LOWMEM | FMOD_SOFTWARE | FMOD_IGNORETAGS;
switch (psd->GetFormat() & GSoundData::Sample_Format)
{
case GSoundData::Sample_PCM:
exinfo.format = (psd->GetFormat() & 0x7) == 2 ? FMOD_SOUND_FORMAT_PCM16 : FMOD_SOUND_FORMAT_PCM8;
exinfo.defaultfrequency = psd->GetRate();
exinfo.numchannels = (psd->GetFormat() & GSoundData::Sample_Stereo) ? 2 : 1;
flags |= FMOD_OPENRAW;
break;
case GSoundData::Sample_MP3:
exinfo.format = FMOD_SOUND_FORMAT_MPEG;
break;
default:
return FMOD_ERR_FORMAT;
}
FMOD_RESULT result = pPlayer->pDevice->createSound((const char*)psd, flags, &exinfo, psound);
return result;
}
*/
FMOD_RESULT GSoundSampleFMODImpl::CreateSubSound(GSoundData* psd, FMOD::Sound** psound)
{
FMOD_CREATESOUNDEXINFO exinfo;
GMemUtil::Set(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
int flags = FMOD_LOWMEM | FMOD_SOFTWARE | FMOD_IGNORETAGS | FMOD_OPENMEMORY_POINT;
switch (psd->GetFormat() & GSoundData::Sample_Format)
{
case GSoundData::Sample_PCM:
exinfo.format = (psd->GetFormat() & 0x7) == 2 ? FMOD_SOUND_FORMAT_PCM16 : FMOD_SOUND_FORMAT_PCM8;
exinfo.defaultfrequency = psd->GetRate();
exinfo.numchannels = (psd->GetFormat() & GSoundData::Sample_Stereo) ? 2 : 1;
flags |= FMOD_OPENRAW;
break;
case GSoundData::Sample_MP3:
flags |= FMOD_CREATECOMPRESSEDSAMPLE;
exinfo.format = FMOD_SOUND_FORMAT_MPEG;
break;
default:
return FMOD_ERR_FORMAT;
}
exinfo.length = psd->GetDataSize();
FMOD_RESULT result = pPlayer->pDevice->createSound((const char*)psd->GetData(), flags, &exinfo, psound);
return result;
}
FMOD_RESULT GSoundSampleFMODImpl::CreateSubSound(GAppendableSoundData* psd, FMOD::Sound** psound)
{
FMOD_CREATESOUNDEXINFO exinfo;
GMemUtil::Set(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
exinfo.useropen = &DecodeOpen;
exinfo.userclose= &DecodeClose;
exinfo.userread = &DecodeRead;
exinfo.userseek = &DecodeSeek;
exinfo.decodebuffersize = 1024 * 4;
int flags = FMOD_LOWMEM | FMOD_SOFTWARE | FMOD_CREATESTREAM | FMOD_IGNORETAGS;
switch (psd->GetFormat() & GSoundData::Sample_Format)
{
case GSoundData::Sample_PCM:
exinfo.format = (psd->GetFormat() & 0x7) == 2 ? FMOD_SOUND_FORMAT_PCM16 : FMOD_SOUND_FORMAT_PCM8;
exinfo.defaultfrequency = psd->GetRate();
exinfo.numchannels = (psd->GetFormat() & GSoundData::Sample_Stereo) ? 2 : 1;
flags |= FMOD_OPENRAW;
break;
case GSoundData::Sample_MP3:
exinfo.defaultfrequency = psd->GetRate();
exinfo.format = FMOD_SOUND_FORMAT_MPEG;
break;
default:
return FMOD_ERR_FORMAT;
}
exinfo.initialseekposition = psd->GetSeekSample();
exinfo.initialseekpostype = FMOD_TIMEUNIT_PCM;
FMOD_RESULT result = pPlayer->pDevice->createSound((const char*)psd, flags, &exinfo, psound);
return result;
}
FMOD_RESULT GSoundSampleFMODImpl::CreateSubSound(GSoundFile* psd, FMOD::Sound** psound)
{
int flags = FMOD_SOFTWARE;
if (psd->IsStreamSample())
flags |= FMOD_CREATESTREAM;
else
flags |= FMOD_ACCURATETIME;
FMOD_RESULT result = pPlayer->pDevice->createSound(psd->GetFileName(), flags, NULL, psound);
if (result != FMOD_OK)
{
pPlayer->LogError(result);
*psound = NULL;
return result;
}
return result;
}
GSoundChannelFMODImpl* GSoundSampleFMODImpl::Start(bool paused)
{
if (pSound)
{
UInt latency = pSoundData ? pSoundData->GetSeekSample() : 0;
UInt scount = pSoundData ? pSoundData->GetSampleCount() : 0;
if (scount == 0)
pSound->getLength(&scount, FMOD_TIMEUNIT_PCM);
Float sample_rate;
pSound->getDefaults(&sample_rate,NULL,NULL,NULL);
FMOD::Channel* pchan;
FMOD_RESULT r;
r = pPlayer->pDevice->playSound(FMOD_CHANNEL_FREE, pSound, true, &pchan);
if (r == FMOD_OK)
{
UInt hi = 0;
UInt lo = 0;
r = pPlayer->pDevice->getDSPClock(&hi, &lo);
scount = UInt(scount * pPlayer->SystemBitRate/sample_rate);
FMOD_64BIT_ADD(hi, lo, 0, scount);
r = pchan->setDelay(FMOD_DELAYTYPE_DSPCLOCK_END, hi, lo);
if (latency > 0)
{
r = pchan->setPosition(latency, FMOD_TIMEUNIT_PCM);
if (r == FMOD_OK)
r = pchan->setPaused(paused);
}
}
if (r != FMOD_OK)
{
pPlayer->LogError(r);
return NULL;
}
GSoundChannelFMODImpl* pchannel = GNEW GSoundChannelFMODImpl(pPlayer, this, pchan);
return pchannel;
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////
//
inline GSoundRenderer* GSoundChannelFMODImpl::GetSoundRenderer() const
{
return pPlayer;
}
GSoundChannelFMODImpl::GSoundChannelFMODImpl(GSoundRendererFMODImpl* pp, GSoundSampleFMODImpl* ps, FMOD::Channel* pc)
: GSoundChannelImplNode(&pp->SampleList)
{
pPlayer = pp;
pSample = ps;
pChan = pc;
pChan->setUserData(this);
pChan->setCallback(CallBackFunc);
}
GSoundChannelFMODImpl::~GSoundChannelFMODImpl()
{
ReleaseFMODObjects();
}
void GSoundChannelFMODImpl::ReleaseResource()
{
if (GetRefCount() > 0)
ReleaseFMODObjects();
pPlayer = NULL;
if (pNext)
RemoveNode();
}
void GSoundChannelFMODImpl::ReleaseFMODObjects()
{
/*
for (GHash<FMOD_SYNCPOINT*, Transform>::ConstIterator i = Tranforms.Begin();
i != Tranforms.End(); ++i)
{
FMOD_SYNCPOINT* psync = i->First;
FMOD_RESULT r = pSample && pSample->pSound ? pSample->pSound->deleteSyncPoint(psync) : FMOD_OK;
GASSERT(r == FMOD_OK);
}
*/
if (pChan)
{
Stop();
pChan->setCallback(NULL);
pChan->setUserData(NULL);
pChan = NULL;
}
}
void GSoundChannelFMODImpl::Stop()
{
if (pChan)
{
FMOD_RESULT r = pChan->stop();
pPlayer->LogError(r);
pChan = NULL;
}
}
void GSoundChannelFMODImpl::Pause(bool pause)
{
if (pChan)
{
FMOD_RESULT r = pChan->setPaused(pause);
pPlayer->LogError(r);
}
}
bool GSoundChannelFMODImpl::IsPlaying() const
{
bool res = false;
if (pChan)
{
FMOD_RESULT r = pChan->isPlaying(&res);
pPlayer->LogError(r);
}
return res;
}
void GSoundChannelFMODImpl::SetPosition(Float seconds)
{
UInt ms = UInt(seconds * 1000.0f);
if (pChan)
{
UInt latency = pSample && pSample->pSoundData ?
pSample->pSoundData->GetSeekSample() * 1000 / pSample->pSoundData->GetRate():
0;
FMOD_RESULT r = pChan->setPosition(ms+latency, FMOD_TIMEUNIT_MS);
pPlayer->LogError(r);
}
}
Float GSoundChannelFMODImpl::GetPosition()
{
UInt pos = 0;
UInt latency = 0;
if (pChan)
{
latency = pSample && pSample->pSoundData ?
pSample->pSoundData->GetSeekSample() * 1000 / pSample->pSoundData->GetRate():
0;
FMOD_RESULT r;
r = pChan->getPosition(&pos, FMOD_TIMEUNIT_MS);
pPlayer->LogError(r);
UInt pcmbytes = 0;
r = pChan->getPosition(&pcmbytes, FMOD_TIMEUNIT_PCMBYTES);
pPlayer->LogError(r);
}
return Float((pos-latency)/1000.0f);
}
void GSoundChannelFMODImpl::Loop(SInt count, Float start, Float end)
{
GASSERT(pSample);
if (!pChan || count == 0)
return;
FMOD_RESULT result;
if (count > 1)
{
pChan->setMode(FMOD_LOOP_NORMAL);
pChan->setLoopCount(count);
}
UInt seekpos = pSample->pSoundData ? pSample->pSoundData->GetSeekSample() : 0;
UInt scount = pSample->pSoundData ? pSample->pSoundData->GetSampleCount() : 0;
UInt slen = 0;
pSample->pSound->getLength(&slen, FMOD_TIMEUNIT_PCM);
Float sample_rate;
pSample->pSound->getDefaults(&sample_rate,NULL,NULL,NULL);
UInt start_pcm = start > 0.0f? UInt(start * sample_rate) : seekpos;
UInt end_pcm = UInt(end * sample_rate);
if (end_pcm == 0 || end_pcm > slen - 1)
{
if (scount > 0)
end_pcm = start_pcm + scount;
else
end_pcm = slen - 1;
}
result = pChan->setPosition(start_pcm, FMOD_TIMEUNIT_PCM);
result = pChan->setLoopPoints(start_pcm, FMOD_TIMEUNIT_PCM, end_pcm, FMOD_TIMEUNIT_PCM);
UInt hi = 0;
UInt lo = 0;
result = pPlayer->pDevice->getDSPClock(&hi, &lo);
slen = UInt((end_pcm - start_pcm) * count * (pPlayer->SystemBitRate/sample_rate));
FMOD_64BIT_ADD(hi, lo, 0, slen);
result = pChan->setDelay(FMOD_DELAYTYPE_DSPCLOCK_END, hi, lo);
}
void GSoundChannelFMODImpl::SetVolume(Float volume)
{
if (pChan)
{
FMOD_RESULT r = pChan->setVolume(volume);
pPlayer->LogError(r);
}
}
Float GSoundChannelFMODImpl::GetVolume()
{
float v = 1.0f;
if (pChan)
{
FMOD_RESULT r = pChan->getVolume(&v);
pPlayer->LogError(r);
}
return v;
}
Float GSoundChannelFMODImpl::GetPan()
{
float pan = 0.0f;
if (pChan)
{
FMOD_RESULT r = pChan->getPan(&pan);
pPlayer->LogError(r);
}
return pan;
}
void GSoundChannelFMODImpl::SetPan(Float pan)
{
if (pChan)
{
FMOD_RESULT r = pChan->setPan(pan);
pPlayer->LogError(r);
}
}
#define deltaT 0.1f
#define deltaV 0.1f
static inline void s_SetChannelPan(FMOD::Channel* pchan, const GSoundChannel::Transform& transform)
{
FMOD_RESULT r;
r = pchan->setVolume((transform.LeftVolume + transform.RightVolume)/2);
float levels[2];
levels[0] = transform.LeftVolume;
levels[1] = transform.RightVolume;
float pan = fabs(transform.LeftVolume - transform.RightVolume);
if (transform.LeftVolume > transform.RightVolume)
pan *= -1;
r = pchan->setPan(pan);
GUNUSED(r);
}
void GSoundChannelFMODImpl::SetTransforms(const GArray<Transform>& transforms)
{
UPInt size = transforms.GetSize();
if (size == 0)
return;
if (!pSample || !pSample->pSound)
return;
pChan->setCallback(CallBackFunc);
for (UInt i = 0; i < size; ++i)
{
if (i == 0)
{
s_SetChannelPan(pChan, transforms[0]);
continue;
}
Float ar = (transforms[i-1].RightVolume - transforms[i].RightVolume) /
(transforms[i-1].Position - transforms[i].Position);
Float al = (transforms[i-1].LeftVolume - transforms[i].LeftVolume) /
(transforms[i-1].Position - transforms[i].Position);
Float br = transforms[i-1].RightVolume - ar * transforms[i-1].Position;
Float bl = transforms[i-1].LeftVolume - al * transforms[i-1].Position;
bool done = false;
for(UInt j = 1; !done ; j++)
{
Transform tr;
if (transforms[i].Position < transforms[i-1].Position + deltaT * j)
{
tr = transforms[i];
done = true;
}
else
{
tr.Position = transforms[i-1].Position + deltaT * j;
tr.RightVolume = ar * tr.Position + br;
tr.LeftVolume = al * tr.Position + bl;
}
FMOD_SYNCPOINT* psync;
FMOD_RESULT r = pSample->pSound->addSyncPoint(UInt(tr.Position * 1000.0f), FMOD_TIMEUNIT_MS, "", &psync);
if (r == FMOD_OK)
{
Tranforms.Add(psync, tr);
}
else
pPlayer->LogError(r);
}
}
}
FMOD_RESULT F_CALLBACK GSoundChannelFMODImpl::CallBackFunc(
FMOD_CHANNEL* pchan, FMOD_CHANNEL_CALLBACKTYPE cb, void* cmddata1, void *cmddata2)
{
GUNUSED(cmddata2);
void *vp;
((FMOD::Channel *)pchan)->getUserData(&vp);
if (!vp)
return FMOD_OK;
GSoundChannelFMODImpl* pChan = (GSoundChannelFMODImpl *)vp;
FMOD_RESULT r;
if (cb == FMOD_CHANNEL_CALLBACKTYPE_SYNCPOINT)
{
if (pChan && pChan->pSample && pChan->pSample->pSound)
{
FMOD_SYNCPOINT* psync;
r = pChan->pSample->pSound->getSyncPoint((int)(SPInt)cmddata1, &psync);
if (r == FMOD_OK)
{
GSoundChannel::Transform* tr = pChan->Tranforms.Get(psync);
if (tr)
s_SetChannelPan(pChan->pChan, *tr);
}
else
pChan->pPlayer->LogError(r);
}
}
else if (cb == FMOD_CHANNEL_CALLBACKTYPE_END)
{
}
return FMOD_OK;
}
//////////////////////////////////////////////////////////////////////////
//
GSoundRendererFMOD* GCDECL GSoundRendererFMOD::CreateSoundRenderer()
{
return new GSoundRendererFMODImpl;
}
#endif // GFC_NO_SOUND
#endif