Hi All, We have a USB based Speaker and microphone device which already supports Windows XP. We have our own driver for AEC for XP. Now we need to support the device for Windows Vista. We initially tried to use Sysfx APOs and that did not work out since the support team at Microsoft itself were not clear as to how it was implemented and how we need to make use of it. We waited for the SP1 for Vista hoping that it would fix a lot of issues around audio related stuff. Meanwhile we have started to explore using Microsoft built in AEC for our device. We have implemented the AECMicArray example from the Microsoft SDK in Source mode. But the audio quality does not seem to be upto the mark. So, we need help on 2 things to improve this: 1 - See what are the tunable parameters in the Source Mode. 2 - Implement the Filter Mode AEC. For the first issue, i browsed the Microsoft forums for help and the conclusion is we need to use the AecQualityMetrics_Struct structure. But i am not able to print out the parameters. The piece of code for this is : PROPVARIANT pvAECStats; PropVariantInit(&pvAECStats); pvAECStats.vt = VT_BLOB; //pvAECStats.lVal=1; CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_RETRIEVE_TS_STATS, pvAECStats)); CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_RETRIEVE_TS_STATS, &pvAECStats)); #if 0 AecQualityMetrics_Struct sAEC; CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_RETRIEVE_TS_STATS, (PROPVARIANT &)sAEC)); CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_RETRIEVE_TS_STATS, (PROPVARIANT *)&sAEC)); //MFPKEY_WMAAECMA_QUALITY_METRICS. TODO : have to see how to use this :( printf("%20s %5d \n", "ConvergenceFlag is ", sAEC.ConvergenceFlag); printf("%20s %5d \n", "MicClippedFlag is ", sAEC.MicClippedFlag); printf("%20s %5d \n", "MicSilenceFlag is ", sAEC.MicSilenceFlag); printf("%20s %5d \n", "PstvFeadbackFlag is ", sAEC.PstvFeadbackFlag); printf("%20s %5d \n", "SpkClippedFlag is ", sAEC.SpkClippedFlag); printf("%20s %5d \n", "SpkMuteFlag is ", sAEC.SpkMuteFlag); printf("%20s %5d \n", "GlitchFlag is ", sAEC.GlitchFlag); printf("%20s %5d \n", "DoubleTalkFlag is ", sAEC.DoubleTalkFlag); printf("%20s %5f \n", "fDuration is ", sAEC.fDuration); printf("%20s %5f \n", "fTSVariance is ", sAEC.fTSVariance); printf("%20s %5f \n", "fTSDriftRate is ", sAEC.fTSDriftRate); printf("%20s %5f \n", "fVoiceLevel is ", sAEC.fVoiceLevel); printf("%20s %5f \n", "fNoiseLevel is ", sAEC.fNoiseLevel); printf("%20s %5f \n", "fERLE is ", sAEC.fERLE); printf("%20s %5f \n", "fAvgERLE is ", sAEC.fAvgERLE); PropVariantClear(&pvAECStats); #endif This code prints out 0 for all the values during recording to PCM file. Can anyone please correct me in the code if i am going wrong anywhere??? 2 - Coming to the Filter mode, the AECMicArray code was modified as follows: #include <windows.h> #include <dmo.h> #include <Mmsystem.h> #include <objbase.h> #include <mediaobj.h> #include <uuids.h> #include <propidl.h> #include <wmcodecdsp.h> #include <atlbase.h> #include <ATLComCli.h> #include <audioclient.h> #include <MMDeviceApi.h> #include <AudioEngineEndPoint.h> #include <DeviceTopology.h> #include <propkey.h> #include <strsafe.h> #include <conio.h> #include <Dmodshow.h> // added by Venkat.R.N. #include <DshowUtil.h> // added by Venkat.R.N. #include <AudioClient.h> // added by Venkat.R.N. #include <FunctionDiscoveryKeys_devpkey.h> // added by Venkat.R.N. #include "mediabuf.h" #include "AecKsBinder.h" void OutputUsage(); //HRESULT Stream( BYTE **ppbOutData, ULONG *pbDataSize, LPWAVEFORMATEX *ppwfx ); #define SAFE_ARRAYDELETE(p) {if (p) delete[] (p); (p) = NULL;} #define SAFE_RELEASE(p) {if (NULL != p) {(p)->Release(); (p) = NULL;}} #define VBFALSE (VARIANT_BOOL)0 #define VBTRUE (VARIANT_BOOL)-1 #define STREAM_BUFFER_LENGTH 0.1f //streaming buffer is 0.1 second long. #define CHECK_RET(hr, message) if (FAILED(hr)) { puts(message); goto exit;} #define CHECKHR(x) hr = x; if (FAILED(hr)) {printf("%d: %08X\n", __LINE__, hr); goto exit;} #define CHECK_ALLOC(pb, message) if (NULL == pb) { puts(message); goto exit;} class CStaticMediaBuffer : public CBaseMediaBuffer { public: STDMETHODIMP_(ULONG) AddRef() {return 2;} STDMETHODIMP_(ULONG) Release() {return 1;} void Init(BYTE *pData, ULONG ulSize, ULONG ulData) { m_pData = pData; m_ulSize = ulSize; m_ulData = ulData; } }; int __cdecl _tmain(int argc, const TCHAR ** argv) { HRESULT hr = S_OK; CoInitialize(NULL); ULONG cbProduced = 0; DWORD dwStatus; IMediaObject* m_pObject = NULL; IPropertyStore* pPS = NULL; IMediaObjectInPlace *m_pObjectInPlace = NULL; FILE * pfMicOutPCM; // dump output signal using PCM format DWORD cOutputBufLen = 0; BYTE *pbOutputBuffer = NULL; //LPWAVEFORMATEX m_pwfx; // pointer to input/output waveformatex structure. DMO_MEDIA_TYPE m_mt = {0}; DMO_MEDIA_TYPE m_mt1 = {0}; WAVEFORMATEX wfxIn = {WAVE_FORMAT_PCM, 1, 16000, 32000, 2, 16, 0}; CStaticMediaBuffer outputBuffer; DMO_OUTPUT_DATA_BUFFER OutputBufferStruct = {0}; OutputBufferStruct.pBuffer = &outputBuffer; DMO_MEDIA_TYPE inputmt = {0}; // added by Venkat.R.N. // Parameters to config DMO int iDuration = 60; // seconds int iSystemMode = MODE_NOT_SET; // AEC-MicArray DMO system mode int iOutFileIdx = -1; // argument index for otuput file name int iMicDevIdx = -2; // microphone device index int iSpkDevIdx = -2; // speaker device index BOOL bFeatrModeOn = 0; // turn feature mode on/off BOOL bNoiseSup = 1; // turn noise suppression on/off BOOL bAGC = 0; // turn digital auto gain control on/off BOOL bCntrClip = 0; // turn center clippng on/off // control how long the Demo runs //int iDuration = 60; // seconds int cTtlToGo = 0; AUDIO_DEVICE_INFO *pCaptureDeviceInfo = NULL, *pRenderDeviceInfo = NULL; int i; for ( i = 1; i < argc-1; i++ ) { if (argv[i][0] == '-') { if ( !_tcscmp (_T("-micdev"), argv[i])) { // microphone device index. The valid range is -1, 0~N-1 // where N is the number of capture device. Use -1 if // you want to use the default device. iMicDevIdx = _ttoi ( argv[i+1] ); i++; } else if ( !_tcscmp (_T("-spkdev"), argv[i])) { // speaker device index. The valid values are -1, 0~N-1 // where N is the number of capture device. Use -1 if // you want to use the default device. iSpkDevIdx = _ttoi ( argv[i+1] ); i++; } else if ( !_tcscmp (_T("-out"), argv[i])) { // output file name iOutFileIdx = i + 1; i++; } else if ( !_tcscmp (_T("-mod"), argv[i])) { // AEC-MicArray system mode. The valid modes are // SINGLE_CHANNEL_AEC = 0 // OPTIBEAM_ARRAY_ONLY = 2 // OPTIBEAM_ARRAY_AND_AEC = 4 // SINGLE_CHANNEL_NSAGC = 5 // // Mode 1 and 3 are reserved for future features. iSystemMode = _ttoi ( argv[i+1] ); i++; } else if ( !_tcscmp (_T("-feat"), argv[i])) { // turn feature mode on/off. The valid values are 0 or 1 // The feature mode must be turned on in order to config // noise suppression, AGC, centerclip, and other AEC features. bFeatrModeOn = _ttoi ( argv[i+1] ); i++; } else if ( !_tcscmp (_T("-ns"), argv[i])) { // turn noise suppression on/off. The valid values are 0 or 1 // Feature mode must be on in order to set noise suppression bNoiseSup = _ttoi ( argv[i+1] ); i++; } else if ( !_tcscmp (_T("-agc"), argv[i])) { // turn digital AGC on/off. The valid values are 0 or 1 // Feature mode must be on in order to set digital AGC bAGC = _ttoi ( argv[i+1] ); i++; } else if ( !_tcscmp (_T("-cntrclip"), argv[i])) { // turn center clipping on/off. The valid values are 0 or 1 // Center clipping is an post process to remove small echo residuals // which are not completely cancelled. Comfort noise with a same level // of background noise will be filled after the removal. // Feature mode must be on in order to set center clipping bCntrClip = (BOOL) _ttoi ( argv[i+1] ); i++; } else if ( !_tcscmp (_T("-duration"), argv[i])) { // control program running duration in seconds. The default // value is 60 seconds. iDuration = _ttoi ( argv[i+1] ); i++; } else { OutputUsage(); goto exit; } } } HANDLE currThread; HANDLE currProcess; BOOL iRet; currProcess = GetCurrentProcess (); currThread = GetCurrentThread (); iRet = SetPriorityClass (currProcess, HIGH_PRIORITY_CLASS); if ( 0 == iRet ) { // call getLastError. puts("failed to set process priority\n"); goto exit; } // create DMO hr = CoCreateInstance(CLSID_CWMAudioAEC, NULL, CLSCTX_INPROC, IID_IMediaObject, (void **) &m_pObject); CHECK_RET(hr,"CoCreateInstance() failed!!!!!!!!!"); //hr = m_pObject->QueryInterface(IID_IMediaObjectInPlace, (void**)&m_pObjectInPlace); CHECKHR(m_pObject->QueryInterface(IID_IPropertyStore, (void**)&pPS)); CHECK_RET(hr,"QueryInterface() failed!!!!!!!!!"); /////////////////////////////////// start of code for selecting device...////////////////////////////////// // Select capture device UINT uCapDevCount = 0; UINT uRenDevCount = 0; char pcScanBuf[256]= {0}; hr = GetCaptureDeviceNum(uCapDevCount); CHECK_RET(hr, "GetCaptureDeviceNum failed"); pCaptureDeviceInfo = new AUDIO_DEVICE_INFO[uCapDevCount]; hr = EnumCaptureDevice(uCapDevCount, pCaptureDeviceInfo); CHECK_RET(hr, "EnumCaptureDevice failed"); printf("\nSystem has totally %d capture devices\n", uCapDevCount); for (i=0; i<(int)uCapDevCount; i++) { _tprintf(_T("Device %d is %s"), i, pCaptureDeviceInfo[i].szDeviceName); if (pCaptureDeviceInfo[i].bIsMicArrayDevice) _tprintf(_T(" -- Mic Array Device \n")); else _tprintf(_T("\n")); } if (iMicDevIdx<-1 || iMicDevIdx>=(int)uCapDevCount) { do{ printf("Select device "); scanf_s("%255s", pcScanBuf, 255); iMicDevIdx = atoi(pcScanBuf); if (iMicDevIdx < -1 || iMicDevIdx >= (int)uCapDevCount) printf("Invalid Capture Device ID \n"); else break; }while(1); } if (iMicDevIdx == -1) _tprintf(_T("\n Default device will be used for capturing \n")); else _tprintf(_T("\n %s is selected for capturing\n"), pCaptureDeviceInfo [iMicDevIdx].szDeviceName); SAFE_ARRAYDELETE(pCaptureDeviceInfo); // Select render device if( iSystemMode == SINGLE_CHANNEL_AEC || iSystemMode == ADAPTIVE_ARRAY_AND_AEC || iSystemMode == OPTIBEAM_ARRAY_AND_AEC ) { hr = GetRenderDeviceNum(uRenDevCount); CHECK_RET(hr, "GetRenderDeviceNum failed"); pRenderDeviceInfo = new AUDIO_DEVICE_INFO[uRenDevCount]; hr = EnumRenderDevice(uRenDevCount, pRenderDeviceInfo); CHECK_RET(hr, "EnumRenderDevice failed"); printf("\nSystem has totally %d render devices\n", uRenDevCount); for (i=0; i<(int)uRenDevCount; i++) { _tprintf(_T("Device %d is %s \n"), i, pRenderDeviceInfo[i].szDeviceName); } if (iSpkDevIdx<-1 || iSpkDevIdx>=(int)uRenDevCount) { do{ printf("Select device "); scanf_s("%255s", pcScanBuf, 255); iSpkDevIdx = atoi(pcScanBuf); if (iSpkDevIdx < -1 || iSpkDevIdx >= (int)uRenDevCount) printf("Invalid Render Device ID \n"); else break; }while(1); } if (iSpkDevIdx == -1) _tprintf(_T("\n Default device will be used for rendering \n")); else _tprintf(_T("\n %s is selected for rendering \n"), pRenderDeviceInfo [iSpkDevIdx].szDeviceName); }else{ iSpkDevIdx = -1; } SAFE_ARRAYDELETE(pRenderDeviceInfo); // --- PREPARE OUTPUT --- // if (NULL != _tfopen_s(&pfMicOutPCM, argv[iOutFileIdx], _T("wb"))) { puts("cannot open file for output.\n"); goto exit; } m_mt.majortype = MEDIATYPE_Audio; m_mt.subtype = MEDIASUBTYPE_PCM; m_mt.formattype = FORMAT_WaveFormatEx; m_mt.cbFormat = sizeof(WAVEFORMATEX); m_mt.pbFormat = (BYTE*) &wfxIn; m_mt.pUnk = NULL; // CopyMediaType will crash if we don't intialize this hr = MoInitMediaType(&m_mt, sizeof(WAVEFORMATEX)); CHECK_RET(hr, "MoInitMediaType(m_mt) failed"); memcpy(m_mt.pbFormat, &wfxIn, sizeof(WAVEFORMATEX)); /* hr = m_pObject->SetInputType( 0, //Input Stream index &m_mt, 0 ); */ // No flags specified //CHECK_RET(hr,"SetInputType() failed!!!!!!!!!"); m_mt1.majortype = MEDIATYPE_Audio; m_mt1.subtype = MEDIASUBTYPE_PCM; m_mt1.formattype = FORMAT_WaveFormatEx; m_mt1.cbFormat = sizeof(WAVEFORMATEX); m_mt1.pbFormat = (BYTE*) &wfxIn; m_mt1.pUnk = NULL; // CopyMediaType will crash if we don't intialize this hr = MoInitMediaType(&m_mt1, sizeof(WAVEFORMATEX)); CHECK_RET(hr, "MoInitMediaType(m_mt1) failed"); memcpy(m_mt1.pbFormat, &wfxIn, sizeof(WAVEFORMATEX)); hr = m_pObject->SetOutputType( 0, // Output Stream Index &m_mt1, 0); // No flags specified CHECK_RET(hr,"SetOutputType() failed!!!!!!!!!"); hr = m_pObject->AllocateStreamingResources(); CHECK_RET(hr, "AllocateStreamingResources failed"); /////////////////set feature mode///////////////////// PROPVARIANT pvFeatrModeOn; PropVariantInit(&pvFeatrModeOn); pvFeatrModeOn.vt = VT_BOOL; pvFeatrModeOn.boolVal = VBTRUE; pPS->SetValue(MFPKEY_WMAAECMA_FEATURE_MODE, pvFeatrModeOn); pPS->GetValue(MFPKEY_WMAAECMA_FEATURE_MODE, &pvFeatrModeOn); PropVariantClear(&pvFeatrModeOn); // critical feature mode uncomment working!!! :) /////////////////set system mode////////////////////// LONG system_mode = SINGLE_CHANNEL_AEC; // changed OPTIBEAM_ARRAY_ONLY to SINGLE_CHANNEL_AEC... // AllocateStreamingResources() failed for OPTIBEAM_ARRAY_ONLY!!!! PROPVARIANT pvSysMode; PropVariantInit(&pvSysMode); pvSysMode.vt = VT_I4; pvSysMode.lVal = (LONG)(system_mode); hr = pPS->SetValue(MFPKEY_WMAAECMA_SYSTEM_MODE, pvSysMode); hr =pPS->GetValue(MFPKEY_WMAAECMA_SYSTEM_MODE, &pvSysMode); PropVariantClear(&pvSysMode); ///////////////set filter mode//////////////////////// PROPVARIANT pvSystemMode; PropVariantInit(&pvSystemMode); pvSystemMode.vt = VT_BOOL; pvSystemMode.boolVal = VARIANT_FALSE; // comment by Venkat.R.N --> : VBFALSE tells that it is FILTER MODE!!! :) // VBTRUE is source mode and hence it is working... :( hr = pPS->SetValue(MFPKEY_WMAAECMA_DMO_SOURCE_MODE, pvSystemMode); hr = pPS->GetValue(MFPKEY_WMAAECMA_DMO_SOURCE_MODE, &pvSystemMode); PropVariantClear(&pvSystemMode); //very very critical comment!!! // AllocateStreamingResources() failed if SetInputType() is there... :( // ProcessOutput() failed if SetInputType() is commented... :( // allocate output buffer cOutputBufLen = wfxIn.nSamplesPerSec * wfxIn.nBlockAlign; pbOutputBuffer = new BYTE[cOutputBufLen]; CHECK_ALLOC (pbOutputBuffer, "out of memory.\n"); // number of frames to play cTtlToGo = iDuration * 100; // main loop to get mic output from the DMO puts("\nAEC-MicArray is running ... Press \"s\" to stop"); while (1) { Sleep(10); //sleep 10ms if (cTtlToGo--<=0) break; do{ outputBuffer.Init((byte*)pbOutputBuffer, cOutputBufLen, 0); OutputBufferStruct.dwStatus = 0; hr = m_pObject->ProcessOutput(0, 1, &OutputBufferStruct, &dwStatus); CHECK_RET (hr, "ProcessOutput failed"); if (hr == S_FALSE) { cbProduced = 0; } else { hr = outputBuffer.GetBufferAndLength(NULL, &cbProduced); CHECK_RET (hr, "GetBufferAndLength failed"); } //printf("%d \n", "cbProduced is ", cbProduced); // dump output data into a file with PCM format. if (fwrite(pbOutputBuffer, 1, cbProduced, pfMicOutPCM) != cbProduced) { puts("write error"); goto exit; } } while (OutputBufferStruct.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE); // check keyboard input to stop if (_kbhit()) { int ch = _getch(); if (ch == 's' || ch == 'S') break; } } exit: //SAFE_ARRAYDELETE(pbOutputBuffer); //SAFE_ARRAYDELETE(pCaptureDeviceInfo); //SAFE_ARRAYDELETE(pRenderDeviceInfo); SAFE_RELEASE(m_pObject); SAFE_RELEASE(pPS); CoUninitialize(); return hr; } void OutputUsage() { printf("MFWMAAEC (Aec-MicArray DMO) Demo. \n"); printf("Copyright (c) 2004-2006, Microsoft Corporation. All rights reserved. \n\n"); printf("Usage: AecSDKDemo.exe -out mic_out.pcm -mod 0 [-feat 1] [-ns 1] [-agc 0] \n"); printf(" [-cntrclip 0] [-micdev 0] [-spkdev 0] [-duration 60]\n"); return; } /* HRESULT Stream( BYTE **ppbOutData, ULONG *pbDataSize, LPWAVEFORMATEX *ppwfx ) { HRESULT hr = S_OK; BYTE *pOut; ULONG m_uDataSize; // Size of input data buffer. LPWAVEFORMATEX m_pwfx; // pointer to input/output waveformatex structure. BYTE *pbOutData=0; ULONG uDataSize =0 ; LPWAVEFORMATEX pwfx = NULL; // pointer to waveformatex structure. *pbDataSize = m_uDataSize; *ppwfx = m_pwfx; */ /* if ( m_pObjectInPlace ){ pOut = new BYTE [m_uDataSize]; if( pOut == 0 ){ return E_OUTOFMEMORY; } CopyMemory(pOut, m_pbInData, m_uDataSize); // pass the number of samples to Process() hr = m_pObjectInPlace->Process( m_uDataSize, pOut, 0, DMO_INPLACE_NORMAL); if( FAILED( hr ) ){ return hr; } *ppbOutData = pOut; SAFE_RELEASE( m_pObjectInPlace ); } */ //else //{ /* CMediaBuffer *pInputBuffer; const REFERENCE_TIME rtStart = 0; const REFERENCE_TIME rtStop = 0; BYTE* pBuffer; DWORD dwLength; // create and fill CMediaBuffer hr = CreateBuffer(m_uDataSize, &pInputBuffer); if( FAILED( hr ) ){ return hr; } hr = pInputBuffer->GetBufferAndLength( &pBuffer, &dwLength ); if( FAILED( hr ) ){ return hr; } CopyMemory(pBuffer, m_pbInData, m_uDataSize); hr = pInputBuffer->SetLength( m_uDataSize ); if( FAILED( hr ) ){ return hr; } // call processInput hr = m_pObject->ProcessInput( 0, pInputBuffer, DMO_INPUT_DATA_BUFFERF_SYNCPOINT, rtStart, rtStop - rtStart); if( FAILED( hr ) ){ return hr; } //release input buffer SAFE_RELEASE( pInputBuffer ); // retrieve the output data from DMO and put into pOut if(S_FALSE == hr){ return E_FAIL; } else { pOut = NULL; hr = ProcessOutputs( &pOut ); if( FAILED( hr ) ){ delete [] pOut; return hr; } } *ppbOutData = pOut; SAFE_RELEASE( m_pObject ); //} return S_OK; } */ On setting to Filter mode, the AllocateStreamingResources() fails. I am not able to go into the depth of problem. No help from any of the forums too. Can someone suggest what might be the possible causes for failure??? Thanks in advance.