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.

1514 lines
56KB

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