//
// (c) Yuri Kiryanov, openh323@kiryanov.com
// and Yuriy Gorvitovskiy
//
// Portions (c) 1997 Tim Kientzle
// from ``The Programmer's Guide to Sound.''
//
// Windows CE port of OpenH323 Open Source Project, www.openh323.org
// Extra Multimedia functionality
//
#include <ptlib.h>
#include <stdlibx.h>
#include <mmsystemx.h>
////////////////////////////////////////////////////////////////////
// Chunks
class ChunkFinder
{
struct Chunk {
unsigned long type; // Type of chunk
unsigned long size; // Size of chunk
unsigned long remaining; // Bytes left to read
bool isContainer; // true if this is a container
unsigned long containerType; // type of container
} m_chunkStack[5];
public:
ChunkFinder(HANDLE hFile) : m_hFile(hFile),
m_fGoodFile(false), m_currentChunk(-1), m_fFormatErrorsFound(false),
m_type(0L), m_size(0L)
{
m_fGoodFile = !(SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == -1L);
memset(m_chunkStack, 0, sizeof(m_chunkStack));
};
HANDLE m_hFile;
bool m_fGoodFile;
bool m_fFormatErrorsFound;
int m_currentChunk; // top of stack
DWORD m_type;
DWORD m_size;
bool FindNext(void)
{
if(!m_fGoodFile)
return false;
if ((m_currentChunk >= 0) && (!m_chunkStack[m_currentChunk].isContainer)) {
unsigned long lastChunkSize = m_chunkStack[m_currentChunk].size;
if (lastChunkSize & 1) { // Is there padding?
m_chunkStack[m_currentChunk].remaining++;
lastChunkSize++; // Account for padding in the container update
}
SetFilePointer(m_hFile, m_chunkStack[m_currentChunk].remaining, NULL, FILE_CURRENT); // Flush the chunk
m_currentChunk--; // Drop chunk from the stack
// Sanity check: containing chunk must be container
if ((m_currentChunk < 0) || (!m_chunkStack[m_currentChunk].isContainer)) {
// Chunk contained in non-Container
m_fFormatErrorsFound = true;
return false;
}
// Reduce size of container
if (m_currentChunk >= 0) {
// Sanity check: make sure container is big enough.
// Also, avoid a really nasty underflow situation.
if ((lastChunkSize+8) > m_chunkStack[m_currentChunk].remaining) {
m_fFormatErrorsFound = true; // Chunk is too large to fit in container
m_chunkStack[m_currentChunk].remaining = 0; // container is empty
} else
m_chunkStack[m_currentChunk].remaining -= lastChunkSize + 8;
}
}
// There may be forms that are finished, drop them too
while ( (m_currentChunk >= 0) // there is a chunk
&& (m_chunkStack[m_currentChunk].remaining < 8)
)
{
SetFilePointer(m_hFile, m_chunkStack[m_currentChunk].remaining, NULL, FILE_CURRENT); // Flush the chunk
unsigned long lastChunkSize = m_chunkStack[m_currentChunk].size;
m_currentChunk--; // Drop container chunk
// Sanity check, containing chunk must be container
if (!m_chunkStack[m_currentChunk].isContainer) {
// Chunk contained in non-container
return false;
}
// Reduce size of container
if (m_currentChunk >= 0) {
if ((lastChunkSize+8) > m_chunkStack[m_currentChunk].remaining) {
// Error in WAVE file: Chunk is too large to fit
lastChunkSize = m_chunkStack[m_currentChunk].remaining;
}
m_chunkStack[m_currentChunk].remaining -= lastChunkSize + 8;
}
}
// Read the next chunk
DWORD dwRead = 0L;
DWORD dwResult = ReadFile(m_hFile, &m_type, sizeof(long), &dwRead, NULL);
if ((dwResult != 0) && (dwRead == 0L))
{
m_currentChunk = -1; // empty the stack
return false;
}
dwResult = ReadFile(m_hFile, &m_size, sizeof(long), &dwRead, NULL);
if ((dwResult != 0) && (dwRead == 0L))
{
m_currentChunk = -1; // empty the stack
return false;
}
// Put this chunk on the stack
m_currentChunk++;
m_chunkStack[m_currentChunk].type = m_type;
m_chunkStack[m_currentChunk].size = m_size;
m_chunkStack[m_currentChunk].remaining = m_size;
m_chunkStack[m_currentChunk].isContainer = false;
m_chunkStack[m_currentChunk].containerType = 0;
if ((m_currentChunk >= 0) &&
(m_chunkStack[0].type != MAKEFOURCC('R','I','F','F')))
{
// Outermost chunk is not RIFF
m_currentChunk = -1;
return false;
}
if (m_type == MAKEFOURCC('R','I','F','F'))
{
m_chunkStack[m_currentChunk].isContainer = true;
// Need to check size of container first.
dwResult = ReadFile(m_hFile, &m_chunkStack[m_currentChunk].containerType,
sizeof(long), &dwRead, NULL);
if ((dwResult != 0) && (dwRead == 0L))
{
m_currentChunk = -1; // empty the stack
return false;
}
m_chunkStack[m_currentChunk].remaining -= 4;
if (m_currentChunk > 0)
m_fFormatErrorsFound = true; // RIFF chunk seen at inner level
return true;
}
if (m_type == MAKEFOURCC('f','m','t',' '))
{
if (m_currentChunk != 1)
m_fFormatErrorsFound = true; // FMT chunk seen at wrong level?!?!\n";
m_chunkStack[m_currentChunk].remaining = 0;
return true;
}
if (m_type == MAKEFOURCC('d','a','t','a'))
{
return true;
}
// Some unknown chunk found
return true;
}
bool FindRiffHeader()
{
if ( !FindNext() || (m_currentChunk != 0)
|| (m_chunkStack[0].type != MAKEFOURCC('R','I','F','F'))
|| (m_chunkStack[0].isContainer != true)
|| (m_chunkStack[0].containerType != MAKEFOURCC('W','A','V','E'))
)
return false;
return true;
}
bool FindFmtChunk()
{
bool found = false;
if( m_currentChunk < 0 )
found = FindRiffHeader();
if ( !found || !FindNext() || (m_currentChunk != 1)
|| (m_chunkStack[1].type != MAKEFOURCC('f','m','t',' '))
) return false;
return true;
}
bool FindDataChunk()
{
bool found = false;
if( m_currentChunk < 1 )
found = FindFmtChunk();
if (!found) // Skip format
return false;
else
{
SetFilePointer(m_hFile, m_size, NULL, FILE_CURRENT);
while ( (found = FindNext()) &&
(m_chunkStack[m_currentChunk].type != MAKEFOURCC('d','a','t','a'))
) ;
return found &&
(m_chunkStack[m_currentChunk].type == MAKEFOURCC('d','a','t','a'));
}
}
};
HMMIO WINAPI mmioOpen(LPSTR pszFileName, LPMMIOINFO pmmioinfo, DWORD fdwOpen)
{
USES_CONVERSION;
DWORD dwAccess = fdwOpen & MMIO_READ ? GENERIC_READ : 0;
dwAccess |= (fdwOpen & MMIO_WRITE) ? GENERIC_WRITE : 0xFFFF;
DWORD dwFlags = fdwOpen & MMIO_CREATE ? \
CREATE_ALWAYS : OPEN_EXISTING;
HANDLE hFile = CreateFile(A2T(pszFileName),
dwAccess, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
dwFlags, FILE_ATTRIBUTE_NORMAL, NULL);
if (pmmioinfo != NULL)
{
memset(pmmioinfo, 0, sizeof(MMIOINFO));
pmmioinfo->wErrorRet = GetLastError();
if( pmmioinfo->wErrorRet == ERROR_ALREADY_EXISTS )
pmmioinfo->wErrorRet = 0L;
}
return (HMMIO) hFile;
}
MMRESULT WINAPI mmioClose(HMMIO hmmio, UINT fuClose)
{
return CloseHandle(hmmio) ? MMSYSERR_NOERROR : MMSYSERR_INVALHANDLE;
}
LONG WINAPI mmioRead(HMMIO hmmio, HPSTR pch, LONG cch)
{
DWORD dwRead = 0L;
ReadFile(hmmio, pch, cch, &dwRead, NULL);
return dwRead;
}
LONG WINAPI mmioWrite(HMMIO hmmio, const char * pch, LONG cch)
{
DWORD dwWritten = 0L;
WriteFile(hmmio, pch, cch, &dwWritten, NULL);
return dwWritten;
}
MMRESULT WINAPI mmioDescend(HMMIO hmmio, LPMMCKINFO pmmcki,
const MMCKINFO FAR* pmmckiParent, UINT fuDescend)
{
if( fuDescend & MMIO_FINDRIFF )
{
// Locate a 'RIFF' chunk with a 'WAVE' form type
ChunkFinder cf(hmmio);
if( !cf.FindRiffHeader() )
return MMSYSERR_ERROR;
}
if( fuDescend & MMIO_FINDCHUNK && pmmcki )
{
if(pmmcki->ckid == mmioFOURCC('f', 'm', 't', ' '))
{
// Find the format chunk
ChunkFinder cf(hmmio);
if( !cf.FindFmtChunk() )
return MMSYSERR_ERROR;
pmmcki->cksize = cf.m_size + 2;
}
if(pmmcki->ckid == mmioFOURCC('d', 'a', 't', 'a'))
{
// Find the data chunk
ChunkFinder cf(hmmio);
if( !cf.FindDataChunk() )
return MMSYSERR_ERROR;
pmmcki->cksize = cf.m_size;
}
}
return MMSYSERR_NOERROR;
}
MMRESULT WINAPI mmioAscend(HMMIO hmmio, LPMMCKINFO pmmcki, UINT fuAscend)
{
// Do nothing - mmioDescend traverses from beginning, no need to move up
return MMSYSERR_NOERROR;
}
MMRESULT WINAPI mmioCreateChunk(HMMIO hmmio, LPMMCKINFO pmmcki, UINT fuCreate)
{
return MMSYSERR_NOERROR;
}
BOOL WINAPI PlaySound( LPCSTR pszSound, HMODULE hmod, DWORD fdwSound)
{
// [YG] I have changed all A2W to A2T for normal
USES_CONVERSION;
return ::PlaySound( A2T(pszSound), hmod, fdwSound);
}
MMRESULT WINAPI waveInGetErrorText(MMRESULT mmrError, char* pszText, UINT cchText)
{
TCHAR tch[1024];
MMRESULT mmResult = waveInGetErrorText(mmrError, tch, 1024);
wcstombs(pszText, tch, cchText);
return mmResult;
}
MMRESULT WINAPI waveOutGetErrorText(MMRESULT mmrError, char* pszText, UINT cchText)
{
TCHAR tch[1024];
MMRESULT mmResult = waveOutGetErrorText(mmrError, tch, 1024);
wcstombs(pszText, tch, cchText);
return mmResult;
}
// Some missing string functions used by multimedia stuff
int __cdecl stricmp(const unsigned short* s1, const char* s2)
{
USES_CONVERSION;
return wcsicmp(s1, A2T(s2));
}
syntax highlighted by Code2HTML, v. 0.9.1