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.

1290 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. if (pInputBuffer != nullptr)
  450. {
  451. JUCE_DS_LOG ("closing input: " + name);
  452. HRESULT hr = pInputBuffer->Stop();
  453. JUCE_DS_LOG_ERROR (hr); (void) hr;
  454. pInputBuffer->Release();
  455. pInputBuffer = nullptr;
  456. }
  457. if (pDirectSoundCapture != nullptr)
  458. {
  459. pDirectSoundCapture->Release();
  460. pDirectSoundCapture = nullptr;
  461. }
  462. if (pDirectSound != nullptr)
  463. {
  464. pDirectSound->Release();
  465. pDirectSound = nullptr;
  466. }
  467. }
  468. String open()
  469. {
  470. JUCE_DS_LOG ("opening input: " + name
  471. + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
  472. pDirectSound = nullptr;
  473. pDirectSoundCapture = nullptr;
  474. pInputBuffer = nullptr;
  475. readOffset = 0;
  476. totalBytesPerBuffer = 0;
  477. HRESULT hr = dsDirectSoundCaptureCreate != nullptr
  478. ? dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, nullptr)
  479. : E_NOINTERFACE;
  480. if (SUCCEEDED (hr))
  481. {
  482. const int numChannels = 2;
  483. bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
  484. totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15;
  485. WAVEFORMATEX wfFormat;
  486. wfFormat.wFormatTag = WAVE_FORMAT_PCM;
  487. wfFormat.nChannels = (unsigned short)numChannels;
  488. wfFormat.nSamplesPerSec = (DWORD) sampleRate;
  489. wfFormat.wBitsPerSample = (unsigned short) bitDepth;
  490. wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
  491. wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
  492. wfFormat.cbSize = 0;
  493. DSCBUFFERDESC captureDesc = { 0 };
  494. captureDesc.dwSize = sizeof (DSCBUFFERDESC);
  495. captureDesc.dwFlags = 0;
  496. captureDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer;
  497. captureDesc.lpwfxFormat = &wfFormat;
  498. JUCE_DS_LOG ("object created");
  499. hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0);
  500. if (SUCCEEDED (hr))
  501. {
  502. hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */);
  503. if (SUCCEEDED (hr))
  504. return String::empty;
  505. }
  506. }
  507. JUCE_DS_LOG_ERROR (hr);
  508. const String error (DSoundLogging::getErrorMessage (hr));
  509. close();
  510. return error;
  511. }
  512. void synchronisePosition()
  513. {
  514. if (pInputBuffer != nullptr)
  515. {
  516. DWORD capturePos;
  517. pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*) &readOffset);
  518. }
  519. }
  520. bool service()
  521. {
  522. if (pInputBuffer == 0)
  523. return true;
  524. DWORD capturePos, readPos;
  525. HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos);
  526. JUCE_DS_LOG_ERROR (hr);
  527. if (FAILED (hr))
  528. return true;
  529. int bytesFilled = (int) (readPos - readOffset);
  530. if (bytesFilled < 0)
  531. bytesFilled += totalBytesPerBuffer;
  532. if (bytesFilled >= bytesPerBuffer)
  533. {
  534. short* buf1 = nullptr;
  535. short* buf2 = nullptr;
  536. DWORD dwsize1 = 0;
  537. DWORD dwsize2 = 0;
  538. HRESULT hr = pInputBuffer->Lock ((DWORD) readOffset, (DWORD) bytesPerBuffer,
  539. (void**) &buf1, &dwsize1,
  540. (void**) &buf2, &dwsize2, 0);
  541. if (SUCCEEDED (hr))
  542. {
  543. if (bitDepth == 16)
  544. {
  545. const float g = 1.0f / 32768.0f;
  546. float* destL = leftBuffer;
  547. float* destR = rightBuffer;
  548. int samples1 = (int) (dwsize1 >> 2);
  549. int samples2 = (int) (dwsize2 >> 2);
  550. if (destL == nullptr)
  551. {
  552. for (const short* src = buf1; --samples1 >= 0;) { ++src; *destR++ = *src++ * g; }
  553. for (const short* src = buf2; --samples2 >= 0;) { ++src; *destR++ = *src++ * g; }
  554. }
  555. else if (destR == nullptr)
  556. {
  557. for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; ++src; }
  558. for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; ++src; }
  559. }
  560. else
  561. {
  562. for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; }
  563. for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; }
  564. }
  565. }
  566. else
  567. {
  568. jassertfalse;
  569. }
  570. readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer;
  571. pInputBuffer->Unlock (buf1, dwsize1, buf2, dwsize2);
  572. }
  573. else
  574. {
  575. JUCE_DS_LOG_ERROR (hr);
  576. jassertfalse;
  577. }
  578. bytesFilled -= bytesPerBuffer;
  579. return true;
  580. }
  581. else
  582. {
  583. return false;
  584. }
  585. }
  586. unsigned int readOffset;
  587. int bytesPerBuffer, totalBytesPerBuffer;
  588. int bitDepth;
  589. bool doneFlag;
  590. private:
  591. String name;
  592. GUID guid;
  593. int sampleRate, bufferSizeSamples;
  594. float* leftBuffer;
  595. float* rightBuffer;
  596. IDirectSound* pDirectSound;
  597. IDirectSoundCapture* pDirectSoundCapture;
  598. IDirectSoundCaptureBuffer* pInputBuffer;
  599. JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel)
  600. };
  601. //==============================================================================
  602. class DSoundAudioIODevice : public AudioIODevice,
  603. public Thread
  604. {
  605. public:
  606. DSoundAudioIODevice (const String& deviceName,
  607. const int outputDeviceIndex_,
  608. const int inputDeviceIndex_)
  609. : AudioIODevice (deviceName, "DirectSound"),
  610. Thread ("Juce DSound"),
  611. outputDeviceIndex (outputDeviceIndex_),
  612. inputDeviceIndex (inputDeviceIndex_),
  613. isOpen_ (false),
  614. isStarted (false),
  615. bufferSizeSamples (0),
  616. sampleRate (0.0),
  617. inputBuffers (1, 1),
  618. outputBuffers (1, 1),
  619. callback (nullptr)
  620. {
  621. if (outputDeviceIndex_ >= 0)
  622. {
  623. outChannels.add (TRANS("Left"));
  624. outChannels.add (TRANS("Right"));
  625. }
  626. if (inputDeviceIndex_ >= 0)
  627. {
  628. inChannels.add (TRANS("Left"));
  629. inChannels.add (TRANS("Right"));
  630. }
  631. }
  632. ~DSoundAudioIODevice()
  633. {
  634. close();
  635. }
  636. String open (const BigInteger& inputChannels,
  637. const BigInteger& outputChannels,
  638. double sampleRate, int bufferSizeSamples)
  639. {
  640. lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
  641. isOpen_ = lastError.isEmpty();
  642. return lastError;
  643. }
  644. void close()
  645. {
  646. stop();
  647. if (isOpen_)
  648. {
  649. closeDevice();
  650. isOpen_ = false;
  651. }
  652. }
  653. bool isOpen() { return isOpen_ && isThreadRunning(); }
  654. int getCurrentBufferSizeSamples() { return bufferSizeSamples; }
  655. double getCurrentSampleRate() { return sampleRate; }
  656. BigInteger getActiveOutputChannels() const { return enabledOutputs; }
  657. BigInteger getActiveInputChannels() const { return enabledInputs; }
  658. int getOutputLatencyInSamples() { return (int) (getCurrentBufferSizeSamples() * 1.5); }
  659. int getInputLatencyInSamples() { return getOutputLatencyInSamples(); }
  660. StringArray getOutputChannelNames() { return outChannels; }
  661. StringArray getInputChannelNames() { return inChannels; }
  662. int getNumSampleRates() { return 4; }
  663. int getDefaultBufferSize() { return 2560; }
  664. int getNumBufferSizesAvailable() { return 50; }
  665. double getSampleRate (int index)
  666. {
  667. const double samps[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
  668. return samps [jlimit (0, 3, index)];
  669. }
  670. int getBufferSizeSamples (int index)
  671. {
  672. int n = 64;
  673. for (int i = 0; i < index; ++i)
  674. n += (n < 512) ? 32
  675. : ((n < 1024) ? 64
  676. : ((n < 2048) ? 128 : 256));
  677. return n;
  678. }
  679. int getCurrentBitDepth()
  680. {
  681. int bits = 256;
  682. for (int i = inChans.size(); --i >= 0;)
  683. bits = jmin (bits, inChans[i]->bitDepth);
  684. for (int i = outChans.size(); --i >= 0;)
  685. bits = jmin (bits, outChans[i]->bitDepth);
  686. if (bits > 32)
  687. bits = 16;
  688. return bits;
  689. }
  690. void start (AudioIODeviceCallback* call)
  691. {
  692. if (isOpen_ && call != nullptr && ! isStarted)
  693. {
  694. if (! isThreadRunning())
  695. {
  696. // something gone wrong and the thread's stopped..
  697. isOpen_ = false;
  698. return;
  699. }
  700. call->audioDeviceAboutToStart (this);
  701. const ScopedLock sl (startStopLock);
  702. callback = call;
  703. isStarted = true;
  704. }
  705. }
  706. void stop()
  707. {
  708. if (isStarted)
  709. {
  710. AudioIODeviceCallback* const callbackLocal = callback;
  711. {
  712. const ScopedLock sl (startStopLock);
  713. isStarted = false;
  714. }
  715. if (callbackLocal != nullptr)
  716. callbackLocal->audioDeviceStopped();
  717. }
  718. }
  719. bool isPlaying() { return isStarted && isOpen_ && isThreadRunning(); }
  720. String getLastError() { return lastError; }
  721. //==============================================================================
  722. StringArray inChannels, outChannels;
  723. int outputDeviceIndex, inputDeviceIndex;
  724. private:
  725. bool isOpen_;
  726. bool isStarted;
  727. String lastError;
  728. OwnedArray <DSoundInternalInChannel> inChans;
  729. OwnedArray <DSoundInternalOutChannel> outChans;
  730. WaitableEvent startEvent;
  731. int bufferSizeSamples;
  732. double sampleRate;
  733. BigInteger enabledInputs, enabledOutputs;
  734. AudioSampleBuffer inputBuffers, outputBuffers;
  735. AudioIODeviceCallback* callback;
  736. CriticalSection startStopLock;
  737. String openDevice (const BigInteger& inputChannels,
  738. const BigInteger& outputChannels,
  739. double sampleRate_, int bufferSizeSamples_);
  740. void closeDevice()
  741. {
  742. isStarted = false;
  743. stopThread (5000);
  744. inChans.clear();
  745. outChans.clear();
  746. inputBuffers.setSize (1, 1);
  747. outputBuffers.setSize (1, 1);
  748. }
  749. void resync()
  750. {
  751. if (! threadShouldExit())
  752. {
  753. sleep (5);
  754. for (int i = 0; i < outChans.size(); ++i)
  755. outChans.getUnchecked(i)->synchronisePosition();
  756. for (int i = 0; i < inChans.size(); ++i)
  757. inChans.getUnchecked(i)->synchronisePosition();
  758. }
  759. }
  760. public:
  761. void run()
  762. {
  763. while (! threadShouldExit())
  764. {
  765. if (wait (100))
  766. break;
  767. }
  768. const int latencyMs = (int) (bufferSizeSamples * 1000.0 / sampleRate);
  769. const int maxTimeMS = jmax (5, 3 * latencyMs);
  770. while (! threadShouldExit())
  771. {
  772. int numToDo = 0;
  773. uint32 startTime = Time::getMillisecondCounter();
  774. for (int i = inChans.size(); --i >= 0;)
  775. {
  776. inChans.getUnchecked(i)->doneFlag = false;
  777. ++numToDo;
  778. }
  779. for (int i = outChans.size(); --i >= 0;)
  780. {
  781. outChans.getUnchecked(i)->doneFlag = false;
  782. ++numToDo;
  783. }
  784. if (numToDo > 0)
  785. {
  786. const int maxCount = 3;
  787. int count = maxCount;
  788. for (;;)
  789. {
  790. for (int i = inChans.size(); --i >= 0;)
  791. {
  792. DSoundInternalInChannel* const in = inChans.getUnchecked(i);
  793. if ((! in->doneFlag) && in->service())
  794. {
  795. in->doneFlag = true;
  796. --numToDo;
  797. }
  798. }
  799. for (int i = outChans.size(); --i >= 0;)
  800. {
  801. DSoundInternalOutChannel* const out = outChans.getUnchecked(i);
  802. if ((! out->doneFlag) && out->service())
  803. {
  804. out->doneFlag = true;
  805. --numToDo;
  806. }
  807. }
  808. if (numToDo <= 0)
  809. break;
  810. if (Time::getMillisecondCounter() > startTime + maxTimeMS)
  811. {
  812. resync();
  813. break;
  814. }
  815. if (--count <= 0)
  816. {
  817. Sleep (1);
  818. count = maxCount;
  819. }
  820. if (threadShouldExit())
  821. return;
  822. }
  823. }
  824. else
  825. {
  826. sleep (1);
  827. }
  828. const ScopedLock sl (startStopLock);
  829. if (isStarted)
  830. {
  831. callback->audioDeviceIOCallback (const_cast <const float**> (inputBuffers.getArrayOfChannels()),
  832. inputBuffers.getNumChannels(),
  833. outputBuffers.getArrayOfChannels(),
  834. outputBuffers.getNumChannels(),
  835. bufferSizeSamples);
  836. }
  837. else
  838. {
  839. outputBuffers.clear();
  840. sleep (1);
  841. }
  842. }
  843. }
  844. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice)
  845. };
  846. //==============================================================================
  847. struct DSoundDeviceList
  848. {
  849. StringArray outputDeviceNames, inputDeviceNames;
  850. Array<GUID> outputGuids, inputGuids;
  851. void scan()
  852. {
  853. outputDeviceNames.clear();
  854. inputDeviceNames.clear();
  855. outputGuids.clear();
  856. inputGuids.clear();
  857. if (dsDirectSoundEnumerateW != 0)
  858. {
  859. dsDirectSoundEnumerateW (outputEnumProcW, this);
  860. dsDirectSoundCaptureEnumerateW (inputEnumProcW, this);
  861. }
  862. }
  863. bool operator!= (const DSoundDeviceList& other) const noexcept
  864. {
  865. return outputDeviceNames != other.outputDeviceNames
  866. || inputDeviceNames != other.inputDeviceNames
  867. || outputGuids != other.outputGuids
  868. || inputGuids != other.inputGuids;
  869. }
  870. private:
  871. static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids)
  872. {
  873. desc = desc.trim();
  874. if (desc.isNotEmpty())
  875. {
  876. const String origDesc (desc);
  877. int n = 2;
  878. while (names.contains (desc))
  879. desc = origDesc + " (" + String (n++) + ")";
  880. names.add (desc);
  881. guids.add (lpGUID != nullptr ? *lpGUID : GUID());
  882. }
  883. return TRUE;
  884. }
  885. BOOL outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); }
  886. BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); }
  887. static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
  888. {
  889. return static_cast<DSoundDeviceList*> (object)->outputEnumProc (lpGUID, description);
  890. }
  891. static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
  892. {
  893. return static_cast<DSoundDeviceList*> (object)->inputEnumProc (lpGUID, description);
  894. }
  895. };
  896. //==============================================================================
  897. String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels,
  898. const BigInteger& outputChannels,
  899. double sampleRate_, int bufferSizeSamples_)
  900. {
  901. closeDevice();
  902. sampleRate = sampleRate_;
  903. if (bufferSizeSamples_ <= 0)
  904. bufferSizeSamples_ = 960; // use as a default size if none is set.
  905. bufferSizeSamples = bufferSizeSamples_ & ~7;
  906. DSoundDeviceList dlh;
  907. dlh.scan();
  908. enabledInputs = inputChannels;
  909. enabledInputs.setRange (inChannels.size(),
  910. enabledInputs.getHighestBit() + 1 - inChannels.size(),
  911. false);
  912. inputBuffers.setSize (jmax (1, enabledInputs.countNumberOfSetBits()), bufferSizeSamples);
  913. inputBuffers.clear();
  914. int numIns = 0;
  915. for (int i = 0; i <= enabledInputs.getHighestBit(); i += 2)
  916. {
  917. float* left = nullptr;
  918. if (enabledInputs[i])
  919. left = inputBuffers.getSampleData (numIns++);
  920. float* right = nullptr;
  921. if (enabledInputs[i + 1])
  922. right = inputBuffers.getSampleData (numIns++);
  923. if (left != nullptr || right != nullptr)
  924. inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex],
  925. dlh.inputGuids [inputDeviceIndex],
  926. (int) sampleRate, bufferSizeSamples,
  927. left, right));
  928. }
  929. enabledOutputs = outputChannels;
  930. enabledOutputs.setRange (outChannels.size(),
  931. enabledOutputs.getHighestBit() + 1 - outChannels.size(),
  932. false);
  933. outputBuffers.setSize (jmax (1, enabledOutputs.countNumberOfSetBits()), bufferSizeSamples);
  934. outputBuffers.clear();
  935. int numOuts = 0;
  936. for (int i = 0; i <= enabledOutputs.getHighestBit(); i += 2)
  937. {
  938. float* left = nullptr;
  939. if (enabledOutputs[i])
  940. left = outputBuffers.getSampleData (numOuts++);
  941. float* right = nullptr;
  942. if (enabledOutputs[i + 1])
  943. right = outputBuffers.getSampleData (numOuts++);
  944. if (left != nullptr || right != nullptr)
  945. outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex],
  946. dlh.outputGuids [outputDeviceIndex],
  947. (int) sampleRate, bufferSizeSamples,
  948. left, right));
  949. }
  950. String error;
  951. // boost our priority while opening the devices to try to get better sync between them
  952. const int oldThreadPri = GetThreadPriority (GetCurrentThread());
  953. const DWORD oldProcPri = GetPriorityClass (GetCurrentProcess());
  954. SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  955. SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  956. for (int i = 0; i < outChans.size(); ++i)
  957. {
  958. error = outChans[i]->open();
  959. if (error.isNotEmpty())
  960. {
  961. error = "Error opening " + dlh.outputDeviceNames[i] + ": \"" + error + "\"";
  962. break;
  963. }
  964. }
  965. if (error.isEmpty())
  966. {
  967. for (int i = 0; i < inChans.size(); ++i)
  968. {
  969. error = inChans[i]->open();
  970. if (error.isNotEmpty())
  971. {
  972. error = "Error opening " + dlh.inputDeviceNames[i] + ": \"" + error + "\"";
  973. break;
  974. }
  975. }
  976. }
  977. if (error.isEmpty())
  978. {
  979. for (int i = 0; i < outChans.size(); ++i)
  980. outChans.getUnchecked(i)->synchronisePosition();
  981. for (int i = 0; i < inChans.size(); ++i)
  982. inChans.getUnchecked(i)->synchronisePosition();
  983. startThread (9);
  984. sleep (10);
  985. notify();
  986. }
  987. else
  988. {
  989. JUCE_DS_LOG ("Opening failed: " + error);
  990. }
  991. SetThreadPriority (GetCurrentThread(), oldThreadPri);
  992. SetPriorityClass (GetCurrentProcess(), oldProcPri);
  993. return error;
  994. }
  995. //==============================================================================
  996. class DSoundAudioIODeviceType : public AudioIODeviceType,
  997. private DeviceChangeDetector
  998. {
  999. public:
  1000. DSoundAudioIODeviceType()
  1001. : AudioIODeviceType ("DirectSound"),
  1002. DeviceChangeDetector (L"DirectSound"),
  1003. hasScanned (false)
  1004. {
  1005. initialiseDSoundFunctions();
  1006. }
  1007. void scanForDevices()
  1008. {
  1009. hasScanned = true;
  1010. deviceList.scan();
  1011. }
  1012. StringArray getDeviceNames (bool wantInputNames) const
  1013. {
  1014. jassert (hasScanned); // need to call scanForDevices() before doing this
  1015. return wantInputNames ? deviceList.inputDeviceNames
  1016. : deviceList.outputDeviceNames;
  1017. }
  1018. int getDefaultDeviceIndex (bool /*forInput*/) const
  1019. {
  1020. jassert (hasScanned); // need to call scanForDevices() before doing this
  1021. return 0;
  1022. }
  1023. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  1024. {
  1025. jassert (hasScanned); // need to call scanForDevices() before doing this
  1026. if (DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device))
  1027. return asInput ? d->inputDeviceIndex
  1028. : d->outputDeviceIndex;
  1029. return -1;
  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. DSoundDeviceList deviceList;
  1046. bool hasScanned;
  1047. void systemDeviceChanged()
  1048. {
  1049. DSoundDeviceList newList;
  1050. newList.scan();
  1051. if (newList != deviceList)
  1052. {
  1053. deviceList = newList;
  1054. callDeviceChangeListeners();
  1055. }
  1056. }
  1057. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType)
  1058. };
  1059. //==============================================================================
  1060. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
  1061. {
  1062. return new DSoundAudioIODeviceType();
  1063. }