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.

1384 lines
53KB

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