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.

1594 lines
59KB

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