windows-nt/Source/XPSP1/NT/multimedia/directx/dplay/dvoice/dxvtlib/loopback.cpp
2020-09-26 16:20:57 +08:00

682 lines
17 KiB
C++

/*==========================================================================
*
* 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;
}