/*========================================================================== * * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved. * * File: loopback.cpp * Content: Implements the loopback portion of the full duplex test * * History: * Date By Reason * ==== == ====== * 09/10/99 pnewson Created * 10/25/99 rodtoll Removed lpszVoicePassword member from sessiondesc * 10/27/99 pnewson Fix: Bug #113936 - Wizard should reset the AGC level before loopback test * Note: this fix adds the DVCLIENTCONFIG_AUTOVOLUMERESET flag * 10/28/99 pnewson Bug #114176 updated DVSOUNDDEVICECONFIG struct * 11/04/99 pnewson Bug #115279 changed SendMessage to PostMessage to resolve some deadlocks * 11/30/99 pnewson use default codec * use devices passed to CheckAudioSetup, instead of default devices * 12/01/99 rodtoll Added flag to cause wizard to auto-select the microphone * 01/14/2000 rodtoll Updated with API changes * 01/21/2000 pnewson Updated for UI revisions * Updated to support use of loopback tests for full duplex testing * 01/27/2000 rodtoll Updated with API changes * 02/08/2000 rodtoll Bug #131496 - Selecting DVTHRESHOLD_DEFAULT results in voice * never being detected * 03/03/2000 rodtoll Updated to handle alternative gamevoice build. * 04/19/2000 pnewson Error handling cleanup * 04/21/2000 rodtoll Bug #32952 Does not run on Win95 GOLD w/o IE4 installed * 06/21/2000 rodtoll Updated to use new parameters * 06/28/2000 rodtoll Prefix Bug #38022 * 07/31/2000 rodtoll Bug #39590 - SB16 class soundcards are passing when they should fail * Half duplex code was being ignored in mic test portion. * 08/28/2000 masonb Voice Merge: Changed ccomutil.h to ccomutil.h * 08/31/2000 rodtoll Bug #43804 - DVOICE: dwSensitivity structure member is confusing - should be dwThreshold * 11/29/2000 rodtoll Bug #48348 - DPVOICE: Modify wizard to make use of DirectPlay8 as the transport. * NOTE: Now requires a TCP/IP adapter to be present (or at least loopback) * 02/04/2001 simonpow Bug #354859 PREfast spotted errors (PostMessage return codes incorrectly * treated as HRESULTs) * 04/12/2001 kareemc WINBUG #360971 - Wizard Memory Leaks * ***************************************************************************/ #include "dxvtlibpch.h" #undef DPF_SUBCOMP #define DPF_SUBCOMP DN_SUBCOMP_VOICE // application defined message ids //#define WMAPP_LOOPBACKRUNNING WM_USER + 1 //#define WMAPP_INPUTVOLUME WM_USER + 2 // {53CA3FB7-4FD5-4a67-99E4-6F2496E6FEC2} static const GUID GUID_LOOPBACKTEST = { 0x53ca3fb7, 0x4fd5, 0x4a67, { 0x99, 0xe4, 0x6f, 0x24, 0x96, 0xe6, 0xfe, 0xc2 } }; HRESULT StartDirectPlay( PDIRECTPLAY8SERVER* lplpdp8 ); HRESULT StopDirectPlay( PDIRECTPLAY8SERVER lpdp8 ); #undef DPF_MODNAME #define DPF_MODNAME "DVMessageHandlerServer" HRESULT PASCAL DVMessageHandlerServer( LPVOID lpvUserContext, DWORD dwMessageType, LPVOID lpMessage ) { DPF_ENTER(); switch( dwMessageType ) { case DVMSGID_CREATEVOICEPLAYER: break; case DVMSGID_DELETEVOICEPLAYER: break; case DVMSGID_SESSIONLOST: break; case DVMSGID_PLAYERVOICESTART: break; case DVMSGID_PLAYERVOICESTOP: break; case DVMSGID_RECORDSTART: break; case DVMSGID_RECORDSTOP: break; case DVMSGID_CONNECTRESULT: break; case DVMSGID_DISCONNECTRESULT: break; case DVMSGID_INPUTLEVEL: break; case DVMSGID_OUTPUTLEVEL: break; default: break; } DPF_EXIT(); return DV_OK; } #undef DPF_MODNAME #define DPF_MODNAME "DVMessageHandlerClient" HRESULT PASCAL DVMessageHandlerClient( LPVOID lpvUserContext, DWORD dwMessageType, LPVOID lpMessage ) { DPF_EXIT(); HWND hwndDialog; HWND hwndPeakMeter; HWND hwndSlider; LONG lRet; HRESULT hr; CSupervisorInfo* lpsinfo; PDVMSG_INPUTLEVEL pdvInputLevel; PDVMSG_OUTPUTLEVEL pdvOutputLevel; lpsinfo = (CSupervisorInfo*)lpvUserContext; if( lpsinfo ) { if( !lpsinfo->GetLoopbackRunning() ) return DV_OK; } switch( dwMessageType ) { case DVMSGID_CREATEVOICEPLAYER: break; case DVMSGID_DELETEVOICEPLAYER: break; case DVMSGID_SESSIONLOST: break; case DVMSGID_PLAYERVOICESTART: break; case DVMSGID_PLAYERVOICESTOP: break; case DVMSGID_RECORDSTART: if (lpsinfo == NULL) { break; } // forward the message along to the appropriate window lpsinfo->GetHWNDDialog(&hwndDialog); if (!PostMessage(hwndDialog, WM_APP_RECORDSTART, 0, 0)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "PostMessage failed, code: %i", GetLastError()); break; // no error return, just continue } break; case DVMSGID_RECORDSTOP: if (lpsinfo == NULL) { break; } // forward the message along to the appropriate window lpsinfo->GetHWNDDialog(&hwndDialog); if (!PostMessage(hwndDialog, WM_APP_RECORDSTOP, 0, 0)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "PostMessage failed, code: %i", GetLastError()); break; // no error return, just continue } break; case DVMSGID_CONNECTRESULT: break; case DVMSGID_DISCONNECTRESULT: break; case DVMSGID_INPUTLEVEL: if (lpsinfo == NULL) { break; } // update the peak meter lpsinfo->GetHWNDInputPeak(&hwndPeakMeter); if (IsWindow(hwndPeakMeter)) { pdvInputLevel = (PDVMSG_INPUTLEVEL) lpMessage; if (!PostMessage(hwndPeakMeter, PM_SETCUR, 0, pdvInputLevel->dwPeakLevel )) { DPFX(DPFPREP, DVF_ERRORLEVEL, "PostMessage failed, code: %i", GetLastError()); break; // no error return, just continue } } // update the volume slider lpsinfo->GetHWNDInputVolumeSlider(&hwndSlider); if (IsWindow(hwndSlider)) { pdvInputLevel = (PDVMSG_INPUTLEVEL) lpMessage; if (!PostMessage(hwndSlider, TBM_SETPOS, 1, DBToAmpFactor(DSBVOLUME_MAX)-DBToAmpFactor(pdvInputLevel->lRecordVolume))) { DPFX(DPFPREP, DVF_ERRORLEVEL, "PostMessage failed, code: %i", GetLastError()); break; // no error return, just continue } } break; case DVMSGID_OUTPUTLEVEL: if (lpsinfo == NULL) { break; } // update the peak meter lpsinfo->GetHWNDOutputPeak(&hwndPeakMeter); if (IsWindow(hwndPeakMeter)) { pdvOutputLevel = (PDVMSG_OUTPUTLEVEL) lpMessage; if (!PostMessage(hwndPeakMeter, PM_SETCUR, 0, pdvOutputLevel->dwPeakLevel )) { DPFX(DPFPREP, DVF_ERRORLEVEL, "PostMessage failed, code: %i", GetLastError()); break; // no error return, just continue } } // update the volume slider lpsinfo->GetHWNDOutputVolumeSlider(&hwndSlider); if (IsWindow(hwndSlider)) { DWORD dwVolume = 0; // Set to 0 to avoid PREFIX bug // Get the current waveOut volume and set the slider to that position hr = lpsinfo->GetWaveOutVolume(&dwVolume); if (FAILED(hr)) { // couldn't get the volume - set the slider to top PostMessage(hwndSlider, TBM_SETPOS, 1, DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MAX)); // disable the slider PostMessage(hwndSlider, WM_CANCELMODE, 0, 0 ); } else { PostMessage(hwndSlider, TBM_SETPOS, 1, (DBToAmpFactor(DSBVOLUME_MAX) - DBToAmpFactor(DSBVOLUME_MIN)) - dwVolume); } } break; default: break; } DPF_EXIT(); return DV_OK; } #undef DPF_MODNAME #define DPF_MODNAME "StartDirectPlay" HRESULT StartDirectPlay( PDIRECTPLAY8SERVER* lplpdp8 ) { HRESULT hr = DPN_OK; LONG lRet = S_OK; PDIRECTPLAY8ADDRESS pDeviceAddress = NULL; PDIRECTPLAY8SERVER pdp8Server = NULL; DPN_APPLICATION_DESC dpnApplicationDesc; DPF_ENTER(); *lplpdp8 = NULL; hr = COM_CoCreateInstance( CLSID_DirectPlay8Server, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay8Server, (void **)lplpdp8); if (FAILED(hr)) { *lplpdp8 = NULL; DPFX(DPFPREP, DVF_ERRORLEVEL, "CoCreateInstance for DirectPlay failed, code: %i", hr); goto error_cleanup; } pdp8Server = *lplpdp8; hr = COM_CoCreateInstance( CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay8Address, (void **)&pDeviceAddress ); if( FAILED( hr ) ) { pDeviceAddress = NULL; DPFX(DPFPREP, DVF_ERRORLEVEL, "CoCreateInstance for DirectPlay8Address failed, code: 0x%x", hr ); goto error_cleanup; } // // NOTE: This now causes the wizard to require TCP/IP to be installed. // (doesn't have to be dialed up -- as long as local loopback is available) // // Eventually build a loopback SP for directplay8. // // TODO: Allow this to fall back to other SPs or use loopback SP // hr = pDeviceAddress->SetSP( &CLSID_DP8SP_TCPIP ); if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed setting SP for address, code: 0x%x", hr ); goto error_cleanup; } hr = pdp8Server->Initialize( NULL, DVMessageHandlerServer, 0 ); if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed initializing directplay layer, code: 0x%x", hr ); goto error_cleanup; } ZeroMemory( &dpnApplicationDesc, sizeof( DPN_APPLICATION_DESC ) ); dpnApplicationDesc.dwSize = sizeof( DPN_APPLICATION_DESC ); dpnApplicationDesc.guidApplication = GUID_LOOPBACKTEST; dpnApplicationDesc.dwFlags = DPNSESSION_NODPNSVR | DPNSESSION_CLIENT_SERVER; hr = pdp8Server->Host( &dpnApplicationDesc, &pDeviceAddress, 1, NULL, NULL, NULL, 0 ); if( FAILED( hr ) ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to host on directplay layer, code: 0x%x", hr ); goto error_cleanup; } pDeviceAddress->Release(); DPF_EXIT(); return S_OK; error_cleanup: if (*lplpdp8 != NULL) { pdp8Server->Close(0); pdp8Server->Release(); *lplpdp8 = NULL; } if( pDeviceAddress ) { pDeviceAddress->Release(); } DPF_EXIT(); return hr; } #undef DPF_MODNAME #define DPF_MODNAME "StopDirectPlay" HRESULT StopDirectPlay(PDIRECTPLAY8SERVER lpdp8) { DPF_ENTER(); // Kill the session if (lpdp8 != NULL) { lpdp8->Close(0); lpdp8->Release(); } DPF_EXIT(); return S_OK; } #undef DPF_MODNAME #define DPF_MODNAME "StartLoopback" HRESULT StartLoopback( LPDIRECTPLAYVOICESERVER* lplpdvs, LPDIRECTPLAYVOICECLIENT* lplpdvc, PDIRECTPLAY8SERVER* lplpdp8, LPVOID lpvCallbackContext, HWND hwndAppWindow, GUID guidCaptureDevice, GUID guidRenderDevice, DWORD dwFlags) { HRESULT hr; DWORD dwSize = 0; DVCLIENTCONFIG dvcc; DVSOUNDDEVICECONFIG dvsdc; DVID dvidAllPlayers = DVID_ALLPLAYERS; PBYTE pDeviceConfigBuffer = NULL; PDVSOUNDDEVICECONFIG pdvsdc = NULL; BOOL fVoiceSessionStarted = FALSE; BOOL fClientConnected = FALSE; DPF_ENTER(); *lplpdvs = NULL; *lplpdvc = NULL; hr = COM_CoCreateInstance( DPVOICE_CLSID_DPVOICE, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayVoiceServer, (void **)lplpdvs); if (FAILED(hr)) { *lplpdvs = NULL; DPFX(DPFPREP, DVF_ERRORLEVEL, "CoCreateInstance failed, code: %i", hr); goto error_cleanup; } hr = (*lplpdvs)->Initialize(*lplpdp8, DVMessageHandlerServer, lpvCallbackContext, NULL, 0); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceServer::Initialize failed, code: %i", hr); goto error_cleanup; } DVSESSIONDESC dvSessionDesc; dvSessionDesc.dwSize = sizeof( DVSESSIONDESC ); dvSessionDesc.dwBufferAggressiveness = DVBUFFERAGGRESSIVENESS_DEFAULT; dvSessionDesc.dwBufferQuality = DVBUFFERQUALITY_DEFAULT; dvSessionDesc.dwFlags = 0; dvSessionDesc.dwSessionType = DVSESSIONTYPE_ECHO; // Note this compression type is used for its short frame size so // we can quickly detect lockups. dvSessionDesc.guidCT = DPVCTGUID_NONE; hr = (*lplpdvs)->StartSession( &dvSessionDesc, 0 ); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceServer::StartSession failed, code: %i", hr); goto error_cleanup; } fVoiceSessionStarted = TRUE; hr = COM_CoCreateInstance( DPVOICE_CLSID_DPVOICE, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlayVoiceClient, (void **)lplpdvc); if (FAILED(hr)) { *lplpdvc = NULL; DPFX(DPFPREP, DVF_ERRORLEVEL, "CoCreateInstance failed, code: %i", hr); goto error_cleanup; } hr = (*lplpdvc)->Initialize(*lplpdp8, DVMessageHandlerClient, lpvCallbackContext, NULL, 0); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceClient::Initialize failed, code: %i", hr); goto error_cleanup; } dvsdc.dwSize = sizeof( DVSOUNDDEVICECONFIG ); dvsdc.hwndAppWindow = hwndAppWindow; dvsdc.dwFlags = DVSOUNDCONFIG_AUTOSELECT; if (dwFlags & DVSOUNDCONFIG_HALFDUPLEX) { // The caller wants a half duplex session. dvsdc.dwFlags |= DVSOUNDCONFIG_HALFDUPLEX; } if (dwFlags & DVSOUNDCONFIG_TESTMODE) { // The caller wants a test mode session. dvsdc.dwFlags |= DVSOUNDCONFIG_TESTMODE; } dvsdc.guidCaptureDevice = guidCaptureDevice; dvsdc.guidPlaybackDevice = guidRenderDevice; dvsdc.lpdsPlaybackDevice = NULL; dvsdc.lpdsCaptureDevice = NULL; dvsdc.dwMainBufferFlags = 0; dvsdc.dwMainBufferPriority = 0; dvsdc.lpdsMainBuffer = NULL; dvcc.dwSize = sizeof( DVCLIENTCONFIG ); dvcc.dwFlags = DVCLIENTCONFIG_AUTOVOICEACTIVATED | DVCLIENTCONFIG_AUTORECORDVOLUME | DVCLIENTCONFIG_AUTOVOLUMERESET | DVCLIENTCONFIG_PLAYBACKMUTE; // we don't want the user to hear his/her voice right away dvcc.dwThreshold = DVTHRESHOLD_UNUSED; dvcc.lPlaybackVolume = DSBVOLUME_MAX; dvcc.lRecordVolume = DSBVOLUME_MAX; dvcc.dwNotifyPeriod = 50; dvcc.dwBufferQuality = DVBUFFERQUALITY_DEFAULT; dvcc.dwBufferAggressiveness = DVBUFFERAGGRESSIVENESS_DEFAULT; hr = (*lplpdvc)->Connect( &dvsdc, &dvcc, DVFLAGS_SYNC|DVFLAGS_NOQUERY ); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceClient::Connect failed, code: %i", hr); goto error_cleanup; } fClientConnected = TRUE; hr = (*lplpdvc)->SetTransmitTargets(&dvidAllPlayers, 1 , 0); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceClient::SetTransmitTargets failed, code: %i", hr); goto error_cleanup; } dwSize = 0; hr = (*lplpdvc)->GetSoundDeviceConfig(pdvsdc, &dwSize); if( hr != DVERR_BUFFERTOOSMALL ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceClient::GetSoundDeviceConfig failed, hr: %i", hr); goto error_cleanup; } pDeviceConfigBuffer = new BYTE[dwSize]; if( pDeviceConfigBuffer == NULL ) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Memory alloc failure" ); hr = DVERR_OUTOFMEMORY; goto error_cleanup; } pdvsdc = (PDVSOUNDDEVICECONFIG) pDeviceConfigBuffer; pdvsdc->dwSize = sizeof( DVSOUNDDEVICECONFIG ); hr = (*lplpdvc)->GetSoundDeviceConfig(pdvsdc, &dwSize ); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceClient::GetSoundDeviceConfig failed, hr: %i", hr); goto error_cleanup; } // If we're looking for full duplex fail and notify caller if we get half duplex if( !(dwFlags & DVSOUNDCONFIG_HALFDUPLEX) ) { if (pdvsdc->dwFlags & DVSOUNDCONFIG_HALFDUPLEX) { DPFX(DPFPREP, DVF_ERRORLEVEL, "We received a half duplex when we expected full duplex" ); // It only started up in half duplex. Notify the caller. hr = DV_HALFDUPLEX; goto error_cleanup; } } if( pdvsdc->dwFlags & DVSOUNDCONFIG_HALFDUPLEX ) { DPFX(DPFPREP, DVF_INFOLEVEL, "StartLoopBack() returning DV_HALFDUPLEX flags=0x%x dwFlags = 0x%x", pdvsdc->dwFlags, dwFlags ); // it started in fullduplex, notify the caller delete [] pDeviceConfigBuffer; DPF_EXIT(); return DV_HALFDUPLEX; } else { DPFX(DPFPREP, DVF_INFOLEVEL, "StartLoopBack() returning DV_FULLDUPLEX flags=0x%x dwFlags = 0x%x", pdvsdc->dwFlags, dwFlags ); // it started in fullduplex, notify the caller delete [] pDeviceConfigBuffer; DPF_EXIT(); return DV_FULLDUPLEX; } error_cleanup: if (pDeviceConfigBuffer != NULL) { delete [] pDeviceConfigBuffer; pDeviceConfigBuffer = NULL; } if (*lplpdvc != NULL) { if (fClientConnected) { (*lplpdvc)->Disconnect(DVFLAGS_SYNC); fClientConnected = FALSE; } (*lplpdvc)->Release(); *lplpdvc = NULL; } if (*lplpdvs != NULL) { if (fVoiceSessionStarted) { (*lplpdvs)->StopSession(0); fVoiceSessionStarted = FALSE; } (*lplpdvs)->Release(); *lplpdvs = NULL; } DPFX(DPFPREP, DVF_ERRORLEVEL, "StartLoopback() returning hr=0x%x", hr ); DPF_EXIT(); return hr; } #undef DPF_MODNAME #define DPF_MODNAME "StopLoopback" HRESULT StopLoopback( LPDIRECTPLAYVOICESERVER lpdvs, LPDIRECTPLAYVOICECLIENT lpdvc, PDIRECTPLAY8SERVER lpdp8 ) { HRESULT hr; LONG lRet; BOOL fRet; BOOL fClientConnected = TRUE; BOOL fVoiceSessionRunning = TRUE; DPF_ENTER(); if (lpdvc != NULL) { hr = lpdvc->Disconnect(DVFLAGS_SYNC); fClientConnected = FALSE; if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceClient::Disconnect failed, hr: %i", hr); goto error_cleanup; } lpdvc->Release(); lpdvc = NULL; } if (lpdvs != NULL) { hr = lpdvs->StopSession(0); fVoiceSessionRunning = FALSE; if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "IDirectPlayVoiceServer::StopSession failed, hr: %i", hr); goto error_cleanup; } lpdvs->Release(); lpdvs = NULL; } DPF_EXIT(); return S_OK; error_cleanup: if (lpdvc != NULL) { if (fClientConnected) { lpdvc->Disconnect(DVFLAGS_SYNC); fClientConnected = FALSE; } lpdvc->Release(); lpdvc = NULL; } if (lpdvs != NULL) { if (fVoiceSessionRunning) { lpdvs->StopSession(0); fVoiceSessionRunning = FALSE; } lpdvs->Release(); lpdvs = NULL; } DPF_EXIT(); return hr; }