// CCNMediaTerm.cpp : Implementation of CCCNMediaTerm #include "stdafx.h" #include "stdlib.h" #include "CCNSMT.h" #include "CCNMediaTerm.h" #include "AudioCodecs.h" #include "../MTC/AudioSampleManager.h" #include "../MTC/Exception.h" #include extern "C" { #include "../MTC/DSP/FIRCoefficients.h" } #include #include using namespace std; long nextCookie; int FilePlay::numOutstandingEvents; bool FilePlay::closing; CRITICAL_SECTION FilePlay::fireEventThreadMutex; HANDLE FilePlay::fireEventThreadEvent; CCCNMediaTerm * FilePlay::mediaTerm; // #define NO_RECV_MIXER int Connect(AudioSource *source, AudioSink *sink) { source->SubscribeAudioSink(sink); sink->SubscribeAudioSource(source); return 0; } int Disconnect(AudioSource *source, AudioSink *sink) { source->UnsubscribeAudioSink(sink); sink->UnsubscribeAudioSource(source); return 0; } int CCCNMediaTerm::SetTraceLevel() { long SystemMask = 0; GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "MTC", 0x100000); if ((SystemMask = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "AllComponents", 0x0)) == 0) SystemMask = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "MTC", 0x100000); tracer.SetSystemMask(SystemMask); return NOERROR; } // #define WM_GRAPHNOTIFY WM_USER + 13 // using namespace std; ///////////////////////////////////////////////////////////////////////////// // CCCNMediaTerm HRESULT CCCNMediaTerm::Initialize() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) CSingleLock cLock(&Mutex); cLock.Lock(); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "Initialize\n"); if (!initialized) { AudioSampleManager::Initialize(); InitializeCriticalSection(&filesPlayingMutex); waveSource = new WaveAudioSource(); waveSink = new WaveAudioSink(); filePlayWaveSink = new WaveAudioSink(); rtpSink = new RTPAudioSink(); rtpSource = new RTPAudioSource(); inputFIRTransformer = new FIRTransformer(); inputFIRTransformer->SetCoefficients(FIR_TELECASTER_TX, FIR_TELECASTER_TX_NUM, 16); outputFIRTransformer = new FIRTransformer(); outputFIRTransformer->SetCoefficients(FIR_TELECASTER_RX, FIR_TELECASTER_RX_NUM, 16); pcm2G711Transformer = new PCM2G711Transformer(); g7112PcmTransformer = new G7112PCMTransformer(); limiter = new Limiter(-8.0, 0.075, -0.00075); voiceActivityDetector = new VoiceActivityDetector(); rtpJitterBuffer = new RTPJitterBuffer(); fixedSizeAudioBuffer = new FixedSizeAudioBuffer(); genericRTP2PCMDecoder = new GenericRTP2PCMDecoder(); rxPCMMixer = new PCMMixer(); txPCMMixer = new PCMMixer(); pcmVolumeMaximizer = new PCMVolumeMaximizer; rxTransformer = g7112PcmTransformer; txTransformer = pcm2G711Transformer; txDuration = 20; rxDuration = 20; filesPlaying.clear(); extraFilePlay = NULL; brxRunning = false; btxRunning = false; bFilePlayRunning = false; nextCookie = 0; waveinDeviceID = WAVE_MAPPER; waveoutDeviceID = WAVE_MAPPER; FilePlay::Initialize(this); initialized = true; } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~Initialize\n"); return NOERROR; } HRESULT CCCNMediaTerm::UnInitialize() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) CSingleLock cLock(&Mutex); cLock.Lock(); tracer.tracef(EE, "UnInitialize\n"); if (initialized) { tracer.tracef(EE, "UnInitialize : Stopping streaming\n"); StopRX(); StopTX(); if (extraFilePlay && extraFilePlay->IsRunning()) { StopPlayingFileRX(extraFilePlay->GetCookie()); } RemoveNonPlayingFiles(); if (g7112PcmTransformer) { delete g7112PcmTransformer; } if (pcm2G711Transformer) { delete pcm2G711Transformer; } if (waveSink) { delete waveSink; } if (filePlayWaveSink) { delete filePlayWaveSink; } if (waveSource) { delete waveSource; } if (rtpSource) { delete rtpSource; } if (rtpSink) { delete rtpSink; } if (inputFIRTransformer) { delete inputFIRTransformer; } if (outputFIRTransformer) { delete outputFIRTransformer; } if (limiter) { delete limiter; } if (voiceActivityDetector) { delete voiceActivityDetector; } if (rxPCMMixer) { delete rxPCMMixer; } if (txPCMMixer) { delete txPCMMixer; } if (rtpJitterBuffer) { delete rtpJitterBuffer; } if (fixedSizeAudioBuffer) { delete fixedSizeAudioBuffer; } if (genericRTP2PCMDecoder) { delete genericRTP2PCMDecoder; } if (pcmVolumeMaximizer) { delete pcmVolumeMaximizer; } DeleteCriticalSection(&filesPlayingMutex); FilePlay::Uninitialize(); AudioSampleManager::Uninitialize(); initialized = false; } else { tracer.tracef(EE, "UnInitialize : initialize = false\n"); } tracer.tracef(EE, "~UnInitialize\n"); return NOERROR; } STDMETHODIMP CCCNMediaTerm::SetAudioCodecRX(long CompressionType, long MillisecPacketSize, long EchoCancellationValue, long G723BitRate) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetAudioCodecRX : %d %d %d %d\n", CompressionType, MillisecPacketSize, EchoCancellationValue, G723BitRate); CSingleLock cLock(&Mutex); cLock.Lock(); SetTraceLevel(); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetAudioCodecRX : brxRunning = %d\n", brxRunning); bool restart = false; int result(0); int resultCode(0); bool needToSet = true; if (brxRunning) { needToSet = false; do { if (CompressionType != rxParameters.CompressionType) { needToSet = true; break; } if (CompressionType == Media_Payload_G7231 && G723BitRate != rxParameters.G723BitRate) { needToSet = true; break; } } while (false); } if (needToSet) { // update rxParameters rxParameters.CompressionType = CompressionType; rxParameters.EchoCancellationValue = EchoCancellationValue; rxParameters.G723BitRate = G723BitRate; rxParameters.MillisecPacketSize = MillisecPacketSize; if (brxRunning) { result = StopRX(); if (result < 0) { resultCode = -10; } restart = true; } if (resultCode == 0) { rxDuration = MillisecPacketSize; waveSink->SetFrameSize(16 * MillisecPacketSize); switch (CompressionType) { case Media_Payload_AutoDetect: rxTransformer = genericRTP2PCMDecoder; break; case Media_Payload_G711Ulaw64k: rxTransformer = g7112PcmTransformer; g7112PcmTransformer->SetULaw(); break; case Media_Payload_G711Alaw64k: rxTransformer = g7112PcmTransformer; g7112PcmTransformer->SetALaw(); break; default: tracer.tracef(SDI_LEVEL_ERROR, "SetAudioCodecRX : Invalid Codec # %d\n", CompressionType); return -30; } if (restart) { result = StartRX(waveoutDeviceID); if (result < 0) { resultCode = -20; } } } } else { tracer.tracef(ARB, "SetAudioCodecRX : no need to restart\n"); } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SetAudioCodecRX : %d\n", resultCode); return resultCode; } STDMETHODIMP CCCNMediaTerm::SetAudioCodecTX(long CompressionType, long MillisecPacketSize, long PrecedenceValue, long SilenceSuppression, unsigned short MaxFramesPerPacket, long G723BitRate) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetAudioCodecTX : %d %d %d %d %d %d\n", CompressionType, MillisecPacketSize, PrecedenceValue, SilenceSuppression, MaxFramesPerPacket, G723BitRate); CSingleLock cLock(&Mutex); cLock.Lock(); SetTraceLevel(); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetAudioCodecTX : btxRunning = %d\n", btxRunning); bool needToSet = false; bool forceVADOff = GetRegKeyBool(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "ForceVADOff", false); int resultCode(0); SilenceSuppression = forceVADOff && SilenceSuppression; if (CompressionType != txParameters.CompressionType || MillisecPacketSize != txParameters.MillisecPacketSize || SilenceSuppression != txParameters.SilenceSuppression || (CompressionType == Media_Payload_G7231 && G723BitRate != txParameters.G723BitRate) ) { needToSet = true; } if (needToSet) { bool restart = false; int result(0); if (btxRunning) { result = StopTX(); if (result < 0) { resultCode = -10; } restart = true; } // update btxVAD after StopTX, so that StopTX knows whether VAD was on or off the last time btxVAD = SilenceSuppression == 0 ? false : true; if (resultCode == 0) { txDuration = MillisecPacketSize; waveSource->SetBufferSize(MillisecPacketSize * 16); switch (CompressionType) { case Media_Payload_G711Ulaw64k: txTransformer = pcm2G711Transformer; pcm2G711Transformer->SetULaw(); break; case Media_Payload_G711Alaw64k: txTransformer = pcm2G711Transformer; pcm2G711Transformer->SetALaw(); break; default: tracer.tracef(SDI_LEVEL_ERROR, "SetAudioCodecTX : Invalid Codec # %d\n", CompressionType); return -30; } if (restart) { result = StartTX(waveinDeviceID); if (result < 0) { resultCode = -20; } } } } if (resultCode == 0) { txParameters.CompressionType = CompressionType; txParameters.G723BitRate = G723BitRate; txParameters.MaxFramesPerPacket = MaxFramesPerPacket; txParameters.MillisecPacketSize = MillisecPacketSize; txParameters.PrecedenceValue = PrecedenceValue; txParameters.SilenceSuppression = SilenceSuppression; } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SetAudioCodecTX : %d\n", resultCode); return resultCode; } STDMETHODIMP CCCNMediaTerm::SetAudioDestination(BSTR strHostName, long nUDPPortNumber) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) USES_CONVERSION; LPSTR hostname = W2A((wchar_t *) strHostName); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetAudioDestination %s %d\n", hostname, nUDPPortNumber); CSingleLock cLock(&Mutex); cLock.Lock(); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetAudioDestination : entered Mutex\n"); int resultCode(0); bool bUseFixedTransmitPort = GetRegKeyBool(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "UseFixedTransmitPort", false); if (bUseFixedTransmitPort) { unsigned short localTransmitPort = (unsigned short)(GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "TransmitPort", 0) % 65536); rtpSink->SetLocalPort(localTransmitPort); } resultCode = rtpSink->SetDestination(hostname, (short)nUDPPortNumber); strcpy(txParameters.strHostName, hostname); txParameters.UDPPortNumber = nUDPPortNumber; tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SetAudioDestination : %d\n", resultCode); return resultCode; } STDMETHODIMP CCCNMediaTerm::SetAudioReceivePort(long nUDPPortNumber) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetAudioReceivePort %d\n", nUDPPortNumber); CSingleLock cLock(&Mutex); cLock.Lock(); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetAudioReceivePort : entered Mutex\n"); bool restart = false; int result(0); int resultCode(0); bool needToSet = false; unsigned short currentReceivePort(0); resultCode = rtpSource->GetReceivePort(¤tReceivePort); if (resultCode != 0 || currentReceivePort != (unsigned short)nUDPPortNumber) { needToSet = true; } if (needToSet) { if (brxRunning) { result = StopRX(); if (result < 0) { resultCode = -10; } restart = true; } if (resultCode == 0) { result = rtpSource->SetReceivePort(nUDPPortNumber); if (result < 0) { resultCode = -20; } } // update rxParameters if (resultCode == 0) { if (restart) { result = StartRX(waveoutDeviceID); } if (result < 0) { resultCode = -30; } } } else { tracer.tracef(ARB, "SetAudioReceivePort : no need to restart\n"); } rtpSource->GetReceivePort(¤tReceivePort); rxParameters.UDPPortNumber = currentReceivePort; if (resultCode == 0) { tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SetAudioReceivePort : %d\n", resultCode); } else { tracer.tracef(ERR, "~StartAudioReceivePort : error %d\n", resultCode); } return resultCode; } STDMETHODIMP CCCNMediaTerm::StartPlayingFileTX(BSTR Filename, unsigned long Mode, unsigned long volume, long * Cookie) { USES_CONVERSION; int result(0); std::string filename = std::string(W2A((wchar_t *)Filename)); tracer.tracef(EE, "StartPlayingFileTX file = %s, mode = %d\n", filename.c_str(), Mode); CSingleLock cLock(&Mutex); cLock.Lock(); try { tracer.tracef(ARB, "StartPlayingFileTX : calling RemoveNonPlayingFiles()\n"); RemoveNonPlayingFiles(); if (btxRunning) { int returnCode(0); tracer.tracef(ARB, "StartPlayingFileTX : btxRunning = true. Playing file\n"); FilePlay *newFilePlay = new FilePlay(txPCMMixer, txDuration, true); returnCode = newFilePlay->LoadFile(filename.c_str()); if (returnCode != 0) { delete newFilePlay; newFilePlay = NULL; throw Exception(-10, "Error loading file %s", filename.c_str()); } if (Mode == 0) { newFilePlay->PlayInLoop(); } newFilePlay->SetVolume((int)volume); newFilePlay->SetDestinationFormat(WaveFormat::GetWaveFormat(WaveFormat_PCM_16_8_1)); tracer.tracef(ARB, "StartPlayingFileTX : Entering filesPlayingMutex\n"); EnterCriticalSection(&filesPlayingMutex); filesPlaying[newFilePlay->GetCookie()] = newFilePlay; *Cookie = newFilePlay->GetCookie(); newFilePlay->Start(); LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "StartPlayingFileTX : Left filesPlayingMutex\n"); } } catch (Exception ex) { tracer.tracef(ERR, "StartPlayingFileTX : %s\n", ex.ErrorMessage()); result = ex.ErrorCode(); } tracer.tracef(EE, "~StartPlayingFileTX : cookie = %d\n", *Cookie); return result; } STDMETHODIMP CCCNMediaTerm::StopPlayingFileTX(unsigned long Cookie) { tracer.tracef(EE, "StopPlayingFileTX : cookie = %u\n", Cookie); CSingleLock cLock(&Mutex); cLock.Lock(); tracer.tracef(ARB, "StopPlayingFileTX : Entering filesPlayingMutex\n"); EnterCriticalSection(&filesPlayingMutex); map::iterator iter = filesPlaying.find(Cookie); if (iter != filesPlaying.end()) { FilePlay *filePlay = (*iter).second; if (filePlay->Transmitting() == true) { filePlay->StopImmediately(); } } LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "StopPlayingFileTX : Left filesPlayingMutex\n"); tracer.tracef(EE, "~StopPlayingFileTX\n"); return NOERROR; } STDMETHODIMP CCCNMediaTerm::StartPlayingFileRX(BSTR Filename, unsigned long Mode, unsigned long waveoutDeviceID, unsigned long volume, long * Cookie) { USES_CONVERSION; int result(0); std::string filename = std::string(W2A((wchar_t *)Filename)); tracer.tracef(EE, "StartPlayingFileRX file = %s, mode = %d, waveOutDeviceID = %d\n", filename.c_str(), Mode, waveoutDeviceID); CSingleLock cLock(&Mutex); cLock.Lock(); try { tracer.tracef(ARB, "StartPlayingFileRX : calling RemoveNonPlayingFiles()\n"); RemoveNonPlayingFiles(); if (brxRunning && waveoutDeviceID == this->waveoutDeviceID) { tracer.tracef(ARB, "StartPlayingFileRX : playing file in audio stream\n"); int resultCode(0); FilePlay *newFilePlay = new FilePlay(rxPCMMixer, rxDuration, false); resultCode = newFilePlay->LoadFile(filename.c_str()); if (resultCode != 0) { delete newFilePlay; newFilePlay = NULL; throw Exception(-10, "Error loading file %s", filename.c_str()); } if (Mode == 0) { newFilePlay->PlayInLoop(); } newFilePlay->SetVolume((int)volume); newFilePlay->SetDestinationFormat(WaveFormat::GetWaveFormat(WaveFormat_PCM_16_8_1)); tracer.tracef(ARB, "StartPlayingFileRX : Entering filesPlayingMutex\n"); EnterCriticalSection(&filesPlayingMutex); filesPlaying[newFilePlay->GetCookie()] = newFilePlay; *Cookie = newFilePlay->GetCookie(); newFilePlay->Start(); LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "StartPlayingFileRX : Left filesPlayingMutex\n"); } else { if (bFilePlayRunning) { if (extraFilePlay == NULL) { tracer.tracef(ERR, "StartPlayingFileRX : bFilePlayRunning = true but extraFilePlay = NULL\n"); bFilePlayRunning = false; } else { tracer.tracef(ARB, "StartPlayingFileRX : bFilePlayRunning = true. Calling StopPlayingFileRX\n"); StopPlayingFileRX(extraFilePlay->GetCookie()); } } if (extraFilePlay == NULL) { tracer.tracef(ARB, "StartPlayingFileRX : extraFilePlay = NULL. Playing file\n"); int resultCode(0); extraFilePlay = new FilePlay(filePlayWaveSink, 60, false); resultCode = extraFilePlay->LoadFile(filename.c_str()); if (resultCode != 0) { delete extraFilePlay; extraFilePlay = NULL; throw Exception(-20, "Error loading file %s to extraFilePlay", filename.c_str()); } extraFilePlay->SetDestinationFormat(WaveFormat::GetWaveFormat(WaveFormat_PCM_16_8_1)); extraFilePlay->SetVolume((int)volume); if (Mode == 0) { extraFilePlay->PlayInLoop(); } filePlayWaveSink->SetDeviceID(waveoutDeviceID); filePlayWaveSink->SetFrameSize(60 * 16); filePlayWaveSink->SetNumFrames(10); filePlayWaveSink->SetFormat(WaveFormat_PCM_16_8_1); tracer.tracef(ARB, "StartPlayingFileRX : Entering filesPlayingMutex\n"); EnterCriticalSection(&filesPlayingMutex); *Cookie = extraFilePlay->GetCookie(); LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "StartPlayingFileRX : Left filesPlayingMutex\n"); extraFilePlay->Start(); filePlayWaveSink->StartSinkThread(); bFilePlayRunning = true; } } } catch (Exception ex) { tracer.tracef(ERR, "StartPlayingFileRX : %s\n", ex.ErrorMessage()); result = ex.ErrorCode(); *Cookie = 0; } if (result == 0) { tracer.tracef(EE, "~StartPlayingFileRX : cookie = %u\n", *Cookie); } else { tracer.tracef(ERR, "~StartPlayingFileRX : returning error code %d\n", result); } return result; } STDMETHODIMP CCCNMediaTerm::StopPlayingFileRX(unsigned long Cookie) { tracer.tracef(EE, "StopPlayingFileRX %u\n", Cookie); CSingleLock cLock(&Mutex); cLock.Lock(); tracer.tracef(ARB, "StopPlayingFileRX : Entering filesPlayingMutex\n"); EnterCriticalSection(&filesPlayingMutex); map::iterator iter = filesPlaying.find(Cookie); if (iter != filesPlaying.end()) { FilePlay *filePlay = (*iter).second; if (filePlay->Transmitting() == false) { filePlay->StopImmediately(); } LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "StopPlayingFileRX : Left filesPlayingMutex\n"); } else { LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "StopPlayingFileRX : Left filesPlayingMutex\n"); if (extraFilePlay) { tracer.tracef(DET, "StopPlayingFileRX : extraFilePlay->cookie = %d\n", extraFilePlay->GetCookie()); if (Cookie == extraFilePlay->GetCookie()) { tracer.tracef(DET, "StopPlayingFileRX : calling filePlayWaveSink->StopSinkThread\n"); int i; i = filePlayWaveSink->StopSinkThread(); if (i != 0) { tracer.tracef(ERR, "StopPlayingFileRX : e-filePlayWaveSink->StopSinkThread() : %d\n", i); } tracer.tracef(DET, "StopPlayingFileRX : calling extraFilePlay->StopImmediately\n"); i = extraFilePlay->StopImmediately(); if (i != 0) { tracer.tracef(ERR, "StopPlayingFileRX : e-extraFilePlay->StopImmediately() : %d\n", i); } if (!extraFilePlay->IsRunning()) { bFilePlayRunning = false; tracer.tracef(DET, "StopPlayingFileRX : deleting extraFilePlay\n"); delete extraFilePlay; extraFilePlay = NULL; } else { int i=10; } } } } tracer.tracef(EE, "~StopPlayingFileRX\n"); return S_OK; } STDMETHODIMP CCCNMediaTerm::StartTX(unsigned long waveinDeviceID) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "StartTX\n"); CSingleLock cLock(&Mutex); cLock.Lock(); int resultCode(0); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "StartTX : 0\n"); if (!btxRunning) { // find out if pre-emphasis needs to be done, and if so with what parameters btxPreEmphasis = GetRegKeyBool(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "MicrophonePreprocess", true); long txFIRFilter = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "TxFIRFilter", 1); if (txFIRFilter == 2) { outputFIRTransformer->SetCoefficients(FIR_TELECASTER_TX, FIR_TELECASTER_TX_NUM, 16); } else { outputFIRTransformer->SetCoefficients(FIR_ITU_TX_IRS, FIR_ITU_TX_IRS_NUM, 16); } this->waveinDeviceID = waveinDeviceID; waveSource->SetDeviceID(waveinDeviceID); AudioSource *temp = waveSource; if (btxPreEmphasis) { Connect(temp, outputFIRTransformer); temp = outputFIRTransformer; } if (btxVAD) { Connect(temp, voiceActivityDetector); temp = voiceActivityDetector; } Connect(temp, txPCMMixer); Connect(txPCMMixer, txTransformer); Connect(txTransformer, rtpSink); do { int result(0); long transmitTOS = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "TransmitTOS", 0xb8); unsigned char tos = (unsigned char)transmitTOS; result = rtpSink->SetTOS(tos); result = rtpSink->StartSink(); if (result < 0) { resultCode = -10; break; } result = txTransformer->StartTransform(); if (result < 0) { rtpSink->StopSink(); resultCode = -20; break; } result = txPCMMixer->StartTransform(); if (result < 0) { rtpSink->StopSink(); txTransformer->StopTransform(); resultCode = -30; break; } if (btxVAD) { voiceActivityDetector->EnableDetection(); result = voiceActivityDetector->StartTransform(); if (result < 0) { rtpSink->StopSink(); txTransformer->StopTransform(); txPCMMixer->StopTransform(); resultCode = -40; break; } } if (btxPreEmphasis) { result = outputFIRTransformer->StartTransform(); if (result < 0) { rtpSink->StopSink(); txTransformer->StopTransform(); txPCMMixer->StopTransform(); if (btxVAD) { voiceActivityDetector->StopTransform(); } resultCode = -50; break; } } result = waveSource->StartSourceThread(); if (result < 0) { rtpSink->StopSink(); txTransformer->StopTransform(); txPCMMixer->StopTransform(); if (btxVAD) { voiceActivityDetector->StopTransform(); } if (btxPreEmphasis) { outputFIRTransformer->StopTransform(); } resultCode = -60; break; } } while (false); } if (resultCode == 0) { btxRunning = true; } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~StartTX : %d\n", resultCode); return resultCode; } STDMETHODIMP CCCNMediaTerm::StopTX() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "StopTX\n"); CSingleLock cLock(&Mutex); cLock.Lock(); bool bError(false); int resultCode(0); tracer.tracef(ARB, "StopTX : after cLock.Lock()\n"); if (btxRunning) { int result(0); result = StopAllFiles(true); if (result < 0) { bError = true; tracer.tracef(ERR, "StopTX : e-StopAllFiles %d\n", result); } tracer.tracef(ARB, "StopTX : before waveSource->StopSourceThread()\n"); result = waveSource->StopSourceThread(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopTX : e-waveSource->StopSourceThread %d\n", result); } if (btxPreEmphasis) { tracer.tracef(ARB, "StopTX : before outputFIRTransformer->StopTransform()\n"); result = outputFIRTransformer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopTX : e-outputFIRTransformer->StopTransform %d\n", result); } } if (btxVAD) { tracer.tracef(ARB, "StopTX : before voiceActivityDetector->StopTransform()\n"); result = voiceActivityDetector->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopTX : e-voiceActivityDetector->StopTransform %d\n", result); } } tracer.tracef(ARB, "StopTX : before txPCMMixer->StopTransform()\n"); result = txPCMMixer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopTX : e-txPCMMixer->StopTransform %d\n", result); } tracer.tracef(ARB, "StopTX : before txTransformer->StopTransform()\n"); result = txTransformer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopTX : e-txTransformer->StopTransform %d\n", result); } tracer.tracef(ARB, "StopTX : before rtpSink->StopSink()\n"); result = rtpSink->StopSink(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopTX : e-rtpSink->StopSink %d\n", result); } tracer.tracef(ARB, "StopTX : before Disconnects\n"); AudioSource *temp = waveSource; if (btxPreEmphasis) { Disconnect(temp, outputFIRTransformer); temp = outputFIRTransformer; } if (btxVAD) { Disconnect(temp, voiceActivityDetector); temp = voiceActivityDetector; } Disconnect(temp, txPCMMixer); Disconnect(txPCMMixer, txTransformer); Disconnect(txTransformer, rtpSink); tracer.tracef(ARB, "StopTX : after Disconnects\n"); } if (!bError) { btxRunning = false; } else { resultCode = -10; } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~StopTX : %d\n", resultCode); return resultCode; } STDMETHODIMP CCCNMediaTerm::StartRX(unsigned long waveoutDeviceID) { AFX_MANAGE_STATE(AfxGetStaticModuleState()) tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "StartRX\n"); CSingleLock cLock(&Mutex); cLock.Lock(); int result(0); int returnCode(0); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "StartRX : brxRunning = %d\n", brxRunning); if (!brxRunning) { brxPostEmphasis = GetRegKeyBool(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "SpeakerPostprocess", true); brxVolumeLimit = GetRegKeyBool(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "LimitVolume", true); // set up the post emphasis filter long rxFIRFilter = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "RxFIRFilter", 1); if (rxFIRFilter == 2) { inputFIRTransformer->SetCoefficients(FIR_TELECASTER_RX, FIR_TELECASTER_RX_NUM, 16); } else { inputFIRTransformer->SetCoefficients(FIR_ITU_RX_IRS, FIR_ITU_RX_IRS_NUM, 16); } // set up the limiter // set up RTP jitter buffer and fixed size audio buffer long jitterBufferDepth = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "JitterBufferTime", 100); rtpJitterBuffer->SetJitterBufferDepth(jitterBufferDepth); fixedSizeAudioBuffer->SetOutputDuration(rxDuration); int numFrames = _winmajor < 5 ? 10 : 6; bool useDynamicJitterBuffer = GetRegKeyBool(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "UseDynamicJitterBuffer", true); if (useDynamicJitterBuffer) { waveSink->SetNumFrames(numFrames); } else { waveSink->SetNumFrames(max(numFrames, jitterBufferDepth/rxDuration)); } double threshold; double lossIncrement; double lossDecrement; std::string strThreshold = GetRegKeyString(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "LimiterThreshold", "-8.0"); std::string strLossIncrement = GetRegKeyString(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "LimiterLossIncrement", "0.075"); std::string strLossDecrement = GetRegKeyString(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "LimiterLossDecrement", "-0.00075"); threshold = atof(strThreshold.c_str()); lossIncrement = atof(strLossIncrement.c_str()); lossDecrement = atof(strLossDecrement.c_str()); limiter->SetParameters(threshold, lossIncrement, lossDecrement); this->waveoutDeviceID = waveoutDeviceID; waveSink->SetDeviceID(waveoutDeviceID); double volumeMaximizePercentile = atof((GetRegKeyString(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "VolumeMaximizePercentile", "95.00")).c_str()); double volumeMaximizeMaxAmplification = fabs(atof((GetRegKeyString(HKEY_CURRENT_USER, "Software\\Cisco Systems\\CCNMediaTerm\\1.0", "VolumeMaximizeMaxGain", "8.00")).c_str())); unsigned int maxGain = (unsigned int)(volumeMaximizeMaxAmplification * 100); pcmVolumeMaximizer->SetMaximizePercentile(volumeMaximizePercentile); pcmVolumeMaximizer->SetMaximumAmplification(maxGain); Connect(rtpSource, rxTransformer); Connect(rxTransformer, rtpJitterBuffer); Connect(rtpJitterBuffer, fixedSizeAudioBuffer); Connect(fixedSizeAudioBuffer, pcmVolumeMaximizer); Connect(pcmVolumeMaximizer, rxPCMMixer); AudioSource *tempSource = rxPCMMixer; if (brxPostEmphasis) { Connect(tempSource, inputFIRTransformer); tempSource = inputFIRTransformer; } if (brxVolumeLimit) { Connect(tempSource, limiter); tempSource = limiter; } Connect(tempSource, waveSink); do { result = rxTransformer->StartTransform(); if (result < 0) { returnCode = -10; break; } result = rtpJitterBuffer->StartTransform(); if (result < 0) { rxTransformer->StopTransform(); returnCode = -20; break; } result = fixedSizeAudioBuffer->StartTransform(); if (result < 0) { rxTransformer->StopTransform(); rtpJitterBuffer->StopTransform(); returnCode = -30; break; } result = pcmVolumeMaximizer->StartTransform(); if (result < 0) { rxTransformer->StopTransform(); rtpJitterBuffer->StopTransform(); fixedSizeAudioBuffer->StopTransform(); returnCode = -40; break; } result = rxPCMMixer->StartTransform(); if (result < 0) { rxTransformer->StopTransform(); rtpJitterBuffer->StopTransform(); fixedSizeAudioBuffer->StopTransform(); pcmVolumeMaximizer->StopTransform(); returnCode = -50; break; } if (brxPostEmphasis) { result = inputFIRTransformer->StartTransform(); if (result < 0) { rxTransformer->StopTransform(); rtpJitterBuffer->StopTransform(); fixedSizeAudioBuffer->StopTransform(); pcmVolumeMaximizer->StopTransform(); rxPCMMixer->StopTransform(); returnCode = -60; break; } } if (brxVolumeLimit) { result = limiter->StartTransform(); if (result < 0) { rxTransformer->StopTransform(); rtpJitterBuffer->StopTransform(); fixedSizeAudioBuffer->StopTransform(); pcmVolumeMaximizer->StopTransform(); rxPCMMixer->StopTransform(); inputFIRTransformer->StopTransform(); returnCode = -70; break; } } result = waveSink->StartSinkThread(); if (result < 0) { rxTransformer->StopTransform(); rtpJitterBuffer->StopTransform(); fixedSizeAudioBuffer->StopTransform(); pcmVolumeMaximizer->StopTransform(); rxPCMMixer->StopTransform(); inputFIRTransformer->StopTransform(); limiter->StopTransform(); returnCode = -80; break; } result = rtpSource->StartSourceThread(); if (result < 0) { waveSink->StopSinkThread(); rxTransformer->StopTransform(); rtpJitterBuffer->StopTransform(); fixedSizeAudioBuffer->StopTransform(); pcmVolumeMaximizer->StopTransform(); rxPCMMixer->StopTransform(); inputFIRTransformer->StopTransform(); limiter->StopTransform(); returnCode = -90; break; } } while (false); } if (returnCode == 0) { brxRunning = true; } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~StartRX : returning %d\n", returnCode); return returnCode; } STDMETHODIMP CCCNMediaTerm::StopRX() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "StopRX\n"); CSingleLock cLock(&Mutex); cLock.Lock(); bool bError = false; int resultCode(0); tracer.tracef(ARB, "StopRX : brxRunning = %d\n", brxRunning); if (brxRunning) { int result(0); tracer.tracef(ARB, "StopRX : calling StopAllFiles(false)\n"); result = StopAllFiles(false); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-StopAllFiles = %d\n", result); } tracer.tracef(ARB, "StopRX : calling waveSink->StopSinkThread()\n"); result = waveSink->StopSinkThread(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-waveSink->StopSinkThread = %d\n", result); } tracer.tracef(ARB, "StopRX : calling rtpSource->UnprepareSource()\n"); result = rtpSource->UnprepareSource(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-rtpSource->UnprepareSource = %d\n", result); } tracer.tracef(ARB, "StopRX : calling rtpSource->StopSourceThread()\n"); result = rtpSource->StopSourceThread(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-rtpSource->StopSourceThread = %d\n", result); } if (brxVolumeLimit) { tracer.tracef(ARB, "StopRX : calling limiter->StopTransform()\n"); result = limiter->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-limiter->StopTransform = %d\n", result); } } if (brxPostEmphasis) { tracer.tracef(ARB, "StopRX : calling inputFIRTransformer->StopTransform()\n"); result = inputFIRTransformer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-inputFIRTransformer->StopTransform = %d\n", result); } } tracer.tracef(ARB, "StopRX : calling rxPCMMixer->StopTransform()\n"); result = rxPCMMixer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-rxPCMMixer->StopTransform = %d\n", result); } tracer.tracef(ARB, "StopRX : calling pcmVolumeMaximizer->StopTransform()\n"); result = pcmVolumeMaximizer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-pcmVolumeMaximizer->StopTransform = %d\n", result); } tracer.tracef(ARB, "StopRX : calling fixedSizeAudioBuffer->StopTransform()\n"); result = fixedSizeAudioBuffer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-fixedSizeAudioBuffer->StopTransform = %d\n", result); } tracer.tracef(ARB, "StopRX : calling rtpJitterBuffer->StopTransform()\n"); result = rtpJitterBuffer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-rtpJitterBuffer->StopTransform = %d\n", result); } tracer.tracef(ARB, "StopRX : calling rxTransformer->StopTransform()\n"); result = rxTransformer->StopTransform(); if (result < 0) { bError = true; tracer.tracef(ERR, "StopRX : e-rxTransformer->StopTransform = %d\n", result); } tracer.tracef(ARB, "StopRX : disconnecting filters\n"); Disconnect(rtpSource, rxTransformer); Disconnect(rxTransformer, rtpJitterBuffer); Disconnect(rtpJitterBuffer, fixedSizeAudioBuffer); Disconnect(fixedSizeAudioBuffer, pcmVolumeMaximizer); Disconnect(pcmVolumeMaximizer, rxPCMMixer); AudioSource *tempSource = rxPCMMixer; if (brxPostEmphasis) { Disconnect(tempSource, inputFIRTransformer); tempSource = inputFIRTransformer; } if (brxVolumeLimit) { Disconnect(tempSource, limiter); tempSource = limiter; } Disconnect(tempSource, waveSink); } if (!bError) { brxRunning = false; } else { resultCode = -10; } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~StopRX : %d\n", resultCode); return resultCode; } int CCCNMediaTerm::RemoveNonPlayingFiles() { tracer.tracef(EE, "RemoveNonPlayingFiles\n"); tracer.tracef(ARB, "RemoveNonPlayingFiles : Entering filesPlayingMutex\n"); EnterCriticalSection(&filesPlayingMutex); vector::iterator> filesToDelete; map::iterator iter = filesPlaying.begin(); while (iter != filesPlaying.end()) { FilePlay *filePlay = (*iter).second; tracer.tracef(DET, "RemoveNonPlayingFiles : file found with cookie = %d\n", filePlay->GetCookie()); if (!(filePlay->IsRunning())) { tracer.tracef(DET, "RemoveNonPlayingFiles : file with cookie = %d is not playing\n", filePlay->GetCookie()); filesToDelete.push_back(iter); } iter++; } tracer.tracef(DET, "RemoveNonPlayingFiles : deleting non-playing files from list\n"); for (int i=0; i::iterator iter = filesToDelete[i]; FilePlay *filePlay = (*iter).second; filesPlaying.erase(iter); delete filePlay; } if (extraFilePlay) { if (!extraFilePlay->IsRunning()) { tracer.tracef(DET, "RemoveNonPlayingFiles : extrafileplay found with cookie = %d\n", extraFilePlay->GetCookie()); tracer.tracef(DET, "RemoveNonPlayingFiles : deleting extraFilePlay\n"); delete extraFilePlay; extraFilePlay = NULL; bFilePlayRunning = false; } } LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "RemoveNonPlayingFiles : Left filesPlayingMutex\n"); tracer.tracef(EE, "~RemoveNonPlayingFiles\n"); return NOERROR; } int CCCNMediaTerm::StopAllFiles(bool transmitSide) { tracer.tracef(EE, "StopAllFiles %d\n", transmitSide); tracer.tracef(ARB, "StopAllFiles : Entering filesPlayingMutex\n"); EnterCriticalSection(&filesPlayingMutex); map::iterator iter = filesPlaying.begin(); while(iter != filesPlaying.end()) { FilePlay *filePlay = (*iter).second; tracer.tracef(DET, "StopAllFiles : file with cookie = %d\n", filePlay->GetCookie()); if (filePlay->IsRunning() && filePlay->Transmitting() == transmitSide) { tracer.tracef(DET, "StopAllFiles : stopping filePlay with cookie = %d\n", filePlay->GetCookie()); filePlay->StopImmediately(); } iter++; } LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "StopAllFiles : Left filesPlayingMutex\n"); tracer.tracef(EE, "~StopAllFiles\n"); return 0; } STDMETHODIMP CCCNMediaTerm::SetSpeakerVolume(unsigned long deviceID, unsigned long volume) { tracer.tracef(EE, "SetSpeakerVolume device=%u, volume=%u\n", deviceID, volume); int result(0); if (deviceID == filePlayWaveSink->GetDeviceID() && bFilePlayRunning) { result = filePlayWaveSink->SetVolume(volume); } else if (deviceID == waveSink->GetDeviceID() && brxRunning) { result = waveSink->SetVolume(volume); } tracer.tracef(EE, "~SetSpeakerVolume : returning %d 0x%x\n", result, result); return result; } // sets the components internal setting for mic volume. max volume = system's current mic volume setting STDMETHODIMP CCCNMediaTerm::SetMicrophoneVolume(unsigned long deviceID, unsigned long volume) { tracer.tracef(EE, "SetMicrophoneVolume device=%u, volume=%u\n", deviceID, volume); waveSource->SetVolume(volume); tracer.tracef(EE, "~SetMicrophoneVolume\n"); return S_OK; } // changes the system's microphone volume setting HRESULT CCCNMediaTerm::SetSystemMicrophoneVolume(unsigned long volume) { return S_OK; } bool CCCNMediaTerm::SpeakerAvailable() { bool available = true; tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SpeakerAvailable\n"); WAVEFORMATEX waveFormatex; waveFormatex.nAvgBytesPerSec = 16000; waveFormatex.wFormatTag = WAVE_FORMAT_PCM; waveFormatex.wBitsPerSample = 16; waveFormatex.nChannels = 1; waveFormatex.nBlockAlign = 2; waveFormatex.nSamplesPerSec = 8000; waveFormatex.cbSize = 0; // see if the speaker is available HWAVEOUT hWaveOut; tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SpeakerAvailable : opening wave device\n"); MMRESULT mmr = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormatex, NULL, NULL, CALLBACK_NULL); if (mmr != MMSYSERR_NOERROR) { available = false; int retryInterval = 2000; tracer.tracef(SDI_LEVEL_ERROR, "SpeakerAvailable : error : could not open wave device. waveOutOpen returned 0x%x\n", mmr); tracer.tracef(SDI_LEVEL_ARBITRARY, "SpeakerAvailable : trying again after %d ms\n", retryInterval); Sleep(retryInterval); MMRESULT mmr = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveFormatex, NULL, NULL, CALLBACK_NULL); if (mmr != MMSYSERR_NOERROR) { tracer.tracef(SDI_LEVEL_ERROR, "SpeakerAvailable : error : still could not open wave device. waveOutOpen returned 0x%x\n", mmr); } else { tracer.tracef(SDI_LEVEL_ARBITRARY, "SpeakerAvailable : got the speaker second time\n"); waveOutReset(hWaveOut); waveOutClose(hWaveOut); available = true; } } else { waveOutReset(hWaveOut); waveOutClose(hWaveOut); available = true; } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SpeakerAvailable : returning %d\n", available); return available; } bool CCCNMediaTerm::MicrophoneAvailable() { bool available = true; tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "MicrophoneAvailable\n"); WAVEFORMATEX waveFormatex; waveFormatex.nAvgBytesPerSec = 16000; waveFormatex.wFormatTag = WAVE_FORMAT_PCM; waveFormatex.wBitsPerSample = 16; waveFormatex.nChannels = 1; waveFormatex.nBlockAlign = 2; waveFormatex.nSamplesPerSec = 8000; waveFormatex.cbSize = 0; // see if the microphone is available HWAVEIN hwaveIn; tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "MicrophoneAvailable : opening wavein device\n"); MMRESULT mmr = waveInOpen(&hwaveIn, WAVE_MAPPER, &waveFormatex, NULL, NULL, CALLBACK_NULL); if (mmr != MMSYSERR_NOERROR) { tracer.tracef(SDI_LEVEL_ERROR, "MicrophoneAvailable : error : could not open wavein device. waveInOpen returned 0x%x\n", mmr); available = false; } else { waveInReset(hwaveIn); waveInClose(hwaveIn); } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~MicrophoneAvailable : returning %d\n", available); return available; } STDMETHODIMP CCCNMediaTerm::SetFilePlayVolume(unsigned long cookie, unsigned long volume) { tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "SetFilePlayVolume cookie=%u volume=%u\n", cookie, volume); if (bFilePlayRunning && extraFilePlay && cookie == extraFilePlay->GetCookie()) { tracer.tracef(ARB, "SetFilePlayVolume : Setting volume for extraFilePlay\n"); extraFilePlay->SetVolume((int)volume); } else { tracer.tracef(ARB, "SetFilePlayVolume : Entering filesPlayingMutex\n"); EnterCriticalSection(&filesPlayingMutex); map::iterator iter = filesPlaying.find(cookie); if (iter != filesPlaying.end()) { FilePlay *filePlay = (*iter).second; filePlay->SetVolume((int)volume); } LeaveCriticalSection(&filesPlayingMutex); tracer.tracef(ARB, "SetFilePlayVolume : Left filesPlayingMutex\n"); } tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~SetFilePlayVolume\n"); return S_OK; } void CCCNMediaTerm::FireEndOfFileRX(unsigned long cookie) { int resultCode(0); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "FireEndOfFileRX cookie=%u\n", cookie); if ((extraFilePlay && (cookie == extraFilePlay->GetCookie())) || !bFilePlayRunning ) { resultCode = filePlayWaveSink->StopSinkThread(); if (resultCode != 0) { if (resultCode != -10) { tracer.tracef(ERR, "FireEndOfFileRX : e-filePlayWaveSink->StopSinkThread() : %d\n", resultCode); } else { tracer.tracef(SIG, "FireEndOfFileRX : e-filePlayWaveSink->StopSinkThread() : %d\n", resultCode); } } tracer.tracef(DET, "FireEndOfFileRX : setting bFilePlayRunning to false\n"); bFilePlayRunning = false; } Fire_EndOfFileEventRX(cookie); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~FireEndOfFileRX\n"); } void CCCNMediaTerm::FireEndOfFileTX(unsigned long cookie) { tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "FireEndOfFileTX cookie=%u\n", cookie); Fire_EndOfFileEventTX(cookie); tracer.tracef(SDI_LEVEL_ENTRY_EXIT, "~FireEndOfFileTX\n"); } STDMETHODIMP CCCNMediaTerm::NetworkMonitor(unsigned long, unsigned long) { return S_OK; } STDMETHODIMP CCCNMediaTerm::StartDtmfTone(long, long, long) { return S_OK; } STDMETHODIMP CCCNMediaTerm::StopDtmfTone(void) { return S_OK; } STDMETHODIMP CCCNMediaTerm::StopAudioReceive(void) { return S_OK; } STDMETHODIMP CCCNMediaTerm::StartAudioReceive(void) { return S_OK; } STDMETHODIMP CCCNMediaTerm::StopMicrophone(void) { return S_OK; } STDMETHODIMP CCCNMediaTerm::StartMicrophone(void) { return S_OK; } int FilePlay::Initialize(CCCNMediaTerm *mediaTerm) { FilePlay::mediaTerm = mediaTerm; InitializeCriticalSection(&fireEventThreadMutex); fireEventThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL); numOutstandingEvents = 0; closing = false; return 0; } int FilePlay::Uninitialize() { EnterCriticalSection(&fireEventThreadMutex); closing = true; LeaveCriticalSection(&fireEventThreadMutex); while (true) { EnterCriticalSection(&fireEventThreadMutex); { if (numOutstandingEvents > 0) { LeaveCriticalSection(&fireEventThreadMutex); WaitForSingleObject(fireEventThreadEvent, INFINITE); } else { LeaveCriticalSection(&fireEventThreadMutex); break; } } } DeleteCriticalSection(&fireEventThreadMutex); CloseHandle(fireEventThreadEvent); return 0; } DWORD WINAPI FilePlay::FireEventThread(LPVOID params) { if (FilePlay::closing) { return 0; } EnterCriticalSection(&fireEventThreadMutex); numOutstandingEvents++; LeaveCriticalSection(&fireEventThreadMutex); FireEventThreadParams *fetParams = (FireEventThreadParams *)params; if (fetParams->receiveSide) { mediaTerm->FireEndOfFileRX(fetParams->cookie); } else { mediaTerm->FireEndOfFileTX(fetParams->cookie); } SetEvent(FilePlay::fireEventThreadEvent); delete fetParams; EnterCriticalSection(&fireEventThreadMutex); numOutstandingEvents--; LeaveCriticalSection(&fireEventThreadMutex); return 0; } FilePlay::FilePlay(AudioSink *audioSink, int outputDuration, bool bTransmit) { char subFacilityName[100]; sprintf(subFacilityName, "FilePlay:%x", this); tracer.SetSubFacilityName(subFacilityName); SetTraceLevel(); InitializeCriticalSection(&stateMutex); stateChangedEvent = CreateEvent(NULL, NULL, FALSE, NULL); this->bTransmit = bTransmit; waveFileSource = new WaveFileSource(); waveFileSource->AddFilePlayListener(this); acmTransformer = new ACMTransformer(); acmTransformer->SetOutputDuration(outputDuration); pcmVolumeTransformer = new PCMVolumeTransformer(); this->audioSink = NULL; // will be set in the call to Connect(audioSink) below currentState = STATE_STOPPED; ::Connect(waveFileSource, acmTransformer); ::Connect(acmTransformer, pcmVolumeTransformer); Connect(audioSink); cookie = ++nextCookie; } FilePlay::~FilePlay() { StopImmediately(); Disconnect(); delete waveFileSource; delete acmTransformer; delete pcmVolumeTransformer; DeleteCriticalSection(&stateMutex); CloseHandle(stateChangedEvent); } int FilePlay::SetTraceLevel() { long SystemMask = 0; GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "FilePlay", 0x100000); if ((SystemMask = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "AllComponents", 0x0)) == 0) SystemMask = GetRegKeyLong(HKEY_CURRENT_USER, "Software\\Cisco Systems\\MTC\\Tracing", "FilePlay", 0x100000); tracer.SetSystemMask(SystemMask); return NOERROR; } bool FilePlay::IsRunning() { tracer.tracef(EE, "IsRunning\n"); bool isRunning(true); tracer.tracef(ARB, "IsRunning : entering stateMutex\n"); EnterCriticalSection(&stateMutex); if (currentState == STATE_STOPPED) { isRunning = false; } LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "IsRunning : left stateMutex\n"); tracer.tracef(EE, "~IsRunning : returning %d\n", isRunning); return isRunning; } int FilePlay::LoadFile(const char *fileName) { tracer.tracef(EE, "LoadFile %s\n", fileName); int result = waveFileSource->OpenWaveFile(fileName); if (result != 0) { tracer.tracef(ERR, "LoadFile : e-waveFileSource->OpenWaveFile : %d\n", result); } tracer.tracef(EE, "~LoadFile : %d\n", result); return result; } int FilePlay::PlayOnce() { tracer.tracef(EE, "PlayOnce\n"); int result = waveFileSource->PlayOnce(); if (result != 0) { tracer.tracef(ERR, "LoadFile : e-waveFileSource->PlayOnce : %d\n", result); } tracer.tracef(EE, "~PlayOnce : %d\n", result); return result; } int FilePlay::PlayInLoop() { tracer.tracef(EE, "PlayInLoop\n"); int result = waveFileSource->PlayInLoop(); if (result != 0) { tracer.tracef(ERR, "LoadFile : e-waveFileSource->PlayInLoop : %d\n", result); } tracer.tracef(EE, "~PlayInLoop : %d\n", result); return result; } int FilePlay::Start() { tracer.tracef(EE, "Start\n"); int returnCode(0); tracer.tracef(ARB, "Start : entering stateMutex\n"); EnterCriticalSection(&stateMutex); tracer.tracef(ARB, "Start : currentState = %d\n", currentState); bool startNeeded = false; switch(currentState) { case STATE_STOPPED: currentState = STATE_STARTING; SetEvent(stateChangedEvent); tracer.tracef(ARB, "Start STATE_STOPPED: setting stateChangedEvent. Went to STATE_STARTING\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Start STATE_STOPPED: left stateMutex\n"); returnCode = 0; startNeeded = true; break; case STATE_RUNNING: LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Start STATE_RUNNING: left stateMutex\n"); returnCode = 0; break; case STATE_STARTING: while (currentState != STATE_RUNNING && currentState != STATE_STOPPED) { tracer.tracef(ARB, "Start STATE_STARTING: currentState = %d\n", currentState); ResetEvent(stateChangedEvent); tracer.tracef(ARB, "Start STATE_STARTING: resetting stateChangedEvent\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Start STATE_STARTING: left stateMutex\n"); tracer.tracef(ARB, "Start STATE_STARTING: waiting for stateChangedEvent\n"); WaitForSingleObject(stateChangedEvent, INFINITE); tracer.tracef(ARB, "Start STATE_STARTING: entering stateMutex\n"); EnterCriticalSection(&stateMutex); } if (currentState == STATE_STOPPED) { tracer.tracef(ERR, "Start STATE_STARTING: went to STATE_STOPPED ! Returning error\n"); returnCode = -10; } else { returnCode = 0; } LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Start STATE_STARTING: left stateMutex\n"); break; case STATE_STOPPING: LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Start STATE_STOPPING: left stateMutex\n"); tracer.tracef(ERR, "Start STATE_STOPPING: invalid state to call Start\n"); returnCode = -20; break; default: LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Start default: left stateMutex\n"); tracer.tracef(ERR, "Start default: Error : invalid current state %d\n", currentState); returnCode = -30; break; } if (returnCode == 0 && startNeeded) { int result(0); tracer.tracef(ARB, "Start : bRunning = false\n"); WAVEFORMATEX wfx; do { tracer.tracef(DET, "Start : calling waveFileSource->GetFormat\n"); result = waveFileSource->GetFormat(&wfx); if (result < 0) { tracer.tracef(ERR, "Start : e-waveFileSource->GetFormat : %d %x\n", result, result); returnCode = -35; break; } tracer.tracef(DET, "Start : calling acmTransformer->SetSourceFormat\n"); result = acmTransformer->SetSourceFormat(wfx); if (result < 0) { tracer.tracef(ERR, "Start : e-acmTransformer->SetSourceFormat : %d %x\n", result, result); returnCode = -40; break; } tracer.tracef(DET, "Start : calling pcmVolumeTransformer->StartTransform\n"); result = pcmVolumeTransformer->StartTransform(); if (result < 0) { tracer.tracef(ERR, "Start : e-pcmVolumeTransformer->StartTransform : %d %x\n", result, result); returnCode = -50; break; } tracer.tracef(DET, "Start : calling acmTransformer->StartTransform\n"); result = acmTransformer->StartTransform(); if (result < 0) { tracer.tracef(ERR, "Start : e-acmTransformer->StartTransform : %d %x\n", result, result); pcmVolumeTransformer->StopTransform(); returnCode = -60; break; } tracer.tracef(DET, "Start : calling waveFileSource->StartSourceThread\n"); waveFileSource->StartSourceThread(); if (result < 0) { tracer.tracef(ERR, "Start : e-waveFileSource->StartSourceThread : %d %x\n", result, result); pcmVolumeTransformer->StopTransform(); acmTransformer->StopTransform(); returnCode = -70; break; } } while (false); if (returnCode == 0) { // this means start was successful. then FilePlayStarted would have // set the current State to STATE_RUNNING tracer.tracef(ARB, "Start : successful start\n"); } else { tracer.tracef(ARB, "Start : entering stateMutex\n"); EnterCriticalSection(&stateMutex); tracer.tracef(ERR, "Start : unsuccessful start. Going to STOPPED state\n"); currentState = STATE_STOPPED; tracer.tracef(ARB, "Start : setting stateChangedEvent. Went to STATE_STOPPED\n"); SetEvent(stateChangedEvent); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Start : left stateMutex\n"); } } if (returnCode == 0) { tracer.tracef(EE, "~Start : success\n"); } else { tracer.tracef(ERR, "~Start : e-Start : failure : returnCode = %d\n", returnCode); } return returnCode; } void FilePlay::Cleanup() { tracer.tracef(EE, "Cleanup\n"); tracer.tracef(ARB, "Cleanup : entering stateMutex\n"); EnterCriticalSection(&stateMutex); if (currentState != STATE_STOPPING) { tracer.tracef(ERR, "Cleanup : invalid state %d to call Cleanup\n", currentState); } LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Cleanup : left stateMutex\n"); tracer.tracef(ARB, "Cleanup : calling ::Disconnect(waveFileSource, acmTransformer)\n"); ::Disconnect(waveFileSource, acmTransformer); tracer.tracef(ARB, "Cleanup : calling acmTransformer->StopTransform\n"); acmTransformer->StopTransform(); tracer.tracef(ARB, "Cleanup : calling pcmVolumeTransformer->StopTransform\n"); pcmVolumeTransformer->StopTransform(); tracer.tracef(ARB, "Cleanup : calling ::Disconnect(acmTransformer, pcmVolumeTransformer)\n"); ::Disconnect(acmTransformer, pcmVolumeTransformer); tracer.tracef(ARB, "Cleanup : calling Disconnect\n"); Disconnect(); FireEventThreadParams *fetParams = new FireEventThreadParams(); fetParams->cookie = cookie; DWORD threadID; if (bTransmit) { tracer.tracef(ARB, "Cleanup : calling mediaTerm->FireEndOfFileTX\n"); fetParams->receiveSide = false; } else { tracer.tracef(ARB, "Cleanup : calling mediaTerm->FireEndOfFileRX\n"); fetParams->receiveSide = true; } CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FireEventThread, (LPVOID)fetParams, 0, &threadID); tracer.tracef(ARB, "CleanupImmediately : entering stateMutex\n"); EnterCriticalSection(&stateMutex); if (currentState != STATE_STOPPING) { tracer.tracef(ERR, "Cleanup : invalid state %d to call Cleanup\n", currentState); } currentState = STATE_STOPPED; SetEvent(stateChangedEvent); tracer.tracef(ARB, "Cleanup : setting stateChangedEvent. Went to STATE_STOPPED\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "Cleanup : left stateMutex\n"); tracer.tracef(EE, "~Cleanup\n"); } void FilePlay::CleanupImmediately() { tracer.tracef(EE, "CleanupImmediately\n"); tracer.tracef(ARB, "CleanupImmediately : entering stateMutex\n"); EnterCriticalSection(&stateMutex); if (currentState != STATE_STOPPING) { tracer.tracef(ERR, "CleanupImmediately : invalid state %d to call CleanupImmediately\n", currentState); } LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "CleanupImmediately : left stateMutex\n"); tracer.tracef(ARB, "CleanupImmediately : calling acmTransformer->StopTransform\n"); acmTransformer->StopTransform(); tracer.tracef(ARB, "CleanupImmediately : calling pcmVolumeTransformer->StopTransform\n"); pcmVolumeTransformer->StopTransform(); tracer.tracef(ARB, "CleanupImmediately : calling ::Disconnect(waveFileSource, acmTransformer)\n"); ::Disconnect(waveFileSource, acmTransformer); tracer.tracef(ARB, "CleanupImmediately : calling ::Disconnect(acmTransformer, pcmVolumeTransformer)\n"); ::Disconnect(acmTransformer, pcmVolumeTransformer); tracer.tracef(ARB, "CleanupImmediately : calling Disconnect\n"); Disconnect(); if (bTransmit) { tracer.tracef(ARB, "CleanupImmediately : calling mediaTerm->FireEndOfFileTX\n"); mediaTerm->FireEndOfFileTX(cookie); } else { tracer.tracef(ARB, "CleanupImmediately : calling mediaTerm->FireEndOfFileRX\n"); mediaTerm->FireEndOfFileRX(cookie); } tracer.tracef(ARB, "CleanupImmediately : entering stateMutex\n"); EnterCriticalSection(&stateMutex); if (currentState != STATE_STOPPING) { tracer.tracef(ERR, "CleanupImmediately : invalid state %d to call CleanupImmediately\n", currentState); } currentState = STATE_STOPPED; SetEvent(stateChangedEvent); tracer.tracef(ARB, "CleanupImmediately : setting stateChangedEvent. Went to STATE_STOPPED\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "CleanupImmediately : left stateMutex\n"); tracer.tracef(EE, "~CleanupImmediately\n"); } int FilePlay::Stop() { tracer.tracef(ERR, "Stop : should not be called\n"); /* tracer.tracef(ARB, "Stop : calling waveFileSource->StopSourceThread\n"); waveFileSource->StopSourceThread(); tracer.tracef(ARB, "Stop : calling Cleanup\n"); Cleanup(); } else { tracer.tracef(ARB, "Stop : bRunning = false\n"); } */ tracer.tracef(EE, "~Stop : did nothing\n"); return 0; } int FilePlay::StopImmediately() { tracer.tracef(EE, "StopImmediately\n"); int result(0); bool stopNeeded(false); tracer.tracef(ARB, "StopImmediately : entering stateMutex\n"); EnterCriticalSection(&stateMutex); tracer.tracef(ARB, "StopImmediately : currentState = %d\n", currentState); switch(currentState) { case STATE_STOPPED: result = 0; tracer.tracef(ARB, "StopImmediately STATE_STOPPED: already stopped\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "StopImmediately STATE_STOPPED: left stateMutex\n"); break; case STATE_STARTING: result = -10; tracer.tracef(ARB, "StopImmediately STATE_STARTING: invalid state to call StopImmediately\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "StopImmediately STATE_STARTING: left stateMutex\n"); break; case STATE_RUNNING: result = 0; stopNeeded = true; currentState = STATE_STOPPING; tracer.tracef(ARB, "StopImmediately STATE_RUNNING: trying to stop the file play\n"); SetEvent(stateChangedEvent); tracer.tracef(ARB, "StopImmediately STATE_RUNNING: setting stateChangedEvent. Went to STATE_STOPPING\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "StopImmediately STATE_RUNNING: left stateMutex\n"); break; case STATE_STOPPING: while(currentState != STATE_STOPPED) { tracer.tracef(ARB, "StopImmediately STATE_STOPPING: current state = %d\n", currentState); tracer.tracef(ARB, "StopImmediately STATE_STOPPING: resetting stateChangedEvent\n"); ResetEvent(stateChangedEvent); tracer.tracef(ARB, "StopImmediately STATE_STOPPING: flushing acmTransformer buffer\n"); acmTransformer->FlushBuffer(); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "StopImmediately STATE_STOPPING: left stateMutex\n"); tracer.tracef(ARB, "StopImmediately STATE_STOPPING: waiting for stateChangedEvent\n"); WaitForSingleObject(stateChangedEvent, INFINITE); tracer.tracef(ARB, "StopImmediately STATE_STOPPING: entering stateMutex\n"); EnterCriticalSection(&stateMutex); } LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "StopImmediately STATE_STOPPING: left stateMutex\n"); result = 0; stopNeeded = false; break; default: tracer.tracef(ARB, "StopImmediately default : invalid state %d\n", currentState); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "StopImmediately default : left stateMutex\n"); break; } if (result == 0 && stopNeeded) { // in case we are already stopping because file ran out, then we need to stop // right away before buffer is finished. Guarantees that FilePlayStopped will // return soon so entering stateMutex will not wait forever tracer.tracef(ARB, "StopImmediately : flushing acmTransformer buffer\n"); acmTransformer->FlushBuffer(); tracer.tracef(ARB, "StopImmediately : calling acmTransformer->StopTransform\n"); acmTransformer->StopTransform(); tracer.tracef(ARB, "StopImmediately : calling waveFileSource->StopSourceThread\n"); waveFileSource->StopSourceThread(); tracer.tracef(ARB, "StopImmediately : calling CleanupImmediately\n"); CleanupImmediately(); tracer.tracef(ARB, "Start : entering stateMutex\n"); EnterCriticalSection(&stateMutex); currentState = STATE_STOPPED; SetEvent(stateChangedEvent); tracer.tracef(ARB, "StopImmediately : setting stateChangedEvent. Went to STATE_STOPPED\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "StopImmediately : left stateMutex\n"); } if (result == 0) { tracer.tracef(EE, "~StopImmediately\n"); } else { tracer.tracef(ERR, "~StopImmediately : returning %d\n", result); } return result; } int FilePlay::SetVolume(int volume) { tracer.tracef(EE, "SetVolume %d\n", volume); int result = pcmVolumeTransformer->SetVolume(volume); if (result != 0) { tracer.tracef(ERR, "SetVolume : e-pcmVolumeTransformer->SetVolume : %d\n", result); } tracer.tracef(EE, "~SetVolume : %d\n", result); return result; } int FilePlay::GetVolume() { tracer.tracef(EE, "GetVolume\n"); int result = pcmVolumeTransformer->GetVolume(); if (result != 0) { tracer.tracef(ERR, "GetVolume : e-pcmVolumeTransformer->GetVolume: %d\n", result); } tracer.tracef(EE, "~GetVolume : %d\n", result); return result; } unsigned int FilePlay::GetCookie() { return cookie; } int FilePlay::Connect(AudioSink *audioSink) { tracer.tracef(EE, "Connect\n"); tracer.tracef(ARB, "Connect : calling Disconnect\n"); Disconnect(); if (audioSink) { tracer.tracef(ARB, "Connect : calling ::Connect(pcmVolumeTransformer, audioSink)\n"); ::Connect(pcmVolumeTransformer, audioSink); } this->audioSink = audioSink; tracer.tracef(EE, "~Connect\n"); return 0; } int FilePlay::Disconnect() { tracer.tracef(EE, "Disconnect\n"); if (audioSink) { tracer.tracef(ARB, "Disconnect : calling ::Disconnect(pcmVolumeTransformer, audioSink)\n"); ::Disconnect(pcmVolumeTransformer, audioSink); } audioSink = NULL; tracer.tracef(EE, "~Disconnect\n"); return 0; } void FilePlay::FilePlayStarted(WaveFileSource *waveFileSource, char *waveFileName) { tracer.tracef(EE, "FilePlayStarted : %s\n", waveFileName); tracer.tracef(ARB, "FilePlayStarted : entering stateMutex\n"); EnterCriticalSection(&stateMutex); if (currentState != STATE_STARTING) { tracer.tracef(ERR, "FilePlayStarted : invalid current state %d\n", currentState); } currentState = STATE_RUNNING; SetEvent(stateChangedEvent); tracer.tracef(ARB, "FilePlayStarted : setting stateChangedEvent. Went to STATE_RUNNING\n"); LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "FilePlayStarted : left stateMutex\n"); tracer.tracef(EE, "~FilePlayStarted\n"); } void FilePlay::FilePlayStopped(WaveFileSource *waveFileSource, char *waveFileName) { tracer.tracef(EE, "FilePlayStopped : %s\n", waveFileName); bool needToCallCleanup = false; tracer.tracef(ARB, "FilePlayStopped : entering stateMutex\n"); EnterCriticalSection(&stateMutex); // if we are in STATE_STOPPING this is because StopImmediately was called // StopImmediately will call CleanupImmediately which will also set the // state to STATE_STOPPED if (currentState != STATE_RUNNING && currentState != STATE_STOPPING) { tracer.tracef(ERR, "FilePlayStopped : invalid current state %d\n", currentState); } if (currentState == STATE_RUNNING) { tracer.tracef(ARB, "FilePlayStopped : currently RUNNING. need to call cleanup\n"); needToCallCleanup = true; currentState = STATE_STOPPING; SetEvent(stateChangedEvent); tracer.tracef(ARB, "FilePlayStopped : setting stateChangedEvent. Went to STATE_STOPPING\n"); } LeaveCriticalSection(&stateMutex); tracer.tracef(ARB, "FilePlayStopped : left stateMutex\n"); if (needToCallCleanup) { tracer.tracef(ARB, "FilePlayStopped : calling Cleanup\n"); Cleanup(); // Cleanup will set the state to STATE_STOPPED } tracer.tracef(EE, "~FilePlayStopped\n"); } int FilePlay::SetOutputDuration(int outputDuration) { tracer.tracef(EE, "SetOutputDuration %d\n", outputDuration); int result = acmTransformer->SetOutputDuration(outputDuration); if (result != 0) { tracer.tracef(ERR, "SetOutputDuration : e-acmTransformer->SetOutputDuration : %d\n", result); } tracer.tracef(EE, "~SetOutputDuration : %d\n", result); return result; } int FilePlay::SetDestinationFormat(WAVEFORMATEX wfx) { tracer.tracef(EE, "SetDestinationFormat\n"); int result = acmTransformer->SetDestinationFormat(wfx); if (result != 0) { tracer.tracef(ERR, "SetDestinationFormat : e-acmTransformer->SetDestinationFormat : %d\n", result); } tracer.tracef(EE, "~SetDestinationFormat : %d\n", result); return result; } bool FilePlay::Transmitting() { return bTransmit; }