Files
FC1/CrySoundSystem/OGGDecoder.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

263 lines
7.3 KiB
C++

#include "StdAfx.h"
#include <CrySizer.h>
#include <ISound.h>
#include <ISystem.h>
#include <ICryPak.h>
#include "oggdecoder.h"
#include "MusicSystem.h"
COGGDecoder::COGGDecoder(IMusicSystem *pMusicSystem)
{
m_pMusicSystem=pMusicSystem;
ISystem *pSystem=m_pMusicSystem->GetSystem();
ASSERT(pSystem);
m_FileAccess.pPak=pSystem->GetIPak();
ASSERT(m_FileAccess.pPak);
}
COGGDecoder::~COGGDecoder()
{
Close();
}
void getTicks(int64* pnTime)
{
#ifdef WIN64
*pnTime = __rdtsc();
#else
__asm {
mov ebx, pnTime
rdtsc
mov [ebx], eax
mov [ebx+4], edx
}
#endif
}
bool COGGDecoder::Open(const char *pszFilename)
{
if (m_FileAccess.pFile)
return false;
m_FileAccess.pFile=m_FileAccess.pPak->FOpen(pszFilename, "rb");
if (!m_FileAccess.pFile)
return false;
OggVorbis_File TestOVFile;
ov_callbacks Callbacks;
Callbacks.read_func=COGGDecoderInstance::ReadFile;
Callbacks.seek_func=COGGDecoderInstance::SeekFile;
Callbacks.close_func=COGGDecoderInstance::CloseFile;
Callbacks.tell_func=COGGDecoderInstance::TellFile;
if (ov_open_callbacks(&m_FileAccess, &TestOVFile, NULL, 0, Callbacks)!=0)
return false;
int nSeekable=ov_seekable(&TestOVFile);
m_FileInfo.sFilename=pszFilename;
m_FileInfo.nHeaderSize=0;
m_FileInfo.nSamples=(int)ov_pcm_total(&TestOVFile, -1);
// Throw the comments plus a few lines about the bitstream we're decoding
MTRACE("File-information of %s...", m_FileInfo.sFilename.c_str());
vorbis_comment *pComment=ov_comment(&TestOVFile, -1);
vorbis_info *pInfo=ov_info(&TestOVFile, -1);
char **pPtr=pComment->user_comments;
while (*pPtr)
{
MTRACE(" %s", *pPtr);
pPtr++;
}
MTRACE(" Bitstream is %d channel, %ld Hz, %d kbit/s", pInfo->channels, pInfo->rate, pInfo->bitrate_nominal/1000);
MTRACE(" Encoded by: %s", pComment->vendor);
if (pInfo->rate!=44100)
{
MTRACE("Warning: [COGGDecoder::Open()] Cannot load file %s, due to unsupported samplerate (%d Hz found; 44100 Hz needed) !", m_FileInfo.sFilename.c_str(), pInfo->rate);
return false;
}
if (pInfo->channels!=2)
{
MTRACE("Warning: [COGGDecoder::Open()] Cannot load file %s, due to unsupported channel-number (%d channels found; 2 channels needed) !", m_FileInfo.sFilename.c_str(), pInfo->channels);
return false;
}
if (ov_clear(&TestOVFile)!=0)
return false;
if (!nSeekable)
return false;
return true;
}
bool COGGDecoder::Close()
{
if (!m_FileAccess.pFile)
return false;
m_FileAccess.pPak->FClose(m_FileAccess.pFile);
m_FileAccess.pFile=NULL;
return true;
}
bool COGGDecoder::GetFileInfo(SMusicPatternFileInfo &FileInfo)
{
if (!m_FileAccess.pFile)
return false;
FileInfo=m_FileInfo;
return true;
}
void COGGDecoder::GetMemoryUsage(class ICrySizer* pSizer)
{
if (!pSizer->Add(*this))
return;
}
IMusicPatternDecoderInstance* COGGDecoder::CreateInstance()
{
return new COGGDecoderInstance(this);
}
//////////////////////////////////////////////////////////////////////////
/*** INSTANCE ***/
//////////////////////////////////////////////////////////////////////////
size_t COGGDecoderInstance::ReadFile(void *ptr, size_t size, size_t nmemb, void *datasource)
{
SFileAccess *pFileAccess=(SFileAccess*)datasource;
return pFileAccess->pPak->FRead(ptr, size, nmemb, pFileAccess->pFile);
}
int COGGDecoderInstance::SeekFile(void *datasource, ogg_int64_t offset, int whence)
{
SFileAccess *pFileAccess=(SFileAccess*)datasource;
return pFileAccess->pPak->FSeek(pFileAccess->pFile, (long)offset, whence);
}
int COGGDecoderInstance::CloseFile(void *datasource)
{
return 0;
}
long COGGDecoderInstance::TellFile(void *datasource)
{
SFileAccess *pFileAccess=(SFileAccess*)datasource;
return pFileAccess->pPak->FTell(pFileAccess->pFile);
}
COGGDecoderInstance::COGGDecoderInstance(COGGDecoder *pDecoder)
{
// int64 nStartTime, nEndTime;
// getTicks(&nStartTime);
ASSERT(pDecoder);
m_pDecoder=pDecoder;
m_pDecoder->m_FileAccess.pPak->FSeek(m_pDecoder->m_FileAccess.pFile, 0, SEEK_SET);
ov_callbacks Callbacks;
Callbacks.read_func=ReadFile;
Callbacks.seek_func=SeekFile;
Callbacks.close_func=CloseFile;
Callbacks.tell_func=TellFile;
if (ov_open_callbacks(&(m_pDecoder->m_FileAccess), &m_OVFile, NULL, 0, Callbacks)!=0)
MTRACE("Warning: [COGGDecoderInstance::c_tor()] ov_open() failed !");
// Seek0 is quite expensive for OGGs so we do a simpler version here instead...
m_nPos=0;
m_nPosBytes=m_pDecoder->m_FileAccess.pPak->FTell(m_pDecoder->m_FileAccess.pFile); // store current file-cursor
// getTicks(&nEndTime);
// MTRACE("Info: [COGGDecoderInstance::c_tor()] It took %d cycles to initialize ogg-decoder-instance of %s.", (long)(nEndTime-nStartTime), m_pDecoder->m_FileInfo.sFilename.c_str());
}
COGGDecoderInstance::~COGGDecoderInstance()
{
if (ov_clear(&m_OVFile)!=0)
MTRACE("Warning: [COGGDecoderInstance::d_tor()] ov_clear() failed !");
}
bool COGGDecoderInstance::Seek0(int nDelay)
{
int nOldPos=m_nPos;
m_nPos=-nDelay;
if (nOldPos>0) // only seek if it is really necessary, since it takes some time on OGGs...
{
if (ov_pcm_seek(&m_OVFile, 0)!=0)
return false;
m_nPosBytes=m_pDecoder->m_FileAccess.pPak->FTell(m_pDecoder->m_FileAccess.pFile); // store current file-cursor
}
return true;
}
int COGGDecoderInstance::GetPos()
{
if (!m_pDecoder->m_FileAccess.pFile)
return -1;
return m_nPos;
}
bool COGGDecoderInstance::GetPCMData(signed long *pDataOut, int nSamples, bool bLoop)
{
if (!m_pDecoder->m_FileAccess.pFile)
return false;
int nOfs=0;
if (m_nPos<0)
{
if (-m_nPos>=nSamples)
{
memset(pDataOut, 0, nSamples*m_pDecoder->m_pMusicSystem->GetBytesPerSample());
m_nPos+=nSamples;
return true;
}else
{
nOfs=-m_nPos;
m_nPos=0;
nSamples-=nOfs;
memset(pDataOut, 0, nOfs*m_pDecoder->m_pMusicSystem->GetBytesPerSample());
}
}
// prepare file-cursor
m_pDecoder->m_FileAccess.pPak->FSeek(m_pDecoder->m_FileAccess.pFile, m_nPosBytes, SEEK_SET);
int nSamplesToRead;
for (;;)
{
if ((m_nPos+nSamples)>m_pDecoder->m_FileInfo.nSamples)
nSamplesToRead=m_pDecoder->m_FileInfo.nSamples-m_nPos;
else
nSamplesToRead=nSamples;
if (!FillPCMBuffer(&(pDataOut[nOfs]), nSamplesToRead))
return false;
m_nPos+=nSamplesToRead;
if (nSamplesToRead==nSamples)
break;
nOfs+=nSamplesToRead;
if (!bLoop)
{
memset(&(pDataOut[nOfs]), 0, (nSamples-nSamplesToRead)*m_pDecoder->m_pMusicSystem->GetBytesPerSample());
break;
}
nSamples-=nSamplesToRead;
Seek0();
}
// store file-cursor
m_nPosBytes=m_pDecoder->m_FileAccess.pPak->FTell(m_pDecoder->m_FileAccess.pFile);
return true;
}
bool COGGDecoderInstance::FillPCMBuffer(signed long *pBuffer, int nSamples)
{
int nBytesToRead=nSamples*m_pDecoder->m_pMusicSystem->GetBytesPerSample();
int nBytesRead=0;
while (nBytesToRead>nBytesRead)
{
int nCurrStream=0;
long nRet=ov_read(&m_OVFile, &((char*)pBuffer)[nBytesRead], nBytesToRead-nBytesRead, 0, 2, 1, &nCurrStream);
if (nRet==OV_HOLE)
{
MTRACE("Warning: [COGGDecoderInstance::FillPCMBuffer] Hole found in stream %s !", m_pDecoder->m_FileInfo.sFilename.c_str());
return false;
}else
if (nRet==OV_EBADLINK)
{
MTRACE("Warning: [COGGDecoderInstance::FillPCMBuffer] Bad link found in stream %s !", m_pDecoder->m_FileInfo.sFilename.c_str());
return false;
}else
if (nRet==0)
{
MTRACE("Warning: [COGGDecoderInstance::FillPCMBuffer] Unexpected end of stream %s !", m_pDecoder->m_FileInfo.sFilename.c_str());
return false;
}else
{
nBytesRead+=nRet;
}
}
return true;
}