Audio plugin host https://kx.studio/carla
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.

1712 lines
62KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2016 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license/
  7. Permission to use, copy, modify, and/or distribute this software for any
  8. purpose with or without fee is hereby granted, provided that the above
  9. copyright notice and this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
  11. TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  12. FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  13. OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  14. USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  15. TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  16. OF THIS SOFTWARE.
  17. -----------------------------------------------------------------------------
  18. To release a closed-source product which uses other parts of JUCE not
  19. licensed under the ISC terms, commercial licenses are available: visit
  20. www.juce.com for more information.
  21. ==============================================================================
  22. */
  23. #ifndef JUCE_WASAPI_LOGGING
  24. #define JUCE_WASAPI_LOGGING 0
  25. #endif
  26. //==============================================================================
  27. namespace WasapiClasses
  28. {
  29. void logFailure (HRESULT hr)
  30. {
  31. ignoreUnused (hr);
  32. jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from
  33. // a thread which hasn't been initialised with CoInitialize().
  34. #if JUCE_WASAPI_LOGGING
  35. if (FAILED (hr))
  36. {
  37. const char* m = nullptr;
  38. switch (hr)
  39. {
  40. case E_POINTER: m = "E_POINTER"; break;
  41. case E_INVALIDARG: m = "E_INVALIDARG"; break;
  42. case E_NOINTERFACE: m = "E_NOINTERFACE"; break;
  43. #define JUCE_WASAPI_ERR(desc, n) \
  44. case MAKE_HRESULT(1, 0x889, n): m = #desc; break;
  45. JUCE_WASAPI_ERR (AUDCLNT_E_NOT_INITIALIZED, 0x001)
  46. JUCE_WASAPI_ERR (AUDCLNT_E_ALREADY_INITIALIZED, 0x002)
  47. JUCE_WASAPI_ERR (AUDCLNT_E_WRONG_ENDPOINT_TYPE, 0x003)
  48. JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_INVALIDATED, 0x004)
  49. JUCE_WASAPI_ERR (AUDCLNT_E_NOT_STOPPED, 0x005)
  50. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_TOO_LARGE, 0x006)
  51. JUCE_WASAPI_ERR (AUDCLNT_E_OUT_OF_ORDER, 0x007)
  52. JUCE_WASAPI_ERR (AUDCLNT_E_UNSUPPORTED_FORMAT, 0x008)
  53. JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_SIZE, 0x009)
  54. JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_IN_USE, 0x00a)
  55. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_OPERATION_PENDING, 0x00b)
  56. JUCE_WASAPI_ERR (AUDCLNT_E_THREAD_NOT_REGISTERED, 0x00c)
  57. JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, 0x00e)
  58. JUCE_WASAPI_ERR (AUDCLNT_E_ENDPOINT_CREATE_FAILED, 0x00f)
  59. JUCE_WASAPI_ERR (AUDCLNT_E_SERVICE_NOT_RUNNING, 0x010)
  60. JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED, 0x011)
  61. JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_ONLY, 0x012)
  62. JUCE_WASAPI_ERR (AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL, 0x013)
  63. JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_SET, 0x014)
  64. JUCE_WASAPI_ERR (AUDCLNT_E_INCORRECT_BUFFER_SIZE, 0x015)
  65. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_ERROR, 0x016)
  66. JUCE_WASAPI_ERR (AUDCLNT_E_CPUUSAGE_EXCEEDED, 0x017)
  67. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_ERROR, 0x018)
  68. JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED, 0x019)
  69. JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_DEVICE_PERIOD, 0x020)
  70. default: break;
  71. }
  72. Logger::writeToLog ("WASAPI error: " + (m != nullptr ? String (m)
  73. : String::toHexString ((int) hr)));
  74. }
  75. #endif
  76. }
  77. #undef check
  78. bool check (HRESULT hr)
  79. {
  80. logFailure (hr);
  81. return SUCCEEDED (hr);
  82. }
  83. //==============================================================================
  84. }
  85. #if JUCE_MINGW
  86. #define JUCE_COMCLASS(name, guid) \
  87. struct name; \
  88. template<> struct UUIDGetter<name> { static CLSID get() { return uuidFromString (guid); } }; \
  89. struct name
  90. #ifdef __uuidof
  91. #undef __uuidof
  92. #endif
  93. #define __uuidof(cls) UUIDGetter<cls>::get()
  94. struct PROPERTYKEY
  95. {
  96. GUID fmtid;
  97. DWORD pid;
  98. };
  99. WINOLEAPI PropVariantClear (PROPVARIANT*);
  100. #else
  101. #define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name
  102. #endif
  103. #if JUCE_MINGW && defined (KSDATAFORMAT_SUBTYPE_PCM)
  104. #undef KSDATAFORMAT_SUBTYPE_PCM
  105. #undef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
  106. #endif
  107. #ifndef KSDATAFORMAT_SUBTYPE_PCM
  108. #define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71")
  109. #define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71")
  110. #endif
  111. #define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown
  112. #define JUCE_COMCALL virtual HRESULT STDMETHODCALLTYPE
  113. enum EDataFlow
  114. {
  115. eRender = 0,
  116. eCapture = (eRender + 1),
  117. eAll = (eCapture + 1)
  118. };
  119. enum
  120. {
  121. DEVICE_STATE_ACTIVE = 1,
  122. AUDCLNT_BUFFERFLAGS_SILENT = 2
  123. };
  124. JUCE_IUNKNOWNCLASS (IPropertyStore, "886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")
  125. {
  126. JUCE_COMCALL GetCount (DWORD*) = 0;
  127. JUCE_COMCALL GetAt (DWORD, PROPERTYKEY*) = 0;
  128. JUCE_COMCALL GetValue (const PROPERTYKEY&, PROPVARIANT*) = 0;
  129. JUCE_COMCALL SetValue (const PROPERTYKEY&, const PROPVARIANT&) = 0;
  130. JUCE_COMCALL Commit() = 0;
  131. };
  132. JUCE_IUNKNOWNCLASS (IMMDevice, "D666063F-1587-4E43-81F1-B948E807363F")
  133. {
  134. JUCE_COMCALL Activate (REFIID, DWORD, PROPVARIANT*, void**) = 0;
  135. JUCE_COMCALL OpenPropertyStore (DWORD, IPropertyStore**) = 0;
  136. JUCE_COMCALL GetId (LPWSTR*) = 0;
  137. JUCE_COMCALL GetState (DWORD*) = 0;
  138. };
  139. JUCE_IUNKNOWNCLASS (IMMEndpoint, "1BE09788-6894-4089-8586-9A2A6C265AC5")
  140. {
  141. JUCE_COMCALL GetDataFlow (EDataFlow*) = 0;
  142. };
  143. struct IMMDeviceCollection : public IUnknown
  144. {
  145. JUCE_COMCALL GetCount (UINT*) = 0;
  146. JUCE_COMCALL Item (UINT, IMMDevice**) = 0;
  147. };
  148. enum ERole
  149. {
  150. eConsole = 0,
  151. eMultimedia = (eConsole + 1),
  152. eCommunications = (eMultimedia + 1)
  153. };
  154. JUCE_IUNKNOWNCLASS (IMMNotificationClient, "7991EEC9-7E89-4D85-8390-6C703CEC60C0")
  155. {
  156. JUCE_COMCALL OnDeviceStateChanged (LPCWSTR, DWORD) = 0;
  157. JUCE_COMCALL OnDeviceAdded (LPCWSTR) = 0;
  158. JUCE_COMCALL OnDeviceRemoved (LPCWSTR) = 0;
  159. JUCE_COMCALL OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) = 0;
  160. JUCE_COMCALL OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) = 0;
  161. };
  162. JUCE_IUNKNOWNCLASS (IMMDeviceEnumerator, "A95664D2-9614-4F35-A746-DE8DB63617E6")
  163. {
  164. JUCE_COMCALL EnumAudioEndpoints (EDataFlow, DWORD, IMMDeviceCollection**) = 0;
  165. JUCE_COMCALL GetDefaultAudioEndpoint (EDataFlow, ERole, IMMDevice**) = 0;
  166. JUCE_COMCALL GetDevice (LPCWSTR, IMMDevice**) = 0;
  167. JUCE_COMCALL RegisterEndpointNotificationCallback (IMMNotificationClient*) = 0;
  168. JUCE_COMCALL UnregisterEndpointNotificationCallback (IMMNotificationClient*) = 0;
  169. };
  170. JUCE_COMCLASS (MMDeviceEnumerator, "BCDE0395-E52F-467C-8E3D-C4579291692E");
  171. typedef LONGLONG REFERENCE_TIME;
  172. enum AVRT_PRIORITY
  173. {
  174. AVRT_PRIORITY_LOW = -1,
  175. AVRT_PRIORITY_NORMAL,
  176. AVRT_PRIORITY_HIGH,
  177. AVRT_PRIORITY_CRITICAL
  178. };
  179. enum AUDCLNT_SHAREMODE
  180. {
  181. AUDCLNT_SHAREMODE_SHARED,
  182. AUDCLNT_SHAREMODE_EXCLUSIVE
  183. };
  184. JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")
  185. {
  186. JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0;
  187. JUCE_COMCALL GetBufferSize (UINT32*) = 0;
  188. JUCE_COMCALL GetStreamLatency (REFERENCE_TIME*) = 0;
  189. JUCE_COMCALL GetCurrentPadding (UINT32*) = 0;
  190. JUCE_COMCALL IsFormatSupported (AUDCLNT_SHAREMODE, const WAVEFORMATEX*, WAVEFORMATEX**) = 0;
  191. JUCE_COMCALL GetMixFormat (WAVEFORMATEX**) = 0;
  192. JUCE_COMCALL GetDevicePeriod (REFERENCE_TIME*, REFERENCE_TIME*) = 0;
  193. JUCE_COMCALL Start() = 0;
  194. JUCE_COMCALL Stop() = 0;
  195. JUCE_COMCALL Reset() = 0;
  196. JUCE_COMCALL SetEventHandle (HANDLE) = 0;
  197. JUCE_COMCALL GetService (REFIID, void**) = 0;
  198. };
  199. JUCE_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317")
  200. {
  201. JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0;
  202. JUCE_COMCALL ReleaseBuffer (UINT32) = 0;
  203. JUCE_COMCALL GetNextPacketSize (UINT32*) = 0;
  204. };
  205. JUCE_IUNKNOWNCLASS (IAudioRenderClient, "F294ACFC-3146-4483-A7BF-ADDCA7C260E2")
  206. {
  207. JUCE_COMCALL GetBuffer (UINT32, BYTE**) = 0;
  208. JUCE_COMCALL ReleaseBuffer (UINT32, DWORD) = 0;
  209. };
  210. JUCE_IUNKNOWNCLASS (IAudioEndpointVolume, "5CDF2C82-841E-4546-9722-0CF74078229A")
  211. {
  212. JUCE_COMCALL RegisterControlChangeNotify (void*) = 0;
  213. JUCE_COMCALL UnregisterControlChangeNotify (void*) = 0;
  214. JUCE_COMCALL GetChannelCount (UINT*) = 0;
  215. JUCE_COMCALL SetMasterVolumeLevel (float, LPCGUID) = 0;
  216. JUCE_COMCALL SetMasterVolumeLevelScalar (float, LPCGUID) = 0;
  217. JUCE_COMCALL GetMasterVolumeLevel (float*) = 0;
  218. JUCE_COMCALL GetMasterVolumeLevelScalar (float*) = 0;
  219. JUCE_COMCALL SetChannelVolumeLevel (UINT, float, LPCGUID) = 0;
  220. JUCE_COMCALL SetChannelVolumeLevelScalar (UINT, float, LPCGUID) = 0;
  221. JUCE_COMCALL GetChannelVolumeLevel (UINT, float*) = 0;
  222. JUCE_COMCALL GetChannelVolumeLevelScalar (UINT, float*) = 0;
  223. JUCE_COMCALL SetMute (BOOL, LPCGUID) = 0;
  224. JUCE_COMCALL GetMute (BOOL*) = 0;
  225. JUCE_COMCALL GetVolumeStepInfo (UINT*, UINT*) = 0;
  226. JUCE_COMCALL VolumeStepUp (LPCGUID) = 0;
  227. JUCE_COMCALL VolumeStepDown (LPCGUID) = 0;
  228. JUCE_COMCALL QueryHardwareSupport (DWORD*) = 0;
  229. JUCE_COMCALL GetVolumeRange (float*, float*, float*) = 0;
  230. };
  231. enum AudioSessionDisconnectReason
  232. {
  233. DisconnectReasonDeviceRemoval = 0,
  234. DisconnectReasonServerShutdown = 1,
  235. DisconnectReasonFormatChanged = 2,
  236. DisconnectReasonSessionLogoff = 3,
  237. DisconnectReasonSessionDisconnected = 4,
  238. DisconnectReasonExclusiveModeOverride = 5
  239. };
  240. enum AudioSessionState
  241. {
  242. AudioSessionStateInactive = 0,
  243. AudioSessionStateActive = 1,
  244. AudioSessionStateExpired = 2
  245. };
  246. JUCE_IUNKNOWNCLASS (IAudioSessionEvents, "24918ACC-64B3-37C1-8CA9-74A66E9957A8")
  247. {
  248. JUCE_COMCALL OnDisplayNameChanged (LPCWSTR, LPCGUID) = 0;
  249. JUCE_COMCALL OnIconPathChanged (LPCWSTR, LPCGUID) = 0;
  250. JUCE_COMCALL OnSimpleVolumeChanged (float, BOOL, LPCGUID) = 0;
  251. JUCE_COMCALL OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) = 0;
  252. JUCE_COMCALL OnGroupingParamChanged (LPCGUID, LPCGUID) = 0;
  253. JUCE_COMCALL OnStateChanged (AudioSessionState) = 0;
  254. JUCE_COMCALL OnSessionDisconnected (AudioSessionDisconnectReason) = 0;
  255. };
  256. JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD")
  257. {
  258. JUCE_COMCALL GetState (AudioSessionState*) = 0;
  259. JUCE_COMCALL GetDisplayName (LPWSTR*) = 0;
  260. JUCE_COMCALL SetDisplayName (LPCWSTR, LPCGUID) = 0;
  261. JUCE_COMCALL GetIconPath (LPWSTR*) = 0;
  262. JUCE_COMCALL SetIconPath (LPCWSTR, LPCGUID) = 0;
  263. JUCE_COMCALL GetGroupingParam (GUID*) = 0;
  264. JUCE_COMCALL SetGroupingParam (LPCGUID, LPCGUID) = 0;
  265. JUCE_COMCALL RegisterAudioSessionNotification (IAudioSessionEvents*) = 0;
  266. JUCE_COMCALL UnregisterAudioSessionNotification (IAudioSessionEvents*) = 0;
  267. };
  268. #undef JUCE_COMCALL
  269. #undef JUCE_COMCLASS
  270. #undef JUCE_IUNKNOWNCLASS
  271. //==============================================================================
  272. namespace WasapiClasses
  273. {
  274. String getDeviceID (IMMDevice* const device)
  275. {
  276. String s;
  277. WCHAR* deviceId = nullptr;
  278. if (check (device->GetId (&deviceId)))
  279. {
  280. s = String (deviceId);
  281. CoTaskMemFree (deviceId);
  282. }
  283. return s;
  284. }
  285. EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
  286. {
  287. EDataFlow flow = eRender;
  288. ComSmartPtr<IMMEndpoint> endPoint;
  289. if (check (device.QueryInterface (endPoint)))
  290. (void) check (endPoint->GetDataFlow (&flow));
  291. return flow;
  292. }
  293. int refTimeToSamples (const REFERENCE_TIME& t, const double sampleRate) noexcept
  294. {
  295. return roundToInt (sampleRate * ((double) t) * 0.0000001);
  296. }
  297. REFERENCE_TIME samplesToRefTime (const int numSamples, const double sampleRate) noexcept
  298. {
  299. return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5);
  300. }
  301. void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) noexcept
  302. {
  303. memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE)
  304. : sizeof (WAVEFORMATEX));
  305. }
  306. //==============================================================================
  307. class WASAPIDeviceBase
  308. {
  309. public:
  310. WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
  311. : device (d),
  312. sampleRate (0),
  313. defaultSampleRate (0),
  314. numChannels (0),
  315. actualNumChannels (0),
  316. minBufferSize (0),
  317. defaultBufferSize (0),
  318. latencySamples (0),
  319. useExclusiveMode (exclusiveMode),
  320. actualBufferSize (0),
  321. bytesPerSample (0),
  322. bytesPerFrame (0),
  323. sampleRateHasChanged (false)
  324. {
  325. clientEvent = CreateEvent (nullptr, false, false, nullptr);
  326. ComSmartPtr<IAudioClient> tempClient (createClient());
  327. if (tempClient == nullptr)
  328. return;
  329. REFERENCE_TIME defaultPeriod, minPeriod;
  330. if (! check (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod)))
  331. return;
  332. WAVEFORMATEX* mixFormat = nullptr;
  333. if (! check (tempClient->GetMixFormat (&mixFormat)))
  334. return;
  335. WAVEFORMATEXTENSIBLE format;
  336. copyWavFormat (format, mixFormat);
  337. CoTaskMemFree (mixFormat);
  338. actualNumChannels = numChannels = format.Format.nChannels;
  339. defaultSampleRate = format.Format.nSamplesPerSec;
  340. minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate);
  341. defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate);
  342. mixFormatChannelMask = format.dwChannelMask;
  343. rates.addUsingDefaultSort (defaultSampleRate);
  344. if (useExclusiveMode
  345. && findSupportedFormat (tempClient, defaultSampleRate, format.dwChannelMask, format))
  346. {
  347. // Got a format that is supported by the device so we can ask what sample rates are supported (in whatever format)
  348. }
  349. static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
  350. for (int i = 0; i < numElementsInArray (ratesToTest); ++i)
  351. {
  352. if (rates.contains (ratesToTest[i]))
  353. continue;
  354. format.Format.nSamplesPerSec = (DWORD) ratesToTest[i];
  355. format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8);
  356. if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
  357. : AUDCLNT_SHAREMODE_SHARED,
  358. (WAVEFORMATEX*) &format, 0)))
  359. if (! rates.contains (ratesToTest[i]))
  360. rates.addUsingDefaultSort (ratesToTest[i]);
  361. }
  362. }
  363. virtual ~WASAPIDeviceBase()
  364. {
  365. device = nullptr;
  366. CloseHandle (clientEvent);
  367. }
  368. bool isOk() const noexcept { return defaultBufferSize > 0 && defaultSampleRate > 0; }
  369. bool openClient (const double newSampleRate, const BigInteger& newChannels, const int bufferSizeSamples)
  370. {
  371. sampleRate = newSampleRate;
  372. channels = newChannels;
  373. channels.setRange (actualNumChannels, channels.getHighestBit() + 1 - actualNumChannels, false);
  374. numChannels = channels.getHighestBit() + 1;
  375. if (numChannels == 0)
  376. return true;
  377. client = createClient();
  378. if (client != nullptr
  379. && tryInitialisingWithBufferSize (bufferSizeSamples))
  380. {
  381. sampleRateHasChanged = false;
  382. channelMaps.clear();
  383. for (int i = 0; i <= channels.getHighestBit(); ++i)
  384. if (channels[i])
  385. channelMaps.add (i);
  386. REFERENCE_TIME latency;
  387. if (check (client->GetStreamLatency (&latency)))
  388. latencySamples = refTimeToSamples (latency, sampleRate);
  389. (void) check (client->GetBufferSize (&actualBufferSize));
  390. createSessionEventCallback();
  391. return check (client->SetEventHandle (clientEvent));
  392. }
  393. return false;
  394. }
  395. void closeClient()
  396. {
  397. if (client != nullptr)
  398. client->Stop();
  399. deleteSessionEventCallback();
  400. client = nullptr;
  401. ResetEvent (clientEvent);
  402. }
  403. void deviceSampleRateChanged()
  404. {
  405. sampleRateHasChanged = true;
  406. }
  407. //==============================================================================
  408. ComSmartPtr<IMMDevice> device;
  409. ComSmartPtr<IAudioClient> client;
  410. double sampleRate, defaultSampleRate;
  411. int numChannels, actualNumChannels;
  412. int minBufferSize, defaultBufferSize, latencySamples;
  413. DWORD mixFormatChannelMask;
  414. const bool useExclusiveMode;
  415. Array<double> rates;
  416. HANDLE clientEvent;
  417. BigInteger channels;
  418. Array<int> channelMaps;
  419. UINT32 actualBufferSize;
  420. int bytesPerSample, bytesPerFrame;
  421. bool sampleRateHasChanged;
  422. virtual void updateFormat (bool isFloat) = 0;
  423. private:
  424. //==============================================================================
  425. class SessionEventCallback : public ComBaseClassHelper<IAudioSessionEvents>
  426. {
  427. public:
  428. SessionEventCallback (WASAPIDeviceBase& d) : owner (d) {}
  429. JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) { return S_OK; }
  430. JUCE_COMRESULT OnIconPathChanged (LPCWSTR, LPCGUID) { return S_OK; }
  431. JUCE_COMRESULT OnSimpleVolumeChanged (float, BOOL, LPCGUID) { return S_OK; }
  432. JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; }
  433. JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; }
  434. JUCE_COMRESULT OnStateChanged (AudioSessionState) { return S_OK; }
  435. JUCE_COMRESULT OnSessionDisconnected (AudioSessionDisconnectReason reason)
  436. {
  437. if (reason == DisconnectReasonFormatChanged)
  438. owner.deviceSampleRateChanged();
  439. return S_OK;
  440. }
  441. private:
  442. WASAPIDeviceBase& owner;
  443. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SessionEventCallback)
  444. };
  445. ComSmartPtr<IAudioSessionControl> audioSessionControl;
  446. ComSmartPtr<SessionEventCallback> sessionEventCallback;
  447. void createSessionEventCallback()
  448. {
  449. deleteSessionEventCallback();
  450. client->GetService (__uuidof (IAudioSessionControl),
  451. (void**) audioSessionControl.resetAndGetPointerAddress());
  452. if (audioSessionControl != nullptr)
  453. {
  454. sessionEventCallback = new SessionEventCallback (*this);
  455. audioSessionControl->RegisterAudioSessionNotification (sessionEventCallback);
  456. sessionEventCallback->Release(); // (required because ComBaseClassHelper objects are constructed with a ref count of 1)
  457. }
  458. }
  459. void deleteSessionEventCallback()
  460. {
  461. if (audioSessionControl != nullptr && sessionEventCallback != nullptr)
  462. audioSessionControl->UnregisterAudioSessionNotification (sessionEventCallback);
  463. audioSessionControl = nullptr;
  464. sessionEventCallback = nullptr;
  465. }
  466. //==============================================================================
  467. ComSmartPtr<IAudioClient> createClient()
  468. {
  469. ComSmartPtr<IAudioClient> newClient;
  470. if (device != nullptr)
  471. logFailure (device->Activate (__uuidof (IAudioClient), CLSCTX_INPROC_SERVER,
  472. nullptr, (void**) newClient.resetAndGetPointerAddress()));
  473. return newClient;
  474. }
  475. struct AudioSampleFormat
  476. {
  477. bool useFloat;
  478. int bitsPerSampleToTry;
  479. int bytesPerSampleContainer;
  480. };
  481. bool tryFormat (const AudioSampleFormat sampleFormat, IAudioClient* clientToUse, double newSampleRate,
  482. DWORD newMixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const
  483. {
  484. zerostruct (format);
  485. if (numChannels <= 2 && sampleFormat.bitsPerSampleToTry <= 16)
  486. {
  487. format.Format.wFormatTag = WAVE_FORMAT_PCM;
  488. }
  489. else
  490. {
  491. format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  492. format.Format.cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
  493. }
  494. format.Format.nSamplesPerSec = (DWORD) newSampleRate;
  495. format.Format.nChannels = (WORD) numChannels;
  496. format.Format.wBitsPerSample = (WORD) (8 * sampleFormat.bytesPerSampleContainer);
  497. format.Samples.wValidBitsPerSample = (WORD) (sampleFormat.bitsPerSampleToTry);
  498. format.Format.nBlockAlign = (WORD) (format.Format.nChannels * format.Format.wBitsPerSample / 8);
  499. format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign);
  500. format.SubFormat = sampleFormat.useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
  501. format.dwChannelMask = newMixFormatChannelMask;
  502. WAVEFORMATEXTENSIBLE* nearestFormat = nullptr;
  503. HRESULT hr = clientToUse->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
  504. : AUDCLNT_SHAREMODE_SHARED,
  505. (WAVEFORMATEX*) &format,
  506. useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat);
  507. logFailure (hr);
  508. if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec)
  509. {
  510. copyWavFormat (format, (const WAVEFORMATEX*) nearestFormat);
  511. hr = S_OK;
  512. }
  513. CoTaskMemFree (nearestFormat);
  514. return check (hr);
  515. }
  516. bool findSupportedFormat (IAudioClient* clientToUse, double newSampleRate,
  517. DWORD newMixFormatChannelMask, WAVEFORMATEXTENSIBLE& format) const
  518. {
  519. static const AudioSampleFormat formats[] =
  520. {
  521. { true, 32, 4 },
  522. { false, 32, 4 },
  523. { false, 24, 4 },
  524. { false, 24, 3 },
  525. { false, 20, 4 },
  526. { false, 20, 3 },
  527. { false, 16, 2 }
  528. };
  529. for (int i = 0; i < numElementsInArray (formats); ++i)
  530. if (tryFormat (formats[i], clientToUse, newSampleRate, newMixFormatChannelMask, format))
  531. return true;
  532. return false;
  533. }
  534. bool tryInitialisingWithBufferSize (const int bufferSizeSamples)
  535. {
  536. WAVEFORMATEXTENSIBLE format;
  537. if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format))
  538. {
  539. REFERENCE_TIME defaultPeriod = 0, minPeriod = 0;
  540. check (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
  541. if (useExclusiveMode && bufferSizeSamples > 0)
  542. defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec));
  543. for (;;)
  544. {
  545. GUID session;
  546. HRESULT hr = client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
  547. 0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
  548. defaultPeriod, useExclusiveMode ? defaultPeriod : 0, (WAVEFORMATEX*) &format, &session);
  549. if (check (hr))
  550. {
  551. actualNumChannels = format.Format.nChannels;
  552. const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  553. bytesPerSample = format.Format.wBitsPerSample / 8;
  554. bytesPerFrame = format.Format.nBlockAlign;
  555. updateFormat (isFloat);
  556. return true;
  557. }
  558. // Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks)
  559. if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
  560. break;
  561. UINT32 numFrames = 0;
  562. if (! check (client->GetBufferSize (&numFrames)))
  563. break;
  564. // Recreate client
  565. client = nullptr;
  566. client = createClient();
  567. defaultPeriod = samplesToRefTime (numFrames, format.Format.nSamplesPerSec);
  568. }
  569. }
  570. return false;
  571. }
  572. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIDeviceBase)
  573. };
  574. //==============================================================================
  575. class WASAPIInputDevice : public WASAPIDeviceBase
  576. {
  577. public:
  578. WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
  579. : WASAPIDeviceBase (d, exclusiveMode),
  580. reservoir (1, 1)
  581. {
  582. }
  583. ~WASAPIInputDevice()
  584. {
  585. close();
  586. }
  587. bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
  588. {
  589. return openClient (newSampleRate, newChannels, bufferSizeSamples)
  590. && (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient),
  591. (void**) captureClient.resetAndGetPointerAddress())));
  592. }
  593. void close()
  594. {
  595. closeClient();
  596. captureClient = nullptr;
  597. reservoir.reset();
  598. reservoirReadPos = reservoirWritePos = 0;
  599. }
  600. template<class SourceType>
  601. void updateFormatWithType (SourceType*) noexcept
  602. {
  603. typedef AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> NativeType;
  604. converter = new AudioData::ConverterInstance<AudioData::Pointer<SourceType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, NativeType> (actualNumChannels, 1);
  605. }
  606. void updateFormat (bool isFloat) override
  607. {
  608. if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr);
  609. else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr);
  610. else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr);
  611. else updateFormatWithType ((AudioData::Int16*) nullptr);
  612. }
  613. bool start (const int userBufferSize)
  614. {
  615. reservoirSize = actualBufferSize + userBufferSize;
  616. reservoirMask = nextPowerOfTwo (reservoirSize) - 1;
  617. reservoir.setSize ((reservoirMask + 1) * bytesPerFrame, true);
  618. reservoirReadPos = reservoirWritePos = 0;
  619. if (! check (client->Start()))
  620. return false;
  621. purgeInputBuffers();
  622. return true;
  623. }
  624. void purgeInputBuffers()
  625. {
  626. uint8* inputData;
  627. UINT32 numSamplesAvailable;
  628. DWORD flags;
  629. while (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)
  630. != MAKE_HRESULT (0, 0x889, 0x1) /* AUDCLNT_S_BUFFER_EMPTY */)
  631. captureClient->ReleaseBuffer (numSamplesAvailable);
  632. }
  633. int getNumSamplesInReservoir() const noexcept { return reservoirWritePos - reservoirReadPos; }
  634. void handleDeviceBuffer()
  635. {
  636. if (numChannels <= 0)
  637. return;
  638. uint8* inputData;
  639. UINT32 numSamplesAvailable;
  640. DWORD flags;
  641. while (check (captureClient->GetBuffer (&inputData, &numSamplesAvailable, &flags, nullptr, nullptr)) && numSamplesAvailable > 0)
  642. {
  643. int samplesLeft = (int) numSamplesAvailable;
  644. while (samplesLeft > 0)
  645. {
  646. const int localWrite = reservoirWritePos & reservoirMask;
  647. const int samplesToDo = jmin (samplesLeft, reservoirMask + 1 - localWrite);
  648. const int samplesToDoBytes = samplesToDo * bytesPerFrame;
  649. void* reservoirPtr = addBytesToPointer (reservoir.getData(), localWrite * bytesPerFrame);
  650. if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) != 0)
  651. zeromem (reservoirPtr, samplesToDoBytes);
  652. else
  653. memcpy (reservoirPtr, inputData, samplesToDoBytes);
  654. reservoirWritePos += samplesToDo;
  655. inputData += samplesToDoBytes;
  656. samplesLeft -= samplesToDo;
  657. }
  658. if (getNumSamplesInReservoir() > reservoirSize)
  659. reservoirReadPos = reservoirWritePos - reservoirSize;
  660. captureClient->ReleaseBuffer (numSamplesAvailable);
  661. }
  662. }
  663. void copyBuffersFromReservoir (float** destBuffers, int numDestBuffers, int bufferSize)
  664. {
  665. if ((numChannels <= 0 && bufferSize == 0) || reservoir.getSize() == 0)
  666. return;
  667. int offset = jmax (0, bufferSize - getNumSamplesInReservoir());
  668. if (offset > 0)
  669. {
  670. for (int i = 0; i < numDestBuffers; ++i)
  671. zeromem (destBuffers[i], offset * sizeof (float));
  672. bufferSize -= offset;
  673. reservoirReadPos -= offset / 2;
  674. }
  675. while (bufferSize > 0)
  676. {
  677. const int localRead = reservoirReadPos & reservoirMask;
  678. const int samplesToDo = jmin (bufferSize, getNumSamplesInReservoir(), reservoirMask + 1 - localRead);
  679. if (samplesToDo <= 0)
  680. break;
  681. const int reservoirOffset = localRead * bytesPerFrame;
  682. for (int i = 0; i < numDestBuffers; ++i)
  683. converter->convertSamples (destBuffers[i] + offset, 0, addBytesToPointer (reservoir.getData(), reservoirOffset), channelMaps.getUnchecked(i), samplesToDo);
  684. bufferSize -= samplesToDo;
  685. offset += samplesToDo;
  686. reservoirReadPos += samplesToDo;
  687. }
  688. }
  689. ComSmartPtr<IAudioCaptureClient> captureClient;
  690. MemoryBlock reservoir;
  691. int reservoirSize, reservoirMask;
  692. volatile int reservoirReadPos, reservoirWritePos;
  693. ScopedPointer<AudioData::Converter> converter;
  694. private:
  695. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIInputDevice)
  696. };
  697. //==============================================================================
  698. class WASAPIOutputDevice : public WASAPIDeviceBase
  699. {
  700. public:
  701. WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, const bool exclusiveMode)
  702. : WASAPIDeviceBase (d, exclusiveMode)
  703. {
  704. }
  705. ~WASAPIOutputDevice()
  706. {
  707. close();
  708. }
  709. bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
  710. {
  711. return openClient (newSampleRate, newChannels, bufferSizeSamples)
  712. && (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient),
  713. (void**) renderClient.resetAndGetPointerAddress())));
  714. }
  715. void close()
  716. {
  717. closeClient();
  718. renderClient = nullptr;
  719. }
  720. template<class DestType>
  721. void updateFormatWithType (DestType*)
  722. {
  723. typedef AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> NativeType;
  724. converter = new AudioData::ConverterInstance<NativeType, AudioData::Pointer<DestType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, actualNumChannels);
  725. }
  726. void updateFormat (bool isFloat) override
  727. {
  728. if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr);
  729. else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr);
  730. else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr);
  731. else updateFormatWithType ((AudioData::Int16*) nullptr);
  732. }
  733. bool start()
  734. {
  735. int samplesToDo = getNumSamplesAvailableToCopy();
  736. uint8* outputData;
  737. if (check (renderClient->GetBuffer (samplesToDo, &outputData)))
  738. renderClient->ReleaseBuffer (samplesToDo, AUDCLNT_BUFFERFLAGS_SILENT);
  739. return check (client->Start());
  740. }
  741. int getNumSamplesAvailableToCopy() const
  742. {
  743. if (numChannels <= 0)
  744. return 0;
  745. if (! useExclusiveMode)
  746. {
  747. UINT32 padding = 0;
  748. if (check (client->GetCurrentPadding (&padding)))
  749. return actualBufferSize - (int) padding;
  750. }
  751. return actualBufferSize;
  752. }
  753. void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize,
  754. WASAPIInputDevice* inputDevice, Thread& thread)
  755. {
  756. if (numChannels <= 0)
  757. return;
  758. int offset = 0;
  759. while (bufferSize > 0)
  760. {
  761. // This is needed in order not to drop any input data if the output device endpoint buffer was full
  762. if ((! useExclusiveMode) && inputDevice != nullptr
  763. && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
  764. inputDevice->handleDeviceBuffer();
  765. int samplesToDo = jmin (getNumSamplesAvailableToCopy(), bufferSize);
  766. if (samplesToDo == 0)
  767. {
  768. // This can ONLY occur in non-exclusive mode
  769. if (! thread.threadShouldExit() && WaitForSingleObject (clientEvent, 1000) == WAIT_OBJECT_0)
  770. continue;
  771. break;
  772. }
  773. if (useExclusiveMode && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
  774. break;
  775. uint8* outputData = nullptr;
  776. if (check (renderClient->GetBuffer ((UINT32) samplesToDo, &outputData)))
  777. {
  778. for (int i = 0; i < numSrcBuffers; ++i)
  779. converter->convertSamples (outputData, channelMaps.getUnchecked(i), srcBuffers[i] + offset, 0, samplesToDo);
  780. renderClient->ReleaseBuffer ((UINT32) samplesToDo, 0);
  781. }
  782. bufferSize -= samplesToDo;
  783. offset += samplesToDo;
  784. }
  785. }
  786. ComSmartPtr<IAudioRenderClient> renderClient;
  787. ScopedPointer<AudioData::Converter> converter;
  788. private:
  789. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIOutputDevice)
  790. };
  791. //==============================================================================
  792. class WASAPIAudioIODevice : public AudioIODevice,
  793. public Thread,
  794. private AsyncUpdater
  795. {
  796. public:
  797. WASAPIAudioIODevice (const String& deviceName,
  798. const String& typeName,
  799. const String& outputDeviceID,
  800. const String& inputDeviceID,
  801. const bool exclusiveMode)
  802. : AudioIODevice (deviceName, typeName),
  803. Thread ("Juce WASAPI"),
  804. outputDeviceId (outputDeviceID),
  805. inputDeviceId (inputDeviceID),
  806. useExclusiveMode (exclusiveMode),
  807. isOpen_ (false),
  808. isStarted (false),
  809. currentBufferSizeSamples (0),
  810. currentSampleRate (0),
  811. callback (nullptr)
  812. {
  813. }
  814. ~WASAPIAudioIODevice()
  815. {
  816. close();
  817. }
  818. bool initialise()
  819. {
  820. latencyIn = latencyOut = 0;
  821. Array<double> ratesIn, ratesOut;
  822. if (createDevices())
  823. {
  824. jassert (inputDevice != nullptr || outputDevice != nullptr);
  825. if (inputDevice != nullptr && outputDevice != nullptr)
  826. {
  827. defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate);
  828. minBufferSize = jmin (inputDevice->minBufferSize, outputDevice->minBufferSize);
  829. defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize);
  830. sampleRates = inputDevice->rates;
  831. sampleRates.removeValuesNotIn (outputDevice->rates);
  832. }
  833. else
  834. {
  835. WASAPIDeviceBase* d = inputDevice != nullptr ? static_cast<WASAPIDeviceBase*> (inputDevice)
  836. : static_cast<WASAPIDeviceBase*> (outputDevice);
  837. defaultSampleRate = d->defaultSampleRate;
  838. minBufferSize = d->minBufferSize;
  839. defaultBufferSize = d->defaultBufferSize;
  840. sampleRates = d->rates;
  841. }
  842. bufferSizes.addUsingDefaultSort (defaultBufferSize);
  843. if (minBufferSize != defaultBufferSize)
  844. bufferSizes.addUsingDefaultSort (minBufferSize);
  845. int n = 64;
  846. for (int i = 0; i < 40; ++i)
  847. {
  848. if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n))
  849. bufferSizes.addUsingDefaultSort (n);
  850. n += (n < 512) ? 32 : (n < 1024 ? 64 : 128);
  851. }
  852. return true;
  853. }
  854. return false;
  855. }
  856. StringArray getOutputChannelNames() override
  857. {
  858. StringArray outChannels;
  859. if (outputDevice != nullptr)
  860. for (int i = 1; i <= outputDevice->actualNumChannels; ++i)
  861. outChannels.add ("Output channel " + String (i));
  862. return outChannels;
  863. }
  864. StringArray getInputChannelNames() override
  865. {
  866. StringArray inChannels;
  867. if (inputDevice != nullptr)
  868. for (int i = 1; i <= inputDevice->actualNumChannels; ++i)
  869. inChannels.add ("Input channel " + String (i));
  870. return inChannels;
  871. }
  872. Array<double> getAvailableSampleRates() override { return sampleRates; }
  873. Array<int> getAvailableBufferSizes() override { return bufferSizes; }
  874. int getDefaultBufferSize() override { return defaultBufferSize; }
  875. int getCurrentBufferSizeSamples() override { return currentBufferSizeSamples; }
  876. double getCurrentSampleRate() override { return currentSampleRate; }
  877. int getCurrentBitDepth() override { return 32; }
  878. int getOutputLatencyInSamples() override { return latencyOut; }
  879. int getInputLatencyInSamples() override { return latencyIn; }
  880. BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); }
  881. BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); }
  882. String getLastError() override { return lastError; }
  883. String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
  884. double sampleRate, int bufferSizeSamples) override
  885. {
  886. close();
  887. lastError.clear();
  888. if (sampleRates.size() == 0 && inputDevice != nullptr && outputDevice != nullptr)
  889. {
  890. lastError = TRANS("The input and output devices don't share a common sample rate!");
  891. return lastError;
  892. }
  893. currentBufferSizeSamples = bufferSizeSamples <= 0 ? defaultBufferSize : jmax (bufferSizeSamples, minBufferSize);
  894. currentSampleRate = sampleRate > 0 ? sampleRate : defaultSampleRate;
  895. lastKnownInputChannels = inputChannels;
  896. lastKnownOutputChannels = outputChannels;
  897. if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels, bufferSizeSamples))
  898. {
  899. lastError = TRANS("Couldn't open the input device!");
  900. return lastError;
  901. }
  902. if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels, bufferSizeSamples))
  903. {
  904. close();
  905. lastError = TRANS("Couldn't open the output device!");
  906. return lastError;
  907. }
  908. if (useExclusiveMode)
  909. {
  910. // This is to make sure that the callback uses actualBufferSize in case of exclusive mode
  911. if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize)
  912. {
  913. close();
  914. lastError = TRANS("Couldn't open the output device (buffer size mismatch)");
  915. return lastError;
  916. }
  917. currentBufferSizeSamples = outputDevice != nullptr ? outputDevice->actualBufferSize
  918. : inputDevice->actualBufferSize;
  919. }
  920. if (inputDevice != nullptr) ResetEvent (inputDevice->clientEvent);
  921. if (outputDevice != nullptr) ResetEvent (outputDevice->clientEvent);
  922. startThread (8);
  923. Thread::sleep (5);
  924. if (inputDevice != nullptr && inputDevice->client != nullptr)
  925. {
  926. latencyIn = (int) (inputDevice->latencySamples + currentBufferSizeSamples);
  927. if (! inputDevice->start (currentBufferSizeSamples))
  928. {
  929. close();
  930. lastError = TRANS("Couldn't start the input device!");
  931. return lastError;
  932. }
  933. }
  934. if (outputDevice != nullptr && outputDevice->client != nullptr)
  935. {
  936. latencyOut = (int) (outputDevice->latencySamples + currentBufferSizeSamples);
  937. if (! outputDevice->start())
  938. {
  939. close();
  940. lastError = TRANS("Couldn't start the output device!");
  941. return lastError;
  942. }
  943. }
  944. isOpen_ = true;
  945. return lastError;
  946. }
  947. void close() override
  948. {
  949. stop();
  950. signalThreadShouldExit();
  951. if (inputDevice != nullptr) SetEvent (inputDevice->clientEvent);
  952. if (outputDevice != nullptr) SetEvent (outputDevice->clientEvent);
  953. stopThread (5000);
  954. if (inputDevice != nullptr) inputDevice->close();
  955. if (outputDevice != nullptr) outputDevice->close();
  956. isOpen_ = false;
  957. }
  958. bool isOpen() override { return isOpen_ && isThreadRunning(); }
  959. bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); }
  960. void start (AudioIODeviceCallback* call) override
  961. {
  962. if (isOpen_ && call != nullptr && ! isStarted)
  963. {
  964. if (! isThreadRunning())
  965. {
  966. // something's gone wrong and the thread's stopped..
  967. isOpen_ = false;
  968. return;
  969. }
  970. call->audioDeviceAboutToStart (this);
  971. const ScopedLock sl (startStopLock);
  972. callback = call;
  973. isStarted = true;
  974. }
  975. }
  976. void stop() override
  977. {
  978. if (isStarted)
  979. {
  980. AudioIODeviceCallback* const callbackLocal = callback;
  981. {
  982. const ScopedLock sl (startStopLock);
  983. isStarted = false;
  984. }
  985. if (callbackLocal != nullptr)
  986. callbackLocal->audioDeviceStopped();
  987. }
  988. }
  989. void setMMThreadPriority()
  990. {
  991. DynamicLibrary dll ("avrt.dll");
  992. JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadCharacteristicsW, avSetMmThreadCharacteristics, HANDLE, (LPCWSTR, LPDWORD))
  993. JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, (HANDLE, AVRT_PRIORITY))
  994. if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0)
  995. {
  996. DWORD dummy = 0;
  997. HANDLE h = avSetMmThreadCharacteristics (L"Pro Audio", &dummy);
  998. if (h != 0)
  999. avSetMmThreadPriority (h, AVRT_PRIORITY_NORMAL);
  1000. }
  1001. }
  1002. void run() override
  1003. {
  1004. setMMThreadPriority();
  1005. const int bufferSize = currentBufferSizeSamples;
  1006. const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits();
  1007. const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits();
  1008. bool sampleRateHasChanged = false;
  1009. AudioSampleBuffer ins (jmax (1, numInputBuffers), bufferSize + 32);
  1010. AudioSampleBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32);
  1011. float** const inputBuffers = ins.getArrayOfWritePointers();
  1012. float** const outputBuffers = outs.getArrayOfWritePointers();
  1013. ins.clear();
  1014. outs.clear();
  1015. while (! threadShouldExit())
  1016. {
  1017. if (inputDevice != nullptr)
  1018. {
  1019. if (outputDevice == nullptr)
  1020. {
  1021. if (WaitForSingleObject (inputDevice->clientEvent, 1000) == WAIT_TIMEOUT)
  1022. break;
  1023. inputDevice->handleDeviceBuffer();
  1024. if (inputDevice->getNumSamplesInReservoir() < bufferSize)
  1025. continue;
  1026. }
  1027. else
  1028. {
  1029. if (useExclusiveMode && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0)
  1030. inputDevice->handleDeviceBuffer();
  1031. }
  1032. inputDevice->copyBuffersFromReservoir (inputBuffers, numInputBuffers, bufferSize);
  1033. if (inputDevice->sampleRateHasChanged)
  1034. {
  1035. sampleRateHasChanged = true;
  1036. sampleRateChangedByOutput = false;
  1037. }
  1038. }
  1039. {
  1040. const ScopedTryLock sl (startStopLock);
  1041. if (sl.isLocked() && isStarted)
  1042. callback->audioDeviceIOCallback (const_cast<const float**> (inputBuffers), numInputBuffers,
  1043. outputBuffers, numOutputBuffers, bufferSize);
  1044. else
  1045. outs.clear();
  1046. }
  1047. if (outputDevice != nullptr)
  1048. {
  1049. // Note that this function is handed the input device so it can check for the event and make sure
  1050. // the input reservoir is filled up correctly even when bufferSize > device actualBufferSize
  1051. outputDevice->copyBuffers (const_cast<const float**> (outputBuffers), numOutputBuffers, bufferSize, inputDevice, *this);
  1052. if (outputDevice->sampleRateHasChanged)
  1053. {
  1054. sampleRateHasChanged = true;
  1055. sampleRateChangedByOutput = true;
  1056. }
  1057. }
  1058. if (sampleRateHasChanged)
  1059. {
  1060. triggerAsyncUpdate();
  1061. break; // Quit the thread... will restart it later!
  1062. }
  1063. }
  1064. }
  1065. //==============================================================================
  1066. String outputDeviceId, inputDeviceId;
  1067. String lastError;
  1068. private:
  1069. // Device stats...
  1070. ScopedPointer<WASAPIInputDevice> inputDevice;
  1071. ScopedPointer<WASAPIOutputDevice> outputDevice;
  1072. const bool useExclusiveMode;
  1073. double defaultSampleRate;
  1074. int minBufferSize, defaultBufferSize;
  1075. int latencyIn, latencyOut;
  1076. Array<double> sampleRates;
  1077. Array<int> bufferSizes;
  1078. // Active state...
  1079. bool isOpen_, isStarted;
  1080. int currentBufferSizeSamples;
  1081. double currentSampleRate;
  1082. bool sampleRateChangedByOutput;
  1083. AudioIODeviceCallback* callback;
  1084. CriticalSection startStopLock;
  1085. BigInteger lastKnownInputChannels, lastKnownOutputChannels;
  1086. //==============================================================================
  1087. bool createDevices()
  1088. {
  1089. ComSmartPtr<IMMDeviceEnumerator> enumerator;
  1090. if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
  1091. return false;
  1092. ComSmartPtr<IMMDeviceCollection> deviceCollection;
  1093. if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())))
  1094. return false;
  1095. UINT32 numDevices = 0;
  1096. if (! check (deviceCollection->GetCount (&numDevices)))
  1097. return false;
  1098. for (UINT32 i = 0; i < numDevices; ++i)
  1099. {
  1100. ComSmartPtr<IMMDevice> device;
  1101. if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
  1102. continue;
  1103. const String deviceId (getDeviceID (device));
  1104. if (deviceId.isEmpty())
  1105. continue;
  1106. const EDataFlow flow = getDataFlow (device);
  1107. if (deviceId == inputDeviceId && flow == eCapture)
  1108. inputDevice = new WASAPIInputDevice (device, useExclusiveMode);
  1109. else if (deviceId == outputDeviceId && flow == eRender)
  1110. outputDevice = new WASAPIOutputDevice (device, useExclusiveMode);
  1111. }
  1112. return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk()))
  1113. && (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk()));
  1114. }
  1115. //==============================================================================
  1116. void handleAsyncUpdate() override
  1117. {
  1118. stop();
  1119. outputDevice = nullptr;
  1120. inputDevice = nullptr;
  1121. initialise();
  1122. open (lastKnownInputChannels, lastKnownOutputChannels,
  1123. getChangedSampleRate(), currentBufferSizeSamples);
  1124. start (callback);
  1125. }
  1126. double getChangedSampleRate() const
  1127. {
  1128. if (outputDevice != nullptr && sampleRateChangedByOutput)
  1129. return outputDevice->defaultSampleRate;
  1130. if (inputDevice != nullptr && ! sampleRateChangedByOutput)
  1131. return inputDevice->defaultSampleRate;
  1132. return 0.0;
  1133. }
  1134. //==============================================================================
  1135. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice)
  1136. };
  1137. //==============================================================================
  1138. class WASAPIAudioIODeviceType : public AudioIODeviceType,
  1139. private DeviceChangeDetector
  1140. {
  1141. public:
  1142. WASAPIAudioIODeviceType (bool exclusive)
  1143. : AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"),
  1144. DeviceChangeDetector (L"Windows Audio"),
  1145. exclusiveMode (exclusive),
  1146. hasScanned (false)
  1147. {
  1148. }
  1149. ~WASAPIAudioIODeviceType()
  1150. {
  1151. if (notifyClient != nullptr)
  1152. enumerator->UnregisterEndpointNotificationCallback (notifyClient);
  1153. }
  1154. //==============================================================================
  1155. void scanForDevices()
  1156. {
  1157. hasScanned = true;
  1158. outputDeviceNames.clear();
  1159. inputDeviceNames.clear();
  1160. outputDeviceIds.clear();
  1161. inputDeviceIds.clear();
  1162. scan (outputDeviceNames, inputDeviceNames,
  1163. outputDeviceIds, inputDeviceIds);
  1164. }
  1165. StringArray getDeviceNames (bool wantInputNames) const
  1166. {
  1167. jassert (hasScanned); // need to call scanForDevices() before doing this
  1168. return wantInputNames ? inputDeviceNames
  1169. : outputDeviceNames;
  1170. }
  1171. int getDefaultDeviceIndex (bool /*forInput*/) const
  1172. {
  1173. jassert (hasScanned); // need to call scanForDevices() before doing this
  1174. return 0;
  1175. }
  1176. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  1177. {
  1178. jassert (hasScanned); // need to call scanForDevices() before doing this
  1179. if (WASAPIAudioIODevice* const d = dynamic_cast<WASAPIAudioIODevice*> (device))
  1180. return asInput ? inputDeviceIds.indexOf (d->inputDeviceId)
  1181. : outputDeviceIds.indexOf (d->outputDeviceId);
  1182. return -1;
  1183. }
  1184. bool hasSeparateInputsAndOutputs() const { return true; }
  1185. AudioIODevice* createDevice (const String& outputDeviceName,
  1186. const String& inputDeviceName)
  1187. {
  1188. jassert (hasScanned); // need to call scanForDevices() before doing this
  1189. ScopedPointer<WASAPIAudioIODevice> device;
  1190. const int outputIndex = outputDeviceNames.indexOf (outputDeviceName);
  1191. const int inputIndex = inputDeviceNames.indexOf (inputDeviceName);
  1192. if (outputIndex >= 0 || inputIndex >= 0)
  1193. {
  1194. device = new WASAPIAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
  1195. : inputDeviceName,
  1196. getTypeName(),
  1197. outputDeviceIds [outputIndex],
  1198. inputDeviceIds [inputIndex],
  1199. exclusiveMode);
  1200. if (! device->initialise())
  1201. device = nullptr;
  1202. }
  1203. return device.release();
  1204. }
  1205. //==============================================================================
  1206. StringArray outputDeviceNames, outputDeviceIds;
  1207. StringArray inputDeviceNames, inputDeviceIds;
  1208. private:
  1209. bool exclusiveMode, hasScanned;
  1210. ComSmartPtr<IMMDeviceEnumerator> enumerator;
  1211. //==============================================================================
  1212. class ChangeNotificationClient : public ComBaseClassHelper<IMMNotificationClient>
  1213. {
  1214. public:
  1215. ChangeNotificationClient (WASAPIAudioIODeviceType& d)
  1216. : ComBaseClassHelper<IMMNotificationClient> (0), device (d) {}
  1217. HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); }
  1218. HRESULT STDMETHODCALLTYPE OnDeviceRemoved (LPCWSTR) { return notify(); }
  1219. HRESULT STDMETHODCALLTYPE OnDeviceStateChanged (LPCWSTR, DWORD) { return notify(); }
  1220. HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); }
  1221. HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); }
  1222. private:
  1223. WASAPIAudioIODeviceType& device;
  1224. HRESULT notify() { device.triggerAsyncDeviceChangeCallback(); return S_OK; }
  1225. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeNotificationClient)
  1226. };
  1227. ComSmartPtr<ChangeNotificationClient> notifyClient;
  1228. //==============================================================================
  1229. static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture)
  1230. {
  1231. String s;
  1232. IMMDevice* dev = nullptr;
  1233. if (check (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender,
  1234. eMultimedia, &dev)))
  1235. {
  1236. WCHAR* deviceId = nullptr;
  1237. if (check (dev->GetId (&deviceId)))
  1238. {
  1239. s = deviceId;
  1240. CoTaskMemFree (deviceId);
  1241. }
  1242. dev->Release();
  1243. }
  1244. return s;
  1245. }
  1246. //==============================================================================
  1247. void scan (StringArray& outDeviceNames,
  1248. StringArray& inDeviceNames,
  1249. StringArray& outDeviceIds,
  1250. StringArray& inDeviceIds)
  1251. {
  1252. if (enumerator == nullptr)
  1253. {
  1254. if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
  1255. return;
  1256. notifyClient = new ChangeNotificationClient (*this);
  1257. enumerator->RegisterEndpointNotificationCallback (notifyClient);
  1258. }
  1259. const String defaultRenderer (getDefaultEndpoint (enumerator, false));
  1260. const String defaultCapture (getDefaultEndpoint (enumerator, true));
  1261. ComSmartPtr<IMMDeviceCollection> deviceCollection;
  1262. UINT32 numDevices = 0;
  1263. if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress()))
  1264. && check (deviceCollection->GetCount (&numDevices))))
  1265. return;
  1266. for (UINT32 i = 0; i < numDevices; ++i)
  1267. {
  1268. ComSmartPtr<IMMDevice> device;
  1269. if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress())))
  1270. continue;
  1271. DWORD state = 0;
  1272. if (! (check (device->GetState (&state)) && state == DEVICE_STATE_ACTIVE))
  1273. continue;
  1274. const String deviceId (getDeviceID (device));
  1275. String name;
  1276. {
  1277. ComSmartPtr<IPropertyStore> properties;
  1278. if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress())))
  1279. continue;
  1280. PROPVARIANT value;
  1281. zerostruct (value);
  1282. const PROPERTYKEY PKEY_Device_FriendlyName
  1283. = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 14 };
  1284. if (check (properties->GetValue (PKEY_Device_FriendlyName, &value)))
  1285. name = value.pwszVal;
  1286. PropVariantClear (&value);
  1287. }
  1288. const EDataFlow flow = getDataFlow (device);
  1289. if (flow == eRender)
  1290. {
  1291. const int index = (deviceId == defaultRenderer) ? 0 : -1;
  1292. outDeviceIds.insert (index, deviceId);
  1293. outDeviceNames.insert (index, name);
  1294. }
  1295. else if (flow == eCapture)
  1296. {
  1297. const int index = (deviceId == defaultCapture) ? 0 : -1;
  1298. inDeviceIds.insert (index, deviceId);
  1299. inDeviceNames.insert (index, name);
  1300. }
  1301. }
  1302. inDeviceNames.appendNumbersToDuplicates (false, false);
  1303. outDeviceNames.appendNumbersToDuplicates (false, false);
  1304. }
  1305. //==============================================================================
  1306. void systemDeviceChanged() override
  1307. {
  1308. StringArray newOutNames, newInNames, newOutIds, newInIds;
  1309. scan (newOutNames, newInNames, newOutIds, newInIds);
  1310. if (newOutNames != outputDeviceNames
  1311. || newInNames != inputDeviceNames
  1312. || newOutIds != outputDeviceIds
  1313. || newInIds != inputDeviceIds)
  1314. {
  1315. hasScanned = true;
  1316. outputDeviceNames = newOutNames;
  1317. inputDeviceNames = newInNames;
  1318. outputDeviceIds = newOutIds;
  1319. inputDeviceIds = newInIds;
  1320. }
  1321. callDeviceChangeListeners();
  1322. }
  1323. //==============================================================================
  1324. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType)
  1325. };
  1326. //==============================================================================
  1327. struct MMDeviceMasterVolume
  1328. {
  1329. MMDeviceMasterVolume()
  1330. {
  1331. ComSmartPtr<IMMDeviceEnumerator> enumerator;
  1332. if (check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
  1333. {
  1334. ComSmartPtr<IMMDevice> device;
  1335. if (check (enumerator->GetDefaultAudioEndpoint (eRender, eConsole, device.resetAndGetPointerAddress())))
  1336. check (device->Activate (__uuidof (IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr,
  1337. (void**) endpointVolume.resetAndGetPointerAddress()));
  1338. }
  1339. }
  1340. float getGain() const
  1341. {
  1342. float vol = 0.0f;
  1343. if (endpointVolume != nullptr)
  1344. check (endpointVolume->GetMasterVolumeLevelScalar (&vol));
  1345. return vol;
  1346. }
  1347. bool setGain (float newGain) const
  1348. {
  1349. return endpointVolume != nullptr
  1350. && check (endpointVolume->SetMasterVolumeLevelScalar (jlimit (0.0f, 1.0f, newGain), nullptr));
  1351. }
  1352. bool isMuted() const
  1353. {
  1354. BOOL mute = 0;
  1355. return endpointVolume != nullptr
  1356. && check (endpointVolume->GetMute (&mute)) && mute != 0;
  1357. }
  1358. bool setMuted (bool shouldMute) const
  1359. {
  1360. return endpointVolume != nullptr
  1361. && check (endpointVolume->SetMute (shouldMute, nullptr));
  1362. }
  1363. ComSmartPtr<IAudioEndpointVolume> endpointVolume;
  1364. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MMDeviceMasterVolume)
  1365. };
  1366. }
  1367. //==============================================================================
  1368. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode)
  1369. {
  1370. #if ! JUCE_WASAPI_EXCLUSIVE
  1371. if (exclusiveMode)
  1372. return nullptr;
  1373. #endif
  1374. return SystemStats::getOperatingSystemType() >= SystemStats::WinVista
  1375. ? new WasapiClasses::WASAPIAudioIODeviceType (exclusiveMode)
  1376. : nullptr;
  1377. }
  1378. //==============================================================================
  1379. #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1
  1380. float JUCE_CALLTYPE SystemAudioVolume::getGain() { return WasapiClasses::MMDeviceMasterVolume().getGain(); }
  1381. bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return WasapiClasses::MMDeviceMasterVolume().setGain (gain); }
  1382. bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return WasapiClasses::MMDeviceMasterVolume().isMuted(); }
  1383. bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return WasapiClasses::MMDeviceMasterVolume().setMuted (mute); }