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.

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