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 43KB

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