// WaveAudioSink.cpp: implementation of the WaveAudioSink class.
|
//
|
//////////////////////////////////////////////////////////////////////
|
|
#include "AudioSample.h"
|
#include "AudioSource.h"
|
#include "WaveAudioSink.h"
|
|
using namespace std;
|
|
//////////////////////////////////////////////////////////////////////
|
// Construction/Destruction
|
//////////////////////////////////////////////////////////////////////
|
|
WaveAudioSink::WaveAudioSink()
|
{
|
char subFacilityName[100];
|
sprintf(subFacilityName, "WaveAudioSink:%x", this);
|
tracer.SetSubFacilityName(subFacilityName);
|
SetTraceLevel();
|
tracer.tracef(EE, "Constructor begin\n");
|
enabled = true;
|
waveoutDeviceID = WAVE_MAPPER;
|
frameSize = WAVESINK_DEFAULT_WAVEHDR_SIZE;
|
numFrames = WAVESINK_NUM_WAVEHDR;
|
InitializeCriticalSection(&dataMutex);
|
InitializeCriticalSection(&freeBuffersMutex);
|
InitializeCriticalSection(&filterMutex);
|
dataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
pauseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
// set default format
|
wfx.nAvgBytesPerSec = 16000;
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
wfx.wBitsPerSample = 16;
|
wfx.nChannels = 1;
|
wfx.nBlockAlign = 2;
|
wfx.nSamplesPerSec = 8000;
|
wfx.cbSize = 0;
|
tracer.tracef(EE, "Constructor end\n");
|
}
|
|
WaveAudioSink::~WaveAudioSink()
|
{
|
tracer.tracef(EE, "Destructor begin\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "WaveAudioSink~ : entered filterMutex\n");
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "WaveAudioSink~ : left filterMutex\n");
|
DeleteCriticalSection(&dataMutex);
|
DeleteCriticalSection(&freeBuffersMutex);
|
DeleteCriticalSection(&filterMutex);
|
CloseHandle(dataEvent);
|
CloseHandle(pauseEvent);
|
tracer.tracef(EE, "Destructor end\n");
|
}
|
|
int
|
WaveAudioSink::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", "WaveAudioSink", 0x100000);
|
}
|
tracer.SetSystemMask(SystemMask);
|
return 0;
|
}
|
|
int
|
WaveAudioSink::SetVolume(unsigned long volume)
|
{
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetVolume %d\n", volume);
|
volume = min(100, volume);
|
unsigned long normalizedVolume = (volume * 0xffff) / 100;
|
unsigned long volumeToSet = (normalizedVolume << 16) + normalizedVolume;
|
tracer.tracef(SDI_LEVEL_DETAILED, "SetVolume : Entering dataMutex\n");
|
EnterCriticalSection(&dataMutex);
|
if (bRunning)
|
{
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "SetVolume : waveOutSetVolume\n");
|
MMRESULT mr = waveOutSetVolume(hWaveOut, volumeToSet);
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "SetVolume : ~waveOutSetVolume\n");
|
LeaveCriticalSection(&dataMutex);
|
tracer.tracef(SDI_LEVEL_DETAILED, "SetVolume : Left dataMutex\n");
|
if (FAILED(mr))
|
{
|
tracer.tracef(SDI_LEVEL_ERROR, "SetVolume : e-waveOutSetVolume : 0x%x\n", mr);
|
}
|
}
|
else
|
{
|
LeaveCriticalSection(&dataMutex);
|
tracer.tracef(SDI_LEVEL_DETAILED, "SetVolume : Left dataMutex\n");
|
}
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SetVolume\n");
|
return NOERROR;
|
}
|
|
|
int
|
WaveAudioSink::SetFrameSize(unsigned long size)
|
{
|
tracer.tracef(EE, "SetFrameSize %u\n", size);
|
frameSize = min(size, WAVESINK_MAX_WAVEHDR_SIZE);
|
tracer.tracef(EE, "~SetFrameSize\n");
|
return NOERROR;
|
}
|
|
|
int
|
WaveAudioSink::SetNumFrames(unsigned long num)
|
{
|
tracer.tracef(EE, "SetNumFrames %u\n", num);
|
numFrames = min(num, WAVESINK_MAX_WAVEHDR);
|
tracer.tracef(EE, "~SetNumFrames\n");
|
return NOERROR;
|
}
|
|
|
|
int
|
WaveAudioSink::BeginAudioRender()
|
{
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "BeginAudioRender\n");
|
HRESULT result = NOERROR;
|
|
tracer.tracef(SDI_LEVEL_DETAILED, "BeginAudioRender : Entering freeBuffersMutex\n");
|
EnterCriticalSection(&freeBuffersMutex);
|
freeBuffers.clear();
|
LeaveCriticalSection(&freeBuffersMutex);
|
tracer.tracef(SDI_LEVEL_DETAILED, "BeginAudioRender : Left freeBuffersMutex\n");
|
|
MMRESULT mr = NOERROR;
|
WaveFormat::TraceFormat(&tracer, ARB, wfx);
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "BeginAudioRender : waveOutOpen\n");
|
mr = waveOutOpen(&hWaveOut, waveoutDeviceID, &wfx, (DWORD)SpeakerCallback, DWORD(this), CALLBACK_FUNCTION);
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "BeginAudioRender : ~waveOutOpen\n");
|
if (mr != NOERROR)
|
{
|
tracer.tracef(SDI_LEVEL_ERROR, "BeginAudioRender : e-waveOutOpen : 0x%x\n", mr);
|
WaveFormat::TraceFormat(&tracer, ERR, wfx);
|
Sleep(1000);
|
tracer.tracef(SDI_LEVEL_ERROR, "BeginAudioRender : waveOutOpen : 2nd try\n");
|
mr = waveOutOpen(&hWaveOut, waveoutDeviceID, &wfx, (DWORD)SpeakerCallback, DWORD(this), CALLBACK_FUNCTION);
|
tracer.tracef(SDI_LEVEL_ERROR, "BeginAudioRender : ~waveOutOpen : returned 0x%x\n", mr);
|
if (mr != NOERROR)
|
{
|
tracer.tracef(SDI_LEVEL_ERROR, "BeginAudioRender : e-waveOutOpen : 0x%x\n", mr);
|
WaveFormat::TraceFormat(&tracer, ERR, wfx);
|
result = -10;
|
}
|
}
|
if (result == 0)
|
{
|
for (int i=0; i<numFrames; i++)
|
{
|
memset(&(waveHdr[i]), 0, sizeof(waveHdr[i]));
|
waveHdr[i].lpData = dataBuffer[i];
|
waveHdr[i].dwBufferLength = frameSize;
|
waveHdr[i].dwFlags = 0;
|
waveHdr[i].dwUser = i;
|
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "BeginAudioRender : waveOutPrepareHeader\n");
|
result = waveOutPrepareHeader(hWaveOut, &(waveHdr[i]), sizeof(waveHdr[i]));
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "BeginAudioRender : ~waveOutPrepareHeader\n");
|
if (result != MMSYSERR_NOERROR)
|
{
|
tracer.tracef(SDI_LEVEL_ERROR, "BeginAudioRender : e-waveOutPrepareHeader : 0x%x\n", result);
|
}
|
else
|
{
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "BeginAudioRender : Entering freeBuffersMutex\n");
|
EnterCriticalSection(&freeBuffersMutex);
|
freeBuffers.push_back(&(waveHdr[i]));
|
LeaveCriticalSection(&freeBuffersMutex);
|
}
|
}
|
}
|
if (result == 0)
|
{
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~BeginAudioRender : returning %d 0x%x\n", result, result);
|
}
|
else
|
{
|
tracer.tracef(ERR, "~BeginAudioRender : returning %d 0x%x\n", result, result);
|
}
|
return result;
|
}
|
|
|
int
|
WaveAudioSink::EndAudioRender()
|
{
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "EndAudioRender\n");
|
HRESULT result = NOERROR;
|
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "EndAudioRender : waveOutReset\n");
|
result = waveOutReset(hWaveOut);
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "EndAudioRender : ~waveOutReset\n");
|
if (result != MMSYSERR_NOERROR)
|
{
|
tracer.tracef(SDI_LEVEL_ERROR, "EndAudioRender : e-waveOutReset : 0x%x\n", result);
|
result = -10;
|
}
|
else
|
{
|
for (int i=0; i<numFrames; i++)
|
{
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "EndAudioRender : waveOutUnprepareHeader\n");
|
result = waveOutUnprepareHeader(hWaveOut, &(waveHdr[i]), sizeof(waveHdr[i]));
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "EndAudioRender : ~waveOutUnprepareHeader\n");
|
if (result != MMSYSERR_NOERROR)
|
{
|
tracer.tracef(SDI_LEVEL_ERROR, "EndAudioRender : e-waveOutUnprepareHeader : 0x%x\n", result);
|
}
|
}
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "EndAudioRender : waveOutClose\n");
|
result = waveOutClose(hWaveOut);
|
tracer.tracef(SDI_LEVEL_ARBITRARY, "EndAudioRender : ~waveOutClose\n");
|
if (result != MMSYSERR_NOERROR)
|
{
|
tracer.tracef(SDI_LEVEL_ERROR, "EndAudioRender : e-waveOutClose : 0x%x\n", result);
|
result = -30;
|
}
|
}
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~EndAudioRender : returning %d 0x%x\n", result, result);
|
return result;
|
}
|
|
|
int
|
WaveAudioSink::ResetAudioRender()
|
{
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "ResetAudioRender\n");
|
HRESULT result = NOERROR;
|
result = EndAudioRender();
|
if (result == MMSYSERR_NOERROR)
|
{
|
result = BeginAudioRender();
|
}
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~ResetAudioRender : %d 0x%x\n", result, result);
|
return result;
|
}
|
|
|
WAVEHDR *
|
WaveAudioSink::GetBuffer()
|
{
|
tracer.tracef(DET, "GetBuffer\n");
|
HRESULT result = NOERROR;
|
WAVEHDR *wHdr = NULL;
|
int bufferLength;
|
while(true) {
|
tracer.tracef(SDI_LEVEL_DETAILED, "GetBuffer : Entering dataMutex\n");
|
EnterCriticalSection(&dataMutex);
|
bool running = bRunning;
|
LeaveCriticalSection(&dataMutex);
|
tracer.tracef(SDI_LEVEL_DETAILED, "GetBuffer : Left dataMutex\n");
|
if (!running)
|
{
|
tracer.tracef(SDI_LEVEL_DETAILED, "GetBuffer : !bRunning\n");
|
break;
|
}
|
tracer.tracef(SDI_LEVEL_DETAILED, "GetBuffer : Entering freeBuffersMutex\n");
|
EnterCriticalSection(&freeBuffersMutex);
|
bufferLength = freeBuffers.size();
|
if (bufferLength > 0)
|
{
|
tracer.tracef(SDI_LEVEL_DETAILED, "GetBuffer : bufferLength > 0\n");
|
wHdr = freeBuffers.front();
|
freeBuffers.pop_front();
|
LeaveCriticalSection(&freeBuffersMutex);
|
tracer.tracef(SDI_LEVEL_DETAILED, "GetBuffer : Left freeBuffersMutex\n");
|
break;
|
}
|
else
|
{
|
tracer.tracef(SDI_LEVEL_DETAILED, "GetBuffer : bufferLength <= 0\n");
|
ResetEvent(dataEvent);
|
LeaveCriticalSection(&freeBuffersMutex);
|
tracer.tracef(SDI_LEVEL_DETAILED, "GetBuffer : Left freeBuffersMutex - waiting for dataEvent (1000 ms)\n");
|
WaitForSingleObject(dataEvent, 1000);
|
}
|
}
|
tracer.tracef(DET, "~GetBuffer\n");
|
return wHdr;
|
}
|
|
|
void CALLBACK
|
WaveAudioSink::SpeakerCallback(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) {
|
WAVEHDR *wHdr;
|
WaveAudioSink *filter = (WaveAudioSink *)dwInstance;
|
// (filter->tracer).tracef(SDI_LEVEL_ENTRY_EXIT, "SpeakerCallback\n");
|
switch(uMsg) {
|
case WOM_OPEN:
|
// (filter->tracer).tracef(SDI_LEVEL_ARBITRARY, "SpeakerCallback : WOM_OPEN\n");
|
break;
|
case WOM_CLOSE:
|
// (filter->tracer).tracef(SDI_LEVEL_ARBITRARY, "SpeakerCallback : WOM_CLOSE\n");
|
SetEvent(filter->dataEvent);
|
break;
|
case WOM_DONE:
|
(filter->tracer).tracef(SDI_LEVEL_DETAILED, "SpeakerCallback : WOM_DONE\n");
|
wHdr = (WAVEHDR *)dwParam1;
|
(filter->tracer).tracef(SDI_LEVEL_DETAILED, "SpeakerCallback : Entering freeBuffersMutex\n");
|
EnterCriticalSection(&(filter->freeBuffersMutex));
|
(filter->freeBuffers).push_back(wHdr);
|
SetEvent(filter->dataEvent); // we do this within the critsec because getbuffer resets this within the same critsec
|
LeaveCriticalSection(&(filter->freeBuffersMutex));
|
(filter->tracer).tracef(SDI_LEVEL_DETAILED, "SpeakerCallback : Left freeBuffersMutex\n");
|
break;
|
default:
|
break;
|
}
|
// (filter->tracer).tracef(SDI_LEVEL_ENTRY_EXIT, "~SpeakerCallback\n");
|
}
|
|
|
int
|
WaveAudioSink::SinkStarted()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SinkStarted\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkStarted : entered filterMutex\n");
|
do
|
{
|
if (bRunning)
|
{
|
tracer.tracef(ERR, "SinkStarted : e-error : already running\n");
|
result = -10;
|
break;
|
}
|
returnCode = BeginAudioRender();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SinkStarted : e-BeginAudioRender : %d 0x%x\n", returnCode, returnCode);
|
result = -20;
|
break;
|
}
|
bRunning = true;
|
}
|
while (false);
|
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkStarted : left filterMutex\n");
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SinkStarted : returning %d\n", result);
|
return result;
|
}
|
|
|
int
|
WaveAudioSink::SinkStopped()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SinkStopped\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkStopped : entered filterMutex\n");
|
do
|
{
|
if (!bRunning)
|
{
|
tracer.tracef(ERR, "SinkStopped : e-error : not running\n");
|
result = -10;
|
break;
|
}
|
returnCode = EndAudioRender();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SinkStopped : e-EndAudioRender : %d 0x%x\n", returnCode, returnCode);
|
result = -20;
|
break;
|
}
|
bRunning = false;
|
}
|
while (false);
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkStopped : left filterMutex\n");
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SinkStopped : returning %d\n", result);
|
return result;
|
}
|
|
int
|
WaveAudioSink::SinkThreadStarted(HANDLE sourceThreadHandle, DWORD sourceThreadID)
|
{
|
int result(0);
|
int returnCode(0);
|
SetTraceLevel();
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SinkThreadStarted\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkThreadStarted : entered filterMutex\n");
|
do
|
{
|
returnCode = SetThreadPriority(sinkThreadHandle, THREAD_PRIORITY_TIME_CRITICAL);
|
if (returnCode == 0)
|
{
|
returnCode = GetLastError();
|
tracer.tracef(ERR, "SinkThreadStarted : e-SetThreadPriority : %d 0x%x\n", returnCode, returnCode);
|
}
|
returnCode = BeginAudioRender();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SinkThreadStarted : e-BeginAudioRender : %d 0x%x\n", returnCode, returnCode);
|
result = -10;
|
break;
|
}
|
bRunning = true;
|
}
|
while (false);
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkThreadStarted : left filterMutex\n");
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SinkThreadStarted : returning %d\n", result);
|
return result;
|
}
|
|
int
|
WaveAudioSink::SinkThreadStopped(HANDLE sourceThreadHandle, DWORD sourceThreadID)
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SinkThreadStopped\n");
|
EnterCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkThreadStopped : entered filterMutex\n");
|
do
|
{
|
if (!bRunning)
|
{
|
tracer.tracef(ERR, "SinkThreadStopped : not running\n");
|
result = -10;
|
break;
|
}
|
returnCode = EndAudioRender();
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "SinkThreadStopped : e-EndAudioRender : %d 0x%x\n", returnCode, returnCode);
|
result = -20;
|
break;
|
}
|
bRunning = false;
|
}
|
while (false);
|
LeaveCriticalSection(&filterMutex);
|
tracer.tracef(ARB, "SinkThreadStopped : left filterMutex\n");
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SinkThreadStopped : returning %d\n", result);
|
return result;
|
}
|
|
int
|
WaveAudioSink::SetDeviceID(unsigned int deviceID)
|
{
|
waveoutDeviceID = deviceID;
|
return 0;
|
}
|
|
unsigned int
|
WaveAudioSink::GetDeviceID()
|
{
|
return (unsigned int)waveoutDeviceID;
|
}
|
|
int
|
WaveAudioSink::SetFormat(int waveFormatNumber)
|
{
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetFormat %d\n", waveFormatNumber);
|
wfx = WaveFormat::GetWaveFormat(waveFormatNumber);
|
tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SetFormat\n");
|
return 0;
|
}
|
|
|
int
|
WaveAudioSink::RenderAudioSamples(std::vector<std::pair<AudioSample*, AudioSource* > > &data)
|
{
|
tracer.tracef(SDI_LEVEL_DETAILED, "RenderAudioSamples\n");
|
HRESULT result = NOERROR;
|
AudioSample *sample = NULL;
|
if (data.size() > 0)
|
{
|
sample = data[0].first;
|
}
|
else
|
{
|
Sleep(5);
|
}
|
if (sample)
|
{
|
WAVEHDR *wHdr = GetBuffer();
|
if (!wHdr)
|
{
|
Sleep(5);
|
result = -10;
|
}
|
else
|
{
|
int bytesToPlay;
|
if (sample->DataSize() > 0)
|
{
|
bytesToPlay = min(frameSize, sample->DataSize());
|
tracer.tracef(SDI_LEVEL_DETAILED, "RenderAudioSamples : memcpy(0x%x, 0x%x, %d)\n", wHdr->lpData, sample->Data(), bytesToPlay);
|
memcpy(wHdr->lpData, sample->Data(), bytesToPlay);
|
}
|
else
|
{
|
bytesToPlay = min(frameSize, sample->SilenceSize());
|
tracer.tracef(SDI_LEVEL_DETAILED, "RenderAudioSamples : memset(0x%x, 0, %d)\n", wHdr->lpData, bytesToPlay);
|
memset(wHdr->lpData, 0, bytesToPlay);
|
}
|
wHdr->dwBufferLength = bytesToPlay;
|
tracer.tracef(SDI_LEVEL_DETAILED, "RenderAudioSamples : Entering dataMutex\n");
|
EnterCriticalSection(&dataMutex);
|
tracer.tracef(SDI_LEVEL_DETAILED, "RenderAudioSamples : waveOutWrite\n");
|
MMRESULT mmr = waveOutWrite(hWaveOut, wHdr, sizeof(*wHdr));
|
tracer.tracef(SDI_LEVEL_DETAILED, "RenderAudioSamples : ~waveOutWrite\n");
|
LeaveCriticalSection(&dataMutex);
|
tracer.tracef(SDI_LEVEL_DETAILED, "RenderAudioSamples : Left dataMutex\n");
|
}
|
}
|
tracer.tracef(SDI_LEVEL_DETAILED, "~RenderAudioSamples\n");
|
return result;
|
}
|