// AudioSink.cpp: implementation of the AudioSink class.
|
//
|
//////////////////////////////////////////////////////////////////////
|
|
#include <algorithm>
|
|
#include "AudioSink.h"
|
#include "AudioSource.h"
|
#include "AudioSample.h"
|
|
using namespace std;
|
|
//////////////////////////////////////////////////////////////////////
|
// Construction/Destruction
|
//////////////////////////////////////////////////////////////////////
|
|
AudioSink::AudioSink()
|
{
|
bSinkRunning = false;
|
bSinkThreadRunning = false;
|
sinkThreadHandle = NULL;
|
sinkThreadID = 0;
|
sinkThreadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
sinkThreadStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
InitializeCriticalSection(&audioSourcesMutex);
|
InitializeCriticalSection(&sinkThreadMutex);
|
InitializeCriticalSection(&sinkStateMutex);
|
InitializeCriticalSection(&sinkStartStopMutex);
|
}
|
|
AudioSink::~AudioSink()
|
{
|
CloseHandle(sinkThreadStartEvent);
|
CloseHandle(sinkThreadStopEvent);
|
DeleteCriticalSection(&audioSourcesMutex);
|
DeleteCriticalSection(&sinkThreadMutex);
|
DeleteCriticalSection(&sinkStateMutex);
|
DeleteCriticalSection(&sinkStartStopMutex);
|
}
|
|
int
|
AudioSink::SubscribeAudioSource(AudioSource *audioSource)
|
{
|
EnterCriticalSection(&audioSourcesMutex);
|
if (find(audioSources.begin(), audioSources.end(), audioSource) == audioSources.end())
|
{
|
audioSources.push_back(audioSource);
|
}
|
LeaveCriticalSection(&audioSourcesMutex);
|
return 0;
|
}
|
|
int
|
AudioSink::UnsubscribeAudioSource(AudioSource *audioSource)
|
{
|
EnterCriticalSection(&audioSourcesMutex);
|
vector<AudioSource *>::iterator position = NULL;
|
position = find(audioSources.begin(), audioSources.end(), audioSource);
|
if (position != audioSources.end())
|
{
|
audioSources.erase(position);
|
}
|
LeaveCriticalSection(&audioSourcesMutex);
|
return 0;
|
}
|
|
int
|
AudioSink::RenderAudioSamples(std::vector<std::pair<AudioSample *, AudioSource *> > &data)
|
{
|
tracer.tracef(ERR, "RenderAudioSamples : ERROR : pure virtual function call !!!\n");
|
return 0;
|
}
|
|
int
|
AudioSink::TakeNextAudioSample(AudioSample *audioSample, AudioSource *audioSource)
|
{
|
int result(0);
|
inputData.clear();
|
if (audioSample)
|
{
|
audioSample->AddRef(this); // we later release it
|
inputData.push_back(make_pair(audioSample, audioSource));
|
}
|
|
EnterCriticalSection(&audioSourcesMutex);
|
vector<AudioSource *>::iterator iter = audioSources.begin();
|
for (iter=audioSources.begin(); iter!=audioSources.end(); iter++)
|
{
|
if (*iter == audioSource)
|
{
|
continue;
|
}
|
AudioSample *pAudioSample = NULL;
|
// tracer.tracef(DET, "TakeNextAudioSample : calling GiveNextAudioSample\n");
|
result = (*iter)->GiveNextAudioSample(&pAudioSample, this);
|
if (result != 0)
|
{
|
tracer.tracef(ERR, "TakeNextAudioSample : GiveNextAudioSample from %s returned error %d\n", (*iter)->TraceName(), result);
|
if (pAudioSample)
|
{
|
pAudioSample->Release(this);
|
pAudioSample = NULL;
|
}
|
}
|
else
|
{
|
if (pAudioSample)
|
{
|
inputData.push_back(make_pair(pAudioSample, *iter));
|
}
|
}
|
}
|
LeaveCriticalSection(&audioSourcesMutex);
|
result = RenderAudioSamples(inputData);
|
for (int i=0; i<inputData.size(); i++)
|
{
|
AudioSample *sample = inputData[i].first;
|
if (sample)
|
{
|
sample->Release(this);
|
sample = NULL;
|
}
|
}
|
return result;
|
}
|
|
int
|
AudioSink::StartSink()
|
{
|
int result(0);
|
SetTraceLevel();
|
EnterCriticalSection(&sinkStateMutex);
|
if (bSinkThreadRunning || bSinkRunning)
|
{
|
result = -10;
|
tracer.tracef(ERR, "StartSink : bSinkRunning = %d, bSinkThreadRunning = %d\n", bSinkRunning, bSinkThreadRunning);
|
}
|
else
|
{
|
result = SinkStarted();
|
if (result != 0)
|
{
|
tracer.tracef(ERR, "StartSink : SinkStarted returned %d\n", result);
|
result = -20;
|
}
|
else
|
{
|
bSinkRunning = true;
|
}
|
}
|
LeaveCriticalSection(&sinkStateMutex);
|
return result;
|
}
|
|
int
|
AudioSink::StopSink()
|
{
|
int result(0);
|
EnterCriticalSection(&sinkStateMutex);
|
if (bSinkThreadRunning)
|
{
|
result = -10;
|
tracer.tracef(ERR, "StopSink : bSinkThreadRunning = %d\n", bSinkThreadRunning);
|
}
|
else
|
{
|
if (bSinkRunning)
|
{
|
result = SinkStopped();
|
if (result != 0)
|
{
|
tracer.tracef(ERR, "StartSink : SinkStarted returned %d\n", result);
|
result = -20;
|
}
|
else
|
{
|
bSinkRunning = false;
|
}
|
}
|
else
|
{
|
tracer.tracef(SIG, "StopSink : bSinkRunning = false\n");
|
}
|
}
|
LeaveCriticalSection(&sinkStateMutex);
|
return result;
|
}
|
|
int
|
AudioSink::StartSinkThread()
|
{
|
int result(0);
|
int returnCode(0);
|
tracer.tracef(EE, "StartSinkThread\n");
|
SetTraceLevel();
|
EnterCriticalSection(&sinkStateMutex);
|
tracer.tracef(ARB, "StartSinkThread : entered sinkStateMutex\n");
|
do
|
{
|
if (bSinkRunning || bSinkThreadRunning)
|
{
|
tracer.tracef(ERR, "StartSinkThread : e-error : bSinkRunning = %d, bSinkThreadRunning = %d\n", bSinkRunning, bSinkThreadRunning);
|
result = -10;
|
break;
|
}
|
if (sinkThreadHandle != NULL || sinkThreadID != 0)
|
{
|
tracer.tracef(ERR, "StartSinkThread : e-error : sinkThreadHandle = 0x%x, sinkThreadID = 0x%x\n", sinkThreadHandle, sinkThreadID);
|
result = -20;
|
break;
|
}
|
ResetEvent(sinkThreadStartEvent);
|
ResetEvent(sinkThreadStopEvent);
|
bSinkThreadShouldStop = true; // we make it false only if everything else succeeds
|
sinkThreadHandle = CreateThread(NULL, 0, SinkThreadProc, (void *)this, CREATE_SUSPENDED, &sinkThreadID);
|
if (sinkThreadHandle == NULL)
|
{
|
sinkThreadID = 0;
|
tracer.tracef(ERR, "StartSinkThread : e-CreateThread\n");
|
result = -30;
|
break;
|
}
|
returnCode = SinkThreadStarted(sinkThreadHandle, sinkThreadID);
|
if (returnCode != 0)
|
{
|
tracer.tracef(ERR, "StartSinkThread : e-SinkThreadStarted : %d 0x%x\n", returnCode, returnCode);
|
result = -40;
|
}
|
if (result == 0) // this ensures that if SinkThreadStarted failed, then we stop the thread
|
{
|
bSinkThreadShouldStop = false;
|
}
|
returnCode = ResumeThread(sinkThreadHandle);
|
if (result == -1)
|
{
|
TerminateThread(sinkThreadHandle, 0);
|
sinkThreadHandle = NULL;
|
sinkThreadID = 0;
|
tracer.tracef(ERR, "StartSinkThread : e-ResumeThread : %d 0x%x\n", returnCode, returnCode);
|
result = -50;
|
break;
|
}
|
WaitForSingleObject(sinkThreadStartEvent, INFINITE);
|
}
|
while (false);
|
LeaveCriticalSection(&sinkStateMutex);
|
tracer.tracef(ARB, "StartSinkThread : left sinkStateMutex\n");
|
tracer.tracef(EE, "~StartSinkThread : returning %d\n", result);
|
return result;
|
}
|
|
int
|
AudioSink::StopSinkThread()
|
{
|
int result(0);
|
int returnCode(0);
|
bool waitForThread = true;
|
tracer.tracef(EE, "StopSinkThread\n");
|
EnterCriticalSection(&sinkStartStopMutex);
|
tracer.tracef(ARB, "StopSinkThread : entered sinkStartStopMutex\n");
|
EnterCriticalSection(&sinkStateMutex);
|
tracer.tracef(ARB, "StopSinkThread : entered sinkStateMutex\n");
|
do
|
{
|
if (!bSinkThreadRunning)
|
{
|
tracer.tracef(SIG, "StopSinkThread : bSinkThreadRunning = false\n");
|
result = 0;
|
waitForThread = false;
|
break;
|
}
|
bSinkThreadShouldStop = true;
|
}
|
while (false);
|
|
LeaveCriticalSection(&sinkStateMutex);
|
tracer.tracef(ARB, "StopSinkThread : left sinkStateMutex\n");
|
if (waitForThread)
|
{
|
tracer.tracef(ARB, "StopSinkThread : before WaitForSingleObject\n");
|
WaitForSingleObject(sinkThreadStopEvent, INFINITE);
|
}
|
LeaveCriticalSection(&sinkStartStopMutex);
|
tracer.tracef(ARB, "StopSinkThread : left sinkStartStopMutex\n");
|
tracer.tracef(EE, "~StopSinkThread : returning %d\n", result);
|
return result;
|
}
|
|
DWORD WINAPI
|
AudioSink::SinkThreadProc(LPVOID params)
|
{
|
int result(0);
|
int returnCode(0);
|
AudioSink *audioSink = (AudioSink *)params;
|
Tracer &tracer = audioSink->tracer;
|
tracer.tracef(EE, "SinkThreadProc : begin\n");
|
audioSink->bSinkThreadRunning = true;
|
tracer.tracef(ARB, "SinkThreadProc : setting sinkThreadStartEvent\n");
|
SetEvent(audioSink->sinkThreadStartEvent);
|
while (!audioSink->bSinkThreadShouldStop)
|
{
|
(audioSink->inputData).clear();
|
tracer.tracef(DET, "SinkThreadProc : Entering audioSourcesMutex\n");
|
EnterCriticalSection(&(audioSink->audioSourcesMutex));
|
vector<AudioSource *>::iterator iter = (audioSink->audioSources).begin();
|
for (iter = (audioSink->audioSources).begin(); iter != (audioSink->audioSources).end(); iter++)
|
{
|
AudioSample *pAudioSample = NULL;
|
returnCode = (*iter)->GiveNextAudioSample(&pAudioSample, audioSink);
|
if (returnCode != 0)
|
{
|
if (pAudioSample)
|
{
|
pAudioSample->Release(audioSink);
|
pAudioSample = NULL;
|
}
|
}
|
else
|
{
|
if (pAudioSample)
|
{
|
(audioSink->inputData).push_back(make_pair(pAudioSample, *iter));
|
}
|
}
|
}
|
LeaveCriticalSection(&(audioSink->audioSourcesMutex));
|
tracer.tracef(DET, "SinkThreadProc : Left audioSourcesMutex\n");
|
tracer.tracef(DET, "SinkThreadProc : Rendering audioSamples\n");
|
returnCode = audioSink->RenderAudioSamples(audioSink->inputData);
|
for (int i=0; i<(audioSink->inputData).size(); i++)
|
{
|
AudioSample *sample = (audioSink->inputData[i]).first;
|
if (sample)
|
{
|
sample->Release(audioSink);
|
sample = NULL;
|
}
|
}
|
}
|
tracer.tracef(ARB, "SinkThreadProc : broke out of while loop\n");
|
|
tracer.tracef(EE, "SinkThreadProc : entering sinkStateMutex\n");
|
EnterCriticalSection(&(audioSink->sinkStateMutex));
|
audioSink->bSinkThreadRunning = false;
|
|
tracer.tracef(ARB, "SinkThreadProc : calling SinkThreadStopped\n");
|
audioSink->SinkThreadStopped(audioSink->sinkThreadHandle, audioSink->sinkThreadID);
|
|
CloseHandle(audioSink->sinkThreadHandle);
|
audioSink->sinkThreadHandle = NULL;
|
audioSink->sinkThreadID = 0;
|
|
LeaveCriticalSection(&(audioSink->sinkStateMutex));
|
tracer.tracef(EE, "SinkThreadProc : left sinkStateMutex\n");
|
|
tracer.tracef(ARB, "SinkThreadProc : setting sinkThreadStopEvent and exiting\n");
|
BOOL setResult = SetEvent(audioSink->sinkThreadStopEvent);
|
return result;
|
}
|
|
int
|
AudioSink::SinkThreadStarted(HANDLE sinkThreadHandle, DWORD sinkThreadID)
|
{
|
return 0;
|
}
|
|
int
|
AudioSink::SinkThreadStopped(HANDLE sinkThreadHandle, DWORD sinkThreadID)
|
{
|
return 0;
|
}
|
|
int
|
AudioSink::SinkStarted()
|
{
|
return 0;
|
}
|
|
int
|
AudioSink::SinkStopped()
|
{
|
return 0;
|
}
|