Add TAL-Reverb-II plugin to test
[juce-lv2.git] / juce / source / src / native / windows / juce_win32_DirectSound.cpp
blobd8e1ebda9d89ab3912fc2d09e85c97bc574a7ee6
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 // (This file gets included by juce_win32_NativeCode.cpp, rather than being
27 // compiled on its own).
28 #if JUCE_INCLUDED_FILE && JUCE_DIRECTSOUND
30 //==============================================================================
31 END_JUCE_NAMESPACE
33 extern "C"
36 // Declare just the minimum number of interfaces for the DSound objects that we need..
37 typedef struct typeDSBUFFERDESC
39 DWORD dwSize;
40 DWORD dwFlags;
41 DWORD dwBufferBytes;
42 DWORD dwReserved;
43 LPWAVEFORMATEX lpwfxFormat;
44 GUID guid3DAlgorithm;
45 } DSBUFFERDESC;
47 struct IDirectSoundBuffer;
49 #undef INTERFACE
50 #define INTERFACE IDirectSound
51 DECLARE_INTERFACE_(IDirectSound, IUnknown)
53 STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
54 STDMETHOD_(ULONG,AddRef) (THIS) PURE;
55 STDMETHOD_(ULONG,Release) (THIS) PURE;
56 STDMETHOD(CreateSoundBuffer) (THIS_ DSBUFFERDESC*, IDirectSoundBuffer**, LPUNKNOWN) PURE;
57 STDMETHOD(GetCaps) (THIS_ void*) PURE;
58 STDMETHOD(DuplicateSoundBuffer) (THIS_ IDirectSoundBuffer*, IDirectSoundBuffer**) PURE;
59 STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE;
60 STDMETHOD(Compact) (THIS) PURE;
61 STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE;
62 STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE;
63 STDMETHOD(Initialize) (THIS_ const GUID*) PURE;
66 #undef INTERFACE
67 #define INTERFACE IDirectSoundBuffer
68 DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown)
70 STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
71 STDMETHOD_(ULONG,AddRef) (THIS) PURE;
72 STDMETHOD_(ULONG,Release) (THIS) PURE;
73 STDMETHOD(GetCaps) (THIS_ void*) PURE;
74 STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE;
75 STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE;
76 STDMETHOD(GetVolume) (THIS_ LPLONG) PURE;
77 STDMETHOD(GetPan) (THIS_ LPLONG) PURE;
78 STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE;
79 STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE;
80 STDMETHOD(Initialize) (THIS_ IDirectSound*, DSBUFFERDESC*) PURE;
81 STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE;
82 STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE;
83 STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE;
84 STDMETHOD(SetFormat) (THIS_ const WAVEFORMATEX*) PURE;
85 STDMETHOD(SetVolume) (THIS_ LONG) PURE;
86 STDMETHOD(SetPan) (THIS_ LONG) PURE;
87 STDMETHOD(SetFrequency) (THIS_ DWORD) PURE;
88 STDMETHOD(Stop) (THIS) PURE;
89 STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE;
90 STDMETHOD(Restore) (THIS) PURE;
93 //==============================================================================
94 typedef struct typeDSCBUFFERDESC
96 DWORD dwSize;
97 DWORD dwFlags;
98 DWORD dwBufferBytes;
99 DWORD dwReserved;
100 LPWAVEFORMATEX lpwfxFormat;
101 } DSCBUFFERDESC;
103 struct IDirectSoundCaptureBuffer;
105 #undef INTERFACE
106 #define INTERFACE IDirectSoundCapture
107 DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown)
109 STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
110 STDMETHOD_(ULONG,AddRef) (THIS) PURE;
111 STDMETHOD_(ULONG,Release) (THIS) PURE;
112 STDMETHOD(CreateCaptureBuffer) (THIS_ DSCBUFFERDESC*, IDirectSoundCaptureBuffer**, LPUNKNOWN) PURE;
113 STDMETHOD(GetCaps) (THIS_ void*) PURE;
114 STDMETHOD(Initialize) (THIS_ const GUID*) PURE;
117 #undef INTERFACE
118 #define INTERFACE IDirectSoundCaptureBuffer
119 DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown)
121 STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
122 STDMETHOD_(ULONG,AddRef) (THIS) PURE;
123 STDMETHOD_(ULONG,Release) (THIS) PURE;
124 STDMETHOD(GetCaps) (THIS_ void*) PURE;
125 STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE;
126 STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE;
127 STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE;
128 STDMETHOD(Initialize) (THIS_ IDirectSoundCapture*, DSCBUFFERDESC*) PURE;
129 STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID*, LPDWORD, LPVOID*, LPDWORD, DWORD) PURE;
130 STDMETHOD(Start) (THIS_ DWORD) PURE;
131 STDMETHOD(Stop) (THIS) PURE;
132 STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE;
137 //==============================================================================
138 BEGIN_JUCE_NAMESPACE
140 namespace
142 String getDSErrorMessage (HRESULT hr)
144 const char* result = nullptr;
146 switch (hr)
148 case MAKE_HRESULT(1, 0x878, 10): result = "Device already allocated"; break;
149 case MAKE_HRESULT(1, 0x878, 30): result = "Control unavailable"; break;
150 case E_INVALIDARG: result = "Invalid parameter"; break;
151 case MAKE_HRESULT(1, 0x878, 50): result = "Invalid call"; break;
152 case E_FAIL: result = "Generic error"; break;
153 case MAKE_HRESULT(1, 0x878, 70): result = "Priority level error"; break;
154 case E_OUTOFMEMORY: result = "Out of memory"; break;
155 case MAKE_HRESULT(1, 0x878, 100): result = "Bad format"; break;
156 case E_NOTIMPL: result = "Unsupported function"; break;
157 case MAKE_HRESULT(1, 0x878, 120): result = "No driver"; break;
158 case MAKE_HRESULT(1, 0x878, 130): result = "Already initialised"; break;
159 case CLASS_E_NOAGGREGATION: result = "No aggregation"; break;
160 case MAKE_HRESULT(1, 0x878, 150): result = "Buffer lost"; break;
161 case MAKE_HRESULT(1, 0x878, 160): result = "Another app has priority"; break;
162 case MAKE_HRESULT(1, 0x878, 170): result = "Uninitialised"; break;
163 case E_NOINTERFACE: result = "No interface"; break;
164 case S_OK: result = "No error"; break;
165 default: return "Unknown error: " + String ((int) hr);
168 return result;
171 //==============================================================================
172 #define DS_DEBUGGING 1
174 #ifdef DS_DEBUGGING
175 #define CATCH JUCE_CATCH_EXCEPTION
176 #undef log
177 #define log(a) Logger::writeToLog(a);
178 #undef logError
179 #define logError(a) logDSError(a, __LINE__);
181 static void logDSError (HRESULT hr, int lineNum)
183 if (hr != S_OK)
185 String error ("DS error at line ");
186 error << lineNum << " - " << getDSErrorMessage (hr);
187 log (error);
190 #else
191 #define CATCH JUCE_CATCH_ALL
192 #define log(a)
193 #define logError(a)
194 #endif
196 //==============================================================================
197 #define DSOUND_FUNCTION(functionName, params) \
198 typedef HRESULT (WINAPI *type##functionName) params; \
199 static type##functionName ds##functionName = 0;
201 #define DSOUND_FUNCTION_LOAD(functionName) \
202 ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \
203 jassert (ds##functionName != 0);
205 typedef BOOL (CALLBACK *LPDSENUMCALLBACKW) (LPGUID, LPCWSTR, LPCWSTR, LPVOID);
206 typedef BOOL (CALLBACK *LPDSENUMCALLBACKA) (LPGUID, LPCSTR, LPCSTR, LPVOID);
208 DSOUND_FUNCTION (DirectSoundCreate, (const GUID*, IDirectSound**, LPUNKNOWN))
209 DSOUND_FUNCTION (DirectSoundCaptureCreate, (const GUID*, IDirectSoundCapture**, LPUNKNOWN))
210 DSOUND_FUNCTION (DirectSoundEnumerateW, (LPDSENUMCALLBACKW, LPVOID))
211 DSOUND_FUNCTION (DirectSoundCaptureEnumerateW, (LPDSENUMCALLBACKW, LPVOID))
213 void initialiseDSoundFunctions()
215 if (dsDirectSoundCreate == 0)
217 HMODULE h = LoadLibraryA ("dsound.dll");
219 DSOUND_FUNCTION_LOAD (DirectSoundCreate)
220 DSOUND_FUNCTION_LOAD (DirectSoundCaptureCreate)
221 DSOUND_FUNCTION_LOAD (DirectSoundEnumerateW)
222 DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW)
227 //==============================================================================
228 class DSoundInternalOutChannel
230 public:
231 DSoundInternalOutChannel (const String& name_, const GUID& guid_, int rate,
232 int bufferSize, float* left, float* right)
233 : bitDepth (16), name (name_), guid (guid_), sampleRate (rate),
234 bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right),
235 pDirectSound (nullptr), pOutputBuffer (nullptr)
239 ~DSoundInternalOutChannel()
241 close();
244 void close()
246 HRESULT hr;
248 if (pOutputBuffer != nullptr)
250 log ("closing dsound out: " + name);
251 hr = pOutputBuffer->Stop();
252 logError (hr);
254 hr = pOutputBuffer->Release();
255 pOutputBuffer = nullptr;
256 logError (hr);
259 if (pDirectSound != nullptr)
261 hr = pDirectSound->Release();
262 pDirectSound = nullptr;
263 logError (hr);
267 String open()
269 log ("opening dsound out device: " + name + " rate=" + String (sampleRate)
270 + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
272 pDirectSound = nullptr;
273 pOutputBuffer = nullptr;
274 writeOffset = 0;
276 String error;
277 HRESULT hr = E_NOINTERFACE;
279 if (dsDirectSoundCreate != 0)
280 hr = dsDirectSoundCreate (&guid, &pDirectSound, 0);
282 if (hr == S_OK)
284 bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
285 totalBytesPerBuffer = (3 * bytesPerBuffer) & ~15;
286 const int numChannels = 2;
288 hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */);
289 logError (hr);
291 if (hr == S_OK)
293 IDirectSoundBuffer* pPrimaryBuffer;
295 DSBUFFERDESC primaryDesc = { 0 };
296 primaryDesc.dwSize = sizeof (DSBUFFERDESC);
297 primaryDesc.dwFlags = 1 /* DSBCAPS_PRIMARYBUFFER */;
298 primaryDesc.dwBufferBytes = 0;
299 primaryDesc.lpwfxFormat = 0;
301 log ("opening dsound out step 2");
302 hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0);
303 logError (hr);
305 if (hr == S_OK)
307 WAVEFORMATEX wfFormat;
308 wfFormat.wFormatTag = WAVE_FORMAT_PCM;
309 wfFormat.nChannels = (unsigned short) numChannels;
310 wfFormat.nSamplesPerSec = sampleRate;
311 wfFormat.wBitsPerSample = (unsigned short) bitDepth;
312 wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * wfFormat.wBitsPerSample / 8);
313 wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
314 wfFormat.cbSize = 0;
316 hr = pPrimaryBuffer->SetFormat (&wfFormat);
317 logError (hr);
319 if (hr == S_OK)
321 DSBUFFERDESC secondaryDesc = { 0 };
322 secondaryDesc.dwSize = sizeof (DSBUFFERDESC);
323 secondaryDesc.dwFlags = 0x8000 /* DSBCAPS_GLOBALFOCUS */
324 | 0x10000 /* DSBCAPS_GETCURRENTPOSITION2 */;
325 secondaryDesc.dwBufferBytes = totalBytesPerBuffer;
326 secondaryDesc.lpwfxFormat = &wfFormat;
328 hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0);
329 logError (hr);
331 if (hr == S_OK)
333 log ("opening dsound out step 3");
335 DWORD dwDataLen;
336 unsigned char* pDSBuffData;
338 hr = pOutputBuffer->Lock (0, totalBytesPerBuffer,
339 (LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0);
340 logError (hr);
342 if (hr == S_OK)
344 zeromem (pDSBuffData, dwDataLen);
346 hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0);
348 if (hr == S_OK)
350 hr = pOutputBuffer->SetCurrentPosition (0);
352 if (hr == S_OK)
354 hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */);
356 if (hr == S_OK)
357 return String::empty;
367 error = getDSErrorMessage (hr);
368 close();
369 return error;
372 void synchronisePosition()
374 if (pOutputBuffer != nullptr)
376 DWORD playCursor;
377 pOutputBuffer->GetCurrentPosition (&playCursor, &writeOffset);
381 bool service()
383 if (pOutputBuffer == 0)
384 return true;
386 DWORD playCursor, writeCursor;
388 for (;;)
390 HRESULT hr = pOutputBuffer->GetCurrentPosition (&playCursor, &writeCursor);
392 if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
394 pOutputBuffer->Restore();
395 continue;
398 if (hr == S_OK)
399 break;
401 logError (hr);
402 jassertfalse;
403 return true;
406 int playWriteGap = writeCursor - playCursor;
407 if (playWriteGap < 0)
408 playWriteGap += totalBytesPerBuffer;
410 int bytesEmpty = playCursor - writeOffset;
412 if (bytesEmpty < 0)
413 bytesEmpty += totalBytesPerBuffer;
415 if (bytesEmpty > (totalBytesPerBuffer - playWriteGap))
417 writeOffset = writeCursor;
418 bytesEmpty = totalBytesPerBuffer - playWriteGap;
421 if (bytesEmpty >= bytesPerBuffer)
423 void* lpbuf1 = nullptr;
424 void* lpbuf2 = nullptr;
425 DWORD dwSize1 = 0;
426 DWORD dwSize2 = 0;
428 HRESULT hr = pOutputBuffer->Lock (writeOffset, bytesPerBuffer,
429 &lpbuf1, &dwSize1,
430 &lpbuf2, &dwSize2, 0);
432 if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST
434 pOutputBuffer->Restore();
436 hr = pOutputBuffer->Lock (writeOffset, bytesPerBuffer,
437 &lpbuf1, &dwSize1,
438 &lpbuf2, &dwSize2, 0);
441 if (hr == S_OK)
443 if (bitDepth == 16)
445 int* dest = static_cast<int*> (lpbuf1);
446 const float* left = leftBuffer;
447 const float* right = rightBuffer;
448 int samples1 = dwSize1 >> 2;
449 int samples2 = dwSize2 >> 2;
451 if (left == 0)
453 while (--samples1 >= 0)
454 *dest++ = (convertInputValue (*right++) << 16);
456 dest = static_cast<int*> (lpbuf2);
458 while (--samples2 >= 0)
459 *dest++ = (convertInputValue (*right++) << 16);
461 else if (right == 0)
463 while (--samples1 >= 0)
464 *dest++ = (0xffff & convertInputValue (*left++));
466 dest = static_cast<int*> (lpbuf2);
468 while (--samples2 >= 0)
469 *dest++ = (0xffff & convertInputValue (*left++));
471 else
473 while (--samples1 >= 0)
475 const int l = convertInputValue (*left++);
476 const int r = convertInputValue (*right++);
477 *dest++ = (r << 16) | (0xffff & l);
480 dest = static_cast<int*> (lpbuf2);
482 while (--samples2 >= 0)
484 const int l = convertInputValue (*left++);
485 const int r = convertInputValue (*right++);
486 *dest++ = (r << 16) | (0xffff & l);
490 else
492 jassertfalse;
495 writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer;
497 pOutputBuffer->Unlock (lpbuf1, dwSize1, lpbuf2, dwSize2);
499 else
501 jassertfalse;
502 logError (hr);
505 bytesEmpty -= bytesPerBuffer;
506 return true;
508 else
510 return false;
514 int bitDepth;
515 bool doneFlag;
517 private:
518 String name;
519 GUID guid;
520 int sampleRate, bufferSizeSamples;
521 float* leftBuffer;
522 float* rightBuffer;
524 IDirectSound* pDirectSound;
525 IDirectSoundBuffer* pOutputBuffer;
526 DWORD writeOffset;
527 int totalBytesPerBuffer, bytesPerBuffer;
528 unsigned int lastPlayCursor;
530 static inline int convertInputValue (const float v) noexcept
532 return jlimit (-32768, 32767, roundToInt (32767.0f * v));
535 JUCE_DECLARE_NON_COPYABLE (DSoundInternalOutChannel);
538 //==============================================================================
539 struct DSoundInternalInChannel
541 public:
542 DSoundInternalInChannel (const String& name_, const GUID& guid_, int rate,
543 int bufferSize, float* left, float* right)
544 : bitDepth (16), name (name_), guid (guid_), sampleRate (rate),
545 bufferSizeSamples (bufferSize), leftBuffer (left), rightBuffer (right),
546 pDirectSound (nullptr), pDirectSoundCapture (nullptr), pInputBuffer (nullptr)
550 ~DSoundInternalInChannel()
552 close();
555 void close()
557 HRESULT hr;
559 if (pInputBuffer != nullptr)
561 log ("closing dsound in: " + name);
562 hr = pInputBuffer->Stop();
563 logError (hr);
565 hr = pInputBuffer->Release();
566 pInputBuffer = nullptr;
567 logError (hr);
570 if (pDirectSoundCapture != nullptr)
572 hr = pDirectSoundCapture->Release();
573 pDirectSoundCapture = nullptr;
574 logError (hr);
577 if (pDirectSound != nullptr)
579 hr = pDirectSound->Release();
580 pDirectSound = nullptr;
581 logError (hr);
585 String open()
587 log ("opening dsound in device: " + name
588 + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples));
590 pDirectSound = nullptr;
591 pDirectSoundCapture = nullptr;
592 pInputBuffer = nullptr;
593 readOffset = 0;
594 totalBytesPerBuffer = 0;
596 String error;
597 HRESULT hr = E_NOINTERFACE;
599 if (dsDirectSoundCaptureCreate != 0)
600 hr = dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, 0);
602 logError (hr);
604 if (hr == S_OK)
606 const int numChannels = 2;
607 bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15;
608 totalBytesPerBuffer = (3 * bytesPerBuffer) & ~15;
610 WAVEFORMATEX wfFormat;
611 wfFormat.wFormatTag = WAVE_FORMAT_PCM;
612 wfFormat.nChannels = (unsigned short)numChannels;
613 wfFormat.nSamplesPerSec = sampleRate;
614 wfFormat.wBitsPerSample = (unsigned short)bitDepth;
615 wfFormat.nBlockAlign = (unsigned short)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
616 wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
617 wfFormat.cbSize = 0;
619 DSCBUFFERDESC captureDesc = { 0 };
620 captureDesc.dwSize = sizeof (DSCBUFFERDESC);
621 captureDesc.dwFlags = 0;
622 captureDesc.dwBufferBytes = totalBytesPerBuffer;
623 captureDesc.lpwfxFormat = &wfFormat;
625 log ("opening dsound in step 2");
626 hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0);
628 logError (hr);
630 if (hr == S_OK)
632 hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */);
633 logError (hr);
635 if (hr == S_OK)
636 return String::empty;
640 error = getDSErrorMessage (hr);
641 close();
643 return error;
646 void synchronisePosition()
648 if (pInputBuffer != nullptr)
650 DWORD capturePos;
651 pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*)&readOffset);
655 bool service()
657 if (pInputBuffer == 0)
658 return true;
660 DWORD capturePos, readPos;
661 HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos);
662 logError (hr);
664 if (hr != S_OK)
665 return true;
667 int bytesFilled = readPos - readOffset;
668 if (bytesFilled < 0)
669 bytesFilled += totalBytesPerBuffer;
671 if (bytesFilled >= bytesPerBuffer)
673 LPBYTE lpbuf1 = nullptr;
674 LPBYTE lpbuf2 = nullptr;
675 DWORD dwsize1 = 0;
676 DWORD dwsize2 = 0;
678 HRESULT hr = pInputBuffer->Lock (readOffset, bytesPerBuffer,
679 (void**) &lpbuf1, &dwsize1,
680 (void**) &lpbuf2, &dwsize2, 0);
682 if (hr == S_OK)
684 if (bitDepth == 16)
686 const float g = 1.0f / 32768.0f;
688 float* destL = leftBuffer;
689 float* destR = rightBuffer;
690 int samples1 = dwsize1 >> 2;
691 int samples2 = dwsize2 >> 2;
693 const short* src = (const short*)lpbuf1;
695 if (destL == 0)
697 while (--samples1 >= 0)
699 ++src;
700 *destR++ = *src++ * g;
703 src = (const short*)lpbuf2;
705 while (--samples2 >= 0)
707 ++src;
708 *destR++ = *src++ * g;
711 else if (destR == 0)
713 while (--samples1 >= 0)
715 *destL++ = *src++ * g;
716 ++src;
719 src = (const short*)lpbuf2;
721 while (--samples2 >= 0)
723 *destL++ = *src++ * g;
724 ++src;
727 else
729 while (--samples1 >= 0)
731 *destL++ = *src++ * g;
732 *destR++ = *src++ * g;
735 src = (const short*)lpbuf2;
737 while (--samples2 >= 0)
739 *destL++ = *src++ * g;
740 *destR++ = *src++ * g;
744 else
746 jassertfalse;
749 readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer;
751 pInputBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2);
753 else
755 logError (hr);
756 jassertfalse;
759 bytesFilled -= bytesPerBuffer;
761 return true;
763 else
765 return false;
769 unsigned int readOffset;
770 int bytesPerBuffer, totalBytesPerBuffer;
771 int bitDepth;
772 bool doneFlag;
774 private:
775 String name;
776 GUID guid;
777 int sampleRate, bufferSizeSamples;
778 float* leftBuffer;
779 float* rightBuffer;
781 IDirectSound* pDirectSound;
782 IDirectSoundCapture* pDirectSoundCapture;
783 IDirectSoundCaptureBuffer* pInputBuffer;
785 JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel);
788 //==============================================================================
789 class DSoundAudioIODevice : public AudioIODevice,
790 public Thread
792 public:
793 DSoundAudioIODevice (const String& deviceName,
794 const int outputDeviceIndex_,
795 const int inputDeviceIndex_)
796 : AudioIODevice (deviceName, "DirectSound"),
797 Thread ("Juce DSound"),
798 outputDeviceIndex (outputDeviceIndex_),
799 inputDeviceIndex (inputDeviceIndex_),
800 isOpen_ (false),
801 isStarted (false),
802 bufferSizeSamples (0),
803 totalSamplesOut (0),
804 sampleRate (0.0),
805 inputBuffers (1, 1),
806 outputBuffers (1, 1),
807 callback (nullptr)
809 if (outputDeviceIndex_ >= 0)
811 outChannels.add (TRANS("Left"));
812 outChannels.add (TRANS("Right"));
815 if (inputDeviceIndex_ >= 0)
817 inChannels.add (TRANS("Left"));
818 inChannels.add (TRANS("Right"));
822 ~DSoundAudioIODevice()
824 close();
827 const String open (const BigInteger& inputChannels,
828 const BigInteger& outputChannels,
829 double sampleRate, int bufferSizeSamples)
831 lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples);
832 isOpen_ = lastError.isEmpty();
834 return lastError;
837 void close()
839 stop();
841 if (isOpen_)
843 closeDevice();
844 isOpen_ = false;
848 bool isOpen() { return isOpen_ && isThreadRunning(); }
849 int getCurrentBufferSizeSamples() { return bufferSizeSamples; }
850 double getCurrentSampleRate() { return sampleRate; }
851 const BigInteger getActiveOutputChannels() const { return enabledOutputs; }
852 const BigInteger getActiveInputChannels() const { return enabledInputs; }
853 int getOutputLatencyInSamples() { return (int) (getCurrentBufferSizeSamples() * 1.5); }
854 int getInputLatencyInSamples() { return getOutputLatencyInSamples(); }
855 StringArray getOutputChannelNames() { return outChannels; }
856 StringArray getInputChannelNames() { return inChannels; }
858 int getNumSampleRates() { return 4; }
859 int getDefaultBufferSize() { return 2560; }
860 int getNumBufferSizesAvailable() { return 50; }
862 double getSampleRate (int index)
864 const double samps[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
865 return samps [jlimit (0, 3, index)];
868 int getBufferSizeSamples (int index)
870 int n = 64;
871 for (int i = 0; i < index; ++i)
872 n += (n < 512) ? 32
873 : ((n < 1024) ? 64
874 : ((n < 2048) ? 128 : 256));
876 return n;
879 int getCurrentBitDepth()
881 int i, bits = 256;
883 for (i = inChans.size(); --i >= 0;)
884 bits = jmin (bits, inChans[i]->bitDepth);
886 for (i = outChans.size(); --i >= 0;)
887 bits = jmin (bits, outChans[i]->bitDepth);
889 if (bits > 32)
890 bits = 16;
892 return bits;
895 void start (AudioIODeviceCallback* call)
897 if (isOpen_ && call != nullptr && ! isStarted)
899 if (! isThreadRunning())
901 // something gone wrong and the thread's stopped..
902 isOpen_ = false;
903 return;
906 call->audioDeviceAboutToStart (this);
908 const ScopedLock sl (startStopLock);
909 callback = call;
910 isStarted = true;
914 void stop()
916 if (isStarted)
918 AudioIODeviceCallback* const callbackLocal = callback;
921 const ScopedLock sl (startStopLock);
922 isStarted = false;
925 if (callbackLocal != nullptr)
926 callbackLocal->audioDeviceStopped();
930 bool isPlaying() { return isStarted && isOpen_ && isThreadRunning(); }
931 const String getLastError() { return lastError; }
933 //==============================================================================
934 StringArray inChannels, outChannels;
935 int outputDeviceIndex, inputDeviceIndex;
937 private:
938 bool isOpen_;
939 bool isStarted;
940 String lastError;
942 OwnedArray <DSoundInternalInChannel> inChans;
943 OwnedArray <DSoundInternalOutChannel> outChans;
944 WaitableEvent startEvent;
946 int bufferSizeSamples;
947 int volatile totalSamplesOut;
948 int64 volatile lastBlockTime;
949 double sampleRate;
950 BigInteger enabledInputs, enabledOutputs;
951 AudioSampleBuffer inputBuffers, outputBuffers;
953 AudioIODeviceCallback* callback;
954 CriticalSection startStopLock;
956 String openDevice (const BigInteger& inputChannels,
957 const BigInteger& outputChannels,
958 double sampleRate_, int bufferSizeSamples_);
960 void closeDevice()
962 isStarted = false;
963 stopThread (5000);
965 inChans.clear();
966 outChans.clear();
967 inputBuffers.setSize (1, 1);
968 outputBuffers.setSize (1, 1);
971 void resync()
973 if (! threadShouldExit())
975 sleep (5);
977 int i;
978 for (i = 0; i < outChans.size(); ++i)
979 outChans.getUnchecked(i)->synchronisePosition();
981 for (i = 0; i < inChans.size(); ++i)
982 inChans.getUnchecked(i)->synchronisePosition();
986 public:
987 void run()
989 while (! threadShouldExit())
991 if (wait (100))
992 break;
995 const int latencyMs = (int) (bufferSizeSamples * 1000.0 / sampleRate);
996 const int maxTimeMS = jmax (5, 3 * latencyMs);
998 while (! threadShouldExit())
1000 int numToDo = 0;
1001 uint32 startTime = Time::getMillisecondCounter();
1003 int i;
1004 for (i = inChans.size(); --i >= 0;)
1006 inChans.getUnchecked(i)->doneFlag = false;
1007 ++numToDo;
1010 for (i = outChans.size(); --i >= 0;)
1012 outChans.getUnchecked(i)->doneFlag = false;
1013 ++numToDo;
1016 if (numToDo > 0)
1018 const int maxCount = 3;
1019 int count = maxCount;
1021 for (;;)
1023 for (i = inChans.size(); --i >= 0;)
1025 DSoundInternalInChannel* const in = inChans.getUnchecked(i);
1027 if ((! in->doneFlag) && in->service())
1029 in->doneFlag = true;
1030 --numToDo;
1034 for (i = outChans.size(); --i >= 0;)
1036 DSoundInternalOutChannel* const out = outChans.getUnchecked(i);
1038 if ((! out->doneFlag) && out->service())
1040 out->doneFlag = true;
1041 --numToDo;
1045 if (numToDo <= 0)
1046 break;
1048 if (Time::getMillisecondCounter() > startTime + maxTimeMS)
1050 resync();
1051 break;
1054 if (--count <= 0)
1056 Sleep (1);
1057 count = maxCount;
1060 if (threadShouldExit())
1061 return;
1064 else
1066 sleep (1);
1069 const ScopedLock sl (startStopLock);
1071 if (isStarted)
1073 JUCE_TRY
1075 callback->audioDeviceIOCallback (const_cast <const float**> (inputBuffers.getArrayOfChannels()),
1076 inputBuffers.getNumChannels(),
1077 outputBuffers.getArrayOfChannels(),
1078 outputBuffers.getNumChannels(),
1079 bufferSizeSamples);
1081 JUCE_CATCH_EXCEPTION
1083 totalSamplesOut += bufferSizeSamples;
1085 else
1087 outputBuffers.clear();
1088 totalSamplesOut = 0;
1089 sleep (1);
1094 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice);
1097 //==============================================================================
1098 struct DSoundDeviceList
1100 StringArray outputDeviceNames, inputDeviceNames;
1101 Array<GUID> outputGuids, inputGuids;
1103 void scan()
1105 outputDeviceNames.clear();
1106 inputDeviceNames.clear();
1107 outputGuids.clear();
1108 inputGuids.clear();
1110 if (dsDirectSoundEnumerateW != 0)
1112 dsDirectSoundEnumerateW (outputEnumProcW, this);
1113 dsDirectSoundCaptureEnumerateW (inputEnumProcW, this);
1117 bool operator!= (const DSoundDeviceList& other) const noexcept
1119 return outputDeviceNames != other.outputDeviceNames
1120 || inputDeviceNames != other.inputDeviceNames
1121 || outputGuids != other.outputGuids
1122 || inputGuids != other.inputGuids;
1125 private:
1126 static BOOL enumProc (LPGUID lpGUID, String desc, StringArray& names, Array<GUID>& guids)
1128 desc = desc.trim();
1130 if (desc.isNotEmpty())
1132 const String origDesc (desc);
1134 int n = 2;
1135 while (names.contains (desc))
1136 desc = origDesc + " (" + String (n++) + ")";
1138 names.add (desc);
1139 guids.add (lpGUID != nullptr ? *lpGUID : GUID());
1142 return TRUE;
1145 BOOL outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); }
1146 BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); }
1148 static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
1150 return static_cast<DSoundDeviceList*> (object)->outputEnumProc (lpGUID, description);
1153 static BOOL CALLBACK inputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object)
1155 return static_cast<DSoundDeviceList*> (object)->inputEnumProc (lpGUID, description);
1159 //==============================================================================
1160 String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels,
1161 const BigInteger& outputChannels,
1162 double sampleRate_, int bufferSizeSamples_)
1164 closeDevice();
1165 totalSamplesOut = 0;
1167 sampleRate = sampleRate_;
1169 if (bufferSizeSamples_ <= 0)
1170 bufferSizeSamples_ = 960; // use as a default size if none is set.
1172 bufferSizeSamples = bufferSizeSamples_ & ~7;
1174 DSoundDeviceList dlh;
1175 dlh.scan();
1177 enabledInputs = inputChannels;
1178 enabledInputs.setRange (inChannels.size(),
1179 enabledInputs.getHighestBit() + 1 - inChannels.size(),
1180 false);
1182 inputBuffers.setSize (jmax (1, enabledInputs.countNumberOfSetBits()), bufferSizeSamples);
1183 inputBuffers.clear();
1184 int i, numIns = 0;
1186 for (i = 0; i <= enabledInputs.getHighestBit(); i += 2)
1188 float* left = nullptr;
1189 if (enabledInputs[i])
1190 left = inputBuffers.getSampleData (numIns++);
1192 float* right = nullptr;
1193 if (enabledInputs[i + 1])
1194 right = inputBuffers.getSampleData (numIns++);
1196 if (left != nullptr || right != nullptr)
1197 inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex],
1198 dlh.inputGuids [inputDeviceIndex],
1199 (int) sampleRate, bufferSizeSamples,
1200 left, right));
1203 enabledOutputs = outputChannels;
1204 enabledOutputs.setRange (outChannels.size(),
1205 enabledOutputs.getHighestBit() + 1 - outChannels.size(),
1206 false);
1208 outputBuffers.setSize (jmax (1, enabledOutputs.countNumberOfSetBits()), bufferSizeSamples);
1209 outputBuffers.clear();
1210 int numOuts = 0;
1212 for (i = 0; i <= enabledOutputs.getHighestBit(); i += 2)
1214 float* left = nullptr;
1215 if (enabledOutputs[i])
1216 left = outputBuffers.getSampleData (numOuts++);
1218 float* right = nullptr;
1219 if (enabledOutputs[i + 1])
1220 right = outputBuffers.getSampleData (numOuts++);
1222 if (left != nullptr || right != nullptr)
1223 outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex],
1224 dlh.outputGuids [outputDeviceIndex],
1225 (int) sampleRate, bufferSizeSamples,
1226 left, right));
1229 String error;
1231 // boost our priority while opening the devices to try to get better sync between them
1232 const int oldThreadPri = GetThreadPriority (GetCurrentThread());
1233 const int oldProcPri = GetPriorityClass (GetCurrentProcess());
1234 SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
1235 SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
1237 for (i = 0; i < outChans.size(); ++i)
1239 error = outChans[i]->open();
1241 if (error.isNotEmpty())
1243 error = "Error opening " + dlh.outputDeviceNames[i] + ": \"" + error + "\"";
1244 break;
1248 if (error.isEmpty())
1250 for (i = 0; i < inChans.size(); ++i)
1252 error = inChans[i]->open();
1254 if (error.isNotEmpty())
1256 error = "Error opening " + dlh.inputDeviceNames[i] + ": \"" + error + "\"";
1257 break;
1262 if (error.isEmpty())
1264 totalSamplesOut = 0;
1266 for (i = 0; i < outChans.size(); ++i)
1267 outChans.getUnchecked(i)->synchronisePosition();
1269 for (i = 0; i < inChans.size(); ++i)
1270 inChans.getUnchecked(i)->synchronisePosition();
1272 startThread (9);
1273 sleep (10);
1275 notify();
1277 else
1279 log (error);
1282 SetThreadPriority (GetCurrentThread(), oldThreadPri);
1283 SetPriorityClass (GetCurrentProcess(), oldProcPri);
1285 return error;
1288 //==============================================================================
1289 class DSoundAudioIODeviceType : public AudioIODeviceType,
1290 private DeviceChangeDetector
1292 public:
1293 DSoundAudioIODeviceType()
1294 : AudioIODeviceType ("DirectSound"),
1295 DeviceChangeDetector (L"DirectSound"),
1296 hasScanned (false)
1298 initialiseDSoundFunctions();
1301 //==============================================================================
1302 void scanForDevices()
1304 hasScanned = true;
1305 deviceList.scan();
1308 StringArray getDeviceNames (bool wantInputNames) const
1310 jassert (hasScanned); // need to call scanForDevices() before doing this
1312 return wantInputNames ? deviceList.inputDeviceNames
1313 : deviceList.outputDeviceNames;
1316 int getDefaultDeviceIndex (bool /*forInput*/) const
1318 jassert (hasScanned); // need to call scanForDevices() before doing this
1319 return 0;
1322 int getIndexOfDevice (AudioIODevice* device, bool asInput) const
1324 jassert (hasScanned); // need to call scanForDevices() before doing this
1326 DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device);
1327 if (d == 0)
1328 return -1;
1330 return asInput ? d->inputDeviceIndex
1331 : d->outputDeviceIndex;
1334 bool hasSeparateInputsAndOutputs() const { return true; }
1336 AudioIODevice* createDevice (const String& outputDeviceName,
1337 const String& inputDeviceName)
1339 jassert (hasScanned); // need to call scanForDevices() before doing this
1341 const int outputIndex = deviceList.outputDeviceNames.indexOf (outputDeviceName);
1342 const int inputIndex = deviceList.inputDeviceNames.indexOf (inputDeviceName);
1344 if (outputIndex >= 0 || inputIndex >= 0)
1345 return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
1346 : inputDeviceName,
1347 outputIndex, inputIndex);
1349 return nullptr;
1352 private:
1353 //==============================================================================
1354 DSoundDeviceList deviceList;
1355 bool hasScanned;
1357 void systemDeviceChanged()
1359 DSoundDeviceList newList;
1360 newList.scan();
1362 if (newList != deviceList)
1364 deviceList = newList;
1365 callDeviceChangeListeners();
1369 //==============================================================================
1370 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType);
1373 //==============================================================================
1374 AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
1376 return new DSoundAudioIODeviceType();
1379 #undef log
1381 #endif