AkMMDevice.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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. #include <mmdeviceapi.h>
  25. namespace AK
  26. {
  27. namespace Win32
  28. {
  29. class DeviceProperty
  30. {
  31. public:
  32. // Default constructor
  33. DeviceProperty()
  34. {
  35. PropVariantInit(&variant);
  36. }
  37. // Construct a property from an existing property store
  38. DeviceProperty(const PROPERTYKEY &key, IPropertyStore* in_pProps)
  39. {
  40. PropVariantInit(&variant);
  41. if (in_pProps) in_pProps->GetValue(key, &variant);
  42. }
  43. // Move constructor
  44. DeviceProperty(DeviceProperty&& other)
  45. {
  46. variant = other.variant;
  47. PropVariantInit(&other.variant);
  48. }
  49. // Copy construction is not allowed
  50. DeviceProperty(const DeviceProperty& other) = delete;
  51. // Destructor
  52. ~DeviceProperty()
  53. {
  54. PropVariantClear(&variant);
  55. }
  56. // Move assignment
  57. DeviceProperty& operator=(DeviceProperty&& other)
  58. {
  59. PropVariantClear(&variant);
  60. variant = other.variant;
  61. PropVariantInit(&other.variant);
  62. return *this;
  63. }
  64. // Copy assignment is not allowed
  65. DeviceProperty& operator=(const DeviceProperty& other) = delete;
  66. bool IsEmpty() const { return variant.vt == VT_EMPTY; }
  67. PROPVARIANT variant;
  68. };
  69. class DeviceProperties
  70. {
  71. public:
  72. // Default constructor
  73. DeviceProperties()
  74. : pProps(nullptr)
  75. {
  76. }
  77. // Construct properties from an IMMDevice
  78. DeviceProperties(IMMDevice* in_pDevice)
  79. {
  80. in_pDevice->OpenPropertyStore(STGM_READ, &pProps);
  81. }
  82. // Move constructor
  83. DeviceProperties(DeviceProperties&& other)
  84. {
  85. pProps = other.pProps;
  86. other.pProps = nullptr;
  87. }
  88. // Copy construction is not allowed
  89. DeviceProperties(const DeviceProperties& other) = delete;
  90. // Destructor
  91. ~DeviceProperties()
  92. {
  93. if (pProps)
  94. {
  95. pProps->Release();
  96. pProps = nullptr;
  97. }
  98. }
  99. // Move assignment
  100. DeviceProperties& operator=(DeviceProperties&& other)
  101. {
  102. if (pProps)
  103. pProps->Release();
  104. pProps = other.pProps;
  105. other.pProps = nullptr;
  106. return *this;
  107. }
  108. // Copy assignment is not allowed
  109. DeviceProperties& operator=(const DeviceProperties& other) = delete;
  110. bool IsValid() { return pProps != nullptr; }
  111. DeviceProperty GetProperty(const PROPERTYKEY& key)
  112. {
  113. return DeviceProperty(key, pProps);
  114. }
  115. IPropertyStore* pProps;
  116. };
  117. class Device
  118. {
  119. public:
  120. // Default constructor
  121. Device()
  122. : pDevice(nullptr)
  123. , idDevice(AK_INVALID_DEVICE_ID)
  124. {
  125. }
  126. // Move constructor
  127. Device(Device&& other)
  128. : pDevice(nullptr)
  129. {
  130. SetDevice(other.pDevice);
  131. other.pDevice = nullptr;
  132. }
  133. Device(const Device& other)
  134. : pDevice(nullptr)
  135. {
  136. *this = other;
  137. }
  138. // Destructor
  139. ~Device()
  140. {
  141. if (pDevice)
  142. {
  143. pDevice->Release();
  144. pDevice = nullptr;
  145. }
  146. }
  147. // Move assignment
  148. Device& operator=(Device&& other)
  149. {
  150. if (pDevice)
  151. {
  152. pDevice->Release();
  153. pDevice = nullptr; // Do not remove, protects against this == &other
  154. }
  155. pDevice = other.pDevice;
  156. idDevice = other.idDevice;
  157. other.pDevice = nullptr;
  158. return *this;
  159. }
  160. Device& operator=(const Device& other)
  161. {
  162. if (pDevice)
  163. {
  164. pDevice->Release();
  165. pDevice = nullptr; // Do not remove, protects against this == &other
  166. }
  167. pDevice = other.pDevice;
  168. if (pDevice)
  169. pDevice->AddRef();
  170. idDevice = other.idDevice;
  171. return *this;
  172. }
  173. Device& operator=(IMMDevice* pOther)
  174. {
  175. SetDevice(pOther);
  176. if (pDevice)
  177. pDevice->AddRef();
  178. return *this;
  179. }
  180. bool IsValid() const { return pDevice != nullptr; }
  181. AkUInt32 GetDeviceID() const
  182. {
  183. return pDevice ? idDevice : AK_INVALID_DEVICE_ID;
  184. }
  185. DeviceProperties GetProperties() const
  186. {
  187. return DeviceProperties(pDevice);
  188. }
  189. void ComputeId()
  190. {
  191. idDevice = AK_INVALID_DEVICE_ID;
  192. if (pDevice)
  193. {
  194. //Inlined version of GetDeviceID.
  195. LPWSTR pwszID = NULL;
  196. if (pDevice->GetId(&pwszID) == S_OK)
  197. {
  198. char szString[260];
  199. AKPLATFORM::AkWideCharToChar(pwszID, 260 - 1, szString);
  200. szString[260 - 1] = 0;
  201. idDevice = FNVHash32::ComputeLowerCase((const char*)szString);
  202. CoTaskMemFree(pwszID);
  203. }
  204. }
  205. }
  206. void SetDevice(IMMDevice* in_pNew)
  207. {
  208. if (pDevice)
  209. pDevice->Release();
  210. pDevice = in_pNew;
  211. ComputeId();
  212. }
  213. interface IMMDevice* pDevice;
  214. AkUInt32 idDevice;
  215. };
  216. class DeviceCollection
  217. {
  218. public:
  219. class Iterator
  220. {
  221. public:
  222. Iterator(IMMDeviceCollection* in_pDevices, UINT in_i)
  223. : pDevices(in_pDevices)
  224. , i(in_i)
  225. {
  226. }
  227. /// + operator
  228. Iterator operator+(AkUInt32 inc) const
  229. {
  230. Iterator returnedIt(pDevices, i + inc);
  231. return returnedIt;
  232. }
  233. /// - operator
  234. AkUInt32 operator-(Iterator const& rhs) const
  235. {
  236. return (AkUInt32)(i - rhs.i);
  237. }
  238. /// ++ operator
  239. Iterator& operator++()
  240. {
  241. ++i;
  242. return *this;
  243. }
  244. /// -- operator
  245. Iterator& operator--()
  246. {
  247. --i;
  248. return *this;
  249. }
  250. /// == operator
  251. bool operator ==(const Iterator& in_rOp) const
  252. {
  253. return (pDevices == in_rOp.pDevices && i == in_rOp.i);
  254. }
  255. /// != operator
  256. bool operator !=(const Iterator& in_rOp) const
  257. {
  258. return (pDevices != in_rOp.pDevices || i != in_rOp.i);
  259. }
  260. Device GetDevice()
  261. {
  262. Device pDevice;
  263. pDevices->Item(i, (IMMDevice**)&pDevice.pDevice); //Transfer the ref
  264. if (pDevice.pDevice)
  265. pDevice.ComputeId();
  266. return pDevice;
  267. }
  268. interface IMMDeviceCollection* pDevices; //Keep first.
  269. UINT i;
  270. };
  271. DeviceCollection(IMMDeviceEnumerator* pEnumerator)
  272. : pDevices(nullptr)
  273. , uCount(0)
  274. {
  275. pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATEMASK_ALL, &pDevices);
  276. if (pDevices)
  277. pDevices->GetCount(&uCount);
  278. }
  279. DeviceCollection(IMMDeviceEnumerator* pEnumerator, EDataFlow eFlow, DWORD dwStateMask)
  280. : pDevices(nullptr)
  281. , uCount(0)
  282. {
  283. pEnumerator->EnumAudioEndpoints(eFlow, dwStateMask, &pDevices);
  284. if (pDevices)
  285. pDevices->GetCount(&uCount);
  286. }
  287. // Move constructor
  288. DeviceCollection(DeviceCollection&& other)
  289. {
  290. pDevices = other.pDevices;
  291. uCount = other.uCount;
  292. other.pDevices = nullptr;
  293. other.uCount = 0;
  294. }
  295. // Copy construction is not allowed
  296. DeviceCollection(const DeviceCollection& other) = delete;
  297. // Destructor
  298. ~DeviceCollection()
  299. {
  300. if (pDevices)
  301. {
  302. pDevices->Release();
  303. pDevices = nullptr;
  304. }
  305. }
  306. // Move assignment
  307. DeviceCollection& operator=(DeviceCollection&& other)
  308. {
  309. if (pDevices)
  310. {
  311. pDevices->Release();
  312. }
  313. pDevices = other.pDevices;
  314. uCount = other.uCount;
  315. other.pDevices = nullptr;
  316. other.uCount = 0;
  317. return *this;
  318. }
  319. // Copy assignment is not allowed
  320. DeviceCollection& operator=(const DeviceCollection& other) = delete;
  321. bool IsValid() const { return pDevices != nullptr; }
  322. UINT Count() const
  323. {
  324. return uCount;
  325. }
  326. Iterator Begin()
  327. {
  328. Iterator it(pDevices, 0);
  329. return it;
  330. }
  331. Iterator End()
  332. {
  333. Iterator it(pDevices, uCount);
  334. return it;
  335. }
  336. interface IMMDeviceCollection* pDevices;
  337. UINT uCount;
  338. };
  339. class DeviceEnumerator
  340. {
  341. public:
  342. DeviceEnumerator()
  343. : pEnumerator(nullptr)
  344. {
  345. CoCreateInstance(
  346. __uuidof(MMDeviceEnumerator), NULL,
  347. CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
  348. (void**)&pEnumerator);
  349. }
  350. // Move constructor
  351. DeviceEnumerator(DeviceEnumerator&& other)
  352. {
  353. pEnumerator = other.pEnumerator;
  354. other.pEnumerator = nullptr;
  355. }
  356. // Copy construction is not allowed
  357. DeviceEnumerator(const DeviceEnumerator& other) = delete;
  358. // Destructor
  359. ~DeviceEnumerator()
  360. {
  361. if (pEnumerator)
  362. {
  363. pEnumerator->Release();
  364. pEnumerator = nullptr;
  365. }
  366. }
  367. // Move assignment
  368. DeviceEnumerator& operator=(DeviceEnumerator&& other)
  369. {
  370. if (pEnumerator)
  371. {
  372. pEnumerator->Release();
  373. }
  374. pEnumerator = other.pEnumerator;
  375. other.pEnumerator = nullptr;
  376. return *this;
  377. }
  378. // Copy assignment is not allowed
  379. DeviceEnumerator& operator=(const DeviceEnumerator& other) = delete;
  380. bool IsValid() { return pEnumerator != nullptr; }
  381. Device GetDefaultDevice(ERole in_eRole)
  382. {
  383. Device pDevice;
  384. pEnumerator->GetDefaultAudioEndpoint(eRender, in_eRole, (IMMDevice**)&pDevice.pDevice); //Transfer the ref
  385. if (pDevice.pDevice)
  386. pDevice.ComputeId();
  387. return pDevice;
  388. }
  389. interface IMMDeviceEnumerator* pEnumerator;
  390. };
  391. /// Interface to access the IMMDevice cache. This avoids driver accesses.
  392. class IAkDeviceEnumerator
  393. {
  394. public:
  395. virtual AkUInt32 Count() = 0; ///Returns the number of devices. This function can block.
  396. virtual Device Item(AkUInt32 in_idx) = 0; ///Gets item in_idx from the cache. Must be smaller than Count(). This function can block.
  397. 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.
  398. virtual void Unlock() = 0; /// For thread safety. See \ref Lock()
  399. virtual Device FindDevice(AkUInt32 in_id) = 0; ///Find a device that has this unique ID. The Id is one returned by AK::GetDeviceID.
  400. };
  401. }
  402. };