The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2039 lines
74KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
  20. #ifndef JUCE_WASAPI_LOGGING
  21. #define JUCE_WASAPI_LOGGING 0
  22. #endif
  23. //==============================================================================
  24. namespace WasapiClasses
  25. {
  26. static void logFailure ([[maybe_unused]] HRESULT hr)
  27. {
  28. jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from
  29. // a thread which hasn't been initialised with CoInitialize().
  30. #if JUCE_WASAPI_LOGGING
  31. if (FAILED (hr))
  32. {
  33. const char* m = nullptr;
  34. switch (hr)
  35. {
  36. case E_POINTER: m = "E_POINTER"; break;
  37. case E_INVALIDARG: m = "E_INVALIDARG"; break;
  38. case E_NOINTERFACE: m = "E_NOINTERFACE"; break;
  39. #define JUCE_WASAPI_ERR(desc, n) \
  40. case MAKE_HRESULT(1, 0x889, n): m = #desc; break;
  41. JUCE_WASAPI_ERR (AUDCLNT_E_NOT_INITIALIZED, 0x001)
  42. JUCE_WASAPI_ERR (AUDCLNT_E_ALREADY_INITIALIZED, 0x002)
  43. JUCE_WASAPI_ERR (AUDCLNT_E_WRONG_ENDPOINT_TYPE, 0x003)
  44. JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_INVALIDATED, 0x004)
  45. JUCE_WASAPI_ERR (AUDCLNT_E_NOT_STOPPED, 0x005)
  46. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_TOO_LARGE, 0x006)
  47. JUCE_WASAPI_ERR (AUDCLNT_E_OUT_OF_ORDER, 0x007)
  48. JUCE_WASAPI_ERR (AUDCLNT_E_UNSUPPORTED_FORMAT, 0x008)
  49. JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_SIZE, 0x009)
  50. JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_IN_USE, 0x00a)
  51. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_OPERATION_PENDING, 0x00b)
  52. JUCE_WASAPI_ERR (AUDCLNT_E_THREAD_NOT_REGISTERED, 0x00c)
  53. JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, 0x00e)
  54. JUCE_WASAPI_ERR (AUDCLNT_E_ENDPOINT_CREATE_FAILED, 0x00f)
  55. JUCE_WASAPI_ERR (AUDCLNT_E_SERVICE_NOT_RUNNING, 0x010)
  56. JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED, 0x011)
  57. JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_ONLY, 0x012)
  58. JUCE_WASAPI_ERR (AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL, 0x013)
  59. JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_SET, 0x014)
  60. JUCE_WASAPI_ERR (AUDCLNT_E_INCORRECT_BUFFER_SIZE, 0x015)
  61. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_ERROR, 0x016)
  62. JUCE_WASAPI_ERR (AUDCLNT_E_CPUUSAGE_EXCEEDED, 0x017)
  63. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_ERROR, 0x018)
  64. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED, 0x019)
  65. JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_DEVICE_PERIOD, 0x020)
  66. default: break;
  67. }
  68. Logger::writeToLog ("WASAPI error: " + (m != nullptr ? String (m)
  69. : String::toHexString ((int) hr)));
  70. }
  71. #endif
  72. }
  73. #undef check
  74. static bool check (HRESULT hr)
  75. {
  76. logFailure (hr);
  77. return SUCCEEDED (hr);
  78. }
  79. //==============================================================================
  80. }
  81. #if JUCE_MINGW
  82. struct PROPERTYKEY
  83. {
  84. GUID fmtid;
  85. DWORD pid;
  86. };
  87. WINOLEAPI PropVariantClear (PROPVARIANT*);
  88. #endif
  89. #if JUCE_MINGW && defined (KSDATAFORMAT_SUBTYPE_PCM)
  90. #undef KSDATAFORMAT_SUBTYPE_PCM
  91. #undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
  92. #endif
  93. #ifndef KSDATAFORMAT_SUBTYPE_PCM
  94. #define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71")
  95. #define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71")
  96. #endif
  97. enum EDataFlow
  98. {
  99. eRender = 0,
  100. eCapture = (eRender + 1),
  101. eAll = (eCapture + 1)
  102. };
  103. enum
  104. {
  105. DEVICE_STATE_ACTIVE = 1
  106. };
  107. enum
  108. {
  109. AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY = 1,
  110. AUDCLNT_BUFFERFLAGS_SILENT = 2
  111. };
  112. JUCE_IUNKNOWNCLASS (IPropertyStore, "886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")
  113. {
  114. JUCE_COMCALL GetCount (DWORD*) = 0;
  115. JUCE_COMCALL GetAt (DWORD, PROPERTYKEY*) = 0;
  116. JUCE_COMCALL GetValue (const PROPERTYKEY&, PROPVARIANT*) = 0;
  117. JUCE_COMCALL SetValue (const PROPERTYKEY&, const PROPVARIANT&) = 0;
  118. JUCE_COMCALL Commit() = 0;
  119. };
  120. JUCE_IUNKNOWNCLASS (IMMDevice, "D666063F-1587-4E43-81F1-B948E807363F")
  121. {
  122. JUCE_COMCALL Activate (REFIID, DWORD, PROPVARIANT*, void**) = 0;
  123. JUCE_COMCALL OpenPropertyStore (DWORD, IPropertyStore**) = 0;
  124. JUCE_COMCALL GetId (LPWSTR*) = 0;
  125. JUCE_COMCALL GetState (DWORD*) = 0;
  126. };
  127. JUCE_IUNKNOWNCLASS (IMMEndpoint, "1BE09788-6894-4089-8586-9A2A6C265AC5")
  128. {
  129. JUCE_COMCALL GetDataFlow (EDataFlow*) = 0;
  130. };
  131. struct IMMDeviceCollection : public IUnknown
  132. {
  133. JUCE_COMCALL GetCount (UINT*) = 0;
  134. JUCE_COMCALL Item (UINT, IMMDevice**) = 0;
  135. };
  136. enum ERole
  137. {
  138. eConsole = 0,
  139. eMultimedia = (eConsole + 1),
  140. eCommunications = (eMultimedia + 1)
  141. };
  142. JUCE_IUNKNOWNCLASS (IMMNotificationClient, "7991EEC9-7E89-4D85-8390-6C703CEC60C0")
  143. {
  144. JUCE_COMCALL OnDeviceStateChanged (LPCWSTR, DWORD) = 0;
  145. JUCE_COMCALL OnDeviceAdded (LPCWSTR) = 0;
  146. JUCE_COMCALL OnDeviceRemoved (LPCWSTR) = 0;
  147. JUCE_COMCALL OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) = 0;
  148. JUCE_COMCALL OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) = 0;
  149. };
  150. JUCE_IUNKNOWNCLASS (IMMDeviceEnumerator, "A95664D2-9614-4F35-A746-DE8DB63617E6")
  151. {
  152. JUCE_COMCALL EnumAudioEndpoints (EDataFlow, DWORD, IMMDeviceCollection**) = 0;
  153. JUCE_COMCALL GetDefaultAudioEndpoint (EDataFlow, ERole, IMMDevice**) = 0;
  154. JUCE_COMCALL GetDevice (LPCWSTR, IMMDevice**) = 0;
  155. JUCE_COMCALL RegisterEndpointNotificationCallback (IMMNotificationClient*) = 0;
  156. JUCE_COMCALL UnregisterEndpointNotificationCallback (IMMNotificationClient*) = 0;
  157. };
  158. JUCE_COMCLASS (MMDeviceEnumerator, "BCDE0395-E52F-467C-8E3D-C4579291692E");
  159. using REFERENCE_TIME = LONGLONG;
  160. enum AVRT_PRIORITY
  161. {
  162. AVRT_PRIORITY_LOW = -1,
  163. AVRT_PRIORITY_NORMAL,
  164. AVRT_PRIORITY_HIGH,
  165. AVRT_PRIORITY_CRITICAL
  166. };
  167. enum AUDCLNT_SHAREMODE
  168. {
  169. AUDCLNT_SHAREMODE_SHARED,
  170. AUDCLNT_SHAREMODE_EXCLUSIVE
  171. };
  172. enum AUDIO_STREAM_CATEGORY
  173. {
  174. AudioCategory_Other = 0,
  175. AudioCategory_ForegroundOnlyMedia,
  176. AudioCategory_BackgroundCapableMedia,
  177. AudioCategory_Communications,
  178. AudioCategory_Alerts,
  179. AudioCategory_SoundEffects,
  180. AudioCategory_GameEffects,
  181. AudioCategory_GameMedia,
  182. AudioCategory_GameChat,
  183. AudioCategory_Speech,
  184. AudioCategory_Movie,
  185. AudioCategory_Media
  186. };
  187. struct AudioClientProperties
  188. {
  189. UINT32 cbSize;
  190. BOOL bIsOffload;
  191. AUDIO_STREAM_CATEGORY eCategory;
  192. };
  193. JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")
  194. {
  195. JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0;
  196. JUCE_COMCALL GetBufferSize (UINT32*) = 0;
  197. JUCE_COMCALL GetStreamLatency (REFERENCE_TIME*) = 0;
  198. JUCE_COMCALL GetCurrentPadding (UINT32*) = 0;
  199. JUCE_COMCALL IsFormatSupported (AUDCLNT_SHAREMODE, const WAVEFORMATEX*, WAVEFORMATEX**) = 0;
  200. JUCE_COMCALL GetMixFormat (WAVEFORMATEX**) = 0;
  201. JUCE_COMCALL GetDevicePeriod (REFERENCE_TIME*, REFERENCE_TIME*) = 0;
  202. JUCE_COMCALL Start() = 0;
  203. JUCE_COMCALL Stop() = 0;
  204. JUCE_COMCALL Reset() = 0;
  205. JUCE_COMCALL SetEventHandle (HANDLE) = 0;
  206. JUCE_COMCALL GetService (REFIID, void**) = 0;
  207. };
  208. JUCE_COMCLASS (IAudioClient2, "726778CD-F60A-4eda-82DE-E47610CD78AA") : public IAudioClient
  209. {
  210. JUCE_COMCALL IsOffloadCapable (AUDIO_STREAM_CATEGORY, BOOL*) = 0;
  211. JUCE_COMCALL SetClientProperties (const AudioClientProperties*) = 0;
  212. JUCE_COMCALL GetBufferSizeLimits (const WAVEFORMATEX*, BOOL, REFERENCE_TIME*, REFERENCE_TIME*) = 0;
  213. };
  214. JUCE_COMCLASS (IAudioClient3, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") : public IAudioClient2
  215. {
  216. JUCE_COMCALL GetSharedModeEnginePeriod (const WAVEFORMATEX*, UINT32*, UINT32*, UINT32*, UINT32*) = 0;
  217. JUCE_COMCALL GetCurrentSharedModeEnginePeriod (WAVEFORMATEX**, UINT32*) = 0;
  218. JUCE_COMCALL InitializeSharedAudioStream (DWORD, UINT32, const WAVEFORMATEX*, LPCGUID) = 0;
  219. };
  220. JUCE_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317")
  221. {
  222. JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0;
  223. JUCE_COMCALL ReleaseBuffer (UINT32) = 0;
  224. JUCE_COMCALL GetNextPacketSize (UINT32*) = 0;
  225. };
  226. JUCE_IUNKNOWNCLASS (IAudioRenderClient, "F294ACFC-3146-4483-A7BF-ADDCA7C260E2")
  227. {
  228. JUCE_COMCALL GetBuffer (UINT32, BYTE**) = 0;
  229. JUCE_COMCALL ReleaseBuffer (UINT32, DWORD) = 0;
  230. };
  231. JUCE_IUNKNOWNCLASS (IAudioEndpointVolume, "5CDF2C82-841E-4546-9722-0CF74078229A")
  232. {
  233. JUCE_COMCALL RegisterControlChangeNotify (void*) = 0;
  234. JUCE_COMCALL UnregisterControlChangeNotify (void*) = 0;
  235. JUCE_COMCALL GetChannelCount (UINT*) = 0;
  236. JUCE_COMCALL SetMasterVolumeLevel (float, LPCGUID) = 0;
  237. JUCE_COMCALL SetMasterVolumeLevelScalar (float, LPCGUID) = 0;
  238. JUCE_COMCALL GetMasterVolumeLevel (float*) = 0;
  239. JUCE_COMCALL GetMasterVolumeLevelScalar (float*) = 0;
  240. JUCE_COMCALL SetChannelVolumeLevel (UINT, float, LPCGUID) = 0;
  241. JUCE_COMCALL SetChannelVolumeLevelScalar (UINT, float, LPCGUID) = 0;
  242. JUCE_COMCALL GetChannelVolumeLevel (UINT, float*) = 0;
  243. JUCE_COMCALL GetChannelVolumeLevelScalar (UINT, float*) = 0;
  244. JUCE_COMCALL SetMute (BOOL, LPCGUID) = 0;
  245. JUCE_COMCALL GetMute (BOOL*) = 0;
  246. JUCE_COMCALL GetVolumeStepInfo (UINT*, UINT*) = 0;
  247. JUCE_COMCALL VolumeStepUp (LPCGUID) = 0;
  248. JUCE_COMCALL VolumeStepDown (LPCGUID) = 0;
  249. JUCE_COMCALL QueryHardwareSupport (DWORD*) = 0;
  250. JUCE_COMCALL GetVolumeRange (float*, float*, float*) = 0;
  251. };
  252. enum AudioSessionDisconnectReason
  253. {
  254. DisconnectReasonDeviceRemoval = 0,
  255. DisconnectReasonServerShutdown = 1,
  256. DisconnectReasonFormatChanged = 2,
  257. DisconnectReasonSessionLogoff = 3,
  258. DisconnectReasonSessionDisconnected = 4,
  259. DisconnectReasonExclusiveModeOverride = 5
  260. };
  261. enum AudioSessionState
  262. {
  263. AudioSessionStateInactive = 0,
  264. AudioSessionStateActive = 1,
  265. AudioSessionStateExpired = 2
  266. };
  267. JUCE_IUNKNOWNCLASS (IAudioSessionEvents, "24918ACC-64B3-37C1-8CA9-74A66E9957A8")
  268. {
  269. JUCE_COMCALL OnDisplayNameChanged (LPCWSTR, LPCGUID) = 0;
  270. JUCE_COMCALL OnIconPathChanged (LPCWSTR, LPCGUID) = 0;
  271. JUCE_COMCALL OnSimpleVolumeChanged (float, BOOL, LPCGUID) = 0;
  272. JUCE_COMCALL OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) = 0;
  273. JUCE_COMCALL OnGroupingParamChanged (LPCGUID, LPCGUID) = 0;
  274. JUCE_COMCALL OnStateChanged (AudioSessionState) = 0;
  275. JUCE_COMCALL OnSessionDisconnected (AudioSessionDisconnectReason) = 0;
  276. };
  277. JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD")
  278. {
  279. JUCE_COMCALL GetState (AudioSessionState*) = 0;
  280. JUCE_COMCALL GetDisplayName (LPWSTR*) = 0;
  281. JUCE_COMCALL SetDisplayName (LPCWSTR, LPCGUID) = 0;
  282. JUCE_COMCALL GetIconPath (LPWSTR*) = 0;
  283. JUCE_COMCALL SetIconPath (LPCWSTR, LPCGUID) = 0;
  284. JUCE_COMCALL GetGroupingParam (GUID*) = 0;
  285. JUCE_COMCALL SetGroupingParam (LPCGUID, LPCGUID) = 0;
  286. JUCE_COMCALL RegisterAudioSessionNotification (IAudioSessionEvents*) = 0;
  287. JUCE_COMCALL UnregisterAudioSessionNotification (IAudioSessionEvents*) = 0;
  288. };
  289. } // namespace juce
  290. #ifdef __CRT_UUID_DECL
  291. __CRT_UUID_DECL (juce::IPropertyStore, 0x886d8eeb, 0x8cf2, 0x4446, 0x8d, 0x02, 0xcd, 0xba, 0x1d, 0xbd, 0xcf, 0x99)
  292. __CRT_UUID_DECL (juce::IMMDevice, 0xD666063F, 0x1587, 0x4E43, 0x81, 0xF1, 0xB9, 0x48, 0xE8, 0x07, 0x36, 0x3F)
  293. __CRT_UUID_DECL (juce::IMMEndpoint, 0x1BE09788, 0x6894, 0x4089, 0x85, 0x86, 0x9A, 0x2A, 0x6C, 0x26, 0x5A, 0xC5)
  294. __CRT_UUID_DECL (juce::IMMNotificationClient, 0x7991EEC9, 0x7E89, 0x4D85, 0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0)
  295. __CRT_UUID_DECL (juce::IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6)
  296. __CRT_UUID_DECL (juce::MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E)
  297. __CRT_UUID_DECL (juce::IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2)
  298. __CRT_UUID_DECL (juce::IAudioClient2, 0x726778CD, 0xF60A, 0x4eda, 0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA)
  299. __CRT_UUID_DECL (juce::IAudioClient3, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2)
  300. __CRT_UUID_DECL (juce::IAudioCaptureClient, 0xC8ADBD64, 0xE71E, 0x48a0, 0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17)
  301. __CRT_UUID_DECL (juce::IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2)
  302. __CRT_UUID_DECL (juce::IAudioEndpointVolume, 0x5CDF2C82, 0x841E, 0x4546, 0x97, 0x22, 0x0C, 0xF7, 0x40, 0x78, 0x22, 0x9A)
  303. __CRT_UUID_DECL (juce::IAudioSessionEvents, 0x24918ACC, 0x64B3, 0x37C1, 0x8C, 0xA9, 0x74, 0xA6, 0x6E, 0x99, 0x57, 0xA8)
  304. __CRT_UUID_DECL (juce::IAudioSessionControl, 0xF4B1A599, 0x7266, 0x4319, 0xA8, 0xCA, 0xE7, 0x0A, 0xCB, 0x11, 0xE8, 0xCD)
  305. #endif
  306. //==============================================================================
  307. namespace juce
  308. {
  309. namespace WasapiClasses
  310. {
  311. static String getDeviceID (IMMDevice* device)
  312. {
  313. String s;
  314. WCHAR* deviceId = nullptr;
  315. if (check (device->GetId (&deviceId)))
  316. {
  317. s = String (deviceId);
  318. CoTaskMemFree (deviceId);
  319. }
  320. return s;
  321. }
  322. static EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
  323. {
  324. EDataFlow flow = eRender;
  325. if (auto endpoint = device.getInterface<IMMEndpoint>())
  326. (void) check (endpoint->GetDataFlow (&flow));
  327. return flow;
  328. }
  329. static int refTimeToSamples (const REFERENCE_TIME& t, double sampleRate) noexcept
  330. {
  331. return roundToInt (sampleRate * ((double) t) * 0.0000001);
  332. }
  333. static REFERENCE_TIME samplesToRefTime (int numSamples, double sampleRate) noexcept
  334. {
  335. return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5);
  336. }
  337. static void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcept
  338. {
  339. memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE)
  340. : sizeof (WAVEFORMATEX));
  341. }
  342. static bool isExclusiveMode (WASAPIDeviceMode deviceMode) noexcept
  343. {
  344. return deviceMode == WASAPIDeviceMode::exclusive;
  345. }
  346. static bool isLowLatencyMode (WASAPIDeviceMode deviceMode) noexcept
  347. {
  348. return deviceMode == WASAPIDeviceMode::sharedLowLatency;
  349. }
  350. static bool supportsSampleRateConversion (WASAPIDeviceMode deviceMode) noexcept
  351. {
  352. return deviceMode == WASAPIDeviceMode::shared;
  353. }
  354. //==============================================================================
  355. class WASAPIDeviceBase
  356. {
  357. public:
  358. WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, WASAPIDeviceMode mode)
  359. : device (d),
  360. deviceMode (mode)
  361. {
  362. clientEvent = CreateEvent (nullptr, false, false, nullptr);
  363. ComSmartPtr<IAudioClient> tempClient (createClient());
  364. if (tempClient == nullptr)
  365. return;
  366. auto format = getClientMixFormat (tempClient);
  367. if (! format)
  368. return;
  369. defaultNumChannels = maxNumChannels = format->Format.nChannels;
  370. defaultSampleRate = format->Format.nSamplesPerSec;
  371. rates.addUsingDefaultSort (defaultSampleRate);
  372. defaultFormatChannelMask = format->dwChannelMask;
  373. if (isExclusiveMode (deviceMode))
  374. if (auto optFormat = findSupportedFormat (tempClient, defaultNumChannels, defaultSampleRate))
  375. format = optFormat;
  376. querySupportedBufferSizes (*format, tempClient);
  377. querySupportedSampleRates (*format, tempClient);
  378. maxNumChannels = queryMaxNumChannels (tempClient);
  379. }
  380. virtual ~WASAPIDeviceBase()
  381. {
  382. device = nullptr;
  383. CloseHandle (clientEvent);
  384. }
  385. bool isOk() const noexcept { return defaultBufferSize > 0 && defaultSampleRate > 0; }
  386. bool openClient (const double newSampleRate, const BigInteger& newChannels, const int bufferSizeSamples)
  387. {
  388. sampleRate = newSampleRate;
  389. channels = newChannels;
  390. channels.setRange (maxNumChannels, channels.getHighestBit() + 1 - maxNumChannels, false);
  391. numChannels = channels.getHighestBit() + 1;
  392. if (numChannels == 0)
  393. return true;
  394. client = createClient();
  395. if (client != nullptr
  396. && tryInitialisingWithBufferSize (bufferSizeSamples))
  397. {
  398. sampleRateHasChanged = false;
  399. shouldShutdown = false;
  400. channelMaps.clear();
  401. for (int i = 0; i <= channels.getHighestBit(); ++i)
  402. if (channels[i])
  403. channelMaps.add (i);
  404. REFERENCE_TIME latency;
  405. if (check (client->GetStreamLatency (&latency)))
  406. latencySamples = refTimeToSamples (latency, sampleRate);
  407. (void) check (client->GetBufferSize (&actualBufferSize));
  408. createSessionEventCallback();
  409. return check (client->SetEventHandle (clientEvent));
  410. }
  411. return false;
  412. }
  413. void closeClient()
  414. {
  415. if (client != nullptr)
  416. client->Stop();
  417. // N.B. this is needed to prevent a double-deletion of the IAudioSessionEvents object
  418. // on older versions of Windows
  419. Thread::sleep (5);
  420. deleteSessionEventCallback();
  421. client = nullptr;
  422. ResetEvent (clientEvent);
  423. }
  424. void deviceSampleRateChanged()
  425. {
  426. sampleRateHasChanged = true;
  427. }
  428. void deviceSessionBecameInactive()
  429. {
  430. isActive = false;
  431. }
  432. void deviceSessionExpired()
  433. {
  434. shouldShutdown = true;
  435. }
  436. void deviceSessionBecameActive()
  437. {
  438. isActive = true;
  439. }
  440. std::optional<BigInteger> getDefaultLayout() const
  441. {
  442. if (countNumberOfBits ((uint64) defaultFormatChannelMask) == defaultNumChannels)
  443. return BigInteger ((int64) defaultFormatChannelMask);
  444. BigInteger integer;
  445. integer.setRange (0, defaultNumChannels, true);
  446. return integer;
  447. }
  448. //==============================================================================
  449. ComSmartPtr<IMMDevice> device;
  450. ComSmartPtr<IAudioClient> client;
  451. WASAPIDeviceMode deviceMode;
  452. double sampleRate = 0, defaultSampleRate = 0;
  453. int numChannels = 0, actualNumChannels = 0, maxNumChannels = 0, defaultNumChannels = 0;
  454. int minBufferSize = 0, defaultBufferSize = 0, latencySamples = 0;
  455. int lowLatencyBufferSizeMultiple = 0, lowLatencyMaxBufferSize = 0;
  456. DWORD defaultFormatChannelMask = 0;
  457. Array<double> rates;
  458. HANDLE clientEvent = {};
  459. BigInteger channels;
  460. Array<int> channelMaps;
  461. UINT32 actualBufferSize = 0;
  462. int bytesPerSample = 0, bytesPerFrame = 0;
  463. std::atomic<bool> sampleRateHasChanged { false }, shouldShutdown { false }, isActive { true };
  464. virtual void updateFormat (bool isFloat) = 0;
  465. private:
  466. //==============================================================================
  467. struct SessionEventCallback : public ComBaseClassHelper<IAudioSessionEvents>
  468. {
  469. SessionEventCallback (WASAPIDeviceBase& d) : owner (d) {}
  470. JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) { return S_OK; }
  471. JUCE_COMRESULT OnIconPathChanged (LPCWSTR, LPCGUID) { return S_OK; }
  472. JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) { return S_OK; }
  473. JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; }
  474. JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; }
  475. JUCE_COMRESULT OnStateChanged (AudioSessionState state)
  476. {
  477. switch (state)
  478. {
  479. case AudioSessionStateInactive:
  480. owner.deviceSessionBecameInactive();
  481. break;
  482. case AudioSessionStateExpired:
  483. owner.deviceSessionExpired();
  484. break;
  485. case AudioSessionStateActive:
  486. owner.deviceSessionBecameActive();
  487. break;
  488. }
  489. return S_OK;
  490. }
  491. JUCE_COMRESULT OnSessionDisconnected (AudioSessionDisconnectReason reason)
  492. {
  493. if (reason == DisconnectReasonFormatChanged)
  494. owner.deviceSampleRateChanged();
  495. return S_OK;
  496. }
  497. WASAPIDeviceBase& owner;
  498. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SessionEventCallback)
  499. };
  500. ComSmartPtr<IAudioSessionControl> audioSessionControl;
  501. ComSmartPtr<SessionEventCallback> sessionEventCallback;
  502. void createSessionEventCallback()
  503. {
  504. deleteSessionEventCallback();
  505. client->GetService (__uuidof (IAudioSessionControl),
  506. (void**) audioSessionControl.resetAndGetPointerAddress());
  507. if (audioSessionControl != nullptr)
  508. {
  509. sessionEventCallback = new SessionEventCallback (*this);
  510. audioSessionControl->RegisterAudioSessionNotification (sessionEventCallback);
  511. sessionEventCallback->Release(); // (required because ComBaseClassHelper objects are constructed with a ref count of 1)
  512. }
  513. }
  514. void deleteSessionEventCallback()
  515. {
  516. if (audioSessionControl != nullptr && sessionEventCallback != nullptr)
  517. audioSessionControl->UnregisterAudioSessionNotification (sessionEventCallback);
  518. audioSessionControl = nullptr;
  519. sessionEventCallback = nullptr;
  520. }
  521. //==============================================================================
  522. ComSmartPtr<IAudioClient> createClient()
  523. {
  524. ComSmartPtr<IAudioClient> newClient;
  525. if (device != nullptr)
  526. logFailure (device->Activate (__uuidof (IAudioClient), CLSCTX_INPROC_SERVER,
  527. nullptr, (void**) newClient.resetAndGetPointerAddress()));
  528. return newClient;
  529. }
  530. static std::optional<WAVEFORMATEXTENSIBLE> getClientMixFormat (ComSmartPtr<IAudioClient>& client)
  531. {
  532. WAVEFORMATEX* mixFormat = nullptr;
  533. if (! check (client->GetMixFormat (&mixFormat)))
  534. return {};
  535. WAVEFORMATEXTENSIBLE format{};
  536. copyWavFormat (format, mixFormat);
  537. CoTaskMemFree (mixFormat);
  538. return format;
  539. }
  540. //==============================================================================
  541. void querySupportedBufferSizes (WAVEFORMATEXTENSIBLE format, ComSmartPtr<IAudioClient>& audioClient)
  542. {
  543. if (isLowLatencyMode (deviceMode))
  544. {
  545. if (auto audioClient3 = audioClient.getInterface<IAudioClient3>())
  546. {
  547. UINT32 defaultPeriod = 0, fundamentalPeriod = 0, minPeriod = 0, maxPeriod = 0;
  548. if (check (audioClient3->GetSharedModeEnginePeriod ((WAVEFORMATEX*) &format,
  549. &defaultPeriod,
  550. &fundamentalPeriod,
  551. &minPeriod,
  552. &maxPeriod)))
  553. {
  554. minBufferSize = (int) minPeriod;
  555. defaultBufferSize = (int) defaultPeriod;
  556. lowLatencyMaxBufferSize = (int) maxPeriod;
  557. lowLatencyBufferSizeMultiple = (int) fundamentalPeriod;
  558. }
  559. }
  560. }
  561. else
  562. {
  563. REFERENCE_TIME defaultPeriod, minPeriod;
  564. if (! check (audioClient->GetDevicePeriod (&defaultPeriod, &minPeriod)))
  565. return;
  566. minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate);
  567. defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate);
  568. }
  569. }
  570. void querySupportedSampleRates (WAVEFORMATEXTENSIBLE format, ComSmartPtr<IAudioClient>& audioClient)
  571. {
  572. for (auto rate : SampleRateHelpers::getAllSampleRates())
  573. {
  574. if (rates.contains (rate))
  575. continue;
  576. format.Format.nSamplesPerSec = (DWORD) rate;
  577. format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8);
  578. WAVEFORMATEX* nearestFormat = nullptr;
  579. if (SUCCEEDED (audioClient->IsFormatSupported (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE
  580. : AUDCLNT_SHAREMODE_SHARED,
  581. (WAVEFORMATEX*) &format,
  582. isExclusiveMode (deviceMode) ? nullptr
  583. : &nearestFormat)))
  584. {
  585. if (nearestFormat != nullptr)
  586. rate = (double) nearestFormat->nSamplesPerSec;
  587. if (! rates.contains (rate))
  588. rates.addUsingDefaultSort (rate);
  589. }
  590. CoTaskMemFree (nearestFormat);
  591. }
  592. }
  593. struct AudioSampleFormat
  594. {
  595. bool useFloat;
  596. int bitsPerSampleToTry;
  597. int bytesPerSampleContainer;
  598. };
  599. static constexpr AudioSampleFormat formatsToTry[] =
  600. {
  601. { true, 32, 4 },
  602. { false, 32, 4 },
  603. { false, 24, 4 },
  604. { false, 24, 3 },
  605. { false, 20, 4 },
  606. { false, 20, 3 },
  607. { false, 16, 2 }
  608. };
  609. static std::optional<WAVEFORMATEXTENSIBLE> tryFormat (const AudioSampleFormat sampleFormat, IAudioClient* clientToUse,
  610. WASAPIDeviceMode mode, int newNumChannels, double newSampleRate,
  611. DWORD newMixFormatChannelMask)
  612. {
  613. WAVEFORMATEXTENSIBLE format;
  614. zerostruct (format);
  615. if (newNumChannels <= 2 && sampleFormat.bitsPerSampleToTry <= 16)
  616. {
  617. format.Format.wFormatTag = WAVE_FORMAT_PCM;
  618. }
  619. else
  620. {
  621. format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  622. format.Format.cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
  623. }
  624. format.Format.nSamplesPerSec = (DWORD) newSampleRate;
  625. format.Format.nChannels = (WORD) newNumChannels;
  626. format.Format.wBitsPerSample = (WORD) (8 * sampleFormat.bytesPerSampleContainer);
  627. format.Samples.wValidBitsPerSample = (WORD) (sampleFormat.bitsPerSampleToTry);
  628. format.Format.nBlockAlign = (WORD) (format.Format.nChannels * format.Format.wBitsPerSample / 8);
  629. format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign);
  630. format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
  631. format.dwChannelMask = newMixFormatChannelMask;
  632. WAVEFORMATEX* nearestFormat = nullptr;
  633. HRESULT hr = clientToUse->IsFormatSupported (isExclusiveMode (mode) ? AUDCLNT_SHAREMODE_EXCLUSIVE
  634. : AUDCLNT_SHAREMODE_SHARED,
  635. (WAVEFORMATEX*) &format,
  636. isExclusiveMode (mode) ? nullptr
  637. : &nearestFormat);
  638. logFailure (hr);
  639. auto supportsSRC = supportsSampleRateConversion (mode);
  640. if (hr == S_FALSE
  641. && nearestFormat != nullptr
  642. && (format.Format.nSamplesPerSec == nearestFormat->nSamplesPerSec
  643. || supportsSRC))
  644. {
  645. copyWavFormat (format, nearestFormat);
  646. if (supportsSRC)
  647. {
  648. format.Format.nSamplesPerSec = (DWORD) newSampleRate;
  649. format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign);
  650. }
  651. hr = S_OK;
  652. }
  653. CoTaskMemFree (nearestFormat);
  654. if (hr != S_OK)
  655. return {};
  656. return format;
  657. }
  658. std::optional<WAVEFORMATEXTENSIBLE> findSupportedFormat (IAudioClient* clientToUse, int newNumChannels, double newSampleRate) const
  659. {
  660. for (auto ch = newNumChannels; ch <= maxNumChannels; ++ch)
  661. {
  662. auto maskWithLowestNBitsSet = static_cast<DWORD> ((1 << ch) - 1);
  663. auto mixFormatChannelMask = (ch == defaultNumChannels ? defaultFormatChannelMask : maskWithLowestNBitsSet);
  664. for (auto const& sampleFormat: formatsToTry)
  665. if (auto format = tryFormat (sampleFormat, clientToUse, deviceMode, ch, newSampleRate, mixFormatChannelMask))
  666. return format;
  667. }
  668. return {};
  669. }
  670. int queryMaxNumChannels (IAudioClient* clientToUse) const
  671. {
  672. static constexpr auto maxNumChannelsToQuery = static_cast<int> (AudioChannelSet::maxChannelsOfNamedLayout);
  673. const auto fallbackNumChannels = defaultNumChannels;
  674. if (fallbackNumChannels >= maxNumChannelsToQuery)
  675. return fallbackNumChannels;
  676. auto result = fallbackNumChannels;
  677. for (auto ch = maxNumChannelsToQuery; ch > result; --ch)
  678. {
  679. auto channelMask = static_cast<DWORD> ((1 << ch) - 1);
  680. for (auto rate : rates)
  681. for (auto const& sampleFormat: formatsToTry)
  682. if (auto format = tryFormat (sampleFormat, clientToUse, deviceMode, ch, rate, channelMask))
  683. result = jmax (static_cast<int> (format->Format.nChannels), result);
  684. }
  685. return result;
  686. }
  687. DWORD getStreamFlags()
  688. {
  689. DWORD streamFlags = 0x40000; /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/
  690. if (supportsSampleRateConversion (deviceMode))
  691. streamFlags |= (0x80000000 /*AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM*/
  692. | 0x8000000); /*AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY*/
  693. return streamFlags;
  694. }
  695. bool initialiseLowLatencyClient (int bufferSizeSamples, WAVEFORMATEXTENSIBLE format)
  696. {
  697. if (auto audioClient3 = client.getInterface<IAudioClient3>())
  698. return check (audioClient3->InitializeSharedAudioStream (getStreamFlags(),
  699. (UINT32) bufferSizeSamples,
  700. (WAVEFORMATEX*) &format,
  701. nullptr));
  702. return false;
  703. }
  704. bool initialiseStandardClient (int bufferSizeSamples, WAVEFORMATEXTENSIBLE format)
  705. {
  706. REFERENCE_TIME defaultPeriod = 0, minPeriod = 0;
  707. check (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
  708. if (isExclusiveMode (deviceMode) && bufferSizeSamples > 0)
  709. defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec));
  710. for (;;)
  711. {
  712. GUID session;
  713. auto hr = client->Initialize (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE
  714. : AUDCLNT_SHAREMODE_SHARED,
  715. getStreamFlags(),
  716. defaultPeriod,
  717. isExclusiveMode (deviceMode) ? defaultPeriod : 0,
  718. (WAVEFORMATEX*) &format,
  719. &session);
  720. if (check (hr))
  721. return true;
  722. // Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks)
  723. if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
  724. break;
  725. UINT32 numFrames = 0;
  726. if (! check (client->GetBufferSize (&numFrames)))
  727. break;
  728. // Recreate client
  729. client = nullptr;
  730. client = createClient();
  731. defaultPeriod = samplesToRefTime ((int) numFrames, format.Format.nSamplesPerSec);
  732. }
  733. return false;
  734. }
  735. bool tryInitialisingWithBufferSize (int bufferSizeSamples)
  736. {
  737. if (auto format = findSupportedFormat (client, numChannels, sampleRate))
  738. {
  739. auto isInitialised = isLowLatencyMode (deviceMode) ? initialiseLowLatencyClient (bufferSizeSamples, *format)
  740. : initialiseStandardClient (bufferSizeSamples, *format);
  741. if (isInitialised)
  742. {
  743. actualNumChannels = format->Format.nChannels;
  744. const bool isFloat = format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  745. bytesPerSample = format->Format.wBitsPerSample / 8;
  746. bytesPerFrame = format->Format.nBlockAlign;
  747. updateFormat (isFloat);
  748. return true;
  749. }
  750. }
  751. return false;
  752. }
  753. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIDeviceBase)
  754. };
  755. //==============================================================================
  756. class WASAPIInputDevice : public WASAPIDeviceBase
  757. {
  758. public:
  759. WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, WASAPIDeviceMode mode)
  760. : WASAPIDeviceBase (d, mode)
  761. {
  762. }
  763. ~WASAPIInputDevice() override
  764. {
  765. close();
  766. }
  767. bool open (double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
  768. {
  769. return openClient (newSampleRate, newChannels, bufferSizeSamples)
  770. && (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient),
  771. (void**) captureClient.resetAndGetPointerAddress())));
  772. }
  773. void close()
  774. {
  775. closeClient();
  776. captureClient = nullptr;
  777. reservoir.reset();
  778. queue = SingleThreadedAbstractFifo();
  779. }
  780. template <class SourceType>
  781. void updateFormatWithType (SourceType*) noexcept
  782. {
  783. using NativeType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
  784. converter.reset (new AudioData::ConverterInstance<AudioData::Pointer<SourceType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1));
  785. }
  786. void updateFormat (bool isFloat) override
  787. {
  788. if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr);
  789. else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr);
  790. else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr);
  791. else updateFormatWithType ((AudioData::Int16*) nullptr);
  792. }
  793. bool start (int userBufferSizeIn)
  794. {
  795. const auto reservoirSize = nextPowerOfTwo ((int) (actualBufferSize + (UINT32) userBufferSizeIn));
  796. queue = SingleThreadedAbstractFifo (reservoirSize);
  797. reservoir.setSize ((size_t) (queue.getSize() * bytesPerFrame), true);
  798. xruns = 0;
  799. if (! check (client->Start()))
  800. return false;
  801. purgeInputBuffers();
  802. isActive = true;
  803. return true;
  804. }
  805. void purgeInputBuffers()
  806. {
  807. uint8* inputData;
  808. UINT32 numSamplesAvailable;
  809. DWORD flags;
  810. while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr) != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */)
  811. captureClient->ReleaseBuffer (numSamplesAvailable);
  812. }
  813. int getNumSamplesInReservoir() const noexcept { return queue.getNumReadable(); }
  814. void handleDeviceBuffer()
  815. {
  816. if (numChannels <= 0)
  817. return;
  818. uint8* inputData = nullptr;
  819. UINT32 numSamplesAvailable = 0;
  820. DWORD flags = 0;
  821. while (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)) && numSamplesAvailable > 0)
  822. {
  823. if ((flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0)
  824. xruns++;
  825. if (numSamplesAvailable > (UINT32) queue.getRemainingSpace())
  826. {
  827. captureClient->ReleaseBuffer (0);
  828. return;
  829. }
  830. auto offset = 0;
  831. for (const auto& block : queue.write ((int) numSamplesAvailable))
  832. {
  833. const auto samplesToDoBytes = block.getLength() * bytesPerFrame;
  834. auto* reservoirPtr = addBytesToPointer (reservoir.getData(), block.getStart() * bytesPerFrame);
  835. if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0)
  836. zeromem (reservoirPtr, (size_t) samplesToDoBytes);
  837. else
  838. memcpy (reservoirPtr, inputData + offset * bytesPerFrame, (size_t) samplesToDoBytes);
  839. offset += block.getLength();
  840. }
  841. captureClient->ReleaseBuffer (numSamplesAvailable);
  842. }
  843. }
  844. void copyBuffersFromReservoir (float* const* destBuffers, const int numDestBuffers, const int bufferSize)
  845. {
  846. if ((numChannels <= 0 && bufferSize == 0) || reservoir.isEmpty())
  847. return;
  848. auto offset = jmax (0, bufferSize - queue.getNumReadable());
  849. if (offset > 0)
  850. for (int i = 0; i < numDestBuffers; ++i)
  851. zeromem (destBuffers[i], (size_t) offset * sizeof (float));
  852. for (const auto& block : queue.read (jmin (queue.getNumReadable(), bufferSize)))
  853. {
  854. for (auto i = 0; i < numDestBuffers; ++i)
  855. converter->convertSamples (destBuffers[i] + offset,
  856. 0,
  857. addBytesToPointer (reservoir.getData(), block.getStart() * bytesPerFrame),
  858. channelMaps.getUnchecked (i),
  859. block.getLength());
  860. offset += block.getLength();
  861. }
  862. }
  863. ComSmartPtr<IAudioCaptureClient> captureClient;
  864. MemoryBlock reservoir;
  865. SingleThreadedAbstractFifo queue;
  866. int xruns = 0;
  867. std::unique_ptr<AudioData::Converter> converter;
  868. private:
  869. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIInputDevice)
  870. };
  871. //==============================================================================
  872. class WASAPIOutputDevice : public WASAPIDeviceBase
  873. {
  874. public:
  875. WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, WASAPIDeviceMode mode)
  876. : WASAPIDeviceBase (d, mode)
  877. {
  878. }
  879. ~WASAPIOutputDevice() override
  880. {
  881. close();
  882. }
  883. bool open (double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
  884. {
  885. return openClient (newSampleRate, newChannels, bufferSizeSamples)
  886. && (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient),
  887. (void**) renderClient.resetAndGetPointerAddress())));
  888. }
  889. void close()
  890. {
  891. closeClient();
  892. renderClient = nullptr;
  893. }
  894. template <class DestType>
  895. void updateFormatWithType (DestType*)
  896. {
  897. using NativeType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
  898. converter.reset (new AudioData::ConverterInstance<NativeType, AudioData::Pointer<DestType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst>> (1, actualNumChannels));
  899. }
  900. void updateFormat (bool isFloat) override
  901. {
  902. if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr);
  903. else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr);
  904. else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr);
  905. else updateFormatWithType ((AudioData::Int16*) nullptr);
  906. }
  907. bool start()
  908. {
  909. auto samplesToDo = getNumSamplesAvailableToCopy();
  910. uint8* outputData;
  911. if (check (renderClient->GetBuffer ((UINT32) samplesToDo, &outputData)))
  912. renderClient->ReleaseBuffer ((UINT32) samplesToDo, AUDCLNT_BUFFERFLAGS_SILENT);
  913. if (! check (client->Start()))
  914. return false;
  915. isActive = true;
  916. return true;
  917. }
  918. int getNumSamplesAvailableToCopy() const
  919. {
  920. if (numChannels <= 0)
  921. return 0;
  922. if (! isExclusiveMode (deviceMode))
  923. {
  924. UINT32 padding = 0;
  925. if (check (client->GetCurrentPadding (&padding)))
  926. return (int) actualBufferSize - (int) padding;
  927. }
  928. return (int) actualBufferSize;
  929. }
  930. void copyBuffers (const float* const* srcBuffers, int numSrcBuffers, int bufferSize,
  931. WASAPIInputDevice* inputDevice, Thread& thread)
  932. {
  933. if (numChannels <= 0)
  934. return;
  935. int offset = 0;
  936. while (bufferSize > 0)
  937. {
  938. // This is needed in order not to drop any input data if the output device endpoint buffer was full
  939. if ((! isExclusiveMode (deviceMode)) && inputDevice != nullptr
  940. && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
  941. inputDevice->handleDeviceBuffer();
  942. int samplesToDo = jmin (getNumSamplesAvailableToCopy(), bufferSize);
  943. if (samplesToDo == 0)
  944. {
  945. // This can ONLY occur in non-exclusive mode
  946. if (! thread.threadShouldExit() && WaitForSingleObject (clientEvent, 1000) == WAIT_OBJECT_0)
  947. continue;
  948. break;
  949. }
  950. if (isExclusiveMode (deviceMode) && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
  951. break;
  952. const auto numChannelsToCopy = jmin (actualNumChannels, numSrcBuffers);
  953. jassert (numChannelsToCopy <= channelMaps.size());
  954. uint8* outputData = nullptr;
  955. if (check (renderClient->GetBuffer ((UINT32) samplesToDo, &outputData)))
  956. {
  957. for (int i = 0; i < numChannelsToCopy; ++i)
  958. converter->convertSamples (outputData, channelMaps.getUnchecked (i), srcBuffers[i] + offset, 0, samplesToDo);
  959. renderClient->ReleaseBuffer ((UINT32) samplesToDo, 0);
  960. }
  961. bufferSize -= samplesToDo;
  962. offset += samplesToDo;
  963. }
  964. }
  965. ComSmartPtr<IAudioRenderClient> renderClient;
  966. std::unique_ptr<AudioData::Converter> converter;
  967. private:
  968. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIOutputDevice)
  969. };
  970. //==============================================================================
  971. class WASAPIAudioIODevice : public AudioIODevice,
  972. public Thread,
  973. private AsyncUpdater
  974. {
  975. public:
  976. WASAPIAudioIODevice (const String& deviceName,
  977. const String& typeNameIn,
  978. const String& outputDeviceID,
  979. const String& inputDeviceID,
  980. WASAPIDeviceMode mode)
  981. : AudioIODevice (deviceName, typeNameIn),
  982. Thread ("JUCE WASAPI"),
  983. outputDeviceId (outputDeviceID),
  984. inputDeviceId (inputDeviceID),
  985. deviceMode (mode)
  986. {
  987. }
  988. ~WASAPIAudioIODevice() override
  989. {
  990. cancelPendingUpdate();
  991. close();
  992. }
  993. bool initialise()
  994. {
  995. latencyIn = latencyOut = 0;
  996. Array<double> ratesIn, ratesOut;
  997. if (createDevices())
  998. {
  999. jassert (inputDevice != nullptr || outputDevice != nullptr);
  1000. sampleRates.clear();
  1001. if (inputDevice != nullptr && outputDevice != nullptr)
  1002. {
  1003. defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate);
  1004. minBufferSize = jmax (inputDevice->minBufferSize, outputDevice->minBufferSize);
  1005. defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize);
  1006. if (isLowLatencyMode (deviceMode))
  1007. {
  1008. lowLatencyMaxBufferSize = jmin (inputDevice->lowLatencyMaxBufferSize, outputDevice->lowLatencyMaxBufferSize);
  1009. lowLatencyBufferSizeMultiple = jmax (inputDevice->lowLatencyBufferSizeMultiple, outputDevice->lowLatencyBufferSizeMultiple);
  1010. }
  1011. sampleRates.addArray (inputDevice->rates);
  1012. if (supportsSampleRateConversion (deviceMode))
  1013. {
  1014. for (auto r : outputDevice->rates)
  1015. if (! sampleRates.contains (r))
  1016. sampleRates.addUsingDefaultSort (r);
  1017. }
  1018. else
  1019. {
  1020. sampleRates.removeValuesNotIn (outputDevice->rates);
  1021. }
  1022. }
  1023. else
  1024. {
  1025. auto* d = inputDevice != nullptr ? static_cast<WASAPIDeviceBase*> (inputDevice.get())
  1026. : static_cast<WASAPIDeviceBase*> (outputDevice.get());
  1027. defaultSampleRate = d->defaultSampleRate;
  1028. minBufferSize = d->minBufferSize;
  1029. defaultBufferSize = d->defaultBufferSize;
  1030. if (isLowLatencyMode (deviceMode))
  1031. {
  1032. lowLatencyMaxBufferSize = d->lowLatencyMaxBufferSize;
  1033. lowLatencyBufferSizeMultiple = d->lowLatencyBufferSizeMultiple;
  1034. }
  1035. sampleRates = d->rates;
  1036. }
  1037. bufferSizes.clear();
  1038. bufferSizes.addUsingDefaultSort (defaultBufferSize);
  1039. if (minBufferSize != defaultBufferSize)
  1040. bufferSizes.addUsingDefaultSort (minBufferSize);
  1041. if (isLowLatencyMode (deviceMode))
  1042. {
  1043. auto size = minBufferSize;
  1044. while (size < lowLatencyMaxBufferSize)
  1045. {
  1046. size += lowLatencyBufferSizeMultiple;
  1047. if (! bufferSizes.contains (size))
  1048. bufferSizes.addUsingDefaultSort (size);
  1049. }
  1050. }
  1051. else
  1052. {
  1053. int n = 64;
  1054. for (int i = 0; i < 40; ++i)
  1055. {
  1056. if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n))
  1057. bufferSizes.addUsingDefaultSort (n);
  1058. n += (n < 512) ? 32 : (n < 1024 ? 64 : 128);
  1059. }
  1060. }
  1061. return true;
  1062. }
  1063. return false;
  1064. }
  1065. StringArray getOutputChannelNames() override
  1066. {
  1067. StringArray outChannels;
  1068. if (outputDevice != nullptr)
  1069. for (int i = 1; i <= outputDevice->maxNumChannels; ++i)
  1070. outChannels.add ("Output channel " + String (i));
  1071. return outChannels;
  1072. }
  1073. StringArray getInputChannelNames() override
  1074. {
  1075. StringArray inChannels;
  1076. if (inputDevice != nullptr)
  1077. for (int i = 1; i <= inputDevice->maxNumChannels; ++i)
  1078. inChannels.add ("Input channel " + String (i));
  1079. return inChannels;
  1080. }
  1081. Array<double> getAvailableSampleRates() override { return sampleRates; }
  1082. Array<int> getAvailableBufferSizes() override { return bufferSizes; }
  1083. int getDefaultBufferSize() override { return defaultBufferSize; }
  1084. int getCurrentBufferSizeSamples() override { return currentBufferSizeSamples; }
  1085. double getCurrentSampleRate() override { return currentSampleRate; }
  1086. int getCurrentBitDepth() override { return 32; }
  1087. int getOutputLatencyInSamples() override { return latencyOut; }
  1088. int getInputLatencyInSamples() override { return latencyIn; }
  1089. BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); }
  1090. BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); }
  1091. String getLastError() override { return lastError; }
  1092. int getXRunCount() const noexcept override { return inputDevice != nullptr ? inputDevice->xruns : -1; }
  1093. std::optional<BigInteger> getDefaultOutputChannels() const override
  1094. {
  1095. return outputDevice != nullptr ? outputDevice->getDefaultLayout() : std::nullopt;
  1096. }
  1097. std::optional<BigInteger> getDefaultInputChannels() const override
  1098. {
  1099. return inputDevice != nullptr ? inputDevice->getDefaultLayout() : std::nullopt;
  1100. }
  1101. String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
  1102. double sampleRate, int bufferSizeSamples) override
  1103. {
  1104. close();
  1105. lastError.clear();
  1106. if (sampleRates.size() == 0 && inputDevice != nullptr && outputDevice != nullptr)
  1107. {
  1108. lastError = TRANS ("The input and output devices don't share a common sample rate!");
  1109. return lastError;
  1110. }
  1111. currentBufferSizeSamples = bufferSizeSamples <= 0 ? defaultBufferSize : jmax (bufferSizeSamples, minBufferSize);
  1112. currentSampleRate = sampleRate > 0 ? sampleRate : defaultSampleRate;
  1113. lastKnownInputChannels = inputChannels;
  1114. lastKnownOutputChannels = outputChannels;
  1115. if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels, bufferSizeSamples))
  1116. {
  1117. lastError = TRANS ("Couldn't open the input device!");
  1118. return lastError;
  1119. }
  1120. if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels, bufferSizeSamples))
  1121. {
  1122. close();
  1123. lastError = TRANS ("Couldn't open the output device!");
  1124. return lastError;
  1125. }
  1126. if (isExclusiveMode (deviceMode))
  1127. {
  1128. // This is to make sure that the callback uses actualBufferSize in case of exclusive mode
  1129. if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize)
  1130. {
  1131. close();
  1132. lastError = TRANS ("Couldn't open the output device (buffer size mismatch)");
  1133. return lastError;
  1134. }
  1135. currentBufferSizeSamples = (int) (outputDevice != nullptr ? outputDevice->actualBufferSize
  1136. : inputDevice->actualBufferSize);
  1137. }
  1138. if (inputDevice != nullptr) ResetEvent (inputDevice->clientEvent);
  1139. if (outputDevice != nullptr) ResetEvent (outputDevice->clientEvent);
  1140. shouldShutdown = false;
  1141. deviceSampleRateChanged = false;
  1142. startThread (Priority::high);
  1143. Thread::sleep (5);
  1144. if (inputDevice != nullptr && inputDevice->client != nullptr)
  1145. {
  1146. latencyIn = (int) (inputDevice->latencySamples + currentBufferSizeSamples);
  1147. if (! inputDevice->start (currentBufferSizeSamples))
  1148. {
  1149. close();
  1150. lastError = TRANS ("Couldn't start the input device!");
  1151. return lastError;
  1152. }
  1153. }
  1154. if (outputDevice != nullptr && outputDevice->client != nullptr)
  1155. {
  1156. latencyOut = (int) (outputDevice->latencySamples + currentBufferSizeSamples);
  1157. if (! outputDevice->start())
  1158. {
  1159. close();
  1160. lastError = TRANS ("Couldn't start the output device!");
  1161. return lastError;
  1162. }
  1163. }
  1164. isOpen_ = true;
  1165. return lastError;
  1166. }
  1167. void close() override
  1168. {
  1169. stop();
  1170. signalThreadShouldExit();
  1171. if (inputDevice != nullptr) SetEvent (inputDevice->clientEvent);
  1172. if (outputDevice != nullptr) SetEvent (outputDevice->clientEvent);
  1173. stopThread (5000);
  1174. if (inputDevice != nullptr) inputDevice->close();
  1175. if (outputDevice != nullptr) outputDevice->close();
  1176. isOpen_ = false;
  1177. }
  1178. bool isOpen() override { return isOpen_ && isThreadRunning(); }
  1179. bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); }
  1180. void start (AudioIODeviceCallback* call) override
  1181. {
  1182. if (isOpen_ && call != nullptr && ! isStarted)
  1183. {
  1184. if (! isThreadRunning())
  1185. {
  1186. // something's gone wrong and the thread's stopped..
  1187. isOpen_ = false;
  1188. return;
  1189. }
  1190. call->audioDeviceAboutToStart (this);
  1191. const ScopedLock sl (startStopLock);
  1192. callback = call;
  1193. isStarted = true;
  1194. }
  1195. }
  1196. void stop() override
  1197. {
  1198. if (isStarted)
  1199. {
  1200. auto* callbackLocal = callback;
  1201. {
  1202. const ScopedLock sl (startStopLock);
  1203. isStarted = false;
  1204. }
  1205. if (callbackLocal != nullptr)
  1206. callbackLocal->audioDeviceStopped();
  1207. }
  1208. }
  1209. void setMMThreadPriority()
  1210. {
  1211. DynamicLibrary dll ("avrt.dll");
  1212. JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadCharacteristicsW, avSetMmThreadCharacteristics, HANDLE, (LPCWSTR, LPDWORD))
  1213. JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, (HANDLE, AVRT_PRIORITY))
  1214. if (avSetMmThreadCharacteristics != nullptr && avSetMmThreadPriority != nullptr)
  1215. {
  1216. DWORD dummy = 0;
  1217. if (auto h = avSetMmThreadCharacteristics (L"Pro Audio", &dummy))
  1218. avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
  1219. }
  1220. }
  1221. void run() override
  1222. {
  1223. setMMThreadPriority();
  1224. auto bufferSize = currentBufferSizeSamples;
  1225. auto numInputBuffers = getActiveInputChannels().countNumberOfSetBits();
  1226. auto numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits();
  1227. AudioBuffer<float> ins (jmax (1, numInputBuffers), bufferSize + 32);
  1228. AudioBuffer<float> outs (jmax (1, numOutputBuffers), bufferSize + 32);
  1229. auto inputBuffers = ins.getArrayOfWritePointers();
  1230. auto outputBuffers = outs.getArrayOfWritePointers();
  1231. ins.clear();
  1232. outs.clear();
  1233. while (! threadShouldExit())
  1234. {
  1235. if ((outputDevice != nullptr && outputDevice->shouldShutdown)
  1236. || (inputDevice != nullptr && inputDevice->shouldShutdown))
  1237. {
  1238. shouldShutdown = true;
  1239. triggerAsyncUpdate();
  1240. break;
  1241. }
  1242. auto inputDeviceActive = (inputDevice != nullptr && inputDevice->isActive);
  1243. auto outputDeviceActive = (outputDevice != nullptr && outputDevice->isActive);
  1244. if (! inputDeviceActive && ! outputDeviceActive)
  1245. continue;
  1246. if (inputDeviceActive)
  1247. {
  1248. if (outputDevice == nullptr)
  1249. {
  1250. if (WaitForSingleObject (inputDevice->clientEvent, 1000) == WAIT_TIMEOUT)
  1251. break;
  1252. inputDevice->handleDeviceBuffer();
  1253. if (inputDevice->getNumSamplesInReservoir() < bufferSize)
  1254. continue;
  1255. }
  1256. else
  1257. {
  1258. if (isExclusiveMode (deviceMode) && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
  1259. inputDevice->handleDeviceBuffer();
  1260. }
  1261. inputDevice->copyBuffersFromReservoir (inputBuffers, numInputBuffers, bufferSize);
  1262. if (inputDevice->sampleRateHasChanged)
  1263. {
  1264. deviceSampleRateChanged = true;
  1265. triggerAsyncUpdate();
  1266. break;
  1267. }
  1268. }
  1269. {
  1270. const ScopedTryLock sl (startStopLock);
  1271. if (sl.isLocked() && isStarted)
  1272. callback->audioDeviceIOCallbackWithContext (inputBuffers,
  1273. numInputBuffers,
  1274. outputBuffers,
  1275. numOutputBuffers,
  1276. bufferSize,
  1277. {});
  1278. else
  1279. outs.clear();
  1280. }
  1281. if (outputDeviceActive)
  1282. {
  1283. // Note that this function is handed the input device so it can check for the event and make sure
  1284. // the input reservoir is filled up correctly even when bufferSize > device actualBufferSize
  1285. outputDevice->copyBuffers (outputBuffers, numOutputBuffers, bufferSize, inputDevice.get(), *this);
  1286. if (outputDevice->sampleRateHasChanged)
  1287. {
  1288. deviceSampleRateChanged = true;
  1289. triggerAsyncUpdate();
  1290. break;
  1291. }
  1292. }
  1293. }
  1294. }
  1295. //==============================================================================
  1296. String outputDeviceId, inputDeviceId;
  1297. String lastError;
  1298. private:
  1299. // Device stats...
  1300. std::unique_ptr<WASAPIInputDevice> inputDevice;
  1301. std::unique_ptr<WASAPIOutputDevice> outputDevice;
  1302. WASAPIDeviceMode deviceMode;
  1303. double defaultSampleRate = 0;
  1304. int minBufferSize = 0, defaultBufferSize = 0;
  1305. int lowLatencyMaxBufferSize = 0, lowLatencyBufferSizeMultiple = 0;
  1306. int latencyIn = 0, latencyOut = 0;
  1307. Array<double> sampleRates;
  1308. Array<int> bufferSizes;
  1309. // Active state...
  1310. bool isOpen_ = false, isStarted = false;
  1311. int currentBufferSizeSamples = 0;
  1312. double currentSampleRate = 0;
  1313. AudioIODeviceCallback* callback = {};
  1314. CriticalSection startStopLock;
  1315. std::atomic<bool> shouldShutdown { false }, deviceSampleRateChanged { false };
  1316. BigInteger lastKnownInputChannels, lastKnownOutputChannels;
  1317. //==============================================================================
  1318. bool createDevices()
  1319. {
  1320. ComSmartPtr<IMMDeviceEnumerator> enumerator;
  1321. if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
  1322. return false;
  1323. ComSmartPtr<IMMDeviceCollection> deviceCollection;
  1324. if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())))
  1325. return false;
  1326. UINT32 numDevices = 0;
  1327. if (! check (deviceCollection->GetCount (&numDevices)))
  1328. return false;
  1329. for (UINT32 i = 0; i < numDevices; ++i)
  1330. {
  1331. ComSmartPtr<IMMDevice> device;
  1332. if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
  1333. continue;
  1334. auto deviceId = getDeviceID (device);
  1335. if (deviceId.isEmpty())
  1336. continue;
  1337. auto flow = getDataFlow (device);
  1338. if (deviceId == inputDeviceId && flow == eCapture)
  1339. inputDevice.reset (new WASAPIInputDevice (device, deviceMode));
  1340. else if (deviceId == outputDeviceId && flow == eRender)
  1341. outputDevice.reset (new WASAPIOutputDevice (device, deviceMode));
  1342. }
  1343. return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk()))
  1344. && (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk()));
  1345. }
  1346. //==============================================================================
  1347. void handleAsyncUpdate() override
  1348. {
  1349. auto closeDevices = [this]
  1350. {
  1351. close();
  1352. outputDevice = nullptr;
  1353. inputDevice = nullptr;
  1354. };
  1355. if (shouldShutdown)
  1356. {
  1357. closeDevices();
  1358. }
  1359. else if (deviceSampleRateChanged)
  1360. {
  1361. auto sampleRateChangedByInput = (inputDevice != nullptr && inputDevice->sampleRateHasChanged);
  1362. closeDevices();
  1363. initialise();
  1364. auto changedSampleRate = [this, sampleRateChangedByInput]()
  1365. {
  1366. if (inputDevice != nullptr && sampleRateChangedByInput)
  1367. return inputDevice->defaultSampleRate;
  1368. if (outputDevice != nullptr && ! sampleRateChangedByInput)
  1369. return outputDevice->defaultSampleRate;
  1370. return 0.0;
  1371. }();
  1372. open (lastKnownInputChannels, lastKnownOutputChannels,
  1373. changedSampleRate, currentBufferSizeSamples);
  1374. start (callback);
  1375. }
  1376. }
  1377. //==============================================================================
  1378. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice)
  1379. };
  1380. //==============================================================================
  1381. class WASAPIAudioIODeviceType : public AudioIODeviceType
  1382. {
  1383. public:
  1384. explicit WASAPIAudioIODeviceType (WASAPIDeviceMode mode)
  1385. : AudioIODeviceType (getDeviceTypename (mode)),
  1386. deviceMode (mode)
  1387. {
  1388. }
  1389. ~WASAPIAudioIODeviceType() override
  1390. {
  1391. if (notifyClient != nullptr)
  1392. enumerator->UnregisterEndpointNotificationCallback (notifyClient);
  1393. }
  1394. //==============================================================================
  1395. void scanForDevices() override
  1396. {
  1397. hasScanned = true;
  1398. devices = scan();
  1399. }
  1400. StringArray getDeviceNames (bool wantInputNames) const override
  1401. {
  1402. jassert (hasScanned); // need to call scanForDevices() before doing this
  1403. return wantInputNames ? devices.inputDeviceNames
  1404. : devices.outputDeviceNames;
  1405. }
  1406. int getDefaultDeviceIndex (bool /*forInput*/) const override
  1407. {
  1408. jassert (hasScanned); // need to call scanForDevices() before doing this
  1409. return 0;
  1410. }
  1411. int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
  1412. {
  1413. jassert (hasScanned); // need to call scanForDevices() before doing this
  1414. if (auto d = dynamic_cast<WASAPIAudioIODevice*> (device))
  1415. return asInput ? devices.inputDeviceIds .indexOf (d->inputDeviceId)
  1416. : devices.outputDeviceIds.indexOf (d->outputDeviceId);
  1417. return -1;
  1418. }
  1419. bool hasSeparateInputsAndOutputs() const override { return true; }
  1420. AudioIODevice* createDevice (const String& outputDeviceName,
  1421. const String& inputDeviceName) override
  1422. {
  1423. jassert (hasScanned); // need to call scanForDevices() before doing this
  1424. std::unique_ptr<WASAPIAudioIODevice> device;
  1425. auto outputIndex = devices.outputDeviceNames.indexOf (outputDeviceName);
  1426. auto inputIndex = devices.inputDeviceNames .indexOf (inputDeviceName);
  1427. if (outputIndex >= 0 || inputIndex >= 0)
  1428. {
  1429. device.reset (new WASAPIAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
  1430. : inputDeviceName,
  1431. getTypeName(),
  1432. devices.outputDeviceIds[outputIndex],
  1433. devices.inputDeviceIds [inputIndex],
  1434. deviceMode));
  1435. if (! device->initialise())
  1436. device = nullptr;
  1437. }
  1438. return device.release();
  1439. }
  1440. //==============================================================================
  1441. struct Devices
  1442. {
  1443. StringArray outputDeviceNames, outputDeviceIds;
  1444. StringArray inputDeviceNames, inputDeviceIds;
  1445. auto tie() const
  1446. {
  1447. return std::tie (outputDeviceNames, outputDeviceIds, inputDeviceNames, inputDeviceIds);
  1448. }
  1449. bool operator== (const Devices& other) const { return tie() == other.tie(); }
  1450. bool operator!= (const Devices& other) const { return tie() != other.tie(); }
  1451. };
  1452. Devices devices;
  1453. private:
  1454. DeviceChangeDetector deviceChangeDetector { L"Windows Audio", [this] { systemDeviceChanged(); } };
  1455. WASAPIDeviceMode deviceMode;
  1456. bool hasScanned = false;
  1457. ComSmartPtr<IMMDeviceEnumerator> enumerator;
  1458. //==============================================================================
  1459. class ChangeNotificationClient : public ComBaseClassHelper<IMMNotificationClient>
  1460. {
  1461. public:
  1462. explicit ChangeNotificationClient (WASAPIAudioIODeviceType* d)
  1463. : ComBaseClassHelper (0), device (d) {}
  1464. JUCE_COMRESULT OnDeviceAdded (LPCWSTR) { return notify(); }
  1465. JUCE_COMRESULT OnDeviceRemoved (LPCWSTR) { return notify(); }
  1466. JUCE_COMRESULT OnDeviceStateChanged (LPCWSTR, DWORD) { return notify(); }
  1467. JUCE_COMRESULT OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); }
  1468. JUCE_COMRESULT OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); }
  1469. private:
  1470. WeakReference<WASAPIAudioIODeviceType> device;
  1471. HRESULT notify()
  1472. {
  1473. if (device != nullptr)
  1474. device->deviceChangeDetector.triggerAsyncDeviceChangeCallback();
  1475. return S_OK;
  1476. }
  1477. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeNotificationClient)
  1478. };
  1479. ComSmartPtr<ChangeNotificationClient> notifyClient;
  1480. //==============================================================================
  1481. static String getDefaultEndpoint (IMMDeviceEnumerator* enumerator, bool forCapture)
  1482. {
  1483. String s;
  1484. IMMDevice* dev = nullptr;
  1485. if (check (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender,
  1486. eMultimedia, &dev)))
  1487. {
  1488. WCHAR* deviceId = nullptr;
  1489. if (check (dev->GetId (&deviceId)))
  1490. {
  1491. s = deviceId;
  1492. CoTaskMemFree (deviceId);
  1493. }
  1494. dev->Release();
  1495. }
  1496. return s;
  1497. }
  1498. //==============================================================================
  1499. Devices scan()
  1500. {
  1501. if (enumerator == nullptr)
  1502. {
  1503. if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
  1504. return {};
  1505. notifyClient = new ChangeNotificationClient (this);
  1506. enumerator->RegisterEndpointNotificationCallback (notifyClient);
  1507. }
  1508. auto defaultRenderer = getDefaultEndpoint (enumerator, false);
  1509. auto defaultCapture = getDefaultEndpoint (enumerator, true);
  1510. ComSmartPtr<IMMDeviceCollection> deviceCollection;
  1511. UINT32 numDevices = 0;
  1512. if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress()))
  1513. && check (deviceCollection->GetCount (&numDevices))))
  1514. return {};
  1515. Devices result;
  1516. for (UINT32 i = 0; i < numDevices; ++i)
  1517. {
  1518. ComSmartPtr<IMMDevice> device;
  1519. if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
  1520. continue;
  1521. DWORD state = 0;
  1522. if (! (check (device->GetState (&state)) && state == DEVICE_STATE_ACTIVE))
  1523. continue;
  1524. auto deviceId = getDeviceID (device);
  1525. String name;
  1526. {
  1527. ComSmartPtr<IPropertyStore> properties;
  1528. if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress())))
  1529. continue;
  1530. PROPVARIANT value;
  1531. zerostruct (value);
  1532. const PROPERTYKEY PKEY_Device_FriendlyName
  1533. = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 14 };
  1534. if (check (properties->GetValue (PKEY_Device_FriendlyName, &value)))
  1535. name = value.pwszVal;
  1536. PropVariantClear (&value);
  1537. }
  1538. auto flow = getDataFlow (device);
  1539. if (flow == eRender)
  1540. {
  1541. const int index = (deviceId == defaultRenderer) ? 0 : -1;
  1542. result.outputDeviceIds.insert (index, deviceId);
  1543. result.outputDeviceNames.insert (index, name);
  1544. }
  1545. else if (flow == eCapture)
  1546. {
  1547. const int index = (deviceId == defaultCapture) ? 0 : -1;
  1548. result.inputDeviceIds.insert (index, deviceId);
  1549. result.inputDeviceNames.insert (index, name);
  1550. }
  1551. }
  1552. result.inputDeviceNames .appendNumbersToDuplicates (false, false);
  1553. result.outputDeviceNames.appendNumbersToDuplicates (false, false);
  1554. return result;
  1555. }
  1556. //==============================================================================
  1557. void systemDeviceChanged()
  1558. {
  1559. const auto newDevices = scan();
  1560. if (std::exchange (devices, newDevices) != newDevices)
  1561. {
  1562. hasScanned = true;
  1563. callDeviceChangeListeners();
  1564. }
  1565. }
  1566. //==============================================================================
  1567. static String getDeviceTypename (WASAPIDeviceMode mode)
  1568. {
  1569. if (mode == WASAPIDeviceMode::shared) return "Windows Audio";
  1570. if (mode == WASAPIDeviceMode::sharedLowLatency) return "Windows Audio (Low Latency Mode)";
  1571. if (mode == WASAPIDeviceMode::exclusive) return "Windows Audio (Exclusive Mode)";
  1572. jassertfalse;
  1573. return {};
  1574. }
  1575. //==============================================================================
  1576. JUCE_DECLARE_WEAK_REFERENCEABLE (WASAPIAudioIODeviceType)
  1577. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType)
  1578. };
  1579. //==============================================================================
  1580. struct MMDeviceMasterVolume
  1581. {
  1582. MMDeviceMasterVolume()
  1583. {
  1584. ComSmartPtr<IMMDeviceEnumerator> enumerator;
  1585. if (check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
  1586. {
  1587. ComSmartPtr<IMMDevice> device;
  1588. if (check (enumerator->GetDefaultAudioEndpoint (eRender, eConsole, device.resetAndGetPointerAddress())))
  1589. check (device->Activate (__uuidof (IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr,
  1590. (void**) endpointVolume.resetAndGetPointerAddress()));
  1591. }
  1592. }
  1593. float getGain() const
  1594. {
  1595. float vol = 0.0f;
  1596. if (endpointVolume != nullptr)
  1597. check (endpointVolume->GetMasterVolumeLevelScalar (&vol));
  1598. return vol;
  1599. }
  1600. bool setGain (float newGain) const
  1601. {
  1602. return endpointVolume != nullptr
  1603. && check (endpointVolume->SetMasterVolumeLevelScalar (jlimit (0.0f, 1.0f, newGain), nullptr));
  1604. }
  1605. bool isMuted() const
  1606. {
  1607. BOOL mute = 0;
  1608. return endpointVolume != nullptr
  1609. && check (endpointVolume->GetMute (&mute)) && mute != 0;
  1610. }
  1611. bool setMuted (bool shouldMute) const
  1612. {
  1613. return endpointVolume != nullptr
  1614. && check (endpointVolume->SetMute (shouldMute, nullptr));
  1615. }
  1616. ComSmartPtr<IAudioEndpointVolume> endpointVolume;
  1617. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MMDeviceMasterVolume)
  1618. };
  1619. } // namespace WasapiClasses
  1620. //==============================================================================
  1621. #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1
  1622. float JUCE_CALLTYPE SystemAudioVolume::getGain() { return WasapiClasses::MMDeviceMasterVolume().getGain(); }
  1623. bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return WasapiClasses::MMDeviceMasterVolume().setGain (gain); }
  1624. bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return WasapiClasses::MMDeviceMasterVolume().isMuted(); }
  1625. bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return WasapiClasses::MMDeviceMasterVolume().setMuted (mute); }
  1626. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  1627. } // namespace juce