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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  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)
  638. {
  639. lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
  640. isOpen_ = lastError.isEmpty();
  641. return lastError;
  642. }
  643. void close()
  644. {
  645. stop();
  646. if (isOpen_)
  647. {
  648. closeDevice();
  649. isOpen_ = false;
  650. }
  651. }
  652. bool isOpen() { return isOpen_ && isThreadRunning(); }
  653. int getCurrentBufferSizeSamples() { return bufferSizeSamples; }
  654. double getCurrentSampleRate() { return sampleRate; }
  655. BigInteger getActiveOutputChannels() const { return enabledOutputs; }
  656. BigInteger getActiveInputChannels() const { return enabledInputs; }
  657. int getOutputLatencyInSamples() { return (int) (getCurrentBufferSizeSamples() * 1.5); }
  658. int getInputLatencyInSamples() { return getOutputLatencyInSamples(); }
  659. StringArray getOutputChannelNames() { return outChannels; }
  660. StringArray getInputChannelNames() { return inChannels; }
  661. int getNumSampleRates() { return 4; }
  662. int getDefaultBufferSize() { return 2560; }
  663. int getNumBufferSizesAvailable() { return 50; }
  664. double getSampleRate (int index)
  665. {
  666. const double samps[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
  667. return samps [jlimit (0, 3, index)];
  668. }
  669. int getBufferSizeSamples (int index)
  670. {
  671. int n = 64;
  672. for (int i = 0; i < index; ++i)
  673. n += (n < 512) ? 32
  674. : ((n < 1024) ? 64
  675. : ((n < 2048) ? 128 : 256));
  676. return n;
  677. }
  678. int getCurrentBitDepth()
  679. {
  680. int bits = 256;
  681. for (int i = inChans.size(); --i >= 0;)
  682. bits = jmin (bits, inChans[i]->bitDepth);
  683. for (int i = outChans.size(); --i >= 0;)
  684. bits = jmin (bits, outChans[i]->bitDepth);
  685. if (bits > 32)
  686. bits = 16;
  687. return bits;
  688. }
  689. void start (AudioIODeviceCallback* call)
  690. {
  691. if (isOpen_ && call != nullptr && ! isStarted)
  692. {
  693. if (! isThreadRunning())
  694. {
  695. // something gone wrong and the thread's stopped..
  696. isOpen_ = false;
  697. return;
  698. }
  699. call->audioDeviceAboutToStart (this);
  700. const ScopedLock sl (startStopLock);
  701. callback = call;
  702. isStarted = true;
  703. }
  704. }
  705. void stop()
  706. {
  707. if (isStarted)
  708. {
  709. AudioIODeviceCallback* const callbackLocal = callback;
  710. {
  711. const ScopedLock sl (startStopLock);
  712. isStarted = false;
  713. }
  714. if (callbackLocal != nullptr)
  715. callbackLocal->audioDeviceStopped();
  716. }
  717. }
  718. bool isPlaying() { return isStarted && isOpen_ && isThreadRunning(); }
  719. String getLastError() { return lastError; }
  720. //==============================================================================
  721. StringArray inChannels, outChannels;
  722. int outputDeviceIndex, inputDeviceIndex;
  723. private:
  724. bool isOpen_;
  725. bool isStarted;
  726. String lastError;
  727. OwnedArray <DSoundInternalInChannel> inChans;
  728. OwnedArray <DSoundInternalOutChannel> outChans;
  729. WaitableEvent startEvent;
  730. int bufferSizeSamples;
  731. double sampleRate;
  732. BigInteger enabledInputs, enabledOutputs;
  733. AudioSampleBuffer inputBuffers, outputBuffers;
  734. AudioIODeviceCallback* callback;
  735. CriticalSection startStopLock;
  736. String openDevice (const BigInteger& inputChannels,
  737. const BigInteger& outputChannels,
  738. double sampleRate_, int bufferSizeSamples_);
  739. void closeDevice()
  740. {
  741. isStarted = false;
  742. stopThread (5000);
  743. inChans.clear();
  744. outChans.clear();
  745. inputBuffers.setSize (1, 1);
  746. outputBuffers.setSize (1, 1);
  747. }
  748. void resync()
  749. {
  750. if (! threadShouldExit())
  751. {
  752. sleep (5);
  753. for (int i = 0; i < outChans.size(); ++i)
  754. outChans.getUnchecked(i)->synchronisePosition();
  755. for (int i = 0; i < inChans.size(); ++i)
  756. inChans.getUnchecked(i)->synchronisePosition();
  757. }
  758. }
  759. public:
  760. void run() override
  761. {
  762. while (! threadShouldExit())
  763. {
  764. if (wait (100))
  765. break;
  766. }
  767. const int latencyMs = (int) (bufferSizeSamples * 1000.0 / sampleRate);
  768. const int maxTimeMS = jmax (5, 3 * latencyMs);
  769. while (! threadShouldExit())
  770. {
  771. int numToDo = 0;
  772. uint32 startTime = Time::getMillisecondCounter();
  773. for (int i = inChans.size(); --i >= 0;)
  774. {
  775. inChans.getUnchecked(i)->doneFlag = false;
  776. ++numToDo;
  777. }
  778. for (int i = outChans.size(); --i >= 0;)
  779. {
  780. outChans.getUnchecked(i)->doneFlag = false;
  781. ++numToDo;
  782. }
  783. if (numToDo > 0)
  784. {
  785. const int maxCount = 3;
  786. int count = maxCount;
  787. for (;;)
  788. {
  789. for (int i = inChans.size(); --i >= 0;)
  790. {
  791. DSoundInternalInChannel* const in = inChans.getUnchecked(i);
  792. if ((! in->doneFlag) && in->service())
  793. {
  794. in->doneFlag = true;
  795. --numToDo;
  796. }
  797. }
  798. for (int i = outChans.size(); --i >= 0;)
  799. {
  800. DSoundInternalOutChannel* const out = outChans.getUnchecked(i);
  801. if ((! out->doneFlag) && out->service())
  802. {
  803. out->doneFlag = true;
  804. --numToDo;
  805. }
  806. }
  807. if (numToDo <= 0)
  808. break;
  809. if (Time::getMillisecondCounter() > startTime + maxTimeMS)
  810. {
  811. resync();
  812. break;
  813. }
  814. if (--count <= 0)
  815. {
  816. Sleep (1);
  817. count = maxCount;
  818. }
  819. if (threadShouldExit())
  820. return;
  821. }
  822. }
  823. else
  824. {
  825. sleep (1);
  826. }
  827. const ScopedLock sl (startStopLock);
  828. if (isStarted)
  829. {
  830. callback->audioDeviceIOCallback (const_cast <const float**> (inputBuffers.getArrayOfChannels()),
  831. inputBuffers.getNumChannels(),
  832. outputBuffers.getArrayOfChannels(),
  833. outputBuffers.getNumChannels(),
  834. bufferSizeSamples);
  835. }
  836. else
  837. {
  838. outputBuffers.clear();
  839. sleep (1);
  840. }
  841. }
  842. }
  843. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice)
  844. };
  845. //==============================================================================
  846. struct DSoundDeviceList
  847. {
  848. StringArray outputDeviceNames, inputDeviceNames;
  849. Array<GUID> outputGuids, inputGuids;
  850. void scan()
  851. {
  852. outputDeviceNames.clear();
  853. inputDeviceNames.clear();
  854. outputGuids.clear();
  855. inputGuids.clear();
  856. if (dsDirectSoundEnumerateW != 0)
  857. {
  858. dsDirectSoundEnumerateW (outputEnumProcW, this);
  859. dsDirectSoundCaptureEnumerateW (inputEnumProcW, this);
  860. }
  861. }
  862. bool operator!= (const DSoundDeviceList& other) const noexcept
  863. {
  864. return outputDeviceNames != other.outputDeviceNames
  865. || inputDeviceNames != other.inputDeviceNames
  866. || outputGuids != other.outputGuids
  867. || inputGuids != other.inputGuids;
  868. }
  869. private:
  870. static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids)
  871. {
  872. desc = desc.trim();
  873. if (desc.isNotEmpty())
  874. {
  875. const String origDesc (desc);
  876. int n = 2;
  877. while (names.contains (desc))
  878. desc = origDesc + " (" + String (n++) + ")";
  879. names.add (desc);
  880. guids.add (lpGUID != nullptr ? *lpGUID : GUID());
  881. }
  882. return TRUE;
  883. }
  884. BOOL outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); }
  885. BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); }
  886. static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
  887. {
  888. return static_cast<DSoundDeviceList*> (object)->outputEnumProc (lpGUID, description);
  889. }
  890. static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
  891. {
  892. return static_cast<DSoundDeviceList*> (object)->inputEnumProc (lpGUID, description);
  893. }
  894. };
  895. //==============================================================================
  896. String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels,
  897. const BigInteger& outputChannels,
  898. double sampleRate_, int bufferSizeSamples_)
  899. {
  900. closeDevice();
  901. sampleRate = sampleRate_;
  902. if (bufferSizeSamples_ <= 0)
  903. bufferSizeSamples_ = 960; // use as a default size if none is set.
  904. bufferSizeSamples = bufferSizeSamples_ & ~7;
  905. DSoundDeviceList dlh;
  906. dlh.scan();
  907. enabledInputs = inputChannels;
  908. enabledInputs.setRange (inChannels.size(),
  909. enabledInputs.getHighestBit() + 1 - inChannels.size(),
  910. false);
  911. inputBuffers.setSize (jmax (1, enabledInputs.countNumberOfSetBits()), bufferSizeSamples);
  912. inputBuffers.clear();
  913. int numIns = 0;
  914. for (int i = 0; i <= enabledInputs.getHighestBit(); i += 2)
  915. {
  916. float* left = nullptr;
  917. if (enabledInputs[i])
  918. left = inputBuffers.getSampleData (numIns++);
  919. float* right = nullptr;
  920. if (enabledInputs[i + 1])
  921. right = inputBuffers.getSampleData (numIns++);
  922. if (left != nullptr || right != nullptr)
  923. inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex],
  924. dlh.inputGuids [inputDeviceIndex],
  925. (int) sampleRate, bufferSizeSamples,
  926. left, right));
  927. }
  928. enabledOutputs = outputChannels;
  929. enabledOutputs.setRange (outChannels.size(),
  930. enabledOutputs.getHighestBit() + 1 - outChannels.size(),
  931. false);
  932. outputBuffers.setSize (jmax (1, enabledOutputs.countNumberOfSetBits()), bufferSizeSamples);
  933. outputBuffers.clear();
  934. int numOuts = 0;
  935. for (int i = 0; i <= enabledOutputs.getHighestBit(); i += 2)
  936. {
  937. float* left = nullptr;
  938. if (enabledOutputs[i])
  939. left = outputBuffers.getSampleData (numOuts++);
  940. float* right = nullptr;
  941. if (enabledOutputs[i + 1])
  942. right = outputBuffers.getSampleData (numOuts++);
  943. if (left != nullptr || right != nullptr)
  944. outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex],
  945. dlh.outputGuids [outputDeviceIndex],
  946. (int) sampleRate, bufferSizeSamples,
  947. left, right));
  948. }
  949. String error;
  950. // boost our priority while opening the devices to try to get better sync between them
  951. const int oldThreadPri = GetThreadPriority (GetCurrentThread());
  952. const DWORD oldProcPri = GetPriorityClass (GetCurrentProcess());
  953. SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  954. SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  955. for (int i = 0; i < outChans.size(); ++i)
  956. {
  957. error = outChans[i]->open();
  958. if (error.isNotEmpty())
  959. {
  960. error = "Error opening " + dlh.outputDeviceNames[i] + ": \"" + error + "\"";
  961. break;
  962. }
  963. }
  964. if (error.isEmpty())
  965. {
  966. for (int i = 0; i < inChans.size(); ++i)
  967. {
  968. error = inChans[i]->open();
  969. if (error.isNotEmpty())
  970. {
  971. error = "Error opening " + dlh.inputDeviceNames[i] + ": \"" + error + "\"";
  972. break;
  973. }
  974. }
  975. }
  976. if (error.isEmpty())
  977. {
  978. for (int i = 0; i < outChans.size(); ++i)
  979. outChans.getUnchecked(i)->synchronisePosition();
  980. for (int i = 0; i < inChans.size(); ++i)
  981. inChans.getUnchecked(i)->synchronisePosition();
  982. startThread (9);
  983. sleep (10);
  984. notify();
  985. }
  986. else
  987. {
  988. JUCE_DS_LOG ("Opening failed: " + error);
  989. }
  990. SetThreadPriority (GetCurrentThread(), oldThreadPri);
  991. SetPriorityClass (GetCurrentProcess(), oldProcPri);
  992. return error;
  993. }
  994. //==============================================================================
  995. class DSoundAudioIODeviceType : public AudioIODeviceType,
  996. private DeviceChangeDetector
  997. {
  998. public:
  999. DSoundAudioIODeviceType()
  1000. : AudioIODeviceType ("DirectSound"),
  1001. DeviceChangeDetector (L"DirectSound"),
  1002. hasScanned (false)
  1003. {
  1004. initialiseDSoundFunctions();
  1005. }
  1006. void scanForDevices()
  1007. {
  1008. hasScanned = true;
  1009. deviceList.scan();
  1010. }
  1011. StringArray getDeviceNames (bool wantInputNames) const
  1012. {
  1013. jassert (hasScanned); // need to call scanForDevices() before doing this
  1014. return wantInputNames ? deviceList.inputDeviceNames
  1015. : deviceList.outputDeviceNames;
  1016. }
  1017. int getDefaultDeviceIndex (bool /*forInput*/) const
  1018. {
  1019. jassert (hasScanned); // need to call scanForDevices() before doing this
  1020. return 0;
  1021. }
  1022. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  1023. {
  1024. jassert (hasScanned); // need to call scanForDevices() before doing this
  1025. if (DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device))
  1026. return asInput ? d->inputDeviceIndex
  1027. : d->outputDeviceIndex;
  1028. return -1;
  1029. }
  1030. bool hasSeparateInputsAndOutputs() const { return true; }
  1031. AudioIODevice* createDevice (const String& outputDeviceName,
  1032. const String& inputDeviceName)
  1033. {
  1034. jassert (hasScanned); // need to call scanForDevices() before doing this
  1035. const int outputIndex = deviceList.outputDeviceNames.indexOf (outputDeviceName);
  1036. const int inputIndex = deviceList.inputDeviceNames.indexOf (inputDeviceName);
  1037. if (outputIndex >= 0 || inputIndex >= 0)
  1038. return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
  1039. : inputDeviceName,
  1040. outputIndex, inputIndex);
  1041. return nullptr;
  1042. }
  1043. private:
  1044. DSoundDeviceList deviceList;
  1045. bool hasScanned;
  1046. void systemDeviceChanged()
  1047. {
  1048. DSoundDeviceList newList;
  1049. newList.scan();
  1050. if (newList != deviceList)
  1051. {
  1052. deviceList = newList;
  1053. callDeviceChangeListeners();
  1054. }
  1055. }
  1056. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType)
  1057. };
  1058. //==============================================================================
  1059. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
  1060. {
  1061. return new DSoundAudioIODeviceType();
  1062. }