home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Game Audio Programming
/
GameAudioProgramming.iso
/
Game_Audio
/
audio_sdk
/
src
/
AudioLib
/
WMA.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2002-07-15
|
17KB
|
715 lines
/***********************************************************\
Copyright (C) James Boer, 2002.
All rights reserved worldwide.
This software is provided "as is" without express or implied
warranties. You may freely copy and compile this source into
applications you distribute provided that the copyright text
below is included in the resulting source code, for example:
"Portions Copyright (C) James Boer, 2002"
\***********************************************************/
#include "Audio.h"
#include "WMA.h"
#include "AudioMgr.h"
#ifdef USE_WMA
using namespace std;
using namespace Audio;
const uint32 DECODING_BUFFER_ERROR = 8192;
//------------------------------------------------------------------------//
WMACallback::WMACallback()
{
Clear();
}
//------------------------------------------------------------------------//
WMACallback::~WMACallback()
{
assert(!m_iRefCount);
}
//------------------------------------------------------------------------//
void WMACallback::Clear()
{
m_iRefCount = 0;
}
//------------------------------------------------------------------------//
HRESULT WMACallback::QueryInterface(
const struct _GUID& guid,
void** ppInterface)
{
if ( guid == IID_IWMReaderCallback )
*ppInterface = ( IWMReaderCallback* )this;
else if( guid == IID_IWMReaderCallbackAdvanced )
*ppInterface = ( IWMReaderCallbackAdvanced* )this;
else
return E_NOINTERFACE;
return S_OK;
}
//------------------------------------------------------------------------//
ULONG WMACallback::AddRef()
{
return 1;
}
//------------------------------------------------------------------------//
ULONG WMACallback::Release()
{
return 1;
}
//------------------------------------------------------------------------//
HRESULT WMACallback::OnStatus(
WMT_STATUS Status,
HRESULT hr,
WMT_ATTR_DATATYPE dwType,
BYTE* pValue,
void* pvContext)
{
if(!pvContext)
return E_FAIL;
WMA* pWMA = static_cast<WMA*>(pvContext);
return pWMA->OnStatus(Status, hr, dwType, pValue);
}
//------------------------------------------------------------------------//
HRESULT WMACallback::OnSample(
DWORD dwOutputNum,
QWORD cnsSampleTime,
QWORD cnsSampleDuration,
DWORD dwFlags,
INSSBuffer* pSample,
void* pvContext)
{
if(!pvContext)
return E_FAIL;
WMA* pWMA = static_cast<WMA*>(pvContext);
return pWMA->OnSample(dwOutputNum, cnsSampleTime, cnsSampleDuration, dwFlags, pSample);
}
//------------------------------------------------------------------------//
HRESULT WMACallback::OnStreamSample(
WORD wStreamNum,
QWORD cnsSampleTime,
QWORD cnsSampleDuration,
DWORD dwFlags,
INSSBuffer __RPC_FAR *pSample,
void __RPC_FAR *pvContext)
{
return E_NOTIMPL;
}
//------------------------------------------------------------------------//
HRESULT WMACallback::OnTime(
QWORD qwCurrentTime,
void __RPC_FAR *pvContext)
{
if(!pvContext)
return E_FAIL;
WMA* pWMA = static_cast<WMA*>(pvContext);
return pWMA->OnTime(qwCurrentTime);
}
//------------------------------------------------------------------------//
HRESULT WMACallback::OnStreamSelection(
WORD wStreamCount,
WORD __RPC_FAR *pStreamNumbers,
WMT_STREAM_SELECTION __RPC_FAR *pSelections,
void __RPC_FAR *pvContext)
{
return S_OK;
}
//------------------------------------------------------------------------//
HRESULT WMACallback::OnOutputPropsChanged(
DWORD dwOutputNum,
WM_MEDIA_TYPE __RPC_FAR *pMediaType,
void __RPC_FAR *pvContext )
{
return S_OK;
}
//------------------------------------------------------------------------//
HRESULT WMACallback::AllocateForOutput(
DWORD dwOutputNum,
DWORD cbBuffer,
INSSBuffer __RPC_FAR *__RPC_FAR *ppBuffer,
void __RPC_FAR *pvContext)
{
return E_NOTIMPL;
}
//------------------------------------------------------------------------//
HRESULT WMACallback::AllocateForStream(
WORD wStreamNum,
DWORD cbBuffer,
INSSBuffer __RPC_FAR *__RPC_FAR *ppBuffer,
void __RPC_FAR *pvContext)
{
return E_NOTIMPL;
}
IMPLEMENT_POOL(WMA);
//------------------------------------------------------------------------//
WMA::WMA()
{
FN("WMA::WMA()");
m_pBuffer = 0;
m_nBufferSize = 0;
Clear();
}
//------------------------------------------------------------------------//
WMA::~WMA()
{
FN("WMA::~WMA()");
Close();
SAFE_DELETE_ARRAY(m_pBuffer);
m_nBufferSize = 0;
}
//------------------------------------------------------------------------//
void WMA::Clear()
{
FN("WMA::Clear()");
m_pReader = 0;
m_pReaderAdvanced = 0;
m_pReaderAdvanced2 = 0;
m_pHeaderInfo = 0;
ZeroMemory(&m_WaveFormatEx, sizeof(WAVEFORMATEX));
m_hWaitEvent = 0;
m_hRespondEvent = 0;
m_bOpen = false;
m_Callback.Clear();
m_hrCallbackResult = 0;
m_nStreamSize = 0;
m_nBytesRead = 0;
m_bFirstRead = true;
m_nTargetPtr = 0;
m_nWritePtr = 0;
m_bEOF = false;
m_qwTime = 0;
ZeroMemory(&m_csTerm, sizeof(CRITICAL_SECTION));
}
//------------------------------------------------------------------------//
bool WMA::PreOpen()
{
InitializeCriticalSection(&m_csTerm);
m_hWaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_hRespondEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
HRESULT hr = WMCreateReader(0, 0, &m_pReader);
if(FAILED(hr))
return false;
hr = m_pReader->QueryInterface(
IID_IWMReaderAdvanced,
(void**)&m_pReaderAdvanced);
if(FAILED(hr))
return false;
hr = m_pReaderAdvanced->QueryInterface(
IID_IWMReaderAdvanced2,
(void**)&m_pReaderAdvanced2);
if(FAILED(hr))
return false;
hr = m_pReader->QueryInterface(
IID_IWMHeaderInfo,
(void**)&m_pHeaderInfo);
if(FAILED(hr))
return false;
return true;
}
//------------------------------------------------------------------------//
// This Open function is used if the source is either streaming or a
// one-shot load. It also is used for custom file systems.
bool WMA::Open(string sFileName)
{
FN("WMA::Open()");
if(!PreOpen())
return false;
IAudioStream* pStream;
CreateAudioStream cas(pStream);
if(!pStream)
return false;
HRESULT hr = pStream->Open(sFileName);
if(FAILED(hr))
return false;
hr = m_pReaderAdvanced2->OpenStream(pStream, &m_Callback, this);
if(FAILED(hr))
return false;
if(!PostOpen())
return false;
return true;
}
//------------------------------------------------------------------------//
// This Open function is used for loading directly from memory
bool WMA::Open(uint8* pbData, uint32 dwDataSize)
{
FN("WMA::Open()");
if(!PreOpen())
return false;
HGLOBAL hMem;
IStream* pStream;
hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD | GMEM_SHARE, dwDataSize);
LPVOID pMem = GlobalLock(hMem);
CopyMemory(pMem, pbData, dwDataSize);
GlobalUnlock(hMem);
HRESULT hr = CreateStreamOnHGlobal(hMem, TRUE, &pStream);
if(FAILED(hr))
return false;
hr = m_pReaderAdvanced2->OpenStream(pStream, &m_Callback, this);
if(FAILED(hr))
return false;
if(!PostOpen())
return false;
return true;
}
//------------------------------------------------------------------------//
bool WMA::PostOpen()
{
WaitForSingleObject(m_hRespondEvent, INFINITE);
if(FAILED(m_hrCallbackResult))
return false;
uint32 nOutputCount;
HRESULT hr = m_pReader->GetOutputCount(&nOutputCount);
if(FAILED(hr))
return false;
if(nOutputCount != 1)
return false;
uint32 nOutputFormatCount;
hr = m_pReader->GetOutputFormatCount(0, &nOutputFormatCount);
if(FAILED(hr))
return false;
uint32 nFormatSize = 0;
BYTE* pBuf = 0;
IWMOutputMediaProps* pProps;
for(uint32 j = 0; j < nOutputFormatCount; j++)
{
hr = m_pReader->GetOutputFormat(0, j, &pProps);
if(FAILED(hr))
continue;
// Get the required size of the media type structure
uint32 nNewSize = 0;
hr = pProps->GetMediaType(NULL, &nNewSize);
if(FAILED(hr))
continue;
if(nNewSize > nFormatSize)
{
SAFE_DELETE_ARRAY(pBuf);
nFormatSize = nNewSize;
pBuf = new BYTE[nFormatSize];
}
WM_MEDIA_TYPE* pType = (WM_MEDIA_TYPE*) pBuf;
hr = pProps->GetMediaType(pType, &nFormatSize);
if(FAILED(hr))
continue;
if(pType->formattype == WMFORMAT_WaveFormatEx)
{
memcpy(&m_WaveFormatEx, pType->pbFormat, pType->cbFormat);
if((m_WaveFormatEx.nChannels == 2) &&
(m_WaveFormatEx.wBitsPerSample == DXAudioMgr()->GetOptimalSampleBits()) &&
(m_WaveFormatEx.nSamplesPerSec == DXAudioMgr()->GetOptimalSampleRate()))
{
break;
}
}
SAFE_RELEASE(pProps);
}
SAFE_DELETE_ARRAY(pBuf);
// Now set the format we want
hr = m_pReader->SetOutputProps(0, pProps);
if(FAILED(hr))
return false;
SAFE_RELEASE(pProps);
// Tell the reader to read as fast as possible
hr = m_pReaderAdvanced->SetUserProvidedClock(true);
if(FAILED(hr))
return false;
// Determine the size of the opened file
WORD wStreamNum = 0;
WMT_ATTR_DATATYPE Type;
DWORD dwDuration = 0;
WORD wLength = 8;
hr = m_pHeaderInfo->GetAttributeByName(
&wStreamNum,
g_wszWMDuration,
&Type,
(unsigned char*)&dwDuration,
&wLength);
if(FAILED(hr))
return false;
// divide by 10 million to get seconds
double fTime = double(dwDuration) / 10000000.0f;
// Calculate the stream size
m_nStreamSize = fTime * m_WaveFormatEx.nAvgBytesPerSec;
// Create a default one-point-five second scratch buffer for decoding streams
if(!m_pBuffer)
{
m_nBufferSize = m_WaveFormatEx.nAvgBytesPerSec * 1.5;
m_pBuffer = new uint8[m_nBufferSize];
}
m_nTargetPtr = 0;
m_nWritePtr = 0;
m_bOpen = true;
return true;
}
//------------------------------------------------------------------------//
bool WMA::Close()
{
FN("WMA::Close()");
m_bOpen = false;
if(m_hWaitEvent)
{
if(m_pReader)
m_pReader->Stop();
SetEvent(m_hWaitEvent);
EnterCriticalSection(&m_csTerm);
LeaveCriticalSection(&m_csTerm);
DeleteCriticalSection(&m_csTerm);
}
if(m_hWaitEvent)
{
CloseHandle(m_hWaitEvent);
m_hWaitEvent = 0;
}
if(m_hRespondEvent)
{
CloseHandle(m_hRespondEvent);
m_hRespondEvent = 0;
}
SAFE_RELEASE(m_pHeaderInfo);
SAFE_RELEASE(m_pReaderAdvanced2);
SAFE_RELEASE(m_pReaderAdvanced);
SAFE_RELEASE(m_pReader);
Clear();
return true;
}
//------------------------------------------------------------------------//
bool WMA::Read(uint8* pBuffer, uint32 dwSizeToRead, uint32* pdwSizeRead )
{
FN("WMA::Read()");
if(!m_bOpen)
return false;
DebugOut(5, "Reading %d bytes from WMA file", dwSizeToRead);
m_nTargetPtr = dwSizeToRead - m_nWritePtr;
// First, we have to check to see if we have enough extra data
// stored in the buffer to satisfy the read without doing
// any decoding.
if(dwSizeToRead > m_nWritePtr)
{
if(m_bFirstRead)
{
// Grow the decoding buffer if necessary
if(dwSizeToRead > m_nBufferSize - DECODING_BUFFER_ERROR)
{
SAFE_DELETE(m_pBuffer);
m_nBufferSize = dwSizeToRead;
m_pBuffer = new uint8[m_nBufferSize + DECODING_BUFFER_ERROR];
}
// Start decoding now
HRESULT hr;
hr = m_pReader->Start(0, 0, 1.0f, this);
m_bFirstRead = false;
if(FAILED(hr))
return false;
}
else
{
// Release the decoding thread so decoding continues
SetEvent(m_hWaitEvent);
}
WaitForSingleObject(m_hRespondEvent, INFINITE);
if(FAILED(m_hrCallbackResult))
return false;
}
// Normally we should have more than enough data in the buffer...
if(m_nWritePtr >= dwSizeToRead)
{
memcpy(pBuffer, m_pBuffer, dwSizeToRead);
*pdwSizeRead = dwSizeToRead;
memmove(m_pBuffer, m_pBuffer + dwSizeToRead, m_nWritePtr - dwSizeToRead);
m_nWritePtr -= dwSizeToRead;
}
// but if we're at the end of the file, we may have less than requested
else
{
memcpy(pBuffer, m_pBuffer, m_nWritePtr);
*pdwSizeRead = m_nWritePtr;
m_nWritePtr = 0;
}
m_nBytesRead += *pdwSizeRead;
if(m_bEOF)
m_pReader->Stop();
return true;
}
//------------------------------------------------------------------------//
uint32 WMA::GetSize()
{
FN("WMA::GetSize()");
return m_nStreamSize;
}
//------------------------------------------------------------------------//
bool WMA::Reset()
{
FN("WMA::Reset()");
if(!m_bOpen)
return false;
m_nBytesRead = 0;
m_pReader->Stop();
m_bFirstRead = true;
m_bEOF = false;
m_nTargetPtr = 0;
m_nWritePtr = 0;
m_qwTime = 0;
ZeroMemory(m_pBuffer, m_nBufferSize);
SetEvent(m_hWaitEvent);
return true;
}
//------------------------------------------------------------------------//
WAVEFORMATEX* WMA::GetFormat()
{
FN("WMA::GetFormat()");
return &m_WaveFormatEx;
}
//------------------------------------------------------------------------//
bool WMA::IsEOF()
{
FN("WMA::IsEOF()");
return m_bEOF;
}
//------------------------------------------------------------------------//
void WMA::Destroy()
{
Close();
WMA::DestroyObject(this);
}
//------------------------------------------------------------------------//
HRESULT WMA::OnStatus(
WMT_STATUS Status,
HRESULT hr,
WMT_ATTR_DATATYPE dwType,
BYTE* pValue)
{
m_hrCallbackResult = hr;
switch(Status)
{
case WMT_ERROR:
SetEvent(m_hWaitEvent);
SetEvent(m_hRespondEvent);
DebugOut(1, "Status: WMT_ERROR");
return E_FAIL;
case WMT_OPENED:
DebugOut(5, "Status: WMT_OPENED");
SetEvent(m_hRespondEvent);
break;
case WMT_BUFFERING_START:
DebugOut(5, "Status: WMT_BUFFERING_START");
break;
case WMT_BUFFERING_STOP:
DebugOut(5, "Status: WMT_BUFFERING_STOP");
break;
case WMT_END_OF_FILE:
DebugOut(5, "Status: WMT_END_OF_FILE");
m_bEOF = true;
// Make sure no threads are kept waiting, since no more reads will come
SetEvent(m_hWaitEvent);
SetEvent(m_hRespondEvent);
break;
case WMT_END_OF_SEGMENT:
DebugOut(5, "Status: WMT_END_OF_SEGMENT");
break;
case WMT_END_OF_STREAMING:
DebugOut(5, "Status: WMT_END_OF_STREAMING");
break;
case WMT_STARTED:
DebugOut(5, "Status: WMT_STARTED");
DebugOut(5, "Advancing deliver time at start");
m_qwTime += 1000000;
if(m_pReaderAdvanced)
m_pReaderAdvanced->DeliverTime(m_qwTime);
break;
case WMT_STOPPED:
DebugOut(5, "Status: WMT_STOPPED");
SetEvent(m_hWaitEvent);
SetEvent(m_hRespondEvent);
break;
case WMT_CLOSED:
DebugOut(5, "Status: WMT_CLOSED");
break;
case WMT_TIMER:
DebugOut(5, "Status: WMT_TIMER");
break;
case WMT_SOURCE_SWITCH:
DebugOut(5, "Status: WMT_SOURCE_SWITCH");
break;
};
return S_OK;
}
//------------------------------------------------------------------------//
HRESULT WMA::OnTime(
QWORD qwCurrentTime)
{
// Keep asking for the specific duration of the stream till EOF
if ( !m_bEOF )
{
DebugOut(5, "Advancing deliver time in OnTime() function");
m_qwTime += 1000000;
if(m_pReaderAdvanced)
m_pReaderAdvanced->DeliverTime( m_qwTime );
}
return S_OK;
}
//------------------------------------------------------------------------//
HRESULT WMA::OnSample(
DWORD dwOutputNum,
QWORD cnsSampleTime,
QWORD cnsSampleDuration,
DWORD dwFlags,
INSSBuffer* pSample)
{
if(!m_bOpen)
{
if(m_hRespondEvent)
SetEvent(m_hRespondEvent);
return S_OK;
}
if(m_bEOF)
{
SetEvent(m_hRespondEvent);
return S_OK;
}
BYTE* pBuf;
DWORD dwLen;
if(!pSample)
return E_FAIL;
HRESULT hr = pSample->GetBuffer(&pBuf);
if(FAILED(hr))
return E_FAIL;
hr = pSample->GetLength(&dwLen);
if(FAILED(hr))
return E_FAIL;
// Expand the decoding buffer if needed
if((m_nWritePtr + dwLen) > m_nBufferSize)
{
uint32 nNewBufferSize = m_nWritePtr + dwLen + DECODING_BUFFER_ERROR;
DebugOut(1, "Expanding buffer from length %d to %d", m_nBufferSize, nNewBufferSize);
uint8* pNewBuffer = new uint8[nNewBufferSize];
memcpy(pNewBuffer, m_pBuffer, m_nWritePtr);
SAFE_DELETE_ARRAY(m_pBuffer);
m_pBuffer = pNewBuffer;
m_nBufferSize = nNewBufferSize;
}
memcpy(m_pBuffer + m_nWritePtr, pBuf, dwLen);
m_nWritePtr += dwLen;
if(m_nWritePtr >= m_nTargetPtr)
{
SetEvent(m_hRespondEvent);
CRITICAL_FUNCTION(&m_csTerm);
WaitForSingleObject(m_hWaitEvent, INFINITE);
}
return S_OK;
}
#endif // USE_WMA