// WaveFileSource.cpp: implementation of the WaveFileSource class.
|
//
|
//////////////////////////////////////////////////////////////////////
|
|
#include "WaveFileSource.h"
|
#include "AudioSampleManager.h"
|
#include "AudioSample.h"
|
|
//////////////////////////////////////////////////////////////////////
|
// Construction/Destruction
|
//////////////////////////////////////////////////////////////////////
|
|
WaveFileSource::WaveFileSource()
|
{
|
szFileName[0] = '\0';
|
pFormat = NULL;
|
bufferDuration = WAVEFILESOURCE_DEFAULT_BUFFERDURATION;
|
char subFacilityName[100];
|
sprintf(subFacilityName, "WaveFileSource:%x", this);
|
tracer.SetSubFacilityName(subFacilityName);
|
SetTraceLevel();
|
tracer.tracef(EE, "Constructor - begin\n");
|
PlayOnce();
|
bRunning = false;
|
InitializeCriticalSection(&filterMutex);
|
hmmio = NULL;
|
tracer.tracef(EE, "Constructor - end\n");
|
}
|
|
WaveFileSource::~WaveFileSource()
|
{
|
tracer.tracef(EE, "Destructor - begin\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "WaveFileSource~ : entered filterMutex\n");
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "WaveFileSource~ : left filterMutex\n");
|
CloseMMIO();
|
DeleteCriticalSection(&filterMutex);
|
tracer.tracef(EE, "Destructor - end\n");
|
}
|
|
int
|
WaveFileSource::SetTraceLevel()
|
{
|
long SystemMask = 0;
|
if ((SystemMask = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "AllComponents", 0x0)) == 0)
|
{
|
SystemMask = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "WaveFileSource", 0x100000);
|
}
|
tracer.SetSystemMask(SystemMask);
|
return 0;
|
}
|
|
int
|
WaveFileSource::PlayInLoop()
|
{
|
looping = true;
|
return 0;
|
}
|
|
int
|
WaveFileSource::PlayOnce()
|
{
|
looping = false;
|
return 0;
|
}
|
|
int
|
WaveFileSource::OpenWaveFile(const char *inputFileName)
|
{
|
int result(0);
|
PlayOnce();
|
if (strlen(inputFileName) < 128 && !bRunning)
|
{
|
do
|
{
|
CloseMMIO();
|
strcpy(szFileName, inputFileName);
|
|
// Open the file for reading with buffered I/O
|
// by using the default internal buffer
|
if(!(hmmio = mmioOpen(szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF)))
|
{
|
tracer.tracef(ERR, "SourceStarted : mmioOpen(%s) returned 0x%x\n", szFileName, hmmio);
|
result = -10;
|
break;
|
}
|
|
MMIOINFO mmInfoStart;
|
mmioGetInfo(hmmio, &mmInfoStart, 0);
|
LONG fileStartPosition = (LONG)mmInfoStart.pchNext;
|
|
// Locate a "RIFF" chunk with a "WAVE" form type to make
|
// sure the file is a waveform-audio file.
|
mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
|
if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL, MMIO_FINDRIFF))
|
{
|
tracer.tracef(ERR, "SourceStarted : %s not wave file", szFileName);
|
result = -20;
|
break;
|
}
|
|
// Find the "FMT" chunk (form type "FMT"); it must be
|
// a subchunk of the "RIFF" chunk.
|
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
|
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK))
|
{
|
tracer.tracef(ERR, "SourceStarted : File %s has no FMT chunk", szFileName);
|
result = -30;
|
break;
|
}
|
|
// Get the size of the "FMT" chunk. Allocate
|
// and lock memory for it.
|
dwFmtSize = mmckinfoSubchunk.cksize;
|
pFormat = (WAVEFORMATEX *) new char[dwFmtSize];
|
|
// Read the "FMT" chunk.
|
if (mmioRead(hmmio, (HPSTR) pFormat, dwFmtSize) != dwFmtSize)
|
{
|
tracer.tracef(ERR, "SourceStarted : Failed to read format chunk");
|
result = -40;
|
break;
|
}
|
|
// Ascend out of the "FMT" subchunk.
|
mmioAscend(hmmio, &mmckinfoSubchunk, 0);
|
|
// Find the data subchunk. The current file position should be at
|
// the beginning of the data chunk; however, you should not make
|
// this assumption. Use mmioDescend to locate the data chunk.
|
mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
|
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK))
|
{
|
tracer.tracef(ERR, "SourceStarted : File %s has no data chunk", szFileName);
|
result = -50;
|
break;
|
}
|
|
// Get the size of the data subchunk.
|
dwDataSize = mmckinfoSubchunk.cksize;
|
if (dwDataSize == 0L)
|
{
|
tracer.tracef(ERR, "SourceStarted : Data chunk of %s contains no data", szFileName);
|
result = -60;
|
break;
|
}
|
MMIOINFO mmInfo;
|
mmioGetInfo(hmmio, &mmInfo, 0);
|
dataStartPosition = (LONG)mmInfo.pchNext - fileStartPosition;
|
}
|
while(false);
|
if (result != 0)
|
{
|
CloseMMIO();
|
}
|
}
|
else
|
{
|
if (bRunning)
|
{
|
result = -70;
|
}
|
else
|
{
|
result = -80;
|
}
|
}
|
return result;
|
}
|
|
int
|
WaveFileSource::CloseMMIO()
|
{
|
if (hmmio)
|
{
|
mmioClose(hmmio, 0);
|
}
|
hmmio = NULL;
|
if (pFormat)
|
{
|
delete [] (char *)pFormat;
|
}
|
pFormat = NULL;
|
return 0;
|
}
|
|
int
|
WaveFileSource::SourceStarted()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "SourceStarted\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SourceStarted : entered filterMutex\n");
|
do
|
{
|
if (bRunning)
|
{
|
tracer.tracef(ERR, "SourceStarted : e-error : already running\n");
|
result = -10;
|
break;
|
}
|
if (!hmmio || !pFormat)
|
{
|
tracer.tracef(ERR, "SourceStarted : e-error : hmmio = 0x%x, pFormat = 0x%x\n", hmmio, pFormat);
|
result = -20;
|
break;
|
}
|
bRunning = true;
|
}
|
while (false);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SourceStarted : left filterMutex\n");
|
tracer.tracef(EE, "~SourceStarted : returning %d\n", result);
|
return result;
|
}
|
|
int
|
WaveFileSource::SourceStopped()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "SourceStopped\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SourceStopped : entered filterMutex\n");
|
do
|
{
|
if (!bRunning)
|
{
|
tracer.tracef(ERR, "SourceStopped : e-error : not running\n");
|
result = -10;
|
break;
|
}
|
returnCode = CloseMMIO();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SourceStopped : e-CloseMMIO : %d 0x%x\n", returnCode, returnCode);
|
result = -20;
|
break;
|
}
|
bRunning = false;
|
}
|
while (false);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SourceStopped : left filterMutex\n");
|
tracer.tracef(EE, "~SourceStopped : returning %d\n", result);
|
return result;
|
}
|
|
int
|
WaveFileSource::SourceThreadStarted(HANDLE sourceThreadHandle, DWORD sourceThreadID)
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "SourceThreadStarted\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SourceThreadStarted : entered filterMutex\n");
|
do
|
{
|
if (!hmmio || !pFormat)
|
{
|
tracer.tracef(ERR, "SourceThreadStarted : e-error : hmmio = 0x%x, pFormat = 0x%x\n", hmmio, pFormat);
|
result = -10;
|
break;
|
}
|
bRunning = true;
|
}
|
while (false);
|
|
tracer.tracef(ARB, "SourceThreadStarted : before firing FILE_STARTED %s\n", szFileName);
|
FireEvent(FILE_STARTED, szFileName);
|
tracer.tracef(ARB, "SourceThreadStarted : after firing FILE_STARTED %s\n", szFileName);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SourceThreadStarted : left filterMutex\n");
|
tracer.tracef(EE, "~SourceThreadStarted : returning %d\n", result);
|
return result;
|
}
|
|
int
|
WaveFileSource::SourceThreadStopped(HANDLE sourceThreadHandle, DWORD sourceThreadID)
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "SourceThreadStopped\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SourceThreadStopped : entered filterMutex\n");
|
do
|
{
|
returnCode = CloseMMIO();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SourceThreadStopped : e-CloseMMIO : %d 0x%x\n", returnCode, returnCode);
|
result = -10;
|
break;
|
}
|
bRunning = false;
|
}
|
while (false);
|
|
tracer.tracef(ARB, "SourceThreadStopped : before firing FILE_STOPPED %s\n", szFileName);
|
FireEvent(FILE_STOPPED, szFileName);
|
tracer.tracef(ARB, "SourceThreadStopped : after firing FILE_STOPPED %s\n", szFileName);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SourceThreadStopped : left filterMutex\n");
|
tracer.tracef(EE, "~SourceThreadStopped : returning %d\n", result);
|
return result;
|
}
|
|
int
|
WaveFileSource::GetFormat(WAVEFORMATEX *waveFormat)
|
{
|
if (pFormat)
|
{
|
*waveFormat = *pFormat;
|
}
|
return 0;
|
}
|
|
int
|
WaveFileSource::SetBufferDuration(int bufferDurationMillisec)
|
{
|
bufferDuration = bufferDurationMillisec;
|
return 0;
|
}
|
|
int
|
WaveFileSource::GenerateData(AudioSample **ppAudioSample)
|
{
|
tracer.tracef(DET, "GenerateData\n");
|
int result(0);
|
AudioSample *audioSample = NULL;
|
result = (AudioSampleManager::GetInstance())->GetAudioSample(&audioSample, this);
|
*ppAudioSample = NULL;
|
if (audioSample)
|
{
|
audioSample->SetDataSize(0);
|
audioSample->SetSilenceDuration(bufferDuration);
|
if (bRunning)
|
{
|
audioSample->SetFormat(*pFormat);
|
int nBytesToRead = (pFormat->nAvgBytesPerSec * bufferDuration) / 1000;
|
if (audioSample->BufferSize() < nBytesToRead)
|
{
|
result = -20;
|
}
|
else
|
{
|
int bytes(0);
|
if((bytes = mmioRead(hmmio, (HPSTR) audioSample->Data(), nBytesToRead)) != nBytesToRead)
|
{
|
if (bytes < 0 || (bytes == 0 && !looping))
|
{
|
bytes = 0;
|
result = -30;
|
}
|
else if (looping)
|
{
|
mmioSeek(hmmio, dataStartPosition, SEEK_SET);
|
int nBytes = mmioRead(hmmio, (HPSTR) audioSample->Data(), nBytesToRead - bytes);
|
if (nBytes <= 0)
|
{
|
result = -40;
|
}
|
else
|
{
|
bytes += nBytes;
|
}
|
}
|
audioSample->SetDataSize(bytes);
|
}
|
else
|
{
|
audioSample->SetDataSize(nBytesToRead);
|
}
|
}
|
}
|
}
|
if (result < 0)
|
{
|
if (audioSample)
|
{
|
audioSample->Release(this);
|
audioSample = NULL;
|
}
|
}
|
*ppAudioSample = audioSample;
|
if (*ppAudioSample)
|
{
|
tracer.tracef(DET, "~GenerateData : data %d bytes, silence %d ms\n", audioSample->DataSize(), audioSample->GetSilenceDuration());
|
}
|
else
|
{
|
tracer.tracef(DET, "~GenerateData : returning NULL : result = %d\n", result);
|
}
|
return result;
|
}
|
|
int
|
WaveFileSource::AddFilePlayListener(WaveFileSourceListener *waveFileSourceListener)
|
{
|
return AddListener((void *)waveFileSourceListener);
|
}
|
|
int
|
WaveFileSource::RemoveFilePlayListener(WaveFileSourceListener *waveFileSourceListener)
|
{
|
return RemoveListener((void *)waveFileSourceListener);
|
}
|
|
int
|
WaveFileSource::FireEvent(int eventType, char *waveFileName)
|
{
|
EnterCriticalSection(&listenersMutex);
|
std::vector<void *>::iterator iter = listeners.begin();
|
while (iter != listeners.end())
|
{
|
switch(eventType)
|
{
|
case FILE_STARTED:
|
((WaveFileSourceListener *)(*iter))->FilePlayStarted(this, waveFileName);
|
break;
|
case FILE_STOPPED:
|
((WaveFileSourceListener *)(*iter))->FilePlayStopped(this, waveFileName);
|
break;
|
default:
|
break;
|
}
|
iter++;
|
}
|
LeaveCriticalSection(&listenersMutex);
|
/* FireEventThreadParams *params = new FireEventThreadParams();
|
params->eventType = eventType;
|
params->waveFileSource = this;
|
strncpy(params->waveFileName, waveFileName, min(strlen(waveFileName)+1, 1024));
|
(params->waveFileName)[1024] = '\0';
|
DWORD threadID;
|
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FireEventThread, (LPVOID)params, 0, &threadID);
|
*/ return 0;
|
}
|
|
DWORD WINAPI
|
WaveFileSource::FireEventThread(LPVOID param)
|
{
|
FireEventThreadParams *params = (FireEventThreadParams *)param;
|
WaveFileSource *waveFileSource = params->waveFileSource;
|
EnterCriticalSection(&(waveFileSource->listenersMutex));
|
std::vector<void *>::iterator iter = waveFileSource->listeners.begin();
|
while (iter != waveFileSource->listeners.end())
|
{
|
switch(params->eventType)
|
{
|
case FILE_STARTED:
|
((WaveFileSourceListener *)(*iter))->FilePlayStarted(waveFileSource, params->waveFileName);
|
break;
|
case FILE_STOPPED:
|
((WaveFileSourceListener *)(*iter))->FilePlayStopped(waveFileSource, params->waveFileName);
|
break;
|
default:
|
break;
|
}
|
iter++;
|
}
|
LeaveCriticalSection(&(waveFileSource->listenersMutex));
|
return 0;
|
}
|
|
void
|
WaveFileSourceListener::FilePlayStarted(WaveFileSource *waveFileSource, char *waveFileName)
|
{
|
// AfxMessageBox("WaveFileSourceListener::FilePlayStarted : ERROR : pure virtual function call !!!\n");
|
}
|
|
void
|
WaveFileSourceListener::FilePlayStopped(WaveFileSource *waveFileSource, char *waveFileName)
|
{
|
// AfxMessageBox("WaveFileSourceListener::FilePlaySopped : ERROR : pure virtual function call !!!\n");
|
}
|