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

10 years ago
10 years ago
10 years ago
9 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  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. callback (nullptr)
  617. {
  618. if (outputDeviceIndex_ >= 0)
  619. {
  620. outChannels.add (TRANS("Left"));
  621. outChannels.add (TRANS("Right"));
  622. }
  623. if (inputDeviceIndex_ >= 0)
  624. {
  625. inChannels.add (TRANS("Left"));
  626. inChannels.add (TRANS("Right"));
  627. }
  628. }
  629. ~DSoundAudioIODevice()
  630. {
  631. close();
  632. }
  633. String open (const BigInteger& inputChannels,
  634. const BigInteger& outputChannels,
  635. double sampleRate, int bufferSizeSamples) override
  636. {
  637. lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
  638. isOpen_ = lastError.isEmpty();
  639. return lastError;
  640. }
  641. void close() override
  642. {
  643. stop();
  644. if (isOpen_)
  645. {
  646. closeDevice();
  647. isOpen_ = false;
  648. }
  649. }
  650. bool isOpen() override { return isOpen_ && isThreadRunning(); }
  651. int getCurrentBufferSizeSamples() override { return bufferSizeSamples; }
  652. double getCurrentSampleRate() override { return sampleRate; }
  653. BigInteger getActiveOutputChannels() const override { return enabledOutputs; }
  654. BigInteger getActiveInputChannels() const override { return enabledInputs; }
  655. int getOutputLatencyInSamples() override { return (int) (getCurrentBufferSizeSamples() * 1.5); }
  656. int getInputLatencyInSamples() override { return getOutputLatencyInSamples(); }
  657. StringArray getOutputChannelNames() override { return outChannels; }
  658. StringArray getInputChannelNames() override { return inChannels; }
  659. Array<double> getAvailableSampleRates() override
  660. {
  661. static const double rates[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
  662. return Array<double> (rates, numElementsInArray (rates));
  663. }
  664. Array<int> getAvailableBufferSizes() override
  665. {
  666. Array<int> r;
  667. int n = 64;
  668. for (int i = 0; i < 50; ++i)
  669. {
  670. r.add (n);
  671. n += (n < 512) ? 32
  672. : ((n < 1024) ? 64
  673. : ((n < 2048) ? 128 : 256));
  674. }
  675. return r;
  676. }
  677. int getDefaultBufferSize() override { return 2560; }
  678. int getCurrentBitDepth() override
  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) override
  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() override
  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() override { return isStarted && isOpen_ && isThreadRunning(); }
  719. String getLastError() override { 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 (inputBuffers.getArrayOfReadPointers(), inputBuffers.getNumChannels(),
  831. outputBuffers.getArrayOfWritePointers(), outputBuffers.getNumChannels(),
  832. bufferSizeSamples);
  833. }
  834. else
  835. {
  836. outputBuffers.clear();
  837. sleep (1);
  838. }
  839. }
  840. }
  841. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice)
  842. };
  843. //==============================================================================
  844. struct DSoundDeviceList
  845. {
  846. StringArray outputDeviceNames, inputDeviceNames;
  847. Array<GUID> outputGuids, inputGuids;
  848. void scan()
  849. {
  850. outputDeviceNames.clear();
  851. inputDeviceNames.clear();
  852. outputGuids.clear();
  853. inputGuids.clear();
  854. if (dsDirectSoundEnumerateW != 0)
  855. {
  856. dsDirectSoundEnumerateW (outputEnumProcW, this);
  857. dsDirectSoundCaptureEnumerateW (inputEnumProcW, this);
  858. }
  859. }
  860. bool operator!= (const DSoundDeviceList& other) const noexcept
  861. {
  862. return outputDeviceNames != other.outputDeviceNames
  863. || inputDeviceNames != other.inputDeviceNames
  864. || outputGuids != other.outputGuids
  865. || inputGuids != other.inputGuids;
  866. }
  867. private:
  868. static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids)
  869. {
  870. desc = desc.trim();
  871. if (desc.isNotEmpty())
  872. {
  873. const String origDesc (desc);
  874. int n = 2;
  875. while (names.contains (desc))
  876. desc = origDesc + " (" + String (n++) + ")";
  877. names.add (desc);
  878. guids.add (lpGUID != nullptr ? *lpGUID : GUID());
  879. }
  880. return TRUE;
  881. }
  882. BOOL outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); }
  883. BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); }
  884. static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
  885. {
  886. return static_cast<DSoundDeviceList*> (object)->outputEnumProc (lpGUID, description);
  887. }
  888. static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
  889. {
  890. return static_cast<DSoundDeviceList*> (object)->inputEnumProc (lpGUID, description);
  891. }
  892. };
  893. //==============================================================================
  894. String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels,
  895. const BigInteger& outputChannels,
  896. double sampleRate_, int bufferSizeSamples_)
  897. {
  898. closeDevice();
  899. sampleRate = sampleRate_;
  900. if (bufferSizeSamples_ <= 0)
  901. bufferSizeSamples_ = 960; // use as a default size if none is set.
  902. bufferSizeSamples = bufferSizeSamples_ & ~7;
  903. DSoundDeviceList dlh;
  904. dlh.scan();
  905. enabledInputs = inputChannels;
  906. enabledInputs.setRange (inChannels.size(),
  907. enabledInputs.getHighestBit() + 1 - inChannels.size(),
  908. false);
  909. inputBuffers.setSize (jmax (1, enabledInputs.countNumberOfSetBits()), bufferSizeSamples);
  910. inputBuffers.clear();
  911. int numIns = 0;
  912. for (int i = 0; i <= enabledInputs.getHighestBit(); i += 2)
  913. {
  914. float* left = enabledInputs[i] ? inputBuffers.getWritePointer (numIns++) : nullptr;
  915. float* right = enabledInputs[i + 1] ? inputBuffers.getWritePointer (numIns++) : nullptr;
  916. if (left != nullptr || right != nullptr)
  917. inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex],
  918. dlh.inputGuids [inputDeviceIndex],
  919. (int) sampleRate, bufferSizeSamples,
  920. left, right));
  921. }
  922. enabledOutputs = outputChannels;
  923. enabledOutputs.setRange (outChannels.size(),
  924. enabledOutputs.getHighestBit() + 1 - outChannels.size(),
  925. false);
  926. outputBuffers.setSize (jmax (1, enabledOutputs.countNumberOfSetBits()), bufferSizeSamples);
  927. outputBuffers.clear();
  928. int numOuts = 0;
  929. for (int i = 0; i <= enabledOutputs.getHighestBit(); i += 2)
  930. {
  931. float* left = enabledOutputs[i] ? outputBuffers.getWritePointer (numOuts++) : nullptr;
  932. float* right = enabledOutputs[i + 1] ? outputBuffers.getWritePointer (numOuts++) : nullptr;
  933. if (left != nullptr || right != nullptr)
  934. outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex],
  935. dlh.outputGuids [outputDeviceIndex],
  936. (int) sampleRate, bufferSizeSamples,
  937. left, right));
  938. }
  939. String error;
  940. // boost our priority while opening the devices to try to get better sync between them
  941. const int oldThreadPri = GetThreadPriority (GetCurrentThread());
  942. const DWORD oldProcPri = GetPriorityClass (GetCurrentProcess());
  943. SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  944. SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
  945. for (int i = 0; i < outChans.size(); ++i)
  946. {
  947. error = outChans[i]->open();
  948. if (error.isNotEmpty())
  949. {
  950. error = "Error opening " + dlh.outputDeviceNames[i] + ": \"" + error + "\"";
  951. break;
  952. }
  953. }
  954. if (error.isEmpty())
  955. {
  956. for (int i = 0; i < inChans.size(); ++i)
  957. {
  958. error = inChans[i]->open();
  959. if (error.isNotEmpty())
  960. {
  961. error = "Error opening " + dlh.inputDeviceNames[i] + ": \"" + error + "\"";
  962. break;
  963. }
  964. }
  965. }
  966. if (error.isEmpty())
  967. {
  968. for (int i = 0; i < outChans.size(); ++i)
  969. outChans.getUnchecked(i)->synchronisePosition();
  970. for (int i = 0; i < inChans.size(); ++i)
  971. inChans.getUnchecked(i)->synchronisePosition();
  972. startThread (9);
  973. sleep (10);
  974. notify();
  975. }
  976. else
  977. {
  978. JUCE_DS_LOG ("Opening failed: " + error);
  979. }
  980. SetThreadPriority (GetCurrentThread(), oldThreadPri);
  981. SetPriorityClass (GetCurrentProcess(), oldProcPri);
  982. return error;
  983. }
  984. //==============================================================================
  985. class DSoundAudioIODeviceType : public AudioIODeviceType,
  986. private DeviceChangeDetector
  987. {
  988. public:
  989. DSoundAudioIODeviceType()
  990. : AudioIODeviceType ("DirectSound"),
  991. DeviceChangeDetector (L"DirectSound"),
  992. hasScanned (false)
  993. {
  994. initialiseDSoundFunctions();
  995. }
  996. void scanForDevices()
  997. {
  998. hasScanned = true;
  999. deviceList.scan();
  1000. }
  1001. StringArray getDeviceNames (bool wantInputNames) const
  1002. {
  1003. jassert (hasScanned); // need to call scanForDevices() before doing this
  1004. return wantInputNames ? deviceList.inputDeviceNames
  1005. : deviceList.outputDeviceNames;
  1006. }
  1007. int getDefaultDeviceIndex (bool /*forInput*/) const
  1008. {
  1009. jassert (hasScanned); // need to call scanForDevices() before doing this
  1010. return 0;
  1011. }
  1012. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  1013. {
  1014. jassert (hasScanned); // need to call scanForDevices() before doing this
  1015. if (DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device))
  1016. return asInput ? d->inputDeviceIndex
  1017. : d->outputDeviceIndex;
  1018. return -1;
  1019. }
  1020. bool hasSeparateInputsAndOutputs() const { return true; }
  1021. AudioIODevice* createDevice (const String& outputDeviceName,
  1022. const String& inputDeviceName)
  1023. {
  1024. jassert (hasScanned); // need to call scanForDevices() before doing this
  1025. const int outputIndex = deviceList.outputDeviceNames.indexOf (outputDeviceName);
  1026. const int inputIndex = deviceList.inputDeviceNames.indexOf (inputDeviceName);
  1027. if (outputIndex >= 0 || inputIndex >= 0)
  1028. return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
  1029. : inputDeviceName,
  1030. outputIndex, inputIndex);
  1031. return nullptr;
  1032. }
  1033. private:
  1034. DSoundDeviceList deviceList;
  1035. bool hasScanned;
  1036. void systemDeviceChanged() override
  1037. {
  1038. DSoundDeviceList newList;
  1039. newList.scan();
  1040. if (newList != deviceList)
  1041. {
  1042. deviceList = newList;
  1043. callDeviceChangeListeners();
  1044. }
  1045. }
  1046. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType)
  1047. };
  1048. //==============================================================================
  1049. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
  1050. {
  1051. return new DSoundAudioIODeviceType();
  1052. }