// ACMTransformer.cpp: implementation of the ACMTransformer class.
|
//
|
//////////////////////////////////////////////////////////////////////
|
|
#include <windows.h>
|
#include <algorithm>
|
|
#include "ACMTransformer.h"
|
#include "AudioSampleManager.h"
|
#include "AudioSample.h"
|
|
using namespace std;
|
|
//////////////////////////////////////////////////////////////////////
|
// Construction/Destruction
|
//////////////////////////////////////////////////////////////////////
|
|
ACMTransformer::ACMTransformer()
|
{
|
SetSourceFormat(WaveFormat::GetWaveFormat(WaveFormat_PCM_16_8_1));
|
SetDestinationFormat(WaveFormat::GetWaveFormat(WaveFormat_PCM_16_8_1));
|
InitializeCriticalSection(&outputQueueMutex);
|
InitializeCriticalSection(&freeBuffersMutex);
|
InitializeCriticalSection(&stateMutex);
|
InitializeCriticalSection(&acmMutex);
|
InitializeCriticalSection(&queryStateMutex);
|
freeBuffersEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
outputEnqueueEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
outputDequeueEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
SetOutputDuration(20);
|
char subFacilityName[100];
|
sprintf(subFacilityName, "ACMTransformer:%x", this);
|
tracer.SetSubFacilityName(subFacilityName);
|
SetRunning(false);
|
SetTraceLevel();
|
}
|
|
ACMTransformer::~ACMTransformer()
|
{
|
tracer.tracef(EE, "~ACMTransformer : begin\n");
|
DeleteCriticalSection(&outputQueueMutex);
|
DeleteCriticalSection(&freeBuffersMutex);
|
DeleteCriticalSection(&stateMutex);
|
DeleteCriticalSection(&acmMutex);
|
DeleteCriticalSection(&queryStateMutex);
|
CloseHandle(freeBuffersEvent);
|
CloseHandle(outputEnqueueEvent);
|
CloseHandle(outputDequeueEvent);
|
tracer.tracef(EE, "~ACMTransformer : end\n");
|
}
|
|
int
|
ACMTransformer::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", "ACMTransformer", 0x100000);
|
}
|
tracer.SetSystemMask(SystemMask);
|
return 0;
|
}
|
|
int
|
ACMTransformer::SetSourceFormat(WAVEFORMATEX &wfxSource)
|
{
|
sourceFormat = wfxSource;
|
return 0;
|
}
|
|
int
|
ACMTransformer::SetDestinationFormat(WAVEFORMATEX &wfxDest)
|
{
|
destFormat = wfxDest;
|
return 0;
|
}
|
|
int
|
ACMTransformer::TransformStarted()
|
{
|
tracer.tracef(EE, "TransformStarted\n");
|
int result(0);
|
tracer.tracef(ARB, "TransformStarted : entering stateMutex\n");
|
EnterCriticalSection(&stateMutex);
|
if (IsRunning())
|
{
|
tracer.tracef(SIG, "TransformStarted : Already running\n");
|
}
|
else
|
{
|
EnterCriticalSection(&acmMutex);
|
WaveFormat::TraceFormat(&tracer, ARB, sourceFormat);
|
WaveFormat::TraceFormat(&tracer, ARB, destFormat);
|
tracer.tracef(ARB, "TransformStarted : calling acmStreamOpen\n");
|
result = acmStreamOpen(&hACMStream, NULL, &sourceFormat, &destFormat, NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME);
|
LeaveCriticalSection(&acmMutex);
|
if (result == 0)
|
{
|
for (int i=0; i<ACMTRANSFORMER_NUM_STREAMHEADER; i++)
|
{
|
tracer.tracef(ARB, "TransformStarted : preparing acm header %d\n", i);
|
memset(&acmStreamHeader[i], 0, sizeof(acmStreamHeader[i]));
|
acmStreamHeader[i].cbStruct = sizeof(acmStreamHeader[i]);
|
acmStreamHeader[i].fdwStatus = 0;
|
acmStreamHeader[i].pbSrc = sourceBuffer[i];
|
acmStreamHeader[i].cbSrcLength = ACMTRANSFORMER_MAX_SOURCEBUFFERSIZE;
|
acmStreamHeader[i].pbDst = destBuffer[i];
|
acmStreamHeader[i].cbDstLength = ACMTRANSFORMER_MAX_DESTBUFFERSIZE;
|
EnterCriticalSection(&acmMutex);
|
tracer.tracef(ARB, "TransformStarted : calling acmStreamPrepareHeader\n");
|
result = acmStreamPrepareHeader(hACMStream, &acmStreamHeader[i], 0);
|
LeaveCriticalSection(&acmMutex);
|
tracer.tracef(ARB, "TransformStarted : acmStreamPrepareHeader returned %d 0x%x\n", result, result);
|
acmStreamHeader[i].cbSrcLength = 0; // to show that no unconverted data
|
acmStreamHeader[i].cbSrcLengthUsed = 0;
|
if (result == 0)
|
{
|
ReleaseBuffer(&acmStreamHeader[i]); // adds it to the free buffer
|
SetRunning(true);
|
}
|
}
|
if (!IsRunning())
|
{
|
acmStreamClose(hACMStream, 0);
|
}
|
}
|
else
|
{
|
tracer.tracef(ERR, "TransformStarted : acmStreamOpen failed with error code %d 0x%x\n", result, result);
|
WaveFormat::TraceFormat(&tracer, ERR, sourceFormat);
|
WaveFormat::TraceFormat(&tracer, ERR, destFormat);
|
result = -10;
|
}
|
}
|
LeaveCriticalSection(&stateMutex);
|
tracer.tracef(ARB, "TransformStarted : left stateMutex\n");
|
if (result != 0)
|
{
|
tracer.tracef(ERR, "~TransformStarted : error %d. bRunning = %d\n", result, bRunning);
|
}
|
else
|
{
|
tracer.tracef(EE, "~TransformStarted : bRunning = %d\n", bRunning);
|
}
|
return result;
|
}
|
|
int
|
ACMTransformer::TransformStopped()
|
{
|
tracer.tracef(EE, "TransformStopped\n");
|
int result(0);
|
tracer.tracef(ARB, "TransformStopped : entering stateMutex\n");
|
EnterCriticalSection(&stateMutex);
|
if (!IsRunning())
|
{
|
tracer.tracef(SIG, "TransformStarted : Already stopped\n");
|
}
|
else
|
{
|
// if there is no input pin, then wait for buffered data to clear
|
int numSources = audioSources.size();
|
int numDest = audioSinks.size();
|
if (numSources == 0 && numDest > 0)
|
{
|
tracer.tracef(ARB, "TransformStopped : numSources = 0 & numDest = %d\n", numDest);
|
AudioSample *audioSample = NULL;
|
AudioSampleManager::GetInstance()->GetAudioSample(&audioSample, this);
|
if (audioSample)
|
{
|
audioSample->SetFormat(destFormat);
|
int minFrameSize = (audioSample->MinFrameSize() * outputDuration * 1000) / audioSample->MinFrameDuration();
|
while (true)
|
{
|
tracer.tracef(ARB, "TransformStopped : entering outputQueueMutex\n");
|
EnterCriticalSection(&outputQueueMutex);
|
if (outputQueue.size() < minFrameSize)
|
{
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(ARB, "TransformStopped : left outputQueueMutex\n");
|
break;
|
}
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(ARB, "TransformStopped : left outputQueueMutex. Begin waiting on outputDequeueEvent\n");
|
WaitForSingleObject(outputDequeueEvent, INFINITE);
|
tracer.tracef(ARB, "TransformStopped : after waiting on outputDequeueEvent\n");
|
}
|
audioSample->Release(this);
|
audioSample = NULL;
|
}
|
}
|
|
SetRunning(false);
|
tracer.tracef(ARB, "TransformStopped : entered freeBuffersMutex\n");
|
EnterCriticalSection(&freeBuffersMutex);
|
freeBuffers.clear();
|
SetEvent(freeBuffersEvent);
|
tracer.tracef(ARB, "TransformStopped : set freeBuffersEvent\n");
|
LeaveCriticalSection(&freeBuffersMutex);
|
tracer.tracef(ARB, "TransformStopped : left freeBuffersMutex\n");
|
|
tracer.tracef(ARB, "TransformStopped : entering outputQueueMutex\n");
|
EnterCriticalSection(&outputQueueMutex);
|
SetEvent(outputEnqueueEvent);
|
tracer.tracef(ARB, "TransformStopped : set outputEnqueueEvent\n");
|
SetEvent(outputDequeueEvent);
|
tracer.tracef(ARB, "TransformStopped : set outputDequeueEvent\n");
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(ARB, "TransformStopped : left outputQueueMutex. Unpreparing ACM headers\n");
|
|
for (int i=0; i<ACMTRANSFORMER_NUM_STREAMHEADER; i++)
|
{
|
acmStreamHeader[i].cbSrcLength = ACMTRANSFORMER_MAX_SOURCEBUFFERSIZE;
|
acmStreamHeader[i].cbDstLength = ACMTRANSFORMER_MAX_DESTBUFFERSIZE;
|
EnterCriticalSection(&acmMutex);
|
tracer.tracef(ARB, "TransformStopped : calling acmStreamUnprepareHeader\n");
|
result = acmStreamUnprepareHeader(hACMStream, &acmStreamHeader[i], 0);
|
LeaveCriticalSection(&acmMutex);
|
if (result != 0)
|
{
|
tracer.tracef(ERR, "TransformStopped : acmStreamUnprepareHeader returned %d 0x%x\n", result, result);
|
}
|
}
|
tracer.tracef(ARB, "TransformStopped : done unpreparing ACM headers\n");
|
EnterCriticalSection(&acmMutex);
|
tracer.tracef(ARB, "TransformStopped : calling acmStreamClose\n");
|
result = acmStreamClose(hACMStream, 0);
|
LeaveCriticalSection(&acmMutex);
|
if (result != 0)
|
{
|
tracer.tracef(ERR, "TransformStopped : acmStreamClose returned %d 0x%x\n", result, result);
|
}
|
}
|
LeaveCriticalSection(&stateMutex);
|
tracer.tracef(ARB, "TransformStopped : left stateMutex\n");
|
tracer.tracef(EE, "~TransformStopped : bRunning = %d\n", bRunning);
|
return result;
|
}
|
|
int
|
ACMTransformer::FlushBuffer()
|
{
|
tracer.tracef(DET, "FlushBuffer : entering outputQueueMutex\n");
|
EnterCriticalSection(&outputQueueMutex);
|
int numBytes = outputQueue.size();
|
for (int i=0; i<numBytes; i++)
|
{
|
outputQueue.pop();
|
}
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(DET, "FlushBuffer : left outputQueueMutex\n");
|
SetEvent(outputDequeueEvent);
|
tracer.tracef(DET, "FlushBuffer : set outputDequeueEvent\n");
|
return 0;
|
}
|
|
int
|
ACMTransformer::TransformAudioSamples(std::vector<std::pair<AudioSample *, AudioSource *> > &data, AudioSample **ppAudioSample)
|
{
|
return 0;
|
}
|
|
bool
|
ACMTransformer::IsRunning()
|
{
|
EnterCriticalSection(&queryStateMutex);
|
bool result = bRunning;
|
LeaveCriticalSection(&queryStateMutex);
|
return result;
|
}
|
|
void
|
ACMTransformer::SetRunning(bool running)
|
{
|
EnterCriticalSection(&queryStateMutex);
|
bRunning = running;
|
LeaveCriticalSection(&queryStateMutex);
|
}
|
|
ACMSTREAMHEADER *
|
ACMTransformer::GetBuffer()
|
{
|
ACMSTREAMHEADER *acmHdr = NULL;
|
while(IsRunning())
|
{
|
tracer.tracef(ARB, "GetBuffer : entering freeBuffersMutex\n");
|
EnterCriticalSection(&freeBuffersMutex);
|
if (freeBuffers.size() > 0)
|
{
|
acmHdr = freeBuffers.front();
|
freeBuffers.pop_front();
|
LeaveCriticalSection(&freeBuffersMutex);
|
tracer.tracef(ARB, "GetBuffer : left freeBuffersMutex\n");
|
break;
|
}
|
else
|
{
|
LeaveCriticalSection(&freeBuffersMutex);
|
tracer.tracef(ARB, "GetBuffer : left freeBuffersMutex\n");
|
tracer.tracef(ARB, "GetBuffer : before waiting for freeBuffersEvent\n");
|
WaitForSingleObject(freeBuffersEvent, INFINITE);
|
tracer.tracef(ARB, "GetBuffer : after waiting for freeBuffersEvent\n");
|
}
|
}
|
return acmHdr;
|
}
|
|
void
|
ACMTransformer::ReleaseBuffer(ACMSTREAMHEADER *acmHdr)
|
{
|
tracer.tracef(ARB, "ReleaseBuffer : entering freeBuffersMutex\n");
|
EnterCriticalSection(&freeBuffersMutex);
|
freeBuffers.push_back(acmHdr);
|
SetEvent(freeBuffersEvent);
|
tracer.tracef(ARB, "ReleaseBuffer : set freeBuffersEvent\n");
|
LeaveCriticalSection(&freeBuffersMutex);
|
tracer.tracef(ARB, "ReleaseBuffer : left freeBuffersMutex\n");
|
}
|
|
int
|
ACMTransformer::RenderAudioSamples(std::vector<std::pair<AudioSample *, AudioSource *> > &data)
|
{
|
tracer.tracef(DET, "RenderAudioSamples\n");
|
int result(0);
|
AudioSample *inSample = NULL;
|
if (data.size() > 0)
|
{
|
inSample = data[0].first;
|
}
|
if (inSample && IsRunning())
|
{
|
if (inSample->DataSize() > 0)
|
{
|
ACMSTREAMHEADER *acmHdr = GetBuffer();
|
if (acmHdr)
|
{
|
int extraBytes = acmHdr->cbSrcLength - acmHdr->cbSrcLengthUsed;
|
int totalBytes = inSample->DataSize() + extraBytes;
|
if ( totalBytes > 0 && totalBytes <= ACMTRANSFORMER_MAX_SOURCEBUFFERSIZE)
|
{
|
acmHdr->cbSrcLength = totalBytes;
|
memcpy(acmHdr->pbSrc, acmHdr->pbSrc + acmHdr->cbSrcLengthUsed, extraBytes);
|
memcpy(acmHdr->pbSrc + extraBytes, inSample->Data(), inSample->DataSize());
|
EnterCriticalSection(&acmMutex);
|
result = acmStreamConvert(hACMStream, acmHdr, ACM_STREAMCONVERTF_BLOCKALIGN);
|
if (result != 0)
|
{
|
LeaveCriticalSection(&acmMutex);
|
ReleaseBuffer(acmHdr);
|
tracer.tracef(ERR, "RenderAudioSamples : acmStreamConvert failed with error code %d 0x%x\n", result, result);
|
result = -10;
|
Sleep(outputDuration);
|
}
|
else if (acmHdr->cbDstLengthUsed > 0)
|
{
|
unsigned char convertedData[ACMTRANSFORMER_MAX_DESTBUFFERSIZE];
|
memcpy(convertedData, acmHdr->pbDst, acmHdr->cbDstLengthUsed);
|
LeaveCriticalSection(&acmMutex);
|
ReleaseBuffer(acmHdr);
|
AddConvertedData(convertedData, acmHdr->cbDstLengthUsed);
|
// AddConvertedData(acmHdr->pbDst, acmHdr->cbDstLengthUsed);
|
}
|
else
|
{
|
LeaveCriticalSection(&acmMutex);
|
ReleaseBuffer(acmHdr);
|
}
|
}
|
else
|
{
|
ReleaseBuffer(acmHdr);
|
Sleep(outputDuration);
|
result = -20;
|
}
|
}
|
else
|
{
|
Sleep(outputDuration);
|
result = -30;
|
}
|
}
|
else
|
{
|
Sleep(inSample->GetSilenceDuration());
|
result = -40;
|
}
|
}
|
else
|
{
|
Sleep(outputDuration);
|
result = -50;
|
}
|
tracer.tracef(DET, "~RenderAudioSamples : %d\n", result);
|
return result;
|
}
|
|
int
|
ACMTransformer::GenerateData(AudioSample **ppAudioSample)
|
{
|
tracer.tracef(DET, "GenerateData\n");
|
int result(0);
|
*ppAudioSample = NULL;
|
AudioSample *audioSample = NULL;
|
(AudioSampleManager::GetInstance())->GetAudioSample(&audioSample, this);
|
if (audioSample)
|
{
|
audioSample->SetFormat(destFormat);
|
audioSample->SetDataSize(0);
|
audioSample->SetSilenceDuration(outputDuration);
|
int numBytesToWrite = (audioSample->MinFrameSize() * outputDuration * 1000) / audioSample->MinFrameDuration();
|
|
if (IsRunning())
|
{
|
tracer.tracef(DET, "GenerateData : entering outputQueueMutex. Filter running\n");
|
EnterCriticalSection(&outputQueueMutex);
|
if (outputQueue.size() < numBytesToWrite)
|
{
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(DET, "GenerateData : left outputQueueMutex\n");
|
result = -10;
|
}
|
else
|
{
|
int numBytesToCopy = min(numBytesToWrite, audioSample->BufferSize());
|
for (int i=0; i<numBytesToCopy; i++)
|
{
|
unsigned char c = outputQueue.front();
|
outputQueue.pop();
|
(audioSample->Data())[i] = c;
|
}
|
SetEvent(outputDequeueEvent);
|
tracer.tracef(DET, "GenerateData : set outputDequeueEvent\n");
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(DET, "GenerateData : left outputQueueMutex\n");
|
audioSample->SetDataSize(numBytesToCopy);
|
}
|
}
|
else
|
{
|
tracer.tracef(DET, "GenerateData : entering outputQueueMutex\n");
|
EnterCriticalSection(&outputQueueMutex);
|
SetEvent(outputDequeueEvent);
|
tracer.tracef(DET, "GenerateData : set outputDequeueEvent\n");
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(DET, "GenerateData : left outputQueueMutex\n");
|
result = -20;
|
}
|
}
|
else
|
{
|
tracer.tracef(ERR, "GenerateData : could not get audioSample from manager\n");
|
result = -30;
|
}
|
if (result != 0)
|
{
|
if (audioSample)
|
{
|
audioSample->Release(this);
|
audioSample = NULL;
|
}
|
}
|
*ppAudioSample = audioSample;
|
if (result == 0)
|
{
|
tracer.tracef(DET, "~GenerateData : 0x%x\n", *ppAudioSample);
|
}
|
else
|
{
|
tracer.tracef(ERR, "~GenerateData : %d, 0x%x\n", result, *ppAudioSample);
|
}
|
|
return result;
|
}
|
|
int
|
ACMTransformer::AddConvertedData(unsigned char *dataIn, int numBytes)
|
{
|
while (true)
|
{
|
if (IsRunning())
|
{
|
tracer.tracef(DET, "AddConvertedData : entering outputQueueMutex\n");
|
EnterCriticalSection(&outputQueueMutex);
|
int queueSpace = ACMTRANSFORMER_OUTPUTBUFFERSIZE - outputQueue.size();
|
if (queueSpace >= numBytes)
|
{
|
for (int i=0; i<numBytes; i++)
|
{
|
outputQueue.push(dataIn[i]);
|
}
|
SetEvent(outputEnqueueEvent);
|
tracer.tracef(DET, "AddConvertedData : set outputEnqueueEvent\n");
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(DET, "AddConvertedData : left outputQueueMutex\n");
|
break;
|
}
|
else
|
{
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(DET, "AddConvertedData : left outputQueueMutex. Begin waiting on outputDequeueEvent\n");
|
WaitForSingleObject(outputDequeueEvent, INFINITE);
|
tracer.tracef(DET, "AddConvertedData : after waiting on outputDequeueEvent\n");
|
}
|
}
|
else
|
{
|
tracer.tracef(DET, "AddConvertedData : entering outputQueueMutex\n");
|
EnterCriticalSection(&outputQueueMutex);
|
SetEvent(outputEnqueueEvent);
|
tracer.tracef(DET, "AddConvertedData : set outputEnqueueEvent\n");
|
LeaveCriticalSection(&outputQueueMutex);
|
tracer.tracef(DET, "AddConvertedData : left outputQueueMutex\n");
|
break;
|
}
|
}
|
return 0;
|
}
|
|
int
|
ACMTransformer::SetOutputDuration(int millisecs)
|
{
|
outputDuration = millisecs;
|
return 0;
|
}
|
|
void CALLBACK
|
ACMTransformer::ACMCallback(HACMSTREAM has, UINT uMsg, DWORD dwInstance, LPARAM lParam1, LPARAM lParam2)
|
{
|
ACMTransformer *acmTransformer = (ACMTransformer *)dwInstance;
|
ACMSTREAMHEADER *acmHdr = (ACMSTREAMHEADER *)lParam1;
|
switch(uMsg)
|
{
|
case MM_ACM_OPEN:
|
break;
|
case MM_ACM_CLOSE:
|
break;
|
case MM_ACM_DONE:
|
if (acmHdr->cbDstLengthUsed > 0)
|
{
|
acmTransformer->AddConvertedData(acmHdr->pbDst, acmHdr->cbDstLengthUsed);
|
}
|
acmTransformer->ReleaseBuffer(acmHdr);
|
break;
|
default:
|
break;
|
}
|
}
|