AecKsBinder.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. //-------------------------------------------------------------------------
  2. // File: AecKsBinder.cpp
  3. //
  4. // Desciption: Definition of audio devices binding functions
  5. //
  6. // Copyright (c) 2004-2006, Microsoft Corporation. All rights reserved.
  7. //---------------------------------------------------------------------------
  8. #include "stdafx.h"
  9. #include "AecKsbinder.h"
  10. #include "strsafe.h"
  11. #include "functiondiscoverykeys.h" // PKEY_Device_FriendlyName
  12. #ifndef IF_FAILED_JUMP
  13. #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
  14. #endif
  15. #ifndef IF_FAILED_RETURN
  16. #define IF_FAILED_RETURN(hr) if(FAILED(hr)) return hr;
  17. #endif
  18. ///////////////////////////////////////////////////////////////////////////
  19. //
  20. // Function:
  21. // DeviceBindTo
  22. //
  23. // Description:
  24. // Bind device to an IAudioClient interface.
  25. //
  26. // Parameters:
  27. // eDataFlow: eRender for render device, eCapture for capture device
  28. // iDevIdx: Index of device in the enumeration list. If it is -1, use default device
  29. // ppVoid: pointer pointer to IAudioClient interface.
  30. // ppszEndpointDeviceId: Device ID. Caller is responsible for freeing memeory
  31. // using CoTaskMemoryFree. If can be NULL if called doesn't need this info.
  32. //
  33. // Return:
  34. // S_OK if successful
  35. //
  36. ///////////////////////////////////////////////////////////////////////////////
  37. HRESULT DeviceBindTo(
  38. EDataFlow eDataFlow, // eCapture/eRender
  39. INT iDevIdx, // Device Index. -1 - default device.
  40. IAudioClient **ppAudioClient, // pointer pointer to IAudioClient interface
  41. IAudioEndpointVolume **ppEndpointVolume,
  42. WCHAR** ppszEndpointDeviceId) // Device ID. Need to be freed in caller with CoTaskMemoryFree if it is not NULL
  43. {
  44. HRESULT hResult;
  45. CComPtr<IMMDeviceEnumerator> spEnumerator;
  46. CComPtr<IMMDeviceCollection> spEndpoints;
  47. CComPtr<IMMDevice> spDevice;
  48. WCHAR *pszDeviceId = NULL;
  49. if (ppAudioClient == NULL)
  50. return E_POINTER;
  51. *ppAudioClient = NULL;
  52. hResult = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
  53. IF_FAILED_JUMP(hResult, Exit);
  54. // use default device
  55. if (iDevIdx < 0 )
  56. {
  57. hResult = spEnumerator->GetDefaultAudioEndpoint(eDataFlow, eConsole, &spDevice);
  58. IF_FAILED_JUMP(hResult, Exit);
  59. }else{
  60. // User selected device
  61. hResult = spEnumerator->EnumAudioEndpoints(eDataFlow, DEVICE_STATE_ACTIVE, &spEndpoints);
  62. IF_FAILED_JUMP(hResult, Exit);
  63. hResult = spEndpoints->Item(iDevIdx, &spDevice);
  64. IF_FAILED_JUMP(hResult, Exit);
  65. }
  66. // get device ID and format
  67. hResult = spDevice->GetId(&pszDeviceId);
  68. IF_FAILED_JUMP(hResult, Exit);
  69. // Active device
  70. hResult = spDevice->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)ppAudioClient);
  71. IF_FAILED_JUMP(hResult, Exit);
  72. if (ppEndpointVolume)
  73. {
  74. hResult = spDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (void **)ppEndpointVolume);
  75. IF_FAILED_JUMP(hResult, Exit);
  76. }
  77. Exit:
  78. if (ppszEndpointDeviceId)
  79. *ppszEndpointDeviceId = pszDeviceId;
  80. else if (pszDeviceId)
  81. CoTaskMemFree(pszDeviceId);
  82. return hResult;
  83. }
  84. ///////////////////////////////////////////////////////////////////////////////
  85. //
  86. // Function:
  87. // GetDeviceNum
  88. //
  89. // Description:
  90. // Enumerate audio device and return the device information.
  91. //
  92. // Parameters:
  93. // eDataFlow: eRender for render device, eCapture for capture device
  94. // uDevCount: Number of device
  95. //
  96. // Return:
  97. // S_OK if successful
  98. //
  99. ///////////////////////////////////////////////////////////////////////////////
  100. HRESULT GetDeviceNum(EDataFlow eDataFlow, UINT &uDevCount)
  101. {
  102. HRESULT hResult = S_OK;
  103. CComPtr<IMMDeviceEnumerator> spEnumerator;
  104. CComPtr<IMMDeviceCollection> spEndpoints;
  105. hResult = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
  106. IF_FAILED_RETURN(hResult);
  107. hResult = spEnumerator->EnumAudioEndpoints(eDataFlow, DEVICE_STATE_ACTIVE, &spEndpoints);
  108. IF_FAILED_RETURN(hResult);
  109. hResult = spEndpoints->GetCount(&uDevCount);
  110. IF_FAILED_RETURN(hResult);
  111. return hResult;
  112. }
  113. ///////////////////////////////////////////////////////////////////////////
  114. //
  115. // Function:
  116. // EnumDevice
  117. //
  118. // Description:
  119. // Enumerate audio device and return the device information.
  120. //
  121. // Parameters:
  122. // eDataFlow: eRender for render device, eCapture for capture device
  123. // uNumElements: Size of audio device info structure array.
  124. // pDevicInfo: device info structure array. Caller is responsible to allocate and free
  125. // memory. The array size is specified by uNumElements.
  126. //
  127. // Return:
  128. // S_OK if successful
  129. //
  130. ///////////////////////////////////////////////////////////////////////////////
  131. HRESULT EnumDevice(EDataFlow eDataFlow, UINT uNumElements, AUDIO_DEVICE_INFO *pDevicInfo)
  132. {
  133. HRESULT hResult = S_OK;
  134. TCHAR* pszDeviceId = NULL;
  135. PROPVARIANT value;
  136. UINT index, dwCount;
  137. bool IsMicArrayDevice;
  138. CComPtr<IMMDeviceEnumerator> spEnumerator;
  139. CComPtr<IMMDeviceCollection> spEndpoints;
  140. hResult = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
  141. IF_FAILED_JUMP(hResult, Exit);
  142. hResult = spEnumerator->EnumAudioEndpoints(eDataFlow, DEVICE_STATE_ACTIVE, &spEndpoints);
  143. IF_FAILED_JUMP(hResult, Exit);
  144. hResult = spEndpoints->GetCount(&dwCount);
  145. IF_FAILED_JUMP(hResult, Exit);
  146. if (dwCount != uNumElements)
  147. return E_INVALIDARG;
  148. ZeroMemory(pDevicInfo, sizeof(AUDIO_DEVICE_INFO)*uNumElements);
  149. for (index = 0; index < dwCount; index++)
  150. {
  151. CComPtr<IMMDevice> spDevice;
  152. CComPtr<IPropertyStore> spProperties;
  153. PropVariantInit(&value);
  154. hResult = spEndpoints->Item(index, &spDevice);
  155. IF_FAILED_JUMP(hResult, Exit);
  156. hResult = spDevice->GetId(&pszDeviceId);
  157. IF_FAILED_JUMP(hResult, Exit);
  158. hResult = spDevice->OpenPropertyStore(STGM_READ, &spProperties);
  159. IF_FAILED_JUMP(hResult, Exit);
  160. hResult = spProperties->GetValue(PKEY_Device_FriendlyName, &value);
  161. IF_FAILED_JUMP(hResult, Exit);
  162. EndpointIsMicArray(spDevice, IsMicArrayDevice);
  163. StringCchCopy(pDevicInfo[index].szDeviceID, MAX_STR_LEN-1, pszDeviceId);
  164. StringCchCopy(pDevicInfo[index].szDeviceName, MAX_STR_LEN-1, value.pszVal);
  165. pDevicInfo[index].bIsMicArrayDevice = IsMicArrayDevice;
  166. PropVariantClear(&value);
  167. CoTaskMemFree(pszDeviceId);
  168. pszDeviceId = NULL;
  169. }
  170. Exit:
  171. return hResult;
  172. }
  173. ///////////////////////////////////////////////////////////////////////////////
  174. // Function:
  175. // DeviceIsMicArray
  176. //
  177. // Description:
  178. // Determines if a given IMMDevice is a microphone array by device ID
  179. //
  180. // Returns:
  181. // S_OK on success
  182. ///////////////////////////////////////////////////////////////////////////////
  183. HRESULT DeviceIsMicArray(wchar_t szDeviceId[], bool &bIsMicArray)
  184. {
  185. HRESULT hr = S_OK;
  186. if (szDeviceId == NULL)
  187. return E_INVALIDARG;
  188. CComPtr<IMMDeviceEnumerator> spEnumerator;
  189. CComPtr<IMMDevice> spDevice;
  190. hr = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
  191. IF_FAILED_RETURN(hr);
  192. hr = spEnumerator->GetDevice(szDeviceId, &spDevice);
  193. IF_FAILED_RETURN(hr);
  194. hr = EndpointIsMicArray(spDevice, bIsMicArray);
  195. return hr;
  196. }
  197. ///////////////////////////////////////////////////////////////////////////////
  198. // Function:
  199. // EndpointIsMicArray
  200. //
  201. // Description:
  202. // Determines if a given IMMDevice is a microphone array by Endpoint pointer
  203. //
  204. // Returns:
  205. // S_OK on success
  206. ///////////////////////////////////////////////////////////////////////////////
  207. HRESULT EndpointIsMicArray(IMMDevice* pEndpoint, bool & isMicrophoneArray)
  208. {
  209. if (pEndpoint == NULL)
  210. return E_POINTER;
  211. GUID subType = {0};
  212. HRESULT hr = GetJackSubtypeForEndpoint(pEndpoint, &subType);
  213. isMicrophoneArray = (subType == KSNODETYPE_MICROPHONE_ARRAY) ? true : false;
  214. return hr;
  215. }// EndpointIsMicArray()
  216. ///////////////////////////////////////////////////////////////////////////////
  217. // Function:
  218. // GetJackSubtypeForEndpoint
  219. //
  220. // Description:
  221. // Gets the subtype of the jack that the specified endpoint device
  222. // is plugged into. e.g. if the endpoint is for an array mic, then
  223. // we would expect the subtype of the jack to be
  224. // KSNODETYPE_MICROPHONE_ARRAY
  225. //
  226. // Return:
  227. // S_OK if successful
  228. //
  229. ///////////////////////////////////////////////////////////////////////////////
  230. HRESULT GetJackSubtypeForEndpoint(IMMDevice* pEndpoint, GUID* pgSubtype)
  231. {
  232. HRESULT hr = S_OK;
  233. if (pEndpoint == NULL)
  234. return E_POINTER;
  235. CComPtr<IDeviceTopology> spEndpointTopology;
  236. CComPtr<IConnector> spPlug;
  237. CComPtr<IConnector> spJack;
  238. CComQIPtr<IPart> spJackAsPart;
  239. // Get the Device Topology interface
  240. hr = pEndpoint->Activate(__uuidof(IDeviceTopology), CLSCTX_INPROC_SERVER,
  241. NULL, (void**)&spEndpointTopology);
  242. IF_FAILED_JUMP(hr, Exit);
  243. hr = spEndpointTopology->GetConnector(0, &spPlug);
  244. IF_FAILED_JUMP(hr, Exit);
  245. hr = spPlug->GetConnectedTo(&spJack);
  246. IF_FAILED_JUMP(hr, Exit);
  247. spJackAsPart = spJack;
  248. hr = spJackAsPart->GetSubType(pgSubtype);
  249. Exit:
  250. return hr;
  251. }//GetJackSubtypeForEndpoint()
  252. ///////////////////////////////////////////////////////////////////////////////
  253. // GetInputJack() -- Gets the IPart interface for the input jack on the
  254. // specified device.
  255. ///////////////////////////////////////////////////////////////////////////////
  256. HRESULT GetInputJack(IMMDevice* pDevice, CComPtr<IPart>& spPart)
  257. {
  258. HRESULT hr = S_OK;
  259. if (pDevice == NULL)
  260. return E_POINTER;
  261. CComPtr<IDeviceTopology> spTopology;
  262. CComPtr<IConnector> spPlug;
  263. CComPtr<IConnector> spJack = NULL;
  264. // Get the Device Topology interface
  265. hr = pDevice->Activate(__uuidof(IDeviceTopology),
  266. CLSCTX_INPROC_SERVER, NULL,
  267. reinterpret_cast<void**>(&spTopology));
  268. IF_FAILED_RETURN(hr);
  269. hr = spTopology->GetConnector(0, &spPlug);
  270. IF_FAILED_RETURN(hr);
  271. hr = spPlug->GetConnectedTo(&spJack);
  272. IF_FAILED_RETURN(hr);
  273. // QI for the part
  274. spPart = spJack;
  275. if (spPart == NULL)
  276. return E_NOINTERFACE;
  277. return hr;
  278. }// GetInputJack()
  279. ///////////////////////////////////////////////////////////////////////////////
  280. // Function:
  281. // GetMicArrayGeometry()
  282. //
  283. // Description:
  284. // Obtains the geometry for the specified mic array.
  285. //
  286. // Parameters: szDeviceId -- The requested device ID, which can be obtained
  287. // from calling EnumAudioCaptureDevices()
  288. //
  289. // ppGeometry -- Address of the pointer to the mic-array gemometry.
  290. // Caller is ressponsible for calling CoTaskMemFree()
  291. // if the call is successfull.
  292. //
  293. // cbSize -- size of the geometry structure
  294. //
  295. // Returns: S_OK on success
  296. ///////////////////////////////////////////////////////////////////////////////
  297. HRESULT GetMicArrayGeometry(wchar_t szDeviceId[], KSAUDIO_MIC_ARRAY_GEOMETRY** ppGeometry, ULONG& cbSize)
  298. {
  299. HRESULT hr = S_OK;
  300. if (szDeviceId == NULL)
  301. return E_INVALIDARG;
  302. if (ppGeometry == NULL)
  303. return E_POINTER;
  304. cbSize = 0;
  305. CComPtr<IMMDeviceEnumerator> spEnumerator;
  306. CComPtr<IMMDevice> spDevice;
  307. CComQIPtr<IPart> spPart;
  308. bool bIsMicArray;
  309. hr = spEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator));
  310. IF_FAILED_RETURN(hr);
  311. hr = spEnumerator->GetDevice(szDeviceId, &spDevice);
  312. IF_FAILED_RETURN(hr);
  313. hr = EndpointIsMicArray(spDevice, bIsMicArray);
  314. IF_FAILED_RETURN(hr);
  315. if (!bIsMicArray)
  316. return E_FAIL;
  317. UINT nPartId = 0;
  318. hr = GetInputJack(spDevice, spPart);
  319. IF_FAILED_RETURN(hr);
  320. hr = spPart->GetLocalId(&nPartId);
  321. IF_FAILED_RETURN(hr);
  322. CComPtr<IDeviceTopology> spTopology;
  323. CComPtr<IMMDeviceEnumerator> spEnum;
  324. CComPtr<IMMDevice> spJackDevice;
  325. CComPtr<IKsControl> spKsControl;
  326. wchar_t * pwstrDevice = 0;
  327. // Get the topology object for the part
  328. hr = spPart->GetTopologyObject(&spTopology);
  329. IF_FAILED_RETURN(hr);
  330. // Get the id of the IMMDevice that this topology object describes.
  331. hr = spTopology->GetDeviceId(&pwstrDevice);
  332. IF_FAILED_RETURN(hr);
  333. // Get an IMMDevice pointer using the ID
  334. hr = spEnum.CoCreateInstance(__uuidof(MMDeviceEnumerator));
  335. IF_FAILED_JUMP(hr, Exit);
  336. hr = spEnum->GetDevice(pwstrDevice, &spJackDevice);
  337. IF_FAILED_JUMP(hr, Exit);
  338. // Activate IKsControl on the IMMDevice
  339. hr = spJackDevice->Activate(__uuidof(IKsControl), CLSCTX_INPROC_SERVER,
  340. NULL, reinterpret_cast<void**>(&spKsControl));
  341. IF_FAILED_JUMP(hr, Exit);
  342. // At this point we can use IKsControl just as we would use DeviceIoControl
  343. KSP_PIN ksp;
  344. ULONG cbData = 0;
  345. ULONG cbGeometry = 0;
  346. // Inititialize the pin property
  347. ::ZeroMemory(&ksp, sizeof(ksp));
  348. ksp.Property.Set = KSPROPSETID_Audio;
  349. ksp.Property.Id = KSPROPERTY_AUDIO_MIC_ARRAY_GEOMETRY;
  350. ksp.Property.Flags = KSPROPERTY_TYPE_GET;
  351. ksp.PinId = nPartId & PARTID_MASK;
  352. // Get data size by passing NULL
  353. hr = spKsControl->KsProperty(reinterpret_cast<PKSPROPERTY>(&ksp),
  354. sizeof(ksp), NULL, 0, &cbGeometry);
  355. IF_FAILED_JUMP(hr, Exit);
  356. // Allocate memory for the microphone array geometry
  357. *ppGeometry = reinterpret_cast<KSAUDIO_MIC_ARRAY_GEOMETRY*>
  358. (::CoTaskMemAlloc(cbGeometry));
  359. if(*ppGeometry == 0)
  360. {
  361. hr = E_OUTOFMEMORY;
  362. }
  363. IF_FAILED_JUMP(hr, Exit);
  364. // Now retriev the mic-array structure...
  365. DWORD cbOut = 0;
  366. hr = spKsControl->KsProperty(reinterpret_cast<PKSPROPERTY>(&ksp),
  367. sizeof(ksp), *ppGeometry, cbGeometry,
  368. &cbOut);
  369. IF_FAILED_JUMP(hr, Exit);
  370. cbSize = cbGeometry;
  371. Exit:
  372. if(pwstrDevice != 0)
  373. {
  374. ::CoTaskMemFree(pwstrDevice);
  375. }
  376. return hr;
  377. }//GetMicArrayGeometry()