Audio plugin host https://kx.studio/carla
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.

juce_win32_DirectSound.cpp 44KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. } // (juce namespace)
  18. extern "C"
  19. {
  20. // Declare just the minimum number of interfaces for the DSound objects that we need..
  21. typedef struct typeDSBUFFERDESC
  22. {
  23. DWORD dwSize;
  24. DWORD dwFlags;
  25. DWORD dwBufferBytes;
  26. DWORD dwReserved;
  27. LPWAVEFORMATEX lpwfxFormat;
  28. GUID guid3DAlgorithm;
  29. } DSBUFFERDESC;
  30. struct IDirectSoundBuffer;
  31. #undef INTERFACE
  32. #define INTERFACE IDirectSound
  33. DECLARE_INTERFACE_(IDirectSound, IUnknown)
  34. {
  35. STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
  36. STDMETHOD_(ULONG,AddRef) (THIS) PURE;
  37. STDMETHOD_(ULONG,Release) (THIS) PURE;
  38. STDMETHOD(CreateSoundBuffer) (THIS_ DSBUFFERDESC*, IDirectSoundBuffer**, LPUNKNOWN) PURE;
  39. STDMETHOD(GetCaps) (THIS_ void*) PURE;
  40. STDMETHOD(DuplicateSoundBuffer) (THIS_ IDirectSoundBuffer*, IDirectSoundBuffer**) PURE;
  41. STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE;
  42. STDMETHOD(Compact) (THIS) PURE;
  43. STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE;
  44. STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE;
  45. STDMETHOD(Initialize) (THIS_ const GUID*) PURE;
  46. };
  47. #undef INTERFACE
  48. #define INTERFACE IDirectSoundBuffer
  49. DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown)
  50. {
  51. STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
  52. STDMETHOD_(ULONG,AddRef) (THIS) PURE;
  53. STDMETHOD_(ULONG,Release) (THIS) PURE;
  54. STDMETHOD(GetCaps) (THIS_ void*) PURE;
  55. STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE;
  56. STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE;
  57. STDMETHOD(GetVolume) (THIS_ LPLONG) PURE;
  58. STDMETHOD(GetPan) (THIS_ LPLONG) PURE;
  59. STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE;
  60. STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE;
  61. STDMETHOD(Initialize) (THIS_ IDirectSound*, DSBUFFERDESC*) PURE;
  62. STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE;
  63. STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE;
  64. STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE;
  65. STDMETHOD(SetFormat) (THIS_ const WAVEFORMATEX*) PURE;
  66. STDMETHOD(SetVolume) (THIS_ LONG) PURE;
  67. STDMETHOD(SetPan) (THIS_ LONG) PURE;
  68. STDMETHOD(SetFrequency) (THIS_ DWORD) PURE;
  69. STDMETHOD(Stop) (THIS) PURE;
  70. STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE;
  71. STDMETHOD(Restore) (THIS) PURE;
  72. };
  73. //==============================================================================
  74. typedef struct typeDSCBUFFERDESC
  75. {
  76. DWORD dwSize;
  77. DWORD dwFlags;
  78. DWORD dwBufferBytes;
  79. DWORD dwReserved;
  80. LPWAVEFORMATEX lpwfxFormat;
  81. } DSCBUFFERDESC;
  82. struct IDirectSoundCaptureBuffer;
  83. #undef INTERFACE
  84. #define INTERFACE IDirectSoundCapture
  85. DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown)
  86. {
  87. STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
  88. STDMETHOD_(ULONG,AddRef) (THIS) PURE;
  89. STDMETHOD_(ULONG,Release) (THIS) PURE;
  90. STDMETHOD(CreateCaptureBuffer) (THIS_ DSCBUFFERDESC*, IDirectSoundCaptureBuffer**, LPUNKNOWN) PURE;
  91. STDMETHOD(GetCaps) (THIS_ void*) PURE;
  92. STDMETHOD(Initialize) (THIS_ const GUID*) PURE;
  93. };
  94. #undef INTERFACE
  95. #define INTERFACE IDirectSoundCaptureBuffer
  96. DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown)
  97. {
  98. STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
  99. STDMETHOD_(ULONG,AddRef) (THIS) PURE;
  100. STDMETHOD_(ULONG,Release) (THIS) PURE;
  101. STDMETHOD(GetCaps) (THIS_ void*) PURE;
  102. STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE;
  103. STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE;
  104. STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE;
  105. STDMETHOD(Initialize) (THIS_ IDirectSoundCapture*, DSCBUFFERDESC*) PURE;
  106. STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE;
  107. STDMETHOD(Start) (THIS_ DWORD) PURE;
  108. STDMETHOD(Stop) (THIS) PURE;
  109. STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE;
  110. };
  111. #undef INTERFACE
  112. }
  113. namespace juce
  114. {
  115. //==============================================================================
  116. namespace DSoundLogging
  117. {
  118. String getErrorMessage (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. #if JUCE_DIRECTSOUND_LOGGING
  146. static void logMessage (String message)
  147. {
  148. message = "DSOUND: " + message;
  149. DBG (message);
  150. Logger::writeToLog (message);
  151. }
  152. static void logError (HRESULT hr, int lineNum)
  153. {
  154. if (FAILED (hr))
  155. {
  156. String error ("Error at line ");
  157. error << lineNum << ": " << getErrorMessage (hr);
  158. logMessage (error);
  159. }
  160. }
  161. #define CATCH JUCE_CATCH_EXCEPTION
  162. #define JUCE_DS_LOG(a) DSoundLogging::logMessage(a);
  163. #define JUCE_DS_LOG_ERROR(a) DSoundLogging::logError(a, __LINE__);
  164. #else
  165. #define CATCH JUCE_CATCH_ALL
  166. #define JUCE_DS_LOG(a)
  167. #define JUCE_DS_LOG_ERROR(a)
  168. #endif
  169. }
  170. //==============================================================================
  171. namespace
  172. {
  173. #define DSOUND_FUNCTION(functionName, params) \
  174. typedef HRESULT (WINAPI *type##functionName) params; \
  175. static type##functionName ds##functionName = nullptr;
  176. #define DSOUND_FUNCTION_LOAD(functionName) \
  177. ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \
  178. jassert (ds##functionName != nullptr);
  179. typedef BOOL (CALLBACK *LPDSENUMCALLBACKW) (LPGUID, LPCWSTR, LPCWSTR, LPVOID);
  180. typedef BOOL (CALLBACK *LPDSENUMCALLBACKA) (LPGUID, LPCSTR, LPCSTR, LPVOID);
  181. DSOUND_FUNCTION (DirectSoundCreate, (const GUID*, IDirectSound**, LPUNKNOWN))
  182. DSOUND_FUNCTION (DirectSoundCaptureCreate, (const GUID*, IDirectSoundCapture**, LPUNKNOWN))
  183. DSOUND_FUNCTION (DirectSoundEnumerateW, (LPDSENUMCALLBACKW, LPVOID))
  184. DSOUND_FUNCTION (DirectSoundCaptureEnumerateW, (LPDSENUMCALLBACKW, LPVOID))
  185. void initialiseDSoundFunctions()
  186. {
  187. if (dsDirectSoundCreate == nullptr)
  188. {
  189. HMODULE h = LoadLibraryA ("dsound.dll");
  190. DSOUND_FUNCTION_LOAD (DirectSoundCreate)
  191. DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate)
  192. DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW)
  193. DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW)
  194. }
  195. }
  196. // the overall size of buffer used is this value x the block size
  197. enum { blocksPerOverallBuffer = 16 };
  198. }
  199. //==============================================================================
  200. class DSoundInternalOutChannel
  201. {
  202. public:
  203. DSoundInternalOutChannel (const String& name_, const GUID& guid_, int rate,
  204. int bufferSize, float* left, float* right)
  205. : bitDepth (16), name (name_), guid (guid_), sampleRate (rate),
  206. bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right),
  207. pDirectSound (nullptr), pOutputBuffer (nullptr)
  208. {
  209. }
  210. ~DSoundInternalOutChannel()
  211. {
  212. close();
  213. }
  214. void close()
  215. {
  216. if (pOutputBuffer != nullptr)
  217. {
  218. JUCE_DS_LOG ("closing output: " + name);
  219. HRESULT hr = pOutputBuffer->Stop();
  220. JUCE_DS_LOG_ERROR (hr); (void) hr;
  221. pOutputBuffer->Release();
  222. pOutputBuffer = nullptr;
  223. }
  224. if (pDirectSound != nullptr)
  225. {
  226. pDirectSound->Release();
  227. pDirectSound = nullptr;
  228. }
  229. }
  230. String open()
  231. {
  232. JUCE_DS_LOG ("opening output: " + name + " rate=" + String (sampleRate)
  233. + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
  234. pDirectSound = nullptr;
  235. pOutputBuffer = nullptr;
  236. writeOffset = 0;
  237. String error;
  238. HRESULT hr = E_NOINTERFACE;
  239. if (dsDirectSoundCreate != nullptr)
  240. hr = dsDirectSoundCreate (&guid, &pDirectSound, nullptr);
  241. if (SUCCEEDED (hr))
  242. {
  243. bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
  244. totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15;
  245. const int numChannels = 2;
  246. hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */);
  247. JUCE_DS_LOG_ERROR (hr);
  248. if (SUCCEEDED (hr))
  249. {
  250. IDirectSoundBuffer* pPrimaryBuffer;
  251. DSBUFFERDESC primaryDesc = { 0 };
  252. primaryDesc.dwSize = sizeof (DSBUFFERDESC);
  253. primaryDesc.dwFlags = 1 /* DSBCAPS_PRIMARYBUFFER */;
  254. primaryDesc.dwBufferBytes = 0;
  255. primaryDesc.lpwfxFormat = 0;
  256. JUCE_DS_LOG ("co-op level set");
  257. hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0);
  258. JUCE_DS_LOG_ERROR (hr);
  259. if (SUCCEEDED (hr))
  260. {
  261. WAVEFORMATEX wfFormat;
  262. wfFormat.wFormatTag = WAVE_FORMAT_PCM;
  263. wfFormat.nChannels = (unsigned short) numChannels;
  264. wfFormat.nSamplesPerSec = (DWORD) sampleRate;
  265. wfFormat.wBitsPerSample = (unsigned short) bitDepth;
  266. wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * wfFormat.wBitsPerSample / 8);
  267. wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
  268. wfFormat.cbSize = 0;
  269. hr = pPrimaryBuffer->SetFormat (&wfFormat);
  270. JUCE_DS_LOG_ERROR (hr);
  271. if (SUCCEEDED (hr))
  272. {
  273. DSBUFFERDESC secondaryDesc = { 0 };
  274. secondaryDesc.dwSize = sizeof (DSBUFFERDESC);
  275. secondaryDesc.dwFlags = 0x8000 /* DSBCAPS_GLOBALFOCUS */
  276. | 0x10000 /* DSBCAPS_GETCURRENTPOSITION2 */;
  277. secondaryDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer;
  278. secondaryDesc.lpwfxFormat = &wfFormat;
  279. hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0);
  280. JUCE_DS_LOG_ERROR (hr);
  281. if (SUCCEEDED (hr))
  282. {
  283. JUCE_DS_LOG ("buffer created");
  284. DWORD dwDataLen;
  285. unsigned char* pDSBuffData;
  286. hr = pOutputBuffer->Lock (0, (DWORD) totalBytesPerBuffer,
  287. (LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0);
  288. JUCE_DS_LOG_ERROR (hr);
  289. if (SUCCEEDED (hr))
  290. {
  291. zeromem (pDSBuffData, dwDataLen);
  292. hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0);
  293. if (SUCCEEDED (hr))
  294. {
  295. hr = pOutputBuffer->SetCurrentPosition (0);
  296. if (SUCCEEDED (hr))
  297. {
  298. hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */);
  299. if (SUCCEEDED (hr))
  300. return String::empty;
  301. }
  302. }
  303. }
  304. }
  305. }
  306. }
  307. }
  308. }
  309. error = DSoundLogging::getErrorMessage (hr);
  310. close();
  311. return error;
  312. }
  313. void synchronisePosition()
  314. {
  315. if (pOutputBuffer != nullptr)
  316. {
  317. DWORD playCursor;
  318. pOutputBuffer->GetCurrentPosition (&playCursor, &writeOffset);
  319. }
  320. }
  321. bool service()
  322. {
  323. if (pOutputBuffer == 0)
  324. return true;
  325. DWORD playCursor, writeCursor;
  326. for (;;)
  327. {
  328. HRESULT hr = pOutputBuffer->GetCurrentPosition (&playCursor, &writeCursor);
  329. if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
  330. {
  331. pOutputBuffer->Restore();
  332. continue;
  333. }
  334. if (SUCCEEDED (hr))
  335. break;
  336. JUCE_DS_LOG_ERROR (hr);
  337. jassertfalse;
  338. return true;
  339. }
  340. int playWriteGap = (int) (writeCursor - playCursor);
  341. if (playWriteGap < 0)
  342. playWriteGap += totalBytesPerBuffer;
  343. int bytesEmpty = (int) (playCursor - writeOffset);
  344. if (bytesEmpty < 0)
  345. bytesEmpty += totalBytesPerBuffer;
  346. if (bytesEmpty > (totalBytesPerBuffer - playWriteGap))
  347. {
  348. writeOffset = writeCursor;
  349. bytesEmpty = totalBytesPerBuffer - playWriteGap;
  350. }
  351. if (bytesEmpty >= bytesPerBuffer)
  352. {
  353. int* buf1 = nullptr;
  354. int* buf2 = nullptr;
  355. DWORD dwSize1 = 0;
  356. DWORD dwSize2 = 0;
  357. HRESULT hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer,
  358. (void**) &buf1, &dwSize1,
  359. (void**) &buf2, &dwSize2, 0);
  360. if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
  361. {
  362. pOutputBuffer->Restore();
  363. hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer,
  364. (void**) &buf1, &dwSize1,
  365. (void**) &buf2, &dwSize2, 0);
  366. }
  367. if (SUCCEEDED (hr))
  368. {
  369. if (bitDepth == 16)
  370. {
  371. const float* left = leftBuffer;
  372. const float* right = rightBuffer;
  373. int samples1 = (int) (dwSize1 >> 2);
  374. int samples2 = (int) (dwSize2 >> 2);
  375. if (left == nullptr)
  376. {
  377. for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (0, *right++);
  378. for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (0, *right++);
  379. }
  380. else if (right == nullptr)
  381. {
  382. for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, 0);
  383. for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, 0);
  384. }
  385. else
  386. {
  387. for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, *right++);
  388. for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, *right++);
  389. }
  390. }
  391. else
  392. {
  393. jassertfalse;
  394. }
  395. writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer;
  396. pOutputBuffer->Unlock (buf1, dwSize1, buf2, dwSize2);
  397. }
  398. else
  399. {
  400. jassertfalse;
  401. JUCE_DS_LOG_ERROR (hr);
  402. }
  403. bytesEmpty -= bytesPerBuffer;
  404. return true;
  405. }
  406. else
  407. {
  408. return false;
  409. }
  410. }
  411. int bitDepth;
  412. bool doneFlag;
  413. private:
  414. String name;
  415. GUID guid;
  416. int sampleRate, bufferSizeSamples;
  417. float* leftBuffer;
  418. float* rightBuffer;
  419. IDirectSound* pDirectSound;
  420. IDirectSoundBuffer* pOutputBuffer;
  421. DWORD writeOffset;
  422. int totalBytesPerBuffer, bytesPerBuffer;
  423. unsigned int lastPlayCursor;
  424. static inline int convertInputValues (const float l, const float r) noexcept
  425. {
  426. return jlimit (-32768, 32767, roundToInt (32767.0f * r)) << 16
  427. | (0xffff & jlimit (-32768, 32767, roundToInt (32767.0f * l)));
  428. }
  429. JUCE_DECLARE_NON_COPYABLE (DSoundInternalOutChannel)
  430. };
  431. //==============================================================================
  432. struct DSoundInternalInChannel
  433. {
  434. public:
  435. DSoundInternalInChannel (const String& name_, const GUID& guid_, int rate,
  436. int bufferSize, float* left, float* right)
  437. : bitDepth (16), name (name_), guid (guid_), sampleRate (rate),
  438. bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right),
  439. pDirectSound (nullptr), pDirectSoundCapture (nullptr), pInputBuffer (nullptr)
  440. {
  441. }
  442. ~DSoundInternalInChannel()
  443. {
  444. close();
  445. }
  446. void close()
  447. {
  448. if (pInputBuffer != nullptr)
  449. {
  450. JUCE_DS_LOG ("closing input: " + name);
  451. HRESULT hr = pInputBuffer->Stop();
  452. JUCE_DS_LOG_ERROR (hr); (void) hr;
  453. pInputBuffer->Release();
  454. pInputBuffer = nullptr;
  455. }
  456. if (pDirectSoundCapture != nullptr)
  457. {
  458. pDirectSoundCapture->Release();
  459. pDirectSoundCapture = nullptr;
  460. }
  461. if (pDirectSound != nullptr)
  462. {
  463. pDirectSound->Release();
  464. pDirectSound = nullptr;
  465. }
  466. }
  467. String open()
  468. {
  469. JUCE_DS_LOG ("opening input: " + name
  470. + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
  471. pDirectSound = nullptr;
  472. pDirectSoundCapture = nullptr;
  473. pInputBuffer = nullptr;
  474. readOffset = 0;
  475. totalBytesPerBuffer = 0;
  476. HRESULT hr = dsDirectSoundCaptureCreate != nullptr
  477. ? dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, nullptr)
  478. : E_NOINTERFACE;
  479. if (SUCCEEDED (hr))
  480. {
  481. const int numChannels = 2;
  482. bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
  483. totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15;
  484. WAVEFORMATEX wfFormat;
  485. wfFormat.wFormatTag = WAVE_FORMAT_PCM;
  486. wfFormat.nChannels = (unsigned short)numChannels;
  487. wfFormat.nSamplesPerSec = (DWORD) sampleRate;
  488. wfFormat.wBitsPerSample = (unsigned short) bitDepth;
  489. wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
  490. wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
  491. wfFormat.cbSize = 0;
  492. DSCBUFFERDESC captureDesc = { 0 };
  493. captureDesc.dwSize = sizeof (DSCBUFFERDESC);
  494. captureDesc.dwFlags = 0;
  495. captureDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer;
  496. captureDesc.lpwfxFormat = &wfFormat;
  497. JUCE_DS_LOG ("object created");
  498. hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0);
  499. if (SUCCEEDED (hr))
  500. {
  501. hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */);
  502. if (SUCCEEDED (hr))
  503. return String::empty;
  504. }
  505. }
  506. JUCE_DS_LOG_ERROR (hr);
  507. const String error (DSoundLogging::getErrorMessage (hr));
  508. close();
  509. return error;
  510. }
  511. void synchronisePosition()
  512. {
  513. if (pInputBuffer != nullptr)
  514. {
  515. DWORD capturePos;
  516. pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*) &readOffset);
  517. }
  518. }
  519. bool service()
  520. {
  521. if (pInputBuffer == 0)
  522. return true;
  523. DWORD capturePos, readPos;
  524. HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos);
  525. JUCE_DS_LOG_ERROR (hr);
  526. if (FAILED (hr))
  527. return true;
  528. int bytesFilled = (int) (readPos - readOffset);
  529. if (bytesFilled < 0)
  530. bytesFilled += totalBytesPerBuffer;
  531. if (bytesFilled >= bytesPerBuffer)
  532. {
  533. short* buf1 = nullptr;
  534. short* buf2 = nullptr;
  535. DWORD dwsize1 = 0;
  536. DWORD dwsize2 = 0;
  537. HRESULT hr = pInputBuffer->Lock ((DWORD) readOffset, (DWORD) bytesPerBuffer,
  538. (void**) &buf1, &dwsize1,
  539. (void**) &buf2, &dwsize2, 0);
  540. if (SUCCEEDED (hr))
  541. {
  542. if (bitDepth == 16)
  543. {
  544. const float g = 1.0f / 32768.0f;
  545. float* destL = leftBuffer;
  546. float* destR = rightBuffer;
  547. int samples1 = (int) (dwsize1 >> 2);
  548. int samples2 = (int) (dwsize2 >> 2);
  549. if (destL == nullptr)
  550. {
  551. for (const short* src = buf1; --samples1 >= 0;) { ++src; *destR++ = *src++ * g; }
  552. for (const short* src = buf2; --samples2 >= 0;) { ++src; *destR++ = *src++ * g; }
  553. }
  554. else if (destR == nullptr)
  555. {
  556. for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; ++src; }
  557. for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; ++src; }
  558. }
  559. else
  560. {
  561. for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; }
  562. for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; }
  563. }
  564. }
  565. else
  566. {
  567. jassertfalse;
  568. }
  569. readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer;
  570. pInputBuffer->Unlock (buf1, dwsize1, buf2, dwsize2);
  571. }
  572. else
  573. {
  574. JUCE_DS_LOG_ERROR (hr);
  575. jassertfalse;
  576. }
  577. bytesFilled -= bytesPerBuffer;
  578. return true;
  579. }
  580. else
  581. {
  582. return false;
  583. }
  584. }
  585. unsigned int readOffset;
  586. int bytesPerBuffer, totalBytesPerBuffer;
  587. int bitDepth;
  588. bool doneFlag;
  589. private:
  590. String name;
  591. GUID guid;
  592. int sampleRate, bufferSizeSamples;
  593. float* leftBuffer;
  594. float* rightBuffer;
  595. IDirectSound* pDirectSound;
  596. IDirectSoundCapture* pDirectSoundCapture;
  597. IDirectSoundCaptureBuffer* pInputBuffer;
  598. JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel)
  599. };
  600. //==============================================================================
  601. class DSoundAudioIODevice : public AudioIODevice,
  602. public Thread
  603. {
  604. public:
  605. DSoundAudioIODevice (const String& deviceName,
  606. const int outputDeviceIndex_,
  607. const int inputDeviceIndex_)
  608. : AudioIODevice (deviceName, "DirectSound"),
  609. Thread ("Juce DSound"),
  610. outputDeviceIndex (outputDeviceIndex_),
  611. inputDeviceIndex (inputDeviceIndex_),
  612. isOpen_ (false),
  613. isStarted (false),
  614. bufferSizeSamples (0),
  615. sampleRate (0.0),
  616. inputBuffers (1, 1),
  617. outputBuffers (1, 1),
  618. callback (nullptr)
  619. {
  620. if (outputDeviceIndex_ >= 0)
  621. {
  622. outChannels.add (TRANS("Left"));
  623. outChannels.add (TRANS("Right"));
  624. }
  625. if (inputDeviceIndex_ >= 0)
  626. {
  627. inChannels.add (TRANS("Left"));
  628. inChannels.add (TRANS("Right"));
  629. }
  630. }
  631. ~DSoundAudioIODevice()
  632. {
  633. close();
  634. }
  635. String open (const BigInteger& inputChannels,
  636. const BigInteger& outputChannels,
  637. double sampleRate, int bufferSizeSamples) override
  638. {
  639. lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
  640. isOpen_ = lastError.isEmpty();
  641. return lastError;
  642. }
  643. void close() override
  644. {
  645. stop();
  646. if (isOpen_)
  647. {
  648. closeDevice();
  649. isOpen_ = false;
  650. }
  651. }
  652. bool isOpen() override { return isOpen_ && isThreadRunning(); }
  653. int getCurrentBufferSizeSamples() override { return bufferSizeSamples; }
  654. double getCurrentSampleRate() override { return sampleRate; }
  655. BigInteger getActiveOutputChannels() const override { return enabledOutputs; }
  656. BigInteger getActiveInputChannels() const override { return enabledInputs; }
  657. int getOutputLatencyInSamples() override { return (int) (getCurrentBufferSizeSamples() * 1.5); }
  658. int getInputLatencyInSamples() override { return getOutputLatencyInSamples(); }
  659. StringArray getOutputChannelNames() override { return outChannels; }
  660. StringArray getInputChannelNames() override { return inChannels; }
  661. Array<double> getAvailableSampleRates() override
  662. {
  663. static const double rates[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
  664. return Array<double> (rates, numElementsInArray (rates));
  665. }
  666. Array<int> getAvailableBufferSizes() override
  667. {
  668. Array<int> r;
  669. int n = 64;
  670. for (int i = 0; i < 50; ++i)
  671. {
  672. r.add (n);
  673. n += (n < 512) ? 32
  674. : ((n < 1024) ? 64
  675. : ((n < 2048) ? 128 : 256));
  676. }
  677. return r;
  678. }
  679. int getDefaultBufferSize() override { return 2560; }
  680. int getCurrentBitDepth() override
  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) override
  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() override
  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() override { return isStarted && isOpen_ && isThreadRunning(); }
  721. String getLastError() override { 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() override
  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. }