AkMMDevice.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. /*******************************************************************************
  2. The content of this file includes portions of the AUDIOKINETIC Wwise Technology
  3. released in source code form as part of the SDK installer package.
  4. Commercial License Usage
  5. Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology
  6. may use this file in accordance with the end user license agreement provided
  7. with the software or, alternatively, in accordance with the terms contained in a
  8. written agreement between you and Audiokinetic Inc.
  9. Apache License Usage
  10. Alternatively, this file may be used under the Apache License, Version 2.0 (the
  11. "Apache License"); you may not use this file except in compliance with the
  12. Apache License. You may obtain a copy of the Apache License at
  13. http://www.apache.org/licenses/LICENSE-2.0.
  14. Unless required by applicable law or agreed to in writing, software distributed
  15. under the Apache License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
  16. OR CONDITIONS OF ANY KIND, either express or implied. See the Apache License for
  17. the specific language governing permissions and limitations under the License.
  18. Copyright (c) 2023 Audiokinetic Inc.
  19. *******************************************************************************/
  20. // AkMMDevice.h -- C++ RIAA object wrappers for Win32 MMDevice enumeration APIs
  21. #pragma once
  22. #include <AK/SoundEngine/Common/AkSoundEngine.h>
  23. #include <AK/Tools/Common/AkPlatformFuncs.h>
  24. // Fix for Windows SDK 10.0.17763.0
  25. #if !defined(_DEBUG)
  26. namespace Microsoft {
  27. namespace WRL {
  28. namespace Details {
  29. template <typename T>
  30. inline void CheckForDuplicateEntries() {}
  31. }
  32. }
  33. }
  34. #endif
  35. #include <mmdeviceapi.h>
  36. namespace AK
  37. {
  38. namespace Win32
  39. {
  40. class DeviceProperty
  41. {
  42. public:
  43. // Default constructor
  44. DeviceProperty()
  45. {
  46. PropVariantInit(&variant);
  47. }
  48. // Construct a property from an existing property store
  49. DeviceProperty(const PROPERTYKEY &key, IPropertyStore* in_pProps)
  50. {
  51. PropVariantInit(&variant);
  52. if (in_pProps) in_pProps->GetValue(key, &variant);
  53. }
  54. // Move constructor
  55. DeviceProperty(DeviceProperty&& other)
  56. {
  57. variant = other.variant;
  58. PropVariantInit(&other.variant);
  59. }
  60. // Copy construction is not allowed
  61. DeviceProperty(const DeviceProperty& other) = delete;
  62. // Destructor
  63. ~DeviceProperty()
  64. {
  65. PropVariantClear(&variant);
  66. }
  67. // Move assignment
  68. DeviceProperty& operator=(DeviceProperty&& other)
  69. {
  70. PropVariantClear(&variant);
  71. variant = other.variant;
  72. PropVariantInit(&other.variant);
  73. return *this;
  74. }
  75. // Copy assignment is not allowed
  76. DeviceProperty& operator=(const DeviceProperty& other) = delete;
  77. bool IsEmpty() const { return variant.vt == VT_EMPTY; }
  78. PROPVARIANT variant;
  79. };
  80. class DeviceProperties
  81. {
  82. public:
  83. // Default constructor
  84. DeviceProperties()
  85. : pProps(nullptr)
  86. {
  87. }
  88. // Construct properties from an IMMDevice
  89. DeviceProperties(IMMDevice* in_pDevice)
  90. {
  91. in_pDevice->OpenPropertyStore(STGM_READ, &pProps);
  92. }
  93. // Move constructor
  94. DeviceProperties(DeviceProperties&& other)
  95. {
  96. pProps = other.pProps;
  97. other.pProps = nullptr;
  98. }
  99. // Copy construction is not allowed
  100. DeviceProperties(const DeviceProperties& other) = delete;
  101. // Destructor
  102. ~DeviceProperties()
  103. {
  104. if (pProps)
  105. {
  106. pProps->Release();
  107. pProps = nullptr;
  108. }
  109. }
  110. // Move assignment
  111. DeviceProperties& operator=(DeviceProperties&& other)
  112. {
  113. if (pProps)
  114. pProps->Release();
  115. pProps = other.pProps;
  116. other.pProps = nullptr;
  117. return *this;
  118. }
  119. // Copy assignment is not allowed
  120. DeviceProperties& operator=(const DeviceProperties& other) = delete;
  121. bool IsValid() { return pProps != nullptr; }
  122. DeviceProperty GetProperty(const PROPERTYKEY& key)
  123. {
  124. return DeviceProperty(key, pProps);
  125. }
  126. IPropertyStore* pProps;
  127. };
  128. class Device
  129. {
  130. public:
  131. // Default constructor
  132. Device()
  133. : pDevice(nullptr)
  134. , idDevice(AK_INVALID_DEVICE_ID)
  135. {
  136. }
  137. // Move constructor
  138. Device(Device&& other)
  139. : pDevice(nullptr)
  140. {
  141. SetDevice(other.pDevice);
  142. other.pDevice = nullptr;
  143. }
  144. Device(const Device& other)
  145. : pDevice(nullptr)
  146. {
  147. *this = other;
  148. }
  149. // Destructor
  150. ~Device()
  151. {
  152. if (pDevice)
  153. {
  154. pDevice->Release();
  155. pDevice = nullptr;
  156. }
  157. }
  158. // Move assignment
  159. Device& operator=(Device&& other)
  160. {
  161. if (pDevice)
  162. {
  163. pDevice->Release();
  164. pDevice = nullptr; // Do not remove, protects against this == &other
  165. }
  166. pDevice = other.pDevice;
  167. idDevice = other.idDevice;
  168. other.pDevice = nullptr;
  169. return *this;
  170. }
  171. Device& operator=(const Device& other)
  172. {
  173. if (pDevice)
  174. {
  175. pDevice->Release();
  176. pDevice = nullptr; // Do not remove, protects against this == &other
  177. }
  178. pDevice = other.pDevice;
  179. if (pDevice)
  180. pDevice->AddRef();
  181. idDevice = other.idDevice;
  182. return *this;
  183. }
  184. Device& operator=(IMMDevice* pOther)
  185. {
  186. SetDevice(pOther);
  187. if (pDevice)
  188. pDevice->AddRef();
  189. return *this;
  190. }
  191. bool IsValid() const { return pDevice != nullptr; }
  192. AkUInt32 GetDeviceID() const
  193. {
  194. return pDevice ? idDevice : AK_INVALID_DEVICE_ID;
  195. }
  196. DeviceProperties GetProperties() const
  197. {
  198. return DeviceProperties(pDevice);
  199. }
  200. void ComputeId()
  201. {
  202. idDevice = AK_INVALID_DEVICE_ID;
  203. if (pDevice)
  204. {
  205. //Inlined version of GetDeviceID.
  206. LPWSTR pwszID = NULL;
  207. if (pDevice->GetId(&pwszID) == S_OK)
  208. {
  209. char szString[260];
  210. AKPLATFORM::AkWideCharToChar(pwszID, 260 - 1, szString);
  211. szString[260 - 1] = 0;
  212. idDevice = FNVHash32::ComputeLowerCase((const char*)szString);
  213. CoTaskMemFree(pwszID);
  214. }
  215. }
  216. }
  217. void SetDevice(IMMDevice* in_pNew)
  218. {
  219. if (pDevice)
  220. pDevice->Release();
  221. pDevice = in_pNew;
  222. ComputeId();
  223. }
  224. interface IMMDevice* pDevice;
  225. AkUInt32 idDevice;
  226. };
  227. class DeviceCollection
  228. {
  229. public:
  230. class Iterator
  231. {
  232. public:
  233. Iterator(IMMDeviceCollection* in_pDevices, UINT in_i)
  234. : pDevices(in_pDevices)
  235. , i(in_i)
  236. {
  237. }
  238. /// + operator
  239. Iterator operator+(AkUInt32 inc) const
  240. {
  241. Iterator returnedIt(pDevices, i + inc);
  242. return returnedIt;
  243. }
  244. /// - operator
  245. AkUInt32 operator-(Iterator const& rhs) const
  246. {
  247. return (AkUInt32)(i - rhs.i);
  248. }
  249. /// ++ operator
  250. Iterator& operator++()
  251. {
  252. ++i;
  253. return *this;
  254. }
  255. /// -- operator
  256. Iterator& operator--()
  257. {
  258. --i;
  259. return *this;
  260. }
  261. /// == operator
  262. bool operator ==(const Iterator& in_rOp) const
  263. {
  264. return (pDevices == in_rOp.pDevices && i == in_rOp.i);
  265. }
  266. /// != operator
  267. bool operator !=(const Iterator& in_rOp) const
  268. {
  269. return (pDevices != in_rOp.pDevices || i != in_rOp.i);
  270. }
  271. Device GetDevice()
  272. {
  273. Device pDevice;
  274. pDevices->Item(i, (IMMDevice**)&pDevice.pDevice); //Transfer the ref
  275. if (pDevice.pDevice)
  276. pDevice.ComputeId();
  277. return pDevice;
  278. }
  279. interface IMMDeviceCollection* pDevices; //Keep first.
  280. UINT i;
  281. };
  282. DeviceCollection(IMMDeviceEnumerator* pEnumerator)
  283. : pDevices(nullptr)
  284. , uCount(0)
  285. {
  286. pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &pDevices);
  287. if (pDevices)
  288. pDevices->GetCount(&uCount);
  289. }
  290. DeviceCollection(IMMDeviceEnumerator* pEnumerator, EDataFlow eFlow, DWORD dwStateMask)
  291. : pDevices(nullptr)
  292. , uCount(0)
  293. {
  294. pEnumerator->EnumAudioEndpoints(eFlow, dwStateMask, &pDevices);
  295. if (pDevices)
  296. pDevices->GetCount(&uCount);
  297. }
  298. // Move constructor
  299. DeviceCollection(DeviceCollection&& other)
  300. {
  301. pDevices = other.pDevices;
  302. uCount = other.uCount;
  303. other.pDevices = nullptr;
  304. other.uCount = 0;
  305. }
  306. // Copy construction is not allowed
  307. DeviceCollection(const DeviceCollection& other) = delete;
  308. // Destructor
  309. ~DeviceCollection()
  310. {
  311. if (pDevices)
  312. {
  313. pDevices->Release();
  314. pDevices = nullptr;
  315. }
  316. }
  317. // Move assignment
  318. DeviceCollection& operator=(DeviceCollection&& other)
  319. {
  320. if (pDevices)
  321. {
  322. pDevices->Release();
  323. }
  324. pDevices = other.pDevices;
  325. uCount = other.uCount;
  326. other.pDevices = nullptr;
  327. other.uCount = 0;
  328. return *this;
  329. }
  330. // Copy assignment is not allowed
  331. DeviceCollection& operator=(const DeviceCollection& other) = delete;
  332. bool IsValid() const { return pDevices != nullptr; }
  333. UINT Count() const
  334. {
  335. return uCount;
  336. }
  337. Iterator Begin()
  338. {
  339. Iterator it(pDevices, 0);
  340. return it;
  341. }
  342. Iterator End()
  343. {
  344. Iterator it(pDevices, uCount);
  345. return it;
  346. }
  347. interface IMMDeviceCollection* pDevices;
  348. UINT uCount;
  349. };
  350. class DeviceEnumerator
  351. {
  352. public:
  353. DeviceEnumerator()
  354. : pEnumerator(nullptr)
  355. {
  356. CoCreateInstance(
  357. __uuidof(MMDeviceEnumerator), NULL,
  358. CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
  359. (void**)&pEnumerator);
  360. }
  361. // Move constructor
  362. DeviceEnumerator(DeviceEnumerator&& other)
  363. {
  364. pEnumerator = other.pEnumerator;
  365. other.pEnumerator = nullptr;
  366. }
  367. // Copy construction is not allowed
  368. DeviceEnumerator(const DeviceEnumerator& other) = delete;
  369. // Destructor
  370. ~DeviceEnumerator()
  371. {
  372. if (pEnumerator)
  373. {
  374. pEnumerator->Release();
  375. pEnumerator = nullptr;
  376. }
  377. }
  378. // Move assignment
  379. DeviceEnumerator& operator=(DeviceEnumerator&& other)
  380. {
  381. if (pEnumerator)
  382. {
  383. pEnumerator->Release();
  384. }
  385. pEnumerator = other.pEnumerator;
  386. other.pEnumerator = nullptr;
  387. return *this;
  388. }
  389. // Copy assignment is not allowed
  390. DeviceEnumerator& operator=(const DeviceEnumerator& other) = delete;
  391. bool IsValid() { return pEnumerator != nullptr; }
  392. Device GetDefaultDevice(ERole in_eRole)
  393. {
  394. Device pDevice;
  395. pEnumerator->GetDefaultAudioEndpoint(eRender, in_eRole, (IMMDevice**)&pDevice.pDevice); //Transfer the ref
  396. if (pDevice.pDevice)
  397. pDevice.ComputeId();
  398. return pDevice;
  399. }
  400. interface IMMDeviceEnumerator* pEnumerator;
  401. };
  402. /// Interface to access the IMMDevice cache. This avoids driver accesses.
  403. class IAkDeviceEnumerator
  404. {
  405. public:
  406. virtual AkUInt32 Count() = 0; ///Returns the number of devices. This function can block.
  407. virtual Device Item(AkUInt32 in_idx) = 0; ///Gets item in_idx from the cache. Must be smaller than Count(). This function can block.
  408. virtual void Lock() = 0; /// For thread safety. If you iterate through all the devices, lock the enumerator to avoid changes. However, if only accessing one single item, Item() is thread safe in itself.
  409. virtual void Unlock() = 0; /// For thread safety. See \ref Lock()
  410. virtual Device FindDevice(AkUInt32 in_id) = 0; ///Find a device that has this unique ID. The Id is one returned by AK::GetDeviceID.
  411. };
  412. }
  413. };