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.

1305 lines
49KB

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