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.

1295 lines
44KB

  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. } // (juce namespace)
  19. extern "C"
  20. {
  21. // Declare just the minimum number of interfaces for the DSound objects that we need..
  22. typedef struct typeDSBUFFERDESC
  23. {
  24. DWORD dwSize;
  25. DWORD dwFlags;
  26. DWORD dwBufferBytes;
  27. DWORD dwReserved;
  28. LPWAVEFORMATEX lpwfxFormat;
  29. GUID guid3DAlgorithm;
  30. } DSBUFFERDESC;
  31. struct IDirectSoundBuffer;
  32. #undef INTERFACE
  33. #define INTERFACE IDirectSound
  34. DECLARE_INTERFACE_(IDirectSound, IUnknown)
  35. {
  36. STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
  37. STDMETHOD_(ULONG,AddRef) (THIS) PURE;
  38. STDMETHOD_(ULONG,Release) (THIS) PURE;
  39. STDMETHOD(CreateSoundBuffer) (THIS_ DSBUFFERDESC*, IDirectSoundBuffer**, LPUNKNOWN) PURE;
  40. STDMETHOD(GetCaps) (THIS_ void*) PURE;
  41. STDMETHOD(DuplicateSoundBuffer) (THIS_ IDirectSoundBuffer*, IDirectSoundBuffer**) PURE;
  42. STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE;
  43. STDMETHOD(Compact) (THIS) PURE;
  44. STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE;
  45. STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE;
  46. STDMETHOD(Initialize) (THIS_ const GUID*) PURE;
  47. };
  48. #undef INTERFACE
  49. #define INTERFACE IDirectSoundBuffer
  50. DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown)
  51. {
  52. STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
  53. STDMETHOD_(ULONG,AddRef) (THIS) PURE;
  54. STDMETHOD_(ULONG,Release) (THIS) PURE;
  55. STDMETHOD(GetCaps) (THIS_ void*) PURE;
  56. STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE;
  57. STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE;
  58. STDMETHOD(GetVolume) (THIS_ LPLONG) PURE;
  59. STDMETHOD(GetPan) (THIS_ LPLONG) PURE;
  60. STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE;
  61. STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE;
  62. STDMETHOD(Initialize) (THIS_ IDirectSound*, DSBUFFERDESC*) PURE;
  63. STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE;
  64. STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE;
  65. STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE;
  66. STDMETHOD(SetFormat) (THIS_ const WAVEFORMATEX*) PURE;
  67. STDMETHOD(SetVolume) (THIS_ LONG) PURE;
  68. STDMETHOD(SetPan) (THIS_ LONG) PURE;
  69. STDMETHOD(SetFrequency) (THIS_ DWORD) PURE;
  70. STDMETHOD(Stop) (THIS) PURE;
  71. STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE;
  72. STDMETHOD(Restore) (THIS) PURE;
  73. };
  74. //==============================================================================
  75. typedef struct typeDSCBUFFERDESC
  76. {
  77. DWORD dwSize;
  78. DWORD dwFlags;
  79. DWORD dwBufferBytes;
  80. DWORD dwReserved;
  81. LPWAVEFORMATEX lpwfxFormat;
  82. } DSCBUFFERDESC;
  83. struct IDirectSoundCaptureBuffer;
  84. #undef INTERFACE
  85. #define INTERFACE IDirectSoundCapture
  86. DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown)
  87. {
  88. STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
  89. STDMETHOD_(ULONG,AddRef) (THIS) PURE;
  90. STDMETHOD_(ULONG,Release) (THIS) PURE;
  91. STDMETHOD(CreateCaptureBuffer) (THIS_ DSCBUFFERDESC*, IDirectSoundCaptureBuffer**, LPUNKNOWN) PURE;
  92. STDMETHOD(GetCaps) (THIS_ void*) PURE;
  93. STDMETHOD(Initialize) (THIS_ const GUID*) PURE;
  94. };
  95. #undef INTERFACE
  96. #define INTERFACE IDirectSoundCaptureBuffer
  97. DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown)
  98. {
  99. STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
  100. STDMETHOD_(ULONG,AddRef) (THIS) PURE;
  101. STDMETHOD_(ULONG,Release) (THIS) PURE;
  102. STDMETHOD(GetCaps) (THIS_ void*) PURE;
  103. STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE;
  104. STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE;
  105. STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE;
  106. STDMETHOD(Initialize) (THIS_ IDirectSoundCapture*, DSCBUFFERDESC*) PURE;
  107. STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE;
  108. STDMETHOD(Start) (THIS_ DWORD) PURE;
  109. STDMETHOD(Stop) (THIS) PURE;
  110. STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE;
  111. };
  112. }
  113. //==============================================================================
  114. namespace juce
  115. {
  116. namespace
  117. {
  118. String getDSErrorMessage (HRESULT hr)
  119. {
  120. const char* result = nullptr;
  121. switch (hr)
  122. {
  123. case MAKE_HRESULT(1, 0x878, 10): result = "Device already allocated"; break;
  124. case MAKE_HRESULT(1, 0x878, 30): result = "Control unavailable"; break;
  125. case E_INVALIDARG: result = "Invalid parameter"; break;
  126. case MAKE_HRESULT(1, 0x878, 50): result = "Invalid call"; break;
  127. case E_FAIL: result = "Generic error"; break;
  128. case MAKE_HRESULT(1, 0x878, 70): result = "Priority level error"; break;
  129. case E_OUTOFMEMORY: result = "Out of memory"; break;
  130. case MAKE_HRESULT(1, 0x878, 100): result = "Bad format"; break;
  131. case E_NOTIMPL: result = "Unsupported function"; break;
  132. case MAKE_HRESULT(1, 0x878, 120): result = "No driver"; break;
  133. case MAKE_HRESULT(1, 0x878, 130): result = "Already initialised"; break;
  134. case CLASS_E_NOAGGREGATION: result = "No aggregation"; break;
  135. case MAKE_HRESULT(1, 0x878, 150): result = "Buffer lost"; break;
  136. case MAKE_HRESULT(1, 0x878, 160): result = "Another app has priority"; break;
  137. case MAKE_HRESULT(1, 0x878, 170): result = "Uninitialised"; break;
  138. case E_NOINTERFACE: result = "No interface"; break;
  139. case S_OK: result = "No error"; break;
  140. default: return "Unknown error: " + String ((int) hr);
  141. }
  142. return result;
  143. }
  144. //==============================================================================
  145. #define DS_DEBUGGING 1
  146. #ifdef DS_DEBUGGING
  147. #define CATCH JUCE_CATCH_EXCEPTION
  148. #undef log
  149. #define log(a) Logger::writeToLog(a);
  150. #undef logError
  151. #define logError(a) logDSError(a, __LINE__);
  152. static void logDSError (HRESULT hr, int lineNum)
  153. {
  154. if (FAILED (hr))
  155. {
  156. String error ("DS error at line ");
  157. error << lineNum << " - " << getDSErrorMessage (hr);
  158. log (error);
  159. }
  160. }
  161. #else
  162. #define CATCH JUCE_CATCH_ALL
  163. #define log(a)
  164. #define logError(a)
  165. #endif
  166. //==============================================================================
  167. #define DSOUND_FUNCTION(functionName, params) \
  168. typedef HRESULT (WINAPI *type##functionName) params; \
  169. static type##functionName ds##functionName = nullptr;
  170. #define DSOUND_FUNCTION_LOAD(functionName) \
  171. ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \
  172. jassert (ds##functionName != nullptr);
  173. typedef BOOL (CALLBACK *LPDSENUMCALLBACKW) (LPGUID, LPCWSTR, LPCWSTR, LPVOID);
  174. typedef BOOL (CALLBACK *LPDSENUMCALLBACKA) (LPGUID, LPCSTR, LPCSTR, LPVOID);
  175. DSOUND_FUNCTION (DirectSoundCreate, (const GUID*, IDirectSound**, LPUNKNOWN))
  176. DSOUND_FUNCTION (DirectSoundCaptureCreate, (const GUID*, IDirectSoundCapture**, LPUNKNOWN))
  177. DSOUND_FUNCTION (DirectSoundEnumerateW, (LPDSENUMCALLBACKW, LPVOID))
  178. DSOUND_FUNCTION (DirectSoundCaptureEnumerateW, (LPDSENUMCALLBACKW, LPVOID))
  179. void initialiseDSoundFunctions()
  180. {
  181. if (dsDirectSoundCreate == nullptr)
  182. {
  183. HMODULE h = LoadLibraryA ("dsound.dll");
  184. DSOUND_FUNCTION_LOAD (DirectSoundCreate)
  185. DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate)
  186. DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW)
  187. DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW)
  188. }
  189. }
  190. // the overall size of buffer used is this value x the block size
  191. enum { blocksPerOverallBuffer = 16 };
  192. }
  193. //==============================================================================
  194. class DSoundInternalOutChannel
  195. {
  196. public:
  197. DSoundInternalOutChannel (const String& name_, const GUID& guid_, int rate,
  198. int bufferSize, float* left, float* right)
  199. : bitDepth (16), name (name_), guid (guid_), sampleRate (rate),
  200. bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right),
  201. pDirectSound (nullptr), pOutputBuffer (nullptr)
  202. {
  203. }
  204. ~DSoundInternalOutChannel()
  205. {
  206. close();
  207. }
  208. void close()
  209. {
  210. if (pOutputBuffer != nullptr)
  211. {
  212. log ("closing dsound out: " + name);
  213. HRESULT hr = pOutputBuffer->Stop();
  214. logError (hr);
  215. pOutputBuffer->Release();
  216. pOutputBuffer = nullptr;
  217. }
  218. if (pDirectSound != nullptr)
  219. {
  220. pDirectSound->Release();
  221. pDirectSound = nullptr;
  222. }
  223. }
  224. String open()
  225. {
  226. log ("opening dsound out device: " + name + " rate=" + String (sampleRate)
  227. + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
  228. pDirectSound = nullptr;
  229. pOutputBuffer = nullptr;
  230. writeOffset = 0;
  231. String error;
  232. HRESULT hr = E_NOINTERFACE;
  233. if (dsDirectSoundCreate != nullptr)
  234. hr = dsDirectSoundCreate (&guid, &pDirectSound, nullptr);
  235. if (SUCCEEDED (hr))
  236. {
  237. bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
  238. totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15;
  239. const int numChannels = 2;
  240. hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */);
  241. logError (hr);
  242. if (SUCCEEDED (hr))
  243. {
  244. IDirectSoundBuffer* pPrimaryBuffer;
  245. DSBUFFERDESC primaryDesc = { 0 };
  246. primaryDesc.dwSize = sizeof (DSBUFFERDESC);
  247. primaryDesc.dwFlags = 1 /* DSBCAPS_PRIMARYBUFFER */;
  248. primaryDesc.dwBufferBytes = 0;
  249. primaryDesc.lpwfxFormat = 0;
  250. log ("opening dsound out step 2");
  251. hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0);
  252. logError (hr);
  253. if (SUCCEEDED (hr))
  254. {
  255. WAVEFORMATEX wfFormat;
  256. wfFormat.wFormatTag = WAVE_FORMAT_PCM;
  257. wfFormat.nChannels = (unsigned short) numChannels;
  258. wfFormat.nSamplesPerSec = (DWORD) sampleRate;
  259. wfFormat.wBitsPerSample = (unsigned short) bitDepth;
  260. wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * wfFormat.wBitsPerSample / 8);
  261. wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
  262. wfFormat.cbSize = 0;
  263. hr = pPrimaryBuffer->SetFormat (&wfFormat);
  264. logError (hr);
  265. if (SUCCEEDED (hr))
  266. {
  267. DSBUFFERDESC secondaryDesc = { 0 };
  268. secondaryDesc.dwSize = sizeof (DSBUFFERDESC);
  269. secondaryDesc.dwFlags = 0x8000 /* DSBCAPS_GLOBALFOCUS */
  270. | 0x10000 /* DSBCAPS_GETCURRENTPOSITION2 */;
  271. secondaryDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer;
  272. secondaryDesc.lpwfxFormat = &wfFormat;
  273. hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0);
  274. logError (hr);
  275. if (SUCCEEDED (hr))
  276. {
  277. log ("opening dsound out step 3");
  278. DWORD dwDataLen;
  279. unsigned char* pDSBuffData;
  280. hr = pOutputBuffer->Lock (0, (DWORD) totalBytesPerBuffer,
  281. (LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0);
  282. logError (hr);
  283. if (SUCCEEDED (hr))
  284. {
  285. zeromem (pDSBuffData, dwDataLen);
  286. hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0);
  287. if (SUCCEEDED (hr))
  288. {
  289. hr = pOutputBuffer->SetCurrentPosition (0);
  290. if (SUCCEEDED (hr))
  291. {
  292. hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */);
  293. if (SUCCEEDED (hr))
  294. return String::empty;
  295. }
  296. }
  297. }
  298. }
  299. }
  300. }
  301. }
  302. }
  303. error = getDSErrorMessage (hr);
  304. close();
  305. return error;
  306. }
  307. void synchronisePosition()
  308. {
  309. if (pOutputBuffer != nullptr)
  310. {
  311. DWORD playCursor;
  312. pOutputBuffer->GetCurrentPosition (&playCursor, &writeOffset);
  313. }
  314. }
  315. bool service()
  316. {
  317. if (pOutputBuffer == 0)
  318. return true;
  319. DWORD playCursor, writeCursor;
  320. for (;;)
  321. {
  322. HRESULT hr = pOutputBuffer->GetCurrentPosition (&playCursor, &writeCursor);
  323. if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
  324. {
  325. pOutputBuffer->Restore();
  326. continue;
  327. }
  328. if (SUCCEEDED (hr))
  329. break;
  330. logError (hr);
  331. jassertfalse;
  332. return true;
  333. }
  334. int playWriteGap = (int) (writeCursor - playCursor);
  335. if (playWriteGap < 0)
  336. playWriteGap += totalBytesPerBuffer;
  337. int bytesEmpty = (int) (playCursor - writeOffset);
  338. if (bytesEmpty < 0)
  339. bytesEmpty += totalBytesPerBuffer;
  340. if (bytesEmpty > (totalBytesPerBuffer - playWriteGap))
  341. {
  342. writeOffset = writeCursor;
  343. bytesEmpty = totalBytesPerBuffer - playWriteGap;
  344. }
  345. if (bytesEmpty >= bytesPerBuffer)
  346. {
  347. int* buf1 = nullptr;
  348. int* buf2 = nullptr;
  349. DWORD dwSize1 = 0;
  350. DWORD dwSize2 = 0;
  351. HRESULT hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer,
  352. (void**) &buf1, &dwSize1,
  353. (void**) &buf2, &dwSize2, 0);
  354. if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
  355. {
  356. pOutputBuffer->Restore();
  357. hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer,
  358. (void**) &buf1, &dwSize1,
  359. (void**) &buf2, &dwSize2, 0);
  360. }
  361. if (SUCCEEDED (hr))
  362. {
  363. if (bitDepth == 16)
  364. {
  365. const float* left = leftBuffer;
  366. const float* right = rightBuffer;
  367. int samples1 = (int) (dwSize1 >> 2);
  368. int samples2 = (int) (dwSize2 >> 2);
  369. if (left == nullptr)
  370. {
  371. for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (0, *right++);
  372. for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (0, *right++);
  373. }
  374. else if (right == nullptr)
  375. {
  376. for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, 0);
  377. for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, 0);
  378. }
  379. else
  380. {
  381. for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, *right++);
  382. for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, *right++);
  383. }
  384. }
  385. else
  386. {
  387. jassertfalse;
  388. }
  389. writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer;
  390. pOutputBuffer->Unlock (buf1, dwSize1, buf2, dwSize2);
  391. }
  392. else
  393. {
  394. jassertfalse;
  395. logError (hr);
  396. }
  397. bytesEmpty -= bytesPerBuffer;
  398. return true;
  399. }
  400. else
  401. {
  402. return false;
  403. }
  404. }
  405. int bitDepth;
  406. bool doneFlag;
  407. private:
  408. String name;
  409. GUID guid;
  410. int sampleRate, bufferSizeSamples;
  411. float* leftBuffer;
  412. float* rightBuffer;
  413. IDirectSound* pDirectSound;
  414. IDirectSoundBuffer* pOutputBuffer;
  415. DWORD writeOffset;
  416. int totalBytesPerBuffer, bytesPerBuffer;
  417. unsigned int lastPlayCursor;
  418. static inline int convertInputValues (const float l, const float r) noexcept
  419. {
  420. return jlimit (-32768, 32767, roundToInt (32767.0f * r)) << 16
  421. | (0xffff & jlimit (-32768, 32767, roundToInt (32767.0f * l)));
  422. }
  423. JUCE_DECLARE_NON_COPYABLE (DSoundInternalOutChannel);
  424. };
  425. //==============================================================================
  426. struct DSoundInternalInChannel
  427. {
  428. public:
  429. DSoundInternalInChannel (const String& name_, const GUID& guid_, int rate,
  430. int bufferSize, float* left, float* right)
  431. : bitDepth (16), name (name_), guid (guid_), sampleRate (rate),
  432. bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right),
  433. pDirectSound (nullptr), pDirectSoundCapture (nullptr), pInputBuffer (nullptr)
  434. {
  435. }
  436. ~DSoundInternalInChannel()
  437. {
  438. close();
  439. }
  440. void close()
  441. {
  442. HRESULT hr;
  443. if (pInputBuffer != nullptr)
  444. {
  445. log ("closing dsound in: " + name);
  446. hr = pInputBuffer->Stop();
  447. logError (hr);
  448. pInputBuffer->Release();
  449. pInputBuffer = nullptr;
  450. }
  451. if (pDirectSoundCapture != nullptr)
  452. {
  453. pDirectSoundCapture->Release();
  454. pDirectSoundCapture = nullptr;
  455. }
  456. if (pDirectSound != nullptr)
  457. {
  458. pDirectSound->Release();
  459. pDirectSound = nullptr;
  460. }
  461. }
  462. String open()
  463. {
  464. log ("opening dsound in device: " + name
  465. + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
  466. pDirectSound = nullptr;
  467. pDirectSoundCapture = nullptr;
  468. pInputBuffer = nullptr;
  469. readOffset = 0;
  470. totalBytesPerBuffer = 0;
  471. HRESULT hr = dsDirectSoundCaptureCreate != nullptr
  472. ? dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, nullptr)
  473. : E_NOINTERFACE;
  474. if (SUCCEEDED (hr))
  475. {
  476. const int numChannels = 2;
  477. bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
  478. totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15;
  479. WAVEFORMATEX wfFormat;
  480. wfFormat.wFormatTag = WAVE_FORMAT_PCM;
  481. wfFormat.nChannels = (unsigned short)numChannels;
  482. wfFormat.nSamplesPerSec = (DWORD) sampleRate;
  483. wfFormat.wBitsPerSample = (unsigned short) bitDepth;
  484. wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
  485. wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
  486. wfFormat.cbSize = 0;
  487. DSCBUFFERDESC captureDesc = { 0 };
  488. captureDesc.dwSize = sizeof (DSCBUFFERDESC);
  489. captureDesc.dwFlags = 0;
  490. captureDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer;
  491. captureDesc.lpwfxFormat = &wfFormat;
  492. log ("opening dsound in step 2");
  493. hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0);
  494. if (SUCCEEDED (hr))
  495. {
  496. hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */);
  497. if (SUCCEEDED (hr))
  498. return String::empty;
  499. }
  500. }
  501. logError (hr);
  502. const String error (getDSErrorMessage (hr));
  503. close();
  504. return error;
  505. }
  506. void synchronisePosition()
  507. {
  508. if (pInputBuffer != nullptr)
  509. {
  510. DWORD capturePos;
  511. pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*) &readOffset);
  512. }
  513. }
  514. bool service()
  515. {
  516. if (pInputBuffer == 0)
  517. return true;
  518. DWORD capturePos, readPos;
  519. HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos);
  520. logError (hr);
  521. if (FAILED (hr))
  522. return true;
  523. int bytesFilled = (int) (readPos - readOffset);
  524. if (bytesFilled < 0)
  525. bytesFilled += totalBytesPerBuffer;
  526. if (bytesFilled >= bytesPerBuffer)
  527. {
  528. short* buf1 = nullptr;
  529. short* buf2 = nullptr;
  530. DWORD dwsize1 = 0;
  531. DWORD dwsize2 = 0;
  532. HRESULT hr = pInputBuffer->Lock ((DWORD) readOffset, (DWORD) bytesPerBuffer,
  533. (void**) &buf1, &dwsize1,
  534. (void**) &buf2, &dwsize2, 0);
  535. if (SUCCEEDED (hr))
  536. {
  537. if (bitDepth == 16)
  538. {
  539. const float g = 1.0f / 32768.0f;
  540. float* destL = leftBuffer;
  541. float* destR = rightBuffer;
  542. int samples1 = (int) (dwsize1 >> 2);
  543. int samples2 = (int) (dwsize2 >> 2);
  544. if (destL == nullptr)
  545. {
  546. for (const short* src = buf1; --samples1 >= 0;) { ++src; *destR++ = *src++ * g; }
  547. for (const short* src = buf2; --samples2 >= 0;) { ++src; *destR++ = *src++ * g; }
  548. }
  549. else if (destR == nullptr)
  550. {
  551. for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; ++src; }
  552. for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; ++src; }
  553. }
  554. else
  555. {
  556. for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; }
  557. for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; }
  558. }
  559. }
  560. else
  561. {
  562. jassertfalse;
  563. }
  564. readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer;
  565. pInputBuffer->Unlock (buf1, dwsize1, buf2, dwsize2);
  566. }
  567. else
  568. {
  569. logError (hr);
  570. jassertfalse;
  571. }
  572. bytesFilled -= bytesPerBuffer;
  573. return true;
  574. }
  575. else
  576. {
  577. return false;
  578. }
  579. }
  580. unsigned int readOffset;
  581. int bytesPerBuffer, totalBytesPerBuffer;
  582. int bitDepth;
  583. bool doneFlag;
  584. private:
  585. String name;
  586. GUID guid;
  587. int sampleRate, bufferSizeSamples;
  588. float* leftBuffer;
  589. float* rightBuffer;
  590. IDirectSound* pDirectSound;
  591. IDirectSoundCapture* pDirectSoundCapture;
  592. IDirectSoundCaptureBuffer* pInputBuffer;
  593. JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel);
  594. };
  595. //==============================================================================
  596. class DSoundAudioIODevice : public AudioIODevice,
  597. public Thread
  598. {
  599. public:
  600. DSoundAudioIODevice (const String& deviceName,
  601. const int outputDeviceIndex_,
  602. const int inputDeviceIndex_)
  603. : AudioIODevice (deviceName, "DirectSound"),
  604. Thread ("Juce DSound"),
  605. outputDeviceIndex (outputDeviceIndex_),
  606. inputDeviceIndex (inputDeviceIndex_),
  607. isOpen_ (false),
  608. isStarted (false),
  609. bufferSizeSamples (0),
  610. sampleRate (0.0),
  611. inputBuffers (1, 1),
  612. outputBuffers (1, 1),
  613. callback (nullptr)
  614. {
  615. if (outputDeviceIndex_ >= 0)
  616. {
  617. outChannels.add (TRANS("Left"));
  618. outChannels.add (TRANS("Right"));
  619. }
  620. if (inputDeviceIndex_ >= 0)
  621. {
  622. inChannels.add (TRANS("Left"));
  623. inChannels.add (TRANS("Right"));
  624. }
  625. }
  626. ~DSoundAudioIODevice()
  627. {
  628. close();
  629. }
  630. String open (const BigInteger& inputChannels,
  631. const BigInteger& outputChannels,
  632. double sampleRate, int bufferSizeSamples)
  633. {
  634. lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
  635. isOpen_ = lastError.isEmpty();
  636. return lastError;
  637. }
  638. void close()
  639. {
  640. stop();
  641. if (isOpen_)
  642. {
  643. closeDevice();
  644. isOpen_ = false;
  645. }
  646. }
  647. bool isOpen() { return isOpen_ && isThreadRunning(); }
  648. int getCurrentBufferSizeSamples() { return bufferSizeSamples; }
  649. double getCurrentSampleRate() { return sampleRate; }
  650. BigInteger getActiveOutputChannels() const { return enabledOutputs; }
  651. BigInteger getActiveInputChannels() const { return enabledInputs; }
  652. int getOutputLatencyInSamples() { return (int) (getCurrentBufferSizeSamples() * 1.5); }
  653. int getInputLatencyInSamples() { return getOutputLatencyInSamples(); }
  654. StringArray getOutputChannelNames() { return outChannels; }
  655. StringArray getInputChannelNames() { return inChannels; }
  656. int getNumSampleRates() { return 4; }
  657. int getDefaultBufferSize() { return 2560; }
  658. int getNumBufferSizesAvailable() { return 50; }
  659. double getSampleRate (int index)
  660. {
  661. const double samps[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
  662. return samps [jlimit (0, 3, index)];
  663. }
  664. int getBufferSizeSamples (int index)
  665. {
  666. int n = 64;
  667. for (int i = 0; i < index; ++i)
  668. n += (n < 512) ? 32
  669. : ((n < 1024) ? 64
  670. : ((n < 2048) ? 128 : 256));
  671. return n;
  672. }
  673. int getCurrentBitDepth()
  674. {
  675. int bits = 256;
  676. for (int i = inChans.size(); --i >= 0;)
  677. bits = jmin (bits, inChans[i]->bitDepth);
  678. for (int i = outChans.size(); --i >= 0;)
  679. bits = jmin (bits, outChans[i]->bitDepth);
  680. if (bits > 32)
  681. bits = 16;
  682. return bits;
  683. }
  684. void start (AudioIODeviceCallback* call)
  685. {
  686. if (isOpen_ && call != nullptr && ! isStarted)
  687. {
  688. if (! isThreadRunning())
  689. {
  690. // something gone wrong and the thread's stopped..
  691. isOpen_ = false;
  692. return;
  693. }
  694. call->audioDeviceAboutToStart (this);
  695. const ScopedLock sl (startStopLock);
  696. callback = call;
  697. isStarted = true;
  698. }
  699. }
  700. void stop()
  701. {
  702. if (isStarted)
  703. {
  704. AudioIODeviceCallback* const callbackLocal = callback;
  705. {
  706. const ScopedLock sl (startStopLock);
  707. isStarted = false;
  708. }
  709. if (callbackLocal != nullptr)
  710. callbackLocal->audioDeviceStopped();
  711. }
  712. }
  713. bool isPlaying() { return isStarted && isOpen_ && isThreadRunning(); }
  714. String getLastError() { return lastError; }
  715. //==============================================================================
  716. StringArray inChannels, outChannels;
  717. int outputDeviceIndex, inputDeviceIndex;
  718. private:
  719. bool isOpen_;
  720. bool isStarted;
  721. String lastError;
  722. OwnedArray <DSoundInternalInChannel> inChans;
  723. OwnedArray <DSoundInternalOutChannel> outChans;
  724. WaitableEvent startEvent;
  725. int bufferSizeSamples;
  726. double sampleRate;
  727. BigInteger enabledInputs, enabledOutputs;
  728. AudioSampleBuffer inputBuffers, outputBuffers;
  729. AudioIODeviceCallback* callback;
  730. CriticalSection startStopLock;
  731. String openDevice (const BigInteger& inputChannels,
  732. const BigInteger& outputChannels,
  733. double sampleRate_, int bufferSizeSamples_);
  734. void closeDevice()
  735. {
  736. isStarted = false;
  737. stopThread (5000);
  738. inChans.clear();
  739. outChans.clear();
  740. inputBuffers.setSize (1, 1);
  741. outputBuffers.setSize (1, 1);
  742. }
  743. void resync()
  744. {
  745. if (! threadShouldExit())
  746. {
  747. sleep (5);
  748. for (int i = 0; i < outChans.size(); ++i)
  749. outChans.getUnchecked(i)->synchronisePosition();
  750. for (int i = 0; i < inChans.size(); ++i)
  751. inChans.getUnchecked(i)->synchronisePosition();
  752. }
  753. }
  754. public:
  755. void run()
  756. {
  757. while (! threadShouldExit())
  758. {
  759. if (wait (100))
  760. break;
  761. }
  762. const int latencyMs = (int) (bufferSizeSamples * 1000.0 / sampleRate);
  763. const int maxTimeMS = jmax (5, 3 * latencyMs);
  764. while (! threadShouldExit())
  765. {
  766. int numToDo = 0;
  767. uint32 startTime = Time::getMillisecondCounter();
  768. for (int i = inChans.size(); --i >= 0;)
  769. {
  770. inChans.getUnchecked(i)->doneFlag = false;
  771. ++numToDo;
  772. }
  773. for (int i = outChans.size(); --i >= 0;)
  774. {
  775. outChans.getUnchecked(i)->doneFlag = false;
  776. ++numToDo;
  777. }
  778. if (numToDo > 0)
  779. {
  780. const int maxCount = 3;
  781. int count = maxCount;
  782. for (;;)
  783. {
  784. for (int i = inChans.size(); --i >= 0;)
  785. {
  786. DSoundInternalInChannel* const in = inChans.getUnchecked(i);
  787. if ((! in->doneFlag) && in->service())
  788. {
  789. in->doneFlag = true;
  790. --numToDo;
  791. }
  792. }
  793. for (int i = outChans.size(); --i >= 0;)
  794. {
  795. DSoundInternalOutChannel* const out = outChans.getUnchecked(i);
  796. if ((! out->doneFlag) && out->service())
  797. {
  798. out->doneFlag = true;
  799. --numToDo;
  800. }
  801. }
  802. if (numToDo <= 0)
  803. break;
  804. if (Time::getMillisecondCounter() > startTime + maxTimeMS)
  805. {
  806. resync();
  807. break;
  808. }
  809. if (--count <= 0)
  810. {
  811. Sleep (1);
  812. count = maxCount;
  813. }
  814. if (threadShouldExit())
  815. return;
  816. }
  817. }
  818. else
  819. {
  820. sleep (1);
  821. }
  822. const ScopedLock sl (startStopLock);
  823. if (isStarted)
  824. {
  825. JUCE_TRY
  826. {
  827. callback->audioDeviceIOCallback (const_cast <const float**> (inputBuffers.getArrayOfChannels()),
  828. inputBuffers.getNumChannels(),
  829. outputBuffers.getArrayOfChannels(),
  830. outputBuffers.getNumChannels(),
  831. bufferSizeSamples);
  832. }
  833. JUCE_CATCH_EXCEPTION
  834. }
  835. else
  836. {
  837. outputBuffers.clear();
  838. sleep (1);
  839. }
  840. }
  841. }
  842. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice);
  843. };
  844. //==============================================================================
  845. struct DSoundDeviceList
  846. {
  847. StringArray outputDeviceNames, inputDeviceNames;
  848. Array<GUID> outputGuids, inputGuids;
  849. void scan()
  850. {
  851. outputDeviceNames.clear();
  852. inputDeviceNames.clear();
  853. outputGuids.clear();
  854. inputGuids.clear();
  855. if (dsDirectSoundEnumerateW != 0)
  856. {
  857. dsDirectSoundEnumerateW (outputEnumProcW, this);
  858. dsDirectSoundCaptureEnumerateW (inputEnumProcW, this);
  859. }
  860. }
  861. bool operator!= (const DSoundDeviceList& other) const noexcept
  862. {
  863. return outputDeviceNames != other.outputDeviceNames
  864. || inputDeviceNames != other.inputDeviceNames
  865. || outputGuids != other.outputGuids
  866. || inputGuids != other.inputGuids;
  867. }
  868. private:
  869. static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids)
  870. {
  871. desc = desc.trim();
  872. if (desc.isNotEmpty())
  873. {
  874. const String origDesc (desc);
  875. int n = 2;
  876. while (names.contains (desc))
  877. desc = origDesc + " (" + String (n++) + ")";
  878. names.add (desc);
  879. guids.add (lpGUID != nullptr ? *lpGUID : GUID());
  880. }
  881. return TRUE;
  882. }
  883. BOOL outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); }
  884. BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); }
  885. static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
  886. {
  887. return static_cast<DSoundDeviceList*> (object)->outputEnumProc (lpGUID, description);
  888. }
  889. static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
  890. {
  891. return static_cast<DSoundDeviceList*> (object)->inputEnumProc (lpGUID, description);
  892. }
  893. };
  894. //==============================================================================
  895. String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels,
  896. const BigInteger& outputChannels,
  897. double sampleRate_, int bufferSizeSamples_)
  898. {
  899. closeDevice();
  900. sampleRate = sampleRate_;
  901. if (bufferSizeSamples_ <= 0)
  902. bufferSizeSamples_ = 960; // use as a default size if none is set.
  903. bufferSizeSamples = bufferSizeSamples_ & ~7;
  904. DSoundDeviceList dlh;
  905. dlh.scan();
  906. enabledInputs = inputChannels;
  907. enabledInputs.setRange (inChannels.size(),
  908. enabledInputs.getHighestBit() + 1 - inChannels.size(),
  909. false);
  910. inputBuffers.setSize (jmax (1, enabledInputs.countNumberOfSetBits()), bufferSizeSamples);
  911. inputBuffers.clear();
  912. int numIns = 0;
  913. for (int i = 0; i <= enabledInputs.getHighestBit(); i += 2)
  914. {
  915. float* left = nullptr;
  916. if (enabledInputs[i])
  917. left = inputBuffers.getSampleData (numIns++);
  918. float* right = nullptr;
  919. if (enabledInputs[i + 1])
  920. right = inputBuffers.getSampleData (numIns++);
  921. if (left != nullptr || right != nullptr)
  922. inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex],
  923. dlh.inputGuids [inputDeviceIndex],
  924. (int) sampleRate, bufferSizeSamples,
  925. left, right));
  926. }
  927. enabledOutputs = outputChannels;
  928. enabledOutputs.setRange (outChannels.size(),
  929. enabledOutputs.getHighestBit() + 1 - outChannels.size(),
  930. false);
  931. outputBuffers.setSize (jmax (1, enabledOutputs.countNumberOfSetBits()), bufferSizeSamples);
  932. outputBuffers.clear();
  933. int numOuts = 0;
  934. for (int i = 0; i <= enabledOutputs.getHighestBit(); i += 2)
  935. {
  936. float* left = nullptr;
  937. if (enabledOutputs[i])
  938. left = outputBuffers.getSampleData (numOuts++);
  939. float* right = nullptr;
  940. if (enabledOutputs[i + 1])
  941. right = outputBuffers.getSampleData (numOuts++);
  942. if (left != nullptr || right != nullptr)
  943. outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex],
  944. dlh.outputGuids [outputDeviceIndex],
  945. (int) sampleRate, bufferSizeSamples,
  946. left, right));
  947. }
  948. String error;
  949. // boost our priority while opening the devices to try to get better sync between them
  950. const int oldThreadPri = GetThreadPriority (GetCurrentThread());
  951. const DWORD oldProcPri = GetPriorityClass (GetCurrentProcess());
  952. SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  953. SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  954. for (int i = 0; i < outChans.size(); ++i)
  955. {
  956. error = outChans[i]->open();
  957. if (error.isNotEmpty())
  958. {
  959. error = "Error opening " + dlh.outputDeviceNames[i] + ": \"" + error + "\"";
  960. break;
  961. }
  962. }
  963. if (error.isEmpty())
  964. {
  965. for (int i = 0; i < inChans.size(); ++i)
  966. {
  967. error = inChans[i]->open();
  968. if (error.isNotEmpty())
  969. {
  970. error = "Error opening " + dlh.inputDeviceNames[i] + ": \"" + error + "\"";
  971. break;
  972. }
  973. }
  974. }
  975. if (error.isEmpty())
  976. {
  977. for (int i = 0; i < outChans.size(); ++i)
  978. outChans.getUnchecked(i)->synchronisePosition();
  979. for (int i = 0; i < inChans.size(); ++i)
  980. inChans.getUnchecked(i)->synchronisePosition();
  981. startThread (9);
  982. sleep (10);
  983. notify();
  984. }
  985. else
  986. {
  987. log (error);
  988. }
  989. SetThreadPriority (GetCurrentThread(), oldThreadPri);
  990. SetPriorityClass (GetCurrentProcess(), oldProcPri);
  991. return error;
  992. }
  993. //==============================================================================
  994. class DSoundAudioIODeviceType : public AudioIODeviceType,
  995. private DeviceChangeDetector
  996. {
  997. public:
  998. DSoundAudioIODeviceType()
  999. : AudioIODeviceType ("DirectSound"),
  1000. DeviceChangeDetector (L"DirectSound"),
  1001. hasScanned (false)
  1002. {
  1003. initialiseDSoundFunctions();
  1004. }
  1005. //==============================================================================
  1006. void scanForDevices()
  1007. {
  1008. hasScanned = true;
  1009. deviceList.scan();
  1010. }
  1011. StringArray getDeviceNames (bool wantInputNames) const
  1012. {
  1013. jassert (hasScanned); // need to call scanForDevices() before doing this
  1014. return wantInputNames ? deviceList.inputDeviceNames
  1015. : deviceList.outputDeviceNames;
  1016. }
  1017. int getDefaultDeviceIndex (bool /*forInput*/) const
  1018. {
  1019. jassert (hasScanned); // need to call scanForDevices() before doing this
  1020. return 0;
  1021. }
  1022. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  1023. {
  1024. jassert (hasScanned); // need to call scanForDevices() before doing this
  1025. DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device);
  1026. if (d == 0)
  1027. return -1;
  1028. return asInput ? d->inputDeviceIndex
  1029. : d->outputDeviceIndex;
  1030. }
  1031. bool hasSeparateInputsAndOutputs() const { return true; }
  1032. AudioIODevice* createDevice (const String& outputDeviceName,
  1033. const String& inputDeviceName)
  1034. {
  1035. jassert (hasScanned); // need to call scanForDevices() before doing this
  1036. const int outputIndex = deviceList.outputDeviceNames.indexOf (outputDeviceName);
  1037. const int inputIndex = deviceList.inputDeviceNames.indexOf (inputDeviceName);
  1038. if (outputIndex >= 0 || inputIndex >= 0)
  1039. return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
  1040. : inputDeviceName,
  1041. outputIndex, inputIndex);
  1042. return nullptr;
  1043. }
  1044. private:
  1045. //==============================================================================
  1046. DSoundDeviceList deviceList;
  1047. bool hasScanned;
  1048. void systemDeviceChanged()
  1049. {
  1050. DSoundDeviceList newList;
  1051. newList.scan();
  1052. if (newList != deviceList)
  1053. {
  1054. deviceList = newList;
  1055. callDeviceChangeListeners();
  1056. }
  1057. }
  1058. //==============================================================================
  1059. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType);
  1060. };
  1061. //==============================================================================
  1062. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
  1063. {
  1064. return new DSoundAudioIODeviceType();
  1065. }
  1066. #undef log
  1067. #undef logError