1014 lines
26 KiB
C++
1014 lines
26 KiB
C++
/*==========================================================================;
|
|
*
|
|
* Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* File: fulldup.cpp
|
|
* Content: Implements a process that uses DirectSound and
|
|
* DirectSoundCapture to test the systems full duplex
|
|
* capability. Note that WinMain is in fdtest.cpp, but
|
|
* the guts are here.
|
|
* History:
|
|
* Date By Reason
|
|
* ============
|
|
* 08/19/99 pnewson created
|
|
* 10/28/99 pnewson Bug #113937 audible clicking during full duplex test
|
|
* 11/02/99 pnewson Fix: Bug #116365 - using wrong DSBUFFERDESC
|
|
* 01/21/2000 pnewson Changed over to using a dpvoice loopback session
|
|
* for full duplex testing
|
|
* 04/19/2000 pnewson Error handling cleanup
|
|
* 07/12/2000 rodtoll Bug #31468 - Add diagnostic spew to logfile to show what is failing the HW Wizard
|
|
***************************************************************************/
|
|
|
|
#include "dxvtlibpch.h"
|
|
|
|
|
|
#undef DPF_SUBCOMP
|
|
#define DPF_SUBCOMP DN_SUBCOMP_VOICE
|
|
|
|
|
|
static HRESULT OpenIPCObjects();
|
|
static HRESULT CloseIPCObjects();
|
|
static HRESULT CommandLoop(CFullDuplexIPC* lpipcFullDuplex);
|
|
static HRESULT DispatchCommand(CFullDuplexIPC* lpipcFullDuplex, SFDTestCommand* pfdtc);
|
|
static HRESULT CommandFullDuplexStart(SFDTestCommandFullDuplexStart* pfdtcFullDuplexStart, HRESULT* phrIPC);
|
|
static HRESULT CommandFullDuplexStop(SFDTestCommandFullDuplexStop* pfdtcFullDuplexStop, HRESULT* phrIPC);
|
|
static HRESULT PlayAndCheckRender(LPDIRECTSOUNDBUFFER lpdsb, HANDLE hEvent);
|
|
static HRESULT PlayAndCheckCapture(LPDIRECTSOUNDCAPTUREBUFFER lpdscb, HANDLE hEvent);
|
|
static HRESULT AttemptCapture();
|
|
|
|
|
|
// one global struct to store this process's state data.
|
|
struct SFullDuplexData
|
|
{
|
|
LPDIRECTPLAYVOICESERVER lpdpvs;
|
|
LPDIRECTPLAYVOICECLIENT lpdpvc;
|
|
PDIRECTPLAY8SERVER lpdp8;
|
|
};
|
|
|
|
SFullDuplexData g_FullDuplexData;
|
|
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "FullDuplexProcess"
|
|
HRESULT FullDuplexProcess(HINSTANCE hResDLLInstance, HINSTANCE hPrevInstance, TCHAR *szCmdLine, int iCmdShow)
|
|
{
|
|
HRESULT hr;
|
|
CFullDuplexIPC ipcFullDuplex;
|
|
BOOL fIPCInitialized = FALSE;
|
|
PDIRECTPLAYVOICECLIENT pdpvClient = NULL;
|
|
|
|
g_FullDuplexData.lpdp8 = NULL;
|
|
|
|
DPF_ENTER();
|
|
|
|
// Create dummy voice object so that voice process state gets initialized
|
|
hr = CoCreateInstance( CLSID_DirectPlayVoiceClient, NULL, CLSCTX_INPROC, IID_IDirectPlayVoiceClient, (void **) &pdpvClient );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
Diagnostics_Write(DVF_ERRORLEVEL, "Unable to create dummy dp8 object hr: 0x%x", hr);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
if (!InitGlobGuard())
|
|
{
|
|
hr = DVERR_OUTOFMEMORY;
|
|
goto error_cleanup;
|
|
}
|
|
|
|
hr = ipcFullDuplex.Init();
|
|
if (FAILED(hr))
|
|
{
|
|
Diagnostics_Write(DVF_ERRORLEVEL, "CIPCFullDuplex::Init() failed, hr: 0x%x", hr);
|
|
goto error_cleanup;
|
|
}
|
|
fIPCInitialized = TRUE;
|
|
|
|
// Startup DirectPlay once so that we don't have to do it over and over
|
|
// again for the test.
|
|
hr = StartDirectPlay( &g_FullDuplexData.lpdp8 );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
Diagnostics_Write(DVF_ERRORLEVEL, "Failed to start transport hr: 0x%x", hr);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
// start up the testing loop
|
|
hr = CommandLoop(&ipcFullDuplex);
|
|
if (FAILED(hr))
|
|
{
|
|
Diagnostics_Write(DVF_ERRORLEVEL, "CommandLoop failed, hr: 0x%x", hr);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
hr = StopDirectPlay( g_FullDuplexData.lpdp8 );
|
|
|
|
g_FullDuplexData.lpdp8 = NULL;
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
Diagnostics_Write(DVF_ERRORLEVEL, "Failed to stop transport hr: 0x%x", hr);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
// close the mutex, events and shared memory stuff
|
|
hr = ipcFullDuplex.Deinit();
|
|
if (FAILED(hr))
|
|
{
|
|
Diagnostics_Write(DVF_ERRORLEVEL, "CIPCFullDuplex::Deinit() failed, hr: 0x%x", hr);
|
|
goto error_cleanup;
|
|
}
|
|
|
|
// Destroy dummy client object which will shutdown dplayvoice state
|
|
pdpvClient->Release();
|
|
|
|
DeinitGlobGuard();
|
|
DPF_EXIT();
|
|
return S_OK;
|
|
|
|
error_cleanup:
|
|
if (fIPCInitialized == TRUE)
|
|
{
|
|
ipcFullDuplex.Deinit();
|
|
fIPCInitialized = FALSE;
|
|
}
|
|
|
|
if( g_FullDuplexData.lpdp8 )
|
|
{
|
|
g_FullDuplexData.lpdp8->Release();
|
|
g_FullDuplexData.lpdp8 = NULL;
|
|
}
|
|
|
|
if( pdpvClient )
|
|
pdpvClient->Release();
|
|
|
|
DeinitGlobGuard();
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CommandLoop"
|
|
HRESULT CommandLoop(CFullDuplexIPC* lpipcFullDuplex)
|
|
{
|
|
BOOL fRet;
|
|
LONG lRet;
|
|
HRESULT hr;
|
|
DWORD dwRet;
|
|
SFDTestCommand fdtc;
|
|
|
|
DPF_ENTER();
|
|
|
|
// Kick the supervisor process to let it know
|
|
// we're ready to go.
|
|
hr = lpipcFullDuplex->SignalParentReady();
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// enter the main command loop
|
|
while (1)
|
|
{
|
|
// wait for a command from the supervisor process
|
|
fdtc.dwSize = sizeof(fdtc);
|
|
hr = lpipcFullDuplex->Receive(&fdtc);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// dispatch the command
|
|
hr = DispatchCommand(lpipcFullDuplex, &fdtc);
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
if (hr == DV_EXIT)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Exiting FullDuplex process command loop");
|
|
break;
|
|
}
|
|
}
|
|
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "DispatchCommand"
|
|
HRESULT DispatchCommand(CFullDuplexIPC* lpipcFullDuplex, SFDTestCommand* pfdtc)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hrIPC;
|
|
|
|
DPF_ENTER();
|
|
|
|
switch (pfdtc->fdtcc)
|
|
{
|
|
case fdtccExit:
|
|
// ok - reply to the calling process to let them
|
|
// know we are getting out.
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "FullDuplex received Exit command");
|
|
lpipcFullDuplex->Reply(DV_EXIT);
|
|
|
|
// returning this code will break us out of
|
|
// the command processing loop
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Exit");
|
|
return DV_EXIT;
|
|
|
|
case fdtccFullDuplexStart:
|
|
hr = CommandFullDuplexStart(&(pfdtc->fdtu.fdtcFullDuplexStart), &hrIPC);
|
|
if (FAILED(hr))
|
|
{
|
|
lpipcFullDuplex->Reply(hrIPC);
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
hr = lpipcFullDuplex->Reply(hrIPC);
|
|
DPF_EXIT();
|
|
return hr;
|
|
|
|
case fdtccFullDuplexStop:
|
|
hr = CommandFullDuplexStop(&(pfdtc->fdtu.fdtcFullDuplexStop), &hrIPC);
|
|
if (FAILED(hr))
|
|
{
|
|
lpipcFullDuplex->Reply(hrIPC);
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
hr = lpipcFullDuplex->Reply(hrIPC);
|
|
DPF_EXIT();
|
|
return hr;
|
|
|
|
default:
|
|
// Don't know this command. Reply with the appropriate
|
|
// code.
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "FullDuplex received Unknown command");
|
|
lpipcFullDuplex->Reply(DVERR_UNKNOWN);
|
|
|
|
// While this is an error, it is one that the calling
|
|
// process needs to figure out. In the meantime, this
|
|
// process will happily continue on.
|
|
DPF_EXIT();
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
/*
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CommandFullDuplexStart"
|
|
HRESULT CommandFullDuplexStart(SFDTestCommandFullDuplexStart* pfdtcFullDuplexStart, HRESULT* phrIPC)
|
|
{
|
|
HRESULT hr;
|
|
DSBUFFERDESC1 dsbd;
|
|
WAVEFORMATEX wfx;
|
|
DWORD dwSizeWritten;
|
|
DSBCAPS dsbc;
|
|
LPVOID lpvAudio1 = NULL;
|
|
DWORD dwAudio1Size = NULL;
|
|
LPVOID lpvAudio2 = NULL;
|
|
DWORD dwAudio2Size = NULL;
|
|
HANDLE hFullDuplexRenderEvent;
|
|
HANDLE hFullDuplexCaptureEvent;
|
|
DSBPOSITIONNOTIFY dsbPositionNotify;
|
|
DWORD dwRet;
|
|
LONG lRet;
|
|
LPDIRECTSOUNDBUFFER lpdsb;
|
|
HANDLE hEvent;
|
|
BYTE bSilence;
|
|
|
|
DPF_ENTER();
|
|
|
|
// create the DirectSound interface
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating DirectSound");
|
|
GlobGuardIn();
|
|
hr = DirectSoundCreate(&pfdtcFullDuplexStart->guidRenderDevice, &g_lpdsFullDuplexRender, NULL);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "DirectSoundCreate failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_0;
|
|
}
|
|
|
|
// create the DirectSoundCapture interface
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating DirectSoundCapture");
|
|
GlobGuardIn();
|
|
hr = DirectSoundCaptureCreate(&pfdtcFullDuplexStart->guidCaptureDevice, &g_lpdscFullDuplexCapture, NULL);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "DirectSoundCaptureCreate failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_1;
|
|
}
|
|
|
|
// set to normal mode
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Setting Cooperative Level");
|
|
GlobGuardIn();
|
|
hr = g_lpdsFullDuplexRender->SetCooperativeLevel(GetDesktopWindow(), DSSCL_NORMAL);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "SetCooperativeLevel failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_2;
|
|
}
|
|
|
|
// Create a secondary buffer object.
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating Secondary Buffer");
|
|
CopyMemory(&wfx, &gc_wfxSecondaryFormat, sizeof(wfx));
|
|
ZeroMemory(&dsbd, sizeof(dsbd));
|
|
dsbd.dwSize = sizeof(dsbd);
|
|
dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLVOLUME;
|
|
dsbd.dwBufferBytes =
|
|
(wfx.nSamplesPerSec
|
|
* wfx.nBlockAlign)
|
|
/ (1000 / gc_dwFrameSize);
|
|
dsbd.dwReserved = 0;
|
|
dsbd.lpwfxFormat = &wfx;
|
|
GlobGuardIn();
|
|
hr = g_lpdsFullDuplexRender->CreateSoundBuffer((LPDSBUFFERDESC)&dsbd, &g_lpdsbFullDuplexSecondary, NULL);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "CreateSoundBuffer failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_2;
|
|
}
|
|
|
|
// clear out the secondary buffer
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Clearing Secondary Buffer");
|
|
GlobGuardIn();
|
|
hr = g_lpdsbFullDuplexSecondary->Lock(
|
|
0,
|
|
0,
|
|
&lpvAudio1,
|
|
&dwAudio1Size,
|
|
&lpvAudio2,
|
|
&dwAudio2Size,
|
|
DSBLOCK_ENTIREBUFFER);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Lock failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_3;
|
|
}
|
|
|
|
if (lpvAudio1 == NULL)
|
|
{
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_3;
|
|
}
|
|
|
|
if (pfdtcFullDuplexStart->wfxRenderFormat.wBitsPerSample == 8)
|
|
{
|
|
bSilence = 0x80;
|
|
}
|
|
else
|
|
{
|
|
bSilence = 0x00;
|
|
}
|
|
|
|
memset(lpvAudio1, bSilence, dwAudio1Size);
|
|
if (lpvAudio2 != NULL)
|
|
{
|
|
memset(lpvAudio2, bSilence, dwAudio2Size);
|
|
}
|
|
|
|
GlobGuardIn();
|
|
hr = g_lpdsbFullDuplexSecondary->Unlock(
|
|
lpvAudio1,
|
|
dwAudio1Size,
|
|
lpvAudio2,
|
|
dwAudio2Size);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Unlock failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_3;
|
|
}
|
|
|
|
// Set up one notification position in the buffer so
|
|
// we can tell if it is really playing, or lying to us.
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Querying for IDirectSoundNotify");
|
|
GlobGuardIn();
|
|
hr = g_lpdsbFullDuplexSecondary->QueryInterface(
|
|
IID_IDirectSoundNotify,
|
|
(LPVOID*)&g_lpdsnFullDuplexSecondary);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "QueryInterface(IID_DirectSoundNotify) failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_3;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating Notification Event");
|
|
GlobGuardIn();
|
|
g_hFullDuplexRenderEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
hFullDuplexRenderEvent = g_hFullDuplexRenderEvent;
|
|
GlobGuardOut();
|
|
if (hFullDuplexRenderEvent == NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "CreateEvent failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_WIN32;
|
|
goto error_level_4;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "calling SetNotificationPositions");
|
|
dsbPositionNotify.dwOffset = 0;
|
|
dsbPositionNotify.hEventNotify = hFullDuplexRenderEvent;
|
|
GlobGuardIn();
|
|
hr = g_lpdsnFullDuplexSecondary->SetNotificationPositions(1, &dsbPositionNotify);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "SetNotificationPositions failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_5;
|
|
}
|
|
|
|
// start the secondary buffer and confirm that it's running
|
|
GlobGuardIn();
|
|
lpdsb = g_lpdsbFullDuplexSecondary;
|
|
hEvent = g_hFullDuplexRenderEvent;
|
|
GlobGuardOut();
|
|
hr = PlayAndCheckRender(lpdsb, hEvent);
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Render verification test failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_5;
|
|
}
|
|
|
|
hr = AttemptCapture();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "AttemptCapture() failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_6;
|
|
}
|
|
|
|
DPF_EXIT();
|
|
return S_OK;
|
|
|
|
// error block
|
|
error_level_6:
|
|
GlobGuardIn();
|
|
g_lpdsbFullDuplexSecondary->Stop();
|
|
GlobGuardOut();
|
|
|
|
error_level_5:
|
|
GlobGuardIn();
|
|
CloseHandle(g_hFullDuplexRenderEvent);
|
|
g_hFullDuplexRenderEvent = NULL;
|
|
GlobGuardOut();
|
|
|
|
error_level_4:
|
|
GlobGuardIn();
|
|
g_lpdsnFullDuplexSecondary->Release();
|
|
g_lpdsnFullDuplexSecondary = NULL;
|
|
GlobGuardOut();
|
|
|
|
error_level_3:
|
|
GlobGuardIn();
|
|
g_lpdsbFullDuplexSecondary->Release();
|
|
g_lpdsbFullDuplexSecondary = NULL;
|
|
GlobGuardOut();
|
|
|
|
error_level_2:
|
|
GlobGuardIn();
|
|
g_lpdscFullDuplexCapture->Release();
|
|
g_lpdscFullDuplexCapture = NULL;
|
|
GlobGuardOut();
|
|
|
|
error_level_1:
|
|
GlobGuardIn();
|
|
g_lpdsFullDuplexRender->Release();
|
|
g_lpdsFullDuplexRender = NULL;
|
|
GlobGuardOut();
|
|
|
|
error_level_0:
|
|
// error for other process, not this one.
|
|
DPF_EXIT();
|
|
return S_OK;
|
|
}
|
|
*/
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CommandFullDuplexStart"
|
|
HRESULT CommandFullDuplexStart(SFDTestCommandFullDuplexStart* pfdtcFullDuplexStart, HRESULT* phrIPC)
|
|
{
|
|
DPF_ENTER();
|
|
|
|
HRESULT hr;
|
|
|
|
Diagnostics_Write( DVF_INFOLEVEL, "-----------------------------------------------------------" );
|
|
|
|
hr = Diagnostics_DeviceInfo( &pfdtcFullDuplexStart->guidRenderDevice, &pfdtcFullDuplexStart->guidCaptureDevice );
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
Diagnostics_Write( 0, "Error getting device information hr=0x%x", hr );
|
|
}
|
|
|
|
Diagnostics_Write( DVF_INFOLEVEL, "-----------------------------------------------------------" );
|
|
|
|
*phrIPC = StartLoopback(
|
|
&g_FullDuplexData.lpdpvs,
|
|
&g_FullDuplexData.lpdpvc,
|
|
&g_FullDuplexData.lpdp8,
|
|
NULL,
|
|
GetDesktopWindow(),
|
|
pfdtcFullDuplexStart->guidCaptureDevice,
|
|
pfdtcFullDuplexStart->guidRenderDevice,
|
|
pfdtcFullDuplexStart->dwFlags);
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "StartLoopback() return hr=0x%x", *phrIPC );
|
|
|
|
DPF_EXIT();
|
|
return DV_OK;
|
|
}
|
|
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CommandFullDuplexStop"
|
|
HRESULT CommandFullDuplexStop(SFDTestCommandFullDuplexStop* pfdtcFullDuplexStop, HRESULT* phrIPC)
|
|
{
|
|
DPF_ENTER();
|
|
|
|
*phrIPC = StopLoopback(
|
|
g_FullDuplexData.lpdpvs,
|
|
g_FullDuplexData.lpdpvc,
|
|
g_FullDuplexData.lpdp8);
|
|
|
|
if( FAILED( *phrIPC ) )
|
|
{
|
|
Diagnostics_Write( DVF_ERRORLEVEL, "Full Duplex Result = 0x%x", *phrIPC );
|
|
}
|
|
else
|
|
{
|
|
Diagnostics_Write( DVF_INFOLEVEL, "Full Duplex Result = DV_OK" );
|
|
}
|
|
|
|
DPF_EXIT();
|
|
return DV_OK;
|
|
}
|
|
|
|
/*
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "CommandFullDuplexStop"
|
|
HRESULT CommandFullDuplexStop(SFDTestCommandFullDuplexStop* pfdtcFullDuplexStop, HRESULT* phrIPC)
|
|
{
|
|
HRESULT hr;
|
|
LONG lRet;
|
|
HANDLE hFullDuplexRenderEvent;
|
|
HANDLE hFullDuplexCaptureEvent;
|
|
DWORD dwRet;
|
|
|
|
DPF_ENTER();
|
|
|
|
*phrIPC = S_OK;
|
|
hr = S_OK;
|
|
|
|
// wait for one more notification to ensure the buffer is
|
|
// still playing - give the buffer up to 10 times
|
|
// as long as it should need to actually notify us.
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Waiting for 2 notifications to confirm playback is still working");
|
|
GlobGuardIn();
|
|
hFullDuplexRenderEvent = g_hFullDuplexRenderEvent;
|
|
GlobGuardOut();
|
|
dwRet = WaitForSingleObject(hFullDuplexRenderEvent, 10 * gc_dwFrameSize);
|
|
if (dwRet != WAIT_OBJECT_0)
|
|
{
|
|
// check for timeout
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Wait for notification timed out! Buffer is not really playing");
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "WaitForSingleObject failed, code: %i", lRet);
|
|
hr = DVERR_WIN32;
|
|
*phrIPC = hr;
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
dwRet = WaitForSingleObject(hFullDuplexRenderEvent, 10 * gc_dwFrameSize);
|
|
if (dwRet != WAIT_OBJECT_0)
|
|
{
|
|
// check for timeout
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Wait for notification timed out! Buffer is not really playing");
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "WaitForSingleObject failed, code: %i", lRet);
|
|
hr = DVERR_WIN32;
|
|
*phrIPC = hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// also wait for the capture buffer...
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Waiting for 2 notifications to confirm capture is still working");
|
|
GlobGuardIn();
|
|
hFullDuplexCaptureEvent = g_hFullDuplexCaptureEvent;
|
|
GlobGuardOut();
|
|
dwRet = WaitForSingleObject(hFullDuplexCaptureEvent, 10 * gc_dwFrameSize);
|
|
if (dwRet != WAIT_OBJECT_0)
|
|
{
|
|
// check for timeout
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Wait for notification timed out! Buffer is not really playing");
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "WaitForSingleObject failed, code: %i", lRet);
|
|
hr = DVERR_WIN32;
|
|
*phrIPC = hr;
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
dwRet = WaitForSingleObject(hFullDuplexCaptureEvent, 10 * gc_dwFrameSize);
|
|
if (dwRet != WAIT_OBJECT_0)
|
|
{
|
|
// check for timeout
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Wait for notification timed out! Buffer is not really playing");
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "WaitForSingleObject failed, code: %i", lRet);
|
|
hr = DVERR_WIN32;
|
|
*phrIPC = hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Stopping Capture Buffer");
|
|
GlobGuardIn();
|
|
hr = g_lpdscbFullDuplexCapture->Stop();
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Stop failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Stopping Secondary Buffer");
|
|
GlobGuardIn();
|
|
hr = g_lpdsbFullDuplexSecondary->Stop();
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Stop failed, code: %i", HRESULT_CODE(hr));
|
|
*phrIPC = DVERR_SOUNDINITFAILURE;
|
|
}
|
|
|
|
GlobGuardIn();
|
|
if (g_hFullDuplexCaptureEvent != NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Closing Capture Buffer Notification Event Handle");
|
|
CloseHandle(g_hFullDuplexCaptureEvent);
|
|
g_hFullDuplexCaptureEvent = NULL;
|
|
}
|
|
if (g_lpdsnFullDuplexCapture != NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Releasing DirectSoundNotifier (capture)");
|
|
g_lpdsnFullDuplexCapture->Release();
|
|
g_lpdsnFullDuplexCapture = NULL;
|
|
}
|
|
if (g_lpdscbFullDuplexCapture != NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Releasing DirectSoundCaptureBuffer");
|
|
g_lpdscbFullDuplexCapture->Release();
|
|
g_lpdscbFullDuplexCapture = NULL;
|
|
}
|
|
|
|
if (g_lpdscFullDuplexCapture != NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Releasing DirectSoundCapture");
|
|
g_lpdscFullDuplexCapture->Release();
|
|
g_lpdscFullDuplexCapture = NULL;
|
|
}
|
|
if (g_hFullDuplexRenderEvent != NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Closing Secondary Buffer Notification Event Handle");
|
|
if (!CloseHandle(g_hFullDuplexRenderEvent))
|
|
{
|
|
lRet = GetLastError();
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "CloseHandle failed, code: %i", lRet);
|
|
*phrIPC = DVERR_WIN32;
|
|
hr = *phrIPC;
|
|
}
|
|
g_hFullDuplexRenderEvent = NULL;
|
|
}
|
|
if (g_lpdsnFullDuplexSecondary != NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Releasing Secondary Notifier");
|
|
g_lpdsnFullDuplexSecondary->Release();
|
|
g_lpdsnFullDuplexSecondary = NULL;
|
|
}
|
|
if (g_lpdsbFullDuplexSecondary != NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Releasing Secondary Buffer");
|
|
g_lpdsbFullDuplexSecondary->Release();
|
|
g_lpdsbFullDuplexSecondary = NULL;
|
|
}
|
|
if (g_lpdsFullDuplexRender != NULL)
|
|
{
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Releasing DirectSound");
|
|
g_lpdsFullDuplexRender->Release();
|
|
g_lpdsFullDuplexRender = NULL;
|
|
}
|
|
GlobGuardOut();
|
|
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PlayAndCheckRender"
|
|
HRESULT PlayAndCheckRender(LPDIRECTSOUNDBUFFER lpdsb, HANDLE hEvent)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwRet;
|
|
LONG lRet;
|
|
|
|
DPF_ENTER();
|
|
|
|
// start playing the secondary buffer
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Playing Secondary Buffer");
|
|
GlobGuardIn();
|
|
hr = lpdsb->Play(0, 0, DSBPLAY_LOOPING);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Play failed, code: %i", HRESULT_CODE(hr));
|
|
hr = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_0;
|
|
}
|
|
|
|
// wait for the first notification to ensure the buffer has
|
|
// really started to play - give the buffer up to 10 times
|
|
// as long as it should need to actually notify us.
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Waiting for notification to confirm playback is working");
|
|
dwRet = WaitForSingleObject(hEvent, 10 * gc_dwFrameSize);
|
|
if (dwRet != WAIT_OBJECT_0)
|
|
{
|
|
// check for timeout
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Wait for notification timed out! Buffer is not really playing");
|
|
hr = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_1;
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "WaitForSingleObject failed, code: %i", lRet);
|
|
hr = DVERR_WIN32;
|
|
goto error_level_1;
|
|
}
|
|
}
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "First notification received, continuing");
|
|
|
|
DPF_EXIT();
|
|
return S_OK;
|
|
|
|
// error block
|
|
error_level_1:
|
|
GlobGuardIn();
|
|
lpdsb->Stop();
|
|
GlobGuardOut();
|
|
|
|
error_level_0:
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "PlayAndCheckCapture"
|
|
HRESULT PlayAndCheckCapture(LPDIRECTSOUNDCAPTUREBUFFER lpdscb, HANDLE hEvent)
|
|
{
|
|
HRESULT hr;
|
|
DWORD dwRet;
|
|
LONG lRet;
|
|
|
|
DPF_ENTER();
|
|
|
|
// start playing the capture buffer
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Starting Capture Buffer");
|
|
GlobGuardIn();
|
|
hr = lpdscb->Start(DSCBSTART_LOOPING);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Start failed, code: %i", HRESULT_CODE(hr));
|
|
hr = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_0;
|
|
}
|
|
|
|
// wait for the first notification to ensure the buffer has
|
|
// really started to capture - give the buffer up to 10 times
|
|
// as long as it should need to actually notify us.
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Waiting for notification to confirm capture is working");
|
|
dwRet = WaitForSingleObject(hEvent, 10 * gc_dwFrameSize);
|
|
if (dwRet != WAIT_OBJECT_0)
|
|
{
|
|
// check for timeout
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Wait for notification timed out! Buffer is not really playing");
|
|
hr = DVERR_SOUNDINITFAILURE;
|
|
goto error_level_1;
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "WaitForSingleObject failed, code: %i", lRet);
|
|
hr = DVERR_WIN32;
|
|
goto error_level_1;
|
|
}
|
|
}
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "First notification received, continuing");
|
|
|
|
DPF_EXIT();
|
|
return S_OK;
|
|
|
|
// error block
|
|
error_level_1:
|
|
GlobGuardIn();
|
|
lpdscb->Stop();
|
|
GlobGuardOut();
|
|
|
|
error_level_0:
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
#undef DPF_MODNAME
|
|
#define DPF_MODNAME "AttemptCapture"
|
|
HRESULT AttemptCapture()
|
|
{
|
|
DPF_ENTER();
|
|
|
|
DWORD dwIndex;
|
|
BOOL fCaptureFailed;
|
|
HANDLE hFullDuplexCaptureEvent;
|
|
DSBPOSITIONNOTIFY dsbPositionNotify;
|
|
DSCBUFFERDESC dscbd;
|
|
LPDIRECTSOUNDCAPTUREBUFFER lpdscb;
|
|
HRESULT hr;
|
|
HANDLE hEvent;
|
|
WAVEFORMATEX wfx;
|
|
|
|
fCaptureFailed = TRUE;
|
|
dwIndex = 0;
|
|
while (1)
|
|
{
|
|
CopyMemory(&wfx, &gc_rgwfxCaptureFormats[dwIndex], sizeof(wfx));
|
|
|
|
if (wfx.wFormatTag == 0
|
|
&& wfx.nChannels == 0
|
|
&& wfx.nSamplesPerSec == 0
|
|
&& wfx.nAvgBytesPerSec == 0
|
|
&& wfx.nBlockAlign == 0
|
|
&& wfx.wBitsPerSample == 0
|
|
&& wfx.cbSize == 0)
|
|
{
|
|
// we've found the last element of the array, break out.
|
|
break;
|
|
}
|
|
|
|
// create the capture buffer
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating DirectSoundCaptureBuffer");
|
|
ZeroMemory(&dscbd, sizeof(dscbd));
|
|
dscbd.dwSize = sizeof(dscbd);
|
|
dscbd.dwFlags = 0;
|
|
dscbd.dwBufferBytes =
|
|
(wfx.nSamplesPerSec
|
|
* wfx.nBlockAlign)
|
|
/ (1000 / gc_dwFrameSize);
|
|
dscbd.dwReserved = 0;
|
|
dscbd.lpwfxFormat = &wfx;
|
|
GlobGuardIn();
|
|
hr = g_lpdscFullDuplexCapture->CreateCaptureBuffer(&dscbd, &g_lpdscbFullDuplexCapture, NULL);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
// try the next format
|
|
++dwIndex;
|
|
continue;
|
|
}
|
|
|
|
// setup the notifier on the capture buffer
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Querying for IDirectSoundNotify");
|
|
GlobGuardIn();
|
|
hr = g_lpdscbFullDuplexCapture->QueryInterface(
|
|
IID_IDirectSoundNotify,
|
|
(LPVOID*)&g_lpdsnFullDuplexCapture);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
// Once the above works, this should not fail, so treat
|
|
// this as a real error
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "QueryInterface(IID_DirectSoundNotify) failed, code: %i", HRESULT_CODE(hr));
|
|
GlobGuardIn();
|
|
g_lpdscbFullDuplexCapture->Release();
|
|
GlobGuardOut();
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "Creating Notification Event");
|
|
GlobGuardIn();
|
|
g_hFullDuplexCaptureEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
hFullDuplexCaptureEvent = g_hFullDuplexCaptureEvent;
|
|
GlobGuardOut();
|
|
if (hFullDuplexCaptureEvent == NULL)
|
|
{
|
|
// Once the above works, this should not fail, so treat
|
|
// this as a real error
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "CreateEvent failed, code: %i", HRESULT_CODE(hr));
|
|
GlobGuardIn();
|
|
g_lpdscbFullDuplexCapture->Release();
|
|
g_lpdsnFullDuplexCapture->Release();
|
|
GlobGuardOut();
|
|
DPF_EXIT();
|
|
return DVERR_WIN32;
|
|
}
|
|
|
|
DPFX(DPFPREP, DVF_INFOLEVEL, "calling SetNotificationPositions");
|
|
dsbPositionNotify.dwOffset = 0;
|
|
dsbPositionNotify.hEventNotify = hFullDuplexCaptureEvent;
|
|
GlobGuardIn();
|
|
hr = g_lpdsnFullDuplexCapture->SetNotificationPositions(1, &dsbPositionNotify);
|
|
GlobGuardOut();
|
|
if (FAILED(hr))
|
|
{
|
|
// Once the above works, this should not fail, so treat
|
|
// this as a real error
|
|
DPFX(DPFPREP, DVF_ERRORLEVEL, "SetNotificationPositions failed, code: %i", HRESULT_CODE(hr));
|
|
GlobGuardIn();
|
|
g_lpdscbFullDuplexCapture->Release();
|
|
g_lpdsnFullDuplexCapture->Release();
|
|
CloseHandle(hFullDuplexCaptureEvent);
|
|
GlobGuardOut();
|
|
DPF_EXIT();
|
|
return hr;
|
|
}
|
|
|
|
// start the capture buffer and confirm that it is actually working
|
|
GlobGuardIn();
|
|
lpdscb = g_lpdscbFullDuplexCapture;
|
|
hEvent = g_hFullDuplexCaptureEvent;
|
|
GlobGuardOut();
|
|
hr = PlayAndCheckCapture(lpdscb, hEvent);
|
|
if (FAILED(hr))
|
|
{
|
|
// This can happen, so just try the next format
|
|
DPFX(DPFPREP, DVF_WARNINGLEVEL, "Capture verification test failed, code: %i", HRESULT_CODE(hr));
|
|
GlobGuardIn();
|
|
g_lpdscbFullDuplexCapture->Release();
|
|
g_lpdsnFullDuplexCapture->Release();
|
|
CloseHandle(hFullDuplexCaptureEvent);
|
|
GlobGuardOut();
|
|
++dwIndex;
|
|
continue;
|
|
}
|
|
|
|
// If we get here, capture is up and running, so return success!
|
|
DPF_EXIT();
|
|
return S_OK;
|
|
}
|
|
// if we get here, none of the formats worked, so return directsound error
|
|
DPF_EXIT();
|
|
return DVERR_SOUNDINITFAILURE;
|
|
}
|
|
*/
|