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